@reekon-tools/boldr-utils 1.13.0 → 1.13.2
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/dist/annotation/canvas/AnnotationCanvasInner.native.js +93 -27
- package/dist/calculator/schema.d.ts +6 -0
- package/dist/theme/index.d.ts +3 -0
- package/dist/theme/index.js +3 -0
- package/dist/theme/preset.d.ts +120 -0
- package/dist/theme/preset.js +25 -0
- package/dist/theme/semantic.d.ts +136 -0
- package/dist/theme/semantic.js +116 -0
- package/dist/types/firestore.d.ts +1 -0
- package/package.json +1 -1
- package/dist/canvas/AnnotationCanvas.d.ts +0 -11
- package/dist/canvas/AnnotationCanvas.js +0 -10
- package/dist/canvas/AnnotationCanvas.native.d.ts +0 -8
- package/dist/canvas/AnnotationCanvas.native.js +0 -6
- package/dist/canvas/AnnotationCanvasInner.d.ts +0 -39
- package/dist/canvas/AnnotationCanvasInner.js +0 -219
- package/dist/canvas/AnnotationCanvasInner.native.d.ts +0 -35
- package/dist/canvas/AnnotationCanvasInner.native.js +0 -138
- package/dist/canvas/AnnotationCanvasSkia.d.ts +0 -27
- package/dist/canvas/AnnotationCanvasSkia.js +0 -20
- package/dist/canvas/Tool.d.ts +0 -38
- package/dist/canvas/Tool.js +0 -1
- package/dist/canvas/elements/BackgroundImageElement.d.ts +0 -9
- package/dist/canvas/elements/BackgroundImageElement.js +0 -37
- package/dist/canvas/elements/MeasurementStampElement.d.ts +0 -13
- package/dist/canvas/elements/MeasurementStampElement.js +0 -30
- package/dist/canvas/elements/ShapeElement.d.ts +0 -7
- package/dist/canvas/elements/ShapeElement.js +0 -62
- package/dist/canvas/elements/StrokeElement.d.ts +0 -7
- package/dist/canvas/elements/StrokeElement.js +0 -18
- package/dist/canvas/measurementPicker.d.ts +0 -10
- package/dist/canvas/measurementPicker.js +0 -1
- package/dist/canvas/measurementStampOverlay.d.ts +0 -11
- package/dist/canvas/measurementStampOverlay.js +0 -1
- package/dist/canvas/pointerAdapter.d.ts +0 -3
- package/dist/canvas/pointerAdapter.js +0 -19
- package/dist/canvas/stampLayout.d.ts +0 -5
- package/dist/canvas/stampLayout.js +0 -14
- package/dist/canvas/tools/measurementStampTool.d.ts +0 -9
- package/dist/canvas/tools/measurementStampTool.js +0 -37
- package/dist/canvas/tools/panTool.d.ts +0 -5
- package/dist/canvas/tools/panTool.js +0 -25
- package/dist/canvas/tools/penTool.d.ts +0 -13
- package/dist/canvas/tools/penTool.js +0 -68
- package/dist/canvas/tools/selectTool.d.ts +0 -2
- package/dist/canvas/tools/selectTool.js +0 -182
- package/dist/canvas/useAnnotationCanvasState.d.ts +0 -54
- package/dist/canvas/useAnnotationCanvasState.js +0 -210
- package/dist/canvas/viewport.d.ts +0 -16
- package/dist/canvas/viewport.js +0 -54
- package/dist/data/AnnotationDataContext.d.ts +0 -8
- package/dist/data/AnnotationDataContext.js +0 -11
- package/dist/data/AnnotationDataProvider.d.ts +0 -65
- package/dist/data/AnnotationDataProvider.js +0 -4
- package/dist/data/InMemoryAnnotationProvider.d.ts +0 -30
- package/dist/data/InMemoryAnnotationProvider.js +0 -197
- package/dist/data/canvasPersistence.d.ts +0 -3
- package/dist/data/canvasPersistence.js +0 -26
- package/dist/data/hooks/useAnnotationCanvasDoc.d.ts +0 -33
- package/dist/data/hooks/useAnnotationCanvasDoc.js +0 -314
- package/dist/data/hooks/useAnnotationDoc.d.ts +0 -7
- package/dist/data/hooks/useAnnotationDoc.js +0 -33
- package/dist/data/hooks/useAnnotationList.d.ts +0 -7
- package/dist/data/hooks/useAnnotationList.js +0 -26
- package/dist/data/hooks/useAnnotationMutations.d.ts +0 -9
- package/dist/data/hooks/useAnnotationMutations.js +0 -11
- package/dist/hooks/useParseMeasurement.d.ts +0 -4
- package/dist/hooks/useParseMeasurement.js +0 -14
- package/dist/utils/evaluateFormula.d.ts +0 -20
- package/dist/utils/evaluateFormula.js +0 -31
|
@@ -121,7 +121,11 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
121
121
|
const zoom = useSharedValue(state.viewport.zoom);
|
|
122
122
|
const panX = useSharedValue(state.viewport.pan.x);
|
|
123
123
|
const panY = useSharedValue(state.viewport.pan.y);
|
|
124
|
-
|
|
124
|
+
// Set by the viewport gestures' onChange, consumed by their onFinalize: the
|
|
125
|
+
// post-gesture sync to React state happens iff a gesture actually moved the
|
|
126
|
+
// viewport, and in onFinalize rather than onEnd because on Android the
|
|
127
|
+
// viewport gestures routinely exit through fail()/cancel() (see the pan).
|
|
128
|
+
const viewportDirty = useSharedValue(false);
|
|
125
129
|
// Skia consumes this directly and re-renders the transform on the UI thread.
|
|
126
130
|
const worldTransform = useDerivedValue(() => {
|
|
127
131
|
'worklet';
|
|
@@ -748,8 +752,17 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
748
752
|
world: stateRef.current.ctx.viewport.screenToWorld(screen),
|
|
749
753
|
});
|
|
750
754
|
// Commit the final viewport to the JS snapshot once a gesture ends, so
|
|
751
|
-
// tools' screenToWorld/worldToScreen reflect the new pan/zoom.
|
|
755
|
+
// tools' screenToWorld/worldToScreen reflect the new pan/zoom. Skips the
|
|
756
|
+
// no-op commit: the pan and the pinch can finalize on the same lift, and
|
|
757
|
+
// the second sync would re-render (and re-settle any background layer)
|
|
758
|
+
// for values React already has.
|
|
752
759
|
const syncViewport = (next) => {
|
|
760
|
+
const current = stateRef.current.ctx.viewport.state;
|
|
761
|
+
if (current.zoom === next.zoom &&
|
|
762
|
+
current.pan.x === next.pan.x &&
|
|
763
|
+
current.pan.y === next.pan.y) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
753
766
|
stateRef.current.setViewport(next);
|
|
754
767
|
};
|
|
755
768
|
const beginViewportGesture = () => {
|
|
@@ -873,9 +886,17 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
873
886
|
// Viewport pan — runs on the UI thread (no runOnJS), mutating the shared
|
|
874
887
|
// viewport directly. Mirrors viewport.ts `panBy`. Used for both the
|
|
875
888
|
// two-finger pan (always available) and the one-finger Hand tool.
|
|
889
|
+
//
|
|
890
|
+
// averageTouches(true) is load-bearing on Android: PanGestureHandler
|
|
891
|
+
// defaults to tracking the LAST-LANDED pointer, not the centroid (iOS
|
|
892
|
+
// always averages). Running simultaneously with the pinch, a non-averaged
|
|
893
|
+
// two-finger pan translates the canvas by finger 2's whole travel — during
|
|
894
|
+
// a spread that is half the spread distance of sideways drift, which reads
|
|
895
|
+
// as "the zoom runs away from where I aimed".
|
|
876
896
|
const buildViewportPan = (minP, maxP) => Gesture.Pan()
|
|
877
897
|
.minPointers(minP)
|
|
878
898
|
.maxPointers(maxP)
|
|
899
|
+
.averageTouches(true)
|
|
879
900
|
.onBegin(() => {
|
|
880
901
|
'worklet';
|
|
881
902
|
runOnJS(beginViewportGesture)();
|
|
@@ -884,47 +905,91 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
884
905
|
'worklet';
|
|
885
906
|
panX.value -= e.changeX / zoom.value;
|
|
886
907
|
panY.value -= e.changeY / zoom.value;
|
|
908
|
+
viewportDirty.value = true;
|
|
887
909
|
})
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
910
|
+
// onFinalize, not onEnd: on Android this pan normally exits through
|
|
911
|
+
// fail() — a (2,2) pan the moment the first finger lifts (pointer
|
|
912
|
+
// count below minPointers), and a pure two-finger translate whose
|
|
913
|
+
// pinch never activated at the final lift. onEnd never runs on those
|
|
914
|
+
// paths, and the movement would never reach React state: hit-tests go
|
|
915
|
+
// stale, a background layer never re-rasterizes for the new view, and
|
|
916
|
+
// the next JS-side viewport write snaps the canvas back.
|
|
895
917
|
.onFinalize(() => {
|
|
896
918
|
'worklet';
|
|
919
|
+
if (viewportDirty.value) {
|
|
920
|
+
viewportDirty.value = false;
|
|
921
|
+
runOnJS(syncViewport)({
|
|
922
|
+
zoom: zoom.value,
|
|
923
|
+
pan: { x: panX.value, y: panY.value },
|
|
924
|
+
});
|
|
925
|
+
}
|
|
897
926
|
runOnJS(endViewportGesture)();
|
|
898
927
|
});
|
|
899
|
-
|
|
928
|
+
// With the Hand tool the viewport pan spans one AND two fingers as a
|
|
929
|
+
// single gesture. Two separate gestures (a one-finger pan racing the
|
|
930
|
+
// two-finger pair) cannot express a staggered pinch: finger 1 panning
|
|
931
|
+
// past the slop resolves the Race before finger 2 lands, the pinch is
|
|
932
|
+
// cancelled with it, and finger 2's touch then kills the one-finger pan
|
|
933
|
+
// too (pointer count over maxPointers cancels an active Android pan) —
|
|
934
|
+
// a dead canvas until every finger lifts. One pan spanning both counts
|
|
935
|
+
// stays active across 1→2→1, and its translation is continuous across
|
|
936
|
+
// pointer transitions (the handler rebases its origin on every
|
|
937
|
+
// POINTER_DOWN/UP).
|
|
938
|
+
const viewportPan = buildViewportPan(panViewport ? 1 : 2, 2);
|
|
900
939
|
// Pinch-to-zoom about the focal point — UI thread. Mirrors viewport.ts
|
|
901
940
|
// `zoomAt`: keep the world point under the focal screen point fixed.
|
|
941
|
+
//
|
|
942
|
+
// Driven by the per-event ratio (`scaleChange`), not by cumulative
|
|
943
|
+
// `e.scale` against a zoom snapshotted in onBegin. On Android onBegin
|
|
944
|
+
// fires at the FIRST finger down — before a pinch provably exists — so a
|
|
945
|
+
// snapshot there can go stale against anything else driving the shared
|
|
946
|
+
// values (the glide animation, the simultaneous pan). The native
|
|
947
|
+
// cumulative scale also freezes rather than rebases across pointer-set
|
|
948
|
+
// changes (2→1→2, a third finger) and sticks at 0 for the rest of the
|
|
949
|
+
// gesture if the fingers ever collapse to a single point, so only ratios
|
|
950
|
+
// between consecutive events are trustworthy. The live focal needs no
|
|
951
|
+
// snapshot either: a focal that merely moves leaves this update a no-op
|
|
952
|
+
// (translation is the pan's job), which is also what makes Android's
|
|
953
|
+
// wrong-focal BEGAN event (it reports the first finger's touch-down
|
|
954
|
+
// position, not a centroid) harmless here.
|
|
902
955
|
const pinch = Gesture.Pinch()
|
|
903
956
|
.onBegin(() => {
|
|
904
957
|
'worklet';
|
|
905
|
-
pinchStartZoom.value = zoom.value;
|
|
906
958
|
runOnJS(beginViewportGesture)();
|
|
907
959
|
})
|
|
908
|
-
.
|
|
960
|
+
.onChange((e) => {
|
|
909
961
|
'worklet';
|
|
910
|
-
|
|
962
|
+
// Drop corrupt ratios instead of applying them: a native span
|
|
963
|
+
// collapse yields scale 0, and the ratio of the frame after that is
|
|
964
|
+
// 0/0. Applying either would slam the zoom to a bound (or NaN it).
|
|
965
|
+
const ratio = e.scaleChange;
|
|
966
|
+
if (!Number.isFinite(ratio) || ratio <= 0)
|
|
967
|
+
return;
|
|
968
|
+
const next = Math.min(50, Math.max(0.05, zoom.value * ratio));
|
|
969
|
+
if (next === zoom.value)
|
|
970
|
+
return; // clamped at a bound, or ratio 1
|
|
911
971
|
// World point currently under the focal screen point (using the live
|
|
912
972
|
// zoom/pan), then re-anchor pan so it stays put at the new zoom.
|
|
913
973
|
const focalWorldX = e.focalX / zoom.value + panX.value;
|
|
914
974
|
const focalWorldY = e.focalY / zoom.value + panY.value;
|
|
915
|
-
panX.value = focalWorldX - e.focalX /
|
|
916
|
-
panY.value = focalWorldY - e.focalY /
|
|
917
|
-
zoom.value =
|
|
918
|
-
|
|
919
|
-
.onEnd(() => {
|
|
920
|
-
'worklet';
|
|
921
|
-
runOnJS(syncViewport)({
|
|
922
|
-
zoom: zoom.value,
|
|
923
|
-
pan: { x: panX.value, y: panY.value },
|
|
924
|
-
});
|
|
975
|
+
panX.value = focalWorldX - e.focalX / next;
|
|
976
|
+
panY.value = focalWorldY - e.focalY / next;
|
|
977
|
+
zoom.value = next;
|
|
978
|
+
viewportDirty.value = true;
|
|
925
979
|
})
|
|
980
|
+
// onFinalize, not onEnd, for the same reason as the pan: an Android
|
|
981
|
+
// pinch that never activated fails at the final lift, and a cancelled
|
|
982
|
+
// gesture never reaches onEnd — the dirty check keeps the no-gesture
|
|
983
|
+
// paths from committing anything.
|
|
926
984
|
.onFinalize(() => {
|
|
927
985
|
'worklet';
|
|
986
|
+
if (viewportDirty.value) {
|
|
987
|
+
viewportDirty.value = false;
|
|
988
|
+
runOnJS(syncViewport)({
|
|
989
|
+
zoom: zoom.value,
|
|
990
|
+
pan: { x: panX.value, y: panY.value },
|
|
991
|
+
});
|
|
992
|
+
}
|
|
928
993
|
runOnJS(endViewportGesture)();
|
|
929
994
|
});
|
|
930
995
|
// Freehand drawing (pen/marker/highlighter) — one finger, UI thread.
|
|
@@ -1425,7 +1490,8 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
1425
1490
|
// One finger, by active tool:
|
|
1426
1491
|
// - freehand (pen/marker/highlighter) → draw on the UI thread
|
|
1427
1492
|
// - shape tools (line/rect/…) → rubber-band on the UI thread
|
|
1428
|
-
// - Hand →
|
|
1493
|
+
// - Hand → no separate gesture: viewportPan above already spans one
|
|
1494
|
+
// finger (min 1), so panning and pinching stay one uncancelled stream
|
|
1429
1495
|
// - select → drag the hit element on the UI thread
|
|
1430
1496
|
// - everything else → dispatch pointer events on the JS thread
|
|
1431
1497
|
const oneFinger = freehand
|
|
@@ -1433,18 +1499,18 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
1433
1499
|
: shapeDraw
|
|
1434
1500
|
? buildShapeDrawPan(shapeDraw)
|
|
1435
1501
|
: panViewport
|
|
1436
|
-
?
|
|
1502
|
+
? null
|
|
1437
1503
|
: dragSelection
|
|
1438
1504
|
? buildSelectDragPan(dragSelection)
|
|
1439
1505
|
: toolPan;
|
|
1440
1506
|
// Long-press only joins the race when the active tool acts on it, so it
|
|
1441
1507
|
// never pre-empts a one-finger drag/draw on tools that ignore holds.
|
|
1442
|
-
return Gesture.Race(tap, ...(longPressEnabled ? [longPress] : []), Gesture.Simultaneous(viewportPan, pinch), oneFinger);
|
|
1508
|
+
return Gesture.Race(tap, ...(longPressEnabled ? [longPress] : []), Gesture.Simultaneous(viewportPan, pinch), ...(oneFinger ? [oneFinger] : []));
|
|
1443
1509
|
}, [
|
|
1444
1510
|
zoom,
|
|
1445
1511
|
panX,
|
|
1446
1512
|
panY,
|
|
1447
|
-
|
|
1513
|
+
viewportDirty,
|
|
1448
1514
|
livePoints,
|
|
1449
1515
|
handoffStrokes,
|
|
1450
1516
|
removeHandoffStrokes,
|
|
@@ -195,6 +195,12 @@ export interface CalculatorDefinition {
|
|
|
195
195
|
/** Public calculators are readable by everyone; rules/query key. */
|
|
196
196
|
isPublic: boolean;
|
|
197
197
|
sharedUserIds?: string[];
|
|
198
|
+
/**
|
|
199
|
+
* Orgs this calculator is shared with. Written only by the backend
|
|
200
|
+
* shareCalculatorWithOrg / unshareCalculatorWithOrg callables, which also
|
|
201
|
+
* maintain the organizations/{orgId}/sharedCalculators pointer docs clients
|
|
202
|
+
* use for discovery (rules can't authorize an array-contains query here).
|
|
203
|
+
*/
|
|
198
204
|
sharedOrgIds?: string[];
|
|
199
205
|
createdBy?: {
|
|
200
206
|
userId: string;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Tailwind preset. COLORS ONLY, by design.
|
|
3
|
+
*
|
|
4
|
+
* Must never define `content`, `plugins`, `presets`, or `darkMode`:
|
|
5
|
+
* NativeWind recurses `config.presets[].presets` hunting for its own marker
|
|
6
|
+
* preset, and a `content` key here would silently narrow the consuming app's
|
|
7
|
+
* globs.
|
|
8
|
+
*
|
|
9
|
+
* Consumed from CJS Tailwind configs, so this is a NAMED export with no
|
|
10
|
+
* default — that keeps ESM/CJS interop semantics irrelevant:
|
|
11
|
+
*
|
|
12
|
+
* const { boldrPreset } = require('@reekon-tools/boldr-utils/theme/preset');
|
|
13
|
+
* module.exports = { presets: [require('nativewind/preset'), boldrPreset] };
|
|
14
|
+
*
|
|
15
|
+
* Tailwind loads configs through jiti + sucrase, so requiring this ESM file
|
|
16
|
+
* from a CJS config works regardless of Node version.
|
|
17
|
+
*
|
|
18
|
+
* Note that Tailwind merges `theme.extend` per key with the *app* winning, and
|
|
19
|
+
* replaces a color key wholesale rather than deep-merging. An app that keeps
|
|
20
|
+
* its own `colors.background` will silently shadow this preset.
|
|
21
|
+
*/
|
|
22
|
+
export declare const boldrPreset: {
|
|
23
|
+
theme: {
|
|
24
|
+
extend: {
|
|
25
|
+
colors: {
|
|
26
|
+
background: "#1D1F21";
|
|
27
|
+
foreground: {
|
|
28
|
+
DEFAULT: "#FFFFFF";
|
|
29
|
+
disabled: "#626364";
|
|
30
|
+
};
|
|
31
|
+
card: {
|
|
32
|
+
DEFAULT: "#2B2C2E";
|
|
33
|
+
foreground: "#FFFFFF";
|
|
34
|
+
};
|
|
35
|
+
popover: {
|
|
36
|
+
DEFAULT: "#2B2C2E";
|
|
37
|
+
foreground: "#FFFFFF";
|
|
38
|
+
};
|
|
39
|
+
muted: {
|
|
40
|
+
DEFAULT: "#424345";
|
|
41
|
+
foreground: "#9FA0A2";
|
|
42
|
+
};
|
|
43
|
+
accent: {
|
|
44
|
+
DEFAULT: "#424345";
|
|
45
|
+
foreground: "#FFFFFF";
|
|
46
|
+
};
|
|
47
|
+
secondary: {
|
|
48
|
+
DEFAULT: "#424345";
|
|
49
|
+
foreground: "#FFFFFF";
|
|
50
|
+
};
|
|
51
|
+
border: {
|
|
52
|
+
DEFAULT: "#424345";
|
|
53
|
+
strong: "#626364";
|
|
54
|
+
};
|
|
55
|
+
input: "#424345";
|
|
56
|
+
ring: "#FFAD00";
|
|
57
|
+
primary: {
|
|
58
|
+
DEFAULT: "#FFAD00";
|
|
59
|
+
foreground: "#1D1F21";
|
|
60
|
+
};
|
|
61
|
+
destructive: {
|
|
62
|
+
DEFAULT: "#DF6358";
|
|
63
|
+
foreground: "#1D1F21";
|
|
64
|
+
};
|
|
65
|
+
elevated: {
|
|
66
|
+
DEFAULT: "#424345";
|
|
67
|
+
foreground: "#FFFFFF";
|
|
68
|
+
};
|
|
69
|
+
interactive: {
|
|
70
|
+
DEFAULT: "#0066FF";
|
|
71
|
+
foreground: "#FFFFFF";
|
|
72
|
+
};
|
|
73
|
+
nav: {
|
|
74
|
+
DEFAULT: "#2D3843";
|
|
75
|
+
foreground: "#FFFFFF";
|
|
76
|
+
};
|
|
77
|
+
paper: {
|
|
78
|
+
DEFAULT: "#E8EAED";
|
|
79
|
+
foreground: "#1D1F21";
|
|
80
|
+
field: "#FFFFFF";
|
|
81
|
+
line: "#BCBEC2";
|
|
82
|
+
muted: "#626364";
|
|
83
|
+
};
|
|
84
|
+
tile: {
|
|
85
|
+
foreground: "#FFFFFF";
|
|
86
|
+
measurement: {
|
|
87
|
+
DEFAULT: "#0066FF";
|
|
88
|
+
badge: "#50A6FF";
|
|
89
|
+
};
|
|
90
|
+
formula: {
|
|
91
|
+
DEFAULT: "#BD30A8";
|
|
92
|
+
badge: "#6C155F";
|
|
93
|
+
};
|
|
94
|
+
count: {
|
|
95
|
+
DEFAULT: "#02274B";
|
|
96
|
+
foreground: "#FFFFFF";
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
diagram: {
|
|
100
|
+
1: "#2E4C6A";
|
|
101
|
+
2: "#4B6074";
|
|
102
|
+
3: "#67819A";
|
|
103
|
+
};
|
|
104
|
+
tolerance: {
|
|
105
|
+
in: {
|
|
106
|
+
DEFAULT: "#58A942";
|
|
107
|
+
warning: "#00E920";
|
|
108
|
+
muted: "#8BB380";
|
|
109
|
+
};
|
|
110
|
+
out: {
|
|
111
|
+
DEFAULT: "#E83D2F";
|
|
112
|
+
warning: "#DA1100";
|
|
113
|
+
muted: "#DF6358";
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
export type BoldrPreset = typeof boldrPreset;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { boldrColors } from './semantic.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shared Tailwind preset. COLORS ONLY, by design.
|
|
4
|
+
*
|
|
5
|
+
* Must never define `content`, `plugins`, `presets`, or `darkMode`:
|
|
6
|
+
* NativeWind recurses `config.presets[].presets` hunting for its own marker
|
|
7
|
+
* preset, and a `content` key here would silently narrow the consuming app's
|
|
8
|
+
* globs.
|
|
9
|
+
*
|
|
10
|
+
* Consumed from CJS Tailwind configs, so this is a NAMED export with no
|
|
11
|
+
* default — that keeps ESM/CJS interop semantics irrelevant:
|
|
12
|
+
*
|
|
13
|
+
* const { boldrPreset } = require('@reekon-tools/boldr-utils/theme/preset');
|
|
14
|
+
* module.exports = { presets: [require('nativewind/preset'), boldrPreset] };
|
|
15
|
+
*
|
|
16
|
+
* Tailwind loads configs through jiti + sucrase, so requiring this ESM file
|
|
17
|
+
* from a CJS config works regardless of Node version.
|
|
18
|
+
*
|
|
19
|
+
* Note that Tailwind merges `theme.extend` per key with the *app* winning, and
|
|
20
|
+
* replaces a color key wholesale rather than deep-merging. An app that keeps
|
|
21
|
+
* its own `colors.background` will silently shadow this preset.
|
|
22
|
+
*/
|
|
23
|
+
export const boldrPreset = {
|
|
24
|
+
theme: { extend: { colors: boldrColors } },
|
|
25
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural shape of a Tailwind `theme.extend.colors` tree.
|
|
3
|
+
*
|
|
4
|
+
* Declared locally on purpose: annotating with tailwindcss's own `Config` type
|
|
5
|
+
* would put `import type { Config } from 'tailwindcss'` into the emitted .d.ts
|
|
6
|
+
* and make tailwindcss a hard type dependency of this package.
|
|
7
|
+
*/
|
|
8
|
+
export type ColorTree = {
|
|
9
|
+
[key: string]: string | ColorTree;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Semantic design tokens — the layer apps actually consume.
|
|
13
|
+
*
|
|
14
|
+
* `colors.ts` is the palette (raw Figma variables, named by value).
|
|
15
|
+
* This file maps those values onto *roles*, and is the only color surface the
|
|
16
|
+
* apps should reference. The raw palette is deliberately NOT exposed as Tailwind
|
|
17
|
+
* classes; naming by value is how both apps accumulated hundreds of arbitrary
|
|
18
|
+
* hex classes in the first place.
|
|
19
|
+
*
|
|
20
|
+
* The shadcn/ui token names (background, card, popover, foreground, muted,
|
|
21
|
+
* accent, secondary, border, input, ring, primary, destructive) are reused
|
|
22
|
+
* rather than paralleled, so every shadcn component is re-skinned without any
|
|
23
|
+
* component edits and `shadcn add` keeps generating markup that lands on these
|
|
24
|
+
* tokens.
|
|
25
|
+
*
|
|
26
|
+
* The design is dark-only. The light calculator / form surfaces are the
|
|
27
|
+
* explicitly-named `paper.*` tokens, not a light/dark variant pair — on native
|
|
28
|
+
* NativeWind's `dark:` variant is global, and portal-rendered surfaces
|
|
29
|
+
* (bottom sheets, dialogs, selects) mount outside any scoped subtree, so a
|
|
30
|
+
* scoped override would leak dark chrome onto a light page.
|
|
31
|
+
*/
|
|
32
|
+
export declare const boldrColors: {
|
|
33
|
+
background: "#1D1F21";
|
|
34
|
+
foreground: {
|
|
35
|
+
DEFAULT: "#FFFFFF";
|
|
36
|
+
disabled: "#626364";
|
|
37
|
+
};
|
|
38
|
+
card: {
|
|
39
|
+
DEFAULT: "#2B2C2E";
|
|
40
|
+
foreground: "#FFFFFF";
|
|
41
|
+
};
|
|
42
|
+
popover: {
|
|
43
|
+
DEFAULT: "#2B2C2E";
|
|
44
|
+
foreground: "#FFFFFF";
|
|
45
|
+
};
|
|
46
|
+
muted: {
|
|
47
|
+
DEFAULT: "#424345";
|
|
48
|
+
foreground: "#9FA0A2";
|
|
49
|
+
};
|
|
50
|
+
accent: {
|
|
51
|
+
DEFAULT: "#424345";
|
|
52
|
+
foreground: "#FFFFFF";
|
|
53
|
+
};
|
|
54
|
+
secondary: {
|
|
55
|
+
DEFAULT: "#424345";
|
|
56
|
+
foreground: "#FFFFFF";
|
|
57
|
+
};
|
|
58
|
+
border: {
|
|
59
|
+
DEFAULT: "#424345";
|
|
60
|
+
strong: "#626364";
|
|
61
|
+
};
|
|
62
|
+
input: "#424345";
|
|
63
|
+
ring: "#FFAD00";
|
|
64
|
+
primary: {
|
|
65
|
+
DEFAULT: "#FFAD00";
|
|
66
|
+
foreground: "#1D1F21";
|
|
67
|
+
};
|
|
68
|
+
destructive: {
|
|
69
|
+
DEFAULT: "#DF6358";
|
|
70
|
+
foreground: "#1D1F21";
|
|
71
|
+
};
|
|
72
|
+
/** Chips, inset controls, thumbnail placeholders. */
|
|
73
|
+
elevated: {
|
|
74
|
+
DEFAULT: "#424345";
|
|
75
|
+
foreground: "#FFFFFF";
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Steppers and other interactive fills. FILL ONLY — #0066FF as text on
|
|
79
|
+
* #2B2C2E is 2.89:1. Never `text-interactive`.
|
|
80
|
+
*/
|
|
81
|
+
interactive: {
|
|
82
|
+
DEFAULT: "#0066FF";
|
|
83
|
+
foreground: "#FFFFFF";
|
|
84
|
+
};
|
|
85
|
+
/** Nav chrome (the web app's sidebar). */
|
|
86
|
+
nav: {
|
|
87
|
+
DEFAULT: "#2D3843";
|
|
88
|
+
foreground: "#FFFFFF";
|
|
89
|
+
};
|
|
90
|
+
paper: {
|
|
91
|
+
DEFAULT: "#E8EAED";
|
|
92
|
+
foreground: "#1D1F21";
|
|
93
|
+
field: "#FFFFFF";
|
|
94
|
+
line: "#BCBEC2";
|
|
95
|
+
muted: "#626364";
|
|
96
|
+
};
|
|
97
|
+
tile: {
|
|
98
|
+
/**
|
|
99
|
+
* Ink for any tile fill — `text-tile-foreground`. Shared across the
|
|
100
|
+
* measurement and formula variants because both carry white type; keeping it
|
|
101
|
+
* at the family level avoids two identical `foreground` keys that could
|
|
102
|
+
* drift apart by accident.
|
|
103
|
+
*/
|
|
104
|
+
foreground: "#FFFFFF";
|
|
105
|
+
measurement: {
|
|
106
|
+
DEFAULT: "#0066FF";
|
|
107
|
+
badge: "#50A6FF";
|
|
108
|
+
};
|
|
109
|
+
formula: {
|
|
110
|
+
DEFAULT: "#BD30A8";
|
|
111
|
+
badge: "#6C155F";
|
|
112
|
+
};
|
|
113
|
+
count: {
|
|
114
|
+
DEFAULT: "#02274B";
|
|
115
|
+
foreground: "#FFFFFF";
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
diagram: {
|
|
119
|
+
1: "#2E4C6A";
|
|
120
|
+
2: "#4B6074";
|
|
121
|
+
3: "#67819A";
|
|
122
|
+
};
|
|
123
|
+
tolerance: {
|
|
124
|
+
in: {
|
|
125
|
+
DEFAULT: "#58A942";
|
|
126
|
+
warning: "#00E920";
|
|
127
|
+
muted: "#8BB380";
|
|
128
|
+
};
|
|
129
|
+
out: {
|
|
130
|
+
DEFAULT: "#E83D2F";
|
|
131
|
+
warning: "#DA1100";
|
|
132
|
+
muted: "#DF6358";
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
export type BoldrColors = typeof boldrColors;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { GeneralColors, SecondaryColors, FormColors, TileColors, FormulaColors, YellowColors, RedColors, GreenColors, } from './colors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Semantic design tokens — the layer apps actually consume.
|
|
4
|
+
*
|
|
5
|
+
* `colors.ts` is the palette (raw Figma variables, named by value).
|
|
6
|
+
* This file maps those values onto *roles*, and is the only color surface the
|
|
7
|
+
* apps should reference. The raw palette is deliberately NOT exposed as Tailwind
|
|
8
|
+
* classes; naming by value is how both apps accumulated hundreds of arbitrary
|
|
9
|
+
* hex classes in the first place.
|
|
10
|
+
*
|
|
11
|
+
* The shadcn/ui token names (background, card, popover, foreground, muted,
|
|
12
|
+
* accent, secondary, border, input, ring, primary, destructive) are reused
|
|
13
|
+
* rather than paralleled, so every shadcn component is re-skinned without any
|
|
14
|
+
* component edits and `shadcn add` keeps generating markup that lands on these
|
|
15
|
+
* tokens.
|
|
16
|
+
*
|
|
17
|
+
* The design is dark-only. The light calculator / form surfaces are the
|
|
18
|
+
* explicitly-named `paper.*` tokens, not a light/dark variant pair — on native
|
|
19
|
+
* NativeWind's `dark:` variant is global, and portal-rendered surfaces
|
|
20
|
+
* (bottom sheets, dialogs, selects) mount outside any scoped subtree, so a
|
|
21
|
+
* scoped override would leak dark chrome onto a light page.
|
|
22
|
+
*/
|
|
23
|
+
export const boldrColors = {
|
|
24
|
+
// ── dark app chrome — shadcn-compatible names, re-pointed ────────────────
|
|
25
|
+
background: GeneralColors.darkGrey, // screen + header
|
|
26
|
+
foreground: {
|
|
27
|
+
DEFAULT: GeneralColors.white,
|
|
28
|
+
disabled: GeneralColors.grey3, // text-foreground-disabled
|
|
29
|
+
},
|
|
30
|
+
card: { DEFAULT: GeneralColors.grey1, foreground: GeneralColors.white },
|
|
31
|
+
popover: { DEFAULT: GeneralColors.grey1, foreground: GeneralColors.white },
|
|
32
|
+
// muted / accent / secondary exist so unmodified shadcn components look
|
|
33
|
+
// right. Prefer `elevated` in new boldr components — it gives future
|
|
34
|
+
// divergence a seam.
|
|
35
|
+
muted: { DEFAULT: GeneralColors.grey2, foreground: GeneralColors.grey4 },
|
|
36
|
+
accent: { DEFAULT: GeneralColors.grey2, foreground: GeneralColors.white },
|
|
37
|
+
secondary: { DEFAULT: GeneralColors.grey2, foreground: GeneralColors.white },
|
|
38
|
+
border: { DEFAULT: GeneralColors.grey2, strong: GeneralColors.grey3 },
|
|
39
|
+
input: GeneralColors.grey2,
|
|
40
|
+
ring: YellowColors.mainYellow,
|
|
41
|
+
primary: {
|
|
42
|
+
DEFAULT: YellowColors.mainYellow,
|
|
43
|
+
// White on #FFAD00 is 1.87:1. Dark ink is 8.84:1, and matches the mockups.
|
|
44
|
+
foreground: GeneralColors.darkGrey,
|
|
45
|
+
},
|
|
46
|
+
destructive: {
|
|
47
|
+
DEFAULT: RedColors.mutedRed,
|
|
48
|
+
// Confirmed against the Settings frame: the Log Out label is dark ink.
|
|
49
|
+
// White on #DF6358 would be 3.46:1; #1D1F21 is 4.77:1.
|
|
50
|
+
foreground: GeneralColors.darkGrey,
|
|
51
|
+
},
|
|
52
|
+
// ── boldr additions the shadcn vocabulary has no word for ───────────────
|
|
53
|
+
/** Chips, inset controls, thumbnail placeholders. */
|
|
54
|
+
elevated: { DEFAULT: GeneralColors.grey2, foreground: GeneralColors.white },
|
|
55
|
+
/**
|
|
56
|
+
* Steppers and other interactive fills. FILL ONLY — #0066FF as text on
|
|
57
|
+
* #2B2C2E is 2.89:1. Never `text-interactive`.
|
|
58
|
+
*/
|
|
59
|
+
interactive: {
|
|
60
|
+
DEFAULT: TileColors.activeBlue,
|
|
61
|
+
foreground: GeneralColors.white,
|
|
62
|
+
},
|
|
63
|
+
/** Nav chrome (the web app's sidebar). */
|
|
64
|
+
nav: {
|
|
65
|
+
DEFAULT: SecondaryColors.accentBlue1,
|
|
66
|
+
foreground: GeneralColors.white,
|
|
67
|
+
},
|
|
68
|
+
// ── light "paper": the calculator page and form fields ──────────────────
|
|
69
|
+
paper: {
|
|
70
|
+
DEFAULT: FormColors.lightGrey2, // bg-paper page / canvas
|
|
71
|
+
foreground: GeneralColors.darkGrey, // text-paper-foreground
|
|
72
|
+
field: GeneralColors.white, // bg-paper-field inputs, cards on paper
|
|
73
|
+
line: FormColors.lightGrey1, // border-paper-line field borders, dividers
|
|
74
|
+
// grey3, not lightGrey1: #BCBEC2 on #E8EAED is 1.54:1 — invisible.
|
|
75
|
+
// lightGrey1 is a border color only, which is why it's `paper.line`.
|
|
76
|
+
muted: GeneralColors.grey3, // text-paper-muted
|
|
77
|
+
},
|
|
78
|
+
// ── product tiles ───────────────────────────────────────────────────────
|
|
79
|
+
tile: {
|
|
80
|
+
/**
|
|
81
|
+
* Ink for any tile fill — `text-tile-foreground`. Shared across the
|
|
82
|
+
* measurement and formula variants because both carry white type; keeping it
|
|
83
|
+
* at the family level avoids two identical `foreground` keys that could
|
|
84
|
+
* drift apart by accident.
|
|
85
|
+
*/
|
|
86
|
+
foreground: GeneralColors.white,
|
|
87
|
+
measurement: {
|
|
88
|
+
DEFAULT: TileColors.activeBlue,
|
|
89
|
+
badge: TileColors.lightBlue1,
|
|
90
|
+
},
|
|
91
|
+
// Note the badge is DARKER than the tile here, inverting the old app-local
|
|
92
|
+
// values. Confirmed in the mockups: a magenta tile with a darker magenta
|
|
93
|
+
// label bar.
|
|
94
|
+
formula: { DEFAULT: FormulaColors.purple, badge: FormulaColors.darkPurple },
|
|
95
|
+
count: { DEFAULT: TileColors.darkBlue, foreground: GeneralColors.white },
|
|
96
|
+
},
|
|
97
|
+
// ── 3D / diagram depth ramp (ordered; the steps have no individual meaning) ─
|
|
98
|
+
diagram: {
|
|
99
|
+
1: SecondaryColors.accentBlue2,
|
|
100
|
+
2: SecondaryColors.accentBlue3,
|
|
101
|
+
3: SecondaryColors.accentBlue4,
|
|
102
|
+
},
|
|
103
|
+
// ── measurement tolerance ───────────────────────────────────────────────
|
|
104
|
+
tolerance: {
|
|
105
|
+
in: {
|
|
106
|
+
DEFAULT: GreenColors.green,
|
|
107
|
+
warning: GreenColors.warningGreen,
|
|
108
|
+
muted: GreenColors.mutedGreen,
|
|
109
|
+
},
|
|
110
|
+
out: {
|
|
111
|
+
DEFAULT: RedColors.red,
|
|
112
|
+
warning: RedColors.warningRed,
|
|
113
|
+
muted: RedColors.mutedRed,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
};
|
package/package.json
CHANGED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
2
|
-
import type { AnnotationCanvasInnerProps } from './AnnotationCanvasInner.js';
|
|
3
|
-
export type { AnnotationCanvasHandle, GestureConfig, PanTrigger, } from './AnnotationCanvasInner.js';
|
|
4
|
-
export interface CanvasKitOpts {
|
|
5
|
-
locateFile?: (file: string) => string;
|
|
6
|
-
}
|
|
7
|
-
export type AnnotationCanvasProps = AnnotationCanvasInnerProps & {
|
|
8
|
-
fallback?: ReactNode;
|
|
9
|
-
canvasKitOpts?: CanvasKitOpts;
|
|
10
|
-
};
|
|
11
|
-
export declare const AnnotationCanvas: ({ fallback, canvasKitOpts, ...rest }: AnnotationCanvasProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { WithSkiaWeb } from '@shopify/react-native-skia/lib/module/web/index.js';
|
|
3
|
-
// Web-only entry point: lazy-loads CanvasKit wasm via WithSkiaWeb. Imperative
|
|
4
|
-
// API (undo/redo/zoom) is exposed through the `imperativeRef` prop rather
|
|
5
|
-
// than React refs, since WithSkiaWeb can't forward refs through its lazy
|
|
6
|
-
// component boundary. The native build sits in `AnnotationCanvas.native.tsx`
|
|
7
|
-
// and skips WithSkiaWeb entirely (no wasm to load).
|
|
8
|
-
export const AnnotationCanvas = ({ fallback, canvasKitOpts, ...rest }) => (_jsx(WithSkiaWeb, { getComponent: () => import('./AnnotationCanvasInner.js').then((m) => ({
|
|
9
|
-
default: m.AnnotationCanvasInner,
|
|
10
|
-
})), fallback: fallback ?? null, componentProps: rest, opts: canvasKitOpts }));
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { AnnotationCanvasInnerProps } from './AnnotationCanvasInner.native.js';
|
|
2
|
-
export type { AnnotationCanvasHandle, } from './useAnnotationCanvasState.js';
|
|
3
|
-
export type { GestureConfig, PanTrigger, } from './AnnotationCanvasInner.js';
|
|
4
|
-
export type AnnotationCanvasProps = AnnotationCanvasInnerProps & {
|
|
5
|
-
fallback?: unknown;
|
|
6
|
-
canvasKitOpts?: unknown;
|
|
7
|
-
};
|
|
8
|
-
export declare const AnnotationCanvas: (props: AnnotationCanvasProps) => import("react/jsx-runtime").JSX.Element;
|