@lingfan-engine/editor 0.0.0-stage → 0.1.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 (79) hide show
  1. package/LICENSE +201 -0
  2. package/package.json +39 -3
  3. package/src/chapters/build.ts +180 -0
  4. package/src/chapters/index.ts +34 -0
  5. package/src/chapters/types.ts +71 -0
  6. package/src/contracts/diagnostics.ts +62 -0
  7. package/src/contracts/editing.ts +26 -0
  8. package/src/contracts/forms.ts +69 -0
  9. package/src/contracts/index.ts +22 -0
  10. package/src/contracts/texts.ts +204 -0
  11. package/src/diagnostics/analyze.ts +161 -0
  12. package/src/diagnostics/grouping.ts +164 -0
  13. package/src/diagnostics/index.ts +35 -0
  14. package/src/diagnostics/message.ts +60 -0
  15. package/src/diagnostics/refs.ts +123 -0
  16. package/src/diagnostics/symbols.ts +221 -0
  17. package/src/editing/columnGrouping.ts +331 -0
  18. package/src/editing/graphConnect.ts +88 -0
  19. package/src/editing/index.ts +72 -0
  20. package/src/editing/pointers.ts +291 -0
  21. package/src/editing/saveNormalization.ts +58 -0
  22. package/src/editing/session.ts +134 -0
  23. package/src/editing/storyOps.ts +268 -0
  24. package/src/editing/tsSourceGuard.ts +40 -0
  25. package/src/element/drag.ts +22 -0
  26. package/src/element/dragCreate.ts +60 -0
  27. package/src/element/index.ts +11 -0
  28. package/src/i18n/coverage.ts +191 -0
  29. package/src/i18n/extract.ts +38 -0
  30. package/src/i18n/index.ts +47 -0
  31. package/src/i18n/reconcile.ts +71 -0
  32. package/src/i18n/skeleton.ts +112 -0
  33. package/src/i18n/storyKeys.ts +27 -0
  34. package/src/i18n/surfaces.ts +54 -0
  35. package/src/i18n/table.ts +235 -0
  36. package/src/index.ts +342 -0
  37. package/src/layout/index.ts +14 -0
  38. package/src/layout/steps.ts +378 -0
  39. package/src/schema/catalog.ts +300 -0
  40. package/src/schema/elementForms.ts +307 -0
  41. package/src/schema/elementPalette.ts +100 -0
  42. package/src/schema/forms.ts +196 -0
  43. package/src/schema/index.ts +55 -0
  44. package/src/schema/opSchemas/commands.ts +374 -0
  45. package/src/schema/opSchemas/fragments.ts +24 -0
  46. package/src/schema/opSchemas/index.ts +16 -0
  47. package/src/schema/opSchemas/map.ts +134 -0
  48. package/src/schema/opSchemas/types.ts +42 -0
  49. package/src/schema/opSchemas/validate.ts +184 -0
  50. package/src/schema/surface.ts +101 -0
  51. package/src/schema/validation.ts +92 -0
  52. package/src/schema/walk.ts +167 -0
  53. package/src/script/expr/audit/index.ts +8 -0
  54. package/src/script/expr/audit/parser.ts +370 -0
  55. package/src/script/expr/audit/token.ts +23 -0
  56. package/src/script/expr/build.ts +203 -0
  57. package/src/script/expr/index.ts +17 -0
  58. package/src/script/expr/registry.ts +130 -0
  59. package/src/script/index.ts +168 -0
  60. package/src/script/vocabulary-gen.ts +202 -0
  61. package/src/script/vocabulary.g.ts +175 -0
  62. package/src/script/words/audio.ts +114 -0
  63. package/src/script/words/cells.ts +78 -0
  64. package/src/script/words/compile-time.ts +27 -0
  65. package/src/script/words/composite.ts +125 -0
  66. package/src/script/words/elements.ts +202 -0
  67. package/src/script/words/extension.ts +25 -0
  68. package/src/script/words/flow.ts +391 -0
  69. package/src/script/words/index.ts +23 -0
  70. package/src/script/words/interaction.ts +44 -0
  71. package/src/script/words/minigame.ts +66 -0
  72. package/src/script/words/narrative.ts +261 -0
  73. package/src/script/words/save.ts +54 -0
  74. package/src/script/words/vars.ts +155 -0
  75. package/src/script/words/video.ts +80 -0
  76. package/src/shared/guards.ts +19 -0
  77. package/src/shared/index.ts +6 -0
  78. package/src/shared/pointer.ts +23 -0
  79. package/README.md +0 -3
@@ -0,0 +1,100 @@
1
+ /**
2
+ * 组件面板:**元素 36 类型**与**命令 op**的归类源,为拖入画布提供拖拽源。
3
+ *
4
+ * 两条约定:
5
+ * 1. **分组不新增事实**——元素分组是 `ELEMENT_TYPES` 既有顺序(文本/交互/图像/容器/滚动/进度/间隔)
6
+ * 的显式化;互锁测试断言「按组序拍平 == ELEMENT_TYPES」
7
+ * ⇒ 契约加类型而此处不同步会立刻变红(不越界、不缺项)。
8
+ * 2. **op 分组标签单一事实源**——时间线与组件面板共用本模块导出的标签,
9
+ * 避免两份中文标签表漂移。
10
+ */
11
+
12
+ import type { OpGroup } from "../contracts/index.ts";
13
+ import { OP_GROUP_LABELS, OP_GROUP_ORDER } from "./catalog.ts";
14
+ import { listOps } from "./forms.ts";
15
+ import { BUILTIN_OP_SURFACE, type OpSurface } from "./surface.ts";
16
+
17
+ /** 元素类型分组(顺序即 `ELEMENT_TYPES` 顺序;标签供面板展示) */
18
+ export interface ElementTypeGroup {
19
+ group: string;
20
+ label: string;
21
+ types: readonly string[];
22
+ }
23
+
24
+ /**
25
+ * 七组(对齐 `contracts/element.ts` 的分组注释)。
26
+ * `types` 显式列出而非按索引切分——可读性优先;一致性由互锁测试锁定。
27
+ */
28
+ export const ELEMENT_TYPE_GROUPS: readonly ElementTypeGroup[] = [
29
+ {
30
+ group: "text",
31
+ label: "文本",
32
+ types: ["text", "dialog", "narrator", "speaker"],
33
+ },
34
+ {
35
+ group: "interaction",
36
+ label: "交互",
37
+ types: ["button", "choice", "imagebutton"],
38
+ },
39
+ {
40
+ group: "media",
41
+ label: "图像",
42
+ types: ["image", "background", "portrait", "video"],
43
+ },
44
+ {
45
+ group: "container",
46
+ label: "容器",
47
+ types: [
48
+ "panel",
49
+ "frame",
50
+ "window",
51
+ "dialogbox",
52
+ "choicebox",
53
+ "infobox",
54
+ "overlay",
55
+ "popup",
56
+ "vbox",
57
+ "hbox",
58
+ "grid",
59
+ "stack",
60
+ "stackpanel",
61
+ "canvas",
62
+ "border",
63
+ ],
64
+ },
65
+ {
66
+ group: "scroll",
67
+ label: "滚动",
68
+ types: ["scroll", "scrollviewer", "viewport"],
69
+ },
70
+ {
71
+ group: "progress",
72
+ label: "进度",
73
+ types: ["bar", "vbar", "progressbar", "slider", "checkbox"],
74
+ },
75
+ { group: "spacing", label: "间隔", types: ["separator", "spacer"] },
76
+ ];
77
+
78
+ export interface OpGroupEntries {
79
+ group: OpGroup;
80
+ label: string;
81
+ ops: { op: string; label: string }[];
82
+ }
83
+
84
+ /**
85
+ * op 按分组归类(时间线插入菜单与组件面板共用)。
86
+ * `OP_GROUP_ORDER` 是**显式顺序**(不依赖 `OP_META` 的书写次序);组内保持 `OP_META` 序。
87
+ * `surface`(可选)= op 合并面(缺省内建;扩展注册后由组合根传入)。
88
+ */
89
+ export function listOpGroups(
90
+ surface: OpSurface = BUILTIN_OP_SURFACE,
91
+ ): readonly OpGroupEntries[] {
92
+ const meta = listOps(surface);
93
+ return OP_GROUP_ORDER.map((group) => ({
94
+ group,
95
+ label: OP_GROUP_LABELS[group],
96
+ ops: meta
97
+ .filter((entry) => entry.group === group)
98
+ .map((entry) => ({ op: entry.op, label: entry.label })),
99
+ }));
100
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * 表单描述符派生:字段类型/必填/可选值/默认值取自 Zod schema(唯一事实源),
3
+ * 标签/呈现语义取自 catalog;两者经互锁测试锁定字段名集合一致。
4
+ */
5
+
6
+ import type { z } from "zod";
7
+ import type {
8
+ FieldDescriptor,
9
+ FieldKind,
10
+ OpFormDescriptor,
11
+ OpMeta,
12
+ } from "../contracts/index.ts";
13
+ import { FIELD_META } from "./catalog.ts";
14
+ import { elementLabel } from "./elementForms.ts";
15
+ import { BUILTIN_OP_SURFACE, type OpSurface } from "./surface.ts";
16
+
17
+ interface Unwrapped {
18
+ inner: z.ZodType;
19
+ required: boolean;
20
+ hasDefault: boolean;
21
+ defaultValue?: unknown;
22
+ }
23
+
24
+ function unwrap(schema: z.ZodType): Unwrapped {
25
+ let inner: z.ZodType = schema;
26
+ let required = true;
27
+ let hasDefault = false;
28
+ let defaultValue: unknown;
29
+ for (;;) {
30
+ const type = inner.def.type;
31
+ if (type === "optional") {
32
+ required = false;
33
+ inner = (inner.def as z.core.$ZodOptionalDef).innerType as z.ZodType;
34
+ continue;
35
+ }
36
+ if (type === "default") {
37
+ const def = inner.def as z.core.$ZodDefaultDef;
38
+ hasDefault = true;
39
+ defaultValue = def.defaultValue;
40
+ inner = def.innerType as z.ZodType;
41
+ continue;
42
+ }
43
+ return { inner, required, hasDefault, defaultValue };
44
+ }
45
+ }
46
+
47
+ function enumValuesOf(def: z.core.$ZodEnumDef): string[] {
48
+ const values = Array.isArray(def.entries)
49
+ ? def.entries
50
+ : Object.values(def.entries);
51
+ return values.map(String);
52
+ }
53
+
54
+ function describeField(
55
+ key: string,
56
+ schema: z.ZodType,
57
+ metaKey: string,
58
+ ): FieldDescriptor {
59
+ const { inner, required, hasDefault, defaultValue } = unwrap(schema);
60
+ const meta = FIELD_META[metaKey];
61
+ const base: FieldDescriptor = {
62
+ key,
63
+ label: meta?.label ?? key,
64
+ kind: meta?.kind ?? "string",
65
+ required,
66
+ hasDefault,
67
+ ...(hasDefault ? { defaultValue } : {}),
68
+ };
69
+ switch (inner.def.type) {
70
+ case "enum":
71
+ return {
72
+ ...base,
73
+ kind: "enum",
74
+ enumValues: enumValuesOf(inner.def as z.core.$ZodEnumDef),
75
+ };
76
+ case "tuple": {
77
+ const items = (inner.def as z.core.$ZodTupleDef).items;
78
+ return {
79
+ ...base,
80
+ kind: meta?.kind ?? "tuple",
81
+ tupleItems: items.map((item, index) =>
82
+ describeField(`${key}[${index}]`, item as z.ZodType, metaKey),
83
+ ),
84
+ };
85
+ }
86
+ case "array": {
87
+ const element = (inner.def as z.core.$ZodArrayDef).element as z.ZodType;
88
+ const itemObject =
89
+ element.def.type === "object"
90
+ ? describeField(`${key}[]`, element, `${metaKey}[]`)
91
+ : undefined;
92
+ return {
93
+ ...base,
94
+ kind: meta?.kind ?? "array",
95
+ ...(itemObject ? { item: itemObject } : {}),
96
+ };
97
+ }
98
+ case "object": {
99
+ const shape = (inner.def as z.core.$ZodObjectDef).shape;
100
+ return {
101
+ ...base,
102
+ kind: meta?.kind ?? "object",
103
+ properties: Object.entries(shape).map(([childKey, childSchema]) =>
104
+ describeField(
105
+ childKey,
106
+ childSchema as z.ZodType,
107
+ `${metaKey}.${childKey}`,
108
+ ),
109
+ ),
110
+ };
111
+ }
112
+ case "unknown":
113
+ return { ...base, kind: meta?.kind ?? "value" };
114
+ case "record":
115
+ return { ...base, kind: meta?.kind ?? "object" };
116
+ // 扩展 op 无 FIELD_META → 从 zod 派生兜底(meta 恒优先,内建 op 行为不变)
117
+ case "number":
118
+ return { ...base, kind: meta?.kind ?? "number" };
119
+ case "boolean":
120
+ return { ...base, kind: meta?.kind ?? "boolean" };
121
+ default:
122
+ return base;
123
+ }
124
+ }
125
+
126
+ /** 单 op 表单描述符;未知 op 返回 undefined(诊断层报 unknown-op)。
127
+ * `surface`(可选)= op 面(缺省内建;扩展注册后由组合根传合并面)。 */
128
+ export function describeForm(
129
+ op: string,
130
+ surface: OpSurface = BUILTIN_OP_SURFACE,
131
+ ): OpFormDescriptor | undefined {
132
+ const schema = surface.schemas[op];
133
+ const meta = surface.meta.find((entry) => entry.op === op);
134
+ if (schema === undefined || meta === undefined) return undefined;
135
+ const shape = (schema.def as z.core.$ZodObjectDef).shape;
136
+ return {
137
+ op,
138
+ label: meta.label,
139
+ group: meta.group,
140
+ fields: Object.entries(shape).map(([key, childSchema]) =>
141
+ describeField(key, childSchema as z.ZodType, `${op}.${key}`),
142
+ ),
143
+ };
144
+ }
145
+
146
+ /** op 目录(时间线插入菜单/节点图调色板用;`surface` = 扩展合并面,缺省内建) */
147
+ export function listOps(
148
+ surface: OpSurface = BUILTIN_OP_SURFACE,
149
+ ): readonly OpMeta[] {
150
+ return surface.meta;
151
+ }
152
+
153
+ /**
154
+ * 行标签(时间线/命令体列表共用,单一事实源):
155
+ * 命令取 op 标签;**元素取元素类型标签**(元素没有 `op`——按 op 取会让整个元素层显示「(坏命令)」);
156
+ * 两者都不是才是真正无法识别的节点。
157
+ */
158
+ export function describeNodeLabel(
159
+ node: Record<string, unknown> | undefined,
160
+ surface: OpSurface = BUILTIN_OP_SURFACE,
161
+ ): string {
162
+ const op = node?.op;
163
+ if (typeof op === "string") return describeForm(op, surface)?.label ?? op;
164
+ const type = node?.type;
165
+ if (typeof type === "string") return elementLabel(type);
166
+ return "(坏命令)";
167
+ }
168
+
169
+ /**
170
+ * 表单控件的原始文本 → 落树值(kind 语义,与 `describeField` 同域故放此处)。
171
+ *
172
+ * - `number`/`integer`:`Number`(NaN 由诊断层兜)
173
+ * - `boolean`:字面 `"true"`
174
+ * - `value`:智能字面量——`true`/`false`/纯数字还原为对应类型,其余保持字符串(`{expr}` 等)
175
+ * - 其余(string/identifier/resource/expression/text…):原样字符串
176
+ *
177
+ * **注意 `value` 的智能还原**:`x`/`width` 这类「数字或 CSS 长度」字段靠它把 `120`
178
+ * 还原为数字(运行时 `len()` 才会补 `px`;退化成字符串 `"120"` 会被 CSS 判为非法而静默丢弃)。
179
+ */
180
+ export function coerceFieldValue(kind: FieldKind, raw: string): unknown {
181
+ switch (kind) {
182
+ case "number":
183
+ case "integer":
184
+ return Number(raw);
185
+ case "boolean":
186
+ return raw === "true";
187
+ case "value":
188
+ if (raw === "true") return true;
189
+ if (raw === "false") return false;
190
+ if (raw === "null") return null; // 状态槽空值字面量(StateValue 值域)
191
+ if (/^-?\d+(\.\d+)?$/.test(raw)) return Number(raw);
192
+ return raw;
193
+ default:
194
+ return raw;
195
+ }
196
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * schema 域出口:op 全集定义(单一事实源)同时驱动属性面板表单派生与编辑期校验,
3
+ * 另含表单/元素描述符、op 分组清单、故事校验与命令体遍历器。
4
+ */
5
+ export {
6
+ OP_META,
7
+ FIELD_META,
8
+ fieldMetaOf,
9
+ OP_GROUP_ORDER,
10
+ OP_GROUP_LABELS,
11
+ } from "./catalog.ts";
12
+ export {
13
+ UNIMPLEMENTED_ELEMENT_ATTRS,
14
+ describeElement,
15
+ listElementTypes,
16
+ elementLabel,
17
+ specificAttrsOf,
18
+ type ElementFormDescriptor,
19
+ } from "./elementForms.ts";
20
+ export {
21
+ ELEMENT_TYPE_GROUPS,
22
+ listOpGroups,
23
+ type ElementTypeGroup,
24
+ type OpGroupEntries,
25
+ } from "./elementPalette.ts";
26
+ export {
27
+ describeForm,
28
+ listOps,
29
+ describeNodeLabel,
30
+ coerceFieldValue,
31
+ } from "./forms.ts";
32
+ export {
33
+ OP_SCHEMAS,
34
+ validateCommand,
35
+ type ScriptOpName,
36
+ type CommandOf,
37
+ type ScriptCommand,
38
+ type ScriptValue,
39
+ type StateScriptValue,
40
+ } from "./opSchemas/index.ts";
41
+ export {
42
+ BUILTIN_OP_SURFACE,
43
+ mergeOpSchemas,
44
+ mergeOpMeta,
45
+ mergeOpSurface,
46
+ type OpSurface,
47
+ } from "./surface.ts";
48
+ export { columnSchema, storySchema, validateStory } from "./validation.ts";
49
+ export {
50
+ walkCommandBodies,
51
+ walkStoryCommands,
52
+ walkStoryElements,
53
+ type CommandVisitor,
54
+ type ElementVisitor,
55
+ } from "./walk.ts";