@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
@@ -0,0 +1,299 @@
1
+ import { Toolbar } from './chunk-PGRPNDWM.js';
2
+ import { IconButton } from './chunk-4FQT6ZAY.js';
3
+ import { SegmentedControl } from './chunk-26PEPONN.js';
4
+ import { tabId } from './chunk-4LDTQWS7.js';
5
+ import { cx } from './chunk-ODEQTPFM.js';
6
+ import './MarkdownEditor-ZLSHTOIZ.css';
7
+ import { useState, useId, useRef, useLayoutEffect, useImperativeHandle } from 'react';
8
+ import { TableGlyph, ListChecksGlyph, ListOrderedGlyph, ListGlyph, CodeGlyph, ItalicGlyph, BoldGlyph, Heading3Glyph, Heading2Glyph } from '@crewlethq/icons/glyphs';
9
+ import { jsxs, jsx } from 'react/jsx-runtime';
10
+
11
+ // src/MarkdownEditor/format.ts
12
+ var MARKDOWN_FORMATS = [
13
+ "heading2",
14
+ "heading3",
15
+ "bold",
16
+ "italic",
17
+ "code",
18
+ "bulletList",
19
+ "numberedList",
20
+ "checklist",
21
+ "table"
22
+ ];
23
+ var WRAP = { bold: "**", italic: "_", code: "`" };
24
+ var LINE_MARKER = /^(#{1,6} |- \[[ xX]\] |[-*+] |\d+[.)] )/;
25
+ function insertMarkdown(value, { start, end }, text) {
26
+ const next = value.slice(0, start) + text + value.slice(end);
27
+ const caret = start + text.length;
28
+ return { value: next, selection: { start: caret, end: caret } };
29
+ }
30
+ function formatMarkdown(value, selection, format) {
31
+ switch (format) {
32
+ case "bold":
33
+ case "italic":
34
+ case "code":
35
+ return wrap(value, selection, WRAP[format]);
36
+ case "heading2":
37
+ return prefixLines(value, selection, () => "## ", (marker) => marker === "## ");
38
+ case "heading3":
39
+ return prefixLines(value, selection, () => "### ", (marker) => marker === "### ");
40
+ case "bulletList":
41
+ return prefixLines(value, selection, () => "- ", (marker) => /^[-*+] $/.test(marker));
42
+ case "numberedList":
43
+ return prefixLines(value, selection, (index) => `${index + 1}. `, (marker) => /^\d+[.)] $/.test(marker));
44
+ case "checklist":
45
+ return prefixLines(value, selection, () => "- [ ] ", (marker) => /^- \[[ xX]\] $/.test(marker));
46
+ case "table":
47
+ return table(value, selection);
48
+ }
49
+ }
50
+ function wrap(value, selection, marker) {
51
+ let { start, end } = selection;
52
+ while (start < end && /\s/.test(value[start] ?? "")) start += 1;
53
+ while (end > start && /\s/.test(value[end - 1] ?? "")) end -= 1;
54
+ const size = marker.length;
55
+ if (value.slice(start - size, start) === marker && value.slice(end, end + size) === marker) {
56
+ return {
57
+ value: value.slice(0, start - size) + value.slice(start, end) + value.slice(end + size),
58
+ selection: { start: start - size, end: end - size }
59
+ };
60
+ }
61
+ const selected = value.slice(start, end);
62
+ if (selected.length >= size * 2 && selected.startsWith(marker) && selected.endsWith(marker)) {
63
+ const inner = selected.slice(size, selected.length - size);
64
+ return { value: value.slice(0, start) + inner + value.slice(end), selection: { start, end: start + inner.length } };
65
+ }
66
+ return {
67
+ value: value.slice(0, start) + marker + selected + marker + value.slice(end),
68
+ selection: { start: start + size, end: end + size }
69
+ };
70
+ }
71
+ function lineSpan(value, { start, end }) {
72
+ const from = value.lastIndexOf("\n", start - 1) + 1;
73
+ const last = end > start && value[end - 1] === "\n" ? end - 1 : end;
74
+ const newline = value.indexOf("\n", last);
75
+ return [from, newline === -1 ? value.length : newline];
76
+ }
77
+ function prefixLines(value, selection, marker, has) {
78
+ const [from, to] = lineSpan(value, selection);
79
+ const lines = value.slice(from, to).split("\n");
80
+ const single = lines.length === 1;
81
+ const written = lines.filter((line) => single || line.trim() !== "");
82
+ const off = written.length > 0 && written.every((line) => has(LINE_MARKER.exec(line)?.[0] ?? ""));
83
+ let index = 0;
84
+ let caretShift = 0;
85
+ const caretLine = value.slice(from, selection.start).split("\n").length - 1;
86
+ const next = lines.map((line, at) => {
87
+ if (!single && line.trim() === "") return line;
88
+ const old = LINE_MARKER.exec(line)?.[0] ?? "";
89
+ const body = line.slice(old.length);
90
+ const added = off ? "" : marker(index);
91
+ index += 1;
92
+ if (at === caretLine) caretShift = added.length - old.length;
93
+ return added + body;
94
+ });
95
+ const block = next.join("\n");
96
+ const result = value.slice(0, from) + block + value.slice(to);
97
+ if (selection.start === selection.end) {
98
+ const caret = Math.max(from, selection.start + caretShift);
99
+ return { value: result, selection: { start: caret, end: caret } };
100
+ }
101
+ return { value: result, selection: { start: from, end: from + block.length } };
102
+ }
103
+ var TABLE = "| Column | Column |\n| --- | --- |\n| | |";
104
+ function table(value, { start, end }) {
105
+ const before = value.slice(0, start);
106
+ const after = value.slice(end);
107
+ const lead = before === "" ? "" : before.endsWith("\n\n") ? "" : before.endsWith("\n") ? "\n" : "\n\n";
108
+ const trail = after === "" ? "" : after.startsWith("\n\n") ? "" : after.startsWith("\n") ? "\n" : "\n\n";
109
+ const at = before.length + lead.length + 2;
110
+ return {
111
+ value: before + lead + TABLE + trail + after,
112
+ selection: { start: at, end: at + "Column".length }
113
+ };
114
+ }
115
+ function markdownWords(value) {
116
+ return (value.match(/[\p{L}\p{N}][\p{L}\p{N}'’-]*/gu) ?? []).length;
117
+ }
118
+ var LABELS = {
119
+ toolbar: "Formatting",
120
+ view: "Editor view",
121
+ write: "Write",
122
+ preview: "Preview",
123
+ empty: "Nothing to preview yet.",
124
+ formats: {
125
+ heading2: "Heading",
126
+ heading3: "Subheading",
127
+ bold: "Bold",
128
+ italic: "Italic",
129
+ code: "Code",
130
+ bulletList: "Bulleted list",
131
+ numberedList: "Numbered list",
132
+ checklist: "Checklist",
133
+ table: "Table"
134
+ }
135
+ };
136
+ var GROUPS = [
137
+ ["heading2", "heading3"],
138
+ ["bold", "italic", "code"],
139
+ ["bulletList", "numberedList", "checklist", "table"]
140
+ ];
141
+ var GLYPHS = {
142
+ heading2: /* @__PURE__ */ jsx(Heading2Glyph, {}),
143
+ heading3: /* @__PURE__ */ jsx(Heading3Glyph, {}),
144
+ bold: /* @__PURE__ */ jsx(BoldGlyph, {}),
145
+ italic: /* @__PURE__ */ jsx(ItalicGlyph, {}),
146
+ code: /* @__PURE__ */ jsx(CodeGlyph, {}),
147
+ bulletList: /* @__PURE__ */ jsx(ListGlyph, {}),
148
+ numberedList: /* @__PURE__ */ jsx(ListOrderedGlyph, {}),
149
+ checklist: /* @__PURE__ */ jsx(ListChecksGlyph, {}),
150
+ table: /* @__PURE__ */ jsx(TableGlyph, {})
151
+ };
152
+ function MarkdownEditor({
153
+ value,
154
+ onChange,
155
+ label,
156
+ renderPreview,
157
+ placeholder,
158
+ mode: controlled,
159
+ defaultMode = "write",
160
+ onModeChange,
161
+ actions,
162
+ footer,
163
+ onSubmit,
164
+ disabled = false,
165
+ invalid = false,
166
+ rows = 14,
167
+ id,
168
+ "aria-describedby": describedBy,
169
+ labels,
170
+ ref,
171
+ className
172
+ }) {
173
+ const text = { ...LABELS, ...labels, formats: { ...LABELS.formats, ...labels?.formats } };
174
+ const [own, setOwn] = useState(defaultMode);
175
+ const mode = controlled ?? own;
176
+ const setMode = (next) => {
177
+ if (controlled === void 0) setOwn(next);
178
+ onModeChange?.(next);
179
+ };
180
+ const panelId = useId();
181
+ const field = useRef(null);
182
+ const pending = useRef(null);
183
+ useLayoutEffect(() => {
184
+ const el = field.current;
185
+ const selection2 = pending.current;
186
+ if (!el || !selection2) return;
187
+ pending.current = null;
188
+ el.focus();
189
+ el.setSelectionRange(selection2.start, selection2.end);
190
+ }, [value]);
191
+ const selection = () => {
192
+ const el = field.current;
193
+ return el ? { start: el.selectionStart, end: el.selectionEnd } : { start: value.length, end: value.length };
194
+ };
195
+ const apply = (edit) => {
196
+ if (edit.value === value) return;
197
+ pending.current = edit.selection;
198
+ onChange(edit.value);
199
+ };
200
+ const editor = {
201
+ mode,
202
+ insert: (inserted) => apply(insertMarkdown(value, selection(), inserted)),
203
+ format: (format) => apply(formatMarkdown(value, selection(), format)),
204
+ focus: () => field.current?.focus()
205
+ };
206
+ useImperativeHandle(ref, () => editor);
207
+ const onKeyDown = (event) => {
208
+ const command = event.metaKey || event.ctrlKey;
209
+ if (!command || event.altKey) return;
210
+ if (event.key === "Enter" && onSubmit) {
211
+ event.preventDefault();
212
+ onSubmit();
213
+ return;
214
+ }
215
+ if (event.shiftKey) return;
216
+ const key = event.key.toLowerCase();
217
+ if (key === "b" || key === "i") {
218
+ event.preventDefault();
219
+ editor.format(key === "b" ? "bold" : "italic");
220
+ }
221
+ };
222
+ const writing = mode === "write";
223
+ return /* @__PURE__ */ jsxs(
224
+ "div",
225
+ {
226
+ className: cx(
227
+ "crewlet-markdown-editor",
228
+ invalid && "crewlet-markdown-editor--invalid",
229
+ disabled && "crewlet-markdown-editor--disabled",
230
+ className
231
+ ),
232
+ style: { "--crewlet-markdown-editor-rows": rows },
233
+ children: [
234
+ /* @__PURE__ */ jsxs(Toolbar, { label: text.toolbar, className: "crewlet-markdown-editor__bar", children: [
235
+ GROUPS.map((group, index) => /* @__PURE__ */ jsx("span", { className: "crewlet-markdown-editor__group", children: group.map((format) => /* @__PURE__ */ jsx(
236
+ IconButton,
237
+ {
238
+ size: "sm",
239
+ variant: "ghost",
240
+ label: text.formats[format],
241
+ icon: GLYPHS[format],
242
+ disabled: disabled || !writing,
243
+ onMouseDown: (event) => event.preventDefault(),
244
+ onClick: () => editor.format(format)
245
+ },
246
+ format
247
+ )) }, index)),
248
+ actions && /* @__PURE__ */ jsx("span", { className: "crewlet-markdown-editor__group", children: actions(editor) }),
249
+ /* @__PURE__ */ jsx("span", { className: "crewlet-markdown-editor__spacer" }),
250
+ /* @__PURE__ */ jsx(
251
+ SegmentedControl,
252
+ {
253
+ semantics: "tabs",
254
+ panelId,
255
+ size: "sm",
256
+ label: text.view,
257
+ options: [
258
+ { value: "write", label: text.write },
259
+ { value: "preview", label: text.preview }
260
+ ],
261
+ value: mode,
262
+ onValueChange: setMode
263
+ }
264
+ )
265
+ ] }),
266
+ /* @__PURE__ */ jsx(
267
+ "div",
268
+ {
269
+ role: "tabpanel",
270
+ id: panelId,
271
+ "aria-labelledby": tabId(panelId, mode),
272
+ className: "crewlet-markdown-editor__panel",
273
+ children: writing ? /* @__PURE__ */ jsx(
274
+ "textarea",
275
+ {
276
+ ref: field,
277
+ id,
278
+ className: "crewlet-markdown-editor__field",
279
+ value,
280
+ onChange: (event) => onChange(event.target.value),
281
+ onKeyDown,
282
+ "aria-label": label,
283
+ "aria-describedby": describedBy,
284
+ "aria-invalid": invalid || void 0,
285
+ placeholder,
286
+ disabled,
287
+ rows,
288
+ spellCheck: true
289
+ }
290
+ ) : /* @__PURE__ */ jsx("div", { className: "crewlet-markdown-editor__preview", children: value.trim() === "" ? /* @__PURE__ */ jsx("p", { className: "crewlet-markdown-editor__empty", children: text.empty }) : renderPreview(value) })
291
+ }
292
+ ),
293
+ footer !== void 0 && footer !== null && /* @__PURE__ */ jsx("div", { className: "crewlet-markdown-editor__footer", children: footer })
294
+ ]
295
+ }
296
+ );
297
+ }
298
+
299
+ export { MARKDOWN_FORMATS, MarkdownEditor, formatMarkdown, insertMarkdown, markdownWords };
@@ -0,0 +1,94 @@
1
+ import { cx } from './chunk-ODEQTPFM.js';
2
+ import './Outline-VCXKKU3E.css';
3
+ import { useState, useRef, useEffect } from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ function Outline({ label, items, activeId = null, onSelect, className, ...rest }) {
7
+ const shallowest = items.reduce((min, item) => Math.min(min, item.level), Number.POSITIVE_INFINITY);
8
+ const select = onSelect ?? ((id) => scrollToHeading(id));
9
+ return /* @__PURE__ */ jsxs("nav", { ...rest, "aria-label": label, className: cx("crewlet-outline", className), children: [
10
+ /* @__PURE__ */ jsx("p", { className: "crewlet-outline__title", "aria-hidden": "true", children: label }),
11
+ /* @__PURE__ */ jsx("ol", { className: "crewlet-outline__list", children: items.map((item) => {
12
+ const current = item.id === activeId;
13
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
14
+ "button",
15
+ {
16
+ type: "button",
17
+ className: cx("crewlet-outline__item", current && "crewlet-outline__item--current"),
18
+ style: { "--crewlet-outline-depth": item.level - shallowest },
19
+ "aria-current": current ? "location" : void 0,
20
+ onClick: () => select(item.id),
21
+ children: item.label
22
+ }
23
+ ) }, item.id);
24
+ }) })
25
+ ] });
26
+ }
27
+ function scrollToHeading(id, ownerDocument = document) {
28
+ const heading = ownerDocument.getElementById(id);
29
+ if (!heading) return false;
30
+ if (!heading.hasAttribute("tabindex")) heading.setAttribute("tabindex", "-1");
31
+ const still = ownerDocument.defaultView?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
32
+ heading.scrollIntoView?.({ behavior: still ? "auto" : "smooth", block: "start" });
33
+ heading.focus({ preventScroll: true });
34
+ return true;
35
+ }
36
+ function nextFrame(view, run) {
37
+ if (typeof view.requestAnimationFrame === "function") {
38
+ const frame = view.requestAnimationFrame(run);
39
+ return () => view.cancelAnimationFrame(frame);
40
+ }
41
+ const timer = view.setTimeout(run, 0);
42
+ return () => view.clearTimeout(timer);
43
+ }
44
+ function useActiveHeading(ids, { root = null, offset = 0 } = {}) {
45
+ const key = ids.join("\0");
46
+ const [active, setActive] = useState(ids[0] ?? null);
47
+ const latest = useRef(active);
48
+ latest.current = active;
49
+ useEffect(() => {
50
+ const list = key === "" ? [] : key.split("\0");
51
+ const owner = root?.ownerDocument ?? document;
52
+ const view = owner.defaultView;
53
+ if (!view) return void 0;
54
+ const measure = () => {
55
+ if (list.length === 0) {
56
+ if (latest.current !== null) setActive(null);
57
+ return;
58
+ }
59
+ const top = root ? root.getBoundingClientRect().top : 0;
60
+ let current = list[0] ?? null;
61
+ for (const id of list) {
62
+ const heading = owner.getElementById(id);
63
+ if (!heading) continue;
64
+ if (heading.getBoundingClientRect().top - top <= offset + 1) current = id;
65
+ else break;
66
+ }
67
+ const scrolled = root ? root.scrollTop : view.scrollY;
68
+ const height = root ? root.clientHeight : view.innerHeight;
69
+ const extent = root ? root.scrollHeight : owner.documentElement.scrollHeight;
70
+ if (scrolled > 0 && scrolled + height >= extent - 1) current = list[list.length - 1] ?? current;
71
+ if (current !== latest.current) setActive(current);
72
+ };
73
+ let cancel = null;
74
+ const schedule = () => {
75
+ if (cancel) return;
76
+ cancel = nextFrame(view, () => {
77
+ cancel = null;
78
+ measure();
79
+ });
80
+ };
81
+ schedule();
82
+ const target = root ?? view;
83
+ target.addEventListener("scroll", schedule, { passive: true });
84
+ view.addEventListener("resize", schedule);
85
+ return () => {
86
+ target.removeEventListener("scroll", schedule);
87
+ view.removeEventListener("resize", schedule);
88
+ cancel?.();
89
+ };
90
+ }, [key, root, offset]);
91
+ return active;
92
+ }
93
+
94
+ export { Outline, scrollToHeading, useActiveHeading };
@@ -1,9 +1,9 @@
1
1
  import { today, CalendarGrid, shiftMonth, compareDays } from './chunk-GXQ3B46I.js';
2
2
  import { Button } from './chunk-OG6CL3EB.js';
3
3
  import { IconButton } from './chunk-4FQT6ZAY.js';
4
- import { Popover } from './chunk-PDUZO35I.js';
4
+ import { Popover } from './chunk-O2ZDZZS7.js';
5
5
  import { cx } from './chunk-ODEQTPFM.js';
6
- import './DateTimePicker-4AZBTODT.css';
6
+ import './DateTimePicker-NJ6XH2TE.css';
7
7
  import { useMemo, useState, useEffect } from 'react';
8
8
  import { CalendarGlyph, ChevronDownGlyph, ChevronsLeftGlyph, ChevronLeftGlyph, ChevronRightGlyph, ChevronsRightGlyph } from '@crewlethq/icons/glyphs';
9
9
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -1,17 +1,17 @@
1
1
  import { useLayerContainer } from './chunk-7RPGY3JE.js';
2
2
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
- import './Toaster-NVDCEIP5.css';
4
+ import './Toaster-VZKISP2S.css';
5
5
  import { createContext, useMemo, useState, useRef, useEffect, useContext, useCallback } from 'react';
6
6
  import { createPortal } from 'react-dom';
7
7
  import { XGlyph, CircleAlertGlyph, TriangleAlertGlyph, CircleCheckGlyph, InfoGlyph } from '@crewlethq/icons/glyphs';
8
8
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
9
 
10
10
  var ICONS = {
11
- info: /* @__PURE__ */ jsx(InfoGlyph, { size: "md" }),
12
- success: /* @__PURE__ */ jsx(CircleCheckGlyph, { size: "md" }),
13
- warning: /* @__PURE__ */ jsx(TriangleAlertGlyph, { size: "md" }),
14
- danger: /* @__PURE__ */ jsx(CircleAlertGlyph, { size: "md" })
11
+ info: /* @__PURE__ */ jsx(InfoGlyph, { size: "lg" }),
12
+ success: /* @__PURE__ */ jsx(CircleCheckGlyph, { size: "lg" }),
13
+ warning: /* @__PURE__ */ jsx(TriangleAlertGlyph, { size: "lg" }),
14
+ danger: /* @__PURE__ */ jsx(CircleAlertGlyph, { size: "lg" })
15
15
  };
16
16
  var TITLES = {
17
17
  info: null,
@@ -1,5 +1,5 @@
1
1
  import { avatarCorner, Avatar } from './chunk-HI6Q2UXQ.js';
2
- import './ImageUpload-L2GE4JUM.css';
2
+ import './ImageUpload-NVGMN7DP.css';
3
3
  import { useRef } from 'react';
4
4
  import { CameraGlyph, XGlyph } from '@crewlethq/icons/glyphs';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -1,6 +1,6 @@
1
1
  import { usePopupLayer, isComposing } from './chunk-R25RGPUB.js';
2
2
  import './Listbox-6WJNJQVQ.css';
3
- import { useState, useRef, useCallback, useEffect } from 'react';
3
+ import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
4
4
 
5
5
  function useListbox({
6
6
  id,
@@ -146,5 +146,50 @@ function useOptionKeys({ listbox, count, disabled, textOf, ends = true }) {
146
146
  );
147
147
  return { onKeyDown, step, opening };
148
148
  }
149
+ var HIGHLIGHT_REST_MS = 120;
150
+ function useHighlightRest(onRest) {
151
+ const told = useRef(null);
152
+ const timer = useRef(null);
153
+ const callback = useRef(onRest);
154
+ callback.current = onRest;
155
+ const cancel = useCallback(() => {
156
+ if (timer.current === null) return;
157
+ clearTimeout(timer.current);
158
+ timer.current = null;
159
+ }, []);
160
+ const tell = useCallback((key) => {
161
+ if (told.current === key) return;
162
+ told.current = key;
163
+ callback.current?.(key);
164
+ }, []);
165
+ const now = useCallback(
166
+ (key) => {
167
+ cancel();
168
+ tell(key);
169
+ },
170
+ [cancel, tell]
171
+ );
172
+ const rest = useCallback(
173
+ (key) => {
174
+ cancel();
175
+ timer.current = setTimeout(() => {
176
+ timer.current = null;
177
+ tell(key);
178
+ }, HIGHLIGHT_REST_MS);
179
+ },
180
+ [cancel, tell]
181
+ );
182
+ useEffect(
183
+ () => () => {
184
+ cancel();
185
+ if (told.current !== null) {
186
+ told.current = null;
187
+ callback.current?.(null);
188
+ }
189
+ },
190
+ [cancel]
191
+ );
192
+ return useMemo(() => ({ now, rest }), [now, rest]);
193
+ }
149
194
 
150
- export { TYPE_AHEAD_WINDOW, useListbox, useOptionKeys };
195
+ export { HIGHLIGHT_REST_MS, TYPE_AHEAD_WINDOW, useHighlightRest, useListbox, useOptionKeys };
@@ -1,6 +1,6 @@
1
1
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
2
2
  import { cx } from './chunk-ODEQTPFM.js';
3
- import './Link-6BEFNMN2.css';
3
+ import './Link-WPMVLKLY.css';
4
4
  import { forwardRef, Children, isValidElement, cloneElement } from 'react';
5
5
  import { ExternalLinkGlyph } from '@crewlethq/icons/glyphs';
6
6
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
@@ -0,0 +1,40 @@
1
+ import { cx } from './chunk-ODEQTPFM.js';
2
+ import './KeyChip-4XJU4BBE.css';
3
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
4
+
5
+ var WORD_BREAK = /[\s\-_/·]/;
6
+ function findMatch(text, query, prefer) {
7
+ const wanted = query.trim().toLowerCase();
8
+ if (wanted === "") return null;
9
+ const low = text.toLowerCase();
10
+ if (low.length !== text.length) return null;
11
+ let at = -1;
12
+ if (prefer === "start") {
13
+ at = low.startsWith(wanted) ? 0 : -1;
14
+ } else {
15
+ for (let i = 0; i < low.length; i += 1) {
16
+ const atWord = i === 0 || WORD_BREAK.test(low.charAt(i - 1));
17
+ if (atWord && low.startsWith(wanted, i)) {
18
+ at = i;
19
+ break;
20
+ }
21
+ }
22
+ }
23
+ if (at < 0) at = low.indexOf(wanted);
24
+ return at < 0 ? null : [at, at + wanted.length];
25
+ }
26
+ function highlightMatch(text, query, prefer, className) {
27
+ const range = query ? findMatch(text, query, prefer) : null;
28
+ if (!range) return text;
29
+ const [from, to] = range;
30
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
31
+ text.slice(0, from),
32
+ /* @__PURE__ */ jsx("b", { className, children: text.slice(from, to) }),
33
+ text.slice(to)
34
+ ] });
35
+ }
36
+ function KeyChip({ children, highlight, className, ...rest }) {
37
+ return /* @__PURE__ */ jsx("span", { ...rest, className: cx("crewlet-key-chip", className), children: highlightMatch(children, highlight, "start", "crewlet-key-chip__match") });
38
+ }
39
+
40
+ export { KeyChip, highlightMatch };
@@ -0,0 +1,41 @@
1
+ import { cx } from './chunk-ODEQTPFM.js';
2
+ import './Highlight-DXLV2R6L.css';
3
+ import { Fragment } from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ function highlightTerms(query) {
7
+ const words = query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((word) => [...word].length >= 2);
8
+ return [...new Set(words)].sort((a, b) => b.length - a.length || a.localeCompare(b));
9
+ }
10
+ var escape = (term) => term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11
+ function highlightRanges(text, terms) {
12
+ const wanted = terms.map((term) => term.trim()).filter((term) => term !== "");
13
+ if (text === "" || wanted.length === 0) return [];
14
+ const ordered = [...new Set(wanted)].sort((a, b) => b.length - a.length);
15
+ const pattern = new RegExp(`(?<![\\p{L}\\p{N}])(?:${ordered.map(escape).join("|")})`, "giu");
16
+ const ranges = [];
17
+ for (const match of text.matchAll(pattern)) {
18
+ const start = match.index ?? 0;
19
+ const end = start + match[0].length;
20
+ const last = ranges[ranges.length - 1];
21
+ if (last && start <= last[1]) ranges[ranges.length - 1] = [last[0], Math.max(last[1], end)];
22
+ else ranges.push([start, end]);
23
+ }
24
+ return ranges;
25
+ }
26
+ function Highlight({ text, terms, className, ...rest }) {
27
+ const ranges = highlightRanges(text, terms);
28
+ const parts = [];
29
+ let at = 0;
30
+ ranges.forEach(([start, end], index) => {
31
+ if (start > at) parts.push(/* @__PURE__ */ jsx(Fragment, { children: text.slice(at, start) }, `t${index}`));
32
+ parts.push(
33
+ /* @__PURE__ */ jsx("mark", { className: "crewlet-highlight__match", children: text.slice(start, end) }, `m${index}`)
34
+ );
35
+ at = end;
36
+ });
37
+ if (at < text.length) parts.push(/* @__PURE__ */ jsx(Fragment, { children: text.slice(at) }, "tail"));
38
+ return /* @__PURE__ */ jsx("span", { ...rest, className: cx("crewlet-highlight", className), children: parts });
39
+ }
40
+
41
+ export { Highlight, highlightRanges, highlightTerms };
@@ -1,12 +1,12 @@
1
- import { Select } from './chunk-SEFMO2EV.js';
1
+ import { Select } from './chunk-IXUU5DLZ.js';
2
2
  import { Input } from './chunk-FTEJPHBI.js';
3
3
  import { today, shiftMonth, CalendarGrid, compareDays } from './chunk-GXQ3B46I.js';
4
4
  import { Button } from './chunk-OG6CL3EB.js';
5
5
  import { IconButton } from './chunk-4FQT6ZAY.js';
6
- import { Popover } from './chunk-PDUZO35I.js';
6
+ import { Popover } from './chunk-O2ZDZZS7.js';
7
7
  import { isComposing } from './chunk-R25RGPUB.js';
8
8
  import { cx } from './chunk-ODEQTPFM.js';
9
- import './TimeWindowPicker-GBF4JDFI.css';
9
+ import './TimeWindowPicker-BZ5HHATU.css';
10
10
  import { useMemo, useState, useRef, useId, useEffect } from 'react';
11
11
  import { ClockGlyph, ChevronDownGlyph, CheckGlyph, ChevronLeftGlyph, ChevronRightGlyph } from '@crewlethq/icons/glyphs';
12
12
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -0,0 +1,22 @@
1
+ import { useCallback, useSyncExternalStore } from 'react';
2
+
3
+ // src/utils/useMediaMatch.ts
4
+ function useMediaMatch(query) {
5
+ const subscribe = useCallback(
6
+ (notify) => {
7
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => {
8
+ };
9
+ const list = window.matchMedia(query);
10
+ list.addEventListener("change", notify);
11
+ return () => list.removeEventListener("change", notify);
12
+ },
13
+ [query]
14
+ );
15
+ const read = useCallback(
16
+ () => typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(query).matches : false,
17
+ [query]
18
+ );
19
+ return useSyncExternalStore(subscribe, read, () => false);
20
+ }
21
+
22
+ export { useMediaMatch };