@things-factory/figure-ui 10.1.225 → 10.1.227
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 +6 -6
- package/client/modeller/consumer-capabilities.ts +15 -0
- package/client/modeller/editor-styles.ts +18 -1
- package/client/modeller/editor.ts +38 -26
- package/client/modeller/figure-capability-panel.ts +31 -10
- package/client/modeller/figure-clips.ts +2 -1
- package/client/modeller/figure-controls.ts +4 -3
- package/client/modeller/figure-data-panel.ts +24 -7
- package/client/modeller/figure-history-panel.ts +7 -7
- package/client/modeller/figure-links.ts +5 -5
- package/client/modeller/figure-material-panel.ts +2 -0
- package/client/modeller/figure-preview.ts +69 -2
- package/client/modeller/figure-recipe-catalog.ts +4 -0
- package/client/modeller/figure-slot-layout-panel.ts +25 -8
- package/client/modeller/figure-verdict.ts +4 -1
- package/client/modeller/unit-words.ts +11 -0
- package/client/pages/figure-list-page.ts +2 -2
- package/client/pages/figure-modeller-page.ts +7 -6
- package/client/types.ts +2 -2
- package/client/viewparts/figure-thumb.ts +1 -1
- package/dist-client/graphql/index.d.ts +5 -5
- package/dist-client/graphql/index.js +6 -6
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/consumer-capabilities.d.ts +11 -0
- package/dist-client/modeller/consumer-capabilities.js +14 -0
- package/dist-client/modeller/consumer-capabilities.js.map +1 -1
- package/dist-client/modeller/editor-styles.js +18 -1
- package/dist-client/modeller/editor-styles.js.map +1 -1
- package/dist-client/modeller/editor.js +38 -26
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-capability-panel.d.ts +3 -0
- package/dist-client/modeller/figure-capability-panel.js +29 -11
- package/dist-client/modeller/figure-capability-panel.js.map +1 -1
- package/dist-client/modeller/figure-clips.js +2 -1
- package/dist-client/modeller/figure-clips.js.map +1 -1
- package/dist-client/modeller/figure-controls.js +4 -3
- package/dist-client/modeller/figure-controls.js.map +1 -1
- package/dist-client/modeller/figure-data-panel.js +24 -7
- package/dist-client/modeller/figure-data-panel.js.map +1 -1
- package/dist-client/modeller/figure-history-panel.d.ts +5 -5
- package/dist-client/modeller/figure-history-panel.js +7 -7
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-links.js +5 -5
- package/dist-client/modeller/figure-links.js.map +1 -1
- package/dist-client/modeller/figure-material-panel.js +2 -0
- package/dist-client/modeller/figure-material-panel.js.map +1 -1
- package/dist-client/modeller/figure-preview.d.ts +3 -0
- package/dist-client/modeller/figure-preview.js +78 -2
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-recipe-catalog.js +4 -0
- package/dist-client/modeller/figure-recipe-catalog.js.map +1 -1
- package/dist-client/modeller/figure-slot-layout-panel.d.ts +1 -1
- package/dist-client/modeller/figure-slot-layout-panel.js +25 -8
- package/dist-client/modeller/figure-slot-layout-panel.js.map +1 -1
- package/dist-client/modeller/figure-verdict.js +4 -1
- package/dist-client/modeller/figure-verdict.js.map +1 -1
- package/dist-client/modeller/unit-words.d.ts +5 -0
- package/dist-client/modeller/unit-words.js +12 -0
- package/dist-client/modeller/unit-words.js.map +1 -0
- package/dist-client/pages/figure-list-page.d.ts +1 -1
- package/dist-client/pages/figure-list-page.js +2 -2
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +3 -3
- package/dist-client/pages/figure-modeller-page.js +7 -6
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +2 -2
- package/dist-client/types.js.map +1 -1
- package/dist-client/viewparts/figure-thumb.js +1 -1
- package/dist-client/viewparts/figure-thumb.js.map +1 -1
- package/package.json +9 -9
- package/test/browser/detail-page.js +1 -1
- package/test/browser/editing-page.js +3 -3
- package/test/browser/editing.check.mjs +5 -5
- package/test/browser/links-page.js +1 -1
- package/test/browser/links.check.mjs +2 -3
- package/test/browser/modeller-page.js +4 -4
- package/test/browser/modeller-revision.check.mjs +5 -5
- package/test/browser/modeller.check.mjs +5 -5
- package/test/browser/parts.check.mjs +2 -2
- package/test/browser/product-page.check.mjs +6 -0
- package/test/browser/props.check.mjs +3 -4
- package/test/browser/row-preview.check.mjs +3 -3
- package/test/browser/screen.check.mjs +13 -11
- package/test/consumer-capabilities.test.ts +7 -0
- package/test/links.test.ts +1 -1
- package/test/refusals.test.ts +1 -1
- package/test/resolve-ts-siblings.mjs +3 -3
- package/translations/en.json +23 -12
- package/translations/es.json +23 -12
- package/translations/ja.json +24 -13
- package/translations/ko.json +83 -72
- package/translations/ms.json +23 -12
- package/translations/zh.json +23 -12
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { isFigureConsumerCapability } from '@hatiolab/figure-model';
|
|
8
8
|
import { figureCapabilityDescriptors } from '@hatiolab/things-scene';
|
|
9
|
+
import { RUNGS } from '@things-factory/figure-viewer/dist-client/capability-rung.js';
|
|
9
10
|
/** The products' capabilities this modeller knows: registered descriptors with a namespaced name. */
|
|
10
11
|
export const consumerDescriptors = () => figureCapabilityDescriptors().filter(d => isFigureConsumerCapability(d.name));
|
|
11
12
|
/** The products' capabilities the figure declares that this modeller has no descriptor for: kept, said, removable. */
|
|
@@ -19,6 +20,19 @@ export function needsMissing(descriptor, asset) {
|
|
|
19
20
|
const has = (need) => (need === 'item' ? !!asset.item : names.has(need));
|
|
20
21
|
return descriptor.needs.filter(need => (Array.isArray(need) ? !need.some(has) : !has(need))).map(need => (Array.isArray(need) ? need.join(' | ') : need));
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* What a product's capability needs that the figure lacks, as the tab offers it: the item tag, and the rungs that would
|
|
25
|
+
* give the names it needs (Holdable | FlowNode and Capacity | FlowNode: hold or flow). Its own names are not shown to the
|
|
26
|
+
* author (sweep 2026-10-27 U19); `names` keeps a need no rung gives.
|
|
27
|
+
*/
|
|
28
|
+
export function needsOffered(descriptor, asset) {
|
|
29
|
+
const missing = needsMissing(descriptor, asset);
|
|
30
|
+
const groups = descriptor.needs.filter(need => need !== 'item').map(need => (Array.isArray(need) ? need : [need]));
|
|
31
|
+
const gives = (rung) => groups.every(group => group.some(name => RUNGS[rung].includes(name)));
|
|
32
|
+
const lacking = missing.filter(m => m !== 'item');
|
|
33
|
+
const rungs = lacking.length ? ['hold', 'flow'].filter(gives) : [];
|
|
34
|
+
return { item: missing.includes('item'), rungs, names: lacking.length && !rungs.length ? lacking : [] };
|
|
35
|
+
}
|
|
22
36
|
/** The names with a product's capability put on or taken off; the shared ones as they are. */
|
|
23
37
|
export function namesWith(asset, name, on) {
|
|
24
38
|
const names = asset.document.capabilities.names ?? [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consumer-capabilities.js","sourceRoot":"","sources":["../../client/modeller/consumer-capabilities.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * A product's capabilities in the modeller (ADR-0167 3, 9, 12): the descriptors registered with the scene, which ones\n * the figure wears, what each still needs, and its settings on the asset as its descriptor draws them -- parts picked,\n * an input and a value per fixed key. The descriptor's own check says what the settings lack. Kept apart from the\n * element so it is tested without a browser.\n */\nimport { isFigureConsumerCapability } from '@hatiolab/figure-model'\nimport type { FigureAsset } from '@hatiolab/figure-model'\nimport { figureCapabilityDescriptors, type FigureCapabilityDescriptor, type FigureCapabilitySetting } from '@hatiolab/things-scene'\n\n/** The products' capabilities this modeller knows: registered descriptors with a namespaced name. */\nexport const consumerDescriptors = (): FigureCapabilityDescriptor[] => figureCapabilityDescriptors().filter(d => isFigureConsumerCapability(d.name))\n\n/** The products' capabilities the figure declares that this modeller has no descriptor for: kept, said, removable. */\nexport function unknownConsumerNames(asset: FigureAsset): string[] {\n const known = new Set(consumerDescriptors().map(d => d.name))\n return (asset.document.capabilities.names ?? []).filter(n => isFigureConsumerCapability(n) && !known.has(n))\n}\n\n/** What a capability still needs on this figure: a shared or a product's name not declared, or the item mark. */\nexport function needsMissing(descriptor: FigureCapabilityDescriptor, asset: FigureAsset): string[] {\n const names = new Set(asset.document.capabilities.names ?? [])\n const has = (need: string) => (need === 'item' ? !!asset.item : names.has(need))\n return descriptor.needs.filter(need => (Array.isArray(need) ? !need.some(has) : !has(need as string))).map(need => (Array.isArray(need) ? need.join(' | ') : (need as string)))\n}\n\n/** The names with a product's capability put on or taken off; the shared ones as they are. */\nexport function namesWith(asset: FigureAsset, name: string, on: boolean): string[] {\n const names = asset.document.capabilities.names ?? []\n return on ? [...new Set([...names, name])] : names.filter(n => n !== name)\n}\n\n/** A capability's settings on the asset, or an empty object. */\nexport const settingsOf = (asset: FigureAsset, name: string): Record<string, unknown> => ({ ...(asset.document.capabilities.extensions?.[name] ?? {}) })\n\n/** The parts a parts setting holds. */\nexport function partsAt(settings: Record<string, unknown>, key: string): string[] {\n const v = settings[key]\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string') : []\n}\n\n/** The settings with a parts setting changed; none is left out. */\nexport function withParts(settings: Record<string, unknown>, key: string, parts: string[]): Record<string, unknown> {\n const next = { ...settings }\n if (parts.length) next[key] = [...parts]\n else delete next[key]\n return next\n}\n\n/** An input table as held: the input, and a value per fixed key (the descriptor's start where none is given yet). */\nexport function inputTableAt(settings: Record<string, unknown>, setting: Extract<FigureCapabilitySetting, { kind: 'input-table' }>): { input?: string; values: Record<string, number> } {\n const v = settings[setting.key] as { input?: unknown; values?: Record<string, unknown> } | undefined\n const values = Object.fromEntries(setting.keys.map(k => [k, typeof v?.values?.[k] === 'number' ? (v.values[k] as number) : setting.start[k]!]))\n return { ...(typeof v?.input === 'string' ? { input: v.input } : {}), values }\n}\n\n/** The settings with an input table changed: an input chosen writes every fixed key's value; none takes the table away. */\nexport function withInputTable(settings: Record<string, unknown>, setting: Extract<FigureCapabilitySetting, { kind: 'input-table' }>, input: string | undefined, values: Record<string, number>): Record<string, unknown> {\n const next = { ...settings }\n if (input) next[setting.key] = { input, values: Object.fromEntries(setting.keys.map(k => [k, values[k] ?? setting.start[k]!])) }\n else delete next[setting.key]\n return next\n}\n\n/** What the capability's own check says of the settings now; nothing when it has no check. */\nexport const settingsIssues = (descriptor: FigureCapabilityDescriptor, asset: FigureAsset): string[] => descriptor.check?.(asset.document.capabilities.extensions?.[descriptor.name], asset) ?? []\n"]}
|
|
1
|
+
{"version":3,"file":"consumer-capabilities.js","sourceRoot":"","sources":["../../client/modeller/consumer-capabilities.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * A product's capabilities in the modeller (ADR-0167 3, 9, 12): the descriptors registered with the scene, which ones\n * the figure wears, what each still needs, and its settings on the asset as its descriptor draws them -- parts picked,\n * an input and a value per fixed key. The descriptor's own check says what the settings lack. Kept apart from the\n * element so it is tested without a browser.\n */\nimport { isFigureConsumerCapability } from '@hatiolab/figure-model'\nimport type { FigureAsset } from '@hatiolab/figure-model'\nimport { figureCapabilityDescriptors, type FigureCapabilityDescriptor, type FigureCapabilitySetting } from '@hatiolab/things-scene'\nimport { RUNGS, type Rung } from '@things-factory/figure-viewer/dist-client/capability-rung.js'\n\n/** The products' capabilities this modeller knows: registered descriptors with a namespaced name. */\nexport const consumerDescriptors = (): FigureCapabilityDescriptor[] => figureCapabilityDescriptors().filter(d => isFigureConsumerCapability(d.name))\n\n/** The products' capabilities the figure declares that this modeller has no descriptor for: kept, said, removable. */\nexport function unknownConsumerNames(asset: FigureAsset): string[] {\n const known = new Set(consumerDescriptors().map(d => d.name))\n return (asset.document.capabilities.names ?? []).filter(n => isFigureConsumerCapability(n) && !known.has(n))\n}\n\n/** What a capability still needs on this figure: a shared or a product's name not declared, or the item mark. */\nexport function needsMissing(descriptor: FigureCapabilityDescriptor, asset: FigureAsset): string[] {\n const names = new Set(asset.document.capabilities.names ?? [])\n const has = (need: string) => (need === 'item' ? !!asset.item : names.has(need))\n return descriptor.needs.filter(need => (Array.isArray(need) ? !need.some(has) : !has(need as string))).map(need => (Array.isArray(need) ? need.join(' | ') : (need as string)))\n}\n\n/**\n * What a product's capability needs that the figure lacks, as the tab offers it: the item tag, and the rungs that would\n * give the names it needs (Holdable | FlowNode and Capacity | FlowNode: hold or flow). Its own names are not shown to the\n * author (sweep 2026-10-27 U19); `names` keeps a need no rung gives.\n */\nexport function needsOffered(descriptor: FigureCapabilityDescriptor, asset: FigureAsset): { item: boolean; rungs: Rung[]; names: string[] } {\n const missing = needsMissing(descriptor, asset)\n const groups = descriptor.needs.filter(need => need !== 'item').map(need => (Array.isArray(need) ? need : [need as string]))\n const gives = (rung: Rung) => groups.every(group => group.some(name => (RUNGS[rung] as readonly string[]).includes(name)))\n const lacking = missing.filter(m => m !== 'item')\n const rungs = lacking.length ? (['hold', 'flow'] as const).filter(gives) : []\n return { item: missing.includes('item'), rungs, names: lacking.length && !rungs.length ? lacking : [] }\n}\n\n/** The names with a product's capability put on or taken off; the shared ones as they are. */\nexport function namesWith(asset: FigureAsset, name: string, on: boolean): string[] {\n const names = asset.document.capabilities.names ?? []\n return on ? [...new Set([...names, name])] : names.filter(n => n !== name)\n}\n\n/** A capability's settings on the asset, or an empty object. */\nexport const settingsOf = (asset: FigureAsset, name: string): Record<string, unknown> => ({ ...(asset.document.capabilities.extensions?.[name] ?? {}) })\n\n/** The parts a parts setting holds. */\nexport function partsAt(settings: Record<string, unknown>, key: string): string[] {\n const v = settings[key]\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string') : []\n}\n\n/** The settings with a parts setting changed; none is left out. */\nexport function withParts(settings: Record<string, unknown>, key: string, parts: string[]): Record<string, unknown> {\n const next = { ...settings }\n if (parts.length) next[key] = [...parts]\n else delete next[key]\n return next\n}\n\n/** An input table as held: the input, and a value per fixed key (the descriptor's start where none is given yet). */\nexport function inputTableAt(settings: Record<string, unknown>, setting: Extract<FigureCapabilitySetting, { kind: 'input-table' }>): { input?: string; values: Record<string, number> } {\n const v = settings[setting.key] as { input?: unknown; values?: Record<string, unknown> } | undefined\n const values = Object.fromEntries(setting.keys.map(k => [k, typeof v?.values?.[k] === 'number' ? (v.values[k] as number) : setting.start[k]!]))\n return { ...(typeof v?.input === 'string' ? { input: v.input } : {}), values }\n}\n\n/** The settings with an input table changed: an input chosen writes every fixed key's value; none takes the table away. */\nexport function withInputTable(settings: Record<string, unknown>, setting: Extract<FigureCapabilitySetting, { kind: 'input-table' }>, input: string | undefined, values: Record<string, number>): Record<string, unknown> {\n const next = { ...settings }\n if (input) next[setting.key] = { input, values: Object.fromEntries(setting.keys.map(k => [k, values[k] ?? setting.start[k]!])) }\n else delete next[setting.key]\n return next\n}\n\n/** What the capability's own check says of the settings now; nothing when it has no check. */\nexport const settingsIssues = (descriptor: FigureCapabilityDescriptor, asset: FigureAsset): string[] => descriptor.check?.(asset.document.capabilities.extensions?.[descriptor.name], asset) ?? []\n"]}
|
|
@@ -18,7 +18,8 @@ export const EditorStyles = css `
|
|
|
18
18
|
/* The bar over the work is ox-canvas-bar (ADR-0060 decision 8): the folds at its edges, undo, redo and
|
|
19
19
|
fit in the suite's places. What the editor puts in it - the mode switch, the status, save - is styled here. */
|
|
20
20
|
ox-canvas-bar button[mode] {
|
|
21
|
-
height
|
|
21
|
+
/* The bar's height, beside its 32px icons and the save (sweep 2026-10-27 U25). */
|
|
22
|
+
height: 32px;
|
|
22
23
|
border: 1px solid transparent;
|
|
23
24
|
padding: 0 10px;
|
|
24
25
|
border-radius: 999em;
|
|
@@ -206,6 +207,22 @@ export const EditorStyles = css `
|
|
|
206
207
|
margin-top: 4px;
|
|
207
208
|
color: var(--md-sys-color-on-surface-variant);
|
|
208
209
|
}
|
|
210
|
+
div[start] button[start-more] {
|
|
211
|
+
display: block;
|
|
212
|
+
text-align: left;
|
|
213
|
+
margin: 4px 0 0;
|
|
214
|
+
padding: 0;
|
|
215
|
+
font: inherit;
|
|
216
|
+
font-size: var(--ui-t-small);
|
|
217
|
+
color: var(--md-sys-color-primary);
|
|
218
|
+
background: none;
|
|
219
|
+
border: none;
|
|
220
|
+
cursor: pointer;
|
|
221
|
+
}
|
|
222
|
+
/* No rule under the last way to start: it closed nothing (sweep 2026-10-27 U23). */
|
|
223
|
+
div[start-paths] > section:last-of-type {
|
|
224
|
+
border-bottom: none;
|
|
225
|
+
}
|
|
209
226
|
/* The side: tabs over one panel at a time, each using the whole height. */
|
|
210
227
|
/*
|
|
211
228
|
The side's words at the panel's size (side-panel-styles: titles body, the rest small). The editor's host is at body
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-styles.js","sourceRoot":"","sources":["../../client/modeller/editor-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,gHAAgH;AAChH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6a9B,CAAA","sourcesContent":["import { css } from 'lit'\n\n/** The modeller editor's own styles: the bar's tools, the panes, the start card, the side's tabs and panels. */\nexport const EditorStyles = css`\n :host {\n position: relative;\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n /* The editor folds by the width it is given, not the window's (UX ruling, 2026-09-24). */\n container-type: inline-size;\n /* The app's one face (its typography theme's --theme-font) at the body size: a bare sans-serif here put a second\n face under everything the editor draws, beside the parts list and the shell in the app's own (2026-10-16). */\n font-family: var(--theme-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-t-body, 13px);\n color: var(--md-sys-color-on-surface);\n }\n /* The bar over the work is ox-canvas-bar (ADR-0060 decision 8): the folds at its edges, undo, redo and\n fit in the suite's places. What the editor puts in it - the mode switch, the status, save - is styled here. */\n ox-canvas-bar button[mode] {\n height: 28px;\n border: 1px solid transparent;\n padding: 0 10px;\n border-radius: 999em;\n border-color: transparent;\n background: none;\n }\n ox-canvas-bar button[mode][aria-pressed='true'] {\n border-color: var(--md-sys-color-primary);\n }\n /* The gizmo's tools, drawn as the bar's own icon buttons (undo, redo, fit). */\n ox-canvas-bar .gizmo-tools {\n display: inline-flex;\n gap: 2px;\n padding-left: 4px;\n border-left: 1px solid var(--md-sys-color-outline-variant);\n }\n ox-canvas-bar button[tool] {\n display: inline-grid;\n place-items: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: none;\n border-radius: var(--md-sys-shape-corner-small, 8px);\n background: transparent;\n color: var(--md-sys-color-on-surface-variant);\n cursor: pointer;\n }\n ox-canvas-bar button[tool]:hover:not(:disabled) {\n background: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface);\n }\n ox-canvas-bar button[tool][aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n ox-canvas-bar button[tool]:disabled {\n opacity: 0.38;\n cursor: default;\n }\n /*\n Slotted into the bar from here, the tools do not get the bar's own icon size (20px, set inside its shadow): they\n showed at md-icon's 24px, heavier than undo, redo and fit beside them. The same size, and the bar's focus ring.\n */\n ox-canvas-bar button[tool] md-icon {\n --md-icon-size: 20px;\n }\n ox-canvas-bar button[tool]:focus-visible {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 1px;\n }\n figure-preview {\n min-width: 0;\n min-height: 0;\n }\n /* The edit canvas stays standing while the preview is in front (a 3D scene costs a third of a second to make), out of sight. */\n div[middle][hidden] {\n display: none;\n }\n /* The preview, once made, stays standing out of sight too: its 3D scene is not made again each time it is asked for. */\n figure-preview[hidden] {\n display: none;\n }\n ox-canvas-bar .status {\n padding: 0 8px;\n color: var(--md-sys-color-on-surface-variant);\n }\n /*\n The bar's two commitments, one shape (user 2026-10-18: 「발행버튼도 저장버튼도 라운드 버튼으로 … 버튼 안에 적절한 아이콘」):\n round, an icon inside, so neither is taken for the other. Save, done many times a day, is the filled one; release,\n a promise to whoever draws the figure, is outlined.\n */\n ox-canvas-bar button[save],\n ox-canvas-bar button[publish] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 32px;\n padding: 0 14px 0 10px;\n border-radius: 999em;\n font-weight: 600;\n }\n ox-canvas-bar button[save] md-icon,\n ox-canvas-bar button[publish] md-icon {\n --md-icon-size: 18px;\n }\n ox-canvas-bar button[save] {\n border: 1px solid transparent;\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n }\n /* The three panes are ox-three-pane's (ADR-0060 decision 8): widths 200 | 1fr | 340, 160 | 300 under\n 860px and one column with the middle first under 560px are the frame's, as are the dividers. */\n ox-three-pane {\n flex: 1;\n min-height: 0;\n }\n figure-parts {\n min-height: 0;\n }\n figure-canvas {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n div[middle] {\n position: relative;\n display: flex;\n min-width: 0;\n min-height: 0;\n }\n /* Which size handles change one diameter (ADR-0140 4.1): a quiet line over the canvas, out of the gizmo's way. */\n p[same-measure] {\n position: absolute;\n bottom: 12px;\n left: 50%;\n z-index: 5;\n transform: translateX(-50%);\n margin: 0;\n padding: 4px 10px;\n border-radius: 999em;\n background: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface-variant);\n font-size: var(--ui-t-small, 11.5px);\n pointer-events: none;\n white-space: nowrap;\n }\n /* A new figure's first question, over the empty canvas. */\n div[start] {\n position: absolute;\n inset: 50% auto auto 50%;\n z-index: 10;\n color: var(--md-sys-color-on-surface);\n transform: translate(-50%, -50%);\n width: min(440px, calc(100% - 32px));\n box-sizing: border-box;\n padding: 20px;\n border-radius: 12px;\n background: var(--md-sys-color-surface, #fff);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);\n }\n div[start] button[start-button] {\n width: auto;\n margin: 0;\n font-size: var(--ui-t-body);\n height: 36px;\n padding: 0 16px;\n border: 1px solid var(--md-sys-color-outline);\n border-radius: 999em;\n color: var(--md-sys-color-primary);\n background: var(--md-sys-color-surface, #fff);\n cursor: pointer;\n }\n div[start] button[start-button]:hover:not(:disabled) {\n background: var(--md-sys-color-primary-container);\n }\n div[start] button[start-button][pressed] {\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n border-color: var(--md-sys-color-primary);\n }\n div[start] button[start-button]:disabled {\n opacity: 0.5;\n cursor: default;\n }\n div[start] h2 {\n margin: 0 0 12px;\n font-size: var(--ui-t-title);\n }\n div[start-paths] {\n display: grid;\n gap: 14px;\n }\n div[start-paths] h3 {\n margin: 0 0 6px;\n font-size: var(--ui-t-body);\n }\n div[start-shapes] {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n }\n div[start] small {\n display: block;\n margin-top: 4px;\n color: var(--md-sys-color-on-surface-variant);\n }\n /* The side: tabs over one panel at a time, each using the whole height. */\n /*\n The side's words at the panel's size (side-panel-styles: titles body, the rest small). The editor's host is at body\n for the bar, and the fields the editor drew itself -- the part's name -- stood a step larger than every other field.\n */\n aside {\n display: flex;\n flex-direction: column;\n min-height: 0;\n font-size: var(--ui-t-small, 11.5px);\n background: var(--md-sys-color-surface-container-lowest);\n }\n div[tabs] {\n display: flex;\n flex: none;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n button[tab] {\n flex: 1;\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 1px;\n min-width: 0;\n height: 48px;\n padding: 0;\n border: none;\n border-radius: 0;\n border-bottom: 2px solid transparent;\n background: none;\n color: var(--md-sys-color-on-surface-variant);\n }\n button[tab]:hover:not([on]) {\n background: var(--md-sys-color-surface-container);\n }\n button[tab][on] {\n color: var(--md-sys-color-primary);\n border-bottom-color: var(--md-sys-color-primary);\n }\n button[tab] md-icon {\n font-size: 20px;\n width: 20px;\n height: 20px;\n }\n button[tab] span[tab-label] {\n max-width: 100%;\n overflow: hidden;\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.2;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n /* A range's two ends read as one: smallest – largest. */\n div[pair][range] {\n align-items: center;\n }\n div[pair][range] > span[dash] {\n flex: none;\n color: var(--md-sys-color-on-surface-variant);\n }\n /* The sections inside carry the margins (side-panel-styles); the panel only scrolls. */\n div[panel] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 0;\n }\n div[panel][flush] {\n padding: 0;\n }\n /* Hidden, not removed: a panel keeps what it holds (the verdict its last answer) while another tab is open. */\n [off] {\n display: none !important;\n }\n /* The part's own numbers inside a group the links draw (size, place): one column, the panel's gap. */\n div[group-fields] {\n display: grid;\n gap: 6px;\n }\n /* The folded 「더 보기」: its title is the fold's handle, at the title's size. */\n section[more] > details > summary {\n padding: 0;\n }\n section[more] > details > summary > h3 {\n margin: 0;\n }\n div[more-body] {\n display: grid;\n gap: 16px;\n margin-top: 12px;\n }\n /*\n The view settings and the release history, opened from the bar over the right of the work: neither is about the\n part chosen, so neither takes a tab beside the part's (chief architect 2026-10-16). The fastening window stands in\n the same place, over the side panel, so the canvas it previews on stays in sight (user 2026-10-19).\n */\n :host {\n position: relative;\n }\n div[side-overlay] {\n position: absolute;\n top: var(--figure-bar-height, 44px);\n right: 8px;\n z-index: 5;\n display: flex;\n flex-direction: column;\n width: min(360px, calc(100% - 16px));\n max-height: calc(100% - var(--figure-bar-height, 44px) - 16px);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);\n }\n div[overlay-head] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 8px 8px 12px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n div[overlay-head] h3 {\n margin: 0;\n }\n div[overlay-head] button[quiet] {\n margin-left: 0;\n }\n [overlay-body] {\n min-height: 0;\n overflow: auto;\n }\n /* Which part the panel speaks of. Above z-index 1: a chip's label (md-filter-chip) sits at 1 and, coming later, was\n drawn over it when the panel scrolled (sweep 2026-10-27 U2). */\n div[subject] {\n position: sticky;\n top: 0;\n z-index: 2;\n display: grid;\n gap: 4px;\n padding: 10px 12px;\n background: var(--md-sys-color-surface-container-lowest);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n /* The panel's three sizes (side-panel-styles): the part's kind at the title's, its two lines at the hint's. */\n div[subject-summary] {\n display: grid;\n gap: 2px;\n }\n div[subject-summary] b {\n font-size: var(--ui-t-body, 13px);\n font-weight: 600;\n }\n div[subject-summary] b span {\n font-weight: 400;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[subject-summary] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n /*\n No rule here draws every button as a box: the side's buttons take the shared panel rules (SidePanelStyles), one\n height with the fields beside them, and only the bar's publish keeps an outline (user 2026-10-16: 「버튼 박스도\n 과하게 사용되어서」 -- a bare 「button」 rule had put a 32px box round every button the editor drew).\n */\n button {\n font: inherit;\n box-sizing: border-box;\n color: inherit;\n min-width: 0;\n cursor: pointer;\n }\n /* What blocks release, beside 「발행」: one red word button, and its list under the bar. */\n ox-canvas-bar button[release-blocked] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 32px;\n padding: 0 14px 0 10px;\n border: none;\n /* Round as save and release beside it: one shape for the bar's end. */\n border-radius: 999em;\n color: var(--md-sys-color-on-error-container);\n background: var(--md-sys-color-error-container);\n }\n ox-canvas-bar button[release-blocked] md-icon {\n --md-icon-size: 18px;\n }\n div[release-blockers] {\n position: absolute;\n top: 46px;\n right: 8px;\n z-index: 6;\n width: min(340px, calc(100% - 16px));\n padding: 8px 10px 10px;\n box-sizing: border-box;\n background: var(--md-sys-color-surface);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n box-shadow: var(--md-sys-elevation-2, 0 2px 8px #0003);\n }\n ox-canvas-bar button[publish] {\n border: 1px solid var(--md-sys-color-outline);\n color: var(--md-sys-color-primary);\n background: var(--md-sys-color-surface);\n }\n ox-canvas-bar button[publish][aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n button:disabled {\n opacity: 0.45;\n cursor: default;\n }\n button[aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n border-color: var(--md-sys-color-primary);\n }\n .hint {\n color: var(--md-sys-color-on-surface-variant);\n line-height: 1.5;\n }\n`\n"]}
|
|
1
|
+
{"version":3,"file":"editor-styles.js","sourceRoot":"","sources":["../../client/modeller/editor-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,gHAAgH;AAChH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8b9B,CAAA","sourcesContent":["import { css } from 'lit'\n\n/** The modeller editor's own styles: the bar's tools, the panes, the start card, the side's tabs and panels. */\nexport const EditorStyles = css`\n :host {\n position: relative;\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n /* The editor folds by the width it is given, not the window's (UX ruling, 2026-09-24). */\n container-type: inline-size;\n /* The app's one face (its typography theme's --theme-font) at the body size: a bare sans-serif here put a second\n face under everything the editor draws, beside the parts list and the shell in the app's own (2026-10-16). */\n font-family: var(--theme-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-t-body, 13px);\n color: var(--md-sys-color-on-surface);\n }\n /* The bar over the work is ox-canvas-bar (ADR-0060 decision 8): the folds at its edges, undo, redo and\n fit in the suite's places. What the editor puts in it - the mode switch, the status, save - is styled here. */\n ox-canvas-bar button[mode] {\n /* The bar's height, beside its 32px icons and the save (sweep 2026-10-27 U25). */\n height: 32px;\n border: 1px solid transparent;\n padding: 0 10px;\n border-radius: 999em;\n border-color: transparent;\n background: none;\n }\n ox-canvas-bar button[mode][aria-pressed='true'] {\n border-color: var(--md-sys-color-primary);\n }\n /* The gizmo's tools, drawn as the bar's own icon buttons (undo, redo, fit). */\n ox-canvas-bar .gizmo-tools {\n display: inline-flex;\n gap: 2px;\n padding-left: 4px;\n border-left: 1px solid var(--md-sys-color-outline-variant);\n }\n ox-canvas-bar button[tool] {\n display: inline-grid;\n place-items: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: none;\n border-radius: var(--md-sys-shape-corner-small, 8px);\n background: transparent;\n color: var(--md-sys-color-on-surface-variant);\n cursor: pointer;\n }\n ox-canvas-bar button[tool]:hover:not(:disabled) {\n background: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface);\n }\n ox-canvas-bar button[tool][aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n ox-canvas-bar button[tool]:disabled {\n opacity: 0.38;\n cursor: default;\n }\n /*\n Slotted into the bar from here, the tools do not get the bar's own icon size (20px, set inside its shadow): they\n showed at md-icon's 24px, heavier than undo, redo and fit beside them. The same size, and the bar's focus ring.\n */\n ox-canvas-bar button[tool] md-icon {\n --md-icon-size: 20px;\n }\n ox-canvas-bar button[tool]:focus-visible {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 1px;\n }\n figure-preview {\n min-width: 0;\n min-height: 0;\n }\n /* The edit canvas stays standing while the preview is in front (a 3D scene costs a third of a second to make), out of sight. */\n div[middle][hidden] {\n display: none;\n }\n /* The preview, once made, stays standing out of sight too: its 3D scene is not made again each time it is asked for. */\n figure-preview[hidden] {\n display: none;\n }\n ox-canvas-bar .status {\n padding: 0 8px;\n color: var(--md-sys-color-on-surface-variant);\n }\n /*\n The bar's two commitments, one shape (user 2026-10-18: 「발행버튼도 저장버튼도 라운드 버튼으로 … 버튼 안에 적절한 아이콘」):\n round, an icon inside, so neither is taken for the other. Save, done many times a day, is the filled one; release,\n a promise to whoever draws the figure, is outlined.\n */\n ox-canvas-bar button[save],\n ox-canvas-bar button[publish] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 32px;\n padding: 0 14px 0 10px;\n border-radius: 999em;\n font-weight: 600;\n }\n ox-canvas-bar button[save] md-icon,\n ox-canvas-bar button[publish] md-icon {\n --md-icon-size: 18px;\n }\n ox-canvas-bar button[save] {\n border: 1px solid transparent;\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n }\n /* The three panes are ox-three-pane's (ADR-0060 decision 8): widths 200 | 1fr | 340, 160 | 300 under\n 860px and one column with the middle first under 560px are the frame's, as are the dividers. */\n ox-three-pane {\n flex: 1;\n min-height: 0;\n }\n figure-parts {\n min-height: 0;\n }\n figure-canvas {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n div[middle] {\n position: relative;\n display: flex;\n min-width: 0;\n min-height: 0;\n }\n /* Which size handles change one diameter (ADR-0140 4.1): a quiet line over the canvas, out of the gizmo's way. */\n p[same-measure] {\n position: absolute;\n bottom: 12px;\n left: 50%;\n z-index: 5;\n transform: translateX(-50%);\n margin: 0;\n padding: 4px 10px;\n border-radius: 999em;\n background: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface-variant);\n font-size: var(--ui-t-small, 11.5px);\n pointer-events: none;\n white-space: nowrap;\n }\n /* A new figure's first question, over the empty canvas. */\n div[start] {\n position: absolute;\n inset: 50% auto auto 50%;\n z-index: 10;\n color: var(--md-sys-color-on-surface);\n transform: translate(-50%, -50%);\n width: min(440px, calc(100% - 32px));\n box-sizing: border-box;\n padding: 20px;\n border-radius: 12px;\n background: var(--md-sys-color-surface, #fff);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);\n }\n div[start] button[start-button] {\n width: auto;\n margin: 0;\n font-size: var(--ui-t-body);\n height: 36px;\n padding: 0 16px;\n border: 1px solid var(--md-sys-color-outline);\n border-radius: 999em;\n color: var(--md-sys-color-primary);\n background: var(--md-sys-color-surface, #fff);\n cursor: pointer;\n }\n div[start] button[start-button]:hover:not(:disabled) {\n background: var(--md-sys-color-primary-container);\n }\n div[start] button[start-button][pressed] {\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n border-color: var(--md-sys-color-primary);\n }\n div[start] button[start-button]:disabled {\n opacity: 0.5;\n cursor: default;\n }\n div[start] h2 {\n margin: 0 0 12px;\n font-size: var(--ui-t-title);\n }\n div[start-paths] {\n display: grid;\n gap: 14px;\n }\n div[start-paths] h3 {\n margin: 0 0 6px;\n font-size: var(--ui-t-body);\n }\n div[start-shapes] {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n }\n div[start] small {\n display: block;\n margin-top: 4px;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[start] button[start-more] {\n display: block;\n text-align: left;\n margin: 4px 0 0;\n padding: 0;\n font: inherit;\n font-size: var(--ui-t-small);\n color: var(--md-sys-color-primary);\n background: none;\n border: none;\n cursor: pointer;\n }\n /* No rule under the last way to start: it closed nothing (sweep 2026-10-27 U23). */\n div[start-paths] > section:last-of-type {\n border-bottom: none;\n }\n /* The side: tabs over one panel at a time, each using the whole height. */\n /*\n The side's words at the panel's size (side-panel-styles: titles body, the rest small). The editor's host is at body\n for the bar, and the fields the editor drew itself -- the part's name -- stood a step larger than every other field.\n */\n aside {\n display: flex;\n flex-direction: column;\n min-height: 0;\n font-size: var(--ui-t-small, 11.5px);\n background: var(--md-sys-color-surface-container-lowest);\n }\n div[tabs] {\n display: flex;\n flex: none;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n button[tab] {\n flex: 1;\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 1px;\n min-width: 0;\n height: 48px;\n padding: 0;\n border: none;\n border-radius: 0;\n border-bottom: 2px solid transparent;\n background: none;\n color: var(--md-sys-color-on-surface-variant);\n }\n button[tab]:hover:not([on]) {\n background: var(--md-sys-color-surface-container);\n }\n button[tab][on] {\n color: var(--md-sys-color-primary);\n border-bottom-color: var(--md-sys-color-primary);\n }\n button[tab] md-icon {\n font-size: 20px;\n width: 20px;\n height: 20px;\n }\n button[tab] span[tab-label] {\n max-width: 100%;\n overflow: hidden;\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.2;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n /* A range's two ends read as one: smallest – largest. */\n div[pair][range] {\n align-items: center;\n }\n div[pair][range] > span[dash] {\n flex: none;\n color: var(--md-sys-color-on-surface-variant);\n }\n /* The sections inside carry the margins (side-panel-styles); the panel only scrolls. */\n div[panel] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 0;\n }\n div[panel][flush] {\n padding: 0;\n }\n /* Hidden, not removed: a panel keeps what it holds (the verdict its last answer) while another tab is open. */\n [off] {\n display: none !important;\n }\n /* The part's own numbers inside a group the links draw (size, place): one column, the panel's gap. */\n div[group-fields] {\n display: grid;\n gap: 6px;\n }\n /* The folded 「더 보기」: its title is the fold's handle, at the title's size. */\n section[more] > details > summary {\n padding: 0;\n }\n section[more] > details > summary > h3 {\n margin: 0;\n }\n div[more-body] {\n display: grid;\n gap: 16px;\n margin-top: 12px;\n }\n /*\n The view settings and the release history, opened from the bar over the right of the work: neither is about the\n part chosen, so neither takes a tab beside the part's (chief architect 2026-10-16). The fastening window stands in\n the same place, over the side panel, so the canvas it previews on stays in sight (user 2026-10-19).\n */\n :host {\n position: relative;\n }\n div[side-overlay] {\n position: absolute;\n top: var(--figure-bar-height, 44px);\n right: 8px;\n z-index: 5;\n display: flex;\n flex-direction: column;\n width: min(360px, calc(100% - 16px));\n max-height: calc(100% - var(--figure-bar-height, 44px) - 16px);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);\n }\n div[overlay-head] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 8px 8px 12px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n div[overlay-head] h3 {\n margin: 0;\n }\n div[overlay-head] button[quiet] {\n margin-left: 0;\n }\n [overlay-body] {\n min-height: 0;\n overflow: auto;\n }\n /* Which part the panel speaks of. Above z-index 1: a chip's label (md-filter-chip) sits at 1 and, coming later, was\n drawn over it when the panel scrolled (sweep 2026-10-27 U2). */\n div[subject] {\n position: sticky;\n top: 0;\n z-index: 2;\n display: grid;\n gap: 4px;\n padding: 10px 12px;\n background: var(--md-sys-color-surface-container-lowest);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n /* The panel's three sizes (side-panel-styles): the part's kind at the title's, its two lines at the hint's. */\n div[subject-summary] {\n display: grid;\n gap: 2px;\n }\n div[subject-summary] b {\n font-size: var(--ui-t-body, 13px);\n font-weight: 600;\n }\n div[subject-summary] b span {\n font-weight: 400;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[subject-summary] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n /*\n No rule here draws every button as a box: the side's buttons take the shared panel rules (SidePanelStyles), one\n height with the fields beside them, and only the bar's publish keeps an outline (user 2026-10-16: 「버튼 박스도\n 과하게 사용되어서」 -- a bare 「button」 rule had put a 32px box round every button the editor drew).\n */\n button {\n font: inherit;\n box-sizing: border-box;\n color: inherit;\n min-width: 0;\n cursor: pointer;\n }\n /* What blocks release, beside 「발행」: one red word button, and its list under the bar. */\n ox-canvas-bar button[release-blocked] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 32px;\n padding: 0 14px 0 10px;\n border: none;\n /* Round as save and release beside it: one shape for the bar's end. */\n border-radius: 999em;\n color: var(--md-sys-color-on-error-container);\n background: var(--md-sys-color-error-container);\n }\n ox-canvas-bar button[release-blocked] md-icon {\n --md-icon-size: 18px;\n }\n div[release-blockers] {\n position: absolute;\n top: 46px;\n right: 8px;\n z-index: 6;\n width: min(340px, calc(100% - 16px));\n padding: 8px 10px 10px;\n box-sizing: border-box;\n background: var(--md-sys-color-surface);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n box-shadow: var(--md-sys-elevation-2, 0 2px 8px #0003);\n }\n ox-canvas-bar button[publish] {\n border: 1px solid var(--md-sys-color-outline);\n color: var(--md-sys-color-primary);\n background: var(--md-sys-color-surface);\n }\n ox-canvas-bar button[publish][aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n button:disabled {\n opacity: 0.45;\n cursor: default;\n }\n button[aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n border-color: var(--md-sys-color-primary);\n }\n .hint {\n color: var(--md-sys-color-on-surface-variant);\n line-height: 1.5;\n }\n`\n"]}
|
|
@@ -2,6 +2,8 @@ var FigureEditor_1;
|
|
|
2
2
|
import { __decorate, __metadata } from "tslib";
|
|
3
3
|
import { LitElement, html, nothing } from 'lit';
|
|
4
4
|
import { i18next } from '@operato/i18n';
|
|
5
|
+
import { isMacOS } from '@operato/utils';
|
|
6
|
+
import { unitWord } from './unit-words.js';
|
|
5
7
|
import '@operato/layout';
|
|
6
8
|
import { openPopup } from '@operato/layout';
|
|
7
9
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
@@ -893,6 +895,14 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
893
895
|
<summary><h3>${i18next.t('figure.label.group-more')}</h3></summary>
|
|
894
896
|
<div more-body>
|
|
895
897
|
${tools ? html `<div group-fields>${tools}</div>` : nothing}
|
|
898
|
+
<figure-material-panel
|
|
899
|
+
show="more"
|
|
900
|
+
.body=${{ ...part, id: this.selected }}
|
|
901
|
+
.palette=${palette}
|
|
902
|
+
?locked=${unavailable}
|
|
903
|
+
@part-edit=${(e) => this.run(() => this.session.edit(e.detail.edit))}
|
|
904
|
+
@part-live=${(e) => this.hold({}, e.detail.edit)}
|
|
905
|
+
></figure-material-panel>
|
|
896
906
|
<figure-section-panel
|
|
897
907
|
embedded
|
|
898
908
|
.body=${{ ...part, id: this.selected }}
|
|
@@ -904,14 +914,6 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
904
914
|
@command-live=${(e) => this.holdAction(e.detail.action)}
|
|
905
915
|
@section-editor-open=${() => (this.sectionEditing = this.selected)}
|
|
906
916
|
></figure-section-panel>
|
|
907
|
-
<figure-material-panel
|
|
908
|
-
show="more"
|
|
909
|
-
.body=${{ ...part, id: this.selected }}
|
|
910
|
-
.palette=${palette}
|
|
911
|
-
?locked=${unavailable}
|
|
912
|
-
@part-edit=${(e) => this.run(() => this.session.edit(e.detail.edit))}
|
|
913
|
-
@part-live=${(e) => this.hold({}, e.detail.edit)}
|
|
914
|
-
></figure-material-panel>
|
|
915
917
|
</div>
|
|
916
918
|
</details>
|
|
917
919
|
</section>`;
|
|
@@ -1165,7 +1167,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1165
1167
|
return html `<section board-size>
|
|
1166
1168
|
${title('figure.text.board-size-range-explained')}
|
|
1167
1169
|
${['x', 'y', 'z'].map(axis => html `<div inline-field>
|
|
1168
|
-
<label>${i18next.t('figure.label.resize-' + axis)} <span unit>mm</span></label>
|
|
1170
|
+
<label>${i18next.t('figure.label.resize-' + axis)} <span unit>(mm)</span></label>
|
|
1169
1171
|
<div pair range>
|
|
1170
1172
|
${['min', 'max'].map((bound, i) => html `${i ? html `<span dash aria-hidden="true">–</span>` : nothing}<input
|
|
1171
1173
|
aria-label=${`size.${axis}.${bound}`}
|
|
@@ -1297,7 +1299,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1297
1299
|
<div inline-field><label>${i18next.t('figure.label.repeat-axis')}</label>${axes.length ? html `<select aria-label=${i18next.t('figure.label.repeat-axis')} ?disabled=${unavailable} .value=${live(axis)} @change=${(e) => update({ axis: e.target.value.toUpperCase() })}>${axes.map(a => html `<option value=${a} ?selected=${a === axis}>${a.toUpperCase()}</option>`)}</select>` : html `<span value>${axis.toUpperCase()}</span>`}</div>
|
|
1298
1300
|
<div inline-field><label>${i18next.t('figure.label.repeat-count')}</label>${!fits && record ? html `<input type="number" min="1" step="1" aria-label=${i18next.t('figure.label.repeat-count')} .value=${live(String(repeat.count))} ?disabled=${unavailable} @change=${(e) => update({ count: e.target.valueAsNumber })}>` : html `<span value>${repeat.count}</span>`}</div>
|
|
1299
1301
|
<div inline-field>
|
|
1300
|
-
<label>${i18next.t('figure.label.repeat-gap')} <span unit>mm</span></label>
|
|
1302
|
+
<label>${i18next.t('figure.label.repeat-gap')} <span unit>(mm)</span></label>
|
|
1301
1303
|
${fits ? html `<input
|
|
1302
1304
|
type="number"
|
|
1303
1305
|
step="any"
|
|
@@ -1391,8 +1393,8 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1391
1393
|
</label>
|
|
1392
1394
|
${open
|
|
1393
1395
|
? html `<div inline-field><label>${i18next.t('figure.label.board-name')}</label><input aria-label="figure.label.board-name" .value=${form.label} @change=${edit('label')} /></div>
|
|
1394
|
-
<div inline-field><label>${i18next.t('figure.label.least')} <span unit>mm</span></label><input type="number" step="any" aria-label="figure.label.least" .value=${String(form.min)} @change=${edit('min')} /></div>
|
|
1395
|
-
<div inline-field><label>${i18next.t('figure.label.most')} <span unit>mm</span></label><input type="number" step="any" aria-label="figure.label.most" .value=${String(form.max)} @change=${edit('max')} /></div>
|
|
1396
|
+
<div inline-field><label>${i18next.t('figure.label.least')} <span unit>(mm)</span></label><input type="number" step="any" aria-label="figure.label.least" .value=${String(form.min)} @change=${edit('min')} /></div>
|
|
1397
|
+
<div inline-field><label>${i18next.t('figure.label.most')} <span unit>(mm)</span></label><input type="number" step="any" aria-label="figure.label.most" .value=${String(form.max)} @change=${edit('max')} /></div>
|
|
1396
1398
|
<button act publish-pitch ?disabled=${unavailable || !form.label.trim()} @click=${() => run(steps())}>
|
|
1397
1399
|
<md-icon>${listed ? 'check' : 'ios_share'}</md-icon>${i18next.t(listed ? 'figure.button.apply' : 'figure.button.publish-input')}
|
|
1398
1400
|
</button>`
|
|
@@ -1627,9 +1629,12 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1627
1629
|
}
|
|
1628
1630
|
/** A side tab says what it is in words under its icon: the icons alone had to be learnt (designer 2026-09-27, U05). */
|
|
1629
1631
|
renderTab(tab, icon, label) {
|
|
1630
|
-
/*
|
|
1631
|
-
|
|
1632
|
-
|
|
1632
|
+
/*
|
|
1633
|
+
Its accessible name is the name it shows (designer 2026-09-27); the longer title stays as its tooltip. One name
|
|
1634
|
+
whatever is picked: the size tab was 「반응형」 with nothing picked and 「배치」 with a part (sweep 2026-10-27 J2).
|
|
1635
|
+
*/
|
|
1636
|
+
const name = i18next.t('figure.label.tab-short-' + tab);
|
|
1637
|
+
return html `<button tab ?on=${this.tab === tab} title=${label} aria-label=${name} @click=${() => (this.tab = tab)}>
|
|
1633
1638
|
<md-icon>${icon}</md-icon><span tab-label aria-hidden="true">${name}</span>
|
|
1634
1639
|
</button>`;
|
|
1635
1640
|
}
|
|
@@ -1727,7 +1732,13 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1727
1732
|
${i18next.t(kind === 'box' ? 'figure.label.primitive-cube' : `figure.label.primitive-${kind}`)}
|
|
1728
1733
|
</button>`)}
|
|
1729
1734
|
</div>
|
|
1730
|
-
|
|
1735
|
+
${this.panes?.foldStart
|
|
1736
|
+
? /* The list it points to is folded at this width: a way to it, not words about a list not there (sweep 2026-10-27 U23). */
|
|
1737
|
+
html `<button start-more @click=${() => {
|
|
1738
|
+
this.panes.foldStart = false;
|
|
1739
|
+
this.requestUpdate();
|
|
1740
|
+
}}>${i18next.t('figure.button.start-shape-more')}</button>`
|
|
1741
|
+
: html `<small>${i18next.t('figure.text.start-shape-more')}</small>`}
|
|
1731
1742
|
</section>
|
|
1732
1743
|
${this.aiDraft
|
|
1733
1744
|
? html `<section>
|
|
@@ -1735,7 +1746,6 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1735
1746
|
<button start-button start-ai @click=${() => this.dispatchEvent(new CustomEvent('ask-ai-draft', { bubbles: true, composed: true }))}>
|
|
1736
1747
|
${i18next.t('figure.button.start-ai')}
|
|
1737
1748
|
</button>
|
|
1738
|
-
<small>${i18next.t('figure.text.start-ai-optional')}</small>
|
|
1739
1749
|
</section>`
|
|
1740
1750
|
: nothing}
|
|
1741
1751
|
</div>
|
|
@@ -1820,8 +1830,8 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1820
1830
|
return html `<ox-canvas-bar
|
|
1821
1831
|
history
|
|
1822
1832
|
fit
|
|
1823
|
-
undo-shortcut
|
|
1824
|
-
redo-shortcut
|
|
1833
|
+
undo-shortcut=${isMacOS() ? '⌘Z' : 'Ctrl+Z'}
|
|
1834
|
+
redo-shortcut=${isMacOS() ? '⇧⌘Z' : 'Ctrl+Y'}
|
|
1825
1835
|
?can-undo=${!unavailable && view.canUndo}
|
|
1826
1836
|
?can-redo=${!unavailable && view.canRedo}
|
|
1827
1837
|
.pane=${this.panes}
|
|
@@ -1851,7 +1861,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1851
1861
|
>
|
|
1852
1862
|
<md-icon>publish</md-icon>${i18next.t('figure.button.release')}
|
|
1853
1863
|
</button>
|
|
1854
|
-
<button slot="commit" save ?disabled=${unavailable || (!view.dirty && !!view.id)} @click=${this.save}><md-icon>save</md-icon>${i18next.t('figure.button.save')}</button>
|
|
1864
|
+
<button slot="commit" save title=${!view.id && !view.geometry.length ? i18next.t('figure.text.save-needs-a-part') : ''} ?disabled=${unavailable || (!view.dirty && !!view.id) || (!view.id && !view.geometry.length)} @click=${this.save}><md-icon>save</md-icon>${i18next.t('figure.button.save')}</button>
|
|
1855
1865
|
</ox-canvas-bar>
|
|
1856
1866
|
${this.blockersOpen && blockers.length ? this.renderReleaseBlockers(blockers, asset) : nothing}
|
|
1857
1867
|
${this.catalogOpen
|
|
@@ -1941,7 +1951,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1941
1951
|
${this.renderTab('motion', 'animation', i18next.t('figure.label.motion'))}
|
|
1942
1952
|
${this.renderTab('capability', 'conveyor_belt', i18next.t('figure.label.tab-capability'))}
|
|
1943
1953
|
${this.renderTab('data', 'sensors', i18next.t('figure.label.tab-data'))}
|
|
1944
|
-
${this.renderTab('verdict', 'insights', i18next.t('figure.label.
|
|
1954
|
+
${this.renderTab('verdict', 'insights', i18next.t('figure.label.tab-verdict'))}
|
|
1945
1955
|
</div>
|
|
1946
1956
|
<slot name="notice"></slot>
|
|
1947
1957
|
<div panel ?off=${tab !== 'properties'}>
|
|
@@ -2064,17 +2074,18 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
2064
2074
|
return v === undefined || !Number.isFinite(v) ? '—' : String(Math.round(v * 10) / 10);
|
|
2065
2075
|
};
|
|
2066
2076
|
return html `<div vector-row>
|
|
2067
|
-
<label>${i18next.t('figure.label.group-' + group.name)}${group.name === 'rotation' ? '' : html ` <span unit
|
|
2077
|
+
<label>${i18next.t('figure.label.group-' + group.name)}${group.name === 'rotation' ? '' : html ` <span unit>(${unit})</span>`}</label>
|
|
2068
2078
|
<div triple>
|
|
2069
2079
|
${['x', 'y', 'z'].map(axis => !group.cells[axis]
|
|
2070
2080
|
? html `<div cell held title=${i18next.t('figure.text.axis-held-by-rule')}>
|
|
2071
2081
|
<span axis>${axis.toUpperCase()}</span>
|
|
2072
|
-
<input type="text" readonly .value=${given(axis)} aria-label=${`${group.name}
|
|
2082
|
+
<input type="text" readonly .value=${given(axis)} data-field=${`${group.name}.${axis}`} aria-label=${`${i18next.t('figure.label.group-' + group.name)} ${axis.toUpperCase()} · ${i18next.t('figure.text.axis-held-by-rule')}`} />
|
|
2073
2083
|
</div>`
|
|
2074
2084
|
: html `<div cell>
|
|
2075
2085
|
<span axis ?scrub=${!unavailable} @pointerdown=${(e) => this.scrub(group.cells[axis], e, unavailable)}>${axis.toUpperCase()}</span>
|
|
2076
2086
|
<input
|
|
2077
|
-
|
|
2087
|
+
data-field=${group.cells[axis].id}
|
|
2088
|
+
aria-label=${`${i18next.t('figure.label.group-' + group.name)} ${axis.toUpperCase()}`}
|
|
2078
2089
|
type="number"
|
|
2079
2090
|
step="any"
|
|
2080
2091
|
.value=${String(group.cells[axis].value)}
|
|
@@ -2110,9 +2121,10 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
2110
2121
|
}
|
|
2111
2122
|
renderSingle(name, field, unavailable) {
|
|
2112
2123
|
const words = name.split('.').map(part => i18next.t('figure.label.field-' + part, { defaultValue: part }));
|
|
2113
|
-
return html `<span ?scrub=${!unavailable} @pointerdown=${(e) => this.scrub(field, e, unavailable)}>${words.join(' · ')} (${field.unit})</span
|
|
2124
|
+
return html `<span ?scrub=${!unavailable} @pointerdown=${(e) => this.scrub(field, e, unavailable)}>${words.join(' · ')} (${unitWord(field.unit)})</span
|
|
2114
2125
|
><input
|
|
2115
|
-
|
|
2126
|
+
data-field=${field.id}
|
|
2127
|
+
aria-label=${words.join(' · ')}
|
|
2116
2128
|
type="number"
|
|
2117
2129
|
step="any"
|
|
2118
2130
|
.value=${String(field.value)}
|