@crewlethq/ui 0.12.0 → 0.14.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 (157) hide show
  1. package/README.md +82 -5
  2. package/dist/AppShell/index.js +2 -1
  3. package/dist/Avatar/index.js +1 -1
  4. package/dist/{Avatar-QHYOFID3.css → Avatar-BPJ36QZR.css} +1 -1
  5. package/dist/AvatarPicker/index.js +5 -5
  6. package/dist/Breadcrumb/Breadcrumb.d.ts +68 -0
  7. package/dist/Breadcrumb/index.d.ts +3 -0
  8. package/dist/Breadcrumb/index.js +3 -0
  9. package/dist/Breadcrumb-YMWVM3SV.css +193 -0
  10. package/dist/ChoiceButtons/ChoiceButtons.d.ts +43 -0
  11. package/dist/ChoiceButtons/index.d.ts +3 -0
  12. package/dist/ChoiceButtons/index.js +3 -0
  13. package/dist/ChoiceButtons-DJ5EOGUA.css +127 -0
  14. package/dist/Combobox/index.js +2 -2
  15. package/dist/CommandPalette/CommandPalette.d.ts +48 -1
  16. package/dist/CommandPalette/index.js +3 -3
  17. package/dist/{CommandPalette-C5AMKGN2.css → CommandPalette-HBTNKHZQ.css} +55 -2
  18. package/dist/DataTable/index.js +4 -4
  19. package/dist/DataView/index.js +11 -11
  20. package/dist/DateTimePicker/index.js +2 -2
  21. package/dist/{DateTimePicker-4AZBTODT.css → DateTimePicker-NJ6XH2TE.css} +4 -3
  22. package/dist/Document/Document.d.ts +56 -0
  23. package/dist/Document/index.d.ts +3 -0
  24. package/dist/Document/index.js +2 -0
  25. package/dist/Document-PQGXJHB7.css +288 -0
  26. package/dist/EntityChip/index.js +1 -1
  27. package/dist/Highlight/Highlight.d.ts +45 -0
  28. package/dist/Highlight/index.d.ts +3 -0
  29. package/dist/Highlight/index.js +2 -0
  30. package/dist/Highlight-DXLV2R6L.css +29 -0
  31. package/dist/HuePicker/index.js +3 -3
  32. package/dist/ImageUpload/index.js +2 -2
  33. package/dist/{ImageUpload-L2GE4JUM.css → ImageUpload-NVGMN7DP.css} +9 -6
  34. package/dist/InboxList/InboxList.d.ts +116 -0
  35. package/dist/InboxList/index.d.ts +3 -0
  36. package/dist/InboxList/index.js +5 -0
  37. package/dist/InboxList-YMFWKGGS.css +252 -0
  38. package/dist/KeyChip/KeyChip.d.ts +21 -0
  39. package/dist/KeyChip/index.d.ts +3 -0
  40. package/dist/KeyChip/index.js +2 -0
  41. package/dist/KeyChip-4XJU4BBE.css +30 -0
  42. package/dist/Link/Link.d.ts +17 -2
  43. package/dist/Link/index.js +1 -1
  44. package/dist/{Link-6BEFNMN2.css → Link-WPMVLKLY.css} +36 -1
  45. package/dist/Listbox/index.d.ts +2 -0
  46. package/dist/Listbox/index.js +1 -1
  47. package/dist/Listbox/useHighlightRest.d.ts +35 -0
  48. package/dist/MarkdownEditor/MarkdownEditor.d.ts +93 -0
  49. package/dist/MarkdownEditor/format.d.ts +34 -0
  50. package/dist/MarkdownEditor/index.d.ts +5 -0
  51. package/dist/MarkdownEditor/index.js +12 -0
  52. package/dist/MarkdownEditor-ZLSHTOIZ.css +146 -0
  53. package/dist/Modal/Modal.d.ts +5 -1
  54. package/dist/Modal/index.js +1 -1
  55. package/dist/{Modal-BNT3TDV2.css → Modal-RQAYGG4L.css} +34 -1
  56. package/dist/OrgLabel/index.js +1 -1
  57. package/dist/OrgNode/index.js +1 -1
  58. package/dist/OrgTable/index.js +4 -4
  59. package/dist/Outline/Outline.d.ts +72 -0
  60. package/dist/Outline/index.d.ts +3 -0
  61. package/dist/Outline/index.js +2 -0
  62. package/dist/Outline-VCXKKU3E.css +78 -0
  63. package/dist/Popover/Popover.d.ts +21 -1
  64. package/dist/Popover/index.js +1 -1
  65. package/dist/ReadingPane/ReadingPane.d.ts +95 -0
  66. package/dist/ReadingPane/index.d.ts +3 -0
  67. package/dist/ReadingPane/index.js +4 -0
  68. package/dist/ReadingPane-5UOLPWY5.css +194 -0
  69. package/dist/SegmentedControl/SegmentedControl.d.ts +9 -1
  70. package/dist/SegmentedControl/index.js +3 -3
  71. package/dist/{SegmentedControl-76HODRBT.css → SegmentedControl-PKBLJVSA.css} +18 -0
  72. package/dist/Select/Select.d.ts +22 -0
  73. package/dist/Select/index.js +2 -2
  74. package/dist/{Select-4ANVLHUY.css → Select-2P7AWUE4.css} +56 -6
  75. package/dist/SidebarNav/index.js +2 -1
  76. package/dist/{SidebarNav-X5NLZNWA.css → SidebarNav-KF66MLOM.css} +4 -19
  77. package/dist/Switcher/Switcher.d.ts +151 -0
  78. package/dist/Switcher/index.d.ts +3 -0
  79. package/dist/Switcher/index.js +16 -0
  80. package/dist/Switcher-B44Q4WF4.css +346 -0
  81. package/dist/Tabs/Tabs.d.ts +7 -1
  82. package/dist/Tabs/index.js +1 -1
  83. package/dist/{Tabs-5I5Q5KKM.css → Tabs-ITJYJFMU.css} +49 -0
  84. package/dist/TagsInput/index.js +2 -2
  85. package/dist/ThemeGallery/ThemeGallery.d.ts +65 -0
  86. package/dist/ThemeGallery/index.d.ts +3 -0
  87. package/dist/ThemeGallery/index.js +6 -0
  88. package/dist/ThemeGallery-NITASULQ.css +93 -0
  89. package/dist/ThemeSwatch/ThemeSwatch.d.ts +71 -0
  90. package/dist/ThemeSwatch/index.d.ts +3 -0
  91. package/dist/ThemeSwatch/index.js +2 -0
  92. package/dist/ThemeSwatch-4DL457ZB.css +256 -0
  93. package/dist/ThemeSwitcher/ThemeSwitcher.d.ts +5 -1
  94. package/dist/ThemeSwitcher/index.js +4 -4
  95. package/dist/ThemeToggle/ThemeToggle.d.ts +1 -1
  96. package/dist/ThemeToggle/index.js +1 -1
  97. package/dist/TimeWindowPicker/index.js +4 -4
  98. package/dist/{TimeWindowPicker-GBF4JDFI.css → TimeWindowPicker-BZ5HHATU.css} +5 -4
  99. package/dist/Toaster/index.js +1 -1
  100. package/dist/{Toaster-NVDCEIP5.css → Toaster-VZKISP2S.css} +81 -87
  101. package/dist/Tree/index.d.ts +1 -1
  102. package/dist/Tree/index.js +1 -1
  103. package/dist/Tree/useTreeState.d.ts +37 -7
  104. package/dist/TreeCanvas/index.js +2 -2
  105. package/dist/TreeGrid/TreeGrid.d.ts +22 -2
  106. package/dist/TreeGrid/index.js +2 -2
  107. package/dist/{TreeGrid-NSW7VMOX.css → TreeGrid-FBX3MSJA.css} +19 -0
  108. package/dist/{chunk-EBBAAQDY.js → chunk-26PEPONN.js} +3 -1
  109. package/dist/{chunk-GOUKOXJK.js → chunk-3VVKJ7AZ.js} +3 -3
  110. package/dist/{chunk-HV6IZFBK.js → chunk-4FFHJBEN.js} +2 -2
  111. package/dist/{chunk-JHQUT4WJ.js → chunk-4LDTQWS7.js} +1 -1
  112. package/dist/{chunk-GXYNEDNA.js → chunk-4Y7WKXAW.js} +1 -1
  113. package/dist/{chunk-IINFVK3W.js → chunk-5IXMLWGW.js} +6 -6
  114. package/dist/{chunk-MCPKHYMC.js → chunk-6LXMLQUM.js} +17 -4
  115. package/dist/{chunk-3UK4VFSK.js → chunk-6NIZCQUM.js} +1 -1
  116. package/dist/{chunk-ORNOOMVY.js → chunk-6TSZCBID.js} +2 -2
  117. package/dist/{chunk-ZMJHK7DR.js → chunk-75WOCYGC.js} +2 -2
  118. package/dist/chunk-77IEHFY4.js +299 -0
  119. package/dist/chunk-A5YKNRG6.js +94 -0
  120. package/dist/{chunk-JCNUQ3US.js → chunk-AXNQCAFC.js} +2 -2
  121. package/dist/{chunk-UG5ZMAXW.js → chunk-CBZSSKH3.js} +5 -5
  122. package/dist/{chunk-LAQK4V3Z.js → chunk-DJMLHCLD.js} +1 -1
  123. package/dist/{chunk-6MU7HNB4.js → chunk-F73MC7EZ.js} +47 -2
  124. package/dist/{chunk-ZPSOF6VL.js → chunk-FBSEJO2C.js} +1 -1
  125. package/dist/chunk-FYTIJM2T.js +40 -0
  126. package/dist/chunk-H6VKCIWV.js +41 -0
  127. package/dist/{chunk-HLBRPES3.js → chunk-IFM63LAG.js} +3 -3
  128. package/dist/chunk-IG75YMUN.js +22 -0
  129. package/dist/{chunk-SEFMO2EV.js → chunk-IXUU5DLZ.js} +55 -6
  130. package/dist/{chunk-ZHOM65MI.js → chunk-JCO3SIST.js} +1 -1
  131. package/dist/chunk-KLIEPFEZ.js +95 -0
  132. package/dist/{chunk-7LD6P32I.js → chunk-L3CTFFLZ.js} +2 -18
  133. package/dist/chunk-MNFU2RNT.js +74 -0
  134. package/dist/{chunk-S3BCVWTU.js → chunk-MY374UUD.js} +49 -31
  135. package/dist/{chunk-K5Q2DK3P.js → chunk-NXDEPUVG.js} +1 -1
  136. package/dist/{chunk-PDUZO35I.js → chunk-O2ZDZZS7.js} +15 -6
  137. package/dist/{chunk-5WEVLIB6.js → chunk-RZ7RGECB.js} +1 -1
  138. package/dist/{chunk-WEIINZPA.js → chunk-SBRE7IML.js} +6 -2
  139. package/dist/{chunk-TEEJSTFG.js → chunk-TFMJH2E6.js} +1 -1
  140. package/dist/{chunk-IVFJD7WY.js → chunk-UCCC7YVZ.js} +3 -2
  141. package/dist/chunk-VWNC2L6W.js +110 -0
  142. package/dist/chunk-VYY56AMX.js +2 -0
  143. package/dist/chunk-WI6K5M5X.js +24 -0
  144. package/dist/{chunk-XV65NTIT.js → chunk-WYQOHFML.js} +94 -41
  145. package/dist/chunk-XYCADWZN.js +100 -0
  146. package/dist/chunk-Y6NR7QJ3.js +336 -0
  147. package/dist/chunk-YSJC7AZC.js +62 -0
  148. package/dist/chunk-ZAJHGDAW.js +93 -0
  149. package/dist/generated/index.d.ts +12 -0
  150. package/dist/generated/index.js +64 -51
  151. package/dist/index.js +64 -51
  152. package/dist/styles.css +2711 -402
  153. package/dist/utils/highlight.d.ts +17 -0
  154. package/dist/utils/useMediaMatch.d.ts +12 -0
  155. package/package.json +3 -3
  156. package/dist/chunk-UAQIUSSK.js +0 -2
  157. /package/dist/{chunk-74OVQWA5.js → chunk-6AR2T5Y4.js} +0 -0
@@ -1,7 +1,7 @@
1
- import { useListbox, useOptionKeys } from './chunk-6MU7HNB4.js';
1
+ import { useListbox, useOptionKeys, useHighlightRest } from './chunk-F73MC7EZ.js';
2
2
  import { useLayerContainer, viewportBounds, outsideBounds, placePopup, LAYER_GAP, LAYER_REPOSITION_EVENT } from './chunk-7RPGY3JE.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
- import './Select-4ANVLHUY.css';
4
+ import './Select-2P7AWUE4.css';
5
5
  import { forwardRef, useId, useMemo, useState, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
6
6
  import { createPortal } from 'react-dom';
7
7
  import { ChevronDownGlyph, CheckGlyph, SearchGlyph } from '@crewlethq/icons/glyphs';
@@ -32,6 +32,7 @@ var Select = forwardRef(function Select2({
32
32
  renderOption,
33
33
  renderMultiLabel,
34
34
  emptyMessage = "No matches",
35
+ onHighlight,
35
36
  ...native
36
37
  }, ref) {
37
38
  const generatedId = useId();
@@ -89,6 +90,7 @@ var Select = forwardRef(function Select2({
89
90
  renderOption,
90
91
  renderMultiLabel,
91
92
  emptyMessage,
93
+ onHighlight,
92
94
  describedBy: native["aria-describedby"],
93
95
  required: native.required,
94
96
  ariaRequired: native["aria-required"],
@@ -166,6 +168,7 @@ var ListboxSelect = forwardRef(function ListboxSelect2({
166
168
  renderOption,
167
169
  renderMultiLabel,
168
170
  emptyMessage,
171
+ onHighlight,
169
172
  describedBy,
170
173
  required,
171
174
  ariaRequired,
@@ -205,6 +208,7 @@ var ListboxSelect = forwardRef(function ListboxSelect2({
205
208
  return out;
206
209
  }, [offered]);
207
210
  const flat = useMemo(() => groups.flatMap((group) => group.items), [groups]);
211
+ const tickAtEnd = useMemo(() => flat.some((option) => option.leading !== void 0), [flat]);
208
212
  const close = useCallback((returnFocus) => {
209
213
  setOpen(false);
210
214
  setQuery("");
@@ -274,6 +278,21 @@ var ListboxSelect = forwardRef(function ListboxSelect2({
274
278
  if (spot.top < bounds.top) box.scrollTop -= bounds.top - spot.top;
275
279
  else if (spot.bottom > bounds.bottom) box.scrollTop += spot.bottom - bounds.bottom;
276
280
  }, [listbox.active, flat.length, open]);
281
+ const offeredNow = useRef(flat);
282
+ offeredNow.current = flat;
283
+ const highlight = useHighlightRest(
284
+ onHighlight ? (key) => onHighlight(key === null ? null : offeredNow.current.find((option) => String(option.value) === key) ?? null) : void 0
285
+ );
286
+ const byPointer = useRef(false);
287
+ const resting = open ? flat[listbox.active] : void 0;
288
+ const restingKey = resting ? String(resting.value) : null;
289
+ useEffect(() => {
290
+ const pointer = byPointer.current;
291
+ byPointer.current = false;
292
+ if (restingKey === null) highlight.now(null);
293
+ else if (pointer) highlight.rest(restingKey);
294
+ else highlight.now(restingKey);
295
+ }, [restingKey, highlight]);
277
296
  const position = useCallback(() => {
278
297
  const at = trigger.current;
279
298
  const box = panel.current;
@@ -362,6 +381,18 @@ var ListboxSelect = forwardRef(function ListboxSelect2({
362
381
  const activeId = open && listbox.active >= 0 ? listbox.optionId(listbox.active) : void 0;
363
382
  const comboOnTrigger = !searchable;
364
383
  const requiredForAria = ariaRequired ?? required;
384
+ const optionHandlers = (at) => {
385
+ const handlers = listbox.optionHandlers(at);
386
+ return {
387
+ ...handlers,
388
+ onMouseEnter: () => {
389
+ const option = flat[at];
390
+ if (at === listbox.active && option) highlight.rest(String(option.value));
391
+ else byPointer.current = true;
392
+ handlers.onMouseEnter();
393
+ }
394
+ };
395
+ };
365
396
  let index = -1;
366
397
  const rows = groups.map((group, position2) => {
367
398
  const items = group.items.map((option) => {
@@ -369,6 +400,14 @@ var ListboxSelect = forwardRef(function ListboxSelect2({
369
400
  const at = index;
370
401
  const selected = isChosen(option.value);
371
402
  const highlighted = at === listbox.active;
403
+ const tick = /* @__PURE__ */ jsx(
404
+ "span",
405
+ {
406
+ className: cx("crewlet-select__option-tick", tickAtEnd && "crewlet-select__option-tick--end"),
407
+ "aria-hidden": "true",
408
+ children: selected ? /* @__PURE__ */ jsx(CheckGlyph, { size: "sm" }) : null
409
+ }
410
+ );
372
411
  return /* @__PURE__ */ jsx(
373
412
  "div",
374
413
  {
@@ -384,13 +423,19 @@ var ListboxSelect = forwardRef(function ListboxSelect2({
384
423
  highlighted && "is-highlight",
385
424
  option.disabled && "is-disabled"
386
425
  ),
387
- ...option.disabled ? {} : listbox.optionHandlers(at),
426
+ ...option.disabled ? {} : optionHandlers(at),
388
427
  children: renderOption ? renderOption(option, { active: highlighted, selected }) : /* @__PURE__ */ jsxs(Fragment, { children: [
389
- /* @__PURE__ */ jsx("span", { className: "crewlet-select__option-tick", "aria-hidden": "true", children: selected ? /* @__PURE__ */ jsx(CheckGlyph, { size: "sm" }) : null }),
428
+ tickAtEnd ? null : tick,
429
+ option.leading !== void 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-select__option-leading", "aria-hidden": "true", children: option.leading }) : null,
390
430
  /* @__PURE__ */ jsxs("span", { className: "crewlet-select__option-body", children: [
391
431
  /* @__PURE__ */ jsx("span", { className: "crewlet-select__option-label", children: option.label }),
392
432
  option.description ? /* @__PURE__ */ jsx("span", { className: "crewlet-select__option-desc", children: option.description }) : null
393
- ] })
433
+ ] }),
434
+ option.hint !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
435
+ " ",
436
+ /* @__PURE__ */ jsx("span", { className: "crewlet-listbox__hint", children: option.hint })
437
+ ] }) : null,
438
+ tickAtEnd ? tick : null
394
439
  ] })
395
440
  },
396
441
  String(option.value)
@@ -423,6 +468,7 @@ var ListboxSelect = forwardRef(function ListboxSelect2({
423
468
  ),
424
469
  role: "presentation",
425
470
  onPointerDown: (event) => event.stopPropagation(),
471
+ onMouseLeave: () => highlight.now(null),
426
472
  children: [
427
473
  searchable ? /* @__PURE__ */ jsxs("div", { className: "crewlet-select__search", children: [
428
474
  /* @__PURE__ */ jsx(SearchGlyph, { size: "sm" }),
@@ -504,7 +550,10 @@ var ListboxSelect = forwardRef(function ListboxSelect2({
504
550
  onClick: () => open ? close(false) : setOpen(true),
505
551
  onKeyDown,
506
552
  children: [
507
- renderTrigger ? renderTrigger(single, { open }) : /* @__PURE__ */ jsx("span", { className: cx("crewlet-select__label", !hasValue && "is-placeholder"), children: label }),
553
+ renderTrigger ? renderTrigger(single, { open }) : /* @__PURE__ */ jsxs(Fragment, { children: [
554
+ !multiple && single?.leading !== void 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-select__leading", "aria-hidden": "true", children: single.leading }) : null,
555
+ /* @__PURE__ */ jsx("span", { className: cx("crewlet-select__label", !hasValue && "is-placeholder"), children: label })
556
+ ] }),
508
557
  /* @__PURE__ */ jsx(ChevronDownGlyph, { className: "crewlet-select__chevron", size: "sm" })
509
558
  ]
510
559
  }
@@ -1,5 +1,5 @@
1
1
  import { Tag } from './chunk-LOECRMHA.js';
2
- import { useListbox } from './chunk-6MU7HNB4.js';
2
+ import { useListbox } from './chunk-F73MC7EZ.js';
3
3
  import { announce } from './chunk-RSAQOKH3.js';
4
4
  import { IconButton } from './chunk-4FQT6ZAY.js';
5
5
  import { isComposing } from './chunk-R25RGPUB.js';
@@ -0,0 +1,95 @@
1
+ import { ThemeSwatch } from './chunk-MNFU2RNT.js';
2
+ import { useHighlightRest } from './chunk-F73MC7EZ.js';
3
+ import { useRoving, tabStop } from './chunk-CBOICT64.js';
4
+ import { cx } from './chunk-ODEQTPFM.js';
5
+ import './ThemeGallery-NITASULQ.css';
6
+ import { useId } from 'react';
7
+ import { themeGroups, themeRegistry } from '@crewlethq/tokens';
8
+ import { jsx, jsxs } from 'react/jsx-runtime';
9
+
10
+ function ThemeGallery({
11
+ label = "Theme",
12
+ value,
13
+ onValueChange,
14
+ onPreview,
15
+ themes = themeRegistry,
16
+ groups = themeGroups,
17
+ columns = 3,
18
+ disabled = false,
19
+ className,
20
+ style,
21
+ ...rest
22
+ }) {
23
+ const id = useId();
24
+ const sections = [
25
+ ...groups.map((group) => ({ group, items: themes.filter((theme) => theme.group === group.id) })),
26
+ { group: null, items: themes.filter((theme) => !groups.some((group) => group.id === theme.group)) }
27
+ ].filter((section) => section.items.length > 0);
28
+ const flat = sections.flatMap((section) => section.items);
29
+ const { buttons, onKeyDown, onFocusAt } = useRoving(
30
+ flat.length,
31
+ disabled ? null : (index2) => onValueChange(flat[index2].id)
32
+ );
33
+ const stop = tabStop(
34
+ flat.map(() => ({ disabled })),
35
+ flat.findIndex((theme) => theme.id === value)
36
+ );
37
+ const preview = useHighlightRest(onPreview ? (key) => onPreview(key) : void 0);
38
+ let index = -1;
39
+ return /* @__PURE__ */ jsx(
40
+ "div",
41
+ {
42
+ ...rest,
43
+ className: cx("crewlet-theme-gallery", className),
44
+ style: { ...style, "--crewlet-theme-gallery-columns": columns },
45
+ onMouseLeave: () => preview.now(null),
46
+ children: /* @__PURE__ */ jsx(
47
+ "div",
48
+ {
49
+ className: "crewlet-theme-gallery__groups",
50
+ role: "radiogroup",
51
+ "aria-label": label,
52
+ "aria-disabled": disabled || void 0,
53
+ children: sections.map((section, position) => {
54
+ const headingId = `${id}-group-${position}`;
55
+ return /* @__PURE__ */ jsxs("div", { className: "crewlet-theme-gallery__group", children: [
56
+ section.group ? /* @__PURE__ */ jsx("span", { id: headingId, className: "crewlet-theme-gallery__heading", children: section.group.name }) : null,
57
+ /* @__PURE__ */ jsx("div", { className: "crewlet-theme-gallery__cards", children: section.items.map((theme) => {
58
+ index += 1;
59
+ const at = index;
60
+ const chosen = theme.id === value;
61
+ return /* @__PURE__ */ jsxs(
62
+ "button",
63
+ {
64
+ ref: (el) => {
65
+ buttons.current[at] = el;
66
+ },
67
+ type: "button",
68
+ role: "radio",
69
+ "aria-checked": chosen,
70
+ "aria-label": `${theme.name}, ${theme.tag}`,
71
+ "aria-describedby": section.group ? headingId : void 0,
72
+ tabIndex: at === stop ? 0 : -1,
73
+ disabled,
74
+ className: cx("crewlet-theme-gallery__card", chosen && "is-chosen"),
75
+ onClick: () => onValueChange(theme.id),
76
+ onFocus: () => onFocusAt(at),
77
+ onKeyDown: (event) => onKeyDown(event, true),
78
+ onMouseEnter: () => preview.rest(theme.id),
79
+ children: [
80
+ /* @__PURE__ */ jsx(ThemeSwatch, { swatch: theme.swatch, size: "card" }),
81
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-gallery__name", children: theme.name })
82
+ ]
83
+ },
84
+ theme.id
85
+ );
86
+ }) })
87
+ ] }, section.group?.id ?? "-");
88
+ })
89
+ }
90
+ )
91
+ }
92
+ );
93
+ }
94
+
95
+ export { ThemeGallery };
@@ -1,8 +1,9 @@
1
+ import { useMediaMatch } from './chunk-IG75YMUN.js';
1
2
  import { IconButton } from './chunk-4FQT6ZAY.js';
2
3
  import { useModalLayer, focusables } from './chunk-R25RGPUB.js';
3
4
  import { cx } from './chunk-ODEQTPFM.js';
4
5
  import './AppShell-CEVLW4QG.css';
5
- import { createContext, useContext, useId, useState, useRef, useCallback, useEffect, useLayoutEffect, useSyncExternalStore } from 'react';
6
+ import { createContext, useContext, useId, useState, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
6
7
  import { breakpoint } from '@crewlethq/tokens';
7
8
  import { MenuGlyph } from '@crewlethq/icons/glyphs';
8
9
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -14,23 +15,6 @@ function useAppShell() {
14
15
  if (!shell) throw new Error("useAppShell must be called inside an <AppShell>.");
15
16
  return shell;
16
17
  }
17
- function useMediaMatch(query) {
18
- const subscribe = useCallback(
19
- (notify) => {
20
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => {
21
- };
22
- const list = window.matchMedia(query);
23
- list.addEventListener("change", notify);
24
- return () => list.removeEventListener("change", notify);
25
- },
26
- [query]
27
- );
28
- const read = useCallback(
29
- () => typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(query).matches : false,
30
- [query]
31
- );
32
- return useSyncExternalStore(subscribe, read, () => false);
33
- }
34
18
  function AppShell({
35
19
  sidebar,
36
20
  topbar,
@@ -0,0 +1,74 @@
1
+ import { cx } from './chunk-ODEQTPFM.js';
2
+ import './ThemeSwatch-4DL457ZB.css';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+
5
+ var PROPERTY = {
6
+ frame: "--crewlet-swatch-frame",
7
+ sheet: "--crewlet-swatch-sheet",
8
+ card: "--crewlet-swatch-card",
9
+ raised: "--crewlet-swatch-raised",
10
+ hairline: "--crewlet-swatch-hairline",
11
+ text: "--crewlet-swatch-text",
12
+ muted: "--crewlet-swatch-muted",
13
+ accent: "--crewlet-swatch-accent",
14
+ onAccent: "--crewlet-swatch-on-accent",
15
+ info: "--crewlet-swatch-info",
16
+ warning: "--crewlet-swatch-warning",
17
+ danger: "--crewlet-swatch-danger",
18
+ success: "--crewlet-swatch-success"
19
+ };
20
+ var STATES = ["info", "warning", "danger", "success"];
21
+ function paint(colours) {
22
+ const style = {};
23
+ for (const key of Object.keys(PROPERTY)) style[PROPERTY[key]] = colours[key];
24
+ return style;
25
+ }
26
+ function Half({ colours, size }) {
27
+ if (size === "chip") {
28
+ return /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__half", style: paint(colours), children: /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__dot" }) });
29
+ }
30
+ const pane = size === "pane";
31
+ return /* @__PURE__ */ jsxs("span", { className: "crewlet-theme-swatch__half", style: paint(colours), children: [
32
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-theme-swatch__rail", children: [
33
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__rail-row is-current" }),
34
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__rail-row" }),
35
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__rail-row" }),
36
+ pane ? /* @__PURE__ */ jsxs(Fragment, { children: [
37
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__rail-row" }),
38
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__rail-row" })
39
+ ] }) : null
40
+ ] }),
41
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-theme-swatch__sheet", children: [
42
+ pane ? /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__title" }) : null,
43
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__card", children: pane ? STATES.map((state) => /* @__PURE__ */ jsxs("span", { className: "crewlet-theme-swatch__item", children: [
44
+ /* @__PURE__ */ jsx("span", { className: `crewlet-theme-swatch__state crewlet-theme-swatch__state--${state}` }),
45
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__line" }),
46
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__muted" })
47
+ ] }, state)) : /* @__PURE__ */ jsxs(Fragment, { children: [
48
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__line" }),
49
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-theme-swatch__row", children: [
50
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__action" }),
51
+ STATES.map((state) => /* @__PURE__ */ jsx("span", { className: `crewlet-theme-swatch__state crewlet-theme-swatch__state--${state}` }, state))
52
+ ] })
53
+ ] }) }),
54
+ pane ? /* @__PURE__ */ jsxs("span", { className: "crewlet-theme-swatch__actions", children: [
55
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__action", children: /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__label" }) }),
56
+ /* @__PURE__ */ jsx("span", { className: "crewlet-theme-swatch__secondary" })
57
+ ] }) : null
58
+ ] })
59
+ ] });
60
+ }
61
+ function ThemeSwatch({ swatch, size = "card", mode, label, className, ...rest }) {
62
+ const halves = mode ? [swatch[mode]] : [swatch.dark, swatch.light];
63
+ return /* @__PURE__ */ jsx(
64
+ "span",
65
+ {
66
+ ...rest,
67
+ className: cx("crewlet-theme-swatch", `crewlet-theme-swatch--${size}`, className),
68
+ ...label ? { role: "img", "aria-label": label } : { "aria-hidden": true },
69
+ children: halves.map((colours, index) => /* @__PURE__ */ jsx(Half, { colours, size }, index))
70
+ }
71
+ );
72
+ }
73
+
74
+ export { ThemeSwatch };
@@ -1,4 +1,4 @@
1
- import { useMemo, useState, useCallback, useRef, useEffect, useLayoutEffect } from 'react';
1
+ import { useMemo, useState, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
2
2
  import { flushSync } from 'react-dom';
3
3
 
4
4
  // src/Tree/model.ts
@@ -506,14 +506,18 @@ function layoutConnectors(layout, shape = "elbow") {
506
506
  }
507
507
  return out;
508
508
  }
509
- function useTreeState(forest, selected) {
509
+ function useTreeState(forest, selected, { defaultExpanded = "all", expanded: held, onExpandedChange } = {}) {
510
510
  const model = useMemo(() => createTreeModel(forest), [forest]);
511
- const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
511
+ const [toggled, setToggled] = useState(() => /* @__PURE__ */ new Set());
512
512
  const expanded = useMemo(() => {
513
- const out = treeAllExpandable(model);
514
- for (const id of collapsed) out.delete(id);
515
- return out;
516
- }, [model, collapsed]);
513
+ const expandable = treeAllExpandable(model);
514
+ if (held) return new Set([...held].filter((id) => expandable.has(id)));
515
+ if (defaultExpanded === "all") {
516
+ for (const id of toggled) expandable.delete(id);
517
+ return expandable;
518
+ }
519
+ return new Set([...toggled].filter((id) => expandable.has(id)));
520
+ }, [model, toggled, held, defaultExpanded]);
517
521
  const rows = useMemo(() => treeVisible(model, expanded), [model, expanded]);
518
522
  const [wanted, setActive] = useState(null);
519
523
  const active = useMemo(() => {
@@ -525,34 +529,48 @@ function useTreeState(forest, selected) {
525
529
  }
526
530
  return treeFirst(model);
527
531
  }, [wanted, selected, model, rows]);
528
- const toggle = useCallback((id) => {
529
- setCollapsed((was) => {
530
- const next = new Set(was);
531
- if (next.has(id)) next.delete(id);
532
- else next.add(id);
533
- return next;
532
+ const current = useRef({ model, expanded, held, defaultExpanded, onExpandedChange });
533
+ current.current = { model, expanded, held, defaultExpanded, onExpandedChange };
534
+ const commit = useCallback((next) => {
535
+ const now = current.current;
536
+ now.onExpandedChange?.(next);
537
+ if (now.held) return;
538
+ const all = treeAllExpandable(now.model);
539
+ setToggled((was) => {
540
+ const kept = new Set(was);
541
+ for (const id of all) {
542
+ const differs = now.defaultExpanded === "all" ? !next.has(id) : next.has(id);
543
+ if (differs) kept.add(id);
544
+ else kept.delete(id);
545
+ }
546
+ return kept;
534
547
  });
535
548
  }, []);
536
- const current = useRef({ model, collapsed });
537
- current.current = { model, collapsed };
538
- const expandAll = useCallback(() => setCollapsed(/* @__PURE__ */ new Set()), []);
549
+ const toggle = useCallback(
550
+ (id) => {
551
+ const next = new Set(current.current.expanded);
552
+ if (next.has(id)) next.delete(id);
553
+ else next.add(id);
554
+ commit(next);
555
+ },
556
+ [commit]
557
+ );
558
+ const expandAll = useCallback(() => commit(treeAllExpandable(current.current.model)), [commit]);
539
559
  const collapseAll = useCallback(() => {
540
560
  const { model: shown } = current.current;
541
- const next = treeAllExpandable(shown);
542
- shown.roots.forEach((root) => next.delete(root));
543
- setCollapsed(next);
544
- }, []);
545
- const open2 = useCallback((id) => {
546
- const { model: shown, collapsed: closed } = current.current;
547
- const hidden = treeAncestors(shown, id).filter((ancestor) => closed.has(ancestor));
548
- if (hidden.length === 0) return false;
549
- setCollapsed((was) => {
550
- const next = new Set(was);
551
- hidden.forEach((ancestor) => next.delete(ancestor));
552
- return next;
553
- });
554
- return true;
555
- }, []);
561
+ const tops = new Set(shown.roots.filter((root) => treeAllExpandable(shown).has(root)));
562
+ commit(tops);
563
+ }, [commit]);
564
+ const open2 = useCallback(
565
+ (id) => {
566
+ const { model: shown, expanded: now } = current.current;
567
+ const hidden = treeAncestors(shown, id).filter((ancestor) => !now.has(ancestor));
568
+ if (hidden.length === 0) return false;
569
+ commit(/* @__PURE__ */ new Set([...now, ...hidden]));
570
+ return true;
571
+ },
572
+ [commit]
573
+ );
556
574
  const buffer = useRef({ text: "", at: 0 });
557
575
  const typed = (event, from, now) => {
558
576
  if (!isTypeAheadKey(event, buffer.current, now)) return void 0;
@@ -1,5 +1,5 @@
1
1
  import { NODE_HUES, NODE_HUE_NAMES } from './chunk-AYKXMIOR.js';
2
- import { SegmentedControl } from './chunk-EBBAAQDY.js';
2
+ import { SegmentedControl } from './chunk-26PEPONN.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
4
  import { jsx } from 'react/jsx-runtime';
5
5
 
@@ -12,6 +12,9 @@ function Popover({
12
12
  align = "start",
13
13
  side = "bottom",
14
14
  width = "auto",
15
+ alignOffset,
16
+ sideOffset = LAYER_GAP,
17
+ initialFocus,
15
18
  role = "dialog",
16
19
  label,
17
20
  className,
@@ -29,8 +32,12 @@ function Popover({
29
32
  const container = useLayerContainer();
30
33
  const isControlled = useRef(controlled !== void 0);
31
34
  const report = useRef(onOpenChange);
35
+ const shift = useRef(alignOffset);
36
+ const start = useRef(initialFocus);
32
37
  isControlled.current = controlled !== void 0;
33
38
  report.current = onOpenChange;
39
+ shift.current = alignOffset;
40
+ start.current = initialFocus;
34
41
  const setOpen = useCallback((next, reason) => {
35
42
  if (!isControlled.current) setUncontrolled(next);
36
43
  report.current?.(next, reason);
@@ -52,8 +59,10 @@ function Popover({
52
59
  const rect = at.getBoundingClientRect();
53
60
  box.style.width = width === "match" ? `${rect.width}px` : "";
54
61
  const own = box.getBoundingClientRect();
62
+ const asked = shift.current;
63
+ const offset = typeof asked === "function" ? asked(box, at) : asked ?? 0;
55
64
  const local = {
56
- x: (align === "end" ? rect.right - own.width : rect.left) - (inHost ? host.x : 0),
65
+ x: (align === "end" ? rect.right - own.width : rect.left) + offset - (inHost ? host.x : 0),
57
66
  y: rect.top - (inHost ? host.y : 0),
58
67
  width: rect.width,
59
68
  height: rect.height
@@ -69,18 +78,18 @@ function Popover({
69
78
  close("anchor-gone");
70
79
  return;
71
80
  }
72
- const asked = width === "match" ? rect.width : void 0;
81
+ const matched = width === "match" ? rect.width : void 0;
73
82
  const spot = placePopup(
74
83
  local,
75
84
  { width: own.width, height: own.height },
76
85
  bounds,
77
- LAYER_GAP,
86
+ sideOffset,
78
87
  side === "top" ? "above" : "below"
79
88
  );
80
89
  setPlace(
81
- (was) => was && was.left === spot.left && was.top === spot.top && was.side === spot.side && was.width === asked ? was : { left: spot.left, top: spot.top, side: spot.side, width: asked }
90
+ (was) => was && was.left === spot.left && was.top === spot.top && was.side === spot.side && was.width === matched ? was : { left: spot.left, top: spot.top, side: spot.side, width: matched }
82
91
  );
83
- }, [align, close, container, side, width]);
92
+ }, [align, close, container, side, sideOffset, width]);
84
93
  useLayoutEffect(() => {
85
94
  if (!open) return;
86
95
  position();
@@ -104,7 +113,7 @@ function Popover({
104
113
  useEffect(() => {
105
114
  if (!placed || role !== "dialog") return;
106
115
  const box = panel.current;
107
- if (box && !box.contains(document.activeElement)) (focusables(box)[0] ?? box).focus();
116
+ if (box && !box.contains(document.activeElement)) (start.current?.() ?? focusables(box)[0] ?? box).focus();
108
117
  }, [placed, role]);
109
118
  const toggle = useCallback(() => {
110
119
  if (open) close("trigger");
@@ -1,4 +1,4 @@
1
- import { useTreeState, useMeasuredSizes, layoutForest, treeAncestors, useLayoutAnchor, treeExpandable, layoutConnectors, treeItemAction, treeStep, treePosInSet, treeSetSize, treeLevel, placeGroups } from './chunk-S3BCVWTU.js';
1
+ import { useTreeState, useMeasuredSizes, layoutForest, treeAncestors, useLayoutAnchor, treeExpandable, layoutConnectors, treeItemAction, treeStep, treePosInSet, treeSetSize, treeLevel, placeGroups } from './chunk-MY374UUD.js';
2
2
  import { CANVAS_COMPOSE_CONTEXT, CANVAS_FIT_MAX_ZOOM, Canvas } from './chunk-2PZBIHRV.js';
3
3
  import { focusables } from './chunk-R25RGPUB.js';
4
4
  import { cx } from './chunk-ODEQTPFM.js';
@@ -1,5 +1,5 @@
1
1
  import { useThemePreference, useDensityPreference } from './chunk-K3OWD7BJ.js';
2
- import { SegmentedControl } from './chunk-EBBAAQDY.js';
2
+ import { SegmentedControl } from './chunk-26PEPONN.js';
3
3
  import { SunGlyph, MonitorGlyph, MoonGlyph } from '@crewlethq/icons/glyphs';
4
4
  import { jsx } from 'react/jsx-runtime';
5
5
 
@@ -32,11 +32,12 @@ function StoredThemeSwitcher({
32
32
  function ThemeRow({
33
33
  value,
34
34
  onChange,
35
- label = "Theme",
35
+ label = "Mode",
36
36
  lightLabel = "Light",
37
37
  systemLabel = "Follow the system",
38
38
  darkLabel = "Dark",
39
39
  size = "sm",
40
+ fill,
40
41
  className
41
42
  }) {
42
43
  return /* @__PURE__ */ jsx(
@@ -45,6 +46,7 @@ function ThemeRow({
45
46
  label,
46
47
  semantics: "radio",
47
48
  size,
49
+ fill,
48
50
  className,
49
51
  value,
50
52
  onValueChange: onChange,
@@ -93,6 +95,7 @@ function DensityRow({
93
95
  normalMark = "M",
94
96
  comfortableMark = "L",
95
97
  size = "sm",
98
+ fill,
96
99
  className
97
100
  }) {
98
101
  return /* @__PURE__ */ jsx(
@@ -101,6 +104,7 @@ function DensityRow({
101
104
  label,
102
105
  semantics: "radio",
103
106
  size,
107
+ fill,
104
108
  className,
105
109
  value,
106
110
  onValueChange: onChange,
@@ -4,7 +4,7 @@ import { IconButton } from './chunk-4FQT6ZAY.js';
4
4
  import { useLayerContainer } from './chunk-7RPGY3JE.js';
5
5
  import { focusables, useModalLayer, useBodyScrollLock } from './chunk-R25RGPUB.js';
6
6
  import { cx } from './chunk-ODEQTPFM.js';
7
- import './Modal-BNT3TDV2.css';
7
+ import './Modal-RQAYGG4L.css';
8
8
  import { useId, useRef, useCallback } from 'react';
9
9
  import { createPortal } from 'react-dom';
10
10
  import { XGlyph } from '@crewlethq/icons/glyphs';
@@ -1,6 +1,7 @@
1
+ import { KeyChip } from './chunk-FYTIJM2T.js';
1
2
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
2
3
  import { cx } from './chunk-ODEQTPFM.js';
3
- import './SidebarNav-X5NLZNWA.css';
4
+ import './SidebarNav-KF66MLOM.css';
4
5
  import { useId, useState } from 'react';
5
6
  import { ChevronDownGlyph } from '@crewlethq/icons/glyphs';
6
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -66,7 +67,7 @@ function NavItem({
66
67
  }
67
68
  const inside = /* @__PURE__ */ jsxs(Fragment, { children: [
68
69
  icon ? /* @__PURE__ */ jsx("span", { className: "crewlet-nav-item__icon", "aria-hidden": true, children: icon }) : null,
69
- lead ? /* @__PURE__ */ jsx("span", { className: "crewlet-nav-item__lead", children: lead }) : null,
70
+ lead ? /* @__PURE__ */ jsx(KeyChip, { className: "crewlet-nav-item__lead", children: lead }) : null,
70
71
  lead ? " " : null,
71
72
  /* @__PURE__ */ jsxs("span", { className: "crewlet-nav-item__label", children: [
72
73
  label,