@things-factory/figure-ui 10.1.91 → 10.1.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/client/modeller/editor.ts +61 -90
  2. package/client/modeller/fasten-geometry.ts +152 -0
  3. package/client/modeller/fields.ts +8 -1
  4. package/client/modeller/figure-cavity-drawing.ts +259 -0
  5. package/client/modeller/figure-fasten-drawing.ts +406 -0
  6. package/client/modeller/figure-links.ts +71 -35
  7. package/client/modeller/figure-section-close-choice.ts +52 -0
  8. package/client/modeller/figure-section-editor.ts +58 -3
  9. package/client/modeller/figure-section-panel.ts +5 -0
  10. package/client/modeller/section-controls.ts +19 -0
  11. package/client/modeller/side-panel-styles.ts +4 -11
  12. package/dist-client/modeller/editor.d.ts +10 -4
  13. package/dist-client/modeller/editor.js +59 -88
  14. package/dist-client/modeller/editor.js.map +1 -1
  15. package/dist-client/modeller/fasten-geometry.d.ts +60 -0
  16. package/dist-client/modeller/fasten-geometry.js +138 -0
  17. package/dist-client/modeller/fasten-geometry.js.map +1 -0
  18. package/dist-client/modeller/fields.js +8 -1
  19. package/dist-client/modeller/fields.js.map +1 -1
  20. package/dist-client/modeller/figure-cavity-drawing.d.ts +48 -0
  21. package/dist-client/modeller/figure-cavity-drawing.js +253 -0
  22. package/dist-client/modeller/figure-cavity-drawing.js.map +1 -0
  23. package/dist-client/modeller/figure-fasten-drawing.d.ts +74 -0
  24. package/dist-client/modeller/figure-fasten-drawing.js +452 -0
  25. package/dist-client/modeller/figure-fasten-drawing.js.map +1 -0
  26. package/dist-client/modeller/figure-links.d.ts +7 -0
  27. package/dist-client/modeller/figure-links.js +70 -33
  28. package/dist-client/modeller/figure-links.js.map +1 -1
  29. package/dist-client/modeller/figure-section-close-choice.d.ts +17 -0
  30. package/dist-client/modeller/figure-section-close-choice.js +58 -0
  31. package/dist-client/modeller/figure-section-close-choice.js.map +1 -0
  32. package/dist-client/modeller/figure-section-editor.d.ts +11 -1
  33. package/dist-client/modeller/figure-section-editor.js +57 -3
  34. package/dist-client/modeller/figure-section-editor.js.map +1 -1
  35. package/dist-client/modeller/figure-section-panel.d.ts +1 -0
  36. package/dist-client/modeller/figure-section-panel.js +5 -0
  37. package/dist-client/modeller/figure-section-panel.js.map +1 -1
  38. package/dist-client/modeller/section-controls.d.ts +10 -0
  39. package/dist-client/modeller/section-controls.js +17 -0
  40. package/dist-client/modeller/section-controls.js.map +1 -1
  41. package/dist-client/modeller/side-panel-styles.js +4 -11
  42. package/dist-client/modeller/side-panel-styles.js.map +1 -1
  43. package/dist-client/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +2 -2
  45. package/test/browser/acceptance-page.js +10 -0
  46. package/test/cavity-drawing.test.ts +69 -0
  47. package/test/fasten-drawing.test.ts +45 -0
  48. package/test/fasten-geometry.test.ts +79 -0
  49. package/test/layout.test.ts +19 -4
  50. package/test/links.test.ts +3 -1
  51. package/test/section-editor.test.ts +31 -8
  52. package/translations/en.json +29 -15
  53. package/translations/ja.json +25 -11
  54. package/translations/ko.json +29 -15
  55. package/translations/ms.json +25 -11
  56. package/translations/zh.json +25 -11
@@ -41,4 +41,14 @@ export declare function cavityRefusalOf(shape: FigureEvaluatedShape, request: Fi
41
41
  export declare function cavityResultOf(cavity: NonNullable<FigureEvaluatedShape['cavity']>): 'pipe' | 'container' | 'sealed';
42
42
  /** How deep the inside is along the push: the length less the closed ends. */
43
43
  export declare function cavityDepthOf(shape: FigureEvaluatedShape): number;
44
+ /** The inside as it stands, every side's wall written out. */
45
+ export declare function cavityWorkingOf(shape: FigureEvaluatedShape): {
46
+ wall: Record<string, number>;
47
+ start: FigureCavityRequest['start'];
48
+ end: FigureCavityRequest['end'];
49
+ } | undefined;
50
+ /** A wall dragged to `value`: that side alone, or every side when the walls are one. */
51
+ export declare function wallDragged(wall: Record<string, number>, side: string, value: number, perSide: boolean): Record<string, number>;
52
+ /** An end pressed: open becomes closed by the default wall, closed becomes open. */
53
+ export declare function endToggled(shape: FigureEvaluatedShape, now: FigureCavityRequest['start']): FigureCavityRequest['start'];
44
54
  export {};
@@ -88,4 +88,21 @@ export function cavityDepthOf(shape) {
88
88
  const closed = (x) => (x === 'open' ? 0 : x.closed);
89
89
  return mm(e.end - e.start - closed(c.start) - closed(c.end));
90
90
  }
91
+ /** The inside as it stands, every side's wall written out. */
92
+ export function cavityWorkingOf(shape) {
93
+ const c = shape.cavity;
94
+ const e = shape.extrusion;
95
+ if (!c || !e)
96
+ return undefined;
97
+ return { wall: Object.fromEntries(sidesOf(e.axis).map(s => [s, c.wall[s]])), start: c.start, end: c.end };
98
+ }
99
+ /** A wall dragged to `value`: that side alone, or every side when the walls are one. */
100
+ export function wallDragged(wall, side, value, perSide) {
101
+ const v = Math.max(1, Math.round(value));
102
+ return perSide ? { ...wall, [side]: v } : Object.fromEntries(Object.keys(wall).map(s => [s, v]));
103
+ }
104
+ /** An end pressed: open becomes closed by the default wall, closed becomes open. */
105
+ export function endToggled(shape, now) {
106
+ return now === 'open' ? { closed: defaultWallOf(shape) } : 'open';
107
+ }
91
108
  //# sourceMappingURL=section-controls.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"section-controls.js","sourceRoot":"","sources":["../../client/modeller/section-controls.ts"],"names":[],"mappings":"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","sourcesContent":["import { FIGURE_SECTION_PLANE, checkFigureSection, figureExtrusionFeaturesOf } from '@hatiolab/figure-model'\nimport type { FigureCavityRequest, FigureEvaluatedShape } from '@hatiolab/figure-model'\n\n/*\n * What the 단면 section of the properties tab reads and asks (ADR-0100 §3.3): a pushed part's corners and its inside.\n * Whether a value can be taken is asked of the kernel's own section checks -- the screen keeps no rule of its own -- and\n * the most it can take is said beside the field, so a refusal names the value to type rather than only that it failed.\n */\n\ntype Axis = 'x' | 'y' | 'z'\ntype End = 'start' | 'end'\ntype Extrusion = NonNullable<FigureEvaluatedShape['extrusion']>\n\n/** The face each side of a section is, and each end of a push: a push's start is its low side. */\nexport const SIDE_FACE: Record<string, string> = { 'x-': 'left', 'x+': 'right', 'y-': 'bottom', 'y+': 'top', 'z-': 'back', 'z+': 'front' }\nexport const END_FACE = (axis: Axis, end: End) => SIDE_FACE[`${axis}${end === 'start' ? '-' : '+'}`]!\n\n/** The one value every entry holds, or undefined when they differ (or there are none). */\nexport const sameOf = (values: readonly (number | undefined)[]) =>\n values.length && values.every(v => v !== undefined && Math.abs(v - values[0]!) < 1e-6) ? values[0] : undefined\n\nexport const mm = (value: number) => Math.round(value * 1000) / 1000\n\n/** The four sides of a push's section, in the kernel's order: the first plane axis low and high, then the second. */\nexport const sidesOf = (axis: Axis) => FIGURE_SECTION_PLANE[axis].flatMap(a => [`${a}-`, `${a}+`])\n\n/** The section's widths across, one per plane axis: a rectangle's sides, an ellipse's diameters. */\nfunction acrossOf(e: Extrusion): number[] {\n const s = e.section\n if (s.kind === 'rectangle') return FIGURE_SECTION_PLANE[e.axis].map(a => s.size[a]!)\n if (s.kind === 'ellipse') return FIGURE_SECTION_PLANE[e.axis].map(a => 2 * s.radius[a]!)\n return []\n}\n\n/** The wall a new inside starts with: a tenth of the section's narrower side, at least 1 mm. */\nexport function defaultWallOf(shape: FigureEvaluatedShape): number {\n return Math.max(1, Math.round(Math.min(...acrossOf(shape.extrusion!)) / 10))\n}\n\n/**\n * The inside as the panel asks for it: the part's own, with one thing changed. A part with none yet gets a default\n * wall all round, a closed start as thick as the wall and an open end -- a tray standing on its floor.\n */\nexport function cavityRequestOf(shape: FigureEvaluatedShape, patch: Partial<FigureCavityRequest> = {}): FigureCavityRequest {\n const cavity = shape.cavity\n return {\n wall: cavity ? (sameOf(Object.values(cavity.wall)) ?? { ...cavity.wall }) : defaultWallOf(shape),\n start: cavity?.start ?? { closed: defaultWallOf(shape) },\n end: cavity?.end ?? 'open',\n ...patch\n }\n}\n\n/** The most a rectangle's corners can reach: half its narrower side (the kernel's CORNER_TOO_LARGE says where). */\nexport function cornerLimitOf(shape: FigureEvaluatedShape): number | undefined {\n const e = shape.extrusion\n if (e?.section.kind !== 'rectangle') return undefined\n const [u, v] = FIGURE_SECTION_PLANE[e.axis]\n const far = 2 * Math.max(...acrossOf(e))\n const issues = checkFigureSection({ kind: 'rectangle', size: e.section.size, corner: { [u]: far, [v]: far } }, e.axis)\n const maxes = issues.flatMap(i => (i.code === 'CORNER_TOO_LARGE' ? [i.max] : []))\n return maxes.length ? Math.min(...maxes) : undefined\n}\n\n/** Whether a corner radius can be taken; if not, the most it can be. */\nexport function cornerRefusalOf(shape: FigureEvaluatedShape, radius: number): { max: number } | null {\n const e = shape.extrusion\n if (e?.section.kind !== 'rectangle' || !(radius > 0)) return null\n const [u, v] = FIGURE_SECTION_PLANE[e.axis]\n const issues = checkFigureSection({ kind: 'rectangle', size: e.section.size, corner: { [u]: radius, [v]: radius } }, e.axis)\n return issues.some(i => i.code === 'CORNER_TOO_LARGE') ? { max: cornerLimitOf(shape)! } : null\n}\n\n/** One side's wall, the others kept: it must leave room inside, so under the width across it less the opposite wall. */\nexport function sideWallLimitOf(shape: FigureEvaluatedShape, wall: Record<string, number>, side: string): number {\n const e = shape.extrusion!\n const axis = side[0] as Axis\n const across = acrossOf(e)[FIGURE_SECTION_PLANE[e.axis].indexOf(axis)]!\n return across - (wall[`${axis}${side[1] === '-' ? '+' : '-'}`] ?? 0)\n}\n\n/** A wall must leave room inside: under half the narrower width the section has. */\nexport const wallLimitOf = (shape: FigureEvaluatedShape) => Math.min(...acrossOf(shape.extrusion!)) / 2\n\nexport type FigureCavityAsk =\n | { code: 'wall-too-thick'; max: number }\n | { code: 'ends-too-thick'; max: number }\n | { code: 'not-positive' }\n\n/** Whether an inside can be taken, asked of the kernel; if not, what to change and the most it can be. */\nexport function cavityRefusalOf(shape: FigureEvaluatedShape, request: FigureCavityRequest): FigureCavityAsk | null {\n const e = shape.extrusion!\n const sides = sidesOf(e.axis)\n const wall = typeof request.wall === 'number' ? Object.fromEntries(sides.map(s => [s, request.wall as number])) : request.wall\n const made = figureExtrusionFeaturesOf(e, [{ id: 'inside', wall: wall as Record<string, number>, start: request.start, end: request.end }])\n if (!('refused' in made)) return null\n const r = made.refused\n if (r.code === 'CAVITY' && r.refused === 'NO_ROOM_INSIDE') return { code: 'wall-too-thick', max: mm(wallLimitOf(shape)) }\n if (r.code === 'CAVITY_NO_LENGTH') return { code: 'ends-too-thick', max: mm(e.end - e.start) }\n return { code: 'not-positive' }\n}\n\n/** What the inside makes of the part (§3.3): open at both ends a pipe, at one a container, at neither a closed space. */\nexport function cavityResultOf(cavity: NonNullable<FigureEvaluatedShape['cavity']>): 'pipe' | 'container' | 'sealed' {\n const open = [cavity.start, cavity.end].filter(e => e === 'open').length\n return open === 2 ? 'pipe' : open === 1 ? 'container' : 'sealed'\n}\n\n/** How deep the inside is along the push: the length less the closed ends. */\nexport function cavityDepthOf(shape: FigureEvaluatedShape): number {\n const e = shape.extrusion!,\n c = shape.cavity!\n const closed = (x: typeof c.start) => (x === 'open' ? 0 : x.closed)\n return mm(e.end - e.start - closed(c.start) - closed(c.end))\n}\n"]}
1
+ {"version":3,"file":"section-controls.js","sourceRoot":"","sources":["../../client/modeller/section-controls.ts"],"names":[],"mappings":"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","sourcesContent":["import { FIGURE_SECTION_PLANE, checkFigureSection, figureExtrusionFeaturesOf } from '@hatiolab/figure-model'\nimport type { FigureCavityRequest, FigureEvaluatedShape } from '@hatiolab/figure-model'\n\n/*\n * What the 단면 section of the properties tab reads and asks (ADR-0100 §3.3): a pushed part's corners and its inside.\n * Whether a value can be taken is asked of the kernel's own section checks -- the screen keeps no rule of its own -- and\n * the most it can take is said beside the field, so a refusal names the value to type rather than only that it failed.\n */\n\ntype Axis = 'x' | 'y' | 'z'\ntype End = 'start' | 'end'\ntype Extrusion = NonNullable<FigureEvaluatedShape['extrusion']>\n\n/** The face each side of a section is, and each end of a push: a push's start is its low side. */\nexport const SIDE_FACE: Record<string, string> = { 'x-': 'left', 'x+': 'right', 'y-': 'bottom', 'y+': 'top', 'z-': 'back', 'z+': 'front' }\nexport const END_FACE = (axis: Axis, end: End) => SIDE_FACE[`${axis}${end === 'start' ? '-' : '+'}`]!\n\n/** The one value every entry holds, or undefined when they differ (or there are none). */\nexport const sameOf = (values: readonly (number | undefined)[]) =>\n values.length && values.every(v => v !== undefined && Math.abs(v - values[0]!) < 1e-6) ? values[0] : undefined\n\nexport const mm = (value: number) => Math.round(value * 1000) / 1000\n\n/** The four sides of a push's section, in the kernel's order: the first plane axis low and high, then the second. */\nexport const sidesOf = (axis: Axis) => FIGURE_SECTION_PLANE[axis].flatMap(a => [`${a}-`, `${a}+`])\n\n/** The section's widths across, one per plane axis: a rectangle's sides, an ellipse's diameters. */\nfunction acrossOf(e: Extrusion): number[] {\n const s = e.section\n if (s.kind === 'rectangle') return FIGURE_SECTION_PLANE[e.axis].map(a => s.size[a]!)\n if (s.kind === 'ellipse') return FIGURE_SECTION_PLANE[e.axis].map(a => 2 * s.radius[a]!)\n return []\n}\n\n/** The wall a new inside starts with: a tenth of the section's narrower side, at least 1 mm. */\nexport function defaultWallOf(shape: FigureEvaluatedShape): number {\n return Math.max(1, Math.round(Math.min(...acrossOf(shape.extrusion!)) / 10))\n}\n\n/**\n * The inside as the panel asks for it: the part's own, with one thing changed. A part with none yet gets a default\n * wall all round, a closed start as thick as the wall and an open end -- a tray standing on its floor.\n */\nexport function cavityRequestOf(shape: FigureEvaluatedShape, patch: Partial<FigureCavityRequest> = {}): FigureCavityRequest {\n const cavity = shape.cavity\n return {\n wall: cavity ? (sameOf(Object.values(cavity.wall)) ?? { ...cavity.wall }) : defaultWallOf(shape),\n start: cavity?.start ?? { closed: defaultWallOf(shape) },\n end: cavity?.end ?? 'open',\n ...patch\n }\n}\n\n/** The most a rectangle's corners can reach: half its narrower side (the kernel's CORNER_TOO_LARGE says where). */\nexport function cornerLimitOf(shape: FigureEvaluatedShape): number | undefined {\n const e = shape.extrusion\n if (e?.section.kind !== 'rectangle') return undefined\n const [u, v] = FIGURE_SECTION_PLANE[e.axis]\n const far = 2 * Math.max(...acrossOf(e))\n const issues = checkFigureSection({ kind: 'rectangle', size: e.section.size, corner: { [u]: far, [v]: far } }, e.axis)\n const maxes = issues.flatMap(i => (i.code === 'CORNER_TOO_LARGE' ? [i.max] : []))\n return maxes.length ? Math.min(...maxes) : undefined\n}\n\n/** Whether a corner radius can be taken; if not, the most it can be. */\nexport function cornerRefusalOf(shape: FigureEvaluatedShape, radius: number): { max: number } | null {\n const e = shape.extrusion\n if (e?.section.kind !== 'rectangle' || !(radius > 0)) return null\n const [u, v] = FIGURE_SECTION_PLANE[e.axis]\n const issues = checkFigureSection({ kind: 'rectangle', size: e.section.size, corner: { [u]: radius, [v]: radius } }, e.axis)\n return issues.some(i => i.code === 'CORNER_TOO_LARGE') ? { max: cornerLimitOf(shape)! } : null\n}\n\n/** One side's wall, the others kept: it must leave room inside, so under the width across it less the opposite wall. */\nexport function sideWallLimitOf(shape: FigureEvaluatedShape, wall: Record<string, number>, side: string): number {\n const e = shape.extrusion!\n const axis = side[0] as Axis\n const across = acrossOf(e)[FIGURE_SECTION_PLANE[e.axis].indexOf(axis)]!\n return across - (wall[`${axis}${side[1] === '-' ? '+' : '-'}`] ?? 0)\n}\n\n/** A wall must leave room inside: under half the narrower width the section has. */\nexport const wallLimitOf = (shape: FigureEvaluatedShape) => Math.min(...acrossOf(shape.extrusion!)) / 2\n\nexport type FigureCavityAsk =\n | { code: 'wall-too-thick'; max: number }\n | { code: 'ends-too-thick'; max: number }\n | { code: 'not-positive' }\n\n/** Whether an inside can be taken, asked of the kernel; if not, what to change and the most it can be. */\nexport function cavityRefusalOf(shape: FigureEvaluatedShape, request: FigureCavityRequest): FigureCavityAsk | null {\n const e = shape.extrusion!\n const sides = sidesOf(e.axis)\n const wall = typeof request.wall === 'number' ? Object.fromEntries(sides.map(s => [s, request.wall as number])) : request.wall\n const made = figureExtrusionFeaturesOf(e, [{ id: 'inside', wall: wall as Record<string, number>, start: request.start, end: request.end }])\n if (!('refused' in made)) return null\n const r = made.refused\n if (r.code === 'CAVITY' && r.refused === 'NO_ROOM_INSIDE') return { code: 'wall-too-thick', max: mm(wallLimitOf(shape)) }\n if (r.code === 'CAVITY_NO_LENGTH') return { code: 'ends-too-thick', max: mm(e.end - e.start) }\n return { code: 'not-positive' }\n}\n\n/** What the inside makes of the part (§3.3): open at both ends a pipe, at one a container, at neither a closed space. */\nexport function cavityResultOf(cavity: NonNullable<FigureEvaluatedShape['cavity']>): 'pipe' | 'container' | 'sealed' {\n const open = [cavity.start, cavity.end].filter(e => e === 'open').length\n return open === 2 ? 'pipe' : open === 1 ? 'container' : 'sealed'\n}\n\n/** How deep the inside is along the push: the length less the closed ends. */\nexport function cavityDepthOf(shape: FigureEvaluatedShape): number {\n const e = shape.extrusion!,\n c = shape.cavity!\n const closed = (x: typeof c.start) => (x === 'open' ? 0 : x.closed)\n return mm(e.end - e.start - closed(c.start) - closed(c.end))\n}\n\n/** The inside as it stands, every side's wall written out. */\nexport function cavityWorkingOf(shape: FigureEvaluatedShape): { wall: Record<string, number>; start: FigureCavityRequest['start']; end: FigureCavityRequest['end'] } | undefined {\n const c = shape.cavity\n const e = shape.extrusion\n if (!c || !e) return undefined\n return { wall: Object.fromEntries(sidesOf(e.axis).map(s => [s, c.wall[s]!])), start: c.start, end: c.end }\n}\n\n/** A wall dragged to `value`: that side alone, or every side when the walls are one. */\nexport function wallDragged(wall: Record<string, number>, side: string, value: number, perSide: boolean): Record<string, number> {\n const v = Math.max(1, Math.round(value))\n return perSide ? { ...wall, [side]: v } : Object.fromEntries(Object.keys(wall).map(s => [s, v]))\n}\n\n/** An end pressed: open becomes closed by the default wall, closed becomes open. */\nexport function endToggled(shape: FigureEvaluatedShape, now: FigureCavityRequest['start']): FigureCavityRequest['start'] {\n return now === 'open' ? { closed: defaultWallOf(shape) } : 'open'\n}\n"]}
@@ -100,19 +100,17 @@ export const SidePanelStyles = css `
100
100
  div[vector-row] + div[inline-field] {
101
101
  margin-top: 5px;
102
102
  }
103
- /* A row of icon tools (align, mirror, snap). */
103
+ /* A row of worded tools (align, snap; mirror copies). */
104
104
  div[tools] {
105
105
  display: flex;
106
106
  flex-wrap: wrap;
107
107
  gap: 4px;
108
108
  }
109
109
  div[tools] button {
110
- display: inline-flex;
111
- align-items: center;
112
- justify-content: center;
113
- width: 30px;
114
110
  height: 28px;
115
- padding: 0;
111
+ padding: 0 10px;
112
+ font: inherit;
113
+ font-size: 0.78rem;
116
114
  color: var(--md-sys-color-on-surface);
117
115
  background: var(--md-sys-color-surface-container-lowest);
118
116
  border: 1px solid var(--md-sys-color-outline-variant);
@@ -126,11 +124,6 @@ export const SidePanelStyles = css `
126
124
  opacity: 0.45;
127
125
  cursor: default;
128
126
  }
129
- div[tools] md-icon {
130
- font-size: 17px;
131
- width: 17px;
132
- height: 17px;
133
- }
134
127
  div[pair] {
135
128
  display: flex;
136
129
  gap: 4px;
@@ -1 +1 @@
1
- {"version":3,"file":"side-panel-styles.js","sourceRoot":"","sources":["../../client/modeller/side-panel-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiUjC,CAAA","sourcesContent":["import { css } from 'lit'\n\n/**\n * The side panel's one look, for every element that draws in the modeller's side.\n *\n * The side is drawn by several elements (the type field, the figure's properties, a part's values, fastening, places,\n * motion, material, controls, clips, the environment) and each had styled its own fields: three label styles and\n * three field heights in one column (user, 2026-09-24: \"why are the styles and layout all different?\"). They share\n * this sheet now and keep only what is theirs.\n *\n * The vocabulary:\n * section one titled group; the side is a stack of them\n * h3 a group's title -- small capitals\n * div[inline-field] a short label and its one field on one line\n * div[field] a label above a full-width field\n * div[form] short labels beside their fields, the fields starting on one line\n * div[vector-row] a title, then X · Y · Z in one row of cells with the axis inside (`div[triple]` of\n * `div[cell]`) -- three values read together, as vector tools lay them out\n * div[pair] two controls side by side\n * label[toggle] a checkbox and its words\n * p[hint] · small the quiet line under a field\n * button[act] an action under the fields it acts on\n * [scrub] a label dragged sideways to scrub its field\n */\nexport const SidePanelStyles = css`\n :host {\n --figure-control-height: 30px;\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n }\n /* The side is a stack of sections, each a titled group. */\n section {\n display: grid;\n gap: 6px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n section > h3 {\n margin: 0;\n }\n section div[field] + div[field],\n section div[inline-field] + div[inline-field],\n section div[vector-row] + div[vector-row] {\n margin-top: 0;\n }\n h3 {\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 14px 0 6px;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant);\n }\n h3:first-child {\n margin-top: 0;\n }\n label,\n div[form] > span {\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[field] {\n display: grid;\n gap: 2px;\n }\n div[field] + div[field],\n div[field] + div[form],\n div[form] + div[field],\n div[vector-row] + div[vector-row],\n div[vector-row] + div[field],\n div[field] + div[vector-row] {\n margin-top: 5px;\n }\n div[form],\n div[inline-field],\n div[vector-row] {\n display: grid;\n grid-template-columns: 64px minmax(0, 1fr);\n align-items: center;\n gap: 5px 7px;\n }\n div[form] > span,\n div[inline-field] > label,\n div[vector-row] > label {\n overflow-wrap: anywhere;\n white-space: normal;\n }\n /* A value read, not edited, beside its label: the field's size and colour, not the page's. */\n div[inline-field] > span[value] {\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n }\n label span[unit] {\n color: var(--md-sys-color-outline);\n }\n div[inline-field] + div[inline-field],\n div[inline-field] + div[vector-row],\n div[vector-row] + div[inline-field] {\n margin-top: 5px;\n }\n /* A row of icon tools (align, mirror, snap). */\n div[tools] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n div[tools] button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 28px;\n padding: 0;\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 cursor: pointer;\n }\n div[tools] button:hover:not(:disabled) {\n background: var(--md-sys-color-surface-container);\n }\n div[tools] button:disabled {\n opacity: 0.45;\n cursor: default;\n }\n div[tools] md-icon {\n font-size: 17px;\n width: 17px;\n height: 17px;\n }\n div[pair] {\n display: flex;\n gap: 4px;\n }\n div[pair] > * {\n flex: 1;\n min-width: 0;\n }\n div[triple] {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n }\n input,\n select {\n width: 100%;\n min-width: 0;\n min-height: var(--figure-control-height);\n box-sizing: border-box;\n padding: 4px 7px;\n font: inherit;\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n input[type='checkbox'],\n input[type='radio'] {\n width: auto;\n min-height: 0;\n }\n /* A thin track and a small thumb: a slider is a line to drag along, not a block (user, 2026-09-24). */\n input[type='range'] {\n -webkit-appearance: none;\n appearance: none;\n min-height: 16px;\n height: 16px;\n padding: 0;\n border: none;\n background: transparent;\n cursor: pointer;\n }\n input[type='range']::-webkit-slider-runnable-track {\n height: 3px;\n border-radius: 2px;\n background: var(--md-sys-color-outline-variant);\n }\n input[type='range']::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 12px;\n margin-top: -4.5px;\n border: none;\n border-radius: 50%;\n background: var(--md-sys-color-primary);\n }\n input[type='range']::-moz-range-track {\n height: 3px;\n border-radius: 2px;\n background: var(--md-sys-color-outline-variant);\n }\n input[type='range']::-moz-range-thumb {\n width: 12px;\n height: 12px;\n border: none;\n border-radius: 50%;\n background: var(--md-sys-color-primary);\n }\n input[type='range']:focus {\n outline: none;\n }\n input[type='range']:focus-visible::-webkit-slider-thumb {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 2px;\n }\n input[type='color'] {\n padding: 2px;\n width: 40px;\n }\n input:focus,\n select:focus {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: -1px;\n }\n input:disabled,\n select:disabled {\n opacity: 0.6;\n }\n /* A number with its axis inside the cell, on the left. */\n div[cell] {\n display: flex;\n box-sizing: border-box;\n height: var(--figure-control-height);\n align-items: center;\n gap: 2px;\n padding-left: 6px;\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n div[cell]:focus-within {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: -1px;\n border-color: transparent;\n }\n /* A label that scrubs its field when dragged sideways (vector tools' way). */\n [scrub] {\n cursor: ew-resize;\n user-select: none;\n touch-action: none;\n }\n div[cell] span[axis] {\n flex: none;\n font-size: 0.64rem;\n letter-spacing: 0.02em;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[cell] input {\n min-height: 0;\n height: calc(var(--figure-control-height) - 2px);\n padding: 4px 6px 4px 2px;\n font-family: var(--mono-font, monospace);\n text-align: right;\n background: none;\n border: none;\n border-radius: 0;\n }\n div[cell] input:focus {\n outline: none;\n }\n /* An axis a rule sets: its number, read-only, on a quieter ground (ADR-0097). */\n div[cell][held] {\n background-color: var(--md-sys-color-surface-container);\n border-style: dashed;\n }\n div[cell][held] input {\n color: var(--md-sys-color-on-surface-variant);\n font-style: italic;\n }\n p[held-axes] {\n margin: 2px 0 6px;\n }\n label[toggle] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n }\n /* The 「?」 beside a title: no border, it says there is more rather than asking to be pressed. */\n button[help] {\n display: inline-flex;\n align-items: center;\n padding: 0;\n margin: 0;\n color: var(--md-sys-color-outline);\n background: none;\n border: none;\n cursor: pointer;\n }\n button[help] md-icon {\n font-size: 13px;\n width: 13px;\n height: 13px;\n }\n button[help]:hover,\n button[help][aria-expanded='true'] {\n color: var(--md-sys-color-primary);\n }\n p[hint],\n small {\n display: block;\n margin: 2px 0 4px;\n font-size: 0.68rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant);\n }\n p[hint] {\n padding-left: 6px;\n border-left: 2px solid var(--md-sys-color-outline-variant);\n }\n button[act] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n margin-top: 4px;\n padding: 3px 8px;\n min-height: 28px;\n font: inherit;\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[act]:hover:not(:disabled) {\n background-color: var(--md-sys-color-surface-container-high);\n }\n button[act]:disabled {\n opacity: 0.5;\n cursor: default;\n }\n button[act] md-icon {\n font-size: 15px;\n width: 15px;\n height: 15px;\n }\n`\n"]}
1
+ {"version":3,"file":"side-panel-styles.js","sourceRoot":"","sources":["../../client/modeller/side-panel-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0TjC,CAAA","sourcesContent":["import { css } from 'lit'\n\n/**\n * The side panel's one look, for every element that draws in the modeller's side.\n *\n * The side is drawn by several elements (the type field, the figure's properties, a part's values, fastening, places,\n * motion, material, controls, clips, the environment) and each had styled its own fields: three label styles and\n * three field heights in one column (user, 2026-09-24: \"why are the styles and layout all different?\"). They share\n * this sheet now and keep only what is theirs.\n *\n * The vocabulary:\n * section one titled group; the side is a stack of them\n * h3 a group's title -- small capitals\n * div[inline-field] a short label and its one field on one line\n * div[field] a label above a full-width field\n * div[form] short labels beside their fields, the fields starting on one line\n * div[vector-row] a title, then X · Y · Z in one row of cells with the axis inside (`div[triple]` of\n * `div[cell]`) -- three values read together, as vector tools lay them out\n * div[pair] two controls side by side\n * label[toggle] a checkbox and its words\n * p[hint] · small the quiet line under a field\n * button[act] an action under the fields it acts on\n * [scrub] a label dragged sideways to scrub its field\n */\nexport const SidePanelStyles = css`\n :host {\n --figure-control-height: 30px;\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n }\n /* The side is a stack of sections, each a titled group. */\n section {\n display: grid;\n gap: 6px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n section > h3 {\n margin: 0;\n }\n section div[field] + div[field],\n section div[inline-field] + div[inline-field],\n section div[vector-row] + div[vector-row] {\n margin-top: 0;\n }\n h3 {\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 14px 0 6px;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant);\n }\n h3:first-child {\n margin-top: 0;\n }\n label,\n div[form] > span {\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[field] {\n display: grid;\n gap: 2px;\n }\n div[field] + div[field],\n div[field] + div[form],\n div[form] + div[field],\n div[vector-row] + div[vector-row],\n div[vector-row] + div[field],\n div[field] + div[vector-row] {\n margin-top: 5px;\n }\n div[form],\n div[inline-field],\n div[vector-row] {\n display: grid;\n grid-template-columns: 64px minmax(0, 1fr);\n align-items: center;\n gap: 5px 7px;\n }\n div[form] > span,\n div[inline-field] > label,\n div[vector-row] > label {\n overflow-wrap: anywhere;\n white-space: normal;\n }\n /* A value read, not edited, beside its label: the field's size and colour, not the page's. */\n div[inline-field] > span[value] {\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n }\n label span[unit] {\n color: var(--md-sys-color-outline);\n }\n div[inline-field] + div[inline-field],\n div[inline-field] + div[vector-row],\n div[vector-row] + div[inline-field] {\n margin-top: 5px;\n }\n /* A row of worded tools (align, snap; mirror copies). */\n div[tools] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n div[tools] button {\n height: 28px;\n padding: 0 10px;\n font: inherit;\n font-size: 0.78rem;\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 cursor: pointer;\n }\n div[tools] button:hover:not(:disabled) {\n background: var(--md-sys-color-surface-container);\n }\n div[tools] button:disabled {\n opacity: 0.45;\n cursor: default;\n }\n div[pair] {\n display: flex;\n gap: 4px;\n }\n div[pair] > * {\n flex: 1;\n min-width: 0;\n }\n div[triple] {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n }\n input,\n select {\n width: 100%;\n min-width: 0;\n min-height: var(--figure-control-height);\n box-sizing: border-box;\n padding: 4px 7px;\n font: inherit;\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n input[type='checkbox'],\n input[type='radio'] {\n width: auto;\n min-height: 0;\n }\n /* A thin track and a small thumb: a slider is a line to drag along, not a block (user, 2026-09-24). */\n input[type='range'] {\n -webkit-appearance: none;\n appearance: none;\n min-height: 16px;\n height: 16px;\n padding: 0;\n border: none;\n background: transparent;\n cursor: pointer;\n }\n input[type='range']::-webkit-slider-runnable-track {\n height: 3px;\n border-radius: 2px;\n background: var(--md-sys-color-outline-variant);\n }\n input[type='range']::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 12px;\n margin-top: -4.5px;\n border: none;\n border-radius: 50%;\n background: var(--md-sys-color-primary);\n }\n input[type='range']::-moz-range-track {\n height: 3px;\n border-radius: 2px;\n background: var(--md-sys-color-outline-variant);\n }\n input[type='range']::-moz-range-thumb {\n width: 12px;\n height: 12px;\n border: none;\n border-radius: 50%;\n background: var(--md-sys-color-primary);\n }\n input[type='range']:focus {\n outline: none;\n }\n input[type='range']:focus-visible::-webkit-slider-thumb {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 2px;\n }\n input[type='color'] {\n padding: 2px;\n width: 40px;\n }\n input:focus,\n select:focus {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: -1px;\n }\n input:disabled,\n select:disabled {\n opacity: 0.6;\n }\n /* A number with its axis inside the cell, on the left. */\n div[cell] {\n display: flex;\n box-sizing: border-box;\n height: var(--figure-control-height);\n align-items: center;\n gap: 2px;\n padding-left: 6px;\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n div[cell]:focus-within {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: -1px;\n border-color: transparent;\n }\n /* A label that scrubs its field when dragged sideways (vector tools' way). */\n [scrub] {\n cursor: ew-resize;\n user-select: none;\n touch-action: none;\n }\n div[cell] span[axis] {\n flex: none;\n font-size: 0.64rem;\n letter-spacing: 0.02em;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[cell] input {\n min-height: 0;\n height: calc(var(--figure-control-height) - 2px);\n padding: 4px 6px 4px 2px;\n font-family: var(--mono-font, monospace);\n text-align: right;\n background: none;\n border: none;\n border-radius: 0;\n }\n div[cell] input:focus {\n outline: none;\n }\n /* An axis a rule sets: its number, read-only, on a quieter ground (ADR-0097). */\n div[cell][held] {\n background-color: var(--md-sys-color-surface-container);\n border-style: dashed;\n }\n div[cell][held] input {\n color: var(--md-sys-color-on-surface-variant);\n font-style: italic;\n }\n p[held-axes] {\n margin: 2px 0 6px;\n }\n label[toggle] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n }\n /* The 「?」 beside a title: no border, it says there is more rather than asking to be pressed. */\n button[help] {\n display: inline-flex;\n align-items: center;\n padding: 0;\n margin: 0;\n color: var(--md-sys-color-outline);\n background: none;\n border: none;\n cursor: pointer;\n }\n button[help] md-icon {\n font-size: 13px;\n width: 13px;\n height: 13px;\n }\n button[help]:hover,\n button[help][aria-expanded='true'] {\n color: var(--md-sys-color-primary);\n }\n p[hint],\n small {\n display: block;\n margin: 2px 0 4px;\n font-size: 0.68rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant);\n }\n p[hint] {\n padding-left: 6px;\n border-left: 2px solid var(--md-sys-color-outline-variant);\n }\n button[act] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n margin-top: 4px;\n padding: 3px 8px;\n min-height: 28px;\n font: inherit;\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[act]:hover:not(:disabled) {\n background-color: var(--md-sys-color-surface-container-high);\n }\n button[act]:disabled {\n opacity: 0.5;\n cursor: default;\n }\n button[act] md-icon {\n font-size: 15px;\n width: 15px;\n height: 15px;\n }\n`\n"]}