quoin-editor 2.0.4 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -0
- package/MIGRATION.md +1 -1
- package/README.md +14 -13
- package/dist/ai.cjs +124 -94
- package/dist/ai.d.cts +7 -0
- package/dist/ai.d.ts +7 -0
- package/dist/ai.js +50 -20
- package/dist/ai.js.map +1 -1
- package/dist/catalogue.cjs +28 -26
- package/dist/catalogue.js +19 -17
- package/dist/catalogue.js.map +1 -1
- package/dist/{chunk-JGEDJYIV.cjs → chunk-2FUWXPPC.cjs} +92 -98
- package/dist/{chunk-NHNW5DK3.js → chunk-3F76QQ65.js} +6 -6
- package/dist/{chunk-NHNW5DK3.js.map → chunk-3F76QQ65.js.map} +1 -1
- package/dist/{chunk-DBOZFNWU.js → chunk-3QYFAAQR.js} +3 -3
- package/dist/{chunk-DBOZFNWU.js.map → chunk-3QYFAAQR.js.map} +1 -1
- package/dist/{chunk-I7X7B3WD.cjs → chunk-4JDRDPLC.cjs} +17 -8
- package/dist/{chunk-EBSDPHPS.cjs → chunk-4UFWFV43.cjs} +1060 -719
- package/dist/{chunk-LM3DHROX.cjs → chunk-5AUIA4B3.cjs} +2 -2
- package/dist/{chunk-3INFNIGN.cjs → chunk-5H3BB5VJ.cjs} +131 -78
- package/dist/{chunk-DJU25PFC.cjs → chunk-5L2NSIHZ.cjs} +11 -11
- package/dist/{chunk-W376L2GM.js → chunk-6Y7JAWYY.js} +8 -8
- package/dist/chunk-6Y7JAWYY.js.map +1 -0
- package/dist/{chunk-UDBLZILE.js → chunk-ATKDXQEY.js} +12 -7
- package/dist/{chunk-UDBLZILE.js.map → chunk-ATKDXQEY.js.map} +1 -1
- package/dist/{chunk-A2IQHMIH.js → chunk-BE2SYPK5.js} +49 -54
- package/dist/chunk-BE2SYPK5.js.map +1 -0
- package/dist/{chunk-PURYVDC3.cjs → chunk-CFICD2JP.cjs} +21 -16
- package/dist/{chunk-3HW3WRYC.cjs → chunk-CSIUXYRU.cjs} +2 -2
- package/dist/{chunk-N7BSFDDM.cjs → chunk-DBKL524I.cjs} +23 -23
- package/dist/{chunk-XE3IYW5B.js → chunk-DEFYFIWW.js} +90 -37
- package/dist/chunk-DEFYFIWW.js.map +1 -0
- package/dist/{chunk-7E43TURF.cjs → chunk-ECI3PSMS.cjs} +7 -7
- package/dist/{chunk-4G645EYO.js → chunk-F73QEF7E.js} +5 -5
- package/dist/{chunk-4G645EYO.js.map → chunk-F73QEF7E.js.map} +1 -1
- package/dist/{chunk-KHRVCTQQ.cjs → chunk-FUT7XLMP.cjs} +61 -74
- package/dist/{chunk-7J63427U.cjs → chunk-GQSBOBOM.cjs} +375 -320
- package/dist/{chunk-LU3T3JB4.js → chunk-HPNSQWRD.js} +15 -28
- package/dist/chunk-HPNSQWRD.js.map +1 -0
- package/dist/{chunk-7Y4B7NHO.js → chunk-I34BFOLL.js} +18 -11
- package/dist/chunk-I34BFOLL.js.map +1 -0
- package/dist/{chunk-AJT54OMZ.js → chunk-IFRXEFQH.js} +9 -15
- package/dist/chunk-IFRXEFQH.js.map +1 -0
- package/dist/{chunk-YYW33WUL.cjs → chunk-K3XWVRL4.cjs} +8 -8
- package/dist/{chunk-LSI4RMTY.js → chunk-L5L6YNLT.js} +3 -3
- package/dist/{chunk-LSI4RMTY.js.map → chunk-L5L6YNLT.js.map} +1 -1
- package/dist/{chunk-2BPXVQBP.js → chunk-MO4UMXMT.js} +238 -86
- package/dist/chunk-MO4UMXMT.js.map +1 -0
- package/dist/{chunk-2QKFRKOP.cjs → chunk-N25EH2GZ.cjs} +351 -205
- package/dist/{chunk-CSPCDVCT.js → chunk-NCV2ML25.js} +3 -3
- package/dist/{chunk-CSPCDVCT.js.map → chunk-NCV2ML25.js.map} +1 -1
- package/dist/{chunk-NUQ64S5B.cjs → chunk-OJSKSQTL.cjs} +26 -26
- package/dist/chunk-PIWMU4BV.js +9 -0
- package/dist/chunk-PIWMU4BV.js.map +1 -0
- package/dist/chunk-QFGWSFOI.cjs +10 -0
- package/dist/{chunk-GPTJYWGS.js → chunk-RNBLVGWY.js} +3 -3
- package/dist/{chunk-GPTJYWGS.js.map → chunk-RNBLVGWY.js.map} +1 -1
- package/dist/{chunk-5RBMZGKV.js → chunk-TL2MJOWM.js} +325 -57
- package/dist/chunk-TL2MJOWM.js.map +1 -0
- package/dist/{chunk-YEDPBYO5.cjs → chunk-UDQ52EEC.cjs} +4 -4
- package/dist/{chunk-EIWPJWY3.js → chunk-UE2NQPNG.js} +5 -5
- package/dist/{chunk-EIWPJWY3.js.map → chunk-UE2NQPNG.js.map} +1 -1
- package/dist/{chunk-BE2YHZXA.cjs → chunk-ULV3SCYP.cjs} +26 -15
- package/dist/{chunk-WDBFGX3U.cjs → chunk-VTTMJGWP.cjs} +72 -77
- package/dist/{chunk-556PFYES.js → chunk-WK4UGDZN.js} +290 -235
- package/dist/chunk-WK4UGDZN.js.map +1 -0
- package/dist/{chunk-JZF6UQH5.cjs → chunk-WMKA37NJ.cjs} +12 -12
- package/dist/{chunk-F2ZK6Z52.js → chunk-Y26F2X4E.js} +3 -3
- package/dist/{chunk-F2ZK6Z52.js.map → chunk-Y26F2X4E.js.map} +1 -1
- package/dist/{chunk-FK7EBZPX.cjs → chunk-Z5EEDCRI.cjs} +549 -276
- package/dist/{chunk-G6NBQYMM.js → chunk-ZGFZRVQA.js} +817 -478
- package/dist/chunk-ZGFZRVQA.js.map +1 -0
- package/dist/{chunk-QLVX7MPS.js → chunk-ZQF45LA4.js} +19 -8
- package/dist/chunk-ZQF45LA4.js.map +1 -0
- package/dist/comments.cjs +127 -65
- package/dist/comments.d.cts +35 -2
- package/dist/comments.d.ts +35 -2
- package/dist/comments.js +73 -12
- package/dist/comments.js.map +1 -1
- package/dist/{diagram-extension-22x_njXB.d.ts → diagram-extension-BMlh8GCw.d.ts} +1 -1
- package/dist/{diagram-extension-C_FSkoj9.d.cts → diagram-extension-gmndUpti.d.cts} +1 -1
- package/dist/diagram.cjs +10 -9
- package/dist/diagram.d.cts +2 -2
- package/dist/diagram.d.ts +2 -2
- package/dist/diagram.js +7 -6
- package/dist/diff.cjs +25 -25
- package/dist/diff.js +3 -3
- package/dist/docx.cjs +31 -30
- package/dist/docx.js +5 -4
- package/dist/docx.js.map +1 -1
- package/dist/embed.cjs +10 -9
- package/dist/embed.js +8 -7
- package/dist/{emoji-extension-Dk5IQLZf.d.cts → emoji-extension-DD9CQTQj.d.cts} +1 -1
- package/dist/{emoji-extension-C832oGeT.d.ts → emoji-extension-U_PWZ2fX.d.ts} +1 -1
- package/dist/emoji.cjs +10 -9
- package/dist/emoji.d.cts +2 -2
- package/dist/emoji.d.ts +2 -2
- package/dist/emoji.js +7 -6
- package/dist/engine.cjs +58 -58
- package/dist/engine.d.cts +3 -3
- package/dist/engine.d.ts +3 -3
- package/dist/engine.js +3 -3
- package/dist/{find-extension-DmK9_N81.d.ts → find-extension-BGlqooZi.d.ts} +1 -1
- package/dist/{find-extension-BfruAPLi.d.cts → find-extension-BPnAJ8bY.d.cts} +1 -1
- package/dist/find.cjs +11 -10
- package/dist/find.d.cts +1 -1
- package/dist/find.d.ts +1 -1
- package/dist/find.js +7 -6
- package/dist/headless.cjs +70 -69
- package/dist/headless.d.cts +33 -10
- package/dist/headless.d.ts +33 -10
- package/dist/headless.js +5 -4
- package/dist/highlight.cjs +7 -6
- package/dist/highlight.js +6 -5
- package/dist/highlight.js.map +1 -1
- package/dist/{index-3fYvwqUZ.d.cts → index-DN7vRKFw.d.cts} +1 -1
- package/dist/{index-CgxnzCO5.d.ts → index-DyIYI3PM.d.ts} +1 -1
- package/dist/index.cjs +82 -81
- package/dist/index.d.cts +19 -12
- package/dist/index.d.ts +19 -12
- package/dist/index.js +6 -5
- package/dist/{labels-CiCxj2jP.d.cts → labels-BJORhN93.d.ts} +10 -5
- package/dist/{labels-BhRJYo46.d.ts → labels-CVYJ2-1X.d.cts} +10 -5
- package/dist/locale-ar.cjs +10 -0
- package/dist/locale-ar.d.cts +1 -1
- package/dist/locale-ar.d.ts +1 -1
- package/dist/locale-ar.js +10 -0
- package/dist/locale-ar.js.map +1 -1
- package/dist/locale-de.cjs +4 -0
- package/dist/locale-de.d.cts +1 -1
- package/dist/locale-de.d.ts +1 -1
- package/dist/locale-de.js +4 -0
- package/dist/locale-de.js.map +1 -1
- package/dist/locale-es.cjs +4 -0
- package/dist/locale-es.d.cts +1 -1
- package/dist/locale-es.d.ts +1 -1
- package/dist/locale-es.js +4 -0
- package/dist/locale-es.js.map +1 -1
- package/dist/locale-fr.cjs +4 -0
- package/dist/locale-fr.d.cts +1 -1
- package/dist/locale-fr.d.ts +1 -1
- package/dist/locale-fr.js +4 -0
- package/dist/locale-fr.js.map +1 -1
- package/dist/locale-ja.cjs +4 -0
- package/dist/locale-ja.d.cts +1 -1
- package/dist/locale-ja.d.ts +1 -1
- package/dist/locale-ja.js +4 -0
- package/dist/locale-ja.js.map +1 -1
- package/dist/locale-pt.cjs +4 -0
- package/dist/locale-pt.d.cts +1 -1
- package/dist/locale-pt.d.ts +1 -1
- package/dist/locale-pt.js +4 -0
- package/dist/locale-pt.js.map +1 -1
- package/dist/locale-zh.cjs +4 -0
- package/dist/locale-zh.d.cts +1 -1
- package/dist/locale-zh.d.ts +1 -1
- package/dist/locale-zh.js +4 -0
- package/dist/locale-zh.js.map +1 -1
- package/dist/math.cjs +11 -10
- package/dist/math.js +9 -8
- package/dist/mention.cjs +11 -10
- package/dist/mention.js +9 -8
- package/dist/{node-reveal-DY6bimv3.d.cts → node-reveal-Be7Xa0BL.d.cts} +1 -1
- package/dist/{node-reveal-BnFcnHhz.d.ts → node-reveal-CftIEqF_.d.ts} +1 -1
- package/dist/palette-DuIFHYHc.d.cts +10 -0
- package/dist/palette-DuIFHYHc.d.ts +10 -0
- package/dist/presence.cjs +39 -38
- package/dist/presence.js +8 -7
- package/dist/presence.js.map +1 -1
- package/dist/source.cjs +10 -9
- package/dist/source.d.cts +1 -1
- package/dist/source.d.ts +1 -1
- package/dist/source.js +6 -5
- package/dist/source.js.map +1 -1
- package/dist/{stats-extension-Dmw0Ujxe.d.ts → stats-extension-Bbpr593p.d.ts} +1 -1
- package/dist/{stats-extension-D5HySF0a.d.cts → stats-extension-D7FaeydW.d.cts} +1 -1
- package/dist/stats.cjs +12 -10
- package/dist/stats.d.cts +2 -2
- package/dist/stats.d.ts +2 -2
- package/dist/stats.js +9 -7
- package/dist/styles.css +1 -1
- package/dist/{table-extension-7LkLNK6Q.d.cts → table-extension-DNijjmxV.d.cts} +1 -1
- package/dist/{table-extension-DEpV0DnP.d.ts → table-extension-Dp6FYGPh.d.ts} +1 -1
- package/dist/table.cjs +26 -25
- package/dist/table.d.cts +4 -3
- package/dist/table.d.ts +4 -3
- package/dist/table.js +7 -6
- package/dist/{toc-extension-Dvh37bWQ.d.cts → toc-extension-BGEVAmSY.d.cts} +1 -1
- package/dist/{toc-extension-SVckrFmb.d.ts → toc-extension-DZIe40eV.d.ts} +1 -1
- package/dist/toc.cjs +10 -9
- package/dist/toc.d.cts +2 -2
- package/dist/toc.d.ts +2 -2
- package/dist/toc.js +7 -6
- package/dist/{types-DMuuDBVh.d.ts → types-Byu2a9qk.d.ts} +6 -1
- package/dist/{types-Bzhq5AXj.d.cts → types-DdDlCyyZ.d.cts} +6 -1
- package/dist/view.cjs +4 -4
- package/dist/view.js +2 -2
- package/dist/yjs.cjs +121 -121
- package/dist/yjs.d.cts +2 -2
- package/dist/yjs.d.ts +2 -2
- package/dist/yjs.js +5 -5
- package/package.json +4 -3
- package/dist/chunk-2BPXVQBP.js.map +0 -1
- package/dist/chunk-556PFYES.js.map +0 -1
- package/dist/chunk-5RBMZGKV.js.map +0 -1
- package/dist/chunk-7Y4B7NHO.js.map +0 -1
- package/dist/chunk-A2IQHMIH.js.map +0 -1
- package/dist/chunk-AJT54OMZ.js.map +0 -1
- package/dist/chunk-G6NBQYMM.js.map +0 -1
- package/dist/chunk-LU3T3JB4.js.map +0 -1
- package/dist/chunk-QLVX7MPS.js.map +0 -1
- package/dist/chunk-W376L2GM.js.map +0 -1
- package/dist/chunk-XE3IYW5B.js.map +0 -1
- package/dist/palette-BrX-hEd-.d.cts +0 -4
- package/dist/palette-BrX-hEd-.d.ts +0 -4
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { useQuoinEditor, EditorView, useIsomorphicLayoutEffect, inlineDirection, useReadOnly, useEditorState, listen, wholeState, NODE_ENTRY,
|
|
1
|
+
import { englishRendererLabels, useQuoinEditor, EditorView, useIsomorphicLayoutEffect, inlineDirection, useReadOnly, ToggleBlock, useEditorState, listen, wholeState, NODE_ENTRY, EditableRegion, MarkdownText, drawnBlockCount, isComposingKey, NATIVE_INTERACTION, createAutoScroll, arrowAsWritten, domMapOf, dispatchSelection, regionInline, onBlock, hostKey, BlockErrorContext, textDiff, moveToNeighbour, visibleBlockIndexes } from './chunk-MO4UMXMT.js';
|
|
2
2
|
import { mergeLabels } from './chunk-DLQCTGC3.js';
|
|
3
3
|
import { defineBlock, devWarn, highlightedLines, blockByType, paletteBlocks, tokenColour, seedData, parsedData } from './chunk-HDJ653LM.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { calloutTone, CALLOUT_TONES } from './chunk-PIWMU4BV.js';
|
|
5
|
+
import { createPluginKey, coreExtensions, defineExtension, duplicateBlocks, primaryRegion, regionLength, headBlockId, sameSelection, Transaction, insertBlocksStep, startOf, replaceBlockStep, descendantsEnd, PASTED_HTML, LINE_JOIN, detectMac, selectedBlockIds, blockRangeSelection, runCommands, setSelection, selectedBlockRange, toMarkdown, graphemePrefix, isCollapsed, createBlock, isParagraph, nodeSelection, validateSelection, endOf, insertText, isTextFlow, markRanges, setTextAlign, orderedRange, unsetLink, textSelection, setAttrsStep, LINE_SPLIT, toggleMark, isActive, setBlockType, getPluginState, rangeContent, unsetMark, setMark, markExtent, setLink, deleteBlocks, setAttrs, HISTORY_GROUP, setBlockColor, removeMarkStep, caretMarks, replaceInlineStep, keyName, normalizeKey, movedBlockIds, isClosedToggle, selectAll, redo, undo, moveBlocksUp, moveBlocksDown, moveBlocksTo, indent, outdent } from './chunk-TL2MJOWM.js';
|
|
6
|
+
import { str, blockPlainText, sanitizeHref, inlineText, parseInline, newId, createDoc, sameItems, headingLevel, sanitizeAssetUrl, blockById, sliceInline, specFor, createBlockCodec, hasMark, alignOrUndefined, optionalNumber, requireBlockIndex, SCHEME, inlineLength, paletteOrUndefined, PALETTE, addMark, text, sameValue } from './chunk-I34BFOLL.js';
|
|
7
|
+
import { DirectionProvider, useDirection } from '@radix-ui/react-direction';
|
|
7
8
|
import * as React7 from 'react';
|
|
8
9
|
import { createContext, forwardRef, useRef, useImperativeHandle, useState, useMemo, useEffect, Fragment as Fragment$1, useContext, useSyncExternalStore, useCallback, useId, createElement } from 'react';
|
|
9
10
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -447,21 +448,9 @@ function frozen(labels) {
|
|
|
447
448
|
var englishLabels = frozen({
|
|
448
449
|
document: "Editor",
|
|
449
450
|
editorHint: "Press Escape, then Tab, to leave the editor.",
|
|
450
|
-
|
|
451
|
-
heading: (level) => `Heading ${level}`,
|
|
452
|
-
bulletItem: "Bullet list item",
|
|
453
|
-
numberedItem: (n) => `Numbered list item ${n}`,
|
|
454
|
-
task: "Task",
|
|
455
|
-
quote: "Quote",
|
|
456
|
-
callout: (tone) => `Callout, ${tone}`,
|
|
457
|
-
calloutTones: { info: "info", tip: "tip", success: "success", warning: "warning", danger: "danger" },
|
|
451
|
+
...englishRendererLabels,
|
|
458
452
|
calloutIcon: "Callout icon",
|
|
459
453
|
calloutToneMenu: (tone) => `Callout tone: ${tone}`,
|
|
460
|
-
toggle: "Toggle",
|
|
461
|
-
toggleButton: (hidden) => {
|
|
462
|
-
if (hidden === 0) return "Toggle";
|
|
463
|
-
return `Toggle, ${hidden} ${hidden === 1 ? "block" : "blocks"} hidden`;
|
|
464
|
-
},
|
|
465
454
|
placeholderEmpty: "Type '/' for commands",
|
|
466
455
|
placeholderHeading: (level) => `Heading ${level}`,
|
|
467
456
|
placeholderList: "List",
|
|
@@ -484,18 +473,21 @@ var englishLabels = frozen({
|
|
|
484
473
|
color: "Color",
|
|
485
474
|
textColor: "Text color",
|
|
486
475
|
backgroundColor: "Background color",
|
|
476
|
+
textColorSample: "A",
|
|
487
477
|
tone: "Tone",
|
|
488
478
|
icon: "Icon",
|
|
489
479
|
duplicate: "Duplicate",
|
|
490
480
|
copy: "Copy",
|
|
491
481
|
copyLink: "Copy link to block",
|
|
492
482
|
copied: "Copied",
|
|
483
|
+
copyFailed: "Could not copy",
|
|
493
484
|
moveUp: "Move up",
|
|
494
485
|
moveDown: "Move down",
|
|
495
486
|
delete: "Delete",
|
|
496
487
|
askAi: "Ask AI",
|
|
497
488
|
addBelow: "Add block below",
|
|
498
489
|
dragHandle: "Drag to move, click for options",
|
|
490
|
+
blockOptions: "Block options",
|
|
499
491
|
slashMenu: "Commands",
|
|
500
492
|
noResults: "No results",
|
|
501
493
|
resultCount: (n) => `${n} ${n === 1 ? "result" : "results"}`,
|
|
@@ -559,6 +551,7 @@ var englishLabels = frozen({
|
|
|
559
551
|
},
|
|
560
552
|
blockMoved: (position, total) => `Block moved to position ${position} of ${total}`,
|
|
561
553
|
blockInserted: (position, total) => `Block inserted at position ${position} of ${total}`,
|
|
554
|
+
blocksInserted: (n, position, total) => `${n} blocks inserted at position ${position} of ${total}`,
|
|
562
555
|
blockDeleted: (n) => n === 1 ? "Block deleted" : `${n} blocks deleted`,
|
|
563
556
|
blocksSelected: (n) => `${n} ${n === 1 ? "block" : "blocks"} selected`,
|
|
564
557
|
undoHint: (shortcut) => `Press ${shortcut} to undo`,
|
|
@@ -751,9 +744,8 @@ function CalloutPreview({ data }) {
|
|
|
751
744
|
const labels = useLabels();
|
|
752
745
|
const body = str(data.body);
|
|
753
746
|
if (body.trim() === "") return /* @__PURE__ */ jsx("p", { className: HINT, children: labels.emptyCallout });
|
|
754
|
-
const tone =
|
|
755
|
-
|
|
756
|
-
return /* @__PURE__ */ jsx("div", { "data-tone": tone, className: `rounded-md border-l-4 ${paint} p-3 text-sm`, children: body });
|
|
747
|
+
const tone = calloutTone(data.tone);
|
|
748
|
+
return /* @__PURE__ */ jsx("div", { "data-tone": tone, className: `rounded-md border-l-4 ${TONE[tone]} p-3 text-sm`, children: body });
|
|
757
749
|
}
|
|
758
750
|
|
|
759
751
|
// src/blocks/core.ts
|
|
@@ -873,7 +865,8 @@ function createVoidEditing() {
|
|
|
873
865
|
// src/blocks/text/return-focus-to-text.ts
|
|
874
866
|
function focusBelongsInText(controller, focused) {
|
|
875
867
|
if (!focused || focused === focused.ownerDocument.body) return true;
|
|
876
|
-
|
|
868
|
+
const canvasMenuButton = focused.getAttribute("aria-haspopup") === "menu" && focused.tabIndex < 0;
|
|
869
|
+
return canvasMenuButton && controller.root?.contains(focused) === true;
|
|
877
870
|
}
|
|
878
871
|
function returnFocusToText(controller) {
|
|
879
872
|
return (event) => {
|
|
@@ -949,7 +942,10 @@ var FOCUS_RING = [
|
|
|
949
942
|
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid",
|
|
950
943
|
"focus-visible:outline-[var(--quoin-ring)]"
|
|
951
944
|
].join(" ");
|
|
952
|
-
var HIGHLIGHT_BAR =
|
|
945
|
+
var HIGHLIGHT_BAR = [
|
|
946
|
+
"data-[highlighted]:shadow-[inset_2px_0_0_var(--quoin-ring)]",
|
|
947
|
+
"rtl:data-[highlighted]:shadow-[inset_-2px_0_0_var(--quoin-ring)]"
|
|
948
|
+
].join(" ");
|
|
953
949
|
|
|
954
950
|
// src/ui/touch-target.ts
|
|
955
951
|
var TAP_TARGET_FLOOR = "pointer-coarse:min-h-11";
|
|
@@ -965,24 +961,24 @@ var FLOATING_TOOLBAR = "flex w-auto items-center gap-0.5 p-1 shadow-md";
|
|
|
965
961
|
var MENU_CONTENT = "quoin bg-popover text-popover-foreground z-50 max-h-(--radix-popper-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-lg border border-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";
|
|
966
962
|
var MENU_SUB_CONTENT = "quoin bg-popover text-popover-foreground z-50 max-h-(--radix-popper-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-lg border border-border p-1 shadow-lg";
|
|
967
963
|
var MENU_ITEM = [
|
|
968
|
-
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm focus:outline-hidden select-none data-[inset]:
|
|
964
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm focus:outline-hidden select-none data-[inset]:ps-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4",
|
|
969
965
|
HIGHLIGHT_BAR,
|
|
970
966
|
TAP_TARGET_FLOOR
|
|
971
967
|
].join(" ");
|
|
972
968
|
var MENU_CHECKABLE_ITEM = [
|
|
973
|
-
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5
|
|
969
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pe-2 ps-8 text-sm focus:outline-hidden select-none",
|
|
974
970
|
HIGHLIGHT_BAR,
|
|
975
971
|
TAP_TARGET_FLOOR
|
|
976
972
|
].join(" ");
|
|
977
|
-
var MENU_ITEM_INDICATOR = "pointer-events-none absolute
|
|
973
|
+
var MENU_ITEM_INDICATOR = "pointer-events-none absolute start-2 flex size-3.5 items-center justify-center";
|
|
978
974
|
var MENU_SUB_TRIGGER = [
|
|
979
|
-
"focus:bg-accent data-[state=open]:bg-accent flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm focus:outline-hidden select-none data-[inset]:
|
|
975
|
+
"focus:bg-accent data-[state=open]:bg-accent flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm focus:outline-hidden select-none data-[inset]:ps-8",
|
|
980
976
|
HIGHLIGHT_BAR,
|
|
981
977
|
TAP_TARGET_FLOOR
|
|
982
978
|
].join(" ");
|
|
983
|
-
var MENU_LABEL = "px-2 py-1.5 text-sm font-medium data-[inset]:
|
|
979
|
+
var MENU_LABEL = "px-2 py-1.5 text-sm font-medium data-[inset]:ps-8";
|
|
984
980
|
var MENU_SEPARATOR = "bg-border -mx-1 my-1 h-px";
|
|
985
|
-
var MENU_SHORTCUT = "text-muted-foreground
|
|
981
|
+
var MENU_SHORTCUT = "text-muted-foreground ms-auto text-xs tracking-widest";
|
|
986
982
|
function ContextMenu({ modal = false, ...props }) {
|
|
987
983
|
return /* @__PURE__ */ jsx(ContextMenuPrimitive.Root, { modal, ...props });
|
|
988
984
|
}
|
|
@@ -1073,7 +1069,7 @@ var ContextMenuSubTrigger = /* @__PURE__ */ React7.forwardRef(function ContextMe
|
|
|
1073
1069
|
...props,
|
|
1074
1070
|
children: [
|
|
1075
1071
|
children,
|
|
1076
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "
|
|
1072
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "ms-auto size-4 rtl:rotate-180" })
|
|
1077
1073
|
]
|
|
1078
1074
|
}
|
|
1079
1075
|
);
|
|
@@ -1090,74 +1086,14 @@ var ContextMenuSubContent = /* @__PURE__ */ React7.forwardRef(function ContextMe
|
|
|
1090
1086
|
);
|
|
1091
1087
|
});
|
|
1092
1088
|
|
|
1093
|
-
// src/
|
|
1094
|
-
function copyThroughTextArea(text2, doc) {
|
|
1095
|
-
const previous = doc.activeElement instanceof HTMLElement ? doc.activeElement : null;
|
|
1096
|
-
const area = doc.createElement("textarea");
|
|
1097
|
-
area.value = text2;
|
|
1098
|
-
area.readOnly = true;
|
|
1099
|
-
area.setAttribute("aria-hidden", "true");
|
|
1100
|
-
area.style.cssText = "position:fixed;top:0;left:0;width:1px;height:1px;opacity:0;pointer-events:none";
|
|
1101
|
-
const beside = previous !== null && previous !== doc.body ? previous.parentElement : null;
|
|
1102
|
-
(beside ?? doc.body).append(area);
|
|
1103
|
-
area.focus({ preventScroll: true });
|
|
1104
|
-
area.select();
|
|
1105
|
-
try {
|
|
1106
|
-
return doc.execCommand("copy");
|
|
1107
|
-
} catch {
|
|
1108
|
-
return false;
|
|
1109
|
-
} finally {
|
|
1110
|
-
area.remove();
|
|
1111
|
-
previous?.focus({ preventScroll: true });
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
async function copyText(text2, doc = document) {
|
|
1115
|
-
const clipboard = doc.defaultView?.navigator.clipboard;
|
|
1116
|
-
if (clipboard === void 0) return copyThroughTextArea(text2, doc);
|
|
1117
|
-
try {
|
|
1118
|
-
await clipboard.writeText(text2);
|
|
1119
|
-
return true;
|
|
1120
|
-
} catch {
|
|
1121
|
-
return copyThroughTextArea(text2, doc);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
// src/gutter/run-on-blocks.ts
|
|
1126
|
-
function settle(tr, before) {
|
|
1127
|
-
const { doc } = tr;
|
|
1128
|
-
const { schema } = tr.before;
|
|
1129
|
-
const result = tr.selection;
|
|
1130
|
-
if (result?.kind !== "blocks" || before?.kind === "blocks") return result;
|
|
1131
|
-
const ids = selectedBlockIds(doc, result);
|
|
1132
|
-
const head = headBlockId(before);
|
|
1133
|
-
if (head !== null && ids.includes(head) && validateSelection(doc, schema, before) === before) return before;
|
|
1134
|
-
return endOf(doc, schema, ids[0]);
|
|
1135
|
-
}
|
|
1136
|
-
function runOnBlocks(controller, blockIds, command) {
|
|
1137
|
-
const { state } = controller.editor;
|
|
1138
|
-
const ids = blockIds.filter((id) => state.doc.index.has(id));
|
|
1139
|
-
if (ids.length === 0) return false;
|
|
1140
|
-
const before = state.selection;
|
|
1141
|
-
const exact = sameItems(selectedBlockIds(state.doc, before), ids);
|
|
1142
|
-
const ran = exact ? before : blockRangeSelection(ids[0], ids[ids.length - 1]);
|
|
1143
|
-
const { ok, tr } = runCommands(state, [setSelection(ran), command]);
|
|
1144
|
-
const result = ok && tr !== null ? tr : new Transaction(state).setSelection(ran);
|
|
1145
|
-
const settled = exact ? result.selection : settle(result, before);
|
|
1146
|
-
if (!sameSelection(settled, result.selection)) result.setSelection(settled);
|
|
1147
|
-
if (result.docChanged || !sameSelection(result.selection, before)) controller.dispatch(result);
|
|
1148
|
-
return ok;
|
|
1149
|
-
}
|
|
1150
|
-
var noChange = () => true;
|
|
1151
|
-
|
|
1152
|
-
// src/gutter/block-menu-items.ts
|
|
1153
|
-
var rangeOf = (blockIds) => blockRangeSelection(blockIds[0], blockIds[blockIds.length - 1]);
|
|
1089
|
+
// src/canvas/block-targets.ts
|
|
1154
1090
|
function selectedWith(state, blockId) {
|
|
1155
1091
|
const { doc, selection } = state;
|
|
1156
1092
|
const selected = selectedBlockIds(doc, selection);
|
|
1157
1093
|
const ofBlocks = selection?.kind === "blocks" || selection?.kind === "text" && selected.length > 1;
|
|
1158
1094
|
return ofBlocks && selected.includes(blockId) ? selected : null;
|
|
1159
1095
|
}
|
|
1160
|
-
function
|
|
1096
|
+
function handleTargets(state, blockId) {
|
|
1161
1097
|
const selected = selectedWith(state, blockId);
|
|
1162
1098
|
if (selected !== null) return selected;
|
|
1163
1099
|
const { doc } = state;
|
|
@@ -1167,73 +1103,10 @@ function menuTargets(state, blockId) {
|
|
|
1167
1103
|
function styleTargets(state, blockId) {
|
|
1168
1104
|
return selectedWith(state, blockId) ?? [blockId];
|
|
1169
1105
|
}
|
|
1170
|
-
function menuActions(core, extra = []) {
|
|
1171
|
-
return [...core.filter((action) => !action.destructive), ...extra, ...core.filter((action) => action.destructive)];
|
|
1172
|
-
}
|
|
1173
|
-
function markdownOf(editor, blockIds, resolveAssetUrl) {
|
|
1174
|
-
const { doc } = editor.state;
|
|
1175
|
-
const range = selectedBlockRange(doc, rangeOf(blockIds));
|
|
1176
|
-
if (range === null) return "";
|
|
1177
|
-
const blocks = editor.codec.blocksFromDoc(createDoc(doc.blocks.slice(range.start, range.end)));
|
|
1178
|
-
return toMarkdown(blocks, { schema: editor.schema, resolveAssetUrl, comments: false });
|
|
1179
|
-
}
|
|
1180
|
-
var applies = (command) => (state, blockIds) => blockIds.every((id) => state.doc.index.has(id)) && runCommands(state, [setSelection(rangeOf(blockIds)), command]).ok;
|
|
1181
|
-
function coreBlockMenuActions(labels, options) {
|
|
1182
|
-
const copy = (controller, blockIds, text2) => {
|
|
1183
|
-
void copyText(text2).then((copied) => {
|
|
1184
|
-
if (copied) options.onCopied?.();
|
|
1185
|
-
});
|
|
1186
|
-
runOnBlocks(controller, blockIds, noChange);
|
|
1187
|
-
};
|
|
1188
|
-
return [
|
|
1189
|
-
{
|
|
1190
|
-
id: "duplicate",
|
|
1191
|
-
label: labels.duplicate,
|
|
1192
|
-
icon: CopyPlus,
|
|
1193
|
-
shortcut: "Mod-d",
|
|
1194
|
-
run: (controller, blockIds) => runOnBlocks(controller, blockIds, duplicateBlocks())
|
|
1195
|
-
},
|
|
1196
|
-
{
|
|
1197
|
-
id: "copy",
|
|
1198
|
-
label: labels.copy,
|
|
1199
|
-
icon: Copy,
|
|
1200
|
-
run: (controller, blockIds) => copy(controller, blockIds, markdownOf(controller.editor, blockIds, options.resolveAssetUrl))
|
|
1201
|
-
},
|
|
1202
|
-
{
|
|
1203
|
-
id: "copy-link",
|
|
1204
|
-
label: labels.copyLink,
|
|
1205
|
-
icon: Link,
|
|
1206
|
-
run: (controller, blockIds) => copy(controller, blockIds, options.blockLink(blockIds[0]))
|
|
1207
|
-
},
|
|
1208
|
-
{
|
|
1209
|
-
id: "move-up",
|
|
1210
|
-
label: labels.moveUp,
|
|
1211
|
-
icon: ArrowUp,
|
|
1212
|
-
shortcut: "Mod-Shift-ArrowUp",
|
|
1213
|
-
run: (controller, blockIds) => runOnBlocks(controller, blockIds, moveBlocksUp),
|
|
1214
|
-
available: applies(moveBlocksUp)
|
|
1215
|
-
},
|
|
1216
|
-
{
|
|
1217
|
-
id: "move-down",
|
|
1218
|
-
label: labels.moveDown,
|
|
1219
|
-
icon: ArrowDown,
|
|
1220
|
-
shortcut: "Mod-Shift-ArrowDown",
|
|
1221
|
-
run: (controller, blockIds) => runOnBlocks(controller, blockIds, moveBlocksDown),
|
|
1222
|
-
available: applies(moveBlocksDown)
|
|
1223
|
-
},
|
|
1224
|
-
{
|
|
1225
|
-
id: "delete",
|
|
1226
|
-
label: labels.delete,
|
|
1227
|
-
icon: Trash,
|
|
1228
|
-
destructive: true,
|
|
1229
|
-
run: (controller, blockIds) => runOnBlocks(controller, blockIds, deleteBlocks)
|
|
1230
|
-
}
|
|
1231
|
-
];
|
|
1232
|
-
}
|
|
1233
1106
|
|
|
1234
1107
|
// src/gutter/block-menu-state.ts
|
|
1235
1108
|
var targetsOf = (state, blockId) => ({
|
|
1236
|
-
blockIds:
|
|
1109
|
+
blockIds: handleTargets(state, blockId),
|
|
1237
1110
|
styleIds: styleTargets(state, blockId)
|
|
1238
1111
|
});
|
|
1239
1112
|
function openBlockMenu(store, state, blockId) {
|
|
@@ -1258,9 +1131,39 @@ var TONE_ICONS = {
|
|
|
1258
1131
|
function calloutIcon(attrs, tone) {
|
|
1259
1132
|
return typeof attrs.icon === "string" && attrs.icon !== "" ? attrs.icon : TONE_ICONS[tone];
|
|
1260
1133
|
}
|
|
1134
|
+
function iconTellsTone(attrs, tone) {
|
|
1135
|
+
return calloutIcon(attrs, tone) === TONE_ICONS[tone];
|
|
1136
|
+
}
|
|
1261
1137
|
function MenuIcon({ icon: Icon2 }) {
|
|
1262
1138
|
return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex shrink-0", children: /* @__PURE__ */ jsx(Icon2, { className: "size-4" }) });
|
|
1263
1139
|
}
|
|
1140
|
+
|
|
1141
|
+
// src/gutter/run-on-blocks.ts
|
|
1142
|
+
function settle(tr, before) {
|
|
1143
|
+
const { doc } = tr;
|
|
1144
|
+
const { schema } = tr.before;
|
|
1145
|
+
const result = tr.selection;
|
|
1146
|
+
if (result?.kind !== "blocks" || before?.kind === "blocks") return result;
|
|
1147
|
+
const ids = selectedBlockIds(doc, result);
|
|
1148
|
+
const head = headBlockId(before);
|
|
1149
|
+
if (head !== null && ids.includes(head) && validateSelection(doc, schema, before) === before) return before;
|
|
1150
|
+
return endOf(doc, schema, ids[0]);
|
|
1151
|
+
}
|
|
1152
|
+
function runOnBlocks(controller, blockIds, command) {
|
|
1153
|
+
const { state } = controller.editor;
|
|
1154
|
+
const ids = blockIds.filter((id) => state.doc.index.has(id));
|
|
1155
|
+
if (ids.length === 0) return false;
|
|
1156
|
+
const before = state.selection;
|
|
1157
|
+
const exact = sameItems(selectedBlockIds(state.doc, before), ids);
|
|
1158
|
+
const ran = exact ? before : blockRangeSelection(ids[0], ids[ids.length - 1]);
|
|
1159
|
+
const { ok, tr } = runCommands(state, [setSelection(ran), command]);
|
|
1160
|
+
const result = ok && tr !== null ? tr : new Transaction(state).setSelection(ran);
|
|
1161
|
+
const settled = exact ? result.selection : settle(result, before);
|
|
1162
|
+
if (!sameSelection(settled, result.selection)) result.setSelection(settled);
|
|
1163
|
+
if (result.docChanged || !sameSelection(result.selection, before)) controller.dispatch(result);
|
|
1164
|
+
return ok;
|
|
1165
|
+
}
|
|
1166
|
+
var noChange = () => true;
|
|
1264
1167
|
var sharedTone = (blocks) => {
|
|
1265
1168
|
const tones = new Set(blocks.map((block) => calloutTone(block.attrs.tone)));
|
|
1266
1169
|
return tones.size === 1 ? [...tones][0] : "";
|
|
@@ -1292,7 +1195,7 @@ function sharedColor(blocks, key) {
|
|
|
1292
1195
|
return colors.size === 1 ? [...colors][0] : "";
|
|
1293
1196
|
}
|
|
1294
1197
|
var swatches = {
|
|
1295
|
-
color: (name) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-color": name, className: "w-4 text-center font-medium", children:
|
|
1198
|
+
color: (name, labels) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-color": name, className: "w-4 text-center font-medium", children: labels.textColorSample ?? englishLabels.textColorSample }),
|
|
1296
1199
|
background: (name) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-bg": name, className: "size-4 rounded-sm border border-border" })
|
|
1297
1200
|
};
|
|
1298
1201
|
function ColorGroups({ kit, value, onPick }) {
|
|
@@ -1302,7 +1205,7 @@ function ColorGroups({ kit, value, onPick }) {
|
|
|
1302
1205
|
const group = (key, title, titleId) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1303
1206
|
/* @__PURE__ */ jsx(kit.Label, { id: titleId, children: title }),
|
|
1304
1207
|
/* @__PURE__ */ jsx(kit.RadioGroup, { "aria-labelledby": titleId, value: value[key], onValueChange: (name) => onPick(key, name), children: PALETTE.map((name) => /* @__PURE__ */ jsxs(kit.RadioItem, { value: name, indicator: /* @__PURE__ */ jsx(Check, { className: "size-4" }), children: [
|
|
1305
|
-
swatches[key](name),
|
|
1208
|
+
swatches[key](name, labels),
|
|
1306
1209
|
labels.colors[name]
|
|
1307
1210
|
] }, name)) })
|
|
1308
1211
|
] });
|
|
@@ -1623,13 +1526,16 @@ function selectAndFocus({ controller }, command) {
|
|
|
1623
1526
|
controller.focus();
|
|
1624
1527
|
return true;
|
|
1625
1528
|
}
|
|
1626
|
-
function travel({ controller, blockId, area }, command) {
|
|
1529
|
+
function travel({ controller, blockId, area, reveal }, command) {
|
|
1627
1530
|
const before = area.value;
|
|
1628
1531
|
controller.run(command);
|
|
1629
1532
|
const { selection } = controller.editor.state;
|
|
1630
1533
|
const edit2 = textDiff(before, area.value);
|
|
1631
1534
|
if (selection?.kind !== "node" || selection.block !== blockId) controller.focus();
|
|
1632
|
-
else if (edit2 !== null)
|
|
1535
|
+
else if (edit2 !== null) {
|
|
1536
|
+
area.setSelectionRange(edit2.from + edit2.insert.length, edit2.from + edit2.insert.length);
|
|
1537
|
+
reveal();
|
|
1538
|
+
}
|
|
1633
1539
|
return true;
|
|
1634
1540
|
}
|
|
1635
1541
|
function runCodeAction(action, context) {
|
|
@@ -1714,7 +1620,7 @@ function CodeEditor({ block, controller, field, focusControls }) {
|
|
|
1714
1620
|
const area = event.currentTarget;
|
|
1715
1621
|
if (isComposingKey(controller, event.nativeEvent)) return;
|
|
1716
1622
|
const action = codeKeyAction(event, fieldOf(area), controller.isMac);
|
|
1717
|
-
if (action !== null && runCodeAction(action, { controller, blockId, area, edit: edit2, focusControls })) event.preventDefault();
|
|
1623
|
+
if (action !== null && runCodeAction(action, { controller, blockId, area, edit: edit2, reveal, focusControls })) event.preventDefault();
|
|
1718
1624
|
};
|
|
1719
1625
|
return /* @__PURE__ */ jsx("div", { ...{ [NATIVE_INTERACTION]: "" }, dir: "ltr", className: "quoin-code-scroll", children: /* @__PURE__ */ jsxs("div", { className: "quoin-code-lines", children: [
|
|
1720
1626
|
/* @__PURE__ */ jsx(
|
|
@@ -1752,6 +1658,38 @@ function CodeEditor({ block, controller, field, focusControls }) {
|
|
|
1752
1658
|
)
|
|
1753
1659
|
] }) });
|
|
1754
1660
|
}
|
|
1661
|
+
|
|
1662
|
+
// src/gutter/copy-text.ts
|
|
1663
|
+
function copyThroughTextArea(text2, doc) {
|
|
1664
|
+
const previous = doc.activeElement instanceof HTMLElement ? doc.activeElement : null;
|
|
1665
|
+
const area = doc.createElement("textarea");
|
|
1666
|
+
area.value = text2;
|
|
1667
|
+
area.readOnly = true;
|
|
1668
|
+
area.setAttribute("aria-hidden", "true");
|
|
1669
|
+
area.style.cssText = "position:fixed;top:0;left:0;width:1px;height:1px;opacity:0;pointer-events:none";
|
|
1670
|
+
const beside = previous !== null && previous !== doc.body ? previous.parentElement : null;
|
|
1671
|
+
(beside ?? doc.body).append(area);
|
|
1672
|
+
area.focus({ preventScroll: true });
|
|
1673
|
+
area.select();
|
|
1674
|
+
try {
|
|
1675
|
+
return doc.execCommand("copy");
|
|
1676
|
+
} catch {
|
|
1677
|
+
return false;
|
|
1678
|
+
} finally {
|
|
1679
|
+
area.remove();
|
|
1680
|
+
previous?.focus({ preventScroll: true });
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
async function copyText(text2, doc = document) {
|
|
1684
|
+
const clipboard = doc.defaultView?.navigator.clipboard;
|
|
1685
|
+
if (clipboard === void 0) return copyThroughTextArea(text2, doc);
|
|
1686
|
+
try {
|
|
1687
|
+
await clipboard.writeText(text2);
|
|
1688
|
+
return true;
|
|
1689
|
+
} catch {
|
|
1690
|
+
return copyThroughTextArea(text2, doc);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1755
1693
|
var BASE = [
|
|
1756
1694
|
"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",
|
|
1757
1695
|
FOCUS_RING
|
|
@@ -1848,10 +1786,12 @@ var PopoverAnchor = /* @__PURE__ */ React7.forwardRef(function PopoverAnchor2(pr
|
|
|
1848
1786
|
return /* @__PURE__ */ jsx(PopoverPrimitive.Anchor, { ref, "data-slot": "popover-anchor", ...props });
|
|
1849
1787
|
});
|
|
1850
1788
|
var PopoverContent = /* @__PURE__ */ React7.forwardRef(function PopoverContent2({ className, align = "center", sideOffset = 4, ...props }, ref) {
|
|
1789
|
+
const dir = useDirection();
|
|
1851
1790
|
return /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
1852
1791
|
PopoverPrimitive.Content,
|
|
1853
1792
|
{
|
|
1854
1793
|
ref,
|
|
1794
|
+
dir,
|
|
1855
1795
|
"data-slot": "popover-content",
|
|
1856
1796
|
align,
|
|
1857
1797
|
sideOffset,
|
|
@@ -2018,6 +1958,7 @@ var CodeBlock = ({ block, controller }) => {
|
|
|
2018
1958
|
};
|
|
2019
1959
|
function FilePickerButton({ label, accept, disabled, busy = false, onPick }) {
|
|
2020
1960
|
const inputRef = React7.useRef(null);
|
|
1961
|
+
const wrapper = React7.useRef(null);
|
|
2021
1962
|
const restoreFocus = React7.useRef(false);
|
|
2022
1963
|
React7.useEffect(() => {
|
|
2023
1964
|
if (disabled || !restoreFocus.current) return;
|
|
@@ -2025,34 +1966,49 @@ function FilePickerButton({ label, accept, disabled, busy = false, onPick }) {
|
|
|
2025
1966
|
const { activeElement } = document;
|
|
2026
1967
|
if (activeElement === null || activeElement === document.body) inputRef.current?.focus();
|
|
2027
1968
|
}, [disabled]);
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
},
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
1969
|
+
React7.useEffect(() => {
|
|
1970
|
+
const doc = wrapper.current?.ownerDocument;
|
|
1971
|
+
const pressed = (event) => {
|
|
1972
|
+
if (!wrapper.current?.contains(event.target)) restoreFocus.current = false;
|
|
1973
|
+
};
|
|
1974
|
+
doc?.addEventListener("pointerdown", pressed, true);
|
|
1975
|
+
return () => doc?.removeEventListener("pointerdown", pressed, true);
|
|
1976
|
+
}, []);
|
|
1977
|
+
return /* @__PURE__ */ jsxs(
|
|
1978
|
+
"span",
|
|
1979
|
+
{
|
|
1980
|
+
ref: wrapper,
|
|
1981
|
+
className: "relative inline-flex rounded-md has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-[var(--quoin-ring)]",
|
|
1982
|
+
children: [
|
|
1983
|
+
/* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", size: "sm", children: /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: disabled || busy ? "pointer-events-none opacity-50" : void 0, children: [
|
|
1984
|
+
/* @__PURE__ */ jsx(Upload, { "aria-hidden": "true" }),
|
|
1985
|
+
label
|
|
1986
|
+
] }) }),
|
|
1987
|
+
/* @__PURE__ */ jsx(
|
|
1988
|
+
"input",
|
|
1989
|
+
{
|
|
1990
|
+
ref: inputRef,
|
|
1991
|
+
type: "file",
|
|
1992
|
+
accept,
|
|
1993
|
+
"aria-label": label,
|
|
1994
|
+
disabled,
|
|
1995
|
+
"aria-disabled": busy || void 0,
|
|
1996
|
+
className: "absolute inset-0 cursor-pointer opacity-0 disabled:cursor-not-allowed aria-disabled:cursor-not-allowed",
|
|
1997
|
+
onClick: (e) => {
|
|
1998
|
+
if (busy) e.preventDefault();
|
|
1999
|
+
},
|
|
2000
|
+
onChange: (e) => {
|
|
2001
|
+
const file = e.target.files?.[0];
|
|
2002
|
+
e.target.value = "";
|
|
2003
|
+
if (!file || busy) return;
|
|
2004
|
+
restoreFocus.current = true;
|
|
2005
|
+
onPick(file);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
)
|
|
2009
|
+
]
|
|
2010
|
+
}
|
|
2011
|
+
);
|
|
2056
2012
|
}
|
|
2057
2013
|
|
|
2058
2014
|
// src/blocks/void/interactive-target.ts
|
|
@@ -2099,6 +2055,29 @@ function selectHandlers(controller, blockId, root, onSelect) {
|
|
|
2099
2055
|
}
|
|
2100
2056
|
};
|
|
2101
2057
|
}
|
|
2058
|
+
|
|
2059
|
+
// src/blocks/upload/upload-targets.ts
|
|
2060
|
+
var targets = /* @__PURE__ */ new WeakMap();
|
|
2061
|
+
function offerUploadTarget(controller, blockId, target) {
|
|
2062
|
+
const offered = targets.get(controller) ?? /* @__PURE__ */ new Map();
|
|
2063
|
+
targets.set(controller, offered.set(blockId, target));
|
|
2064
|
+
return () => {
|
|
2065
|
+
if (offered.get(blockId) === target) offered.delete(blockId);
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
function fillSelectedBlock(controller, files) {
|
|
2069
|
+
const { selection } = controller.editor.state;
|
|
2070
|
+
const target = selection?.kind === "node" ? targets.get(controller)?.get(selection.block) : void 0;
|
|
2071
|
+
const taken = target && files.find((file) => target.takes(file));
|
|
2072
|
+
if (taken === void 0) return files;
|
|
2073
|
+
target.start(taken);
|
|
2074
|
+
return files.filter((file) => file !== taken);
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
// src/upload/upload-error.ts
|
|
2078
|
+
var uploadError = (cause, failed) => cause instanceof Error && cause.message ? cause : new Error(failed);
|
|
2079
|
+
|
|
2080
|
+
// src/blocks/upload/use-block-upload.ts
|
|
2102
2081
|
var IDLE = { busy: false, error: "" };
|
|
2103
2082
|
function useBlockUpload(controller, blockId, area, uploader) {
|
|
2104
2083
|
const { upload, resolveAssetUrl, onError, labels } = useQuoin();
|
|
@@ -2112,12 +2091,13 @@ function useBlockUpload(controller, blockId, area, uploader) {
|
|
|
2112
2091
|
if (started.current === run) setState(next);
|
|
2113
2092
|
};
|
|
2114
2093
|
setState({ busy: true, error: "" });
|
|
2094
|
+
controller.announce(labels.uploading);
|
|
2115
2095
|
const onLand = () => {
|
|
2116
2096
|
if (area.current?.contains(area.current.ownerDocument.activeElement)) focusNode(controller, blockId);
|
|
2117
2097
|
};
|
|
2118
2098
|
uploader(controller, blockId, file, { upload, resolveAssetUrl, onLand }).then(
|
|
2119
2099
|
() => settle2(IDLE),
|
|
2120
|
-
(cause) => settle2({ busy: false, error: cause
|
|
2100
|
+
(cause) => settle2({ busy: false, error: uploadError(cause, labels.uploadFailed).message })
|
|
2121
2101
|
);
|
|
2122
2102
|
},
|
|
2123
2103
|
[controller, blockId, area, uploader, upload, resolveAssetUrl, onError, labels]
|
|
@@ -2142,6 +2122,7 @@ function UploadArea(props) {
|
|
|
2142
2122
|
element?.addEventListener("drop", onDrop);
|
|
2143
2123
|
return () => element?.removeEventListener("drop", onDrop);
|
|
2144
2124
|
}, [start, takes]);
|
|
2125
|
+
useEffect(() => offerUploadTarget(controller, blockId, { takes, start }), [controller, blockId, takes, start]);
|
|
2145
2126
|
const onPaste = (event) => {
|
|
2146
2127
|
const file = fileIn(event.clipboardData, takes);
|
|
2147
2128
|
if (file === void 0) return;
|
|
@@ -2162,7 +2143,7 @@ function UploadArea(props) {
|
|
|
2162
2143
|
] }),
|
|
2163
2144
|
children,
|
|
2164
2145
|
/* @__PURE__ */ jsx("p", { className: "quoin-block-message", children: upload ? hint : labels.uploadsUnavailable }),
|
|
2165
|
-
/* @__PURE__ */ jsx("p", {
|
|
2146
|
+
busy && /* @__PURE__ */ jsx("p", { className: "quoin-block-message", children: labels.uploading }),
|
|
2166
2147
|
error !== "" && /* @__PURE__ */ jsx("p", { role: "alert", className: "quoin-block-message text-destructive", children: error })
|
|
2167
2148
|
]
|
|
2168
2149
|
}
|
|
@@ -2170,8 +2151,8 @@ function UploadArea(props) {
|
|
|
2170
2151
|
}
|
|
2171
2152
|
var CONTROL = "input:not([disabled]), button:not([disabled])";
|
|
2172
2153
|
var typed = (event) => !event.ctrlKey && !event.metaKey && [...event.key].length === 1;
|
|
2173
|
-
function useNodeKeys(controller, blockId, editable,
|
|
2174
|
-
const { editing, stop } = useVoidEditing(controller, blockId, editable &&
|
|
2154
|
+
function useNodeKeys(controller, blockId, editable, opens2, root, { enter, type } = {}) {
|
|
2155
|
+
const { editing, stop } = useVoidEditing(controller, blockId, editable && opens2);
|
|
2175
2156
|
useEffect(() => {
|
|
2176
2157
|
if (!editing) return;
|
|
2177
2158
|
stop();
|
|
@@ -2191,7 +2172,7 @@ function useNodeKeys(controller, blockId, editable, opens, root, { enter, type }
|
|
|
2191
2172
|
}, [controller, blockId, editable, type]);
|
|
2192
2173
|
}
|
|
2193
2174
|
|
|
2194
|
-
// src/
|
|
2175
|
+
// src/toolbar/bubble-part.ts
|
|
2195
2176
|
var BUBBLE_PART = "data-quoin-bubble";
|
|
2196
2177
|
var partIds = /* @__PURE__ */ new WeakMap();
|
|
2197
2178
|
var created = 0;
|
|
@@ -2275,6 +2256,7 @@ function assetOf(controller, blockId, type) {
|
|
|
2275
2256
|
if (block?.type !== type || controller.readOnly) return null;
|
|
2276
2257
|
return { url: str(block.attrs.url), storageKey: str(block.attrs.storageKey) };
|
|
2277
2258
|
}
|
|
2259
|
+
var isStored = (stored) => typeof stored?.storageKey === "string" && stored.storageKey !== "";
|
|
2278
2260
|
var sameAsset = (a, b) => a !== null && b !== null && a.url === b.url && a.storageKey === b.storageKey;
|
|
2279
2261
|
function writeAttrs(controller, blockId, patch, recorded) {
|
|
2280
2262
|
const { editor } = controller;
|
|
@@ -2285,7 +2267,7 @@ async function uploadInto(controller, blockId, file, { type, category }, { uploa
|
|
|
2285
2267
|
const token = claimUpload(controller, blockId);
|
|
2286
2268
|
const before = assetOf(controller, blockId, type);
|
|
2287
2269
|
const stored = await upload(file, category);
|
|
2288
|
-
if (
|
|
2270
|
+
if (!isStored(stored)) throw new Error();
|
|
2289
2271
|
if (!isNewestUpload(controller, blockId, token) || !sameAsset(assetOf(controller, blockId, type), before)) return;
|
|
2290
2272
|
onLand?.();
|
|
2291
2273
|
await land2(stored, token);
|
|
@@ -2335,7 +2317,7 @@ function useReplacePicker(controller, blockId, uploader, accept) {
|
|
|
2335
2317
|
if (file === void 0 || !upload) return;
|
|
2336
2318
|
controller.announce(labels.uploading);
|
|
2337
2319
|
uploader(controller, blockId, file, { upload, resolveAssetUrl }).catch(
|
|
2338
|
-
(cause) => onError(cause
|
|
2320
|
+
(cause) => onError(uploadError(cause, labels.uploadFailed))
|
|
2339
2321
|
);
|
|
2340
2322
|
};
|
|
2341
2323
|
return {
|
|
@@ -2385,37 +2367,10 @@ var deleteItem = (blockId, title) => ({
|
|
|
2385
2367
|
run: (controller) => deleteBlock(controller, blockId)
|
|
2386
2368
|
});
|
|
2387
2369
|
var PRESSED = "aria-pressed:bg-accent aria-pressed:text-accent-foreground";
|
|
2388
|
-
function markItem(mark, title, icon) {
|
|
2389
|
-
return {
|
|
2390
|
-
id: mark,
|
|
2391
|
-
title,
|
|
2392
|
-
icon,
|
|
2393
|
-
isActive: (state) => isActive(state, mark),
|
|
2394
|
-
run: (controller) => void controller.run(toggleMark(mark))
|
|
2395
|
-
};
|
|
2396
|
-
}
|
|
2397
|
-
var linkItem = (labels, openLink) => ({
|
|
2398
|
-
id: "link",
|
|
2399
|
-
title: labels.link,
|
|
2400
|
-
icon: Link,
|
|
2401
|
-
run: openLink
|
|
2402
|
-
});
|
|
2403
|
-
function bubbleItems(labels, openLink) {
|
|
2404
|
-
return {
|
|
2405
|
-
inline: [
|
|
2406
|
-
markItem("bold", labels.bold, Bold),
|
|
2407
|
-
markItem("italic", labels.italic, Italic),
|
|
2408
|
-
markItem("underline", labels.underline, Underline),
|
|
2409
|
-
markItem("strike", labels.strike, Strikethrough),
|
|
2410
|
-
markItem("code", labels.code, Code),
|
|
2411
|
-
linkItem(labels, openLink)
|
|
2412
|
-
],
|
|
2413
|
-
scripts: [markItem("sub", labels.subscript, Subscript), markItem("sup", labels.superscript, Superscript)]
|
|
2414
|
-
};
|
|
2415
|
-
}
|
|
2416
2370
|
function ToolbarButton({ item, state, controller, buttonRef }) {
|
|
2417
2371
|
if (item.available?.(state) === false) return null;
|
|
2418
2372
|
const Icon2 = item.icon;
|
|
2373
|
+
const disabled = item.enabled?.(state) === false;
|
|
2419
2374
|
return /* @__PURE__ */ jsx(
|
|
2420
2375
|
Button,
|
|
2421
2376
|
{
|
|
@@ -2425,8 +2380,12 @@ function ToolbarButton({ item, state, controller, buttonRef }) {
|
|
|
2425
2380
|
size: "icon-sm",
|
|
2426
2381
|
"aria-label": item.title,
|
|
2427
2382
|
"aria-pressed": item.isActive?.(state),
|
|
2383
|
+
"aria-haspopup": item.opensDialog ? "dialog" : void 0,
|
|
2384
|
+
"aria-disabled": disabled || void 0,
|
|
2428
2385
|
className: PRESSED,
|
|
2429
|
-
onClick: () =>
|
|
2386
|
+
onClick: () => {
|
|
2387
|
+
if (!disabled) item.run(controller);
|
|
2388
|
+
},
|
|
2430
2389
|
children: /* @__PURE__ */ jsx(Icon2, {})
|
|
2431
2390
|
}
|
|
2432
2391
|
);
|
|
@@ -2535,10 +2494,12 @@ function createSettled(controller) {
|
|
|
2535
2494
|
let pressed = false;
|
|
2536
2495
|
let composing = false;
|
|
2537
2496
|
const inEditor = (event) => event.target instanceof Node && controller.root?.contains(event.target) === true;
|
|
2497
|
+
const release = () => void (pressed = false);
|
|
2538
2498
|
const handlers = {
|
|
2539
2499
|
pointerdown: (event) => void (pressed ||= inEditor(event)),
|
|
2540
|
-
pointerup:
|
|
2541
|
-
pointercancel:
|
|
2500
|
+
pointerup: release,
|
|
2501
|
+
pointercancel: release,
|
|
2502
|
+
dragend: release,
|
|
2542
2503
|
compositionstart: (event) => void (composing ||= inEditor(event)),
|
|
2543
2504
|
compositionend: () => void (composing = false),
|
|
2544
2505
|
focusin: () => {
|
|
@@ -2559,9 +2520,27 @@ function createSettled(controller) {
|
|
|
2559
2520
|
doc.addEventListener(type, listener, true);
|
|
2560
2521
|
return () => doc.removeEventListener(type, listener, true);
|
|
2561
2522
|
});
|
|
2523
|
+
const timers = /* @__PURE__ */ new Set();
|
|
2524
|
+
const afterView = () => {
|
|
2525
|
+
const timer = setTimeout(() => {
|
|
2526
|
+
timers.delete(timer);
|
|
2527
|
+
notify();
|
|
2528
|
+
}, 0);
|
|
2529
|
+
timers.add(timer);
|
|
2530
|
+
};
|
|
2531
|
+
const stopDoc = listen(doc, { compositionend: afterView });
|
|
2532
|
+
const onWindowBlur = () => {
|
|
2533
|
+
release();
|
|
2534
|
+
notify();
|
|
2535
|
+
};
|
|
2536
|
+
const stopWindow = doc.defaultView ? listen(doc.defaultView, { blur: onWindowBlur }) : () => {
|
|
2537
|
+
};
|
|
2562
2538
|
const stopState = controller.editor.subscribe(() => notify());
|
|
2563
2539
|
return () => {
|
|
2564
2540
|
listeners2.forEach((remove) => remove());
|
|
2541
|
+
timers.forEach(clearTimeout);
|
|
2542
|
+
stopDoc();
|
|
2543
|
+
stopWindow();
|
|
2565
2544
|
stopState();
|
|
2566
2545
|
};
|
|
2567
2546
|
}
|
|
@@ -2620,6 +2599,23 @@ function requestAlt(controller, blockId) {
|
|
|
2620
2599
|
listeners.get(controller)?.(blockId);
|
|
2621
2600
|
}
|
|
2622
2601
|
|
|
2602
|
+
// src/blocks/image/owed-size.ts
|
|
2603
|
+
var owedSizes = /* @__PURE__ */ new WeakMap();
|
|
2604
|
+
var showsKey = (controller, blockId, storageKey) => sameAsset(assetOf(controller, blockId, "IMAGE"), { url: "", storageKey });
|
|
2605
|
+
function writeSize(controller, blockId, measured) {
|
|
2606
|
+
if (!isNewestUpload(controller, blockId, measured.token)) return;
|
|
2607
|
+
if (showsKey(controller, blockId, measured.storageKey)) writeAttrs(controller, blockId, { ...measured.size }, false);
|
|
2608
|
+
else owedSizes.set(controller, (owedSizes.get(controller) ?? /* @__PURE__ */ new Map()).set(blockId, measured));
|
|
2609
|
+
}
|
|
2610
|
+
function payOwedSize(controller, blockId) {
|
|
2611
|
+
const owing = owedSizes.get(controller);
|
|
2612
|
+
const owed = owing?.get(blockId);
|
|
2613
|
+
if (owed === void 0 || !showsKey(controller, blockId, owed.storageKey)) return;
|
|
2614
|
+
owing.delete(blockId);
|
|
2615
|
+
const unsized = blockById(controller.editor.state.doc, blockId)?.attrs.width === void 0;
|
|
2616
|
+
if (unsized && isNewestUpload(controller, blockId, owed.token)) writeAttrs(controller, blockId, { ...owed.size }, false);
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2623
2619
|
// src/blocks/image/resize-width.ts
|
|
2624
2620
|
var MIN_WIDTH = 10;
|
|
2625
2621
|
var MAX_WIDTH = 100;
|
|
@@ -2731,6 +2727,7 @@ function ImageFigure({ block, controller, readOnly }) {
|
|
|
2731
2727
|
const badgeText = labels.noAltBadge ?? englishLabels.noAltBadge;
|
|
2732
2728
|
const badgeName = labels.addAltText ?? englishLabels.addAltText;
|
|
2733
2729
|
const translatedAlone = badgeText !== englishLabels.noAltBadge && badgeName === englishLabels.addAltText;
|
|
2730
|
+
useEffect(() => payOwedSize(controller, id), [controller, id, attrs.storageKey]);
|
|
2734
2731
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2735
2732
|
/* @__PURE__ */ jsxs("div", { ref: frame, className: "quoin-image-frame", style: width === void 0 ? void 0 : { width: `${width}%` }, children: [
|
|
2736
2733
|
src !== null && src !== failed ? /* @__PURE__ */ jsx(
|
|
@@ -2787,12 +2784,14 @@ function ImageFigure({ block, controller, readOnly }) {
|
|
|
2787
2784
|
}
|
|
2788
2785
|
|
|
2789
2786
|
// src/menus/link/link-address.ts
|
|
2790
|
-
var
|
|
2791
|
-
var EMAIL = /^[
|
|
2787
|
+
var DOMAIN_START = /^[^/#?.]/;
|
|
2788
|
+
var EMAIL = /^[^@/]+@([^@/]+)$/;
|
|
2792
2789
|
var WHITESPACE = /\s/;
|
|
2790
|
+
var isEmail = (address) => EMAIL.exec(address)?.[1].slice(1, -1).includes(".") === true;
|
|
2791
|
+
var isBareDomain = (address) => DOMAIN_START.test(address) && address.includes(".", 1);
|
|
2793
2792
|
function scheme(address) {
|
|
2794
|
-
if (
|
|
2795
|
-
return
|
|
2793
|
+
if (isEmail(address)) return `mailto:${address}`;
|
|
2794
|
+
return isBareDomain(address) ? `https://${address}` : address;
|
|
2796
2795
|
}
|
|
2797
2796
|
function linkAddress(typed2) {
|
|
2798
2797
|
const address = typed2.trim();
|
|
@@ -2872,9 +2871,7 @@ var isImageFile = (file) => file.type.startsWith("image/");
|
|
|
2872
2871
|
async function measureImage(controller, blockId, storageKey, resolveAssetUrl, token = claimUpload(controller, blockId)) {
|
|
2873
2872
|
const src = assetHref(resolveAssetUrl, storageKey);
|
|
2874
2873
|
const size = src === null ? null : await intrinsicSize(src);
|
|
2875
|
-
if (size
|
|
2876
|
-
writeAttrs(controller, blockId, { ...size }, false);
|
|
2877
|
-
}
|
|
2874
|
+
if (size) writeSize(controller, blockId, { storageKey, size, token });
|
|
2878
2875
|
}
|
|
2879
2876
|
var uploadImage = (controller, blockId, file, host) => uploadInto(controller, blockId, file, { type: "IMAGE", category: "image" }, host, async ({ storageKey }, token) => {
|
|
2880
2877
|
writeAttrs(controller, blockId, { storageKey, url: void 0, width: void 0, height: void 0 }, true);
|
|
@@ -2960,6 +2957,28 @@ var ImageBlock = ({ block, controller }) => {
|
|
|
2960
2957
|
}
|
|
2961
2958
|
);
|
|
2962
2959
|
};
|
|
2960
|
+
function useClosing(controller, { store, onClose, onReturn }) {
|
|
2961
|
+
const closed = useRef(false);
|
|
2962
|
+
const finish = (save, refocus = false) => {
|
|
2963
|
+
if (closed.current) return false;
|
|
2964
|
+
closed.current = true;
|
|
2965
|
+
if (save) store(refocus);
|
|
2966
|
+
return true;
|
|
2967
|
+
};
|
|
2968
|
+
const latestFinish = useRef(finish);
|
|
2969
|
+
latestFinish.current = finish;
|
|
2970
|
+
useEffect(() => {
|
|
2971
|
+
closed.current = false;
|
|
2972
|
+
return () => void latestFinish.current(true);
|
|
2973
|
+
}, []);
|
|
2974
|
+
const close = (save, refocus = true) => {
|
|
2975
|
+
if (!refocus) controller.readSelectionFromDom();
|
|
2976
|
+
if (!finish(save, refocus)) return;
|
|
2977
|
+
onClose();
|
|
2978
|
+
if (refocus) onReturn();
|
|
2979
|
+
};
|
|
2980
|
+
return { finish, close };
|
|
2981
|
+
}
|
|
2963
2982
|
function useOutsidePress(element, ownerDocument, onPress) {
|
|
2964
2983
|
const latest = useRef(onPress);
|
|
2965
2984
|
useIsomorphicLayoutEffect(() => {
|
|
@@ -2997,29 +3016,18 @@ function AltTextPopover({ blockId, controller, anchor, opener, alt, decorative,
|
|
|
2997
3016
|
const [marked, setMarked] = useState(decorative);
|
|
2998
3017
|
const fieldId = useId();
|
|
2999
3018
|
const hintId = `${fieldId}-hint`;
|
|
3000
|
-
const
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
latestFinish.current = finish;
|
|
3010
|
-
useEffect(() => {
|
|
3011
|
-
closed.current = false;
|
|
3012
|
-
return () => void latestFinish.current(true);
|
|
3013
|
-
}, []);
|
|
3019
|
+
const { finish, close } = useClosing(controller, {
|
|
3020
|
+
store() {
|
|
3021
|
+
if (draft !== alt || marked !== decorative) {
|
|
3022
|
+
storeAlt(controller, blockId, opened, { alt: draft, decorative: marked || void 0 });
|
|
3023
|
+
}
|
|
3024
|
+
},
|
|
3025
|
+
onClose,
|
|
3026
|
+
onReturn: () => controller.focus()
|
|
3027
|
+
});
|
|
3014
3028
|
useEffect(() => {
|
|
3015
3029
|
if ((alt !== opened.alt || decorative !== opened.decorative) && finish(false)) onClose();
|
|
3016
3030
|
});
|
|
3017
|
-
const close = (store, refocus = true) => {
|
|
3018
|
-
if (!refocus) controller.readSelectionFromDom();
|
|
3019
|
-
if (!finish(store)) return;
|
|
3020
|
-
onClose();
|
|
3021
|
-
if (refocus) controller.focus();
|
|
3022
|
-
};
|
|
3023
3031
|
const content = useRef(null);
|
|
3024
3032
|
const ownerDocument = useCallback(() => controller.root?.ownerDocument ?? document, [controller]);
|
|
3025
3033
|
useOutsidePress(content, ownerDocument, (target) => {
|
|
@@ -3099,6 +3107,21 @@ function AltTextPopover({ blockId, controller, anchor, opener, alt, decorative,
|
|
|
3099
3107
|
}
|
|
3100
3108
|
);
|
|
3101
3109
|
}
|
|
3110
|
+
function around(a, b) {
|
|
3111
|
+
const left = Math.min(a.left, b.left);
|
|
3112
|
+
const top = Math.min(a.top, b.top);
|
|
3113
|
+
return new DOMRect(left, top, Math.max(a.right, b.right) - left, Math.max(a.bottom, b.bottom) - top);
|
|
3114
|
+
}
|
|
3115
|
+
function useAroundToolbar(part, inToolbar) {
|
|
3116
|
+
return useMemo(() => {
|
|
3117
|
+
const measure2 = () => {
|
|
3118
|
+
const box = part.current.getBoundingClientRect();
|
|
3119
|
+
const toolbar = inToolbar.current?.closest('[role="toolbar"]')?.getBoundingClientRect();
|
|
3120
|
+
return toolbar === void 0 ? box : around(box, toolbar);
|
|
3121
|
+
};
|
|
3122
|
+
return { current: virtualAnchor(measure2, () => part.current.contextElement) };
|
|
3123
|
+
}, [part, inToolbar]);
|
|
3124
|
+
}
|
|
3102
3125
|
|
|
3103
3126
|
// src/blocks/image/image-toolbar-items.ts
|
|
3104
3127
|
var ALIGNS = [
|
|
@@ -3158,6 +3181,7 @@ function ImageToolbar({ blockId, controller, editingAlt, onEditAlt }) {
|
|
|
3158
3181
|
const picker = useReplacePicker(controller, blockId, uploadImage, "image/*");
|
|
3159
3182
|
const altButton = useRef(null);
|
|
3160
3183
|
const anchor = usePartAnchor(controller, blockId, ".quoin-image-frame");
|
|
3184
|
+
const altAnchor = useAroundToolbar(anchor, altButton);
|
|
3161
3185
|
const attrs = blockById(state.doc, blockId)?.attrs ?? {};
|
|
3162
3186
|
const items = imageToolbarItems(blockId, labels, {
|
|
3163
3187
|
replace: picker.open,
|
|
@@ -3182,7 +3206,7 @@ function ImageToolbar({ blockId, controller, editingAlt, onEditAlt }) {
|
|
|
3182
3206
|
{
|
|
3183
3207
|
blockId,
|
|
3184
3208
|
controller,
|
|
3185
|
-
anchor,
|
|
3209
|
+
anchor: altAnchor,
|
|
3186
3210
|
opener: altButton,
|
|
3187
3211
|
alt: str(attrs.alt),
|
|
3188
3212
|
decorative: attrs.decorative === true,
|
|
@@ -3312,7 +3336,7 @@ var DropdownMenuSubTrigger = /* @__PURE__ */ React7.forwardRef(function Dropdown
|
|
|
3312
3336
|
...props,
|
|
3313
3337
|
children: [
|
|
3314
3338
|
children,
|
|
3315
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "
|
|
3339
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "ms-auto size-4 rtl:rotate-180" })
|
|
3316
3340
|
]
|
|
3317
3341
|
}
|
|
3318
3342
|
);
|
|
@@ -3358,7 +3382,7 @@ var SHOWN_WITH_THE_BLOCK = [
|
|
|
3358
3382
|
"opacity-0 group-hover/callout:opacity-100 group-focus-within/callout:opacity-100",
|
|
3359
3383
|
"in-data-[selected=true]:opacity-100 pointer-coarse:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100"
|
|
3360
3384
|
].join(" ");
|
|
3361
|
-
function CalloutToneMenu({ blockId, controller, tone }) {
|
|
3385
|
+
function CalloutToneMenu({ blockId, controller, tone, alwaysShown }) {
|
|
3362
3386
|
const labels = useLabels();
|
|
3363
3387
|
const name = labels.calloutTones[tone];
|
|
3364
3388
|
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
@@ -3370,7 +3394,7 @@ function CalloutToneMenu({ blockId, controller, tone }) {
|
|
|
3370
3394
|
size: "xs",
|
|
3371
3395
|
tabIndex: -1,
|
|
3372
3396
|
"aria-label": labels.calloutToneMenu(name),
|
|
3373
|
-
className: SHOWN_WITH_THE_BLOCK,
|
|
3397
|
+
className: alwaysShown ? void 0 : SHOWN_WITH_THE_BLOCK,
|
|
3374
3398
|
children: name
|
|
3375
3399
|
}
|
|
3376
3400
|
) }),
|
|
@@ -3389,6 +3413,7 @@ var CalloutBlock = ({ block, controller }) => {
|
|
|
3389
3413
|
const readOnly = useReadOnly(controller);
|
|
3390
3414
|
const tone = calloutTone(block.attrs.tone);
|
|
3391
3415
|
const icon = calloutIcon(block.attrs, tone);
|
|
3416
|
+
const toneInWords = !iconTellsTone(block.attrs, tone);
|
|
3392
3417
|
return /* @__PURE__ */ jsxs("div", { className: cn("quoin-callout group/callout flex items-start gap-2", TONE[tone]), "data-tone": tone, children: [
|
|
3393
3418
|
readOnly ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex size-6 items-center justify-center text-base", children: icon }) : /* @__PURE__ */ jsx(CalloutIconMenu, { blockId: block.id, controller, icon }),
|
|
3394
3419
|
/* @__PURE__ */ jsx(
|
|
@@ -3400,7 +3425,7 @@ var CalloutBlock = ({ block, controller }) => {
|
|
|
3400
3425
|
className: "min-w-0 flex-1"
|
|
3401
3426
|
}
|
|
3402
3427
|
),
|
|
3403
|
-
|
|
3428
|
+
readOnly ? toneInWords && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex h-6 items-center px-2 text-xs", children: labels.calloutTones[tone] }) : /* @__PURE__ */ jsx(CalloutToneMenu, { blockId: block.id, controller, tone, alwaysShown: toneInWords })
|
|
3404
3429
|
] });
|
|
3405
3430
|
};
|
|
3406
3431
|
var DividerBlock = ({ block, controller }) => /* @__PURE__ */ jsx("div", { className: "py-2", contentEditable: false, onClick: () => focusNode(controller, block.id), children: /* @__PURE__ */ jsx("hr", { className: "border-t border-border" }) });
|
|
@@ -3454,80 +3479,11 @@ function orderedLabel(n, depth) {
|
|
|
3454
3479
|
function ListMarker(props) {
|
|
3455
3480
|
const text2 = props.list === "bullet" ? bulletGlyph(props.depth) : orderedLabel(props.number, props.depth);
|
|
3456
3481
|
return /* @__PURE__ */ jsx("span", { className: "quoin-marker", "aria-hidden": "true", children: text2 });
|
|
3457
|
-
}
|
|
3458
|
-
var
|
|
3459
|
-
var
|
|
3460
|
-
var
|
|
3461
|
-
|
|
3462
|
-
const labels = useLabels();
|
|
3463
|
-
const level = headingLevel(block.attrs.level);
|
|
3464
|
-
return /* @__PURE__ */ jsx("div", { role: "heading", "aria-level": level, className: headingStyles(level), children: /* @__PURE__ */ jsx(EditableRegion, { block: block.id, region: REGION, label: labels.heading(level) }) });
|
|
3465
|
-
}
|
|
3466
|
-
function NumberedItem({ block, controller }) {
|
|
3467
|
-
const labels = useLabels();
|
|
3468
|
-
const number = useOrderedNumber(controller, block.id);
|
|
3469
|
-
return /* @__PURE__ */ jsxs("div", { className: "quoin-list-item", "data-list": "ordered", children: [
|
|
3470
|
-
/* @__PURE__ */ jsx(ListMarker, { list: "ordered", number, depth: block.indent }),
|
|
3471
|
-
/* @__PURE__ */ jsx(EditableRegion, { block: block.id, region: REGION, label: labels.numberedItem(number) })
|
|
3472
|
-
] });
|
|
3473
|
-
}
|
|
3474
|
-
function TaskItem({ block, controller }) {
|
|
3475
|
-
const labels = useLabels();
|
|
3476
|
-
const hostId = useRegionDomId(block.id, REGION);
|
|
3477
|
-
return /* @__PURE__ */ jsxs("div", { className: "quoin-task", "data-checked": block.attrs.checked === true, children: [
|
|
3478
|
-
/* @__PURE__ */ jsx(TaskCheckbox, { block, controller, label: labels.task, labelledBy: hostId }),
|
|
3479
|
-
/* @__PURE__ */ jsx(EditableRegion, { id: hostId, block: block.id, region: REGION, label: labels.task })
|
|
3480
|
-
] });
|
|
3481
|
-
}
|
|
3482
|
-
var MarkdownBlock = (props) => {
|
|
3483
|
-
const { block } = props;
|
|
3484
|
-
const labels = useLabels();
|
|
3485
|
-
switch (block.attrs.kind) {
|
|
3486
|
-
case "heading":
|
|
3487
|
-
return /* @__PURE__ */ jsx(Heading, { block });
|
|
3488
|
-
case "bullet":
|
|
3489
|
-
return /* @__PURE__ */ jsxs("div", { className: "quoin-list-item", "data-list": "bullet", children: [
|
|
3490
|
-
/* @__PURE__ */ jsx(ListMarker, { list: "bullet", depth: block.indent }),
|
|
3491
|
-
/* @__PURE__ */ jsx(EditableRegion, { block: block.id, region: REGION, label: labels.bulletItem })
|
|
3492
|
-
] });
|
|
3493
|
-
case "ordered":
|
|
3494
|
-
return /* @__PURE__ */ jsx(NumberedItem, { ...props });
|
|
3495
|
-
case "task":
|
|
3496
|
-
return /* @__PURE__ */ jsx(TaskItem, { ...props });
|
|
3497
|
-
case "quote":
|
|
3498
|
-
return /* @__PURE__ */ jsx("div", { className: "quoin-quote", children: /* @__PURE__ */ jsx(EditableRegion, { block: block.id, region: REGION, label: labels.quote }) });
|
|
3499
|
-
default:
|
|
3500
|
-
return /* @__PURE__ */ jsx(EditableRegion, { block: block.id, region: REGION, label: labels.paragraph });
|
|
3501
|
-
}
|
|
3502
|
-
};
|
|
3503
|
-
|
|
3504
|
-
// src/blocks/text/use-nested-blocks.ts
|
|
3505
|
-
function nestedBlockIds({ doc }, blockId) {
|
|
3506
|
-
const index = doc.index.get(blockId);
|
|
3507
|
-
if (index === void 0) return [];
|
|
3508
|
-
return doc.blocks.slice(index + 1, descendantsEnd(doc, index)).map((block) => block.id);
|
|
3509
|
-
}
|
|
3510
|
-
function useNestedBlocks(controller, blockId) {
|
|
3511
|
-
return useEditorState(controller, (state) => nestedBlockIds(state, blockId), sameItems);
|
|
3512
|
-
}
|
|
3513
|
-
var ToggleBlock = ({ block, controller }) => {
|
|
3514
|
-
const labels = useLabels();
|
|
3515
|
-
const domId = useBlockDomId();
|
|
3516
|
-
const nested = useNestedBlocks(controller, block.id);
|
|
3517
|
-
const open = useToggleOpen(controller, block);
|
|
3518
|
-
return /* @__PURE__ */ jsxs("div", { className: "quoin-toggle", "data-open": open, children: [
|
|
3519
|
-
/* @__PURE__ */ jsx(
|
|
3520
|
-
ToggleChevron,
|
|
3521
|
-
{
|
|
3522
|
-
block,
|
|
3523
|
-
controller,
|
|
3524
|
-
label: labels.toggleButton(open ? 0 : nested.length),
|
|
3525
|
-
controls: nested.length === 0 ? void 0 : nested.map(domId).join(" ")
|
|
3526
|
-
}
|
|
3527
|
-
),
|
|
3528
|
-
/* @__PURE__ */ jsx(EditableRegion, { block: block.id, region: "text", label: labels.toggle })
|
|
3529
|
-
] });
|
|
3530
|
-
};
|
|
3482
|
+
}
|
|
3483
|
+
var HEADING_LEVELS = ["mt-6 text-3xl", "mt-5 text-2xl", "mt-4 text-xl", "mt-3 text-lg", "mt-3 text-base", "mt-3 text-sm"];
|
|
3484
|
+
var headingStyles = (level) => `quoin-heading mb-1 font-semibold leading-tight in-[[data-quoin-block]:first-child]:mt-0 ${HEADING_LEVELS[level - 1]}`;
|
|
3485
|
+
var CANVAS_LOOK = { Marker: ListMarker, headingClassName: headingStyles };
|
|
3486
|
+
var MarkdownBlock = (props) => /* @__PURE__ */ jsx(MarkdownText, { ...props, look: CANVAS_LOOK });
|
|
3531
3487
|
|
|
3532
3488
|
// src/blocks/text/text-renderers.ts
|
|
3533
3489
|
var textRenderers = {
|
|
@@ -3551,6 +3507,7 @@ function drawnAsText(definitions, type, readOnly) {
|
|
|
3551
3507
|
return !hostDefines(definitions, type) || !readOnly && TYPED_IN_PLACE.includes(type);
|
|
3552
3508
|
}
|
|
3553
3509
|
var definedTypedInPlace = (definitions) => TYPED_IN_PLACE.filter((type) => hostDefines(definitions, type));
|
|
3510
|
+
var hostLabel = (definitions, type) => !TYPED_IN_PLACE.includes(type) && hostDefines(definitions, type) ? blockByType(definitions, type)?.label : void 0;
|
|
3554
3511
|
function drawnInPlace(definitions, type) {
|
|
3555
3512
|
return Object.hasOwn(inPlaceRenderers, type) && blockByType(definitions, type) !== void 0 && !hostDefines(definitions, type);
|
|
3556
3513
|
}
|
|
@@ -3586,10 +3543,10 @@ function turnIntoTargets(labels) {
|
|
|
3586
3543
|
}
|
|
3587
3544
|
function TurnIntoItems({ kit, controller, blockIds }) {
|
|
3588
3545
|
const { blockTypes, labels } = useQuoin();
|
|
3589
|
-
const
|
|
3590
|
-
return
|
|
3546
|
+
const targets2 = turnIntoTargets(labels).filter((target) => drawsType(blockTypes, target.type));
|
|
3547
|
+
return targets2.map((target) => /* @__PURE__ */ jsxs(kit.Item, { onSelect: () => runOnBlocks(controller, blockIds, setBlockType(target.type, target.attrs)), children: [
|
|
3591
3548
|
/* @__PURE__ */ jsx(MenuIcon, { icon: target.icon }),
|
|
3592
|
-
target.label
|
|
3549
|
+
hostLabel(blockTypes, target.type) ?? target.label
|
|
3593
3550
|
] }, target.id));
|
|
3594
3551
|
}
|
|
3595
3552
|
function TurnIntoMenu(props) {
|
|
@@ -3687,8 +3644,15 @@ function useContextMenuOpener(controller, store, trigger) {
|
|
|
3687
3644
|
const onMouseDown = (event) => {
|
|
3688
3645
|
if (event.button === 2 && blockOf(event) !== null) event.preventDefault();
|
|
3689
3646
|
};
|
|
3647
|
+
let pressed = null;
|
|
3648
|
+
const onPointerDown = (event) => {
|
|
3649
|
+
const block = event.button === 2 ? blockOf(event) : null;
|
|
3650
|
+
pressed = block === null ? null : { block, x: event.clientX, y: event.clientY };
|
|
3651
|
+
};
|
|
3652
|
+
const pressedAt = ({ clientX, clientY }) => pressed !== null && Math.hypot(clientX - pressed.x, clientY - pressed.y) <= 2 ? pressed.block : null;
|
|
3690
3653
|
const onContextMenu = (event) => {
|
|
3691
|
-
const block = blockOf(event);
|
|
3654
|
+
const block = blockOf(event) ?? pressedAt(event);
|
|
3655
|
+
pressed = null;
|
|
3692
3656
|
const hidden = trigger.current;
|
|
3693
3657
|
if (block === null || hidden === null) return;
|
|
3694
3658
|
event.preventDefault();
|
|
@@ -3696,7 +3660,7 @@ function useContextMenuOpener(controller, store, trigger) {
|
|
|
3696
3660
|
openBlockMenuAt(store, controller.editor.state, block, clientX, clientY);
|
|
3697
3661
|
hidden.dispatchEvent(new MouseEvent("contextmenu", { bubbles: true, cancelable: true, clientX, clientY }));
|
|
3698
3662
|
};
|
|
3699
|
-
return listen(root, { mousedown: onMouseDown, contextmenu: onContextMenu });
|
|
3663
|
+
return listen(root, { pointerdown: onPointerDown, mousedown: onMouseDown, contextmenu: onContextMenu });
|
|
3700
3664
|
}, [controller, store, trigger]);
|
|
3701
3665
|
}
|
|
3702
3666
|
|
|
@@ -3720,6 +3684,8 @@ var nameIn = (names, key) => typeof key === "string" && Object.hasOwn(names, key
|
|
|
3720
3684
|
function blockTypeLabel(labels, block, definitions, extensionNames = {}) {
|
|
3721
3685
|
const { kind, level } = block.attrs;
|
|
3722
3686
|
if (block.type === "MARKDOWN" && kind === "heading") return labels.heading(typeof level === "number" ? level : 1);
|
|
3687
|
+
const own = hostLabel(definitions, block.type);
|
|
3688
|
+
if (own !== void 0) return own;
|
|
3723
3689
|
const name = block.type === "MARKDOWN" ? nameIn(KIND_NAMES, kind) : nameIn(TYPE_NAMES, block.type);
|
|
3724
3690
|
if (name !== void 0) return labels.blockNames[name];
|
|
3725
3691
|
return nameIn(extensionNames, block.type) ?? blockByType(definitions, block.type)?.label ?? labels.unsupportedBlock;
|
|
@@ -3782,6 +3748,72 @@ function BlockContextMenu({ controller, store, actions }) {
|
|
|
3782
3748
|
);
|
|
3783
3749
|
}
|
|
3784
3750
|
|
|
3751
|
+
// src/gutter/block-menu-items.ts
|
|
3752
|
+
var rangeOf = (blockIds) => blockRangeSelection(blockIds[0], blockIds[blockIds.length - 1]);
|
|
3753
|
+
function menuActions(core, extra = []) {
|
|
3754
|
+
return [...core.filter((action) => !action.destructive), ...extra, ...core.filter((action) => action.destructive)];
|
|
3755
|
+
}
|
|
3756
|
+
function markdownOf(editor, blockIds, resolveAssetUrl) {
|
|
3757
|
+
const { doc } = editor.state;
|
|
3758
|
+
const range = selectedBlockRange(doc, rangeOf(blockIds));
|
|
3759
|
+
if (range === null) return "";
|
|
3760
|
+
const blocks = editor.codec.blocksFromDoc(createDoc(doc.blocks.slice(range.start, range.end)));
|
|
3761
|
+
return toMarkdown(blocks, { schema: editor.schema, resolveAssetUrl, comments: false });
|
|
3762
|
+
}
|
|
3763
|
+
var applies = (command) => (state, blockIds) => blockIds.every((id) => state.doc.index.has(id)) && runCommands(state, [setSelection(rangeOf(blockIds)), command]).ok;
|
|
3764
|
+
function coreBlockMenuActions(labels, options) {
|
|
3765
|
+
const copy = (controller, blockIds, text2) => {
|
|
3766
|
+
void copyText(text2, controller.root?.ownerDocument).then((copied) => {
|
|
3767
|
+
(copied ? options.onCopied : options.onCopyFailed)?.();
|
|
3768
|
+
});
|
|
3769
|
+
runOnBlocks(controller, blockIds, noChange);
|
|
3770
|
+
};
|
|
3771
|
+
return [
|
|
3772
|
+
{
|
|
3773
|
+
id: "duplicate",
|
|
3774
|
+
label: labels.duplicate,
|
|
3775
|
+
icon: CopyPlus,
|
|
3776
|
+
shortcut: "Mod-d",
|
|
3777
|
+
run: (controller, blockIds) => runOnBlocks(controller, blockIds, duplicateBlocks())
|
|
3778
|
+
},
|
|
3779
|
+
{
|
|
3780
|
+
id: "copy",
|
|
3781
|
+
label: labels.copy,
|
|
3782
|
+
icon: Copy,
|
|
3783
|
+
run: (controller, blockIds) => copy(controller, blockIds, markdownOf(controller.editor, blockIds, options.resolveAssetUrl))
|
|
3784
|
+
},
|
|
3785
|
+
{
|
|
3786
|
+
id: "copy-link",
|
|
3787
|
+
label: labels.copyLink,
|
|
3788
|
+
icon: Link,
|
|
3789
|
+
run: (controller, blockIds) => copy(controller, blockIds, options.blockLink(blockIds[0]))
|
|
3790
|
+
},
|
|
3791
|
+
{
|
|
3792
|
+
id: "move-up",
|
|
3793
|
+
label: labels.moveUp,
|
|
3794
|
+
icon: ArrowUp,
|
|
3795
|
+
shortcut: "Mod-Shift-ArrowUp",
|
|
3796
|
+
run: (controller, blockIds) => runOnBlocks(controller, blockIds, moveBlocksUp),
|
|
3797
|
+
available: applies(moveBlocksUp)
|
|
3798
|
+
},
|
|
3799
|
+
{
|
|
3800
|
+
id: "move-down",
|
|
3801
|
+
label: labels.moveDown,
|
|
3802
|
+
icon: ArrowDown,
|
|
3803
|
+
shortcut: "Mod-Shift-ArrowDown",
|
|
3804
|
+
run: (controller, blockIds) => runOnBlocks(controller, blockIds, moveBlocksDown),
|
|
3805
|
+
available: applies(moveBlocksDown)
|
|
3806
|
+
},
|
|
3807
|
+
{
|
|
3808
|
+
id: "delete",
|
|
3809
|
+
label: labels.delete,
|
|
3810
|
+
icon: Trash,
|
|
3811
|
+
destructive: true,
|
|
3812
|
+
run: (controller, blockIds) => runOnBlocks(controller, blockIds, deleteBlocks)
|
|
3813
|
+
}
|
|
3814
|
+
];
|
|
3815
|
+
}
|
|
3816
|
+
|
|
3785
3817
|
// src/gutter/block-menu-keymap.ts
|
|
3786
3818
|
function blockMenuKeymap(store) {
|
|
3787
3819
|
const openForHead = (state, dispatch) => {
|
|
@@ -3855,14 +3887,28 @@ var DragOverlay = forwardRef(function DragOverlay2(_props, ref) {
|
|
|
3855
3887
|
] });
|
|
3856
3888
|
});
|
|
3857
3889
|
|
|
3890
|
+
// src/canvas/removed-blocks.ts
|
|
3891
|
+
function blockRemovals(tr) {
|
|
3892
|
+
return tr.steps.flatMap((step, at) => {
|
|
3893
|
+
if (step.stepType !== "removeBlocks") return [];
|
|
3894
|
+
const { index, count } = step.toJSON();
|
|
3895
|
+
return typeof index === "number" && typeof count === "number" ? [{ step: at, index, count }] : [];
|
|
3896
|
+
});
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3858
3899
|
// src/canvas/structure-announcement.ts
|
|
3859
3900
|
var DELETION_STEPS = /* @__PURE__ */ new Set(["removeBlocks", "setIndent"]);
|
|
3860
3901
|
function drawnPosition(doc, blockId) {
|
|
3861
|
-
const index = doc.index.get(blockId)
|
|
3862
|
-
return [drawnBlockCount(doc, 0, index) + 1, drawnBlockCount(doc)];
|
|
3902
|
+
const index = doc.index.get(blockId);
|
|
3903
|
+
return index === void 0 ? null : [drawnBlockCount(doc, 0, index) + 1, drawnBlockCount(doc)];
|
|
3863
3904
|
}
|
|
3864
3905
|
function blockMovedMessage(doc, blockId, labels) {
|
|
3865
|
-
|
|
3906
|
+
const position = drawnPosition(doc, blockId);
|
|
3907
|
+
return position && labels.blockMoved(...position);
|
|
3908
|
+
}
|
|
3909
|
+
function announceBlockMoved(controller, blockId, labels) {
|
|
3910
|
+
const message = blockMovedMessage(controller.editor.state.doc, blockId, labels);
|
|
3911
|
+
if (message !== null && controller.announcer.current()?.message !== message) controller.announce(message);
|
|
3866
3912
|
}
|
|
3867
3913
|
function movedBlock(tr) {
|
|
3868
3914
|
const at = tr.steps.findIndex((step) => step.stepType === "moveBlocks");
|
|
@@ -3872,19 +3918,39 @@ function movedBlock(tr) {
|
|
|
3872
3918
|
}
|
|
3873
3919
|
function deletedCount(tr) {
|
|
3874
3920
|
if (!tr.steps.every((step) => DELETION_STEPS.has(step.stepType))) return 0;
|
|
3875
|
-
return tr.
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
}, 0);
|
|
3921
|
+
return blockRemovals(tr).reduce(
|
|
3922
|
+
(deleted, { step, index, count }) => deleted + drawnBlockCount(tr.docs[step], index, index + count),
|
|
3923
|
+
0
|
|
3924
|
+
);
|
|
3880
3925
|
}
|
|
3881
|
-
function
|
|
3882
|
-
if (tr.getMeta(LINE_SPLIT) === true || tr.steps.some((step) => step.stepType === "removeBlocks")) return null;
|
|
3926
|
+
function insertedIds(tr) {
|
|
3883
3927
|
const inserted = tr.steps.flatMap((step) => {
|
|
3884
3928
|
const { blocks } = step.toJSON();
|
|
3885
3929
|
return step.stepType === "insertBlocks" && Array.isArray(blocks) ? blocks : [];
|
|
3886
3930
|
});
|
|
3887
|
-
return inserted.
|
|
3931
|
+
return new Set(inserted.map(({ id }) => id));
|
|
3932
|
+
}
|
|
3933
|
+
function isCaretLine(tr, block) {
|
|
3934
|
+
const { selection } = tr;
|
|
3935
|
+
const empty = isParagraph(block) && regionLength(block, primaryRegion(tr.before.schema, block) ?? "") === 0;
|
|
3936
|
+
return empty && selection?.kind === "text" && selection.head.block === block.id;
|
|
3937
|
+
}
|
|
3938
|
+
function insertedBlocks(tr) {
|
|
3939
|
+
if (tr.getMeta(LINE_SPLIT) === true || tr.steps.some((step) => step.stepType === "removeBlocks")) return [];
|
|
3940
|
+
const inserted = insertedIds(tr);
|
|
3941
|
+
const { doc } = tr;
|
|
3942
|
+
const drawn = doc.blocks.filter((block, index) => inserted.has(block.id) && drawnBlockCount(doc, index, index + 1));
|
|
3943
|
+
const last2 = drawn.at(-1);
|
|
3944
|
+
if (drawn.length > 1 && last2 !== void 0 && isCaretLine(tr, last2)) drawn.pop();
|
|
3945
|
+
return drawn.map((block) => block.id);
|
|
3946
|
+
}
|
|
3947
|
+
function insertedMessage(tr, labels) {
|
|
3948
|
+
const [first, ...more] = insertedBlocks(tr);
|
|
3949
|
+
const position = first === void 0 ? null : drawnPosition(tr.doc, first);
|
|
3950
|
+
if (position === null) return null;
|
|
3951
|
+
if (more.length === 0) return labels.blockInserted(...position);
|
|
3952
|
+
const blocksInserted = labels.blocksInserted ?? englishLabels.blocksInserted;
|
|
3953
|
+
return blocksInserted(more.length + 1, ...position);
|
|
3888
3954
|
}
|
|
3889
3955
|
function structureAnnouncement(tr, labels, undoKeys) {
|
|
3890
3956
|
if (tr.origin !== "command" || tr.getMeta(LINE_JOIN) === true) return null;
|
|
@@ -3892,36 +3958,56 @@ function structureAnnouncement(tr, labels, undoKeys) {
|
|
|
3892
3958
|
if (moved !== null) return blockMovedMessage(tr.doc, moved, labels);
|
|
3893
3959
|
const deleted = deletedCount(tr);
|
|
3894
3960
|
if (deleted > 0) return `${labels.blockDeleted(deleted)}. ${labels.undoHint(undoKeys)}`;
|
|
3895
|
-
|
|
3896
|
-
return inserted === null ? null : labels.blockInserted(...drawnPosition(tr.doc, inserted));
|
|
3961
|
+
return insertedMessage(tr, labels);
|
|
3897
3962
|
}
|
|
3898
3963
|
|
|
3899
3964
|
// src/drag/drag-layout.ts
|
|
3900
|
-
var INDENT_STEP_REM = 1.5;
|
|
3901
3965
|
function rootOrigin(root) {
|
|
3902
3966
|
const box = root.getBoundingClientRect();
|
|
3903
3967
|
return { x: box.left + root.clientLeft - root.scrollLeft, y: box.top + root.clientTop - root.scrollTop };
|
|
3904
3968
|
}
|
|
3905
3969
|
var spanBetween = (a, b) => ({ left: Math.min(a, b), width: Math.abs(b - a) });
|
|
3906
|
-
|
|
3907
|
-
|
|
3970
|
+
var leadingMargin = (element, rtl) => parseFloat(getComputedStyle(element)[rtl ? "marginRight" : "marginLeft"]) || 0;
|
|
3971
|
+
function indentStep(shell, rtl) {
|
|
3972
|
+
const copies = [0, 1].map((depth) => {
|
|
3973
|
+
const copy = shell.cloneNode(false);
|
|
3974
|
+
copy.removeAttribute("id");
|
|
3975
|
+
copy.removeAttribute("data-block-id");
|
|
3976
|
+
copy.style.setProperty("--quoin-indent", String(depth));
|
|
3977
|
+
copy.style.display = "none";
|
|
3978
|
+
return copy;
|
|
3979
|
+
});
|
|
3980
|
+
shell.after(...copies);
|
|
3981
|
+
const [flat, nested] = copies.map((copy) => leadingMargin(copy, rtl));
|
|
3982
|
+
for (const copy of copies) copy.remove();
|
|
3983
|
+
return nested > flat ? nested - flat : parseFloat(getComputedStyle(shell).fontSize) || 16;
|
|
3908
3984
|
}
|
|
3909
3985
|
function inlineEdges(shell, box, rtl) {
|
|
3910
|
-
const
|
|
3911
|
-
|
|
3912
|
-
return { start: box.left - (parseFloat(style.marginLeft) || 0), end: box.right };
|
|
3986
|
+
const margin = leadingMargin(shell, rtl);
|
|
3987
|
+
return rtl ? { start: box.right + margin, end: box.left } : { start: box.left - margin, end: box.right };
|
|
3913
3988
|
}
|
|
3914
3989
|
function measure(controller, root, moved, rtl) {
|
|
3915
3990
|
const { doc } = controller.editor.state;
|
|
3916
3991
|
const origin = rootOrigin(root);
|
|
3917
3992
|
return visibleBlockIndexes(doc).flatMap((index) => {
|
|
3918
|
-
const
|
|
3919
|
-
const shell = controller.blockElement(id);
|
|
3993
|
+
const block = doc.blocks[index];
|
|
3994
|
+
const shell = controller.blockElement(block.id);
|
|
3920
3995
|
if (shell === void 0) return [];
|
|
3921
3996
|
const box = shell.getBoundingClientRect();
|
|
3922
3997
|
const { start, end } = inlineEdges(shell, box, rtl);
|
|
3923
|
-
|
|
3924
|
-
|
|
3998
|
+
return [
|
|
3999
|
+
{
|
|
4000
|
+
id: block.id,
|
|
4001
|
+
top: box.top - origin.y,
|
|
4002
|
+
bottom: box.bottom - origin.y,
|
|
4003
|
+
start: start - origin.x,
|
|
4004
|
+
end: end - origin.x,
|
|
4005
|
+
indent: block.indent,
|
|
4006
|
+
dragged: moved.has(block.id),
|
|
4007
|
+
hidesNested: isClosedToggle(block),
|
|
4008
|
+
shell
|
|
4009
|
+
}
|
|
4010
|
+
];
|
|
3925
4011
|
});
|
|
3926
4012
|
}
|
|
3927
4013
|
var furthestEnd = (blocks, rtl) => blocks.reduce((end, block) => rtl ? Math.min(end, block.end) : Math.max(end, block.end), blocks[0].end);
|
|
@@ -3936,7 +4022,7 @@ function measureDrag(controller, root, ids) {
|
|
|
3936
4022
|
const [first] = dragged;
|
|
3937
4023
|
const origin = rootOrigin(root);
|
|
3938
4024
|
const viewport = root.ownerDocument.defaultView?.innerHeight ?? Infinity;
|
|
3939
|
-
const step = indentStep(
|
|
4025
|
+
const step = indentStep(first.shell, rtl);
|
|
3940
4026
|
const rest = doc.blocks.filter((block) => !moved.has(block.id));
|
|
3941
4027
|
const restIndex = new Map(rest.map((block, index) => [block.id, index]));
|
|
3942
4028
|
const staying = blocks.filter((block) => !block.dragged);
|
|
@@ -3967,6 +4053,7 @@ function nearestGap(blocks, pointerY) {
|
|
|
3967
4053
|
return nearest2;
|
|
3968
4054
|
}
|
|
3969
4055
|
function computeDropTarget({ pointerX, pointerY, blocks, indentStep: indentStep2, direction }) {
|
|
4056
|
+
if (blocks.length === 0) return null;
|
|
3970
4057
|
const staying = blocks.filter((block) => !block.dragged);
|
|
3971
4058
|
if (staying.length === 0) return { index: 0, indent: 0, lineTop: blocks[0].top, lineStart: blocks[0].start };
|
|
3972
4059
|
const index = nearestGap(staying, pointerY);
|
|
@@ -3974,7 +4061,8 @@ function computeDropTarget({ pointerX, pointerY, blocks, indentStep: indentStep2
|
|
|
3974
4061
|
if (index === 0) return { index, indent: 0, lineTop, lineStart: staying[0].start };
|
|
3975
4062
|
const above = staying[index - 1];
|
|
3976
4063
|
const step = direction === "rtl" ? -indentStep2 : indentStep2;
|
|
3977
|
-
const
|
|
4064
|
+
const deepest = above.hidesNested ? above.indent : above.indent + 1;
|
|
4065
|
+
const indent3 = Math.min(Math.max(Math.round((pointerX - above.start) / step), 0), deepest);
|
|
3978
4066
|
return { index, indent: indent3, lineTop, lineStart: above.start + indent3 * step };
|
|
3979
4067
|
}
|
|
3980
4068
|
|
|
@@ -3984,7 +4072,7 @@ var SCROLL_EDGE = 48;
|
|
|
3984
4072
|
var sameTarget = (a, b) => a?.index === b.index && a.indent === b.indent;
|
|
3985
4073
|
function draggedBlocks(controller, blockId) {
|
|
3986
4074
|
const { state } = controller.editor;
|
|
3987
|
-
return state.doc.index.has(blockId) ?
|
|
4075
|
+
return state.doc.index.has(blockId) ? handleTargets(state, blockId) : [];
|
|
3988
4076
|
}
|
|
3989
4077
|
function followPress(press, root, view) {
|
|
3990
4078
|
const { controller, store, overlay, handle: handle2, pointerId } = press;
|
|
@@ -4017,6 +4105,7 @@ function followPress(press, root, view) {
|
|
|
4017
4105
|
const { indentStep: indentStep2, blocks, direction } = layout;
|
|
4018
4106
|
const target = computeDropTarget({ pointerX: layout.draggedStart + dx, pointerY, blocks, indentStep: indentStep2, direction });
|
|
4019
4107
|
overlay.follow(dx, pointer.y - press.y);
|
|
4108
|
+
if (target === null) return;
|
|
4020
4109
|
overlay.mark({ top: target.lineTop, ...spanBetween(target.lineStart, layout.end) });
|
|
4021
4110
|
if (!sameTarget(active.target, target)) {
|
|
4022
4111
|
store.set({ drag: { blockIds: ids, targetIndent: target.indent } });
|
|
@@ -4027,6 +4116,7 @@ function followPress(press, root, view) {
|
|
|
4027
4116
|
if (done) return;
|
|
4028
4117
|
done = true;
|
|
4029
4118
|
unlistenDocument();
|
|
4119
|
+
unlistenScroll();
|
|
4030
4120
|
unlistenBlur();
|
|
4031
4121
|
unlistenKeys();
|
|
4032
4122
|
scroll.stop();
|
|
@@ -4065,6 +4155,7 @@ function followPress(press, root, view) {
|
|
|
4065
4155
|
finish();
|
|
4066
4156
|
}
|
|
4067
4157
|
const unlistenDocument = listen(doc, { pointermove: onPointerMove, pointerup: onPointerUp, pointercancel: finish });
|
|
4158
|
+
const unlistenScroll = listen(doc, { scroll: follow }, true);
|
|
4068
4159
|
const unlistenBlur = listen(view, { blur: finish });
|
|
4069
4160
|
const unlistenKeys = listen(view, { keydown: onKeyDown }, true);
|
|
4070
4161
|
return finish;
|
|
@@ -4101,9 +4192,9 @@ function useBlockDrag(controller, store, blockId, overlay) {
|
|
|
4101
4192
|
onDragStart: () => {
|
|
4102
4193
|
dragged.current = true;
|
|
4103
4194
|
},
|
|
4104
|
-
// The canvas has already said where a drop moved the block,
|
|
4105
|
-
//
|
|
4106
|
-
onMoved: (movedId) =>
|
|
4195
|
+
// The canvas has already said where a drop moved the block, so this is heard only for a drop that nested the
|
|
4196
|
+
// block in place.
|
|
4197
|
+
onMoved: (movedId) => announceBlockMoved(controller, movedId, labels)
|
|
4107
4198
|
});
|
|
4108
4199
|
},
|
|
4109
4200
|
takeClick(event) {
|
|
@@ -4113,15 +4204,42 @@ function useBlockDrag(controller, store, blockId, overlay) {
|
|
|
4113
4204
|
}
|
|
4114
4205
|
};
|
|
4115
4206
|
}
|
|
4207
|
+
var listOf = (query) => typeof window.matchMedia === "function" ? window.matchMedia(query) : null;
|
|
4208
|
+
var readOnServer = () => false;
|
|
4209
|
+
function useMediaQuery(query, unsupported) {
|
|
4210
|
+
const subscribe3 = useCallback(
|
|
4211
|
+
(notify) => {
|
|
4212
|
+
const list2 = listOf(query);
|
|
4213
|
+
list2?.addEventListener("change", notify);
|
|
4214
|
+
return () => list2?.removeEventListener("change", notify);
|
|
4215
|
+
},
|
|
4216
|
+
[query]
|
|
4217
|
+
);
|
|
4218
|
+
const read2 = () => listOf(query)?.matches ?? unsupported;
|
|
4219
|
+
return useSyncExternalStore(subscribe3, read2, readOnServer);
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
// src/mobile/use-coarse-pointer.ts
|
|
4223
|
+
var useCoarsePointer = () => useMediaQuery("(pointer: coarse)", false);
|
|
4116
4224
|
|
|
4117
4225
|
// src/gutter/add-block-below.ts
|
|
4226
|
+
var paragraphAfter = (blockId) => (state, dispatch) => {
|
|
4227
|
+
const { doc, schema } = state;
|
|
4228
|
+
const index = requireBlockIndex(doc, blockId);
|
|
4229
|
+
const paragraph = createBlock(schema, "MARKDOWN", { kind: "paragraph" }, doc.blocks[index].indent);
|
|
4230
|
+
const tr = new Transaction(state).step(insertBlocksStep(descendantsEnd(doc, index), [paragraph]));
|
|
4231
|
+
dispatch?.(tr.setSelection(startOf(tr.doc, schema, paragraph.id)));
|
|
4232
|
+
return true;
|
|
4233
|
+
};
|
|
4118
4234
|
function addBlockBelow(controller, blockId) {
|
|
4119
4235
|
const { state } = controller.editor;
|
|
4120
4236
|
const { doc, schema } = state;
|
|
4121
|
-
const
|
|
4122
|
-
if (
|
|
4123
|
-
const
|
|
4124
|
-
const
|
|
4237
|
+
const index = doc.index.get(blockId);
|
|
4238
|
+
if (index === void 0) return false;
|
|
4239
|
+
const block = doc.blocks[index];
|
|
4240
|
+
const alone = descendantsEnd(doc, index) === index + 1;
|
|
4241
|
+
const empty = alone && isParagraph(block) && regionLength(block, primaryRegion(schema, block) ?? "") === 0;
|
|
4242
|
+
const fill = empty ? setSelection(startOf(doc, schema, blockId)) : paragraphAfter(blockId);
|
|
4125
4243
|
const slash = insertText("/", { origin: "command" });
|
|
4126
4244
|
const { ok, tr } = runCommands(state, [setSelection(blockRangeSelection(blockId)), fill, slash]);
|
|
4127
4245
|
if (ok && tr !== null) controller.dispatch(tr);
|
|
@@ -4172,6 +4290,20 @@ function BlockMenu({ controller, store, actions, onOpen, children }) {
|
|
|
4172
4290
|
);
|
|
4173
4291
|
}
|
|
4174
4292
|
|
|
4293
|
+
// src/ui/watch-direction.ts
|
|
4294
|
+
var TURNS_ROUND = { attributeFilter: ["dir", "class", "style"] };
|
|
4295
|
+
function watchDirection(element, onChange) {
|
|
4296
|
+
let current = inlineDirection(element);
|
|
4297
|
+
const observer = new MutationObserver(() => {
|
|
4298
|
+
const next = inlineDirection(element);
|
|
4299
|
+
if (next === current) return;
|
|
4300
|
+
current = next;
|
|
4301
|
+
onChange(next);
|
|
4302
|
+
});
|
|
4303
|
+
for (let node = element; node !== null; node = node.parentElement) observer.observe(node, TURNS_ROUND);
|
|
4304
|
+
return () => observer.disconnect();
|
|
4305
|
+
}
|
|
4306
|
+
|
|
4175
4307
|
// src/gutter/use-gutter-position.ts
|
|
4176
4308
|
var GAP = 4;
|
|
4177
4309
|
var VOID_LINE = 24;
|
|
@@ -4214,12 +4346,14 @@ function useGutterPosition(controller, gutter, block) {
|
|
|
4214
4346
|
frame = view.requestAnimationFrame(update);
|
|
4215
4347
|
};
|
|
4216
4348
|
const unsubscribe = controller.editor.subscribe(later);
|
|
4349
|
+
const unwatch = watchDirection(root, later);
|
|
4217
4350
|
const observer = new ResizeObserver(later);
|
|
4218
4351
|
observer.observe(root);
|
|
4219
4352
|
view.addEventListener("resize", later);
|
|
4220
4353
|
return () => {
|
|
4221
4354
|
view.cancelAnimationFrame(frame);
|
|
4222
4355
|
unsubscribe();
|
|
4356
|
+
unwatch();
|
|
4223
4357
|
observer.disconnect();
|
|
4224
4358
|
view.removeEventListener("resize", later);
|
|
4225
4359
|
};
|
|
@@ -4261,6 +4395,7 @@ var GUTTER = "absolute top-0 left-0 z-10 flex items-center gap-0.5 pointer-coars
|
|
|
4261
4395
|
var ADD_BELOW = "text-muted-foreground pointer-coarse:hidden";
|
|
4262
4396
|
function Gutter({ controller, store, actions }) {
|
|
4263
4397
|
const labels = useLabels();
|
|
4398
|
+
const coarse = useCoarsePointer();
|
|
4264
4399
|
const element = useRef(null);
|
|
4265
4400
|
const overlay = useRef(null);
|
|
4266
4401
|
const menuOpenAtPress = useRef(false);
|
|
@@ -4306,7 +4441,7 @@ function Gutter({ controller, store, actions }) {
|
|
|
4306
4441
|
variant: "ghost",
|
|
4307
4442
|
size: null,
|
|
4308
4443
|
tabIndex: -1,
|
|
4309
|
-
"aria-label": labels.dragHandle,
|
|
4444
|
+
"aria-label": coarse ? labels.blockOptions ?? englishLabels.blockOptions : labels.dragHandle,
|
|
4310
4445
|
className: cn("text-muted-foreground h-6 w-4 cursor-grab rounded-md", SQUARE_TAP_TARGET_FLOOR),
|
|
4311
4446
|
onPointerDown: (event) => {
|
|
4312
4447
|
menuOpenAtPress.current = event.button === 0 && store.get().blockMenu?.anchor === "handle";
|
|
@@ -4328,17 +4463,6 @@ function Gutter({ controller, store, actions }) {
|
|
|
4328
4463
|
] });
|
|
4329
4464
|
}
|
|
4330
4465
|
|
|
4331
|
-
// src/i18n/resolve-labels.ts
|
|
4332
|
-
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
4333
|
-
function ontoRecords(overrides) {
|
|
4334
|
-
const taken = Object.entries(overrides).map(([key, value]) => {
|
|
4335
|
-
const fallback = englishLabels[key];
|
|
4336
|
-
return [key, isRecord(value) && isRecord(fallback) ? { ...fallback, ...value } : value];
|
|
4337
|
-
});
|
|
4338
|
-
return Object.fromEntries(taken);
|
|
4339
|
-
}
|
|
4340
|
-
var resolveLabels = (overrides) => mergeLabels(englishLabels, overrides && ontoRecords(overrides));
|
|
4341
|
-
|
|
4342
4466
|
// src/menus/floating/range-rect.ts
|
|
4343
4467
|
var TEXT_NODE = 3;
|
|
4344
4468
|
function lineBox(rect, host) {
|
|
@@ -4457,6 +4581,39 @@ function AlignDropdown({ controller, state }) {
|
|
|
4457
4581
|
labels[LABEL_KEYS[align]]
|
|
4458
4582
|
] }, align)) }) });
|
|
4459
4583
|
}
|
|
4584
|
+
|
|
4585
|
+
// src/toolbar/toolbar-items.ts
|
|
4586
|
+
function markItem(mark, title, icon) {
|
|
4587
|
+
return {
|
|
4588
|
+
id: mark,
|
|
4589
|
+
title,
|
|
4590
|
+
icon,
|
|
4591
|
+
isActive: (state) => isActive(state, mark),
|
|
4592
|
+
run: (controller) => void controller.run(toggleMark(mark))
|
|
4593
|
+
};
|
|
4594
|
+
}
|
|
4595
|
+
var linkItem = (labels, openLink) => ({
|
|
4596
|
+
id: "link",
|
|
4597
|
+
title: labels.link,
|
|
4598
|
+
icon: Link,
|
|
4599
|
+
opensDialog: true,
|
|
4600
|
+
run: openLink
|
|
4601
|
+
});
|
|
4602
|
+
|
|
4603
|
+
// src/menus/bubble/bubble-items.ts
|
|
4604
|
+
function bubbleItems(labels, openLink) {
|
|
4605
|
+
return {
|
|
4606
|
+
inline: [
|
|
4607
|
+
markItem("bold", labels.bold, Bold),
|
|
4608
|
+
markItem("italic", labels.italic, Italic),
|
|
4609
|
+
markItem("underline", labels.underline, Underline),
|
|
4610
|
+
markItem("strike", labels.strike, Strikethrough),
|
|
4611
|
+
markItem("code", labels.code, Code),
|
|
4612
|
+
linkItem(labels, openLink)
|
|
4613
|
+
],
|
|
4614
|
+
scripts: [markItem("sub", labels.subscript, Subscript), markItem("sup", labels.superscript, Superscript)]
|
|
4615
|
+
};
|
|
4616
|
+
}
|
|
4460
4617
|
var MARKS = { color: "color", background: "highlight" };
|
|
4461
4618
|
function selectionColor(state, mark) {
|
|
4462
4619
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -4486,6 +4643,22 @@ function TurnIntoDropdown({ controller, state }) {
|
|
|
4486
4643
|
] });
|
|
4487
4644
|
return /* @__PURE__ */ jsx(ToolbarMenu, { controller, trigger, children: /* @__PURE__ */ jsx(TurnIntoItems, { kit: DROPDOWN_KIT, controller, blockIds: selectedBlockIds(state.doc, state.selection) }) });
|
|
4488
4645
|
}
|
|
4646
|
+
function useFocusBackToText(controller, holds = isBubblePart) {
|
|
4647
|
+
const latest = useRef(holds);
|
|
4648
|
+
useIsomorphicLayoutEffect(() => {
|
|
4649
|
+
latest.current = holds;
|
|
4650
|
+
});
|
|
4651
|
+
useIsomorphicLayoutEffect(
|
|
4652
|
+
() => () => {
|
|
4653
|
+
const doc = controller.root?.ownerDocument;
|
|
4654
|
+
if (!doc || !latest.current(doc.activeElement, controller)) return;
|
|
4655
|
+
queueMicrotask(() => {
|
|
4656
|
+
if (doc.activeElement === null || doc.activeElement === doc.body) controller.focus();
|
|
4657
|
+
});
|
|
4658
|
+
},
|
|
4659
|
+
[controller]
|
|
4660
|
+
);
|
|
4661
|
+
}
|
|
4489
4662
|
var DIVIDER = "mx-0.5 h-5 w-px shrink-0 bg-border";
|
|
4490
4663
|
function BubbleToolbar({ controller, labels, store, extraItems }) {
|
|
4491
4664
|
const state = useEditorState(controller, wholeState);
|
|
@@ -4493,6 +4666,7 @@ function BubbleToolbar({ controller, labels, store, extraItems }) {
|
|
|
4493
4666
|
const roving = useRovingFocus();
|
|
4494
4667
|
const items = useMemo(() => bubbleItems(labels, () => store.set({ linkPopover: { open: true } })), [labels, store]);
|
|
4495
4668
|
useFocusKey(controller, roving.focusCurrent);
|
|
4669
|
+
useFocusBackToText(controller);
|
|
4496
4670
|
const onKeyDown = (event) => {
|
|
4497
4671
|
if (!roving.owns(event.target)) return;
|
|
4498
4672
|
if (event.key === "Escape" || event.key === "Tab" && !event.altKey && !event.ctrlKey && !event.metaKey) {
|
|
@@ -4531,20 +4705,6 @@ function BubbleToolbar({ controller, labels, store, extraItems }) {
|
|
|
4531
4705
|
)
|
|
4532
4706
|
] });
|
|
4533
4707
|
}
|
|
4534
|
-
var listOf = (query) => typeof window.matchMedia === "function" ? window.matchMedia(query) : null;
|
|
4535
|
-
var readOnServer = () => false;
|
|
4536
|
-
function useMediaQuery(query, unsupported) {
|
|
4537
|
-
const subscribe3 = useCallback(
|
|
4538
|
-
(notify) => {
|
|
4539
|
-
const list2 = listOf(query);
|
|
4540
|
-
list2?.addEventListener("change", notify);
|
|
4541
|
-
return () => list2?.removeEventListener("change", notify);
|
|
4542
|
-
},
|
|
4543
|
-
[query]
|
|
4544
|
-
);
|
|
4545
|
-
const read2 = () => listOf(query)?.matches ?? unsupported;
|
|
4546
|
-
return useSyncExternalStore(subscribe3, read2, readOnServer);
|
|
4547
|
-
}
|
|
4548
4708
|
|
|
4549
4709
|
// src/menus/bubble/use-fine-pointer.ts
|
|
4550
4710
|
var useFinePointer = () => useMediaQuery("(pointer: fine)", true);
|
|
@@ -4590,6 +4750,12 @@ function linkAtSelection(state) {
|
|
|
4590
4750
|
const { from, to } = orderedRange(state.doc, selection.anchor, selection.head);
|
|
4591
4751
|
return linkAround(state, from, to);
|
|
4592
4752
|
}
|
|
4753
|
+
function sameLink(a, b) {
|
|
4754
|
+
if (a === null || b === null) return a === b;
|
|
4755
|
+
const [x, y] = [a.range, b.range];
|
|
4756
|
+
const sameRange2 = x.block === y.block && x.region === y.region && x.from === y.from && x.to === y.to;
|
|
4757
|
+
return sameRange2 && a.href === b.href && a.title === b.title;
|
|
4758
|
+
}
|
|
4593
4759
|
|
|
4594
4760
|
// src/menus/link/link-element.ts
|
|
4595
4761
|
var LINK = "a[data-quoin-link]";
|
|
@@ -4604,13 +4770,18 @@ function linkAtElement(controller, state, element) {
|
|
|
4604
4770
|
return linkAround(state, { block: host.block, region: host.region, offset });
|
|
4605
4771
|
}
|
|
4606
4772
|
var sameStart = (a, b) => a.block === b.block && a.region === b.region && a.from === b.from;
|
|
4607
|
-
function
|
|
4773
|
+
function sameLink2(controller, a, b) {
|
|
4608
4774
|
if (a === b || a === null || b === null) return a === b;
|
|
4609
4775
|
const { state } = controller.editor;
|
|
4610
4776
|
const first = linkAtElement(controller, state, a)?.range;
|
|
4611
4777
|
const second = linkAtElement(controller, state, b)?.range;
|
|
4612
4778
|
return first !== void 0 && second !== void 0 && sameStart(first, second);
|
|
4613
4779
|
}
|
|
4780
|
+
function OpenLinkButton({ href, label }) {
|
|
4781
|
+
const safe = sanitizeHref(href);
|
|
4782
|
+
if (!safe) return null;
|
|
4783
|
+
return /* @__PURE__ */ jsx(Button, { asChild: true, variant: "ghost", size: "icon-sm", children: /* @__PURE__ */ jsx("a", { href: safe, target: "_blank", rel: "noopener noreferrer", "aria-label": label, children: /* @__PURE__ */ jsx(ExternalLink, {}) }) });
|
|
4784
|
+
}
|
|
4614
4785
|
|
|
4615
4786
|
// src/menus/link/truncate-middle.ts
|
|
4616
4787
|
var ELLIPSIS = "\u2026";
|
|
@@ -4671,7 +4842,7 @@ function usePointerLink(controller, card, delay) {
|
|
|
4671
4842
|
const onCard = !!card.current?.contains(event.target);
|
|
4672
4843
|
const next = onCard ? shown.current : linkElementAt(controller, event.target);
|
|
4673
4844
|
clearTimeout(timer.current);
|
|
4674
|
-
if (!
|
|
4845
|
+
if (!sameLink2(controller, next, shown.current)) timer.current = setTimeout(() => show(next), delay);
|
|
4675
4846
|
};
|
|
4676
4847
|
document.addEventListener("pointerover", onOver);
|
|
4677
4848
|
return () => {
|
|
@@ -4682,15 +4853,16 @@ function usePointerLink(controller, card, delay) {
|
|
|
4682
4853
|
return { element, dismiss: useCallback(() => show(null), [show]) };
|
|
4683
4854
|
}
|
|
4684
4855
|
var REST_MS = 300;
|
|
4856
|
+
var OPEN_POPOVER = "Mod-k";
|
|
4685
4857
|
var URL_LENGTH = 48;
|
|
4686
4858
|
var CARD = "flex w-auto items-center gap-0.5 py-1 ps-3 pe-1 shadow-md";
|
|
4687
4859
|
var ADDRESS = "me-1 whitespace-nowrap text-sm text-muted-foreground";
|
|
4688
4860
|
function LinkCard({ controller, labels, store, cardRef, element, onUnlinked }) {
|
|
4689
|
-
const
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
[controller, state, element]
|
|
4861
|
+
const select = useCallback(
|
|
4862
|
+
(state) => element === null ? linkAtSelection(state) : linkAtElement(controller, state, element),
|
|
4863
|
+
[controller, element]
|
|
4693
4864
|
);
|
|
4865
|
+
const link = useEditorState(controller, select, sameLink);
|
|
4694
4866
|
const anchor = useMemo(() => {
|
|
4695
4867
|
if (link === null) return { current: null };
|
|
4696
4868
|
const { range } = link;
|
|
@@ -4699,7 +4871,6 @@ function LinkCard({ controller, labels, store, cardRef, element, onUnlinked }) {
|
|
|
4699
4871
|
}, [controller, link]);
|
|
4700
4872
|
if (link === null) return null;
|
|
4701
4873
|
const { block, region, from, to } = link.range;
|
|
4702
|
-
const href = sanitizeHref(link.href);
|
|
4703
4874
|
const address = truncateMiddle(link.href, URL_LENGTH);
|
|
4704
4875
|
const edit2 = () => {
|
|
4705
4876
|
if (element !== null) {
|
|
@@ -4718,6 +4889,7 @@ function LinkCard({ controller, labels, store, cardRef, element, onUnlinked }) {
|
|
|
4718
4889
|
PopoverContent,
|
|
4719
4890
|
{
|
|
4720
4891
|
ref: cardRef,
|
|
4892
|
+
role: "group",
|
|
4721
4893
|
"aria-label": labels.link,
|
|
4722
4894
|
side: "bottom",
|
|
4723
4895
|
align: "start",
|
|
@@ -4728,8 +4900,19 @@ function LinkCard({ controller, labels, store, cardRef, element, onUnlinked }) {
|
|
|
4728
4900
|
onMouseDown: keepFocus,
|
|
4729
4901
|
children: [
|
|
4730
4902
|
/* @__PURE__ */ jsx("span", { className: ADDRESS, title: address === link.href ? void 0 : link.href, children: address }),
|
|
4731
|
-
|
|
4732
|
-
/* @__PURE__ */ jsx(
|
|
4903
|
+
/* @__PURE__ */ jsx(OpenLinkButton, { href: link.href, label: labels.linkOpen }),
|
|
4904
|
+
/* @__PURE__ */ jsx(
|
|
4905
|
+
Button,
|
|
4906
|
+
{
|
|
4907
|
+
type: "button",
|
|
4908
|
+
variant: "ghost",
|
|
4909
|
+
size: "icon-sm",
|
|
4910
|
+
"aria-label": labels.linkEdit,
|
|
4911
|
+
"aria-keyshortcuts": shortcutHint(OPEN_POPOVER, controller.isMac, labels).aria,
|
|
4912
|
+
onClick: edit2,
|
|
4913
|
+
children: /* @__PURE__ */ jsx(Pencil, {})
|
|
4914
|
+
}
|
|
4915
|
+
),
|
|
4733
4916
|
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": labels.linkRemove, onClick: unlink, children: /* @__PURE__ */ jsx(Unlink, {}) })
|
|
4734
4917
|
]
|
|
4735
4918
|
}
|
|
@@ -4769,6 +4952,7 @@ function applyLink(href, value) {
|
|
|
4769
4952
|
var CONTENT2 = "w-80 p-2 shadow-md";
|
|
4770
4953
|
var FIELD2 = "h-8";
|
|
4771
4954
|
var MESSAGE = "mt-1.5 px-1 text-sm text-destructive";
|
|
4955
|
+
var CONTROLS = "input, button, a[href]";
|
|
4772
4956
|
function popoverAnchor(controller, link) {
|
|
4773
4957
|
const { selection } = controller.editor.state;
|
|
4774
4958
|
if (selection?.kind !== "text") return null;
|
|
@@ -4786,7 +4970,8 @@ function LinkPopover({ controller, labels, onClose }) {
|
|
|
4786
4970
|
const [invalid, setInvalid] = useState(false);
|
|
4787
4971
|
const messageId = useId();
|
|
4788
4972
|
const anchor = useMemo(() => ({ current: popoverAnchor(controller, link) }), [controller, link]);
|
|
4789
|
-
const
|
|
4973
|
+
const content = useRef(null);
|
|
4974
|
+
useFocusBackToText(controller, (focused) => focused !== null && content.current?.contains(focused) === true);
|
|
4790
4975
|
const backToText = () => {
|
|
4791
4976
|
onClose();
|
|
4792
4977
|
controller.focus();
|
|
@@ -4798,7 +4983,11 @@ function LinkPopover({ controller, labels, onClose }) {
|
|
|
4798
4983
|
const apply = (event) => {
|
|
4799
4984
|
event.preventDefault();
|
|
4800
4985
|
const href = linkAddress(address);
|
|
4801
|
-
if (href === null)
|
|
4986
|
+
if (href === null) {
|
|
4987
|
+
setInvalid(true);
|
|
4988
|
+
controller.announce(labels.invalidLink);
|
|
4989
|
+
return;
|
|
4990
|
+
}
|
|
4802
4991
|
controller.run(applyLink(href, address.trim()));
|
|
4803
4992
|
backToText();
|
|
4804
4993
|
};
|
|
@@ -4806,6 +4995,13 @@ function LinkPopover({ controller, labels, onClose }) {
|
|
|
4806
4995
|
controller.run(unsetLink);
|
|
4807
4996
|
backToText();
|
|
4808
4997
|
};
|
|
4998
|
+
const tabOut = (event) => {
|
|
4999
|
+
if (event.key !== "Tab" || event.ctrlKey || event.metaKey) return;
|
|
5000
|
+
const controls = [...event.currentTarget.querySelectorAll(CONTROLS)];
|
|
5001
|
+
if (event.target !== (event.shiftKey ? controls[0] : controls.at(-1))) return;
|
|
5002
|
+
event.preventDefault();
|
|
5003
|
+
backToText();
|
|
5004
|
+
};
|
|
4809
5005
|
return /* @__PURE__ */ jsxs(
|
|
4810
5006
|
Popover,
|
|
4811
5007
|
{
|
|
@@ -4819,6 +5015,7 @@ function LinkPopover({ controller, labels, onClose }) {
|
|
|
4819
5015
|
/* @__PURE__ */ jsxs(
|
|
4820
5016
|
PopoverContent,
|
|
4821
5017
|
{
|
|
5018
|
+
ref: content,
|
|
4822
5019
|
"aria-label": labels.link,
|
|
4823
5020
|
side: "bottom",
|
|
4824
5021
|
align: "start",
|
|
@@ -4828,6 +5025,7 @@ function LinkPopover({ controller, labels, onClose }) {
|
|
|
4828
5025
|
event.preventDefault();
|
|
4829
5026
|
backToText();
|
|
4830
5027
|
},
|
|
5028
|
+
onKeyDown: tabOut,
|
|
4831
5029
|
children: [
|
|
4832
5030
|
/* @__PURE__ */ jsxs("form", { noValidate: true, className: "flex items-center gap-0.5", onSubmit: apply, children: [
|
|
4833
5031
|
/* @__PURE__ */ jsx(
|
|
@@ -4843,10 +5041,10 @@ function LinkPopover({ controller, labels, onClose }) {
|
|
|
4843
5041
|
}
|
|
4844
5042
|
),
|
|
4845
5043
|
/* @__PURE__ */ jsx(Button, { type: "submit", variant: "ghost", size: "icon-sm", "aria-label": labels.linkApply, children: /* @__PURE__ */ jsx(Check, {}) }),
|
|
4846
|
-
|
|
5044
|
+
link !== null && /* @__PURE__ */ jsx(OpenLinkButton, { href: link.href, label: labels.linkOpen }),
|
|
4847
5045
|
link !== null && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": labels.linkRemove, onClick: remove, children: /* @__PURE__ */ jsx(Unlink, {}) })
|
|
4848
5046
|
] }),
|
|
4849
|
-
invalid && /* @__PURE__ */ jsx("p", { id: messageId,
|
|
5047
|
+
invalid && /* @__PURE__ */ jsx("p", { id: messageId, className: MESSAGE, children: labels.invalidLink })
|
|
4850
5048
|
]
|
|
4851
5049
|
}
|
|
4852
5050
|
)
|
|
@@ -4903,7 +5101,8 @@ function triggerOffset(text2, start, config) {
|
|
|
4903
5101
|
if (character === "\n" || character === ATOM) return null;
|
|
4904
5102
|
if (character === char) {
|
|
4905
5103
|
const atRegionStart = start + i === 0;
|
|
4906
|
-
|
|
5104
|
+
const opens2 = atRegionStart || !startOfLine && i > 0 && WHITESPACE2.test(text2[i - 1]);
|
|
5105
|
+
if (opens2) return WHITESPACE2.test(text2[i + 1] ?? "") ? null : start + i;
|
|
4907
5106
|
}
|
|
4908
5107
|
if (!allowSpaces && WHITESPACE2.test(character)) return null;
|
|
4909
5108
|
}
|
|
@@ -4934,11 +5133,13 @@ function stillDismissed(tr, dismissed, found) {
|
|
|
4934
5133
|
const same = found.block === at.block && found.region === at.region && found.from === at.offset;
|
|
4935
5134
|
return same ? rangeOf2(found) : null;
|
|
4936
5135
|
}
|
|
4937
|
-
|
|
4938
|
-
|
|
5136
|
+
var opens = (found, { minLength = 0 }) => found !== null && found.query.length >= minLength;
|
|
5137
|
+
function nearest(state, found, config) {
|
|
5138
|
+
if (!opens(found, config)) return null;
|
|
4939
5139
|
for (const { trigger } of state.plugins) {
|
|
4940
|
-
|
|
4941
|
-
|
|
5140
|
+
if (trigger === void 0) continue;
|
|
5141
|
+
const other = findTrigger(state, trigger);
|
|
5142
|
+
if (opens(other, trigger) && other.from > found.from) return null;
|
|
4942
5143
|
}
|
|
4943
5144
|
return found;
|
|
4944
5145
|
}
|
|
@@ -4946,15 +5147,15 @@ function suggestionPlugin(key, config) {
|
|
|
4946
5147
|
return {
|
|
4947
5148
|
key,
|
|
4948
5149
|
trigger: config,
|
|
4949
|
-
init: (state) => ({ active: nearest(state, findTrigger(state, config)), dismissedAt: null }),
|
|
5150
|
+
init: (state) => ({ active: nearest(state, findTrigger(state, config), config), dismissedAt: null }),
|
|
4950
5151
|
apply(tr, value, oldState, newState) {
|
|
4951
|
-
const dismissing = tr.getMeta(DISMISS)
|
|
5152
|
+
const dismissing = tr.getMeta(DISMISS) !== void 0;
|
|
4952
5153
|
if (!dismissing && !tr.docChanged && sameSelection(oldState.selection, newState.selection)) return value;
|
|
4953
5154
|
const found = findTrigger(newState, config);
|
|
4954
5155
|
let dismissedAt = null;
|
|
4955
5156
|
if (dismissing) dismissedAt = found && rangeOf2(found);
|
|
4956
5157
|
else if (value.dismissedAt !== null) dismissedAt = stillDismissed(tr, value.dismissedAt, found);
|
|
4957
|
-
const active = dismissedAt === null ? nearest(newState, found) : null;
|
|
5158
|
+
const active = dismissedAt === null ? nearest(newState, found, config) : null;
|
|
4958
5159
|
const unchanged = sameTrigger(active, value.active) && sameRange(dismissedAt, value.dismissedAt);
|
|
4959
5160
|
if (unchanged) return value;
|
|
4960
5161
|
return {
|
|
@@ -4997,7 +5198,7 @@ function insertionItem(id, insertion, labels, definitions) {
|
|
|
4997
5198
|
const { label, icon, type, attrs } = insertion;
|
|
4998
5199
|
return {
|
|
4999
5200
|
id,
|
|
5000
|
-
title: label,
|
|
5201
|
+
title: hostLabel(definitions, type) ?? label,
|
|
5001
5202
|
description: labels.slashDescriptions[id],
|
|
5002
5203
|
icon,
|
|
5003
5204
|
...DETAILS[id],
|
|
@@ -5061,33 +5262,59 @@ function useRangeAnchor(controller, range) {
|
|
|
5061
5262
|
}, [controller, start]);
|
|
5062
5263
|
return { anchor, side };
|
|
5063
5264
|
}
|
|
5265
|
+
|
|
5266
|
+
// src/to-error.ts
|
|
5267
|
+
var toError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
|
|
5064
5268
|
var ADDED = ["aria-expanded", "aria-controls", "aria-autocomplete", "aria-activedescendant"];
|
|
5065
5269
|
var SWAPPED = ["role", "aria-multiline"];
|
|
5066
5270
|
function restore(host, name, value) {
|
|
5067
5271
|
if (value === null) host.removeAttribute(name);
|
|
5068
5272
|
else host.setAttribute(name, value);
|
|
5069
5273
|
}
|
|
5070
|
-
|
|
5071
|
-
|
|
5274
|
+
var held = /* @__PURE__ */ new WeakMap();
|
|
5275
|
+
function makeCombobox(host, listboxId) {
|
|
5072
5276
|
host.setAttribute("role", "combobox");
|
|
5073
5277
|
host.removeAttribute("aria-multiline");
|
|
5074
5278
|
host.setAttribute("aria-expanded", "true");
|
|
5075
5279
|
host.setAttribute("aria-controls", listboxId);
|
|
5076
5280
|
host.setAttribute("aria-autocomplete", "list");
|
|
5281
|
+
}
|
|
5282
|
+
function openCombobox(host, listboxId) {
|
|
5283
|
+
const saved = () => SWAPPED.map((name) => [name, host.getAttribute(name)]);
|
|
5284
|
+
const entry = held.get(host) ?? { saved: saved(), listboxes: [] };
|
|
5285
|
+
held.set(host, entry);
|
|
5286
|
+
entry.listboxes.push(listboxId);
|
|
5287
|
+
makeCombobox(host, listboxId);
|
|
5077
5288
|
return () => {
|
|
5289
|
+
entry.listboxes.splice(entry.listboxes.lastIndexOf(listboxId), 1);
|
|
5290
|
+
const remaining = entry.listboxes.at(-1);
|
|
5291
|
+
if (remaining !== void 0) return makeCombobox(host, remaining);
|
|
5292
|
+
held.delete(host);
|
|
5078
5293
|
for (const name of ADDED) host.removeAttribute(name);
|
|
5079
|
-
for (const [name, value] of saved) restore(host, name, value);
|
|
5294
|
+
for (const [name, value] of entry.saved) restore(host, name, value);
|
|
5080
5295
|
};
|
|
5081
5296
|
}
|
|
5082
5297
|
function useComboboxHost(controller, block, region, listboxId, activeOptionId) {
|
|
5083
|
-
const
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
}, [controller, block, region, listboxId]);
|
|
5298
|
+
const open = block !== null && region !== null;
|
|
5299
|
+
const docWhileOpen = useCallback((state) => open ? state.doc : null, [open]);
|
|
5300
|
+
useEditorState(controller, docWhileOpen);
|
|
5301
|
+
const opened = useRef(null);
|
|
5088
5302
|
useIsomorphicLayoutEffect(() => {
|
|
5089
|
-
|
|
5090
|
-
|
|
5303
|
+
const host = open ? controller.registry.get(block, region) ?? null : null;
|
|
5304
|
+
const last2 = opened.current;
|
|
5305
|
+
if (last2?.host !== host || last2.listboxId !== listboxId) {
|
|
5306
|
+
last2?.close();
|
|
5307
|
+
opened.current = host && { host, listboxId, close: openCombobox(host, listboxId) };
|
|
5308
|
+
}
|
|
5309
|
+
if (opened.current !== null) restore(opened.current.host, "aria-activedescendant", activeOptionId);
|
|
5310
|
+
});
|
|
5311
|
+
useIsomorphicLayoutEffect(
|
|
5312
|
+
() => () => {
|
|
5313
|
+
opened.current?.close();
|
|
5314
|
+
opened.current = null;
|
|
5315
|
+
},
|
|
5316
|
+
[]
|
|
5317
|
+
);
|
|
5091
5318
|
}
|
|
5092
5319
|
|
|
5093
5320
|
// src/menus/suggestion/group-items.ts
|
|
@@ -5144,11 +5371,6 @@ function useSuggestionKeys(controller, open, menu) {
|
|
|
5144
5371
|
return interceptKeys(controller, intercept);
|
|
5145
5372
|
}, [controller, open]);
|
|
5146
5373
|
}
|
|
5147
|
-
|
|
5148
|
-
// src/to-error.ts
|
|
5149
|
-
var toError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
|
|
5150
|
-
|
|
5151
|
-
// src/menus/suggestion/use-suggestion-results.ts
|
|
5152
5374
|
var FORGOTTEN = { source: null, query: null, items: [] };
|
|
5153
5375
|
var CLOSED = { items: [], pending: false };
|
|
5154
5376
|
var isPromise = (value) => typeof value?.then === "function";
|
|
@@ -5199,7 +5421,8 @@ function useSuggestion({
|
|
|
5199
5421
|
itemKey: itemKey2,
|
|
5200
5422
|
groupOf: groupOf2,
|
|
5201
5423
|
onSelect,
|
|
5202
|
-
emptyLabel
|
|
5424
|
+
emptyLabel,
|
|
5425
|
+
countLabel
|
|
5203
5426
|
}) {
|
|
5204
5427
|
const { labels, onError } = useQuoin();
|
|
5205
5428
|
const readOnly = useReadOnly(controller);
|
|
@@ -5227,19 +5450,31 @@ function useSuggestion({
|
|
|
5227
5450
|
const select = useCallback(
|
|
5228
5451
|
(index) => {
|
|
5229
5452
|
const current = getPluginState(controller.editor.state, pluginKey).active;
|
|
5230
|
-
if (current
|
|
5453
|
+
if (current === null || index >= ordered.length) return;
|
|
5454
|
+
try {
|
|
5455
|
+
onSelect(ordered[index], rangeOf2(current));
|
|
5456
|
+
} catch (error) {
|
|
5457
|
+
onError(toError(error));
|
|
5458
|
+
}
|
|
5231
5459
|
},
|
|
5232
|
-
[controller, pluginKey, ordered, onSelect]
|
|
5460
|
+
[controller, pluginKey, ordered, onSelect, onError]
|
|
5233
5461
|
);
|
|
5234
5462
|
const dismiss = useCallback(() => void controller.run(dismissSuggestion(pluginKey)), [controller, pluginKey]);
|
|
5235
5463
|
useSuggestionKeys(controller, trigger !== null, { count: ordered.length, activeIndex, highlight, select, dismiss });
|
|
5236
5464
|
useComboboxHost(controller, trigger?.block ?? null, trigger?.region ?? null, listboxId, activeId);
|
|
5237
5465
|
const query = trigger?.query ?? null;
|
|
5238
5466
|
const count = ordered.length;
|
|
5239
|
-
const resultMessage = count === 0 ? emptyLabel : labels.resultCount(count);
|
|
5467
|
+
const resultMessage = count === 0 ? emptyLabel : (countLabel ?? labels.resultCount)(count);
|
|
5468
|
+
const said = useRef(null);
|
|
5240
5469
|
useEffect(() => {
|
|
5241
5470
|
if (query === null || results.pending) return;
|
|
5242
|
-
const timer = setTimeout(() =>
|
|
5471
|
+
const timer = setTimeout(() => {
|
|
5472
|
+
const current = controller.announcer.current()?.message;
|
|
5473
|
+
const own = current === void 0 || current === said.current;
|
|
5474
|
+
const message = own ? resultMessage : `${current}. ${resultMessage}`;
|
|
5475
|
+
said.current = message;
|
|
5476
|
+
controller.announce(message);
|
|
5477
|
+
}, SETTLE_MS);
|
|
5243
5478
|
return () => clearTimeout(timer);
|
|
5244
5479
|
}, [controller, query, resultMessage, results.pending]);
|
|
5245
5480
|
return { trigger, sections, pending: results.pending, activeIndex, listboxId, optionId, highlight, select, dismiss };
|
|
@@ -5469,7 +5704,7 @@ function slashExtension() {
|
|
|
5469
5704
|
|
|
5470
5705
|
// src/mobile/mobile-items.tsx
|
|
5471
5706
|
function commandItem(id, title, icon, command) {
|
|
5472
|
-
return { id, title, icon, run: (controller) => void controller.run(command) };
|
|
5707
|
+
return { id, title, icon, enabled: (state) => command(state), run: (controller) => void controller.run(command) };
|
|
5473
5708
|
}
|
|
5474
5709
|
function addBlockAtCaret(controller) {
|
|
5475
5710
|
const block = headBlockId(controller.editor.state.selection);
|
|
@@ -5491,9 +5726,6 @@ function mobileItems(labels, openLink) {
|
|
|
5491
5726
|
]
|
|
5492
5727
|
};
|
|
5493
5728
|
}
|
|
5494
|
-
|
|
5495
|
-
// src/mobile/use-coarse-pointer.ts
|
|
5496
|
-
var useCoarsePointer = () => useMediaQuery("(pointer: coarse)", false);
|
|
5497
5729
|
var FOCUS_EVENTS = ["focusin", "focusout"];
|
|
5498
5730
|
function subscribe(notify) {
|
|
5499
5731
|
for (const type of FOCUS_EVENTS) document.addEventListener(type, notify);
|
|
@@ -5506,6 +5738,7 @@ function useEditorFocus(controller) {
|
|
|
5506
5738
|
const read2 = useCallback(() => focusInEditor(controller), [controller]);
|
|
5507
5739
|
return useSyncExternalStore(subscribe, read2, NOT_FOCUSED);
|
|
5508
5740
|
}
|
|
5741
|
+
var KEYS3 = ["offsetTop", "offsetLeft", "height", "width", "scale"];
|
|
5509
5742
|
var EVENTS = ["resize", "scroll"];
|
|
5510
5743
|
function subscribe2(notify) {
|
|
5511
5744
|
const viewport = window.visualViewport;
|
|
@@ -5520,10 +5753,8 @@ var last = null;
|
|
|
5520
5753
|
function read() {
|
|
5521
5754
|
const viewport = window.visualViewport;
|
|
5522
5755
|
if (!viewport) return null;
|
|
5523
|
-
const
|
|
5524
|
-
if (last
|
|
5525
|
-
last = { offsetTop, height, width };
|
|
5526
|
-
}
|
|
5756
|
+
const box = Object.fromEntries(KEYS3.map((key) => [key, viewport[key]]));
|
|
5757
|
+
if (last === null || KEYS3.some((key) => last?.[key] !== box[key])) last = box;
|
|
5527
5758
|
return last;
|
|
5528
5759
|
}
|
|
5529
5760
|
var readOnServer2 = () => null;
|
|
@@ -5536,7 +5767,16 @@ var TOOLBAR = [
|
|
|
5536
5767
|
].join(" ");
|
|
5537
5768
|
function placement(viewport) {
|
|
5538
5769
|
if (viewport === null) return { bottom: 0 };
|
|
5539
|
-
|
|
5770
|
+
const { offsetLeft, offsetTop, height, width, scale } = viewport;
|
|
5771
|
+
const shift = "translateY(-100%)";
|
|
5772
|
+
return {
|
|
5773
|
+
top: offsetTop + height,
|
|
5774
|
+
left: offsetLeft,
|
|
5775
|
+
right: "auto",
|
|
5776
|
+
width: width * scale,
|
|
5777
|
+
transform: scale === 1 ? shift : `scale(${1 / scale}) ${shift}`,
|
|
5778
|
+
transformOrigin: "0 0"
|
|
5779
|
+
};
|
|
5540
5780
|
}
|
|
5541
5781
|
function EditingTools({ controller }) {
|
|
5542
5782
|
const labels = useLabels();
|
|
@@ -5544,6 +5784,9 @@ function EditingTools({ controller }) {
|
|
|
5544
5784
|
const state = useEditorState(controller, wholeState);
|
|
5545
5785
|
const viewport = useVisualViewport();
|
|
5546
5786
|
const roving = useRovingFocus();
|
|
5787
|
+
const dir = useDirection();
|
|
5788
|
+
useFocusBackToText(controller);
|
|
5789
|
+
useFocusKey(controller, roving.focusCurrent);
|
|
5547
5790
|
const items = useMemo(() => mobileItems(labels, () => store.set({ linkPopover: { open: true } })), [labels, store]);
|
|
5548
5791
|
const onKeyDown = (event) => {
|
|
5549
5792
|
if (event.key === "Escape" && roving.owns(event.target)) {
|
|
@@ -5557,6 +5800,7 @@ function EditingTools({ controller }) {
|
|
|
5557
5800
|
"div",
|
|
5558
5801
|
{
|
|
5559
5802
|
ref: roving.ref,
|
|
5803
|
+
dir,
|
|
5560
5804
|
role: "toolbar",
|
|
5561
5805
|
"aria-label": labels.mobileToolbar,
|
|
5562
5806
|
className: TOOLBAR,
|
|
@@ -5603,12 +5847,14 @@ var DialogOverlay = /* @__PURE__ */ React7.forwardRef(function DialogOverlay2({
|
|
|
5603
5847
|
);
|
|
5604
5848
|
});
|
|
5605
5849
|
var DialogContent = /* @__PURE__ */ React7.forwardRef(function DialogContent2({ className, children, closeLabel = "Close", ...props }, ref) {
|
|
5850
|
+
const dir = useDirection();
|
|
5606
5851
|
return /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
5607
5852
|
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
5608
5853
|
/* @__PURE__ */ jsxs(
|
|
5609
5854
|
DialogPrimitive.Content,
|
|
5610
5855
|
{
|
|
5611
5856
|
ref,
|
|
5857
|
+
dir,
|
|
5612
5858
|
"data-slot": "dialog-content",
|
|
5613
5859
|
className: cn(
|
|
5614
5860
|
"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-xl 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",
|
|
@@ -5621,7 +5867,7 @@ var DialogContent = /* @__PURE__ */ React7.forwardRef(function DialogContent2({
|
|
|
5621
5867
|
DialogPrimitive.Close,
|
|
5622
5868
|
{
|
|
5623
5869
|
className: cn(
|
|
5624
|
-
"absolute top-4
|
|
5870
|
+
"absolute top-4 end-4 rounded-xs opacity-70 transition-opacity hover:opacity-100",
|
|
5625
5871
|
FOCUS_RING,
|
|
5626
5872
|
ICON_TAP_TARGET_FLOOR
|
|
5627
5873
|
),
|
|
@@ -5738,14 +5984,14 @@ function shortcutGroups(labels, isMac) {
|
|
|
5738
5984
|
}
|
|
5739
5985
|
];
|
|
5740
5986
|
}
|
|
5741
|
-
var
|
|
5987
|
+
var KEYS4 = "rounded border bg-muted px-1.5 py-0.5 font-sans text-xs whitespace-nowrap";
|
|
5742
5988
|
function ShortcutSection({ group }) {
|
|
5743
5989
|
const titleId = useId();
|
|
5744
5990
|
return /* @__PURE__ */ jsxs("section", { "aria-labelledby": titleId, children: [
|
|
5745
5991
|
/* @__PURE__ */ jsx("h3", { id: titleId, className: "mb-2 text-sm font-semibold", children: group.title }),
|
|
5746
5992
|
/* @__PURE__ */ jsx("dl", { className: "grid gap-1.5 text-sm", children: group.items.map(({ label, keys }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
5747
5993
|
/* @__PURE__ */ jsx("dt", { children: label }),
|
|
5748
|
-
/* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsx("kbd", { className:
|
|
5994
|
+
/* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsx("kbd", { className: KEYS4, children: keys }) })
|
|
5749
5995
|
] }, `${label} ${keys}`)) })
|
|
5750
5996
|
] });
|
|
5751
5997
|
}
|
|
@@ -5808,7 +6054,9 @@ function useRootDirection(controller) {
|
|
|
5808
6054
|
const [direction, setDirection] = useState("ltr");
|
|
5809
6055
|
useIsomorphicLayoutEffect(() => {
|
|
5810
6056
|
const root = controller.root;
|
|
5811
|
-
if (root
|
|
6057
|
+
if (root === null) return;
|
|
6058
|
+
setDirection(inlineDirection(root));
|
|
6059
|
+
return watchDirection(root, setDirection);
|
|
5812
6060
|
}, [controller]);
|
|
5813
6061
|
return direction;
|
|
5814
6062
|
}
|
|
@@ -5893,16 +6141,20 @@ function useEditingFocus(root, editing, stop) {
|
|
|
5893
6141
|
(element.querySelector(FOCUSABLE) ?? element).focus();
|
|
5894
6142
|
const doc = element.ownerDocument;
|
|
5895
6143
|
const canvas = element.closest(CANVAS);
|
|
5896
|
-
const leaveForCanvas = (event) => {
|
|
5897
|
-
const target = event.target;
|
|
5898
|
-
if (!element.contains(target) && canvas?.contains(target)) stop();
|
|
5899
|
-
};
|
|
5900
6144
|
const leaveUnlessInside = (event) => {
|
|
5901
6145
|
if (!inside.current.has(event)) stop();
|
|
5902
6146
|
};
|
|
5903
|
-
|
|
6147
|
+
let afterDispatch;
|
|
6148
|
+
const onPress = (event) => {
|
|
6149
|
+
const target = event.target;
|
|
6150
|
+
if (!element.contains(target) && canvas?.contains(target)) return stop();
|
|
6151
|
+
clearTimeout(afterDispatch);
|
|
6152
|
+
afterDispatch = setTimeout(() => leaveUnlessInside(event));
|
|
6153
|
+
};
|
|
6154
|
+
const unlistenCapture = listen(doc, { pointerdown: onPress }, true);
|
|
5904
6155
|
const unlisten = listen(doc, { pointerdown: leaveUnlessInside, focusin: leaveUnlessInside });
|
|
5905
6156
|
return () => {
|
|
6157
|
+
clearTimeout(afterDispatch);
|
|
5906
6158
|
unlistenCapture();
|
|
5907
6159
|
unlisten();
|
|
5908
6160
|
};
|
|
@@ -5912,6 +6164,18 @@ function useEditingFocus(root, editing, stop) {
|
|
|
5912
6164
|
return { onPointerDownCapture: mark, onFocusCapture: mark };
|
|
5913
6165
|
}, []);
|
|
5914
6166
|
}
|
|
6167
|
+
function useWhileMounted(callback) {
|
|
6168
|
+
const mounted = useRef(false);
|
|
6169
|
+
useEffect(() => {
|
|
6170
|
+
mounted.current = true;
|
|
6171
|
+
return () => {
|
|
6172
|
+
mounted.current = false;
|
|
6173
|
+
};
|
|
6174
|
+
}, []);
|
|
6175
|
+
return useCallback(() => {
|
|
6176
|
+
if (mounted.current) callback();
|
|
6177
|
+
}, [callback]);
|
|
6178
|
+
}
|
|
5915
6179
|
function useMissingPreviewWarning(readOnly, { type, preview }) {
|
|
5916
6180
|
useEffect(() => {
|
|
5917
6181
|
if (!readOnly || preview !== void 0) return;
|
|
@@ -5924,9 +6188,10 @@ function VoidPreview({ definition: { preview: Preview }, name, ...props }) {
|
|
|
5924
6188
|
if (Preview === void 0) return /* @__PURE__ */ jsx("p", { className: "py-1 text-sm text-muted-foreground", children: name });
|
|
5925
6189
|
return /* @__PURE__ */ jsx(Preview, { ...props });
|
|
5926
6190
|
}
|
|
5927
|
-
function VoidEditor({ Editor, controller, ...props }) {
|
|
6191
|
+
function VoidEditor({ Editor, controller, onDone, ...props }) {
|
|
5928
6192
|
const index = useBlockIndex(controller, props.blockId);
|
|
5929
|
-
|
|
6193
|
+
const done = useWhileMounted(onDone);
|
|
6194
|
+
return /* @__PURE__ */ jsx(Editor, { ...props, index, onDone: done });
|
|
5930
6195
|
}
|
|
5931
6196
|
function VoidBlock({ block, controller, definition }) {
|
|
5932
6197
|
const { blockTypes, labels } = useQuoin();
|
|
@@ -6066,6 +6331,11 @@ function extensionUi(extension) {
|
|
|
6066
6331
|
}
|
|
6067
6332
|
var list = (value) => Array.isArray(value) ? value : [];
|
|
6068
6333
|
var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
6334
|
+
function doesSomething(item) {
|
|
6335
|
+
if (item.command !== void 0 || item.run !== void 0) return true;
|
|
6336
|
+
devWarn(`the slash command "${item.id}" has neither a command nor a run, so the slash menu leaves it out.`);
|
|
6337
|
+
return false;
|
|
6338
|
+
}
|
|
6069
6339
|
function mergeRender(uis) {
|
|
6070
6340
|
const given = uis.flatMap(({ render }) => render === void 0 ? [] : [render]);
|
|
6071
6341
|
if (given.length === 0) return void 0;
|
|
@@ -6078,7 +6348,10 @@ function mergeRender(uis) {
|
|
|
6078
6348
|
function collectUi(extensions, definitions, readOnly = false, onError) {
|
|
6079
6349
|
const uis = extensions.map(extensionUi);
|
|
6080
6350
|
return {
|
|
6081
|
-
slashItems: [
|
|
6351
|
+
slashItems: [
|
|
6352
|
+
...uis.flatMap((ui) => list(ui.slashItems)).filter(doesSomething),
|
|
6353
|
+
...blockTypeSlashItems(definitions, onError)
|
|
6354
|
+
],
|
|
6082
6355
|
bubbleItems: uis.flatMap((ui) => list(ui.bubbleItems)),
|
|
6083
6356
|
blockMenuItems: uis.flatMap((ui) => list(ui.blockMenuItems)),
|
|
6084
6357
|
renderers: Object.assign({}, canvasRenderers(definitions, readOnly), ...uis.map((ui) => record(ui.renderers))),
|
|
@@ -6111,12 +6384,15 @@ function movedOn(controller, before) {
|
|
|
6111
6384
|
}
|
|
6112
6385
|
async function insertUploads(controller, files, at, { upload, resolveAssetUrl, onError, unavailable, failed }) {
|
|
6113
6386
|
if (!upload) return onError(new Error(unavailable));
|
|
6387
|
+
controller.readSelectionFromDom();
|
|
6388
|
+
const rest = at === null ? fillSelectedBlock(controller, files) : files;
|
|
6389
|
+
if (rest.length === 0) return;
|
|
6114
6390
|
const writer = writerOf(controller);
|
|
6115
|
-
const outcomes = await Promise.allSettled(
|
|
6391
|
+
const outcomes = await Promise.allSettled(rest.map(async (file) => upload(file, isImageFile(file) ? "image" : "file")));
|
|
6116
6392
|
const blocks = [];
|
|
6117
6393
|
outcomes.forEach((outcome, index) => {
|
|
6118
|
-
if (outcome.status === "rejected") onError(
|
|
6119
|
-
else if (
|
|
6394
|
+
if (outcome.status === "rejected") onError(uploadError(outcome.reason, failed));
|
|
6395
|
+
else if (isStored(outcome.value)) blocks.push(fileBlock(rest[index], outcome.value));
|
|
6120
6396
|
else onError(new Error(failed));
|
|
6121
6397
|
});
|
|
6122
6398
|
if (blocks.length === 0 || controller.root === null || controller.readOnly) return;
|
|
@@ -6159,8 +6435,8 @@ function placeholderFor(state, block, labels) {
|
|
|
6159
6435
|
// src/canvas/selected-block-label.ts
|
|
6160
6436
|
var LONGEST = 80;
|
|
6161
6437
|
function firstLine2(text2) {
|
|
6162
|
-
const
|
|
6163
|
-
return
|
|
6438
|
+
const line = text2.split("\n", 1)[0].trim();
|
|
6439
|
+
return graphemePrefix(line, LONGEST) === line ? line : `${graphemePrefix(line, LONGEST - 1)}\u2026`;
|
|
6164
6440
|
}
|
|
6165
6441
|
var selectedBlockLabel = (labels, definitions, extensionNames) => (block, schema) => {
|
|
6166
6442
|
const { attrs } = block;
|
|
@@ -6205,11 +6481,8 @@ function filledLine({ origin, before }, schema) {
|
|
|
6205
6481
|
}
|
|
6206
6482
|
function deletions(codec, tr) {
|
|
6207
6483
|
const filled = filledLine(tr, tr.before.schema);
|
|
6208
|
-
return tr.
|
|
6209
|
-
|
|
6210
|
-
const { index, count } = step.toJSON();
|
|
6211
|
-
if (typeof index !== "number" || typeof count !== "number") return [];
|
|
6212
|
-
const { blocks } = tr.docs[i];
|
|
6484
|
+
return blockRemovals(tr).flatMap(({ step, index, count }) => {
|
|
6485
|
+
const { blocks } = tr.docs[step];
|
|
6213
6486
|
const first = codec.blocksFromDoc(createDoc(blocks.slice(0, index))).length;
|
|
6214
6487
|
return blocks.slice(index, index + count).flatMap((block, offset) => filled(block) ? [] : [{ block: codec.toQuoin(block), index: first + offset }]);
|
|
6215
6488
|
});
|
|
@@ -6228,6 +6501,61 @@ function useDeleteReporter(controller, onDelete) {
|
|
|
6228
6501
|
});
|
|
6229
6502
|
}, [controller]);
|
|
6230
6503
|
}
|
|
6504
|
+
|
|
6505
|
+
// src/canvas/upload-pasted-images.ts
|
|
6506
|
+
var FETCHABLE = /^https?:\/\//i;
|
|
6507
|
+
function pastedPictures(tr, state) {
|
|
6508
|
+
if (tr.getMeta(PASTED_HTML) !== true) return [];
|
|
6509
|
+
return state.doc.blocks.flatMap(({ id, type, attrs }) => {
|
|
6510
|
+
const url = str(attrs.url);
|
|
6511
|
+
const pasted = type === "IMAGE" && FETCHABLE.test(url) && !str(attrs.storageKey) && !tr.before.doc.index.has(id);
|
|
6512
|
+
return pasted ? [{ id, url }] : [];
|
|
6513
|
+
});
|
|
6514
|
+
}
|
|
6515
|
+
async function pictureAt(url) {
|
|
6516
|
+
const response = await fetch(url).catch(() => null);
|
|
6517
|
+
const picture = response?.ok ? await response.blob().catch(() => null) : null;
|
|
6518
|
+
if (!picture?.type.startsWith("image/")) return null;
|
|
6519
|
+
return new File([picture], url.split(/[?#]/)[0].split("/").pop() || "image", { type: picture.type });
|
|
6520
|
+
}
|
|
6521
|
+
function uploadPastedImages(controller, tr, state, host) {
|
|
6522
|
+
for (const { id, url } of pastedPictures(tr, state)) {
|
|
6523
|
+
void pictureAt(url).then(async (file) => {
|
|
6524
|
+
if (file === null) return;
|
|
6525
|
+
await uploadImage(controller, id, file, host).catch(
|
|
6526
|
+
(cause) => host.onError(new Error(cause instanceof Error && cause.message || host.failed))
|
|
6527
|
+
);
|
|
6528
|
+
});
|
|
6529
|
+
}
|
|
6530
|
+
}
|
|
6531
|
+
|
|
6532
|
+
// src/i18n/resolve-labels.ts
|
|
6533
|
+
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
6534
|
+
function ontoRecords(overrides) {
|
|
6535
|
+
const taken = Object.entries(overrides).map(([key, value]) => {
|
|
6536
|
+
const fallback = englishLabels[key];
|
|
6537
|
+
return [key, isRecord(value) && isRecord(fallback) ? { ...fallback, ...value } : value];
|
|
6538
|
+
});
|
|
6539
|
+
return Object.fromEntries(taken);
|
|
6540
|
+
}
|
|
6541
|
+
var resolveLabels = (overrides) => mergeLabels(englishLabels, overrides && ontoRecords(overrides));
|
|
6542
|
+
|
|
6543
|
+
// src/canvas/use-stable-labels.ts
|
|
6544
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6545
|
+
function sameEntries(a, b, deeper) {
|
|
6546
|
+
const keys = Object.keys(a);
|
|
6547
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
6548
|
+
return keys.every((key) => {
|
|
6549
|
+
const [x, y] = [a[key], b[key]];
|
|
6550
|
+
return Object.is(x, y) || deeper && isRecord2(x) && isRecord2(y) && sameEntries(x, y, false);
|
|
6551
|
+
});
|
|
6552
|
+
}
|
|
6553
|
+
var sameOverrides = (a, b) => a === b || a !== void 0 && b !== void 0 && sameEntries(a, b, true);
|
|
6554
|
+
function useStableLabels(overrides) {
|
|
6555
|
+
const [kept, setKept] = useState(overrides);
|
|
6556
|
+
if (!sameOverrides(kept, overrides)) setKept(overrides);
|
|
6557
|
+
return useMemo(() => resolveLabels(kept), [kept]);
|
|
6558
|
+
}
|
|
6231
6559
|
function useStructureAnnouncements(controller, labels) {
|
|
6232
6560
|
useEffect(() => {
|
|
6233
6561
|
const undoKeys = formatShortcut("Mod-z", { isMac: controller.isMac, labels });
|
|
@@ -6284,7 +6612,7 @@ var BlockCanvas = forwardRef(function BlockCanvas2({
|
|
|
6284
6612
|
shortcutsExtension(uiStore),
|
|
6285
6613
|
...extensions ?? []
|
|
6286
6614
|
]);
|
|
6287
|
-
const labels =
|
|
6615
|
+
const labels = useStableLabels(labelOverrides);
|
|
6288
6616
|
const latestOnError = useRef(onError);
|
|
6289
6617
|
latestOnError.current = onError;
|
|
6290
6618
|
const ui = useMemo(
|
|
@@ -6299,6 +6627,11 @@ var BlockCanvas = forwardRef(function BlockCanvas2({
|
|
|
6299
6627
|
readOnly,
|
|
6300
6628
|
extensions: allExtensions,
|
|
6301
6629
|
render: ui.render,
|
|
6630
|
+
onTransaction(tr, state) {
|
|
6631
|
+
if (!upload) return;
|
|
6632
|
+
const host = { upload, resolveAssetUrl: resolveAssetUrl ?? String, onError: onError ?? console.error };
|
|
6633
|
+
uploadPastedImages(controller, tr, state, { ...host, failed: labels.uploadFailed });
|
|
6634
|
+
},
|
|
6302
6635
|
onFiles(files, at) {
|
|
6303
6636
|
const report = onError ?? console.error;
|
|
6304
6637
|
void insertUploads(controller, files, at, {
|
|
@@ -6315,7 +6648,9 @@ var BlockCanvas = forwardRef(function BlockCanvas2({
|
|
|
6315
6648
|
useStructureAnnouncements(controller, labels);
|
|
6316
6649
|
const actions = useMemo(() => {
|
|
6317
6650
|
const onCopied = () => controller.announce(labels.copied);
|
|
6318
|
-
|
|
6651
|
+
const onCopyFailed = () => controller.announce(labels.copyFailed ?? englishLabels.copyFailed);
|
|
6652
|
+
const core = coreBlockMenuActions(labels, { blockLink, onCopied, onCopyFailed, resolveAssetUrl });
|
|
6653
|
+
return menuActions(core, ui.blockMenuItems);
|
|
6319
6654
|
}, [controller, labels, blockLink, resolveAssetUrl, ui.blockMenuItems]);
|
|
6320
6655
|
const slashItems = useMemo(
|
|
6321
6656
|
() => [shortcutsSlashItem(labels, uiStore), ...ui.slashItems],
|
|
@@ -6349,6 +6684,7 @@ var BlockCanvas = forwardRef(function BlockCanvas2({
|
|
|
6349
6684
|
id,
|
|
6350
6685
|
"aria-label": ariaLabel ?? labels.document,
|
|
6351
6686
|
hint: labels.editorHint,
|
|
6687
|
+
rendererLabels: labels,
|
|
6352
6688
|
paragraphLabel: labels.paragraph,
|
|
6353
6689
|
selectedContentLabel: labels.selectedContent,
|
|
6354
6690
|
blockLabel,
|
|
@@ -6375,22 +6711,25 @@ function progressText(state, labels) {
|
|
|
6375
6711
|
return "";
|
|
6376
6712
|
}
|
|
6377
6713
|
function UploadField({ category, label, accept, onUploaded }) {
|
|
6378
|
-
const { upload, onError } = useQuoin();
|
|
6714
|
+
const { upload, onError, controller } = useQuoin();
|
|
6379
6715
|
const labels = useLabels();
|
|
6380
6716
|
const [state, setState] = React7.useState({ phase: "idle" });
|
|
6381
6717
|
async function run(file) {
|
|
6382
6718
|
if (!upload) return;
|
|
6383
6719
|
setState({ phase: "busy" });
|
|
6720
|
+
controller?.announce(labels.uploading);
|
|
6384
6721
|
let result;
|
|
6385
6722
|
try {
|
|
6386
6723
|
result = await upload(file, category);
|
|
6387
6724
|
} catch (cause) {
|
|
6388
|
-
setState({ phase: "failed", message: cause
|
|
6725
|
+
setState({ phase: "failed", message: uploadError(cause, labels.uploadFailed).message });
|
|
6389
6726
|
return;
|
|
6390
6727
|
}
|
|
6391
6728
|
try {
|
|
6392
6729
|
await onUploaded(result);
|
|
6393
|
-
|
|
6730
|
+
const name = result.filename || file.name;
|
|
6731
|
+
setState({ phase: "uploaded", name });
|
|
6732
|
+
controller?.announce(labels.uploaded(name));
|
|
6394
6733
|
} catch (cause) {
|
|
6395
6734
|
onError(toError(cause));
|
|
6396
6735
|
setState({ phase: "idle" });
|
|
@@ -6409,7 +6748,7 @@ function UploadField({ category, label, accept, onUploaded }) {
|
|
|
6409
6748
|
}
|
|
6410
6749
|
),
|
|
6411
6750
|
!upload && /* @__PURE__ */ jsx("span", { role: "note", className: "text-xs text-muted-foreground", children: labels.uploadsUnavailable }),
|
|
6412
|
-
/* @__PURE__ */ jsx("span", { role: "status", className: progress === "" ? "sr-only" : "text-xs text-muted-foreground", children: progress }),
|
|
6751
|
+
/* @__PURE__ */ jsx("span", { role: controller ? void 0 : "status", className: progress === "" ? "sr-only" : "text-xs text-muted-foreground", children: progress }),
|
|
6413
6752
|
state.phase === "failed" && /* @__PURE__ */ jsx("span", { role: "alert", className: "text-xs text-destructive", children: state.message })
|
|
6414
6753
|
] });
|
|
6415
6754
|
}
|
|
@@ -6567,6 +6906,6 @@ function useAutosave({
|
|
|
6567
6906
|
return { status, error, retry: runner.retry, flush: runner.flush };
|
|
6568
6907
|
}
|
|
6569
6908
|
|
|
6570
|
-
export { AlignCenter, AlignLeft, AlignRight, AppWindow, ArrowDown, ArrowDownToLine, ArrowLeft, ArrowLeftToLine, ArrowRight, ArrowRightToLine, ArrowUp, ArrowUpToLine, BlockCanvas, Bookmark, Button, CODE_LANGUAGES, CaseSensitive, ChevronDown, ChevronUp, CodeBlock, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, FLOATING_TOOLBAR, FOCUS_RING, FilePickerButton, GripHorizontal, GripVertical, Input, MENU_ITEM, MenuIcon, MessageSquare, MessageSquarePlus, PenLine, Plus, Popover, PopoverAnchor, PopoverContent, QuoinProvider, Radical, Regex, SendHorizontal, Sigma, SmilePlus, Sparkles, SuggestionMenu, Table, TableOfContents, Trash, UploadField, WholeWord, X, blockFragment, bubblePart, cn, coreBlocks, dismissSuggestion, englishLabels, focusNode, formatShortcut, insertBlockToWrite, insertBlocks, interceptKeys, keepFocus, linkAddress, linkedBlockId, noChange, preventDefault, rangeRect, replaceTrigger, resolveLabels, returnFocusToText, runOnBlocks, selectHandlers, selectNode, styleTargets, suggestionPlugin, toError, useAutosave, useFocusKey, useLabels, useNodeKeys, useOutsidePress, usePartAnchor, useQuoin, useRangeAnchor, useRovingFocus, virtualAnchor };
|
|
6571
|
-
//# sourceMappingURL=chunk-
|
|
6572
|
-
//# sourceMappingURL=chunk-
|
|
6909
|
+
export { AlignCenter, AlignLeft, AlignRight, AppWindow, ArrowDown, ArrowDownToLine, ArrowLeft, ArrowLeftToLine, ArrowRight, ArrowRightToLine, ArrowUp, ArrowUpToLine, BlockCanvas, Bookmark, Button, CODE_LANGUAGES, CaseSensitive, ChevronDown, ChevronUp, CodeBlock, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, FLOATING_TOOLBAR, FOCUS_RING, FilePickerButton, GripHorizontal, GripVertical, Input, MENU_ITEM, MenuIcon, MessageSquare, MessageSquarePlus, PenLine, Plus, Popover, PopoverAnchor, PopoverContent, QuoinProvider, Radical, Regex, SendHorizontal, Sigma, SmilePlus, Sparkles, SuggestionMenu, Table, TableOfContents, Trash, UploadField, WholeWord, X, blockFragment, bubblePart, cn, coreBlocks, dismissSuggestion, englishLabels, focusNode, formatShortcut, insertBlockToWrite, insertBlocks, interceptKeys, keepFocus, linkAddress, linkedBlockId, noChange, preventDefault, rangeRect, replaceTrigger, resolveLabels, returnFocusToText, runOnBlocks, selectHandlers, selectNode, styleTargets, suggestionPlugin, toError, useAutosave, useClosing, useFocusKey, useLabels, useNodeKeys, useOutsidePress, usePartAnchor, useQuoin, useRangeAnchor, useRovingFocus, virtualAnchor, watchDirection };
|
|
6910
|
+
//# sourceMappingURL=chunk-ZGFZRVQA.js.map
|
|
6911
|
+
//# sourceMappingURL=chunk-ZGFZRVQA.js.map
|