@things-factory/figure-ui 10.1.68 → 10.1.69
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.
- package/client/graphql/index.ts +16 -0
- package/client/modeller/figure-v3-links.ts +73 -7
- package/client/modeller/figure-v3-parts.ts +153 -2
- package/client/modeller/v3-editor.ts +133 -5
- package/client/modeller/v3-gizmo.ts +18 -2
- package/client/modeller/v3-links.ts +71 -2
- package/client/modeller/v3-palette.ts +2 -1
- package/client/modeller/v3-product-session.ts +8 -0
- package/client/modeller/v3-recipes.ts +51 -0
- package/client/pages/figure-v3-modeller-page.ts +25 -2
- package/dist-client/graphql/index.d.ts +5 -0
- package/dist-client/graphql/index.js +15 -0
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-v3-capability.d.ts +1 -1
- package/dist-client/modeller/figure-v3-clips.d.ts +1 -1
- package/dist-client/modeller/figure-v3-controls.d.ts +1 -1
- package/dist-client/modeller/figure-v3-links.d.ts +11 -1
- package/dist-client/modeller/figure-v3-links.js +69 -8
- package/dist-client/modeller/figure-v3-links.js.map +1 -1
- package/dist-client/modeller/figure-v3-material.d.ts +1 -1
- package/dist-client/modeller/figure-v3-parts.d.ts +15 -1
- package/dist-client/modeller/figure-v3-parts.js +166 -1
- package/dist-client/modeller/figure-v3-parts.js.map +1 -1
- package/dist-client/modeller/figure-v3-properties.d.ts +1 -1
- package/dist-client/modeller/v3-clips.d.ts +1 -1
- package/dist-client/modeller/v3-editor.d.ts +26 -0
- package/dist-client/modeller/v3-editor.js +147 -4
- package/dist-client/modeller/v3-editor.js.map +1 -1
- package/dist-client/modeller/v3-gizmo.d.ts +6 -0
- package/dist-client/modeller/v3-gizmo.js +20 -2
- package/dist-client/modeller/v3-gizmo.js.map +1 -1
- package/dist-client/modeller/v3-links.d.ts +27 -0
- package/dist-client/modeller/v3-links.js +68 -2
- package/dist-client/modeller/v3-links.js.map +1 -1
- package/dist-client/modeller/v3-palette.js +2 -1
- package/dist-client/modeller/v3-palette.js.map +1 -1
- package/dist-client/modeller/v3-product-session.d.ts +34 -0
- package/dist-client/modeller/v3-product-session.js +8 -0
- package/dist-client/modeller/v3-product-session.js.map +1 -1
- package/dist-client/modeller/v3-recipes.d.ts +16 -0
- package/dist-client/modeller/v3-recipes.js +51 -0
- package/dist-client/modeller/v3-recipes.js.map +1 -0
- package/dist-client/pages/figure-v3-modeller-page.d.ts +4 -0
- package/dist-client/pages/figure-v3-modeller-page.js +29 -2
- package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.fui.tsbuildinfo +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/test/browser/modeller-page.js +15 -1
- package/test/browser/modeller.check.mjs +217 -2
- package/test/modeller-route.test.ts +9 -0
- package/test/v3-gizmo.test.ts +44 -0
- package/test/v3-links.test.ts +91 -1
- package/test/v3-recipes.test.ts +44 -0
- package/translations/en.json +18 -8
- package/translations/ja.json +18 -8
- package/translations/ko.json +18 -8
- package/translations/ms.json +18 -8
- package/translations/zh.json +18 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v3-product-session.js","sourceRoot":"","sources":["../../client/modeller/v3-product-session.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createV3Asset,\n createV3EditorWorkspace,\n serializeV3Asset,\n parseV3Asset,\n type V3InputChange,\n type V3PrimitiveCreation,\n type V3PartEdit,\n type V3AuthoringAction,\n type V3ProposalStep\n} from '@hatiolab/figure-model'\n\nexport interface V3ProductRecord {\n id: string\n source: string\n}\n\n/** Persistence is injected; neither the model engine nor the renderer owns the product ID. */\nexport interface V3ProductStore {\n load(id: string): Promise<V3ProductRecord>\n save(request: {\n id?: string\n source: string\n expectedSource?: string\n creationId?: string\n /** The card's picture. Absent when the browser could not draw one; the source is saved either way. */\n thumbnail?: string\n /** The type name a new figure is created under (the first save asks for it). Not sent for an existing one. */\n type?: string\n }): Promise<V3ProductRecord>\n}\n\n/** Product lifecycle boundary. A late response can never change another editor's asset or ID. */\nexport function createV3ProductSession(store: V3ProductStore) {\n const workspace = createV3EditorWorkspace()\n let epoch = 0,\n id: string | undefined,\n baseline: string | undefined\n let loading = false,\n saving = false,\n error: string | undefined\n let creationId: string | undefined\n function read() {\n return { ...workspace.read(), id, loading, saving, error }\n }\n function writable() {\n const view = workspace.read()\n if (loading || error || !view.context || view.revision === null) throw Error('먼저 모델을 정상적으로 열어 주세요.')\n // Serializing writes also makes create retries safe: no second create while the first is pending.\n if (saving) throw Error('저장이 끝난 뒤 편집해 주세요.')\n return { context: view.context, expectedRevision: view.revision, origin: 'ui' as const }\n }\n function clear() {\n workspace.close({ discardDesignChanges: true })\n id = undefined\n baseline = undefined\n error = undefined\n saving = false\n creationId = undefined\n }\n function canLeave(discard: boolean) {\n if (saving) throw Error('저장이 끝난 뒤 모델을 전환해 주세요.')\n if (workspace.read().dirty && !discard) throw Error('저장하지 않은 변경이 있습니다.')\n }\n return {\n read,\n startNew({ discard = false } = {}) {\n canLeave(discard)\n ++epoch\n clear()\n loading = false\n workspace.open(serializeV3Asset(createV3Asset('새 Figure')))\n creationId = crypto.randomUUID()\n return read()\n },\n async open(nextId: string, { discard = false } = {}) {\n canLeave(discard)\n const requestEpoch = ++epoch\n clear()\n loading = true\n try {\n const record = await store.load(nextId)\n if (requestEpoch !== epoch) return read()\n if (record.id !== nextId) throw Error('요청한 Figure와 응답의 대상이 다릅니다.')\n workspace.open(record.source)\n id = nextId\n baseline = record.source\n } catch (cause) {\n if (requestEpoch === epoch) error = cause instanceof Error ? cause.message : String(cause)\n } finally {\n if (requestEpoch === epoch) loading = false\n }\n return read()\n },\n close({ discard = false } = {}) {\n canLeave(discard)\n ++epoch\n clear()\n loading = false\n return read()\n },\n apply(changes: V3InputChange[], origin: 'ui' | 'ai' = 'ui', expectedRevision?: number, context?: string) {\n const control = writable()\n // AI must supply the context AND revision captured when it started. Never substitute current values.\n if (origin === 'ai' && (context === undefined || expectedRevision === undefined))\n throw Error('AI 편집에는 요청 당시의 모델 문맥과 리비전이 필요합니다.')\n return workspace.apply({\n ...control,\n origin,\n context: context ?? control.context,\n expectedRevision: expectedRevision ?? control.expectedRevision,\n changes\n })\n },\n /**\n * An AI proposal (ADR-0094): the modeller's commands, as one step of the history, on the version the model saw.\n * Never on the current one instead -- a person's edit in between makes the proposal stale, and it is refused.\n */\n applyProposal(steps: readonly V3ProposalStep[], expectedRevision: number, context: string) {\n writable()\n return workspace.applyProposal({ origin: 'ai', context, expectedRevision, steps })\n },\n add(part: V3PrimitiveCreation) {\n return workspace.addPrimitive({ ...writable(), part })\n },\n edit(edit: V3PartEdit) {\n return workspace.editPart({ ...writable(), edit })\n },\n /** One authoring command — what a person means, not a graph edit — as one step of the same history. */\n command(action: V3AuthoringAction) {\n return workspace.command({ ...writable(), action })\n },\n undo() {\n return workspace.undo(writable())\n },\n redo() {\n return workspace.redo(writable())\n },\n source() {\n const { context } = writable()\n return workspace.export({ context }).text\n },\n /**\n * Saves the model, and the card's picture when the caller managed to take one.\n *\n * The picture is the caller's because taking it needs a canvas, and this session is the place that knows\n * nothing about drawing. A save with no picture is a save: a tab in the background cannot draw, and the\n * source matters more than the card (see `v3-thumbnail.ts`).\n */\n async save({ thumbnail, type }: { thumbnail?: string; type?: string } = {}) {\n const control = writable(),\n requestEpoch = epoch\n const exported = workspace.export(control)\n saving = true\n try {\n const record = await store.save({ id, source: exported.text, expectedSource: baseline, creationId, thumbnail, ...(id || !type ? {} : { type }) })\n if (requestEpoch !== epoch) return read()\n if (!record.id || (id !== undefined && record.id !== id)) throw Error('저장 응답의 Figure 대상이 다릅니다.')\n if (serializeV3Asset(parseV3Asset(record.source)) !== exported.text)\n throw Error('저장 응답이 보낸 모델과 다릅니다. 저장 상태를 다시 확인해 주세요.')\n workspace.acknowledgeSaved(exported)\n id = record.id\n baseline = record.source\n } finally {\n if (requestEpoch === epoch) saving = false\n }\n return read()\n }\n }\n}\nexport type V3ProductSession = ReturnType<typeof createV3ProductSession>\n"]}
|
|
1
|
+
{"version":3,"file":"v3-product-session.js","sourceRoot":"","sources":["../../client/modeller/v3-product-session.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createV3Asset,\n createV3EditorWorkspace,\n serializeV3Asset,\n parseV3Asset,\n type V3InputChange,\n type V3PrimitiveCreation,\n type V3PartEdit,\n type V3AuthoringAction,\n type V3ProposalStep\n} from '@hatiolab/figure-model'\n\nexport interface V3ProductRecord {\n id: string\n source: string\n}\n\n/** Persistence is injected; neither the model engine nor the renderer owns the product ID. */\nexport interface V3ProductStore {\n load(id: string): Promise<V3ProductRecord>\n save(request: {\n id?: string\n source: string\n expectedSource?: string\n creationId?: string\n /** The card's picture. Absent when the browser could not draw one; the source is saved either way. */\n thumbnail?: string\n /** The type name a new figure is created under (the first save asks for it). Not sent for an existing one. */\n type?: string\n }): Promise<V3ProductRecord>\n}\n\n/** Product lifecycle boundary. A late response can never change another editor's asset or ID. */\nexport function createV3ProductSession(store: V3ProductStore) {\n const workspace = createV3EditorWorkspace()\n let epoch = 0,\n id: string | undefined,\n baseline: string | undefined\n let loading = false,\n saving = false,\n error: string | undefined\n let creationId: string | undefined\n function read() {\n return { ...workspace.read(), id, loading, saving, error }\n }\n function writable() {\n const view = workspace.read()\n if (loading || error || !view.context || view.revision === null) throw Error('먼저 모델을 정상적으로 열어 주세요.')\n // Serializing writes also makes create retries safe: no second create while the first is pending.\n if (saving) throw Error('저장이 끝난 뒤 편집해 주세요.')\n return { context: view.context, expectedRevision: view.revision, origin: 'ui' as const }\n }\n function clear() {\n workspace.close({ discardDesignChanges: true })\n id = undefined\n baseline = undefined\n error = undefined\n saving = false\n creationId = undefined\n }\n function canLeave(discard: boolean) {\n if (saving) throw Error('저장이 끝난 뒤 모델을 전환해 주세요.')\n if (workspace.read().dirty && !discard) throw Error('저장하지 않은 변경이 있습니다.')\n }\n return {\n read,\n startNew({ discard = false } = {}) {\n canLeave(discard)\n ++epoch\n clear()\n loading = false\n workspace.open(serializeV3Asset(createV3Asset('새 Figure')))\n creationId = crypto.randomUUID()\n return read()\n },\n async open(nextId: string, { discard = false } = {}) {\n canLeave(discard)\n const requestEpoch = ++epoch\n clear()\n loading = true\n try {\n const record = await store.load(nextId)\n if (requestEpoch !== epoch) return read()\n if (record.id !== nextId) throw Error('요청한 Figure와 응답의 대상이 다릅니다.')\n workspace.open(record.source)\n id = nextId\n baseline = record.source\n } catch (cause) {\n if (requestEpoch === epoch) error = cause instanceof Error ? cause.message : String(cause)\n } finally {\n if (requestEpoch === epoch) loading = false\n }\n return read()\n },\n close({ discard = false } = {}) {\n canLeave(discard)\n ++epoch\n clear()\n loading = false\n return read()\n },\n apply(changes: V3InputChange[], origin: 'ui' | 'ai' = 'ui', expectedRevision?: number, context?: string) {\n const control = writable()\n // AI must supply the context AND revision captured when it started. Never substitute current values.\n if (origin === 'ai' && (context === undefined || expectedRevision === undefined))\n throw Error('AI 편집에는 요청 당시의 모델 문맥과 리비전이 필요합니다.')\n return workspace.apply({\n ...control,\n origin,\n context: context ?? control.context,\n expectedRevision: expectedRevision ?? control.expectedRevision,\n changes\n })\n },\n /**\n * An AI proposal (ADR-0094): the modeller's commands, as one step of the history, on the version the model saw.\n * Never on the current one instead -- a person's edit in between makes the proposal stale, and it is refused.\n */\n applyProposal(steps: readonly V3ProposalStep[], expectedRevision: number, context: string) {\n writable()\n return workspace.applyProposal({ origin: 'ai', context, expectedRevision, steps })\n },\n /**\n * A recipe put in by the person (ADR-0095): its resolved commands, as one step of the history, on the version open\n * now. The same path as a proposal; the person is its origin.\n */\n applyRecipe(steps: readonly V3ProposalStep[]) {\n const at = writable()\n return workspace.applyProposal({ origin: 'ui', context: at.context, expectedRevision: at.expectedRevision, steps })\n },\n add(part: V3PrimitiveCreation) {\n return workspace.addPrimitive({ ...writable(), part })\n },\n edit(edit: V3PartEdit) {\n return workspace.editPart({ ...writable(), edit })\n },\n /** One authoring command — what a person means, not a graph edit — as one step of the same history. */\n command(action: V3AuthoringAction) {\n return workspace.command({ ...writable(), action })\n },\n undo() {\n return workspace.undo(writable())\n },\n redo() {\n return workspace.redo(writable())\n },\n source() {\n const { context } = writable()\n return workspace.export({ context }).text\n },\n /**\n * Saves the model, and the card's picture when the caller managed to take one.\n *\n * The picture is the caller's because taking it needs a canvas, and this session is the place that knows\n * nothing about drawing. A save with no picture is a save: a tab in the background cannot draw, and the\n * source matters more than the card (see `v3-thumbnail.ts`).\n */\n async save({ thumbnail, type }: { thumbnail?: string; type?: string } = {}) {\n const control = writable(),\n requestEpoch = epoch\n const exported = workspace.export(control)\n saving = true\n try {\n const record = await store.save({ id, source: exported.text, expectedSource: baseline, creationId, thumbnail, ...(id || !type ? {} : { type }) })\n if (requestEpoch !== epoch) return read()\n if (!record.id || (id !== undefined && record.id !== id)) throw Error('저장 응답의 Figure 대상이 다릅니다.')\n if (serializeV3Asset(parseV3Asset(record.source)) !== exported.text)\n throw Error('저장 응답이 보낸 모델과 다릅니다. 저장 상태를 다시 확인해 주세요.')\n workspace.acknowledgeSaved(exported)\n id = record.id\n baseline = record.source\n } finally {\n if (requestEpoch === epoch) saving = false\n }\n return read()\n }\n }\n}\nexport type V3ProductSession = ReturnType<typeof createV3ProductSession>\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { V3Recipe, V3RecipeParam, V3RecipeValue } from '@hatiolab/figure-model';
|
|
2
|
+
/** A recipe's words in the language on screen: that language, else English, else the first it has. */
|
|
3
|
+
export declare function recipeWord(words: Record<string, string> | undefined, language: string): string;
|
|
4
|
+
/** The recipes as the server gave them, parsed; a row that is not a recipe is left out rather than breaking the list. */
|
|
5
|
+
export declare function recipesOf(rows: ReadonlyArray<{
|
|
6
|
+
source: string;
|
|
7
|
+
}>): V3Recipe[];
|
|
8
|
+
/** Whether a recipe can be put in now: one for a part needs a part chosen. */
|
|
9
|
+
export declare function recipeReady(recipe: Pick<V3Recipe, 'target'>, selected: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* A field's value as the recipe takes it. A ratio is shown and typed as a percentage (35 for 0.35), since that is how a
|
|
12
|
+
* person says a share; the rest are taken as they are. A number field left empty or not a number keeps the default.
|
|
13
|
+
*/
|
|
14
|
+
export declare function recipeValueOf(param: V3RecipeParam, typed: string | boolean): V3RecipeValue;
|
|
15
|
+
/** What a field shows for a value: a ratio as a percentage. */
|
|
16
|
+
export declare function recipeFieldOf(param: V3RecipeParam, value: V3RecipeValue): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The recipe section's rules (ADR-0095), kept apart from the element so they can be tested without a browser.
|
|
3
|
+
*/
|
|
4
|
+
/** A recipe's words in the language on screen: that language, else English, else the first it has. */
|
|
5
|
+
export function recipeWord(words, language) {
|
|
6
|
+
if (!words)
|
|
7
|
+
return '';
|
|
8
|
+
return words[language] ?? words[language.split('-')[0]] ?? words.en ?? Object.values(words)[0] ?? '';
|
|
9
|
+
}
|
|
10
|
+
/** The recipes as the server gave them, parsed; a row that is not a recipe is left out rather than breaking the list. */
|
|
11
|
+
export function recipesOf(rows) {
|
|
12
|
+
const out = [];
|
|
13
|
+
for (const row of rows) {
|
|
14
|
+
try {
|
|
15
|
+
const recipe = JSON.parse(row.source);
|
|
16
|
+
if (recipe && typeof recipe.name === 'string' && Array.isArray(recipe.steps))
|
|
17
|
+
out.push(recipe);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
/* not a recipe */
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
/** Whether a recipe can be put in now: one for a part needs a part chosen. */
|
|
26
|
+
export function recipeReady(recipe, selected) {
|
|
27
|
+
return recipe.target === 'figure' || !!selected;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A field's value as the recipe takes it. A ratio is shown and typed as a percentage (35 for 0.35), since that is how a
|
|
31
|
+
* person says a share; the rest are taken as they are. A number field left empty or not a number keeps the default.
|
|
32
|
+
*/
|
|
33
|
+
export function recipeValueOf(param, typed) {
|
|
34
|
+
if (param.kind === 'flag')
|
|
35
|
+
return typed === true || typed === 'true';
|
|
36
|
+
if (param.kind === 'ratio' || param.kind === 'mm') {
|
|
37
|
+
const n = typeof typed === 'string' && typed.trim() !== '' ? Number(typed) : NaN;
|
|
38
|
+
if (!Number.isFinite(n))
|
|
39
|
+
return param.default;
|
|
40
|
+
return param.kind === 'ratio' ? Math.round(n * 1e4) / 1e6 : n;
|
|
41
|
+
}
|
|
42
|
+
const option = (param.options ?? []).find(o => String(o) === String(typed));
|
|
43
|
+
return option ?? param.default;
|
|
44
|
+
}
|
|
45
|
+
/** What a field shows for a value: a ratio as a percentage. */
|
|
46
|
+
export function recipeFieldOf(param, value) {
|
|
47
|
+
if (param.kind === 'ratio' && typeof value === 'number')
|
|
48
|
+
return String(Math.round(value * 1e6) / 1e4);
|
|
49
|
+
return String(value);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=v3-recipes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3-recipes.js","sourceRoot":"","sources":["../../client/modeller/v3-recipes.ts"],"names":[],"mappings":"AAEA;;GAEG;AAEH,sGAAsG;AACtG,MAAM,UAAU,UAAU,CAAC,KAAyC,EAAE,QAAgB;IACpF,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,KAAK,CAAC,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AACvG,CAAC;AAED,yHAAyH;AACzH,MAAM,UAAU,SAAS,CAAC,IAAuC;IAC/D,MAAM,GAAG,GAAe,EAAE,CAAA;IAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YACrC,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAChG,CAAC;QAAC,MAAM,CAAC;YACP,kBAAkB;QACpB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,MAAgC,EAAE,QAAgB;IAC5E,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAA;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAoB,EAAE,KAAuB;IACzE,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,CAAA;IACpE,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAClD,MAAM,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;QAChF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC,OAAO,CAAA;QAC7C,OAAO,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IAC/D,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IAC3E,OAAO,MAAM,IAAI,KAAK,CAAC,OAAO,CAAA;AAChC,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,aAAa,CAAC,KAAoB,EAAE,KAAoB;IACtE,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAA;IACrG,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;AACtB,CAAC","sourcesContent":["import type { V3Recipe, V3RecipeParam, V3RecipeValue } from '@hatiolab/figure-model'\n\n/*\n * The recipe section's rules (ADR-0095), kept apart from the element so they can be tested without a browser.\n */\n\n/** A recipe's words in the language on screen: that language, else English, else the first it has. */\nexport function recipeWord(words: Record<string, string> | undefined, language: string): string {\n if (!words) return ''\n return words[language] ?? words[language.split('-')[0]!] ?? words.en ?? Object.values(words)[0] ?? ''\n}\n\n/** The recipes as the server gave them, parsed; a row that is not a recipe is left out rather than breaking the list. */\nexport function recipesOf(rows: ReadonlyArray<{ source: string }>): V3Recipe[] {\n const out: V3Recipe[] = []\n for (const row of rows) {\n try {\n const recipe = JSON.parse(row.source)\n if (recipe && typeof recipe.name === 'string' && Array.isArray(recipe.steps)) out.push(recipe)\n } catch {\n /* not a recipe */\n }\n }\n return out\n}\n\n/** Whether a recipe can be put in now: one for a part needs a part chosen. */\nexport function recipeReady(recipe: Pick<V3Recipe, 'target'>, selected: string): boolean {\n return recipe.target === 'figure' || !!selected\n}\n\n/**\n * A field's value as the recipe takes it. A ratio is shown and typed as a percentage (35 for 0.35), since that is how a\n * person says a share; the rest are taken as they are. A number field left empty or not a number keeps the default.\n */\nexport function recipeValueOf(param: V3RecipeParam, typed: string | boolean): V3RecipeValue {\n if (param.kind === 'flag') return typed === true || typed === 'true'\n if (param.kind === 'ratio' || param.kind === 'mm') {\n const n = typeof typed === 'string' && typed.trim() !== '' ? Number(typed) : NaN\n if (!Number.isFinite(n)) return param.default\n return param.kind === 'ratio' ? Math.round(n * 1e4) / 1e6 : n\n }\n const option = (param.options ?? []).find(o => String(o) === String(typed))\n return option ?? param.default\n}\n\n/** What a field shows for a value: a ratio as a percentage. */\nexport function recipeFieldOf(param: V3RecipeParam, value: V3RecipeValue): string {\n if (param.kind === 'ratio' && typeof value === 'number') return String(Math.round(value * 1e6) / 1e4)\n return String(value)\n}\n"]}
|
|
@@ -17,6 +17,9 @@ export declare class FigureV3ModellerPage extends PageView {
|
|
|
17
17
|
private routeId;
|
|
18
18
|
private discardApproved;
|
|
19
19
|
private revision;
|
|
20
|
+
/** The recipes this domain sees (ADR-0095), read once when the page is first shown. */
|
|
21
|
+
private recipes;
|
|
22
|
+
private recipesRead;
|
|
20
23
|
private error;
|
|
21
24
|
/**
|
|
22
25
|
* 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.
|
|
@@ -77,6 +80,7 @@ export declare class FigureV3ModellerPage extends PageView {
|
|
|
77
80
|
* page on a saved figure leaves a new figure's proposal untaken, and the handler opens a new figure for it.
|
|
78
81
|
*/
|
|
79
82
|
private applyAI;
|
|
83
|
+
private holdUnsaved;
|
|
80
84
|
connectedCallback(): void;
|
|
81
85
|
disconnectedCallback(): void;
|
|
82
86
|
canDeactivate(): Promise<boolean>;
|
|
@@ -6,7 +6,8 @@ import { modellerPathOf } from '../modeller/modeller-route.js';
|
|
|
6
6
|
import { createV3ProductSession } from '../modeller/v3-product-session.js';
|
|
7
7
|
import { v3ProductStore } from '../graphql/v3-store.js';
|
|
8
8
|
import { i18next } from '@operato/i18n';
|
|
9
|
-
import { fetchFigure, fetchFigureTypeNames, updateFigure } from '../graphql/index.js';
|
|
9
|
+
import { fetchFigure, fetchFigureRecipes, fetchFigureTypeNames, updateFigure } from '../graphql/index.js';
|
|
10
|
+
import { recipesOf } from '../modeller/v3-recipes.js';
|
|
10
11
|
import { occupancyFromParts } from '../modeller/v3-occupancy.js';
|
|
11
12
|
import '../modeller/v3-editor.js';
|
|
12
13
|
import '@material/web/icon/icon.js';
|
|
@@ -32,6 +33,9 @@ let FigureV3ModellerPage = class FigureV3ModellerPage extends PageView {
|
|
|
32
33
|
this.session = createV3ProductSession(v3ProductStore);
|
|
33
34
|
this.discardApproved = false;
|
|
34
35
|
this.revision = 0;
|
|
36
|
+
/** The recipes this domain sees (ADR-0095), read once when the page is first shown. */
|
|
37
|
+
this.recipes = [];
|
|
38
|
+
this.recipesRead = false;
|
|
35
39
|
this.error = '';
|
|
36
40
|
/**
|
|
37
41
|
* 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.
|
|
@@ -222,6 +226,16 @@ let FigureV3ModellerPage = class FigureV3ModellerPage extends PageView {
|
|
|
222
226
|
detail.error = error instanceof Error ? error.message : String(error);
|
|
223
227
|
}
|
|
224
228
|
};
|
|
229
|
+
/*
|
|
230
|
+
Reloading or closing the tab is not a move the shell asks `canDeactivate` about: a new figure not yet saved was lost
|
|
231
|
+
without a word (2026-09-26). The browser's own question is the only one it allows there.
|
|
232
|
+
*/
|
|
233
|
+
this.holdUnsaved = (event) => {
|
|
234
|
+
if (!this.active || !this.session.read().dirty)
|
|
235
|
+
return;
|
|
236
|
+
event.preventDefault();
|
|
237
|
+
event.returnValue = '';
|
|
238
|
+
};
|
|
225
239
|
/** The first save's question, over the names this domain already uses. */
|
|
226
240
|
this.askFirstSave = async (name) => {
|
|
227
241
|
const taken = await fetchFigureTypeNames().catch(() => []);
|
|
@@ -342,10 +356,12 @@ let FigureV3ModellerPage = class FigureV3ModellerPage extends PageView {
|
|
|
342
356
|
connectedCallback() {
|
|
343
357
|
super.connectedCallback();
|
|
344
358
|
window.addEventListener('figure-v3-apply', this.applyAI);
|
|
359
|
+
window.addEventListener('beforeunload', this.holdUnsaved);
|
|
345
360
|
}
|
|
346
361
|
disconnectedCallback() {
|
|
347
362
|
this.withdrawAssistant();
|
|
348
363
|
window.removeEventListener('figure-v3-apply', this.applyAI);
|
|
364
|
+
window.removeEventListener('beforeunload', this.holdUnsaved);
|
|
349
365
|
super.disconnectedCallback();
|
|
350
366
|
}
|
|
351
367
|
async canDeactivate() {
|
|
@@ -374,6 +390,13 @@ let FigureV3ModellerPage = class FigureV3ModellerPage extends PageView {
|
|
|
374
390
|
this.withdrawAssistant();
|
|
375
391
|
return;
|
|
376
392
|
}
|
|
393
|
+
if (!this.recipesRead) {
|
|
394
|
+
this.recipesRead = true;
|
|
395
|
+
/* A recipe list that cannot be read leaves the section out; the modeller works without it. */
|
|
396
|
+
fetchFigureRecipes()
|
|
397
|
+
.then(rows => (this.recipes = recipesOf(rows)))
|
|
398
|
+
.catch(() => (this.recipesRead = false));
|
|
399
|
+
}
|
|
377
400
|
const id = lifecycle?.resourceId;
|
|
378
401
|
if (this.session.read().context && id === this.routeId && !changes.active)
|
|
379
402
|
return;
|
|
@@ -435,7 +458,7 @@ let FigureV3ModellerPage = class FigureV3ModellerPage extends PageView {
|
|
|
435
458
|
if (this.error)
|
|
436
459
|
return html `<p role="alert" class="error">${this.error}</p>`;
|
|
437
460
|
const view = this.session.read();
|
|
438
|
-
return html `<figure-v3-editor .session=${this.session} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} @v3-changed=${this.changed}>
|
|
461
|
+
return html `<figure-v3-editor .session=${this.session} .recipes=${this.recipes} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} @v3-changed=${this.changed}>
|
|
439
462
|
${this.commandError ? html `<p slot="notice" fit-error role="alert">${this.commandError}</p>` : ''}
|
|
440
463
|
<figure-v3-properties
|
|
441
464
|
slot="figure"
|
|
@@ -487,6 +510,10 @@ __decorate([
|
|
|
487
510
|
state(),
|
|
488
511
|
__metadata("design:type", Object)
|
|
489
512
|
], FigureV3ModellerPage.prototype, "revision", void 0);
|
|
513
|
+
__decorate([
|
|
514
|
+
state(),
|
|
515
|
+
__metadata("design:type", Array)
|
|
516
|
+
], FigureV3ModellerPage.prototype, "recipes", void 0);
|
|
490
517
|
__decorate([
|
|
491
518
|
state(),
|
|
492
519
|
__metadata("design:type", Object)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-v3-modeller-page.js","sourceRoot":"","sources":["../../client/pages/figure-v3-modeller-page.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { navigate, PageView } from '@operato/shell'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { createV3ProductSession } from '../modeller/v3-product-session.js'\nimport { v3ProductStore } from '../graphql/v3-store.js'\nimport { i18next } from '@operato/i18n'\nimport { fetchFigure, fetchFigureTypeNames, updateFigure } from '../graphql/index.js'\nimport { occupancyFromParts } from '../modeller/v3-occupancy.js'\nimport type { V3AuthoringAction } from '@hatiolab/figure-model'\nimport '../modeller/v3-editor.js'\nimport '@material/web/icon/icon.js'\nimport '../modeller/figure-history-panel.js'\nimport '../modeller/figure-v3-verdict.js'\nimport '../modeller/figure-v3-properties.js'\nimport type { V3BudgetLine } from '../modeller/v3-maturity.js'\nimport { OxPrompt } from '@operato/popup'\nimport { openPopup } from '@operato/layout'\nimport '../modeller/figure-v3-first-save.js'\nimport { activePalette } from '@hatiolab/things-scene'\nimport {\n clearAssistantSurface,\n notifyAssistantSurface,\n provideAssistantSurface\n} from '@things-factory/ai-assistant/dist-client/index.js'\nimport type { FirstSave } from '../modeller/v3-first-save.js'\nimport { placementRefusalOf } from '../modeller/v3-refusals.js'\nimport { takePendingNewFigure } from '../modeller/v3-ai-proposal.js'\n\n/**\n * The V3 product page has exactly one authoritative workspace for UI and AI input transactions.\n *\n * The one modeller since ADR-0087 step 5 (2026-09-24): it answers to `figure-modeller`, and to `figure-v3-modeller`\n * as an alias for links made before the switch.\n */\n@customElement('figure-v3-modeller-page')\nexport class FigureV3ModellerPage extends PageView {\n static styles = css`\n :host {\n display: flex;\n flex-direction: row;\n height: 100%;\n min-height: 0;\n /*\n The page must fit the width it is given. Without this the shell sizes it to its content — the editor's\n 660 plus the 320 release panel — and the panel runs off the right edge (seen at 945 px, 2026-09-24).\n */\n overflow: hidden;\n }\n figure-v3-editor {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n /* The first field of the properties tab, in the side panel's look (side-panel-styles): a label above the field. */\n label[type-field] {\n display: grid;\n gap: 2px;\n margin: 0 0 5px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n label[type-field] input,\n label[type-field] code {\n box-sizing: border-box;\n min-height: 30px;\n padding: 4px 7px;\n font-family: var(--mono-font, monospace);\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n label[type-field] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n p[fit-error] {\n margin: 0;\n padding: 8px 12px;\n font-size: 12px;\n color: var(--md-sys-color-error);\n }\n label[type-field] small[role='alert'] {\n color: var(--md-sys-color-error);\n }\n .error {\n padding: 16px;\n color: var(--md-sys-color-error);\n }\n `\n private readonly session = createV3ProductSession(v3ProductStore)\n private routeId: string | undefined\n private discardApproved = false\n @state() private revision = 0\n @state() private error = ''\n /**\n * 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.\n *\n * 이 화면에는 발행 패널이 없었다. 저장은 되고 발행 · 판본 · 복원 · 판정을 볼 자리가 없어서, ADR-0087 5단계에서\n * `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 V3 도형은 공간을 선언하기 전에는 전부\n * 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 그래서 발행 패널을 건다.\n */\n @state() private row: { state?: 'draft' | 'released'; version: number; type?: string; category?: string; tags?: string[] } = {\n version: 0\n }\n @state() private typeError = ''\n /** Why a command from the side did nothing. Shown beside the tabs — the editor stays, since nothing in it was lost. */\n @state() private commandError = ''\n /** The verdict's count, handed to the editor's verdict tab. */\n @state() private instances?: number\n @state() private budget?: V3BudgetLine\n /**\n * The source the verdict judges. The session refuses to export while it loads or saves, so the last one read is\n * kept rather than blanked — the verdict would otherwise flicker to 「waiting」 on every save.\n */\n private draft?: string\n private draftSource(): string | undefined {\n try {\n this.draft = this.session.source()\n } catch {\n /* loading or saving — keep the last source */\n }\n return this.draft\n }\n private wasDirty = false\n private titled?: string | null\n /** The figure the AI dock was last told about - another id is another conversation. */\n private assistantFor?: string\n /*\n The figure on screen is what the AI dock talks about (design/ai/shared-dock.md §3). A figure not saved yet has no\n row to hang a conversation on, so it provides nothing and the dock stays on the application's conversation.\n The draft is read at the moment of sending, never cached - the dock must not answer about a shape already changed.\n */\n private provideAssistant = () => {\n const view = this.session.read()\n if (!this.active || view.loading || view.error || !view.context) return\n if (!view.id) {\n this.withdrawAssistant()\n return\n }\n if (this.assistantFor === view.id) {\n notifyAssistantSurface(this)\n return\n }\n const id = view.id\n this.assistantFor = id\n provideAssistantSurface(this, {\n key: `figure:${id}`,\n subject: 'figure',\n icon: 'category',\n label: () => this.session.read().name || id,\n detail: () => (this.session.read().dirty ? i18next.t('figure.text.unsaved-changes') : ''),\n anchor: () => ({ anchorType: 'figure', anchorId: id }),\n hostContext: () => ({\n surface: 'figure-modeller',\n editorMode: 'existing',\n figureId: id,\n draftSource: JSON.parse(this.session.source()),\n /* The version the model sees; a proposal is applied on it, or refused if the figure moved on (ADR-0094). */\n context: this.session.read().context,\n revision: this.session.read().revision,\n palette: Object.keys(activePalette())\n })\n })\n }\n private withdrawAssistant() {\n this.assistantFor = undefined\n clearAssistantSurface(this)\n }\n private changed = () => {\n this.revision++\n /* Any edit after a failed command makes the message stale — the author has done something about it. */\n this.commandError = ''\n const view = this.session.read()\n /* The title bar reads the name through `context`, which the shell asks for only when told — a rename left it stale. */\n if (view.name !== this.titled) {\n this.titled = view.name\n this.updateContext()\n }\n if (!view.saving) this.provideAssistant()\n /*\n A new figure's first save gives it an id: the address becomes the figure's own, in place of the history entry.\n Left at the bare modeller address, opening the modeller again from the menu was taken for the same page and kept\n the saved figure -- its type showing where a new figure's was expected (2026-09-25).\n */\n if (view.id && !this.routeId && !view.saving) {\n this.routeId = view.id\n navigate(modellerPathOf({ id: view.id }), true)\n }\n /* 저장이 끝난 순간 행을 다시 읽는다 — 발행된 도형을 고쳐 저장하면 서버가 초안으로 내린다. */\n if (this.wasDirty && !view.dirty && !view.saving && view.id) this.readRow(view.id)\n this.wasDirty = !!view.dirty\n }\n private async readRow(id: string) {\n try {\n const figure = await fetchFigure(id)\n if (this.session.read().id === id)\n this.row = {\n state: figure.state as 'draft' | 'released',\n version: figure.version ?? 0,\n type: figure.type,\n category: figure.category ?? undefined,\n tags: figure.tags ?? undefined\n }\n } catch {\n /* 행을 못 읽으면 패널은 판 번호 0 으로 보인다 — 발행 문이 서버에서 다시 판단하므로 여기서 지어내지 않는다. */\n }\n }\n /**\n * 타입을 정한다 — 보드가 도형을 부르는 이름이고, 발행하면 굳는다(ADR-0063 결정 4).\n *\n * 새 V3 도형은 저장할 때 임시 타입을 받는데, 이 화면에는 그것을 바꿀 칸이 없어서 임시 타입 그대로 발행됐다.\n * 행만 바뀐다 — V3 자산은 타입을 원본에 적지 않으므로(적으면 형식이 거절한다) 편집 중인 원본과 부딪히지 않는다.\n * 서버의 거절(이미 쓰는 타입 · 발행 뒤)은 그 문장 그대로 칸 아래에 둔다.\n */\n private renameType = async (event: Event) => {\n const input = event.target as HTMLInputElement\n const id = this.session.read().id\n const wanted = input.value.trim().replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()\n input.value = wanted\n /*\n A figure not saved yet has no row to rename: the type is held here and the first save's question opens with it.\n The field was disabled until the first save, so a type typed there went nowhere and was asked for again\n (2026-09-25).\n */\n if (!id) {\n this.row = { ...this.row, type: wanted || undefined }\n return\n }\n if (!wanted || wanted === this.row.type) return\n try {\n const result = await updateFigure(id, { type: wanted })\n this.typeError = ''\n this.row = { ...this.row, type: result.figure.type }\n } catch (error) {\n this.typeError = error instanceof Error ? error.message : String(error)\n input.value = this.row.type ?? ''\n }\n }\n /** 「부품에 맞추기」 — 공간을 지금 부품에서 다시 선언한다. 저장은 저작자가 따로 한다. */\n private fitToParts = () => {\n this.runCommand(() => occupancyFromParts(this.session.source()))\n }\n /** A command from the properties tab — rename, declare the room, the mounting face. */\n private onCommand = (event: Event) => {\n const action = (event as CustomEvent).detail?.action as V3AuthoringAction | undefined\n if (action) this.runCommand(() => action)\n }\n /**\n * One step of the session's history, whichever part of the side asked for it — so undo takes it back like any\n * other edit, and it is saved with the figure.\n */\n private runCommand(actionOf: () => V3AuthoringAction) {\n try {\n this.session.command(actionOf())\n this.commandError = ''\n this.changed()\n } catch (error) {\n /*\n This used to set the page error, which replaces the whole editor with one raw line — for a figure with no\n parts that line was 「OCCUPANCY_NO_PARTS: occupancy: no part could be measured」 (2026-09-24).\n */\n this.commandError =\n (error as { code?: string })?.code === 'OCCUPANCY_NO_PARTS'\n ? i18next.t('figure.text.fit-needs-parts')\n : placementRefusalOf(error, (key, params) => i18next.t(key, params)) ??\n (error instanceof Error\n ? error.message\n : String(error))\n }\n }\n /**\n * Category and tags — the catalogue's, kept on the row, not in the asset. Written at once; the server's\n * refusal is shown as it gave it.\n */\n private onCatalog = async (event: Event) => {\n const patch = (event as CustomEvent).detail as { category?: string; tags?: string[] }\n const id = this.session.read().id\n if (!id || !patch) return\n try {\n const result = await updateFigure(id, patch)\n this.commandError = ''\n this.row = { ...this.row, category: result.figure.category ?? undefined, tags: result.figure.tags ?? undefined }\n } catch (error) {\n this.commandError = error instanceof Error ? error.message : String(error)\n }\n }\n private released = (event: Event) => {\n const figure = (event as CustomEvent).detail?.figure\n /* Keep the rest of the row: rebuilding it from these two dropped the type, and the locked type field went blank. */\n if (figure?.id && figure.id === this.session.read().id) this.row = { ...this.row, state: figure.state, version: figure.version ?? 0 }\n }\n /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 패널은 저장 안 된 것이 있으면 되돌리기를 막는다. */\n private reverted = async () => {\n const id = this.session.read().id\n if (!id) return\n await this.session.open(id, { discard: true })\n this.changed()\n this.readRow(id)\n }\n /**\n * The dock's way in (`figure-assistant.ts`): input changes, or a proposal's commands as one step (ADR-0094). A\n * revision is applied on the version the model saw; a new figure's proposal only on a new figure not yet touched. A\n * page on a saved figure leaves a new figure's proposal untaken, and the handler opens a new figure for it.\n */\n private applyAI = (event: Event) => {\n const detail = (event as CustomEvent).detail\n if (!this.active || !detail) return\n const view = this.session.read()\n if (detail.create && this.routeId) return\n if (detail.create && view.dirty) {\n /* A new figure already being made is the person's; a proposal does not go on top of it or replace it. */\n detail.applied = false\n detail.code = 'NEW_FIGURE_UNSAVED'\n detail.error = i18next.t('figure.text.proposal-new-figure-unsaved')\n return\n }\n try {\n if (detail.create) this.session.applyProposal(detail.steps, view.revision!, view.context!)\n else if (detail.steps) this.session.applyProposal(detail.steps, detail.revision, detail.context)\n else this.session.apply(detail.changes, 'ai', detail.revision, detail.context)\n detail.applied = true\n this.changed()\n } catch (error) {\n detail.applied = false\n detail.code = (error as { code?: string })?.code\n detail.error = error instanceof Error ? error.message : String(error)\n }\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('figure-v3-apply', this.applyAI)\n }\n disconnectedCallback() {\n this.withdrawAssistant()\n window.removeEventListener('figure-v3-apply', this.applyAI)\n super.disconnectedCallback()\n }\n async canDeactivate() {\n const view = this.session.read()\n if (view.saving) return false\n if (!view.dirty) return true\n /*\n The shell asks this while the edited model is still on screen. The app's own prompt, as every operato screen\n asks -- not the browser's confirm box (user, 2026-09-24).\n */\n this.discardApproved = !!(await OxPrompt.open({\n type: 'warning',\n title: i18next.t('figure.text.unsaved-changes'),\n text: i18next.t('figure.text.leave-without-saving'),\n confirmButton: { text: i18next.t('figure.button.leave-without-saving') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n }))\n return this.discardApproved\n }\n async pageUpdated(changes: Record<string, unknown>, lifecycle: { resourceId?: string }) {\n if (!this.active) {\n if (this.session.read().loading) this.session.close()\n this.withdrawAssistant()\n return\n }\n const id = lifecycle?.resourceId\n if (this.session.read().context && id === this.routeId && !changes.active) return\n try {\n const discard = this.discardApproved\n this.discardApproved = false\n if (id) {\n this.draft = undefined\n /* The row is read after the figure opens; until then the last figure's type must not stand in the field (C2). */\n this.row = { version: 0 }\n this.typeError = ''\n const opening = this.session.open(id, { discard })\n this.changed()\n await opening\n this.readRow(id)\n } else {\n this.session.startNew({ discard })\n this.draft = undefined\n this.row = { version: 0 }\n this.typeError = ''\n this.takeNewFigureProposal()\n }\n this.routeId = id\n this.error = ''\n this.changed()\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n }\n }\n /** A new figure the dock opened for a proposal: its steps, applied as the new figure's first step. */\n private takeNewFigureProposal() {\n const waiting = takePendingNewFigure()\n if (!waiting) return\n const view = this.session.read()\n try {\n this.session.applyProposal(waiting.steps, view.revision!, view.context!)\n waiting.settle({ applied: true })\n } catch (error) {\n waiting.settle({ applied: false, code: (error as { code?: string })?.code, error: error instanceof Error ? error.message : String(error) })\n }\n }\n /** The first save's question, over the names this domain already uses. */\n private askFirstSave = async (name: string): Promise<FirstSave | undefined> => {\n const taken = await fetchFigureTypeNames().catch(() => [] as string[])\n return openPopup<FirstSave>(html`<figure-v3-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-v3-first-save>`, {\n backdrop: true,\n size: 'small',\n title: i18next.t('figure.title.save-new-figure')\n }).result\n }\n get context() {\n return {\n title: this.session.read().name ?? 'Figure',\n actions: [],\n /*\n No heading line: the canvas is what this page is for, and the name is already in the properties' first section\n (and the browser tab). The title stays, not emptied, because the MDI tab reads it too -- the heading is switched\n off where it is drawn (`ox-page-title-bar`).\n */\n heading: false\n }\n }\n render() {\n if (this.error) return html`<p role=\"alert\" class=\"error\">${this.error}</p>`\n const view = this.session.read()\n return html`<figure-v3-editor .session=${this.session} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} @v3-changed=${this.changed}>\n ${this.commandError ? html`<p slot=\"notice\" fit-error role=\"alert\">${this.commandError}</p>` : ''}\n <figure-v3-properties\n slot=\"figure\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureCategory=${this.row.category}\n .figureTags=${this.row.tags}\n @v3-command=${this.onCommand}\n @v3-catalog=${this.onCatalog}\n >\n <label slot=\"type\" type-field>\n <span>${i18next.t('figure.label.figure-type')}</span>\n ${this.row.version > 0\n ? html`<code>${this.row.type}</code><small>${i18next.t('figure.text.type-name-cannot-change')}</small>`\n : html`<input\n .value=${this.row.type ?? ''}\n placeholder=${i18next.t('figure.text.figure-type-placeholder')}\n @change=${this.renameType}\n />`}\n ${this.typeError ? html`<small role=\"alert\">${this.typeError}</small>` : ''}\n </label>\n </figure-v3-properties>\n <figure-v3-verdict\n slot=\"verdict\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureType=${this.row.type}\n @capacity-changed=${(event: CustomEvent) => {\n this.instances = event.detail.instances\n this.budget = event.detail.budget\n }}\n @fit-base-to-parts=${this.fitToParts}\n ></figure-v3-verdict>\n <figure-history-panel\n slot=\"history\"\n .figureId=${view.id}\n .figureState=${this.row.state}\n .figureVersion=${this.row.version}\n .figureType=${this.row.type}\n .dirty=${!!view.dirty}\n @fit-base-to-parts=${this.fitToParts}\n @figure-released=${this.released}\n @figure-reverted=${this.reverted}\n ></figure-history-panel>\n </figure-v3-editor>`\n }\n\n}\n"]}
|
|
1
|
+
{"version":3,"file":"figure-v3-modeller-page.js","sourceRoot":"","sources":["../../client/pages/figure-v3-modeller-page.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACzG,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAErD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAEhE,OAAO,0BAA0B,CAAA;AACjC,OAAO,4BAA4B,CAAA;AACnC,OAAO,qCAAqC,CAAA;AAC5C,OAAO,kCAAkC,CAAA;AACzC,OAAO,qCAAqC,CAAA;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,qCAAqC,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AACtD,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,mDAAmD,CAAA;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AAEpE;;;;;GAKG;AAEI,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,QAAQ;IAA3C;;QAuDY,YAAO,GAAG,sBAAsB,CAAC,cAAc,CAAC,CAAA;QAEzD,oBAAe,GAAG,KAAK,CAAA;QACd,aAAQ,GAAG,CAAC,CAAA;QAC7B,uFAAuF;QACtE,YAAO,GAAe,EAAE,CAAA;QACjC,gBAAW,GAAG,KAAK,CAAA;QACV,UAAK,GAAG,EAAE,CAAA;QAC3B;;;;;;WAMG;QACc,QAAG,GAAyG;YAC3H,OAAO,EAAE,CAAC;SACX,CAAA;QACgB,cAAS,GAAG,EAAE,CAAA;QAC/B,uHAAuH;QACtG,iBAAY,GAAG,EAAE,CAAA;QAiB1B,aAAQ,GAAG,KAAK,CAAA;QAIxB;;;;UAIE;QACM,qBAAgB,GAAG,GAAG,EAAE;YAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;YAChC,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,OAAM;YACvE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACb,IAAI,CAAC,iBAAiB,EAAE,CAAA;gBACxB,OAAM;YACR,CAAC;YACD,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;gBAClC,sBAAsB,CAAC,IAAI,CAAC,CAAA;gBAC5B,OAAM;YACR,CAAC;YACD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAA;YAClB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAA;YACtB,uBAAuB,CAAC,IAAI,EAAE;gBAC5B,GAAG,EAAE,UAAU,EAAE,EAAE;gBACnB,OAAO,EAAE,QAAQ;gBACjB,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE;gBAC3C,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzF,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;gBACtD,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;oBAClB,OAAO,EAAE,iBAAiB;oBAC1B,UAAU,EAAE,UAAU;oBACtB,QAAQ,EAAE,EAAE;oBACZ,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBAC9C,4GAA4G;oBAC5G,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO;oBACpC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,QAAQ;oBACtC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;iBACtC,CAAC;aACH,CAAC,CAAA;QACJ,CAAC,CAAA;QAKO,YAAO,GAAG,GAAG,EAAE;YACrB,IAAI,CAAC,QAAQ,EAAE,CAAA;YACf,uGAAuG;YACvG,IAAI,CAAC,YAAY,GAAG,EAAE,CAAA;YACtB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;YAChC,uHAAuH;YACvH,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAA;gBACvB,IAAI,CAAC,aAAa,EAAE,CAAA;YACtB,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,IAAI,CAAC,gBAAgB,EAAE,CAAA;YACzC;;;;cAIE;YACF,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC7C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAA;gBACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAA;YACjD,CAAC;YACD,yDAAyD;YACzD,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE;gBAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAClF,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAA;QAC9B,CAAC,CAAA;QAgBD;;;;;;WAMG;QACK,eAAU,GAAG,KAAK,EAAE,KAAY,EAAE,EAAE;YAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,MAA0B,CAAA;YAC9C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,CAAA;YACjC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAA;YAC9E,KAAK,CAAC,KAAK,GAAG,MAAM,CAAA;YACpB;;;;cAIE;YACF,IAAI,CAAC,EAAE,EAAE,CAAC;gBACR,IAAI,CAAC,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,IAAI,SAAS,EAAE,CAAA;gBACrD,OAAM;YACR,CAAC;YACD,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI;gBAAE,OAAM;YAC/C,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAA;gBACvD,IAAI,CAAC,SAAS,GAAG,EAAE,CAAA;gBACnB,IAAI,CAAC,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;YACtD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBACvE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAA;YACnC,CAAC;QACH,CAAC,CAAA;QACD,uDAAuD;QAC/C,eAAU,GAAG,GAAG,EAAE;YACxB,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QAClE,CAAC,CAAA;QACD,uFAAuF;QAC/E,cAAS,GAAG,CAAC,KAAY,EAAE,EAAE;YACnC,MAAM,MAAM,GAAI,KAAqB,CAAC,MAAM,EAAE,MAAuC,CAAA;YACrF,IAAI,MAAM;gBAAE,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAA;QAC3C,CAAC,CAAA;QAwBD;;;WAGG;QACK,cAAS,GAAG,KAAK,EAAE,KAAY,EAAE,EAAE;YACzC,MAAM,KAAK,GAAI,KAAqB,CAAC,MAAgD,CAAA;YACrF,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,CAAA;YACjC,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK;gBAAE,OAAM;YACzB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;gBAC5C,IAAI,CAAC,YAAY,GAAG,EAAE,CAAA;gBACtB,IAAI,CAAC,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,IAAI,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,EAAE,CAAA;YAClH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAC5E,CAAC;QACH,CAAC,CAAA;QACO,aAAQ,GAAG,CAAC,KAAY,EAAE,EAAE;YAClC,MAAM,MAAM,GAAI,KAAqB,CAAC,MAAM,EAAE,MAAM,CAAA;YACpD,oHAAoH;YACpH,IAAI,MAAM,EAAE,EAAE,IAAI,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE;gBAAE,IAAI,CAAC,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC,EAAE,CAAA;QACvI,CAAC,CAAA;QACD,wEAAwE;QAChE,aAAQ,GAAG,KAAK,IAAI,EAAE;YAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,CAAA;YACjC,IAAI,CAAC,EAAE;gBAAE,OAAM;YACf,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;YAC9C,IAAI,CAAC,OAAO,EAAE,CAAA;YACd,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAClB,CAAC,CAAA;QACD;;;;WAIG;QACK,YAAO,GAAG,CAAC,KAAY,EAAE,EAAE;YACjC,MAAM,MAAM,GAAI,KAAqB,CAAC,MAAM,CAAA;YAC5C,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM;gBAAE,OAAM;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;YAChC,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO;gBAAE,OAAM;YACzC,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAChC,yGAAyG;gBACzG,MAAM,CAAC,OAAO,GAAG,KAAK,CAAA;gBACtB,MAAM,CAAC,IAAI,GAAG,oBAAoB,CAAA;gBAClC,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,yCAAyC,CAAC,CAAA;gBACnE,OAAM;YACR,CAAC;YACD,IAAI,CAAC;gBACH,IAAI,MAAM,CAAC,MAAM;oBAAE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,QAAS,EAAE,IAAI,CAAC,OAAQ,CAAC,CAAA;qBACrF,IAAI,MAAM,CAAC,KAAK;oBAAE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;;oBAC3F,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;gBAC9E,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;gBACrB,IAAI,CAAC,OAAO,EAAE,CAAA;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,OAAO,GAAG,KAAK,CAAA;gBACtB,MAAM,CAAC,IAAI,GAAI,KAA2B,EAAE,IAAI,CAAA;gBAChD,MAAM,CAAC,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvE,CAAC;QACH,CAAC,CAAA;QACD;;;UAGE;QACM,gBAAW,GAAG,CAAC,KAAwB,EAAE,EAAE;YACjD,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK;gBAAE,OAAM;YACtD,KAAK,CAAC,cAAc,EAAE,CAAA;YACtB,KAAK,CAAC,WAAW,GAAG,EAAE,CAAA;QACxB,CAAC,CAAA;QAkFD,0EAA0E;QAClE,iBAAY,GAAG,KAAK,EAAE,IAAY,EAAkC,EAAE;YAC5E,MAAM,KAAK,GAAG,MAAM,oBAAoB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAc,CAAC,CAAA;YACtE,OAAO,SAAS,CAAY,IAAI,CAAA,+BAA+B,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,UAAU,IAAI,WAAW,KAAK,0BAA0B,EAAE;gBAC1I,QAAQ,EAAE,IAAI;gBACd,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;aACjD,CAAC,CAAC,MAAM,CAAA;QACX,CAAC,CAAA;IAgEH,CAAC;aA1cQ,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDlB,AArDY,CAqDZ;IA8BO,WAAW;QACjB,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAA;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,8CAA8C;QAChD,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IA0CO,iBAAiB;QACvB,IAAI,CAAC,YAAY,GAAG,SAAS,CAAA;QAC7B,qBAAqB,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IAyBO,KAAK,CAAC,OAAO,CAAC,EAAU;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAA;YACpC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE;gBAC/B,IAAI,CAAC,GAAG,GAAG;oBACT,KAAK,EAAE,MAAM,CAAC,KAA6B;oBAC3C,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC;oBAC5B,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,SAAS;oBACtC,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,SAAS;iBAC/B,CAAA;QACL,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;QACtE,CAAC;IACH,CAAC;IAyCD;;;OAGG;IACK,UAAU,CAAC,QAAiC;QAClD,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAA;YAChC,IAAI,CAAC,YAAY,GAAG,EAAE,CAAA;YACtB,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf;;;cAGE;YACF,IAAI,CAAC,YAAY;gBACd,KAA2B,EAAE,IAAI,KAAK,oBAAoB;oBACzD,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;oBAC1C,CAAC,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;wBAClE,CAAC,KAAK,YAAY,KAAK;4BACvB,CAAC,CAAC,KAAK,CAAC,OAAO;4BACf,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACxB,CAAC;IACH,CAAC;IAoED,iBAAiB;QACf,KAAK,CAAC,iBAAiB,EAAE,CAAA;QACzB,MAAM,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QACxD,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;IAC3D,CAAC;IACD,oBAAoB;QAClB,IAAI,CAAC,iBAAiB,EAAE,CAAA;QACxB,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QAC3D,MAAM,CAAC,mBAAmB,CAAC,cAAc,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;QAC5D,KAAK,CAAC,oBAAoB,EAAE,CAAA;IAC9B,CAAC;IACD,KAAK,CAAC,aAAa;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;QAChC,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAA;QAC7B,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAC5B;;;UAGE;QACF,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC;YAC5C,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;YAC/C,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,kCAAkC,CAAC;YACnD,aAAa,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,oCAAoC,CAAC,EAAE;YACxE,YAAY,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC,EAAE;SAChE,CAAC,CAAC,CAAA;QACH,OAAO,IAAI,CAAC,eAAe,CAAA;IAC7B,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,OAAgC,EAAE,SAAkC;QACpF,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO;gBAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;YACrD,IAAI,CAAC,iBAAiB,EAAE,CAAA;YACxB,OAAM;QACR,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;YACvB,8FAA8F;YAC9F,kBAAkB,EAAE;iBACjB,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;iBAC9C,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,CAAC,CAAA;QAC5C,CAAC;QACD,MAAM,EAAE,GAAG,SAAS,EAAE,UAAU,CAAA;QAChC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,KAAK,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAM;QACjF,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAA;YACpC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAA;YAC5B,IAAI,EAAE,EAAE,CAAC;gBACP,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;gBACtB,iHAAiH;gBACjH,IAAI,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;gBACzB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAA;gBACnB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;gBAClD,IAAI,CAAC,OAAO,EAAE,CAAA;gBACd,MAAM,OAAO,CAAA;gBACb,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;YAClB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;gBAClC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;gBACtB,IAAI,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;gBACzB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAA;gBACnB,IAAI,CAAC,qBAAqB,EAAE,CAAA;YAC9B,CAAC;YACD,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;YACjB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;YACf,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACrE,CAAC;IACH,CAAC;IACD,sGAAsG;IAC9F,qBAAqB;QAC3B,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAA;QACtC,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;QAChC,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,QAAS,EAAE,IAAI,CAAC,OAAQ,CAAC,CAAA;YACxE,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QACnC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAG,KAA2B,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC7I,CAAC;IACH,CAAC;IAUD,IAAI,OAAO;QACT,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,QAAQ;YAC3C,OAAO,EAAE,EAAE;YACX;;;;cAIE;YACF,OAAO,EAAE,KAAK;SACf,CAAA;IACH,CAAC;IACD,MAAM;QACJ,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA,iCAAiC,IAAI,CAAC,KAAK,MAAM,CAAA;QAC5E,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;QAChC,OAAO,IAAI,CAAA,8BAA8B,IAAI,CAAC,OAAO,aAAa,IAAI,CAAC,OAAO,eAAe,IAAI,CAAC,YAAY,cAAc,IAAI,CAAC,QAAQ,eAAe,IAAI,CAAC,SAAS,YAAY,IAAI,CAAC,MAAM,gBAAgB,IAAI,CAAC,OAAO;QACrN,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAA,2CAA2C,IAAI,CAAC,YAAY,MAAM,CAAC,CAAC,CAAC,EAAE;;;kBAGrF,IAAI,CAAC,WAAW,EAAE;oBAChB,IAAI,CAAC,EAAE;0BACD,IAAI,CAAC,GAAG,CAAC,QAAQ;sBACrB,IAAI,CAAC,GAAG,CAAC,IAAI;sBACb,IAAI,CAAC,SAAS;sBACd,IAAI,CAAC,SAAS;;;oBAGhB,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;cAC3C,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC;YACpB,CAAC,CAAC,IAAI,CAAA,SAAS,IAAI,CAAC,GAAG,CAAC,IAAI,iBAAiB,OAAO,CAAC,CAAC,CAAC,qCAAqC,CAAC,UAAU;YACvG,CAAC,CAAC,IAAI,CAAA;2BACO,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE;gCACd,OAAO,CAAC,CAAC,CAAC,qCAAqC,CAAC;4BACpD,IAAI,CAAC,UAAU;mBACxB;cACL,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA,uBAAuB,IAAI,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE;;;;;kBAKrE,IAAI,CAAC,WAAW,EAAE;oBAChB,IAAI,CAAC,EAAE;sBACL,IAAI,CAAC,GAAG,CAAC,IAAI;4BACP,CAAC,KAAkB,EAAE,EAAE;YACzC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAA;YACvC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAA;QACnC,CAAC;6BACoB,IAAI,CAAC,UAAU;;;;oBAIxB,IAAI,CAAC,EAAE;uBACJ,IAAI,CAAC,GAAG,CAAC,KAAK;yBACZ,IAAI,CAAC,GAAG,CAAC,OAAO;sBACnB,IAAI,CAAC,GAAG,CAAC,IAAI;iBAClB,CAAC,CAAC,IAAI,CAAC,KAAK;6BACA,IAAI,CAAC,UAAU;2BACjB,IAAI,CAAC,QAAQ;2BACb,IAAI,CAAC,QAAQ;;wBAEhB,CAAA;IACtB,CAAC;;AA/YgB;IAAhB,KAAK,EAAE;;sDAAqB;AAEZ;IAAhB,KAAK,EAAE;;qDAAiC;AAExB;IAAhB,KAAK,EAAE;;mDAAmB;AAQV;IAAhB,KAAK,EAAE;;iDAEP;AACgB;IAAhB,KAAK,EAAE;;uDAAuB;AAEd;IAAhB,KAAK,EAAE;;0DAA0B;AAEjB;IAAhB,KAAK,EAAE;;uDAA2B;AAClB;IAAhB,KAAK,EAAE;;oDAA8B;AA9E3B,oBAAoB;IADhC,aAAa,CAAC,yBAAyB,CAAC;GAC5B,oBAAoB,CA2chC","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { navigate, PageView } from '@operato/shell'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { createV3ProductSession } from '../modeller/v3-product-session.js'\nimport { v3ProductStore } from '../graphql/v3-store.js'\nimport { i18next } from '@operato/i18n'\nimport { fetchFigure, fetchFigureRecipes, fetchFigureTypeNames, updateFigure } from '../graphql/index.js'\nimport { recipesOf } from '../modeller/v3-recipes.js'\nimport type { V3Recipe } from '@hatiolab/figure-model'\nimport { occupancyFromParts } from '../modeller/v3-occupancy.js'\nimport type { V3AuthoringAction } from '@hatiolab/figure-model'\nimport '../modeller/v3-editor.js'\nimport '@material/web/icon/icon.js'\nimport '../modeller/figure-history-panel.js'\nimport '../modeller/figure-v3-verdict.js'\nimport '../modeller/figure-v3-properties.js'\nimport type { V3BudgetLine } from '../modeller/v3-maturity.js'\nimport { OxPrompt } from '@operato/popup'\nimport { openPopup } from '@operato/layout'\nimport '../modeller/figure-v3-first-save.js'\nimport { activePalette } from '@hatiolab/things-scene'\nimport {\n clearAssistantSurface,\n notifyAssistantSurface,\n provideAssistantSurface\n} from '@things-factory/ai-assistant/dist-client/index.js'\nimport type { FirstSave } from '../modeller/v3-first-save.js'\nimport { placementRefusalOf } from '../modeller/v3-refusals.js'\nimport { takePendingNewFigure } from '../modeller/v3-ai-proposal.js'\n\n/**\n * The V3 product page has exactly one authoritative workspace for UI and AI input transactions.\n *\n * The one modeller since ADR-0087 step 5 (2026-09-24): it answers to `figure-modeller`, and to `figure-v3-modeller`\n * as an alias for links made before the switch.\n */\n@customElement('figure-v3-modeller-page')\nexport class FigureV3ModellerPage extends PageView {\n static styles = css`\n :host {\n display: flex;\n flex-direction: row;\n height: 100%;\n min-height: 0;\n /*\n The page must fit the width it is given. Without this the shell sizes it to its content — the editor's\n 660 plus the 320 release panel — and the panel runs off the right edge (seen at 945 px, 2026-09-24).\n */\n overflow: hidden;\n }\n figure-v3-editor {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n /* The first field of the properties tab, in the side panel's look (side-panel-styles): a label above the field. */\n label[type-field] {\n display: grid;\n gap: 2px;\n margin: 0 0 5px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n label[type-field] input,\n label[type-field] code {\n box-sizing: border-box;\n min-height: 30px;\n padding: 4px 7px;\n font-family: var(--mono-font, monospace);\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n label[type-field] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n p[fit-error] {\n margin: 0;\n padding: 8px 12px;\n font-size: 12px;\n color: var(--md-sys-color-error);\n }\n label[type-field] small[role='alert'] {\n color: var(--md-sys-color-error);\n }\n .error {\n padding: 16px;\n color: var(--md-sys-color-error);\n }\n `\n private readonly session = createV3ProductSession(v3ProductStore)\n private routeId: string | undefined\n private discardApproved = false\n @state() private revision = 0\n /** The recipes this domain sees (ADR-0095), read once when the page is first shown. */\n @state() private recipes: V3Recipe[] = []\n private recipesRead = false\n @state() private error = ''\n /**\n * 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.\n *\n * 이 화면에는 발행 패널이 없었다. 저장은 되고 발행 · 판본 · 복원 · 판정을 볼 자리가 없어서, ADR-0087 5단계에서\n * `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 V3 도형은 공간을 선언하기 전에는 전부\n * 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 그래서 발행 패널을 건다.\n */\n @state() private row: { state?: 'draft' | 'released'; version: number; type?: string; category?: string; tags?: string[] } = {\n version: 0\n }\n @state() private typeError = ''\n /** Why a command from the side did nothing. Shown beside the tabs — the editor stays, since nothing in it was lost. */\n @state() private commandError = ''\n /** The verdict's count, handed to the editor's verdict tab. */\n @state() private instances?: number\n @state() private budget?: V3BudgetLine\n /**\n * The source the verdict judges. The session refuses to export while it loads or saves, so the last one read is\n * kept rather than blanked — the verdict would otherwise flicker to 「waiting」 on every save.\n */\n private draft?: string\n private draftSource(): string | undefined {\n try {\n this.draft = this.session.source()\n } catch {\n /* loading or saving — keep the last source */\n }\n return this.draft\n }\n private wasDirty = false\n private titled?: string | null\n /** The figure the AI dock was last told about - another id is another conversation. */\n private assistantFor?: string\n /*\n The figure on screen is what the AI dock talks about (design/ai/shared-dock.md §3). A figure not saved yet has no\n row to hang a conversation on, so it provides nothing and the dock stays on the application's conversation.\n The draft is read at the moment of sending, never cached - the dock must not answer about a shape already changed.\n */\n private provideAssistant = () => {\n const view = this.session.read()\n if (!this.active || view.loading || view.error || !view.context) return\n if (!view.id) {\n this.withdrawAssistant()\n return\n }\n if (this.assistantFor === view.id) {\n notifyAssistantSurface(this)\n return\n }\n const id = view.id\n this.assistantFor = id\n provideAssistantSurface(this, {\n key: `figure:${id}`,\n subject: 'figure',\n icon: 'category',\n label: () => this.session.read().name || id,\n detail: () => (this.session.read().dirty ? i18next.t('figure.text.unsaved-changes') : ''),\n anchor: () => ({ anchorType: 'figure', anchorId: id }),\n hostContext: () => ({\n surface: 'figure-modeller',\n editorMode: 'existing',\n figureId: id,\n draftSource: JSON.parse(this.session.source()),\n /* The version the model sees; a proposal is applied on it, or refused if the figure moved on (ADR-0094). */\n context: this.session.read().context,\n revision: this.session.read().revision,\n palette: Object.keys(activePalette())\n })\n })\n }\n private withdrawAssistant() {\n this.assistantFor = undefined\n clearAssistantSurface(this)\n }\n private changed = () => {\n this.revision++\n /* Any edit after a failed command makes the message stale — the author has done something about it. */\n this.commandError = ''\n const view = this.session.read()\n /* The title bar reads the name through `context`, which the shell asks for only when told — a rename left it stale. */\n if (view.name !== this.titled) {\n this.titled = view.name\n this.updateContext()\n }\n if (!view.saving) this.provideAssistant()\n /*\n A new figure's first save gives it an id: the address becomes the figure's own, in place of the history entry.\n Left at the bare modeller address, opening the modeller again from the menu was taken for the same page and kept\n the saved figure -- its type showing where a new figure's was expected (2026-09-25).\n */\n if (view.id && !this.routeId && !view.saving) {\n this.routeId = view.id\n navigate(modellerPathOf({ id: view.id }), true)\n }\n /* 저장이 끝난 순간 행을 다시 읽는다 — 발행된 도형을 고쳐 저장하면 서버가 초안으로 내린다. */\n if (this.wasDirty && !view.dirty && !view.saving && view.id) this.readRow(view.id)\n this.wasDirty = !!view.dirty\n }\n private async readRow(id: string) {\n try {\n const figure = await fetchFigure(id)\n if (this.session.read().id === id)\n this.row = {\n state: figure.state as 'draft' | 'released',\n version: figure.version ?? 0,\n type: figure.type,\n category: figure.category ?? undefined,\n tags: figure.tags ?? undefined\n }\n } catch {\n /* 행을 못 읽으면 패널은 판 번호 0 으로 보인다 — 발행 문이 서버에서 다시 판단하므로 여기서 지어내지 않는다. */\n }\n }\n /**\n * 타입을 정한다 — 보드가 도형을 부르는 이름이고, 발행하면 굳는다(ADR-0063 결정 4).\n *\n * 새 V3 도형은 저장할 때 임시 타입을 받는데, 이 화면에는 그것을 바꿀 칸이 없어서 임시 타입 그대로 발행됐다.\n * 행만 바뀐다 — V3 자산은 타입을 원본에 적지 않으므로(적으면 형식이 거절한다) 편집 중인 원본과 부딪히지 않는다.\n * 서버의 거절(이미 쓰는 타입 · 발행 뒤)은 그 문장 그대로 칸 아래에 둔다.\n */\n private renameType = async (event: Event) => {\n const input = event.target as HTMLInputElement\n const id = this.session.read().id\n const wanted = input.value.trim().replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()\n input.value = wanted\n /*\n A figure not saved yet has no row to rename: the type is held here and the first save's question opens with it.\n The field was disabled until the first save, so a type typed there went nowhere and was asked for again\n (2026-09-25).\n */\n if (!id) {\n this.row = { ...this.row, type: wanted || undefined }\n return\n }\n if (!wanted || wanted === this.row.type) return\n try {\n const result = await updateFigure(id, { type: wanted })\n this.typeError = ''\n this.row = { ...this.row, type: result.figure.type }\n } catch (error) {\n this.typeError = error instanceof Error ? error.message : String(error)\n input.value = this.row.type ?? ''\n }\n }\n /** 「부품에 맞추기」 — 공간을 지금 부품에서 다시 선언한다. 저장은 저작자가 따로 한다. */\n private fitToParts = () => {\n this.runCommand(() => occupancyFromParts(this.session.source()))\n }\n /** A command from the properties tab — rename, declare the room, the mounting face. */\n private onCommand = (event: Event) => {\n const action = (event as CustomEvent).detail?.action as V3AuthoringAction | undefined\n if (action) this.runCommand(() => action)\n }\n /**\n * One step of the session's history, whichever part of the side asked for it — so undo takes it back like any\n * other edit, and it is saved with the figure.\n */\n private runCommand(actionOf: () => V3AuthoringAction) {\n try {\n this.session.command(actionOf())\n this.commandError = ''\n this.changed()\n } catch (error) {\n /*\n This used to set the page error, which replaces the whole editor with one raw line — for a figure with no\n parts that line was 「OCCUPANCY_NO_PARTS: occupancy: no part could be measured」 (2026-09-24).\n */\n this.commandError =\n (error as { code?: string })?.code === 'OCCUPANCY_NO_PARTS'\n ? i18next.t('figure.text.fit-needs-parts')\n : placementRefusalOf(error, (key, params) => i18next.t(key, params)) ??\n (error instanceof Error\n ? error.message\n : String(error))\n }\n }\n /**\n * Category and tags — the catalogue's, kept on the row, not in the asset. Written at once; the server's\n * refusal is shown as it gave it.\n */\n private onCatalog = async (event: Event) => {\n const patch = (event as CustomEvent).detail as { category?: string; tags?: string[] }\n const id = this.session.read().id\n if (!id || !patch) return\n try {\n const result = await updateFigure(id, patch)\n this.commandError = ''\n this.row = { ...this.row, category: result.figure.category ?? undefined, tags: result.figure.tags ?? undefined }\n } catch (error) {\n this.commandError = error instanceof Error ? error.message : String(error)\n }\n }\n private released = (event: Event) => {\n const figure = (event as CustomEvent).detail?.figure\n /* Keep the rest of the row: rebuilding it from these two dropped the type, and the locked type field went blank. */\n if (figure?.id && figure.id === this.session.read().id) this.row = { ...this.row, state: figure.state, version: figure.version ?? 0 }\n }\n /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 패널은 저장 안 된 것이 있으면 되돌리기를 막는다. */\n private reverted = async () => {\n const id = this.session.read().id\n if (!id) return\n await this.session.open(id, { discard: true })\n this.changed()\n this.readRow(id)\n }\n /**\n * The dock's way in (`figure-assistant.ts`): input changes, or a proposal's commands as one step (ADR-0094). A\n * revision is applied on the version the model saw; a new figure's proposal only on a new figure not yet touched. A\n * page on a saved figure leaves a new figure's proposal untaken, and the handler opens a new figure for it.\n */\n private applyAI = (event: Event) => {\n const detail = (event as CustomEvent).detail\n if (!this.active || !detail) return\n const view = this.session.read()\n if (detail.create && this.routeId) return\n if (detail.create && view.dirty) {\n /* A new figure already being made is the person's; a proposal does not go on top of it or replace it. */\n detail.applied = false\n detail.code = 'NEW_FIGURE_UNSAVED'\n detail.error = i18next.t('figure.text.proposal-new-figure-unsaved')\n return\n }\n try {\n if (detail.create) this.session.applyProposal(detail.steps, view.revision!, view.context!)\n else if (detail.steps) this.session.applyProposal(detail.steps, detail.revision, detail.context)\n else this.session.apply(detail.changes, 'ai', detail.revision, detail.context)\n detail.applied = true\n this.changed()\n } catch (error) {\n detail.applied = false\n detail.code = (error as { code?: string })?.code\n detail.error = error instanceof Error ? error.message : String(error)\n }\n }\n /*\n Reloading or closing the tab is not a move the shell asks `canDeactivate` about: a new figure not yet saved was lost\n without a word (2026-09-26). The browser's own question is the only one it allows there.\n */\n private holdUnsaved = (event: BeforeUnloadEvent) => {\n if (!this.active || !this.session.read().dirty) return\n event.preventDefault()\n event.returnValue = ''\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('figure-v3-apply', this.applyAI)\n window.addEventListener('beforeunload', this.holdUnsaved)\n }\n disconnectedCallback() {\n this.withdrawAssistant()\n window.removeEventListener('figure-v3-apply', this.applyAI)\n window.removeEventListener('beforeunload', this.holdUnsaved)\n super.disconnectedCallback()\n }\n async canDeactivate() {\n const view = this.session.read()\n if (view.saving) return false\n if (!view.dirty) return true\n /*\n The shell asks this while the edited model is still on screen. The app's own prompt, as every operato screen\n asks -- not the browser's confirm box (user, 2026-09-24).\n */\n this.discardApproved = !!(await OxPrompt.open({\n type: 'warning',\n title: i18next.t('figure.text.unsaved-changes'),\n text: i18next.t('figure.text.leave-without-saving'),\n confirmButton: { text: i18next.t('figure.button.leave-without-saving') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n }))\n return this.discardApproved\n }\n async pageUpdated(changes: Record<string, unknown>, lifecycle: { resourceId?: string }) {\n if (!this.active) {\n if (this.session.read().loading) this.session.close()\n this.withdrawAssistant()\n return\n }\n if (!this.recipesRead) {\n this.recipesRead = true\n /* A recipe list that cannot be read leaves the section out; the modeller works without it. */\n fetchFigureRecipes()\n .then(rows => (this.recipes = recipesOf(rows)))\n .catch(() => (this.recipesRead = false))\n }\n const id = lifecycle?.resourceId\n if (this.session.read().context && id === this.routeId && !changes.active) return\n try {\n const discard = this.discardApproved\n this.discardApproved = false\n if (id) {\n this.draft = undefined\n /* The row is read after the figure opens; until then the last figure's type must not stand in the field (C2). */\n this.row = { version: 0 }\n this.typeError = ''\n const opening = this.session.open(id, { discard })\n this.changed()\n await opening\n this.readRow(id)\n } else {\n this.session.startNew({ discard })\n this.draft = undefined\n this.row = { version: 0 }\n this.typeError = ''\n this.takeNewFigureProposal()\n }\n this.routeId = id\n this.error = ''\n this.changed()\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n }\n }\n /** A new figure the dock opened for a proposal: its steps, applied as the new figure's first step. */\n private takeNewFigureProposal() {\n const waiting = takePendingNewFigure()\n if (!waiting) return\n const view = this.session.read()\n try {\n this.session.applyProposal(waiting.steps, view.revision!, view.context!)\n waiting.settle({ applied: true })\n } catch (error) {\n waiting.settle({ applied: false, code: (error as { code?: string })?.code, error: error instanceof Error ? error.message : String(error) })\n }\n }\n /** The first save's question, over the names this domain already uses. */\n private askFirstSave = async (name: string): Promise<FirstSave | undefined> => {\n const taken = await fetchFigureTypeNames().catch(() => [] as string[])\n return openPopup<FirstSave>(html`<figure-v3-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-v3-first-save>`, {\n backdrop: true,\n size: 'small',\n title: i18next.t('figure.title.save-new-figure')\n }).result\n }\n get context() {\n return {\n title: this.session.read().name ?? 'Figure',\n actions: [],\n /*\n No heading line: the canvas is what this page is for, and the name is already in the properties' first section\n (and the browser tab). The title stays, not emptied, because the MDI tab reads it too -- the heading is switched\n off where it is drawn (`ox-page-title-bar`).\n */\n heading: false\n }\n }\n render() {\n if (this.error) return html`<p role=\"alert\" class=\"error\">${this.error}</p>`\n const view = this.session.read()\n return html`<figure-v3-editor .session=${this.session} .recipes=${this.recipes} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} @v3-changed=${this.changed}>\n ${this.commandError ? html`<p slot=\"notice\" fit-error role=\"alert\">${this.commandError}</p>` : ''}\n <figure-v3-properties\n slot=\"figure\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureCategory=${this.row.category}\n .figureTags=${this.row.tags}\n @v3-command=${this.onCommand}\n @v3-catalog=${this.onCatalog}\n >\n <label slot=\"type\" type-field>\n <span>${i18next.t('figure.label.figure-type')}</span>\n ${this.row.version > 0\n ? html`<code>${this.row.type}</code><small>${i18next.t('figure.text.type-name-cannot-change')}</small>`\n : html`<input\n .value=${this.row.type ?? ''}\n placeholder=${i18next.t('figure.text.figure-type-placeholder')}\n @change=${this.renameType}\n />`}\n ${this.typeError ? html`<small role=\"alert\">${this.typeError}</small>` : ''}\n </label>\n </figure-v3-properties>\n <figure-v3-verdict\n slot=\"verdict\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureType=${this.row.type}\n @capacity-changed=${(event: CustomEvent) => {\n this.instances = event.detail.instances\n this.budget = event.detail.budget\n }}\n @fit-base-to-parts=${this.fitToParts}\n ></figure-v3-verdict>\n <figure-history-panel\n slot=\"history\"\n .figureId=${view.id}\n .figureState=${this.row.state}\n .figureVersion=${this.row.version}\n .figureType=${this.row.type}\n .dirty=${!!view.dirty}\n @fit-base-to-parts=${this.fitToParts}\n @figure-released=${this.released}\n @figure-reverted=${this.reverted}\n ></figure-history-panel>\n </figure-v3-editor>`\n }\n\n}\n"]}
|
package/dist-client/route.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function route(page: string): "figure-
|
|
1
|
+
export default function route(page: string): "figure-list" | "figure-modeller" | "figure-v3-modeller" | undefined;
|