@thatopen/services 0.1.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +59 -46
  2. package/dist/built-in/index.d.ts +486 -563
  3. package/dist/cli/lib/npmrc.d.ts +28 -0
  4. package/dist/cli/lib/npmrc.test.d.ts +1 -0
  5. package/dist/cli.js +91 -7
  6. package/dist/core/client.d.ts +10 -0
  7. package/dist/index.cjs.js +2 -2
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.es.js +42 -32
  10. package/dist/types/npm.d.ts +14 -0
  11. package/dist/vite-env.d.ts +1 -1
  12. package/docs/builtin/paths.json +74 -70
  13. package/docs/cli/create.md +28 -28
  14. package/docs/cli/local-server.md +38 -38
  15. package/docs/cli/login.md +32 -32
  16. package/docs/cli/publish.md +31 -31
  17. package/docs/cli/run.md +26 -26
  18. package/docs/cli/serve.md +43 -43
  19. package/docs/cli/swap.md +30 -30
  20. package/package.json +100 -100
  21. package/resources/AGENTS.md +40 -40
  22. package/src/built-in/CDEManager/example.ts +214 -214
  23. package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
  24. package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
  25. package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
  26. package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
  27. package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
  28. package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
  29. package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
  30. package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
  31. package/src/built-in/UIManager/src/app/example.ts +99 -99
  32. package/src/built-in/UIManager/src/app/examples/AppState/example.ts +142 -142
  33. package/src/built-in/UIManager/src/app/examples/Banner/example.ts +161 -0
  34. package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +125 -125
  35. package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +126 -0
  36. package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +92 -92
  37. package/src/built-in/UIManager/src/app/examples/Modal/example.ts +165 -0
  38. package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +135 -161
  39. package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +104 -104
  40. package/src/built-in/index.ts +649 -755
  41. package/src/cli/templates/app/README.md +241 -241
  42. package/src/cli/templates/app/index.html +35 -27
  43. package/src/cli/templates/app/package.json +31 -27
  44. package/src/cli/templates/app/src/app.ts +37 -0
  45. package/src/cli/templates/app/src/assets/tool-placeholder.ts +50 -0
  46. package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -87
  47. package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
  48. package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
  49. package/src/cli/templates/app/src/bim-components/index.ts +1 -1
  50. package/src/cli/templates/app/src/globals.ts +1 -0
  51. package/src/cli/templates/app/src/main.ts +340 -56
  52. package/src/cli/templates/app/src/setups/active-tool-hud.ts +69 -0
  53. package/src/cli/templates/app/src/setups/camera-tools.ts +63 -0
  54. package/src/cli/templates/app/src/setups/camera-views.ts +201 -0
  55. package/src/cli/templates/app/src/setups/card-header.ts +35 -0
  56. package/src/cli/templates/app/src/setups/clipper-panel.ts +167 -0
  57. package/src/cli/templates/app/src/setups/clipper-tool.ts +754 -0
  58. package/src/cli/templates/app/src/setups/clipper.ts +75 -0
  59. package/src/cli/templates/app/src/setups/cloud-runner.ts +13 -9
  60. package/src/cli/templates/app/src/setups/commands-panel.ts +239 -0
  61. package/src/cli/templates/app/src/setups/data-table-panel.ts +2224 -0
  62. package/src/cli/templates/app/src/setups/exploded-view.ts +246 -0
  63. package/src/cli/templates/app/src/setups/file-format-icons.ts +95 -0
  64. package/src/cli/templates/app/src/setups/files-panel.ts +1503 -0
  65. package/src/cli/templates/app/src/setups/fps-indicator.ts +63 -0
  66. package/src/cli/templates/app/src/setups/graphics-panel.ts +490 -0
  67. package/src/cli/templates/app/src/setups/helper-panel.ts +88 -0
  68. package/src/cli/templates/app/src/setups/hider.ts +98 -0
  69. package/src/cli/templates/app/src/setups/index.ts +33 -3
  70. package/src/cli/templates/app/src/setups/inspection.ts +136 -0
  71. package/src/cli/templates/app/src/setups/measurement-panel.ts +188 -0
  72. package/src/cli/templates/app/src/setups/measurement-settings-panel.ts +161 -0
  73. package/src/cli/templates/app/src/setups/measurement-tool.ts +570 -0
  74. package/src/cli/templates/app/src/setups/measurements.ts +133 -0
  75. package/src/cli/templates/app/src/setups/model-tree.ts +797 -0
  76. package/src/cli/templates/app/src/setups/navigation-gizmo.ts +316 -0
  77. package/src/cli/templates/app/src/setups/objects-panel.ts +127 -0
  78. package/src/cli/templates/app/src/setups/plans-panel.ts +619 -0
  79. package/src/cli/templates/app/src/setups/properties-panel.ts +1048 -0
  80. package/src/cli/templates/app/src/setups/reality-capture/lib/hidden-tiles-plugin.ts +88 -0
  81. package/src/cli/templates/app/src/setups/reality-capture/lib/point-tile-plugin.ts +166 -0
  82. package/src/cli/templates/app/src/setups/reality-capture/lib/render-frame.ts +31 -0
  83. package/src/cli/templates/app/src/setups/reality-capture/lib/splat-tile-plugin.ts +51 -0
  84. package/src/cli/templates/app/src/setups/reality-capture-viewer.ts +1383 -0
  85. package/src/cli/templates/app/src/setups/right-sidebar.ts +178 -0
  86. package/src/cli/templates/app/src/setups/settings-panel.ts +94 -0
  87. package/src/cli/templates/app/src/setups/styles-panel.ts +110 -0
  88. package/src/cli/templates/app/src/setups/styles.ts +375 -0
  89. package/src/cli/templates/app/src/setups/tool-mode-manager.ts +150 -0
  90. package/src/cli/templates/app/src/setups/tool-mode.ts +23 -0
  91. package/src/cli/templates/app/src/setups/toolbar.ts +229 -0
  92. package/src/cli/templates/app/src/setups/ui-manager.ts +27 -0
  93. package/src/cli/templates/app/src/setups/visibility-toolbar.ts +498 -0
  94. package/src/cli/templates/app/src/setups/walkthrough.ts +528 -0
  95. package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -0
  96. package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -0
  97. package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -0
  98. package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -0
  99. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -0
  100. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -0
  101. package/src/cli/templates/app/src/ui-components/index.ts +2 -1
  102. package/src/cli/templates/app/tsconfig.json +16 -17
  103. package/src/cli/templates/app/vite.config.js +45 -42
  104. package/src/cli/templates/cloud-component/README.md +205 -205
  105. package/src/cli/templates/cloud-component/_thatopen +5 -5
  106. package/src/cli/templates/cloud-component/declarations.json +4 -4
  107. package/src/cli/templates/cloud-component/package.json +21 -21
  108. package/src/cli/templates/cloud-component/src/main.ts +70 -70
  109. package/src/cli/templates/cloud-component/tsconfig.json +16 -16
  110. package/src/cli/templates/cloud-component/vite.config.js +46 -46
  111. package/src/cli/templates/shared/AGENTS.md +8 -8
  112. package/src/cli/templates/shared/CLAUDE.md +1 -1
  113. package/src/cli/templates/shared/_gitignore +5 -4
  114. package/src/built-in/AppManager/example.ts +0 -86
  115. package/src/built-in/FileList/example.ts +0 -107
  116. package/src/built-in/TabbedNavigation/example.ts +0 -115
  117. package/src/built-in/ViewportsManager/example.ts +0 -106
  118. package/src/cli/templates/app/src/setups/fragments.ts +0 -18
  119. package/src/cli/templates/app/src/setups/ifc-loader.ts +0 -12
  120. package/src/cli/templates/app/src/ui-components/AppPanel/index.ts +0 -160
@@ -0,0 +1,201 @@
1
+ import * as THREE from "three";
2
+ import * as OBC from "@thatopen/components";
3
+ import * as BUI from "@thatopen/ui";
4
+ import { cameraTools } from "./camera-tools";
5
+
6
+ /**
7
+ * Camera navigation — a COMPACT floating overlay pinned top-right of the
8
+ * viewport (mirrors visibility-toolbar's self-mounting floating-grid overlay).
9
+ *
10
+ * Controls:
11
+ * - View presets (Iso / Top / Bottom / Front / Back / Left / Right) via a
12
+ * compact dropdown. Each frames the union bounding box of all loaded models:
13
+ * `setLookAt` along the preset axis, then `fitToSphere` to frame precisely
14
+ * while keeping the new direction.
15
+ * - Zoom-to-fit + Perspective⇄Orthographic toggle — reused from
16
+ * `cameraTools(components)` (no reimplementation).
17
+ *
18
+ * Self-mounts into the viewport overlay. Wire from main.ts with one line
19
+ * (after the viewport exists), like the visibility toolbar:
20
+ *
21
+ * cameraViews(components, viewerElement);
22
+ *
23
+ * @param components engine components
24
+ * @param container optional viewport element to overlay; if omitted, the first
25
+ * world's renderer container is used.
26
+ */
27
+
28
+ // Preset → camera direction (from target toward camera). Normalized + scaled by
29
+ // the model radius at apply time.
30
+ const VIEWS: Record<string, [number, number, number]> = {
31
+ Iso: [1, 0.8, 1],
32
+ Top: [0, 1, 0],
33
+ Bottom: [0, -1, 0],
34
+ Front: [0, 0, 1],
35
+ Back: [0, 0, -1],
36
+ Left: [-1, 0, 0],
37
+ Right: [1, 0, 0],
38
+ };
39
+
40
+ export const cameraViews = (
41
+ components: OBC.Components,
42
+ container?: HTMLElement,
43
+ ) => {
44
+ const fragments = components.get(OBC.FragmentsManager);
45
+ const cam = cameraTools(components);
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ const firstWorld = (): any => [...components.get(OBC.Worlds).list.values()][0];
48
+
49
+ const unionBox = () => {
50
+ const box = new THREE.Box3();
51
+ for (const model of fragments.list.values()) {
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
+ const b = (model as any).box as THREE.Box3 | undefined;
54
+ if (b && !b.isEmpty()) box.union(b);
55
+ }
56
+ return box;
57
+ };
58
+
59
+ const setView = async (name: string) => {
60
+ const dir = VIEWS[name];
61
+ if (!dir) return;
62
+ const controls = firstWorld()?.camera?.controls;
63
+ if (!controls?.setLookAt) return;
64
+ const box = unionBox();
65
+ if (box.isEmpty()) return;
66
+ const sphere = box.getBoundingSphere(new THREE.Sphere());
67
+ const r = sphere.radius || 1;
68
+ const c = sphere.center;
69
+ const v = new THREE.Vector3(dir[0], dir[1], dir[2])
70
+ .normalize()
71
+ .multiplyScalar(r * 3);
72
+ try {
73
+ // Move to the preset direction, then frame the sphere exactly (keeps the
74
+ // new direction, snaps the distance to fit).
75
+ await controls.setLookAt(
76
+ c.x + v.x, c.y + v.y, c.z + v.z,
77
+ c.x, c.y, c.z,
78
+ true,
79
+ );
80
+ await controls.fitToSphere(sphere, true);
81
+ } catch (error) {
82
+ console.warn("[camera-views] setView failed", error);
83
+ }
84
+ };
85
+
86
+ let busy = false;
87
+ const run = async (fn: () => void | Promise<void>) => {
88
+ if (busy) return;
89
+ busy = true;
90
+ try {
91
+ await fn();
92
+ } catch (error) {
93
+ console.warn("[camera-views] action failed", error);
94
+ } finally {
95
+ busy = false;
96
+ }
97
+ };
98
+
99
+ let tick = 0;
100
+ const [bar, barUpdate] = BUI.Component.create<HTMLElement, { tick: number }>(
101
+ // Param required: create() returns a single element (not the [element,
102
+ // update] tuple) when the template has arity 0.
103
+ (_state) => {
104
+ // Guard: the very first render can run before a world exists, and reading
105
+ // the projection spreads the (possibly empty/not-yet-ready) worlds list.
106
+ let ortho = false;
107
+ try {
108
+ ortho = cam.orthoToggle.active();
109
+ } catch {
110
+ /* world not ready yet — default to perspective icon, re-render later */
111
+ }
112
+ return BUI.html`
113
+ <bim-toolbar
114
+ style="
115
+ pointer-events: auto; overflow: visible; padding: 0.15rem;
116
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
117
+ "
118
+ >
119
+ <bim-toolbar-section label-hidden style="background: transparent;">
120
+ <bim-dropdown
121
+ icon="mdi:camera-control"
122
+ style="min-width: 6rem;"
123
+ @change=${(e: Event) => {
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
+ const picked = (e.target as any).value?.[0];
126
+ if (picked) void run(() => setView(String(picked)));
127
+ }}
128
+ >
129
+ ${Object.keys(VIEWS).map(
130
+ (name) => BUI.html`<bim-option label=${name}></bim-option>`,
131
+ )}
132
+ </bim-dropdown>
133
+ </bim-toolbar-section>
134
+ <bim-toolbar-section label-hidden style="background: transparent;">
135
+ <bim-button
136
+ icon=${cam.fitAll.icon}
137
+ @click=${() => run(() => cam.fitAll.run())}
138
+ style="width: 1.9rem; min-width: 1.9rem; height: 1.9rem;"
139
+ ><bim-tooltip placement="bottom">${cam.fitAll.label}</bim-tooltip></bim-button>
140
+ <bim-button
141
+ icon=${ortho ? "mdi:perspective-less" : "mdi:perspective-more"}
142
+ @click=${() =>
143
+ run(async () => {
144
+ if (cam.orthoToggle.active()) await cam.orthoToggle.deactivate();
145
+ else await cam.orthoToggle.activate();
146
+ barUpdate({ tick: ++tick });
147
+ })}
148
+ style="width: 1.9rem; min-width: 1.9rem; height: 1.9rem;"
149
+ ><bim-tooltip placement="bottom">${
150
+ ortho
151
+ ? "Orthographic — switch to Perspective"
152
+ : "Perspective — switch to Orthographic"
153
+ }</bim-tooltip></bim-button>
154
+ </bim-toolbar-section>
155
+ </bim-toolbar>
156
+ `;
157
+ },
158
+ { tick: 0 },
159
+ );
160
+
161
+ // ── Floating grid overlay: bar pinned TOP-RIGHT; empty areas click through ──
162
+ const grid = BUI.Component.create(() => {
163
+ const onCreated = (element?: Element) => {
164
+ if (!element) return;
165
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
166
+ const g = element as any;
167
+ g.elements = { bar };
168
+ g.layouts = {
169
+ main: {
170
+ template: `
171
+ "restL bar" auto
172
+ "fillL fillR" 1fr
173
+ / 1fr auto
174
+ `,
175
+ },
176
+ };
177
+ g.layout = "main";
178
+ };
179
+ return BUI.html`
180
+ <bim-grid style="padding: 1rem;" ${BUI.ref(onCreated)} floating></bim-grid>
181
+ `;
182
+ });
183
+
184
+ const resolveContainer = (): HTMLElement | undefined => {
185
+ if (container) return container;
186
+ const world = [...components.get(OBC.Worlds).list.values()][0] as
187
+ | { renderer?: { three?: { domElement?: HTMLElement } } }
188
+ | undefined;
189
+ const canvas = world?.renderer?.three?.domElement;
190
+ return (canvas?.parentElement as HTMLElement | undefined) ?? undefined;
191
+ };
192
+
193
+ const host = resolveContainer();
194
+ if (host) host.append(grid);
195
+ else
196
+ console.warn(
197
+ "[camera-views] no viewport found to overlay; append the returned element manually",
198
+ );
199
+
200
+ return bar;
201
+ };
@@ -0,0 +1,35 @@
1
+ import * as BUI from "@thatopen/ui";
2
+
3
+ /**
4
+ * A card title styled to match the nxt-bld-demo panels: an icon (#99A0AE) +
5
+ * a white→#99A0AE gradient text label, semibold with wide letter-spacing.
6
+ * Use inside a `header-hidden` `bim-panel` (replaces the default header). Sticky
7
+ * to the top so it stays put while the card body scrolls.
8
+ *
9
+ * @param icon iconify/mdi name (e.g. "mdi:file-tree")
10
+ * @param label the title text
11
+ * @param padLeft left padding so the title aligns with the card's content inset
12
+ */
13
+ export const cardHeader = (icon: string, label: string, padLeft = "0.6rem") => BUI.html`
14
+ <div
15
+ style="
16
+ position: sticky; top: 0; z-index: 2;
17
+ display: flex; align-items: center; gap: 0.45rem;
18
+ padding: 0.4rem 0.6rem 0.4rem ${padLeft};
19
+ font-size: 0.8rem; font-weight: 600; letter-spacing: 0.04em;
20
+ /* Opaque surface bg (#262629 = panel bg) so scrolled content doesn't show
21
+ behind the sticky title. */
22
+ background: var(--bim-ui_bg-contrast-10, #262629);
23
+ border-bottom: 1px solid var(--bim-ui_bg-contrast-20, rgba(255, 255, 255, 0.1));
24
+ "
25
+ >
26
+ <bim-icon icon=${icon} style="color: #99A0AE; font-size: 0.95rem;"></bim-icon>
27
+ <span
28
+ style="
29
+ background: linear-gradient(90deg, #ffffff 0%, #99a0ae 100%);
30
+ -webkit-background-clip: text; background-clip: text;
31
+ -webkit-text-fill-color: transparent; color: transparent;
32
+ "
33
+ >${label}</span>
34
+ </div>
35
+ `;
@@ -0,0 +1,167 @@
1
+ import * as OBC from "@thatopen/components";
2
+ import * as BUI from "@thatopen/ui";
3
+ import type { ClipperTool } from "./clipper-tool";
4
+
5
+ /**
6
+ * CLIPPING panel — a docked side-panel (a 4th layout alongside
7
+ * Explorer/Files/Graphics) for section planes + per-category section styling.
8
+ *
9
+ * Sections:
10
+ * - Section planes — master Enabled, Add plane (placing mode: double-click a
11
+ * surface), Clear all.
12
+ * - Active planes — one row per plane (per-plane enable + delete).
13
+ * - Section styling — master fills/edges visibility + a per-IFC-category list,
14
+ * each with a fill colour, an edge colour, and an include toggle.
15
+ *
16
+ * Vanilla BUI, matching graphics-panel.ts / files-panel.ts: `bim-panel` (native
17
+ * header label+icon), muted section bands, 1px contrast-20 hairline rows, the
18
+ * #3C3C41 scrollbar, library `bim-checkbox[toggle]` switches, `bim-button`s and
19
+ * `bim-color-input`s. Factory returns the element WITHOUT self-mounting.
20
+ */
21
+ export const clipperPanel = (
22
+ _components: OBC.Components,
23
+ tool: ClipperTool,
24
+ ) => {
25
+ const [panel, update] = BUI.Component.create<BUI.Panel, { tick: number }>(
26
+ (state) => {
27
+ const refresh = () => update({ tick: state.tick + 1 });
28
+
29
+ const enabled = tool.isEnabled();
30
+ const sectionStyle = tool.getSectionStyle();
31
+ const stylingVisible = tool.isStylingVisible();
32
+
33
+ return BUI.html`
34
+ <bim-panel label="Clipping" icon="mdi:scissors-cutting"
35
+ style="width: 100%; height: 100%; pointer-events: auto;">
36
+ <style>
37
+ .c-vp::-webkit-scrollbar { width: 0.4rem; height: 0.4rem; }
38
+ .c-vp::-webkit-scrollbar-thumb { border-radius: 0.25rem; background-color: var(--bim-scrollbar--c, #3C3C41); }
39
+ .c-vp::-webkit-scrollbar-track { background-color: var(--bim-scrollbar--bgc, var(--bim-ui_bg-contrast-10)); }
40
+
41
+ .c-band { box-sizing: border-box; padding: 0.4rem 0.4rem 0.4rem 1.1rem;
42
+ display: flex; align-items: center; gap: 0.4rem;
43
+ font-size: 0.76rem; font-weight: 500; letter-spacing: 0.01em;
44
+ color: var(--bim-ui_bg-contrast-70, #a7a7ab);
45
+ background: var(--bim-ui_bg-contrast-20, rgba(255,255,255,0.1));
46
+ border-top: 1px solid var(--bim-ui_bg-contrast-20, rgba(255,255,255,0.1));
47
+ border-bottom: 1px solid var(--bim-ui_bg-contrast-20, rgba(255,255,255,0.1)); }
48
+ .c-band-ico { flex: 0 0 auto; font-size: 0.9rem; color: var(--bim-ui_bg-contrast-70, #a7a7ab); }
49
+ .c-band:first-child { border-top: none; }
50
+
51
+ .c-row { box-sizing: border-box; min-height: 32px; display: flex;
52
+ align-items: center; justify-content: space-between; gap: 0.6rem;
53
+ padding: 0.35rem 0.4rem 0.35rem 1.1rem; font-size: 0.78rem;
54
+ color: var(--bim-ui_bg-contrast-100, #e3e3e3);
55
+ border-bottom: 1px solid var(--bim-ui_bg-contrast-20, rgba(255,255,255,0.1)); }
56
+ .c-k { opacity: 0.6; flex: 0 1 auto; min-width: 0; white-space: nowrap;
57
+ overflow: hidden; text-overflow: ellipsis; }
58
+ .c-v { flex: 1 1 auto; min-width: 0; display: flex; align-items: center;
59
+ justify-content: flex-end; gap: 0.5rem; }
60
+ .c-row bim-checkbox.c-toggle, .c-cat bim-checkbox.c-toggle { flex: 0 0 2rem; width: 2rem; }
61
+ .c-row bim-button.c-del { flex: 0 0 auto; --bim-button--bgc: transparent; }
62
+
63
+ .c-empty { padding: 0.6rem 0.4rem 0.6rem 1.1rem; opacity: 0.6;
64
+ font-size: 0.78rem; line-height: 1.3; white-space: normal; }
65
+
66
+ .c-actions { display: flex; flex-direction: column; gap: 0.4rem;
67
+ padding: 0.5rem 0.4rem 0.5rem 1.1rem; }
68
+ .c-actions bim-button { width: 100%; }
69
+
70
+ /* Per-category style row: [toggle] [name grows] [fill][edge swatches] */
71
+ .c-cat { box-sizing: border-box; min-height: 32px; display: flex;
72
+ align-items: center; gap: 0.5rem;
73
+ padding: 0.3rem 0.4rem 0.3rem 1.1rem; font-size: 0.78rem;
74
+ color: var(--bim-ui_bg-contrast-100, #e3e3e3);
75
+ border-bottom: 1px solid var(--bim-ui_bg-contrast-20, rgba(255,255,255,0.1)); }
76
+ .c-cat-name { flex: 1 1 auto; min-width: 0; opacity: 0.85;
77
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
78
+ .c-cat-colors { flex: 0 0 auto; display: flex; align-items: center; gap: 0.35rem; }
79
+ .c-cat-colors bim-color-input { flex: 0 0 auto; }
80
+ .c-cat-off { opacity: 0.45; }
81
+ .c-cat-off .c-cat-colors { pointer-events: none; }
82
+ </style>
83
+ <div style="display: flex; flex-direction: column; height: 100%; width: 100%;">
84
+ <div style="flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding: 0 0.4rem 0.4rem 1.1rem;">
85
+ <div class="c-vp" style="flex: 1 1 auto; min-height: 0; overflow-y: auto;
86
+ margin-left: -1.1rem; margin-right: -0.4rem;">
87
+
88
+ <div class="c-row">
89
+ <span class="c-k">Enabled</span>
90
+ <span class="c-v">
91
+ <bim-checkbox class="c-toggle" toggle ?checked=${enabled}
92
+ @change=${(e: Event) => {
93
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
+ tool.setEnabled(!!(e.target as any).checked);
95
+ refresh();
96
+ }}></bim-checkbox>
97
+ </span>
98
+ </div>
99
+ <div class="c-row">
100
+ <span class="c-k">Show fills / edges</span>
101
+ <span class="c-v">
102
+ <bim-checkbox class="c-toggle" toggle ?checked=${stylingVisible}
103
+ @change=${(e: Event) => {
104
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
+ tool.setStylingVisible(!!(e.target as any).checked);
106
+ refresh();
107
+ }}></bim-checkbox>
108
+ </span>
109
+ </div>
110
+ <div class="c-row">
111
+ <span class="c-k">Fill color</span>
112
+ <span class="c-v">
113
+ <bim-color-input color=${sectionStyle.fill}
114
+ @input=${(e: Event) => {
115
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
+ const t = e.target as any;
117
+ tool.setSectionFill(String(t.color ?? t.value));
118
+ }}></bim-color-input>
119
+ </span>
120
+ </div>
121
+ <div class="c-row">
122
+ <span class="c-k">Edge color</span>
123
+ <span class="c-v">
124
+ <bim-color-input color=${sectionStyle.line}
125
+ @input=${(e: Event) => {
126
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
+ const t = e.target as any;
128
+ tool.setSectionLine(String(t.color ?? t.value));
129
+ }}></bim-color-input>
130
+ </span>
131
+ </div>
132
+ <div class="c-row">
133
+ <span class="c-k">Fill opacity</span>
134
+ <span class="c-v">
135
+ <bim-number-input slider value=${sectionStyle.opacity}
136
+ min="0" max="1" step="0.05"
137
+ @change=${(e: Event) => {
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
+ tool.setSectionOpacity(Number((e.target as any).value));
140
+ }}></bim-number-input>
141
+ </span>
142
+ </div>
143
+ <div class="c-row">
144
+ <span class="c-k">Edge width</span>
145
+ <span class="c-v">
146
+ <bim-number-input slider value=${sectionStyle.width}
147
+ min="1" max="20" step="1"
148
+ @change=${(e: Event) => {
149
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
150
+ tool.setSectionWidth(Number((e.target as any).value));
151
+ }}></bim-number-input>
152
+ </span>
153
+ </div>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </bim-panel>
158
+ `;
159
+ },
160
+ { tick: 0 },
161
+ );
162
+
163
+ tool.onChanged.add(() => update({ tick: 0 }));
164
+ tool.onStyleChanged.add(() => update({ tick: 0 }));
165
+
166
+ return panel;
167
+ };