@agent-native/toolkit 0.11.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +19 -0
  2. package/agent-native.eject.json +38 -0
  3. package/dist/canvas-annotations/CanvasCommentPins.d.ts +68 -0
  4. package/dist/canvas-annotations/CanvasCommentPins.d.ts.map +1 -0
  5. package/dist/canvas-annotations/CanvasCommentPins.js +513 -0
  6. package/dist/canvas-annotations/CanvasCommentPins.js.map +1 -0
  7. package/dist/canvas-annotations/DrawOverlay.d.ts +108 -0
  8. package/dist/canvas-annotations/DrawOverlay.d.ts.map +1 -0
  9. package/dist/canvas-annotations/DrawOverlay.js +746 -0
  10. package/dist/canvas-annotations/DrawOverlay.js.map +1 -0
  11. package/dist/canvas-annotations/DrawOverlay.spec.d.ts +2 -0
  12. package/dist/canvas-annotations/DrawOverlay.spec.d.ts.map +1 -0
  13. package/dist/canvas-annotations/DrawOverlay.spec.js +111 -0
  14. package/dist/canvas-annotations/DrawOverlay.spec.js.map +1 -0
  15. package/dist/canvas-annotations/index.d.ts +4 -0
  16. package/dist/canvas-annotations/index.d.ts.map +1 -0
  17. package/dist/canvas-annotations/index.js +3 -0
  18. package/dist/canvas-annotations/index.js.map +1 -0
  19. package/dist/canvas-annotations/types.d.ts +8 -0
  20. package/dist/canvas-annotations/types.d.ts.map +1 -0
  21. package/dist/canvas-annotations/types.js +2 -0
  22. package/dist/canvas-annotations/types.js.map +1 -0
  23. package/dist/canvas-interactions/canvas-interactions.d.ts +334 -0
  24. package/dist/canvas-interactions/canvas-interactions.d.ts.map +1 -0
  25. package/dist/canvas-interactions/canvas-interactions.js +451 -0
  26. package/dist/canvas-interactions/canvas-interactions.js.map +1 -0
  27. package/dist/canvas-interactions/canvas-interactions.spec.d.ts +2 -0
  28. package/dist/canvas-interactions/canvas-interactions.spec.d.ts.map +1 -0
  29. package/dist/canvas-interactions/canvas-interactions.spec.js +538 -0
  30. package/dist/canvas-interactions/canvas-interactions.spec.js.map +1 -0
  31. package/dist/canvas-interactions/index.d.ts +2 -0
  32. package/dist/canvas-interactions/index.d.ts.map +1 -0
  33. package/dist/canvas-interactions/index.js +2 -0
  34. package/dist/canvas-interactions/index.js.map +1 -0
  35. package/dist/design-tweaks/scrub-input-utils.d.ts +61 -0
  36. package/dist/design-tweaks/scrub-input-utils.d.ts.map +1 -0
  37. package/dist/design-tweaks/scrub-input-utils.js +235 -0
  38. package/dist/design-tweaks/scrub-input-utils.js.map +1 -0
  39. package/dist/design-tweaks/scrub-input.d.ts +66 -0
  40. package/dist/design-tweaks/scrub-input.d.ts.map +1 -0
  41. package/dist/design-tweaks/scrub-input.js +313 -0
  42. package/dist/design-tweaks/scrub-input.js.map +1 -0
  43. package/dist/design-tweaks/visual-style-controls.d.ts +2 -12
  44. package/dist/design-tweaks/visual-style-controls.d.ts.map +1 -1
  45. package/dist/design-tweaks/visual-style-controls.js +3 -107
  46. package/dist/design-tweaks/visual-style-controls.js.map +1 -1
  47. package/dist/design-tweaks/visual-style-controls.spec.js +26 -0
  48. package/dist/design-tweaks/visual-style-controls.spec.js.map +1 -1
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -0
  52. package/dist/index.js.map +1 -1
  53. package/package.json +29 -1
  54. package/src/canvas-annotations/CanvasCommentPins.tsx +861 -0
  55. package/src/canvas-annotations/DrawOverlay.spec.tsx +145 -0
  56. package/src/canvas-annotations/DrawOverlay.tsx +1233 -0
  57. package/src/canvas-annotations/index.ts +15 -0
  58. package/src/canvas-annotations/types.ts +14 -0
  59. package/src/canvas-interactions/canvas-interactions.spec.ts +688 -0
  60. package/src/canvas-interactions/canvas-interactions.ts +933 -0
  61. package/src/canvas-interactions/index.ts +67 -0
  62. package/src/design-tweaks/scrub-input-utils.ts +311 -0
  63. package/src/design-tweaks/scrub-input.tsx +491 -0
  64. package/src/design-tweaks/visual-style-controls.spec.tsx +60 -0
  65. package/src/design-tweaks/visual-style-controls.tsx +23 -168
  66. package/src/index.ts +1 -0
@@ -0,0 +1,61 @@
1
+ export interface ScrubExpressionOptions {
2
+ unit?: string;
3
+ min?: number;
4
+ max?: number;
5
+ precision?: number;
6
+ }
7
+ /** Cumulative pointer movement (px) required before a drag start is treated
8
+ * as a real scrub rather than jitter during a plain click. Mirrors
9
+ * ScrubInput's DRAG_THRESHOLD_PX. */
10
+ export declare const SCRUB_DRAG_THRESHOLD_PX = 3;
11
+ export interface ScrubDragState {
12
+ startX: number;
13
+ prevX: number;
14
+ hasDragged: boolean;
15
+ }
16
+ /** Begins tracking a new scrub gesture at the given starting pointer X. */
17
+ export declare function startScrubDrag(startX: number): ScrubDragState;
18
+ export interface ScrubDragTick {
19
+ /** The updated drag state after this pointermove sample. */
20
+ state: ScrubDragState;
21
+ /** The incremental delta (px) to apply this tick, or null when the move
22
+ * should be ignored (no movement, or still under the jitter threshold). */
23
+ deltaX: number | null;
24
+ }
25
+ /**
26
+ * Processes one pointermove sample against the in-progress drag state.
27
+ * Mirrors ScrubInput's handlePointerMove threshold/hasDragged logic exactly:
28
+ * a move is ignored until cumulative movement clears SCRUB_DRAG_THRESHOLD_PX,
29
+ * after which every subsequent move (including this one) yields an
30
+ * incremental delta and marks the gesture as a real drag.
31
+ */
32
+ export declare function updateScrubDrag(state: ScrubDragState, clientX: number): ScrubDragTick;
33
+ export interface ParsedScrubExpression {
34
+ value: number;
35
+ normalized: string;
36
+ }
37
+ export declare function parseScrubExpression(input: string, currentValue: number, options?: ScrubExpressionOptions): ParsedScrubExpression | null;
38
+ export declare function normalizeScrubNumber(value: number, options?: ScrubExpressionOptions): number;
39
+ /**
40
+ * Whether a field's unit should snap to whole numbers while the user is
41
+ * actively pointer-dragging (scrubbing) it. Px-type fields (padding, gap,
42
+ * position, size, radius, etc.) read as integers in Figma-style editors even
43
+ * though the underlying `precision` option (which also governs *typed* input
44
+ * and keyboard nudges) allows one decimal place so a typed "12.5" still
45
+ * commits legally. Scoped to "px" specifically — unitless fields like
46
+ * line-height (precision-based, fractional by design) and other units (deg,
47
+ * %) are untouched.
48
+ */
49
+ export declare function scrubSnapsToInteger(unit: string | undefined): boolean;
50
+ /**
51
+ * Rounds a live scrub-drag value to a whole number when the field's unit
52
+ * calls for integer-only scrubbing (see `scrubSnapsToInteger`), applied
53
+ * *before* `normalizeScrubNumber`'s own min/max/precision clamp so a
54
+ * following precision clamp (if any) can't reintroduce a fraction. Only the
55
+ * pointer-drag scrub gesture should call this — typed input and keyboard
56
+ * nudges keep their existing `precision`-based rounding untouched.
57
+ */
58
+ export declare function roundScrubDragValue(value: number, unit: string | undefined): number;
59
+ export declare function formatScrubValue(value: number, options?: Pick<ScrubExpressionOptions, "precision" | "unit">): string;
60
+ export declare function getScrubStepFromEvent(event: Pick<KeyboardEvent | PointerEvent, "altKey" | "shiftKey">, step: number): number;
61
+ //# sourceMappingURL=scrub-input-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrub-input-utils.d.ts","sourceRoot":"","sources":["../../src/design-tweaks/scrub-input-utils.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AASD;;qCAEqC;AACrC,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAE7D;AAED,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,KAAK,EAAE,cAAc,CAAC;IACtB;+EAC2E;IAC3E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,GACd,aAAa,CAaf;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAeD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,sBAA2B,GACnC,qBAAqB,GAAG,IAAI,CAa9B;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAWR;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAErE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,MAAM,CAER;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,IAAI,CAAC,sBAAsB,EAAE,WAAW,GAAG,MAAM,CAAM,GAC/D,MAAM,CAoBR;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,IAAI,CAAC,aAAa,GAAG,YAAY,EAAE,QAAQ,GAAG,UAAU,CAAC,EAChE,IAAI,EAAE,MAAM,GACX,MAAM,CAQR"}
@@ -0,0 +1,235 @@
1
+ // ─── Scrub-drag gesture-lifecycle state machine ───────────────────────────────
2
+ //
3
+ // Pure mirror of the pointerdown/pointermove/pointerup bookkeeping in
4
+ // ScrubInput's dragRef, extracted so the "exactly one commit-phase emission
5
+ // per gesture" contract can be unit tested without simulating real DOM
6
+ // pointer events (this template has no jsdom/testing-library dependency).
7
+ /** Cumulative pointer movement (px) required before a drag start is treated
8
+ * as a real scrub rather than jitter during a plain click. Mirrors
9
+ * ScrubInput's DRAG_THRESHOLD_PX. */
10
+ export const SCRUB_DRAG_THRESHOLD_PX = 3;
11
+ /** Begins tracking a new scrub gesture at the given starting pointer X. */
12
+ export function startScrubDrag(startX) {
13
+ return { startX, prevX: startX, hasDragged: false };
14
+ }
15
+ /**
16
+ * Processes one pointermove sample against the in-progress drag state.
17
+ * Mirrors ScrubInput's handlePointerMove threshold/hasDragged logic exactly:
18
+ * a move is ignored until cumulative movement clears SCRUB_DRAG_THRESHOLD_PX,
19
+ * after which every subsequent move (including this one) yields an
20
+ * incremental delta and marks the gesture as a real drag.
21
+ */
22
+ export function updateScrubDrag(state, clientX) {
23
+ const incr = clientX - state.prevX;
24
+ if (incr === 0) {
25
+ return { state, deltaX: null };
26
+ }
27
+ const cumulativeDelta = Math.abs(clientX - state.startX);
28
+ if (!state.hasDragged && cumulativeDelta < SCRUB_DRAG_THRESHOLD_PX) {
29
+ return { state: { ...state, prevX: clientX }, deltaX: null };
30
+ }
31
+ return {
32
+ state: { ...state, prevX: clientX, hasDragged: true },
33
+ deltaX: incr,
34
+ };
35
+ }
36
+ const NUMBER_CHAR_PATTERN = /[0-9.]/;
37
+ // Comma is only treated as a digit character while scanning a number token
38
+ // (see tokenizeExpression) so a locale-style decimal comma ("12,5") parses as
39
+ // 12.5, without swallowing the "," that a caller might use to separate
40
+ // distinct expressions elsewhere.
41
+ const NUMBER_OR_COMMA_CHAR_PATTERN = /[0-9.,]/;
42
+ export function parseScrubExpression(input, currentValue, options = {}) {
43
+ const raw = input.trim();
44
+ if (!raw)
45
+ return null;
46
+ const expression = toNumericExpression(raw, currentValue, options.unit);
47
+ const value = evaluateNumericExpression(expression);
48
+ if (value === null)
49
+ return null;
50
+ const normalizedValue = normalizeScrubNumber(value, options);
51
+ return {
52
+ value: normalizedValue,
53
+ normalized: formatScrubValue(normalizedValue, options),
54
+ };
55
+ }
56
+ export function normalizeScrubNumber(value, options = {}) {
57
+ if (!Number.isFinite(value))
58
+ return 0;
59
+ let next = value;
60
+ if (Number.isFinite(options.min))
61
+ next = Math.max(options.min, next);
62
+ if (Number.isFinite(options.max))
63
+ next = Math.min(options.max, next);
64
+ if (Number.isFinite(options.precision)) {
65
+ const scale = 10 ** Math.max(0, options.precision);
66
+ next = Math.round(next * scale) / scale;
67
+ }
68
+ return Object.is(next, -0) ? 0 : next;
69
+ }
70
+ /**
71
+ * Whether a field's unit should snap to whole numbers while the user is
72
+ * actively pointer-dragging (scrubbing) it. Px-type fields (padding, gap,
73
+ * position, size, radius, etc.) read as integers in Figma-style editors even
74
+ * though the underlying `precision` option (which also governs *typed* input
75
+ * and keyboard nudges) allows one decimal place so a typed "12.5" still
76
+ * commits legally. Scoped to "px" specifically — unitless fields like
77
+ * line-height (precision-based, fractional by design) and other units (deg,
78
+ * %) are untouched.
79
+ */
80
+ export function scrubSnapsToInteger(unit) {
81
+ return unit === "px";
82
+ }
83
+ /**
84
+ * Rounds a live scrub-drag value to a whole number when the field's unit
85
+ * calls for integer-only scrubbing (see `scrubSnapsToInteger`), applied
86
+ * *before* `normalizeScrubNumber`'s own min/max/precision clamp so a
87
+ * following precision clamp (if any) can't reintroduce a fraction. Only the
88
+ * pointer-drag scrub gesture should call this — typed input and keyboard
89
+ * nudges keep their existing `precision`-based rounding untouched.
90
+ */
91
+ export function roundScrubDragValue(value, unit) {
92
+ return scrubSnapsToInteger(unit) ? Math.round(value) : value;
93
+ }
94
+ export function formatScrubValue(value, options = {}) {
95
+ const normalized = normalizeScrubNumber(value, options);
96
+ let numeric;
97
+ if (Number.isFinite(options.precision) && options.precision >= 0) {
98
+ const fixed = normalized.toFixed(options.precision);
99
+ if (options.unit) {
100
+ // For fields with units (px, %, °, etc.) collapse all trailing zeros
101
+ // including the decimal point: "12.30px" → "12.3px", "10.00px" → "10px".
102
+ // Leave integer strings alone; otherwise precision 0 turns "100" into "1".
103
+ numeric = fixed.includes(".") ? fixed.replace(/\.?0+$/, "") : fixed;
104
+ }
105
+ else {
106
+ // For unitless fields (e.g. line-height), preserve at least one decimal
107
+ // digit so values like "2.0" stay "2.0" rather than collapsing to "2".
108
+ // Only strip redundant trailing zeros beyond the first decimal digit.
109
+ numeric = fixed.includes(".") ? fixed.replace(/(?<=\.\d)0+$/, "") : fixed;
110
+ }
111
+ }
112
+ else {
113
+ numeric = String(normalized);
114
+ }
115
+ return `${numeric}${options.unit ?? ""}`;
116
+ }
117
+ export function getScrubStepFromEvent(event, step) {
118
+ // Alt (fine-step) and Shift (coarse) are mutually exclusive — alt takes
119
+ // priority, matching Figma's modifier convention. Applying both independently
120
+ // would make Shift+Alt a no-op (×10 × 0.1 = ×1).
121
+ let multiplier = 1;
122
+ if (event.altKey)
123
+ multiplier = 0.1;
124
+ else if (event.shiftKey)
125
+ multiplier = 10;
126
+ return step * multiplier;
127
+ }
128
+ function toNumericExpression(raw, currentValue, unit) {
129
+ let expression = raw.trim();
130
+ if (unit) {
131
+ expression = expression.replace(new RegExp(escapeRegExp(unit), "gi"), "");
132
+ }
133
+ if (expression.startsWith("="))
134
+ return expression.slice(1).trim();
135
+ if (/^[+\-*/]/.test(expression))
136
+ return `${currentValue}${expression}`;
137
+ return expression;
138
+ }
139
+ function evaluateNumericExpression(expression) {
140
+ const tokens = tokenizeExpression(expression);
141
+ if (!tokens.length)
142
+ return null;
143
+ const values = [];
144
+ const operators = [];
145
+ for (const token of tokens) {
146
+ if (token.type === "number") {
147
+ values.push(token.value);
148
+ continue;
149
+ }
150
+ while (operators.length &&
151
+ precedence(operators[operators.length - 1]) >= precedence(token.value)) {
152
+ if (!applyTopOperator(values, operators))
153
+ return null;
154
+ }
155
+ operators.push(token.value);
156
+ }
157
+ while (operators.length) {
158
+ if (!applyTopOperator(values, operators))
159
+ return null;
160
+ }
161
+ if (values.length !== 1 || !Number.isFinite(values[0]))
162
+ return null;
163
+ return values[0];
164
+ }
165
+ function tokenizeExpression(expression) {
166
+ const tokens = [];
167
+ let index = 0;
168
+ let previousWasOperator = true;
169
+ while (index < expression.length) {
170
+ const char = expression[index];
171
+ if (/\s/.test(char)) {
172
+ index += 1;
173
+ continue;
174
+ }
175
+ const signedNumber = (char === "+" || char === "-") &&
176
+ previousWasOperator &&
177
+ NUMBER_CHAR_PATTERN.test(expression[index + 1] ?? "");
178
+ if (NUMBER_CHAR_PATTERN.test(char) || signedNumber) {
179
+ const start = index;
180
+ index += 1;
181
+ // Allow a single comma decimal separator within this number token
182
+ // ("12,5" → 12.5), matching common European locale input. A second
183
+ // comma/period is left for the caller's Number() parse to reject.
184
+ while (NUMBER_OR_COMMA_CHAR_PATTERN.test(expression[index] ?? ""))
185
+ index += 1;
186
+ const value = Number(expression.slice(start, index).replace(",", "."));
187
+ if (!Number.isFinite(value))
188
+ return [];
189
+ tokens.push({ type: "number", value });
190
+ previousWasOperator = false;
191
+ continue;
192
+ }
193
+ if (isOperator(char)) {
194
+ tokens.push({ type: "operator", value: char });
195
+ previousWasOperator = true;
196
+ index += 1;
197
+ continue;
198
+ }
199
+ return [];
200
+ }
201
+ return tokens;
202
+ }
203
+ function applyTopOperator(values, operators) {
204
+ const operator = operators.pop();
205
+ const right = values.pop();
206
+ const left = values.pop();
207
+ if (!operator || right === undefined || left === undefined)
208
+ return false;
209
+ switch (operator) {
210
+ case "+":
211
+ values.push(left + right);
212
+ return true;
213
+ case "-":
214
+ values.push(left - right);
215
+ return true;
216
+ case "*":
217
+ values.push(left * right);
218
+ return true;
219
+ case "/":
220
+ if (right === 0)
221
+ return false;
222
+ values.push(left / right);
223
+ return true;
224
+ }
225
+ }
226
+ function precedence(operator) {
227
+ return operator === "*" || operator === "/" ? 2 : 1;
228
+ }
229
+ function isOperator(char) {
230
+ return char === "+" || char === "-" || char === "*" || char === "/";
231
+ }
232
+ function escapeRegExp(value) {
233
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
234
+ }
235
+ //# sourceMappingURL=scrub-input-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrub-input-utils.js","sourceRoot":"","sources":["../../src/design-tweaks/scrub-input-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { type ReactNode } from "react";
2
+ import { type ScrubExpressionOptions } from "./scrub-input-utils.js";
3
+ type ScrubInputIcon = (props: {
4
+ className?: string;
5
+ "aria-hidden"?: boolean;
6
+ }) => ReactNode;
7
+ export interface ScrubInputChangeMeta {
8
+ source: "commit" | "keyboard" | "scrub";
9
+ expression?: string;
10
+ /**
11
+ * Gesture-lifecycle signal for downstream consumers that want to throttle
12
+ * expensive work during a drag and only do the expensive commit once.
13
+ *
14
+ * - "preview": a live, in-progress tick — e.g. one pointermove sample while
15
+ * scrubbing. There can be many of these per gesture; treat each as a
16
+ * cheap, throttleable preview of the value, not a point to commit at full
17
+ * cost.
18
+ * - "commit": the gesture's authoritative, final value. Fired exactly once
19
+ * per gesture: on pointerup that ends a scrub drag, and for every
20
+ * `source: "commit"` (blur/Enter) or `source: "keyboard"` (arrow step)
21
+ * change, since those are already discrete, complete edits.
22
+ */
23
+ phase: "preview" | "commit";
24
+ /**
25
+ * Set when an arrow-key nudge fires on a `mixed` selection (see
26
+ * `handleKeyDown`): there is no single current value to step from across a
27
+ * mixed selection, so `onChange`'s `value` arg is the step delta itself
28
+ * (not a new absolute value) and consumers that support per-target relative
29
+ * application should add this delta to each selected target's own current
30
+ * value instead of overwriting every target with `value`. Omitted for
31
+ * every other change — existing consumers that don't check for it keep
32
+ * receiving absolute values exactly as before.
33
+ */
34
+ relativeDelta?: number;
35
+ }
36
+ export interface ScrubInputProps extends ScrubExpressionOptions {
37
+ label: string;
38
+ value: number;
39
+ onChange: (value: number, meta: ScrubInputChangeMeta) => void;
40
+ id?: string;
41
+ step?: number;
42
+ icon?: ScrubInputIcon | null;
43
+ /** Use the compact icon-only prefix treatment without relying on CSS selectors. */
44
+ prefix?: "label" | "icon";
45
+ disabled?: boolean;
46
+ placeholder?: string;
47
+ mixed?: boolean;
48
+ mixedLabel?: string;
49
+ className?: string;
50
+ inputClassName?: string;
51
+ labelClassName?: string;
52
+ ariaLabel?: string;
53
+ tooltipLabel?: string;
54
+ }
55
+ export interface PendingScrubCommit {
56
+ value: number;
57
+ /** Incoming prop value at commit time. While this exact value remains, the
58
+ * host has not acknowledged the write yet and the optimistic draft should
59
+ * stay visible. A different incoming value is authoritative host
60
+ * normalization/rejection and must supersede the optimistic draft. */
61
+ baseline: number;
62
+ }
63
+ export declare function resolvePendingScrubCommit(pending: PendingScrubCommit | null, incomingValue: number, options: ScrubExpressionOptions): "none" | "hold" | "confirmed" | "superseded";
64
+ export declare function VisualScrubInput({ label, value, onChange, id, step, unit, min, max, precision, icon: Icon, prefix, disabled, placeholder, mixed, mixedLabel, className, inputClassName, labelClassName, ariaLabel, tooltipLabel, }: ScrubInputProps): import("react").JSX.Element;
65
+ export {};
66
+ //# sourceMappingURL=scrub-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrub-input.d.ts","sourceRoot":"","sources":["../../src/design-tweaks/scrub-input.tsx"],"names":[],"mappings":"AACA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAWf,OAAO,EAQL,KAAK,sBAAsB,EAC5B,MAAM,wBAAwB,CAAC;AAEhC,KAAK,cAAc,GAAG,CAAC,KAAK,EAAE;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,KAAK,SAAS,CAAC;AAEhB,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAgB,SAAQ,sBAAsB;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd;;;0EAGsE;IACtE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,kBAAkB,GAAG,IAAI,EAClC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,sBAAsB,GAC9B,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,YAAY,CAK9C;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,KAAK,EACL,QAAQ,EACR,EAAE,EACF,IAAQ,EACR,IAAI,EACJ,GAAG,EACH,GAAG,EACH,SAAS,EACT,IAAI,EAAE,IAA2B,EACjC,MAAgB,EAChB,QAAgB,EAChB,WAAW,EACX,KAAa,EACb,UAAU,EACV,SAAS,EACT,cAAc,EACd,cAAc,EACd,SAAS,EACT,YAAY,GACb,EAAE,eAAe,+BA2WjB"}