@reekon-tools/boldr-utils 1.10.3 → 1.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 (98) hide show
  1. package/dist/annotation/canvas/AnnotationCanvasInner.native.js +48 -7
  2. package/dist/annotation/canvas/AnnotationCanvasSkia.d.ts +2 -2
  3. package/dist/annotation/canvas/AnnotationCanvasSkia.js +16 -3
  4. package/dist/annotation/canvas/Tool.d.ts +8 -1
  5. package/dist/annotation/canvas/backgroundLayers.d.ts +20 -0
  6. package/dist/annotation/canvas/backgroundLayers.js +98 -0
  7. package/dist/annotation/canvas/elements/BackgroundImageElement.d.ts +10 -7
  8. package/dist/annotation/canvas/elements/BackgroundImageElement.js +45 -11
  9. package/dist/annotation/canvas/elements/BackgroundSvg.d.ts +3 -4
  10. package/dist/annotation/canvas/elements/BackgroundSvg.js +23 -29
  11. package/dist/annotation/canvas/elements/backgroundUrl.js +2 -2
  12. package/dist/annotation/canvas/tools/selectTool.js +183 -19
  13. package/dist/annotation/canvas/useAnnotationCanvasState.js +29 -20
  14. package/dist/annotation/canvas/viewport.js +7 -6
  15. package/dist/annotation/data/AnnotationDataProvider.d.ts +1 -1
  16. package/dist/annotation/data/InMemoryAnnotationProvider.d.ts +1 -1
  17. package/dist/annotation/data/hooks/useAnnotationCanvasDoc.d.ts +7 -1
  18. package/dist/annotation/data/hooks/useAnnotationCanvasDoc.js +124 -22
  19. package/dist/annotation/data/hooks/useAnnotationMutations.d.ts +1 -1
  20. package/dist/calculator/chips.d.ts +32 -0
  21. package/dist/calculator/chips.js +79 -0
  22. package/dist/calculator/display.d.ts +78 -0
  23. package/dist/calculator/display.js +223 -0
  24. package/dist/calculator/index.d.ts +3 -0
  25. package/dist/calculator/index.js +8 -3
  26. package/dist/calculator/runtime.d.ts +61 -0
  27. package/dist/calculator/runtime.js +430 -0
  28. package/dist/exports.d.ts +1 -0
  29. package/dist/exports.js +1 -0
  30. package/dist/theme/index.d.ts +3 -0
  31. package/dist/theme/index.js +3 -0
  32. package/dist/theme/preset.d.ts +120 -0
  33. package/dist/theme/preset.js +25 -0
  34. package/dist/theme/semantic.d.ts +136 -0
  35. package/dist/theme/semantic.js +116 -0
  36. package/dist/types/annotation.d.ts +27 -0
  37. package/dist/types/annotation.js +68 -0
  38. package/package.json +1 -1
  39. package/dist/canvas/AnnotationCanvas.d.ts +0 -11
  40. package/dist/canvas/AnnotationCanvas.js +0 -10
  41. package/dist/canvas/AnnotationCanvas.native.d.ts +0 -8
  42. package/dist/canvas/AnnotationCanvas.native.js +0 -6
  43. package/dist/canvas/AnnotationCanvasInner.d.ts +0 -39
  44. package/dist/canvas/AnnotationCanvasInner.js +0 -219
  45. package/dist/canvas/AnnotationCanvasInner.native.d.ts +0 -35
  46. package/dist/canvas/AnnotationCanvasInner.native.js +0 -138
  47. package/dist/canvas/AnnotationCanvasSkia.d.ts +0 -27
  48. package/dist/canvas/AnnotationCanvasSkia.js +0 -20
  49. package/dist/canvas/Tool.d.ts +0 -38
  50. package/dist/canvas/Tool.js +0 -1
  51. package/dist/canvas/elements/BackgroundImageElement.d.ts +0 -9
  52. package/dist/canvas/elements/BackgroundImageElement.js +0 -37
  53. package/dist/canvas/elements/MeasurementStampElement.d.ts +0 -13
  54. package/dist/canvas/elements/MeasurementStampElement.js +0 -30
  55. package/dist/canvas/elements/ShapeElement.d.ts +0 -7
  56. package/dist/canvas/elements/ShapeElement.js +0 -62
  57. package/dist/canvas/elements/StrokeElement.d.ts +0 -7
  58. package/dist/canvas/elements/StrokeElement.js +0 -18
  59. package/dist/canvas/measurementPicker.d.ts +0 -10
  60. package/dist/canvas/measurementPicker.js +0 -1
  61. package/dist/canvas/measurementStampOverlay.d.ts +0 -11
  62. package/dist/canvas/measurementStampOverlay.js +0 -1
  63. package/dist/canvas/pointerAdapter.d.ts +0 -3
  64. package/dist/canvas/pointerAdapter.js +0 -19
  65. package/dist/canvas/stampLayout.d.ts +0 -5
  66. package/dist/canvas/stampLayout.js +0 -14
  67. package/dist/canvas/tools/measurementStampTool.d.ts +0 -9
  68. package/dist/canvas/tools/measurementStampTool.js +0 -37
  69. package/dist/canvas/tools/panTool.d.ts +0 -5
  70. package/dist/canvas/tools/panTool.js +0 -25
  71. package/dist/canvas/tools/penTool.d.ts +0 -13
  72. package/dist/canvas/tools/penTool.js +0 -68
  73. package/dist/canvas/tools/selectTool.d.ts +0 -2
  74. package/dist/canvas/tools/selectTool.js +0 -182
  75. package/dist/canvas/useAnnotationCanvasState.d.ts +0 -54
  76. package/dist/canvas/useAnnotationCanvasState.js +0 -210
  77. package/dist/canvas/viewport.d.ts +0 -16
  78. package/dist/canvas/viewport.js +0 -54
  79. package/dist/data/AnnotationDataContext.d.ts +0 -8
  80. package/dist/data/AnnotationDataContext.js +0 -11
  81. package/dist/data/AnnotationDataProvider.d.ts +0 -65
  82. package/dist/data/AnnotationDataProvider.js +0 -4
  83. package/dist/data/InMemoryAnnotationProvider.d.ts +0 -30
  84. package/dist/data/InMemoryAnnotationProvider.js +0 -197
  85. package/dist/data/canvasPersistence.d.ts +0 -3
  86. package/dist/data/canvasPersistence.js +0 -26
  87. package/dist/data/hooks/useAnnotationCanvasDoc.d.ts +0 -33
  88. package/dist/data/hooks/useAnnotationCanvasDoc.js +0 -314
  89. package/dist/data/hooks/useAnnotationDoc.d.ts +0 -7
  90. package/dist/data/hooks/useAnnotationDoc.js +0 -33
  91. package/dist/data/hooks/useAnnotationList.d.ts +0 -7
  92. package/dist/data/hooks/useAnnotationList.js +0 -26
  93. package/dist/data/hooks/useAnnotationMutations.d.ts +0 -9
  94. package/dist/data/hooks/useAnnotationMutations.js +0 -11
  95. package/dist/hooks/useParseMeasurement.d.ts +0 -4
  96. package/dist/hooks/useParseMeasurement.js +0 -14
  97. package/dist/utils/evaluateFormula.d.ts +0 -20
  98. package/dist/utils/evaluateFormula.js +0 -31
@@ -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
+ };
@@ -96,11 +96,27 @@ export interface AnnotationBackgroundImage {
96
96
  heightPx: number;
97
97
  format?: 'raster' | 'svg';
98
98
  }
99
+ export interface BackgroundLayerTransform {
100
+ x: number;
101
+ y: number;
102
+ scaleX: number;
103
+ scaleY: number;
104
+ }
105
+ export interface AnnotationBackgroundLayer {
106
+ id: AnnotationElementId;
107
+ storagePath: string;
108
+ downloadUrl: string;
109
+ widthPx: number;
110
+ heightPx: number;
111
+ format?: 'raster' | 'svg';
112
+ transform: BackgroundLayerTransform;
113
+ }
99
114
  export interface AnnotationViewport {
100
115
  width: number;
101
116
  height: number;
102
117
  backgroundImage?: AnnotationBackgroundImage;
103
118
  backgroundFit?: BackgroundFit;
119
+ backgroundImages?: AnnotationBackgroundLayer[];
104
120
  }
105
121
  export type TileScalePlatform = 'web' | 'mobile';
106
122
  export interface AnnotationCanvasState {
@@ -155,6 +171,17 @@ export type AnnotationPatchOp = {
155
171
  } | {
156
172
  op: 'removeMeasurement';
157
173
  id: AnnotationElementId;
174
+ } | {
175
+ op: 'addBackgroundImage';
176
+ image: AnnotationBackgroundLayer;
177
+ index?: number;
178
+ } | {
179
+ op: 'updateBackgroundImage';
180
+ id: AnnotationElementId;
181
+ patch: Partial<Omit<AnnotationBackgroundLayer, 'id'>>;
182
+ } | {
183
+ op: 'removeBackgroundImage';
184
+ id: AnnotationElementId;
158
185
  } | {
159
186
  op: 'setViewport';
160
187
  patch: Partial<AnnotationViewport>;
@@ -1,3 +1,4 @@
1
+ import { backgroundLayersOf } from '../annotation/canvas/backgroundLayers.js';
1
2
  export const DEFAULT_LAYER_ID = 'default';
2
3
  export const createDefaultLayer = () => ({
3
4
  id: DEFAULT_LAYER_ID,
@@ -20,11 +21,40 @@ export const createEmptyCanvasState = (viewport) => ({
20
21
  ...(viewport?.backgroundFit !== undefined
21
22
  ? { backgroundFit: viewport.backgroundFit }
22
23
  : {}),
24
+ ...(viewport?.backgroundImages !== undefined
25
+ ? { backgroundImages: viewport.backgroundImages }
26
+ : {}),
23
27
  },
24
28
  strokes: [],
25
29
  shapes: [],
26
30
  placedMeasurements: [],
27
31
  });
32
+ // Layer 0 projected into the legacy single-slot shape. The transform is
33
+ // dropped — old clients draw the legacy field fit-centered, an accepted
34
+ // degradation. `format` is included only when defined so the mirror survives
35
+ // the write path's JSON round-trip without changing shape.
36
+ const legacyBackgroundOf = (layer) => ({
37
+ storagePath: layer.storagePath,
38
+ downloadUrl: layer.downloadUrl,
39
+ widthPx: layer.widthPx,
40
+ heightPx: layer.heightPx,
41
+ ...(layer.format ? { format: layer.format } : {}),
42
+ });
43
+ // Every background mutation lands here: it writes the (already-normalized)
44
+ // layer array and mirrors layer 0 into the legacy slot, so old clients —
45
+ // which read only `backgroundImage` and rewrite the whole canvas on save —
46
+ // keep rendering the first image instead of persisting its loss. An empty
47
+ // stack sets BOTH fields to explicit `undefined`: the write path's JSON
48
+ // round-trip strips those keys (Firestore rejects undefined values).
49
+ // `backgroundFit` is left alone — it belongs to the legacy render path.
50
+ const withBackgroundLayers = (state, layers) => ({
51
+ ...state,
52
+ viewport: {
53
+ ...state.viewport,
54
+ backgroundImages: layers.length > 0 ? layers : undefined,
55
+ backgroundImage: layers.length > 0 ? legacyBackgroundOf(layers[0]) : undefined,
56
+ },
57
+ });
28
58
  // Apply an in-memory patch to a canvas state. Pure — returns a new object.
29
59
  // Used by the canvas reducer and by InMemoryAnnotationProvider; consumers
30
60
  // can also call this when they receive a patch from the canvas to compute
@@ -71,6 +101,22 @@ const applyOp = (state, op) => {
71
101
  ...state,
72
102
  placedMeasurements: state.placedMeasurements.filter((m) => m.id !== op.id),
73
103
  };
104
+ case 'addBackgroundImage': {
105
+ const layers = backgroundLayersOf(state.viewport);
106
+ // Clamp so a stale index (e.g. an undo replayed after a concurrent
107
+ // removal) can never create a sparse array — a hole becomes `null`
108
+ // through the write path's JSON round-trip and corrupts the doc.
109
+ const index = Math.max(0, Math.min(op.index ?? layers.length, layers.length));
110
+ return withBackgroundLayers(state, [
111
+ ...layers.slice(0, index),
112
+ op.image,
113
+ ...layers.slice(index),
114
+ ]);
115
+ }
116
+ case 'updateBackgroundImage':
117
+ return withBackgroundLayers(state, backgroundLayersOf(state.viewport).map((l) => l.id === op.id ? { ...l, ...op.patch } : l));
118
+ case 'removeBackgroundImage':
119
+ return withBackgroundLayers(state, backgroundLayersOf(state.viewport).filter((l) => l.id !== op.id));
74
120
  case 'setViewport':
75
121
  return { ...state, viewport: { ...state.viewport, ...op.patch } };
76
122
  case 'setTileScaleFactor':
@@ -148,6 +194,28 @@ const invertOp = (before, op) => {
148
194
  }
149
195
  return { op: 'updateMeasurement', id: op.id, patch: inversePatch };
150
196
  }
197
+ case 'addBackgroundImage':
198
+ return { op: 'removeBackgroundImage', id: op.image.id };
199
+ case 'removeBackgroundImage': {
200
+ // Invert against the NORMALIZED stack: the before-state may hold only
201
+ // the legacy single field (the op being inverted is what materialized
202
+ // the array), and undo must still restore that layer — at its index.
203
+ const layers = backgroundLayersOf(before.viewport);
204
+ const index = layers.findIndex((l) => l.id === op.id);
205
+ return index >= 0
206
+ ? { op: 'addBackgroundImage', image: layers[index], index }
207
+ : null;
208
+ }
209
+ case 'updateBackgroundImage': {
210
+ const prev = backgroundLayersOf(before.viewport).find((l) => l.id === op.id);
211
+ if (!prev)
212
+ return null;
213
+ const inversePatch = {};
214
+ for (const k of Object.keys(op.patch)) {
215
+ inversePatch[k] = prev[k];
216
+ }
217
+ return { op: 'updateBackgroundImage', id: op.id, patch: inversePatch };
218
+ }
151
219
  case 'setViewport': {
152
220
  const inversePatch = {};
153
221
  for (const k of Object.keys(op.patch)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reekon-tools/boldr-utils",
3
- "version": "1.10.3",
3
+ "version": "1.12.0",
4
4
  "description": "Shared utilities for formulas and measurement conversion used in Reekon apps",
5
5
  "author": "REEKON Tools",
6
6
  "license": "MIT",
@@ -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;
@@ -1,6 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { AnnotationCanvasInner } from './AnnotationCanvasInner.native.js';
3
- export const AnnotationCanvas = (props) => {
4
- const { fallback: _f, canvasKitOpts: _o, ...rest } = props;
5
- return _jsx(AnnotationCanvasInner, { ...rest });
6
- };
@@ -1,39 +0,0 @@
1
- import { type CSSProperties, type MutableRefObject } from 'react';
2
- import type { DecimalTolerance, FractionalTolerance, Measurement, Units } from '../types/firestore.js';
3
- import type { AnnotationCanvasState, AnnotationDocumentPatch, Selection } from '../types/annotation.js';
4
- import type { MeasurementRef } from './measurementPicker.js';
5
- import type { Tool } from './Tool.js';
6
- import { type AnnotationCanvasHandle } from './useAnnotationCanvasState.js';
7
- import { type ViewportState } from './viewport.js';
8
- import type { RenderMeasurementStamp } from './measurementStampOverlay.js';
9
- export type { AnnotationCanvasHandle };
10
- export interface AnnotationCanvasInnerProps {
11
- canvas: AnnotationCanvasState;
12
- onCommit(patch: AnnotationDocumentPatch): void;
13
- tools: Tool[];
14
- activeToolId: string;
15
- selection: Selection | null;
16
- onSelectionChange(selection: Selection | null): void;
17
- measurements?: Measurement[];
18
- fallbackUnit?: Units;
19
- fractionalTolerance?: FractionalTolerance;
20
- decimalTolerance?: DecimalTolerance;
21
- resolveImageUrl?: (storagePath: string) => Promise<string>;
22
- pickMeasurement?: () => Promise<MeasurementRef | null>;
23
- renderMeasurementStamp?: RenderMeasurementStamp;
24
- stampFontSource?: unknown;
25
- stampValueFontSize?: number;
26
- stampLabelFontSize?: number;
27
- gestures?: GestureConfig;
28
- width: number;
29
- height: number;
30
- initialViewport?: ViewportState;
31
- style?: CSSProperties;
32
- imperativeRef?: MutableRefObject<AnnotationCanvasHandle | null>;
33
- }
34
- export type PanTrigger = 'middleMouse' | 'rightMouse' | 'space';
35
- export interface GestureConfig {
36
- wheel?: 'zoom' | 'pan' | 'auto';
37
- panTriggers?: PanTrigger[];
38
- }
39
- export declare const AnnotationCanvasInner: (props: AnnotationCanvasInnerProps) => import("react/jsx-runtime").JSX.Element;