@kurot/cli 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +115 -331
  3. package/dist/core/components/discover-components.d.ts.map +1 -1
  4. package/dist/core/components/discover-components.js +12 -11
  5. package/dist/core/components/discover-components.js.map +1 -1
  6. package/dist/core/config.d.ts +8 -16
  7. package/dist/core/config.d.ts.map +1 -1
  8. package/dist/core/config.js +12 -4
  9. package/dist/core/config.js.map +1 -1
  10. package/dist/core/dev-server.d.ts +1 -1
  11. package/dist/core/dev-server.js +13 -13
  12. package/dist/core/dev-server.js.map +1 -1
  13. package/dist/core/diagnostics/codes.d.ts +3 -6
  14. package/dist/core/diagnostics/codes.d.ts.map +1 -1
  15. package/dist/core/diagnostics/codes.js +4 -10
  16. package/dist/core/diagnostics/codes.js.map +1 -1
  17. package/dist/core/diagnostics/output.d.ts +1 -1
  18. package/dist/core/diagnostics/output.d.ts.map +1 -1
  19. package/dist/core/{exml → kui}/ast.d.ts +17 -107
  20. package/dist/core/kui/ast.d.ts.map +1 -0
  21. package/dist/core/kui/ast.js +8 -0
  22. package/dist/core/{exml → kui}/ast.js.map +1 -1
  23. package/dist/core/kui/codegen.d.ts.map +1 -0
  24. package/dist/core/{exml → kui}/codegen.js +17 -176
  25. package/dist/core/kui/codegen.js.map +1 -0
  26. package/dist/core/kui/index.d.ts +29 -0
  27. package/dist/core/kui/index.d.ts.map +1 -0
  28. package/dist/core/kui/index.js +23 -0
  29. package/dist/core/kui/index.js.map +1 -0
  30. package/dist/core/{exml/exml-diagnostics.d.ts → kui/kui-diagnostics.d.ts} +2 -2
  31. package/dist/core/kui/kui-diagnostics.d.ts.map +1 -0
  32. package/dist/core/{exml/exml-diagnostics.js → kui/kui-diagnostics.js} +4 -4
  33. package/dist/core/kui/kui-diagnostics.js.map +1 -0
  34. package/dist/core/kui/kui-parser.d.ts +7 -0
  35. package/dist/core/kui/kui-parser.d.ts.map +1 -0
  36. package/dist/core/kui/kui-parser.js +157 -0
  37. package/dist/core/kui/kui-parser.js.map +1 -0
  38. package/dist/core/{exml → kui}/registry.d.ts +3 -26
  39. package/dist/core/kui/registry.d.ts.map +1 -0
  40. package/dist/core/{exml → kui}/registry.js +12 -43
  41. package/dist/core/kui/registry.js.map +1 -0
  42. package/dist/core/{exml → kui}/skin-module-builder.d.ts +6 -6
  43. package/dist/core/kui/skin-module-builder.d.ts.map +1 -0
  44. package/dist/core/{exml → kui}/skin-module-builder.js +7 -6
  45. package/dist/core/kui/skin-module-builder.js.map +1 -0
  46. package/dist/core/kui/skin-parts-declaration.d.ts.map +1 -0
  47. package/dist/core/{exml → kui}/skin-parts-declaration.js +2 -2
  48. package/dist/core/kui/skin-parts-declaration.js.map +1 -0
  49. package/dist/core/kui/source-location.d.ts.map +1 -0
  50. package/dist/core/kui/source-location.js.map +1 -0
  51. package/dist/core/namespace-external-plugin.d.ts +1 -1
  52. package/dist/core/namespace-external-plugin.js +1 -1
  53. package/dist/core/plugins/compile-custom-namespaces.d.ts +3 -3
  54. package/dist/core/plugins/compile-custom-namespaces.js +3 -3
  55. package/dist/core/plugins/compile-kui.d.ts +6 -0
  56. package/dist/core/plugins/compile-kui.d.ts.map +1 -0
  57. package/dist/core/plugins/compile-kui.js +143 -0
  58. package/dist/core/plugins/compile-kui.js.map +1 -0
  59. package/dist/core/plugins/copy-assets.d.ts +3 -3
  60. package/dist/core/plugins/copy-assets.d.ts.map +1 -1
  61. package/dist/core/plugins/copy-assets.js +7 -6
  62. package/dist/core/plugins/copy-assets.js.map +1 -1
  63. package/dist/core/plugins/index.d.ts +1 -1
  64. package/dist/core/plugins/index.d.ts.map +1 -1
  65. package/dist/core/plugins/index.js +3 -3
  66. package/dist/core/plugins/index.js.map +1 -1
  67. package/dist/core/project.d.ts +14 -10
  68. package/dist/core/project.d.ts.map +1 -1
  69. package/dist/core/project.js +11 -7
  70. package/dist/core/project.js.map +1 -1
  71. package/dist/core/template.js +1 -1
  72. package/dist/core/template.js.map +1 -1
  73. package/dist/define.d.ts +1 -1
  74. package/dist/define.d.ts.map +1 -1
  75. package/package.json +5 -3
  76. package/templates/game/kurot.config.ts +3 -3
  77. package/templates/game/resource/ui/skins/ButtonSkin.kui.xml +24 -0
  78. package/templates/game/resource/ui/skins/CheckBoxSkin.kui.xml +45 -0
  79. package/templates/game/resource/ui/skins/ComboBoxSkin.kui.xml +32 -0
  80. package/templates/game/resource/ui/skins/GroupSkin.kui.xml +11 -0
  81. package/templates/game/resource/ui/skins/HScrollBarSkin.kui.xml +11 -0
  82. package/templates/game/resource/ui/skins/HSliderSkin.kui.xml +13 -0
  83. package/templates/game/resource/ui/skins/ImageSkin.kui.xml +11 -0
  84. package/templates/game/resource/ui/skins/ItemRendererSkin.kui.xml +22 -0
  85. package/templates/game/resource/ui/skins/LabelSkin.kui.xml +11 -0
  86. package/templates/game/resource/ui/skins/ListSkin.kui.xml +15 -0
  87. package/templates/game/resource/ui/skins/PanelSkin.kui.xml +18 -0
  88. package/templates/game/resource/ui/skins/ProgressBarSkin.kui.xml +14 -0
  89. package/templates/game/resource/ui/skins/RadioButtonSkin.kui.xml +45 -0
  90. package/templates/game/resource/ui/skins/ScrollerSkin.kui.xml +13 -0
  91. package/templates/game/resource/ui/skins/TabBarSkin.kui.xml +12 -0
  92. package/templates/game/resource/ui/skins/TextInputSkin.kui.xml +28 -0
  93. package/templates/game/resource/ui/skins/ToggleButtonSkin.kui.xml +34 -0
  94. package/templates/game/resource/ui/skins/ToggleSwitchSkin.kui.xml +29 -0
  95. package/templates/game/resource/ui/skins/VScrollBarSkin.kui.xml +11 -0
  96. package/templates/game/resource/ui/skins/VSliderSkin.kui.xml +13 -0
  97. package/templates/game/resource/ui/skins/ViewStackSkin.kui.xml +11 -0
  98. package/dist/core/exml/ast.d.ts.map +0 -1
  99. package/dist/core/exml/ast.js +0 -8
  100. package/dist/core/exml/codegen.d.ts.map +0 -1
  101. package/dist/core/exml/codegen.js.map +0 -1
  102. package/dist/core/exml/exml-diagnostics.d.ts.map +0 -1
  103. package/dist/core/exml/exml-diagnostics.js.map +0 -1
  104. package/dist/core/exml/exml-parser.d.ts +0 -27
  105. package/dist/core/exml/exml-parser.d.ts.map +0 -1
  106. package/dist/core/exml/exml-parser.js +0 -380
  107. package/dist/core/exml/exml-parser.js.map +0 -1
  108. package/dist/core/exml/index.d.ts +0 -54
  109. package/dist/core/exml/index.d.ts.map +0 -1
  110. package/dist/core/exml/index.js +0 -51
  111. package/dist/core/exml/index.js.map +0 -1
  112. package/dist/core/exml/registry.d.ts.map +0 -1
  113. package/dist/core/exml/registry.js.map +0 -1
  114. package/dist/core/exml/skin-module-builder.d.ts.map +0 -1
  115. package/dist/core/exml/skin-module-builder.js.map +0 -1
  116. package/dist/core/exml/skin-parts-declaration.d.ts.map +0 -1
  117. package/dist/core/exml/skin-parts-declaration.js.map +0 -1
  118. package/dist/core/exml/source-location.d.ts.map +0 -1
  119. package/dist/core/exml/source-location.js.map +0 -1
  120. package/dist/core/exml/xml-parser.d.ts +0 -86
  121. package/dist/core/exml/xml-parser.d.ts.map +0 -1
  122. package/dist/core/exml/xml-parser.js +0 -196
  123. package/dist/core/exml/xml-parser.js.map +0 -1
  124. package/dist/core/plugins/compile-exml.d.ts +0 -14
  125. package/dist/core/plugins/compile-exml.d.ts.map +0 -1
  126. package/dist/core/plugins/compile-exml.js +0 -256
  127. package/dist/core/plugins/compile-exml.js.map +0 -1
  128. package/templates/game/resource/default.thm.json +0 -19
  129. package/templates/game/resource/skins/eui/ButtonSkin.exml +0 -16
  130. package/templates/game/resource/skins/eui/CheckBoxSkin.exml +0 -18
  131. package/templates/game/resource/skins/eui/ComboBoxSkin.exml +0 -31
  132. package/templates/game/resource/skins/eui/GroupSkin.exml +0 -4
  133. package/templates/game/resource/skins/eui/HScrollBarSkin.exml +0 -6
  134. package/templates/game/resource/skins/eui/HSliderSkin.exml +0 -7
  135. package/templates/game/resource/skins/eui/ImageSkin.exml +0 -4
  136. package/templates/game/resource/skins/eui/ItemRendererSkin.exml +0 -15
  137. package/templates/game/resource/skins/eui/LabelSkin.exml +0 -7
  138. package/templates/game/resource/skins/eui/ListSkin.exml +0 -11
  139. package/templates/game/resource/skins/eui/PanelSkin.exml +0 -15
  140. package/templates/game/resource/skins/eui/ProgressBarSkin.exml +0 -11
  141. package/templates/game/resource/skins/eui/RadioButtonSkin.exml +0 -18
  142. package/templates/game/resource/skins/eui/ScrollerSkin.exml +0 -8
  143. package/templates/game/resource/skins/eui/TabBarSkin.exml +0 -6
  144. package/templates/game/resource/skins/eui/TextInputSkin.exml +0 -22
  145. package/templates/game/resource/skins/eui/ToggleButtonSkin.exml +0 -21
  146. package/templates/game/resource/skins/eui/ToggleSwitchSkin.exml +0 -15
  147. package/templates/game/resource/skins/eui/VScrollBarSkin.exml +0 -6
  148. package/templates/game/resource/skins/eui/VSliderSkin.exml +0 -7
  149. package/templates/game/resource/skins/eui/ViewStackSkin.exml +0 -4
  150. /package/dist/core/{exml → kui}/codegen.d.ts +0 -0
  151. /package/dist/core/{exml → kui}/skin-parts-declaration.d.ts +0 -0
  152. /package/dist/core/{exml → kui}/source-location.d.ts +0 -0
  153. /package/dist/core/{exml → kui}/source-location.js +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,33 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 2.0.0 — 2026-09-21
11
+
12
+ This release is scoped to the Kurot Editor toolchain. Existing EXML game
13
+ projects remain supported by the 1.3.x line and are not expected to upgrade or
14
+ change their project configuration.
15
+
16
+ ### Added
17
+
18
+ - Canonical KUI XML Skin compilation through `@kurot/ui-document`.
19
+ - Generated default theme mappings derived from Skin `target` and `default`
20
+ metadata, including duplicate-default diagnostics.
21
+ - KUI XML project templates for all 21 built-in UI skins.
22
+
23
+ ### Changed
24
+
25
+ - Replaced the `exml` project configuration with `ui.sourceDir`,
26
+ `ui.namespaces`, and `ui.components`.
27
+ - Simplified SkinIR to the runtime operations produced by semantic KUI
28
+ documents and removed syntax-specific compatibility branches.
29
+ - The theme JSON is generated at the fixed `resource/default.thm.json` output
30
+ path rather than configured or supplied as an authored input.
31
+
32
+ ### Removed
33
+
34
+ - EXML parsing, theme-input discovery, `.exml` templates, and compatibility
35
+ diagnostics.
36
+
10
37
  ## 1.3.0 — 2026-09-20
11
38
 
12
39
  ### Added
package/README.md CHANGED
@@ -1,399 +1,183 @@
1
1
  # @kurot/cli
2
2
 
3
- CLI tool for the Kurot game engine — a modern replacement for the legacy Egret CLI. Powered by esbuild for fast compilation, with a built-in EXML skin parser and code generator.
3
+ Build tooling for the Kurot UI Editor workflow. It uses esbuild, emits ES2022
4
+ ESM, and compiles canonical KUI XML skins into runtime theme modules.
4
5
 
5
- > **Current release: 1.3.0.** Requires Node.js 20 or later and emits ES2022 ESM projects.
6
+ > **Current release: 2.0.0.** Node.js 20 or later is required.
6
7
 
7
- > Migrating from Egret? See [egret-migration.md](../../docs/egret-migration.md)
8
- >
9
- > Release history: [CHANGELOG.md](CHANGELOG.md)
8
+ > **Release scope:** 2.0.0 is currently dedicated to Kurot Editor integration.
9
+ > Existing game projects that use EXML, including CrashMaster, should remain on
10
+ > `@kurot/cli@1.3.x`. Version 2.0.0 is not an in-place project upgrade and does
11
+ > not require those projects to change their configuration or UI assets.
10
12
 
11
- ## Usage
12
-
13
- `@kurot/cli` does **not** require a global install.
13
+ See [CHANGELOG.md](CHANGELOG.md) for release history.
14
14
 
15
- ### Creating a project
15
+ ## Usage
16
16
 
17
- Use `npx` to scaffold a new project — no installation needed:
17
+ The CLI does not require a global install.
18
18
 
19
19
  ```bash
20
20
  npx @kurot/cli create my-game
21
- npx @kurot/cli create my-lib --template empty
22
- ```
23
-
24
- ### In-project commands
25
-
26
- Scaffolded projects include `@kurot/cli` as a devDependency and expose commands via npm scripts:
27
-
28
- ```bash
29
21
  cd my-game
30
22
  pnpm install
31
- pnpm build # build
32
- pnpm dev # dev server
33
- pnpm clean # clean output
23
+ pnpm dev
34
24
  ```
35
25
 
36
- You can also add it to an existing project manually:
37
-
38
- ```bash
39
- pnpm add -D @kurot/cli
40
- ```
26
+ Scaffolded projects expose `build`, `dev`, and `clean` scripts. For an
27
+ Editor-managed KUI XML project, install the package with
28
+ `pnpm add -D @kurot/cli@2`. Existing EXML projects should keep their current
29
+ 1.3.x dependency.
41
30
 
42
31
  ## Commands
43
32
 
44
33
  ### `kurot create`
45
34
 
46
- Scaffold a new project from a template.
47
-
48
35
  ```bash
49
- kurot create <name> [options]
36
+ kurot create <name> [--template game|empty]
50
37
  ```
51
38
 
52
- | Option | Description | Default |
53
- | ----------------------- | --------------------------- | ------- |
54
- | `--template <template>` | Template: `game` \| `empty` | `game` |
55
-
56
- **Templates:**
57
-
58
- | Template | Extends | Dependencies | Description |
59
- | -------- | --------- | ------------------------------------------------- | -------------------------------------------------------------------------------- |
60
- | `game` | `UILayer` | `@kurot/core` + `@kurot/game` + `@kurot/ui` | Full-featured project with resource loading, scene building, and Tween animation |
61
- | `empty` | `Sprite` | `@kurot/core` | Minimal project — pure Canvas rendering, no extra dependencies |
62
-
63
- **Lifecycle:**
64
-
65
- | Template | Entry class | Lifecycle |
66
- | -------- | ---------------------- | ------------------------------------------------------------------------------------------------- |
67
- | `game` | `Main extends UILayer` | `createChildren` → `runGame` → `loadResource` → `loadTheme` → `createGameScene` → `startAnimation` |
68
- | `empty` | `Main extends Sprite` | constructor → `ADDED_TO_STAGE` → `onAddToStage` |
39
+ The `game` template includes `@kurot/core`, `@kurot/game`, `@kurot/ui`, KUI
40
+ skins, resource loading, and an editable HTML template. The `empty` template
41
+ contains a minimal `Sprite` application.
69
42
 
70
43
  ### `kurot build`
71
44
 
72
- Compile the project into ESM application, engine, namespace, and theme bundles.
73
-
74
- ```bash
75
- kurot build [options]
76
- ```
77
-
78
- | Option | Description | Default |
79
- | ------------------------ | ------------------------------------------------------ | ------- |
80
- | `-r, --release` | Minified, content-hashed release build (→ bin-release) | `false` |
81
- | `--sourcemap` | Generate sourcemaps | `false` |
82
- | `--watch` | Rebuild source on file changes | `false` |
83
- | `--analyze` | Print bundle size analysis (esbuild metafile) | `false` |
84
- | `--strict` | Promote supported warnings to build errors | `false` |
85
- | `--diagnostics <format>` | Diagnostic output: `human` or `json` | `human` |
86
-
87
- `--diagnostics json` writes exactly one JSON result to stdout. It includes
88
- `success`, `mode`, `durationMs`, the output directory on success, and all
89
- structured diagnostics. Release builds use strict diagnostic policy by default.
90
-
91
45
  ```bash
92
- kurot build --strict --diagnostics json
46
+ kurot build [--release] [--sourcemap] [--watch] [--analyze]
47
+ [--strict] [--diagnostics human|json]
93
48
  ```
94
49
 
95
- **Output (Egret-aligned shape, ESM under the hood):**
50
+ Development output is written to `bin-debug/`. Release output is written to
51
+ `bin-release/web/<timestamp>/` with minified, content-hashed files. Engine,
52
+ project namespace, theme, and application code are separate ESM chunks joined
53
+ by the generated HTML import map.
96
54
 
97
- | Mode | Layout |
98
- | -------------- | --------------------------------------------------------------------------------------------------------- |
99
- | development | `bin-debug/` — per-file `.js` mirroring `src/` (`Main.js`, `com/.../X.js`) + engine chunks in `js/` |
100
- | release (`-r`) | `bin-release/web/<timestamp>/` — `js/main.min_<hash>.js` + `js/kurot.*.min_<hash>.js` + `manifest.json` |
101
-
102
- Engine packages (`@kurot/*`) are bundled into separate `js/kurot.<name>.js`
103
- chunks and wired up through an HTML **import map**, so the app bundle and engine
104
- resolve bare specifiers (`import { Sprite } from '@kurot/core'`) in the browser
105
- without duplicating engine code. `resource/` (including the compiled
106
- `default.thm.json`) is copied with fixed names, since user code references those
107
- paths directly. The entry script bootstraps via your own `createPlayer()` call.
55
+ `--diagnostics json` reserves stdout for one machine-readable build result.
56
+ Release builds apply strict diagnostic policy by default.
108
57
 
109
58
  ### `kurot dev`
110
59
 
111
- Start a development server with auto-recompilation on file changes (manual browser refresh required).
112
-
113
60
  ```bash
114
- kurot dev [options]
61
+ kurot dev [--port 3000] [--sourcemap] [--strict]
62
+ [--diagnostics human|jsonl]
115
63
  ```
116
64
 
117
- | Option | Description | Default |
118
- | ------------------------ | ------------------------------------------ | ------- |
119
- | `-p, --port <port>` | Port to listen | `3000` |
120
- | `--sourcemap` | Generate sourcemaps | `false` |
121
- | `--strict` | Promote supported warnings to build errors | `false` |
122
- | `--diagnostics <format>` | Diagnostic output: `human` or `jsonl` | `human` |
123
-
124
- Unlike build's single JSON result, `kurot dev --diagnostics jsonl` writes one
125
- JSON event per line so an agent can follow initial builds, diagnostics,
126
- rebuilds, and server readiness incrementally.
127
-
128
- ```bash
129
- kurot dev --strict --diagnostics jsonl
130
- ```
131
-
132
- Machine-readable modes reserve stdout for their JSON protocol and never include
133
- ANSI color sequences. Failures set a non-zero process exit code.
65
+ The development server rebuilds TypeScript, KUI XML, custom namespaces, and
66
+ the component catalog as their sources change. Browser refresh is currently
67
+ manual. JSONL mode reserves stdout for incremental build and server events.
134
68
 
135
69
  ### `kurot clean`
136
70
 
137
- Remove the build output directories (`bin-debug` and `bin-release`).
138
-
139
- ```bash
140
- kurot clean
141
- ```
71
+ Removes `bin-debug` and `bin-release`.
142
72
 
143
73
  ## Configuration
144
74
 
145
- Create a `kurot.config.ts` in your project root:
75
+ Create `kurot.config.ts` in the project root:
146
76
 
147
77
  ```ts
148
78
  export default {
149
- target: 'html5',
150
- entry: 'src/Main.ts',
151
- output: { dir: 'bin-debug' },
152
- html: { template: 'template/web/index.html' },
153
- stage: {
154
- width: 640,
155
- height: 1136,
156
- scaleMode: 'showAll',
157
- orientation: 'auto',
158
- frameRate: 60,
159
- },
160
- // Optional: enable EXML skin compilation
161
- exml: {
162
- themeFile: 'resource/default.thm.json',
163
- // Optional: discover reusable component source/Skin pairs
164
- components: {
165
- namespace: 'game',
166
- sourceDir: 'src/components',
167
- skinDir: 'resource/skins/components',
168
- },
169
- },
79
+ target: 'html5',
80
+ entry: 'src/Main.ts',
81
+ output: { dir: 'bin-debug' },
82
+ html: { template: 'template/web/index.html' },
83
+ stage: {
84
+ width: 640,
85
+ height: 1136,
86
+ scaleMode: 'showAll',
87
+ orientation: 'auto',
88
+ frameRate: 60,
89
+ },
90
+ ui: {
91
+ sourceDir: 'resource/ui',
92
+ components: {
93
+ namespace: 'game',
94
+ sourceDir: 'src/components',
95
+ skinDir: 'resource/ui/components',
96
+ },
97
+ },
170
98
  };
171
99
  ```
172
100
 
173
- **Options:**
174
-
175
- | Field | Type | Description |
176
- | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
177
- | `target` | `string` | Build target — currently only `'html5'` |
178
- | `entry` | `string` | Entry file path, default `'src/Main.ts'` |
179
- | `output.dir` | `string` | Output directory, default `'bin-debug'` |
180
- | `html.template` | `string` | Optional project-owned HTML template; the CLI default page is used when omitted |
181
- | `stage.width` | `number` | Stage width |
182
- | `stage.height` | `number` | Stage height |
183
- | `stage.scaleMode` | `string` | Scale mode: `showAll` / `noScale` / `exactFit` / `noBorder` / `fixedHeight` / `fixedWidth` / `fixedNarrow` / `fixedWide` |
184
- | `stage.orientation` | `string` | Orientation: `auto` / `portrait` / `landscape` |
185
- | `stage.frameRate` | `number` | Frame rate — must be a positive integer |
186
- | `exml.themeFile` | `string` | Theme JSON file path |
187
- | `exml.components` | `ComponentsConfig` | Optional reusable-component convention: namespace, TypeScript source directory, and Skin directory |
188
- | `exml.namespaces` | `Record<string, string>` | Optional EXML prefix → source barrel-file mapping |
189
-
190
- ## HTML Template
191
-
192
- New projects include an editable `template/web/index.html`. The build reads
193
- this file and writes the rendered page to the active output directory. Existing
194
- projects that do not configure `html.template` continue to use the CLI's
195
- built-in default page.
196
-
197
- The following placeholders are required in a configured project template:
198
-
199
- | Placeholder | Generated value |
200
- | --- | --- |
201
- | `{{KUROT_IMPORT_MAP}}` | Engine and custom namespace import map |
202
- | `{{KUROT_STAGE_WIDTH}}` | Configured stage width |
203
- | `{{KUROT_STAGE_HEIGHT}}` | Configured stage height |
204
- | `{{KUROT_SCALE_MODE}}` | Configured scale mode |
205
- | `{{KUROT_ORIENTATION}}` | Configured orientation |
206
- | `{{KUROT_FRAME_RATE}}` | Configured frame rate |
207
- | `{{KUROT_ENTRY_SCRIPT}}` | Compiled application entry script |
208
-
209
- The template may otherwise contain any project-specific HTML, styles, loading
210
- screen, platform SDK, analytics, fonts, or additional containers. A build fails
211
- with a clear error when a configured template is missing a required placeholder.
212
-
213
- ## EXML Skin Compiler
214
-
215
- The CLI includes a complete EXML skin parsing and code generation pipeline (XML → SkinIR → ESM JavaScript). `.exml` files placed in the `resource/` directory are compiled automatically during `kurot build`.
216
-
217
- ### Features
218
-
219
- - **XML Parsing** — lightweight parser with namespace, CDATA, and comment support
220
- - **AST / IR Generation** — converts to an intermediate representation (SkinIR)
221
- - **Code Generation** — outputs ESM factory functions
222
- - **Component Registry** — built-in `eui:*` / `egret:*` namespace mapping to `@kurot/ui` / `@kurot/core`
223
- - **Reusable Components** — pairs `src/components/<Name>.ts` with `resource/skins/components/<Name>Skin.exml`, then exposes `<game:Name>` without a hand-written barrel
224
- - **Custom Namespaces** — retains `exml.namespaces` for advanced manually maintained source barrels
225
- - **View States** — supports `<eui:states>`, shorthand `states="up,down"`, state properties, `includeIn`, and `excludeFrom`
226
- - **Skin Properties** — preserves root properties such as `minWidth`, `minHeight`, and state-specific values
227
- - **Percent Layout** — auto-detects `width="100%"` and converts to `percentWidth`
228
- - **Data Binding** — parses `{expression}` binding syntax and generates `Binding.bindProperty` calls
229
- - **Structured Diagnostics** — stable codes, source locations, suggestions, and strict warning promotion
230
-
231
- Unknown tags remain warnings in normal development builds and are omitted from
232
- the generated visual tree. Under `--strict` (and in release builds), those
233
- warnings become errors. Syntax errors, invalid theme JSON, and other genuine
234
- Skin compilation failures always stop the build; the compiler never substitutes
235
- an empty Skin factory.
236
-
237
- The standard declarations `xmlns:eui="http://ns.egret.com/eui"` and
238
- `xmlns:egret="http://ns.egret.com/egret"` are namespace identifiers. The CLI
239
- resolves their prefixes internally and does not access those URLs over the
240
- network, so the original Egret namespace pages do not need to be hosted.
241
-
242
- ### Reusable components
243
-
244
- The game template keeps reusable component logic and white-Egret-compatible
245
- skins in parallel directories:
101
+ `ui.sourceDir` contains `.kui.xml` documents. The build always generates the
102
+ runtime theme manifest at `resource/default.thm.json`; it is not an authored
103
+ input. `ui.namespaces` can map additional XML prefixes to project barrel files.
246
104
 
247
- ```text
248
- src/components/<path>/<Name>.ts
249
- resource/skins/components/<path>/<Name>Skin.exml
250
- ```
251
-
252
- The TypeScript file must export a class named `<Name>`, and the paired EXML
253
- must use a standard `eui:Skin` root with a `class` attribute. The CLI validates
254
- the pair, generates the shared `#ns/game` entry, adds the default Theme mapping,
255
- and accepts `<game:Name />` in other skins. Components are globally unique by
256
- class name within the configured namespace.
257
-
258
- Development builds also emit `.kurot/component-catalog.json` for editor and
259
- agent tooling. The catalog is intentionally omitted from release output.
260
-
261
- Every successful EXML compilation also writes `.kurot/skin-parts.d.ts`. It
262
- augments `@kurot/ui`'s `SkinPartsMap` with the exact named parts and runtime
263
- types found in each compiled skin. It also discovers exported project classes
264
- through a compiled string-literal `this.skinName`, configured reusable-component
265
- pairs, or a unique `<ClassName>Skin` naming match, then narrows their inherited
266
- public `skinParts` property automatically. Ambiguous naming matches are left
267
- untyped instead of being guessed. The generated file is included by the template
268
- `tsconfig.json`, ignored by git, and never enters browser or release bundles.
269
-
270
- `exml.namespaces` remains available for advanced manual namespace barrels, but
271
- its prefix must not conflict with `exml.components.namespace`.
272
-
273
- ### Custom component lifecycle
105
+ The HTML template must contain these placeholders:
274
106
 
275
- Assign the runtime skin once and initialize skin-dependent behavior in
276
- `onSkinReady()`. The generated declaration derives the `skinParts` type from that
277
- assignment:
107
+ - `{{KUROT_IMPORT_MAP}}`
108
+ - `{{KUROT_STAGE_WIDTH}}`
109
+ - `{{KUROT_STAGE_HEIGHT}}`
110
+ - `{{KUROT_SCALE_MODE}}`
111
+ - `{{KUROT_ORIENTATION}}`
112
+ - `{{KUROT_FRAME_RATE}}`
113
+ - `{{KUROT_ENTRY_SCRIPT}}`
278
114
 
279
- ```ts
280
- import { Component } from '@kurot/ui';
281
-
282
-
283
- export class BattlePanel extends Component {
284
- public constructor() {
285
- super();
286
- this.skinName = 'game.ui.BattlePanelSkin';
287
- }
288
-
289
- protected override onSkinReady(): void {
290
- super.onSkinReady();
291
- this.skinParts.groupField.visible = true;
292
- }
293
- }
294
- ```
115
+ ## KUI XML compilation
295
116
 
296
- The same declaration narrowing applies when the runtime assigns the skin
297
- externally. For example, an exported `MultiplierIR` class is matched to a
298
- unique compiled `ui.MultiplierIRSkin`, so the renderer remains free of a
299
- repeated generic skin name:
117
+ KUI XML is the only authored UI format. A skin declares its runtime target and
118
+ whether it is the default skin directly on the document root:
300
119
 
301
- ```ts
302
- import { ItemRenderer } from '@kurot/ui';
303
-
304
- export class MultiplierIR extends ItemRenderer {
305
- protected override onSkinReady(): void {
306
- super.onSkinReady();
307
- this.skinParts.lblMultiplier.text = '';
308
- }
309
- }
120
+ ```xml
121
+ <?xml version="1.0" encoding="utf-8"?>
122
+ <Skin xmlns="https://kurot.dev/ui/1"
123
+ id="skins.ButtonSkin"
124
+ version="2"
125
+ target="kui.Button"
126
+ default="true">
127
+ <Group id="root" minWidth="100" minHeight="50">
128
+ <Label id="labelDisplay" horizontalCenter="0" verticalCenter="0" />
129
+ </Group>
130
+ </Skin>
310
131
  ```
311
132
 
312
- The event-based equivalent is useful when initialization is composed externally:
313
-
314
- ```ts
315
- import { UIEvent } from '@kurot/ui';
133
+ The pipeline is:
316
134
 
317
- this.once(UIEvent.CREATION_COMPLETE, this.onCreationComplete);
135
+ ```text
136
+ .kui.xml → UIDocument → SkinIR → ESM skin factory → theme bundle
318
137
  ```
319
138
 
320
- Use `onSkinReady()` to initialize logic that depends on skin parts and
321
- `onSkinRemoved()` to release listeners or other bindings before replacement.
322
- `childrenCreated()` and `UIEvent.CREATION_COMPLETE` run once for the component's
323
- initial creation and are not skin-replacement hooks.
324
-
325
- UI 2.0 no longer copies part names onto component instances and no longer
326
- supports `setSkinPart()`, `partAdded()`, or `partRemoved()`. Read parts through
327
- `this.skinParts` only while the skin-ready lifecycle is active. Use an explicit
328
- generic skin type when generated host declarations are unavailable outside a
329
- CLI project.
139
+ Default skin mappings are derived from `target` and `default`; the build writes
140
+ `default.thm.json` with the mappings and the generated `skinsJs` module path.
141
+ Duplicate defaults are errors. Unknown tags are warnings in normal development
142
+ and errors under strict or release builds.
330
143
 
331
- EXML skins compiled by the CLI are registered under their complete `class`
332
- attribute, so an Egret-style value such as
333
- `skinName = "game.ui.BattlePanelSkin"` works when that EXML is included in the
334
- loaded theme bundle. A component covered by the theme's `skins` mapping normally
335
- does not need to assign `skinName` itself. Hand-written `Skin` subclasses may be
336
- imported and assigned directly instead of using a string.
144
+ Successful compilation also writes `.kurot/skin-parts.d.ts`. It augments the UI
145
+ runtime with the exact named parts and types declared by each skin. This file is
146
+ editor-only, ignored by git, and never enters browser bundles.
337
147
 
338
- ### Compilation Pipeline
148
+ ## Reusable components
339
149
 
340
- All `.exml` skins compile into a single ESM module — `js/default.thm.js` (dev)
341
- or `js/default.thm.min_<hash>.js` (release) — that registers each skin factory.
342
- `default.thm.json` keeps only the component→skin mapping plus a `skinsJs`
343
- pointer to that module, which the runtime `Theme` imports. No `.exml` is shipped.
150
+ Convention-based reusable components pair:
344
151
 
345
- ```
346
- resource/skins/**/*.exml
347
- ↓ parseXML()
348
- XML Element Tree
349
- ↓ parseEXML()
350
- SkinIR
351
- ↓ generateCode({ format: 'esm' })
352
- per-skin ESM factories
353
- ↓ esbuild bundle (+ minify in release)
354
- js/default.thm[.min_<hash>].js (skins register on globalThis)
152
+ ```text
153
+ src/components/<path>/<Name>.ts
154
+ resource/ui/components/<path>/<Name>Skin.kui.xml
355
155
  ```
356
156
 
357
- ## Project Structure
157
+ The TypeScript module must export `<Name>`. The paired skin must target
158
+ `<namespace>.<Name>`. The CLI exposes the component as `<namespace>:<Name>` in
159
+ KUI XML, refreshes the namespace bundle, and emits development catalog data at
160
+ `.kurot/component-catalog.json`.
358
161
 
359
- A project created with the default template (`game`) has the following structure:
162
+ Use `onSkinReady()` for logic that needs skin parts and `onSkinRemoved()` for
163
+ cleanup before a skin replacement. Access generated parts through
164
+ `this.skinParts`.
360
165
 
361
- ```
166
+ ## Generated project shape
167
+
168
+ ```text
362
169
  my-game/
363
- ├── .kurot/
364
- │ └── skin-parts.d.ts # Generated typed EXML part declarations
365
- ├── .gitignore # Excludes .kurot and build/dependency output
366
- ├── kurot.config.ts # Project config (includes exml options)
367
- ├── package.json # Dependencies & scripts
368
- ├── tsconfig.json # TypeScript config
369
- ├── template/
370
- │ └── web/
371
- │ └── index.html # Editable output page template
170
+ ├── .kurot/skin-parts.d.ts
171
+ ├── kurot.config.ts
372
172
  ├── resource/
373
- │ ├── default.res.json # Resource config
374
- │ ├── default.thm.json # Theme file: component → skin mapping
375
- │ └── skins/ # EXML skin directory
376
- │ ├── components/ # Reusable component skins (standard eui:Skin)
377
- │ └── eui/ # 21 built-in EUI component skins
378
- │ ├── ButtonSkin.exml
379
- │ ├── ...
380
- │ └── ViewStackSkin.exml
381
- └── src/
382
- ├── components/ # Reusable component TypeScript classes
383
- ├── Main.ts # Entry: class Main extends Sprite
384
- └── LoadingUI.ts # Loading progress display
385
- ```
386
-
387
- ## Quick Start
388
-
389
- ```bash
390
- # Full-featured game project (default)
391
- npx @kurot/cli create my-game
392
- cd my-game && pnpm install
393
- pnpm dev
394
-
395
- # Minimal project
396
- npx @kurot/cli create my-lib --template empty
397
- cd my-lib && pnpm install
398
- pnpm dev
173
+ │ ├── default.res.json
174
+ │ ├── assets/
175
+ │ └── ui/
176
+ │ ├── components/
177
+ │ └── skins/*.kui.xml
178
+ ├── src/
179
+ │ ├── components/
180
+ │ ├── LoadingUI.ts
181
+ │ └── Main.ts
182
+ └── template/web/index.html
399
183
  ```
@@ -1 +1 @@
1
- {"version":3,"file":"discover-components.d.ts","sourceRoot":"","sources":["../../../src/core/components/discover-components.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE3E;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,mBAAmB,GAAG,SAAS,GACzC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAqF7B;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD,QAAQ,CAAC,UAAU,EAAE,gBAAgB,EAAE,CAAC;CACxC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhB"}
1
+ {"version":3,"file":"discover-components.d.ts","sourceRoot":"","sources":["../../../src/core/components/discover-components.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE3E;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,mBAAmB,GAAG,SAAS,GACzC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAsF7B;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD,QAAQ,CAAC,UAAU,EAAE,gBAAgB,EAAE,CAAC;CACxC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhB"}
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
+ import { parseUIDocument } from '@kurot/ui-document';
3
4
  import { ConfigError } from '../errors.js';
4
- import { localName, parseXML } from '../exml/index.js';
5
5
  /**
6
6
  * Discovers reusable component source/skin pairs using the configured
7
7
  * directory convention.
@@ -11,7 +11,7 @@ export async function discoverComponents(root, convention) {
11
11
  return [];
12
12
  const [sourceFiles, skinFiles] = await Promise.all([
13
13
  collectFiles(convention.sourceDir, file => file.endsWith('.ts') && !file.endsWith('.d.ts')),
14
- collectFiles(convention.skinDir, file => file.endsWith('Skin.exml')),
14
+ collectFiles(convention.skinDir, file => file.endsWith('Skin.kui.xml')),
15
15
  ]);
16
16
  const sourceByPair = new Map(sourceFiles.map(file => [sourcePairKey(convention.sourceDir, file), file]));
17
17
  const skinByPair = new Map(skinFiles.map(file => [skinPairKey(convention.skinDir, file), file]));
@@ -29,7 +29,7 @@ export async function discoverComponents(root, convention) {
29
29
  continue;
30
30
  }
31
31
  if (!skin) {
32
- errors.push(`Component source '${relative(root, source)}' has no matching skin '${pairKey}Skin.exml'.`);
32
+ errors.push(`Component source '${relative(root, source)}' has no matching skin '${pairKey}Skin.kui.xml'.`);
33
33
  continue;
34
34
  }
35
35
  const name = path.basename(source, '.ts');
@@ -54,20 +54,21 @@ export async function discoverComponents(root, convention) {
54
54
  }
55
55
  let skinClass;
56
56
  try {
57
- const skinRoot = parseXML(await fs.readFile(skin, 'utf-8'));
58
- if (localName(skinRoot.name) !== 'Skin') {
59
- errors.push(`Component skin '${relative(root, skin)}' must use an eui:Skin root.`);
57
+ const skinDocument = parseUIDocument(await fs.readFile(skin, 'utf-8'));
58
+ if (skinDocument.assetKind !== 'appearance') {
59
+ errors.push(`Component skin '${relative(root, skin)}' must be a KUI Skin document.`);
60
60
  continue;
61
61
  }
62
- skinClass = skinRoot.attributes.find(attribute => attribute.name === 'class')?.value ?? '';
63
- if (!skinClass) {
64
- errors.push(`Component skin '${relative(root, skin)}' must declare a class attribute.`);
62
+ const targetType = `${convention.prefix}.${name}`;
63
+ if (skinDocument.contract.targetType !== targetType) {
64
+ errors.push(`Component skin '${relative(root, skin)}' must target '${targetType}'.`);
65
65
  continue;
66
66
  }
67
+ skinClass = skinDocument.id;
67
68
  }
68
69
  catch (error) {
69
70
  const message = error instanceof Error ? error.message : String(error);
70
- errors.push(`Component skin '${relative(root, skin)}' is invalid EXML: ${message}`);
71
+ errors.push(`Component skin '${relative(root, skin)}' is invalid KUI XML: ${message}`);
71
72
  continue;
72
73
  }
73
74
  components.push({
@@ -124,7 +125,7 @@ function sourcePairKey(sourceDir, file) {
124
125
  return toPosix(path.relative(sourceDir, file).slice(0, -'.ts'.length));
125
126
  }
126
127
  function skinPairKey(skinDir, file) {
127
- return toPosix(path.relative(skinDir, file).slice(0, -'Skin.exml'.length));
128
+ return toPosix(path.relative(skinDir, file).slice(0, -'Skin.kui.xml'.length));
128
129
  }
129
130
  function hasNamedClassExport(source, name) {
130
131
  source = stripComments(source);