@crewlethq/ui 0.35.1 → 0.36.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 (164) hide show
  1. package/README.md +73 -0
  2. package/dist/AgentHandoffPanel/AgentHandoffPanel.d.ts +99 -0
  3. package/dist/AgentHandoffPanel/index.d.ts +3 -0
  4. package/dist/AgentHandoffPanel/index.js +16 -0
  5. package/dist/AgentHandoffPanel-JWSW62GU.css +151 -0
  6. package/dist/ArrangeFrame/index.js +1 -1
  7. package/dist/{ArrangeFrame-3PLZAG22.css → ArrangeFrame-QXOQTFAH.css} +22 -5
  8. package/dist/ArrangeList/index.js +4 -3
  9. package/dist/ArtifactBar/ArtifactBar.d.ts +2 -1
  10. package/dist/ArtifactBar/index.js +2 -2
  11. package/dist/{ArtifactBar-HV5AT4RG.css → ArtifactBar-FWPBBKQ5.css} +8 -0
  12. package/dist/ArtifactCard/index.js +3 -3
  13. package/dist/ArtifactCover/index.js +3 -2
  14. package/dist/ArtifactKindIllustration/ArtifactKindIllustration.d.ts +16 -7
  15. package/dist/ArtifactKindIllustration/index.js +1 -1
  16. package/dist/{ArtifactKindIllustration-5FPRJQ3P.css → ArtifactKindIllustration-5KTP43QS.css} +36 -0
  17. package/dist/ArtifactMakeRow/ArtifactMakeRow.d.ts +39 -0
  18. package/dist/ArtifactMakeRow/index.d.ts +4 -0
  19. package/dist/ArtifactMakeRow/index.js +4 -0
  20. package/dist/ArtifactMakeRow-RWUTS5MC.css +167 -0
  21. package/dist/ArtifactPanel/ArtifactPanel.d.ts +8 -1
  22. package/dist/ArtifactPanel/index.js +1 -1
  23. package/dist/{ArtifactPanel-HW3SU7CP.css → ArtifactPanel-KKUUIPEU.css} +9 -0
  24. package/dist/AuthLayout/index.js +2 -2
  25. package/dist/AvatarPicker/index.js +2 -2
  26. package/dist/Canvas/index.js +2 -2
  27. package/dist/CanvasBoard/CanvasBoard.d.ts +27 -13
  28. package/dist/CanvasBoard/index.d.ts +1 -1
  29. package/dist/CanvasBoard/index.js +4 -4
  30. package/dist/{CanvasBoard-5NNSKY6A.css → CanvasBoard-TS4P356F.css} +13 -6
  31. package/dist/CanvasToolbar/index.js +3 -3
  32. package/dist/Card/index.js +1 -1
  33. package/dist/{Card-KJOYHDTJ.css → Card-QOVASQNU.css} +17 -12
  34. package/dist/Checkbox/index.js +3 -2
  35. package/dist/ChoiceButtons/index.js +1 -1
  36. package/dist/{ChoiceButtons-DJ5EOGUA.css → ChoiceButtons-FLIFXFFS.css} +10 -0
  37. package/dist/ChoiceList/index.js +1 -1
  38. package/dist/{ChoiceList-RBL7FNQL.css → ChoiceList-EA2W6MOS.css} +36 -5
  39. package/dist/Combobox/index.js +3 -2
  40. package/dist/CommandPalette/index.js +3 -2
  41. package/dist/CommentThread/index.js +6 -5
  42. package/dist/Composer/Composer.d.ts +4 -0
  43. package/dist/Composer/index.js +3 -2
  44. package/dist/{Composer-ABGJIAIR.css → Composer-PXITWNEK.css} +26 -15
  45. package/dist/ConfirmField/index.js +3 -2
  46. package/dist/DataTable/index.js +5 -4
  47. package/dist/DataView/index.js +9 -8
  48. package/dist/DatasetSource/DatasetSource.d.ts +4 -0
  49. package/dist/DatasetSource/index.js +1 -1
  50. package/dist/{DatasetSource-XFPZBRXL.css → DatasetSource-SMGX4PS5.css} +38 -4
  51. package/dist/DateTimePicker/index.js +2 -1
  52. package/dist/{DateTimePicker-HG2KQXQ6.css → DateTimePicker-2IVTKFV2.css} +15 -2
  53. package/dist/DocTabsEditor/index.js +4 -3
  54. package/dist/Document/Document.d.ts +15 -3
  55. package/dist/Document/index.js +2 -1
  56. package/dist/{Document-JUGCSQ4L.css → Document-YFWTWXJP.css} +34 -2
  57. package/dist/{Editor-BVQHBF7L.js → Editor-HORWMUM3.js} +2 -1
  58. package/dist/FilterChip/index.js +1 -1
  59. package/dist/{FilterChip-EZ4QMW4E.css → FilterChip-VL33P3JL.css} +5 -1
  60. package/dist/Input/index.js +2 -1
  61. package/dist/{Input-GBZSFXLU.css → Input-ABGOFENS.css} +46 -10
  62. package/dist/LayersList/index.js +1 -1
  63. package/dist/{LayersList-NQZEIPZB.css → LayersList-TBNF4XGL.css} +16 -3
  64. package/dist/LinkInterstitial/index.js +2 -2
  65. package/dist/ListInput/index.js +4 -3
  66. package/dist/LivePreview/LivePreview.d.ts +105 -25
  67. package/dist/LivePreview/budget.d.ts +64 -20
  68. package/dist/LivePreview/index.d.ts +5 -3
  69. package/dist/LivePreview/index.js +1 -1
  70. package/dist/LivePreview/stills.d.ts +57 -0
  71. package/dist/LivePreview-BROGKR4N.css +113 -0
  72. package/dist/PresenterView/index.js +2 -2
  73. package/dist/PrincipalPicker/index.js +4 -3
  74. package/dist/RichEditor/index.js +1 -1
  75. package/dist/Select/index.js +2 -1
  76. package/dist/{Select-A54YS5EP.css → Select-W3CHTZXP.css} +18 -2
  77. package/dist/ShareDialog/index.js +5 -4
  78. package/dist/SheetColumnEditor/index.js +6 -5
  79. package/dist/SheetGrid/index.js +4 -4
  80. package/dist/SlideCompare/index.js +2 -2
  81. package/dist/SlideFilmstrip/index.js +3 -3
  82. package/dist/{SlideFilmstrip-4ITZLMO4.css → SlideFilmstrip-PAT7P4OG.css} +26 -7
  83. package/dist/SlideGrid/index.js +2 -2
  84. package/dist/{SlideGrid-S4LTJ4UU.css → SlideGrid-FJA33DV4.css} +11 -1
  85. package/dist/SlideStage/index.js +1 -1
  86. package/dist/{SlideStage-S2FISF4C.css → SlideStage-ZTTEQSS2.css} +4 -2
  87. package/dist/SourceEditor/index.js +2 -1
  88. package/dist/{SourceEditor-G2C3STPX.css → SourceEditor-QAYE2JS6.css} +19 -12
  89. package/dist/Switcher/index.js +5 -4
  90. package/dist/TagsInput/index.js +3 -2
  91. package/dist/{TagsInput-XMXTI5Q4.css → TagsInput-7EGBBXHS.css} +7 -1
  92. package/dist/ThemeGallery/index.js +1 -1
  93. package/dist/{ThemeGallery-HSUKBDWY.css → ThemeGallery-X35Y25EX.css} +26 -5
  94. package/dist/TimeWindowPicker/index.js +4 -3
  95. package/dist/{TimeWindowPicker-T763GLXH.css → TimeWindowPicker-DEFDV77Z.css} +15 -2
  96. package/dist/TreeCanvas/index.js +3 -3
  97. package/dist/VersionTimeline/index.js +2 -2
  98. package/dist/{chunk-V43UVX4H.js → chunk-27GD7LVR.js} +1 -1
  99. package/dist/{chunk-CCLNGVCM.js → chunk-2DV4THNQ.js} +2 -2
  100. package/dist/{chunk-2CQLNML5.js → chunk-3PTUDKSR.js} +4 -2
  101. package/dist/{chunk-ONL54WMH.js → chunk-46RL3E54.js} +1 -1
  102. package/dist/{chunk-UHJQDFKQ.js → chunk-4ZJLOKSS.js} +1 -1
  103. package/dist/{chunk-ACA27MZH.js → chunk-5QXRI35B.js} +1 -1
  104. package/dist/{chunk-LL5AFXU7.js → chunk-5RT5O4JR.js} +1 -1
  105. package/dist/chunk-66ADEPFB.js +51 -0
  106. package/dist/{chunk-3Y56GQPM.js → chunk-7VAUGG6B.js} +1 -1
  107. package/dist/{chunk-NKZCJRKO.js → chunk-APDKUGSH.js} +2 -2
  108. package/dist/{chunk-YSJC7AZC.js → chunk-BWOIUOO3.js} +1 -1
  109. package/dist/{chunk-PI7HKLIT.js → chunk-D7GNNID2.js} +3 -1
  110. package/dist/{chunk-THROPJZC.js → chunk-DEMALFHY.js} +1 -1
  111. package/dist/chunk-EOQFPZEE.js +138 -0
  112. package/dist/{chunk-QFNWMWWY.js → chunk-ES4RKQ7B.js} +4 -4
  113. package/dist/{chunk-J6YZ4IYV.js → chunk-FG4O64DV.js} +3 -3
  114. package/dist/{chunk-E35CMI5D.js → chunk-FHHYXZSP.js} +31 -13
  115. package/dist/{chunk-AYB6AYNH.js → chunk-GMLAKNFY.js} +1 -1
  116. package/dist/{chunk-ZB5O3ELS.js → chunk-GRYZ5EP3.js} +4 -2
  117. package/dist/{chunk-ILAEBSLJ.js → chunk-H5NOLRGH.js} +2 -2
  118. package/dist/{chunk-W3UVOZKL.js → chunk-IPAU6FLH.js} +6 -2
  119. package/dist/{chunk-Z6VW6CXX.js → chunk-IQH3DDZ2.js} +2 -2
  120. package/dist/{chunk-6QFAK2WV.js → chunk-IUYUS2SO.js} +4 -1
  121. package/dist/{chunk-AXBRWRGV.js → chunk-JQLS3MZB.js} +1 -1
  122. package/dist/{chunk-FGYET26P.js → chunk-KJW2AS57.js} +1 -1
  123. package/dist/{chunk-QIXMNOBD.js → chunk-KYZMN6I6.js} +2 -2
  124. package/dist/{chunk-73A7D5CE.js → chunk-L4WZSVMT.js} +3 -1
  125. package/dist/{chunk-W7QBBKUE.js → chunk-MJW6SQYB.js} +7 -3
  126. package/dist/{chunk-GMU6SQ6B.js → chunk-MRMHZ437.js} +3 -3
  127. package/dist/{chunk-ULPL26BU.js → chunk-N5SKHIAP.js} +1 -1
  128. package/dist/{chunk-LV4APO72.js → chunk-NQGPD6NX.js} +1 -1
  129. package/dist/{chunk-7UYONN6K.js → chunk-OSZDHN5Z.js} +2 -2
  130. package/dist/chunk-P6WFZDKS.js +111 -0
  131. package/dist/{chunk-CPEPST4S.js → chunk-PJIPUEJ3.js} +1 -1
  132. package/dist/{chunk-QS4ZGTEU.js → chunk-PPFX6GRK.js} +1 -1
  133. package/dist/{chunk-7DZDSSJS.js → chunk-RH2NCQBZ.js} +5 -4
  134. package/dist/{chunk-3VD3G3IL.js → chunk-T5RQGI6O.js} +1 -1
  135. package/dist/{chunk-F2KY4RLI.js → chunk-TVRBQMIX.js} +5 -3
  136. package/dist/{chunk-QEFHYUCY.js → chunk-UWGUKMBK.js} +1 -1
  137. package/dist/{chunk-5YLON2PW.js → chunk-UYZZV4DZ.js} +1 -1
  138. package/dist/{chunk-BQ7AKL2N.js → chunk-VKKUGZ3G.js} +3 -1
  139. package/dist/{chunk-DEJHAOC5.js → chunk-WMXAX6VA.js} +1 -1
  140. package/dist/{chunk-6JVRY5DQ.js → chunk-WNAYQQ6Y.js} +2 -2
  141. package/dist/{chunk-VDPJGP4Q.js → chunk-WX2NIG4G.js} +1 -1
  142. package/dist/{chunk-ZU3DUXD2.js → chunk-WZGYIVYH.js} +1 -1
  143. package/dist/chunk-XGBLRDSU.js +446 -0
  144. package/dist/{chunk-22CQQDFW.js → chunk-XMRTFPWX.js} +2 -2
  145. package/dist/{chunk-HUVG2FFT.js → chunk-Y5BD4MTE.js} +4 -2
  146. package/dist/chunk-YSGLEHND.js +56 -0
  147. package/dist/generated/index.d.ts +2 -0
  148. package/dist/generated/index.js +95 -92
  149. package/dist/index.js +95 -92
  150. package/dist/styles.css +862 -130
  151. package/dist/utils/focusModality.d.ts +41 -0
  152. package/dist/utils/index.d.ts +2 -0
  153. package/dist/utils/index.js +2 -1
  154. package/package.json +3 -3
  155. package/dist/LivePreview-SRO7KHO4.css +0 -65
  156. package/dist/chunk-KF6YGL4B.js +0 -160
  157. package/dist/chunk-TYKQ33CL.js +0 -35
  158. package/dist/{chunk-3P4Y5RX6.js → chunk-4BYHAOHH.js} +1 -1
  159. package/dist/{chunk-3WCNHYXE.js → chunk-5BP6IS3N.js} +0 -0
  160. package/dist/{chunk-KD2IRYCT.js → chunk-5WDXPHEW.js} +1 -1
  161. package/dist/{chunk-OSFA5ROT.js → chunk-FEJANUZK.js} +0 -0
  162. package/dist/{chunk-FJJHD73S.js → chunk-GYBDPCRC.js} +1 -1
  163. package/dist/{chunk-VHZ3AXJD.js → chunk-NQCY44B3.js} +1 -1
  164. package/dist/{chunk-FF5OZV77.js → chunk-W3HYWD63.js} +2 -2
@@ -1,13 +1,14 @@
1
- import { Textarea } from './chunk-6QFAK2WV.js';
1
+ import { Textarea } from './chunk-IUYUS2SO.js';
2
2
  import { Kbd } from './chunk-SMBKXWML.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
- import './Composer-ABGJIAIR.css';
4
+ import './Composer-PXITWNEK.css';
5
5
  import { forwardRef } from 'react';
6
6
  import { jsxs, jsx } from 'react/jsx-runtime';
7
7
 
8
8
  var COMPOSER_COUNTER_AT = 0.8;
9
9
  var Composer = forwardRef(function Composer2({ value, onChange, onSubmit, label, actions, maxLength, className, rows = 3, onKeyDown, ...rest }, ref) {
10
10
  const near = maxLength !== void 0 && value.length >= maxLength * COMPOSER_COUNTER_AT;
11
+ const hasActions = actions !== void 0 && actions !== null && actions !== false;
11
12
  const keyDown = (event) => {
12
13
  onKeyDown?.(event);
13
14
  if (event.defaultPrevented || !onSubmit) return;
@@ -32,7 +33,7 @@ var Composer = forwardRef(function Composer2({ value, onChange, onSubmit, label,
32
33
  ...rest
33
34
  }
34
35
  ),
35
- /* @__PURE__ */ jsxs("div", { className: "crewlet-composer__footer", children: [
36
+ onSubmit || near || hasActions ? /* @__PURE__ */ jsxs("div", { className: "crewlet-composer__footer", "data-hint-only": !near && !hasActions || void 0, children: [
36
37
  onSubmit ? /* @__PURE__ */ jsxs("span", { className: "crewlet-composer__hint", children: [
37
38
  /* @__PURE__ */ jsx(Kbd, { keys: ["Mod", "Enter"], subtle: true }),
38
39
  " to send"
@@ -40,7 +41,7 @@ var Composer = forwardRef(function Composer2({ value, onChange, onSubmit, label,
40
41
  near ? /* @__PURE__ */ jsx("span", { className: "crewlet-composer__counter", "aria-live": "polite", children: `${value.length.toLocaleString()} / ${maxLength.toLocaleString()}` }) : null,
41
42
  /* @__PURE__ */ jsx("span", { className: "crewlet-composer__spacer" }),
42
43
  actions
43
- ] })
44
+ ] }) : null
44
45
  ] });
45
46
  });
46
47
 
@@ -9,7 +9,7 @@ import { jsx } from 'react/jsx-runtime';
9
9
  var engine = null;
10
10
  var loading = null;
11
11
  function loadEngine() {
12
- loading ??= import('./Editor-BVQHBF7L.js').then(
12
+ loading ??= import('./Editor-HORWMUM3.js').then(
13
13
  (module) => {
14
14
  engine = module.Editor;
15
15
  return engine;
@@ -1,12 +1,13 @@
1
- import { Select } from './chunk-BQ7AKL2N.js';
1
+ import { Select } from './chunk-VKKUGZ3G.js';
2
2
  import { today, shiftMonth, CalendarGrid, compareDays } from './chunk-GXQ3B46I.js';
3
3
  import { Popover } from './chunk-K3IJQNB7.js';
4
- import { Input } from './chunk-6QFAK2WV.js';
4
+ import { Input } from './chunk-IUYUS2SO.js';
5
+ import { useFocusModality } from './chunk-YSGLEHND.js';
5
6
  import { isComposing } from './chunk-R25RGPUB.js';
6
7
  import { Button } from './chunk-OG6CL3EB.js';
7
8
  import { IconButton } from './chunk-4FQT6ZAY.js';
8
9
  import { cx } from './chunk-ODEQTPFM.js';
9
- import './TimeWindowPicker-T763GLXH.css';
10
+ import './TimeWindowPicker-DEFDV77Z.css';
10
11
  import { useMemo, useState, useRef, useId, useEffect } from 'react';
11
12
  import { ClockGlyph, ChevronDownGlyph, CheckGlyph, ChevronLeftGlyph, ChevronRightGlyph } from '@crewlethq/icons/glyphs';
12
13
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -322,6 +323,7 @@ function TimeWindowPicker({
322
323
  size = "md",
323
324
  labels: labelOverrides
324
325
  }) {
326
+ useFocusModality();
325
327
  const labels = useMemo(() => withLabels(labelOverrides), [labelOverrides]);
326
328
  const isSet = isTimeWindowSet(value);
327
329
  const described = describeTimeWindow(value, { presets, labels });
@@ -1,5 +1,5 @@
1
1
  import { useReorder } from './chunk-XJ2QEBMM.js';
2
- import { Input } from './chunk-6QFAK2WV.js';
2
+ import { Input } from './chunk-IUYUS2SO.js';
3
3
  import { Menu } from './chunk-BUVCV3WI.js';
4
4
  import { IconButton } from './chunk-4FQT6ZAY.js';
5
5
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
@@ -1,6 +1,6 @@
1
1
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
2
2
  import { cx } from './chunk-ODEQTPFM.js';
3
- import './FilterChip-EZ4QMW4E.css';
3
+ import './FilterChip-VL33P3JL.css';
4
4
  import { createContext, useContext, useRef, useId, useCallback, useLayoutEffect } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
6
6
 
@@ -1,7 +1,8 @@
1
1
  import { useListbox, useOptionKeys, useHighlightRest } from './chunk-F73MC7EZ.js';
2
+ import { useFocusModality } from './chunk-YSGLEHND.js';
2
3
  import { useLayerContainer, viewportBounds, anchorGone, placePopup, LAYER_GAP, LAYER_REPOSITION_EVENT } from './chunk-OMV5W53A.js';
3
4
  import { cx } from './chunk-ODEQTPFM.js';
4
- import './Select-A54YS5EP.css';
5
+ import './Select-W3CHTZXP.css';
5
6
  import { forwardRef, useId, useMemo, useState, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
6
7
  import { createPortal } from 'react-dom';
7
8
  import { ChevronDownGlyph, CheckGlyph, SearchGlyph } from '@crewlethq/icons/glyphs';
@@ -36,6 +37,7 @@ var Select = forwardRef(function Select2({
36
37
  onHighlight,
37
38
  ...native
38
39
  }, ref) {
40
+ useFocusModality();
39
41
  const generatedId = useId();
40
42
  const rootId = id ?? generatedId;
41
43
  const classes = cx(className, width === "auto" && "crewlet-select--auto", appearance === "quiet" && "crewlet-select--quiet");
@@ -1,7 +1,7 @@
1
1
  import { announce } from './chunk-RSAQOKH3.js';
2
2
  import { IconButton } from './chunk-4FQT6ZAY.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
- import './ArrangeFrame-3PLZAG22.css';
4
+ import './ArrangeFrame-QXOQTFAH.css';
5
5
  import { forwardRef, useRef, useCallback, useState, useEffect } from 'react';
6
6
  import { TrashGlyph } from '@crewlethq/icons/glyphs';
7
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -1,7 +1,7 @@
1
- import { Composer } from './chunk-7DZDSSJS.js';
2
1
  import { PrincipalAvatar } from './chunk-CI3VPIJN.js';
3
2
  import { toInstant, RelativeTime, formatExact, toISO } from './chunk-3CZ7XAVY.js';
4
- import { Document } from './chunk-TYKQ33CL.js';
3
+ import { Document } from './chunk-P6WFZDKS.js';
4
+ import { Composer } from './chunk-RH2NCQBZ.js';
5
5
  import { Count } from './chunk-BHHIIK3M.js';
6
6
  import { useHeadingLevel, headingTag } from './chunk-PQADV6P6.js';
7
7
  import { Button } from './chunk-OG6CL3EB.js';
@@ -1,5 +1,5 @@
1
1
  import { BrandLockup } from './chunk-7KBNFFM3.js';
2
- import { Card } from './chunk-ACA27MZH.js';
2
+ import { Card } from './chunk-5QXRI35B.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
4
  import './AuthLayout-ODHBUPHQ.css';
5
5
  import { useId } from 'react';
@@ -1,5 +1,5 @@
1
1
  import { cx } from './chunk-ODEQTPFM.js';
2
- import './ChoiceList-RBL7FNQL.css';
2
+ import './ChoiceList-EA2W6MOS.css';
3
3
  import { useId } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -0,0 +1,446 @@
1
+ import { cx } from './chunk-ODEQTPFM.js';
2
+ import './LivePreview-BROGKR4N.css';
3
+ import { createContext, useMemo, useContext, useRef, useState, useCallback, useEffect, useLayoutEffect } from 'react';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ // src/LivePreview/budget.ts
7
+ var LIVE_PREVIEW_MAX = 6;
8
+ var LIVE_PREVIEW_MAX_FRAMES = 12;
9
+ var immediately = (run) => {
10
+ run();
11
+ return () => {
12
+ };
13
+ };
14
+ var perFrame = (run) => {
15
+ if (typeof requestAnimationFrame === "undefined") return immediately(run);
16
+ let timer;
17
+ const frame = requestAnimationFrame(() => {
18
+ timer = setTimeout(run, 0);
19
+ });
20
+ return () => {
21
+ cancelAnimationFrame(frame);
22
+ if (timer !== void 0) clearTimeout(timer);
23
+ };
24
+ };
25
+ function createLivePreviewBudget(max = LIVE_PREVIEW_MAX, schedule = immediately, maxFrames = LIVE_PREVIEW_MAX_FRAMES) {
26
+ const rendering = /* @__PURE__ */ new Set();
27
+ const frames = /* @__PURE__ */ new Set();
28
+ const parked = /* @__PURE__ */ new Set();
29
+ const queue = [];
30
+ let pending = null;
31
+ const pick = () => {
32
+ if (rendering.size >= max) return null;
33
+ for (const ask of queue) {
34
+ if (!ask.frame || frames.size < maxFrames) return { ask, victim: void 0 };
35
+ const victim = parked.values().next().value;
36
+ if (victim) return { ask, victim };
37
+ }
38
+ return null;
39
+ };
40
+ const drop = (ask) => {
41
+ rendering.delete(ask);
42
+ frames.delete(ask);
43
+ parked.delete(ask);
44
+ };
45
+ const next = () => {
46
+ if (pending || !pick()) return;
47
+ let ran = false;
48
+ const cancel = schedule(() => {
49
+ ran = true;
50
+ pending = null;
51
+ const chosen = pick();
52
+ if (chosen) {
53
+ if (chosen.victim) {
54
+ drop(chosen.victim);
55
+ chosen.victim.reclaim?.();
56
+ }
57
+ queue.splice(queue.indexOf(chosen.ask), 1);
58
+ rendering.add(chosen.ask);
59
+ if (chosen.ask.frame) frames.add(chosen.ask);
60
+ chosen.ask.grant();
61
+ }
62
+ next();
63
+ });
64
+ if (!ran) pending = cancel;
65
+ };
66
+ const withdraw = (ask) => {
67
+ const at = queue.indexOf(ask);
68
+ if (at >= 0) queue.splice(at, 1);
69
+ if (queue.length === 0 && pending) {
70
+ pending();
71
+ pending = null;
72
+ }
73
+ };
74
+ return {
75
+ max,
76
+ maxFrames,
77
+ request(grant, reclaim, frame = false) {
78
+ const ask = { grant, reclaim, frame };
79
+ queue.push(ask);
80
+ next();
81
+ let done = false;
82
+ const held = () => rendering.has(ask) || frames.has(ask);
83
+ const release = () => {
84
+ if (done) return;
85
+ done = true;
86
+ if (held()) {
87
+ drop(ask);
88
+ next();
89
+ } else withdraw(ask);
90
+ };
91
+ return {
92
+ release,
93
+ drawn() {
94
+ if (done || !rendering.has(ask)) return;
95
+ if (!ask.frame) {
96
+ release();
97
+ return;
98
+ }
99
+ rendering.delete(ask);
100
+ next();
101
+ },
102
+ park() {
103
+ if (done) return;
104
+ if (!held()) {
105
+ release();
106
+ return;
107
+ }
108
+ if (!ask.frame) return;
109
+ parked.add(ask);
110
+ next();
111
+ },
112
+ resume() {
113
+ if (done || !held()) return false;
114
+ parked.delete(ask);
115
+ return true;
116
+ }
117
+ };
118
+ },
119
+ live: () => rendering.size,
120
+ frames: () => frames.size,
121
+ parked: () => parked.size,
122
+ waiting: () => queue.length
123
+ };
124
+ }
125
+
126
+ // src/LivePreview/stills.ts
127
+ var LIVE_PREVIEW_STILLS_KEPT = 200;
128
+ function createLivePreviewStills(kept = LIVE_PREVIEW_STILLS_KEPT) {
129
+ const held = /* @__PURE__ */ new Map();
130
+ const sized = /* @__PURE__ */ new Map();
131
+ let watcher = null;
132
+ return {
133
+ get(key) {
134
+ const got = held.get(key);
135
+ if (got !== void 0) {
136
+ held.delete(key);
137
+ held.set(key, got);
138
+ }
139
+ return got;
140
+ },
141
+ put(key, value) {
142
+ held.delete(key);
143
+ held.set(key, value);
144
+ while (held.size > kept) held.delete(held.keys().next().value);
145
+ },
146
+ forget(key) {
147
+ held.delete(key);
148
+ },
149
+ watch(el, told) {
150
+ if (typeof ResizeObserver === "undefined") return () => {
151
+ };
152
+ watcher ??= new ResizeObserver((entries) => {
153
+ for (const entry of entries) {
154
+ const rect = entry.target.getBoundingClientRect();
155
+ sized.get(entry.target)?.({ w: rect.width, h: rect.height });
156
+ }
157
+ });
158
+ sized.set(el, told);
159
+ watcher.observe(el);
160
+ return () => {
161
+ sized.delete(el);
162
+ watcher?.unobserve(el);
163
+ };
164
+ }
165
+ };
166
+ }
167
+ var LOADS = "iframe, frame, video, audio, object, embed";
168
+ function copyOf(nodes) {
169
+ return nodes.flatMap((node) => {
170
+ if (!(node instanceof Element)) return [node.cloneNode(true)];
171
+ if (node.matches(LOADS)) return [];
172
+ const copy = node.cloneNode(true);
173
+ const drawn = node.querySelectorAll("canvas");
174
+ copy.querySelectorAll("canvas").forEach((blank, i) => {
175
+ const image = pictureOf(drawn[i], blank);
176
+ if (image) blank.replaceWith(image);
177
+ });
178
+ for (const loads of copy.querySelectorAll(LOADS)) loads.remove();
179
+ if (node instanceof HTMLCanvasElement) return [pictureOf(node, copy) ?? copy];
180
+ return [copy];
181
+ });
182
+ }
183
+ function pictureOf(canvas, copy) {
184
+ let src;
185
+ try {
186
+ src = canvas.toDataURL();
187
+ } catch {
188
+ return null;
189
+ }
190
+ const image = canvas.ownerDocument.createElement("img");
191
+ image.src = src;
192
+ image.alt = "";
193
+ for (const { name, value } of copy.attributes) if (name !== "width" && name !== "height") image.setAttribute(name, value);
194
+ image.style.width = `${canvas.clientWidth}px`;
195
+ image.style.height = `${canvas.clientHeight}px`;
196
+ return image;
197
+ }
198
+ var LIVE_PREVIEW_DWELL_MS = 120;
199
+ var LIVE_PREVIEW_MARGIN = "100% 0px";
200
+ var LIVE_PREVIEW_RENDER_MS = 15e3;
201
+ var DefaultShared = { budget: createLivePreviewBudget(LIVE_PREVIEW_MAX, perFrame), stills: createLivePreviewStills() };
202
+ var SharedContext = createContext(DefaultShared);
203
+ function LivePreviewBudgetProvider({
204
+ max = LIVE_PREVIEW_MAX,
205
+ maxFrames = LIVE_PREVIEW_MAX_FRAMES,
206
+ kept = LIVE_PREVIEW_STILLS_KEPT,
207
+ children
208
+ }) {
209
+ const budget = useMemo(() => createLivePreviewBudget(max, perFrame, maxFrames), [max, maxFrames]);
210
+ const stills = useMemo(() => createLivePreviewStills(kept), [kept]);
211
+ const shared = useMemo(() => ({ budget, stills }), [budget, stills]);
212
+ return /* @__PURE__ */ jsx(SharedContext.Provider, { value: shared, children });
213
+ }
214
+ var NOOP = { ready: () => {
215
+ }, fail: () => {
216
+ } };
217
+ var SignalContext = createContext(NOOP);
218
+ function useLivePreview() {
219
+ return useContext(SignalContext);
220
+ }
221
+ function scrollRoot(el) {
222
+ for (let at = el.parentElement; at; at = at.parentElement) {
223
+ const style = getComputedStyle(at);
224
+ if (/(auto|scroll|overlay)/.test(`${style.overflowY} ${style.overflowX}`)) return at;
225
+ }
226
+ return null;
227
+ }
228
+ function afterDrawn(then) {
229
+ let off = false;
230
+ let frame = 0;
231
+ const paint = (left) => {
232
+ if (off) return;
233
+ if (left === 0 || typeof requestAnimationFrame === "undefined") then();
234
+ else frame = requestAnimationFrame(() => paint(left - 1));
235
+ };
236
+ const fonts = typeof document === "undefined" ? void 0 : document.fonts;
237
+ if (fonts) void fonts.ready.then(() => paint(2));
238
+ else paint(2);
239
+ return () => {
240
+ off = true;
241
+ if (frame && typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(frame);
242
+ };
243
+ }
244
+ function fitTo(size, width, height, fit) {
245
+ const k = fit === "contain" && height ? Math.min(size.w / width, size.h / height) : size.w / width;
246
+ const h = height ?? size.h / k;
247
+ const left = fit === "contain" ? (size.w - width * k) / 2 : 0;
248
+ const top = fit === "contain" ? (size.h - h * k) / 2 : 0;
249
+ return { width, height: h, transform: `translate(${left}px, ${top}px) scale(${k})` };
250
+ }
251
+ function stillOf(live, width, box) {
252
+ const rect = box.getBoundingClientRect();
253
+ const from = width === void 0 ? live : live.querySelector(":scope > .crewlet-preview-scale > .crewlet-preview-scale__layer") ?? live;
254
+ return { nodes: copyOf([...from.childNodes]), width, size: { w: rect.width, h: rect.height } };
255
+ }
256
+ function LivePreview(props) {
257
+ return /* @__PURE__ */ jsx(Preview, { ...props }, props.snapshot ?? "");
258
+ }
259
+ function Preview({ children, fallback, width, rootMargin = LIVE_PREVIEW_MARGIN, disabled = false, frame = false, snapshot, dwellMs = LIVE_PREVIEW_DWELL_MS, onStateChange, className }) {
260
+ const { budget, stills } = useContext(SharedContext);
261
+ const box = useRef(null);
262
+ const liveLayer = useRef(null);
263
+ const stillLayer = useRef(null);
264
+ const slot = useRef(null);
265
+ const granted = useRef(false);
266
+ const [kept] = useState(() => snapshot !== void 0 && !frame && !disabled ? stills.get(snapshot) : void 0);
267
+ const [state, setState] = useState(kept === "failed" ? "failed" : kept ? "ready" : "idle");
268
+ const current = useRef(state);
269
+ current.current = state;
270
+ const [still, setStill] = useState(kept && kept !== "failed" ? { drawing: kept, fresh: false } : null);
271
+ const [near, setNear] = useState(false);
272
+ const done = state === "failed" || state === "ready" && !frame;
273
+ const settle = useCallback((next) => {
274
+ slot.current?.release();
275
+ slot.current = null;
276
+ granted.current = false;
277
+ setState(next);
278
+ }, []);
279
+ useEffect(() => {
280
+ const el = box.current;
281
+ if (!el || disabled || done || typeof IntersectionObserver === "undefined") return void 0;
282
+ const seen = new IntersectionObserver((entries) => setNear(entries.some((e) => e.isIntersecting)), { root: scrollRoot(el), rootMargin });
283
+ seen.observe(el);
284
+ return () => seen.disconnect();
285
+ }, [disabled, done, rootMargin]);
286
+ const reclaim = useCallback(() => {
287
+ slot.current = null;
288
+ granted.current = false;
289
+ setState("idle");
290
+ }, []);
291
+ useEffect(() => {
292
+ if (disabled || done) return void 0;
293
+ const held = slot.current;
294
+ if (!near) {
295
+ if (held && granted.current) held.park();
296
+ else if (held) {
297
+ held.release();
298
+ slot.current = null;
299
+ }
300
+ setState((s) => s === "waiting" ? "idle" : s);
301
+ return void 0;
302
+ }
303
+ if (held) {
304
+ if (granted.current) held.resume();
305
+ return void 0;
306
+ }
307
+ setState("waiting");
308
+ const dwell = setTimeout(() => {
309
+ granted.current = false;
310
+ slot.current = budget.request(
311
+ () => {
312
+ granted.current = true;
313
+ setState("live");
314
+ },
315
+ reclaim,
316
+ frame
317
+ );
318
+ }, dwellMs);
319
+ return () => clearTimeout(dwell);
320
+ }, [near, disabled, done, frame, budget, dwellMs, reclaim]);
321
+ useEffect(() => {
322
+ if (!disabled) return;
323
+ slot.current?.release();
324
+ slot.current = null;
325
+ granted.current = false;
326
+ setStill(null);
327
+ setState((s) => s === "failed" ? s : "idle");
328
+ }, [disabled]);
329
+ useEffect(
330
+ () => () => {
331
+ slot.current?.release();
332
+ slot.current = null;
333
+ },
334
+ []
335
+ );
336
+ useEffect(() => {
337
+ if (state !== "live") return void 0;
338
+ const t = setTimeout(() => settle("failed"), LIVE_PREVIEW_RENDER_MS);
339
+ return () => clearTimeout(t);
340
+ }, [state, settle]);
341
+ const capture = useRef(null);
342
+ useEffect(() => () => capture.current?.(), []);
343
+ const signals = useMemo(
344
+ () => ({
345
+ ready: () => {
346
+ if (current.current !== "live" || capture.current) return;
347
+ slot.current?.drawn();
348
+ if (frame) {
349
+ setState("ready");
350
+ return;
351
+ }
352
+ capture.current = afterDrawn(() => {
353
+ capture.current = null;
354
+ const live = liveLayer.current;
355
+ const el = box.current;
356
+ if (current.current !== "live" || !live || !el) return;
357
+ const drawing = stillOf(live, width, el);
358
+ if (snapshot !== void 0) stills.put(snapshot, drawing);
359
+ setStill({ drawing, fresh: true });
360
+ settle("ready");
361
+ });
362
+ },
363
+ fail: () => {
364
+ if (current.current === "failed") return;
365
+ capture.current?.();
366
+ capture.current = null;
367
+ if (snapshot !== void 0 && !frame) stills.put(snapshot, "failed");
368
+ settle("failed");
369
+ }
370
+ }),
371
+ [frame, width, snapshot, stills, settle]
372
+ );
373
+ useLayoutEffect(() => {
374
+ const target = stillLayer.current;
375
+ const el = box.current;
376
+ if (!still || !target || !el) return void 0;
377
+ const { drawing } = still;
378
+ const placed = drawing.nodes.some((n) => n.isConnected) ? copyOf(drawing.nodes) : drawing.nodes;
379
+ if (drawing.width === void 0) {
380
+ target.replaceChildren(...placed);
381
+ return stills.watch(el, (size) => {
382
+ if (Math.abs(size.w - drawing.size.w) < 1 && Math.abs(size.h - drawing.size.h) < 1) return;
383
+ if (snapshot !== void 0) stills.forget(snapshot);
384
+ setStill(null);
385
+ setState("idle");
386
+ });
387
+ }
388
+ const outer = target.ownerDocument.createElement("span");
389
+ outer.className = "crewlet-preview-scale";
390
+ const layer = target.ownerDocument.createElement("span");
391
+ layer.className = "crewlet-preview-scale__layer";
392
+ layer.append(...placed);
393
+ outer.append(layer);
394
+ target.replaceChildren(outer);
395
+ const scale = (size) => {
396
+ if (size.w <= 0) return;
397
+ const fitted = fitTo(size, drawing.width, void 0, "width");
398
+ layer.style.width = `${fitted.width}px`;
399
+ layer.style.height = `${fitted.height}px`;
400
+ layer.style.transform = fitted.transform;
401
+ };
402
+ const rect = el.getBoundingClientRect();
403
+ scale({ w: rect.width, h: rect.height });
404
+ return stills.watch(el, scale);
405
+ }, [still, snapshot, stills]);
406
+ useEffect(() => onStateChange?.(state), [state, onStateChange]);
407
+ const mounted = state === "live" || frame && state === "ready";
408
+ return /* @__PURE__ */ jsxs(
409
+ "span",
410
+ {
411
+ ref: box,
412
+ className: cx("crewlet-live-preview", className),
413
+ "data-state": state,
414
+ "data-still": still ? "" : void 0,
415
+ "data-fresh": still?.fresh ? "" : void 0,
416
+ "data-frame": frame ? "" : void 0,
417
+ "aria-hidden": "true",
418
+ children: [
419
+ /* @__PURE__ */ jsx("span", { className: "crewlet-live-preview__fallback", children: fallback }),
420
+ still ? /* @__PURE__ */ jsx("span", { ref: stillLayer, className: "crewlet-live-preview__still", inert: true }) : null,
421
+ mounted ? /* @__PURE__ */ jsx("span", { ref: liveLayer, className: "crewlet-live-preview__live", inert: true, children: /* @__PURE__ */ jsx(SignalContext.Provider, { value: signals, children: width === void 0 ? children : /* @__PURE__ */ jsx(PreviewScale, { width, children }) }) }) : null
422
+ ]
423
+ }
424
+ );
425
+ }
426
+ function PreviewScale({ children, width, height, fit = "width" }) {
427
+ const box = useRef(null);
428
+ const [size, setSize] = useState(null);
429
+ useLayoutEffect(() => {
430
+ const el = box.current;
431
+ if (!el) return void 0;
432
+ const measure = () => {
433
+ const rect = el.getBoundingClientRect();
434
+ if (rect.width > 0) setSize((was) => was && was.w === rect.width && was.h === rect.height ? was : { w: rect.width, h: rect.height });
435
+ };
436
+ measure();
437
+ if (typeof ResizeObserver === "undefined") return void 0;
438
+ const watch = new ResizeObserver(measure);
439
+ watch.observe(el);
440
+ return () => watch.disconnect();
441
+ }, []);
442
+ const layer = size ? fitTo(size, width, height, fit) : { width, visibility: "hidden" };
443
+ return /* @__PURE__ */ jsx("span", { ref: box, className: "crewlet-preview-scale", children: /* @__PURE__ */ jsx("span", { className: "crewlet-preview-scale__layer", style: layer, children }) });
444
+ }
445
+
446
+ export { LIVE_PREVIEW_DWELL_MS, LIVE_PREVIEW_MARGIN, LIVE_PREVIEW_MAX, LIVE_PREVIEW_MAX_FRAMES, LIVE_PREVIEW_RENDER_MS, LIVE_PREVIEW_STILLS_KEPT, LivePreview, LivePreviewBudgetProvider, PreviewScale, createLivePreviewBudget, createLivePreviewStills, immediately, perFrame, useLivePreview };
@@ -1,6 +1,6 @@
1
- import { useClipboard } from './chunk-STJ7QBC3.js';
2
1
  import { Modal } from './chunk-XEGZHHRH.js';
3
- import { Checkbox } from './chunk-CPEPST4S.js';
2
+ import { useClipboard } from './chunk-STJ7QBC3.js';
3
+ import { Checkbox } from './chunk-PJIPUEJ3.js';
4
4
  import { EmptyValue } from './chunk-EUCC37WY.js';
5
5
  import { useCardHeaderSlot } from './chunk-AV2DXAEL.js';
6
6
  import { Menu } from './chunk-BUVCV3WI.js';
@@ -1,7 +1,7 @@
1
1
  import { HeadingLevelProvider } from './chunk-PQADV6P6.js';
2
2
  import { IconButton } from './chunk-4FQT6ZAY.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
- import './ArtifactPanel-HW3SU7CP.css';
4
+ import './ArtifactPanel-KKUUIPEU.css';
5
5
  import { useId, useRef, useLayoutEffect, useEffect } from 'react';
6
6
  import { MinimizeGlyph, MaximizeGlyph, XGlyph } from '@crewlethq/icons/glyphs';
7
7
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -19,6 +19,7 @@ function ArtifactPanel({
19
19
  expanded = false,
20
20
  onExpandedChange,
21
21
  actions,
22
+ footer,
22
23
  focusOnOpen = true,
23
24
  labels,
24
25
  className,
@@ -82,7 +83,8 @@ function ArtifactPanel({
82
83
  /* @__PURE__ */ jsx(IconButton, { label: words.close(title), icon: /* @__PURE__ */ jsx(XGlyph, { size: "sm" }), size: "sm", variant: "ghost", onClick: onClose })
83
84
  ] })
84
85
  ] }),
85
- /* @__PURE__ */ jsx("div", { className: "crewlet-artifact-panel__body", children: /* @__PURE__ */ jsx(HeadingLevelProvider, { level: 3, children }) })
86
+ /* @__PURE__ */ jsx("div", { className: "crewlet-artifact-panel__body", children: /* @__PURE__ */ jsx(HeadingLevelProvider, { level: 3, children }) }),
87
+ footer ? /* @__PURE__ */ jsx("div", { className: "crewlet-artifact-panel__foot", children: footer }) : null
86
88
  ]
87
89
  }
88
90
  );