@cosxai/ui 0.13.1 → 0.13.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosxai/ui",
3
- "version": "0.13.1",
3
+ "version": "0.13.3",
4
4
  "description": "COSX design system — React 19 component primitives shared across product-meta and other consumers",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -156,14 +156,29 @@ export const SignaturePad = forwardRef<SignaturePadHandle, SignaturePadProps>(
156
156
  return;
157
157
  }
158
158
  let cancelled = false;
159
- void fonts.ready.then(() => {
160
- if (cancelled) return;
161
- setFontReady(true);
162
- });
159
+ // Actively REQUEST the typed font. @font-face registration
160
+ // (fontsource CSS etc.) doesn't fetch the file until some
161
+ // rendered text uses it — and this component's only consumer
162
+ // of the font is the canvas, whose fillText silently falls
163
+ // back to the next family when the face isn't loaded yet.
164
+ // fonts.load() forces the fetch; fonts.ready is the safety
165
+ // net for browsers without it / faces not registered.
166
+ const kick =
167
+ typeof fonts.load === "function"
168
+ ? fonts.load(`32px ${typedFontFamily}`).catch(() => [])
169
+ : Promise.resolve([]);
170
+ void kick
171
+ .then(() => fonts.ready)
172
+ .then(() => {
173
+ if (cancelled) return;
174
+ setFontReady(true);
175
+ });
163
176
  return () => {
164
177
  cancelled = true;
165
178
  };
166
- }, [fontReady]);
179
+ // typedFontFamily is config-stable per consumer; re-kicking on
180
+ // change is correct anyway.
181
+ }, [fontReady, typedFontFamily]);
167
182
 
168
183
  // Mode switch — clear the canvas so a stale typed baseline
169
184
  // doesn't hang around behind a drawn signature (or vice versa).
@@ -236,13 +251,21 @@ export const SignaturePad = forwardRef<SignaturePadHandle, SignaturePadProps>(
236
251
  strokeColor,
237
252
  ]);
238
253
 
239
- // Restore a previously-captured value into the canvas on remount.
240
- // Typed mode uses the effect above; drawn mode needs to explicitly
241
- // paint the dataURL back onto the canvas so subsequent strokes
242
- // continue on top of it (rather than starting from blank).
254
+ // Restore a previously-captured value into the canvas on MOUNT
255
+ // only (parent phase round-trips remount the pad). Typed mode
256
+ // uses the repaint effect above; drawn mode needs to explicitly
257
+ // paint the dataURL back so subsequent strokes continue on top.
258
+ //
259
+ // Deliberately NOT re-run on mode switch: the clear effect wipes
260
+ // the canvas on switch, and re-painting the old capture here
261
+ // resurrected the TYPED baseline inside the drawn pad (v0.13.2
262
+ // regression — switching Type → Draw showed the handwriting-font
263
+ // bake as if it had been drawn).
264
+ const didRestoreRef = useRef(false);
243
265
  useEffect(() => {
244
- if (mode !== "drawn") return;
245
- if (!value) return;
266
+ if (didRestoreRef.current) return;
267
+ didRestoreRef.current = true;
268
+ if (mode !== "drawn" || !value) return;
246
269
  const canvas = canvasRef.current;
247
270
  if (!canvas) return;
248
271
  const ctx = canvas.getContext("2d");
@@ -255,7 +278,7 @@ export const SignaturePad = forwardRef<SignaturePadHandle, SignaturePadProps>(
255
278
  };
256
279
  img.src = value;
257
280
  // eslint-disable-next-line react-hooks/exhaustive-deps
258
- }, [mode]);
281
+ }, []);
259
282
 
260
283
  const clear = useCallback(() => {
261
284
  const canvas = canvasRef.current;