quoin-editor 0.4.2 → 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 (231) hide show
  1. package/CHANGELOG.md +205 -0
  2. package/MIGRATION.md +233 -0
  3. package/README.md +78 -716
  4. package/dist/ai.cjs +1107 -0
  5. package/dist/ai.cjs.map +1 -0
  6. package/dist/ai.d.cts +68 -0
  7. package/dist/ai.d.ts +68 -0
  8. package/dist/ai.js +1107 -0
  9. package/dist/ai.js.map +1 -0
  10. package/dist/catalogue.cjs +59 -0
  11. package/dist/catalogue.cjs.map +1 -0
  12. package/dist/catalogue.d.cts +39 -0
  13. package/dist/catalogue.d.ts +39 -0
  14. package/dist/catalogue.js +59 -0
  15. package/dist/catalogue.js.map +1 -0
  16. package/dist/chunk-2QCRZ7OL.cjs +319 -0
  17. package/dist/chunk-2QCRZ7OL.cjs.map +1 -0
  18. package/dist/chunk-2VDK27IF.cjs +6666 -0
  19. package/dist/chunk-2VDK27IF.cjs.map +1 -0
  20. package/dist/chunk-47MLKSI2.js +326 -0
  21. package/dist/chunk-47MLKSI2.js.map +1 -0
  22. package/dist/chunk-5DNCCN6X.cjs +326 -0
  23. package/dist/chunk-5DNCCN6X.cjs.map +1 -0
  24. package/dist/chunk-AEJXZBVR.cjs +1378 -0
  25. package/dist/chunk-AEJXZBVR.cjs.map +1 -0
  26. package/dist/chunk-CDELOHDY.cjs +239 -0
  27. package/dist/chunk-CDELOHDY.cjs.map +1 -0
  28. package/dist/chunk-E75EHRPA.cjs +175 -0
  29. package/dist/chunk-E75EHRPA.cjs.map +1 -0
  30. package/dist/chunk-EL2OEJQC.cjs +562 -0
  31. package/dist/chunk-EL2OEJQC.cjs.map +1 -0
  32. package/dist/chunk-GLLXHMH2.cjs +25 -0
  33. package/dist/chunk-GLLXHMH2.cjs.map +1 -0
  34. package/dist/chunk-GTTPVJSD.cjs +3286 -0
  35. package/dist/chunk-GTTPVJSD.cjs.map +1 -0
  36. package/dist/chunk-HELJTLNC.cjs +100 -0
  37. package/dist/chunk-HELJTLNC.cjs.map +1 -0
  38. package/dist/chunk-HJIQT3S5.js +4047 -0
  39. package/dist/chunk-HJIQT3S5.js.map +1 -0
  40. package/dist/chunk-HZSSEYJS.cjs +504 -0
  41. package/dist/chunk-HZSSEYJS.cjs.map +1 -0
  42. package/dist/chunk-JDPTZUBW.js +184 -0
  43. package/dist/chunk-JDPTZUBW.js.map +1 -0
  44. package/dist/chunk-JEIFJHEI.js +14 -0
  45. package/dist/chunk-JEIFJHEI.js.map +1 -0
  46. package/dist/chunk-K574XHMM.js +25 -0
  47. package/dist/chunk-K574XHMM.js.map +1 -0
  48. package/dist/chunk-KAVU6T6J.js +249 -0
  49. package/dist/chunk-KAVU6T6J.js.map +1 -0
  50. package/dist/chunk-KXOBZGQO.js +1378 -0
  51. package/dist/chunk-KXOBZGQO.js.map +1 -0
  52. package/dist/chunk-LCQSG4M6.js +3335 -0
  53. package/dist/chunk-LCQSG4M6.js.map +1 -0
  54. package/dist/chunk-MQ2XQ4MX.js +100 -0
  55. package/dist/chunk-MQ2XQ4MX.js.map +1 -0
  56. package/dist/chunk-ND5XJVK2.cjs +3335 -0
  57. package/dist/chunk-ND5XJVK2.cjs.map +1 -0
  58. package/dist/chunk-NNDYYEW5.js +504 -0
  59. package/dist/chunk-NNDYYEW5.js.map +1 -0
  60. package/dist/chunk-NOKWNK2O.js +319 -0
  61. package/dist/chunk-NOKWNK2O.js.map +1 -0
  62. package/dist/chunk-OMOBMXD5.cjs +57 -0
  63. package/dist/chunk-OMOBMXD5.cjs.map +1 -0
  64. package/dist/chunk-PMWCQSQX.js +239 -0
  65. package/dist/chunk-PMWCQSQX.js.map +1 -0
  66. package/dist/chunk-POBM7MGR.js +30 -0
  67. package/dist/chunk-POBM7MGR.js.map +1 -0
  68. package/dist/chunk-PWUEPDA3.cjs +598 -0
  69. package/dist/chunk-PWUEPDA3.cjs.map +1 -0
  70. package/dist/chunk-PZ4GT5JO.cjs +4047 -0
  71. package/dist/chunk-PZ4GT5JO.cjs.map +1 -0
  72. package/dist/chunk-QAW5NH6S.cjs +14 -0
  73. package/dist/chunk-QAW5NH6S.cjs.map +1 -0
  74. package/dist/chunk-RDG4VI6E.cjs +706 -0
  75. package/dist/chunk-RDG4VI6E.cjs.map +1 -0
  76. package/dist/chunk-RUDWMKYX.js +562 -0
  77. package/dist/chunk-RUDWMKYX.js.map +1 -0
  78. package/dist/chunk-SG37UBDO.js +706 -0
  79. package/dist/chunk-SG37UBDO.js.map +1 -0
  80. package/dist/chunk-SLVDAHHK.cjs +30 -0
  81. package/dist/chunk-SLVDAHHK.cjs.map +1 -0
  82. package/dist/chunk-T7SW6OWV.js +598 -0
  83. package/dist/chunk-T7SW6OWV.js.map +1 -0
  84. package/dist/chunk-TC4DARU2.js +175 -0
  85. package/dist/chunk-TC4DARU2.js.map +1 -0
  86. package/dist/chunk-WQHV3MRE.cjs +184 -0
  87. package/dist/chunk-WQHV3MRE.cjs.map +1 -0
  88. package/dist/chunk-WXMYDXPN.cjs +33 -0
  89. package/dist/chunk-WXMYDXPN.cjs.map +1 -0
  90. package/dist/chunk-XEBWC7ZL.js +57 -0
  91. package/dist/chunk-XEBWC7ZL.js.map +1 -0
  92. package/dist/chunk-ZC24WB7Y.cjs +249 -0
  93. package/dist/chunk-ZC24WB7Y.cjs.map +1 -0
  94. package/dist/chunk-ZGZEJCRV.js +6666 -0
  95. package/dist/chunk-ZGZEJCRV.js.map +1 -0
  96. package/dist/chunk-ZHOSSWUM.js +33 -0
  97. package/dist/chunk-ZHOSSWUM.js.map +1 -0
  98. package/dist/chunk-ZS2WA357.js +3286 -0
  99. package/dist/chunk-ZS2WA357.js.map +1 -0
  100. package/dist/comments.cjs +993 -0
  101. package/dist/comments.cjs.map +1 -0
  102. package/dist/comments.d.cts +88 -0
  103. package/dist/comments.d.ts +88 -0
  104. package/dist/comments.js +993 -0
  105. package/dist/comments.js.map +1 -0
  106. package/dist/diff.cjs +343 -0
  107. package/dist/diff.cjs.map +1 -0
  108. package/dist/diff.d.cts +67 -0
  109. package/dist/diff.d.ts +67 -0
  110. package/dist/diff.js +343 -0
  111. package/dist/diff.js.map +1 -0
  112. package/dist/docx.cjs +854 -0
  113. package/dist/docx.cjs.map +1 -0
  114. package/dist/docx.d.cts +27 -0
  115. package/dist/docx.d.ts +27 -0
  116. package/dist/docx.js +854 -0
  117. package/dist/docx.js.map +1 -0
  118. package/dist/embed-provider-jXwlRmnp.d.cts +19 -0
  119. package/dist/embed-provider-jXwlRmnp.d.ts +19 -0
  120. package/dist/embed.cjs +17 -0
  121. package/dist/embed.cjs.map +1 -0
  122. package/dist/embed.d.cts +56 -0
  123. package/dist/embed.d.ts +56 -0
  124. package/dist/embed.js +17 -0
  125. package/dist/embed.js.map +1 -0
  126. package/dist/emoji-data.cjs +1968 -0
  127. package/dist/emoji-data.cjs.map +1 -0
  128. package/dist/emoji-data.js +1968 -0
  129. package/dist/emoji-data.js.map +1 -0
  130. package/dist/emoji-extension-C12gDPm6.d.cts +46 -0
  131. package/dist/emoji-extension-DpQ43HjE.d.ts +46 -0
  132. package/dist/emoji.cjs +18 -0
  133. package/dist/emoji.cjs.map +1 -0
  134. package/dist/emoji.d.cts +31 -0
  135. package/dist/emoji.d.ts +31 -0
  136. package/dist/emoji.js +18 -0
  137. package/dist/emoji.js.map +1 -0
  138. package/dist/engine.cjs +119 -0
  139. package/dist/engine.cjs.map +1 -0
  140. package/dist/engine.d.cts +144 -0
  141. package/dist/engine.d.ts +144 -0
  142. package/dist/engine.js +119 -0
  143. package/dist/engine.js.map +1 -0
  144. package/dist/find-extension-DKqLLSuP.d.ts +41 -0
  145. package/dist/find-extension-_sbZEt3R.d.cts +41 -0
  146. package/dist/find.cjs +20 -0
  147. package/dist/find.cjs.map +1 -0
  148. package/dist/find.d.cts +44 -0
  149. package/dist/find.d.ts +44 -0
  150. package/dist/find.js +20 -0
  151. package/dist/find.js.map +1 -0
  152. package/dist/headless.cjs +151 -518
  153. package/dist/headless.cjs.map +1 -1
  154. package/dist/headless.d.cts +102 -69
  155. package/dist/headless.d.ts +102 -69
  156. package/dist/headless.js +149 -467
  157. package/dist/headless.js.map +1 -1
  158. package/dist/index-CgD72kHB.d.cts +65 -0
  159. package/dist/index-DNYbTK3w.d.ts +65 -0
  160. package/dist/index.cjs +183 -2753
  161. package/dist/index.cjs.map +1 -1
  162. package/dist/index.d.cts +345 -253
  163. package/dist/index.d.ts +345 -253
  164. package/dist/index.js +168 -2696
  165. package/dist/index.js.map +1 -1
  166. package/dist/math.cjs +18 -0
  167. package/dist/math.cjs.map +1 -0
  168. package/dist/math.d.cts +49 -0
  169. package/dist/math.d.ts +49 -0
  170. package/dist/math.js +18 -0
  171. package/dist/math.js.map +1 -0
  172. package/dist/mention.cjs +18 -0
  173. package/dist/mention.cjs.map +1 -0
  174. package/dist/mention.d.cts +50 -0
  175. package/dist/mention.d.ts +50 -0
  176. package/dist/mention.js +18 -0
  177. package/dist/mention.js.map +1 -0
  178. package/dist/presence.cjs +565 -0
  179. package/dist/presence.cjs.map +1 -0
  180. package/dist/presence.d.cts +30 -0
  181. package/dist/presence.d.ts +30 -0
  182. package/dist/presence.js +565 -0
  183. package/dist/presence.js.map +1 -0
  184. package/dist/registry-CWBOSAHm.d.cts +121 -0
  185. package/dist/registry-CWBOSAHm.d.ts +121 -0
  186. package/dist/stats-extension-Bly88uio.d.ts +40 -0
  187. package/dist/stats-extension-CJPijvUb.d.cts +40 -0
  188. package/dist/stats.cjs +19 -0
  189. package/dist/stats.cjs.map +1 -0
  190. package/dist/stats.d.cts +19 -0
  191. package/dist/stats.d.ts +19 -0
  192. package/dist/stats.js +19 -0
  193. package/dist/stats.js.map +1 -0
  194. package/dist/styles.css +1 -1
  195. package/dist/table-extension-BTLxFy2S.d.cts +57 -0
  196. package/dist/table-extension-Dzf4k6hI.d.ts +57 -0
  197. package/dist/table.cjs +50 -0
  198. package/dist/table.cjs.map +1 -0
  199. package/dist/table.d.cts +57 -0
  200. package/dist/table.d.ts +57 -0
  201. package/dist/table.js +50 -0
  202. package/dist/table.js.map +1 -0
  203. package/dist/to-html-C5t7xyaM.d.ts +21 -0
  204. package/dist/to-html-CNCITCln.d.cts +21 -0
  205. package/dist/toc-extension-B3-s6h2x.d.cts +25 -0
  206. package/dist/toc-extension-DWm5alVj.d.ts +25 -0
  207. package/dist/toc.cjs +18 -0
  208. package/dist/toc.cjs.map +1 -0
  209. package/dist/toc.d.cts +24 -0
  210. package/dist/toc.d.ts +24 -0
  211. package/dist/toc.js +18 -0
  212. package/dist/toc.js.map +1 -0
  213. package/dist/types-Cz7XJLaK.d.cts +366 -0
  214. package/dist/types-DULc8wwg.d.cts +120 -0
  215. package/dist/types-DULc8wwg.d.ts +120 -0
  216. package/dist/types-a1CRBlEh.d.ts +366 -0
  217. package/dist/view-controller-DSHw0ltQ.d.cts +118 -0
  218. package/dist/view-controller-UbpzblGl.d.ts +118 -0
  219. package/dist/view.cjs +23 -0
  220. package/dist/view.cjs.map +1 -0
  221. package/dist/view.d.cts +40 -0
  222. package/dist/view.d.ts +40 -0
  223. package/dist/view.js +23 -0
  224. package/dist/view.js.map +1 -0
  225. package/dist/yjs.cjs +1026 -0
  226. package/dist/yjs.cjs.map +1 -0
  227. package/dist/yjs.d.cts +22 -0
  228. package/dist/yjs.d.ts +22 -0
  229. package/dist/yjs.js +1026 -0
  230. package/dist/yjs.js.map +1 -0
  231. package/package.json +217 -18
package/dist/index.cjs CHANGED
@@ -1,2754 +1,184 @@
1
- 'use client';
2
- "use strict";
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __export = (target, all) => {
10
- for (var name in all)
11
- __defProp(target, name, { get: all[name], enumerable: true });
12
- };
13
- var __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from === "object" || typeof from === "function") {
15
- for (let key of __getOwnPropNames(from))
16
- if (!__hasOwnProp.call(to, key) && key !== except)
17
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
- }
19
- return to;
20
- };
21
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
- // If the importer is in node compatibility mode or this is not an ESM
23
- // file that has been converted to a CommonJS file using a Babel-
24
- // compatible transform (i.e. "__esModule" has not been set), then set
25
- // "default" to the CommonJS "module.exports" for node compatibility.
26
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
- mod
28
- ));
29
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
-
31
- // src/index.ts
32
- var src_exports = {};
33
- __export(src_exports, {
34
- BlockCanvas: () => BlockCanvas,
35
- FilePickerButton: () => FilePickerButton,
36
- QuoinProvider: () => QuoinProvider,
37
- TEXT_COMMANDS: () => TEXT_COMMANDS,
38
- UploadField: () => UploadField,
39
- applyEdit: () => applyEdit,
40
- blockByType: () => blockByType,
41
- caretRect: () => caretRect,
42
- continueList: () => continueList,
43
- coreBlocks: () => coreBlocks,
44
- defineBlock: () => defineBlock,
45
- formatShortcut: () => formatShortcut,
46
- indentList: () => indentList,
47
- insertCommand: () => insertCommand,
48
- lineAt: () => lineAt,
49
- linkPaste: () => linkPaste,
50
- paletteBlocks: () => paletteBlocks,
51
- shouldOpenPalette: () => shouldOpenPalette,
52
- toggleLinePrefix: () => toggleLinePrefix,
53
- useAutoGrow: () => useAutoGrow,
54
- useAutosave: () => useAutosave,
55
- useBlockList: () => useBlockList,
56
- useMarkdownKeymap: () => useMarkdownKeymap,
57
- useQuoin: () => useQuoin,
58
- wrapSelection: () => wrapSelection
59
- });
60
- module.exports = __toCommonJS(src_exports);
61
-
62
- // src/canvas/BlockCanvas.tsx
63
- var React15 = __toESM(require("react"), 1);
64
- var import_core = require("@dnd-kit/core");
65
- var import_modifiers = require("@dnd-kit/modifiers");
66
- var import_sortable2 = require("@dnd-kit/sortable");
67
-
68
- // src/context.tsx
69
- var React = __toESM(require("react"), 1);
70
- var import_jsx_runtime = require("react/jsx-runtime");
71
- var QuoinContext = React.createContext(null);
72
- function QuoinProvider({
73
- value,
74
- children
75
- }) {
76
- const { blockTypes, upload, resolveAssetUrl, onError } = value;
77
- const config = React.useMemo(
78
- () => ({
79
- blockTypes,
80
- upload,
81
- // Identity default: an empty key resolves to an empty string, which stays falsy
82
- // wherever a resolved URL is tested for "no asset chosen yet".
83
- resolveAssetUrl: resolveAssetUrl ?? ((key) => key),
84
- onError: onError ?? ((error) => console.error(error))
85
- }),
86
- [blockTypes, upload, resolveAssetUrl, onError]
87
- );
88
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(QuoinContext.Provider, { value: config, children });
89
- }
90
- function useQuoin() {
91
- const config = React.useContext(QuoinContext);
92
- if (!config) throw new Error("useQuoin must be used inside a QuoinProvider.");
93
- return config;
94
- }
95
-
96
- // src/registry.ts
97
- function defineBlock(definition) {
98
- return definition;
99
- }
100
- function blockByType(definitions, type) {
101
- for (let i = definitions.length - 1; i >= 0; i -= 1) {
102
- if (definitions[i].type === type) return definitions[i];
103
- }
104
- return void 0;
105
- }
106
- function paletteBlocks(definitions) {
107
- const seen2 = /* @__PURE__ */ new Set();
108
- const out = [];
109
- for (let i = definitions.length - 1; i >= 0; i -= 1) {
110
- const d = definitions[i];
111
- if (seen2.has(d.type)) continue;
112
- seen2.add(d.type);
113
- if (d.inSlashPalette === false) continue;
114
- out.push(d);
115
- }
116
- return out.reverse();
117
- }
118
-
119
- // src/blocks/core.ts
120
- var import_lucide_react7 = require("lucide-react");
121
-
122
- // src/blocks/MarkdownBlockEditor.tsx
123
- var React7 = __toESM(require("react"), 1);
124
-
125
- // src/upload/FilePickerButton.tsx
126
- var import_lucide_react = require("lucide-react");
127
-
128
- // src/ui/button.tsx
129
- var React2 = __toESM(require("react"), 1);
130
- var import_react_slot = require("@radix-ui/react-slot");
131
- var import_class_variance_authority = require("class-variance-authority");
132
-
133
- // src/ui/cn.ts
134
- var import_clsx = require("clsx");
135
- var import_tailwind_merge = require("tailwind-merge");
136
- function cn(...inputs) {
137
- return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
138
- }
139
-
140
- // src/ui/focus.ts
141
- var FOCUS_RING = [
142
- "outline-hidden",
143
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid",
144
- "focus-visible:outline-[var(--quoin-ring)]"
145
- ].join(" ");
146
- var HIGHLIGHT_BAR = "data-[highlighted]:shadow-[inset_2px_0_0_var(--quoin-foreground)]";
147
- var SELECTED_BAR = "data-[selected=true]:shadow-[inset_2px_0_0_var(--quoin-foreground)]";
148
-
149
- // src/ui/touch-target.ts
150
- var TAP_TARGET_FLOOR = "pointer-coarse:min-h-11";
151
- var SQUARE_TAP_TARGET_FLOOR = `${TAP_TARGET_FLOOR} pointer-coarse:min-w-11`;
152
- var ICON_TAP_TARGET_FLOOR = [
153
- "pointer-coarse:before:absolute pointer-coarse:before:top-1/2 pointer-coarse:before:left-1/2",
154
- "pointer-coarse:before:size-11 pointer-coarse:before:-translate-x-1/2 pointer-coarse:before:-translate-y-1/2",
155
- "pointer-coarse:before:content-['']"
156
- ].join(" ");
157
-
158
- // src/ui/button.tsx
159
- var import_jsx_runtime2 = require("react/jsx-runtime");
160
- var buttonVariants = (0, import_class_variance_authority.cva)(
161
- [
162
- "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,background-color,border-color,box-shadow,opacity] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 shrink-0",
163
- FOCUS_RING
164
- ],
165
- {
166
- variants: {
167
- variant: {
168
- default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
169
- destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 dark:bg-destructive/60 dark:hover:bg-destructive/75",
170
- outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
171
- secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
172
- ghost: "hover:bg-accent hover:text-accent-foreground",
173
- link: "text-primary underline-offset-4 hover:underline"
174
- },
175
- size: {
176
- default: `h-9 px-4 py-2 has-[>svg]:px-3 ${TAP_TARGET_FLOOR}`,
177
- xs: `h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3 ${TAP_TARGET_FLOOR}`,
178
- sm: `h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5 ${TAP_TARGET_FLOOR}`,
179
- lg: `h-10 rounded-md px-6 has-[>svg]:px-4 ${TAP_TARGET_FLOOR}`,
180
- icon: `size-9 ${SQUARE_TAP_TARGET_FLOOR}`,
181
- "icon-xs": `size-6 rounded-md [&_svg:not([class*='size-'])]:size-3 ${SQUARE_TAP_TARGET_FLOOR}`,
182
- "icon-sm": `size-8 ${SQUARE_TAP_TARGET_FLOOR}`,
183
- "icon-lg": `size-10 ${SQUARE_TAP_TARGET_FLOOR}`
184
- }
185
- },
186
- defaultVariants: { variant: "default", size: "default" }
187
- }
188
- );
189
- var Button = React2.forwardRef(function Button2({ className, variant = "default", size = "default", asChild = false, ...props }, ref) {
190
- const Comp = asChild ? import_react_slot.Slot : "button";
191
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
192
- Comp,
193
- {
194
- ref,
195
- "data-slot": "button",
196
- "data-variant": variant,
197
- "data-size": size,
198
- className: cn(buttonVariants({ variant, size, className })),
199
- ...props
200
- }
201
- );
202
- });
203
-
204
- // src/upload/FilePickerButton.tsx
205
- var import_jsx_runtime3 = require("react/jsx-runtime");
206
- function FilePickerButton({ label, accept, disabled, onPick }) {
207
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "relative inline-flex rounded-md has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-[var(--quoin-ring)]", children: [
208
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { asChild: true, variant: "outline", size: "sm", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { "aria-hidden": "true", className: disabled ? "pointer-events-none opacity-50" : void 0, children: [
209
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Upload, { "aria-hidden": "true" }),
210
- label
211
- ] }) }),
212
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
213
- "input",
214
- {
215
- type: "file",
216
- accept,
217
- "aria-label": label,
218
- disabled,
219
- className: "absolute inset-0 cursor-pointer opacity-0 disabled:cursor-not-allowed",
220
- onChange: (e) => {
221
- const file = e.target.files?.[0];
222
- e.target.value = "";
223
- if (file) onPick(file);
224
- }
225
- }
226
- )
227
- ] });
228
- }
229
-
230
- // src/markdown/edit.ts
231
- function changedSpan(before, after) {
232
- let start = 0;
233
- const shortest = Math.min(before.length, after.length);
234
- while (start < shortest && before[start] === after[start]) start += 1;
235
- let endBefore = before.length;
236
- let endAfter = after.length;
237
- while (endBefore > start && endAfter > start && before[endBefore - 1] === after[endAfter - 1]) {
238
- endBefore -= 1;
239
- endAfter -= 1;
240
- }
241
- return { start, endBefore, replacement: after.slice(start, endAfter) };
242
- }
243
- function writeNatively(el, replacement) {
244
- if (typeof document.execCommand !== "function") return false;
245
- try {
246
- return replacement === "" ? document.execCommand("delete") : document.execCommand("insertText", false, replacement);
247
- } catch {
248
- return false;
249
- }
250
- }
251
- function applyEdit(el, edit, commit) {
252
- if (el.value !== edit.value) {
253
- const { start, endBefore, replacement } = changedSpan(el.value, edit.value);
254
- el.setSelectionRange(start, endBefore);
255
- if (!writeNatively(el, replacement)) {
256
- el.value = edit.value;
257
- }
258
- commit(el.value);
259
- }
260
- el.setSelectionRange(edit.selectionStart, edit.selectionEnd);
261
- }
262
-
263
- // src/markdown/line-at.ts
264
- var FENCE = /^ {0,3}(```|~~~)/;
265
- function lineAt(value, offset) {
266
- const start = value.lastIndexOf("\n", offset - 1) + 1;
267
- const next = value.indexOf("\n", offset);
268
- const end = next === -1 ? value.length : next;
269
- return { start, end, text: value.slice(start, end) };
270
- }
271
- function isFenceDelimiter(text) {
272
- return FENCE.test(text);
273
- }
274
- function insideFence(value, offset) {
275
- const lines = value.slice(0, offset).split("\n");
276
- if (isFenceDelimiter(lines[lines.length - 1])) return true;
277
- let open = false;
278
- for (let i = 0; i < lines.length - 1; i += 1) {
279
- if (isFenceDelimiter(lines[i])) open = !open;
280
- }
281
- return open;
282
- }
283
-
284
- // src/markdown/insert-command.ts
285
- function insertCommand(command, value, caret) {
286
- const line = lineAt(value, caret);
287
- const before = value.slice(line.start, caret).replace(/[ \t]+$/, "");
288
- const cut = line.start + before.length;
289
- const prefix = before.trim() === "" ? "" : command.separate ? "\n\n" : "\n";
290
- const at = cut + prefix.length + command.caretOffset;
291
- return {
292
- value: value.slice(0, cut) + prefix + command.insert + value.slice(caret),
293
- selectionStart: at,
294
- selectionEnd: at + (command.selectLength ?? 0)
295
- };
296
- }
297
-
298
- // src/markdown/platform.ts
299
- function isMac() {
300
- if (typeof navigator === "undefined") return false;
301
- const { userAgentData, platform } = navigator;
302
- return /mac|iphone|ipad/i.test(userAgentData?.platform || platform || "");
303
- }
304
-
305
- // src/markdown/shortcut-label.ts
306
- var MAC_LABEL = { mod: "\u2318", alt: "\u2325", shift: "\u21E7" };
307
- var OTHER_LABEL = { mod: "Ctrl", alt: "Alt", shift: "Shift" };
308
- function formatShortcut(hint) {
309
- const mac = isMac();
310
- const labels = mac ? MAC_LABEL : OTHER_LABEL;
311
- const parts = hint.split("+").map((part) => labels[part] ?? part.toUpperCase());
312
- return parts.join(mac ? "" : "+");
313
- }
314
-
315
- // src/ui/dialog.tsx
316
- var DialogPrimitive = __toESM(require("@radix-ui/react-dialog"), 1);
317
- var import_lucide_react2 = require("lucide-react");
318
- var import_jsx_runtime4 = require("react/jsx-runtime");
319
- function Dialog(props) {
320
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
321
- }
322
- function DialogPortal(props) {
323
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
324
- }
325
- function DialogOverlay({
326
- className,
327
- ...props
328
- }) {
329
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
330
- DialogPrimitive.Overlay,
331
- {
332
- "data-slot": "dialog-overlay",
333
- className: cn(
334
- "quoin fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
335
- className
336
- ),
337
- ...props
338
- }
339
- );
340
- }
341
- function DialogContent({
342
- className,
343
- children,
344
- ...props
345
- }) {
346
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(DialogPortal, { children: [
347
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DialogOverlay, {}),
348
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
349
- DialogPrimitive.Content,
350
- {
351
- "data-slot": "dialog-content",
352
- className: cn(
353
- "quoin bg-popover fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
354
- className
355
- ),
356
- ...props,
357
- children: [
358
- children,
359
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
360
- DialogPrimitive.Close,
361
- {
362
- className: cn(
363
- "absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100",
364
- FOCUS_RING,
365
- ICON_TAP_TARGET_FLOOR
366
- ),
367
- children: [
368
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.XIcon, {}),
369
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "sr-only", children: "Close" })
370
- ]
371
- }
372
- )
373
- ]
374
- }
375
- )
376
- ] });
377
- }
378
- function DialogHeader({ className, ...props }) {
379
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
380
- "div",
381
- {
382
- "data-slot": "dialog-header",
383
- className: cn("flex flex-col gap-2 text-center sm:text-left", className),
384
- ...props
385
- }
386
- );
387
- }
388
- function DialogTitle({
389
- className,
390
- ...props
391
- }) {
392
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
393
- DialogPrimitive.Title,
394
- {
395
- "data-slot": "dialog-title",
396
- className: cn("text-lg leading-none font-semibold", className),
397
- ...props
398
- }
399
- );
400
- }
401
- function DialogDescription({
402
- className,
403
- ...props
404
- }) {
405
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
406
- DialogPrimitive.Description,
407
- {
408
- "data-slot": "dialog-description",
409
- className: cn("text-muted-foreground text-sm", className),
410
- ...props
411
- }
412
- );
413
- }
414
-
415
- // src/markdown/ShortcutsReference.tsx
416
- var import_jsx_runtime5 = require("react/jsx-runtime");
417
- var ROWS = [
418
- { label: "Open the command palette", keys: "/" },
419
- { label: "Bold", keys: formatShortcut("mod+b") },
420
- { label: "Italic", keys: formatShortcut("mod+i") },
421
- { label: "Inline code", keys: formatShortcut("mod+e") },
422
- { label: "Link", keys: formatShortcut("mod+k") },
423
- { label: "Heading", keys: formatShortcut("mod+alt+1") },
424
- { label: "Subtitle", keys: formatShortcut("mod+alt+2") },
425
- { label: "Sub-subtitle", keys: formatShortcut("mod+alt+3") },
426
- { label: "Bullet list", keys: formatShortcut("mod+shift+8") },
427
- { label: "Numbered list", keys: formatShortcut("mod+shift+7") },
428
- { label: "Continue a list, or leave it when the item is empty", keys: "Enter" },
429
- { label: "Indent item", keys: "Tab" },
430
- { label: "Outdent item", keys: formatShortcut("shift") + "+Tab" }
431
- ];
432
- function ShortcutsReference({
433
- open,
434
- onOpenChange
435
- }) {
436
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Dialog, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(DialogContent, { className: "sm:max-w-md", children: [
437
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(DialogHeader, { children: [
438
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DialogTitle, { children: "Keyboard shortcuts" }),
439
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DialogDescription, { children: "Formatting without leaving the keyboard." })
440
- ] }),
441
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dl", { className: "grid grid-cols-[1fr_auto] gap-x-4 gap-y-2 text-sm", children: ROWS.map((row) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-baseline", children: [
442
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dt", { className: "text-muted-foreground", children: row.label }),
443
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dd", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("kbd", { className: "rounded border bg-muted px-1.5 py-0.5 font-mono text-xs", children: row.keys }) })
444
- ] }, row.label)) })
445
- ] }) });
446
- }
447
-
448
- // src/markdown/SlashPalette.tsx
449
- var import_lucide_react5 = require("lucide-react");
450
-
451
- // src/markdown/commands.ts
452
- var import_lucide_react3 = require("lucide-react");
453
- var TABLE = ["| Column | Column |", "| --- | --- |", "| | |"].join("\n");
454
- var TEXT_COMMANDS = [
455
- {
456
- id: "heading",
457
- label: "Heading",
458
- keywords: ["h1", "title", "heading", "big"],
459
- icon: import_lucide_react3.Heading1,
460
- hint: "mod+alt+1",
461
- insert: "# ",
462
- caretOffset: 2,
463
- separate: false
464
- },
465
- {
466
- id: "subtitle",
467
- label: "Subtitle",
468
- keywords: ["h2", "subtitle", "section", "subheading"],
469
- icon: import_lucide_react3.Heading2,
470
- hint: "mod+alt+2",
471
- insert: "## ",
472
- caretOffset: 3,
473
- separate: false
474
- },
475
- {
476
- id: "subsubtitle",
477
- label: "Sub-subtitle",
478
- keywords: ["h3", "subsubtitle", "minor"],
479
- icon: import_lucide_react3.Heading3,
480
- hint: "mod+alt+3",
481
- insert: "### ",
482
- caretOffset: 4,
483
- separate: false
484
- },
485
- {
486
- id: "bullet",
487
- label: "Bullet list",
488
- keywords: ["bullet", "unordered", "ul", "list", "point"],
489
- icon: import_lucide_react3.List,
490
- hint: "mod+shift+8",
491
- insert: "- ",
492
- caretOffset: 2,
493
- separate: false
494
- },
495
- {
496
- id: "numbered",
497
- label: "Numbered list",
498
- keywords: ["numbered", "ordered", "ol", "steps"],
499
- icon: import_lucide_react3.ListOrdered,
500
- hint: "mod+shift+7",
501
- insert: "1. ",
502
- caretOffset: 3,
503
- separate: false
504
- },
505
- {
506
- id: "task",
507
- label: "Task list",
508
- keywords: ["task", "todo", "checkbox", "checklist"],
509
- icon: import_lucide_react3.ListTodo,
510
- insert: "- [ ] ",
511
- caretOffset: 6,
512
- separate: false
513
- },
514
- {
515
- id: "quote",
516
- label: "Quote",
517
- keywords: ["quote", "blockquote", "citation"],
518
- icon: import_lucide_react3.Quote,
519
- insert: "> ",
520
- caretOffset: 2,
521
- separate: false
522
- },
523
- {
524
- id: "code",
525
- label: "Code block",
526
- keywords: ["code", "snippet", "fence", "pre"],
527
- icon: import_lucide_react3.Code,
528
- insert: "```\n\n```",
529
- caretOffset: 3,
530
- separate: true
531
- },
532
- {
533
- id: "divider",
534
- label: "Divider",
535
- keywords: ["divider", "rule", "hr", "separator", "break"],
536
- icon: import_lucide_react3.Minus,
537
- insert: "---\n",
538
- caretOffset: 4,
539
- separate: true
540
- },
541
- {
542
- id: "table",
543
- label: "Table",
544
- keywords: ["table", "grid", "rows", "columns"],
545
- icon: import_lucide_react3.Table,
546
- insert: TABLE,
547
- caretOffset: 2,
548
- selectLength: 6,
549
- separate: true
550
- }
551
- ];
552
-
553
- // src/ui/command.tsx
554
- var import_cmdk = require("cmdk");
555
- var import_lucide_react4 = require("lucide-react");
556
- var import_jsx_runtime6 = require("react/jsx-runtime");
557
- function Command({
558
- className,
559
- ...props
560
- }) {
561
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
562
- import_cmdk.Command,
563
- {
564
- "data-slot": "command",
565
- className: cn(
566
- "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
567
- className
568
- ),
569
- ...props
570
- }
571
- );
572
- }
573
- function CommandInput({
574
- className,
575
- ...props
576
- }) {
577
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
578
- "div",
579
- {
580
- "data-slot": "command-input-wrapper",
581
- className: "flex h-9 items-center gap-2 border-b px-3",
582
- children: [
583
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
584
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
585
- import_cmdk.Command.Input,
586
- {
587
- "data-slot": "command-input",
588
- className: cn(
589
- "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
590
- className
591
- ),
592
- ...props
593
- }
594
- )
595
- ]
596
- }
597
- );
598
- }
599
- function CommandList({
600
- className,
601
- ...props
602
- }) {
603
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
604
- import_cmdk.Command.List,
605
- {
606
- "data-slot": "command-list",
607
- className: cn(
608
- "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
609
- className
610
- ),
611
- ...props
612
- }
613
- );
614
- }
615
- function CommandEmpty({
616
- ...props
617
- }) {
618
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
619
- import_cmdk.Command.Empty,
620
- {
621
- "data-slot": "command-empty",
622
- className: "py-6 text-center text-sm",
623
- ...props
624
- }
625
- );
626
- }
627
- function CommandGroup({
628
- className,
629
- ...props
630
- }) {
631
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
632
- import_cmdk.Command.Group,
633
- {
634
- "data-slot": "command-group",
635
- className: cn(
636
- "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
637
- className
638
- ),
639
- ...props
640
- }
641
- );
642
- }
643
- function CommandItem({
644
- className,
645
- ...props
646
- }) {
647
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
648
- import_cmdk.Command.Item,
649
- {
650
- "data-slot": "command-item",
651
- className: cn(
652
- "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
653
- SELECTED_BAR,
654
- className
655
- ),
656
- ...props
657
- }
658
- );
659
- }
660
- function CommandShortcut({
661
- className,
662
- ...props
663
- }) {
664
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
665
- "span",
666
- {
667
- "data-slot": "command-shortcut",
668
- className: cn(
669
- "ml-auto text-xs tracking-widest text-muted-foreground",
670
- className
671
- ),
672
- ...props
673
- }
674
- );
675
- }
676
-
677
- // src/ui/popover.tsx
678
- var PopoverPrimitive = __toESM(require("@radix-ui/react-popover"), 1);
679
- var import_jsx_runtime7 = require("react/jsx-runtime");
680
- function Popover(props) {
681
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
682
- }
683
- function PopoverAnchor(props) {
684
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
685
- }
686
- function PopoverContent({ className, align = "center", sideOffset = 4, ...props }) {
687
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PopoverPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
688
- PopoverPrimitive.Content,
689
- {
690
- "data-slot": "popover-content",
691
- align,
692
- sideOffset,
693
- className: cn(
694
- "quoin bg-popover text-popover-foreground z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
695
- className
696
- ),
697
- ...props
698
- }
699
- ) });
700
- }
701
-
702
- // src/markdown/SlashPalette.tsx
703
- var import_jsx_runtime8 = require("react/jsx-runtime");
704
- function SlashPalette({
705
- open,
706
- caret,
707
- onOpenChange,
708
- onTextCommand,
709
- onBlockCommand,
710
- onShortcuts,
711
- onDismiss
712
- }) {
713
- const { blockTypes } = useQuoin();
714
- const blockOptions = paletteBlocks(blockTypes);
715
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Popover, { open, onOpenChange, children: [
716
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
717
- "span",
718
- {
719
- "aria-hidden": "true",
720
- className: "pointer-events-none absolute",
721
- style: caret ? { top: caret.top, left: caret.left, height: caret.height } : { top: 0, left: 0 }
722
- }
723
- ) }),
724
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
725
- PopoverContent,
726
- {
727
- "aria-label": "Command palette",
728
- align: "start",
729
- side: "bottom",
730
- sideOffset: 4,
731
- className: "w-72 p-0",
732
- onEscapeKeyDown: (event) => {
733
- event.preventDefault();
734
- onDismiss();
735
- },
736
- onCloseAutoFocus: (event) => event.preventDefault(),
737
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Command, { label: "Search commands", children: [
738
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CommandInput, { placeholder: "Search commands" }),
739
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CommandList, { children: [
740
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CommandEmpty, { children: "No command found." }),
741
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CommandGroup, { heading: "Text", children: TEXT_COMMANDS.map((command) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
742
- CommandItem,
743
- {
744
- value: `${command.label} ${command.keywords.join(" ")}`,
745
- onSelect: () => onTextCommand(command),
746
- children: [
747
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(command.icon, { "aria-hidden": "true" }),
748
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: command.label }),
749
- command.hint ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CommandShortcut, { children: formatShortcut(command.hint) }) : null
750
- ]
751
- },
752
- command.id
753
- )) }),
754
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CommandGroup, { heading: "Blocks", children: blockOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CommandItem, { value: option.label, onSelect: () => onBlockCommand(option.type), children: [
755
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(option.icon, { "aria-hidden": "true" }),
756
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: option.label })
757
- ] }, option.type)) }),
758
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CommandGroup, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CommandItem, { value: "keyboard shortcuts help", onSelect: onShortcuts, children: [
759
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Keyboard, { "aria-hidden": "true" }),
760
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Keyboard shortcuts" })
761
- ] }) })
762
- ] })
763
- ] })
764
- }
765
- )
766
- ] });
767
- }
768
-
769
- // src/markdown/useMarkdownKeymap.ts
770
- var React3 = __toESM(require("react"), 1);
771
-
772
- // src/markdown/list-continuation.ts
773
- var LIST_MARKER = /^(\s*)((?:[-*+] \[[ xX]\] )|(?:[-*+] )|(?:\d+[.)] )|(?:> ))(.*)$/;
774
- var INDENT = " ";
775
- function nextMarker(marker) {
776
- const ordered = /^(\d+)([.)] )$/.exec(marker);
777
- if (ordered) return `${Number(ordered[1]) + 1}${ordered[2]}`;
778
- const task = /^([-*+]) \[[ xX]\] $/.exec(marker);
779
- if (task) return `${task[1]} [ ] `;
780
- return marker;
781
- }
782
- function climbOut(value, line, indent, marker, content) {
783
- const outdent = indent.length >= INDENT.length;
784
- if (outdent) {
785
- const text2 = indent.slice(INDENT.length) + marker + content;
786
- const at2 = line.start + indent.length - INDENT.length + marker.length;
787
- return {
788
- value: value.slice(0, line.start) + text2 + value.slice(line.end),
789
- selectionStart: at2,
790
- selectionEnd: at2
791
- };
792
- }
793
- const leavesList = line.start > 0 && LIST_MARKER.test(lineAt(value, line.start - 1).text);
794
- const text = leavesList ? `
795
- ${content}` : content;
796
- const at = leavesList ? line.start + 1 : line.start;
797
- return {
798
- value: value.slice(0, line.start) + text + value.slice(line.end),
799
- selectionStart: at,
800
- selectionEnd: at
801
- };
802
- }
803
- function continueList(value, caret, key) {
804
- if (insideFence(value, caret)) return null;
805
- const line = lineAt(value, caret);
806
- const match = LIST_MARKER.exec(line.text);
807
- if (!match) return null;
808
- const [, indent, marker, content] = match;
809
- if (key === "Backspace") {
810
- if (caret !== line.start + indent.length + marker.length) return null;
811
- return climbOut(value, line, indent, marker, content);
812
- }
813
- if (content.trim() === "") return climbOut(value, line, indent, marker, content);
814
- const inserted = `
815
- ${indent}${nextMarker(marker)}`;
816
- const at = caret + inserted.length;
817
- return {
818
- value: value.slice(0, caret) + inserted + value.slice(caret),
819
- selectionStart: at,
820
- selectionEnd: at
821
- };
822
- }
823
-
824
- // src/markdown/indent-list.ts
825
- var INDENT2 = " ";
826
- function indentOf(text) {
827
- return /^\s*/.exec(text)?.[0].length ?? 0;
828
- }
829
- function shift(text, direction, cap) {
830
- const current = indentOf(text);
831
- if (direction === -1) return text.slice(Math.min(current, INDENT2.length));
832
- if (cap !== void 0 && current >= cap + INDENT2.length) return text;
833
- return INDENT2 + text;
834
- }
835
- function indentList(value, selectionStart, selectionEnd, direction) {
836
- if (insideFence(value, selectionStart)) return null;
837
- const first = lineAt(value, selectionStart);
838
- const last = lineAt(value, selectionEnd);
839
- const block = value.slice(first.start, last.end);
840
- const lines = block.split("\n");
841
- if (!lines.every((text) => LIST_MARKER.test(text))) return null;
842
- const cap = first.start === 0 ? 0 : indentOf(lineAt(value, first.start - 1).text);
843
- const shifted = lines.map((text, i) => shift(text, direction, i === 0 ? cap : void 0));
844
- if (shifted.every((text, i) => text === lines[i])) return null;
845
- const joined = shifted.join("\n");
846
- return {
847
- value: value.slice(0, first.start) + joined + value.slice(last.end),
848
- selectionStart: selectionStart + (shifted[0].length - lines[0].length),
849
- selectionEnd: selectionEnd + (joined.length - block.length)
850
- };
851
- }
852
-
853
- // src/markdown/line-toggles.ts
854
- var EXISTING = /^(\s*)(?:#{1,6} |[-*+] \[[ xX]\] |[-*+] |\d+[.)] )?/;
855
- var PREFIX = {
856
- h1: () => "# ",
857
- h2: () => "## ",
858
- h3: () => "### ",
859
- bullet: () => "- ",
860
- ordered: (index) => `${index + 1}. `
861
- };
862
- var CARRIES = {
863
- h1: /^\s*# /,
864
- h2: /^\s*## /,
865
- h3: /^\s*### /,
866
- bullet: /^\s*[-*+] /,
867
- ordered: /^\s*\d+[.)] /
868
- };
869
- function toggleLinePrefix(kind, value, selectionStart, selectionEnd) {
870
- const first = lineAt(value, selectionStart);
871
- const last = lineAt(value, selectionEnd);
872
- const block = value.slice(first.start, last.end);
873
- const lines = block.split("\n");
874
- let offset = first.start;
875
- const eligible = lines.map((text) => {
876
- const at = offset;
877
- offset += text.length + 1;
878
- return !insideFence(value, at) && !isFenceDelimiter(text);
879
- });
880
- const carried = lines.every((text, i) => !eligible[i] || CARRIES[kind].test(text));
881
- const rewritten = lines.map((text, i) => {
882
- if (!eligible[i]) return text;
883
- const match = EXISTING.exec(text);
884
- const indent = match?.[1] ?? "";
885
- const body = text.slice(match?.[0].length ?? 0);
886
- return carried ? indent + body : indent + PREFIX[kind](i) + body;
887
- });
888
- const joined = rewritten.join("\n");
889
- const next = value.slice(0, first.start) + joined + value.slice(last.end);
890
- if (selectionStart !== selectionEnd) {
891
- return { value: next, selectionStart: first.start, selectionEnd: first.start + joined.length };
892
- }
893
- const caret = Math.max(first.start, selectionStart + (rewritten[0].length - lines[0].length));
894
- return { value: next, selectionStart: caret, selectionEnd: caret };
895
- }
896
-
897
- // src/markdown/slash-trigger.ts
898
- function shouldOpenPalette(value, selectionStart, selectionEnd) {
899
- if (selectionStart !== selectionEnd) return false;
900
- if (insideFence(value, selectionStart)) return false;
901
- if (selectionStart === 0) return true;
902
- const previous = value[selectionStart - 1];
903
- return previous === "\n" || previous === " " || previous === " ";
904
- }
905
-
906
- // src/markdown/wrap-selection.ts
907
- var DELIMITER = {
908
- bold: "**",
909
- italic: "_",
910
- code: "`"
911
- };
912
- var WHOLE_LINK = /^\[([^\]]*)\]\(([^)]*)\)$/;
913
- var BARE_URL = /^https?:\/\/[^\s]+$/;
914
- function unwrap(value, start, end, delimiter) {
915
- const selected = value.slice(start, end);
916
- const width = delimiter.length;
917
- if (selected.length >= 2 * width && selected.startsWith(delimiter) && selected.endsWith(delimiter)) {
918
- const inner = selected.slice(width, selected.length - width);
919
- return {
920
- value: value.slice(0, start) + inner + value.slice(end),
921
- selectionStart: start,
922
- selectionEnd: start + inner.length
923
- };
924
- }
925
- const outside = start >= width && value.slice(start - width, start) === delimiter && value.slice(end, end + width) === delimiter;
926
- if (!outside) return null;
927
- return {
928
- value: value.slice(0, start - width) + selected + value.slice(end + width),
929
- selectionStart: start - width,
930
- selectionEnd: start - width + selected.length
931
- };
932
- }
933
- function link(value, start, end) {
934
- const selected = value.slice(start, end);
935
- const whole = WHOLE_LINK.exec(selected);
936
- if (whole) {
937
- const text = whole[1];
938
- return {
939
- value: value.slice(0, start) + text + value.slice(end),
940
- selectionStart: start,
941
- selectionEnd: start + text.length
942
- };
943
- }
944
- const opened = `[${selected}](`;
945
- const at = selected === "" ? start + 1 : start + opened.length;
946
- return {
947
- value: value.slice(0, start) + opened + ")" + value.slice(end),
948
- selectionStart: at,
949
- selectionEnd: at
950
- };
951
- }
952
- function wrapSelection(kind, value, selectionStart, selectionEnd) {
953
- if (kind === "link") return link(value, selectionStart, selectionEnd);
954
- const delimiter = DELIMITER[kind];
955
- const stripped = unwrap(value, selectionStart, selectionEnd, delimiter);
956
- if (stripped) return stripped;
957
- const selected = value.slice(selectionStart, selectionEnd);
958
- if (kind === "code" && selected.includes("\n")) {
959
- const at2 = selectionStart + 4;
960
- return {
961
- value: value.slice(0, selectionStart) + "```\n" + selected + "\n```" + value.slice(selectionEnd),
962
- selectionStart: at2,
963
- selectionEnd: at2 + selected.length
964
- };
965
- }
966
- const at = selectionStart + delimiter.length;
967
- return {
968
- value: value.slice(0, selectionStart) + delimiter + selected + delimiter + value.slice(selectionEnd),
969
- selectionStart: at,
970
- selectionEnd: at + selected.length
971
- };
972
- }
973
- function linkPaste(value, selectionStart, selectionEnd, pasted) {
974
- if (selectionStart === selectionEnd) return null;
975
- const url = pasted.trim();
976
- if (!BARE_URL.test(url)) return null;
977
- const inserted = `[${value.slice(selectionStart, selectionEnd)}](${url})`;
978
- const at = selectionStart + inserted.length;
979
- return {
980
- value: value.slice(0, selectionStart) + inserted + value.slice(selectionEnd),
981
- selectionStart: at,
982
- selectionEnd: at
983
- };
984
- }
985
-
986
- // src/markdown/useMarkdownKeymap.ts
987
- var WRAP = { b: "bold", i: "italic", e: "code", k: "link" };
988
- var HEADING = { Digit1: "h1", Digit2: "h2", Digit3: "h3" };
989
- var LIST = { Digit7: "ordered", Digit8: "bullet" };
990
- function useMarkdownKeymap({
991
- commit,
992
- onOpenPalette
993
- }) {
994
- const onKeyDown = React3.useCallback(
995
- (event) => {
996
- if (event.nativeEvent.isComposing || event.keyCode === 229) return;
997
- const el = event.currentTarget;
998
- const { value, selectionStart, selectionEnd } = el;
999
- const mod = isMac() ? event.metaKey : event.ctrlKey;
1000
- const run = (edit) => {
1001
- if (!edit) return;
1002
- event.preventDefault();
1003
- applyEdit(el, edit, commit);
1004
- };
1005
- if (mod && event.altKey) {
1006
- const kind = HEADING[event.code];
1007
- if (kind) run(toggleLinePrefix(kind, value, selectionStart, selectionEnd));
1008
- return;
1009
- }
1010
- if (mod && event.shiftKey) {
1011
- const kind = LIST[event.code];
1012
- if (kind) run(toggleLinePrefix(kind, value, selectionStart, selectionEnd));
1013
- return;
1014
- }
1015
- if (mod) {
1016
- const kind = WRAP[event.key.toLowerCase()];
1017
- if (kind) run(wrapSelection(kind, value, selectionStart, selectionEnd));
1018
- return;
1019
- }
1020
- if (event.key === "/") {
1021
- if (!shouldOpenPalette(value, selectionStart, selectionEnd)) return;
1022
- event.preventDefault();
1023
- onOpenPalette();
1024
- return;
1025
- }
1026
- if (event.key === "Enter" && !event.shiftKey) {
1027
- run(continueList(value, selectionStart, "Enter"));
1028
- return;
1029
- }
1030
- if (event.key === "Backspace" && selectionStart === selectionEnd) {
1031
- run(continueList(value, selectionStart, "Backspace"));
1032
- return;
1033
- }
1034
- if (event.key === "Tab") {
1035
- run(indentList(value, selectionStart, selectionEnd, event.shiftKey ? -1 : 1));
1036
- }
1037
- },
1038
- [commit, onOpenPalette]
1039
- );
1040
- const onPaste = React3.useCallback(
1041
- (event) => {
1042
- const el = event.currentTarget;
1043
- const edit = linkPaste(
1044
- el.value,
1045
- el.selectionStart,
1046
- el.selectionEnd,
1047
- event.clipboardData.getData("text/plain")
1048
- );
1049
- if (!edit) return;
1050
- event.preventDefault();
1051
- applyEdit(el, edit, commit);
1052
- },
1053
- [commit]
1054
- );
1055
- return { onKeyDown, onPaste };
1056
- }
1057
-
1058
- // src/markdown/useSlashPalette.ts
1059
- var React4 = __toESM(require("react"), 1);
1060
-
1061
- // src/markdown/caret-coordinates.ts
1062
- var COPIED = [
1063
- "boxSizing",
1064
- "fontFamily",
1065
- "fontSize",
1066
- "fontStyle",
1067
- "fontWeight",
1068
- "letterSpacing",
1069
- "lineHeight",
1070
- "paddingBottom",
1071
- "paddingLeft",
1072
- "paddingRight",
1073
- "paddingTop",
1074
- "textIndent",
1075
- "textTransform",
1076
- "wordBreak",
1077
- "wordSpacing",
1078
- "borderBottomWidth",
1079
- "borderLeftWidth",
1080
- "borderRightWidth",
1081
- "borderTopWidth"
1082
- ];
1083
- function caretRect(el) {
1084
- if (el.offsetWidth === 0) return null;
1085
- const style = window.getComputedStyle(el);
1086
- const mirror = document.createElement("div");
1087
- for (const property of COPIED) mirror.style[property] = style[property];
1088
- mirror.style.position = "absolute";
1089
- mirror.style.top = "0";
1090
- mirror.style.left = "-9999px";
1091
- mirror.style.visibility = "hidden";
1092
- mirror.style.whiteSpace = "pre-wrap";
1093
- mirror.style.overflowWrap = "break-word";
1094
- mirror.style.width = `${el.clientWidth}px`;
1095
- mirror.textContent = el.value.slice(0, el.selectionStart);
1096
- const marker = document.createElement("span");
1097
- marker.textContent = "\u200B";
1098
- mirror.appendChild(marker);
1099
- document.body.appendChild(mirror);
1100
- const top = marker.offsetTop;
1101
- const left = marker.offsetLeft;
1102
- const height = marker.offsetHeight || Number.parseFloat(style.lineHeight) || 0;
1103
- mirror.remove();
1104
- if (height === 0) return null;
1105
- return { top: top - el.scrollTop, left, height };
1106
- }
1107
-
1108
- // src/markdown/useSlashPalette.ts
1109
- function useSlashPalette(ref) {
1110
- const [open, setOpen] = React4.useState(false);
1111
- const [caret, setCaret] = React4.useState(null);
1112
- const offsetRef = React4.useRef(0);
1113
- const openAtCaret = React4.useCallback(() => {
1114
- const el = ref.current;
1115
- if (!el) return;
1116
- offsetRef.current = el.selectionStart;
1117
- setCaret(caretRect(el));
1118
- setOpen(true);
1119
- }, [ref]);
1120
- return { open, setOpen, caret, offsetRef, openAtCaret };
1121
- }
1122
-
1123
- // src/hooks/useAutoGrow.ts
1124
- var React6 = __toESM(require("react"), 1);
1125
-
1126
- // src/hooks/use-isomorphic-layout-effect.ts
1127
- var React5 = __toESM(require("react"), 1);
1128
- var useIsomorphicLayoutEffect = typeof document === "undefined" ? React5.useEffect : React5.useLayoutEffect;
1129
-
1130
- // src/hooks/useAutoGrow.ts
1131
- function useAutoGrow(value) {
1132
- const ref = React6.useRef(null);
1133
- const lastWidth = React6.useRef(null);
1134
- const fit = React6.useCallback(() => {
1135
- const el = ref.current;
1136
- if (!el) return;
1137
- const scroller = document.scrollingElement ?? document.documentElement;
1138
- const scrollTop = scroller.scrollTop;
1139
- el.style.height = "auto";
1140
- if (el.scrollHeight > 0) el.style.height = `${el.scrollHeight}px`;
1141
- if (scroller.scrollTop !== scrollTop) scroller.scrollTop = scrollTop;
1142
- }, []);
1143
- useIsomorphicLayoutEffect(fit, [fit, value]);
1144
- React6.useEffect(() => {
1145
- const el = ref.current;
1146
- if (!el || typeof ResizeObserver === "undefined") return;
1147
- const observer = new ResizeObserver(([entry]) => {
1148
- const width = entry.contentRect.width;
1149
- if (width === lastWidth.current) return;
1150
- lastWidth.current = width;
1151
- fit();
1152
- });
1153
- observer.observe(el);
1154
- return () => observer.disconnect();
1155
- }, [fit]);
1156
- return ref;
1157
- }
1158
-
1159
- // src/blocks/str.ts
1160
- var str = (v) => typeof v === "string" ? v : "";
1161
-
1162
- // src/blocks/MarkdownBlockEditor.tsx
1163
- var import_jsx_runtime9 = require("react/jsx-runtime");
1164
- var BOX = [
1165
- "-mx-2 min-h-7 w-[calc(100%+1rem)] resize-none rounded-md border-0 bg-transparent px-2 py-0",
1166
- "text-base leading-7 placeholder:text-muted-foreground",
1167
- "transition-[color,box-shadow]",
1168
- FOCUS_RING
1169
- ].join(" ");
1170
- function MarkdownBlockEditor({ data, onChange, onInsertBlock, onDone }) {
1171
- const { onError } = useQuoin();
1172
- const markdown = str(data.markdown);
1173
- const ref = useAutoGrow(markdown);
1174
- const palette = useSlashPalette(ref);
1175
- const [shortcutsOpen, setShortcutsOpen] = React7.useState(false);
1176
- const set = (patch) => onChange({ ...data, ...patch });
1177
- const commit = React7.useCallback(
1178
- (next) => onChange({ ...data, markdown: next }),
1179
- [data, onChange]
1180
- );
1181
- const keymap = useMarkdownKeymap({ commit, onOpenPalette: palette.openAtCaret });
1182
- function reopenBox() {
1183
- const el = ref.current;
1184
- el?.focus({ preventScroll: true });
1185
- return el ?? null;
1186
- }
1187
- function runTextCommand(command) {
1188
- palette.setOpen(false);
1189
- const el = reopenBox();
1190
- if (!el) return;
1191
- applyEdit(el, insertCommand(command, el.value, palette.offsetRef.current), commit);
1192
- }
1193
- function writeLiteralSlash() {
1194
- palette.setOpen(false);
1195
- const el = reopenBox();
1196
- if (!el) return;
1197
- const at = palette.offsetRef.current;
1198
- applyEdit(
1199
- el,
1200
- { value: `${el.value.slice(0, at)}/${el.value.slice(at)}`, selectionStart: at + 1, selectionEnd: at + 1 },
1201
- commit
1202
- );
1203
- }
1204
- function readMarkdownFile(file) {
1205
- const reader = new FileReader();
1206
- reader.onload = () => {
1207
- const text = typeof reader.result === "string" ? reader.result : "";
1208
- if (text.trim() === "") {
1209
- onError(new Error(`${file.name} is empty.`));
1210
- return;
1211
- }
1212
- set({ markdown: text });
1213
- onDone?.();
1214
- };
1215
- reader.onerror = () => onError(new Error(`Could not read ${file.name}.`));
1216
- reader.readAsText(file);
1217
- }
1218
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "relative flex flex-col gap-3", children: [
1219
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1220
- "textarea",
1221
- {
1222
- ref,
1223
- "aria-label": "Markdown",
1224
- value: markdown,
1225
- rows: 1,
1226
- placeholder: "Write, or press / for commands",
1227
- onChange: (e) => set({ markdown: e.target.value }),
1228
- onKeyDown: keymap.onKeyDown,
1229
- onPaste: keymap.onPaste,
1230
- className: BOX
1231
- }
1232
- ),
1233
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1234
- SlashPalette,
1235
- {
1236
- open: palette.open,
1237
- caret: palette.caret,
1238
- onOpenChange: palette.setOpen,
1239
- onTextCommand: runTextCommand,
1240
- onBlockCommand: (type) => {
1241
- palette.setOpen(false);
1242
- onInsertBlock?.(type);
1243
- onDone?.();
1244
- },
1245
- onShortcuts: () => {
1246
- palette.setOpen(false);
1247
- setShortcutsOpen(true);
1248
- },
1249
- onDismiss: writeLiteralSlash
1250
- }
1251
- ),
1252
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ShortcutsReference, { open: shortcutsOpen, onOpenChange: setShortcutsOpen }),
1253
- markdown.trim() === "" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1254
- FilePickerButton,
1255
- {
1256
- label: "Upload .md",
1257
- accept: ".md,text/markdown,text/plain",
1258
- onPick: readMarkdownFile
1259
- }
1260
- )
1261
- ] });
1262
- }
1263
-
1264
- // src/ui/select.tsx
1265
- var SelectPrimitive = __toESM(require("@radix-ui/react-select"), 1);
1266
- var import_lucide_react6 = require("lucide-react");
1267
- var import_jsx_runtime10 = require("react/jsx-runtime");
1268
- function Select(props) {
1269
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectPrimitive.Root, { "data-slot": "select", ...props });
1270
- }
1271
- function SelectValue(props) {
1272
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
1273
- }
1274
- function SelectTrigger({ className, children, ...props }) {
1275
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1276
- SelectPrimitive.Trigger,
1277
- {
1278
- "data-slot": "select-trigger",
1279
- className: cn(
1280
- "border-input data-[placeholder]:text-muted-foreground flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm shadow-xs disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
1281
- FOCUS_RING,
1282
- TAP_TARGET_FLOOR,
1283
- className
1284
- ),
1285
- ...props,
1286
- children: [
1287
- children,
1288
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.ChevronDownIcon, { className: "size-4 opacity-50" }) })
1289
- ]
1290
- }
1291
- );
1292
- }
1293
- function SelectContent({ className, children, position = "popper", ...props }) {
1294
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1295
- SelectPrimitive.Content,
1296
- {
1297
- "data-slot": "select-content",
1298
- className: cn(
1299
- "quoin bg-popover text-popover-foreground relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border shadow-md",
1300
- position === "popper" && "data-[side=bottom]:translate-y-1",
1301
- className
1302
- ),
1303
- position,
1304
- ...props,
1305
- children: [
1306
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectPrimitive.ScrollUpButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.ChevronUpIcon, { className: "size-4" }) }),
1307
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"), children }),
1308
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectPrimitive.ScrollDownButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.ChevronDownIcon, { className: "size-4" }) })
1309
- ]
1310
- }
1311
- ) });
1312
- }
1313
- function SelectItem({ className, children, ...props }) {
1314
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1315
- SelectPrimitive.Item,
1316
- {
1317
- "data-slot": "select-item",
1318
- className: cn(
1319
- "focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
1320
- HIGHLIGHT_BAR,
1321
- TAP_TARGET_FLOOR,
1322
- className
1323
- ),
1324
- ...props,
1325
- children: [
1326
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.CheckIcon, { className: "size-4" }) }) }),
1327
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectPrimitive.ItemText, { children })
1328
- ]
1329
- }
1330
- );
1331
- }
1332
-
1333
- // src/ui/textarea.tsx
1334
- var React8 = __toESM(require("react"), 1);
1335
- var import_jsx_runtime11 = require("react/jsx-runtime");
1336
- var Textarea = React8.forwardRef(function Textarea2({ className, ...props }, ref) {
1337
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1338
- "textarea",
1339
- {
1340
- ref,
1341
- "data-slot": "textarea",
1342
- className: cn(
1343
- "border-input placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 md:text-sm",
1344
- FOCUS_RING,
1345
- "focus-visible:border-ring",
1346
- "aria-invalid:border-destructive",
1347
- className
1348
- ),
1349
- ...props
1350
- }
1351
- );
1352
- });
1353
-
1354
- // src/blocks/CalloutBlockEditor.tsx
1355
- var import_jsx_runtime12 = require("react/jsx-runtime");
1356
- var CALLOUT_TONES = ["info", "tip", "warning", "success", "danger"];
1357
- function CalloutBlockEditor({ data, onChange }) {
1358
- const set = (patch) => onChange({ ...data, ...patch });
1359
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
1360
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-1", children: [
1361
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-sm font-medium leading-none", children: "Tone" }),
1362
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Select, { value: str(data.tone) || "info", onValueChange: (v) => set({ tone: v }), children: [
1363
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectTrigger, { "aria-label": "Callout tone", className: "w-40 capitalize", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectValue, {}) }),
1364
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectContent, { children: CALLOUT_TONES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SelectItem, { value: t, className: "capitalize", children: t }, t)) })
1365
- ] })
1366
- ] }),
1367
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex flex-col gap-1", children: [
1368
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-sm font-medium leading-none", children: "Callout text" }),
1369
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Textarea, { value: str(data.body), onChange: (e) => set({ body: e.target.value }), className: "text-sm" })
1370
- ] })
1371
- ] });
1372
- }
1373
-
1374
- // src/ui/input.tsx
1375
- var React9 = __toESM(require("react"), 1);
1376
- var import_jsx_runtime13 = require("react/jsx-runtime");
1377
- var Input = React9.forwardRef(function Input2({ className, type, ...props }, ref) {
1378
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1379
- "input",
1380
- {
1381
- ref,
1382
- type,
1383
- "data-slot": "input",
1384
- className: cn(
1385
- "border-input placeholder:text-muted-foreground flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:opacity-50 md:text-sm",
1386
- FOCUS_RING,
1387
- "focus-visible:border-ring",
1388
- "aria-invalid:border-destructive",
1389
- TAP_TARGET_FLOOR,
1390
- className
1391
- ),
1392
- ...props
1393
- }
1394
- );
1395
- });
1396
-
1397
- // src/blocks/CodeBlockEditor.tsx
1398
- var import_jsx_runtime14 = require("react/jsx-runtime");
1399
- function CodeBlockEditor({ data, onChange }) {
1400
- const set = (patch) => onChange({ ...data, ...patch });
1401
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
1402
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex flex-col gap-1", children: [
1403
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium leading-none", children: "Language" }),
1404
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1405
- Input,
1406
- {
1407
- "aria-label": "Language",
1408
- value: str(data.language),
1409
- onChange: (e) => set({ language: e.target.value }),
1410
- className: "text-sm"
1411
- }
1412
- )
1413
- ] }),
1414
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex flex-col gap-1", children: [
1415
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium leading-none", children: "Code" }),
1416
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1417
- Textarea,
1418
- {
1419
- "aria-label": "Code",
1420
- value: str(data.code),
1421
- onChange: (e) => set({ code: e.target.value }),
1422
- className: "min-h-32 font-mono text-sm"
1423
- }
1424
- )
1425
- ] })
1426
- ] });
1427
- }
1428
-
1429
- // src/blocks/ImageBlockEditor.tsx
1430
- var import_react = require("react");
1431
-
1432
- // src/upload/UploadField.tsx
1433
- var React10 = __toESM(require("react"), 1);
1434
- var import_jsx_runtime15 = require("react/jsx-runtime");
1435
- function UploadField({ category, label, accept, onUploaded }) {
1436
- const { upload, onError } = useQuoin();
1437
- const [busy, setBusy] = React10.useState(false);
1438
- const [error, setError] = React10.useState(null);
1439
- async function run(file) {
1440
- if (!upload) return;
1441
- setBusy(true);
1442
- setError(null);
1443
- let result;
1444
- try {
1445
- result = await upload(file, category);
1446
- } catch (cause) {
1447
- setError(cause instanceof Error && cause.message || "Upload failed.");
1448
- setBusy(false);
1449
- return;
1450
- }
1451
- try {
1452
- await onUploaded(result);
1453
- } catch (cause) {
1454
- onError(cause instanceof Error ? cause : new Error(String(cause)));
1455
- } finally {
1456
- setBusy(false);
1457
- }
1458
- }
1459
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col items-start gap-1.5", children: [
1460
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1461
- FilePickerButton,
1462
- {
1463
- label,
1464
- accept,
1465
- disabled: busy || !upload,
1466
- onPick: (file) => void run(file)
1467
- }
1468
- ),
1469
- !upload && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { role: "note", className: "text-xs text-muted-foreground", children: "Uploads are not configured for this editor." }),
1470
- busy && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { role: "status", className: "text-xs text-muted-foreground", children: "Uploading..." }),
1471
- error && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { role: "alert", className: "text-xs text-destructive", children: error })
1472
- ] });
1473
- }
1474
-
1475
- // src/blocks/intrinsic-size.ts
1476
- function intrinsicSize(src) {
1477
- return new Promise((resolve) => {
1478
- if (typeof window === "undefined") return resolve(null);
1479
- const img = new window.Image();
1480
- img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
1481
- img.onerror = () => resolve(null);
1482
- img.src = src;
1483
- });
1484
- }
1485
-
1486
- // src/blocks/ImageBlockEditor.tsx
1487
- var import_jsx_runtime16 = require("react/jsx-runtime");
1488
- function ImageBlockEditor({ data, onChange }) {
1489
- const { resolveAssetUrl } = useQuoin();
1490
- const latest = (0, import_react.useRef)(data);
1491
- (0, import_react.useEffect)(() => {
1492
- latest.current = data;
1493
- });
1494
- const set = (patch) => onChange({ ...latest.current, ...patch });
1495
- const newestKey = (0, import_react.useRef)("");
1496
- const onUploaded = async ({ storageKey }) => {
1497
- newestKey.current = storageKey;
1498
- const { width: _width, height: _height, ...unsized } = latest.current;
1499
- onChange({ ...unsized, storageKey });
1500
- const src = resolveAssetUrl(storageKey);
1501
- const size = src ? await intrinsicSize(src) : null;
1502
- if (size && newestKey.current === storageKey) set({ storageKey, ...size });
1503
- };
1504
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col gap-2", children: [
1505
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(UploadField, { category: "image", label: "Upload image", onUploaded }),
1506
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex flex-col gap-1", children: [
1507
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sm font-medium leading-none", children: "Alt text" }),
1508
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1509
- Input,
1510
- {
1511
- "aria-label": "Image alt text",
1512
- value: str(data.alt),
1513
- onChange: (e) => set({ alt: e.target.value }),
1514
- className: "text-sm"
1515
- }
1516
- )
1517
- ] }),
1518
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex flex-col gap-1", children: [
1519
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sm font-medium leading-none", children: "Caption (optional)" }),
1520
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1521
- Input,
1522
- {
1523
- "aria-label": "Image caption",
1524
- value: str(data.caption),
1525
- onChange: (e) => set({ caption: e.target.value }),
1526
- className: "text-sm"
1527
- }
1528
- )
1529
- ] }),
1530
- str(data.storageKey) && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-muted-foreground", children: "Image attached" })
1531
- ] });
1532
- }
1533
-
1534
- // src/blocks/FileBlockEditor.tsx
1535
- var import_jsx_runtime17 = require("react/jsx-runtime");
1536
- function FileBlockEditor({ data, onChange }) {
1537
- const set = (patch) => onChange({ ...data, ...patch });
1538
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-col gap-2", children: [
1539
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1540
- UploadField,
1541
- {
1542
- category: "file",
1543
- label: "Upload file",
1544
- onUploaded: (r) => set({ storageKey: r.storageKey, filename: r.filename, size: r.size })
1545
- }
1546
- ),
1547
- str(data.filename) && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
1548
- str(data.filename),
1549
- " \u2713"
1550
- ] })
1551
- ] });
1552
- }
1553
-
1554
- // src/blocks/previews.tsx
1555
- var import_jsx_runtime18 = require("react/jsx-runtime");
1556
- var HINT = "text-sm text-muted-foreground";
1557
- var TONE = {
1558
- info: "border-info bg-info/15",
1559
- tip: "border-primary bg-primary/15",
1560
- warning: "border-warning bg-warning/15",
1561
- success: "border-success bg-success/15",
1562
- danger: "border-destructive bg-destructive/15"
1563
- };
1564
- function MarkdownPreview({ data }) {
1565
- const markdown = str(data.markdown);
1566
- if (markdown.trim() === "") return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: HINT, children: "Empty markdown block" });
1567
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "whitespace-pre-wrap text-base leading-7", children: markdown });
1568
- }
1569
- function CodePreview({ data }) {
1570
- const code = str(data.code);
1571
- if (code.trim() === "") return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: HINT, children: "Empty code block" });
1572
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("pre", { className: "overflow-x-auto rounded-md bg-muted p-3 text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("code", { children: code }) });
1573
- }
1574
- function ImagePreview({ data }) {
1575
- const { resolveAssetUrl } = useQuoin();
1576
- const key = str(data.storageKey);
1577
- if (key === "") return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: HINT, children: "No image chosen" });
1578
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("img", { src: resolveAssetUrl(key), alt: str(data.alt), className: "max-w-full rounded-md" });
1579
- }
1580
- function FilePreview({ data }) {
1581
- const name = str(data.filename);
1582
- if (name === "") return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: HINT, children: "No file chosen" });
1583
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm", children: name });
1584
- }
1585
- function CalloutPreview({ data }) {
1586
- const body = str(data.body);
1587
- if (body.trim() === "") return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: HINT, children: "Empty callout" });
1588
- const tone = str(data.tone) || "info";
1589
- const paint = TONE[tone] ?? "border-border bg-muted/50";
1590
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { "data-tone": tone, className: `rounded-md border-l-4 ${paint} p-3 text-sm`, children: body });
1591
- }
1592
-
1593
- // src/blocks/core.ts
1594
- var coreBlocks = [
1595
- defineBlock({
1596
- type: "MARKDOWN",
1597
- label: "Markdown",
1598
- icon: import_lucide_react7.FileText,
1599
- editor: MarkdownBlockEditor,
1600
- preview: MarkdownPreview,
1601
- initialData: () => ({ markdown: "" })
1602
- }),
1603
- defineBlock({
1604
- type: "CALLOUT",
1605
- label: "Callout",
1606
- icon: import_lucide_react7.MessageSquare,
1607
- editor: CalloutBlockEditor,
1608
- preview: CalloutPreview,
1609
- initialData: () => ({ tone: "info", body: "" })
1610
- }),
1611
- defineBlock({
1612
- type: "CODE",
1613
- label: "Code",
1614
- icon: import_lucide_react7.Code,
1615
- editor: CodeBlockEditor,
1616
- preview: CodePreview,
1617
- initialData: () => ({ language: "", code: "" })
1618
- }),
1619
- defineBlock({
1620
- type: "IMAGE",
1621
- label: "Image",
1622
- icon: import_lucide_react7.Image,
1623
- editor: ImageBlockEditor,
1624
- preview: ImagePreview,
1625
- initialData: () => ({ storageKey: "", alt: "" })
1626
- }),
1627
- defineBlock({
1628
- type: "FILE",
1629
- label: "File",
1630
- icon: import_lucide_react7.Paperclip,
1631
- editor: FileBlockEditor,
1632
- preview: FilePreview,
1633
- initialData: () => ({ storageKey: "", filename: "" })
1634
- })
1635
- ];
1636
-
1637
- // src/hooks/useBlockList.ts
1638
- var React11 = __toESM(require("react"), 1);
1639
-
1640
- // src/hooks/new-block-id.ts
1641
- function newBlockId() {
1642
- const webCrypto = globalThis.crypto;
1643
- if (webCrypto && typeof webCrypto.randomUUID === "function") return webCrypto.randomUUID();
1644
- const random = () => Math.random().toString(36).slice(2, 10);
1645
- return `blk-${Date.now().toString(36)}-${random()}${random()}`;
1646
- }
1647
-
1648
- // src/hooks/useBlockList.ts
1649
- var removalSeq = 0;
1650
- var UNDO_DEPTH = 20;
1651
- function reducer(state, action) {
1652
- switch (action.type) {
1653
- case "insertAt": {
1654
- const next = [...state.blocks];
1655
- next.splice(Math.max(0, Math.min(action.index, state.blocks.length)), 0, action.block);
1656
- return { ...state, blocks: next };
1657
- }
1658
- case "update": {
1659
- return {
1660
- ...state,
1661
- blocks: state.blocks.map((b) => b.id === action.id ? { ...b, data: action.data } : b)
1662
- };
1663
- }
1664
- case "remove": {
1665
- const index = state.blocks.findIndex((b) => b.id === action.id);
1666
- if (index === -1) return state;
1667
- const removals = new Map(state.removals);
1668
- removals.set(action.token, { block: state.blocks[index], index });
1669
- while (removals.size > UNDO_DEPTH) {
1670
- const oldest = removals.keys().next();
1671
- if (oldest.done) break;
1672
- removals.delete(oldest.value);
1673
- }
1674
- return { blocks: state.blocks.filter((b) => b.id !== action.id), removals };
1675
- }
1676
- case "undo": {
1677
- const removal = state.removals.get(action.token);
1678
- if (!removal) return state;
1679
- const next = [...state.blocks];
1680
- next.splice(Math.min(removal.index, state.blocks.length), 0, removal.block);
1681
- const removals = new Map(state.removals);
1682
- removals.delete(action.token);
1683
- return { blocks: next, removals };
1684
- }
1685
- case "move": {
1686
- const j = action.index + action.dir;
1687
- if (j < 0 || j >= state.blocks.length) return state;
1688
- const next = [...state.blocks];
1689
- [next[action.index], next[j]] = [next[j], next[action.index]];
1690
- return { ...state, blocks: next };
1691
- }
1692
- case "reorder": {
1693
- const { from, to } = action;
1694
- if (from < 0 || to < 0 || from >= state.blocks.length || to >= state.blocks.length) return state;
1695
- const next = [...state.blocks];
1696
- const [moved] = next.splice(from, 1);
1697
- next.splice(to, 0, moved);
1698
- return { ...state, blocks: next };
1699
- }
1700
- case "reset":
1701
- return { blocks: action.blocks, removals: /* @__PURE__ */ new Map() };
1702
- }
1703
- }
1704
- function useBlockList(initial) {
1705
- const [state, dispatch] = React11.useReducer(reducer, { blocks: initial, removals: /* @__PURE__ */ new Map() });
1706
- const insertAt = React11.useCallback(
1707
- (index, type, data = {}) => {
1708
- const id = newBlockId();
1709
- dispatch({ type: "insertAt", index, block: { id, type, data } });
1710
- return id;
1711
- },
1712
- []
1713
- );
1714
- const update = React11.useCallback((id, data) => {
1715
- dispatch({ type: "update", id, data });
1716
- }, []);
1717
- const remove = React11.useCallback((id) => {
1718
- removalSeq += 1;
1719
- const token = removalSeq;
1720
- dispatch({ type: "remove", id, token });
1721
- return token;
1722
- }, []);
1723
- const undo = React11.useCallback((token) => {
1724
- dispatch({ type: "undo", token });
1725
- }, []);
1726
- const move = React11.useCallback((index, dir) => {
1727
- dispatch({ type: "move", index, dir });
1728
- }, []);
1729
- const reorder = React11.useCallback((from, to) => {
1730
- dispatch({ type: "reorder", from, to });
1731
- }, []);
1732
- const reset = React11.useCallback((blocks) => {
1733
- dispatch({ type: "reset", blocks });
1734
- }, []);
1735
- return { blocks: state.blocks, insertAt, update, remove, undo, move, reorder, reset };
1736
- }
1737
-
1738
- // src/canvas/announcements.ts
1739
- function describeBlock(blocks, blockTypes, id) {
1740
- const index = blocks.findIndex((b) => b.id === id);
1741
- if (index === -1) return "block";
1742
- const { type } = blocks[index];
1743
- const label = blockByType(blockTypes, type)?.label ?? type;
1744
- return `${label} block, position ${index + 1} of ${blocks.length}`;
1745
- }
1746
- function buildAnnouncements(blocks, blockTypes) {
1747
- const describe = (id) => describeBlock(blocks, blockTypes, String(id));
1748
- return {
1749
- onDragStart({ active }) {
1750
- return `Picked up ${describe(active.id)}.`;
1751
- },
1752
- onDragOver({ active, over }) {
1753
- if (!over) return `${describe(active.id)} is no longer over a drop position.`;
1754
- return `${describe(active.id)} was moved over ${describe(over.id)}.`;
1755
- },
1756
- onDragEnd({ active, over }) {
1757
- if (!over) return `${describe(active.id)} was dropped.`;
1758
- return `${describe(active.id)} was dropped over ${describe(over.id)}.`;
1759
- },
1760
- onDragCancel({ active }) {
1761
- return `Dragging was cancelled. ${describe(active.id)} was dropped.`;
1762
- }
1763
- };
1764
- }
1765
-
1766
- // src/canvas/BlockActionsMenu.tsx
1767
- var import_lucide_react9 = require("lucide-react");
1768
-
1769
- // src/ui/dropdown-menu.tsx
1770
- var DropdownMenuPrimitive = __toESM(require("@radix-ui/react-dropdown-menu"), 1);
1771
- var import_lucide_react8 = require("lucide-react");
1772
- var import_jsx_runtime19 = require("react/jsx-runtime");
1773
- function DropdownMenu(props) {
1774
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DropdownMenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1775
- }
1776
- function DropdownMenuTrigger(props) {
1777
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1778
- DropdownMenuPrimitive.Trigger,
1779
- {
1780
- "data-slot": "dropdown-menu-trigger",
1781
- ...props
1782
- }
1783
- );
1784
- }
1785
- function DropdownMenuContent({
1786
- className,
1787
- sideOffset = 4,
1788
- ...props
1789
- }) {
1790
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1791
- DropdownMenuPrimitive.Content,
1792
- {
1793
- "data-slot": "dropdown-menu-content",
1794
- sideOffset,
1795
- className: cn(
1796
- "quoin bg-popover text-popover-foreground z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
1797
- className
1798
- ),
1799
- ...props
1800
- }
1801
- ) });
1802
- }
1803
- function DropdownMenuItem({
1804
- className,
1805
- inset,
1806
- ...props
1807
- }) {
1808
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1809
- DropdownMenuPrimitive.Item,
1810
- {
1811
- "data-slot": "dropdown-menu-item",
1812
- "data-inset": inset,
1813
- className: cn(
1814
- "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4",
1815
- HIGHLIGHT_BAR,
1816
- TAP_TARGET_FLOOR,
1817
- className
1818
- ),
1819
- ...props
1820
- }
1821
- );
1822
- }
1823
- function DropdownMenuSeparator({
1824
- className,
1825
- ...props
1826
- }) {
1827
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1828
- DropdownMenuPrimitive.Separator,
1829
- {
1830
- "data-slot": "dropdown-menu-separator",
1831
- className: cn("bg-border -mx-1 my-1 h-px", className),
1832
- ...props
1833
- }
1834
- );
1835
- }
1836
-
1837
- // src/canvas/BlockActionsMenu.tsx
1838
- var import_jsx_runtime20 = require("react/jsx-runtime");
1839
- function BlockActionsMenu({
1840
- index,
1841
- count,
1842
- label,
1843
- onMove,
1844
- onDelete
1845
- }) {
1846
- const position = index + 1;
1847
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DropdownMenu, { children: [
1848
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": `${label} block ${position} options`, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.MoreHorizontal, { "aria-hidden": "true" }) }) }),
1849
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DropdownMenuContent, { align: "end", className: "w-48", children: [
1850
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1851
- DropdownMenuItem,
1852
- {
1853
- "aria-label": `Move block ${position} up`,
1854
- disabled: index === 0,
1855
- onSelect: () => onMove(index, -1),
1856
- children: [
1857
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ArrowUp, { "aria-hidden": "true" }),
1858
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Move up" })
1859
- ]
1860
- }
1861
- ),
1862
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1863
- DropdownMenuItem,
1864
- {
1865
- "aria-label": `Move block ${position} down`,
1866
- disabled: index === count - 1,
1867
- onSelect: () => onMove(index, 1),
1868
- children: [
1869
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ArrowDown, { "aria-hidden": "true" }),
1870
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Move down" })
1871
- ]
1872
- }
1873
- ),
1874
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuSeparator, {}),
1875
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1876
- DropdownMenuItem,
1877
- {
1878
- "aria-label": `Delete block ${position}`,
1879
- className: "text-destructive focus:text-destructive",
1880
- onSelect: onDelete,
1881
- children: [
1882
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Trash2, { "aria-hidden": "true" }),
1883
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Delete" })
1884
- ]
1885
- }
1886
- )
1887
- ] })
1888
- ] });
1889
- }
1890
-
1891
- // src/canvas/BlockCard.tsx
1892
- var React12 = __toESM(require("react"), 1);
1893
- var import_lucide_react10 = require("lucide-react");
1894
-
1895
- // src/canvas/BlockEditor.tsx
1896
- var import_jsx_runtime21 = require("react/jsx-runtime");
1897
- function BlockEditor({
1898
- type,
1899
- data,
1900
- onChange,
1901
- onInsertBlock,
1902
- onDone
1903
- }) {
1904
- const { blockTypes } = useQuoin();
1905
- const Editor = blockByType(blockTypes, type)?.editor;
1906
- if (!Editor) return null;
1907
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Editor, { data, onChange, onInsertBlock, onDone });
1908
- }
1909
-
1910
- // src/canvas/source-offset.ts
1911
- function caretPositionAt(x, y) {
1912
- const doc = document;
1913
- const position = doc.caretPositionFromPoint?.(x, y);
1914
- if (position) return position;
1915
- const range = doc.caretRangeFromPoint?.(x, y);
1916
- return range ? { offsetNode: range.startContainer, offset: range.startOffset } : null;
1917
- }
1918
- function renderedOffsetAt(container, x, y) {
1919
- const position = caretPositionAt(x, y);
1920
- if (!position || position.offsetNode.nodeType !== Node.TEXT_NODE) return null;
1921
- if (!container.contains(position.offsetNode)) return null;
1922
- const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);
1923
- let before = 0;
1924
- while (walker.nextNode()) {
1925
- if (walker.currentNode === position.offsetNode) return before + position.offset;
1926
- before += walker.currentNode.textContent?.length ?? 0;
1927
- }
1928
- return null;
1929
- }
1930
- var PROBE_LENGTHS = [40, 20, 8];
1931
- function search(source, probe) {
1932
- const literal = probe.clickedText.trim();
1933
- if (literal === "" || literal === probe.rendered.trim()) return source.length;
1934
- const candidates = [literal, literal.replace(/\s+/g, " ")];
1935
- for (const text of candidates) {
1936
- const whole = source.indexOf(text);
1937
- if (whole !== -1) return whole;
1938
- }
1939
- for (const length of PROBE_LENGTHS) {
1940
- for (const text of candidates) {
1941
- if (text.length < length) continue;
1942
- const at = source.indexOf(text.slice(0, length));
1943
- if (at !== -1) return at;
1944
- }
1945
- }
1946
- return source.length;
1947
- }
1948
- function sourceOffsetFor(source, probe) {
1949
- if (probe.renderedOffset !== null && probe.rendered === source) {
1950
- return Math.max(0, Math.min(probe.renderedOffset, source.length));
1951
- }
1952
- return search(source, probe);
1953
- }
1954
-
1955
- // src/dev-warn.ts
1956
- var seen = /* @__PURE__ */ new Set();
1957
- function devWarn(message) {
1958
- if (typeof process !== "undefined" && process?.env?.NODE_ENV === "production") return;
1959
- if (seen.has(message)) return;
1960
- seen.add(message);
1961
- console.warn(`Quoin: ${message}`);
1962
- }
1963
-
1964
- // src/canvas/BlockCard.tsx
1965
- var import_jsx_runtime22 = require("react/jsx-runtime");
1966
- var INTERACTIVE_SELECTOR = 'a, button, input, textarea, select, [role="button"], [tabindex], iframe';
1967
- var PORTALLED_OVERLAY = '[data-radix-popper-content-wrapper], [role="dialog"]';
1968
- function hasSelectionWithin(container) {
1969
- const selection = window.getSelection();
1970
- if (!selection || selection.isCollapsed || selection.rangeCount === 0) return false;
1971
- return container.contains(selection.getRangeAt(0).commonAncestorContainer);
1972
- }
1973
- function BlockCardImpl({
1974
- block,
1975
- index,
1976
- editingByDefault = false,
1977
- readOnly = false,
1978
- initialSelection = null,
1979
- onUpdate,
1980
- onInsertBlock
1981
- }) {
1982
- const { blockTypes, onError } = useQuoin();
1983
- const definition = blockByType(blockTypes, block.type);
1984
- const Preview = definition?.preview;
1985
- const editable = Boolean(definition?.editor) && !readOnly;
1986
- const [editing, setEditing] = React12.useState(editingByDefault && editable);
1987
- const editButtonRef = React12.useRef(null);
1988
- const editorContainerRef = React12.useRef(null);
1989
- const returnFocusOnClose = React12.useRef(false);
1990
- const clickedRef = React12.useRef(null);
1991
- const scrollTopRef = React12.useRef(null);
1992
- const seedRef = React12.useRef(initialSelection);
1993
- React12.useEffect(() => {
1994
- seedRef.current = initialSelection;
1995
- });
1996
- const handleInsertBlock = React12.useCallback(
1997
- (type) => onInsertBlock?.(index + 1, type),
1998
- [onInsertBlock, index]
1999
- );
2000
- const leaveEditing = React12.useCallback(() => {
2001
- returnFocusOnClose.current = true;
2002
- setEditing(false);
2003
- }, []);
2004
- const settleRef = React12.useRef(null);
2005
- const clearSettle = () => {
2006
- if (settleRef.current === null) return;
2007
- window.clearTimeout(settleRef.current);
2008
- settleRef.current = null;
2009
- };
2010
- React12.useEffect(() => clearSettle, []);
2011
- function stillInside(node) {
2012
- if (!node) return false;
2013
- return Boolean(editorContainerRef.current?.contains(node)) || Boolean(node.closest(PORTALLED_OVERLAY));
2014
- }
2015
- React12.useEffect(() => {
2016
- if (!editing) return;
2017
- function onPointerDown(e) {
2018
- if (stillInside(e.target)) return;
2019
- setEditing(false);
2020
- }
2021
- document.addEventListener("pointerdown", onPointerDown, true);
2022
- return () => document.removeEventListener("pointerdown", onPointerDown, true);
2023
- }, [editing]);
2024
- function closeIfTabbedAway() {
2025
- clearSettle();
2026
- settleRef.current = window.setTimeout(() => {
2027
- settleRef.current = null;
2028
- if (stillInside(document.activeElement)) return;
2029
- setEditing(false);
2030
- }, 0);
2031
- }
2032
- React12.useEffect(() => {
2033
- if (!editing) return;
2034
- const field = editorContainerRef.current?.querySelector(
2035
- 'textarea, input, [role="combobox"]'
2036
- );
2037
- if (!field) return;
2038
- const clicked = clickedRef.current;
2039
- clickedRef.current = null;
2040
- if (clicked === null || !(field instanceof HTMLTextAreaElement)) {
2041
- field.focus();
2042
- if (field instanceof HTMLTextAreaElement) {
2043
- const at = seedRef.current ?? { start: field.value.length, end: field.value.length };
2044
- field.setSelectionRange(at.start, at.end);
2045
- }
2046
- return;
2047
- }
2048
- const offset = sourceOffsetFor(field.value, clicked);
2049
- field.focus({ preventScroll: true });
2050
- field.setSelectionRange(offset, offset);
2051
- }, [editing]);
2052
- useIsomorphicLayoutEffect(() => {
2053
- const scrollTop = scrollTopRef.current;
2054
- scrollTopRef.current = null;
2055
- if (!editing || scrollTop === null) return;
2056
- const scroller = document.scrollingElement ?? document.documentElement;
2057
- if (scroller.scrollTop !== scrollTop) scroller.scrollTop = scrollTop;
2058
- }, [editing]);
2059
- React12.useEffect(() => {
2060
- if (editing || !returnFocusOnClose.current) return;
2061
- returnFocusOnClose.current = false;
2062
- editButtonRef.current?.focus();
2063
- }, [editing]);
2064
- const reportedType = React12.useRef(null);
2065
- React12.useEffect(() => {
2066
- if (definition || reportedType.current === block.type) return;
2067
- reportedType.current = block.type;
2068
- onError(new Error(`No block type is registered for "${block.type}".`));
2069
- }, [definition, block.type, onError]);
2070
- React12.useEffect(() => {
2071
- if (!readOnly || !definition || definition.preview) return;
2072
- devWarn(
2073
- `the block type "${block.type}" has no \`preview\`, so a read-only canvas can draw only its label. Give its definition a \`preview\` to render the block itself.`
2074
- );
2075
- }, [readOnly, definition, block.type]);
2076
- if (definition?.alwaysEditing && editable) {
2077
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2078
- BlockEditor,
2079
- {
2080
- type: block.type,
2081
- data: block.data,
2082
- onChange: (data) => onUpdate(block.id, data),
2083
- onInsertBlock: handleInsertBlock
2084
- }
2085
- );
2086
- }
2087
- if (editing) {
2088
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2089
- "div",
2090
- {
2091
- ref: editorContainerRef,
2092
- onKeyDown: (e) => {
2093
- if (e.key === "Tab") {
2094
- closeIfTabbedAway();
2095
- return;
2096
- }
2097
- if (e.key !== "Escape") return;
2098
- if (!editorContainerRef.current?.contains(e.target)) return;
2099
- returnFocusOnClose.current = true;
2100
- setEditing(false);
2101
- },
2102
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2103
- BlockEditor,
2104
- {
2105
- type: block.type,
2106
- data: block.data,
2107
- onChange: (data) => onUpdate(block.id, data),
2108
- onInsertBlock: handleInsertBlock,
2109
- onDone: leaveEditing
2110
- }
2111
- )
2112
- }
2113
- );
2114
- }
2115
- const rest = Preview ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Preview, { data: block.data, blockId: block.id }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "py-1 text-sm text-muted-foreground", children: definition?.label ?? block.type });
2116
- if (!editable) return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { children: rest });
2117
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "group/card relative", children: [
2118
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2119
- "div",
2120
- {
2121
- onClick: (e) => {
2122
- const target = e.target;
2123
- if (target.closest(INTERACTIVE_SELECTOR)) return;
2124
- if (hasSelectionWithin(e.currentTarget)) return;
2125
- clickedRef.current = {
2126
- rendered: e.currentTarget.textContent ?? "",
2127
- clickedText: target.textContent ?? "",
2128
- renderedOffset: renderedOffsetAt(e.currentTarget, e.clientX, e.clientY)
2129
- };
2130
- scrollTopRef.current = (document.scrollingElement ?? document.documentElement).scrollTop;
2131
- setEditing(true);
2132
- },
2133
- children: rest
2134
- }
2135
- ),
2136
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2137
- Button,
2138
- {
2139
- ref: editButtonRef,
2140
- type: "button",
2141
- variant: "ghost",
2142
- size: "icon-sm",
2143
- "aria-label": `Edit block ${index + 1}`,
2144
- onClick: () => setEditing(true),
2145
- className: "absolute right-0 top-0 bg-background/90 opacity-0 transition-opacity group-hover/card:opacity-100 group-focus-within/card:opacity-100 focus-visible:opacity-100",
2146
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Pencil, { "aria-hidden": "true" })
2147
- }
2148
- )
2149
- ] });
2150
- }
2151
- var BlockCard = React12.memo(BlockCardImpl);
2152
-
2153
- // src/canvas/BlockInserter.tsx
2154
- var React13 = __toESM(require("react"), 1);
2155
- var import_lucide_react11 = require("lucide-react");
2156
- var import_jsx_runtime23 = require("react/jsx-runtime");
2157
- function BlockInserter({
2158
- index,
2159
- onInsert,
2160
- label,
2161
- canvasRef
2162
- }) {
2163
- const { blockTypes } = useQuoin();
2164
- const options = paletteBlocks(blockTypes);
2165
- const insertedAtRef = React13.useRef(null);
2166
- function handleSelect(type) {
2167
- insertedAtRef.current = index;
2168
- onInsert(index, type);
2169
- }
2170
- function handleCloseAutoFocus(event) {
2171
- const insertedAt = insertedAtRef.current;
2172
- insertedAtRef.current = null;
2173
- if (insertedAt === null) return;
2174
- event.preventDefault();
2175
- const card = canvasRef.current?.querySelector(`[data-block-index="${insertedAt}"]`);
2176
- if (!card) return;
2177
- if (typeof card.scrollIntoView === "function") {
2178
- card.scrollIntoView({ block: "center", behavior: "smooth" });
2179
- }
2180
- card.querySelector('textarea, input, [role="combobox"]')?.focus();
2181
- }
2182
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "group/inserter relative flex h-6 items-center justify-center pointer-coarse:h-11", children: [
2183
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2184
- "span",
2185
- {
2186
- "aria-hidden": "true",
2187
- className: "absolute inset-x-0 top-1/2 h-px bg-border opacity-0 transition-opacity group-hover/inserter:opacity-100 group-focus-within/inserter:opacity-100"
2188
- }
2189
- ),
2190
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DropdownMenu, { children: [
2191
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2192
- Button,
2193
- {
2194
- type: "button",
2195
- variant: "outline",
2196
- size: "icon-xs",
2197
- "aria-label": label,
2198
- className: "relative z-10 rounded-full bg-background opacity-0 transition-opacity group-hover/inserter:opacity-100 group-focus-within/inserter:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100",
2199
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Plus, { "aria-hidden": "true" })
2200
- }
2201
- ) }),
2202
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DropdownMenuContent, { align: "center", className: "w-56", onCloseAutoFocus: handleCloseAutoFocus, children: options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DropdownMenuItem, { onSelect: () => handleSelect(opt.type), children: [
2203
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(opt.icon, { "aria-hidden": "true" }),
2204
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: opt.label })
2205
- ] }, opt.type)) })
2206
- ] })
2207
- ] });
2208
- }
2209
-
2210
- // src/canvas/DocumentStarter.tsx
2211
- var React14 = __toESM(require("react"), 1);
2212
- var import_jsx_runtime24 = require("react/jsx-runtime");
2213
- var LINE = [
2214
- "w-full resize-none rounded-md border-0 bg-transparent px-2 py-1",
2215
- "text-base leading-7 placeholder:text-muted-foreground",
2216
- "transition-[color,box-shadow]",
2217
- FOCUS_RING
2218
- ].join(" ");
2219
- function DocumentStarter({
2220
- autoFocus = false,
2221
- onStart,
2222
- onInsertBlock
2223
- }) {
2224
- const ref = React14.useRef(null);
2225
- const palette = useSlashPalette(ref);
2226
- const [shortcutsOpen, setShortcutsOpen] = React14.useState(false);
2227
- function handleChange(e) {
2228
- const value = e.target.value;
2229
- e.target.value = "";
2230
- if (value === "") return;
2231
- if (value === "/") palette.openAtCaret();
2232
- else onStart(value);
2233
- }
2234
- function startWith(command) {
2235
- palette.setOpen(false);
2236
- const edit = insertCommand(command, "", 0);
2237
- onStart(edit.value, { start: edit.selectionStart, end: edit.selectionEnd });
2238
- }
2239
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "relative", children: [
2240
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2241
- "textarea",
2242
- {
2243
- ref,
2244
- "aria-label": "Write something",
2245
- defaultValue: "",
2246
- onChange: handleChange,
2247
- onKeyDown: (e) => {
2248
- if (e.nativeEvent.isComposing || e.keyCode === 229) return;
2249
- if (e.key !== "/") return;
2250
- e.preventDefault();
2251
- palette.openAtCaret();
2252
- },
2253
- autoFocus,
2254
- rows: 1,
2255
- placeholder: "Write, or press / for commands",
2256
- className: LINE
2257
- }
2258
- ),
2259
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2260
- SlashPalette,
2261
- {
2262
- open: palette.open,
2263
- caret: palette.caret,
2264
- onOpenChange: palette.setOpen,
2265
- onTextCommand: startWith,
2266
- onBlockCommand: (type) => {
2267
- palette.setOpen(false);
2268
- onInsertBlock(type);
2269
- },
2270
- onShortcuts: () => {
2271
- palette.setOpen(false);
2272
- setShortcutsOpen(true);
2273
- },
2274
- onDismiss: () => palette.setOpen(false)
2275
- }
2276
- ),
2277
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ShortcutsReference, { open: shortcutsOpen, onOpenChange: setShortcutsOpen })
2278
- ] });
2279
- }
2280
-
2281
- // src/canvas/ReadOnlyCanvas.tsx
2282
- var import_jsx_runtime25 = require("react/jsx-runtime");
2283
- function ReadOnlyCanvas({
2284
- blocks,
2285
- onUpdate,
2286
- root
2287
- }) {
2288
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ...root, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("ul", { className: "flex flex-col", children: blocks.map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { "data-block-index": i, className: "rounded-lg px-2 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(BlockCard, { block: b, index: i, editingByDefault: false, readOnly: true, onUpdate }) }) }, b.id)) }) });
2289
- }
2290
-
2291
- // src/canvas/reorder-indices.ts
2292
- function reorderIndices(ids, activeId, overId) {
2293
- if (overId === null || activeId === overId) return null;
2294
- const from = ids.indexOf(activeId);
2295
- const to = ids.indexOf(overId);
2296
- if (from === -1 || to === -1) return null;
2297
- return { from, to };
2298
- }
2299
-
2300
- // src/canvas/same-blocks.ts
2301
- function sameData(a, b) {
2302
- if (a === b) return true;
2303
- const keys = Object.keys(a);
2304
- if (keys.length !== Object.keys(b).length) return false;
2305
- return keys.every((key) => Object.hasOwn(b, key) && Object.is(a[key], b[key]));
2306
- }
2307
- function sameBlocks(a, b) {
2308
- if (a === b) return true;
2309
- if (a.length !== b.length) return false;
2310
- return a.every((block, i) => {
2311
- const other = b[i];
2312
- return block.id === other.id && block.type === other.type && sameData(block.data, other.data);
2313
- });
2314
- }
2315
-
2316
- // src/canvas/SortableBlock.tsx
2317
- var import_sortable = require("@dnd-kit/sortable");
2318
- var import_utilities = require("@dnd-kit/utilities");
2319
- var import_lucide_react12 = require("lucide-react");
2320
- var import_jsx_runtime26 = require("react/jsx-runtime");
2321
- var GUTTER = [
2322
- "flex items-center justify-end gap-0.5 pb-1",
2323
- "lg:absolute lg:right-full lg:top-1 lg:justify-start lg:pb-0 lg:pr-1",
2324
- "lg:opacity-0 lg:transition-opacity",
2325
- "lg:group-hover/block:opacity-100 lg:group-focus-within/block:opacity-100"
2326
- ].join(" ");
2327
- function SortableBlock({
2328
- id,
2329
- index,
2330
- actions,
2331
- children
2332
- }) {
2333
- const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = (0, import_sortable.useSortable)({ id });
2334
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2335
- "div",
2336
- {
2337
- ref: setNodeRef,
2338
- style: { transform: import_utilities.CSS.Transform.toString(transform), transition },
2339
- className: cn("group/block relative", isDragging && "z-10 opacity-80"),
2340
- children: [
2341
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: GUTTER, children: [
2342
- actions,
2343
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2344
- Button,
2345
- {
2346
- type: "button",
2347
- ref: setActivatorNodeRef,
2348
- variant: "ghost",
2349
- size: "icon-sm",
2350
- className: "cursor-grab text-muted-foreground active:cursor-grabbing",
2351
- "aria-label": `Reorder block ${index + 1}`,
2352
- ...attributes,
2353
- ...listeners,
2354
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react12.GripVertical, { "aria-hidden": "true" })
2355
- }
2356
- )
2357
- ] }),
2358
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2359
- "div",
2360
- {
2361
- "data-block-index": index,
2362
- className: cn(
2363
- "rounded-lg px-2 py-1 transition-colors",
2364
- isDragging ? "bg-popover shadow-md" : "group-hover/block:bg-muted/40"
2365
- ),
2366
- children
2367
- }
2368
- )
2369
- ]
2370
- }
2371
- );
2372
- }
2373
-
2374
- // src/canvas/unacknowledged.ts
2375
- var STRUCTURAL_ECHO_LIMIT = 64;
2376
- function createUnacknowledged() {
2377
- return { sequences: /* @__PURE__ */ new WeakMap(), emitted: 0, acknowledged: 0, newest: [] };
2378
- }
2379
- function keepNewest(outstanding) {
2380
- const kept = Math.min(STRUCTURAL_ECHO_LIMIT, outstanding.emitted - outstanding.acknowledged);
2381
- outstanding.newest.splice(0, outstanding.newest.length - kept);
2382
- }
2383
- function remember(outstanding, blocks) {
2384
- outstanding.emitted += 1;
2385
- outstanding.sequences.set(blocks, outstanding.emitted);
2386
- outstanding.newest.push(blocks);
2387
- keepNewest(outstanding);
2388
- }
2389
- function copiedSequence(outstanding, blocks) {
2390
- for (const emitted of outstanding.newest) {
2391
- if (sameBlocks(emitted, blocks)) return outstanding.sequences.get(emitted);
2392
- }
2393
- return void 0;
2394
- }
2395
- function acknowledge(outstanding, blocks) {
2396
- const echoed = outstanding.sequences.get(blocks) ?? copiedSequence(outstanding, blocks);
2397
- if (echoed === void 0 || echoed <= outstanding.acknowledged) return false;
2398
- outstanding.acknowledged = echoed;
2399
- keepNewest(outstanding);
2400
- return true;
2401
- }
2402
- function forgetAll(outstanding) {
2403
- outstanding.acknowledged = outstanding.emitted;
2404
- keepNewest(outstanding);
2405
- }
2406
-
2407
- // src/canvas/BlockCanvas.tsx
2408
- var import_jsx_runtime27 = require("react/jsx-runtime");
2409
- function Canvas({
2410
- blocks,
2411
- onChange,
2412
- onDelete,
2413
- blockTypes,
2414
- readOnly,
2415
- className,
2416
- id,
2417
- "aria-label": ariaLabel
2418
- }) {
2419
- const list = useBlockList(blocks);
2420
- const { insertAt, update, remove, move, reorder, reset } = list;
2421
- const sensors = (0, import_core.useSensors)(
2422
- (0, import_core.useSensor)(import_core.PointerSensor, { activationConstraint: { distance: 4 } }),
2423
- (0, import_core.useSensor)(import_core.KeyboardSensor, { coordinateGetter: import_sortable2.sortableKeyboardCoordinates })
2424
- );
2425
- const dndId = React15.useId();
2426
- const [newBlockId2, setNewBlockId] = React15.useState(null);
2427
- const [seedSelection, setSeedSelection] = React15.useState(null);
2428
- const listRef = React15.useRef(null);
2429
- const pendingDeleteIndexRef = React15.useRef(null);
2430
- const removedRef = React15.useRef(null);
2431
- const announcements = React15.useMemo(
2432
- () => buildAnnouncements(list.blocks, blockTypes),
2433
- [list.blocks, blockTypes]
2434
- );
2435
- const lastEmitted = React15.useRef(blocks);
2436
- const lastSupplied = React15.useRef(blocks);
2437
- const unacknowledged = React15.useRef(createUnacknowledged());
2438
- React15.useEffect(() => {
2439
- if (list.blocks === lastEmitted.current) return;
2440
- lastEmitted.current = list.blocks;
2441
- remember(unacknowledged.current, list.blocks);
2442
- onChange(list.blocks);
2443
- const removed = removedRef.current;
2444
- removedRef.current = null;
2445
- if (removed) onDelete?.(removed.block, removed.index);
2446
- }, [list.blocks, onChange, onDelete]);
2447
- React15.useEffect(() => {
2448
- const previous = lastSupplied.current;
2449
- lastSupplied.current = blocks;
2450
- if (acknowledge(unacknowledged.current, blocks)) return;
2451
- if (sameBlocks(blocks, previous) || sameBlocks(blocks, list.blocks)) return;
2452
- forgetAll(unacknowledged.current);
2453
- lastEmitted.current = blocks;
2454
- reset(blocks);
2455
- }, [blocks, reset]);
2456
- React15.useEffect(() => {
2457
- if (newBlockId2 === null) return;
2458
- setNewBlockId(null);
2459
- setSeedSelection(null);
2460
- }, [newBlockId2]);
2461
- const insertPlain = React15.useCallback(
2462
- (index, type, data) => {
2463
- const seed = data ?? blockByType(blockTypes, type)?.initialData?.() ?? {};
2464
- setNewBlockId(insertAt(index, type, seed));
2465
- },
2466
- [insertAt, blockTypes]
2467
- );
2468
- function handleDelete(index, id2) {
2469
- const block = list.blocks[index];
2470
- pendingDeleteIndexRef.current = index;
2471
- if (block) removedRef.current = { block, index };
2472
- remove(id2);
2473
- }
2474
- React15.useEffect(() => {
2475
- const index = pendingDeleteIndexRef.current;
2476
- pendingDeleteIndexRef.current = null;
2477
- if (index === null || !listRef.current) return;
2478
- const landingIndex = Math.min(index, list.blocks.length - 1);
2479
- const card = landingIndex >= 0 ? listRef.current.querySelector(`[data-block-index="${landingIndex}"]`) : null;
2480
- (card?.querySelector("button") ?? listRef.current.querySelector("button"))?.focus();
2481
- }, [list.blocks]);
2482
- function handleDragEnd(event) {
2483
- const { active, over } = event;
2484
- const result = reorderIndices(
2485
- list.blocks.map((b) => b.id),
2486
- String(active.id),
2487
- over ? String(over.id) : null
2488
- );
2489
- if (result) reorder(result.from, result.to);
2490
- }
2491
- const rootProps = {
2492
- ref: listRef,
2493
- id,
2494
- className: cn("quoin flex flex-col", className),
2495
- role: ariaLabel ? "group" : void 0,
2496
- "aria-label": ariaLabel
2497
- };
2498
- if (readOnly) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReadOnlyCanvas, { blocks: list.blocks, onUpdate: update, root: rootProps });
2499
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { ...rootProps, children: [
2500
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BlockInserter, { index: 0, onInsert: insertPlain, label: "Insert a block at the start", canvasRef: listRef }),
2501
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2502
- import_core.DndContext,
2503
- {
2504
- id: dndId,
2505
- sensors,
2506
- collisionDetection: import_core.closestCenter,
2507
- modifiers: [import_modifiers.restrictToVerticalAxis],
2508
- accessibility: { announcements },
2509
- onDragEnd: handleDragEnd,
2510
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_sortable2.SortableContext, { items: list.blocks.map((b) => b.id), strategy: import_sortable2.verticalListSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("ul", { className: "flex flex-col", children: list.blocks.map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("li", { children: [
2511
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2512
- SortableBlock,
2513
- {
2514
- id: b.id,
2515
- index: i,
2516
- actions: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2517
- BlockActionsMenu,
2518
- {
2519
- index: i,
2520
- count: list.blocks.length,
2521
- label: blockByType(blockTypes, b.type)?.label ?? b.type,
2522
- onMove: move,
2523
- onDelete: () => handleDelete(i, b.id)
2524
- }
2525
- ),
2526
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2527
- BlockCard,
2528
- {
2529
- block: b,
2530
- index: i,
2531
- editingByDefault: b.id === newBlockId2,
2532
- initialSelection: b.id === newBlockId2 ? seedSelection : null,
2533
- onUpdate: update,
2534
- onInsertBlock: insertPlain
2535
- }
2536
- )
2537
- }
2538
- ),
2539
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2540
- BlockInserter,
2541
- {
2542
- index: i + 1,
2543
- onInsert: insertPlain,
2544
- label: `Insert a block after block ${i + 1}`,
2545
- canvasRef: listRef
2546
- }
2547
- )
2548
- ] }, b.id)) }) })
2549
- }
2550
- ),
2551
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2552
- DocumentStarter,
2553
- {
2554
- autoFocus: list.blocks.length === 0,
2555
- onStart: (markdown, selection) => {
2556
- setSeedSelection(selection ?? null);
2557
- setNewBlockId(insertAt(list.blocks.length, "MARKDOWN", { markdown }));
2558
- },
2559
- onInsertBlock: (type) => insertPlain(list.blocks.length, type)
2560
- }
2561
- )
2562
- ] });
2563
- }
2564
- function BlockCanvas({
2565
- blocks,
2566
- onChange,
2567
- onDelete,
2568
- blockTypes = coreBlocks,
2569
- upload,
2570
- resolveAssetUrl,
2571
- onError,
2572
- readOnly = false,
2573
- className,
2574
- id,
2575
- "aria-label": ariaLabel
2576
- }) {
2577
- const config = React15.useMemo(
2578
- () => ({ blockTypes, upload, resolveAssetUrl, onError }),
2579
- [blockTypes, upload, resolveAssetUrl, onError]
2580
- );
2581
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(QuoinProvider, { value: config, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2582
- Canvas,
2583
- {
2584
- blocks,
2585
- onChange,
2586
- onDelete,
2587
- blockTypes,
2588
- readOnly,
2589
- className,
2590
- id,
2591
- "aria-label": ariaLabel
2592
- }
2593
- ) });
2594
- }
2595
-
2596
- // src/hooks/useAutosave.ts
2597
- var React16 = __toESM(require("react"), 1);
2598
-
2599
- // src/hooks/autosave-backoff.ts
2600
- function clearTimer(ref) {
2601
- if (ref.current !== null) clearTimeout(ref.current);
2602
- ref.current = null;
2603
- }
2604
- var alwaysRetry = () => true;
2605
- function reportedFailure(result) {
2606
- return typeof result === "object" && result !== null && "ok" in result && result.ok === false;
2607
- }
2608
- async function attemptSave(run) {
2609
- try {
2610
- const result = await run();
2611
- return reportedFailure(result) ? { error: result } : null;
2612
- } catch (error) {
2613
- return { error };
2614
- }
2615
- }
2616
- function nextRetryDelay(error, retriesSoFar, { debounceMs, maxBackoffMs, maxRetries, isRetryable }) {
2617
- if (retriesSoFar >= maxRetries || !isRetryable(error)) return null;
2618
- return Math.min(debounceMs * 2 ** (retriesSoFar + 1), maxBackoffMs);
2619
- }
2620
-
2621
- // src/hooks/useAutosave.ts
2622
- function useAutosave({
2623
- deps,
2624
- build,
2625
- save,
2626
- debounceMs = 2e3,
2627
- maxBackoffMs = 3e4,
2628
- maxRetries = 8,
2629
- isRetryable = alwaysRetry,
2630
- flushOnHide = true
2631
- }) {
2632
- const [status, setStatus] = React16.useState("idle");
2633
- const [error, setError] = React16.useState(null);
2634
- const dirtyRef = React16.useRef(false);
2635
- const inFlightRef = React16.useRef(false);
2636
- const disposedRef = React16.useRef(false);
2637
- const retriesRef = React16.useRef(0);
2638
- const debounceRef = React16.useRef(null);
2639
- const backoffRef = React16.useRef(null);
2640
- const buildRef = React16.useRef(build);
2641
- const saveRef = React16.useRef(save);
2642
- const runRef = React16.useRef(() => {
2643
- });
2644
- React16.useEffect(() => {
2645
- buildRef.current = build;
2646
- saveRef.current = save;
2647
- const policy = { debounceMs, maxBackoffMs, maxRetries, isRetryable };
2648
- runRef.current = () => {
2649
- clearTimer(debounceRef);
2650
- clearTimer(backoffRef);
2651
- if (inFlightRef.current) return;
2652
- inFlightRef.current = true;
2653
- dirtyRef.current = false;
2654
- if (!disposedRef.current) setStatus("saving");
2655
- void (async () => {
2656
- const failure = await attemptSave(() => saveRef.current(buildRef.current()));
2657
- inFlightRef.current = false;
2658
- if (disposedRef.current) {
2659
- if (dirtyRef.current) runRef.current();
2660
- return;
2661
- }
2662
- if (!failure) {
2663
- retriesRef.current = 0;
2664
- setError(null);
2665
- if (dirtyRef.current) runRef.current();
2666
- else setStatus("saved");
2667
- return;
2668
- }
2669
- dirtyRef.current = true;
2670
- setError(failure.error);
2671
- const delay = nextRetryDelay(failure.error, retriesRef.current, policy);
2672
- if (delay === null) {
2673
- setStatus("failed");
2674
- return;
2675
- }
2676
- retriesRef.current += 1;
2677
- setStatus("retrying");
2678
- backoffRef.current = setTimeout(() => runRef.current(), delay);
2679
- })();
2680
- };
2681
- });
2682
- const seenRef = React16.useRef(null);
2683
- React16.useEffect(() => {
2684
- const seen2 = seenRef.current;
2685
- seenRef.current = deps;
2686
- if (seen2 === null) return;
2687
- const unchanged = seen2.length === deps.length && seen2.every((v, i) => v === deps[i]);
2688
- if (!unchanged) dirtyRef.current = true;
2689
- }, deps);
2690
- React16.useEffect(() => {
2691
- if (!dirtyRef.current) return;
2692
- clearTimer(debounceRef);
2693
- debounceRef.current = setTimeout(() => runRef.current(), debounceMs);
2694
- return () => clearTimer(debounceRef);
2695
- }, deps);
2696
- React16.useEffect(() => {
2697
- disposedRef.current = false;
2698
- return () => {
2699
- disposedRef.current = true;
2700
- clearTimer(debounceRef);
2701
- clearTimer(backoffRef);
2702
- if (dirtyRef.current) runRef.current();
2703
- };
2704
- }, []);
2705
- const retry = React16.useCallback(() => {
2706
- retriesRef.current = 0;
2707
- runRef.current();
2708
- }, []);
2709
- const flush = React16.useCallback(() => {
2710
- if (dirtyRef.current) retry();
2711
- }, [retry]);
2712
- React16.useEffect(() => {
2713
- if (!flushOnHide) return;
2714
- const onVisibility = () => {
2715
- if (document.visibilityState === "hidden") flush();
2716
- };
2717
- window.addEventListener("pagehide", flush);
2718
- document.addEventListener("visibilitychange", onVisibility);
2719
- return () => {
2720
- window.removeEventListener("pagehide", flush);
2721
- document.removeEventListener("visibilitychange", onVisibility);
2722
- };
2723
- }, [flushOnHide, flush]);
2724
- return { status, error, retry, flush };
2725
- }
2726
- // Annotate the CommonJS export names for ESM import in node:
2727
- 0 && (module.exports = {
2728
- BlockCanvas,
2729
- FilePickerButton,
2730
- QuoinProvider,
2731
- TEXT_COMMANDS,
2732
- UploadField,
2733
- applyEdit,
2734
- blockByType,
2735
- caretRect,
2736
- continueList,
2737
- coreBlocks,
2738
- defineBlock,
2739
- formatShortcut,
2740
- indentList,
2741
- insertCommand,
2742
- lineAt,
2743
- linkPaste,
2744
- paletteBlocks,
2745
- shouldOpenPalette,
2746
- toggleLinePrefix,
2747
- useAutoGrow,
2748
- useAutosave,
2749
- useBlockList,
2750
- useMarkdownKeymap,
2751
- useQuoin,
2752
- wrapSelection
2753
- });
1
+ 'use client';"use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+ var _chunk2VDK27IFcjs = require('./chunk-2VDK27IF.cjs');
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+ var _chunkGTTPVJSDcjs = require('./chunk-GTTPVJSD.cjs');
32
+
33
+
34
+
35
+
36
+ var _chunkOMOBMXD5cjs = require('./chunk-OMOBMXD5.cjs');
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+ var _chunkPZ4GT5JOcjs = require('./chunk-PZ4GT5JO.cjs');
81
+
82
+
83
+
84
+ var _chunk2QCRZ7OLcjs = require('./chunk-2QCRZ7OL.cjs');
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+ var _chunkND5XJVK2cjs = require('./chunk-ND5XJVK2.cjs');
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+
137
+
138
+
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+
164
+
165
+
166
+
167
+
168
+
169
+
170
+
171
+
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+
182
+
183
+ exports.BlockCanvas = _chunk2VDK27IFcjs.BlockCanvas; exports.BlockShell = _chunkGTTPVJSDcjs.BlockShell; exports.EditableRegion = _chunkGTTPVJSDcjs.EditableRegion; exports.EditorView = _chunkGTTPVJSDcjs.EditorView; exports.FilePickerButton = _chunk2VDK27IFcjs.FilePickerButton; exports.PALETTE = _chunkND5XJVK2cjs.PALETTE; exports.QuoinProvider = _chunk2VDK27IFcjs.QuoinProvider; exports.SelectionSink = _chunkGTTPVJSDcjs.SelectionSink; exports.Transaction = _chunkPZ4GT5JOcjs.Transaction; exports.UploadField = _chunk2VDK27IFcjs.UploadField; exports.ViewController = _chunkGTTPVJSDcjs.ViewController; exports.VoidBlockFallback = _chunkGTTPVJSDcjs.VoidBlockFallback; exports.addMarkStep = _chunkPZ4GT5JOcjs.addMarkStep; exports.applyTransaction = _chunkPZ4GT5JOcjs.applyTransaction; exports.atom = _chunkND5XJVK2cjs.atom; exports.blockByType = _chunkOMOBMXD5cjs.blockByType; exports.blockInputRules = _chunkPZ4GT5JOcjs.blockInputRules; exports.blockRangeSelection = _chunkPZ4GT5JOcjs.blockRangeSelection; exports.caretFromPoint = _chunkGTTPVJSDcjs.caretFromPoint; exports.coreBlockSpecs = _chunkND5XJVK2cjs.coreBlockSpecs; exports.coreBlocks = _chunk2VDK27IFcjs.coreBlocks; exports.coreExtensions = _chunkPZ4GT5JOcjs.coreExtensions; exports.countDoc = _chunkPZ4GT5JOcjs.countDoc; exports.countText = _chunkPZ4GT5JOcjs.countText; exports.createBlockCodec = _chunkND5XJVK2cjs.createBlockCodec; exports.createEditor = _chunkPZ4GT5JOcjs.createEditor; exports.createPluginKey = _chunkPZ4GT5JOcjs.createPluginKey; exports.createSchema = _chunkND5XJVK2cjs.createSchema; exports.createState = _chunkPZ4GT5JOcjs.createState; exports.defaultRenderers = _chunkGTTPVJSDcjs.defaultRenderers; exports.defineBlock = _chunkOMOBMXD5cjs.defineBlock; exports.defineExtension = _chunkPZ4GT5JOcjs.defineExtension; exports.diffBlocks = _chunk2QCRZ7OLcjs.diffBlocks; exports.diffInline = _chunk2QCRZ7OLcjs.diffInline; exports.domMapOf = _chunkGTTPVJSDcjs.domMapOf; exports.englishLabels = _chunk2VDK27IFcjs.englishLabels; exports.formatShortcut = _chunk2VDK27IFcjs.formatShortcut; exports.fromHTML = _chunkPZ4GT5JOcjs.fromHTML; exports.fromMarkdown = _chunkND5XJVK2cjs.fromMarkdown; exports.getPluginState = _chunkPZ4GT5JOcjs.getPluginState; exports.hostKey = _chunkGTTPVJSDcjs.hostKey; exports.insertBlockAfter = _chunkPZ4GT5JOcjs.insertBlockAfter; exports.insertBlocksStep = _chunkPZ4GT5JOcjs.insertBlocksStep; exports.insertText = _chunkPZ4GT5JOcjs.insertText; exports.insertTextWithRules = _chunkPZ4GT5JOcjs.insertTextWithRules; exports.isCrossSelection = _chunkGTTPVJSDcjs.isCrossSelection; exports.joinBlocksStep = _chunkPZ4GT5JOcjs.joinBlocksStep; exports.keyName = _chunkPZ4GT5JOcjs.keyName; exports.markInputRules = _chunkPZ4GT5JOcjs.markInputRules; exports.moveBlocksStep = _chunkPZ4GT5JOcjs.moveBlocksStep; exports.nodeSelection = _chunkPZ4GT5JOcjs.nodeSelection; exports.normalizeInline = _chunkND5XJVK2cjs.normalizeInline; exports.normalizeKey = _chunkPZ4GT5JOcjs.normalizeKey; exports.paletteBlocks = _chunkOMOBMXD5cjs.paletteBlocks; exports.parseInline = _chunkND5XJVK2cjs.parseInline; exports.redo = _chunkPZ4GT5JOcjs.redo; exports.removeBlocksStep = _chunkPZ4GT5JOcjs.removeBlocksStep; exports.removeMarkStep = _chunkPZ4GT5JOcjs.removeMarkStep; exports.renderInline = _chunkGTTPVJSDcjs.renderInline; exports.replaceBlockStep = _chunkPZ4GT5JOcjs.replaceBlockStep; exports.replaceDocStep = _chunkPZ4GT5JOcjs.replaceDocStep; exports.replaceInlineStep = _chunkPZ4GT5JOcjs.replaceInlineStep; exports.resolveLabels = _chunk2VDK27IFcjs.resolveLabels; exports.sanitizeHref = _chunkND5XJVK2cjs.sanitizeHref; exports.serializeInline = _chunkND5XJVK2cjs.serializeInline; exports.setAttrsStep = _chunkPZ4GT5JOcjs.setAttrsStep; exports.setBlockType = _chunkPZ4GT5JOcjs.setBlockType; exports.setIndentStep = _chunkPZ4GT5JOcjs.setIndentStep; exports.splitBlockStep = _chunkPZ4GT5JOcjs.splitBlockStep; exports.stepFromJSON = _chunkPZ4GT5JOcjs.stepFromJSON; exports.text = _chunkND5XJVK2cjs.text; exports.textSelection = _chunkPZ4GT5JOcjs.textSelection; exports.toHTML = _chunkPZ4GT5JOcjs.toHTML; exports.toMarkdown = _chunkPZ4GT5JOcjs.toMarkdown; exports.toPlainText = _chunkPZ4GT5JOcjs.toPlainText; exports.toggleMark = _chunkPZ4GT5JOcjs.toggleMark; exports.typographyRules = _chunkPZ4GT5JOcjs.typographyRules; exports.undo = _chunkPZ4GT5JOcjs.undo; exports.useAutosave = _chunk2VDK27IFcjs.useAutosave; exports.useEditorState = _chunkGTTPVJSDcjs.useEditorState; exports.useLabels = _chunk2VDK27IFcjs.useLabels; exports.useQuoin = _chunk2VDK27IFcjs.useQuoin; exports.useQuoinEditor = _chunkGTTPVJSDcjs.useQuoinEditor; exports.useViewController = _chunkGTTPVJSDcjs.useViewController;
2754
184
  //# sourceMappingURL=index.cjs.map