@crewlethq/ui 0.17.1 → 0.19.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 (226) hide show
  1. package/README.md +55 -3
  2. package/dist/AccessDeniedCard/AccessDeniedCard.d.ts +39 -0
  3. package/dist/AccessDeniedCard/index.d.ts +3 -0
  4. package/dist/AccessDeniedCard/index.js +8 -0
  5. package/dist/AccessDeniedCard-WXM5DBDZ.css +8 -0
  6. package/dist/AppShell/index.js +2 -2
  7. package/dist/ArrangeBoard/ArrangeBoard.d.ts +32 -0
  8. package/dist/ArrangeBoard/index.d.ts +3 -0
  9. package/dist/ArrangeBoard/index.js +2 -0
  10. package/dist/ArrangeBoard-MQE35P6Z.css +80 -0
  11. package/dist/ArrangeFrame/ArrangeFrame.d.ts +82 -0
  12. package/dist/ArrangeFrame/index.d.ts +5 -0
  13. package/dist/ArrangeFrame/index.js +5 -0
  14. package/dist/ArrangeFrame/useGridResize.d.ts +66 -0
  15. package/dist/ArrangeFrame-3PLZAG22.css +329 -0
  16. package/dist/ArrangeGroup/ArrangeGroup.d.ts +42 -0
  17. package/dist/ArrangeGroup/index.d.ts +3 -0
  18. package/dist/ArrangeGroup/index.js +4 -0
  19. package/dist/ArrangeGroup-SHFHA7BE.css +170 -0
  20. package/dist/ArrangeList/ArrangeList.d.ts +64 -0
  21. package/dist/ArrangeList/index.d.ts +3 -0
  22. package/dist/ArrangeList/index.js +10 -0
  23. package/dist/ArrangeList-NNRTDFI5.css +94 -0
  24. package/dist/ArtifactCard/ArtifactCard.d.ts +61 -0
  25. package/dist/ArtifactCard/index.d.ts +3 -0
  26. package/dist/ArtifactCard/index.js +12 -0
  27. package/dist/ArtifactCard-EXVAEBV6.css +109 -0
  28. package/dist/AuthLayout/index.js +2 -2
  29. package/dist/AvatarPicker/index.js +5 -5
  30. package/dist/Canvas/index.js +2 -2
  31. package/dist/Card/index.js +1 -1
  32. package/dist/ChangeMark/ChangeMark.d.ts +26 -0
  33. package/dist/ChangeMark/index.d.ts +3 -0
  34. package/dist/ChangeMark/index.js +8 -0
  35. package/dist/ChangeMark-3VMTW7MT.css +22 -0
  36. package/dist/Checkbox/index.js +3 -3
  37. package/dist/Combobox/index.js +3 -3
  38. package/dist/CommandPalette/index.js +5 -5
  39. package/dist/Composer/index.js +3 -3
  40. package/dist/ConfirmField/index.js +3 -3
  41. package/dist/CursorFlag/CursorFlag.d.ts +34 -0
  42. package/dist/CursorFlag/index.d.ts +3 -0
  43. package/dist/CursorFlag/index.js +3 -0
  44. package/dist/CursorFlag-AJQJBUGY.css +61 -0
  45. package/dist/DataTable/index.js +7 -7
  46. package/dist/DataView/index.js +13 -13
  47. package/dist/DateTimePicker/index.js +2 -2
  48. package/dist/Divider/Divider.d.ts +15 -0
  49. package/dist/Divider/index.d.ts +3 -0
  50. package/dist/Divider/index.js +2 -0
  51. package/dist/Divider-3Z77ZAJH.css +37 -0
  52. package/dist/FollowBanner/FollowBanner.d.ts +35 -0
  53. package/dist/FollowBanner/index.d.ts +2 -0
  54. package/dist/FollowBanner/index.js +7 -0
  55. package/dist/FrameProtocol/index.d.ts +2 -0
  56. package/dist/FrameProtocol/index.js +2 -0
  57. package/dist/FrameProtocol/protocol.d.ts +164 -0
  58. package/dist/HuePicker/HuePicker.d.ts +16 -4
  59. package/dist/HuePicker/index.js +3 -3
  60. package/dist/{HuePicker-5QIUR6L7.css → HuePicker-PAIGH6LR.css} +6 -0
  61. package/dist/IconTile/IconTile.d.ts +23 -0
  62. package/dist/IconTile/index.d.ts +3 -0
  63. package/dist/IconTile/index.js +2 -0
  64. package/dist/IconTile-4GNWTLAW.css +30 -0
  65. package/dist/Input/index.js +2 -2
  66. package/dist/LinkInterstitial/LinkInterstitial.d.ts +41 -0
  67. package/dist/LinkInterstitial/index.d.ts +3 -0
  68. package/dist/LinkInterstitial/index.js +11 -0
  69. package/dist/LinkInterstitial-ET4MSVSV.css +34 -0
  70. package/dist/ListInput/index.js +4 -4
  71. package/dist/LiveCursor/LiveCursor.d.ts +34 -0
  72. package/dist/LiveCursor/index.d.ts +3 -0
  73. package/dist/LiveCursor/index.js +4 -0
  74. package/dist/LiveCursor-DZHGIYWE.css +47 -0
  75. package/dist/MarkdownEditor/index.js +3 -3
  76. package/dist/Menu/index.js +2 -2
  77. package/dist/Modal/Modal.d.ts +20 -0
  78. package/dist/Modal/index.js +2 -2
  79. package/dist/{Modal-RQAYGG4L.css → Modal-XOSV6FAH.css} +14 -0
  80. package/dist/Note/Note.d.ts +23 -0
  81. package/dist/Note/index.d.ts +3 -0
  82. package/dist/Note/index.js +2 -0
  83. package/dist/Note-RT4L6VUT.css +58 -0
  84. package/dist/OrgTable/index.js +5 -5
  85. package/dist/PinTile/PinTile.d.ts +37 -0
  86. package/dist/PinTile/index.d.ts +3 -0
  87. package/dist/PinTile/index.js +4 -0
  88. package/dist/PinTile-IIA25MNZ.css +141 -0
  89. package/dist/PresenceModel/index.d.ts +2 -0
  90. package/dist/PresenceModel/index.js +3 -0
  91. package/dist/PresenceModel/presence.d.ts +80 -0
  92. package/dist/PresenceRing/PresenceRing.d.ts +31 -0
  93. package/dist/PresenceRing/index.d.ts +3 -0
  94. package/dist/PresenceRing/index.js +2 -0
  95. package/dist/PresenceRing-M4C22Q6U.css +68 -0
  96. package/dist/PresenceStack/PresenceStack.d.ts +36 -0
  97. package/dist/PresenceStack/index.d.ts +3 -0
  98. package/dist/PresenceStack/index.js +9 -0
  99. package/dist/PresenceStack-QSXQJND5.css +59 -0
  100. package/dist/PricingCard/index.js +2 -2
  101. package/dist/Principal/Principal.d.ts +35 -0
  102. package/dist/Principal/index.d.ts +2 -0
  103. package/dist/Principal/index.js +4 -0
  104. package/dist/PrincipalPicker/PrincipalPicker.d.ts +45 -0
  105. package/dist/PrincipalPicker/index.d.ts +3 -0
  106. package/dist/PrincipalPicker/index.js +12 -0
  107. package/dist/PrincipalPicker-7NNZTGUP.css +41 -0
  108. package/dist/Reorder/index.d.ts +2 -0
  109. package/dist/Reorder/index.js +4 -0
  110. package/dist/Reorder/useReorder.d.ts +138 -0
  111. package/dist/SandboxedFrame/SandboxedFrame.d.ts +142 -0
  112. package/dist/SandboxedFrame/index.d.ts +3 -0
  113. package/dist/SandboxedFrame/index.js +8 -0
  114. package/dist/SandboxedFrame-L6HBQZCD.css +50 -0
  115. package/dist/ShareDialog/ShareDialog.d.ts +101 -0
  116. package/dist/ShareDialog/index.d.ts +5 -0
  117. package/dist/ShareDialog/index.js +19 -0
  118. package/dist/ShareDialog/roles.d.ts +10 -0
  119. package/dist/ShareDialog-YZ4F37MW.css +146 -0
  120. package/dist/SheetGrid/SheetGrid.d.ts +78 -0
  121. package/dist/SheetGrid/cells.d.ts +117 -0
  122. package/dist/SheetGrid/index.d.ts +5 -0
  123. package/dist/SheetGrid/index.js +18 -0
  124. package/dist/SheetGrid-4HOXDVIB.css +595 -0
  125. package/dist/SheetModel/clipboard.d.ts +37 -0
  126. package/dist/SheetModel/edits.d.ts +68 -0
  127. package/dist/SheetModel/index.d.ts +14 -0
  128. package/dist/SheetModel/index.js +3 -0
  129. package/dist/SheetModel/labels.d.ts +71 -0
  130. package/dist/SheetModel/navigation.d.ts +79 -0
  131. package/dist/SheetModel/options.d.ts +28 -0
  132. package/dist/SheetModel/types.d.ts +126 -0
  133. package/dist/SheetModel/values.d.ts +76 -0
  134. package/dist/SheetModel/view.d.ts +32 -0
  135. package/dist/StatGroup/StatGroup.d.ts +7 -1
  136. package/dist/StatGroup/index.js +1 -1
  137. package/dist/{StatGroup-HVG6O62W.css → StatGroup-PADTJCUQ.css} +7 -0
  138. package/dist/Switcher/index.js +8 -8
  139. package/dist/Tag/Tag.d.ts +13 -0
  140. package/dist/Tag/index.js +1 -1
  141. package/dist/{Tag-4BRQS6GN.css → Tag-SRGTK5KI.css} +42 -0
  142. package/dist/TagsInput/index.js +4 -4
  143. package/dist/ThemeSwitcher/apply.d.ts +43 -0
  144. package/dist/ThemeSwitcher/index.d.ts +4 -2
  145. package/dist/ThemeSwitcher/index.js +3 -2
  146. package/dist/ThemeSwitcher/preferences.d.ts +2 -19
  147. package/dist/ThemeToggle/index.js +3 -2
  148. package/dist/TimeWindowPicker/index.js +4 -4
  149. package/dist/Timeline/Timeline.d.ts +7 -2
  150. package/dist/Timeline/index.js +1 -1
  151. package/dist/TreeCanvas/index.js +3 -3
  152. package/dist/TreeGrid/index.js +2 -2
  153. package/dist/VersionTimeline/VersionTimeline.d.ts +76 -0
  154. package/dist/VersionTimeline/index.d.ts +3 -0
  155. package/dist/VersionTimeline/index.js +10 -0
  156. package/dist/VersionTimeline-EBJ6MIPJ.css +108 -0
  157. package/dist/{chunk-SBRE7IML.js → chunk-2ONWEDQT.js} +1 -1
  158. package/dist/chunk-35XVR5JQ.js +81 -0
  159. package/dist/chunk-3BYIWOTV.js +29 -0
  160. package/dist/chunk-4KP4RC3O.js +25 -0
  161. package/dist/chunk-5XACKNYR.js +53 -0
  162. package/dist/chunk-63WJLRB2.js +24 -0
  163. package/dist/{chunk-L4ZLXYPO.js → chunk-6A7P6II6.js} +1 -1
  164. package/dist/chunk-6KWNXIDE.js +18 -0
  165. package/dist/chunk-6MIXD6IW.js +49 -0
  166. package/dist/{chunk-FA2V2VT6.js → chunk-7FQEUNHX.js} +4 -4
  167. package/dist/{chunk-DEEVA6TV.js → chunk-7IJARUIF.js} +17 -4
  168. package/dist/{chunk-JCO3SIST.js → chunk-7ZNKEPLN.js} +2 -2
  169. package/dist/chunk-AIVZVKYZ.js +126 -0
  170. package/dist/{chunk-FSKTOIYC.js → chunk-AJESMWJS.js} +5 -5
  171. package/dist/{chunk-O67VF5MS.js → chunk-AUQPBL4O.js} +2 -2
  172. package/dist/chunk-CENOOTAV.js +1 -0
  173. package/dist/chunk-DEJHAOC5.js +279 -0
  174. package/dist/{chunk-UCYIRDND.js → chunk-DQSXWQMJ.js} +1 -1
  175. package/dist/{chunk-JF7X673Z.js → chunk-EIGEIT4O.js} +2 -2
  176. package/dist/chunk-F6V2PTN6.js +142 -0
  177. package/dist/chunk-FFMCMSJB.js +1 -0
  178. package/dist/chunk-FW7HN6AP.js +40 -0
  179. package/dist/chunk-G3EEUNZQ.js +46 -0
  180. package/dist/{chunk-LOECRMHA.js → chunk-GFV4LJHH.js} +4 -3
  181. package/dist/chunk-HTCJSUIZ.js +216 -0
  182. package/dist/{chunk-75CLJNDQ.js → chunk-I6NZKJGK.js} +1 -1
  183. package/dist/chunk-I6POAB4V.js +20 -0
  184. package/dist/chunk-IXJHTFXG.js +17 -0
  185. package/dist/chunk-JEDWC3GI.js +98 -0
  186. package/dist/{chunk-QVAFL2LE.js → chunk-JXRI5YD2.js} +3 -3
  187. package/dist/{chunk-ZFH7H7GS.js → chunk-LOLELDBW.js} +1 -1
  188. package/dist/{chunk-6TSZCBID.js → chunk-NP2C4Y4E.js} +1 -1
  189. package/dist/{chunk-6NN2F7GM.js → chunk-OIMAK5AE.js} +6 -6
  190. package/dist/chunk-PPAQFGS2.js +12 -0
  191. package/dist/chunk-PUQY4WXR.js +1235 -0
  192. package/dist/chunk-QO7QEQKV.js +24 -0
  193. package/dist/chunk-RT323I4I.js +49 -0
  194. package/dist/chunk-S35G6PNC.js +80 -0
  195. package/dist/{chunk-HENOTGUV.js → chunk-T7ICFXEC.js} +2 -2
  196. package/dist/{chunk-2OLCUNG7.js → chunk-TBIZSQHB.js} +1 -1
  197. package/dist/chunk-TODND5AD.js +99 -0
  198. package/dist/{chunk-PQ63NLFC.js → chunk-UXELEORU.js} +3 -3
  199. package/dist/{chunk-6NIMZ5XC.js → chunk-VJLYGPPE.js} +1 -1
  200. package/dist/chunk-VYDFB6PJ.js +243 -0
  201. package/dist/{chunk-7QFAORCY.js → chunk-WHZFDS62.js} +1 -1
  202. package/dist/chunk-WXAULPBH.js +135 -0
  203. package/dist/chunk-XJ2QEBMM.js +277 -0
  204. package/dist/{chunk-NXDEPUVG.js → chunk-XU4ZUGLE.js} +20 -5
  205. package/dist/chunk-YM3GJHGW.js +69 -0
  206. package/dist/{chunk-T3ZWBZ65.js → chunk-YSJYWQSP.js} +2 -2
  207. package/dist/{chunk-CXBAIVYZ.js → chunk-YSY6UJ4F.js} +1 -1
  208. package/dist/chunk-YYZRLLLY.js +822 -0
  209. package/dist/{chunk-EG6NYWVK.js → chunk-YZCJ63JF.js} +1 -1
  210. package/dist/{chunk-K3OWD7BJ.js → chunk-Z24ZUSTX.js} +4 -19
  211. package/dist/chunk-ZXXDZJQN.js +36 -0
  212. package/dist/generated/index.d.ts +27 -0
  213. package/dist/generated/index.js +98 -69
  214. package/dist/index.js +98 -69
  215. package/dist/styles.css +13625 -11206
  216. package/dist/utils/index.js +1 -1
  217. package/package.json +3 -3
  218. package/dist/chunk-EXHQ2QVW.js +0 -1
  219. package/dist/{chunk-CRVETI5L.js → chunk-4Z7PW3L6.js} +0 -0
  220. package/dist/{chunk-OYOQU7O5.js → chunk-EDGCPVVD.js} +1 -1
  221. package/dist/{chunk-NS7BVLL3.js → chunk-JULMZNGT.js} +1 -1
  222. package/dist/{chunk-2UU5IGZI.js → chunk-L4H7SSWF.js} +1 -1
  223. package/dist/{chunk-4GB57WIT.js → chunk-Q55TXPA3.js} +1 -1
  224. package/dist/{chunk-77IEHFY4.js → chunk-QQGWES46.js} +1 -1
  225. package/dist/{chunk-B2Z2CDM5.js → chunk-T5TFZ2JB.js} +1 -1
  226. package/dist/{chunk-AMH67MLM.js → chunk-TXAFKH2A.js} +1 -1
@@ -1,13 +1,13 @@
1
1
  import { Button } from './chunk-OG6CL3EB.js';
2
- import { IconButton } from './chunk-4FQT6ZAY.js';
3
2
  import { HeadingLevelProvider } from './chunk-PQADV6P6.js';
4
3
  import { useLayerContainer } from './chunk-HMPFGIWM.js';
5
4
  import { focusables, useModalLayer, useBodyScrollLock } from './chunk-R25RGPUB.js';
5
+ import { IconButton } from './chunk-4FQT6ZAY.js';
6
6
  import { cx } from './chunk-ODEQTPFM.js';
7
- import './Modal-RQAYGG4L.css';
7
+ import './Modal-XOSV6FAH.css';
8
8
  import { useId, useRef, useCallback } from 'react';
9
9
  import { createPortal } from 'react-dom';
10
- import { XGlyph } from '@crewlethq/icons/glyphs';
10
+ import { ChevronLeftGlyph, XGlyph } from '@crewlethq/icons/glyphs';
11
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
12
12
 
13
13
  function Modal({ open, ...rest }) {
@@ -35,6 +35,8 @@ function ModalFrame({
35
35
  footer,
36
36
  footerStart,
37
37
  headerActions,
38
+ onBack,
39
+ backLabel = "Back",
38
40
  children,
39
41
  size = "sm",
40
42
  variant = "dialog",
@@ -43,6 +45,7 @@ function ModalFrame({
43
45
  role = "dialog",
44
46
  dismissable = true,
45
47
  closeOnBackdrop = true,
48
+ backdrop = "veil",
46
49
  showCloseButton = true,
47
50
  stackBody = false,
48
51
  flush = false,
@@ -101,7 +104,16 @@ function ModalFrame({
101
104
  };
102
105
  const closes = banded && showCloseButton;
103
106
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
104
- banded && (given(title) || given(subtitle) || given(headerActions) || closes) ? /* @__PURE__ */ jsxs("div", { className: cx("crewlet-modal__header", sheet ? "crewlet-modal__header--sheet" : void 0), children: [
107
+ banded && (given(title) || given(subtitle) || given(headerActions) || closes || onBack) ? /* @__PURE__ */ jsxs("div", { className: cx("crewlet-modal__header", sheet ? "crewlet-modal__header--sheet" : void 0), children: [
108
+ onBack ? /* @__PURE__ */ jsx(
109
+ IconButton,
110
+ {
111
+ className: "crewlet-modal__back",
112
+ label: backLabel,
113
+ icon: /* @__PURE__ */ jsx(ChevronLeftGlyph, { size: "md" }),
114
+ onClick: onBack
115
+ }
116
+ ) : null,
105
117
  given(icon) ? /* @__PURE__ */ jsx("span", { className: "crewlet-modal__icon", "aria-hidden": "true", children: icon }) : null,
106
118
  /* @__PURE__ */ jsxs("div", { className: "crewlet-modal__heading", children: [
107
119
  given(title) ? /* @__PURE__ */ jsx(
@@ -163,6 +175,7 @@ function ModalFrame({
163
175
  "crewlet-modal-overlay",
164
176
  `crewlet-modal-overlay--${variant}`,
165
177
  `crewlet-modal-overlay--${place}`,
178
+ backdrop === "clear" && "crewlet-modal-overlay--clear",
166
179
  // Fixed against the window, absolute inside a LayerHost: a fullscreen
167
180
  // container paints only its own subtree, so a surface drawn there
168
181
  // covers the container rather than a viewport it cannot reach.
@@ -1,8 +1,8 @@
1
- import { Tag } from './chunk-LOECRMHA.js';
1
+ import { Tag } from './chunk-GFV4LJHH.js';
2
2
  import { useListbox } from './chunk-F73MC7EZ.js';
3
+ import { isComposing } from './chunk-R25RGPUB.js';
3
4
  import { announce } from './chunk-RSAQOKH3.js';
4
5
  import { IconButton } from './chunk-4FQT6ZAY.js';
5
- import { isComposing } from './chunk-R25RGPUB.js';
6
6
  import { cx } from './chunk-ODEQTPFM.js';
7
7
  import './TagsInput-P2HS5QL4.css';
8
8
  import { forwardRef, useState, useRef, useId, useImperativeHandle, useEffect, useMemo, useLayoutEffect } from 'react';
@@ -0,0 +1,126 @@
1
+ import { Switch } from './chunk-A722MUNR.js';
2
+ import { useReorder } from './chunk-XJ2QEBMM.js';
3
+ import { Checkbox } from './chunk-I6NZKJGK.js';
4
+ import { IconButton } from './chunk-4FQT6ZAY.js';
5
+ import { VisuallyHidden } from './chunk-BZFWDIV6.js';
6
+ import { cx } from './chunk-ODEQTPFM.js';
7
+ import './ArrangeList-NNRTDFI5.css';
8
+ import { GripVerticalGlyph, ChevronUpGlyph, ChevronDownGlyph } from '@crewlethq/icons/glyphs';
9
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
10
+
11
+ var DEFAULT_ARRANGE_LABELS = {
12
+ handle: (name) => `Move ${name}`,
13
+ hint: "Drag to move, or press the up or down arrow key.",
14
+ moved: (name, position, total) => `${name} moved to position ${position} of ${total}.`,
15
+ moveUp: (name) => `Move ${name} up`,
16
+ moveDown: (name) => `Move ${name} down`,
17
+ atTop: (name) => `${name} is first.`,
18
+ atBottom: (name) => `${name} is last.`
19
+ };
20
+ function ArrangeList({
21
+ label,
22
+ items,
23
+ onReorder,
24
+ onShownChange,
25
+ toggle = "switch",
26
+ moveButtons = true,
27
+ labels: overrides,
28
+ className
29
+ }) {
30
+ const labels = { ...DEFAULT_ARRANGE_LABELS, ...overrides };
31
+ const byKey = new Map(items.map((item) => [item.key, item]));
32
+ const nameOf = (key) => byKey.get(key)?.label ?? key;
33
+ const reorder = useReorder({
34
+ keys: items.map((item) => item.key),
35
+ onReorder,
36
+ nameOf,
37
+ labels
38
+ });
39
+ const last = reorder.order.length - 1;
40
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
41
+ /* @__PURE__ */ jsx(VisuallyHidden, { id: reorder.hintId, children: reorder.hint }),
42
+ /* @__PURE__ */ jsx(
43
+ "ul",
44
+ {
45
+ className: cx("crewlet-arrange-list", className),
46
+ "aria-label": label,
47
+ ...reorder.containerProps,
48
+ children: reorder.order.map((key, index) => {
49
+ const item = byKey.get(key);
50
+ if (!item) return null;
51
+ const { "aria-label": handleName, ...handle } = reorder.handleProps(key);
52
+ const fixed = item.fixed !== void 0 && item.shown ? item.fixed : void 0;
53
+ const Toggle = toggle === "switch" ? Switch : Checkbox;
54
+ return /* @__PURE__ */ jsxs(
55
+ "li",
56
+ {
57
+ className: cx("crewlet-arrange-list__item", !item.shown && "is-off"),
58
+ ...reorder.itemProps(key),
59
+ children: [
60
+ /* @__PURE__ */ jsx(
61
+ IconButton,
62
+ {
63
+ size: "sm",
64
+ variant: "ghost",
65
+ className: "crewlet-arrange-list__handle",
66
+ label: handleName,
67
+ icon: /* @__PURE__ */ jsx(GripVerticalGlyph, { size: "md" }),
68
+ ...handle
69
+ }
70
+ ),
71
+ /* @__PURE__ */ jsx(
72
+ Toggle,
73
+ {
74
+ className: "crewlet-arrange-list__toggle",
75
+ label: item.icon ? /* @__PURE__ */ jsxs("span", { className: "crewlet-arrange-list__label", children: [
76
+ /* @__PURE__ */ jsx("span", { className: "crewlet-arrange-list__icon", "aria-hidden": "true", children: item.icon }),
77
+ item.label
78
+ ] }) : item.label,
79
+ description: fixed ?? item.description,
80
+ checked: item.shown,
81
+ ...fixed ? { "aria-disabled": true } : {},
82
+ onChange: (event) => {
83
+ if (fixed) {
84
+ event.preventDefault();
85
+ return;
86
+ }
87
+ onShownChange(key, event.target.checked);
88
+ }
89
+ }
90
+ ),
91
+ item.trailing ? /* @__PURE__ */ jsx("span", { className: "crewlet-arrange-list__trailing", children: item.trailing }) : null,
92
+ moveButtons ? /* @__PURE__ */ jsxs("span", { className: "crewlet-arrange-list__moves", children: [
93
+ /* @__PURE__ */ jsx(
94
+ IconButton,
95
+ {
96
+ size: "sm",
97
+ variant: "ghost",
98
+ label: labels.moveUp(item.label),
99
+ icon: /* @__PURE__ */ jsx(ChevronUpGlyph, { size: "sm" }),
100
+ ...index === 0 ? { disabledReason: labels.atTop(item.label) } : {},
101
+ onClick: () => reorder.move(key, -1)
102
+ }
103
+ ),
104
+ /* @__PURE__ */ jsx(
105
+ IconButton,
106
+ {
107
+ size: "sm",
108
+ variant: "ghost",
109
+ label: labels.moveDown(item.label),
110
+ icon: /* @__PURE__ */ jsx(ChevronDownGlyph, { size: "sm" }),
111
+ ...index === last ? { disabledReason: labels.atBottom(item.label) } : {},
112
+ onClick: () => reorder.move(key, 1)
113
+ }
114
+ )
115
+ ] }) : null
116
+ ]
117
+ },
118
+ key
119
+ );
120
+ })
121
+ }
122
+ )
123
+ ] });
124
+ }
125
+
126
+ export { ArrangeList, DEFAULT_ARRANGE_LABELS };
@@ -1,11 +1,11 @@
1
- import { Menu } from './chunk-NS7BVLL3.js';
2
- import { EmptyValue } from './chunk-EUCC37WY.js';
3
- import { Checkbox } from './chunk-75CLJNDQ.js';
4
- import { Modal } from './chunk-DEEVA6TV.js';
1
+ import { Menu } from './chunk-JULMZNGT.js';
5
2
  import { useClipboard } from './chunk-STJ7QBC3.js';
3
+ import { Modal } from './chunk-7IJARUIF.js';
6
4
  import { Button } from './chunk-OG6CL3EB.js';
7
- import { IconButton } from './chunk-4FQT6ZAY.js';
5
+ import { Checkbox } from './chunk-I6NZKJGK.js';
6
+ import { EmptyValue } from './chunk-EUCC37WY.js';
8
7
  import { useCardHeaderSlot } from './chunk-AV2DXAEL.js';
8
+ import { IconButton } from './chunk-4FQT6ZAY.js';
9
9
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
10
10
  import { cx } from './chunk-ODEQTPFM.js';
11
11
  import './DataTable-WZK3V62B.css';
@@ -36,7 +36,7 @@ function TimelineItem({
36
36
  if (position === null) {
37
37
  throw new Error("Timeline.Item must be rendered inside a Timeline.");
38
38
  }
39
- const spoken = label ?? `${itemLabel} ${position}`;
39
+ const spoken = label === false ? null : label ?? `${itemLabel} ${position}`;
40
40
  return /* @__PURE__ */ jsxs(
41
41
  "li",
42
42
  {
@@ -46,7 +46,7 @@ function TimelineItem({
46
46
  children: [
47
47
  /* @__PURE__ */ jsx("span", { className: "crewlet-timeline__rail", children: /* @__PURE__ */ jsx("span", { className: "crewlet-timeline__node", "aria-hidden": true, children: marker ?? position }) }),
48
48
  /* @__PURE__ */ jsxs("div", { className: "crewlet-timeline__body", children: [
49
- /* @__PURE__ */ jsx(VisuallyHidden, { children: spoken }),
49
+ spoken === null ? null : /* @__PURE__ */ jsx(VisuallyHidden, { children: spoken }),
50
50
  children
51
51
  ] })
52
52
  ]
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,279 @@
1
+ import { announce } from './chunk-RSAQOKH3.js';
2
+ import { IconButton } from './chunk-4FQT6ZAY.js';
3
+ import { cx } from './chunk-ODEQTPFM.js';
4
+ import './ArrangeFrame-3PLZAG22.css';
5
+ import { forwardRef, useRef, useCallback, useState, useEffect } from 'react';
6
+ import { TrashGlyph } from '@crewlethq/icons/glyphs';
7
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
+
9
+ var clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
10
+ function gridOf(node) {
11
+ const grid = node.parentElement;
12
+ const own = node.getBoundingClientRect();
13
+ if (!grid) return { columns: 12, step: own.width, gap: 0, left: 0, width: own.width };
14
+ const style = getComputedStyle(grid);
15
+ const tracks = style.gridTemplateColumns.split(" ").map(parseFloat).filter((n) => !Number.isNaN(n));
16
+ const gap = parseFloat(style.columnGap) || 0;
17
+ const track = tracks[0] ?? grid.clientWidth / 12;
18
+ const box = grid.getBoundingClientRect();
19
+ const padLeft = parseFloat(style.paddingLeft) || 0;
20
+ const padRight = parseFloat(style.paddingRight) || 0;
21
+ return {
22
+ columns: tracks.length || 12,
23
+ step: track + gap,
24
+ gap,
25
+ left: box.left + padLeft - own.left,
26
+ width: box.width - padLeft - padRight
27
+ };
28
+ }
29
+ function useGridResize(frame, { name, size, rowHeight, minColumns = 1, maxColumns, minRows = 2, maxRows = 60, vertical = true, onResize }) {
30
+ const [live, setLive] = useState(null);
31
+ const [guides, setGuides] = useState(null);
32
+ const drag = useRef(null);
33
+ const say = useCallback(
34
+ (next, columns) => {
35
+ const height = next.rows === null ? "its height fits its content" : `${next.rows * rowHeight} pixels tall`;
36
+ announce(`${name}: ${next.columns} of ${columns} columns wide, ${height}.`);
37
+ },
38
+ [name, rowHeight]
39
+ );
40
+ const cancel = useCallback(() => {
41
+ drag.current = null;
42
+ setLive(null);
43
+ setGuides(null);
44
+ }, []);
45
+ useEffect(() => {
46
+ if (!live) return;
47
+ const onKey = (e) => {
48
+ if (e.key === "Escape") cancel();
49
+ };
50
+ window.addEventListener("keydown", onKey);
51
+ return () => window.removeEventListener("keydown", onKey);
52
+ }, [live, cancel]);
53
+ const sizeAt = (d, x, y) => {
54
+ const hi = Math.min(maxColumns ?? d.columns, d.columns);
55
+ const columns = clamp(Math.round((d.width + (x - d.x) + d.gap) / d.step), Math.min(minColumns, hi), hi);
56
+ const dy = y - d.y;
57
+ const rows = !vertical ? size.rows : size.rows === null && Math.abs(dy) < rowHeight / 2 ? null : clamp(Math.round((d.height + dy) / rowHeight), minRows, maxRows);
58
+ return { columns, rows };
59
+ };
60
+ const handleProps = {
61
+ "aria-label": `Resize ${name}`,
62
+ "aria-keyshortcuts": "ArrowLeft ArrowRight ArrowUp ArrowDown Enter",
63
+ onPointerDown: (event) => {
64
+ if (event.button !== 0 || !frame.current) return;
65
+ event.preventDefault();
66
+ event.stopPropagation();
67
+ try {
68
+ event.currentTarget.setPointerCapture?.(event.pointerId);
69
+ } catch {
70
+ }
71
+ const rect = frame.current.getBoundingClientRect();
72
+ const grid = gridOf(frame.current);
73
+ drag.current = {
74
+ pointer: event.pointerId,
75
+ x: event.clientX,
76
+ y: event.clientY,
77
+ width: rect.width,
78
+ height: rect.height,
79
+ step: grid.step,
80
+ gap: grid.gap,
81
+ columns: grid.columns
82
+ };
83
+ setGuides({ left: grid.left, width: grid.width, columns: grid.columns, gap: grid.gap });
84
+ setLive(size);
85
+ },
86
+ onPointerMove: (event) => {
87
+ const d = drag.current;
88
+ if (!d || d.pointer !== event.pointerId) return;
89
+ const next = sizeAt(d, event.clientX, event.clientY);
90
+ setLive((was) => was && was.columns === next.columns && was.rows === next.rows ? was : next);
91
+ },
92
+ onPointerUp: (event) => {
93
+ const d = drag.current;
94
+ if (!d || d.pointer !== event.pointerId) return;
95
+ const next = sizeAt(d, event.clientX, event.clientY);
96
+ drag.current = null;
97
+ setLive(null);
98
+ setGuides(null);
99
+ if (next.columns !== size.columns || next.rows !== size.rows) {
100
+ onResize(next);
101
+ say(next, d.columns);
102
+ }
103
+ },
104
+ onPointerCancel: cancel,
105
+ onKeyDown: (event) => {
106
+ if (!frame.current || event.altKey || event.ctrlKey || event.metaKey) return;
107
+ const grid = gridOf(frame.current);
108
+ const hi = Math.min(maxColumns ?? grid.columns, grid.columns);
109
+ const rowsNow = size.rows ?? Math.round(frame.current.getBoundingClientRect().height / rowHeight);
110
+ let next = null;
111
+ if (event.key === "ArrowLeft") next = { ...size, columns: clamp(size.columns - 1, Math.min(minColumns, hi), hi) };
112
+ if (event.key === "ArrowRight") next = { ...size, columns: clamp(size.columns + 1, Math.min(minColumns, hi), hi) };
113
+ if (vertical && event.key === "ArrowUp") next = { ...size, rows: clamp(rowsNow - 1, minRows, maxRows) };
114
+ if (vertical && event.key === "ArrowDown") next = { ...size, rows: clamp(rowsNow + 1, minRows, maxRows) };
115
+ if (vertical && event.key === "Enter") next = { ...size, rows: null };
116
+ if (!next) return;
117
+ event.preventDefault();
118
+ event.stopPropagation();
119
+ if (next.columns === size.columns && next.rows === size.rows) return;
120
+ onResize(next);
121
+ say(next, grid.columns);
122
+ },
123
+ onDoubleClick: () => {
124
+ if (!vertical || size.rows === null) return;
125
+ const next = { ...size, rows: null };
126
+ onResize(next);
127
+ say(next, frame.current ? gridOf(frame.current).columns : 12);
128
+ }
129
+ };
130
+ return { live, guides, handleProps };
131
+ }
132
+ var FIELDS = 'input, textarea, select, [contenteditable="true"]';
133
+ function assign(ref, value) {
134
+ if (typeof ref === "function") ref(value);
135
+ else if (ref) ref.current = value;
136
+ }
137
+ var ArrangeFrame = forwardRef(function ArrangeFrame2({
138
+ name,
139
+ editing,
140
+ columns,
141
+ rows = null,
142
+ rowHeight = 24,
143
+ handle,
144
+ resize,
145
+ actions,
146
+ onRemove,
147
+ fixed,
148
+ container = false,
149
+ bare = false,
150
+ selected = false,
151
+ fresh,
152
+ className,
153
+ style,
154
+ children,
155
+ ...rest
156
+ }, ref) {
157
+ const node = useRef(null);
158
+ const setNode = useCallback(
159
+ (el) => {
160
+ node.current = el;
161
+ assign(ref, el);
162
+ },
163
+ [ref]
164
+ );
165
+ const vertical = resize?.vertical !== false;
166
+ const { live, guides, handleProps: resizeProps } = useGridResize(node, {
167
+ name,
168
+ size: { columns, rows },
169
+ rowHeight,
170
+ minColumns: resize?.minColumns,
171
+ maxColumns: resize?.maxColumns,
172
+ minRows: resize?.minRows,
173
+ maxRows: resize?.maxRows,
174
+ vertical,
175
+ onResize: (next) => resize?.onResize(next)
176
+ });
177
+ const shown = live ?? { columns, rows };
178
+ const placed = {
179
+ ...style,
180
+ gridColumn: `span ${shown.columns}`,
181
+ ...shown.rows !== null ? { height: `${shown.rows * rowHeight}px` } : {}
182
+ };
183
+ const onField = useRef(false);
184
+ const surface = editing && handle ? {
185
+ draggable: true,
186
+ tabIndex: 0,
187
+ role: "group",
188
+ "aria-label": name,
189
+ "aria-describedby": handle["aria-describedby"],
190
+ onPointerDownCapture: (event) => {
191
+ onField.current = event.target.closest(FIELDS) !== null;
192
+ },
193
+ onDragStart: (event) => {
194
+ if (onField.current) event.preventDefault();
195
+ else handle.onDragStart(event);
196
+ },
197
+ onDragEnd: handle.onDragEnd,
198
+ // The block's own keys only: arrows typed in a group's name move the caret.
199
+ onKeyDown: (event) => {
200
+ if (event.target === event.currentTarget) handle.onKeyDown(event);
201
+ }
202
+ } : {};
203
+ const resizeName = vertical ? `Resize ${name}` : `Change the width of ${name}`;
204
+ return /* @__PURE__ */ jsxs(
205
+ "div",
206
+ {
207
+ ref: setNode,
208
+ className: cx(
209
+ "crewlet-arrange-frame",
210
+ editing && "is-editing",
211
+ container && "is-container",
212
+ bare && "is-bare",
213
+ editing && selected && "is-selected",
214
+ live && "is-resizing",
215
+ shown.rows !== null && "is-fixed-height",
216
+ fresh && "is-fresh",
217
+ className
218
+ ),
219
+ style: placed,
220
+ "data-columns": shown.columns,
221
+ ...surface,
222
+ ...rest,
223
+ children: [
224
+ /* @__PURE__ */ jsx("div", { className: "crewlet-arrange-frame__content", inert: editing && !container || void 0, children }),
225
+ editing ? /* @__PURE__ */ jsxs(Fragment, { children: [
226
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-arrange-frame__bar", role: "group", "aria-label": `Tools for ${name}`, children: [
227
+ actions,
228
+ onRemove || fixed ? /* @__PURE__ */ jsxs(Fragment, { children: [
229
+ /* @__PURE__ */ jsx("span", { className: "crewlet-arrange-frame__sep", "aria-hidden": "true" }),
230
+ /* @__PURE__ */ jsx(
231
+ IconButton,
232
+ {
233
+ size: "sm",
234
+ variant: "ghost-danger",
235
+ label: `Remove ${name}`,
236
+ icon: /* @__PURE__ */ jsx(TrashGlyph, { size: "sm" }),
237
+ ...fixed ? { disabledReason: fixed } : {},
238
+ onClick: () => onRemove?.()
239
+ }
240
+ )
241
+ ] }) : null
242
+ ] }),
243
+ resize ? /* @__PURE__ */ jsx(
244
+ "button",
245
+ {
246
+ type: "button",
247
+ className: cx("crewlet-arrange-frame__resize", vertical ? "is-corner" : "is-edge"),
248
+ title: vertical ? `${resizeName}. Double click to fit the height to its content.` : resizeName,
249
+ draggable: false,
250
+ ...resizeProps,
251
+ "aria-label": resizeName,
252
+ children: /* @__PURE__ */ jsx("span", { className: "crewlet-arrange-frame__grip", "aria-hidden": "true" })
253
+ }
254
+ ) : null,
255
+ guides ? /* @__PURE__ */ jsx(
256
+ "span",
257
+ {
258
+ className: "crewlet-arrange-frame__guides",
259
+ "aria-hidden": "true",
260
+ style: {
261
+ left: `${guides.left}px`,
262
+ width: `${guides.width}px`,
263
+ gridTemplateColumns: `repeat(${guides.columns}, minmax(0, 1fr))`,
264
+ columnGap: `${guides.gap}px`
265
+ },
266
+ children: Array.from({ length: guides.columns }, (_, i) => /* @__PURE__ */ jsx("span", {}, i))
267
+ }
268
+ ) : null,
269
+ live ? /* @__PURE__ */ jsxs("span", { className: "crewlet-arrange-frame__size", "aria-hidden": "true", children: [
270
+ `${live.columns} ${live.columns === 1 ? "column" : "columns"}`,
271
+ vertical ? ` \xB7 ${live.rows === null ? "Height fits content" : `${live.rows * rowHeight} px tall`}` : ""
272
+ ] }) : null
273
+ ] }) : null
274
+ ]
275
+ }
276
+ );
277
+ });
278
+
279
+ export { ArrangeFrame, useGridResize };
@@ -1,4 +1,4 @@
1
- import { Tag } from './chunk-LOECRMHA.js';
1
+ import { Tag } from './chunk-GFV4LJHH.js';
2
2
  import './PricingCard-HTAHG4ST.css';
3
3
  import { forwardRef } from 'react';
4
4
  import { jsx } from 'react/jsx-runtime';
@@ -1,10 +1,10 @@
1
1
  import { Select } from './chunk-FC4YTTFJ.js';
2
- import { Input } from './chunk-4GB57WIT.js';
3
2
  import { today, shiftMonth, CalendarGrid, compareDays } from './chunk-GXQ3B46I.js';
4
3
  import { Button } from './chunk-OG6CL3EB.js';
5
- import { IconButton } from './chunk-4FQT6ZAY.js';
4
+ import { Input } from './chunk-Q55TXPA3.js';
6
5
  import { Popover } from './chunk-BWRGFKJP.js';
7
6
  import { isComposing } from './chunk-R25RGPUB.js';
7
+ import { IconButton } from './chunk-4FQT6ZAY.js';
8
8
  import { cx } from './chunk-ODEQTPFM.js';
9
9
  import './TimeWindowPicker-BZ5HHATU.css';
10
10
  import { useMemo, useState, useRef, useId, useEffect } from 'react';
@@ -0,0 +1,142 @@
1
+ import { isDensityPreference, isPaletteId, isThemePreference, applyTheme, applyPalette, applyDensity } from './chunk-ZXXDZJQN.js';
2
+ import { DEFAULT_THEME_ID } from '@crewlethq/tokens';
3
+
4
+ var FRAME_MESSAGE = {
5
+ /** Frame to host, on the window: "I am the document you embedded, here is your nonce". */
6
+ hello: "crewlet.hello",
7
+ /** Host to frame, on the window, carrying the transferred port. */
8
+ port: "crewlet.port",
9
+ /** Host to frame, on the port: the reader's appearance changed. */
10
+ theme: "crewlet.theme",
11
+ /** Frame to host, on the port: open this link outside the frame. */
12
+ openLink: "crewlet.openLink",
13
+ /** Frame to host, on the port: give keyboard focus back to the page around the frame. */
14
+ focusExit: "crewlet.focusExit"
15
+ };
16
+ var FRAME_BRIDGE = { min: 1, max: 1 };
17
+ var FRAME_ORIGIN_MODES = ["per_artifact", "shared"];
18
+ var DEFAULT_FRAME_THEME = { mode: "system", palette: DEFAULT_THEME_ID, density: "normal" };
19
+ function frameFragment(nonce, theme) {
20
+ const params = new URLSearchParams();
21
+ params.set("nonce", nonce);
22
+ params.set("theme", theme.mode);
23
+ params.set("palette", theme.palette);
24
+ params.set("density", theme.density);
25
+ return params.toString();
26
+ }
27
+ var NONCE = /^[A-Za-z0-9_-]{16,256}$/;
28
+ function parseFrameFragment(hash) {
29
+ const params = new URLSearchParams(hash.startsWith("#") ? hash.slice(1) : hash);
30
+ const nonce = params.get("nonce");
31
+ const mode = params.get("theme");
32
+ const palette = params.get("palette");
33
+ const density = params.get("density");
34
+ return {
35
+ nonce: nonce !== null && NONCE.test(nonce) ? nonce : null,
36
+ theme: {
37
+ mode: isThemePreference(mode) ? mode : DEFAULT_FRAME_THEME.mode,
38
+ // A well-formed id the tokens do not know selects no block and paints
39
+ // the default, which is what an unknown palette should do.
40
+ palette: isPaletteId(palette) ? palette : DEFAULT_FRAME_THEME.palette,
41
+ density: isDensityPreference(density) ? density : DEFAULT_FRAME_THEME.density
42
+ }
43
+ };
44
+ }
45
+ function isFrameNonce(value) {
46
+ return typeof value === "string" && NONCE.test(value);
47
+ }
48
+ function httpOrigin(raw) {
49
+ let url;
50
+ try {
51
+ url = new URL(raw);
52
+ } catch {
53
+ return null;
54
+ }
55
+ return url.protocol === "https:" || url.protocol === "http:" ? url.origin : null;
56
+ }
57
+ function frameOriginMatches(mode, expected, actual) {
58
+ return mode === "per_artifact" ? actual === expected : actual === "null";
59
+ }
60
+ function portTargetOrigin(mode, origin) {
61
+ return mode === "per_artifact" ? origin : "*";
62
+ }
63
+ var FRAME_SANDBOX_TOKENS = {
64
+ "allow-scripts": "always",
65
+ "allow-forms": "always",
66
+ // Only on an origin of the artifact's own: on the shared host it would hand
67
+ // every artifact the same storage and the same origin.
68
+ "allow-same-origin": "per_artifact",
69
+ // Only when the version declares them, which the engine decides.
70
+ "allow-downloads": "declared",
71
+ "allow-modals": "declared"
72
+ };
73
+ function checkFrameSandbox(sandbox, mode) {
74
+ const tokens = sandbox.split(/\s+/).filter(Boolean);
75
+ const refused = tokens.filter((token) => {
76
+ const rule = FRAME_SANDBOX_TOKENS[token];
77
+ if (rule === void 0) return true;
78
+ return rule === "per_artifact" && mode !== "per_artifact";
79
+ });
80
+ return refused.length > 0 ? { ok: false, refused } : { ok: true, value: tokens.join(" ") };
81
+ }
82
+ var record = (data) => typeof data === "object" && data !== null && !Array.isArray(data) ? data : null;
83
+ var CONTRACT = /^\d{1,4}\.\d{1,5}\.\d{1,6}$/;
84
+ var bridgeVersion = (value) => typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 9999;
85
+ function readFrameHello(data) {
86
+ const message = record(data);
87
+ if (!message || message["type"] !== FRAME_MESSAGE.hello) return null;
88
+ const { nonce, contract } = message;
89
+ const bridge = record(message["bridge"]);
90
+ if (!isFrameNonce(nonce) || typeof contract !== "string" || !CONTRACT.test(contract) || !bridge) return null;
91
+ const { min, max } = bridge;
92
+ if (!bridgeVersion(min) || !bridgeVersion(max) || min > max) return null;
93
+ return { type: FRAME_MESSAGE.hello, nonce, contract, bridge: { min, max } };
94
+ }
95
+ function negotiateBridge(offered, supported = FRAME_BRIDGE) {
96
+ const high = Math.min(offered.max, supported.max);
97
+ return high >= Math.max(offered.min, supported.min) ? high : null;
98
+ }
99
+ function readFramePortMessage(data) {
100
+ const message = record(data);
101
+ if (!message || message["type"] !== FRAME_MESSAGE.port || !bridgeVersion(message["bridge"])) return null;
102
+ return { type: FRAME_MESSAGE.port, bridge: message["bridge"] };
103
+ }
104
+ function frameThemeMessage(theme) {
105
+ return { type: FRAME_MESSAGE.theme, mode: theme.mode, palette: theme.palette, density: theme.density };
106
+ }
107
+ function readFrameTheme(data) {
108
+ const message = record(data);
109
+ if (!message || message["type"] !== FRAME_MESSAGE.theme) return null;
110
+ const { mode, palette, density } = message;
111
+ if (!isThemePreference(mode) || !isPaletteId(palette) || !isDensityPreference(density)) return null;
112
+ return { mode, palette, density };
113
+ }
114
+ function applyFrameTheme(theme, root) {
115
+ applyTheme(theme.mode, root);
116
+ applyPalette(theme.palette, root);
117
+ applyDensity(theme.density, root);
118
+ }
119
+ function readOpenLink(data) {
120
+ const message = record(data);
121
+ if (!message || message["type"] !== FRAME_MESSAGE.openLink) return null;
122
+ return typeof message["url"] === "string" ? message["url"] : null;
123
+ }
124
+ function isFocusExit(data) {
125
+ return record(data)?.["type"] === FRAME_MESSAGE.focusExit;
126
+ }
127
+ var EXTERNAL_URL_MAX = 2048;
128
+ function externalUrl(raw) {
129
+ if (raw.length === 0 || raw.length > EXTERNAL_URL_MAX) return null;
130
+ let url;
131
+ try {
132
+ url = new URL(raw);
133
+ } catch {
134
+ return null;
135
+ }
136
+ if (url.protocol === "mailto:") return url.pathname.length > 0 ? url : null;
137
+ if (url.protocol !== "https:" && url.protocol !== "http:") return null;
138
+ if (url.username !== "" || url.password !== "" || url.hostname === "") return null;
139
+ return url;
140
+ }
141
+
142
+ export { DEFAULT_FRAME_THEME, EXTERNAL_URL_MAX, FRAME_BRIDGE, FRAME_MESSAGE, FRAME_ORIGIN_MODES, FRAME_SANDBOX_TOKENS, applyFrameTheme, checkFrameSandbox, externalUrl, frameFragment, frameOriginMatches, frameThemeMessage, httpOrigin, isFocusExit, isFrameNonce, negotiateBridge, parseFrameFragment, portTargetOrigin, readFrameHello, readFramePortMessage, readFrameTheme, readOpenLink };