@kurot/cli 1.3.0 → 2.0.1

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 +43 -0
  2. package/README.md +118 -330
  3. package/dist/core/components/discover-components.d.ts.map +1 -1
  4. package/dist/core/components/discover-components.js +7 -15
  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 -177
  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 +153 -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 +167 -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 +8 -0
  78. package/templates/game/resource/ui/skins/CheckBoxSkin.kui.xml +12 -0
  79. package/templates/game/resource/ui/skins/ComboBoxSkin.kui.xml +11 -0
  80. package/templates/game/resource/ui/skins/GroupSkin.kui.xml +6 -0
  81. package/templates/game/resource/ui/skins/HScrollBarSkin.kui.xml +6 -0
  82. package/templates/game/resource/ui/skins/HSliderSkin.kui.xml +7 -0
  83. package/templates/game/resource/ui/skins/ImageSkin.kui.xml +6 -0
  84. package/templates/game/resource/ui/skins/ItemRendererSkin.kui.xml +7 -0
  85. package/templates/game/resource/ui/skins/LabelSkin.kui.xml +6 -0
  86. package/templates/game/resource/ui/skins/ListSkin.kui.xml +9 -0
  87. package/templates/game/resource/ui/skins/PanelSkin.kui.xml +11 -0
  88. package/templates/game/resource/ui/skins/ProgressBarSkin.kui.xml +8 -0
  89. package/templates/game/resource/ui/skins/RadioButtonSkin.kui.xml +12 -0
  90. package/templates/game/resource/ui/skins/ScrollerSkin.kui.xml +7 -0
  91. package/templates/game/resource/ui/skins/TabBarSkin.kui.xml +7 -0
  92. package/templates/game/resource/ui/skins/TextInputSkin.kui.xml +8 -0
  93. package/templates/game/resource/ui/skins/ToggleButtonSkin.kui.xml +7 -0
  94. package/templates/game/resource/ui/skins/ToggleSwitchSkin.kui.xml +7 -0
  95. package/templates/game/resource/ui/skins/VScrollBarSkin.kui.xml +6 -0
  96. package/templates/game/resource/ui/skins/VSliderSkin.kui.xml +7 -0
  97. package/templates/game/resource/ui/skins/ViewStackSkin.kui.xml +6 -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,49 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 2.0.1 — 2026-09-21
11
+
12
+ ### Changed
13
+
14
+ - Raised the `@kurot/ui-document` requirement to `^0.6.1` for canonical Skin
15
+ state and internal-node handling.
16
+ - Simplified the authored Skin root to `class`; storage IDs, format versions,
17
+ runtime targets, and default flags are no longer user-edited XML metadata.
18
+ - Default theme mappings now come from internal built-in conventions and
19
+ configured project component pairs.
20
+ - Skin parts are inferred from identified visual nodes; internal nodes can omit
21
+ IDs. States use the root `states` list and local `property.state` attributes,
22
+ so state-only targets do not require artificial part names.
23
+ - Image `source` values use plain resource keys without an
24
+ `@resource:image:` prefix.
25
+
26
+ ## 2.0.0 — 2026-09-21
27
+
28
+ This release is scoped to the Kurot Editor toolchain. Existing EXML game
29
+ projects remain supported by the 1.3.x line and are not expected to upgrade or
30
+ change their project configuration.
31
+
32
+ ### Added
33
+
34
+ - Canonical KUI XML Skin compilation through `@kurot/ui-document`.
35
+ - Generated default theme mappings derived from Skin `target` and `default`
36
+ metadata, including duplicate-default diagnostics.
37
+ - KUI XML project templates for all 21 built-in UI skins.
38
+
39
+ ### Changed
40
+
41
+ - Replaced the `exml` project configuration with `ui.sourceDir`,
42
+ `ui.namespaces`, and `ui.components`.
43
+ - Simplified SkinIR to the runtime operations produced by semantic KUI
44
+ documents and removed syntax-specific compatibility branches.
45
+ - The theme JSON is generated at the fixed `resource/default.thm.json` output
46
+ path rather than configured or supplied as an authored input.
47
+
48
+ ### Removed
49
+
50
+ - EXML parsing, theme-input discovery, `.exml` templates, and compatibility
51
+ diagnostics.
52
+
10
53
  ## 1.3.0 — 2026-09-20
11
54
 
12
55
  ### Added
package/README.md CHANGED
@@ -1,399 +1,187 @@
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.1.** 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:** The 2.0.x line 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.x 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):**
96
-
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` |
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.
101
54
 
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
- ```bash
114
- kurot dev [options]
115
- ```
116
-
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
60
  ```bash
129
- kurot dev --strict --diagnostics jsonl
61
+ kurot dev [--port 3000] [--sourcemap] [--strict]
62
+ [--diagnostics human|jsonl]
130
63
  ```
131
64
 
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:
246
-
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.
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.
269
104
 
270
- `exml.namespaces` remains available for advanced manual namespace barrels, but
271
- its prefix must not conflict with `exml.components.namespace`.
105
+ The HTML template must contain these placeholders:
272
106
 
273
- ### Custom component lifecycle
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}}`
274
114
 
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:
115
+ ## KUI XML compilation
278
116
 
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
- ```
295
-
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 authored Skin format. A Skin root declares its generated class
118
+ name and, when needed, its state names:
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" class="skins.ButtonSkin" states="up,down,disabled">
123
+ <Group id="root" minWidth="100" minHeight="50">
124
+ <Rect fillColor="#315A9D" fillColor.down="#244474" alpha.disabled="0.5" />
125
+ <Label id="labelDisplay" horizontalCenter="0" verticalCenter="0" />
126
+ </Group>
127
+ </Skin>
310
128
  ```
311
129
 
312
- The event-based equivalent is useful when initialization is composed externally:
130
+ Only nodes exposed as runtime skin parts need an `id`. State-specific values
131
+ use `property.state` on the affected node, so internal graphics remain unnamed.
132
+ Image sources use their resource key directly, for example
133
+ `source="button_up_png"`.
313
134
 
314
- ```ts
315
- import { UIEvent } from '@kurot/ui';
135
+ The pipeline is:
316
136
 
317
- this.once(UIEvent.CREATION_COMPLETE, this.onCreationComplete);
137
+ ```text
138
+ .kui.xml → UIDocument → SkinIR → ESM skin factory → theme bundle
318
139
  ```
319
140
 
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.
141
+ The build derives default skin mappings from built-in component conventions and
142
+ configured project component pairs. The build writes `default.thm.json` with
143
+ those mappings and the generated `skinsJs` module path. Duplicate conventional
144
+ mappings are errors. Unknown tags are warnings in normal development and errors
145
+ under strict or release builds.
330
146
 
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.
147
+ Successful compilation also writes `.kurot/skin-parts.d.ts`. Every identified
148
+ node below the visual root is inferred as a skin part; its `id` is the part
149
+ name. The declaration augments the UI runtime with those exact names and types.
150
+ This file is editor-only, ignored by git, and never enters browser bundles.
337
151
 
338
- ### Compilation Pipeline
152
+ ## Reusable components
339
153
 
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.
154
+ Convention-based reusable components pair:
344
155
 
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)
156
+ ```text
157
+ src/components/<path>/<Name>.ts
158
+ resource/ui/components/<path>/<Name>Skin.kui.xml
355
159
  ```
356
160
 
357
- ## Project Structure
161
+ The TypeScript module must export `<Name>`. The paired skin must target
162
+ `<namespace>.<Name>`. The CLI exposes the component as `<namespace>:<Name>` in
163
+ KUI XML, refreshes the namespace bundle, and emits development catalog data at
164
+ `.kurot/component-catalog.json`.
358
165
 
359
- A project created with the default template (`game`) has the following structure:
166
+ Use `onSkinReady()` for logic that needs skin parts and `onSkinRemoved()` for
167
+ cleanup before a skin replacement. Access generated parts through
168
+ `this.skinParts`.
360
169
 
361
- ```
170
+ ## Generated project shape
171
+
172
+ ```text
362
173
  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
174
+ ├── .kurot/skin-parts.d.ts
175
+ ├── kurot.config.ts
372
176
  ├── 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
177
+ │ ├── default.res.json
178
+ │ ├── assets/
179
+ │ └── ui/
180
+ │ ├── components/
181
+ │ └── skins/*.kui.xml
182
+ ├── src/
183
+ │ ├── components/
184
+ │ ├── LoadingUI.ts
185
+ │ └── Main.ts
186
+ └── template/web/index.html
399
187
  ```
@@ -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,CA6E7B;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,12 @@ 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.`);
60
- continue;
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.`);
65
- continue;
66
- }
57
+ const skinDocument = parseUIDocument(await fs.readFile(skin, 'utf-8'));
58
+ skinClass = skinDocument.id;
67
59
  }
68
60
  catch (error) {
69
61
  const message = error instanceof Error ? error.message : String(error);
70
- errors.push(`Component skin '${relative(root, skin)}' is invalid EXML: ${message}`);
62
+ errors.push(`Component skin '${relative(root, skin)}' is invalid KUI XML: ${message}`);
71
63
  continue;
72
64
  }
73
65
  components.push({
@@ -124,7 +116,7 @@ function sourcePairKey(sourceDir, file) {
124
116
  return toPosix(path.relative(sourceDir, file).slice(0, -'.ts'.length));
125
117
  }
126
118
  function skinPairKey(skinDir, file) {
127
- return toPosix(path.relative(skinDir, file).slice(0, -'Skin.exml'.length));
119
+ return toPosix(path.relative(skinDir, file).slice(0, -'Skin.kui.xml'.length));
128
120
  }
129
121
  function hasNamedClassExport(source, name) {
130
122
  source = stripComments(source);