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