beez-ui 0.6.2 → 0.8.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 (127) hide show
  1. package/CHANGELOG.md +150 -96
  2. package/README.md +33 -6
  3. package/dist/components/account-menu.d.ts +53 -0
  4. package/dist/components/account-menu.js +405 -0
  5. package/dist/components/alert.js +1 -1
  6. package/dist/components/animated-collapse.d.ts +19 -0
  7. package/dist/components/animated-collapse.js +81 -0
  8. package/dist/components/animated-count.d.ts +13 -0
  9. package/dist/components/animated-count.js +105 -0
  10. package/dist/components/animated-list-item.d.ts +25 -0
  11. package/dist/components/animated-list-item.js +132 -0
  12. package/dist/components/avatar.d.ts +2 -1
  13. package/dist/components/avatar.js +4 -3
  14. package/dist/components/bouncing-dots-loader.d.ts +19 -0
  15. package/dist/components/bouncing-dots-loader.js +47 -0
  16. package/dist/components/calendar.js +102 -0
  17. package/dist/components/carousel.js +61 -48
  18. package/dist/components/checkbox.js +1 -1
  19. package/dist/components/confirm-delete-button.d.ts +29 -0
  20. package/dist/components/confirm-delete-button.js +254 -0
  21. package/dist/components/data-table.js +34 -4
  22. package/dist/components/dropdown-menu.js +5 -2
  23. package/dist/components/empty-state.d.ts +24 -0
  24. package/dist/components/empty-state.js +37 -0
  25. package/dist/components/error-state.d.ts +20 -0
  26. package/dist/components/error-state.js +145 -0
  27. package/dist/components/external-browser-handoff.d.ts +27 -0
  28. package/dist/components/external-browser-handoff.js +306 -0
  29. package/dist/components/file-upload.d.ts +78 -0
  30. package/dist/components/file-upload.js +678 -0
  31. package/dist/components/filter-query-bar.js +11 -6
  32. package/dist/components/form.d.ts +5 -0
  33. package/dist/components/form.js +11 -9
  34. package/dist/components/info-popover.d.ts +23 -0
  35. package/dist/components/info-popover.js +158 -0
  36. package/dist/components/input-group.js +1 -1
  37. package/dist/components/input.js +1 -1
  38. package/dist/components/month-calendar-header.d.ts +65 -0
  39. package/dist/components/month-calendar-header.js +337 -0
  40. package/dist/components/month-grid.d.ts +59 -0
  41. package/dist/components/month-grid.js +301 -0
  42. package/dist/components/notification-bell.d.ts +50 -0
  43. package/dist/components/notification-bell.js +338 -0
  44. package/dist/components/notification-panel.d.ts +56 -0
  45. package/dist/components/notification-panel.js +281 -0
  46. package/dist/components/open-in-browser-cta.d.ts +22 -0
  47. package/dist/components/open-in-browser-cta.js +61 -0
  48. package/dist/components/pagination.d.ts +1 -0
  49. package/dist/components/pagination.js +9 -4
  50. package/dist/components/presence-swap.d.ts +22 -0
  51. package/dist/components/presence-swap.js +162 -0
  52. package/dist/components/progress-ring.d.ts +20 -0
  53. package/dist/components/progress-ring.js +53 -0
  54. package/dist/components/pwa-update-control.d.ts +18 -0
  55. package/dist/components/pwa-update-control.js +119 -0
  56. package/dist/components/radio-group.js +1 -1
  57. package/dist/components/reaction-button.d.ts +26 -0
  58. package/dist/components/reaction-button.js +114 -0
  59. package/dist/components/rich-link-editor.d.ts +25 -0
  60. package/dist/components/rich-link-editor.js +320 -0
  61. package/dist/components/rich-markdown-content.d.ts +10 -0
  62. package/dist/components/rich-markdown-content.js +56 -0
  63. package/dist/components/rich-text-content.d.ts +19 -0
  64. package/dist/components/rich-text-content.js +26 -0
  65. package/dist/components/select.js +1 -1
  66. package/dist/components/sheet.js +12 -9
  67. package/dist/components/sidebar.js +9 -5
  68. package/dist/components/skeleton.d.ts +6 -0
  69. package/dist/components/skeleton.js +7 -1
  70. package/dist/components/switch.js +1 -1
  71. package/dist/components/textarea.js +1 -1
  72. package/dist/components/typing-animation.js +42 -15
  73. package/dist/emoji-picker.d.ts +27 -0
  74. package/dist/emoji-picker.js +283 -0
  75. package/dist/hooks/use-horizontal-swipe.d.ts +23 -0
  76. package/dist/hooks/use-horizontal-swipe.js +80 -0
  77. package/dist/hooks/use-is-hydrated.d.ts +6 -0
  78. package/dist/hooks/use-is-hydrated.js +20 -0
  79. package/dist/hooks/use-minute-clock.d.ts +23 -0
  80. package/dist/hooks/use-minute-clock.js +147 -0
  81. package/dist/hooks/use-month-transition-direction.d.ts +21 -0
  82. package/dist/hooks/use-month-transition-direction.js +110 -0
  83. package/dist/hooks/use-rich-link-editor.d.ts +55 -0
  84. package/dist/hooks/use-rich-link-editor.js +515 -0
  85. package/dist/hooks/use-viewer-time-zone.d.ts +6 -0
  86. package/dist/hooks/use-viewer-time-zone.js +20 -0
  87. package/dist/index.d.ts +44 -0
  88. package/dist/index.js +43 -0
  89. package/dist/lib/browser-clipboard.d.ts +13 -0
  90. package/dist/lib/browser-clipboard.js +48 -0
  91. package/dist/lib/browser-navigation.d.ts +21 -0
  92. package/dist/lib/browser-navigation.js +42 -0
  93. package/dist/lib/column-filter-value.d.ts +12 -0
  94. package/dist/lib/column-filter-value.js +52 -0
  95. package/dist/lib/file-acceptance.d.ts +43 -0
  96. package/dist/lib/file-acceptance.js +107 -0
  97. package/dist/lib/format-file-size.d.ts +16 -0
  98. package/dist/lib/format-file-size.js +34 -0
  99. package/dist/lib/fuzzy-search.d.ts +17 -0
  100. package/dist/lib/fuzzy-search.js +183 -0
  101. package/dist/lib/horizontal-swipe.d.ts +23 -0
  102. package/dist/lib/horizontal-swipe.js +29 -0
  103. package/dist/lib/in-app-browser.d.ts +35 -0
  104. package/dist/lib/in-app-browser.js +80 -0
  105. package/dist/lib/month-grid.d.ts +42 -0
  106. package/dist/lib/month-grid.js +74 -0
  107. package/dist/lib/name-initials.d.ts +7 -0
  108. package/dist/lib/name-initials.js +11 -0
  109. package/dist/lib/rich-text/link-markdown-constants.d.ts +147 -0
  110. package/dist/lib/rich-text/link-markdown-constants.js +458 -0
  111. package/dist/lib/rich-text/link-markdown-types.d.ts +53 -0
  112. package/dist/lib/rich-text/link-markdown-types.js +1 -0
  113. package/dist/lib/rich-text/link-markdown.d.ts +193 -0
  114. package/dist/lib/rich-text/link-markdown.js +592 -0
  115. package/dist/lib/rich-text/rich-markdown.d.ts +41 -0
  116. package/dist/lib/rich-text/rich-markdown.js +105 -0
  117. package/dist/lib/rich-text/rich-text-editor-dom.d.ts +26 -0
  118. package/dist/lib/rich-text/rich-text-editor-dom.js +79 -0
  119. package/dist/motion/glide-indicator.d.ts +37 -3
  120. package/dist/motion/glide-indicator.js +37 -3
  121. package/dist/motion/motion-slot.js +47 -18
  122. package/dist/motion/surface-keyframes.js +18 -6
  123. package/dist/motion/tokens.d.ts +41 -2
  124. package/dist/motion/tokens.js +40 -1
  125. package/dist/providers/beez-ui-provider.js +49 -17
  126. package/dist/styles.css +1 -1
  127. package/package.json +212 -2
@@ -0,0 +1,515 @@
1
+ "use client";
2
+ import { c as _c } from "react/compiler-runtime";
3
+ /** Owns the state of `RichLinkEditor`: content, tracked links, popover and selection. */
4
+ import { useLayoutEffect, useRef, useState } from "react";
5
+ import { RICH_LINK_KIND, RICH_LINK_POPOVER_MODE, RICH_PREVIEW_LINK_SOURCE, RICH_TEXT_CLIPBOARD_DATA_TYPE, RICH_TEXT_EDITOR_INPUT_TYPE, RICH_TEXT_EDITOR_KEY, RICH_TEXT_EDITOR_KEY_LENGTH, RICH_TEXT_EDITOR_TEXT, RICH_TEXT_EDITOR_WORD_DIRECTION } from "../lib/rich-text/link-markdown-constants.js";
6
+ import { deserializeMarkdownForEditor, getLinksAfterTextChange, getWordDeletionRange, isLinkSynchronized, normalizeMarkdownUrl, parsePreviewSegments, rangesOverlap, serializeEditorContent } from "../lib/rich-text/link-markdown.js";
7
+ import { getEditorSelectionRange, setEditorSelectionRange } from "../lib/rich-text/rich-text-editor-dom.js";
8
+ const EMPTY_TEXT = "";
9
+ /**
10
+ * Controls a rich link editor backed by a `contentEditable` element: it owns the
11
+ * plain-text content, the tracked explicit/suppressed links, the link popover
12
+ * state, and the selection bookkeeping, and exposes serialize/reset/snapshot
13
+ * helpers so a feature can persist or restore the value. The DOM rendering lives
14
+ * in `RichLinkEditor`.
15
+ */
16
+ export function useRichLinkEditor(t0) {
17
+ const $ = _c(72);
18
+ let t1;
19
+ if ($[0] !== t0) {
20
+ t1 = t0 === undefined ? {} : t0;
21
+ $[0] = t0;
22
+ $[1] = t1;
23
+ } else {
24
+ t1 = $[1];
25
+ }
26
+ const options = t1;
27
+ const editorRef = useRef(null);
28
+ let t2;
29
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
30
+ t2 = (node) => {
31
+ editorRef.current = node;
32
+ };
33
+ $[2] = t2;
34
+ } else {
35
+ t2 = $[2];
36
+ }
37
+ const setEditorElement = t2;
38
+ const pendingSelectionRef = useRef(null);
39
+ let t3;
40
+ if ($[3] !== options.initialMarkdown) {
41
+ t3 = () => deserializeMarkdownForEditor(options.initialMarkdown ?? EMPTY_TEXT).content;
42
+ $[3] = options.initialMarkdown;
43
+ $[4] = t3;
44
+ } else {
45
+ t3 = $[4];
46
+ }
47
+ const [content, setContent] = useState(t3);
48
+ let t4;
49
+ if ($[5] !== options.initialMarkdown) {
50
+ t4 = () => deserializeMarkdownForEditor(options.initialMarkdown ?? EMPTY_TEXT).links;
51
+ $[5] = options.initialMarkdown;
52
+ $[6] = t4;
53
+ } else {
54
+ t4 = $[6];
55
+ }
56
+ const [links, setLinks] = useState(t4);
57
+ const [activeLink, setActiveLink] = useState(null);
58
+ const [linkTextInput, setLinkTextInput] = useState("");
59
+ const [linkUrlInput, setLinkUrlInput] = useState("");
60
+ const [popoverMode, setPopoverMode] = useState(RICH_LINK_POPOVER_MODE.actions);
61
+ let t5;
62
+ if ($[7] !== content || $[8] !== links) {
63
+ t5 = parsePreviewSegments(content, links);
64
+ $[7] = content;
65
+ $[8] = links;
66
+ $[9] = t5;
67
+ } else {
68
+ t5 = $[9];
69
+ }
70
+ const segments = t5;
71
+ const hasContent = content.length > 0;
72
+ let t6;
73
+ if ($[10] !== linkUrlInput) {
74
+ t6 = normalizeMarkdownUrl(linkUrlInput);
75
+ $[10] = linkUrlInput;
76
+ $[11] = t6;
77
+ } else {
78
+ t6 = $[11];
79
+ }
80
+ const normalizedLinkUrlInput = t6;
81
+ let t7;
82
+ if ($[12] !== linkUrlInput || $[13] !== normalizedLinkUrlInput) {
83
+ t7 = linkUrlInput.trim().length > 0 && normalizedLinkUrlInput === null;
84
+ $[12] = linkUrlInput;
85
+ $[13] = normalizedLinkUrlInput;
86
+ $[14] = t7;
87
+ } else {
88
+ t7 = $[14];
89
+ }
90
+ const hasInvalidLinkUrl = t7;
91
+ let t8;
92
+ if ($[15] !== linkTextInput || $[16] !== normalizedLinkUrlInput) {
93
+ t8 = normalizedLinkUrlInput !== null && linkTextInput.trim().length > 0;
94
+ $[15] = linkTextInput;
95
+ $[16] = normalizedLinkUrlInput;
96
+ $[17] = t8;
97
+ } else {
98
+ t8 = $[17];
99
+ }
100
+ const isLinkEditValid = t8;
101
+ let t9;
102
+ if ($[18] === Symbol.for("react.memo_cache_sentinel")) {
103
+ t9 = () => {
104
+ const editor = editorRef.current;
105
+ const selectionRange = pendingSelectionRef.current;
106
+ if (!editor || !selectionRange || !editor.contains(document.activeElement)) {
107
+ return;
108
+ }
109
+ setEditorSelectionRange(editor, selectionRange);
110
+ pendingSelectionRef.current = null;
111
+ };
112
+ $[18] = t9;
113
+ } else {
114
+ t9 = $[18];
115
+ }
116
+ let t10;
117
+ if ($[19] !== content || $[20] !== links) {
118
+ t10 = [content, links];
119
+ $[19] = content;
120
+ $[20] = links;
121
+ $[21] = t10;
122
+ } else {
123
+ t10 = $[21];
124
+ }
125
+ useLayoutEffect(t9, t10);
126
+ let t11;
127
+ if ($[22] !== content || $[23] !== options) {
128
+ t11 = (nextContent) => {
129
+ setLinks((currentLinks) => getLinksAfterTextChange({
130
+ links: currentLinks,
131
+ nextText: nextContent,
132
+ previousText: content
133
+ }));
134
+ setContent(nextContent);
135
+ options.onContentChange?.();
136
+ };
137
+ $[22] = content;
138
+ $[23] = options;
139
+ $[24] = t11;
140
+ } else {
141
+ t11 = $[24];
142
+ }
143
+ const handleContentChange = t11;
144
+ let t12;
145
+ if ($[25] !== content || $[26] !== handleContentChange) {
146
+ t12 = (replacementText, selectionRange_0) => {
147
+ const nextSelectionOffset = selectionRange_0.start + replacementText.length;
148
+ const nextContent_0 = content.slice(0, selectionRange_0.start) + replacementText + content.slice(selectionRange_0.end);
149
+ pendingSelectionRef.current = {
150
+ end: nextSelectionOffset,
151
+ start: nextSelectionOffset
152
+ };
153
+ handleContentChange(nextContent_0);
154
+ };
155
+ $[25] = content;
156
+ $[26] = handleContentChange;
157
+ $[27] = t12;
158
+ } else {
159
+ t12 = $[27];
160
+ }
161
+ const replaceContentText = t12;
162
+ let t13;
163
+ if ($[28] !== content || $[29] !== options || $[30] !== replaceContentText) {
164
+ t13 = (event) => {
165
+ const pastedText = event.clipboardData.getData(RICH_TEXT_CLIPBOARD_DATA_TYPE.plainText);
166
+ const pastedUrl = normalizeMarkdownUrl(pastedText);
167
+ const selectionRange_1 = getEditorSelectionRange(event.currentTarget) ?? {
168
+ end: content.length,
169
+ start: content.length
170
+ };
171
+ const hasSelectedText = selectionRange_1.start !== selectionRange_1.end;
172
+ event.preventDefault();
173
+ if (!pastedUrl || !hasSelectedText) {
174
+ replaceContentText(pastedText, selectionRange_1);
175
+ return;
176
+ }
177
+ pendingSelectionRef.current = selectionRange_1;
178
+ setLinks((currentLinks_0) => [...currentLinks_0.filter((link) => !rangesOverlap(link, {
179
+ end: selectionRange_1.end,
180
+ start: selectionRange_1.start
181
+ })), {
182
+ end: selectionRange_1.end,
183
+ id: crypto.randomUUID(),
184
+ isSynced: isLinkSynchronized(content.slice(selectionRange_1.start, selectionRange_1.end), pastedUrl),
185
+ kind: RICH_LINK_KIND.explicit,
186
+ start: selectionRange_1.start,
187
+ url: pastedUrl
188
+ }]);
189
+ options.onContentChange?.();
190
+ };
191
+ $[28] = content;
192
+ $[29] = options;
193
+ $[30] = replaceContentText;
194
+ $[31] = t13;
195
+ } else {
196
+ t13 = $[31];
197
+ }
198
+ const handlePaste = t13;
199
+ let t14;
200
+ if ($[32] !== content || $[33] !== replaceContentText) {
201
+ t14 = (event_0) => {
202
+ if (event_0.altKey || event_0.metaKey) {
203
+ return;
204
+ }
205
+ if (event_0.ctrlKey && event_0.key !== RICH_TEXT_EDITOR_KEY.backspace && event_0.key !== RICH_TEXT_EDITOR_KEY.delete) {
206
+ return;
207
+ }
208
+ const selectionRange_2 = getEditorSelectionRange(event_0.currentTarget) ?? {
209
+ end: content.length,
210
+ start: content.length
211
+ };
212
+ let replacementText_0 = null;
213
+ let replacementRange = selectionRange_2;
214
+ if (event_0.key.length === RICH_TEXT_EDITOR_KEY_LENGTH.character) {
215
+ replacementText_0 = event_0.key;
216
+ }
217
+ if (event_0.key === RICH_TEXT_EDITOR_KEY.enter) {
218
+ replacementText_0 = RICH_TEXT_EDITOR_TEXT.lineBreak;
219
+ }
220
+ if (event_0.key === RICH_TEXT_EDITOR_KEY.backspace) {
221
+ replacementText_0 = EMPTY_TEXT;
222
+ replacementRange = event_0.ctrlKey ? getWordDeletionRange({
223
+ direction: RICH_TEXT_EDITOR_WORD_DIRECTION.backward,
224
+ selectionRange: selectionRange_2,
225
+ text: content
226
+ }) : selectionRange_2.start === selectionRange_2.end ? {
227
+ end: selectionRange_2.end,
228
+ start: Math.max(selectionRange_2.start - 1, 0)
229
+ } : selectionRange_2;
230
+ }
231
+ if (event_0.key === RICH_TEXT_EDITOR_KEY.delete) {
232
+ replacementText_0 = EMPTY_TEXT;
233
+ replacementRange = event_0.ctrlKey ? getWordDeletionRange({
234
+ direction: RICH_TEXT_EDITOR_WORD_DIRECTION.forward,
235
+ selectionRange: selectionRange_2,
236
+ text: content
237
+ }) : selectionRange_2.start === selectionRange_2.end ? {
238
+ end: Math.min(selectionRange_2.end + 1, content.length),
239
+ start: selectionRange_2.start
240
+ } : selectionRange_2;
241
+ }
242
+ if (replacementText_0 === null) {
243
+ return;
244
+ }
245
+ event_0.preventDefault();
246
+ replaceContentText(replacementText_0, replacementRange);
247
+ };
248
+ $[32] = content;
249
+ $[33] = replaceContentText;
250
+ $[34] = t14;
251
+ } else {
252
+ t14 = $[34];
253
+ }
254
+ const handleKeyDown = t14;
255
+ let t15;
256
+ if ($[35] !== content.length || $[36] !== replaceContentText) {
257
+ t15 = (event_1) => {
258
+ const nativeEvent = event_1.nativeEvent;
259
+ const selectionRange_3 = getEditorSelectionRange(event_1.currentTarget) ?? {
260
+ end: content.length,
261
+ start: content.length
262
+ };
263
+ let replacementText_1 = null;
264
+ let replacementRange_0 = selectionRange_3;
265
+ if (nativeEvent.inputType === RICH_TEXT_EDITOR_INPUT_TYPE.insertText) {
266
+ replacementText_1 = nativeEvent.data ?? EMPTY_TEXT;
267
+ }
268
+ if (nativeEvent.inputType === RICH_TEXT_EDITOR_INPUT_TYPE.insertParagraph || nativeEvent.inputType === RICH_TEXT_EDITOR_INPUT_TYPE.insertLineBreak) {
269
+ replacementText_1 = RICH_TEXT_EDITOR_TEXT.lineBreak;
270
+ }
271
+ if (nativeEvent.inputType === RICH_TEXT_EDITOR_INPUT_TYPE.deleteContentBackward) {
272
+ replacementText_1 = EMPTY_TEXT;
273
+ replacementRange_0 = selectionRange_3.start === selectionRange_3.end ? {
274
+ end: selectionRange_3.end,
275
+ start: Math.max(selectionRange_3.start - 1, 0)
276
+ } : selectionRange_3;
277
+ }
278
+ if (nativeEvent.inputType === RICH_TEXT_EDITOR_INPUT_TYPE.deleteContentForward) {
279
+ replacementText_1 = EMPTY_TEXT;
280
+ replacementRange_0 = selectionRange_3.start === selectionRange_3.end ? {
281
+ end: Math.min(selectionRange_3.end + 1, content.length),
282
+ start: selectionRange_3.start
283
+ } : selectionRange_3;
284
+ }
285
+ if (replacementText_1 === null) {
286
+ return;
287
+ }
288
+ event_1.preventDefault();
289
+ replaceContentText(replacementText_1, replacementRange_0);
290
+ };
291
+ $[35] = content.length;
292
+ $[36] = replaceContentText;
293
+ $[37] = t15;
294
+ } else {
295
+ t15 = $[37];
296
+ }
297
+ const handleBeforeInput = t15;
298
+ let t16;
299
+ if ($[38] !== handleContentChange) {
300
+ t16 = (event_2) => {
301
+ const selectionRange_4 = getEditorSelectionRange(event_2.currentTarget);
302
+ if (selectionRange_4) {
303
+ pendingSelectionRef.current = selectionRange_4;
304
+ }
305
+ handleContentChange(event_2.currentTarget.textContent ?? EMPTY_TEXT);
306
+ };
307
+ $[38] = handleContentChange;
308
+ $[39] = t16;
309
+ } else {
310
+ t16 = $[39];
311
+ }
312
+ const handleInput = t16;
313
+ let t17;
314
+ if ($[40] === Symbol.for("react.memo_cache_sentinel")) {
315
+ t17 = (segment) => {
316
+ setActiveLink(segment);
317
+ setLinkTextInput(segment.text);
318
+ setLinkUrlInput(segment.url);
319
+ setPopoverMode(RICH_LINK_POPOVER_MODE.actions);
320
+ };
321
+ $[40] = t17;
322
+ } else {
323
+ t17 = $[40];
324
+ }
325
+ const openLinkPopover = t17;
326
+ let t18;
327
+ if ($[41] === Symbol.for("react.memo_cache_sentinel")) {
328
+ t18 = () => {
329
+ setActiveLink(null);
330
+ setLinkTextInput(EMPTY_TEXT);
331
+ setLinkUrlInput(EMPTY_TEXT);
332
+ setPopoverMode(RICH_LINK_POPOVER_MODE.actions);
333
+ };
334
+ $[41] = t18;
335
+ } else {
336
+ t18 = $[41];
337
+ }
338
+ const closeLinkPopover = t18;
339
+ let t19;
340
+ if ($[42] === Symbol.for("react.memo_cache_sentinel")) {
341
+ t19 = (segment_0) => {
342
+ const shouldSuppressVisibleUrl = Boolean(normalizeMarkdownUrl(segment_0.text));
343
+ const suppressedLink = {
344
+ end: segment_0.end,
345
+ id: crypto.randomUUID(),
346
+ kind: RICH_LINK_KIND.suppressed,
347
+ start: segment_0.start
348
+ };
349
+ setLinks((currentLinks_1) => {
350
+ const linksWithoutRemovedExplicitLink = segment_0.source === RICH_PREVIEW_LINK_SOURCE.explicit && segment_0.id ? currentLinks_1.filter((link_0) => link_0.id !== segment_0.id) : currentLinks_1;
351
+ return shouldSuppressVisibleUrl || segment_0.source === RICH_PREVIEW_LINK_SOURCE.automatic ? [...linksWithoutRemovedExplicitLink, suppressedLink] : linksWithoutRemovedExplicitLink;
352
+ });
353
+ closeLinkPopover();
354
+ };
355
+ $[42] = t19;
356
+ } else {
357
+ t19 = $[42];
358
+ }
359
+ const removeLink = t19;
360
+ let t20;
361
+ if ($[43] !== content || $[44] !== linkTextInput || $[45] !== linkUrlInput) {
362
+ t20 = (segment_1) => {
363
+ const nextUrl = normalizeMarkdownUrl(linkUrlInput);
364
+ const nextText = linkTextInput;
365
+ if (!nextUrl || !nextText.trim()) {
366
+ return;
367
+ }
368
+ const nextContent_1 = content.slice(0, segment_1.start) + nextText + content.slice(segment_1.end);
369
+ const nextEnd = segment_1.start + nextText.length;
370
+ const nextLinkRange = {
371
+ end: nextEnd,
372
+ start: segment_1.start
373
+ };
374
+ pendingSelectionRef.current = {
375
+ end: nextEnd,
376
+ start: nextEnd
377
+ };
378
+ setContent(nextContent_1);
379
+ setLinks((currentLinks_2) => {
380
+ const adjustedLinks = getLinksAfterTextChange({
381
+ links: currentLinks_2,
382
+ nextText: nextContent_1,
383
+ previousText: content
384
+ });
385
+ return [...adjustedLinks.filter((link_1) => {
386
+ if (segment_1.id && link_1.id === segment_1.id) {
387
+ return false;
388
+ }
389
+ return !rangesOverlap(link_1, nextLinkRange);
390
+ }), {
391
+ end: nextEnd,
392
+ id: segment_1.id ?? crypto.randomUUID(),
393
+ isSynced: isLinkSynchronized(nextText, nextUrl),
394
+ kind: RICH_LINK_KIND.explicit,
395
+ start: segment_1.start,
396
+ url: nextUrl
397
+ }];
398
+ });
399
+ closeLinkPopover();
400
+ };
401
+ $[43] = content;
402
+ $[44] = linkTextInput;
403
+ $[45] = linkUrlInput;
404
+ $[46] = t20;
405
+ } else {
406
+ t20 = $[46];
407
+ }
408
+ const saveLinkEdit = t20;
409
+ let t21;
410
+ if ($[47] !== content || $[48] !== links) {
411
+ t21 = () => serializeEditorContent(content, links);
412
+ $[47] = content;
413
+ $[48] = links;
414
+ $[49] = t21;
415
+ } else {
416
+ t21 = $[49];
417
+ }
418
+ const serialize = t21;
419
+ let t22;
420
+ if ($[50] === Symbol.for("react.memo_cache_sentinel")) {
421
+ t22 = (markdown) => {
422
+ const nextState = deserializeMarkdownForEditor(markdown ?? EMPTY_TEXT);
423
+ pendingSelectionRef.current = null;
424
+ setContent(nextState.content);
425
+ setLinks(nextState.links);
426
+ closeLinkPopover();
427
+ };
428
+ $[50] = t22;
429
+ } else {
430
+ t22 = $[50];
431
+ }
432
+ const reset = t22;
433
+ let t23;
434
+ if ($[51] !== content || $[52] !== links) {
435
+ t23 = () => ({
436
+ content,
437
+ links: links.map(_temp)
438
+ });
439
+ $[51] = content;
440
+ $[52] = links;
441
+ $[53] = t23;
442
+ } else {
443
+ t23 = $[53];
444
+ }
445
+ const getDisplayState = t23;
446
+ let t24;
447
+ if ($[54] === Symbol.for("react.memo_cache_sentinel")) {
448
+ t24 = (state) => {
449
+ pendingSelectionRef.current = null;
450
+ setContent(state.content);
451
+ setLinks(state.links.map(_temp2));
452
+ closeLinkPopover();
453
+ };
454
+ $[54] = t24;
455
+ } else {
456
+ t24 = $[54];
457
+ }
458
+ const loadFromDisplay = t24;
459
+ let t25;
460
+ if ($[55] !== activeLink || $[56] !== content || $[57] !== getDisplayState || $[58] !== handleBeforeInput || $[59] !== handleInput || $[60] !== handleKeyDown || $[61] !== handlePaste || $[62] !== hasContent || $[63] !== hasInvalidLinkUrl || $[64] !== isLinkEditValid || $[65] !== linkTextInput || $[66] !== linkUrlInput || $[67] !== popoverMode || $[68] !== saveLinkEdit || $[69] !== segments || $[70] !== serialize) {
461
+ t25 = {
462
+ activeLink,
463
+ closeLinkPopover,
464
+ content,
465
+ getDisplayState,
466
+ handleBeforeInput,
467
+ handleInput,
468
+ handleKeyDown,
469
+ handlePaste,
470
+ hasContent,
471
+ hasInvalidLinkUrl,
472
+ isLinkEditValid,
473
+ linkTextInput,
474
+ linkUrlInput,
475
+ loadFromDisplay,
476
+ openLinkPopover,
477
+ popoverMode,
478
+ removeLink,
479
+ reset,
480
+ saveLinkEdit,
481
+ segments,
482
+ serialize,
483
+ setEditorElement,
484
+ setLinkTextInput,
485
+ setLinkUrlInput,
486
+ setPopoverMode
487
+ };
488
+ $[55] = activeLink;
489
+ $[56] = content;
490
+ $[57] = getDisplayState;
491
+ $[58] = handleBeforeInput;
492
+ $[59] = handleInput;
493
+ $[60] = handleKeyDown;
494
+ $[61] = handlePaste;
495
+ $[62] = hasContent;
496
+ $[63] = hasInvalidLinkUrl;
497
+ $[64] = isLinkEditValid;
498
+ $[65] = linkTextInput;
499
+ $[66] = linkUrlInput;
500
+ $[67] = popoverMode;
501
+ $[68] = saveLinkEdit;
502
+ $[69] = segments;
503
+ $[70] = serialize;
504
+ $[71] = t25;
505
+ } else {
506
+ t25 = $[71];
507
+ }
508
+ return t25;
509
+ }
510
+ function _temp2(link_3) {
511
+ return { ...link_3 };
512
+ }
513
+ function _temp(link_2) {
514
+ return { ...link_2 };
515
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * IANA time zone of the browser (for example "Europe/Madrid"). Returns null on
3
+ * the server and during hydration so zone-dependent copy never causes a
4
+ * markup mismatch; it settles right after hydration without an effect.
5
+ */
6
+ export declare function useViewerTimeZone(): string | null;
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ /** Reads the viewer IANA time zone once hydrated, so zone-dependent copy never mismatches. */
3
+ import { useSyncExternalStore } from "react";
4
+ function subscribeToNothing() {
5
+ return () => undefined;
6
+ }
7
+ function getViewerTimeZoneSnapshot() {
8
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
9
+ }
10
+ function getServerViewerTimeZoneSnapshot() {
11
+ return null;
12
+ }
13
+ /**
14
+ * IANA time zone of the browser (for example "Europe/Madrid"). Returns null on
15
+ * the server and during hydration so zone-dependent copy never causes a
16
+ * markup mismatch; it settles right after hydration without an effect.
17
+ */
18
+ export function useViewerTimeZone() {
19
+ return useSyncExternalStore(subscribeToNothing, getViewerTimeZoneSnapshot, getServerViewerTimeZoneSnapshot);
20
+ }
package/dist/index.d.ts CHANGED
@@ -46,3 +46,47 @@ export * from "./components/typing-animation.js";
46
46
  export * from "./components/themed-toaster.js";
47
47
  export * from "./theme.js";
48
48
  export * from "./lib/year-month-filter.js";
49
+ export * from "./motion/tokens.js";
50
+ export * from "./components/animated-collapse.js";
51
+ export * from "./components/animated-count.js";
52
+ export * from "./components/animated-list-item.js";
53
+ export * from "./components/presence-swap.js";
54
+ export * from "./components/bouncing-dots-loader.js";
55
+ export * from "./components/progress-ring.js";
56
+ export * from "./components/confirm-delete-button.js";
57
+ export * from "./components/info-popover.js";
58
+ export * from "./components/empty-state.js";
59
+ export * from "./components/error-state.js";
60
+ export * from "./components/account-menu.js";
61
+ export * from "./components/rich-text-content.js";
62
+ export * from "./components/rich-markdown-content.js";
63
+ export * from "./components/rich-link-editor.js";
64
+ export * from "./components/open-in-browser-cta.js";
65
+ export * from "./components/external-browser-handoff.js";
66
+ export * from "./components/pwa-update-control.js";
67
+ export * from "./components/reaction-button.js";
68
+ export * from "./components/notification-panel.js";
69
+ export * from "./components/notification-bell.js";
70
+ export * from "./components/month-grid.js";
71
+ export * from "./components/month-calendar-header.js";
72
+ export * from "./components/file-upload.js";
73
+ export * from "./hooks/use-is-hydrated.js";
74
+ export * from "./hooks/use-minute-clock.js";
75
+ export * from "./hooks/use-viewer-time-zone.js";
76
+ export * from "./hooks/use-horizontal-swipe.js";
77
+ export * from "./hooks/use-rich-link-editor.js";
78
+ export * from "./hooks/use-month-transition-direction.js";
79
+ export * from "./hooks/use-prefers-reduced-motion.js";
80
+ export * from "./lib/browser-clipboard.js";
81
+ export * from "./lib/browser-navigation.js";
82
+ export * from "./lib/file-acceptance.js";
83
+ export * from "./lib/format-file-size.js";
84
+ export * from "./lib/fuzzy-search.js";
85
+ export * from "./lib/horizontal-swipe.js";
86
+ export * from "./lib/in-app-browser.js";
87
+ export * from "./lib/month-grid.js";
88
+ export * from "./lib/name-initials.js";
89
+ export { parseRichTextSegments, normalizeMarkdownUrl, normalizeAutolinkUrl } from "./lib/rich-text/link-markdown.js";
90
+ export { RICH_TEXT_SEGMENT_TYPE, RICH_LINK_KIND } from "./lib/rich-text/link-markdown-constants.js";
91
+ export type { RichTextSegment, RichLink } from "./lib/rich-text/link-markdown-types.js";
92
+ export * from "./lib/rich-text/rich-markdown.js";
package/dist/index.js CHANGED
@@ -45,3 +45,46 @@ export * from "./components/typing-animation.js";
45
45
  export * from "./components/themed-toaster.js";
46
46
  export * from "./theme.js";
47
47
  export * from "./lib/year-month-filter.js";
48
+ export * from "./motion/tokens.js";
49
+ export * from "./components/animated-collapse.js";
50
+ export * from "./components/animated-count.js";
51
+ export * from "./components/animated-list-item.js";
52
+ export * from "./components/presence-swap.js";
53
+ export * from "./components/bouncing-dots-loader.js";
54
+ export * from "./components/progress-ring.js";
55
+ export * from "./components/confirm-delete-button.js";
56
+ export * from "./components/info-popover.js";
57
+ export * from "./components/empty-state.js";
58
+ export * from "./components/error-state.js";
59
+ export * from "./components/account-menu.js";
60
+ export * from "./components/rich-text-content.js";
61
+ export * from "./components/rich-markdown-content.js";
62
+ export * from "./components/rich-link-editor.js";
63
+ export * from "./components/open-in-browser-cta.js";
64
+ export * from "./components/external-browser-handoff.js";
65
+ export * from "./components/pwa-update-control.js";
66
+ export * from "./components/reaction-button.js";
67
+ export * from "./components/notification-panel.js";
68
+ export * from "./components/notification-bell.js";
69
+ export * from "./components/month-grid.js";
70
+ export * from "./components/month-calendar-header.js";
71
+ export * from "./components/file-upload.js";
72
+ export * from "./hooks/use-is-hydrated.js";
73
+ export * from "./hooks/use-minute-clock.js";
74
+ export * from "./hooks/use-viewer-time-zone.js";
75
+ export * from "./hooks/use-horizontal-swipe.js";
76
+ export * from "./hooks/use-rich-link-editor.js";
77
+ export * from "./hooks/use-month-transition-direction.js";
78
+ export * from "./hooks/use-prefers-reduced-motion.js";
79
+ export * from "./lib/browser-clipboard.js";
80
+ export * from "./lib/browser-navigation.js";
81
+ export * from "./lib/file-acceptance.js";
82
+ export * from "./lib/format-file-size.js";
83
+ export * from "./lib/fuzzy-search.js";
84
+ export * from "./lib/horizontal-swipe.js";
85
+ export * from "./lib/in-app-browser.js";
86
+ export * from "./lib/month-grid.js";
87
+ export * from "./lib/name-initials.js";
88
+ export { parseRichTextSegments, normalizeMarkdownUrl, normalizeAutolinkUrl } from "./lib/rich-text/link-markdown.js";
89
+ export { RICH_TEXT_SEGMENT_TYPE, RICH_LINK_KIND } from "./lib/rich-text/link-markdown-constants.js";
90
+ export * from "./lib/rich-text/rich-markdown.js";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Browser clipboard adapter with feature detection. The async Clipboard API
3
+ * is preferred; when it is missing (older WebKit, insecure context) or
4
+ * rejects, a hidden textarea plus `document.execCommand("copy")` is tried,
5
+ * which WebKit still honours inside a user gesture.
6
+ */
7
+ /**
8
+ * Copies text to the clipboard.
9
+ *
10
+ * @param text - Text to copy.
11
+ * @returns True when the text reached the clipboard.
12
+ */
13
+ export declare function copyTextToClipboard(text: string): Promise<boolean>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Browser clipboard adapter with feature detection. The async Clipboard API
3
+ * is preferred; when it is missing (older WebKit, insecure context) or
4
+ * rejects, a hidden textarea plus `document.execCommand("copy")` is tried,
5
+ * which WebKit still honours inside a user gesture.
6
+ */
7
+ const FALLBACK_COPY_COMMAND = "copy";
8
+ const FALLBACK_TEXTAREA_TAG = "textarea";
9
+ const FALLBACK_READONLY_ATTRIBUTE = "readonly";
10
+ const OFFSCREEN_POSITION = "fixed";
11
+ const OFFSCREEN_OFFSET = "-9999px";
12
+ function copyWithSelectionFallback(text) {
13
+ if (typeof document.execCommand !== "function") {
14
+ return false;
15
+ }
16
+ const textarea = document.createElement(FALLBACK_TEXTAREA_TAG);
17
+ textarea.value = text;
18
+ textarea.setAttribute(FALLBACK_READONLY_ATTRIBUTE, "");
19
+ textarea.style.position = OFFSCREEN_POSITION;
20
+ textarea.style.top = OFFSCREEN_OFFSET;
21
+ document.body.appendChild(textarea);
22
+ textarea.select();
23
+ // iOS Safari needs an explicit range for read-only fields.
24
+ textarea.setSelectionRange(0, text.length);
25
+ try {
26
+ return document.execCommand(FALLBACK_COPY_COMMAND);
27
+ } catch {
28
+ // The legacy command can throw when copying is blocked; report failure.
29
+ return false;
30
+ } finally {
31
+ textarea.remove();
32
+ }
33
+ }
34
+ /**
35
+ * Copies text to the clipboard.
36
+ *
37
+ * @param text - Text to copy.
38
+ * @returns True when the text reached the clipboard.
39
+ */
40
+ export async function copyTextToClipboard(text) {
41
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
42
+ try {
43
+ await navigator.clipboard.writeText(text);
44
+ return true;
45
+ } catch {}
46
+ }
47
+ return copyWithSelectionFallback(text);
48
+ }