@inkra/react 0.3.0 → 0.4.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.
Files changed (226) hide show
  1. package/README.md +15 -3
  2. package/dist/Button.js +3 -2
  3. package/dist/PencilIcon.d.ts +10 -0
  4. package/dist/PencilIcon.js +83 -39
  5. package/dist/PencilLine.js +9 -1
  6. package/dist/PencilText.js +15 -9
  7. package/dist/accordion/Accordion.js +65 -5
  8. package/dist/alert-dialog/index.js +14 -4
  9. package/dist/audio-player/index.d.ts +2 -0
  10. package/dist/audio-player/index.js +126 -31
  11. package/dist/calendar/index.d.ts +4 -1
  12. package/dist/calendar/index.js +31 -12
  13. package/dist/carousel/index.js +13 -6
  14. package/dist/choice-grid/index.d.ts +6 -1
  15. package/dist/choice-grid/index.js +61 -33
  16. package/dist/color-picker/GradientEditor.d.ts +39 -0
  17. package/dist/color-picker/GradientEditor.js +63 -0
  18. package/dist/color-picker/gradient.d.ts +25 -0
  19. package/dist/color-picker/gradient.js +60 -0
  20. package/dist/color-picker/index.d.ts +27 -7
  21. package/dist/color-picker/index.js +134 -32
  22. package/dist/color-swatch/index.d.ts +10 -0
  23. package/dist/color-swatch/index.js +9 -0
  24. package/dist/command/index.js +27 -4
  25. package/dist/content/index.d.ts +5 -0
  26. package/dist/content/index.js +12 -3
  27. package/dist/context-menu/index.js +1 -1
  28. package/dist/data-table/index.d.ts +7 -1
  29. package/dist/data-table/index.js +64 -16
  30. package/dist/data-table/model.d.ts +2 -0
  31. package/dist/data-table/model.js +7 -0
  32. package/dist/date-picker/index.d.ts +2 -0
  33. package/dist/date-picker/index.js +10 -9
  34. package/dist/day-agenda/index.d.ts +6 -1
  35. package/dist/day-agenda/index.js +46 -21
  36. package/dist/day-agenda/layout.js +65 -9
  37. package/dist/display/ProgressRing.js +7 -3
  38. package/dist/display/index.d.ts +4 -1
  39. package/dist/display/index.js +3 -2
  40. package/dist/dockable-panel/index.js +5 -5
  41. package/dist/drawer/index.js +4 -3
  42. package/dist/editable/index.d.ts +24 -2
  43. package/dist/editable/index.js +142 -37
  44. package/dist/empty/illustrations.d.ts +8 -0
  45. package/dist/empty/illustrations.js +78 -0
  46. package/dist/empty/index.d.ts +6 -1
  47. package/dist/empty/index.js +11 -3
  48. package/dist/flip/index.js +28 -8
  49. package/dist/form/Field.js +11 -10
  50. package/dist/form/Textarea.d.ts +3 -1
  51. package/dist/form/Textarea.js +6 -4
  52. package/dist/hotkey/index.d.ts +1 -0
  53. package/dist/hotkey/index.js +6 -4
  54. package/dist/hover-card/index.js +1 -1
  55. package/dist/image-cropper/index.js +4 -2
  56. package/dist/image-viewer/VideoResource.d.ts +21 -0
  57. package/dist/image-viewer/VideoResource.js +194 -0
  58. package/dist/image-viewer/index.d.ts +14 -0
  59. package/dist/image-viewer/index.js +118 -65
  60. package/dist/image-viewer/lucideGeometry.d.ts +3 -0
  61. package/dist/image-viewer/lucideGeometry.js +19 -0
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.js +2 -0
  64. package/dist/input-number/index.js +9 -4
  65. package/dist/internal/cache.d.ts +4 -0
  66. package/dist/internal/cache.js +18 -1
  67. package/dist/internal/iconGeometry.d.ts +2 -0
  68. package/dist/internal/iconGeometry.js +32 -0
  69. package/dist/internal/renderTokens.d.ts +4 -0
  70. package/dist/internal/renderTokens.js +4 -0
  71. package/dist/internal/stroke.d.ts +7 -0
  72. package/dist/internal/stroke.js +63 -7
  73. package/dist/internal/surfaceBudget.d.ts +20 -0
  74. package/dist/internal/surfaceBudget.js +69 -0
  75. package/dist/internal/surfaceRenderer.js +87 -28
  76. package/dist/internal/surfaceVisibility.d.ts +3 -0
  77. package/dist/internal/surfaceVisibility.js +60 -0
  78. package/dist/internal/textRenderer.d.ts +2 -0
  79. package/dist/internal/textRenderer.js +3 -3
  80. package/dist/internal/usePencilSurface.js +17 -1
  81. package/dist/internal/wash.js +4 -3
  82. package/dist/journey-map/terrain.js +10 -5
  83. package/dist/layout/index.js +1 -1
  84. package/dist/log/index.js +29 -22
  85. package/dist/markdown/CodeBlock.d.ts +19 -0
  86. package/dist/markdown/CodeBlock.js +86 -0
  87. package/dist/markdown/Deletion.d.ts +2 -0
  88. package/dist/markdown/Deletion.js +69 -0
  89. package/dist/markdown/icons.d.ts +14 -0
  90. package/dist/markdown/icons.js +16 -0
  91. package/dist/markdown/index.d.ts +17 -0
  92. package/dist/markdown/index.js +175 -0
  93. package/dist/node-graph/index.d.ts +2 -0
  94. package/dist/node-graph/index.js +49 -25
  95. package/dist/number-ticker/precision.js +9 -2
  96. package/dist/overflow-list/index.js +5 -2
  97. package/dist/overlay/Overlay.js +1 -1
  98. package/dist/overlay/Popover.js +3 -3
  99. package/dist/overlay/themeBridge.js +14 -2
  100. package/dist/pagination/index.d.ts +10 -1
  101. package/dist/pagination/index.js +6 -4
  102. package/dist/pan-zoom/index.d.ts +2 -0
  103. package/dist/pan-zoom/index.js +7 -6
  104. package/dist/pencil.d.ts +1 -0
  105. package/dist/pencil.js +4 -4
  106. package/dist/radar/index.d.ts +2 -0
  107. package/dist/radar/index.js +40 -20
  108. package/dist/rating/index.d.ts +4 -0
  109. package/dist/rating/index.js +128 -24
  110. package/dist/resizable/index.js +1 -1
  111. package/dist/scroll-area/index.d.ts +1 -1
  112. package/dist/scroll-area/index.js +5 -2
  113. package/dist/select/MultipleSelect.js +5 -4
  114. package/dist/select/SearchableSelect.js +61 -6
  115. package/dist/select/Select.d.ts +1 -0
  116. package/dist/select/Select.js +3 -1
  117. package/dist/select/StepperSelect.d.ts +29 -0
  118. package/dist/select/StepperSelect.js +119 -0
  119. package/dist/select/index.d.ts +2 -0
  120. package/dist/select/index.js +1 -0
  121. package/dist/select/stepperModel.d.ts +7 -0
  122. package/dist/select/stepperModel.js +29 -0
  123. package/dist/selection/Checkbox.js +4 -3
  124. package/dist/selection/RadioGroup.js +29 -13
  125. package/dist/selection/Switch.js +4 -3
  126. package/dist/selection/useNativeChecked.d.ts +2 -1
  127. package/dist/selection/useNativeChecked.js +26 -8
  128. package/dist/slider/index.js +50 -6
  129. package/dist/sortable/index.js +6 -2
  130. package/dist/sparkline/index.js +17 -2
  131. package/dist/sparkline/model.js +11 -4
  132. package/dist/styles/accordion.css +228 -0
  133. package/dist/styles/alert-dialog.css +139 -0
  134. package/dist/styles/audio-player.css +22 -0
  135. package/dist/styles/base.css +638 -0
  136. package/dist/styles/breadcrumb.css +7 -0
  137. package/dist/styles/calendar.css +62 -0
  138. package/dist/styles/carousel.css +173 -0
  139. package/dist/styles/choice-grid.css +226 -0
  140. package/dist/styles/color-picker.css +281 -0
  141. package/dist/styles/color-swatch.css +47 -0
  142. package/dist/styles/command.css +52 -0
  143. package/dist/styles/content.css +79 -0
  144. package/dist/styles/context-menu.css +35 -0
  145. package/dist/styles/data-table.css +31 -0
  146. package/dist/styles/date-picker.css +107 -0
  147. package/dist/styles/day-agenda.css +30 -0
  148. package/dist/styles/display.css +304 -0
  149. package/dist/styles/dockable-panel.css +110 -0
  150. package/dist/styles/drawer.css +133 -0
  151. package/dist/styles/dropdown-menu.css +68 -0
  152. package/dist/styles/editable.css +145 -0
  153. package/dist/styles/empty.css +108 -0
  154. package/dist/styles/flip.css +101 -0
  155. package/dist/styles/form.css +634 -0
  156. package/dist/styles/heatmap.css +256 -0
  157. package/dist/styles/hotkey.css +189 -0
  158. package/dist/styles/hover-card.css +27 -0
  159. package/dist/styles/image-cropper.css +205 -0
  160. package/dist/styles/image-viewer.css +422 -0
  161. package/dist/styles/input-number.css +21 -0
  162. package/dist/styles/journey-map.css +19 -0
  163. package/dist/styles/keypad.css +92 -0
  164. package/dist/styles/layout.css +72 -0
  165. package/dist/styles/leaflet.css +661 -0
  166. package/dist/styles/log.css +203 -0
  167. package/dist/styles/markdown.css +379 -0
  168. package/dist/styles/node-graph.css +142 -0
  169. package/dist/styles/number-ticker.css +83 -0
  170. package/dist/styles/overflow-list.css +51 -0
  171. package/dist/styles/overlay.css +284 -0
  172. package/dist/styles/pagination.css +21 -0
  173. package/dist/styles/pan-zoom.css +136 -0
  174. package/dist/styles/radar.css +1 -0
  175. package/dist/styles/rating.css +121 -0
  176. package/dist/styles/resizable.css +108 -0
  177. package/dist/styles/scroll-area.css +122 -0
  178. package/dist/styles/select.css +557 -0
  179. package/dist/styles/selection.css +264 -0
  180. package/dist/styles/slider.css +145 -0
  181. package/dist/styles/slot.css +109 -0
  182. package/dist/styles/sortable.css +159 -0
  183. package/dist/styles/sparkline.css +24 -0
  184. package/dist/styles/steps.css +27 -0
  185. package/dist/styles/tabs.css +79 -0
  186. package/dist/styles/tag-input.css +213 -0
  187. package/dist/styles/time-picker.css +66 -0
  188. package/dist/styles/timeline.css +24 -0
  189. package/dist/styles/toast.css +160 -0
  190. package/dist/styles/toggle-group.css +107 -0
  191. package/dist/styles/tooltip.css +78 -0
  192. package/dist/styles/transfer.css +148 -0
  193. package/dist/styles/tree-select.css +131 -0
  194. package/dist/styles/tree.css +59 -0
  195. package/dist/styles/typewriter.css +62 -0
  196. package/dist/styles/upload.css +60 -0
  197. package/dist/styles/virtual-list.css +112 -0
  198. package/dist/styles.css +2094 -421
  199. package/dist/tabs/index.d.ts +9 -0
  200. package/dist/tabs/index.js +66 -16
  201. package/dist/tabs/useOverflow.d.ts +20 -0
  202. package/dist/tabs/useOverflow.js +145 -0
  203. package/dist/tag-input/index.d.ts +7 -2
  204. package/dist/tag-input/index.js +70 -26
  205. package/dist/time-picker/index.js +38 -20
  206. package/dist/toast/index.d.ts +19 -11
  207. package/dist/toast/index.js +48 -8
  208. package/dist/toast/manager.d.ts +42 -0
  209. package/dist/toast/manager.js +85 -0
  210. package/dist/toast/useToastNavigation.d.ts +3 -0
  211. package/dist/toast/useToastNavigation.js +75 -0
  212. package/dist/tokens.css +4 -0
  213. package/dist/tooltip/Tooltip.js +3 -3
  214. package/dist/transfer/index.d.ts +5 -0
  215. package/dist/transfer/index.js +93 -29
  216. package/dist/transfer/model.d.ts +4 -0
  217. package/dist/transfer/model.js +37 -11
  218. package/dist/tree/index.d.ts +5 -1
  219. package/dist/tree/index.js +5 -5
  220. package/dist/tree-select/index.js +67 -30
  221. package/dist/tree-select/model.d.ts +11 -5
  222. package/dist/tree-select/model.js +111 -83
  223. package/dist/typewriter/index.js +10 -10
  224. package/dist/upload/index.js +30 -16
  225. package/dist/virtual-list/index.js +7 -5
  226. package/package.json +413 -3
package/README.md CHANGED
@@ -348,7 +348,18 @@ All examples use public sample data.
348
348
 
349
349
  `PencilText` keeps its string in the DOM for selection, copy, search, and assistive technology. Browser enhancement paints the same glyph geometry with the traced C lettering preset; unavailable canvas or font loading leaves the readable DOM fallback visible.
350
350
 
351
- `PencilIcon` accepts familiar SVG path data and deposits pigment along sampled geometry. See `docs/pencil-contract.md` in the workspace for the current alpha contract.
351
+ `PencilIcon` accepts familiar SVG path data and deposits pigment along sampled geometry.
352
+ Use `size` for the displayed canvas size (default 40px), `strokeWidth` (0.3-3,
353
+ default 1.5), `intensity` (0-1, default 1), `breaks` (0-1, default 0.18),
354
+ `pressureVariation` (0-1, default 0.82), and `breakCount` (integer 0-12, default 1).
355
+ The gap count is a maximum: short displayed paths use fewer or no gaps, while
356
+ larger icons can use more up to that limit. Device pixel ratio does not change
357
+ the count. Zero breaks or zero breakCount removes intentional gaps; zero pressure
358
+ variation keeps constant pressure without removing the grain or endpoint taper.
359
+ The matching inherited tokens are `--inkra-icon-stroke-width`,
360
+ `--inkra-icon-intensity`, `--inkra-icon-breaks`, `--inkra-icon-pressure-variation`
361
+ and `--inkra-icon-break-count`. Finite explicit props override those tokens.
362
+ See `docs/pencil-contract.md` in the workspace for the material contract.
352
363
 
353
364
  `PencilLine` is an aria-hidden canvas decoration using the same pressure, grain, breaks, and repeated-pass renderer as surfaces and icons. It defaults to horizontal and supports a native vertical stroke without rotating a horizontal bitmap:
354
365
 
@@ -367,8 +378,9 @@ text within that scope without changing layout, selectable text, or labels.
367
378
 
368
379
  `PencilLine` reads `--inkra-stroke-width` (0.3-3) and
369
380
  `--inkra-stroke-amplitude` (0-4) when `weight` or `roughness` is omitted.
370
- Explicit props take precedence. These tokens also scale surface outlines and icon
371
- strokes relative to their original presets, including button/card/input borders.
381
+ Explicit props take precedence. These tokens also scale surface outlines,
382
+ including button/card/input borders. Icon weight uses its independent token;
383
+ the general amplitude token still controls icon roughness.
372
384
  The default values are 2 and 1.5. They do not change layout or text lettering.
373
385
 
374
386
  Surface fills read `--inkra-wash-strength` (0-1, default 0.8),
package/dist/Button.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { createContext, forwardRef, useContext, useId } from 'react';
2
+ import { createContext, forwardRef, useContext, useId, useMemo } from 'react';
3
3
  import { PencilSurface } from './PencilSurface.js';
4
4
  import { PencilText } from './PencilText.js';
5
5
  import { PencilLine } from './PencilLine.js';
@@ -85,5 +85,6 @@ export const ButtonLink = forwardRef(function ButtonLink({ children, 'aria-label
85
85
  export const ButtonGroup = forwardRef(function ButtonGroup({ children, className, orientation = 'horizontal', size, variant, disabled = false, fullWidth = false, equalWidth = false, ...props }, ref) {
86
86
  const classes = ['inkra-button-group', className].filter(Boolean).join(' ');
87
87
  const id = useId();
88
- return (_jsx(ButtonGroupContext.Provider, { value: { orientation, size, variant, disabled }, children: _jsx(PencilSurface, { ...props, className: classes, "data-orientation": orientation, "data-full-width": fullWidth || undefined, "data-equal-width": equalWidth || undefined, "aria-disabled": disabled || undefined, radius: 8, ref: ref, role: "group", seed: `inkra-button-group-${id}`, variant: "outline", children: children }) }));
88
+ const context = useMemo(() => ({ orientation, size, variant, disabled }), [orientation, size, variant, disabled]);
89
+ return (_jsx(ButtonGroupContext.Provider, { value: context, children: _jsx(PencilSurface, { ...props, className: classes, "data-orientation": orientation, "data-full-width": fullWidth || undefined, "data-equal-width": equalWidth || undefined, "aria-disabled": disabled || undefined, radius: 8, ref: ref, role: "group", seed: `inkra-button-group-${id}`, variant: "outline", children: children }) }));
89
90
  });
@@ -17,6 +17,16 @@ export interface PencilIconProps extends Omit<CanvasHTMLAttributes<HTMLCanvasEle
17
17
  size?: number;
18
18
  sourceSize?: number;
19
19
  active?: boolean;
20
+ /** Overrides icon/theme stroke weight. Clamped to 0.3-3. */
21
+ strokeWidth?: number;
22
+ /** Pigment opacity, clamped to 0-1. Defaults to the icon intensity token or 1. */
23
+ intensity?: number;
24
+ /** Stroke gap amount, clamped to 0-1. Zero disables gaps, not pigment grain. */
25
+ breaks?: number;
26
+ /** Maximum gaps per continuous path, limited by displayed length; floored/clamped to 0-12, default 1. */
27
+ breakCount?: number;
28
+ /** Pressure-driven stroke width variation, clamped to 0-1. Defaults to 0.82. */
29
+ pressureVariation?: number;
20
30
  decorative?: boolean;
21
31
  }
22
32
  export declare const PencilIcon: import("react").ForwardRefExoticComponent<PencilIconProps & import("react").RefAttributes<HTMLCanvasElement>>;
@@ -1,32 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
3
- import { BoundedCache, PENCIL_RENDERER_VERSION, canvasByteCost, canvasIsUsable, effectiveDpr, } from './internal/cache.js';
3
+ import { BoundedCache, PENCIL_RENDERER_VERSION, boundedCanvasSize, canvasByteCost, canvasIsUsable, effectiveDpr, } from './internal/cache.js';
4
4
  import { observeCanvasRecovery } from './internal/recovery.js';
5
5
  import { seedToNumber } from './internal/random.js';
6
6
  import { pencilStroke } from './internal/stroke.js';
7
7
  import { maxStrokeRoughness, pencilRenderDefaults, readRenderToken } from './internal/renderTokens.js';
8
- const geometryCache = new BoundedCache(256, 180000);
8
+ import { cachedPathParts } from './internal/iconGeometry.js';
9
9
  const imageCache = new BoundedCache(128, 24 * 1024 * 1024);
10
- function cachedPathParts(data) {
11
- const key = `${PENCIL_RENDERER_VERSION}:${data}`;
12
- const cached = geometryCache.get(key);
13
- if (cached)
14
- return cached;
15
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
16
- path.setAttribute('d', data);
17
- const length = path.getTotalLength();
18
- const count = Math.max(1, Math.ceil(length / 0.25));
19
- const parts = [[]];
20
- for (let index = 0; index <= count; index += 1) {
21
- const point = path.getPointAtLength(length * index / count);
22
- const activePart = parts[parts.length - 1];
23
- const previous = activePart[activePart.length - 1];
24
- if (previous && Math.hypot(point.x - previous[0], point.y - previous[1]) > 0.5)
25
- parts.push([]);
26
- parts[parts.length - 1].push([point.x, point.y]);
27
- }
28
- return geometryCache.set(key, parts, parts.reduce((total, part) => total + part.length, 0));
29
- }
30
10
  function drawFallbackPath(context, data, color, mode, weightScale = 1) {
31
11
  try {
32
12
  const path = new Path2D(data);
@@ -47,9 +27,18 @@ function renderIcon(paths, options) {
47
27
  const { seed, size, sourceSize, active, color, dpr } = options;
48
28
  const weightScale = options.weightScale ?? 1;
49
29
  const roughnessScale = options.roughnessScale ?? 1;
30
+ const breaks = options.breaks ?? pencilRenderDefaults.iconBreaks;
31
+ const breakCount = options.breakCount ?? pencilRenderDefaults.iconBreakCount;
32
+ const pressureVariation = options.pressureVariation ?? pencilRenderDefaults.iconPressureVariation;
33
+ const variationScale = pressureVariation / pencilRenderDefaults.iconPressureVariation;
34
+ const pixels = Math.round(size * dpr);
35
+ boundedCanvasSize(pixels, pixels, 1);
36
+ if (!Number.isFinite(sourceSize) || sourceSize <= 0 || sourceSize > 4096 || paths.length > 1024) {
37
+ throw new RangeError('Pencil icon exceeds the bounded geometry budget');
38
+ }
50
39
  const canvas = document.createElement('canvas');
51
- canvas.width = Math.round(size * dpr);
52
- canvas.height = Math.round(size * dpr);
40
+ canvas.width = pixels;
41
+ canvas.height = pixels;
53
42
  const context = canvas.getContext('2d');
54
43
  if (!context)
55
44
  return canvas;
@@ -57,7 +46,10 @@ function renderIcon(paths, options) {
57
46
  context.scale(dpr * coordinateScale, dpr * coordinateScale);
58
47
  const offset = (40 - sourceSize) / 2;
59
48
  context.translate(offset, offset);
49
+ let sampledPoints = 0;
60
50
  paths.forEach((path, index) => {
51
+ if (path.d.length > 65536)
52
+ return;
61
53
  const mode = path.mode ?? 'stroke';
62
54
  const pathSeed = seed + index * 137;
63
55
  if (mode === 'wash') {
@@ -66,7 +58,7 @@ function renderIcon(paths, options) {
66
58
  context.clip(new Path2D(path.d));
67
59
  context.globalAlpha = active ? 0.76 : 0.34;
68
60
  for (let y = sourceSize * 0.18; y < sourceSize; y += 1.6) {
69
- pencilStroke(context, [[sourceSize * 0.15, y + 3], [sourceSize * 0.85, y - 3]], color, 1.1, pathSeed + Math.round(y * 17), 0.25, 2, false, { variation: 0.85, breaks: 0.25, grain: 0.7, gain: 2.2, step: 0.35 });
61
+ pencilStroke(context, [[sourceSize * 0.15, y + 3], [sourceSize * 0.85, y - 3]], color, 1.1 * weightScale / (pencilRenderDefaults.strokeWidth / 1.2), pathSeed + Math.round(y * 17), 0.25, 2, false, { variation: Math.min(1, 0.85 * variationScale), fitPressure: true, breaks, breakCount, breakScale: coordinateScale, continuousBreaks: true, grain: 0.7, gain: 2.2, step: 0.35 });
70
62
  }
71
63
  context.restore();
72
64
  }
@@ -76,11 +68,24 @@ function renderIcon(paths, options) {
76
68
  return;
77
69
  }
78
70
  try {
71
+ if (sampledPoints >= 65536) {
72
+ drawFallbackPath(context, path.d, color, mode, weightScale);
73
+ return;
74
+ }
79
75
  const parts = cachedPathParts(path.d);
76
+ sampledPoints += parts.reduce((total, part) => total + part.length, 0);
77
+ if (sampledPoints > 65536) {
78
+ drawFallbackPath(context, path.d, color, mode, weightScale);
79
+ return;
80
+ }
80
81
  for (const points of parts) {
81
82
  pencilStroke(context, points, color, (mode === 'scuff' ? 0.55 : 1.65) * weightScale, pathSeed, 0.3 * roughnessScale, mode === 'scuff' ? 1 : 3, false, {
82
- variation: 0.82,
83
- breaks: 0.18,
83
+ variation: pressureVariation,
84
+ fitPressure: true,
85
+ breaks,
86
+ breakCount,
87
+ breakScale: coordinateScale,
88
+ continuousBreaks: true,
84
89
  grain: 0.68,
85
90
  gain: mode === 'scuff' ? 1.5 : 3,
86
91
  step: 0.35,
@@ -93,7 +98,7 @@ function renderIcon(paths, options) {
93
98
  }
94
99
  });
95
100
  if (active) {
96
- pencilStroke(context, [[sourceSize * 0.2, sourceSize - 2], [sourceSize * 0.5, sourceSize - 1.5], [sourceSize * 0.8, sourceSize - 2.2]], color, 1.1 * weightScale, seed + 977, 0.25 * roughnessScale, 3, false, { variation: 0.8, breaks: 0.25, grain: 0.7, gain: 2.8, step: 0.35 });
101
+ pencilStroke(context, [[sourceSize * 0.2, sourceSize - 2], [sourceSize * 0.5, sourceSize - 1.5], [sourceSize * 0.8, sourceSize - 2.2]], color, 1.1 * weightScale, seed + 977, 0.25 * roughnessScale, 3, false, { variation: 0.8 * variationScale, fitPressure: true, breaks, breakCount, breakScale: coordinateScale, continuousBreaks: true, grain: 0.7, gain: 2.8, step: 0.35 });
97
102
  }
98
103
  if (options.badge?.paths.length) {
99
104
  const badge = options.badge;
@@ -106,7 +111,7 @@ function renderIcon(paths, options) {
106
111
  const y = position.startsWith('top') ? 0 : 40 - extent;
107
112
  const badgeImage = renderIcon(badge.paths, {
108
113
  seed: seed + 1543, size: size * scale, sourceSize: badgeSourceSize, active: false, color, dpr,
109
- weightScale: weightScale / scale, roughnessScale,
114
+ weightScale: weightScale / scale, roughnessScale, breaks, breakCount, pressureVariation,
110
115
  });
111
116
  const mask = document.createElement('canvas');
112
117
  mask.width = badgeImage.width;
@@ -135,29 +140,47 @@ function renderIcon(paths, options) {
135
140
  }
136
141
  return canvas;
137
142
  }
138
- export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge, className = '', decorative = true, paths, seed, size = 40, sourceSize = 32, ...props }, forwardedRef) {
143
+ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge, className = '', decorative = true, paths, seed, size = 40, sourceSize = 32, strokeWidth, intensity, breaks, breakCount, pressureVariation, ...props }, forwardedRef) {
139
144
  const canvasRef = useRef(null);
140
145
  const lastPaintRef = useRef('');
141
146
  useImperativeHandle(forwardedRef, () => canvasRef.current);
142
- const geometryKey = useMemo(() => JSON.stringify(paths), [paths]);
143
- const badgeKey = useMemo(() => badge ? JSON.stringify(badge) : '', [badge]);
147
+ // Compare values, not caller object identity. Serializing each render also
148
+ // detects in-place edits; the effect receives a stable snapshot of that value.
149
+ const geometryKey = JSON.stringify(paths);
150
+ const badgeKey = badge ? JSON.stringify(badge) : '';
151
+ const stablePaths = useMemo(() => JSON.parse(geometryKey), [geometryKey]);
152
+ const stableBadge = useMemo(() => badgeKey ? JSON.parse(badgeKey) : undefined, [badgeKey]);
144
153
  const resolvedSeed = seedToNumber(seed ?? geometryKey);
145
154
  useEffect(() => {
146
155
  const canvas = canvasRef.current;
147
156
  if (!canvas)
148
157
  return undefined;
149
158
  let disposed = false;
150
- const paint = () => {
159
+ const paintIcon = () => {
151
160
  if (disposed)
152
161
  return;
153
162
  const dpr = effectiveDpr(3);
154
163
  const computed = getComputedStyle(canvas);
155
164
  const color = computed.color;
156
165
  const rtl = computed.direction === 'rtl';
157
- const weightScale = readRenderToken(computed, '--inkra-stroke-width', pencilRenderDefaults.strokeWidth, 0.3, 3) / 1.2;
166
+ const iconWeight = readRenderToken(computed, '--inkra-icon-stroke-width', pencilRenderDefaults.iconStrokeWidth, 0.3, 3);
167
+ const weightScale = (typeof strokeWidth === 'number' && Number.isFinite(strokeWidth) ? Math.min(3, Math.max(0.3, strokeWidth)) : iconWeight) / 1.2;
168
+ const pigmentOpacity = typeof intensity === 'number' && Number.isFinite(intensity)
169
+ ? Math.min(1, Math.max(0, intensity))
170
+ : readRenderToken(computed, '--inkra-icon-intensity', 1, 0, 1);
158
171
  const roughnessScale = readRenderToken(computed, '--inkra-stroke-amplitude', pencilRenderDefaults.strokeRoughness, 0, maxStrokeRoughness) / 0.6;
172
+ const strokeBreaks = typeof breaks === 'number' && Number.isFinite(breaks)
173
+ ? Math.min(1, Math.max(0, breaks))
174
+ : readRenderToken(computed, '--inkra-icon-breaks', pencilRenderDefaults.iconBreaks, 0, 1);
175
+ const strokeVariation = typeof pressureVariation === 'number' && Number.isFinite(pressureVariation)
176
+ ? Math.min(1, Math.max(0, pressureVariation))
177
+ : readRenderToken(computed, '--inkra-icon-pressure-variation', pencilRenderDefaults.iconPressureVariation, 0, 1);
178
+ const strokeBreakCount = Math.floor(typeof breakCount === 'number' && Number.isFinite(breakCount)
179
+ ? Math.min(12, Math.max(0, breakCount))
180
+ : readRenderToken(computed, '--inkra-icon-break-count', pencilRenderDefaults.iconBreakCount, 0, 12));
159
181
  const key = JSON.stringify([
160
182
  PENCIL_RENDERER_VERSION,
183
+ 'icon-path-pressure-gaps-v4',
161
184
  geometryKey,
162
185
  badgeKey,
163
186
  rtl,
@@ -167,17 +190,19 @@ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge
167
190
  active,
168
191
  color,
169
192
  dpr,
170
- weightScale, roughnessScale,
193
+ weightScale, roughnessScale, strokeBreaks, strokeBreakCount, strokeVariation,
171
194
  ]);
172
- if (key === lastPaintRef.current && canvasIsUsable(canvas))
195
+ const paintKey = `${key}:${pigmentOpacity}`;
196
+ if (paintKey === lastPaintRef.current && canvasIsUsable(canvas))
173
197
  return;
198
+ delete canvas.dataset.pencilIconReady;
174
199
  let image = imageCache.get(key);
175
200
  if (image && !canvasIsUsable(image)) {
176
201
  imageCache.delete(key);
177
202
  image = undefined;
178
203
  }
179
204
  if (!image) {
180
- image = renderIcon(paths, { seed: resolvedSeed, size, sourceSize, active, color, dpr, badge, rtl, weightScale, roughnessScale });
205
+ image = renderIcon(stablePaths, { seed: resolvedSeed, size, sourceSize, active, color, dpr, badge: stableBadge, rtl, weightScale, roughnessScale, breaks: strokeBreaks, breakCount: strokeBreakCount, pressureVariation: strokeVariation });
181
206
  imageCache.set(key, image, canvasByteCost(image));
182
207
  }
183
208
  canvas.width = image.width;
@@ -185,9 +210,27 @@ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge
185
210
  const context = canvas.getContext('2d');
186
211
  if (!context || context.isContextLost?.())
187
212
  return;
213
+ // Composite once so overlapping strokes and badges share the same intensity.
214
+ context.save();
215
+ context.globalAlpha = pigmentOpacity;
188
216
  context.drawImage(image, 0, 0);
189
- lastPaintRef.current = key;
217
+ context.restore();
218
+ lastPaintRef.current = paintKey;
219
+ canvas.dataset.pencilIconReady = 'true';
220
+ };
221
+ const paint = () => {
222
+ try {
223
+ paintIcon();
224
+ }
225
+ catch {
226
+ // Invalid/oversized decoration must not throw from a React effect.
227
+ canvas.width = canvas.height = 0;
228
+ lastPaintRef.current = '';
229
+ delete canvas.dataset.pencilIconReady;
230
+ }
190
231
  };
232
+ const onContextLost = () => { delete canvas.dataset.pencilIconReady; lastPaintRef.current = ''; };
233
+ canvas.addEventListener('contextlost', onContextLost);
191
234
  const inheritedColor = new MutationObserver(paint);
192
235
  for (let element = canvas; element; element = element.parentElement) {
193
236
  inheritedColor.observe(element, { attributes: true, attributeFilter: ['style', 'class', 'dir', 'data-theme', 'data-inkra-theme'] });
@@ -198,11 +241,12 @@ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge
198
241
  paint();
199
242
  return () => {
200
243
  disposed = true;
244
+ canvas.removeEventListener('contextlost', onContextLost);
201
245
  stopRecovery();
202
246
  inheritedColor.disconnect();
203
247
  colorScheme.removeEventListener('change', paint);
204
248
  };
205
- }, [active, badge, badgeKey, geometryKey, paths, resolvedSeed, size, sourceSize]);
249
+ }, [active, stableBadge, badgeKey, geometryKey, stablePaths, resolvedSeed, size, sourceSize, strokeWidth, intensity, breaks, breakCount, pressureVariation]);
206
250
  const accessibilityProps = decorative
207
251
  ? { 'aria-hidden': true }
208
252
  : { role: props.role ?? 'img' };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
3
- import { BoundedCache, PENCIL_RENDERER_VERSION, canvasByteCost, canvasIsUsable, effectiveDpr, } from './internal/cache.js';
3
+ import { BoundedCache, PENCIL_RENDERER_VERSION, boundedCanvasSize, canvasByteCost, canvasIsUsable, effectiveDpr, } from './internal/cache.js';
4
4
  import { observeCanvasRecovery } from './internal/recovery.js';
5
5
  import { seedToNumber } from './internal/random.js';
6
6
  import { createPencilLinePoints, pencilLineHasDrawableSize } from './internal/line.js';
@@ -37,6 +37,14 @@ export const PencilLine = forwardRef(function PencilLine({ className = '', orien
37
37
  if (!pencilLineHasDrawableSize(width, height, orientation))
38
38
  return;
39
39
  const dpr = effectiveDpr(2);
40
+ try {
41
+ boundedCanvasSize(width, height, dpr);
42
+ }
43
+ catch {
44
+ canvas.width = canvas.height = 0;
45
+ lastPaint = '';
46
+ return;
47
+ }
40
48
  const style = getComputedStyle(canvas);
41
49
  const color = style.color;
42
50
  const resolvedWeight = weight ?? readRenderToken(style, '--inkra-stroke-width', pencilRenderDefaults.strokeWidth, 0.3, 3);
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
3
3
  import { useBrowserLayoutEffect } from './internal/useBrowserLayoutEffect.js';
4
- import { effectiveDpr } from './internal/cache.js';
4
+ import { boundedCanvasSize, effectiveDpr } from './internal/cache.js';
5
5
  import { observeCanvasRecovery } from './internal/recovery.js';
6
6
  import { seedToNumber } from './internal/random.js';
7
7
  import { pencilLettering, renderPencilText } from './internal/textRenderer.js';
@@ -108,8 +108,9 @@ function paintText(element, source, canvas, text, seed, glyphSeed) {
108
108
  const dpr = effectiveDpr(2);
109
109
  if (width * height * dpr ** 2 > 2 * 1024 * 1024 || !context || context.isContextLost?.())
110
110
  return false;
111
- canvas.width = Math.ceil(width * dpr);
112
- canvas.height = Math.ceil(height * dpr);
111
+ const pixels = boundedCanvasSize(width, height, dpr);
112
+ canvas.width = pixels.width;
113
+ canvas.height = pixels.height;
113
114
  Object.assign(canvas.style, {
114
115
  left: `-${padding}px`,
115
116
  top: `-${padding}px`,
@@ -119,7 +120,7 @@ function paintText(element, source, canvas, text, seed, glyphSeed) {
119
120
  height: `${height}px`,
120
121
  });
121
122
  context.scale(dpr, dpr);
122
- context.font = `400 ${size}px ${style.fontFamily}`;
123
+ context.font = `${style.fontStyle} ${style.fontWeight} ${size}px ${style.fontFamily}`;
123
124
  context.fontKerning = 'none';
124
125
  const ascent = context.measureText(text).fontBoundingBoxAscent ?? size;
125
126
  const lines = measureLocalTextLines(element, source, text, localWidth, localHeight);
@@ -133,6 +134,8 @@ function paintText(element, source, canvas, text, seed, glyphSeed) {
133
134
  glyphSeed,
134
135
  color: style.color,
135
136
  family: style.fontFamily,
137
+ weight: style.fontWeight,
138
+ style: style.fontStyle,
136
139
  dpr,
137
140
  settings: {
138
141
  variation: readRenderToken(style, '--inkra-letter-variation', pencilLettering.variation, 0, 1),
@@ -176,7 +179,7 @@ const PencilTextImplementation = forwardRef(function PencilText({ as: Element =
176
179
  const context = canvas.getContext('2d');
177
180
  if (!context || context.isContextLost?.())
178
181
  return;
179
- context.font = `400 ${style.fontSize} ${style.fontFamily}`;
182
+ context.font = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
180
183
  context.fontKerning = 'none';
181
184
  const measure = (value) => context.measureText(value).width;
182
185
  if (measure(children) <= width) {
@@ -248,8 +251,8 @@ const PencilTextImplementation = forwardRef(function PencilText({ as: Element =
248
251
  return JSON.stringify([
249
252
  localBox.width, localBox.height, effectiveDpr(2),
250
253
  style.fontSize && style.fontFamily
251
- ? document.fonts?.check(`400 ${style.fontSize} ${style.fontFamily}`, text) : false,
252
- ...['color', 'font-family', 'font-size', 'line-height', 'text-align', 'text-indent',
254
+ ? document.fonts?.check(`${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`, text) : false,
255
+ ...['color', 'font-family', 'font-size', 'font-weight', 'font-style', 'line-height', 'text-align', 'text-indent',
253
256
  'letter-spacing', 'white-space', 'word-break', 'overflow-wrap', 'direction',
254
257
  'padding-top', 'padding-left', 'padding-right', 'padding-bottom',
255
258
  '--inkra-letter-variation', '--inkra-letter-breaks', '--inkra-letter-outline',
@@ -260,12 +263,15 @@ const PencilTextImplementation = forwardRef(function PencilText({ as: Element =
260
263
  pending = null;
261
264
  if (!disposed && visible && dirty) {
262
265
  try {
263
- if (paintText(node, source, canvas, text, resolvedSeed, glyphSeed))
264
- lastAppearance = appearanceKey();
266
+ paintText(node, source, canvas, text, resolvedSeed, glyphSeed);
265
267
  }
266
268
  catch {
267
269
  delete node.dataset.pencilReady;
268
270
  }
271
+ // Remember fallback appearances too. An oversized/unsupported label
272
+ // must not retry expensive measurement for unrelated ancestor styles.
273
+ // Theme/font/size changes and explicit canvas recovery still retry.
274
+ lastAppearance = appearanceKey();
269
275
  dirty = false;
270
276
  }
271
277
  };
@@ -1,23 +1,49 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
3
- import { createContext, forwardRef, useContext, useId } from 'react';
3
+ import { createContext, forwardRef, useCallback, useContext, useId, useRef, useState } from 'react';
4
4
  import { PencilIcon } from '../PencilIcon.js';
5
5
  import { PencilLine } from '../PencilLine.js';
6
6
  import { PencilSurface } from '../PencilSurface.js';
7
7
  import { PencilText } from '../PencilText.js';
8
+ import { useBrowserLayoutEffect } from '../internal/useBrowserLayoutEffect.js';
8
9
  const chevronDownPaths = [
9
10
  { d: 'M6 9L12 15L18 9' },
10
11
  ];
11
12
  const AccordionOrientationContext = createContext('vertical');
13
+ const AccordionOpenValuesContext = createContext(new Set());
14
+ const AccordionItemOpenContext = createContext(false);
12
15
  function classes(...values) {
13
16
  return values.filter(Boolean).join(' ');
14
17
  }
18
+ function assignRef(ref, value) {
19
+ if (typeof ref === 'function')
20
+ return ref(value);
21
+ if (ref)
22
+ ref.current = value;
23
+ }
24
+ function values(value) {
25
+ if (Array.isArray(value))
26
+ return value;
27
+ return value ? [value] : [];
28
+ }
15
29
  export const AccordionRoot = forwardRef(function AccordionRoot({ className, orientation = 'vertical', ...props }, ref) {
16
- return (_jsx(AccordionOrientationContext.Provider, { value: orientation, children: _jsx(AccordionPrimitive.Root, { ...props, className: classes('inkra-accordion', className), orientation: orientation, ref: ref }) }));
30
+ const controlled = props.value !== undefined;
31
+ const [uncontrolledValues, setUncontrolledValues] = useState(() => values(props.defaultValue));
32
+ const currentValues = controlled ? values(props.value) : uncontrolledValues;
33
+ const handleValueChange = (nextValue) => {
34
+ if (!controlled)
35
+ setUncontrolledValues(values(nextValue));
36
+ if (props.type === 'single')
37
+ props.onValueChange?.(nextValue);
38
+ else
39
+ props.onValueChange?.(nextValue);
40
+ };
41
+ return (_jsx(AccordionOrientationContext.Provider, { value: orientation, children: _jsx(AccordionOpenValuesContext.Provider, { value: new Set(currentValues), children: props.type === 'single' ? (_jsx(AccordionPrimitive.Root, { ...props, className: classes('inkra-accordion', className), onValueChange: handleValueChange, orientation: orientation, ref: ref })) : (_jsx(AccordionPrimitive.Root, { ...props, className: classes('inkra-accordion', className), onValueChange: handleValueChange, orientation: orientation, ref: ref })) }) }));
17
42
  });
18
43
  export const AccordionItem = forwardRef(function AccordionItem({ children, className, dividerSeed, showDivider = true, value, ...props }, ref) {
19
44
  const orientation = useContext(AccordionOrientationContext);
20
- return (_jsxs(AccordionPrimitive.Item, { ...props, className: classes('inkra-accordion__item', className), ref: ref, value: value, children: [children, showDivider ? (_jsx(PencilLine, { className: "inkra-accordion__divider", orientation: orientation === 'horizontal' ? 'vertical' : 'horizontal', seed: dividerSeed ?? `inkra-accordion-divider-${value}`, weight: 0.8 })) : null] }));
45
+ const open = useContext(AccordionOpenValuesContext).has(value);
46
+ return (_jsx(AccordionItemOpenContext.Provider, { value: open, children: _jsxs(AccordionPrimitive.Item, { ...props, className: classes('inkra-accordion__item', className), ref: ref, value: value, children: [children, showDivider ? (_jsx(PencilLine, { className: "inkra-accordion__divider", orientation: orientation === 'horizontal' ? 'vertical' : 'horizontal', seed: dividerSeed ?? `inkra-accordion-divider-${value}`, weight: 0.8 })) : null] }) }));
21
47
  });
22
48
  export const AccordionHeader = forwardRef(function AccordionHeader({ className, ...props }, ref) {
23
49
  return (_jsx(AccordionPrimitive.Header, { ...props, className: classes('inkra-accordion__header', className), ref: ref }));
@@ -29,9 +55,43 @@ export const AccordionTrigger = forwardRef(function AccordionTrigger({ children,
29
55
  return (_jsxs(AccordionPrimitive.Trigger, { ...props, className: classes('inkra-accordion__trigger', className), ref: ref, children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-accordion__focus-ring", radius: 6, seed: `${resolvedSeed}-focus`, variant: "outline" }), _jsx("span", { className: "inkra-accordion__trigger-label", children: typeof children === 'string' ? (_jsx(PencilText, { seed: `${resolvedSeed}-text`, children: children })) : children }), triggerIcon == null ? null : (_jsx("span", { "aria-hidden": "true", className: "inkra-accordion__trigger-icon", children: triggerIcon }))] }));
30
56
  });
31
57
  export const AccordionContent = forwardRef(function AccordionContent({ children, className, contentClassName, ...props }, ref) {
32
- return (_jsx(AccordionPrimitive.Content, { ...props, className: classes('inkra-accordion__content', className), ref: ref, children: _jsx("div", { className: classes('inkra-accordion__content-body', contentClassName), children: children }) }));
58
+ const open = useContext(AccordionItemOpenContext);
59
+ const contentRef = useRef(null);
60
+ const focusWithinRef = useRef(false);
61
+ const callerAriaHidden = props['aria-hidden'];
62
+ const callerInert = props.inert;
63
+ const callerOnBlurCapture = props.onBlurCapture;
64
+ const callerOnFocusCapture = props.onFocusCapture;
65
+ const setContentRef = useCallback((node) => {
66
+ contentRef.current = node;
67
+ const cleanup = assignRef(ref, node);
68
+ return () => {
69
+ contentRef.current = null;
70
+ if (typeof cleanup === 'function')
71
+ cleanup();
72
+ else
73
+ assignRef(ref, null);
74
+ };
75
+ }, [ref]);
76
+ useBrowserLayoutEffect(() => {
77
+ const content = contentRef.current;
78
+ if (open || !content || !focusWithinRef.current)
79
+ return;
80
+ focusWithinRef.current = false;
81
+ const triggerId = content.getAttribute('aria-labelledby');
82
+ if (triggerId)
83
+ content.ownerDocument.getElementById(triggerId)?.focus({ preventScroll: true });
84
+ }, [open]);
85
+ return (_jsx(AccordionPrimitive.Content, { ...props, "aria-hidden": open ? callerAriaHidden : true, className: classes('inkra-accordion__content', className), inert: open ? callerInert : true, onBlurCapture: event => {
86
+ callerOnBlurCapture?.(event);
87
+ if (open && !event.currentTarget.contains(event.relatedTarget))
88
+ focusWithinRef.current = false;
89
+ }, onFocusCapture: event => {
90
+ focusWithinRef.current = true;
91
+ callerOnFocusCapture?.(event);
92
+ }, ref: setContentRef, children: _jsx("div", { className: classes('inkra-accordion__content-body', contentClassName), children: children }) }));
33
93
  });
34
- export const Accordion = Object.freeze({
94
+ export const Accordion = /* @__PURE__ */ Object.freeze({
35
95
  Root: AccordionRoot,
36
96
  Item: AccordionItem,
37
97
  Header: AccordionHeader,
@@ -43,7 +43,7 @@ export const AlertDialogContent = forwardRef(function AlertDialogContent({ child
43
43
  const contentRef = useRef(null);
44
44
  const id = useId();
45
45
  props = usePortalThemeProps(props);
46
- return (_jsx(DialogPrimitive.Content, { ...props, role: "alertdialog", "aria-busy": context?.pending || undefined, className: classes('inkra-alert-dialog__content', className), "data-pending": context?.pending || undefined, ref: node => {
46
+ return (_jsxs(DialogPrimitive.Content, { ...props, role: "alertdialog", "aria-busy": context?.pending || undefined, className: classes('inkra-alert-dialog__content', className), "data-pending": context?.pending || undefined, ref: node => {
47
47
  contentRef.current = node;
48
48
  assignRef(forwardedRef, node);
49
49
  }, onEscapeKeyDown: event => {
@@ -61,8 +61,18 @@ export const AlertDialogContent = forwardRef(function AlertDialogContent({ child
61
61
  if (event.defaultPrevented)
62
62
  return;
63
63
  event.preventDefault();
64
- queueMicrotask(() => (context?.cancelRef.current ?? contentRef.current)?.focus());
65
- }, children: _jsx(PencilSurface, { className: "inkra-alert-dialog__surface", radius: 8, seed: seed ?? `inkra-alert-dialog-${id}`, variant: "paper", children: _jsx("div", { className: "inkra-alert-dialog__layout", children: children }) }) }));
64
+ queueMicrotask(() => {
65
+ const cancel = context?.cancelRef.current;
66
+ const content = contentRef.current;
67
+ const ownerDocument = content?.ownerDocument;
68
+ if (cancel && !cancel.disabled && cancel.isConnected)
69
+ cancel.focus({ preventScroll: true });
70
+ if (ownerDocument?.activeElement === cancel)
71
+ return;
72
+ if (content?.isConnected)
73
+ content.focus({ preventScroll: true });
74
+ });
75
+ }, children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-alert-dialog__focus-ring", radius: 8, seed: `${seed ?? `inkra-alert-dialog-${id}`}-focus`, variant: "outline" }), _jsx(PencilSurface, { className: "inkra-alert-dialog__surface", radius: 8, seed: seed ?? `inkra-alert-dialog-${id}`, variant: "paper", children: _jsx("div", { className: "inkra-alert-dialog__layout", children: children }) })] }));
66
76
  });
67
77
  export const AlertDialogTitle = forwardRef(function AlertDialogTitle({ children, className, seed, ...props }, ref) {
68
78
  const id = useId();
@@ -95,7 +105,7 @@ export const AlertDialogAction = forwardRef(function AlertDialogAction({ closeOn
95
105
  const button = (_jsx(Button, { ...props, "aria-busy": pending || undefined, disabled: disabled || pending, loading: loading || pending, ref: ref, type: "button", variant: variant }));
96
106
  return closeOnAction ? _jsx(DialogPrimitive.Close, { asChild: true, children: button }) : button;
97
107
  });
98
- export const AlertDialog = Object.freeze({
108
+ export const AlertDialog = /* @__PURE__ */ Object.freeze({
99
109
  Root: AlertDialogRoot,
100
110
  Trigger: AlertDialogTrigger,
101
111
  Portal: AlertDialogPortal,
@@ -24,6 +24,8 @@ export interface AudioPlayerControls {
24
24
  focus: () => void;
25
25
  }
26
26
  export interface AudioPlayerLabels {
27
+ controls?: string;
28
+ audio?: string;
27
29
  play?: string;
28
30
  pause?: string;
29
31
  mute?: string;