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,458 @@
1
+ /**
2
+ * Shared constants for the rich-text link layer.
3
+ *
4
+ * These values back both the read-only renderer (`RichTextContent`) and the
5
+ * rich link editor (`RichLinkEditor` / `useRichLinkEditor`). They are framework
6
+ * safe and free of business rules, so they live under `lib` and can be reused by
7
+ * any feature that needs autolinking plus `[text](url)` markdown support.
8
+ */
9
+ /** URL protocols accepted when normalizing a markdown link target. */
10
+ export const LINK_MARKDOWN_ALLOWED_PROTOCOL = {
11
+ http: "http:",
12
+ https: "https:"
13
+ };
14
+ /** Protocol prepended to bare domains that lack an explicit scheme. */
15
+ export const LINK_PROTOCOL_PREFIX = { default: "https://" };
16
+ /** Tokens used to assemble and recognize `[text](url)` markdown links. */
17
+ export const LINK_MARKDOWN_FORMAT = {
18
+ closeLabel: "]",
19
+ closeUrl: ")",
20
+ openLabel: "[",
21
+ openUrl: "](",
22
+ suppressedUrl: "#"
23
+ };
24
+ /**
25
+ * Parentheses that the `[text](url)` markdown URL group cannot represent
26
+ * unescaped, paired with their percent-encoded equivalents. The URL group only
27
+ * accepts balanced single-level `(...)` pairs, so any other paren must be
28
+ * encoded when serializing a link target; browsers decode `%28`/`%29` back to
29
+ * the literal character when the link is opened.
30
+ */
31
+ export const LINK_MARKDOWN_URL_PAREN = {
32
+ close: ")",
33
+ encodedClose: "%29",
34
+ encodedOpen: "%28",
35
+ open: "("
36
+ };
37
+ /** Patterns matching characters that must be escaped inside link label text. */
38
+ export const LINK_MARKDOWN_ESCAPE_PATTERN = {
39
+ backslash: /\\/g,
40
+ closeLabel: /\]/g,
41
+ lineBreak: /\n/g,
42
+ openLabel: /\[/g
43
+ };
44
+ /** Escape sequences paired with `LINK_MARKDOWN_ESCAPE_PATTERN`. */
45
+ export const LINK_MARKDOWN_ESCAPE_VALUE = {
46
+ backslash: "\\",
47
+ escapedBackslash: "\\\\",
48
+ escapedCloseLabel: "\\]",
49
+ escapedLineBreak: "\\n",
50
+ escapedOpenLabel: "\\[",
51
+ lineBreakToken: "n"
52
+ };
53
+ /** Discriminant for the parsed rich-text segments (plain text vs. link). */
54
+ export const RICH_TEXT_SEGMENT_TYPE = {
55
+ link: "link",
56
+ text: "text"
57
+ };
58
+ /** Whether an editor link is explicit (`[text](url)`) or a suppressed autolink. */
59
+ export const RICH_LINK_KIND = {
60
+ explicit: "explicit",
61
+ suppressed: "suppressed"
62
+ };
63
+ /** Origin of a preview link segment: auto-detected URL vs. explicit link. */
64
+ export const RICH_PREVIEW_LINK_SOURCE = {
65
+ automatic: "automatic",
66
+ explicit: "explicit"
67
+ };
68
+ /** Editing modes of the link popover. */
69
+ export const RICH_LINK_POPOVER_MODE = {
70
+ actions: "actions",
71
+ edit: "edit"
72
+ };
73
+ /**
74
+ * Regular expressions used to detect links in plain text.
75
+ *
76
+ * The URL character classes exclude both `)` and `]` so a URL wrapped in
77
+ * brackets or parentheses (`[https://example.com/path]`) stops at the closing
78
+ * delimiter instead of swallowing it into the link target. Balanced `(...)`
79
+ * inside the URL is still matched through the dedicated paren-balancing group.
80
+ */
81
+ export const LINK_PATTERN = {
82
+ bareDomain: /^(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}[^\s<>)\]]*$/,
83
+ containsWhitespace: /\s/,
84
+ bareUrl: /(?:https?:\/\/[^\s<>)\]]*(?:\([^\s<>()]*\)[^\s<>)\]]*)*|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}[^\s<>)\]]*(?:\([^\s<>()]*\)[^\s<>)\]]*)*)/g,
85
+ hostBoundary: /[/?#:]/,
86
+ markdown: /\[((?:\\[\s\S]|[^\]\\])+)\]\(((?:[^()\s]+|\([^()\s]*\))+)\)/g,
87
+ protocolPrefix: /^https?:\/\//i,
88
+ trailingPunctuation: /[.,!?;:]+$/,
89
+ whitespace: /^\s+$/
90
+ };
91
+ /**
92
+ * Public top-level domains recognized when auto-linking a scheme-less bare
93
+ * domain found in prose. `new URL()` accepts any syntactically valid hostname,
94
+ * so without this allowlist Spanish abbreviations like `EE.UU.` or
95
+ * end-of-sentence transitions like `China.Por` would resolve to bogus hosts and
96
+ * become clickable links. It covers the common gTLDs plus every assigned
97
+ * two-letter ccTLD; unknown suffixes such as `uu` or `por` are intentionally
98
+ * absent. This gates only the automatic detection path: explicit `http(s)://`
99
+ * URLs and links the user pastes deliberately bypass it and stay permissive.
100
+ */
101
+ export const KNOWN_TOP_LEVEL_DOMAINS = new Set([
102
+ "com",
103
+ "net",
104
+ "org",
105
+ "info",
106
+ "biz",
107
+ "name",
108
+ "pro",
109
+ "int",
110
+ "edu",
111
+ "gov",
112
+ "mil",
113
+ "app",
114
+ "dev",
115
+ "io",
116
+ "co",
117
+ "ai",
118
+ "xyz",
119
+ "online",
120
+ "site",
121
+ "tech",
122
+ "store",
123
+ "shop",
124
+ "blog",
125
+ "me",
126
+ "tv",
127
+ "cc",
128
+ "live",
129
+ "life",
130
+ "world",
131
+ "fun",
132
+ "space",
133
+ "website",
134
+ "page",
135
+ "web",
136
+ "link",
137
+ "click",
138
+ "news",
139
+ "media",
140
+ "studio",
141
+ "design",
142
+ "art",
143
+ "email",
144
+ "cloud",
145
+ "digital",
146
+ "network",
147
+ "agency",
148
+ "company",
149
+ "group",
150
+ "team",
151
+ "work",
152
+ "works",
153
+ "today",
154
+ "ltd",
155
+ "inc",
156
+ "llc",
157
+ "academy",
158
+ "school",
159
+ "courses",
160
+ "education",
161
+ "events",
162
+ "social",
163
+ "chat",
164
+ "video",
165
+ "photo",
166
+ "photos",
167
+ "games",
168
+ "game",
169
+ "finance",
170
+ "money",
171
+ "health",
172
+ "fit",
173
+ "run",
174
+ "travel",
175
+ "tours",
176
+ "global",
177
+ "international",
178
+ "solutions",
179
+ "systems",
180
+ "consulting",
181
+ "marketing",
182
+ "software",
183
+ "host",
184
+ "hosting",
185
+ "wiki",
186
+ "ac",
187
+ "ad",
188
+ "ae",
189
+ "af",
190
+ "ag",
191
+ "al",
192
+ "am",
193
+ "ao",
194
+ "aq",
195
+ "ar",
196
+ "as",
197
+ "at",
198
+ "au",
199
+ "aw",
200
+ "ax",
201
+ "az",
202
+ "ba",
203
+ "bb",
204
+ "bd",
205
+ "be",
206
+ "bf",
207
+ "bg",
208
+ "bh",
209
+ "bi",
210
+ "bj",
211
+ "bm",
212
+ "bn",
213
+ "bo",
214
+ "br",
215
+ "bs",
216
+ "bt",
217
+ "bw",
218
+ "by",
219
+ "bz",
220
+ "ca",
221
+ "cd",
222
+ "cf",
223
+ "cg",
224
+ "ch",
225
+ "ci",
226
+ "ck",
227
+ "cl",
228
+ "cm",
229
+ "cn",
230
+ "cr",
231
+ "cu",
232
+ "cv",
233
+ "cw",
234
+ "cx",
235
+ "cy",
236
+ "cz",
237
+ "de",
238
+ "dj",
239
+ "dk",
240
+ "dm",
241
+ "do",
242
+ "dz",
243
+ "ec",
244
+ "ee",
245
+ "eg",
246
+ "er",
247
+ "es",
248
+ "et",
249
+ "eu",
250
+ "fi",
251
+ "fj",
252
+ "fk",
253
+ "fm",
254
+ "fo",
255
+ "fr",
256
+ "ga",
257
+ "gd",
258
+ "ge",
259
+ "gf",
260
+ "gg",
261
+ "gh",
262
+ "gi",
263
+ "gl",
264
+ "gm",
265
+ "gn",
266
+ "gp",
267
+ "gq",
268
+ "gr",
269
+ "gs",
270
+ "gt",
271
+ "gu",
272
+ "gw",
273
+ "gy",
274
+ "hk",
275
+ "hm",
276
+ "hn",
277
+ "hr",
278
+ "ht",
279
+ "hu",
280
+ "id",
281
+ "ie",
282
+ "il",
283
+ "im",
284
+ "in",
285
+ "iq",
286
+ "ir",
287
+ "is",
288
+ "it",
289
+ "je",
290
+ "jm",
291
+ "jo",
292
+ "jp",
293
+ "ke",
294
+ "kg",
295
+ "kh",
296
+ "ki",
297
+ "km",
298
+ "kn",
299
+ "kp",
300
+ "kr",
301
+ "kw",
302
+ "ky",
303
+ "kz",
304
+ "la",
305
+ "lb",
306
+ "lc",
307
+ "li",
308
+ "lk",
309
+ "lr",
310
+ "ls",
311
+ "lt",
312
+ "lu",
313
+ "lv",
314
+ "ly",
315
+ "ma",
316
+ "mc",
317
+ "md",
318
+ "mg",
319
+ "mh",
320
+ "mk",
321
+ "ml",
322
+ "mm",
323
+ "mn",
324
+ "mo",
325
+ "mp",
326
+ "mq",
327
+ "mr",
328
+ "ms",
329
+ "mt",
330
+ "mu",
331
+ "mv",
332
+ "mw",
333
+ "mx",
334
+ "my",
335
+ "mz",
336
+ "na",
337
+ "nc",
338
+ "ne",
339
+ "nf",
340
+ "ng",
341
+ "ni",
342
+ "nl",
343
+ "no",
344
+ "np",
345
+ "nr",
346
+ "nu",
347
+ "nz",
348
+ "om",
349
+ "pa",
350
+ "pe",
351
+ "pf",
352
+ "pg",
353
+ "ph",
354
+ "pk",
355
+ "pl",
356
+ "pm",
357
+ "pn",
358
+ "pr",
359
+ "ps",
360
+ "pt",
361
+ "pw",
362
+ "py",
363
+ "qa",
364
+ "re",
365
+ "ro",
366
+ "rs",
367
+ "ru",
368
+ "rw",
369
+ "sa",
370
+ "sb",
371
+ "sc",
372
+ "sd",
373
+ "se",
374
+ "sg",
375
+ "sh",
376
+ "si",
377
+ "sk",
378
+ "sl",
379
+ "sm",
380
+ "sn",
381
+ "so",
382
+ "sr",
383
+ "ss",
384
+ "st",
385
+ "sv",
386
+ "sx",
387
+ "sy",
388
+ "sz",
389
+ "tc",
390
+ "td",
391
+ "tf",
392
+ "tg",
393
+ "th",
394
+ "tj",
395
+ "tk",
396
+ "tl",
397
+ "tm",
398
+ "tn",
399
+ "to",
400
+ "tr",
401
+ "tt",
402
+ "tw",
403
+ "tz",
404
+ "ua",
405
+ "ug",
406
+ "uk",
407
+ "us",
408
+ "uy",
409
+ "uz",
410
+ "va",
411
+ "vc",
412
+ "ve",
413
+ "vg",
414
+ "vi",
415
+ "vn",
416
+ "vu",
417
+ "wf",
418
+ "ws",
419
+ "ye",
420
+ "yt",
421
+ "za",
422
+ "zm",
423
+ "zw"
424
+ ]);
425
+ /** Capture group indexes of `LINK_PATTERN.markdown`. */
426
+ export const LINK_MARKDOWN_MATCH_GROUP = {
427
+ text: 1,
428
+ url: 2
429
+ };
430
+ /** Sentinel returned by the text diff when no change is found. */
431
+ export const TEXT_DIFF_FALLBACK_INDEX = { notFound: -1 };
432
+ /** Separator used to build a stable key for preview link segments. */
433
+ export const PREVIEW_LINK_KEY_SEPARATOR = { value: ":" };
434
+ /** Clipboard data type read on paste inside the editor. */
435
+ export const RICH_TEXT_CLIPBOARD_DATA_TYPE = { plainText: "text/plain" };
436
+ /** `InputEvent.inputType` values handled by the editor's `beforeInput`. */
437
+ export const RICH_TEXT_EDITOR_INPUT_TYPE = {
438
+ deleteContentBackward: "deleteContentBackward",
439
+ deleteContentForward: "deleteContentForward",
440
+ insertLineBreak: "insertLineBreak",
441
+ insertParagraph: "insertParagraph",
442
+ insertText: "insertText"
443
+ };
444
+ /** Keyboard keys handled by the editor's `keydown`. */
445
+ export const RICH_TEXT_EDITOR_KEY = {
446
+ backspace: "Backspace",
447
+ delete: "Delete",
448
+ enter: "Enter"
449
+ };
450
+ /** Direction of a word-wise deletion. */
451
+ export const RICH_TEXT_EDITOR_WORD_DIRECTION = {
452
+ backward: "backward",
453
+ forward: "forward"
454
+ };
455
+ /** Length of a single editable character. */
456
+ export const RICH_TEXT_EDITOR_KEY_LENGTH = { character: 1 };
457
+ /** Literal text tokens used by the editor. */
458
+ export const RICH_TEXT_EDITOR_TEXT = { lineBreak: "\n" };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Shared types for the rich-text link layer.
3
+ */
4
+ import type { RICH_LINK_KIND, RICH_PREVIEW_LINK_SOURCE, RICH_TEXT_SEGMENT_TYPE } from "./link-markdown-constants.js";
5
+ /** A parsed piece of rendered content: either plain text or a link. */
6
+ export type RichTextSegment = {
7
+ text: string;
8
+ type: typeof RICH_TEXT_SEGMENT_TYPE.text;
9
+ } | {
10
+ text: string;
11
+ type: typeof RICH_TEXT_SEGMENT_TYPE.link;
12
+ url: string;
13
+ };
14
+ /**
15
+ * A link tracked by the editor over its plain-text content. Explicit links carry
16
+ * a target URL; suppressed links mark an auto-detected URL the author removed.
17
+ */
18
+ export type RichLink = {
19
+ end: number;
20
+ id: string;
21
+ isSynced: boolean;
22
+ kind: typeof RICH_LINK_KIND.explicit;
23
+ start: number;
24
+ url: string;
25
+ } | {
26
+ end: number;
27
+ id: string;
28
+ kind: typeof RICH_LINK_KIND.suppressed;
29
+ start: number;
30
+ };
31
+ /** A segment of the editor preview: plain text or an interactive link. */
32
+ export type RichPreviewSegment = {
33
+ text: string;
34
+ type: typeof RICH_TEXT_SEGMENT_TYPE.text;
35
+ } | {
36
+ end: number;
37
+ id?: string;
38
+ key: string;
39
+ source: typeof RICH_PREVIEW_LINK_SOURCE.automatic | typeof RICH_PREVIEW_LINK_SOURCE.explicit;
40
+ start: number;
41
+ text: string;
42
+ type: typeof RICH_TEXT_SEGMENT_TYPE.link;
43
+ url: string;
44
+ };
45
+ /** The link variant of a preview segment, used when editing a link. */
46
+ export type ActiveRichPreviewLink = Extract<RichPreviewSegment, {
47
+ type: typeof RICH_TEXT_SEGMENT_TYPE.link;
48
+ }>;
49
+ /** A character offset range within the editor's plain-text content. */
50
+ export type RichTextSelectionRange = {
51
+ end: number;
52
+ start: number;
53
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Pure helpers for the rich-text link layer: URL normalization, markdown
3
+ * link (de)serialization, plain-text autolink parsing, and editor link
4
+ * bookkeeping. None of these touch the DOM, so they are unit-testable in
5
+ * isolation and shared by the renderer, the editor hook, and persistence.
6
+ */
7
+ import { RICH_PREVIEW_LINK_SOURCE, RICH_TEXT_EDITOR_WORD_DIRECTION } from "./link-markdown-constants.js";
8
+ import type { RichLink, RichPreviewSegment, RichTextSegment, RichTextSelectionRange } from "./link-markdown-types.js";
9
+ /**
10
+ * Normalizes a raw markdown URL into a safe absolute `http(s)` URL, or `null`
11
+ * when it is not a usable web link. Bare domains gain an `https://` prefix.
12
+ *
13
+ * A candidate that contains whitespace is returned as its parsed `href`, which
14
+ * percent-encodes those characters. The renderer's `LINK_PATTERN.markdown` URL
15
+ * group forbids whitespace, so persisting the raw candidate would serialize a
16
+ * `[text](url with space)` link that cannot be read back and renders as broken
17
+ * markdown. Whitespace-free candidates are returned verbatim to preserve their
18
+ * exact form (no trailing slash, stable synchronization checks).
19
+ *
20
+ * A scheme-less candidate that parses into userinfo (`john.doe@example.com`) is
21
+ * rejected: the `bareDomain` pattern allows `@` in its trailing class, so an
22
+ * email or literal slips through and prepending `https://` would yield
23
+ * `https://john.doe@example.com`, where the browser reads `john.doe` as
24
+ * credentials for `example.com`. That turns the value into a misleading
25
+ * outbound link, against the convention that email addresses are not linked.
26
+ * The check uses the parsed `username`/`password` rather than a naive `@` scan
27
+ * so a legitimate `@` past the authority (a path or query) still links.
28
+ */
29
+ export declare function normalizeMarkdownUrl(rawUrl: string | null | undefined): string | null;
30
+ /**
31
+ * Extracts the top-level domain (the last dot-separated label of the host) from
32
+ * a bare or scheme-prefixed candidate, lowercased, or `null` when the candidate
33
+ * has no host label past a dot. The host ends at the first path, query,
34
+ * fragment, or port boundary so a path segment is never mistaken for the TLD.
35
+ */
36
+ export declare function getCandidateTopLevelDomain(candidate: string): string | null;
37
+ /**
38
+ * Normalizes a candidate for the automatic detection path. Scheme-prefixed URLs
39
+ * are trusted and normalized as-is; a scheme-less bare domain is only accepted
40
+ * when its TLD is a recognized public suffix, so prose abbreviations such as
41
+ * `EE.UU.` or `China.Por` stay plain text instead of resolving to bogus hosts.
42
+ * Explicit paste-as-link flows keep using `normalizeMarkdownUrl` directly and
43
+ * stay permissive.
44
+ */
45
+ export declare function normalizeAutolinkUrl(candidate: string): string | null;
46
+ /** Strips the `http(s)://` prefix from a value for synchronization checks. */
47
+ export declare function removeLinkProtocolPrefix(value: string): string;
48
+ /** Whether a link's visible text matches its URL ignoring the protocol. */
49
+ export declare function isLinkSynchronized(text: string, url: string): boolean;
50
+ /** Returns the protocol prefix of a URL, defaulting to `https://`. */
51
+ export declare function getLinkProtocolPrefix(url: string): string;
52
+ /**
53
+ * Rebuilds the URL of a synchronized link after its visible text changed, so a
54
+ * link whose label is the URL keeps tracking the edited text.
55
+ */
56
+ export declare function getSynchronizedLinkUrl(text: string, currentUrl: string): string | null;
57
+ /** Whether the value contains only whitespace. */
58
+ export declare function isWhitespaceOnly(value: string): boolean;
59
+ /**
60
+ * Computes the range a word-wise deletion (Ctrl/Alt+Backspace/Delete) should
61
+ * remove from a collapsed caret, mirroring native word deletion.
62
+ */
63
+ export declare function getWordDeletionRange(input: {
64
+ direction: typeof RICH_TEXT_EDITOR_WORD_DIRECTION.backward | typeof RICH_TEXT_EDITOR_WORD_DIRECTION.forward;
65
+ selectionRange: RichTextSelectionRange;
66
+ text: string;
67
+ }): RichTextSelectionRange;
68
+ /**
69
+ * Percent-encodes the parentheses a URL cannot keep literal inside a
70
+ * `[text](url)` target, so an explicit link whose URL contains an unmatched or
71
+ * nested `(`/`)` (for example `https://example.com/a)b`) serializes to markdown
72
+ * that `LINK_PATTERN.markdown` reads back as the same link instead of truncating
73
+ * the target at the stray paren and spilling the rest into plain text.
74
+ *
75
+ * The URL group only matches balanced single-level `(...)` pairs, so those are
76
+ * preserved verbatim (keeping links like `.../Foo_(bar)` intact); every other
77
+ * paren becomes `%28`/`%29`, which is decoded back to the literal character when
78
+ * the link is opened.
79
+ */
80
+ export declare function escapeMarkdownLinkUrl(url: string): string;
81
+ /** Builds a `[text](url)` markdown link, escaping the label and the URL. */
82
+ export declare function buildMarkdownLinkFromSelection(text: string, url: string): string;
83
+ /** Escapes characters that would break a markdown link label. */
84
+ export declare function escapeMarkdownLinkText(text: string): string;
85
+ /** Reverses `escapeMarkdownLinkText`, restoring line breaks and literals. */
86
+ export declare function unescapeMarkdownLinkText(text: string): string;
87
+ /** Builds a stable React key for a preview link segment. */
88
+ export declare function buildPreviewLinkKey(segment: {
89
+ id?: string;
90
+ source: typeof RICH_PREVIEW_LINK_SOURCE.automatic | typeof RICH_PREVIEW_LINK_SOURCE.explicit;
91
+ start: number;
92
+ url: string;
93
+ }): string;
94
+ /** Creates a plain-text rendered segment. */
95
+ export declare function createTextSegment(text: string): RichTextSegment;
96
+ /** Creates a link rendered segment. */
97
+ export declare function createLinkSegment(text: string, url: string): RichTextSegment;
98
+ /** Creates a plain-text editor preview segment. */
99
+ export declare function createTextPreviewSegment(text: string): RichPreviewSegment;
100
+ /** Creates a link editor preview segment with a stable key. */
101
+ export declare function createLinkPreviewSegment(input: {
102
+ end: number;
103
+ id?: string;
104
+ source: typeof RICH_PREVIEW_LINK_SOURCE.automatic | typeof RICH_PREVIEW_LINK_SOURCE.explicit;
105
+ start: number;
106
+ text: string;
107
+ url: string;
108
+ }): RichPreviewSegment;
109
+ /** Splits a matched bare URL from any trailing sentence punctuation. */
110
+ export declare function splitBareUrlMatch(matchedUrl: string): {
111
+ trailingText: string;
112
+ urlText: string;
113
+ };
114
+ /** Whether two `[start, end)` ranges overlap. */
115
+ export declare function rangesOverlap(firstRange: {
116
+ end: number;
117
+ start: number;
118
+ }, secondRange: {
119
+ end: number;
120
+ start: number;
121
+ }): boolean;
122
+ /** Whether a matched bare URL is actually part of an email address. */
123
+ export declare function isBareUrlMatchInsideEmail(input: {
124
+ content: string;
125
+ matchedIndex: number;
126
+ matchedUrl: string;
127
+ }): boolean;
128
+ /** Parses plain text into rendered segments, auto-linking bare URLs. */
129
+ export declare function parseBareUrlSegments(content: string): RichTextSegment[];
130
+ /** Parses plain text into editor preview segments, auto-linking bare URLs. */
131
+ export declare function parseBareUrlPreviewSegments(content: string, offset: number, suppressedLinks: RichLink[]): RichPreviewSegment[];
132
+ /**
133
+ * Parses the editor's plain-text content plus its tracked links into preview
134
+ * segments, layering explicit links over auto-detected bare URLs.
135
+ */
136
+ export declare function parsePreviewSegments(content: string, links: RichLink[]): RichPreviewSegment[];
137
+ /**
138
+ * Whether a markdown link whose URL is not a safe web link is a genuine
139
+ * suppressed autolink (`[some-url.com](#)`) produced by the editor, rather than
140
+ * literal text that merely looks like a markdown link (`[PDF](pendiente)`).
141
+ *
142
+ * Suppression only applies when the label itself is a normalizable URL, which
143
+ * mirrors `serializeEditorContent`. Any other `[text](url)` whose URL cannot be
144
+ * normalized is treated as authored plain text and preserved verbatim.
145
+ */
146
+ export declare function isSuppressedAutolinkMarkdown(linkText: string, linkUrl: string): boolean;
147
+ /**
148
+ * Parses persisted content (markdown links plus bare URLs) into rendered
149
+ * segments for read-only display.
150
+ */
151
+ export declare function parseRichTextSegments(content: string): RichTextSegment[];
152
+ /**
153
+ * Converts persisted markdown into the editor's plain-text content plus the
154
+ * tracked links (explicit and suppressed) needed to re-render it for editing.
155
+ */
156
+ export declare function deserializeMarkdownForEditor(content: string): {
157
+ content: string;
158
+ links: RichLink[];
159
+ };
160
+ /**
161
+ * Serializes the editor's plain-text content plus tracked links back into
162
+ * persisted markdown, emitting `[text](url)` for explicit links and
163
+ * `[text](#)` for suppressed auto-detected URLs.
164
+ */
165
+ export declare function serializeEditorContent(content: string, links: RichLink[]): string;
166
+ /**
167
+ * Computes the minimal changed range between two strings: the common prefix
168
+ * end (`start`), the changed-region ends in each string, and the length delta.
169
+ */
170
+ export declare function getTextDiff(input: {
171
+ nextText: string;
172
+ previousText: string;
173
+ }): {
174
+ delta: number;
175
+ endInNextText: number;
176
+ endInPreviousText: number;
177
+ start: number;
178
+ };
179
+ /**
180
+ * Adjusts tracked link ranges after the editor's plain text changed, shifting,
181
+ * shrinking, or dropping links and keeping synchronized links in step with
182
+ * their visible text.
183
+ *
184
+ * A suppressed autolink is dropped once its visible text is no longer a
185
+ * normalizable URL, mirroring `isSuppressedAutolinkMarkdown`: with nothing left
186
+ * to suppress, the edited text serializes as plain text instead of `[text](#)`,
187
+ * which would otherwise be rendered back as literal markdown.
188
+ */
189
+ export declare function getLinksAfterTextChange(input: {
190
+ links: RichLink[];
191
+ nextText: string;
192
+ previousText: string;
193
+ }): RichLink[];