@rtif-sdk/web 1.0.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 (215) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/block-drag-handler.d.ts +189 -0
  4. package/dist/block-drag-handler.d.ts.map +1 -0
  5. package/dist/block-drag-handler.js +745 -0
  6. package/dist/block-drag-handler.js.map +1 -0
  7. package/dist/block-renderer.d.ts +402 -0
  8. package/dist/block-renderer.d.ts.map +1 -0
  9. package/dist/block-renderer.js +424 -0
  10. package/dist/block-renderer.js.map +1 -0
  11. package/dist/clipboard.d.ts +178 -0
  12. package/dist/clipboard.d.ts.map +1 -0
  13. package/dist/clipboard.js +432 -0
  14. package/dist/clipboard.js.map +1 -0
  15. package/dist/command-bus.d.ts +113 -0
  16. package/dist/command-bus.d.ts.map +1 -0
  17. package/dist/command-bus.js +70 -0
  18. package/dist/command-bus.js.map +1 -0
  19. package/dist/composition.d.ts +220 -0
  20. package/dist/composition.d.ts.map +1 -0
  21. package/dist/composition.js +271 -0
  22. package/dist/composition.js.map +1 -0
  23. package/dist/content-extraction.d.ts +69 -0
  24. package/dist/content-extraction.d.ts.map +1 -0
  25. package/dist/content-extraction.js +228 -0
  26. package/dist/content-extraction.js.map +1 -0
  27. package/dist/content-handler-file.d.ts +40 -0
  28. package/dist/content-handler-file.d.ts.map +1 -0
  29. package/dist/content-handler-file.js +91 -0
  30. package/dist/content-handler-file.js.map +1 -0
  31. package/dist/content-handler-image.d.ts +82 -0
  32. package/dist/content-handler-image.d.ts.map +1 -0
  33. package/dist/content-handler-image.js +120 -0
  34. package/dist/content-handler-image.js.map +1 -0
  35. package/dist/content-handler-url.d.ts +129 -0
  36. package/dist/content-handler-url.d.ts.map +1 -0
  37. package/dist/content-handler-url.js +244 -0
  38. package/dist/content-handler-url.js.map +1 -0
  39. package/dist/content-handlers.d.ts +67 -0
  40. package/dist/content-handlers.d.ts.map +1 -0
  41. package/dist/content-handlers.js +263 -0
  42. package/dist/content-handlers.js.map +1 -0
  43. package/dist/content-pipeline.d.ts +383 -0
  44. package/dist/content-pipeline.d.ts.map +1 -0
  45. package/dist/content-pipeline.js +232 -0
  46. package/dist/content-pipeline.js.map +1 -0
  47. package/dist/cursor-nav.d.ts +149 -0
  48. package/dist/cursor-nav.d.ts.map +1 -0
  49. package/dist/cursor-nav.js +230 -0
  50. package/dist/cursor-nav.js.map +1 -0
  51. package/dist/cursor-rect.d.ts +65 -0
  52. package/dist/cursor-rect.d.ts.map +1 -0
  53. package/dist/cursor-rect.js +98 -0
  54. package/dist/cursor-rect.js.map +1 -0
  55. package/dist/drop-indicator.d.ts +108 -0
  56. package/dist/drop-indicator.d.ts.map +1 -0
  57. package/dist/drop-indicator.js +236 -0
  58. package/dist/drop-indicator.js.map +1 -0
  59. package/dist/editor.d.ts +41 -0
  60. package/dist/editor.d.ts.map +1 -0
  61. package/dist/editor.js +710 -0
  62. package/dist/editor.js.map +1 -0
  63. package/dist/floating-toolbar.d.ts +93 -0
  64. package/dist/floating-toolbar.d.ts.map +1 -0
  65. package/dist/floating-toolbar.js +159 -0
  66. package/dist/floating-toolbar.js.map +1 -0
  67. package/dist/index.d.ts +62 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +119 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/input-bridge.d.ts +273 -0
  72. package/dist/input-bridge.d.ts.map +1 -0
  73. package/dist/input-bridge.js +884 -0
  74. package/dist/input-bridge.js.map +1 -0
  75. package/dist/link-popover.d.ts +38 -0
  76. package/dist/link-popover.d.ts.map +1 -0
  77. package/dist/link-popover.js +278 -0
  78. package/dist/link-popover.js.map +1 -0
  79. package/dist/mark-renderer.d.ts +275 -0
  80. package/dist/mark-renderer.d.ts.map +1 -0
  81. package/dist/mark-renderer.js +210 -0
  82. package/dist/mark-renderer.js.map +1 -0
  83. package/dist/perf.d.ts +145 -0
  84. package/dist/perf.d.ts.map +1 -0
  85. package/dist/perf.js +260 -0
  86. package/dist/perf.js.map +1 -0
  87. package/dist/plugin-kit.d.ts +265 -0
  88. package/dist/plugin-kit.d.ts.map +1 -0
  89. package/dist/plugin-kit.js +234 -0
  90. package/dist/plugin-kit.js.map +1 -0
  91. package/dist/plugins/alignment-plugin.d.ts +68 -0
  92. package/dist/plugins/alignment-plugin.d.ts.map +1 -0
  93. package/dist/plugins/alignment-plugin.js +98 -0
  94. package/dist/plugins/alignment-plugin.js.map +1 -0
  95. package/dist/plugins/block-utils.d.ts +113 -0
  96. package/dist/plugins/block-utils.d.ts.map +1 -0
  97. package/dist/plugins/block-utils.js +191 -0
  98. package/dist/plugins/block-utils.js.map +1 -0
  99. package/dist/plugins/blockquote-plugin.d.ts +39 -0
  100. package/dist/plugins/blockquote-plugin.d.ts.map +1 -0
  101. package/dist/plugins/blockquote-plugin.js +88 -0
  102. package/dist/plugins/blockquote-plugin.js.map +1 -0
  103. package/dist/plugins/bold-plugin.d.ts +37 -0
  104. package/dist/plugins/bold-plugin.d.ts.map +1 -0
  105. package/dist/plugins/bold-plugin.js +48 -0
  106. package/dist/plugins/bold-plugin.js.map +1 -0
  107. package/dist/plugins/callout-plugin.d.ts +100 -0
  108. package/dist/plugins/callout-plugin.d.ts.map +1 -0
  109. package/dist/plugins/callout-plugin.js +200 -0
  110. package/dist/plugins/callout-plugin.js.map +1 -0
  111. package/dist/plugins/code-block-plugin.d.ts +62 -0
  112. package/dist/plugins/code-block-plugin.d.ts.map +1 -0
  113. package/dist/plugins/code-block-plugin.js +176 -0
  114. package/dist/plugins/code-block-plugin.js.map +1 -0
  115. package/dist/plugins/code-plugin.d.ts +37 -0
  116. package/dist/plugins/code-plugin.d.ts.map +1 -0
  117. package/dist/plugins/code-plugin.js +48 -0
  118. package/dist/plugins/code-plugin.js.map +1 -0
  119. package/dist/plugins/embed-plugin.d.ts +90 -0
  120. package/dist/plugins/embed-plugin.d.ts.map +1 -0
  121. package/dist/plugins/embed-plugin.js +147 -0
  122. package/dist/plugins/embed-plugin.js.map +1 -0
  123. package/dist/plugins/font-family-plugin.d.ts +58 -0
  124. package/dist/plugins/font-family-plugin.d.ts.map +1 -0
  125. package/dist/plugins/font-family-plugin.js +57 -0
  126. package/dist/plugins/font-family-plugin.js.map +1 -0
  127. package/dist/plugins/font-size-plugin.d.ts +57 -0
  128. package/dist/plugins/font-size-plugin.d.ts.map +1 -0
  129. package/dist/plugins/font-size-plugin.js +56 -0
  130. package/dist/plugins/font-size-plugin.js.map +1 -0
  131. package/dist/plugins/heading-plugin.d.ts +52 -0
  132. package/dist/plugins/heading-plugin.d.ts.map +1 -0
  133. package/dist/plugins/heading-plugin.js +114 -0
  134. package/dist/plugins/heading-plugin.js.map +1 -0
  135. package/dist/plugins/hr-plugin.d.ts +33 -0
  136. package/dist/plugins/hr-plugin.d.ts.map +1 -0
  137. package/dist/plugins/hr-plugin.js +75 -0
  138. package/dist/plugins/hr-plugin.js.map +1 -0
  139. package/dist/plugins/image-plugin.d.ts +115 -0
  140. package/dist/plugins/image-plugin.d.ts.map +1 -0
  141. package/dist/plugins/image-plugin.js +199 -0
  142. package/dist/plugins/image-plugin.js.map +1 -0
  143. package/dist/plugins/indent-plugin.d.ts +62 -0
  144. package/dist/plugins/indent-plugin.d.ts.map +1 -0
  145. package/dist/plugins/indent-plugin.js +128 -0
  146. package/dist/plugins/indent-plugin.js.map +1 -0
  147. package/dist/plugins/index.d.ts +45 -0
  148. package/dist/plugins/index.d.ts.map +1 -0
  149. package/dist/plugins/index.js +42 -0
  150. package/dist/plugins/index.js.map +1 -0
  151. package/dist/plugins/italic-plugin.d.ts +37 -0
  152. package/dist/plugins/italic-plugin.d.ts.map +1 -0
  153. package/dist/plugins/italic-plugin.js +48 -0
  154. package/dist/plugins/italic-plugin.js.map +1 -0
  155. package/dist/plugins/link-plugin.d.ts +129 -0
  156. package/dist/plugins/link-plugin.d.ts.map +1 -0
  157. package/dist/plugins/link-plugin.js +212 -0
  158. package/dist/plugins/link-plugin.js.map +1 -0
  159. package/dist/plugins/list-plugin.d.ts +53 -0
  160. package/dist/plugins/list-plugin.d.ts.map +1 -0
  161. package/dist/plugins/list-plugin.js +309 -0
  162. package/dist/plugins/list-plugin.js.map +1 -0
  163. package/dist/plugins/mark-utils.d.ts +173 -0
  164. package/dist/plugins/mark-utils.d.ts.map +1 -0
  165. package/dist/plugins/mark-utils.js +425 -0
  166. package/dist/plugins/mark-utils.js.map +1 -0
  167. package/dist/plugins/mention-plugin.d.ts +191 -0
  168. package/dist/plugins/mention-plugin.d.ts.map +1 -0
  169. package/dist/plugins/mention-plugin.js +295 -0
  170. package/dist/plugins/mention-plugin.js.map +1 -0
  171. package/dist/plugins/strikethrough-plugin.d.ts +37 -0
  172. package/dist/plugins/strikethrough-plugin.d.ts.map +1 -0
  173. package/dist/plugins/strikethrough-plugin.js +48 -0
  174. package/dist/plugins/strikethrough-plugin.js.map +1 -0
  175. package/dist/plugins/text-color-plugin.d.ts +57 -0
  176. package/dist/plugins/text-color-plugin.d.ts.map +1 -0
  177. package/dist/plugins/text-color-plugin.js +56 -0
  178. package/dist/plugins/text-color-plugin.js.map +1 -0
  179. package/dist/plugins/underline-plugin.d.ts +37 -0
  180. package/dist/plugins/underline-plugin.d.ts.map +1 -0
  181. package/dist/plugins/underline-plugin.js +48 -0
  182. package/dist/plugins/underline-plugin.js.map +1 -0
  183. package/dist/presets.d.ts +95 -0
  184. package/dist/presets.d.ts.map +1 -0
  185. package/dist/presets.js +159 -0
  186. package/dist/presets.js.map +1 -0
  187. package/dist/renderer.d.ts +125 -0
  188. package/dist/renderer.d.ts.map +1 -0
  189. package/dist/renderer.js +415 -0
  190. package/dist/renderer.js.map +1 -0
  191. package/dist/scroll-to-cursor.d.ts +25 -0
  192. package/dist/scroll-to-cursor.d.ts.map +1 -0
  193. package/dist/scroll-to-cursor.js +59 -0
  194. package/dist/scroll-to-cursor.js.map +1 -0
  195. package/dist/selection-sync.d.ts +159 -0
  196. package/dist/selection-sync.d.ts.map +1 -0
  197. package/dist/selection-sync.js +527 -0
  198. package/dist/selection-sync.js.map +1 -0
  199. package/dist/shortcut-handler.d.ts +98 -0
  200. package/dist/shortcut-handler.d.ts.map +1 -0
  201. package/dist/shortcut-handler.js +155 -0
  202. package/dist/shortcut-handler.js.map +1 -0
  203. package/dist/toolbar.d.ts +103 -0
  204. package/dist/toolbar.d.ts.map +1 -0
  205. package/dist/toolbar.js +134 -0
  206. package/dist/toolbar.js.map +1 -0
  207. package/dist/trigger-manager.d.ts +205 -0
  208. package/dist/trigger-manager.d.ts.map +1 -0
  209. package/dist/trigger-manager.js +466 -0
  210. package/dist/trigger-manager.js.map +1 -0
  211. package/dist/types.d.ts +216 -0
  212. package/dist/types.d.ts.map +1 -0
  213. package/dist/types.js +2 -0
  214. package/dist/types.js.map +1 -0
  215. package/package.json +30 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * URL content handler — processes pasted URLs into RTIF links or embed blocks.
3
+ *
4
+ * Three paths:
5
+ * - **Path A**: Selection exists → wrap selected text as a link with the pasted URL
6
+ * - **Path B**: No selection + autoEmbed + embeddable URL → create an embed block
7
+ * - **Path C**: No selection + plain URL → insert URL as linked text
8
+ *
9
+ * Priority: -95 (runs after HTML, before plain text). Uses `canHandle` to
10
+ * distinguish URLs from regular text — non-URLs fall through to the plain
11
+ * text handler.
12
+ *
13
+ * @module
14
+ */
15
+ import type { ContentHandler } from './content-pipeline.js';
16
+ /**
17
+ * An embed URL matcher — maps a URL pattern to an embed type.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const matcher: EmbedUrlMatcher = {
22
+ * pattern: /^https?:\/\/(?:www\.)?youtube\.com\/watch\?v=[\w-]+/,
23
+ * type: 'video',
24
+ * };
25
+ * ```
26
+ */
27
+ export interface EmbedUrlMatcher {
28
+ /** Regex pattern tested against the URL. */
29
+ readonly pattern: RegExp;
30
+ /** The embed type to use when the pattern matches. */
31
+ readonly type: 'video' | 'tweet' | 'generic';
32
+ }
33
+ /**
34
+ * Configuration for the URL content handler.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const handler = createUrlContentHandler({
39
+ * autoEmbed: true,
40
+ * embedPatterns: [
41
+ * { pattern: /^https?:\/\/example\.com\/video\/\d+/, type: 'video' },
42
+ * ],
43
+ * });
44
+ * ```
45
+ */
46
+ export interface UrlContentHandlerConfig {
47
+ /**
48
+ * Auto-embed recognized URLs (YouTube, Vimeo, Twitter).
49
+ * Default: false — URLs are inserted as linked text.
50
+ */
51
+ readonly autoEmbed?: boolean;
52
+ /**
53
+ * Additional embed patterns beyond built-in ones.
54
+ * Merged with built-in patterns during matching.
55
+ */
56
+ readonly embedPatterns?: readonly EmbedUrlMatcher[];
57
+ }
58
+ /**
59
+ * Built-in URL patterns for recognized embeddable services.
60
+ *
61
+ * Matches YouTube (watch + short URLs), Vimeo, and Twitter/X status URLs.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { BUILTIN_EMBED_PATTERNS, matchEmbedUrl } from '@rtif-sdk/web';
66
+ *
67
+ * const match = matchEmbedUrl('https://youtube.com/watch?v=abc123', BUILTIN_EMBED_PATTERNS);
68
+ * // match?.type === 'video'
69
+ * ```
70
+ */
71
+ export declare const BUILTIN_EMBED_PATTERNS: readonly EmbedUrlMatcher[];
72
+ /**
73
+ * Check whether a string is a valid HTTP/HTTPS URL.
74
+ *
75
+ * Returns false for multi-line strings, non-HTTP protocols, and invalid URLs.
76
+ *
77
+ * @param text - The text to check
78
+ * @returns true if the text is a valid HTTP or HTTPS URL
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * isValidUrl('https://example.com'); // true
83
+ * isValidUrl('not a url'); // false
84
+ * isValidUrl('ftp://files.com'); // false
85
+ * isValidUrl('https://a.com\nmore'); // false
86
+ * ```
87
+ */
88
+ export declare function isValidUrl(text: string): boolean;
89
+ /**
90
+ * Match a URL against an array of embed patterns.
91
+ *
92
+ * Returns the first matching pattern, or null if no pattern matches.
93
+ *
94
+ * @param url - The URL to match
95
+ * @param patterns - Array of embed patterns to test
96
+ * @returns The matching pattern, or null
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * const match = matchEmbedUrl('https://youtube.com/watch?v=abc', BUILTIN_EMBED_PATTERNS);
101
+ * // match?.type === 'video'
102
+ * ```
103
+ */
104
+ export declare function matchEmbedUrl(url: string, patterns: readonly EmbedUrlMatcher[]): EmbedUrlMatcher | null;
105
+ /**
106
+ * Create a content handler for URL paste.
107
+ *
108
+ * Accepts `text/plain` and `text/uri-list` content. Uses `canHandle` to
109
+ * distinguish URLs from regular text — non-URLs return false and fall through
110
+ * to the plain text handler.
111
+ *
112
+ * Three behaviors:
113
+ * - **Selection + URL**: Wraps the selected text as a link
114
+ * - **No selection + autoEmbed + recognized URL**: Creates an embed block
115
+ * - **No selection + URL**: Inserts the URL as linked text
116
+ *
117
+ * Priority: -95 (between HTML at -90 and plain text at -100).
118
+ *
119
+ * @param config - Optional configuration for auto-embed behavior
120
+ * @returns A ContentHandler for URLs
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * const handler = createUrlContentHandler({ autoEmbed: true });
125
+ * pipeline.register(handler);
126
+ * ```
127
+ */
128
+ export declare function createUrlContentHandler(config?: UrlContentHandlerConfig): ContentHandler;
129
+ //# sourceMappingURL=content-handler-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-handler-url.d.ts","sourceRoot":"","sources":["../src/content-handler-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,uBAAuB,CAAC;AAM/B;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;CAC9C;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CACrD;AAMD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,eAAe,EAK5D,CAAC;AAMF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUhD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,SAAS,eAAe,EAAE,GACnC,eAAe,GAAG,IAAI,CAKxB;AA0BD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,CAAC,EAAE,uBAAuB,GAC/B,cAAc,CA0HhB"}
@@ -0,0 +1,244 @@
1
+ /**
2
+ * URL content handler — processes pasted URLs into RTIF links or embed blocks.
3
+ *
4
+ * Three paths:
5
+ * - **Path A**: Selection exists → wrap selected text as a link with the pasted URL
6
+ * - **Path B**: No selection + autoEmbed + embeddable URL → create an embed block
7
+ * - **Path C**: No selection + plain URL → insert URL as linked text
8
+ *
9
+ * Priority: -95 (runs after HTML, before plain text). Uses `canHandle` to
10
+ * distinguish URLs from regular text — non-URLs fall through to the plain
11
+ * text handler.
12
+ *
13
+ * @module
14
+ */
15
+ import { resolve } from '@rtif-sdk/core';
16
+ // ---------------------------------------------------------------------------
17
+ // Built-in embed patterns
18
+ // ---------------------------------------------------------------------------
19
+ /**
20
+ * Built-in URL patterns for recognized embeddable services.
21
+ *
22
+ * Matches YouTube (watch + short URLs), Vimeo, and Twitter/X status URLs.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { BUILTIN_EMBED_PATTERNS, matchEmbedUrl } from '@rtif-sdk/web';
27
+ *
28
+ * const match = matchEmbedUrl('https://youtube.com/watch?v=abc123', BUILTIN_EMBED_PATTERNS);
29
+ * // match?.type === 'video'
30
+ * ```
31
+ */
32
+ export const BUILTIN_EMBED_PATTERNS = [
33
+ { pattern: /^https?:\/\/(?:www\.)?youtube\.com\/watch\?v=[\w-]+/, type: 'video' },
34
+ { pattern: /^https?:\/\/youtu\.be\/[\w-]+/, type: 'video' },
35
+ { pattern: /^https?:\/\/(?:www\.)?vimeo\.com\/\d+/, type: 'video' },
36
+ { pattern: /^https?:\/\/(?:www\.)?(?:twitter|x)\.com\/\w+\/status\/\d+/, type: 'tweet' },
37
+ ];
38
+ // ---------------------------------------------------------------------------
39
+ // Helpers
40
+ // ---------------------------------------------------------------------------
41
+ /**
42
+ * Check whether a string is a valid HTTP/HTTPS URL.
43
+ *
44
+ * Returns false for multi-line strings, non-HTTP protocols, and invalid URLs.
45
+ *
46
+ * @param text - The text to check
47
+ * @returns true if the text is a valid HTTP or HTTPS URL
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * isValidUrl('https://example.com'); // true
52
+ * isValidUrl('not a url'); // false
53
+ * isValidUrl('ftp://files.com'); // false
54
+ * isValidUrl('https://a.com\nmore'); // false
55
+ * ```
56
+ */
57
+ export function isValidUrl(text) {
58
+ // Reject multi-line text
59
+ if (text.includes('\n') || text.includes('\r'))
60
+ return false;
61
+ try {
62
+ const url = new URL(text);
63
+ return url.protocol === 'http:' || url.protocol === 'https:';
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ /**
70
+ * Match a URL against an array of embed patterns.
71
+ *
72
+ * Returns the first matching pattern, or null if no pattern matches.
73
+ *
74
+ * @param url - The URL to match
75
+ * @param patterns - Array of embed patterns to test
76
+ * @returns The matching pattern, or null
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * const match = matchEmbedUrl('https://youtube.com/watch?v=abc', BUILTIN_EMBED_PATTERNS);
81
+ * // match?.type === 'video'
82
+ * ```
83
+ */
84
+ export function matchEmbedUrl(url, patterns) {
85
+ for (const matcher of patterns) {
86
+ if (matcher.pattern.test(url))
87
+ return matcher;
88
+ }
89
+ return null;
90
+ }
91
+ /**
92
+ * Extract the first URL from a `text/uri-list` string.
93
+ *
94
+ * URI lists are newline-separated. Lines starting with `#` are comments.
95
+ * Returns the first non-comment, non-empty line, or null.
96
+ *
97
+ * @param text - The URI list text
98
+ * @returns The first URL, or null
99
+ */
100
+ function extractFirstUri(text) {
101
+ const lines = text.split(/\r?\n/);
102
+ for (const line of lines) {
103
+ const trimmed = line.trim();
104
+ if (trimmed.length > 0 && !trimmed.startsWith('#')) {
105
+ return trimmed;
106
+ }
107
+ }
108
+ return null;
109
+ }
110
+ // ---------------------------------------------------------------------------
111
+ // Factory
112
+ // ---------------------------------------------------------------------------
113
+ /**
114
+ * Create a content handler for URL paste.
115
+ *
116
+ * Accepts `text/plain` and `text/uri-list` content. Uses `canHandle` to
117
+ * distinguish URLs from regular text — non-URLs return false and fall through
118
+ * to the plain text handler.
119
+ *
120
+ * Three behaviors:
121
+ * - **Selection + URL**: Wraps the selected text as a link
122
+ * - **No selection + autoEmbed + recognized URL**: Creates an embed block
123
+ * - **No selection + URL**: Inserts the URL as linked text
124
+ *
125
+ * Priority: -95 (between HTML at -90 and plain text at -100).
126
+ *
127
+ * @param config - Optional configuration for auto-embed behavior
128
+ * @returns A ContentHandler for URLs
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * const handler = createUrlContentHandler({ autoEmbed: true });
133
+ * pipeline.register(handler);
134
+ * ```
135
+ */
136
+ export function createUrlContentHandler(config) {
137
+ const autoEmbed = config?.autoEmbed ?? false;
138
+ const customPatterns = config?.embedPatterns ?? [];
139
+ return {
140
+ id: 'rtif:url',
141
+ accept: ['text/plain', 'text/uri-list'],
142
+ priority: -95,
143
+ async canHandle(item) {
144
+ const text = await item.getString();
145
+ if (text === null || text === '')
146
+ return false;
147
+ // For text/uri-list, extract the first URI
148
+ if (item.type === 'text/uri-list') {
149
+ const firstUri = extractFirstUri(text);
150
+ return firstUri !== null && isValidUrl(firstUri);
151
+ }
152
+ return isValidUrl(text.trim());
153
+ },
154
+ async handle(item, context) {
155
+ const rawText = await item.getString();
156
+ if (rawText === null || rawText === '')
157
+ return false;
158
+ // Resolve the URL
159
+ let url;
160
+ if (item.type === 'text/uri-list') {
161
+ const firstUri = extractFirstUri(rawText);
162
+ if (firstUri === null)
163
+ return false;
164
+ url = firstUri;
165
+ }
166
+ else {
167
+ url = rawText.trim();
168
+ }
169
+ if (!isValidUrl(url))
170
+ return false;
171
+ // -------------------------------------------------------------------
172
+ // Path A: Selection exists → wrap as link
173
+ // -------------------------------------------------------------------
174
+ const { selection } = context.engine.state;
175
+ const start = Math.min(selection.anchor.offset, selection.focus.offset);
176
+ const end = Math.max(selection.anchor.offset, selection.focus.offset);
177
+ if (start !== end) {
178
+ // Verify single-block selection
179
+ const doc = context.engine.state.doc;
180
+ try {
181
+ const startResolved = resolve(doc, start);
182
+ const endResolved = resolve(doc, end);
183
+ if (startResolved.blockIndex === endResolved.blockIndex) {
184
+ // Single-block selection — wrap as link
185
+ const ops = [
186
+ {
187
+ type: 'set_span_marks',
188
+ offset: start,
189
+ count: end - start,
190
+ marks: { link: { href: url } },
191
+ },
192
+ ];
193
+ context.dispatch(ops);
194
+ return true;
195
+ }
196
+ }
197
+ catch {
198
+ // Offset error — fall through to Path C
199
+ }
200
+ // Cross-block selection — fall through to Path C
201
+ }
202
+ // -------------------------------------------------------------------
203
+ // Path B: No selection + autoEmbed + recognized URL → embed block
204
+ // -------------------------------------------------------------------
205
+ if (autoEmbed) {
206
+ const allPatterns = [...BUILTIN_EMBED_PATTERNS, ...customPatterns];
207
+ const match = matchEmbedUrl(url, allPatterns);
208
+ if (match) {
209
+ const insertOffset = context.deleteSelectionAndGetOffset();
210
+ const embedBlockId = context.generateBlockId();
211
+ const trailingBlockId = context.generateBlockId();
212
+ const ops = [
213
+ { type: 'split_block', offset: insertOffset, newBlockId: embedBlockId },
214
+ { type: 'split_block', offset: insertOffset + 1, newBlockId: trailingBlockId },
215
+ { type: 'set_block_type', blockId: embedBlockId, blockType: 'embed' },
216
+ {
217
+ type: 'set_block_attrs',
218
+ blockId: embedBlockId,
219
+ attrs: { url, embedType: match.type },
220
+ },
221
+ ];
222
+ context.dispatch(ops);
223
+ return true;
224
+ }
225
+ }
226
+ // -------------------------------------------------------------------
227
+ // Path C: Insert URL as linked text (default)
228
+ // -------------------------------------------------------------------
229
+ const insertOffset = context.deleteSelectionAndGetOffset();
230
+ const ops = [
231
+ { type: 'insert_text', offset: insertOffset, text: url },
232
+ {
233
+ type: 'set_span_marks',
234
+ offset: insertOffset,
235
+ count: url.length,
236
+ marks: { link: { href: url } },
237
+ },
238
+ ];
239
+ context.dispatch(ops);
240
+ return true;
241
+ },
242
+ };
243
+ }
244
+ //# sourceMappingURL=content-handler-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-handler-url.js","sourceRoot":"","sources":["../src/content-handler-url.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Built-in content handlers for the RTIF content pipeline.
3
+ *
4
+ * Provides handlers for:
5
+ * - Plain text (`text/plain`) — splits on newlines, creates blocks
6
+ * - RTIF JSON (`application/x-rtif+json`) — preserves marks and block attrs
7
+ * - HTML (`text/html`) — stub, falls through to plain text handler
8
+ *
9
+ * @module
10
+ */
11
+ import type { ContentHandler } from './content-pipeline.js';
12
+ /**
13
+ * Create a content handler for plain text paste/drop.
14
+ *
15
+ * Accepts `text/plain` content and converts it to RTIF operations by splitting
16
+ * on newlines. Each line becomes a separate block via `split_block`, with the
17
+ * text inserted via `insert_text`.
18
+ *
19
+ * Priority: -100 (lowest built-in, runs last as fallback).
20
+ *
21
+ * @param generateBlockId - Factory for generating unique block IDs
22
+ * @returns A ContentHandler for plain text
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const handler = createPlainTextHandler(() => crypto.randomUUID());
27
+ * pipeline.register(handler);
28
+ * ```
29
+ */
30
+ export declare function createPlainTextHandler(generateBlockId: () => string): ContentHandler;
31
+ /**
32
+ * Create a content handler for RTIF JSON paste.
33
+ *
34
+ * Accepts `application/x-rtif+json` content and preserves full formatting
35
+ * fidelity including span marks and block attributes. Parses the JSON payload,
36
+ * validates its structure, and generates the appropriate RTIF operations.
37
+ *
38
+ * Priority: -80 (highest built-in, runs before HTML and plain text).
39
+ *
40
+ * @param generateBlockId - Factory for generating unique block IDs
41
+ * @returns A ContentHandler for RTIF JSON paste
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const handler = createRtifPasteHandler(() => crypto.randomUUID());
46
+ * pipeline.register(handler);
47
+ * ```
48
+ */
49
+ export declare function createRtifPasteHandler(generateBlockId: () => string): ContentHandler;
50
+ /**
51
+ * Create a stub content handler for HTML paste.
52
+ *
53
+ * Currently always returns `false` (falls through to the plain text handler).
54
+ * Will be implemented when the `@rtif-sdk/format-html` plugin is available.
55
+ *
56
+ * Priority: -90 (between RTIF JSON and plain text).
57
+ *
58
+ * @returns A ContentHandler stub for HTML paste
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const handler = createHtmlPasteHandler();
63
+ * pipeline.register(handler);
64
+ * ```
65
+ */
66
+ export declare function createHtmlPasteHandler(): ContentHandler;
67
+ //# sourceMappingURL=content-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-handlers.d.ts","sourceRoot":"","sources":["../src/content-handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,uBAAuB,CAAC;AAM/B;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CACpC,eAAe,EAAE,MAAM,MAAM,GAC5B,cAAc,CA+ChB;AAcD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CACpC,eAAe,EAAE,MAAM,MAAM,GAC5B,cAAc,CAyHhB;AAqDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,IAAI,cAAc,CAevD"}