@toolpath/dfm 0.1.0 → 0.2.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 CHANGED
@@ -9,6 +9,12 @@ as one JSON file.
9
9
  It also draws a feature's reach chart: the walls beside the feature, how high
10
10
  they stand and how far out.
11
11
 
12
+ And it shows a feature as the Engine read it — the feature panel: the features
13
+ a clicked face could mean, the rules the chosen one breaks, what was measured
14
+ of it, its reach, and its datasheet as sent. Its pinch points, the widest tool
15
+ standing where the feature is tightest, are placed here and drawn by
16
+ `@toolpath/viewer`.
17
+
12
18
  The rules and the checker that counts them ship too, with no React, under
13
19
  `@toolpath/dfm/model`.
14
20
 
@@ -28,15 +34,16 @@ Add the styles after Tailwind and the `@toolpath/ui` theme:
28
34
 
29
35
  ## Exports
30
36
 
31
- | Entry point | What it is |
32
- | ----------------------- | -------------------------------------------------------------------------------------------- |
33
- | `@toolpath/dfm` | `RuleList`, `Swatch`, the list's keyboard helpers, `ReachChart`, `ReachDrawing`, their types |
34
- | `@toolpath/dfm/model` | The rules, `checkPart`, the rule-set file — no React, no DOM |
35
- | `@toolpath/dfm/dfm.css` | Lets the app's Tailwind see the list's and chart's classes |
37
+ | Entry point | What it is |
38
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
39
+ | `@toolpath/dfm` | `RuleList`, `Swatch`, the list's keyboard helpers, `ReachChart`, `ReachDrawing`, the feature panel (`FeaturePanel`, `FeatureDetails`, `CandidateList`, `ComparisonTable`, `storageFolds`), their types |
40
+ | `@toolpath/dfm/model` | The rules, `checkPart`, the rule-set file, the feature panel's rows, pinch points — no React, no DOM |
41
+ | `@toolpath/dfm/dfm.css` | Lets the app's Tailwind see the list's and chart's classes |
36
42
 
37
43
  ## The rules are an input
38
44
 
39
- The package ships no default rules and stores nothing. The app keeps the rules
45
+ The package ships no default rules and stores nothing — not the rules, nor
46
+ which of the feature panel's sections are open. The app keeps the rules
40
47
  — in memory, `localStorage`, a server — and passes them in with the functions
41
48
  that change them:
42
49
 
@@ -96,3 +103,112 @@ height; `ReachDrawing` says so in its note.
96
103
  This is not the clearance overlay in `@toolpath/tool-drawing/clearance`: that
97
104
  draws the walls beside a cutting tool, to show whether the tool fits. This
98
105
  draws the walls alone.
106
+
107
+ ## Feature panel
108
+
109
+ `FeaturePanel` is the card a click on the part opens: every feature the face
110
+ belongs to, then the details of the one being read. With several faces
111
+ shift-clicked, each face's candidates sit under a letter, above a table of
112
+ what each was read as. It fills what it is put in — where it sits, and
113
+ whether it can be dragged, is the app's — and the app decides what a click
114
+ means: which faces, which candidates in which order, which is read.
115
+
116
+ ```tsx
117
+ import { FeatureDetails, FeaturePanel, storageFolds } from '@toolpath/dfm'
118
+ import { brokenRules, featureMeasurements, featureProfile } from '@toolpath/dfm/model'
119
+
120
+ const folds = storageFolds(localStorage, 'toolpath.fold.')
121
+
122
+ export const Inspector = () => (
123
+ <FeaturePanel
124
+ faces={[{ region, rows, selected }]}
125
+ active={0}
126
+ onSelect={(tag) => setSelected(tag)}
127
+ onHover={setHovered}
128
+ onClose={close}
129
+ folds={folds}
130
+ >
131
+ <FeatureDetails
132
+ feature={feature}
133
+ units="mm"
134
+ directionColor={directionHex}
135
+ profile={featureProfile(sheets[key], feature.featureType)}
136
+ required={check.required.has(key)}
137
+ onFrame={() => frame(feature.tag)}
138
+ rules={brokenRules(check, feature.tag, 'mm')}
139
+ measurements={{
140
+ status: 'ready',
141
+ value: featureMeasurements({ features, feature, sheets, units: 'mm' }),
142
+ }}
143
+ record={{ status: 'ready', value: records[key] ?? null }}
144
+ PopOut={AppWindow}
145
+ folds={folds}
146
+ />
147
+ </FeaturePanel>
148
+ )
149
+ ```
150
+
151
+ The parts work alone too. An app with an inspect panel of its own puts
152
+ `CandidateList` and `FeatureDetails` in it and fills their slots with its own
153
+ controls — a folder in a row's `detail`, a Pin button in its `action`, Zoom to
154
+ in the details' `actions`, a folder badge in `status`.
155
+
156
+ - **Rules broken** are `BrokenRule` rows: a colour, the rule's text, the figure
157
+ that broke it, and optionally the limit and a note on hover. `brokenRules`
158
+ builds them from this package's check; an app with a checker of its own
159
+ builds them from its hits.
160
+ - **Measurements** are `featureMeasurements` rows, each with how it was worked
161
+ out behind an ⓘ. `inchMark` writes inches as `0.46"`.
162
+ - **Reach, the datasheet fields and the raw record** read the feature's
163
+ `FeatureRecord`: its report entry and datasheet as the API sent them.
164
+ - **Reads take time.** `measurements` and `record` are `Loadable`. While
165
+ `loading`, a section says what it is reading; on `error` it shows the app's
166
+ message and a Retry, held until `retryAt`.
167
+ - **The look** differs where apps want it to: `look.rules`, `look.selected` and
168
+ `look.muted`. Left out, it is the CAD viewer's.
169
+ - **Folds** are kept in the app's `FoldStore`; `storageFolds(storage, prefix)`
170
+ builds one over a `Storage`. Without one, a section remembers only while it
171
+ is mounted.
172
+ - **Pop-outs** need `PopOut`, the app's window component: the panel decides
173
+ what is open and fills it, and the app draws the frame. Define it at module
174
+ level, and render it through a portal to the page's body: the panel's blur
175
+ makes it the box a `fixed` window inside it would be placed in.
176
+ - **Keys:** Escape closes the panel and the arrow keys walk the candidates. On
177
+ a `CandidateList` used alone, put `candidateListKeys` on whatever holds it.
178
+
179
+ ## Pinch points
180
+
181
+ The widest tool a feature admits, standing where the feature is tightest. The
182
+ data is here; the drawing is `@toolpath/viewer`'s `<ToolMarks>`, which knows
183
+ nothing of datasheets:
184
+
185
+ ```tsx
186
+ import { useMemo } from 'react'
187
+ import { pinchLabel, pinchMark, placePinchTool } from '@toolpath/dfm/model'
188
+ import { featureTriangles, ToolMarks, usePartContext } from '@toolpath/viewer'
189
+
190
+ // A child of <EnginePart> or <PartMesh>, which gives it the part.
191
+ export const PinchPoints = ({ datasheet, sheet, feature, units, shown }) => {
192
+ const { model, geometry } = usePartContext()
193
+ // Placing the tool searches the feature's faces: once per feature, not once per render.
194
+ const marks = useMemo(() => {
195
+ const mark = pinchMark(datasheet, sheet, feature.featureType)
196
+ const triangles = featureTriangles(model, geometry, feature.tag)
197
+ const tool = mark ? placePinchTool(mark, triangles, feature.machiningDirection) : null
198
+ return tool && mark ? [{ ...tool, ...pinchLabel(mark, units) }] : []
199
+ }, [datasheet, sheet, feature, model, geometry, units])
200
+ return <ToolMarks marks={marks} visible={shown} />
201
+ }
202
+ ```
203
+
204
+ `placePinchTool` finds the datasheet's tool frame against the feature's faces —
205
+ the API gives its discs across the tool without saying which way x and y lie —
206
+ and stands the tool on the tightest disc, turned toward the wall that pinches
207
+ it. If the app moved the geometry to centre it, pass `origin`: where the CAD
208
+ file's zero now sits.
209
+
210
+ Show and hide it from the viewer's toolbar — `ViewerToolbar.ToolsButton` on a
211
+ `tools` control — with the state held by the app; start it shown. The tool is
212
+ an end mill made from its diameter and corner; to draw it as the app's other
213
+ tools are drawn, give the mark a `profile` from `@toolpath/tool-drawing`'s
214
+ outline of a flat or bull nose end mill that size.
package/dfm.css CHANGED
@@ -1,6 +1,7 @@
1
1
  /*
2
- * Lets the app's Tailwind see the classes the rule list uses. Import it after
3
- * Tailwind and `@toolpath/ui/theme.css`, whose colours the list is drawn in:
2
+ * Lets the app's Tailwind see the classes the rule list and the feature panel
3
+ * use. Import it after Tailwind and `@toolpath/ui/theme.css`, whose colours
4
+ * they are drawn in:
4
5
  *
5
6
  * @import 'tailwindcss';
6
7
  * @import '@toolpath/ui/theme.css';
@@ -412,4 +412,92 @@ interface FeatureProfile {
412
412
  }
413
413
  declare const featureProfile: (sheet: FeatureSheet | undefined, featureType: string) => FeatureProfile;
414
414
 
415
- export { featureTypeLabel as A, isNoReading as B, CRITICAL_COLOR as C, type DfmRule as D, makeRule as E, type FeatureSheets as F, parseRules as G, peakHeight as H, readPinch as I, readReachCurve as J, runStart as K, trimmedCurve as L, withSurroundedFaces as M, PINCH_TOOL_MM as P, type ReachCurve as R, type Units as U, WARNING_COLOR as W, type DfmCheck as a, type DfmFeature as b, type FeatureProfile as c, type DfmHit as d, type FeatureSheet as e, type ClearanceFit as f, type DfmMetric as g, type DfmOp as h, type DfmQuantity as i, type DfmSources as j, type FactsKind as k, type FeatureAcross as l, type FeaturePinch as m, type FeatureThreading as n, type PinchDisc as o, RULE_COLORS as p, type RangeHigh as q, type RangeLow as r, type ReportFeature as s, type RuleResult as t, checkPart as u, compareColors as v, describeRule as w, dfmFeatures as x, featureProfile as y, featureSheet as z };
415
+ /**
416
+ * A rule a feature breaks, as a row of its details says it: a coloured dot,
417
+ * the rule, and the figure that broke it. Neutral, so an app with rules of its
418
+ * own — the quoting app's, by severity — fills it from those, and an app on
419
+ * this package's checker fills it with {@link brokenRules}.
420
+ */
421
+ interface BrokenRule {
422
+ /** Unique among one feature's rows: the rule's id. */
423
+ readonly key: string;
424
+ /** The dot: the rule's colour, or the app's colour for its severity. */
425
+ readonly color: string;
426
+ /** The rule: `Holes with L/D to top of part ≥ 8`, or a short label where `limit` says the rest. */
427
+ readonly text: string;
428
+ /** What the feature measured: `9.8`. Absent for a yes/no. */
429
+ readonly figure?: string;
430
+ /** The line it crossed, where `text` does not already say it: `≥ 8`. */
431
+ readonly limit?: string;
432
+ /** On hover: the rule written out, and why it matters. */
433
+ readonly note?: string;
434
+ }
435
+ /** The rules a feature breaks, as its details list them: one a measure, the worst, palette first. */
436
+ declare const brokenRules: (check: DfmCheck, tag: string, units: Units) => BrokenRule[];
437
+ /**
438
+ * The colour a feature is painted on the part: the one earliest in the
439
+ * palette of the rules it breaks, for a required feature. Read from a face
440
+ * painted alone (`region`), that face's colour where it is painted for this
441
+ * feature; a feature painted whole, its colour. Undefined otherwise.
442
+ */
443
+ declare const featureColor: (check: DfmCheck, tag: string, region?: number | null) => string | undefined;
444
+
445
+ /** One row of a feature's details. */
446
+ interface Measurement {
447
+ /** Stable, so a list can key its rows. */
448
+ readonly key: string;
449
+ readonly label: string;
450
+ readonly value: string;
451
+ /** The same number in the other unit. Absent on a ratio or a count. */
452
+ readonly alt?: string;
453
+ /**
454
+ * How it was worked out from the datasheet, a line at a time: the fields it
455
+ * reads, by their path in the raw record, and the arithmetic, in the
456
+ * Engine's millimetres.
457
+ */
458
+ readonly derivation: readonly string[];
459
+ /**
460
+ * A machining consideration rather than a measurement of the feature's own
461
+ * shape: the tool that cuts it, and how far it has to reach. The panel lists
462
+ * these under their own heading, for every kind of feature.
463
+ */
464
+ readonly milling?: true;
465
+ /** A row that stands for a tool the viewer can draw on the part, by its kind. */
466
+ readonly tool?: SurfaceToolKind;
467
+ }
468
+ /** Which of a surface's finishing tools: a ball nose, or a bull nose with a flat between its corners. */
469
+ type SurfaceToolKind = 'ball' | 'bull';
470
+ /** Whether a feature type is read as a surface: see `SURFACE_TYPES`. */
471
+ declare const isSurfaceType: (featureType: string) => boolean;
472
+ /**
473
+ * The biggest ball nose a surface admits, mm: a ball's corner radius is half
474
+ * its diameter, so it is held under the tool fit's diameter and corner radius
475
+ * at once. None where the surface reports no fit, or admits no corner at all.
476
+ */
477
+ declare const maxBallDiameter: (sheet: Pick<FeatureSheet, "fitToolDiameter" | "fitCornerRadius">) => number | undefined;
478
+ /**
479
+ * The rows a feature gets, in {@link ROW_ORDER}: its own geometry first, then
480
+ * where it sits in the part and the reach that makes, then the tools a
481
+ * surface admits. A row is left out rather than shown empty: "—" against a
482
+ * field this kind never reports reads as a measurement that failed.
483
+ */
484
+ declare const featureMeasurements: ({ features, feature, sheets, units, inchMark, }: {
485
+ features: readonly DfmFeature[];
486
+ feature: DfmFeature;
487
+ sheets: FeatureSheets;
488
+ units: Units;
489
+ /** Writes inches as `0.46"` rather than `0.46 in`. */
490
+ inchMark?: boolean;
491
+ }) => Measurement[];
492
+
493
+ /**
494
+ * Everything the API said about one feature: its entry in the part report and
495
+ * its datasheet, both as sent, so fields newer than any SDK are still there.
496
+ */
497
+ interface FeatureRecord {
498
+ readonly feature: unknown;
499
+ /** Null where the API sent no datasheet for the feature. */
500
+ readonly datasheet: unknown;
501
+ }
502
+
503
+ export { featureColor as A, type BrokenRule as B, CRITICAL_COLOR as C, type DfmRule as D, featureMeasurements as E, type FeatureSheets as F, featureProfile as G, featureSheet as H, featureTypeLabel as I, isNoReading as J, isSurfaceType as K, makeRule as L, type Measurement as M, maxBallDiameter as N, parseRules as O, PINCH_TOOL_MM as P, peakHeight as Q, type ReachCurve as R, type SurfaceToolKind as S, readPinch as T, type Units as U, readReachCurve as V, WARNING_COLOR as W, runStart as X, trimmedCurve as Y, withSurroundedFaces as Z, type DfmCheck as a, type DfmFeature as b, type FeatureProfile as c, type FeatureRecord as d, type DfmHit as e, type FeaturePinch as f, type FeatureSheet as g, type ClearanceFit as h, type DfmMetric as i, type DfmOp as j, type DfmQuantity as k, type DfmSources as l, type FactsKind as m, type FeatureAcross as n, type FeatureThreading as o, type PinchDisc as p, RULE_COLORS as q, type RangeHigh as r, type RangeLow as s, type ReportFeature as t, type RuleResult as u, brokenRules as v, checkPart as w, compareColors as x, describeRule as y, dfmFeatures as z };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { FC, KeyboardEvent, Ref } from 'react';
3
- import { D as DfmRule, a as DfmCheck, b as DfmFeature, F as FeatureSheets, U as Units, R as ReachCurve, c as FeatureProfile } from './reach-CfmYU_yQ.js';
2
+ import { FC, KeyboardEvent, Ref, ComponentType, ReactNode, ReactElement, HTMLAttributes } from 'react';
3
+ import { D as DfmRule, a as DfmCheck, b as DfmFeature, F as FeatureSheets, U as Units, R as ReachCurve, c as FeatureProfile, B as BrokenRule, M as Measurement, d as FeatureRecord } from './feature-record-BUoFFjN6.js';
4
4
 
5
5
  /**
6
6
  * The rules the list shows, and how to change them. The app owns them —
@@ -114,4 +114,255 @@ declare const ReachChart: FC<ReachChartProps>;
114
114
  /** A larger window's contents: the section, large, with what it is and how to read it. */
115
115
  declare const ReachDrawing: FC<ReachChartProps>;
116
116
 
117
- export { type DfmRules, type Hovered, ReachChart, type ReachChartProps, ReachDrawing, RuleList, type RuleListProps, Swatch, type SwatchProps, isField, listKeys, ruleListKeys };
117
+ /**
118
+ * Where the feature panel keeps which of its sections are open, so a section
119
+ * put away stays away from one feature to the next, and from one visit to the
120
+ * next. The app supplies it: the package stores nothing of its own.
121
+ *
122
+ * Sections are named by fixed ids — `rules`, `measurements`, `milling`,
123
+ * `reach`, `datasheet`, `raw`, `comparison`, `candidates`, and `candidates-A`,
124
+ * `candidates-B`… while comparing faces.
125
+ */
126
+ interface FoldStore {
127
+ /** Whether a section was left open; undefined for one never folded, which then opens as it starts. */
128
+ read: (id: string) => boolean | undefined;
129
+ write: (id: string, open: boolean) => void;
130
+ }
131
+ /**
132
+ * A fold store over a `Storage` — `localStorage`, `sessionStorage` — under a
133
+ * key prefix of the app's: `toolpath.fold.` keeps `toolpath.fold.reach` as
134
+ * `open` or `shut`. A storage that is missing, or that refuses (a private
135
+ * window, say), leaves the sections folding for as long as they are mounted.
136
+ */
137
+ declare const storageFolds: (storage: Pick<Storage, "getItem" | "setItem"> | null | undefined, prefix: string) => FoldStore;
138
+
139
+ /**
140
+ * How the feature panel looks where two apps want it to differ. Every field
141
+ * left out takes the first value, which is the CAD viewer's look.
142
+ */
143
+ interface FeaturePanelLook {
144
+ /**
145
+ * A broken rule: `sentence` — the rule written out, the figure that broke it
146
+ * in grey; or `result` — a short label, the figure in the rule's colour, and
147
+ * the line it crossed, faded.
148
+ */
149
+ rules?: 'sentence' | 'result';
150
+ /** The chosen candidate: `grey` and bold, or an `info` blue border and tint. */
151
+ selected?: 'grey' | 'info';
152
+ /** Secondary text: `pale` grey, or a `strong` grey that reads at full contrast. */
153
+ muted?: 'pale' | 'strong';
154
+ }
155
+
156
+ /** What the panel needs to name a feature: its tag, its type, and the way it is machined from. */
157
+ type FeatureIdentity = Pick<DfmFeature, 'tag' | 'featureType' | 'machiningDirection'>;
158
+ /**
159
+ * Something the app reads for the panel, and how far it has got: the panel
160
+ * says what it is waiting on, or what went wrong, in place of the section.
161
+ */
162
+ type Loadable<T> = {
163
+ readonly status: 'loading';
164
+ } | {
165
+ readonly status: 'error';
166
+ /** In the app's own words: the API's answer, or that it asked to wait. */
167
+ readonly message: string;
168
+ /** Reads it again; left out, there is no Retry. */
169
+ readonly retry?: () => void;
170
+ /** No Retry before this, in `Date.now()` milliseconds: what a 429's `Retry-After` asks. */
171
+ readonly retryAt?: number;
172
+ } | {
173
+ readonly status: 'ready';
174
+ readonly value: T;
175
+ };
176
+ /** Which of a feature's sections pops out into a window of its own. */
177
+ type PopOutId = 'reach' | 'datasheet' | 'raw';
178
+ /**
179
+ * The app's window for a section popped out of the panel. The panel decides
180
+ * when one is open and what is in it; the app draws the frame — moved and
181
+ * sized as its page allows — and calls `onClose`. Define it once, at module
182
+ * level: one written inline remounts the window on every render. Render it
183
+ * through a portal to the page's body: the panel blurs what is behind it,
184
+ * which makes the panel the box a `fixed` window inside it is placed in.
185
+ */
186
+ interface PopOutWindowProps {
187
+ id: PopOutId;
188
+ title: string;
189
+ /** Under the title: the feature it is of. */
190
+ subtitle: ReactNode;
191
+ icon: ReactElement;
192
+ /** In the header, before the close button: a copy button, say. */
193
+ action?: ReactNode;
194
+ onClose: () => void;
195
+ children: ReactNode;
196
+ }
197
+ type PopOutWindow = ComponentType<PopOutWindowProps>;
198
+
199
+ interface FeatureDetailsProps {
200
+ feature: FeatureIdentity;
201
+ /** Lengths in the reach chart; every other figure comes in written. */
202
+ units: Units;
203
+ /** The colour of the feature's machining direction, as the part shows it: any CSS colour. */
204
+ directionColor: string;
205
+ /** The feature beside its walls, for the reach chart: `featureProfile` of its sheet. */
206
+ profile: FeatureProfile;
207
+ /** In place of the feature's type: `Through hole ⌀ 6.35`. */
208
+ label?: ReactNode;
209
+ /** A feature the plan cannot do without: shows the REQUIRED pill. Left out, no pill. */
210
+ required?: boolean;
211
+ /** Turns the camera to the feature: shows a "Show this feature" button. */
212
+ onFrame?: () => void;
213
+ /** Right of the name: the app's own buttons. */
214
+ actions?: ReactNode;
215
+ /** At the end of the "Machined from" line: a folder badge, say. */
216
+ status?: ReactNode;
217
+ /** Under the title, before the sections: a sentence of the app's, its own action. */
218
+ children?: ReactNode;
219
+ /** The rules it breaks, as rows: see `brokenRules`. */
220
+ rules: readonly BrokenRule[];
221
+ /** Its measurements and milling considerations: `featureMeasurements`. */
222
+ measurements: Loadable<readonly Measurement[]>;
223
+ /** Its raw report entry and datasheet, for the reach chart. Null where the app has none. */
224
+ record: Loadable<FeatureRecord | null>;
225
+ /**
226
+ * The app's window, to pop the reach, the datasheet fields and the raw
227
+ * record out into, larger. Left out, there are no pop-out buttons.
228
+ */
229
+ PopOut?: PopOutWindow;
230
+ look?: FeaturePanelLook;
231
+ folds?: FoldStore;
232
+ }
233
+ /**
234
+ * One feature, as the Engine read it: its name and the way it is machined
235
+ * from, then the rules it breaks, what was measured of it, what that asks of
236
+ * the milling, and its reach.
237
+ *
238
+ * Then, shut until opened, every field of its datasheet and its raw API record,
239
+ * to read or copy.
240
+ *
241
+ * Each section folds away under its heading. A section waiting on something
242
+ * the app reads says so, and one whose read failed says why, with a Retry.
243
+ */
244
+ declare const FeatureDetails: FC<FeatureDetailsProps>;
245
+
246
+ /**
247
+ * Up, down, Home and End through the candidates: put them on whatever holds
248
+ * the list — the whole panel, or just the list — as `ruleListKeys` is put on
249
+ * the rules. Each row is chosen as it is reached.
250
+ */
251
+ declare const candidateListKeys: (event: react.KeyboardEvent<HTMLElement>) => void;
252
+ /** One feature a face could have meant, as its row says it. */
253
+ interface CandidateRow {
254
+ readonly feature: FeatureIdentity;
255
+ /** In place of the feature's type: `Blind hole ⌀ 6.35 × 4`. */
256
+ readonly label?: ReactNode;
257
+ /** The dot before it: the colour it is painted, or its direction's. Left out, a gap keeps the rows aligned. */
258
+ readonly color?: string;
259
+ /** How many rules it breaks: `2 rules`. */
260
+ readonly ruleCount?: number;
261
+ readonly required?: boolean;
262
+ /** Right-aligned in the row: the folder it is in, say. */
263
+ readonly detail?: ReactNode;
264
+ /** A second line under it: a note on how it overlaps another. */
265
+ readonly note?: ReactNode;
266
+ /** Beside the row, outside its button: the app's own action on it. */
267
+ readonly action?: ReactNode;
268
+ }
269
+ /** When comparing faces, which face this list is: its letter, and how to stop comparing it. */
270
+ interface ComparedFaceList {
271
+ readonly index: number;
272
+ /** The face being read: its letter is filled. */
273
+ readonly active: boolean;
274
+ readonly onStop?: () => void;
275
+ }
276
+ interface CandidateListProps {
277
+ /** In the app's order: the first is what a click meant first. */
278
+ rows: readonly CandidateRow[];
279
+ selected: string | null;
280
+ onSelect: (tag: string) => void;
281
+ /** A row under the pointer, and leaving it: left out, the rows preview nothing. */
282
+ onHover?: (tag: string | null) => void;
283
+ /** While comparing faces: this list's face. */
284
+ face?: ComparedFaceList;
285
+ look?: FeaturePanelLook;
286
+ folds?: FoldStore;
287
+ }
288
+ /**
289
+ * Every feature a clicked face belongs to, as rows to choose between: its
290
+ * colour, its name and the way it is machined from, how many rules it breaks,
291
+ * whether it is required, and whatever the app adds. The keys walk them, each
292
+ * chosen as it is reached; see {@link candidateListKeys}.
293
+ */
294
+ declare const CandidateList: FC<CandidateListProps>;
295
+
296
+ /** One compared face: the feature it is read as, and what that feature is and measures. */
297
+ interface ComparedFace {
298
+ /** Null for a face no feature owns. */
299
+ readonly feature: FeatureIdentity | null;
300
+ /** Its machining direction's colour, as the part shows it. */
301
+ readonly directionColor: string;
302
+ /** Whether it is required. Left out on every face, the Required row goes. */
303
+ readonly required?: boolean;
304
+ readonly rules: readonly BrokenRule[];
305
+ readonly measurements: readonly Measurement[];
306
+ }
307
+ interface ComparisonTableProps {
308
+ faces: readonly ComparedFace[];
309
+ /** The face being read, whose column head is marked. */
310
+ active: number;
311
+ /** A column head chosen: that face's feature, to read. */
312
+ onSelect: (tag: string, face: number) => void;
313
+ onHover?: (tag: string | null) => void;
314
+ look?: FeaturePanelLook;
315
+ folds?: FoldStore;
316
+ }
317
+ /**
318
+ * What each compared face was read as, side by side: a column per face, its
319
+ * feature at the head, and a row for each thing any of them has — its type,
320
+ * the way it is machined from, whether it is required, the rules it breaks,
321
+ * then every measurement. Rows where the readings disagree stand out; the rest
322
+ * are greyed.
323
+ */
324
+ declare const ComparisonTable: FC<ComparisonTableProps>;
325
+
326
+ /** One face being inspected: the features it could mean, and the one being read. */
327
+ interface InspectedFace {
328
+ /** The face clicked; null for a feature chosen from a list, which has no candidates to show. */
329
+ readonly region: number | null;
330
+ /** In the app's order: see `CandidateList`. */
331
+ readonly rows: readonly CandidateRow[];
332
+ readonly selected: string | null;
333
+ }
334
+ interface FeaturePanelProps {
335
+ /** One face, or several while comparing. */
336
+ faces: readonly InspectedFace[];
337
+ /** Which face's feature the details are of. */
338
+ active: number;
339
+ /** A group of identical holes chosen together: how many, for the title. */
340
+ group?: number;
341
+ /** What each compared face was read as: the comparison table, while comparing. */
342
+ comparison?: readonly ComparedFace[];
343
+ onSelect: (tag: string, face: number) => void;
344
+ /** Takes a face out of the comparison. */
345
+ onDropFace?: (face: number) => void;
346
+ onHover?: (tag: string | null) => void;
347
+ onClose: () => void;
348
+ /** Spread on the header: an app's drag handle, say. */
349
+ headerProps?: HTMLAttributes<HTMLElement>;
350
+ className?: string;
351
+ look?: FeaturePanelLook;
352
+ folds?: FoldStore;
353
+ /** The active face's feature: its `<FeatureDetails>`. */
354
+ children?: ReactNode;
355
+ }
356
+ /**
357
+ * The feature panel as a card: what a click on the part found — every feature
358
+ * the face belongs to, then the details of the one being read — or, with
359
+ * several faces shift-clicked, each face's candidates under a letter and a
360
+ * comparison of what each was read as.
361
+ *
362
+ * It fills what it is put in: where the card sits, and whether it moves, is
363
+ * the app's. Escape calls `onClose`, and the arrow keys walk the candidates
364
+ * from anywhere in it.
365
+ */
366
+ declare const FeaturePanel: FC<FeaturePanelProps>;
367
+
368
+ export { CandidateList, type CandidateListProps, type CandidateRow, type ComparedFace, type ComparedFaceList, ComparisonTable, type ComparisonTableProps, type DfmRules, FeatureDetails, type FeatureDetailsProps, type FeatureIdentity, FeaturePanel, type FeaturePanelLook, type FeaturePanelProps, type FoldStore, type Hovered, type InspectedFace, type Loadable, type PopOutId, type PopOutWindow, type PopOutWindowProps, ReachChart, type ReachChartProps, ReachDrawing, RuleList, type RuleListProps, Swatch, type SwatchProps, candidateListKeys, isField, listKeys, ruleListKeys, storageFolds };