@toolpath/dfm 0.5.0 → 1.0.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.
- package/README.md +33 -186
- package/dist/{chunk-4IRNLDE6.js → chunk-XHYPKN4E.js} +1510 -93
- package/dist/index.d.ts +1 -389
- package/dist/index.js +93 -2610
- package/dist/model/index.d.ts +684 -3
- package/dist/model/index.js +64 -1388
- package/package.json +5 -23
- package/dfm.css +0 -10
- package/dist/feature-record-CfjgDCJU.d.ts +0 -599
package/dist/index.js
CHANGED
|
@@ -1,2649 +1,132 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CRITICAL_COLOR,
|
|
3
|
+
FILE_KIND,
|
|
4
|
+
MAX_DATASHEET_FIELDS,
|
|
2
5
|
MILLING_RULES,
|
|
3
6
|
OPS,
|
|
7
|
+
PINCH_TOOL_MM,
|
|
4
8
|
PRESENCE_METRIC,
|
|
9
|
+
QUICK_SUBJECTS,
|
|
5
10
|
RANGE_HIGHS,
|
|
6
11
|
RANGE_LOWS,
|
|
7
12
|
RULE_COLORS,
|
|
8
13
|
TURNING_RULES,
|
|
14
|
+
WARNING_COLOR,
|
|
15
|
+
brokenRules,
|
|
16
|
+
checkPart,
|
|
17
|
+
checkTurnedPart,
|
|
18
|
+
compareColors,
|
|
19
|
+
datasheetFields,
|
|
20
|
+
describeRule,
|
|
21
|
+
dfmFeatures,
|
|
9
22
|
directionLabel,
|
|
23
|
+
featureColor,
|
|
24
|
+
featureMeasurements,
|
|
25
|
+
featureProfile,
|
|
10
26
|
featureRows,
|
|
27
|
+
featureSheet,
|
|
11
28
|
featureTypeLabel,
|
|
12
29
|
formatMeasured,
|
|
13
30
|
fromField,
|
|
14
31
|
highOf,
|
|
15
32
|
hitFigure,
|
|
33
|
+
isNoReading,
|
|
34
|
+
isSurfaceType,
|
|
16
35
|
lengthValue,
|
|
17
36
|
llmRulePrompt,
|
|
18
37
|
lowOf,
|
|
19
38
|
makeRule,
|
|
39
|
+
maxBallDiameter,
|
|
20
40
|
metricInfo,
|
|
21
41
|
metricsForSubject,
|
|
22
42
|
opSymbol,
|
|
23
43
|
parseRuleSetFile,
|
|
44
|
+
parseRules,
|
|
24
45
|
peakHeight,
|
|
46
|
+
pinchLabel,
|
|
47
|
+
pinchMark,
|
|
48
|
+
placePinchTool,
|
|
25
49
|
quantityUnit,
|
|
50
|
+
readPinch,
|
|
26
51
|
readReachCurve,
|
|
27
52
|
runStart,
|
|
53
|
+
sameDepth,
|
|
28
54
|
serializeRuleSet,
|
|
29
55
|
subjectLabel,
|
|
30
56
|
toField,
|
|
31
57
|
trimmedCurve,
|
|
58
|
+
turnedStretches,
|
|
59
|
+
turnedTypeLabel,
|
|
32
60
|
unitLabel,
|
|
33
61
|
upperOf,
|
|
34
62
|
withMetric,
|
|
35
63
|
withOp,
|
|
36
|
-
withSubject
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// src/rule-list/rule-list.tsx
|
|
40
|
-
import { useMemo as useMemo2, useState as useState8 } from "react";
|
|
41
|
-
import { ScrollArea } from "@toolpath/ui";
|
|
42
|
-
|
|
43
|
-
// src/rule-list/list-keys.ts
|
|
44
|
-
var listKeys = (name, orientation = "vertical") => (event) => {
|
|
45
|
-
const [back, forward] = orientation === "vertical" ? ["ArrowUp", "ArrowDown"] : ["ArrowLeft", "ArrowRight"];
|
|
46
|
-
if (![back, forward, "Home", "End"].includes(event.key) || isField(event.target)) return;
|
|
47
|
-
const items = [
|
|
48
|
-
...event.currentTarget.querySelectorAll(`[data-list-item="${name}"]`)
|
|
49
|
-
];
|
|
50
|
-
if (items.length === 0) return;
|
|
51
|
-
const at = currentIndex(items, document.activeElement);
|
|
52
|
-
const chosen = items.findIndex((item) => item.getAttribute("aria-pressed") === "true");
|
|
53
|
-
const step = (from) => Math.min(Math.max(from + (event.key === forward ? 1 : -1), 0), items.length - 1);
|
|
54
|
-
const index = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : at !== -1 ? step(at) : chosen !== -1 ? items[chosen]?.dataset.follow !== void 0 ? step(chosen) : chosen : event.key === forward ? 0 : items.length - 1;
|
|
55
|
-
const target = items[index];
|
|
56
|
-
if (!target) return;
|
|
57
|
-
event.preventDefault();
|
|
58
|
-
event.stopPropagation();
|
|
59
|
-
if (target === document.activeElement) return;
|
|
60
|
-
target.focus();
|
|
61
|
-
if (target.dataset.follow !== void 0) target.click();
|
|
62
|
-
};
|
|
63
|
-
var currentIndex = (items, active) => {
|
|
64
|
-
if (!active) return -1;
|
|
65
|
-
const exact = items.indexOf(active);
|
|
66
|
-
if (exact !== -1) return exact;
|
|
67
|
-
const row = active.closest("li");
|
|
68
|
-
return row ? items.findIndex((item) => item.closest("li") === row) : -1;
|
|
69
|
-
};
|
|
70
|
-
var isField = (target) => target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement && target.type !== "color" && target.type !== "button" || target instanceof HTMLElement && target.isContentEditable;
|
|
71
|
-
|
|
72
|
-
// src/rule-list/new-rule.tsx
|
|
73
|
-
import { useState as useState2 } from "react";
|
|
74
|
-
|
|
75
|
-
// src/rule-list/options.ts
|
|
76
|
-
var subjectChoices = (current, all, vocabulary = MILLING_RULES) => {
|
|
77
|
-
const { quickSubjects } = vocabulary;
|
|
78
|
-
const quick = [...quickSubjects, ...quickSubjects.includes(current) ? [] : [current]];
|
|
79
|
-
const first = {
|
|
80
|
-
choices: quick.map((value) => ({ value, label: subjectLabel(value, vocabulary) }))
|
|
81
|
-
};
|
|
82
|
-
if (!all) return { groups: [first], more: "More feature types\u2026" };
|
|
83
|
-
const rest = vocabulary.subjects.filter((each) => !quick.includes(each.value));
|
|
84
|
-
return { groups: [first, { heading: "More feature types", choices: rest }] };
|
|
85
|
-
};
|
|
86
|
-
var metricChoices = (subject, current, all, vocabulary = MILLING_RULES) => {
|
|
87
|
-
const { quickMetrics } = vocabulary;
|
|
88
|
-
const offered = metricsForSubject(subject, vocabulary);
|
|
89
|
-
const quick = offered.filter((each) => quickMetrics.includes(each.key) || each.key === current);
|
|
90
|
-
quick.sort((a, b) => rank(quickMetrics, a.key) - rank(quickMetrics, b.key));
|
|
91
|
-
const rest = offered.filter((each) => !quick.includes(each));
|
|
92
|
-
const first = { choices: quick.map(choice) };
|
|
93
|
-
if (!all) return { groups: [first], more: rest.length > 0 ? "More measures\u2026" : void 0 };
|
|
94
|
-
return {
|
|
95
|
-
groups: [
|
|
96
|
-
first,
|
|
97
|
-
...grouped(rest).map(([heading, metrics]) => ({ heading, choices: metrics.map(choice) }))
|
|
98
|
-
]
|
|
99
|
-
};
|
|
100
|
-
};
|
|
101
|
-
var choice = ({ key, label }) => ({ value: key, label });
|
|
102
|
-
var rank = (quickMetrics, key) => {
|
|
103
|
-
const at = quickMetrics.indexOf(key);
|
|
104
|
-
return at === -1 ? quickMetrics.length : at;
|
|
105
|
-
};
|
|
106
|
-
var grouped = (items) => {
|
|
107
|
-
const groups = /* @__PURE__ */ new Map();
|
|
108
|
-
for (const item of items) groups.set(item.group, [...groups.get(item.group) ?? [], item]);
|
|
109
|
-
return [...groups];
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// src/rule-list/picker.tsx
|
|
113
|
-
import {
|
|
114
|
-
Fragment,
|
|
115
|
-
useEffect,
|
|
116
|
-
useId,
|
|
117
|
-
useRef,
|
|
118
|
-
useState
|
|
119
|
-
} from "react";
|
|
120
|
-
import { CheckIcon } from "@phosphor-icons/react";
|
|
121
|
-
import { Menu, cn as cn2 } from "@toolpath/ui";
|
|
122
|
-
|
|
123
|
-
// src/rule-list/styles.ts
|
|
124
|
-
import { cn } from "@toolpath/ui";
|
|
125
|
-
var FOCUS_RING = "outline-none focus-visible:ring-2 focus-visible:ring-info/75";
|
|
126
|
-
var WORD = cn(
|
|
127
|
-
"cursor-pointer appearance-none rounded bg-transparent px-0.5 font-medium text-gray-700 field-sizing-content",
|
|
128
|
-
"underline decoration-gray-300 decoration-dotted underline-offset-4 hover:bg-gray-100",
|
|
129
|
-
"dark:text-zinc-100 dark:decoration-zinc-600 dark:hover:bg-zinc-700",
|
|
130
|
-
"outline-none focus-visible:bg-gray-100 focus-visible:ring-2 focus-visible:ring-info/75 dark:focus-visible:bg-zinc-700"
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
// src/rule-list/picker.tsx
|
|
134
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
135
|
-
var HEADING = "px-2 pt-1.5 pb-1 text-[10px] font-semibold tracking-wider text-gray-300 uppercase font-body dark:text-zinc-500";
|
|
136
|
-
var Picker = ({
|
|
137
|
-
label,
|
|
138
|
-
value,
|
|
139
|
-
choices,
|
|
140
|
-
onChange,
|
|
141
|
-
onMore,
|
|
142
|
-
placeholder,
|
|
143
|
-
title: title2,
|
|
144
|
-
className,
|
|
145
|
-
autoFocus
|
|
146
|
-
}) => {
|
|
147
|
-
const id = useId();
|
|
148
|
-
const held = useRef(null);
|
|
149
|
-
const [holding, setHolding] = useState(0);
|
|
150
|
-
const hold = (next) => {
|
|
151
|
-
held.current = next;
|
|
152
|
-
if (next) setHolding((count) => count + 1);
|
|
153
|
-
};
|
|
154
|
-
const flat = choices.groups.flatMap((group) => group.choices);
|
|
155
|
-
const at = flat.findIndex((choice2) => choice2.value === value);
|
|
156
|
-
const shown = flat[at]?.label ?? placeholder ?? value;
|
|
157
|
-
const starts = choices.groups.map(
|
|
158
|
-
(_, g) => choices.groups.slice(0, g).reduce((count, group) => count + group.choices.length, 0)
|
|
159
|
-
);
|
|
160
|
-
useEffect(() => {
|
|
161
|
-
if (holding === 0) return;
|
|
162
|
-
const frame = requestAnimationFrame(() => focusHeld(id, held.current));
|
|
163
|
-
return () => cancelAnimationFrame(frame);
|
|
164
|
-
}, [holding, id]);
|
|
165
|
-
return (
|
|
166
|
-
// A click on the word, in its menu or on the menu's backdrop is the picker's: React would carry
|
|
167
|
-
// it up through the portal to the row, which opens on a click.
|
|
168
|
-
/* @__PURE__ */ jsx("span", { onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsxs(Menu, { onOpenChange: (open) => hold(open && at !== -1 ? { index: at, reveal: false } : null), children: [
|
|
169
|
-
/* @__PURE__ */ jsx(
|
|
170
|
-
Menu.Trigger,
|
|
171
|
-
{
|
|
172
|
-
nativeButton: true,
|
|
173
|
-
render: /* @__PURE__ */ jsx(
|
|
174
|
-
"button",
|
|
175
|
-
{
|
|
176
|
-
type: "button",
|
|
177
|
-
"aria-label": at === -1 ? label : `${label}: ${shown}`,
|
|
178
|
-
title: title2,
|
|
179
|
-
className: cn2(
|
|
180
|
-
WORD,
|
|
181
|
-
"data-[popup-open]:bg-gray-100 dark:data-[popup-open]:bg-zinc-700",
|
|
182
|
-
className
|
|
183
|
-
),
|
|
184
|
-
autoFocus
|
|
185
|
-
}
|
|
186
|
-
),
|
|
187
|
-
children: shown
|
|
188
|
-
}
|
|
189
|
-
),
|
|
190
|
-
/* @__PURE__ */ jsxs(
|
|
191
|
-
Menu.Popover,
|
|
192
|
-
{
|
|
193
|
-
align: "start",
|
|
194
|
-
sideOffset: 4,
|
|
195
|
-
collisionPadding: 8,
|
|
196
|
-
className: "max-h-[min(24rem,var(--available-height))] min-w-[var(--anchor-width)] overflow-y-auto py-1 shadow-lg",
|
|
197
|
-
onFocus: (event) => {
|
|
198
|
-
const item = held.current && document.getElementById(itemId(id, held.current.index));
|
|
199
|
-
if (item && event.target !== item) focusHeld(id, held.current);
|
|
200
|
-
},
|
|
201
|
-
onPointerMove: () => hold(null),
|
|
202
|
-
onKeyDownCapture: () => hold(null),
|
|
203
|
-
onKeyDown: keepKeys,
|
|
204
|
-
children: [
|
|
205
|
-
choices.groups.map((group, g) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
206
|
-
g > 0 ? /* @__PURE__ */ jsx(Menu.Divider, { className: "my-1" }) : null,
|
|
207
|
-
/* @__PURE__ */ jsxs(
|
|
208
|
-
"div",
|
|
209
|
-
{
|
|
210
|
-
role: "group",
|
|
211
|
-
"aria-labelledby": group.heading ? `${id}-group-${g}` : void 0,
|
|
212
|
-
className: "flex flex-col",
|
|
213
|
-
children: [
|
|
214
|
-
group.heading ? /* @__PURE__ */ jsx("div", { id: `${id}-group-${g}`, className: HEADING, children: group.heading }) : null,
|
|
215
|
-
group.choices.map((choice2, c) => {
|
|
216
|
-
const chosen = choice2.value === value;
|
|
217
|
-
return /* @__PURE__ */ jsxs(
|
|
218
|
-
Menu.Item,
|
|
219
|
-
{
|
|
220
|
-
id: itemId(id, (starts[g] ?? 0) + c),
|
|
221
|
-
label: choice2.label,
|
|
222
|
-
onClick: () => {
|
|
223
|
-
if (!chosen) onChange(choice2.value);
|
|
224
|
-
},
|
|
225
|
-
className: "gap-3",
|
|
226
|
-
children: [
|
|
227
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1", children: choice2.label }),
|
|
228
|
-
/* @__PURE__ */ jsx(
|
|
229
|
-
CheckIcon,
|
|
230
|
-
{
|
|
231
|
-
"aria-hidden": true,
|
|
232
|
-
weight: "bold",
|
|
233
|
-
className: cn2(
|
|
234
|
-
"size-3.5 text-gray-400 dark:text-zinc-300",
|
|
235
|
-
!chosen && "invisible"
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
)
|
|
239
|
-
]
|
|
240
|
-
},
|
|
241
|
-
choice2.value
|
|
242
|
-
);
|
|
243
|
-
})
|
|
244
|
-
]
|
|
245
|
-
}
|
|
246
|
-
)
|
|
247
|
-
] }, group.heading ?? g)),
|
|
248
|
-
choices.more && onMore ? /* @__PURE__ */ jsx(
|
|
249
|
-
Menu.Item,
|
|
250
|
-
{
|
|
251
|
-
closeOnClick: false,
|
|
252
|
-
onClick: () => {
|
|
253
|
-
hold({ index: flat.length, reveal: true });
|
|
254
|
-
onMore();
|
|
255
|
-
},
|
|
256
|
-
className: "text-gray-400 dark:text-zinc-400",
|
|
257
|
-
children: choices.more
|
|
258
|
-
}
|
|
259
|
-
) : null
|
|
260
|
-
]
|
|
261
|
-
}
|
|
262
|
-
)
|
|
263
|
-
] }) })
|
|
264
|
-
);
|
|
265
|
-
};
|
|
266
|
-
var focusHeld = (picker, held) => {
|
|
267
|
-
const item = held && document.getElementById(itemId(picker, held.index));
|
|
268
|
-
if (!item) return;
|
|
269
|
-
item.focus({ preventScroll: held.reveal });
|
|
270
|
-
const menu = item.closest('[role="menu"]');
|
|
271
|
-
const group = item.closest('[role="group"]');
|
|
272
|
-
if (held.reveal && menu && group)
|
|
273
|
-
menu.scrollTop += group.getBoundingClientRect().top - menu.getBoundingClientRect().top - 8;
|
|
274
|
-
};
|
|
275
|
-
var itemId = (picker, index) => `${picker}-${index}`;
|
|
276
|
-
var keepKeys = (event) => {
|
|
277
|
-
if (event.key !== "Tab") event.stopPropagation();
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// src/rule-list/new-rule.tsx
|
|
281
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
282
|
-
var NewRule = ({
|
|
283
|
-
onAdd,
|
|
284
|
-
vocabulary
|
|
285
|
-
}) => {
|
|
286
|
-
const [all, setAll] = useState2(false);
|
|
287
|
-
return /* @__PURE__ */ jsxs2("li", { className: "flex items-start gap-1.5 py-1 pr-1.5", children: [
|
|
288
|
-
/* @__PURE__ */ jsx2("span", { "aria-hidden": true, className: "size-7 shrink-0" }),
|
|
289
|
-
/* @__PURE__ */ jsx2(
|
|
290
|
-
"span",
|
|
291
|
-
{
|
|
292
|
-
"aria-hidden": true,
|
|
293
|
-
className: "mt-1.5 size-4 shrink-0 rounded-full border border-dashed border-gray-300 dark:border-zinc-600"
|
|
294
|
-
}
|
|
295
|
-
),
|
|
296
|
-
/* @__PURE__ */ jsx2("p", { className: "min-w-0 flex-1 text-sm leading-7", children: /* @__PURE__ */ jsx2(
|
|
297
|
-
Picker,
|
|
298
|
-
{
|
|
299
|
-
label: "New rule",
|
|
300
|
-
placeholder: "+ New Rule",
|
|
301
|
-
value: "",
|
|
302
|
-
choices: subjectChoices("any", all, vocabulary),
|
|
303
|
-
onChange: onAdd,
|
|
304
|
-
onMore: () => setAll(true),
|
|
305
|
-
className: "font-normal text-gray-400 dark:text-zinc-500"
|
|
306
|
-
}
|
|
307
|
-
) })
|
|
308
|
-
] });
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
// src/rule-list/rule-row.tsx
|
|
312
|
-
import { useRef as useRef3, useState as useState6 } from "react";
|
|
313
|
-
import { CaretRightIcon as CaretRightIcon2, TrashIcon } from "@phosphor-icons/react";
|
|
314
|
-
import { IconButton, cn as cn7 } from "@toolpath/ui";
|
|
315
|
-
|
|
316
|
-
// src/rule-list/feature-rows.tsx
|
|
317
|
-
import { useMemo } from "react";
|
|
318
|
-
|
|
319
|
-
// src/rule-list/feature-button.tsx
|
|
320
|
-
import { cn as cn3 } from "@toolpath/ui";
|
|
321
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
322
|
-
var FEATURE_ROW = cn3(
|
|
323
|
-
"flex w-full cursor-pointer items-baseline gap-2 rounded px-2 py-1 text-left text-xs",
|
|
324
|
-
"text-gray-500 hover:bg-gray-100 dark:text-zinc-300 dark:hover:bg-zinc-800",
|
|
325
|
-
"aria-pressed:bg-gray-100 aria-pressed:font-semibold aria-pressed:text-gray-700",
|
|
326
|
-
"dark:aria-pressed:bg-zinc-800 dark:aria-pressed:text-zinc-100",
|
|
327
|
-
FOCUS_RING
|
|
328
|
-
);
|
|
329
|
-
var ruleHit = (check, rule, tag) => check.hitsByTag.get(tag.toLowerCase())?.find((each) => each.rule.id === rule.id);
|
|
330
|
-
var HitFigure = ({
|
|
331
|
-
hit,
|
|
332
|
-
units
|
|
333
|
-
}) => hit ? /* @__PURE__ */ jsx3("span", { className: "shrink-0 text-gray-400 tabular-nums dark:text-zinc-400", children: hitFigure(hit, units) }) : null;
|
|
334
|
-
var FeatureButton = ({
|
|
335
|
-
tag,
|
|
336
|
-
featureOf,
|
|
337
|
-
rule,
|
|
338
|
-
check,
|
|
339
|
-
units,
|
|
340
|
-
vocabulary,
|
|
341
|
-
selected,
|
|
342
|
-
selectedGroup,
|
|
343
|
-
onInspect,
|
|
344
|
-
onHover,
|
|
345
|
-
onBack
|
|
346
|
-
}) => {
|
|
347
|
-
const feature = featureOf.get(tag);
|
|
348
|
-
const { name, detail } = feature ? vocabulary.featureLabel(feature) : { name: "Feature", detail: "" };
|
|
349
|
-
return /* @__PURE__ */ jsxs3(
|
|
350
|
-
"button",
|
|
351
|
-
{
|
|
352
|
-
type: "button",
|
|
353
|
-
"data-list-item": "rules",
|
|
354
|
-
"data-follow": true,
|
|
355
|
-
"data-feature-row": true,
|
|
356
|
-
"aria-pressed": selected === tag && !selectedGroup,
|
|
357
|
-
onClick: () => onInspect(tag),
|
|
358
|
-
onKeyDown: (event) => {
|
|
359
|
-
if (event.key !== "ArrowLeft") return;
|
|
360
|
-
event.preventDefault();
|
|
361
|
-
onBack();
|
|
362
|
-
},
|
|
363
|
-
onPointerEnter: () => onHover(tag),
|
|
364
|
-
onPointerLeave: () => onHover(null),
|
|
365
|
-
className: FEATURE_ROW,
|
|
366
|
-
children: [
|
|
367
|
-
/* @__PURE__ */ jsxs3("span", { className: "min-w-0 flex-1 truncate", children: [
|
|
368
|
-
name,
|
|
369
|
-
" ",
|
|
370
|
-
/* @__PURE__ */ jsx3("span", { className: "font-normal text-gray-300 dark:text-zinc-500", children: detail })
|
|
371
|
-
] }),
|
|
372
|
-
/* @__PURE__ */ jsx3(HitFigure, { hit: ruleHit(check, rule, tag), units })
|
|
373
|
-
]
|
|
374
|
-
}
|
|
375
|
-
);
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
// src/rule-list/hole-group-row.tsx
|
|
379
|
-
import { useRef as useRef2, useState as useState3 } from "react";
|
|
380
|
-
import { CaretRightIcon } from "@phosphor-icons/react";
|
|
381
|
-
import { cn as cn4 } from "@toolpath/ui";
|
|
382
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
383
|
-
var HoleGroupRow = ({
|
|
384
|
-
row,
|
|
385
|
-
direction,
|
|
386
|
-
...context
|
|
387
|
-
}) => {
|
|
388
|
-
const [open, setOpen] = useState3(false);
|
|
389
|
-
const groupRef = useRef2(null);
|
|
390
|
-
const { rule, check, units, selectedGroup, featureOf, onInspect, onHover, onBack } = context;
|
|
391
|
-
const [first] = row.tags;
|
|
392
|
-
const chosen = selectedGroup?.[0] === first;
|
|
393
|
-
return /* @__PURE__ */ jsxs4("li", { children: [
|
|
394
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-center", children: [
|
|
395
|
-
/* @__PURE__ */ jsx4(
|
|
396
|
-
"button",
|
|
397
|
-
{
|
|
398
|
-
type: "button",
|
|
399
|
-
"aria-label": open ? "Hide holes" : "Show holes",
|
|
400
|
-
"aria-expanded": open,
|
|
401
|
-
tabIndex: -1,
|
|
402
|
-
onClick: () => setOpen((was) => !was),
|
|
403
|
-
className: "-ml-5 flex size-5 shrink-0 cursor-pointer items-center justify-center text-gray-300 dark:text-zinc-500",
|
|
404
|
-
children: /* @__PURE__ */ jsx4(
|
|
405
|
-
CaretRightIcon,
|
|
406
|
-
{
|
|
407
|
-
weight: "bold",
|
|
408
|
-
className: cn4("size-3 transition-transform", open && "rotate-90")
|
|
409
|
-
}
|
|
410
|
-
)
|
|
411
|
-
}
|
|
412
|
-
),
|
|
413
|
-
/* @__PURE__ */ jsxs4(
|
|
414
|
-
"button",
|
|
415
|
-
{
|
|
416
|
-
ref: groupRef,
|
|
417
|
-
type: "button",
|
|
418
|
-
"data-list-item": "rules",
|
|
419
|
-
"data-follow": true,
|
|
420
|
-
"data-feature-row": true,
|
|
421
|
-
"aria-pressed": chosen,
|
|
422
|
-
onClick: () => onInspect(first, row.tags),
|
|
423
|
-
onKeyDown: (event) => {
|
|
424
|
-
if (event.key === "ArrowRight" && !open) {
|
|
425
|
-
event.preventDefault();
|
|
426
|
-
setOpen(true);
|
|
427
|
-
} else if (event.key === "ArrowLeft") {
|
|
428
|
-
event.preventDefault();
|
|
429
|
-
if (open) setOpen(false);
|
|
430
|
-
else onBack();
|
|
431
|
-
}
|
|
432
|
-
},
|
|
433
|
-
onPointerEnter: () => onHover(row.tags),
|
|
434
|
-
onPointerLeave: () => onHover(null),
|
|
435
|
-
className: FEATURE_ROW,
|
|
436
|
-
children: [
|
|
437
|
-
/* @__PURE__ */ jsxs4("span", { className: "min-w-0 flex-1 truncate", children: [
|
|
438
|
-
featureTypeLabel(featureOf.get(first)?.featureType ?? "hole"),
|
|
439
|
-
" \u2300",
|
|
440
|
-
formatMeasured("length", row.diameter, units),
|
|
441
|
-
" ",
|
|
442
|
-
/* @__PURE__ */ jsxs4("span", { className: "font-normal text-gray-400 dark:text-zinc-400", children: [
|
|
443
|
-
"\xD7",
|
|
444
|
-
row.tags.length,
|
|
445
|
-
" \xB7 from ",
|
|
446
|
-
direction
|
|
447
|
-
] })
|
|
448
|
-
] }),
|
|
449
|
-
/* @__PURE__ */ jsx4(HitFigure, { hit: ruleHit(check, rule, first), units })
|
|
450
|
-
]
|
|
451
|
-
}
|
|
452
|
-
)
|
|
453
|
-
] }),
|
|
454
|
-
open ? /* @__PURE__ */ jsx4("ul", { className: "pl-4", children: row.tags.map((tag) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(FeatureButton, { ...context, tag, onBack: () => groupRef.current?.focus() }) }, tag)) }) : null
|
|
455
|
-
] });
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
// src/rule-list/feature-rows.tsx
|
|
459
|
-
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
460
|
-
var FeatureRows = ({
|
|
461
|
-
tags,
|
|
462
|
-
otherTags,
|
|
463
|
-
features,
|
|
464
|
-
sheets,
|
|
465
|
-
...rest
|
|
466
|
-
}) => {
|
|
467
|
-
const featureOf = useMemo(
|
|
468
|
-
() => new Map(features.map((feature) => [feature.tag, feature])),
|
|
469
|
-
[features]
|
|
470
|
-
);
|
|
471
|
-
const required = useMemo(() => featureRows(tags, features, sheets), [tags, features, sheets]);
|
|
472
|
-
const others = useMemo(
|
|
473
|
-
() => featureRows(otherTags, features, sheets),
|
|
474
|
-
[otherTags, features, sheets]
|
|
475
|
-
);
|
|
476
|
-
const context = { ...rest, featureOf };
|
|
477
|
-
const list = (rows) => rows.map(
|
|
478
|
-
(row) => row.kind === "holes" ? /* @__PURE__ */ jsx5(
|
|
479
|
-
HoleGroupRow,
|
|
480
|
-
{
|
|
481
|
-
row,
|
|
482
|
-
direction: directionOf(featureOf.get(row.key)),
|
|
483
|
-
...context
|
|
484
|
-
},
|
|
485
|
-
row.key
|
|
486
|
-
) : /* @__PURE__ */ jsx5("li", { children: /* @__PURE__ */ jsx5(FeatureButton, { tag: row.tag, ...context }) }, row.tag)
|
|
487
|
-
);
|
|
488
|
-
return /* @__PURE__ */ jsxs5("ul", { className: "pb-1 pl-12", children: [
|
|
489
|
-
list(required),
|
|
490
|
-
otherTags.length > 0 ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
491
|
-
/* @__PURE__ */ jsx5(
|
|
492
|
-
"li",
|
|
493
|
-
{
|
|
494
|
-
className: "pt-1 pb-0.5 text-2xs font-semibold text-gray-400 uppercase dark:text-zinc-500",
|
|
495
|
-
title: "Readings of faces another feature also cuts: not counted or painted, since a plan may never cut them this way",
|
|
496
|
-
children: "Other readings, not required"
|
|
497
|
-
}
|
|
498
|
-
),
|
|
499
|
-
/* @__PURE__ */ jsx5("div", { className: "opacity-70", children: list(others) })
|
|
500
|
-
] }) : null
|
|
501
|
-
] });
|
|
502
|
-
};
|
|
503
|
-
var directionOf = (feature) => feature ? directionLabel(feature.machiningDirection) : "";
|
|
504
|
-
|
|
505
|
-
// src/rule-list/rule-sentence.tsx
|
|
506
|
-
import { useState as useState5 } from "react";
|
|
507
|
-
|
|
508
|
-
// src/rule-list/ops.ts
|
|
509
|
-
var OP_TITLES = {
|
|
510
|
-
gte: "at least",
|
|
511
|
-
gt: "more than",
|
|
512
|
-
lte: "at most",
|
|
513
|
-
lt: "less than",
|
|
514
|
-
eq: "equal to",
|
|
515
|
-
ne: "not equal to",
|
|
516
|
-
between: "a range, between two numbers"
|
|
517
|
-
};
|
|
518
|
-
var LOW_SYMBOLS = { gte: "\u2264", gt: "<" };
|
|
519
|
-
var ONE_SIDED = "one:";
|
|
520
|
-
var ONE_SIDED_CHOICES = {
|
|
521
|
-
heading: "One-sided",
|
|
522
|
-
choices: OPS.filter((op) => op !== "between").map((op) => ({
|
|
523
|
-
value: `${ONE_SIDED}${op}`,
|
|
524
|
-
label: `${opSymbol(op)} ${OP_TITLES[op]}`
|
|
525
|
-
}))
|
|
526
|
-
};
|
|
527
|
-
var rangeEnd = (rule, chosen, hold) => chosen.startsWith(ONE_SIDED) ? withOp(rule, chosen.slice(ONE_SIDED.length)) : { ...rule, ...hold(chosen) };
|
|
528
|
-
|
|
529
|
-
// src/rule-list/comparison.tsx
|
|
530
|
-
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
531
|
-
var COMPARISONS = {
|
|
532
|
-
groups: [{ choices: OPS.map((op) => ({ value: op, label: opSymbol(op) })) }]
|
|
533
|
-
};
|
|
534
|
-
var RANGE_HIGH = {
|
|
535
|
-
groups: [
|
|
536
|
-
{ heading: "Range", choices: RANGE_HIGHS.map((op) => ({ value: op, label: opSymbol(op) })) },
|
|
537
|
-
ONE_SIDED_CHOICES
|
|
538
|
-
]
|
|
539
|
-
};
|
|
540
|
-
var Comparison = ({ rule, ranged, onChange }) => ranged ? /* @__PURE__ */ jsx6(
|
|
541
|
-
Picker,
|
|
542
|
-
{
|
|
543
|
-
label: "High end",
|
|
544
|
-
title: OP_TITLES[highOf(rule)],
|
|
545
|
-
value: highOf(rule),
|
|
546
|
-
choices: RANGE_HIGH,
|
|
547
|
-
onChange: (value) => onChange(rangeEnd(rule, value, (op) => ({ highOp: op })))
|
|
548
|
-
}
|
|
549
|
-
) : /* @__PURE__ */ jsx6(
|
|
550
|
-
Picker,
|
|
551
|
-
{
|
|
552
|
-
label: "Comparison",
|
|
553
|
-
title: OP_TITLES[rule.op],
|
|
554
|
-
value: rule.op,
|
|
555
|
-
choices: COMPARISONS,
|
|
556
|
-
onChange: (value) => onChange(withOp(rule, value))
|
|
557
|
-
}
|
|
558
|
-
);
|
|
559
|
-
|
|
560
|
-
// src/rule-list/value-field.tsx
|
|
561
|
-
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
562
|
-
import { cn as cn5 } from "@toolpath/ui";
|
|
563
|
-
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
564
|
-
var ValueField = ({
|
|
565
|
-
label,
|
|
566
|
-
info,
|
|
567
|
-
value,
|
|
568
|
-
units,
|
|
569
|
-
onChange
|
|
570
|
-
}) => {
|
|
571
|
-
const shown = toField(info.quantity, value, units);
|
|
572
|
-
const [text, setText] = useState4(shown);
|
|
573
|
-
const [typing, setTyping] = useState4(false);
|
|
574
|
-
useEffect2(() => {
|
|
575
|
-
if (!typing) setText(shown);
|
|
576
|
-
}, [shown, typing]);
|
|
577
|
-
const invalid = fromField(info, text, units) === null;
|
|
578
|
-
return /* @__PURE__ */ jsx7(
|
|
579
|
-
"input",
|
|
580
|
-
{
|
|
581
|
-
"aria-label": label,
|
|
582
|
-
"aria-invalid": invalid,
|
|
583
|
-
inputMode: "decimal",
|
|
584
|
-
className: cn5(
|
|
585
|
-
WORD,
|
|
586
|
-
"min-w-[2ch] cursor-text tabular-nums",
|
|
587
|
-
invalid && "text-danger decoration-danger"
|
|
588
|
-
),
|
|
589
|
-
value: text,
|
|
590
|
-
onFocus: () => setTyping(true),
|
|
591
|
-
onBlur: () => setTyping(false),
|
|
592
|
-
onChange: (event) => {
|
|
593
|
-
setText(event.target.value);
|
|
594
|
-
const typed = fromField(info, event.target.value, units);
|
|
595
|
-
if (typed !== null) onChange(typed);
|
|
596
|
-
},
|
|
597
|
-
onKeyDown: (event) => {
|
|
598
|
-
if (event.key === "Enter") event.currentTarget.blur();
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
);
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
// src/rule-list/rule-sentence.tsx
|
|
605
|
-
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
606
|
-
var RANGE_LOW = {
|
|
607
|
-
groups: [
|
|
608
|
-
{ heading: "Range", choices: RANGE_LOWS.map((op) => ({ value: op, label: LOW_SYMBOLS[op] })) },
|
|
609
|
-
ONE_SIDED_CHOICES
|
|
610
|
-
]
|
|
611
|
-
};
|
|
612
|
-
var RuleSentence = ({
|
|
613
|
-
rule,
|
|
614
|
-
units,
|
|
615
|
-
vocabulary,
|
|
616
|
-
onChange,
|
|
617
|
-
autoFocus
|
|
618
|
-
}) => {
|
|
619
|
-
const info = metricInfo(rule.metric, vocabulary);
|
|
620
|
-
const flag = info?.quantity === "flag" || info?.quantity === "presence";
|
|
621
|
-
const presence = info?.quantity === "presence";
|
|
622
|
-
const ranged = info !== void 0 && !flag && rule.op === "between";
|
|
623
|
-
const unit = info ? quantityUnit(info.quantity, units) : "";
|
|
624
|
-
const [allSubjects, setAllSubjects] = useState5(false);
|
|
625
|
-
const [allMetrics, setAllMetrics] = useState5(false);
|
|
626
|
-
const unitSpan = unit ? /* @__PURE__ */ jsx8("span", { className: unit === "\xB0" || unit === "%" ? "" : "pl-1", children: unit }) : null;
|
|
627
|
-
return /* @__PURE__ */ jsxs6("p", { className: "min-w-0 flex-1 text-sm leading-7 text-gray-500 dark:text-zinc-400", children: [
|
|
628
|
-
/* @__PURE__ */ jsx8(
|
|
629
|
-
Picker,
|
|
630
|
-
{
|
|
631
|
-
label: "Features",
|
|
632
|
-
value: rule.subject,
|
|
633
|
-
choices: subjectChoices(rule.subject, allSubjects, vocabulary),
|
|
634
|
-
onChange: (value) => onChange(withSubject(rule, value, units, vocabulary)),
|
|
635
|
-
onMore: () => setAllSubjects(true)
|
|
636
|
-
}
|
|
637
|
-
),
|
|
638
|
-
presence ? ", " : " with ",
|
|
639
|
-
/* @__PURE__ */ jsxs6("span", { className: ranged ? "whitespace-nowrap" : void 0, children: [
|
|
640
|
-
ranged ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
641
|
-
/* @__PURE__ */ jsxs6("span", { className: "whitespace-nowrap", children: [
|
|
642
|
-
/* @__PURE__ */ jsx8(
|
|
643
|
-
ValueField,
|
|
644
|
-
{
|
|
645
|
-
label: "From",
|
|
646
|
-
info,
|
|
647
|
-
value: rule.value,
|
|
648
|
-
units,
|
|
649
|
-
onChange: (value) => onChange({ ...rule, value })
|
|
650
|
-
}
|
|
651
|
-
),
|
|
652
|
-
unitSpan
|
|
653
|
-
] }),
|
|
654
|
-
" ",
|
|
655
|
-
/* @__PURE__ */ jsx8(
|
|
656
|
-
Picker,
|
|
657
|
-
{
|
|
658
|
-
label: "Low end",
|
|
659
|
-
title: OP_TITLES[lowOf(rule)],
|
|
660
|
-
value: lowOf(rule),
|
|
661
|
-
choices: RANGE_LOW,
|
|
662
|
-
onChange: (value) => onChange(rangeEnd(rule, value, (op) => ({ lowOp: op })))
|
|
663
|
-
}
|
|
664
|
-
),
|
|
665
|
-
" "
|
|
666
|
-
] }) : null,
|
|
667
|
-
/* @__PURE__ */ jsx8(
|
|
668
|
-
Picker,
|
|
669
|
-
{
|
|
670
|
-
label: "Measure",
|
|
671
|
-
title: info?.hint,
|
|
672
|
-
value: rule.metric,
|
|
673
|
-
choices: metricChoices(rule.subject, rule.metric, allMetrics, vocabulary),
|
|
674
|
-
onChange: (value) => onChange(withMetric(rule, value, units, vocabulary)),
|
|
675
|
-
onMore: () => setAllMetrics(true),
|
|
676
|
-
autoFocus
|
|
677
|
-
}
|
|
678
|
-
),
|
|
679
|
-
info && !flag ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
680
|
-
" ",
|
|
681
|
-
/* @__PURE__ */ jsx8(Comparison, { rule, ranged, onChange }),
|
|
682
|
-
" ",
|
|
683
|
-
/* @__PURE__ */ jsxs6("span", { className: "whitespace-nowrap", children: [
|
|
684
|
-
/* @__PURE__ */ jsx8(
|
|
685
|
-
ValueField,
|
|
686
|
-
{
|
|
687
|
-
label: ranged ? "Up to" : "Value",
|
|
688
|
-
info,
|
|
689
|
-
value: ranged ? upperOf(rule) : rule.value,
|
|
690
|
-
units,
|
|
691
|
-
onChange: (value) => onChange(ranged ? { ...rule, max: value } : { ...rule, value })
|
|
692
|
-
}
|
|
693
|
-
),
|
|
694
|
-
unitSpan
|
|
695
|
-
] })
|
|
696
|
-
] }) : null
|
|
697
|
-
] })
|
|
698
|
-
] });
|
|
699
|
-
};
|
|
700
|
-
|
|
701
|
-
// src/rule-list/swatch.tsx
|
|
702
|
-
import { cn as cn6 } from "@toolpath/ui";
|
|
703
|
-
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
704
|
-
var swatchKeys = listKeys("swatches", "horizontal");
|
|
705
|
-
var Swatch = ({
|
|
706
|
-
color,
|
|
707
|
-
label,
|
|
708
|
-
onClick,
|
|
709
|
-
onKeyDown,
|
|
710
|
-
listItem,
|
|
711
|
-
className,
|
|
712
|
-
ref
|
|
713
|
-
}) => {
|
|
714
|
-
const dot = cn6("size-4 shrink-0 rounded-full ring-1 ring-black/10 dark:ring-white/15", className);
|
|
715
|
-
return onClick ? /* @__PURE__ */ jsx9(
|
|
716
|
-
"button",
|
|
717
|
-
{
|
|
718
|
-
ref,
|
|
719
|
-
type: "button",
|
|
720
|
-
"aria-label": label,
|
|
721
|
-
"data-list-item": listItem,
|
|
722
|
-
onClick,
|
|
723
|
-
onKeyDown,
|
|
724
|
-
className: cn6(
|
|
725
|
-
dot,
|
|
726
|
-
"cursor-pointer hover:ring-2 hover:ring-gray-300 dark:hover:ring-zinc-500",
|
|
727
|
-
FOCUS_RING
|
|
728
|
-
),
|
|
729
|
-
style: { backgroundColor: color }
|
|
730
|
-
}
|
|
731
|
-
) : /* @__PURE__ */ jsx9("span", { "aria-hidden": true, className: dot, style: { backgroundColor: color } });
|
|
732
|
-
};
|
|
733
|
-
var ColorPicker = ({
|
|
734
|
-
value,
|
|
735
|
-
onChange
|
|
736
|
-
}) => /* @__PURE__ */ jsx9("div", { className: "flex flex-wrap items-center gap-1.5 py-1", onKeyDown: swatchKeys, children: RULE_COLORS.map((each) => /* @__PURE__ */ jsx9(
|
|
737
|
-
"button",
|
|
738
|
-
{
|
|
739
|
-
type: "button",
|
|
740
|
-
"data-list-item": "swatches",
|
|
741
|
-
"aria-label": each.name,
|
|
742
|
-
"aria-pressed": value === each.value,
|
|
743
|
-
title: each.name,
|
|
744
|
-
autoFocus: value === each.value,
|
|
745
|
-
onClick: () => onChange(each.value),
|
|
746
|
-
className: cn6(
|
|
747
|
-
"size-5 cursor-pointer rounded-full ring-1 ring-black/10 outline-none focus-visible:ring-2 focus-visible:ring-info dark:ring-white/15",
|
|
748
|
-
value === each.value && "ring-2 ring-gray-600 ring-offset-1 dark:ring-zinc-100 dark:ring-offset-zinc-900"
|
|
749
|
-
),
|
|
750
|
-
style: { backgroundColor: each.value }
|
|
751
|
-
},
|
|
752
|
-
each.value
|
|
753
|
-
)) });
|
|
754
|
-
|
|
755
|
-
// src/rule-list/rule-row.tsx
|
|
756
|
-
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
757
|
-
var isControl = (target) => target instanceof Element && target.closest("button, select, input, a") !== null;
|
|
758
|
-
var RuleRow = ({
|
|
759
|
-
rule,
|
|
760
|
-
result,
|
|
761
|
-
checking,
|
|
762
|
-
open,
|
|
763
|
-
focusOnMount,
|
|
764
|
-
onToggle,
|
|
765
|
-
onChange,
|
|
766
|
-
onRemove,
|
|
767
|
-
check,
|
|
768
|
-
features,
|
|
769
|
-
sheets,
|
|
770
|
-
units,
|
|
771
|
-
vocabulary,
|
|
772
|
-
...list
|
|
773
|
-
}) => {
|
|
774
|
-
const [picking, setPicking] = useState6(false);
|
|
775
|
-
const swatchRef = useRef3(null);
|
|
776
|
-
const count = result?.tags.length ?? 0;
|
|
777
|
-
const others = result?.otherTags.length ?? 0;
|
|
778
|
-
const canOpen = count + others > 0;
|
|
779
|
-
const onRowKey = (event) => {
|
|
780
|
-
if (event.key === "ArrowRight" && canOpen) {
|
|
781
|
-
event.preventDefault();
|
|
782
|
-
if (!open) onToggle();
|
|
783
|
-
else
|
|
784
|
-
event.currentTarget.closest("li")?.querySelector("[data-feature-row]")?.focus();
|
|
785
|
-
} else if (event.key === "ArrowLeft" && open) {
|
|
786
|
-
event.preventDefault();
|
|
787
|
-
onToggle();
|
|
788
|
-
}
|
|
789
|
-
};
|
|
790
|
-
return /* @__PURE__ */ jsxs7(
|
|
791
|
-
"li",
|
|
792
|
-
{
|
|
793
|
-
className: cn7(
|
|
794
|
-
"group/rule",
|
|
795
|
-
// Nothing on the part breaks it: dimmed, until pointed at or worked in.
|
|
796
|
-
!checking && count === 0 && "opacity-55 transition-opacity focus-within:opacity-100 hover:opacity-100"
|
|
797
|
-
),
|
|
798
|
-
children: [
|
|
799
|
-
/* @__PURE__ */ jsxs7(
|
|
800
|
-
"div",
|
|
801
|
-
{
|
|
802
|
-
onClick: (event) => {
|
|
803
|
-
if (canOpen && !isControl(event.target)) onToggle();
|
|
804
|
-
},
|
|
805
|
-
className: cn7(
|
|
806
|
-
"flex items-start gap-1.5 rounded py-1 pr-1.5 hover:bg-gray-50 dark:hover:bg-zinc-800/60",
|
|
807
|
-
canOpen && "cursor-pointer"
|
|
808
|
-
),
|
|
809
|
-
children: [
|
|
810
|
-
/* @__PURE__ */ jsx10(
|
|
811
|
-
"button",
|
|
812
|
-
{
|
|
813
|
-
type: "button",
|
|
814
|
-
tabIndex: -1,
|
|
815
|
-
"aria-label": open ? "Hide features" : "Show features",
|
|
816
|
-
disabled: !canOpen,
|
|
817
|
-
onClick: onToggle,
|
|
818
|
-
className: cn7(
|
|
819
|
-
"flex size-7 shrink-0 items-center justify-center rounded text-gray-400 dark:text-zinc-400",
|
|
820
|
-
canOpen ? "cursor-pointer hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-zinc-700 dark:hover:text-zinc-100" : "invisible"
|
|
821
|
-
),
|
|
822
|
-
children: /* @__PURE__ */ jsx10(
|
|
823
|
-
CaretRightIcon2,
|
|
824
|
-
{
|
|
825
|
-
weight: "bold",
|
|
826
|
-
className: cn7("size-4 transition-transform duration-150", open && "rotate-90")
|
|
827
|
-
}
|
|
828
|
-
)
|
|
829
|
-
}
|
|
830
|
-
),
|
|
831
|
-
/* @__PURE__ */ jsx10(
|
|
832
|
-
Swatch,
|
|
833
|
-
{
|
|
834
|
-
ref: swatchRef,
|
|
835
|
-
color: rule.color,
|
|
836
|
-
label: "Change colour",
|
|
837
|
-
onClick: () => setPicking((was) => !was),
|
|
838
|
-
onKeyDown: onRowKey,
|
|
839
|
-
listItem: "rules",
|
|
840
|
-
className: "mt-1.5"
|
|
841
|
-
}
|
|
842
|
-
),
|
|
843
|
-
/* @__PURE__ */ jsx10(
|
|
844
|
-
RuleSentence,
|
|
845
|
-
{
|
|
846
|
-
rule,
|
|
847
|
-
units,
|
|
848
|
-
vocabulary,
|
|
849
|
-
onChange,
|
|
850
|
-
autoFocus: focusOnMount
|
|
851
|
-
}
|
|
852
|
-
),
|
|
853
|
-
/* @__PURE__ */ jsxs7("div", { className: "relative mt-1 shrink-0", children: [
|
|
854
|
-
/* @__PURE__ */ jsx10(
|
|
855
|
-
IconButton,
|
|
856
|
-
{
|
|
857
|
-
"aria-label": "Remove rule",
|
|
858
|
-
variant: "muted",
|
|
859
|
-
size: "sm",
|
|
860
|
-
onClick: onRemove,
|
|
861
|
-
className: cn7(
|
|
862
|
-
"absolute top-1/2 right-full mr-1 -translate-y-1/2 bg-white dark:bg-zinc-800",
|
|
863
|
-
"pointer-events-none opacity-0 transition-opacity",
|
|
864
|
-
"group-focus-within/rule:pointer-events-auto group-focus-within/rule:opacity-100",
|
|
865
|
-
"group-hover/rule:pointer-events-auto group-hover/rule:opacity-100"
|
|
866
|
-
),
|
|
867
|
-
children: /* @__PURE__ */ jsx10(TrashIcon, { weight: "bold" })
|
|
868
|
-
}
|
|
869
|
-
),
|
|
870
|
-
/* @__PURE__ */ jsx10(
|
|
871
|
-
CountPill,
|
|
872
|
-
{
|
|
873
|
-
count,
|
|
874
|
-
others,
|
|
875
|
-
checking,
|
|
876
|
-
open,
|
|
877
|
-
canOpen,
|
|
878
|
-
onToggle
|
|
879
|
-
}
|
|
880
|
-
)
|
|
881
|
-
] })
|
|
882
|
-
]
|
|
883
|
-
}
|
|
884
|
-
),
|
|
885
|
-
picking ? /* @__PURE__ */ jsx10("div", { className: "pb-1 pl-[34px]", children: /* @__PURE__ */ jsx10(
|
|
886
|
-
ColorPicker,
|
|
887
|
-
{
|
|
888
|
-
value: rule.color,
|
|
889
|
-
onChange: (color) => {
|
|
890
|
-
onChange({ ...rule, color });
|
|
891
|
-
setPicking(false);
|
|
892
|
-
swatchRef.current?.focus();
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
) }) : null,
|
|
896
|
-
open && result && check && features && sheets ? /* @__PURE__ */ jsx10(
|
|
897
|
-
FeatureRows,
|
|
898
|
-
{
|
|
899
|
-
rule,
|
|
900
|
-
tags: result.tags,
|
|
901
|
-
otherTags: result.otherTags,
|
|
902
|
-
check,
|
|
903
|
-
features,
|
|
904
|
-
sheets,
|
|
905
|
-
units,
|
|
906
|
-
vocabulary,
|
|
907
|
-
onBack: () => swatchRef.current?.focus(),
|
|
908
|
-
...list
|
|
909
|
-
}
|
|
910
|
-
) : null
|
|
911
|
-
]
|
|
912
|
-
}
|
|
913
|
-
);
|
|
914
|
-
};
|
|
915
|
-
var CountPill = ({
|
|
916
|
-
count,
|
|
917
|
-
others,
|
|
918
|
-
checking,
|
|
919
|
-
open,
|
|
920
|
-
canOpen,
|
|
921
|
-
onToggle
|
|
922
|
-
}) => /* @__PURE__ */ jsx10(
|
|
923
|
-
"button",
|
|
924
|
-
{
|
|
925
|
-
type: "button",
|
|
926
|
-
"aria-label": checking ? void 0 : countLabel(count, others),
|
|
927
|
-
title: others > 0 ? `${count} required \xB7 ${others} other ${readings(others)} not counted` : void 0,
|
|
928
|
-
"aria-expanded": canOpen ? open : void 0,
|
|
929
|
-
disabled: !canOpen,
|
|
930
|
-
onClick: onToggle,
|
|
931
|
-
className: cn7(
|
|
932
|
-
"flex h-5 min-w-8 items-center justify-center rounded-full px-1.5 text-xs font-semibold tabular-nums",
|
|
933
|
-
canOpen ? "cursor-pointer bg-gray-100 text-gray-500 hover:bg-gray-200 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-700" : "text-gray-300 dark:text-zinc-600",
|
|
934
|
-
FOCUS_RING
|
|
935
|
-
),
|
|
936
|
-
children: checking ? "\u2026" : count
|
|
937
|
-
}
|
|
938
|
-
);
|
|
939
|
-
var readings = (count) => count === 1 ? "reading" : "readings";
|
|
940
|
-
var countLabel = (count, others) => {
|
|
941
|
-
const required = `${count} required ${count === 1 ? "feature breaks" : "features break"} it`;
|
|
942
|
-
return others > 0 ? `${required}, and ${others} other ${readings(others)}` : required;
|
|
943
|
-
};
|
|
944
|
-
|
|
945
|
-
// src/rule-list/rule-set-actions.tsx
|
|
946
|
-
import { useRef as useRef4 } from "react";
|
|
947
|
-
import { CheckIcon as CheckIcon2, CopyIcon, DownloadSimpleIcon, UploadSimpleIcon } from "@phosphor-icons/react";
|
|
948
|
-
import { Button, cn as cn8 } from "@toolpath/ui";
|
|
949
|
-
|
|
950
|
-
// src/rule-list/use-timed-value.ts
|
|
951
|
-
import { useCallback, useEffect as useEffect3, useState as useState7 } from "react";
|
|
952
|
-
var useTimedValue = (ms) => {
|
|
953
|
-
const [box, setBox] = useState7(null);
|
|
954
|
-
useEffect3(() => {
|
|
955
|
-
if (box === null) return;
|
|
956
|
-
const timer = setTimeout(() => setBox(null), ms);
|
|
957
|
-
return () => clearTimeout(timer);
|
|
958
|
-
}, [box, ms]);
|
|
959
|
-
const set = useCallback((value) => setBox({ value }), []);
|
|
960
|
-
return [box?.value ?? null, set];
|
|
961
|
-
};
|
|
962
|
-
|
|
963
|
-
// src/rule-list/rule-set-actions.tsx
|
|
964
|
-
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
965
|
-
var NOTE_MS = 4e3;
|
|
966
|
-
var COPIED_MS = 2e3;
|
|
967
|
-
var RuleSetActions = ({ rules, actions, vocabulary }) => {
|
|
968
|
-
const [note, setNote] = useTimedValue(NOTE_MS);
|
|
969
|
-
const [copied, setCopied] = useTimedValue(COPIED_MS);
|
|
970
|
-
const fileInput = useRef4(null);
|
|
971
|
-
const importRules = async (file) => {
|
|
972
|
-
if (!file) return;
|
|
973
|
-
let text;
|
|
974
|
-
try {
|
|
975
|
-
text = await file.text();
|
|
976
|
-
} catch {
|
|
977
|
-
setNote("Could not import: the file could not be read.");
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
const read = parseRuleSetFile(text, vocabulary);
|
|
981
|
-
if ("error" in read) {
|
|
982
|
-
setNote(`Could not import: ${read.error}`);
|
|
983
|
-
return;
|
|
984
|
-
}
|
|
985
|
-
rules.replace(read.rules);
|
|
986
|
-
setNote(importedNote(read.rules.length, read.skipped, read.name));
|
|
987
|
-
};
|
|
988
|
-
const copyPrompt = async () => {
|
|
989
|
-
try {
|
|
990
|
-
await navigator.clipboard.writeText(llmRulePrompt(rules.rules, vocabulary));
|
|
991
|
-
} catch {
|
|
992
|
-
setNote("Could not copy the prompt. Allow clipboard access and try again.");
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
|
-
setCopied(true);
|
|
996
|
-
setNote("Paste the prompt into an LLM, then import the JSON it writes.");
|
|
997
|
-
};
|
|
998
|
-
return /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2 px-4 pt-2 pb-3", children: [
|
|
999
|
-
/* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1000
|
-
/* @__PURE__ */ jsx11(Button, { variant: "secondary", onClick: () => fileInput.current?.click(), children: /* @__PURE__ */ jsx11(ActionLabel, { icon: /* @__PURE__ */ jsx11(UploadSimpleIcon, { weight: "bold" }), children: "Import\u2026" }) }),
|
|
1001
|
-
/* @__PURE__ */ jsx11(Button, { variant: "secondary", onClick: () => downloadRuleSet(rules, vocabulary), children: /* @__PURE__ */ jsx11(ActionLabel, { icon: /* @__PURE__ */ jsx11(DownloadSimpleIcon, { weight: "bold" }), children: "Export" }) }),
|
|
1002
|
-
/* @__PURE__ */ jsx11(
|
|
1003
|
-
Button,
|
|
1004
|
-
{
|
|
1005
|
-
variant: "secondary",
|
|
1006
|
-
title: "Copies a prompt that walks an LLM through writing a rule set for you",
|
|
1007
|
-
onClick: () => void copyPrompt(),
|
|
1008
|
-
children: /* @__PURE__ */ jsxs8("span", { className: "grid justify-items-center *:col-start-1 *:row-start-1", children: [
|
|
1009
|
-
/* @__PURE__ */ jsx11(ActionLabel, { hidden: copied !== null, icon: /* @__PURE__ */ jsx11(CopyIcon, { weight: "bold" }), children: "Copy LLM prompt" }),
|
|
1010
|
-
/* @__PURE__ */ jsx11(ActionLabel, { hidden: copied === null, icon: /* @__PURE__ */ jsx11(CheckIcon2, { weight: "bold" }), children: "Copied" })
|
|
1011
|
-
] })
|
|
1012
|
-
}
|
|
1013
|
-
),
|
|
1014
|
-
actions,
|
|
1015
|
-
/* @__PURE__ */ jsx11(
|
|
1016
|
-
"input",
|
|
1017
|
-
{
|
|
1018
|
-
ref: fileInput,
|
|
1019
|
-
type: "file",
|
|
1020
|
-
"aria-label": "Rule set file",
|
|
1021
|
-
accept: ".json,application/json",
|
|
1022
|
-
className: "hidden",
|
|
1023
|
-
onChange: (event) => {
|
|
1024
|
-
void importRules(event.currentTarget.files?.[0]);
|
|
1025
|
-
event.currentTarget.value = "";
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
)
|
|
1029
|
-
] }),
|
|
1030
|
-
note ? /* @__PURE__ */ jsx11("p", { className: "text-xs text-gray-500 dark:text-zinc-400", role: "status", children: note }) : null
|
|
1031
|
-
] });
|
|
1032
|
-
};
|
|
1033
|
-
var ActionLabel = ({ icon, children, hidden = false }) => /* @__PURE__ */ jsxs8(
|
|
1034
|
-
"span",
|
|
1035
|
-
{
|
|
1036
|
-
"aria-hidden": hidden || void 0,
|
|
1037
|
-
className: cn8("flex items-center gap-1.5 [&>svg]:size-3.5", hidden && "invisible"),
|
|
1038
|
-
children: [
|
|
1039
|
-
icon,
|
|
1040
|
-
children
|
|
1041
|
-
]
|
|
1042
|
-
}
|
|
1043
|
-
);
|
|
1044
|
-
var downloadRuleSet = (rules, vocabulary) => {
|
|
1045
|
-
const url = URL.createObjectURL(
|
|
1046
|
-
new Blob([serializeRuleSet(rules.rules, {}, vocabulary)], { type: "application/json" })
|
|
1047
|
-
);
|
|
1048
|
-
const link = document.createElement("a");
|
|
1049
|
-
link.href = url;
|
|
1050
|
-
link.download = vocabulary.fileName;
|
|
1051
|
-
link.click();
|
|
1052
|
-
URL.revokeObjectURL(url);
|
|
1053
|
-
};
|
|
1054
|
-
var importedNote = (count, skipped, name) => {
|
|
1055
|
-
const rulesRead = `Imported ${count} rule${count === 1 ? "" : "s"}`;
|
|
1056
|
-
const named = name ? ` (${name})` : "";
|
|
1057
|
-
const skippedSome = skipped > 0 ? `, ${skipped} skipped` : "";
|
|
1058
|
-
return `${rulesRead}${named}${skippedSome}`;
|
|
1059
|
-
};
|
|
1060
|
-
|
|
1061
|
-
// src/rule-list/rule-list.tsx
|
|
1062
|
-
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1063
|
-
var ruleListKeys = listKeys("rules");
|
|
1064
|
-
var RuleList = ({
|
|
1065
|
-
rules,
|
|
1066
|
-
check,
|
|
1067
|
-
features,
|
|
1068
|
-
sheets,
|
|
1069
|
-
units,
|
|
1070
|
-
actions,
|
|
1071
|
-
vocabulary = MILLING_RULES,
|
|
1072
|
-
...list
|
|
1073
|
-
}) => {
|
|
1074
|
-
const [openIds, setOpenIds] = useState8(/* @__PURE__ */ new Set());
|
|
1075
|
-
const [added, setAdded] = useState8(null);
|
|
1076
|
-
const toggleOpen = (id) => setOpenIds((was) => {
|
|
1077
|
-
const next = new Set(was);
|
|
1078
|
-
if (!next.delete(id)) next.add(id);
|
|
1079
|
-
return next;
|
|
1080
|
-
});
|
|
1081
|
-
const byId = useMemo2(
|
|
1082
|
-
() => new Map(check?.byRule.map((result) => [result.rule.id, result])),
|
|
1083
|
-
[check]
|
|
1084
|
-
);
|
|
1085
|
-
const found = (rule) => {
|
|
1086
|
-
const result = byId.get(rule.id);
|
|
1087
|
-
return check === null || rule.id === added || (result?.tags.length ?? 0) + (result?.otherTags.length ?? 0) > 0;
|
|
1088
|
-
};
|
|
1089
|
-
const active = rules.rules.filter(found);
|
|
1090
|
-
const none = rules.rules.filter((rule) => !found(rule));
|
|
1091
|
-
const row = (rule) => /* @__PURE__ */ jsx12(
|
|
1092
|
-
RuleRow,
|
|
1093
|
-
{
|
|
1094
|
-
rule,
|
|
1095
|
-
result: byId.get(rule.id) ?? null,
|
|
1096
|
-
checking: check === null,
|
|
1097
|
-
open: openIds.has(rule.id),
|
|
1098
|
-
focusOnMount: added === rule.id,
|
|
1099
|
-
onToggle: () => toggleOpen(rule.id),
|
|
1100
|
-
onChange: rules.update,
|
|
1101
|
-
onRemove: () => rules.remove(rule.id),
|
|
1102
|
-
check,
|
|
1103
|
-
features,
|
|
1104
|
-
sheets,
|
|
1105
|
-
units,
|
|
1106
|
-
vocabulary,
|
|
1107
|
-
...list
|
|
1108
|
-
},
|
|
1109
|
-
rule.id
|
|
1110
|
-
);
|
|
1111
|
-
return (
|
|
1112
|
-
// Grows with its rules, and scrolls once the panel can grow no more.
|
|
1113
|
-
/* @__PURE__ */ jsxs9(ScrollArea, { className: "min-h-0 flex-auto", children: [
|
|
1114
|
-
/* @__PURE__ */ jsxs9("ul", { className: "flex flex-col gap-0.5 pt-2 pr-4 pl-2", children: [
|
|
1115
|
-
active.map(row),
|
|
1116
|
-
none.map(row),
|
|
1117
|
-
/* @__PURE__ */ jsx12(
|
|
1118
|
-
NewRule,
|
|
1119
|
-
{
|
|
1120
|
-
vocabulary,
|
|
1121
|
-
onAdd: (subject) => {
|
|
1122
|
-
const rule = makeRule(subject, PRESENCE_METRIC.key, units, void 0, vocabulary);
|
|
1123
|
-
rules.add(rule);
|
|
1124
|
-
setAdded(rule.id);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
)
|
|
1128
|
-
] }),
|
|
1129
|
-
/* @__PURE__ */ jsx12(RuleSetActions, { rules, actions, vocabulary })
|
|
1130
|
-
] })
|
|
1131
|
-
);
|
|
1132
|
-
};
|
|
1133
|
-
|
|
1134
|
-
// src/reach/reach-chart.tsx
|
|
1135
|
-
import { useMemo as useMemo4 } from "react";
|
|
1136
|
-
|
|
1137
|
-
// src/reach/reach-section.tsx
|
|
1138
|
-
import { useId as useId2, useMemo as useMemo3, useState as useState9 } from "react";
|
|
1139
|
-
|
|
1140
|
-
// src/reach/reading.tsx
|
|
1141
|
-
import { heightAt } from "@toolpath/tool-support";
|
|
1142
|
-
|
|
1143
|
-
// src/reach/section-layout.ts
|
|
1144
|
-
var SECTION_PAD = { left: 12, right: 64, top: 24, bottom: 62 };
|
|
1145
|
-
var SECTION_MAX_H = 380;
|
|
1146
|
-
var SECTION_MIN_H = 120;
|
|
1147
|
-
var FEATURE_SHARE = 0.4;
|
|
1148
|
-
var FEATURE_SHARE_MIN = 0.06;
|
|
1149
|
-
var FEATURE_SHARE_MAX = 0.6;
|
|
1150
|
-
var LABEL_GAP_X = 40;
|
|
1151
|
-
var LABEL_GAP_Y = 14;
|
|
1152
|
-
var WALL_LABEL_GAP_X = 30;
|
|
1153
|
-
var READ_CHAR_PX = 6.4;
|
|
1154
|
-
var HEADING_CHAR_PX = 6.5;
|
|
1155
|
-
var READ_INSET = 8;
|
|
1156
|
-
var AXIS_LABEL_HALF_PX = 84;
|
|
1157
|
-
var FLOOR_STRIP = 6;
|
|
1158
|
-
var DEPTH_WORD_PX = 34;
|
|
1159
|
-
var BREAK_STEP = 8;
|
|
1160
|
-
var BREAK_SWAY = 3;
|
|
1161
|
-
var risersOf = (curve) => {
|
|
1162
|
-
const risers = [];
|
|
1163
|
-
let level = 0;
|
|
1164
|
-
curve.heights.forEach((height, index) => {
|
|
1165
|
-
if (height > level) risers.push({ at: runStart(curve, index), from: level, to: height });
|
|
1166
|
-
level = Math.max(level, height);
|
|
1167
|
-
});
|
|
1168
|
-
return risers;
|
|
1169
|
-
};
|
|
1170
|
-
var spaced = (items, position, gap) => {
|
|
1171
|
-
const kept = [];
|
|
1172
|
-
items.forEach((item, index) => {
|
|
1173
|
-
const last = kept[kept.length - 1];
|
|
1174
|
-
const clear = last === void 0 || Math.abs(position(item) - position(last)) >= gap;
|
|
1175
|
-
if (clear) kept.push(item);
|
|
1176
|
-
else if (index === items.length - 1) kept[kept.length - 1] = item;
|
|
1177
|
-
});
|
|
1178
|
-
return kept;
|
|
1179
|
-
};
|
|
1180
|
-
var breakSteps = (top, bottom) => Math.max(2, Math.round((bottom - top) / BREAK_STEP));
|
|
1181
|
-
var featureBreak = (left, top, bottom) => {
|
|
1182
|
-
const steps = breakSteps(top, bottom);
|
|
1183
|
-
let path = `M ${left} ${top}`;
|
|
1184
|
-
for (let step = 1; step <= steps; step += 1) {
|
|
1185
|
-
const sway = step === steps ? 0 : step % 2 === 1 ? BREAK_SWAY : -BREAK_SWAY;
|
|
1186
|
-
path += ` L ${left + sway} ${top + (bottom - top) * step / steps}`;
|
|
1187
|
-
}
|
|
1188
|
-
return path;
|
|
1189
|
-
};
|
|
1190
|
-
var wallBreak = (right, top, bottom) => {
|
|
1191
|
-
const steps = breakSteps(top, bottom);
|
|
1192
|
-
let path = "";
|
|
1193
|
-
for (let step = 1; step < steps; step += 1) {
|
|
1194
|
-
const sway = step % 2 === 1 ? -BREAK_SWAY : BREAK_SWAY;
|
|
1195
|
-
path += ` L ${right + sway} ${top + (bottom - top) * step / steps}`;
|
|
1196
|
-
}
|
|
1197
|
-
return path;
|
|
1198
|
-
};
|
|
1199
|
-
|
|
1200
|
-
// src/reach/styles.ts
|
|
1201
|
-
var LINE = "stroke-gray-500 dark:stroke-zinc-400";
|
|
1202
|
-
var TEXT = "fill-gray-600 dark:fill-zinc-300";
|
|
1203
|
-
var MATERIAL = "fill-neutral-300 stroke-neutral-400 dark:fill-neutral-600 dark:stroke-neutral-500";
|
|
1204
|
-
var FLOOR = "fill-neutral-300 dark:fill-neutral-600";
|
|
1205
|
-
var ARROW = "fill-gray-500 dark:fill-zinc-400";
|
|
1206
|
-
var FEATURE_FILL = "fill-blue-500/15 dark:fill-blue-400/15";
|
|
1207
|
-
var FEATURE_LINE = "stroke-blue-600 dark:stroke-blue-400";
|
|
1208
|
-
var FEATURE_TEXT = "fill-blue-600 dark:fill-blue-400";
|
|
1209
|
-
var READ = "stroke-sky-600 dark:stroke-sky-400";
|
|
1210
|
-
var READ_DOT = "fill-sky-600 dark:fill-sky-400";
|
|
1211
|
-
|
|
1212
|
-
// src/reach/reading.tsx
|
|
1213
|
-
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1214
|
-
var readingAt = ({ offsets, heights }, offset) => ({
|
|
1215
|
-
offset,
|
|
1216
|
-
height: heightAt({ horizontalOffset: offsets, verticalOffset: heights }, offset),
|
|
1217
|
-
past: offset > (offsets[offsets.length - 1] ?? 0)
|
|
1218
|
-
});
|
|
1219
|
-
var readingText = ({ offset, height, past }, units) => `${lengthValue(offset, units)} ${unitLabel(units)} \u2192 ${lengthValue(height, units)} ${unitLabel(units)} reach${past ? " \xB7 past the last reading" : ""}`;
|
|
1220
|
-
var offsetUnder = (clientX, box, geometry) => {
|
|
1221
|
-
const at = (clientX - box.left) * (geometry.width / box.width);
|
|
1222
|
-
const offset = (at - SECTION_PAD.left) / geometry.scale + geometry.x0;
|
|
1223
|
-
return offset >= 0 && at <= geometry.right ? offset : null;
|
|
1224
|
-
};
|
|
1225
|
-
var ReadingMark = ({
|
|
1226
|
-
geometry,
|
|
1227
|
-
reading,
|
|
1228
|
-
units
|
|
1229
|
-
}) => {
|
|
1230
|
-
const { x, y, width, floorY } = geometry;
|
|
1231
|
-
const text = readingText(reading, units);
|
|
1232
|
-
const textWidth = text.length * READ_CHAR_PX;
|
|
1233
|
-
const pointerX = x(reading.offset);
|
|
1234
|
-
const onRight = pointerX + READ_INSET + textWidth <= width - READ_INSET;
|
|
1235
|
-
const labelX = onRight ? pointerX + READ_INSET : Math.min(Math.max(pointerX - READ_INSET, READ_INSET + textWidth), width - READ_INSET);
|
|
1236
|
-
return /* @__PURE__ */ jsxs10("g", { pointerEvents: "none", children: [
|
|
1237
|
-
/* @__PURE__ */ jsx13(
|
|
1238
|
-
"line",
|
|
1239
|
-
{
|
|
1240
|
-
x1: pointerX,
|
|
1241
|
-
x2: pointerX,
|
|
1242
|
-
y1: SECTION_PAD.top,
|
|
1243
|
-
y2: floorY,
|
|
1244
|
-
strokeWidth: 1,
|
|
1245
|
-
strokeDasharray: "3 3",
|
|
1246
|
-
className: READ
|
|
1247
|
-
}
|
|
1248
|
-
),
|
|
1249
|
-
/* @__PURE__ */ jsx13("circle", { cx: pointerX, cy: y(reading.height), r: 4, className: READ_DOT }),
|
|
1250
|
-
/* @__PURE__ */ jsx13(
|
|
1251
|
-
"text",
|
|
1252
|
-
{
|
|
1253
|
-
x: labelX,
|
|
1254
|
-
y: SECTION_PAD.top + 14,
|
|
1255
|
-
textAnchor: onRight ? "start" : "end",
|
|
1256
|
-
fontSize: 11,
|
|
1257
|
-
fontWeight: 600,
|
|
1258
|
-
className: TEXT,
|
|
1259
|
-
children: text
|
|
1260
|
-
}
|
|
1261
|
-
)
|
|
1262
|
-
] });
|
|
1263
|
-
};
|
|
1264
|
-
|
|
1265
|
-
// src/reach/section-geometry.ts
|
|
1266
|
-
var materialPath = (curve, cut, x, y, floorY, right) => {
|
|
1267
|
-
let path = `M ${x(0)} ${floorY}`;
|
|
1268
|
-
let level = 0;
|
|
1269
|
-
curve.offsets.forEach((offset, index) => {
|
|
1270
|
-
level = curve.heights[index] ?? 0;
|
|
1271
|
-
path += ` L ${x(runStart(curve, index))} ${y(level)} L ${Math.min(x(offset), right)} ${y(level)}`;
|
|
1272
|
-
});
|
|
1273
|
-
path += ` L ${right} ${y(level)}`;
|
|
1274
|
-
if (cut) path += wallBreak(right, y(level), floorY);
|
|
1275
|
-
return `${path} L ${right} ${floorY} Z`;
|
|
1276
|
-
};
|
|
1277
|
-
var featureShare = (feature, x1) => {
|
|
1278
|
-
const trueShare = feature.across ? feature.across.width / (feature.across.width + x1) : FEATURE_SHARE;
|
|
1279
|
-
return {
|
|
1280
|
-
share: Math.min(Math.max(trueShare, FEATURE_SHARE_MIN), FEATURE_SHARE_MAX),
|
|
1281
|
-
cut: feature.across !== null && trueShare > FEATURE_SHARE_MAX
|
|
1282
|
-
};
|
|
1283
|
-
};
|
|
1284
|
-
var plotScale = (width, spanMm, topMm) => {
|
|
1285
|
-
const fitted = (width - SECTION_PAD.left - SECTION_PAD.right) / spanMm;
|
|
1286
|
-
if (topMm * fitted > SECTION_MAX_H) return { scale: SECTION_MAX_H / topMm, plotH: SECTION_MAX_H };
|
|
1287
|
-
return { scale: fitted, plotH: Math.max(topMm * fitted, SECTION_MIN_H) };
|
|
1288
|
-
};
|
|
1289
|
-
var heightLabels = (risers, depth, y) => spaced(
|
|
1290
|
-
[...new Set(risers.map((riser) => riser.to))].sort((a, b) => a - b),
|
|
1291
|
-
(h) => -y(h),
|
|
1292
|
-
LABEL_GAP_Y
|
|
1293
|
-
).filter((h) => depth === null || Math.abs(y(h) - y(depth)) >= LABEL_GAP_Y);
|
|
1294
|
-
var offsetLabels = (risers, x) => spaced(
|
|
1295
|
-
risers.filter((riser) => x(riser.at) - x(0) >= WALL_LABEL_GAP_X),
|
|
1296
|
-
(riser) => x(riser.at),
|
|
1297
|
-
LABEL_GAP_X
|
|
1298
|
-
);
|
|
1299
|
-
var headingFor = (feature) => feature.through ? "THROUGH FEATURE" : "FEATURE";
|
|
1300
|
-
var headingMiddle = (heading, wallX) => Math.max(
|
|
1301
|
-
(SECTION_PAD.left + wallX) / 2,
|
|
1302
|
-
SECTION_PAD.left + heading.length * HEADING_CHAR_PX / 2
|
|
1303
|
-
);
|
|
1304
|
-
var sectionGeometry = (whole, feature, width) => {
|
|
1305
|
-
const { curve, cut } = trimmedCurve(whole);
|
|
1306
|
-
const last = curve.offsets[curve.offsets.length - 1] ?? 0;
|
|
1307
|
-
const peak = peakHeight(curve);
|
|
1308
|
-
const span = last > 0 ? last : Math.max(peak, 1);
|
|
1309
|
-
const x1 = span * 1.08;
|
|
1310
|
-
const { share, cut: featureCut } = featureShare(feature, x1);
|
|
1311
|
-
const x0 = -x1 * share / (1 - share);
|
|
1312
|
-
const top = Math.max(peak * 1.06, (feature.depth ?? 0) * 1.06, span * 0.08);
|
|
1313
|
-
const { scale, plotH } = plotScale(width, x1 - x0, top);
|
|
1314
|
-
const floorY = SECTION_PAD.top + plotH;
|
|
1315
|
-
const x = (mm) => SECTION_PAD.left + (mm - x0) * scale;
|
|
1316
|
-
const y = (mm) => floorY - mm * scale;
|
|
1317
|
-
const right = Math.min(x(x1), width - SECTION_PAD.right);
|
|
1318
|
-
const risers = risersOf(curve);
|
|
1319
|
-
const { depth } = feature;
|
|
1320
|
-
const heading = headingFor(feature);
|
|
1321
|
-
return {
|
|
1322
|
-
curve,
|
|
1323
|
-
cut,
|
|
1324
|
-
last,
|
|
1325
|
-
width,
|
|
1326
|
-
height: plotH + SECTION_PAD.top + SECTION_PAD.bottom,
|
|
1327
|
-
scale,
|
|
1328
|
-
x0,
|
|
1329
|
-
x,
|
|
1330
|
-
y,
|
|
1331
|
-
floorY,
|
|
1332
|
-
right,
|
|
1333
|
-
materialPath: materialPath(curve, cut, x, y, floorY, right),
|
|
1334
|
-
across: offsetLabels(risers, x),
|
|
1335
|
-
up: heightLabels(risers, depth, y),
|
|
1336
|
-
feature,
|
|
1337
|
-
featureCut,
|
|
1338
|
-
featureHeading: heading,
|
|
1339
|
-
featureHeadingX: headingMiddle(heading, x(0)),
|
|
1340
|
-
featureTopY: depth === null ? SECTION_PAD.top : y(depth),
|
|
1341
|
-
featureBottomY: feature.through ? floorY + FLOOR_STRIP : floorY,
|
|
1342
|
-
wallTop: y(curve.heights[0] ?? 0),
|
|
1343
|
-
datumX: right + 8,
|
|
1344
|
-
ordinateY: floorY + 22
|
|
1345
|
-
};
|
|
1346
|
-
};
|
|
1347
|
-
|
|
1348
|
-
// src/reach/section-parts.tsx
|
|
1349
|
-
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1350
|
-
var Arrows = ({ id }) => /* @__PURE__ */ jsx14("defs", { children: /* @__PURE__ */ jsx14(
|
|
1351
|
-
"marker",
|
|
1352
|
-
{
|
|
1353
|
-
id,
|
|
1354
|
-
viewBox: "0 0 10 10",
|
|
1355
|
-
refX: "10",
|
|
1356
|
-
refY: "5",
|
|
1357
|
-
markerWidth: "7",
|
|
1358
|
-
markerHeight: "7",
|
|
1359
|
-
orient: "auto-start-reverse",
|
|
1360
|
-
children: /* @__PURE__ */ jsx14("path", { d: "M 0 1.5 L 10 5 L 0 8.5 z", className: ARROW })
|
|
1361
|
-
}
|
|
1362
|
-
) });
|
|
1363
|
-
var SectionShapes = ({ geometry }) => {
|
|
1364
|
-
const { x, floorY, right, featureTopY, featureBottomY, wallTop, feature } = geometry;
|
|
1365
|
-
const floorLeft = feature.through ? x(0) : SECTION_PAD.left;
|
|
1366
|
-
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
1367
|
-
/* @__PURE__ */ jsx14(
|
|
1368
|
-
"rect",
|
|
1369
|
-
{
|
|
1370
|
-
x: SECTION_PAD.left,
|
|
1371
|
-
y: featureTopY,
|
|
1372
|
-
width: x(0) - SECTION_PAD.left,
|
|
1373
|
-
height: featureBottomY - featureTopY,
|
|
1374
|
-
className: FEATURE_FILL
|
|
1375
|
-
}
|
|
1376
|
-
),
|
|
1377
|
-
/* @__PURE__ */ jsx14(
|
|
1378
|
-
"rect",
|
|
1379
|
-
{
|
|
1380
|
-
x: floorLeft,
|
|
1381
|
-
y: floorY,
|
|
1382
|
-
width: right - floorLeft,
|
|
1383
|
-
height: FLOOR_STRIP,
|
|
1384
|
-
className: FLOOR
|
|
1385
|
-
}
|
|
1386
|
-
),
|
|
1387
|
-
/* @__PURE__ */ jsx14("path", { d: geometry.materialPath, strokeWidth: 1, className: MATERIAL }),
|
|
1388
|
-
/* @__PURE__ */ jsx14("line", { x1: floorLeft, x2: right, y1: floorY, y2: floorY, strokeWidth: 1, className: LINE }),
|
|
1389
|
-
feature.through ? null : /* @__PURE__ */ jsx14(
|
|
1390
|
-
"line",
|
|
1391
|
-
{
|
|
1392
|
-
x1: SECTION_PAD.left,
|
|
1393
|
-
x2: x(0),
|
|
1394
|
-
y1: floorY,
|
|
1395
|
-
y2: floorY,
|
|
1396
|
-
strokeWidth: 2,
|
|
1397
|
-
className: FEATURE_LINE
|
|
1398
|
-
}
|
|
1399
|
-
),
|
|
1400
|
-
/* @__PURE__ */ jsx14(
|
|
1401
|
-
"line",
|
|
1402
|
-
{
|
|
1403
|
-
x1: x(0),
|
|
1404
|
-
x2: x(0),
|
|
1405
|
-
y1: featureBottomY,
|
|
1406
|
-
y2: wallTop,
|
|
1407
|
-
strokeWidth: 2,
|
|
1408
|
-
className: FEATURE_LINE
|
|
1409
|
-
}
|
|
1410
|
-
),
|
|
1411
|
-
/* @__PURE__ */ jsx14(
|
|
1412
|
-
"line",
|
|
1413
|
-
{
|
|
1414
|
-
x1: x(0),
|
|
1415
|
-
x2: x(0),
|
|
1416
|
-
y1: wallTop,
|
|
1417
|
-
y2: SECTION_PAD.top,
|
|
1418
|
-
strokeWidth: 0.75,
|
|
1419
|
-
strokeDasharray: "3 2",
|
|
1420
|
-
className: FEATURE_LINE
|
|
1421
|
-
}
|
|
1422
|
-
)
|
|
1423
|
-
] });
|
|
1424
|
-
};
|
|
1425
|
-
var SectionHeadings = ({ geometry }) => {
|
|
1426
|
-
const { right } = geometry;
|
|
1427
|
-
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
1428
|
-
/* @__PURE__ */ jsx14(
|
|
1429
|
-
"text",
|
|
1430
|
-
{
|
|
1431
|
-
x: geometry.featureHeadingX,
|
|
1432
|
-
y: SECTION_PAD.top - 8,
|
|
1433
|
-
textAnchor: "middle",
|
|
1434
|
-
fontSize: 9,
|
|
1435
|
-
fontWeight: 600,
|
|
1436
|
-
className: FEATURE_TEXT,
|
|
1437
|
-
children: geometry.featureHeading
|
|
1438
|
-
}
|
|
1439
|
-
),
|
|
1440
|
-
/* @__PURE__ */ jsx14(
|
|
1441
|
-
"text",
|
|
1442
|
-
{
|
|
1443
|
-
x: right,
|
|
1444
|
-
y: SECTION_PAD.top - 8,
|
|
1445
|
-
textAnchor: "end",
|
|
1446
|
-
fontSize: 9,
|
|
1447
|
-
className: TEXT,
|
|
1448
|
-
opacity: 0.7,
|
|
1449
|
-
children: "ADJACENT WALL HEIGHT"
|
|
1450
|
-
}
|
|
1451
|
-
)
|
|
1452
|
-
] });
|
|
1453
|
-
};
|
|
1454
|
-
var FeatureBreak = ({ geometry }) => geometry.featureCut ? /* @__PURE__ */ jsx14(
|
|
1455
|
-
"path",
|
|
1456
|
-
{
|
|
1457
|
-
d: featureBreak(SECTION_PAD.left, geometry.featureTopY, geometry.featureBottomY),
|
|
1458
|
-
strokeWidth: 1,
|
|
1459
|
-
fill: "none",
|
|
1460
|
-
className: FEATURE_LINE
|
|
1461
|
-
}
|
|
1462
|
-
) : null;
|
|
1463
|
-
var FeatureDepth = ({ geometry, units }) => {
|
|
1464
|
-
const { x, featureTopY, datumX } = geometry;
|
|
1465
|
-
const { depth } = geometry.feature;
|
|
1466
|
-
if (depth === null) return null;
|
|
1467
|
-
return /* @__PURE__ */ jsxs11("g", { children: [
|
|
1468
|
-
/* @__PURE__ */ jsx14("title", { children: "Feature depth" }),
|
|
1469
|
-
/* @__PURE__ */ jsx14(
|
|
1470
|
-
"line",
|
|
1471
|
-
{
|
|
1472
|
-
x1: SECTION_PAD.left,
|
|
1473
|
-
x2: x(0),
|
|
1474
|
-
y1: featureTopY,
|
|
1475
|
-
y2: featureTopY,
|
|
1476
|
-
strokeWidth: 2,
|
|
1477
|
-
className: FEATURE_LINE
|
|
1478
|
-
}
|
|
1479
|
-
),
|
|
1480
|
-
/* @__PURE__ */ jsx14(
|
|
1481
|
-
"line",
|
|
1482
|
-
{
|
|
1483
|
-
x1: x(0),
|
|
1484
|
-
x2: datumX,
|
|
1485
|
-
y1: featureTopY,
|
|
1486
|
-
y2: featureTopY,
|
|
1487
|
-
strokeWidth: 0.75,
|
|
1488
|
-
strokeDasharray: "4 2",
|
|
1489
|
-
className: FEATURE_LINE
|
|
1490
|
-
}
|
|
1491
|
-
),
|
|
1492
|
-
x(0) - SECTION_PAD.left >= DEPTH_WORD_PX ? /* @__PURE__ */ jsx14(
|
|
1493
|
-
"text",
|
|
1494
|
-
{
|
|
1495
|
-
x: SECTION_PAD.left + 4,
|
|
1496
|
-
y: featureTopY - 4,
|
|
1497
|
-
fontSize: 9,
|
|
1498
|
-
fontWeight: 600,
|
|
1499
|
-
className: FEATURE_TEXT,
|
|
1500
|
-
children: "depth"
|
|
1501
|
-
}
|
|
1502
|
-
) : null,
|
|
1503
|
-
/* @__PURE__ */ jsx14(
|
|
1504
|
-
"text",
|
|
1505
|
-
{
|
|
1506
|
-
x: datumX + 4,
|
|
1507
|
-
y: featureTopY + 3.5,
|
|
1508
|
-
fontSize: 10,
|
|
1509
|
-
fontWeight: 600,
|
|
1510
|
-
className: FEATURE_TEXT,
|
|
1511
|
-
children: lengthValue(depth, units)
|
|
1512
|
-
}
|
|
1513
|
-
)
|
|
1514
|
-
] });
|
|
1515
|
-
};
|
|
1516
|
-
var ReadRun = ({ geometry, units, arrowId }) => {
|
|
1517
|
-
const { x, last, cut, floorY, ordinateY } = geometry;
|
|
1518
|
-
if (last <= 0 || cut) return null;
|
|
1519
|
-
return /* @__PURE__ */ jsxs11("g", { children: [
|
|
1520
|
-
/* @__PURE__ */ jsx14(
|
|
1521
|
-
"line",
|
|
1522
|
-
{
|
|
1523
|
-
x1: x(0),
|
|
1524
|
-
x2: x(last),
|
|
1525
|
-
y1: ordinateY - 8,
|
|
1526
|
-
y2: ordinateY - 8,
|
|
1527
|
-
strokeWidth: 0.75,
|
|
1528
|
-
markerStart: `url(#${arrowId})`,
|
|
1529
|
-
markerEnd: `url(#${arrowId})`,
|
|
1530
|
-
className: LINE
|
|
1531
|
-
}
|
|
1532
|
-
),
|
|
1533
|
-
/* @__PURE__ */ jsx14(
|
|
1534
|
-
"line",
|
|
1535
|
-
{
|
|
1536
|
-
x1: x(last),
|
|
1537
|
-
x2: x(last),
|
|
1538
|
-
y1: floorY,
|
|
1539
|
-
y2: ordinateY - 4,
|
|
1540
|
-
strokeWidth: 0.5,
|
|
1541
|
-
className: LINE
|
|
1542
|
-
}
|
|
1543
|
-
),
|
|
1544
|
-
/* @__PURE__ */ jsxs11(
|
|
1545
|
-
"text",
|
|
1546
|
-
{
|
|
1547
|
-
x: (x(0) + x(last)) / 2,
|
|
1548
|
-
y: ordinateY - 11,
|
|
1549
|
-
textAnchor: "middle",
|
|
1550
|
-
fontSize: 10,
|
|
1551
|
-
className: TEXT,
|
|
1552
|
-
children: [
|
|
1553
|
-
lengthValue(last, units),
|
|
1554
|
-
" read"
|
|
1555
|
-
]
|
|
1556
|
-
}
|
|
1557
|
-
)
|
|
1558
|
-
] });
|
|
1559
|
-
};
|
|
1560
|
-
var OffsetOrdinates = (props) => {
|
|
1561
|
-
const { geometry, units } = props;
|
|
1562
|
-
const { x, y, right, width, floorY, ordinateY } = geometry;
|
|
1563
|
-
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
1564
|
-
/* @__PURE__ */ jsx14(
|
|
1565
|
-
"text",
|
|
1566
|
-
{
|
|
1567
|
-
x: Math.min(
|
|
1568
|
-
Math.max((x(0) + right) / 2, AXIS_LABEL_HALF_PX + READ_INSET),
|
|
1569
|
-
width - AXIS_LABEL_HALF_PX - READ_INSET
|
|
1570
|
-
),
|
|
1571
|
-
y: ordinateY + 32,
|
|
1572
|
-
textAnchor: "middle",
|
|
1573
|
-
fontSize: 9,
|
|
1574
|
-
className: TEXT,
|
|
1575
|
-
opacity: 0.7,
|
|
1576
|
-
children: "XY PLANE DISTANCE FROM FEATURE"
|
|
1577
|
-
}
|
|
1578
|
-
),
|
|
1579
|
-
/* @__PURE__ */ jsx14(
|
|
1580
|
-
"line",
|
|
1581
|
-
{
|
|
1582
|
-
x1: x(0),
|
|
1583
|
-
x2: x(0),
|
|
1584
|
-
y1: floorY,
|
|
1585
|
-
y2: ordinateY + 4,
|
|
1586
|
-
strokeWidth: 0.75,
|
|
1587
|
-
className: LINE
|
|
1588
|
-
}
|
|
1589
|
-
),
|
|
1590
|
-
/* @__PURE__ */ jsx14("text", { x: x(0), y: ordinateY + 15, textAnchor: "middle", fontSize: 10, className: TEXT, children: "0" }),
|
|
1591
|
-
geometry.across.map((riser) => /* @__PURE__ */ jsxs11("g", { children: [
|
|
1592
|
-
/* @__PURE__ */ jsx14(
|
|
1593
|
-
"line",
|
|
1594
|
-
{
|
|
1595
|
-
x1: x(riser.at),
|
|
1596
|
-
x2: x(riser.at),
|
|
1597
|
-
y1: y(riser.from) + 2,
|
|
1598
|
-
y2: ordinateY + 4,
|
|
1599
|
-
strokeWidth: 0.5,
|
|
1600
|
-
strokeDasharray: "2 2",
|
|
1601
|
-
className: LINE
|
|
1602
|
-
}
|
|
1603
|
-
),
|
|
1604
|
-
/* @__PURE__ */ jsx14(
|
|
1605
|
-
"text",
|
|
1606
|
-
{
|
|
1607
|
-
x: x(riser.at),
|
|
1608
|
-
y: ordinateY + 15,
|
|
1609
|
-
textAnchor: "middle",
|
|
1610
|
-
fontSize: 10,
|
|
1611
|
-
className: TEXT,
|
|
1612
|
-
children: lengthValue(riser.at, units)
|
|
1613
|
-
}
|
|
1614
|
-
)
|
|
1615
|
-
] }, `x:${riser.at}`)),
|
|
1616
|
-
/* @__PURE__ */ jsx14(ReadRun, { ...props })
|
|
1617
|
-
] });
|
|
1618
|
-
};
|
|
1619
|
-
var HeightOrdinates = ({ geometry, units }) => {
|
|
1620
|
-
const { y, right, floorY, datumX } = geometry;
|
|
1621
|
-
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
1622
|
-
/* @__PURE__ */ jsx14("text", { x: datumX + 4, y: floorY + 3.5, fontSize: 10, className: TEXT, children: "0" }),
|
|
1623
|
-
geometry.up.map((h) => /* @__PURE__ */ jsxs11("g", { children: [
|
|
1624
|
-
/* @__PURE__ */ jsx14(
|
|
1625
|
-
"line",
|
|
1626
|
-
{
|
|
1627
|
-
x1: right - 2,
|
|
1628
|
-
x2: datumX,
|
|
1629
|
-
y1: y(h),
|
|
1630
|
-
y2: y(h),
|
|
1631
|
-
strokeWidth: 0.5,
|
|
1632
|
-
strokeDasharray: "2 2",
|
|
1633
|
-
className: LINE
|
|
1634
|
-
}
|
|
1635
|
-
),
|
|
1636
|
-
/* @__PURE__ */ jsx14("text", { x: datumX + 4, y: y(h) + 3.5, fontSize: 10, className: TEXT, children: lengthValue(h, units) })
|
|
1637
|
-
] }, `y:${h}`))
|
|
1638
|
-
] });
|
|
1639
|
-
};
|
|
1640
|
-
|
|
1641
|
-
// src/reach/reach-section.tsx
|
|
1642
|
-
import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1643
|
-
var ReachSection = ({
|
|
1644
|
-
curve: whole,
|
|
1645
|
-
units,
|
|
1646
|
-
feature,
|
|
1647
|
-
width
|
|
1648
|
-
}) => {
|
|
1649
|
-
const [hover, setHover] = useState9(null);
|
|
1650
|
-
const geometry = useMemo3(() => sectionGeometry(whole, feature, width), [whole, feature, width]);
|
|
1651
|
-
const reactId = useId2();
|
|
1652
|
-
const arrowId = `reach-dim-arrow-${reactId.replace(/[^\w-]/g, "")}`;
|
|
1653
|
-
const reading = hover === null ? null : readingAt(whole, hover);
|
|
1654
|
-
const drawing = useMemo3(
|
|
1655
|
-
() => /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
1656
|
-
/* @__PURE__ */ jsx15(Arrows, { id: arrowId }),
|
|
1657
|
-
/* @__PURE__ */ jsx15(SectionShapes, { geometry }),
|
|
1658
|
-
/* @__PURE__ */ jsx15(SectionHeadings, { geometry }),
|
|
1659
|
-
/* @__PURE__ */ jsx15(FeatureBreak, { geometry }),
|
|
1660
|
-
/* @__PURE__ */ jsx15(FeatureDepth, { geometry, units }),
|
|
1661
|
-
/* @__PURE__ */ jsx15(OffsetOrdinates, { geometry, units, arrowId }),
|
|
1662
|
-
/* @__PURE__ */ jsx15(HeightOrdinates, { geometry, units })
|
|
1663
|
-
] }),
|
|
1664
|
-
[arrowId, geometry, units]
|
|
1665
|
-
);
|
|
1666
|
-
return /* @__PURE__ */ jsxs12(
|
|
1667
|
-
"svg",
|
|
1668
|
-
{
|
|
1669
|
-
viewBox: `0 0 ${width} ${geometry.height}`,
|
|
1670
|
-
width,
|
|
1671
|
-
height: geometry.height,
|
|
1672
|
-
className: "select-none",
|
|
1673
|
-
role: "img",
|
|
1674
|
-
"aria-label": "Reach curve, dimensioned",
|
|
1675
|
-
onPointerMove: (event) => setHover(offsetUnder(event.clientX, event.currentTarget.getBoundingClientRect(), geometry)),
|
|
1676
|
-
onPointerLeave: () => setHover(null),
|
|
1677
|
-
children: [
|
|
1678
|
-
drawing,
|
|
1679
|
-
reading ? /* @__PURE__ */ jsx15(ReadingMark, { geometry, reading, units }) : null
|
|
1680
|
-
]
|
|
1681
|
-
}
|
|
1682
|
-
);
|
|
1683
|
-
};
|
|
1684
|
-
|
|
1685
|
-
// src/reach/use-element-size.ts
|
|
1686
|
-
import { useLayoutEffect, useRef as useRef5, useState as useState10 } from "react";
|
|
1687
|
-
var useElementSize = () => {
|
|
1688
|
-
const ref = useRef5(null);
|
|
1689
|
-
const [size, setSize] = useState10({ width: 0, height: 0 });
|
|
1690
|
-
useLayoutEffect(() => {
|
|
1691
|
-
const element = ref.current;
|
|
1692
|
-
if (!element) return;
|
|
1693
|
-
const observer = new ResizeObserver(([entry]) => {
|
|
1694
|
-
if (!entry) return;
|
|
1695
|
-
const { width, height } = entry.contentRect;
|
|
1696
|
-
setSize(
|
|
1697
|
-
(was) => Math.abs(was.width - width) < 0.5 && Math.abs(was.height - height) < 0.5 ? was : { width, height }
|
|
1698
|
-
);
|
|
1699
|
-
});
|
|
1700
|
-
observer.observe(element);
|
|
1701
|
-
return () => observer.disconnect();
|
|
1702
|
-
}, []);
|
|
1703
|
-
return [ref, size];
|
|
1704
|
-
};
|
|
1705
|
-
|
|
1706
|
-
// src/reach/reach-chart.tsx
|
|
1707
|
-
import { Fragment as Fragment6, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1708
|
-
var CHART_MIN_W = 200;
|
|
1709
|
-
var DRAWING_MIN_W = 240;
|
|
1710
|
-
var ReachChart = ({ curve, units, feature }) => {
|
|
1711
|
-
const [ref, size] = useElementSize();
|
|
1712
|
-
return /* @__PURE__ */ jsx16("div", { ref, className: "w-full", children: /* @__PURE__ */ jsx16(
|
|
1713
|
-
ReachSection,
|
|
1714
|
-
{
|
|
1715
|
-
curve,
|
|
1716
|
-
units,
|
|
1717
|
-
feature,
|
|
1718
|
-
width: Math.max(size.width, CHART_MIN_W)
|
|
1719
|
-
}
|
|
1720
|
-
) });
|
|
1721
|
-
};
|
|
1722
|
-
var CutNote = ({
|
|
1723
|
-
curve,
|
|
1724
|
-
units
|
|
1725
|
-
}) => {
|
|
1726
|
-
const trimmed = useMemo4(() => trimmedCurve(curve), [curve]);
|
|
1727
|
-
if (!trimmed.cut) return null;
|
|
1728
|
-
const drawnTo = trimmed.curve.offsets[trimmed.curve.offsets.length - 1] ?? 0;
|
|
1729
|
-
const readTo = curve.offsets[curve.offsets.length - 1] ?? 0;
|
|
1730
|
-
return /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
1731
|
-
" ",
|
|
1732
|
-
"It is at full height from ",
|
|
1733
|
-
lengthValue(drawnTo, units),
|
|
1734
|
-
" ",
|
|
1735
|
-
unitLabel(units),
|
|
1736
|
-
" out, so the drawing stops there; the Engine read it to ",
|
|
1737
|
-
lengthValue(readTo, units),
|
|
1738
|
-
" ",
|
|
1739
|
-
unitLabel(units),
|
|
1740
|
-
"."
|
|
1741
|
-
] });
|
|
1742
|
-
};
|
|
1743
|
-
var ReachDrawing = ({ curve, units, feature }) => {
|
|
1744
|
-
const [ref, size] = useElementSize();
|
|
1745
|
-
return /* @__PURE__ */ jsxs13("div", { ref, className: "flex flex-col gap-3 p-4", children: [
|
|
1746
|
-
/* @__PURE__ */ jsxs13("figure", { className: "flex flex-col gap-1", children: [
|
|
1747
|
-
/* @__PURE__ */ jsxs13("figcaption", { className: "text-2xs font-semibold text-gray-400 uppercase dark:text-zinc-500", children: [
|
|
1748
|
-
"Section at the wall \xB7 to scale \xB7 ",
|
|
1749
|
-
unitLabel(units)
|
|
1750
|
-
] }),
|
|
1751
|
-
/* @__PURE__ */ jsx16(
|
|
1752
|
-
ReachSection,
|
|
1753
|
-
{
|
|
1754
|
-
curve,
|
|
1755
|
-
units,
|
|
1756
|
-
feature,
|
|
1757
|
-
width: Math.max(size.width, DRAWING_MIN_W)
|
|
1758
|
-
}
|
|
1759
|
-
)
|
|
1760
|
-
] }),
|
|
1761
|
-
/* @__PURE__ */ jsxs13("p", { className: "text-2xs text-gray-400 dark:text-zinc-500", children: [
|
|
1762
|
-
"Point at the walls to read them: the distance from the feature, then the reach a tool needs there.",
|
|
1763
|
-
/* @__PURE__ */ jsx16(CutNote, { curve, units })
|
|
1764
|
-
] })
|
|
1765
|
-
] });
|
|
1766
|
-
};
|
|
1767
|
-
|
|
1768
|
-
// src/feature-panel/fold-store.ts
|
|
1769
|
-
var storageFolds = (storage, prefix) => ({
|
|
1770
|
-
read: (id) => {
|
|
1771
|
-
try {
|
|
1772
|
-
const stored = storage?.getItem(`${prefix}${id}`);
|
|
1773
|
-
return stored === "open" ? true : stored === "shut" ? false : void 0;
|
|
1774
|
-
} catch {
|
|
1775
|
-
return void 0;
|
|
1776
|
-
}
|
|
1777
|
-
},
|
|
1778
|
-
write: (id, open) => {
|
|
1779
|
-
try {
|
|
1780
|
-
storage?.setItem(`${prefix}${id}`, open ? "open" : "shut");
|
|
1781
|
-
} catch {
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
});
|
|
1785
|
-
|
|
1786
|
-
// src/feature-panel/feature-details.tsx
|
|
1787
|
-
import { useCallback as useCallback2, useMemo as useMemo7, useState as useState13 } from "react";
|
|
1788
|
-
import { CrosshairIcon, InfoIcon } from "@phosphor-icons/react";
|
|
1789
|
-
import { Button as Button3, Tooltip, cn as cn12 } from "@toolpath/ui";
|
|
1790
|
-
|
|
1791
|
-
// src/feature-panel/feature-name.tsx
|
|
1792
|
-
import { cn as cn9 } from "@toolpath/ui";
|
|
1793
|
-
|
|
1794
|
-
// src/feature-panel/look.ts
|
|
1795
|
-
var resolveLook = (look) => ({
|
|
1796
|
-
rules: look?.rules ?? "sentence",
|
|
1797
|
-
selected: look?.selected ?? "grey",
|
|
1798
|
-
muted: look?.muted ?? "pale"
|
|
1799
|
-
});
|
|
1800
|
-
var secondaryText = (look) => look.muted === "strong" ? "text-gray dark:text-zinc-300" : "text-gray-400 dark:text-zinc-400";
|
|
1801
|
-
var faintText = (look) => look.muted === "strong" ? "text-gray dark:text-zinc-300" : "text-gray-300 dark:text-zinc-500";
|
|
1802
|
-
|
|
1803
|
-
// src/feature-panel/feature-name.tsx
|
|
1804
|
-
import { Fragment as Fragment7, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1805
|
-
var FeatureName = ({ feature, label, look, vocabulary = MILLING_RULES }) => {
|
|
1806
|
-
const { name, detail } = vocabulary.featureLabel(feature);
|
|
1807
|
-
return /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
1808
|
-
label ?? name,
|
|
1809
|
-
detail ? /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
1810
|
-
" ",
|
|
1811
|
-
/* @__PURE__ */ jsx17("span", { className: cn9("font-normal", faintText(look)), children: detail })
|
|
1812
|
-
] }) : null
|
|
1813
|
-
] });
|
|
1814
|
-
};
|
|
1815
|
-
var RequiredBadge = () => /* @__PURE__ */ jsx17("span", { className: "rounded-full bg-gray-100 px-1.5 text-2xs font-semibold text-gray-500 uppercase dark:bg-zinc-800 dark:text-zinc-300", children: "Required" });
|
|
1816
|
-
|
|
1817
|
-
// src/feature-panel/fold-section.tsx
|
|
1818
|
-
import { useState as useState11 } from "react";
|
|
1819
|
-
import { CaretRightIcon as CaretRightIcon3 } from "@phosphor-icons/react";
|
|
1820
|
-
import { cn as cn10 } from "@toolpath/ui";
|
|
1821
|
-
import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1822
|
-
var FoldSection = ({
|
|
1823
|
-
id,
|
|
1824
|
-
title: title2,
|
|
1825
|
-
action,
|
|
1826
|
-
defaultOpen = true,
|
|
1827
|
-
folds,
|
|
1828
|
-
className,
|
|
1829
|
-
children
|
|
1830
|
-
}) => {
|
|
1831
|
-
const [open, setOpen] = useState11(() => folds?.read(id) ?? defaultOpen);
|
|
1832
|
-
return /* @__PURE__ */ jsxs15("section", { className: cn10("flex flex-col gap-1", className), children: [
|
|
1833
|
-
/* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1", children: [
|
|
1834
|
-
/* @__PURE__ */ jsxs15(
|
|
1835
|
-
"button",
|
|
1836
|
-
{
|
|
1837
|
-
type: "button",
|
|
1838
|
-
"aria-expanded": open,
|
|
1839
|
-
onClick: () => {
|
|
1840
|
-
setOpen(!open);
|
|
1841
|
-
folds?.write(id, !open);
|
|
1842
|
-
},
|
|
1843
|
-
className: cn10(
|
|
1844
|
-
"flex min-w-0 flex-1 cursor-pointer items-center gap-1 rounded text-left text-2xs font-semibold text-gray-400 uppercase",
|
|
1845
|
-
"hover:text-gray-600 dark:text-zinc-500 dark:hover:text-zinc-300",
|
|
1846
|
-
"outline-none focus-visible:ring-2 focus-visible:ring-info/75"
|
|
1847
|
-
),
|
|
1848
|
-
children: [
|
|
1849
|
-
/* @__PURE__ */ jsx18(
|
|
1850
|
-
CaretRightIcon3,
|
|
1851
|
-
{
|
|
1852
|
-
weight: "bold",
|
|
1853
|
-
className: cn10("size-3 shrink-0 transition-transform duration-150", open && "rotate-90")
|
|
1854
|
-
}
|
|
1855
|
-
),
|
|
1856
|
-
/* @__PURE__ */ jsx18("span", { className: "truncate", children: title2 })
|
|
1857
|
-
]
|
|
1858
|
-
}
|
|
1859
|
-
),
|
|
1860
|
-
action
|
|
1861
|
-
] }),
|
|
1862
|
-
open ? children : null
|
|
1863
|
-
] });
|
|
1864
|
-
};
|
|
1865
|
-
|
|
1866
|
-
// src/feature-panel/read-state.tsx
|
|
1867
|
-
import { useEffect as useEffect4, useState as useState12 } from "react";
|
|
1868
|
-
import { Button as Button2 } from "@toolpath/ui";
|
|
1869
|
-
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1870
|
-
var Reading = ({ children }) => /* @__PURE__ */ jsx19("p", { className: "text-sm text-gray-400 dark:text-zinc-400", role: "status", children });
|
|
1871
|
-
var ReadFailure = ({
|
|
1872
|
-
failure
|
|
1873
|
-
}) => {
|
|
1874
|
-
const waiting = useWaiting(failure.retryAt);
|
|
1875
|
-
return /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-start gap-2 text-sm", children: [
|
|
1876
|
-
/* @__PURE__ */ jsx19("p", { className: "text-danger", role: "alert", children: failure.message }),
|
|
1877
|
-
failure.retry ? /* @__PURE__ */ jsx19(Button2, { variant: "secondary", size: "sm", disabled: waiting, onClick: failure.retry, children: "Retry" }) : null
|
|
1878
|
-
] });
|
|
1879
|
-
};
|
|
1880
|
-
var useWaiting = (until) => {
|
|
1881
|
-
const [passed, setPassed] = useState12(void 0);
|
|
1882
|
-
useEffect4(() => {
|
|
1883
|
-
if (until === void 0) return;
|
|
1884
|
-
const timer = setTimeout(() => setPassed(until), Math.max(until - Date.now(), 0));
|
|
1885
|
-
return () => clearTimeout(timer);
|
|
1886
|
-
}, [until]);
|
|
1887
|
-
return until !== void 0 && passed !== until;
|
|
1888
|
-
};
|
|
1889
|
-
|
|
1890
|
-
// src/feature-panel/pop-outs.tsx
|
|
1891
|
-
import { useMemo as useMemo6 } from "react";
|
|
1892
|
-
import {
|
|
1893
|
-
ArrowSquareOutIcon,
|
|
1894
|
-
BracketsCurlyIcon,
|
|
1895
|
-
ChartLineIcon,
|
|
1896
|
-
TableIcon
|
|
1897
|
-
} from "@phosphor-icons/react";
|
|
1898
|
-
import { IconButton as IconButton3 } from "@toolpath/ui";
|
|
1899
|
-
|
|
1900
|
-
// src/feature-panel/record-sections.tsx
|
|
1901
|
-
import { useMemo as useMemo5 } from "react";
|
|
1902
|
-
import { CheckIcon as CheckIcon3, CopyIcon as CopyIcon2 } from "@phosphor-icons/react";
|
|
1903
|
-
import { IconButton as IconButton2, cn as cn11 } from "@toolpath/ui";
|
|
1904
|
-
|
|
1905
|
-
// src/model/feature-record.ts
|
|
1906
|
-
var MAX_DATASHEET_FIELDS = 500;
|
|
1907
|
-
var formatLeaf = (value) => typeof value === "number" ? String(Number(value.toFixed(4))) : typeof value === "string" ? value : value === null ? "null" : String(value);
|
|
1908
|
-
var datasheetFields = (datasheet) => {
|
|
1909
|
-
const fields = [];
|
|
1910
|
-
let more = false;
|
|
1911
|
-
const walk = (value, path) => {
|
|
1912
|
-
if (fields.length >= MAX_DATASHEET_FIELDS) {
|
|
1913
|
-
more = true;
|
|
1914
|
-
return;
|
|
1915
|
-
}
|
|
1916
|
-
if (Array.isArray(value)) {
|
|
1917
|
-
if (value.every((each) => typeof each !== "object" || each === null)) {
|
|
1918
|
-
fields.push({ path, value: value.length === 0 ? "[]" : value.map(formatLeaf).join(", ") });
|
|
1919
|
-
} else {
|
|
1920
|
-
value.forEach((each, index) => walk(each, `${path}[${index}]`));
|
|
1921
|
-
}
|
|
1922
|
-
} else if (typeof value === "object" && value !== null) {
|
|
1923
|
-
const entries = Object.entries(value);
|
|
1924
|
-
if (entries.length === 0) fields.push({ path, value: "{}" });
|
|
1925
|
-
for (const [key, each] of entries) walk(each, path ? `${path}.${key}` : key);
|
|
1926
|
-
} else {
|
|
1927
|
-
fields.push({ path, value: formatLeaf(value) });
|
|
1928
|
-
}
|
|
1929
|
-
};
|
|
1930
|
-
if (datasheet !== null && datasheet !== void 0) walk(datasheet, "");
|
|
1931
|
-
return { fields, more };
|
|
1932
|
-
};
|
|
1933
|
-
|
|
1934
|
-
// src/feature-panel/record-sections.tsx
|
|
1935
|
-
import { Fragment as Fragment8, jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1936
|
-
var COPIED_MS2 = 1500;
|
|
1937
|
-
var RecordSections = ({
|
|
1938
|
-
record,
|
|
1939
|
-
look,
|
|
1940
|
-
folds,
|
|
1941
|
-
popOut
|
|
1942
|
-
}) => {
|
|
1943
|
-
const value = record.status === "ready" ? record.value : null;
|
|
1944
|
-
const { fields, more } = useMemo5(() => datasheetFields(value?.datasheet), [value]);
|
|
1945
|
-
const json = useMemo5(() => value ? JSON.stringify(value, null, 2) : "", [value]);
|
|
1946
|
-
if (record.status === "ready" && !value) return null;
|
|
1947
|
-
const waiting = record.status === "loading" ? /* @__PURE__ */ jsx20(Reading, { children: "Reading the feature\u2019s record\u2026" }) : record.status === "error" ? /* @__PURE__ */ jsx20(ReadFailure, { failure: record }) : null;
|
|
1948
|
-
const count = more ? `${MAX_DATASHEET_FIELDS}+` : String(fields.length);
|
|
1949
|
-
return /* @__PURE__ */ jsxs17(Fragment8, { children: [
|
|
1950
|
-
/* @__PURE__ */ jsx20(
|
|
1951
|
-
FoldSection,
|
|
1952
|
-
{
|
|
1953
|
-
id: "datasheet",
|
|
1954
|
-
defaultOpen: false,
|
|
1955
|
-
title: value ? `All datasheet fields (${count})` : "All datasheet fields",
|
|
1956
|
-
action: value ? popOut?.("datasheet") : null,
|
|
1957
|
-
folds,
|
|
1958
|
-
children: waiting ?? /* @__PURE__ */ jsx20(DatasheetFields, { record: value, look })
|
|
1959
|
-
}
|
|
1960
|
-
),
|
|
1961
|
-
/* @__PURE__ */ jsx20(
|
|
1962
|
-
FoldSection,
|
|
1963
|
-
{
|
|
1964
|
-
id: "raw",
|
|
1965
|
-
defaultOpen: false,
|
|
1966
|
-
title: "Raw API record",
|
|
1967
|
-
action: value ? /* @__PURE__ */ jsxs17(Fragment8, { children: [
|
|
1968
|
-
/* @__PURE__ */ jsx20(CopyButton, { text: json }),
|
|
1969
|
-
popOut?.("raw")
|
|
1970
|
-
] }) : null,
|
|
1971
|
-
folds,
|
|
1972
|
-
children: waiting ?? /* @__PURE__ */ jsx20(RawRecord, { json })
|
|
1973
|
-
}
|
|
1974
|
-
)
|
|
1975
|
-
] });
|
|
1976
|
-
};
|
|
1977
|
-
var DatasheetFields = ({ record, look, wide = false }) => {
|
|
1978
|
-
const { fields } = useMemo5(() => datasheetFields(record?.datasheet), [record]);
|
|
1979
|
-
return record?.datasheet ? /* @__PURE__ */ jsx20(
|
|
1980
|
-
"dl",
|
|
1981
|
-
{
|
|
1982
|
-
className: cn11(
|
|
1983
|
-
"grid grid-cols-[minmax(0,1fr)_auto] gap-y-0.5 text-xs",
|
|
1984
|
-
wide ? "gap-x-4 p-4" : "gap-x-3"
|
|
1985
|
-
),
|
|
1986
|
-
children: fields.map((field) => /* @__PURE__ */ jsxs17("div", { className: "contents", children: [
|
|
1987
|
-
/* @__PURE__ */ jsx20("dt", { className: cn11("truncate font-mono", secondaryText(look)), title: field.path, children: field.path }),
|
|
1988
|
-
/* @__PURE__ */ jsx20(
|
|
1989
|
-
"dd",
|
|
1990
|
-
{
|
|
1991
|
-
className: cn11(
|
|
1992
|
-
"truncate text-right font-mono text-gray-600 tabular-nums select-text dark:text-zinc-100",
|
|
1993
|
-
wide ? "max-w-60" : "max-w-40"
|
|
1994
|
-
),
|
|
1995
|
-
title: field.value,
|
|
1996
|
-
children: field.value
|
|
1997
|
-
}
|
|
1998
|
-
)
|
|
1999
|
-
] }, field.path))
|
|
2000
|
-
}
|
|
2001
|
-
) : /* @__PURE__ */ jsx20("p", { className: cn11("text-sm", secondaryText(look), wide && "p-4"), children: "The API sent no datasheet for this feature." });
|
|
2002
|
-
};
|
|
2003
|
-
var RawRecord = ({
|
|
2004
|
-
json,
|
|
2005
|
-
wide = false
|
|
2006
|
-
}) => /* @__PURE__ */ jsx20(
|
|
2007
|
-
"pre",
|
|
2008
|
-
{
|
|
2009
|
-
className: cn11(
|
|
2010
|
-
"font-mono text-2xs leading-relaxed text-gray-600 select-text dark:text-zinc-200",
|
|
2011
|
-
wide ? "p-4" : "max-h-80 overflow-auto rounded bg-gray-50 p-2 dark:bg-zinc-800/60"
|
|
2012
|
-
),
|
|
2013
|
-
children: json
|
|
2014
|
-
}
|
|
2015
|
-
);
|
|
2016
|
-
var CopyButton = ({ text }) => {
|
|
2017
|
-
const [copied, setCopied] = useTimedValue(COPIED_MS2);
|
|
2018
|
-
return /* @__PURE__ */ jsx20(
|
|
2019
|
-
IconButton2,
|
|
2020
|
-
{
|
|
2021
|
-
"aria-label": copied ? "Copied" : "Copy raw record",
|
|
2022
|
-
title: "Copy",
|
|
2023
|
-
variant: "muted",
|
|
2024
|
-
size: "sm",
|
|
2025
|
-
onClick: () => {
|
|
2026
|
-
void navigator.clipboard?.writeText(text).then(() => setCopied(true)).catch(() => void 0);
|
|
2027
|
-
},
|
|
2028
|
-
children: copied ? /* @__PURE__ */ jsx20(CheckIcon3, { weight: "bold" }) : /* @__PURE__ */ jsx20(CopyIcon2, { weight: "bold" })
|
|
2029
|
-
}
|
|
2030
|
-
);
|
|
2031
|
-
};
|
|
2032
|
-
|
|
2033
|
-
// src/feature-panel/pop-outs.tsx
|
|
2034
|
-
import { Fragment as Fragment9, jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2035
|
-
var TITLES = {
|
|
2036
|
-
reach: "Reach",
|
|
2037
|
-
datasheet: "All datasheet fields",
|
|
2038
|
-
raw: "Raw API record"
|
|
2039
|
-
};
|
|
2040
|
-
var LABELS = {
|
|
2041
|
-
reach: "Pop out the reach drawing",
|
|
2042
|
-
datasheet: "Pop out the datasheet fields",
|
|
2043
|
-
raw: "Pop out the raw record"
|
|
2044
|
-
};
|
|
2045
|
-
var PopOutButton = ({
|
|
2046
|
-
id,
|
|
2047
|
-
onOpen
|
|
2048
|
-
}) => /* @__PURE__ */ jsx21(
|
|
2049
|
-
IconButton3,
|
|
2050
|
-
{
|
|
2051
|
-
"aria-label": LABELS[id],
|
|
2052
|
-
title: "Pop out, larger",
|
|
2053
|
-
variant: "muted",
|
|
2054
|
-
size: "sm",
|
|
2055
|
-
onClick: () => onOpen(id),
|
|
2056
|
-
children: /* @__PURE__ */ jsx21(ArrowSquareOutIcon, { weight: "bold" })
|
|
2057
|
-
}
|
|
2058
|
-
);
|
|
2059
|
-
var PopOuts = ({
|
|
2060
|
-
open,
|
|
2061
|
-
onClose,
|
|
2062
|
-
Window,
|
|
2063
|
-
subtitle,
|
|
2064
|
-
record,
|
|
2065
|
-
units,
|
|
2066
|
-
profile,
|
|
2067
|
-
look
|
|
2068
|
-
}) => {
|
|
2069
|
-
const curve = useMemo6(() => readReachCurve(record?.datasheet), [record]);
|
|
2070
|
-
const json = useMemo6(() => record ? JSON.stringify(record, null, 2) : "", [record]);
|
|
2071
|
-
if (!record) return null;
|
|
2072
|
-
return /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
2073
|
-
open.has("reach") && curve ? /* @__PURE__ */ jsx21(
|
|
2074
|
-
Window,
|
|
2075
|
-
{
|
|
2076
|
-
id: "reach",
|
|
2077
|
-
title: TITLES.reach,
|
|
2078
|
-
subtitle,
|
|
2079
|
-
icon: /* @__PURE__ */ jsx21(ChartLineIcon, { weight: "bold" }),
|
|
2080
|
-
onClose: () => onClose("reach"),
|
|
2081
|
-
children: /* @__PURE__ */ jsx21(ReachDrawing, { curve, units, feature: profile })
|
|
2082
|
-
}
|
|
2083
|
-
) : null,
|
|
2084
|
-
open.has("datasheet") ? /* @__PURE__ */ jsx21(
|
|
2085
|
-
Window,
|
|
2086
|
-
{
|
|
2087
|
-
id: "datasheet",
|
|
2088
|
-
title: TITLES.datasheet,
|
|
2089
|
-
subtitle,
|
|
2090
|
-
icon: /* @__PURE__ */ jsx21(TableIcon, { weight: "bold" }),
|
|
2091
|
-
onClose: () => onClose("datasheet"),
|
|
2092
|
-
children: /* @__PURE__ */ jsx21(DatasheetFields, { record, look, wide: true })
|
|
2093
|
-
}
|
|
2094
|
-
) : null,
|
|
2095
|
-
open.has("raw") ? /* @__PURE__ */ jsx21(
|
|
2096
|
-
Window,
|
|
2097
|
-
{
|
|
2098
|
-
id: "raw",
|
|
2099
|
-
title: TITLES.raw,
|
|
2100
|
-
subtitle,
|
|
2101
|
-
icon: /* @__PURE__ */ jsx21(BracketsCurlyIcon, { weight: "bold" }),
|
|
2102
|
-
action: /* @__PURE__ */ jsx21(CopyButton, { text: json }),
|
|
2103
|
-
onClose: () => onClose("raw"),
|
|
2104
|
-
children: /* @__PURE__ */ jsx21(RawRecord, { json, wide: true })
|
|
2105
|
-
}
|
|
2106
|
-
) : null
|
|
2107
|
-
] });
|
|
2108
|
-
};
|
|
2109
|
-
|
|
2110
|
-
// src/feature-panel/feature-details.tsx
|
|
2111
|
-
import { Fragment as Fragment10, jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2112
|
-
var FeatureDetails = ({
|
|
2113
|
-
feature,
|
|
2114
|
-
units,
|
|
2115
|
-
directionColor,
|
|
2116
|
-
profile,
|
|
2117
|
-
label,
|
|
2118
|
-
required,
|
|
2119
|
-
onFrame,
|
|
2120
|
-
actions,
|
|
2121
|
-
status,
|
|
2122
|
-
children,
|
|
2123
|
-
rules,
|
|
2124
|
-
measurements,
|
|
2125
|
-
record,
|
|
2126
|
-
PopOut,
|
|
2127
|
-
look: lookOption,
|
|
2128
|
-
folds,
|
|
2129
|
-
vocabulary = MILLING_RULES
|
|
2130
|
-
}) => {
|
|
2131
|
-
const look = resolveLook(lookOption);
|
|
2132
|
-
const name = /* @__PURE__ */ jsx22(FeatureName, { feature, label, look, vocabulary });
|
|
2133
|
-
const { detail: cutFrom } = vocabulary.featureLabel(feature);
|
|
2134
|
-
const [out, setOut] = useState13(/* @__PURE__ */ new Set());
|
|
2135
|
-
const openOut = useCallback2((id) => setOut((was) => new Set(was).add(id)), []);
|
|
2136
|
-
const closeOut = useCallback2(
|
|
2137
|
-
(id) => setOut((was) => {
|
|
2138
|
-
const next = new Set(was);
|
|
2139
|
-
next.delete(id);
|
|
2140
|
-
return next;
|
|
2141
|
-
}),
|
|
2142
|
-
[]
|
|
2143
|
-
);
|
|
2144
|
-
const popOut = PopOut ? (id) => /* @__PURE__ */ jsx22(PopOutButton, { id, onOpen: openOut }) : void 0;
|
|
2145
|
-
return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-4 p-4", children: [
|
|
2146
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-2", children: [
|
|
2147
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex items-start gap-2", children: [
|
|
2148
|
-
/* @__PURE__ */ jsxs19("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
|
|
2149
|
-
/* @__PURE__ */ jsx22("h3", { className: "truncate text-sm font-semibold text-gray-600 dark:text-zinc-100", children: name }),
|
|
2150
|
-
/* @__PURE__ */ jsxs19("p", { className: cn12("flex flex-wrap items-center gap-1.5 text-xs", secondaryText(look)), children: [
|
|
2151
|
-
/* @__PURE__ */ jsx22(Swatch, { color: directionColor, className: "size-2.5" }),
|
|
2152
|
-
cutFrom ? `Machined ${cutFrom}` : "Machined",
|
|
2153
|
-
required ? /* @__PURE__ */ jsx22(RequiredBadge, {}) : null,
|
|
2154
|
-
status
|
|
2155
|
-
] })
|
|
2156
|
-
] }),
|
|
2157
|
-
actions,
|
|
2158
|
-
onFrame ? /* @__PURE__ */ jsx22(Button3, { variant: "secondary", size: "sm", onClick: onFrame, className: "shrink-0", children: /* @__PURE__ */ jsxs19("span", { className: "flex items-center gap-1 whitespace-nowrap", children: [
|
|
2159
|
-
/* @__PURE__ */ jsx22(CrosshairIcon, { weight: "bold", className: "size-3.5" }),
|
|
2160
|
-
"Show this feature"
|
|
2161
|
-
] }) }) : null
|
|
2162
|
-
] }),
|
|
2163
|
-
children
|
|
2164
|
-
] }),
|
|
2165
|
-
/* @__PURE__ */ jsx22(FoldSection, { id: "rules", title: "Rules broken", folds, children: /* @__PURE__ */ jsx22(BrokenRuleList, { rules, look }) }),
|
|
2166
|
-
/* @__PURE__ */ jsx22(
|
|
2167
|
-
MeasurementSections,
|
|
2168
|
-
{
|
|
2169
|
-
measurements,
|
|
2170
|
-
considerationsTitle: vocabulary.considerationsTitle,
|
|
2171
|
-
look,
|
|
2172
|
-
folds
|
|
2173
|
-
}
|
|
2174
|
-
),
|
|
2175
|
-
/* @__PURE__ */ jsx22(
|
|
2176
|
-
ReachSection2,
|
|
2177
|
-
{
|
|
2178
|
-
record,
|
|
2179
|
-
units,
|
|
2180
|
-
profile,
|
|
2181
|
-
folds,
|
|
2182
|
-
action: popOut?.("reach")
|
|
2183
|
-
}
|
|
2184
|
-
),
|
|
2185
|
-
/* @__PURE__ */ jsx22(RecordSections, { record, look, folds, popOut }),
|
|
2186
|
-
PopOut ? /* @__PURE__ */ jsx22(
|
|
2187
|
-
PopOuts,
|
|
2188
|
-
{
|
|
2189
|
-
open: out,
|
|
2190
|
-
onClose: closeOut,
|
|
2191
|
-
Window: PopOut,
|
|
2192
|
-
subtitle: name,
|
|
2193
|
-
record: record.status === "ready" ? record.value : null,
|
|
2194
|
-
units,
|
|
2195
|
-
profile,
|
|
2196
|
-
look
|
|
2197
|
-
}
|
|
2198
|
-
) : null
|
|
2199
|
-
] });
|
|
2200
|
-
};
|
|
2201
|
-
var BrokenRuleList = ({
|
|
2202
|
-
rules,
|
|
2203
|
-
look
|
|
2204
|
-
}) => rules.length > 0 ? /* @__PURE__ */ jsx22("ul", { className: "flex flex-col gap-1", children: rules.map((rule) => /* @__PURE__ */ jsxs19(
|
|
2205
|
-
"li",
|
|
2206
|
-
{
|
|
2207
|
-
className: "flex items-start gap-2 text-sm text-gray-600 dark:text-zinc-100",
|
|
2208
|
-
children: [
|
|
2209
|
-
/* @__PURE__ */ jsx22(Swatch, { color: rule.color, className: "mt-0.5" }),
|
|
2210
|
-
/* @__PURE__ */ jsx22(Explained, { note: rule.note, children: /* @__PURE__ */ jsx22("span", { className: "min-w-0 flex-1", children: rule.text }) }),
|
|
2211
|
-
rule.figure ? /* @__PURE__ */ jsxs19(
|
|
2212
|
-
"span",
|
|
2213
|
-
{
|
|
2214
|
-
className: cn12(
|
|
2215
|
-
"shrink-0 tabular-nums",
|
|
2216
|
-
look.rules === "result" ? "font-semibold" : secondaryText(look)
|
|
2217
|
-
),
|
|
2218
|
-
style: look.rules === "result" ? { color: rule.color } : void 0,
|
|
2219
|
-
children: [
|
|
2220
|
-
rule.figure,
|
|
2221
|
-
look.rules === "result" && rule.limit ? /* @__PURE__ */ jsx22("span", { className: "ml-1 font-normal opacity-60", children: rule.limit }) : null
|
|
2222
|
-
]
|
|
2223
|
-
}
|
|
2224
|
-
) : null
|
|
2225
|
-
]
|
|
2226
|
-
},
|
|
2227
|
-
rule.key
|
|
2228
|
-
)) }) : /* @__PURE__ */ jsx22(None, { look });
|
|
2229
|
-
var Explained = ({ note, children }) => note ? /* @__PURE__ */ jsx22(Tooltip, { tip: note, side: "left", children: /* @__PURE__ */ jsx22(
|
|
2230
|
-
"span",
|
|
2231
|
-
{
|
|
2232
|
-
tabIndex: 0,
|
|
2233
|
-
className: "min-w-0 flex-1 rounded outline-none focus-visible:ring-2 focus-visible:ring-info/75",
|
|
2234
|
-
children
|
|
2235
|
-
}
|
|
2236
|
-
) }) : children;
|
|
2237
|
-
var None = ({ look }) => /* @__PURE__ */ jsx22("p", { className: cn12("text-sm", secondaryText(look)), children: "None" });
|
|
2238
|
-
var MeasurementSections = ({ measurements, considerationsTitle, look, folds }) => {
|
|
2239
|
-
if (measurements.status !== "ready") {
|
|
2240
|
-
return /* @__PURE__ */ jsx22(Fragment10, { children: /* @__PURE__ */ jsx22(FoldSection, { id: "measurements", title: "Measurements", folds, children: measurements.status === "loading" ? /* @__PURE__ */ jsx22(Reading, { children: "Reading the feature\u2019s datasheet\u2026" }) : /* @__PURE__ */ jsx22(ReadFailure, { failure: measurements }) }) });
|
|
2241
|
-
}
|
|
2242
|
-
const measured = measurements.value.filter((row) => !row.milling);
|
|
2243
|
-
const milling = measurements.value.filter((row) => row.milling);
|
|
2244
|
-
return /* @__PURE__ */ jsxs19(Fragment10, { children: [
|
|
2245
|
-
/* @__PURE__ */ jsx22(FoldSection, { id: "measurements", title: "Measurements", folds, children: /* @__PURE__ */ jsx22(MeasurementList, { rows: measured, look }) }),
|
|
2246
|
-
milling.length > 0 ? /* @__PURE__ */ jsx22(FoldSection, { id: "milling", title: considerationsTitle, folds, children: /* @__PURE__ */ jsx22(MeasurementList, { rows: milling, look }) }) : null
|
|
2247
|
-
] });
|
|
2248
|
-
};
|
|
2249
|
-
var MeasurementList = ({ rows, look }) => rows.length > 0 ? /* @__PURE__ */ jsx22("dl", { className: "grid grid-cols-[1fr_auto] items-center gap-x-3 gap-y-1 text-sm", children: rows.map((row) => /* @__PURE__ */ jsxs19("div", { className: "contents", children: [
|
|
2250
|
-
/* @__PURE__ */ jsxs19("dt", { className: cn12("flex items-center gap-1", secondaryText(look)), children: [
|
|
2251
|
-
row.label,
|
|
2252
|
-
/* @__PURE__ */ jsx22(Derivation, { lines: row.derivation, look })
|
|
2253
|
-
] }),
|
|
2254
|
-
/* @__PURE__ */ jsx22("dd", { className: "text-right text-gray-600 tabular-nums dark:text-zinc-100", title: row.alt, children: row.value })
|
|
2255
|
-
] }, row.key)) }) : /* @__PURE__ */ jsx22(None, { look });
|
|
2256
|
-
var Derivation = ({
|
|
2257
|
-
lines,
|
|
2258
|
-
look
|
|
2259
|
-
}) => lines.length > 0 ? /* @__PURE__ */ jsx22(
|
|
2260
|
-
Tooltip,
|
|
2261
|
-
{
|
|
2262
|
-
side: "left",
|
|
2263
|
-
hoverable: true,
|
|
2264
|
-
tip: /* @__PURE__ */ jsx22("div", { className: "flex max-w-sm flex-col gap-0.5 font-mono text-2xs leading-snug whitespace-pre-wrap", children: lines.map((line, index) => /* @__PURE__ */ jsx22("span", { children: line }, index)) }),
|
|
2265
|
-
children: /* @__PURE__ */ jsx22(
|
|
2266
|
-
"button",
|
|
2267
|
-
{
|
|
2268
|
-
type: "button",
|
|
2269
|
-
"aria-label": "How this was measured",
|
|
2270
|
-
className: cn12(
|
|
2271
|
-
"shrink-0 rounded outline-none hover:text-gray-500 focus-visible:ring-2 focus-visible:ring-info/75 dark:hover:text-zinc-300",
|
|
2272
|
-
faintText(look)
|
|
2273
|
-
),
|
|
2274
|
-
children: /* @__PURE__ */ jsx22(InfoIcon, { weight: "bold", className: "size-3" })
|
|
2275
|
-
}
|
|
2276
|
-
)
|
|
2277
|
-
}
|
|
2278
|
-
) : null;
|
|
2279
|
-
var ReachSection2 = ({ record, units, profile, folds, action }) => {
|
|
2280
|
-
const curve = useMemo7(
|
|
2281
|
-
() => record.status === "ready" ? readReachCurve(record.value?.datasheet) : null,
|
|
2282
|
-
[record]
|
|
2283
|
-
);
|
|
2284
|
-
if (record.status === "ready" && !curve) return null;
|
|
2285
|
-
return /* @__PURE__ */ jsx22(FoldSection, { id: "reach", title: "Reach", folds, action: curve ? action : null, children: record.status === "loading" ? /* @__PURE__ */ jsx22(Reading, { children: "Reading the reach curve\u2026" }) : record.status === "error" ? /* @__PURE__ */ jsx22(ReadFailure, { failure: record }) : curve ? /* @__PURE__ */ jsx22(ReachChart, { curve, units, feature: profile }) : null });
|
|
2286
|
-
};
|
|
2287
|
-
|
|
2288
|
-
// src/feature-panel/candidate-list.tsx
|
|
2289
|
-
import { XIcon } from "@phosphor-icons/react";
|
|
2290
|
-
import { IconButton as IconButton4, cn as cn13 } from "@toolpath/ui";
|
|
2291
|
-
import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2292
|
-
var candidateListKeys = listKeys("candidates");
|
|
2293
|
-
var faceLetter = (index) => String.fromCharCode(65 + index % 26);
|
|
2294
|
-
var FaceLetter = ({
|
|
2295
|
-
index,
|
|
2296
|
-
active
|
|
2297
|
-
}) => /* @__PURE__ */ jsx23(
|
|
2298
|
-
"span",
|
|
2299
|
-
{
|
|
2300
|
-
className: cn13(
|
|
2301
|
-
"inline-flex size-4 shrink-0 items-center justify-center rounded-sm text-2xs font-bold",
|
|
2302
|
-
active ? "bg-info text-white" : "bg-gray-100 text-gray-500 dark:bg-zinc-800 dark:text-zinc-300"
|
|
2303
|
-
),
|
|
2304
|
-
children: faceLetter(index)
|
|
2305
|
-
}
|
|
2306
|
-
);
|
|
2307
|
-
var CandidateList = ({
|
|
2308
|
-
rows,
|
|
2309
|
-
selected,
|
|
2310
|
-
onSelect,
|
|
2311
|
-
onHover,
|
|
2312
|
-
face,
|
|
2313
|
-
look: lookOption,
|
|
2314
|
-
folds,
|
|
2315
|
-
vocabulary
|
|
2316
|
-
}) => {
|
|
2317
|
-
const look = resolveLook(lookOption);
|
|
2318
|
-
const count = `Candidate features (${rows.length})`;
|
|
2319
|
-
return /* @__PURE__ */ jsx23("div", { className: "border-b border-gray-100 p-2 dark:border-zinc-800", children: /* @__PURE__ */ jsx23(
|
|
2320
|
-
FoldSection,
|
|
2321
|
-
{
|
|
2322
|
-
id: face ? `candidates-${faceLetter(face.index)}` : "candidates",
|
|
2323
|
-
title: face ? /* @__PURE__ */ jsxs20("span", { className: "flex items-center gap-1.5", children: [
|
|
2324
|
-
/* @__PURE__ */ jsx23(FaceLetter, { index: face.index, active: face.active }),
|
|
2325
|
-
count
|
|
2326
|
-
] }) : count,
|
|
2327
|
-
action: face?.onStop ? /* @__PURE__ */ jsx23(
|
|
2328
|
-
IconButton4,
|
|
2329
|
-
{
|
|
2330
|
-
"aria-label": `Stop comparing face ${faceLetter(face.index)}`,
|
|
2331
|
-
variant: "muted",
|
|
2332
|
-
size: "sm",
|
|
2333
|
-
onClick: face.onStop,
|
|
2334
|
-
children: /* @__PURE__ */ jsx23(XIcon, { weight: "bold" })
|
|
2335
|
-
}
|
|
2336
|
-
) : null,
|
|
2337
|
-
folds,
|
|
2338
|
-
className: "px-2",
|
|
2339
|
-
children: /* @__PURE__ */ jsx23("ul", { className: "-mx-2 flex flex-col gap-0.5", children: rows.map((row) => /* @__PURE__ */ jsx23(
|
|
2340
|
-
Row,
|
|
2341
|
-
{
|
|
2342
|
-
row,
|
|
2343
|
-
chosen: row.feature.tag === selected,
|
|
2344
|
-
onSelect,
|
|
2345
|
-
onHover,
|
|
2346
|
-
look,
|
|
2347
|
-
vocabulary
|
|
2348
|
-
},
|
|
2349
|
-
row.feature.tag
|
|
2350
|
-
)) })
|
|
2351
|
-
}
|
|
2352
|
-
) });
|
|
2353
|
-
};
|
|
2354
|
-
var Row = ({ row, chosen, onSelect, onHover, look, vocabulary }) => {
|
|
2355
|
-
const { feature } = row;
|
|
2356
|
-
const info = look.selected === "info";
|
|
2357
|
-
return /* @__PURE__ */ jsxs20(
|
|
2358
|
-
"li",
|
|
2359
|
-
{
|
|
2360
|
-
className: cn13(
|
|
2361
|
-
"flex items-center gap-1",
|
|
2362
|
-
info && "rounded border pr-1",
|
|
2363
|
-
info && (chosen ? "border-info/60 bg-info/15" : "border-transparent")
|
|
2364
|
-
),
|
|
2365
|
-
children: [
|
|
2366
|
-
/* @__PURE__ */ jsxs20(
|
|
2367
|
-
"button",
|
|
2368
|
-
{
|
|
2369
|
-
type: "button",
|
|
2370
|
-
"data-list-item": "candidates",
|
|
2371
|
-
"data-follow": true,
|
|
2372
|
-
"aria-pressed": chosen,
|
|
2373
|
-
onClick: () => onSelect(feature.tag),
|
|
2374
|
-
onPointerEnter: onHover ? () => onHover(feature.tag) : void 0,
|
|
2375
|
-
onPointerLeave: onHover ? () => onHover(null) : void 0,
|
|
2376
|
-
className: cn13(
|
|
2377
|
-
"flex min-w-0 flex-1 cursor-pointer flex-col rounded px-2 py-1.5 text-left text-sm",
|
|
2378
|
-
"outline-none focus-visible:ring-2 focus-visible:ring-info/75",
|
|
2379
|
-
"text-gray-600 dark:text-zinc-200",
|
|
2380
|
-
info ? "hover:bg-gray-50 dark:hover:bg-zinc-800/60" : "hover:bg-gray-100 dark:hover:bg-zinc-800",
|
|
2381
|
-
chosen && (info ? "font-medium" : "bg-gray-100 font-semibold dark:bg-zinc-800")
|
|
2382
|
-
),
|
|
2383
|
-
children: [
|
|
2384
|
-
/* @__PURE__ */ jsxs20("span", { className: "flex w-full items-center gap-2", children: [
|
|
2385
|
-
row.color ? /* @__PURE__ */ jsx23(Swatch, { color: row.color }) : /* @__PURE__ */ jsx23("span", { "aria-hidden": true, className: "size-4 shrink-0" }),
|
|
2386
|
-
/* @__PURE__ */ jsx23("span", { className: "min-w-0 flex-1 truncate", children: /* @__PURE__ */ jsx23(FeatureName, { feature, label: row.label, look, vocabulary }) }),
|
|
2387
|
-
row.ruleCount ? /* @__PURE__ */ jsxs20("span", { className: cn13("text-2xs tabular-nums", secondaryText(look)), children: [
|
|
2388
|
-
row.ruleCount,
|
|
2389
|
-
" ",
|
|
2390
|
-
row.ruleCount === 1 ? "rule" : "rules"
|
|
2391
|
-
] }) : null,
|
|
2392
|
-
row.required ? /* @__PURE__ */ jsx23(RequiredBadge, {}) : null,
|
|
2393
|
-
row.detail ? /* @__PURE__ */ jsx23("span", { className: cn13("max-w-32 shrink-0 truncate text-xs", secondaryText(look)), children: row.detail }) : null
|
|
2394
|
-
] }),
|
|
2395
|
-
row.note ? /* @__PURE__ */ jsx23("span", { className: "pl-6 text-2xs text-warning-darken", children: row.note }) : null
|
|
2396
|
-
]
|
|
2397
|
-
}
|
|
2398
|
-
),
|
|
2399
|
-
row.action
|
|
2400
|
-
]
|
|
2401
|
-
}
|
|
2402
|
-
);
|
|
2403
|
-
};
|
|
2404
|
-
|
|
2405
|
-
// src/feature-panel/comparison-table.tsx
|
|
2406
|
-
import { useMemo as useMemo8 } from "react";
|
|
2407
|
-
import { cn as cn14 } from "@toolpath/ui";
|
|
2408
|
-
import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2409
|
-
var DASH = "\u2014";
|
|
2410
|
-
var ComparisonTable = ({
|
|
2411
|
-
faces,
|
|
2412
|
-
active,
|
|
2413
|
-
onSelect,
|
|
2414
|
-
onHover,
|
|
2415
|
-
look: lookOption,
|
|
2416
|
-
folds
|
|
2417
|
-
}) => {
|
|
2418
|
-
const look = resolveLook(lookOption);
|
|
2419
|
-
const rows = useMemo8(() => {
|
|
2420
|
-
const row = (key, label, read) => {
|
|
2421
|
-
const values = faces.map((face) => face.feature ? read(face, face.feature) : null);
|
|
2422
|
-
const texts = values.map((value) => value?.text ?? DASH);
|
|
2423
|
-
return {
|
|
2424
|
-
key,
|
|
2425
|
-
label,
|
|
2426
|
-
cells: values.map((value) => value?.node ?? value?.text ?? DASH),
|
|
2427
|
-
differs: new Set(texts).size > 1
|
|
2428
|
-
};
|
|
2429
|
-
};
|
|
2430
|
-
const fixed = [
|
|
2431
|
-
row("type", "Type", (_, feature) => ({ text: featureTypeLabel(feature.featureType) })),
|
|
2432
|
-
row("direction", "Machined from", (face, feature) => {
|
|
2433
|
-
const text = directionLabel(feature.machiningDirection);
|
|
2434
|
-
return {
|
|
2435
|
-
text,
|
|
2436
|
-
node: /* @__PURE__ */ jsxs21("span", { className: "inline-flex items-center gap-1", children: [
|
|
2437
|
-
/* @__PURE__ */ jsx24(Swatch, { color: face.directionColor, className: "size-2.5" }),
|
|
2438
|
-
text
|
|
2439
|
-
] })
|
|
2440
|
-
};
|
|
2441
|
-
}),
|
|
2442
|
-
...faces.some((face) => face.required !== void 0) ? [row("required", "Required", (face) => ({ text: face.required ? "Yes" : "No" }))] : [],
|
|
2443
|
-
row("rules", "Rules broken", (face) => ({
|
|
2444
|
-
text: face.rules.map((rule) => rule.key).join(",") || "None",
|
|
2445
|
-
node: face.rules.length > 0 ? /* @__PURE__ */ jsxs21(
|
|
2446
|
-
"span",
|
|
2447
|
-
{
|
|
2448
|
-
className: "inline-flex items-center gap-0.5",
|
|
2449
|
-
title: face.rules.map((rule) => rule.text).join("\n"),
|
|
2450
|
-
children: [
|
|
2451
|
-
face.rules.map((rule) => /* @__PURE__ */ jsx24(Swatch, { color: rule.color, className: "size-2.5" }, rule.key)),
|
|
2452
|
-
/* @__PURE__ */ jsx24("span", { className: "ml-0.5", children: face.rules.length })
|
|
2453
|
-
]
|
|
2454
|
-
}
|
|
2455
|
-
) : "None"
|
|
2456
|
-
}))
|
|
2457
|
-
];
|
|
2458
|
-
const labels = /* @__PURE__ */ new Map();
|
|
2459
|
-
for (const face of faces)
|
|
2460
|
-
for (const each of face.measurements) labels.set(each.key, labels.get(each.key) ?? each.label);
|
|
2461
|
-
const measured = [...labels].map(([key, label]) => {
|
|
2462
|
-
const values = faces.map((face) => face.measurements.find((each) => each.key === key));
|
|
2463
|
-
return {
|
|
2464
|
-
key,
|
|
2465
|
-
label,
|
|
2466
|
-
cells: values.map((value) => value ? /* @__PURE__ */ jsx24("span", { title: value.alt, children: value.value }) : DASH),
|
|
2467
|
-
differs: new Set(values.map((value) => value?.value ?? DASH)).size > 1
|
|
2468
|
-
};
|
|
2469
|
-
});
|
|
2470
|
-
return [...fixed, ...measured];
|
|
2471
|
-
}, [faces]);
|
|
2472
|
-
return /* @__PURE__ */ jsx24("div", { className: "border-b border-gray-100 p-4 dark:border-zinc-800", children: /* @__PURE__ */ jsx24(FoldSection, { id: "comparison", title: "Comparison", folds, children: /* @__PURE__ */ jsx24("div", { className: "-mx-1 overflow-x-auto", children: /* @__PURE__ */ jsxs21("table", { className: "w-full border-separate border-spacing-x-1 border-spacing-y-0.5 text-sm", children: [
|
|
2473
|
-
/* @__PURE__ */ jsx24("thead", { children: /* @__PURE__ */ jsxs21("tr", { children: [
|
|
2474
|
-
/* @__PURE__ */ jsx24("th", { className: "w-0" }),
|
|
2475
|
-
faces.map(({ feature }, index) => /* @__PURE__ */ jsx24("th", { className: "text-left align-bottom font-normal", children: /* @__PURE__ */ jsxs21(
|
|
2476
|
-
"button",
|
|
2477
|
-
{
|
|
2478
|
-
type: "button",
|
|
2479
|
-
disabled: !feature,
|
|
2480
|
-
onClick: () => feature && onSelect(feature.tag, index),
|
|
2481
|
-
onPointerEnter: onHover ? () => onHover(feature?.tag ?? null) : void 0,
|
|
2482
|
-
onPointerLeave: onHover ? () => onHover(null) : void 0,
|
|
2483
|
-
className: cn14(
|
|
2484
|
-
"flex w-full min-w-0 cursor-pointer items-center gap-1.5 rounded px-1 py-1 text-left text-xs",
|
|
2485
|
-
"outline-none focus-visible:ring-2 focus-visible:ring-info/75",
|
|
2486
|
-
"text-gray-600 hover:bg-gray-100 dark:text-zinc-200 dark:hover:bg-zinc-800",
|
|
2487
|
-
index === active && "bg-gray-100 font-semibold dark:bg-zinc-800"
|
|
2488
|
-
),
|
|
2489
|
-
children: [
|
|
2490
|
-
/* @__PURE__ */ jsx24(FaceLetter, { index, active: index === active }),
|
|
2491
|
-
/* @__PURE__ */ jsx24("span", { className: "min-w-0 truncate", children: feature ? /* @__PURE__ */ jsx24(FeatureName, { feature, look }) : "No feature" })
|
|
2492
|
-
]
|
|
2493
|
-
}
|
|
2494
|
-
) }, index))
|
|
2495
|
-
] }) }),
|
|
2496
|
-
/* @__PURE__ */ jsx24("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs21("tr", { "data-differs": row.differs || void 0, children: [
|
|
2497
|
-
/* @__PURE__ */ jsx24(
|
|
2498
|
-
"th",
|
|
2499
|
-
{
|
|
2500
|
-
scope: "row",
|
|
2501
|
-
className: cn14(
|
|
2502
|
-
"pr-2 text-left align-top font-normal whitespace-nowrap",
|
|
2503
|
-
secondaryText(look)
|
|
2504
|
-
),
|
|
2505
|
-
children: row.label
|
|
2506
|
-
}
|
|
2507
|
-
),
|
|
2508
|
-
row.cells.map((cell, index) => /* @__PURE__ */ jsx24(
|
|
2509
|
-
"td",
|
|
2510
|
-
{
|
|
2511
|
-
className: cn14(
|
|
2512
|
-
"px-1 align-top tabular-nums whitespace-nowrap",
|
|
2513
|
-
row.differs ? "text-gray-600 dark:text-zinc-100" : "text-gray-400 dark:text-zinc-500"
|
|
2514
|
-
),
|
|
2515
|
-
children: cell
|
|
2516
|
-
},
|
|
2517
|
-
index
|
|
2518
|
-
))
|
|
2519
|
-
] }, row.key)) })
|
|
2520
|
-
] }) }) }) });
|
|
2521
|
-
};
|
|
2522
|
-
|
|
2523
|
-
// src/feature-panel/feature-panel.tsx
|
|
2524
|
-
import { CubeIcon, XIcon as XIcon2 } from "@phosphor-icons/react";
|
|
2525
|
-
import { IconButton as IconButton5, ScrollArea as ScrollArea2, cn as cn15 } from "@toolpath/ui";
|
|
2526
|
-
import { Fragment as Fragment11, jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2527
|
-
var title = (faces, active, group) => {
|
|
2528
|
-
if (faces.length > 1) return `Comparing ${faces.length} faces`;
|
|
2529
|
-
const clicked = faces[active]?.region !== null && faces[active]?.region !== void 0;
|
|
2530
|
-
if (!clicked && group !== void 0) return `${group} identical holes`;
|
|
2531
|
-
return "Feature details";
|
|
2532
|
-
};
|
|
2533
|
-
var FeaturePanel = ({
|
|
2534
|
-
faces,
|
|
2535
|
-
active,
|
|
2536
|
-
group,
|
|
2537
|
-
comparison,
|
|
2538
|
-
onSelect,
|
|
2539
|
-
onDropFace,
|
|
2540
|
-
onHover,
|
|
2541
|
-
onClose,
|
|
2542
|
-
headerProps,
|
|
2543
|
-
className,
|
|
2544
|
-
look: lookOption,
|
|
2545
|
-
folds,
|
|
2546
|
-
vocabulary,
|
|
2547
|
-
children
|
|
2548
|
-
}) => {
|
|
2549
|
-
const look = resolveLook(lookOption);
|
|
2550
|
-
const comparing = faces.length > 1;
|
|
2551
|
-
const face = faces[active];
|
|
2552
|
-
const heading = title(faces, active, group);
|
|
2553
|
-
return /* @__PURE__ */ jsxs22(
|
|
2554
|
-
"section",
|
|
2555
|
-
{
|
|
2556
|
-
"aria-label": heading,
|
|
2557
|
-
"data-list-root": true,
|
|
2558
|
-
onKeyDown: (event) => {
|
|
2559
|
-
if (event.defaultPrevented || !event.currentTarget.contains(event.target)) return;
|
|
2560
|
-
if (event.key === "Escape") onClose();
|
|
2561
|
-
else candidateListKeys(event);
|
|
2562
|
-
},
|
|
2563
|
-
className: cn15(
|
|
2564
|
-
"flex h-full min-h-0 flex-col overflow-hidden rounded bg-white/90 shadow-lg ring-1 ring-gray-200/70 backdrop-blur-md",
|
|
2565
|
-
"dark:bg-zinc-900/90 dark:ring-zinc-800",
|
|
2566
|
-
className
|
|
2567
|
-
),
|
|
2568
|
-
children: [
|
|
2569
|
-
/* @__PURE__ */ jsxs22(
|
|
2570
|
-
"header",
|
|
2571
|
-
{
|
|
2572
|
-
...headerProps,
|
|
2573
|
-
className: cn15(
|
|
2574
|
-
"flex items-center gap-2 border-b border-gray-100 py-2 pr-2 pl-4 select-none dark:border-zinc-800",
|
|
2575
|
-
headerProps?.className
|
|
2576
|
-
),
|
|
2577
|
-
children: [
|
|
2578
|
-
/* @__PURE__ */ jsx25(CubeIcon, { weight: "bold", className: cn15("size-5 shrink-0", secondaryText(look)) }),
|
|
2579
|
-
/* @__PURE__ */ jsx25("h2", { className: "flex-1 text-sm font-semibold text-gray-600 dark:text-zinc-100", children: heading }),
|
|
2580
|
-
/* @__PURE__ */ jsx25(IconButton5, { "aria-label": "Close", variant: "muted", onClick: onClose, children: /* @__PURE__ */ jsx25(XIcon2, { weight: "bold" }) })
|
|
2581
|
-
]
|
|
2582
|
-
}
|
|
2583
|
-
),
|
|
2584
|
-
/* @__PURE__ */ jsxs22(ScrollArea2, { className: "min-h-0 flex-1", children: [
|
|
2585
|
-
comparing ? /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
2586
|
-
faces.map((each, index) => /* @__PURE__ */ jsx25(
|
|
2587
|
-
CandidateList,
|
|
2588
|
-
{
|
|
2589
|
-
rows: each.rows,
|
|
2590
|
-
selected: each.selected,
|
|
2591
|
-
onSelect: (tag) => onSelect(tag, index),
|
|
2592
|
-
onHover,
|
|
2593
|
-
face: {
|
|
2594
|
-
index,
|
|
2595
|
-
active: index === active,
|
|
2596
|
-
...onDropFace ? { onStop: () => onDropFace(index) } : {}
|
|
2597
|
-
},
|
|
2598
|
-
look: lookOption,
|
|
2599
|
-
folds,
|
|
2600
|
-
vocabulary
|
|
2601
|
-
},
|
|
2602
|
-
`${each.region ?? "list"}-${index}`
|
|
2603
|
-
)),
|
|
2604
|
-
comparison ? /* @__PURE__ */ jsx25(
|
|
2605
|
-
ComparisonTable,
|
|
2606
|
-
{
|
|
2607
|
-
faces: comparison,
|
|
2608
|
-
active,
|
|
2609
|
-
onSelect,
|
|
2610
|
-
onHover,
|
|
2611
|
-
look: lookOption,
|
|
2612
|
-
folds
|
|
2613
|
-
}
|
|
2614
|
-
) : null
|
|
2615
|
-
] }) : face && face.region !== null ? /* @__PURE__ */ jsx25(
|
|
2616
|
-
CandidateList,
|
|
2617
|
-
{
|
|
2618
|
-
rows: face.rows,
|
|
2619
|
-
selected: face.selected,
|
|
2620
|
-
onSelect: (tag) => onSelect(tag, active),
|
|
2621
|
-
onHover,
|
|
2622
|
-
look: lookOption,
|
|
2623
|
-
folds,
|
|
2624
|
-
vocabulary
|
|
2625
|
-
}
|
|
2626
|
-
) : null,
|
|
2627
|
-
face?.selected ? children : /* @__PURE__ */ jsx25("p", { className: cn15("px-4 py-3 text-sm", secondaryText(look)), children: face && face.rows.length > 0 ? "Choose a feature to read its details." : "No feature owns this face." })
|
|
2628
|
-
] })
|
|
2629
|
-
]
|
|
2630
|
-
}
|
|
2631
|
-
);
|
|
2632
|
-
};
|
|
64
|
+
withSubject,
|
|
65
|
+
withSurroundedFaces
|
|
66
|
+
} from "./chunk-XHYPKN4E.js";
|
|
2633
67
|
export {
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
FeaturePanel,
|
|
68
|
+
CRITICAL_COLOR,
|
|
69
|
+
FILE_KIND,
|
|
70
|
+
MAX_DATASHEET_FIELDS,
|
|
2638
71
|
MILLING_RULES,
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
72
|
+
OPS,
|
|
73
|
+
PINCH_TOOL_MM,
|
|
74
|
+
PRESENCE_METRIC,
|
|
75
|
+
QUICK_SUBJECTS,
|
|
76
|
+
RANGE_HIGHS,
|
|
77
|
+
RANGE_LOWS,
|
|
78
|
+
RULE_COLORS,
|
|
2643
79
|
TURNING_RULES,
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
80
|
+
WARNING_COLOR,
|
|
81
|
+
brokenRules,
|
|
82
|
+
checkPart,
|
|
83
|
+
checkTurnedPart,
|
|
84
|
+
compareColors,
|
|
85
|
+
datasheetFields,
|
|
86
|
+
describeRule,
|
|
87
|
+
dfmFeatures,
|
|
88
|
+
directionLabel,
|
|
89
|
+
featureColor,
|
|
90
|
+
featureMeasurements,
|
|
91
|
+
featureProfile,
|
|
92
|
+
featureRows,
|
|
93
|
+
featureSheet,
|
|
94
|
+
featureTypeLabel,
|
|
95
|
+
formatMeasured,
|
|
96
|
+
fromField,
|
|
97
|
+
highOf,
|
|
98
|
+
hitFigure,
|
|
99
|
+
isNoReading,
|
|
100
|
+
isSurfaceType,
|
|
101
|
+
lengthValue,
|
|
102
|
+
llmRulePrompt,
|
|
103
|
+
lowOf,
|
|
104
|
+
makeRule,
|
|
105
|
+
maxBallDiameter,
|
|
106
|
+
metricInfo,
|
|
107
|
+
metricsForSubject,
|
|
108
|
+
opSymbol,
|
|
109
|
+
parseRuleSetFile,
|
|
110
|
+
parseRules,
|
|
111
|
+
peakHeight,
|
|
112
|
+
pinchLabel,
|
|
113
|
+
pinchMark,
|
|
114
|
+
placePinchTool,
|
|
115
|
+
quantityUnit,
|
|
116
|
+
readPinch,
|
|
117
|
+
readReachCurve,
|
|
118
|
+
runStart,
|
|
119
|
+
sameDepth,
|
|
120
|
+
serializeRuleSet,
|
|
121
|
+
subjectLabel,
|
|
122
|
+
toField,
|
|
123
|
+
trimmedCurve,
|
|
124
|
+
turnedStretches,
|
|
125
|
+
turnedTypeLabel,
|
|
126
|
+
unitLabel,
|
|
127
|
+
upperOf,
|
|
128
|
+
withMetric,
|
|
129
|
+
withOp,
|
|
130
|
+
withSubject,
|
|
131
|
+
withSurroundedFaces
|
|
2649
132
|
};
|