diff --git a/@types/packages/localization-editor/@types/gettext-parser/index.d.ts b/@types/packages/localization-editor/@types/gettext-parser/index.d.ts new file mode 100644 index 0000000..fb234b0 --- /dev/null +++ b/@types/packages/localization-editor/@types/gettext-parser/index.d.ts @@ -0,0 +1,42 @@ +// Type definitions for gettext-parser 4.0 +// Project: https://github.com/smhg/gettext-parser +// Definitions by: Lorent Lempereur +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module 'gettext-parser' { + export interface GetTextComment { + translator?: string; + reference?: string; + extracted?: string; + flag?: string; + previous?: string; + } + + export interface GetTextTranslation { + msgctxt?: string | undefined; + msgid: string; + msgid_plural?: string; + msgstr: string[]; + comments: GetTextComment; + } + + export interface GetTextTranslations { + charset: string; + headers: { [headerName: string]: string }; + translations: { [msgctxt: string]: { [msgId: string]: GetTextTranslation } }; + } + + export interface PoParser { + parse: (buffer: Buffer | string, defaultCharset?: string) => GetTextTranslations; + compile: (table: GetTextTranslations, options?: any) => Buffer; + createParseStream: (buffer: any, defaultCharset?: string) => any; + } + + export interface MoParser { + parse: (buffer: Buffer | string, defaultCharset?: string) => GetTextTranslations; + compile: (table: GetTextTranslations, options?: any) => Buffer; + } + + export const po: PoParser; + export const mo: MoParser; +} diff --git a/@types/packages/localization-editor/@types/globals.d.ts b/@types/packages/localization-editor/@types/globals.d.ts new file mode 100644 index 0000000..d82abda --- /dev/null +++ b/@types/packages/localization-editor/@types/globals.d.ts @@ -0,0 +1,104 @@ +/* + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +declare const gfx: any; +declare const global: any; + +interface Window { + + [x: string]: any; + + WebGL2RenderingContext: any; + + sharedCanvas: any; + __canvas: any; + canvas: any; + + XMLHttpRequest: any; + mozRequestAnimationFrame(callback: any, element?: any): any; + oRequestAnimationFrame(callback: any, element?: any): any; + msRequestAnimationFrame(callback: any, element?: any): any; + cancelRequestAnimationFrame(callback: any, element?: any): any; + msCancelRequestAnimationFrame(callback: any, element?: any): any; + mozCancelRequestAnimationFrame(callback: any, element?: any): any; + oCancelRequestAnimationFrame(callback: any, element?: any): any; + webkitCancelRequestAnimationFrame(callback: any, element?: any): any; + msCancelAnimationFrame(callback: any, element?: any): any; + mozCancelAnimationFrame(callback: any, element?: any): any; + ocancelAnimationFrame(callback: any, element?: any): any; +} + +interface Document { + mozHidden: any; + msHidden: any; + webkitHidden: any; +} + +interface HTMLElement { + content: any; + name: any; +} + +declare type CompareFunction = (a: T, b: T) => number; + +declare type RecursivePartial = { + [P in keyof T]?: + T[P] extends Array ? Array> : + T[P] extends ReadonlyArray ? ReadonlyArray> : RecursivePartial; +}; + +declare type TypedArray = Uint8Array | Uint8ClampedArray | Int8Array | Uint16Array | +Int16Array | Uint32Array | Int32Array | Float32Array | Float64Array; + +declare type TypedArrayConstructor = Uint8ArrayConstructor | Uint8ClampedArrayConstructor | +Int8ArrayConstructor | Uint16ArrayConstructor | Int16ArrayConstructor | Uint32ArrayConstructor | +Int32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor; + +declare interface IWritableArrayLike { + readonly length: number; + [index: number]: T; +} + +declare type Constructor = new (...args: any[]) => T; + +declare type AbstractedConstructor = abstract new (...args: any[]) => T; + +/** + * Alias of `Function` but suppress eslint warning. + * Please avoid using it and explicitly specify function signatures as possible. + */ +// eslint-disable-next-line @typescript-eslint/ban-types +declare type AnyFunction = Function; + +declare type Mutable = { -readonly [P in keyof T]: T[P] }; + +declare type Getter = () => any; + +declare type Setter = (value: any) => void; + +declare const Buffer: any; + +declare type EnumAlias = EnumT[keyof EnumT]; diff --git a/@types/packages/localization-editor/@types/index.d.ts b/@types/packages/localization-editor/@types/index.d.ts new file mode 100644 index 0000000..ca3d534 --- /dev/null +++ b/@types/packages/localization-editor/@types/index.d.ts @@ -0,0 +1,37 @@ +interface CCENodeEventMap { + added (node: import('cc').Node): void + change (node: import('cc').Node): void + removed (node: import('cc').Node): void +} + +interface CCEComponentEventMap { + added (component: import('cc').Component): void, + removed (component: import('cc').Component): void, +} + +declare class CCENodeManager extends EventEmitter { + on (message: T, callback: CCENodeEventMap[T]): this; + off (message: T, callback: CCENodeEventMap[T]): this; +} +declare class CCEComponentManager extends EventEmitter { + on (message: T, callback: CCEComponentEventMap[T]): this; + off (message: T, callback: CCEComponentEventMap[T]): this; +} + +type CCE = { + Node: CCENodeManager, + Component: CCEComponentManager, + Prefab: { + generatePrefabDataFromNode(nodeUUID: string| cc.Node): string | null + } +}; + +declare const cce: CCE; +declare type UnPromise = T extends Promise ? R : T; +declare type UUID = string; +declare type Dump = { value: Record }; +declare module 'cc/env' { + export const EDITOR: boolean; + export const BUILD: boolean; +} +declare const EditorExtends: any; diff --git a/@types/packages/localization-editor/@types/intl/index.d.ts b/@types/packages/localization-editor/@types/intl/index.d.ts new file mode 100644 index 0000000..f498b47 --- /dev/null +++ b/@types/packages/localization-editor/@types/intl/index.d.ts @@ -0,0 +1,111 @@ +declare namespace Intl { + type BCP47LanguageTag = string; + /** + * The locale matching algorithm to use. + * + * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#parameters). + */ + type ListFormatLocaleMatcher = 'lookup' | 'best fit'; + + /** + * The format of output message. + * + * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#parameters). + */ + type ListFormatType = 'conjunction' | 'disjunction' | 'unit'; + + /** + * The length of the formatted message. + * + * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#parameters). + */ + type ListFormatStyle = 'long' | 'short' | 'narrow'; + + /** + * An object with some or all properties of the `Intl.ListFormat` constructor `options` parameter. + * + * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#parameters). + */ + interface ListFormatOptions { + /** The locale matching algorithm to use. For information about this option, see [Intl page](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_negotiation). */ + localeMatcher?: ListFormatLocaleMatcher | undefined; + /** The format of output message. */ + type?: ListFormatType | undefined; + /** The length of the internationalized message. */ + style?: ListFormatStyle | undefined; + } + + interface ListFormat { + /** + * Returns a string with a language-specific representation of the list. + * + * @param list - An iterable object, such as an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). + * + * @throws `TypeError` if `list` includes something other than the possible values. + * + * @returns {string} A language-specific formatted string representing the elements of the list. + * + * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/format). + */ + format(list: Iterable): string; + + /** + * Returns an Array of objects representing the different components that can be used to format a list of values in a locale-aware fashion. + * + * @param list - An iterable object, such as an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array), to be formatted according to a locale. + * + * @throws `TypeError` if `list` includes something other than the possible values. + * + * @returns {{ type: "element" | "literal", value: string; }[]} An Array of components which contains the formatted parts from the list. + * + * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/formatToParts). + */ + formatToParts(list: Iterable): { type: 'element' | 'literal', value: string; }[]; + } + + const ListFormat: { + prototype: ListFormat; + + /** + * Creates [Intl.ListFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat) objects that + * enable language-sensitive list formatting. + * + * @param locales - A string with a [BCP 47 language tag](http://tools.ietf.org/html/rfc5646), or an array of such strings. + * For the general form and interpretation of the `locales` argument, + * see the [`Intl` page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation). + * + * @param options - An [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#parameters) + * with some or all options of `ListFormatOptions`. + * + * @returns [Intl.ListFormatOptions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat) object. + * + * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat). + */ + new(locales?: BCP47LanguageTag | BCP47LanguageTag[], options?: ListFormatOptions): ListFormat; + + /** + * Returns an array containing those of the provided locales that are + * supported in list formatting without having to fall back to the runtime's default locale. + * + * @param locales - A string with a [BCP 47 language tag](http://tools.ietf.org/html/rfc5646), or an array of such strings. + * For the general form and interpretation of the `locales` argument, + * see the [`Intl` page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation). + * + * @param options - An [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/supportedLocalesOf#parameters). + * with some or all possible options. + * + * @returns An array of strings representing a subset of the given locale tags that are supported in list + * formatting without having to fall back to the runtime's default locale. + * + * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/supportedLocalesOf). + */ + supportedLocalesOf(locales: BCP47LanguageTag | BCP47LanguageTag[], options?: Pick): BCP47LanguageTag[]; + }; + + type TextInfo = { direction: 'ltr' | 'rtl' } + + interface Locale { + prototype: Locale + textInfo(): TextInfo + } +} diff --git a/@types/packages/localization-editor/@types/po.d.ts b/@types/packages/localization-editor/@types/po.d.ts new file mode 100644 index 0000000..ca0af63 --- /dev/null +++ b/@types/packages/localization-editor/@types/po.d.ts @@ -0,0 +1,25 @@ +export type PoHeader = { + /** This is the name and version of the package. */ + 'Project-Id-Version'?: string + /** (非必须) po 创建日期 */ + 'POT-Creation-Date'?: string + /** (非必须) po 修改日期*/ + 'PO-Revision-Date'?: string + /** 上一个翻译人员 */ + 'Last-Translator'?: string + /** 翻译团队的名称或者邮箱 */ + 'Language-Team'?: string + /** (非必须)要使 MIME 文档符合 RFC 2045,需要此字段在顶级头中值为 1.0 */ + 'MIME-Version'?: '1.0' + /** 译文的语言 */ + Language: string + /** Content-Type 定义了正文的类型,我们实际上是通过这个标识来知道正文内是什么类型的文件。比如:text/plain 表示的是无格式的文本正文,text/html 表示的 Html 文档,image/gif 表示的是 gif 格式的图片等等 */ + 'Content-Type'?: 'text/plain; charset=UTF-8' + /** 它表示了这个部分文档的编码方式。只有识别了这个说明,才能用正确的解码方式实现对其解码。 */ + 'Content-Transfer-Encoding'?: '8bit' + /** (非必须)复数的规则,*/ + 'Plural-Forms'?: string +} + +export type IPluralRulesJson = Record + diff --git a/@types/packages/localization-editor/@types/runtime/components/icu-component.d.ts b/@types/packages/localization-editor/@types/runtime/components/icu-component.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/components/icu-component.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/@types/packages/localization-editor/@types/runtime/components/l10n-component.d.ts b/@types/packages/localization-editor/@types/runtime/components/l10n-component.d.ts new file mode 100644 index 0000000..c2ef8d4 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/components/l10n-component.d.ts @@ -0,0 +1,11 @@ +/// +import { Component, Label } from 'cc'; +export default abstract class L10nComponent extends Component { + protected constructor(); + get string(): string; + label?: Label | null; + protected onLoad(): void; + protected start(): void; + render(): void; + preview(value: string): void; +} diff --git a/@types/packages/localization-editor/@types/runtime/components/l10n-label.d.ts b/@types/packages/localization-editor/@types/runtime/components/l10n-label.d.ts new file mode 100644 index 0000000..9e08628 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/components/l10n-label.d.ts @@ -0,0 +1,11 @@ +import L10nComponent from './l10n-component'; +export default class L10nLabel extends L10nComponent { + _key: string; + set key(value: string); + get key(): string; + _count: number; + set count(value: number); + get count(): number; + onLoad(): void; + render(): void; +} diff --git a/@types/packages/localization-editor/@types/runtime/core/asset-manager-initer.d.ts b/@types/packages/localization-editor/@types/runtime/core/asset-manager-initer.d.ts new file mode 100644 index 0000000..86d14f9 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/core/asset-manager-initer.d.ts @@ -0,0 +1,13 @@ +import { AssetManager } from 'cc'; +import type { L10nManager } from './l10n-manager'; +export default class AMPipeLineManager { + initialized: boolean; + l10n?: L10nManager; + _redirectTask: this['redirectTask']; + initAssetManager(l10n: L10nManager): void; + uninstall(): void; + redirectTask: (task: { + output: AssetManager.RequestItem[]; + input: AssetManager.RequestItem[]; + }) => void; +} diff --git a/@types/packages/localization-editor/@types/runtime/core/auto-config-intl-manager.d.ts b/@types/packages/localization-editor/@types/runtime/core/auto-config-intl-manager.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/core/auto-config-intl-manager.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/@types/packages/localization-editor/@types/runtime/core/icu-options.d.ts b/@types/packages/localization-editor/@types/runtime/core/icu-options.d.ts new file mode 100644 index 0000000..387d4a4 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/core/icu-options.d.ts @@ -0,0 +1,51 @@ +import { FallbackLanguage, L10nValue } from './l10n-options'; +export declare type FormattedValue = string; +export declare type TextInfoDirection = 'ltr' | 'rtl'; +export interface StandardOption { + count?: number; + defaultValue?: L10nValue; + language?: Intl.BCP47LanguageTag; + fallbackLanguage?: FallbackLanguage; +} +export interface Template { + [key: string]: string | { + [key: string]: StandardOption; + }; +} +export interface NumberFormatOptions extends Intl.NumberFormatOptions { + style?: 'decimal' | 'percent' | 'currency' | string; + /** + * 货币代码,采用ISO 4217标准 + * @see ISO4217Tag + */ + currency?: string; + currencySign?: 'standard' | 'accounting' | string; + currencyDisplay?: 'symbol' | 'code' | 'name' | string; + useGrouping?: boolean; + minimumIntegerDigits?: number; + minimumFractionDigits?: number; + maximumFractionDigits?: number; + minimumSignificantDigits?: number; + maximumSignificantDigits?: number; +} +export interface DateTimeFormatOptions { + localeMatcher?: 'best fit' | 'lookup' | undefined | string; + weekday?: 'long' | 'short' | 'narrow' | undefined | string; + era?: 'long' | 'short' | 'narrow' | undefined | string; + year?: 'numeric' | '2-digit' | undefined | string; + month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow' | undefined | string; + day?: 'numeric' | '2-digit' | undefined | string; + hour?: 'numeric' | '2-digit' | undefined | string; + minute?: 'numeric' | '2-digit' | undefined | string; + second?: 'numeric' | '2-digit' | undefined | string; + timeZoneName?: 'long' | 'short' | undefined | string; + formatMatcher?: 'best fit' | 'basic' | undefined | string; + hour12?: boolean | undefined; + timeZone?: string | undefined; +} +export declare type RelativeTimeFormatUnit = 'second' | 'minute' | 'hour' | 'day' | 'month' | 'year' | string; +export interface RelativeTimeFormatOptions { + localeMatcher?: 'lookup' | 'best fit' | string; + style?: 'narrow' | 'short' | 'long' | string; + numeric?: 'auto' | 'always' | string; +} diff --git a/@types/packages/localization-editor/@types/runtime/core/icu-type.d.ts b/@types/packages/localization-editor/@types/runtime/core/icu-type.d.ts new file mode 100644 index 0000000..d818480 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/core/icu-type.d.ts @@ -0,0 +1,10 @@ +/** + * Intl formatting + */ +declare enum ICUType { + DateTime = 0, + Number = 1, + List = 2, + RelativeTime = 3, +} +export default ICUType; diff --git a/@types/packages/localization-editor/@types/runtime/core/l10n-listen-event.d.ts b/@types/packages/localization-editor/@types/runtime/core/l10n-listen-event.d.ts new file mode 100644 index 0000000..4dbd75d --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/core/l10n-listen-event.d.ts @@ -0,0 +1,5 @@ +declare enum L10nListenEvent { + languageChanged = 'languageChanged', + onMissingKey = 'missingKey', +} +export default L10nListenEvent; diff --git a/@types/packages/localization-editor/@types/runtime/core/l10n-manager.d.ts b/@types/packages/localization-editor/@types/runtime/core/l10n-manager.d.ts new file mode 100644 index 0000000..ff93e1f --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/core/l10n-manager.d.ts @@ -0,0 +1,60 @@ +import type { L10nOptions, ResourceData, L10nKey, L10nValue } from './l10n-options'; +import { StandardOption, Template, TextInfoDirection } from './icu-options'; +import L10nListenEvent from './l10n-listen-event'; +import ResourceDataManager from './resource-data-manager'; +import AMPipeLineManager from './asset-manager-initer'; +export declare class L10nManager { + static LOCAL_STORAGE_LANGUAGE_KEY: string; + static DEFAULT_NAMESPACE: string; + static l10n: L10nManager; + /** + * @zh + * i18n 实例 + * @en + * i18next instance + */ + private _intl?; + private _options; + private resourceList?; + private resourceBundle; + resourceDataManager: ResourceDataManager; + amPipeLineManager: AMPipeLineManager; + private constructor(); + isInitialized(): boolean; + createIntl(options: L10nOptions): Promise; + cloneIntl(options: L10nOptions): void; + reloadResourceData(): Promise; + /** 初始化 i18next */ + config(options: L10nOptions): void; + changeLanguage(language: Intl.BCP47LanguageTag): Promise; + t(key: L10nKey, options?: StandardOption | Template): L10nValue; + /** + * 实验性功能暂不开放 + * 数字类ICU + */ + private tn; + /** + * 实验性功能暂不开放 + * 日期/时刻类ICU + */ + private td; + /** + * 实验性功能暂不开放 + * 时长类ICU + */ + private tt; + /** + * 实验性功能暂不开放 + * 数组类ICU + */ + private tl; + exists(key: L10nKey): boolean; + get currentLanguage(): Intl.BCP47LanguageTag; + get languages(): readonly Intl.BCP47LanguageTag[]; + direction(language?: Intl.BCP47LanguageTag): TextInfoDirection; + on(event: L10nListenEvent, callback: (...args: any[]) => void): void; + off(event: L10nListenEvent, callback: (...args: any[]) => void): void; + getResourceBundle(language: string): ResourceData | undefined; +} +declare const l10n: L10nManager; +export default l10n; diff --git a/@types/packages/localization-editor/@types/runtime/core/l10n-options.d.ts b/@types/packages/localization-editor/@types/runtime/core/l10n-options.d.ts new file mode 100644 index 0000000..03b455b --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/core/l10n-options.d.ts @@ -0,0 +1,78 @@ +export declare type L10nKey = string; +export declare type L10nValue = string; +export interface ResourceList { + defaultLanguage?: Intl.BCP47LanguageTag; + fallbackLanguage?: Intl.BCP47LanguageTag; + languages: Intl.BCP47LanguageTag[]; +} +export interface ResourceBundle { + [language: Intl.BCP47LanguageTag]: ResourceData; +} +export interface ResourceData { + [namespace: string]: ResourceItem; +} +export interface ResourceItem { + [key: string]: any; +} +export interface FallbackLanguageObjectList { + [language: string]: readonly string[]; +} +export declare type FallbackLanguage = string | readonly string[] | FallbackLanguageObjectList | ((language: Intl.BCP47LanguageTag) => string | readonly string[] | FallbackLanguageObjectList); +export interface L10nOptions { + /** + * Logs info level to console output. Helps finding issues with loading not working. + * @default false + */ + /** + * Resources to initialize with (if not using loading or not appending using addResourceBundle) + * @default undefined + */ + resources?: ResourceBundle; + /** + * Language to use (overrides language detection) + */ + language?: Intl.BCP47LanguageTag; + /** + * Language to use if translations in user language are not available. + * @default same as language + */ + fallbackLanguage?: false | FallbackLanguage; + /** + * @default IntlManager.LOCAL_STORAGE_LANGUAGE_KEY + */ + localStorageLanguageKey?: string; + /** + * @zh + * 可以对key进行前置处理,返回值应该是处理后的key + * + * @en + * Preprocess the key + * + * @param key + * @return string + * onBeforeProcessHandler + */ + beforeTranslate?: (key: L10nKey) => L10nValue; + /** + * @zh + * 对value进行后置处理,返回值应该是处理后的value + * + * @en + * Postprocess the value, return the processed value + * + * @param key + * @param value + * @return string + */ + afterTranslate?: (key: string, value: string) => string; + /** + * Allows null values as valid translation + * @default true + */ + returnNull?: boolean; + /** + * Allows empty string as valid translation + * @default true + */ + returnEmptyString?: boolean; +} diff --git a/@types/packages/localization-editor/@types/runtime/core/localization-global.d.ts b/@types/packages/localization-editor/@types/runtime/core/localization-global.d.ts new file mode 100644 index 0000000..1a3c79f --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/core/localization-global.d.ts @@ -0,0 +1,5 @@ +export declare const pluginName = 'Localization Editor'; +export declare const mainName = 'localization-editor'; +export declare const runtimeBundleName = 'l10n'; +export declare const resourceListPath = 'resource-list'; +export declare const resourceBundlePath = 'resource-bundle'; diff --git a/@types/packages/localization-editor/@types/runtime/core/resource-data-manager.d.ts b/@types/packages/localization-editor/@types/runtime/core/resource-data-manager.d.ts new file mode 100644 index 0000000..84d770f --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/core/resource-data-manager.d.ts @@ -0,0 +1,25 @@ +/// +import { AssetManager, JsonAsset } from 'cc'; +import { ResourceBundle, ResourceList } from './l10n-options'; +export default class ResourceDataManager { + readResourceList(): Promise; + readResourceBundle(tags: Intl.BCP47LanguageTag[]): Promise; + /** + * 编辑器模式下使用 + * @param locales + */ + editorLoad(locales: Intl.BCP47LanguageTag[]): Promise; + /** + * 构建后运行时使用 + * @param fileName + */ + runtimeLoad(fileName: string): Promise; + /** + * 浏览器预览使用 + * @param urlPath + */ + previewLoad(urlPath: string): Promise; + checkBundle(bundleName: string): Promise; + getBundle(bundleName: string): Promise; + getResource(bundle: AssetManager.Bundle, resourceName: string): Promise; +} diff --git a/@types/packages/localization-editor/@types/runtime/l10n.d.ts b/@types/packages/localization-editor/@types/runtime/l10n.d.ts new file mode 100644 index 0000000..049ecd5 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/l10n.d.ts @@ -0,0 +1,5 @@ +import l10n, { L10nManager } from './core/l10n-manager'; +import L10nListenEvent from './core/l10n-listen-event'; +import L10nLabel from './components/l10n-label'; +export type { L10nKey, L10nValue, ResourceList, ResourceBundle, ResourceData, ResourceItem, FallbackLanguageObjectList, FallbackLanguage, L10nOptions } from './core/l10n-options'; +export { l10n, L10nManager, L10nLabel, L10nListenEvent }; diff --git a/@types/packages/localization-editor/@types/runtime/polyfills/Intl.DateTimeFormat.d.ts b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.DateTimeFormat.d.ts new file mode 100644 index 0000000..cbc1980 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.DateTimeFormat.d.ts @@ -0,0 +1 @@ +import '@formatjs/intl-datetimeformat'; diff --git a/@types/packages/localization-editor/@types/runtime/polyfills/Intl.DisplayNames.d.ts b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.DisplayNames.d.ts new file mode 100644 index 0000000..4ddedc3 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.DisplayNames.d.ts @@ -0,0 +1 @@ +import '@formatjs/intl-displaynames'; diff --git a/@types/packages/localization-editor/@types/runtime/polyfills/Intl.ListFormat.d.ts b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.ListFormat.d.ts new file mode 100644 index 0000000..2953e6f --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.ListFormat.d.ts @@ -0,0 +1 @@ +import '@formatjs/intl-listformat'; diff --git a/@types/packages/localization-editor/@types/runtime/polyfills/Intl.Locale.d.ts b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.Locale.d.ts new file mode 100644 index 0000000..9571d24 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.Locale.d.ts @@ -0,0 +1 @@ +import '@formatjs/intl-locale'; diff --git a/@types/packages/localization-editor/@types/runtime/polyfills/Intl.NumberFormat.d.ts b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.NumberFormat.d.ts new file mode 100644 index 0000000..2affc6d --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.NumberFormat.d.ts @@ -0,0 +1 @@ +import '@formatjs/intl-numberformat'; diff --git a/@types/packages/localization-editor/@types/runtime/polyfills/Intl.PluralRules.d.ts b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.PluralRules.d.ts new file mode 100644 index 0000000..186adb3 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.PluralRules.d.ts @@ -0,0 +1 @@ +import 'intl-pluralrules'; diff --git a/@types/packages/localization-editor/@types/runtime/polyfills/Intl.RelativeTimeFormat.d.ts b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.RelativeTimeFormat.d.ts new file mode 100644 index 0000000..bd301c5 --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.RelativeTimeFormat.d.ts @@ -0,0 +1 @@ +import '@formatjs/intl-relativetimeformat'; diff --git a/@types/packages/localization-editor/@types/runtime/polyfills/Intl.getCanonicalLocales.d.ts b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.getCanonicalLocales.d.ts new file mode 100644 index 0000000..d738f0b --- /dev/null +++ b/@types/packages/localization-editor/@types/runtime/polyfills/Intl.getCanonicalLocales.d.ts @@ -0,0 +1 @@ +import '@formatjs/intl-getcanonicallocales'; diff --git a/assets/core/Oops.ts b/assets/core/Oops.ts index 5f5133a..0beecbd 100644 --- a/assets/core/Oops.ts +++ b/assets/core/Oops.ts @@ -24,7 +24,7 @@ import { GameManager } from "./game/GameManager"; import { LayerManager } from "./gui/layer/LayerManager"; /** 框架版本号 */ -export var version: string = "1.3.3"; +export var version: string = "2.0.0"; /** 框架核心模块访问入口 */ export class oops { diff --git a/dist/assets-menu.js b/dist/assets-menu.js new file mode 100644 index 0000000..c70d143 --- /dev/null +++ b/dist/assets-menu.js @@ -0,0 +1,83 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.onAssetMenu = void 0; +const tinypng_1 = require("./tinypng"); +/** 资源栏右键菜单 */ +function onAssetMenu(assetInfo) { + return [ + { + label: 'i18n:oops-framework.name', + submenu: [ + { + label: `i18n:oops-framework.script`, + submenu: [ + { + label: `i18n:oops-framework.createGameComponent`, + async click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "GameComponent"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + type: `separator`, + }, + { + label: `i18n:oops-framework.createModule`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "Module"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + label: `i18n:oops-framework.createModel`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "Model"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + label: `i18n:oops-framework.createBll`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "Bll"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + label: `i18n:oops-framework.createView`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "View"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + label: `i18n:oops-framework.createViewMvvm`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "ViewMvvm"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + ] + }, + { + label: `i18n:oops-framework.tools`, + submenu: [ + { + label: `i18n:oops-framework.tools_compress`, + click() { + (0, tinypng_1.compress)(assetInfo.file); + }, + } + ] + } + ], + }, + ]; +} +exports.onAssetMenu = onAssetMenu; +; diff --git a/dist/create-script.js b/dist/create-script.js new file mode 100644 index 0000000..578ec3f --- /dev/null +++ b/dist/create-script.js @@ -0,0 +1,120 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createScript = exports.createScriptBll = exports.createScriptModule = exports.createView = void 0; +const fs_1 = require("fs"); +const path_1 = __importDefault(require("path")); +/** 写入文件 */ +function createView(directoryPath, fileName, content, isEcsComp = true) { + return new Promise(async (resolve, reject) => { + // 创建脚本 + let className = fileName + "View"; + let scriptUrl = ""; + if (isEcsComp) { + scriptUrl = path_1.default.join(directoryPath, fileName) + "ViewComp.ts"; + } + else { + scriptUrl = path_1.default.join(directoryPath, fileName) + "View.ts"; + } + if (!(0, fs_1.existsSync)(scriptUrl)) { + content = content.replace(/<%Name%>/g, className); + await Editor.Message.request('asset-db', 'create-asset', scriptUrl, content); + } + // 创建预制 + let prefabUrl = path_1.default.join(directoryPath, fileName) + ".prefab"; + if (!(0, fs_1.existsSync)(prefabUrl)) { + if (isEcsComp) + className = className + "Comp"; + await Editor.Message.request('scene', 'execute-scene-script', { + name: "oops-framework", + method: 'createPrefab', + args: [fileName, className, prefabUrl] + }); + } + // 闪烁提示新创建的脚本文件 + Editor.Message.send('assets', 'twinkle', scriptUrl); + // 打开脚本 + Editor.Message.request('asset-db', 'open-asset', scriptUrl); + // 打开预制 + Editor.Message.request('asset-db', 'open-asset', prefabUrl); + resolve(); + }); +} +exports.createView = createView; +function createScriptModule(directoryPath, fileName, content) { + return new Promise(async (resolve, reject) => { + // 创建目录 + let pathName = fileName.toLowerCase(); + let pathModule = path_1.default.join(directoryPath, pathName); + if (!(0, fs_1.existsSync)(pathModule)) { + await Editor.Message.request('asset-db', 'create-asset', pathModule, null); + } + let subPathView = path_1.default.join(pathModule, "view"); + if (!(0, fs_1.existsSync)(subPathView)) { + await Editor.Message.request('asset-db', 'create-asset', subPathView, null); + } + let subPathBll = path_1.default.join(pathModule, "bll"); + if (!(0, fs_1.existsSync)(subPathBll)) { + await Editor.Message.request('asset-db', 'create-asset', subPathBll, null); + } + let subPathModel = path_1.default.join(pathModule, "model"); + if (!(0, fs_1.existsSync)(subPathModel)) { + await Editor.Message.request('asset-db', 'create-asset', subPathModel, null); + } + // 创建脚本 + let scriptUrl = path_1.default.join(pathModule, fileName) + ".ts"; + if (!(0, fs_1.existsSync)(scriptUrl)) { + content = content.replace(/<%Name%>/g, fileName); + await Editor.Message.request('asset-db', 'create-asset', scriptUrl, content); + } + // 闪烁提示新创建的脚本文件 + Editor.Message.send('assets', 'twinkle', scriptUrl); + // 打开脚本 + Editor.Message.request('asset-db', 'open-asset', scriptUrl); + resolve(); + }); +} +exports.createScriptModule = createScriptModule; +/** 创建脚本 */ +function createScriptBll(directoryPath, fileName, content, moduleName) { + return new Promise(async (resolve, reject) => { + let scriptUrl = path_1.default.join(directoryPath, fileName) + ".ts"; + // 创建脚本 + if (!(0, fs_1.existsSync)(scriptUrl)) { + content = content.replace(/<%Name%>/g, fileName); + content = content.replace(/<%ModuleName%>/g, moduleName); + await Editor.Message.request('asset-db', 'create-asset', scriptUrl, content); + } + // 闪烁提示新创建的脚本文件 + Editor.Message.send('assets', 'twinkle', scriptUrl); + // 打开脚本 + Editor.Message.request('asset-db', 'open-asset', scriptUrl); + resolve(); + }); +} +exports.createScriptBll = createScriptBll; +/** 创建业务层脚本 */ +function createScript(directoryPath, fileName, content, isEcsComp = true) { + return new Promise(async (resolve, reject) => { + let scriptUrl = ""; + if (isEcsComp) { + scriptUrl = path_1.default.join(directoryPath, fileName) + "Comp.ts"; + } + else { + scriptUrl = path_1.default.join(directoryPath, fileName) + ".ts"; + } + // 创建脚本 + if (!(0, fs_1.existsSync)(scriptUrl)) { + content = content.replace(/<%Name%>/g, fileName); + await Editor.Message.request('asset-db', 'create-asset', scriptUrl, content); + } + // 闪烁提示新创建的脚本文件 + Editor.Message.send('assets', 'twinkle', scriptUrl); + // 打开脚本 + Editor.Message.request('asset-db', 'open-asset', scriptUrl); + resolve(); + }); +} +exports.createScript = createScript; diff --git a/dist/default/index.js b/dist/default/index.js new file mode 100644 index 0000000..1677c9c --- /dev/null +++ b/dist/default/index.js @@ -0,0 +1,116 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const fs_extra_1 = require("fs-extra"); +const path_1 = require("path"); +const vue_1 = require("vue"); +const create_script_1 = require("../create-script"); +const GameComponent_1 = require("../template/GameComponent"); +const Module_1 = require("../template/Module"); +const ModuleBll_1 = require("../template/ModuleBll"); +const ModuleModel_1 = require("../template/ModuleModel"); +const ModuleView_1 = require("../template/ModuleView"); +const ModuleViewVM_1 = require("../template/ModuleViewVM"); +const panelDataMap = new WeakMap(); +module.exports = Editor.Panel.define({ + listeners: { + show() { console.log('show'); }, + hide() { console.log('hide'); }, + }, + template: (0, fs_extra_1.readFileSync)((0, path_1.join)(__dirname, '../../static/template/default/index.html'), 'utf-8'), + style: (0, fs_extra_1.readFileSync)((0, path_1.join)(__dirname, '../../static/style/default/index.css'), 'utf-8'), + $: { + app: '#app', + }, + ready() { + let filename = "Default"; + let type = localStorage.getItem('create_type'); + localStorage.removeItem('create_type'); + let path = localStorage.getItem('create_path'); + localStorage.removeItem('create_path'); + let title = "???"; + let showModule = false; + let moduleName = "ModuleName"; + switch (type) { + case "GameComponent": + title = `i18n:oops-framework.createGameComponent`; + break; + case "Module": + title = `i18n:oops-framework.createModule`; + break; + case "Model": + title = `i18n:oops-framework.createModel`; + break; + case "Bll": + title = `i18n:oops-framework.createBll`; + showModule = true; + break; + case "View": + title = `i18n:oops-framework.createView`; + break; + case "ViewMvvm": + title = `i18n:oops-framework.createViewMvvm`; + break; + } + // 创建框架配置界面 + if (this.$.app) { + const app = (0, vue_1.createApp)({}); + app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-'); + app.component('MyConfig', { + template: (0, fs_extra_1.readFileSync)((0, path_1.join)(__dirname, '../../static/template/vue/set_file_name.html'), 'utf-8'), + data() { + return { + title: title, + filename: filename, + showModule: showModule + }; + }, + methods: { + // 记录输入的文件名 + onInputName(event) { + filename = event.target.value; + }, + onModuleName(event) { + moduleName = event.target.value; + }, + // 创建文件 + async onConfirm() { + if (filename.trim().length == 0) { + await Editor.Dialog.info('请输入文件名'); + return; + } + switch (type) { + case "GameComponent": + await (0, create_script_1.createView)(path, filename, GameComponent_1.TemplateGameComponent, false); + break; + case "Module": + await (0, create_script_1.createScriptModule)(path, filename, Module_1.TemplateModule); + break; + case "Model": + await (0, create_script_1.createScript)(path, filename, ModuleModel_1.TemplateModel); + break; + case "Bll": + await (0, create_script_1.createScriptBll)(path, filename, ModuleBll_1.TemplateBll, moduleName); + break; + case "View": + await (0, create_script_1.createView)(path, filename, ModuleView_1.TemplateView); + break; + case "ViewMvvm": + await (0, create_script_1.createView)(path, filename, ModuleViewVM_1.TemplateViewMvvm); + break; + } + close(); + } + }, + }); + app.mount(this.$.app); + panelDataMap.set(this, app); + } + }, + beforeClose() { }, + close() { + const app = panelDataMap.get(this); + if (app) { + app.unmount(); + } + }, +}); diff --git a/dist/main.js b/dist/main.js index 1b35cbd..524ed89 100644 --- a/dist/main.js +++ b/dist/main.js @@ -46,5 +46,5 @@ exports.methods = { /** 点亮 Github 星星 */ github() { electron_1.shell.openExternal('https://github.com/dgflash/oops-framework'); - }, + } }; diff --git a/dist/scene.js b/dist/scene.js new file mode 100644 index 0000000..2d5bb20 --- /dev/null +++ b/dist/scene.js @@ -0,0 +1,35 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.methods = exports.unload = exports.load = void 0; +function load() { } +exports.load = load; +function unload() { } +exports.unload = unload; +// 在其他扩展脚本中,我们可以使用如下代码调用 rotateCamera 函数 +// const options: ExecuteSceneScriptMethodOptions = { +// name: scene.ts 所在的扩展包名, 如: App, +// method: scene.ts 中定义的方法, 如: createPrefab, +// args: 参数,可选, 只传递json +// }; +// const result = await Editor.Message.request('scene', 'execute-scene-script', options); +exports.methods = { + /** 创建视图层制 */ + async createPrefab(fileName, className, prefabUrl) { + const { Node, js, Layers } = require('cc'); + const node = new Node(fileName); + node.layer = Layers.Enum.UI_2D; + while (true) { + const result = js.getClassByName(className); + if (result) + break; + await new Promise((next) => { + setTimeout(next, 100); + }); + } + const com = node.addComponent(className); + com.resetInEditor && com.resetInEditor(); + const info = cce.Prefab.generatePrefabDataFromNode(node); + node.destroy(); + return Editor.Message.request('asset-db', 'create-asset', prefabUrl, info.prefabData || info); + } +}; diff --git a/dist/template/GameComponent.js b/dist/template/GameComponent.js new file mode 100644 index 0000000..149a2d4 --- /dev/null +++ b/dist/template/GameComponent.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TemplateGameComponent = void 0; +exports.TemplateGameComponent = `import { _decorator } from 'cc'; +import { GameComponent } from "db://oops-framework/module/common/GameComponent"; + +const { ccclass, property } = _decorator; + +/** 显示对象控制 */ +@ccclass('<%Name%>') +export class <%Name%> extends GameComponent { + protected start() { + + } +}`; diff --git a/dist/template/Module.js b/dist/template/Module.js new file mode 100644 index 0000000..11e1519 --- /dev/null +++ b/dist/template/Module.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TemplateModule = void 0; +exports.TemplateModule = `import { ecs } from "db://oops-framework/libs/ecs/ECS"; + +/** <%Name%> 模块 */ +@ecs.register('<%Name%>') +export class <%Name%> extends ecs.Entity { + /** ---------- 数据层 ---------- */ + // <%Name%>Model!: <%Name%>ModelComp; + + /** ---------- 业务层 ---------- */ + // <%Name%>Bll!: <%Name%>BllComp; + + /** ---------- 视图层 ---------- */ + // <%Name%>View!: <%Name%>ViewComp; + + /** 初始添加的数据层组件 */ + protected init() { + // this.addComponents(); + } +}`; diff --git a/dist/template/ModuleBll.js b/dist/template/ModuleBll.js new file mode 100644 index 0000000..c38512e --- /dev/null +++ b/dist/template/ModuleBll.js @@ -0,0 +1,27 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TemplateBll = void 0; +exports.TemplateBll = `import { ecs } from "db://oops-framework/libs/ecs/ECS"; + +/** 业务输入参数 */ +@ecs.register('<%Name%>') +export class <%Name%>Comp extends ecs.Comp { + /** 业务层组件移除时,重置所有数据为默认值 */ + reset() { + + } +} + +/** 业务逻辑处理对象 */ +@ecs.register('<%ModuleName%>') +export class <%Name%>System extends ecs.ComblockSystem implements ecs.IEntityEnterSystem { + filter(): ecs.IMatcher { + return ecs.allOf(<%Name%>Comp); + } + + entityEnter(e: ecs.Entity): void { + // 注:自定义业务逻辑 + + e.remove(<%Name%>Comp); + } +}`; diff --git a/dist/template/ModuleModel.js b/dist/template/ModuleModel.js new file mode 100644 index 0000000..36369f9 --- /dev/null +++ b/dist/template/ModuleModel.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TemplateModel = void 0; +exports.TemplateModel = `import { ecs } from "db://oops-framework/libs/ecs/ECS"; + +/** 数据层对象 */ +@ecs.register('<%Name%>') +export class <%Name%>Comp extends ecs.Comp { + id: number = -1; + + /** 数据层组件移除时,重置所有数据为默认值 */ + reset() { + this.id = -1; + } +}`; diff --git a/dist/template/ModuleView.js b/dist/template/ModuleView.js new file mode 100644 index 0000000..d05edbe --- /dev/null +++ b/dist/template/ModuleView.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TemplateView = void 0; +exports.TemplateView = `import { _decorator } from "cc"; +import { ecs } from "db://oops-framework/libs/ecs/ECS"; +import { CCComp } from "db://oops-framework/module/common/CCComp"; + +const { ccclass, property } = _decorator; + +/** 视图层对象 */ +@ccclass('<%Name%>Comp') +@ecs.register('<%Name%>', false) +export class <%Name%>Comp extends CCComp { + /** 视图层逻辑代码分离演示 */ + start() { + // const entity = this.ent as ecs.Entity; // ecs.Entity 可转为当前模块的具体实体对象 + } + + /** 视图对象通过 ecs.Entity.remove(<%Name%>Comp) 删除组件是触发组件处理自定义释放逻辑 */ + reset() { + this.node.destroy(); + } +}`; diff --git a/dist/template/ModuleViewVM.js b/dist/template/ModuleViewVM.js new file mode 100644 index 0000000..9d157eb --- /dev/null +++ b/dist/template/ModuleViewVM.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TemplateViewMvvm = void 0; +exports.TemplateViewMvvm = `import { _decorator } from "cc"; +import { ecs } from "db://oops-framework/libs/ecs/ECS"; +import { CCVMParentComp } from "db://oops-framework/module/common/CCVMParentComp"; + +const { ccclass, property } = _decorator; + +/** 视图层对象 - 支持 MVVM 框架的数据绑定 */ +@ccclass('<%Name%>Comp') +@ecs.register('<%Name%>', false) +export class <%Name%>Comp extends CCVMParentComp { + /** 脚本控制的界面 MVVM 框架绑定数据 */ + data: any = {}; + + /** 视图层逻辑代码分离演示 */ + start() { + // const entity = this.ent as ecs.Entity; // ecs.Entity 可转为当前模块的具体实体对象 + } + + /** 视图对象通过 ecs.Entity.remove(<%Name%>Comp) 删除组件是触发组件处理自定义释放逻辑 */ + reset() { + this.node.destroy(); + } +}`; diff --git a/dist/tinypng.js b/dist/tinypng.js new file mode 100644 index 0000000..79391e2 --- /dev/null +++ b/dist/tinypng.js @@ -0,0 +1,160 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.compress = void 0; +const fs_1 = __importDefault(require("fs")); +const https_1 = __importDefault(require("https")); +const path_1 = __importDefault(require("path")); +const url_1 = __importDefault(require("url")); +const exts = ['.png', '.jpg', '.jpeg']; +const max = 5200000; +const options = { + method: 'POST', + hostname: 'tinypng.com', + path: '/backend/opt/shrink', + headers: { + rejectUnauthorized: 'false', + 'Postman-Token': Date.now(), + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' + } +}; +function compress(filePath) { + if (!fs_1.default.existsSync(filePath)) { + console.log(`路径不存在:${filePath}`); + return; + } + const fileName = path_1.default.basename(filePath); + if (!fs_1.default.statSync(filePath).isDirectory()) { + if (exts.includes(path_1.default.extname(filePath))) { + console.log(`[${fileName}] 压缩中...`); + fileTinyUpload(filePath) + .then(data => { + console.log(`[1/1] [${fileName}] 压缩成功,原始: ${toSize(data.input.size)},压缩: ${toSize(data.output.size)},压缩比: ${toPercent(data.output.ratio)}`); + }) + .catch(err => { + console.log(`[1/1] [${fileName}] 压缩失败!报错:${err}`); + }); + } + else { + console.log(`[${fileName}] 压缩失败!报错:只支持 png、jpg 与 jpeg 格式`); + } + } + else { + let totalCount = 0; + let processedCount = 0; + fileEach(filePath, (filePathInDir) => { + totalCount++; + const relativePath = path_1.default.relative(filePath, filePathInDir); + fileTinyUpload(filePathInDir) + .then(data => { + console.log(`[${++processedCount}/${totalCount}] [${relativePath}] 压缩成功,原始: ${toSize(data.input.size)},压缩: ${toSize(data.output.size)},压缩比: ${toPercent(data.output.ratio)}`); + }) + .catch(err => { + console.log(`[${++processedCount}/${totalCount}] [${relativePath}] 压缩失败!报错:${err}`); + }); + }); + } +} +exports.compress = compress; +function getRandomIP() { + return Array.from(Array(4)).map(() => Math.floor(255 * Math.random())).join('.'); +} +function fileEach(dir, callback) { + fs_1.default.readdir(dir, (err, files) => { + if (err) { + console.error(err); + return; + } + files.forEach((file) => { + const filePath = path_1.default.join(dir, file); + fs_1.default.stat(filePath, (statErr, stats) => { + if (statErr) { + console.error(statErr); + return; + } + if (stats.isDirectory()) { + fileEach(filePath, callback); + } + else { + if (stats.size <= max && stats.isFile() && exts.includes(path_1.default.extname(file))) { + callback(filePath); + } + } + }); + }); + }); +} +function fileUpload(filePath) { + return new Promise((resolve, reject) => { + options.headers['X-Forwarded-For'] = getRandomIP(); + const req = https_1.default.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + try { + const result = JSON.parse(data); + if (result.error) { + reject(result.message); + } + else { + resolve(result); + } + } + catch (parseErr) { + reject(parseErr); + } + }); + }); + req.write(fs_1.default.readFileSync(filePath), 'binary'); + req.on('error', err => { + reject(err); + }); + req.end(); + }); +} +function fileUpdate(filePath, data) { + return new Promise((resolve, reject) => { + const urlObj = new url_1.default.URL(data.output.url); + const req = https_1.default.request(urlObj, (res) => { + let body = ''; + res.setEncoding('binary'); + res.on('data', (chunk) => { + body += chunk; + }); + res.on('end', () => { + fs_1.default.writeFile(filePath, body, 'binary', (err) => { + if (err) { + reject(err); + } + else { + resolve(data); + } + }); + }); + }); + req.on('error', (err) => { + reject(err); + }); + req.end(); + }); +} +function fileTinyUpload(filePath) { + return fileUpload(filePath).then(data => fileUpdate(filePath, data)); +} +function toSize(size) { + if (size < 1024) + return size + 'B'; + else if (size < 1048576) + return (size / 1024).toFixed(2) + 'KB'; + else + return (size / 1024 / 1024).toFixed(2) + 'MB'; +} +function toPercent(ratio) { + return (100 * ratio).toFixed(2) + '%'; +} diff --git a/i18n/en.js b/i18n/en.js index 9aa6aea..8ee1401 100644 --- a/i18n/en.js +++ b/i18n/en.js @@ -5,8 +5,19 @@ module.exports = { document: "Document", document_api: "API Document", document_oops: "Framework Document", + tutorial: "Tutorial Project", solution: "Solution", gitee: "Light up gitee stars", - github: "Light up github stars", + github: "Light up gitee stars", log: "Update Log", + script: "Script", + createGameComponent: "Create display object template", + createModule: "Create ECS module", + createModel: "Create ECS data layer script", + createBll: "Create ECS business layer script", + createView: "Create ECS view layer script", + createViewMvvm: "Create ECS view layer script - MVVM", + tools: "Tools", + tools_compress: "Image Compression", + panel_create_file: "Create Framework Template", }; \ No newline at end of file diff --git a/i18n/zh.js b/i18n/zh.js index b4c9482..16e6a23 100644 --- a/i18n/zh.js +++ b/i18n/zh.js @@ -1,9 +1,3 @@ -/* - * @Author: dgflash - * @Date: 2022-06-14 19:32:16 - * @LastEditors: dgflash - * @LastEditTime: 2022-06-14 19:41:18 - */ "use strict"; module.exports = { description: "Oops Framework 基于 Cocos Creator 3.x 开发的一款游戏框架", @@ -11,8 +5,19 @@ module.exports = { document: "文档", document_api: "API 文档", document_oops: "框架文档", + tutorial: "教程项目", solution: "解决方案", gitee: "点亮 Gitee 星星", github: "点亮 Github 星星", log: "更新日志", + script: "脚本", + createGameComponent: "创建显示对象模板", + createModule: "创建 ECS 模块", + createModel: "创建 ECS 数据层脚本", + createBll: "创建 ECS 业务层脚本", + createView: "创建 ECS 视图层脚本", + createViewMvvm: "创建 ECS 视图层脚本 - MVVM ", + tools: "工具", + tools_compress: "图片压缩", + panel_create_file: "创建框架模板" }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c767f36..6c2aa3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,12 +7,81 @@ "": { "name": "oops-framework", "version": "1.0.0", + "dependencies": { + "fs-extra": "^10.0.0", + "vue": "^3.1.4" + }, "devDependencies": { + "@cocos/creator-types": "^3.8.2", + "@types/fs-extra": "^9.0.5", "@types/node": "^20.16.12", "typedoc": "^0.23.24", "typescript": "^4.8.2" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://mirrors.cloud.tencent.com/npm/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://mirrors.cloud.tencent.com/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.8", + "resolved": "https://mirrors.cloud.tencent.com/npm/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "dependencies": { + "@babel/types": "^7.25.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.8", + "resolved": "https://mirrors.cloud.tencent.com/npm/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "dependencies": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cocos/creator-types": { + "version": "3.8.4", + "resolved": "https://mirrors.cloud.tencent.com/npm/@cocos/creator-types/-/creator-types-3.8.4.tgz", + "integrity": "sha512-z+8qx726Zl/8rUUpbLjVAiNPn4XweRACEaY1vHeR3EHIcSQ9wtFlIFd2SZX5rOE8lt1S95nxv8OeLYXfbN4/2Q==", + "dev": true + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://mirrors.cloud.tencent.com/npm/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.16.12", "resolved": "https://mirrors.cloud.tencent.com/npm/@types/node/-/node-20.16.12.tgz", @@ -22,6 +91,97 @@ "undici-types": "~6.19.2" } }, + "node_modules/@vue/compiler-core": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/compiler-core/-/compiler-core-3.5.12.tgz", + "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.12", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", + "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", + "dependencies": { + "@vue/compiler-core": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", + "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.12", + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.47", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", + "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", + "dependencies": { + "@vue/compiler-dom": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/reactivity/-/reactivity-3.5.12.tgz", + "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", + "dependencies": { + "@vue/shared": "3.5.12" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/runtime-core/-/runtime-core-3.5.12.tgz", + "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", + "dependencies": { + "@vue/reactivity": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", + "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", + "dependencies": { + "@vue/reactivity": "3.5.12", + "@vue/runtime-core": "3.5.12", + "@vue/shared": "3.5.12", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/server-renderer/-/server-renderer-3.5.12.tgz", + "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", + "dependencies": { + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12" + }, + "peerDependencies": { + "vue": "3.5.12" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/shared/-/shared-3.5.12.tgz", + "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -37,18 +197,76 @@ "balanced-match": "^1.0.0" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://mirrors.cloud.tencent.com/npm/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://mirrors.cloud.tencent.com/npm/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://mirrors.cloud.tencent.com/npm/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, "node_modules/jsonc-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "node_modules/magic-string": { + "version": "0.30.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/marked": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", @@ -73,6 +291,55 @@ "node": ">=10" } }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://mirrors.cloud.tencent.com/npm/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://mirrors.cloud.tencent.com/npm/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/shiki": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", @@ -84,6 +351,22 @@ "vscode-textmate": "^8.0.0" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, "node_modules/typedoc": { "version": "0.23.24", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", @@ -109,7 +392,7 @@ "version": "4.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -124,6 +407,14 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/vscode-oniguruma": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", @@ -135,9 +426,77 @@ "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true + }, + "node_modules/vue": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/vue/-/vue-3.5.12.tgz", + "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", + "dependencies": { + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-sfc": "3.5.12", + "@vue/runtime-dom": "3.5.12", + "@vue/server-renderer": "3.5.12", + "@vue/shared": "3.5.12" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } } }, "dependencies": { + "@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://mirrors.cloud.tencent.com/npm/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==" + }, + "@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://mirrors.cloud.tencent.com/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==" + }, + "@babel/parser": { + "version": "7.25.8", + "resolved": "https://mirrors.cloud.tencent.com/npm/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "requires": { + "@babel/types": "^7.25.8" + } + }, + "@babel/types": { + "version": "7.25.8", + "resolved": "https://mirrors.cloud.tencent.com/npm/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "requires": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + } + }, + "@cocos/creator-types": { + "version": "3.8.4", + "resolved": "https://mirrors.cloud.tencent.com/npm/@cocos/creator-types/-/creator-types-3.8.4.tgz", + "integrity": "sha512-z+8qx726Zl/8rUUpbLjVAiNPn4XweRACEaY1vHeR3EHIcSQ9wtFlIFd2SZX5rOE8lt1S95nxv8OeLYXfbN4/2Q==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://mirrors.cloud.tencent.com/npm/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "20.16.12", "resolved": "https://mirrors.cloud.tencent.com/npm/@types/node/-/node-20.16.12.tgz", @@ -147,6 +506,94 @@ "undici-types": "~6.19.2" } }, + "@vue/compiler-core": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/compiler-core/-/compiler-core-3.5.12.tgz", + "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", + "requires": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.12", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "@vue/compiler-dom": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", + "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", + "requires": { + "@vue/compiler-core": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "@vue/compiler-sfc": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", + "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", + "requires": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.12", + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.47", + "source-map-js": "^1.2.0" + } + }, + "@vue/compiler-ssr": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", + "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", + "requires": { + "@vue/compiler-dom": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "@vue/reactivity": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/reactivity/-/reactivity-3.5.12.tgz", + "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", + "requires": { + "@vue/shared": "3.5.12" + } + }, + "@vue/runtime-core": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/runtime-core/-/runtime-core-3.5.12.tgz", + "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", + "requires": { + "@vue/reactivity": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "@vue/runtime-dom": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", + "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", + "requires": { + "@vue/reactivity": "3.5.12", + "@vue/runtime-core": "3.5.12", + "@vue/shared": "3.5.12", + "csstype": "^3.1.3" + } + }, + "@vue/server-renderer": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/server-renderer/-/server-renderer-3.5.12.tgz", + "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", + "requires": { + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12" + } + }, + "@vue/shared": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/@vue/shared/-/shared-3.5.12.tgz", + "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -162,18 +609,65 @@ "balanced-match": "^1.0.0" } }, + "csstype": { + "version": "3.1.3", + "resolved": "https://mirrors.cloud.tencent.com/npm/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "entities": { + "version": "4.5.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://mirrors.cloud.tencent.com/npm/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://mirrors.cloud.tencent.com/npm/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, "jsonc-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, "lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "magic-string": { + "version": "0.30.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "marked": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", @@ -189,6 +683,26 @@ "brace-expansion": "^2.0.1" } }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://mirrors.cloud.tencent.com/npm/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "postcss": { + "version": "8.4.47", + "resolved": "https://mirrors.cloud.tencent.com/npm/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "requires": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + } + }, "shiki": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", @@ -200,6 +714,16 @@ "vscode-textmate": "^8.0.0" } }, + "source-map-js": { + "version": "1.2.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, "typedoc": { "version": "0.23.24", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", @@ -216,7 +740,7 @@ "version": "4.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", - "dev": true + "devOptional": true }, "undici-types": { "version": "6.19.8", @@ -224,6 +748,11 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, + "universalify": { + "version": "2.0.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" + }, "vscode-oniguruma": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", @@ -235,6 +764,18 @@ "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true + }, + "vue": { + "version": "3.5.12", + "resolved": "https://mirrors.cloud.tencent.com/npm/vue/-/vue-3.5.12.tgz", + "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", + "requires": { + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-sfc": "3.5.12", + "@vue/runtime-dom": "3.5.12", + "@vue/server-renderer": "3.5.12", + "@vue/shared": "3.5.12" + } } } } diff --git a/package.json b/package.json index 8fe30c5..c597214 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,35 @@ "watch": "tsc -w", "doc": "npx typedoc" }, + "panels": { + "set_file_name": { + "title": "i18n:oops-framework.panel_create_file", + "type": "dockable", + "main": "dist/default", + "size": { + "min-width": 450, + "min-height": 300, + "width": 450, + "height": 300 + } + } + }, "contributions": { + "scene": { + "script": "./dist/scene.js" + }, "asset-db": { "mount": { "path": "./assets", "readonly": false } }, + "assets": { + "menu": { + "methods": "./dist/assets-menu.js", + "assetMenu": "onAssetMenu" + } + }, "menu": [ { "path": "i18n:oops-framework.name", @@ -29,6 +51,11 @@ "label": "i18n:oops-framework.document_api", "message": "documentApi" }, + { + "path": "i18n:oops-framework.name", + "label": "i18n:oops-framework.tutorial", + "message": "tutorial" + }, { "path": "i18n:oops-framework.name", "label": "i18n:oops-framework.solution", @@ -70,6 +97,11 @@ "log" ] }, + "tutorial": { + "methods": [ + "tutorial" + ] + }, "solution": { "methods": [ "solution" @@ -87,7 +119,13 @@ } } }, + "dependencies": { + "fs-extra": "^10.0.0", + "vue": "^3.1.4" + }, "devDependencies": { + "@cocos/creator-types": "^3.8.2", + "@types/fs-extra": "^9.0.5", "@types/node": "^20.16.12", "typedoc": "^0.23.24", "typescript": "^4.8.2" diff --git a/src/assets-menu.ts b/src/assets-menu.ts new file mode 100644 index 0000000..b0102b9 --- /dev/null +++ b/src/assets-menu.ts @@ -0,0 +1,80 @@ +import { AssetInfo } from "../@types/packages/asset-db/@types/public"; +import { compress } from "./tinypng"; + +/** 资源栏右键菜单 */ +export function onAssetMenu(assetInfo: AssetInfo) { + return [ + { + label: 'i18n:oops-framework.name', + submenu: [ + { + label: `i18n:oops-framework.script`, + submenu: [ + { + label: `i18n:oops-framework.createGameComponent`, + async click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "GameComponent"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + type: `separator`, + }, + { + label: `i18n:oops-framework.createModule`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "Module"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + label: `i18n:oops-framework.createModel`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "Model"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + label: `i18n:oops-framework.createBll`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "Bll"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + label: `i18n:oops-framework.createView`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "View"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + { + label: `i18n:oops-framework.createViewMvvm`, + click() { + localStorage.setItem('create_path', assetInfo.file); + localStorage.setItem('create_type', "ViewMvvm"); + Editor.Panel.open("oops-framework.set_file_name"); + }, + }, + ] + }, + { + label: `i18n:oops-framework.tools`, + submenu: [ + { + label: `i18n:oops-framework.tools_compress`, + click() { + compress(assetInfo.file); + }, + } + ] + } + ], + }, + ]; +}; \ No newline at end of file diff --git a/src/create-script.ts b/src/create-script.ts new file mode 100644 index 0000000..2638b06 --- /dev/null +++ b/src/create-script.ts @@ -0,0 +1,135 @@ +import { existsSync } from 'fs'; +import path from 'path'; + +/** 写入文件 */ +export function createView(directoryPath: string, fileName: string, content: string, isEcsComp: boolean = true): Promise { + return new Promise(async (resolve, reject) => { + // 创建脚本 + let className = fileName + "View"; + let scriptUrl = ""; + if (isEcsComp) { + scriptUrl = path.join(directoryPath, fileName) + "ViewComp.ts"; + } + else { + scriptUrl = path.join(directoryPath, fileName) + "View.ts"; + } + + if (!existsSync(scriptUrl)) { + content = content.replace(/<%Name%>/g, className); + await Editor.Message.request('asset-db', 'create-asset', scriptUrl, content); + } + + // 创建预制 + let prefabUrl = path.join(directoryPath, fileName) + ".prefab"; + if (!existsSync(prefabUrl)) { + if (isEcsComp) className = className + "Comp"; + await Editor.Message.request('scene', 'execute-scene-script', { + name: "oops-framework", + method: 'createPrefab', + args: [fileName, className, prefabUrl] + }); + } + + // 闪烁提示新创建的脚本文件 + Editor.Message.send('assets', 'twinkle', scriptUrl); + + // 打开脚本 + Editor.Message.request('asset-db', 'open-asset', scriptUrl); + + // 打开预制 + Editor.Message.request('asset-db', 'open-asset', prefabUrl); + + resolve(); + }); +} + + +export function createScriptModule(directoryPath: string, fileName: string, content: string): Promise { + return new Promise(async (resolve, reject) => { + // 创建目录 + let pathName = fileName.toLowerCase(); + let pathModule = path.join(directoryPath, pathName); + if (!existsSync(pathModule)) { + await Editor.Message.request('asset-db', 'create-asset', pathModule, null); + } + + let subPathView = path.join(pathModule, "view"); + if (!existsSync(subPathView)) { + await Editor.Message.request('asset-db', 'create-asset', subPathView, null); + } + + let subPathBll = path.join(pathModule, "bll"); + if (!existsSync(subPathBll)) { + await Editor.Message.request('asset-db', 'create-asset', subPathBll, null); + } + + let subPathModel = path.join(pathModule, "model"); + if (!existsSync(subPathModel)) { + await Editor.Message.request('asset-db', 'create-asset', subPathModel, null); + } + + // 创建脚本 + let scriptUrl = path.join(pathModule, fileName) + ".ts"; + if (!existsSync(scriptUrl)) { + content = content.replace(/<%Name%>/g, fileName); + await Editor.Message.request('asset-db', 'create-asset', scriptUrl, content); + } + + // 闪烁提示新创建的脚本文件 + Editor.Message.send('assets', 'twinkle', scriptUrl); + + // 打开脚本 + Editor.Message.request('asset-db', 'open-asset', scriptUrl); + + resolve(); + }); +} + +/** 创建脚本 */ +export function createScriptBll(directoryPath: string, fileName: string, content: string, moduleName: string): Promise { + return new Promise(async (resolve, reject) => { + let scriptUrl = path.join(directoryPath, fileName) + ".ts"; + + // 创建脚本 + if (!existsSync(scriptUrl)) { + content = content.replace(/<%Name%>/g, fileName); + content = content.replace(/<%ModuleName%>/g, moduleName); + await Editor.Message.request('asset-db', 'create-asset', scriptUrl, content); + } + + // 闪烁提示新创建的脚本文件 + Editor.Message.send('assets', 'twinkle', scriptUrl); + + // 打开脚本 + Editor.Message.request('asset-db', 'open-asset', scriptUrl); + + resolve(); + }); +} + +/** 创建业务层脚本 */ +export function createScript(directoryPath: string, fileName: string, content: string, isEcsComp: boolean = true): Promise { + return new Promise(async (resolve, reject) => { + let scriptUrl = ""; + if (isEcsComp) { + scriptUrl = path.join(directoryPath, fileName) + "Comp.ts"; + } + else { + scriptUrl = path.join(directoryPath, fileName) + ".ts"; + } + + // 创建脚本 + if (!existsSync(scriptUrl)) { + content = content.replace(/<%Name%>/g, fileName); + await Editor.Message.request('asset-db', 'create-asset', scriptUrl, content); + } + + // 闪烁提示新创建的脚本文件 + Editor.Message.send('assets', 'twinkle', scriptUrl); + + // 打开脚本 + Editor.Message.request('asset-db', 'open-asset', scriptUrl); + + resolve(); + }); +} \ No newline at end of file diff --git a/src/default/index.ts b/src/default/index.ts new file mode 100644 index 0000000..a06269c --- /dev/null +++ b/src/default/index.ts @@ -0,0 +1,119 @@ +import { readFileSync } from 'fs-extra'; +import { join } from 'path'; +import { App, createApp } from 'vue'; +import { createScript, createScriptBll, createScriptModule, createView } from '../create-script'; +import { TemplateGameComponent } from '../template/GameComponent'; +import { TemplateModule } from '../template/Module'; +import { TemplateBll } from '../template/ModuleBll'; +import { TemplateModel } from '../template/ModuleModel'; +import { TemplateView } from '../template/ModuleView'; +import { TemplateViewMvvm } from '../template/ModuleViewVM'; + +const panelDataMap = new WeakMap(); + +module.exports = Editor.Panel.define({ + listeners: { + show() { console.log('show'); }, + hide() { console.log('hide'); }, + }, + template: readFileSync(join(__dirname, '../../static/template/default/index.html'), 'utf-8'), + style: readFileSync(join(__dirname, '../../static/style/default/index.css'), 'utf-8'), + $: { + app: '#app', + }, + ready() { + let filename = "Default"; + let type = localStorage.getItem('create_type')!; + localStorage.removeItem('create_type'); + let path = localStorage.getItem('create_path')!; + localStorage.removeItem('create_path'); + let title = "???"; + let showModule = false; + let moduleName = "ModuleName"; + + switch (type) { + case "GameComponent": + title = `i18n:oops-framework.createGameComponent`; + break; + case "Module": + title = `i18n:oops-framework.createModule`; + break; + case "Model": + title = `i18n:oops-framework.createModel`; + break; + case "Bll": + title = `i18n:oops-framework.createBll`; + showModule = true; + break; + case "View": + title = `i18n:oops-framework.createView`; + break; + case "ViewMvvm": + title = `i18n:oops-framework.createViewMvvm`; + break; + } + + // 创建框架配置界面 + if (this.$.app) { + const app = createApp({}); + app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-'); + app.component('MyConfig', { + template: readFileSync(join(__dirname, '../../static/template/vue/set_file_name.html'), 'utf-8'), + data() { + return { + title: title, + filename: filename, + showModule: showModule + }; + }, + methods: { + // 记录输入的文件名 + onInputName(event: any) { + filename = event.target.value; + }, + onModuleName(event: any) { + moduleName = event.target.value; + }, + // 创建文件 + async onConfirm() { + if (filename.trim().length == 0) { + await Editor.Dialog.info('请输入文件名'); + return; + } + + switch (type) { + case "GameComponent": + await createView(path, filename, TemplateGameComponent, false); + break; + case "Module": + await createScriptModule(path, filename, TemplateModule); + break; + case "Model": + await createScript(path, filename, TemplateModel); + break; + case "Bll": + await createScriptBll(path, filename, TemplateBll, moduleName); + break; + case "View": + await createView(path, filename, TemplateView); + break; + case "ViewMvvm": + await createView(path, filename, TemplateViewMvvm); + break; + } + close(); + } + }, + }); + app.mount(this.$.app); + panelDataMap.set(this, app); + } + }, + beforeClose() { }, + close() { + const app = panelDataMap.get(this); + if (app) { + app.unmount(); + } + }, +}); diff --git a/src/main.ts b/src/main.ts index 4ebd04a..6f839c7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -46,5 +46,5 @@ export const methods: { [key: string]: (...any: any) => any } = { /** 点亮 Github 星星 */ github() { shell.openExternal('https://github.com/dgflash/oops-framework'); - }, + } }; diff --git a/src/scene.ts b/src/scene.ts new file mode 100644 index 0000000..d400a5b --- /dev/null +++ b/src/scene.ts @@ -0,0 +1,36 @@ +export function load() { } + +export function unload() { } + +// 在其他扩展脚本中,我们可以使用如下代码调用 rotateCamera 函数 +// const options: ExecuteSceneScriptMethodOptions = { +// name: scene.ts 所在的扩展包名, 如: App, +// method: scene.ts 中定义的方法, 如: createPrefab, +// args: 参数,可选, 只传递json +// }; +// const result = await Editor.Message.request('scene', 'execute-scene-script', options); +export const methods = { + /** 创建视图层制 */ + async createPrefab(fileName: string, className: string, prefabUrl: string) { + const { Node, js, Layers } = require('cc'); + const node = new Node(fileName); + node.layer = Layers.Enum.UI_2D; + + while (true) { + const result = js.getClassByName(className); + if (result) break; + + await new Promise((next) => { + setTimeout(next, 100); + }); + } + + const com = node.addComponent(className); + com.resetInEditor && com.resetInEditor(); + + const info = cce.Prefab.generatePrefabDataFromNode(node) as any; + node.destroy(); + + return Editor.Message.request('asset-db', 'create-asset', prefabUrl, info.prefabData || info); + } +}; \ No newline at end of file diff --git a/src/template/GameComponent.ts b/src/template/GameComponent.ts new file mode 100644 index 0000000..c6c8a69 --- /dev/null +++ b/src/template/GameComponent.ts @@ -0,0 +1,12 @@ +export const TemplateGameComponent = `import { _decorator } from 'cc'; +import { GameComponent } from "db://oops-framework/module/common/GameComponent"; + +const { ccclass, property } = _decorator; + +/** 显示对象控制 */ +@ccclass('<%Name%>') +export class <%Name%> extends GameComponent { + protected start() { + + } +}`; \ No newline at end of file diff --git a/src/template/Module.ts b/src/template/Module.ts new file mode 100644 index 0000000..65920e7 --- /dev/null +++ b/src/template/Module.ts @@ -0,0 +1,19 @@ +export const TemplateModule = `import { ecs } from "db://oops-framework/libs/ecs/ECS"; + +/** <%Name%> 模块 */ +@ecs.register('<%Name%>') +export class <%Name%> extends ecs.Entity { + /** ---------- 数据层 ---------- */ + // <%Name%>Model!: <%Name%>ModelComp; + + /** ---------- 业务层 ---------- */ + // <%Name%>Bll!: <%Name%>BllComp; + + /** ---------- 视图层 ---------- */ + // <%Name%>View!: <%Name%>ViewComp; + + /** 初始添加的数据层组件 */ + protected init() { + // this.addComponents(); + } +}`; \ No newline at end of file diff --git a/src/template/ModuleBll.ts b/src/template/ModuleBll.ts new file mode 100644 index 0000000..c14a230 --- /dev/null +++ b/src/template/ModuleBll.ts @@ -0,0 +1,24 @@ +export const TemplateBll = `import { ecs } from "db://oops-framework/libs/ecs/ECS"; + +/** 业务输入参数 */ +@ecs.register('<%Name%>') +export class <%Name%>Comp extends ecs.Comp { + /** 业务层组件移除时,重置所有数据为默认值 */ + reset() { + + } +} + +/** 业务逻辑处理对象 */ +@ecs.register('<%ModuleName%>') +export class <%Name%>System extends ecs.ComblockSystem implements ecs.IEntityEnterSystem { + filter(): ecs.IMatcher { + return ecs.allOf(<%Name%>Comp); + } + + entityEnter(e: ecs.Entity): void { + // 注:自定义业务逻辑 + + e.remove(<%Name%>Comp); + } +}`; \ No newline at end of file diff --git a/src/template/ModuleModel.ts b/src/template/ModuleModel.ts new file mode 100644 index 0000000..2fbeb7e --- /dev/null +++ b/src/template/ModuleModel.ts @@ -0,0 +1,12 @@ +export const TemplateModel = `import { ecs } from "db://oops-framework/libs/ecs/ECS"; + +/** 数据层对象 */ +@ecs.register('<%Name%>') +export class <%Name%>Comp extends ecs.Comp { + id: number = -1; + + /** 数据层组件移除时,重置所有数据为默认值 */ + reset() { + this.id = -1; + } +}`; \ No newline at end of file diff --git a/src/template/ModuleView.ts b/src/template/ModuleView.ts new file mode 100644 index 0000000..c829ae5 --- /dev/null +++ b/src/template/ModuleView.ts @@ -0,0 +1,20 @@ +export const TemplateView = `import { _decorator } from "cc"; +import { ecs } from "db://oops-framework/libs/ecs/ECS"; +import { CCComp } from "db://oops-framework/module/common/CCComp"; + +const { ccclass, property } = _decorator; + +/** 视图层对象 */ +@ccclass('<%Name%>Comp') +@ecs.register('<%Name%>', false) +export class <%Name%>Comp extends CCComp { + /** 视图层逻辑代码分离演示 */ + start() { + // const entity = this.ent as ecs.Entity; // ecs.Entity 可转为当前模块的具体实体对象 + } + + /** 视图对象通过 ecs.Entity.remove(<%Name%>Comp) 删除组件是触发组件处理自定义释放逻辑 */ + reset() { + this.node.destroy(); + } +}`; \ No newline at end of file diff --git a/src/template/ModuleViewVM.ts b/src/template/ModuleViewVM.ts new file mode 100644 index 0000000..6a66236 --- /dev/null +++ b/src/template/ModuleViewVM.ts @@ -0,0 +1,23 @@ +export const TemplateViewMvvm = `import { _decorator } from "cc"; +import { ecs } from "db://oops-framework/libs/ecs/ECS"; +import { CCVMParentComp } from "db://oops-framework/module/common/CCVMParentComp"; + +const { ccclass, property } = _decorator; + +/** 视图层对象 - 支持 MVVM 框架的数据绑定 */ +@ccclass('<%Name%>Comp') +@ecs.register('<%Name%>', false) +export class <%Name%>Comp extends CCVMParentComp { + /** 脚本控制的界面 MVVM 框架绑定数据 */ + data: any = {}; + + /** 视图层逻辑代码分离演示 */ + start() { + // const entity = this.ent as ecs.Entity; // ecs.Entity 可转为当前模块的具体实体对象 + } + + /** 视图对象通过 ecs.Entity.remove(<%Name%>Comp) 删除组件是触发组件处理自定义释放逻辑 */ + reset() { + this.node.destroy(); + } +}` \ No newline at end of file diff --git a/src/tinypng.ts b/src/tinypng.ts new file mode 100644 index 0000000..d48be3c --- /dev/null +++ b/src/tinypng.ts @@ -0,0 +1,159 @@ +import fs from 'fs'; +import https from 'https'; +import path from 'path'; +import url from 'url'; + +const exts = ['.png', '.jpg', '.jpeg']; +const max = 5200000; +const options: any = { + method: 'POST', + hostname: 'tinypng.com', + path: '/backend/opt/shrink', + headers: { + rejectUnauthorized: 'false', + 'Postman-Token': Date.now(), + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' + } +}; + +export function compress(filePath: string): void { + if (!fs.existsSync(filePath)) { + console.log(`路径不存在:${filePath}`); + return; + } + const fileName = path.basename(filePath); + if (!fs.statSync(filePath).isDirectory()) { + if (exts.includes(path.extname(filePath))) { + console.log(`[${fileName}] 压缩中...`); + fileTinyUpload(filePath) + .then(data => { + console.log(`[1/1] [${fileName}] 压缩成功,原始: ${toSize(data.input.size)},压缩: ${toSize(data.output.size)},压缩比: ${toPercent(data.output.ratio)}`); + }) + .catch(err => { + console.log(`[1/1] [${fileName}] 压缩失败!报错:${err}`); + }); + } + else { + console.log(`[${fileName}] 压缩失败!报错:只支持 png、jpg 与 jpeg 格式`); + } + } + else { + let totalCount = 0; + let processedCount = 0; + fileEach(filePath, (filePathInDir) => { + totalCount++; + const relativePath = path.relative(filePath, filePathInDir); + fileTinyUpload(filePathInDir) + .then(data => { + console.log(`[${++processedCount}/${totalCount}] [${relativePath}] 压缩成功,原始: ${toSize(data.input.size)},压缩: ${toSize(data.output.size)},压缩比: ${toPercent(data.output.ratio)}`); + }) + .catch(err => { + console.log(`[${++processedCount}/${totalCount}] [${relativePath}] 压缩失败!报错:${err}`); + }); + }); + } +} + +function getRandomIP(): string { + return Array.from(Array(4)).map(() => Math.floor(255 * Math.random())).join('.'); +} + +function fileEach(dir: string, callback: (filePath: string) => void): void { + fs.readdir(dir, (err: any, files: any[]) => { + if (err) { + console.error(err); + return; + } + files.forEach((file: any) => { + const filePath = path.join(dir, file); + fs.stat(filePath, (statErr: any, stats: { isDirectory: () => any; size: number; isFile: () => any; }) => { + if (statErr) { + console.error(statErr); + return; + } + if (stats.isDirectory()) { + fileEach(filePath, callback); + } + else { + if (stats.size <= max && stats.isFile() && exts.includes(path.extname(file))) { + callback(filePath); + } + } + }); + }); + }); +} + +function fileUpload(filePath: string): Promise { + return new Promise((resolve, reject) => { + options.headers['X-Forwarded-For'] = getRandomIP(); + const req = https.request(options, (res: any) => { + let data = ''; + res.on('data', (chunk: string) => { + data += chunk; + }); + res.on('end', () => { + try { + const result = JSON.parse(data); + if (result.error) { + reject(result.message); + } else { + resolve(result); + } + } catch (parseErr) { + reject(parseErr); + } + }); + }); + req.write(fs.readFileSync(filePath), 'binary'); + req.on('error', err => { + reject(err); + }); + req.end(); + }); +} + +function fileUpdate(filePath: string, data: any): Promise { + return new Promise((resolve, reject) => { + const urlObj = new url.URL(data.output.url); + const req = https.request(urlObj, (res: any) => { + let body = ''; + res.setEncoding('binary'); + res.on('data', (chunk: string) => { + body += chunk; + }); + res.on('end', () => { + fs.writeFile(filePath, body, 'binary', (err: any) => { + if (err) { + reject(err); + } else { + resolve(data); + } + }); + }); + }); + req.on('error', (err: any) => { + reject(err); + }); + req.end(); + }); +} + +function fileTinyUpload(filePath: string): Promise { + return fileUpload(filePath).then(data => fileUpdate(filePath, data)); +} + +function toSize(size: number): string { + if (size < 1024) + return size + 'B'; + else if (size < 1048576) + return (size / 1024).toFixed(2) + 'KB'; + else + return (size / 1024 / 1024).toFixed(2) + 'MB'; +} + +function toPercent(ratio: number): string { + return (100 * ratio).toFixed(2) + '%'; +} \ No newline at end of file diff --git a/static/style/default/index.css b/static/style/default/index.css new file mode 100644 index 0000000..a856db3 --- /dev/null +++ b/static/style/default/index.css @@ -0,0 +1,37 @@ +ui-input { + width: 100%; + min-width: 100px; + min-height: 30px; + line-height: 30px; +} + +ui-button { + height: 30px; +} + +ui-label { + margin-top: 5px; + margin-bottom: 5px; +} + +.f { + flex: 1; + overflow: auto; + padding: 10px; +} + +.c { + display: inline-flex; + width: 100%; + flex-wrap: nowrap; +} + +.c ui-button { + width: 40%; +} + +.b { + text-align: center; + padding: 10px 0; + margin-top: 5px; +} \ No newline at end of file diff --git a/static/template/default/index.html b/static/template/default/index.html new file mode 100644 index 0000000..c9f894b --- /dev/null +++ b/static/template/default/index.html @@ -0,0 +1,5 @@ +
+
+ +
+
\ No newline at end of file diff --git a/static/template/vue/set_file_name.html b/static/template/vue/set_file_name.html new file mode 100644 index 0000000..7d5465e --- /dev/null +++ b/static/template/vue/set_file_name.html @@ -0,0 +1,18 @@ +
+
+
+
+ 文件名: + +
+
+
+
+ 模块名: +
+
+
+ 确认 +
+
\ No newline at end of file