docblocks-react-ui 0.4.3 → 0.4.5
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 +63 -0
- package/dist/{Editor-BHXnrEJ1.mjs → Editor-B1T7ytNQ.mjs} +340 -66
- package/dist/{index-BQRTt8x2.d.mts → index-CZEO5s2i.d.mts} +10 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/mermaid/index.mjs +1 -1
- package/dist/pdf/index.d.mts +1 -1
- package/dist/styles.css +0 -22
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# docblocks-react-ui
|
|
2
2
|
|
|
3
|
+
## 0.4.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 468f457: The callout's custom color picker commits the last scrubbed value when
|
|
8
|
+
the picker section closes without an explicit commit (the former close
|
|
9
|
+
discarded the draft); an untouched picker still commits nothing. The
|
|
10
|
+
swatch grid's custom swatch carries the current color when it matches
|
|
11
|
+
no preset (a small dot over the rainbow swatch).
|
|
12
|
+
- 55f51fc: A DOM selection outside a text-editable inside the canvas view maps to
|
|
13
|
+
none over the page's selection mapping. The engine's focus move over a
|
|
14
|
+
canvas item (the mousedown's default focuses the non-editable — the
|
|
15
|
+
firefox's behavior) fires a selectionchange whose blocks mapping would
|
|
16
|
+
flag the focused item: the stray selection tinted the picked shape
|
|
17
|
+
over the canvas' own selection state (the two-click pick's first
|
|
18
|
+
pick). The canvas manages its selection over the explicit press; the
|
|
19
|
+
page card's editable keeps the mapping.
|
|
20
|
+
- 745f1d7: The redo of a remounting step (a type change) restores the pre-change
|
|
21
|
+
selection seat: the redo's stack item carries the pre-undo snapshot,
|
|
22
|
+
which over a remount is the lost-caret residue (none/blocks) rather
|
|
23
|
+
than the pre-change seat. The redo falls back to the seat the matching
|
|
24
|
+
undo restored when the item's own snapshot is not a text selection and
|
|
25
|
+
the undo itself restored a text seat (the remount's case); a non-text
|
|
26
|
+
undo restoration (the canvas' blocks selection) is a legitimate
|
|
27
|
+
pre-undo selection and the redo restores its own snapshot. The view's
|
|
28
|
+
history reseat window suppresses the remount's transient selection loss
|
|
29
|
+
over the undo/redo, and the reseat re-runs when the restoration's
|
|
30
|
+
emission lands.
|
|
31
|
+
- 7bc9512: The shared popover's enter and exit animations now run: the classes were
|
|
32
|
+
keyed on `data-open` / `data-closed` attributes that Radix does not set
|
|
33
|
+
(it sets `data-state="open" | "closed"`), so the animations never applied
|
|
34
|
+
and the content teleported in. The closed content now gets
|
|
35
|
+
`pointer-events-none` for the exit animation, so a fading popover does
|
|
36
|
+
not swallow the press landing over its area.
|
|
37
|
+
- 00d4596: The canvas connector's line is clickable in webkit (Safari): the wide
|
|
38
|
+
invisible hit path renders in its own svg sized to the path's bounding
|
|
39
|
+
box over the 1x1 center-anchor draw layer. The webkit hit-tests only
|
|
40
|
+
inside an svg's own box (the overflow-visible content of the 1x1 layer
|
|
41
|
+
is draw-only there), so the line was not selectable by a click over
|
|
42
|
+
there. The chromium and the firefox reach the overflow, so the box
|
|
43
|
+
change alters no behavior over them.
|
|
44
|
+
- 4e1bee5: The cross-block selection stands over the engines that clamp the DOM
|
|
45
|
+
selection across contenteditable boundaries (webkit / Safari): the
|
|
46
|
+
selection writes (the keyboard extension, the drag's seat) use a
|
|
47
|
+
document-ordered Range instead of the Selection endpoint setters (the
|
|
48
|
+
clamping writes), and the cross-block reads (the arrow at the text's
|
|
49
|
+
edge, the range's extension) use the range's far endpoint when the
|
|
50
|
+
selection's focus reads clamped into the anchor's editable. The
|
|
51
|
+
selection's mapping reads the cross-editable range from its endpoints
|
|
52
|
+
instead of the anchor / focus nodes.
|
|
53
|
+
- Updated dependencies [745f1d7]
|
|
54
|
+
- docblocks-core@0.2.2
|
|
55
|
+
|
|
56
|
+
## 0.4.4
|
|
57
|
+
|
|
58
|
+
### Patch Changes
|
|
59
|
+
|
|
60
|
+
- ae88381: The motion drag's pointer-Y helper no longer tests the event against the
|
|
61
|
+
global `TouchEvent` (the `instanceof` stand): the global is absent on the
|
|
62
|
+
no-touch Safari (a desktop Safari on macOS), so every drag callback threw
|
|
63
|
+
a `ReferenceError` and the block drag-reorder failed there. The touch's
|
|
64
|
+
stand is duck-typed over the event's own `changedTouches` property.
|
|
65
|
+
|
|
3
66
|
## 0.4.3
|
|
4
67
|
|
|
5
68
|
### Patch Changes
|
|
@@ -850,6 +850,42 @@ function selectionOrphaned() {
|
|
|
850
850
|
const node = selection.getRangeAt(0).startContainer;
|
|
851
851
|
return node !== document && !document.contains(node);
|
|
852
852
|
}
|
|
853
|
+
/**
|
|
854
|
+
* The selection's focus end as the editor reads it: the engine's
|
|
855
|
+
* `focusNode` / `focusOffset` — except when the range crosses a
|
|
856
|
+
* contenteditable boundary and the focus reads back inside the anchor's
|
|
857
|
+
* own editable (the webkit's clamped focus read-back: the engine clamps
|
|
858
|
+
* the far endpoint there, so the anchor/focus pair cannot express the
|
|
859
|
+
* cross range). The range's endpoints stand the derivation then: the
|
|
860
|
+
* engine's range is faithful across the boundary even when the
|
|
861
|
+
* anchor/focus read-back is not. The direction is the range's (the
|
|
862
|
+
* clamped engine's anchor reads as the range's start) — the focus end is
|
|
863
|
+
* the range's far endpoint over the anchor's, whatever its order.
|
|
864
|
+
*/
|
|
865
|
+
function effectiveFocusEnd(domSelection) {
|
|
866
|
+
if (domSelection.rangeCount === 0) return null;
|
|
867
|
+
const { anchorNode, anchorOffset, focusNode, focusOffset } = domSelection;
|
|
868
|
+
if (focusNode === null || anchorNode === null) return null;
|
|
869
|
+
const range = domSelection.getRangeAt(0);
|
|
870
|
+
const anchorEditable = editableOfNode$1(anchorNode);
|
|
871
|
+
const startEditable = editableOfNode$1(range.startContainer);
|
|
872
|
+
const endEditable = range.startContainer === range.endContainer ? startEditable : editableOfNode$1(range.endContainer);
|
|
873
|
+
if (startEditable !== null && endEditable !== null && startEditable !== endEditable && anchorEditable !== null && anchorEditable.contains(focusNode)) return range.startContainer === anchorNode && range.startOffset === anchorOffset ? {
|
|
874
|
+
node: range.endContainer,
|
|
875
|
+
offset: range.endOffset
|
|
876
|
+
} : {
|
|
877
|
+
node: range.startContainer,
|
|
878
|
+
offset: range.startOffset
|
|
879
|
+
};
|
|
880
|
+
return {
|
|
881
|
+
node: focusNode,
|
|
882
|
+
offset: focusOffset
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
/** The contenteditable that owns a node (the node's element or its parent's), or null. */
|
|
886
|
+
function editableOfNode$1(node) {
|
|
887
|
+
return (node === null ? null : node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement)?.closest("[contenteditable=\"true\"]") ?? null;
|
|
888
|
+
}
|
|
853
889
|
//#endregion
|
|
854
890
|
//#region src/editing/block-commands/utils/focus.ts
|
|
855
891
|
/** Run `fn` in the next animation frame (setTimeout where frames do not exist). */
|
|
@@ -957,9 +993,13 @@ function seatedIn(el) {
|
|
|
957
993
|
* the user's and stops the watch — it is never clobbered. The offsets are
|
|
958
994
|
* the gesture's pre-move ones: a structural gesture never edits the
|
|
959
995
|
* block's text, so they remain valid. `scope` confines the lookup to one
|
|
960
|
-
* editor root (a page can render the same doc in several).
|
|
996
|
+
* editor root (a page can render the same doc in several). The deadline
|
|
997
|
+
* stands the remount's commit window over the engine's scheduling (the
|
|
998
|
+
* firefox's engine commits the remount slower over the load) — the watch
|
|
999
|
+
* stops at the first seated attempt or the user's selection, so the
|
|
1000
|
+
* length is the residue's watch lifetime only.
|
|
961
1001
|
*/
|
|
962
|
-
const RESTORE_DEADLINE_MS =
|
|
1002
|
+
const RESTORE_DEADLINE_MS = 2e3;
|
|
963
1003
|
function restoreSelectionIn(doc, blockId, from, to, scope = document, exclude = null) {
|
|
964
1004
|
const selector = `[data-block-id="${CSS.escape(blockId)}"][data-slot="docblocks/block-content"]`;
|
|
965
1005
|
const json = doc.toJSON();
|
|
@@ -1028,6 +1068,40 @@ function reseatHistorySelection(doc, view, scope = document) {
|
|
|
1028
1068
|
restoreSelectionIn(doc, from.blockId, from.offset, to === null ? null : to.offset, scope);
|
|
1029
1069
|
}
|
|
1030
1070
|
/**
|
|
1071
|
+
* Stand a DOM selection over (base, focus) — across the contenteditable
|
|
1072
|
+
* boundary included: the selection is built as one document-ordered Range
|
|
1073
|
+
* and added whole. The engine's `setBaseAndExtent` / `extend` cannot put an
|
|
1074
|
+
* endpoint in ANOTHER editable — the webkit clamps the far endpoint to the
|
|
1075
|
+
* anchor's own editable (the call stands, the range does not cross); the
|
|
1076
|
+
* Range's endpoints are faithful across the boundary. The anchor/focus
|
|
1077
|
+
* READ-BACK may still be clamped in that engine — the mapping's stand
|
|
1078
|
+
* (`selection-mapping`'s cross-editable branch) reads the range there.
|
|
1079
|
+
* The direction's read-back stands the range's (the anchor reads as the
|
|
1080
|
+
* range's start): the core model is document-ordered, so the direction
|
|
1081
|
+
* carries no behavior.
|
|
1082
|
+
*/
|
|
1083
|
+
function applyDomSelection(baseNode, baseOffset, focusNode, focusOffset) {
|
|
1084
|
+
const selection = window.getSelection();
|
|
1085
|
+
if (selection === null) return;
|
|
1086
|
+
const baseProbe = document.createRange();
|
|
1087
|
+
baseProbe.setStart(baseNode, baseOffset);
|
|
1088
|
+
baseProbe.collapse(true);
|
|
1089
|
+
const focusProbe = document.createRange();
|
|
1090
|
+
focusProbe.setStart(focusNode, focusOffset);
|
|
1091
|
+
focusProbe.collapse(true);
|
|
1092
|
+
const baseFirst = baseProbe.compareBoundaryPoints(Range.END_TO_START, focusProbe) <= 0;
|
|
1093
|
+
const range = document.createRange();
|
|
1094
|
+
if (baseFirst) {
|
|
1095
|
+
range.setStart(baseNode, baseOffset);
|
|
1096
|
+
range.setEnd(focusNode, focusOffset);
|
|
1097
|
+
} else {
|
|
1098
|
+
range.setStart(focusNode, focusOffset);
|
|
1099
|
+
range.setEnd(baseNode, baseOffset);
|
|
1100
|
+
}
|
|
1101
|
+
selection.removeAllRanges();
|
|
1102
|
+
selection.addRange(range);
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1031
1105
|
* Re-base the browser's selection anchor at (anchorNode, anchorOffset) and
|
|
1032
1106
|
* put the focus end at the flat offset in the target block (DOM-only: the
|
|
1033
1107
|
* doc is untouched). The browser's shift+arrow cannot cross a contenteditable
|
|
@@ -1039,12 +1113,10 @@ function reseatHistorySelection(doc, view, scope = document) {
|
|
|
1039
1113
|
* selection, not the focus. A no-op when the target element is not rendered.
|
|
1040
1114
|
*/
|
|
1041
1115
|
function extendSelectionTo(anchorNode, anchorOffset, blockId, offset) {
|
|
1042
|
-
const domSelection = window.getSelection();
|
|
1043
|
-
if (domSelection === null) return;
|
|
1044
1116
|
const el = editorRootOf(anchorNode).querySelector(`[data-block-id="${CSS.escape(blockId)}"][data-slot="docblocks/block-content"]`);
|
|
1045
1117
|
if (el === null) return;
|
|
1046
1118
|
const target = locatePosition(el, offset);
|
|
1047
|
-
|
|
1119
|
+
applyDomSelection(anchorNode, anchorOffset, target.node, target.offset);
|
|
1048
1120
|
}
|
|
1049
1121
|
//#endregion
|
|
1050
1122
|
//#region src/editing/slash/context.ts
|
|
@@ -5041,12 +5113,14 @@ function DocblocksI18nProvider({ children }) {
|
|
|
5041
5113
|
/**
|
|
5042
5114
|
* The custom color's picker body (the callout menu's custom swatch opens it
|
|
5043
5115
|
* as an inline section): the native `input type="color"` (the live scrub —
|
|
5044
|
-
* no doc write) and the hex field (the strict `#rrggbb`).
|
|
5045
|
-
* stands one `onCommit` per pick (one undo step, one wire
|
|
5046
|
-
* swatch pick's parity): the native dialog's commit (`change`),
|
|
5047
|
-
* valid Enter / blur. An invalid field value resets to the
|
|
5048
|
-
* (the picker stands the last valid stand). The close without
|
|
5049
|
-
* (the
|
|
5116
|
+
* no doc write over the scrub) and the hex field (the strict `#rrggbb`).
|
|
5117
|
+
* The doc write stands one `onCommit` per pick (one undo step, one wire
|
|
5118
|
+
* update — the swatch pick's parity): the native dialog's commit (`change`),
|
|
5119
|
+
* the field's valid Enter / blur. An invalid field value resets to the
|
|
5120
|
+
* draft on blur (the picker stands the last valid stand). The close without
|
|
5121
|
+
* an explicit commit (the Esc / the toggle / the menu's dismiss) commits the
|
|
5122
|
+
* last scrubbed value (the picker's unmount commits it); an untouched
|
|
5123
|
+
* picker commits nothing.
|
|
5050
5124
|
*/
|
|
5051
5125
|
/** The absent value's stand: the native input's neutral default. */
|
|
5052
5126
|
const DEFAULT_HEX = "#808080";
|
|
@@ -5062,11 +5136,24 @@ function ColorPickerInner({ value, onCommit, onClose }) {
|
|
|
5062
5136
|
onCommitRef.current = onCommit;
|
|
5063
5137
|
const onCloseRef = useRef(onClose);
|
|
5064
5138
|
onCloseRef.current = onClose;
|
|
5139
|
+
const draftRef = useRef(draft);
|
|
5140
|
+
draftRef.current = draft;
|
|
5141
|
+
const touchedRef = useRef(false);
|
|
5142
|
+
const committedRef = useRef(false);
|
|
5143
|
+
useEffect(() => {
|
|
5144
|
+
return () => {
|
|
5145
|
+
if (!touchedRef.current || committedRef.current) return;
|
|
5146
|
+
const committed = normalizeHex$1(draftRef.current);
|
|
5147
|
+
const current = valueRef.current === null ? null : normalizeHex$1(valueRef.current);
|
|
5148
|
+
if (committed !== null && committed !== current) onCommitRef.current(committed);
|
|
5149
|
+
};
|
|
5150
|
+
}, []);
|
|
5065
5151
|
const nativeRef = useRef(null);
|
|
5066
5152
|
useEffect(() => {
|
|
5067
5153
|
const el = nativeRef.current;
|
|
5068
5154
|
if (el === null) return;
|
|
5069
5155
|
const onChange = (event) => {
|
|
5156
|
+
committedRef.current = true;
|
|
5070
5157
|
const committed = normalizeHex$1(event.target.value);
|
|
5071
5158
|
const current = valueRef.current === null ? null : normalizeHex$1(valueRef.current);
|
|
5072
5159
|
if (committed !== null && committed !== current) onCommitRef.current(committed);
|
|
@@ -5079,6 +5166,7 @@ function ColorPickerInner({ value, onCommit, onClose }) {
|
|
|
5079
5166
|
const commitField = (raw) => {
|
|
5080
5167
|
const match = raw.trim().match(STRICT_HEX_RE);
|
|
5081
5168
|
if (match === null) return false;
|
|
5169
|
+
committedRef.current = true;
|
|
5082
5170
|
const hex = `#${match[1].toLowerCase()}`;
|
|
5083
5171
|
setDraft(hex);
|
|
5084
5172
|
setField(hex);
|
|
@@ -5092,7 +5180,10 @@ function ColorPickerInner({ value, onCommit, onClose }) {
|
|
|
5092
5180
|
ref: nativeRef,
|
|
5093
5181
|
type: "color",
|
|
5094
5182
|
value: draft,
|
|
5095
|
-
onInput: (event) =>
|
|
5183
|
+
onInput: (event) => {
|
|
5184
|
+
touchedRef.current = true;
|
|
5185
|
+
setDraft(event.currentTarget.value);
|
|
5186
|
+
},
|
|
5096
5187
|
"aria-label": t("colorPicker.aria"),
|
|
5097
5188
|
className: "h-8 w-8 cursor-pointer rounded-xs border border-(--border) bg-transparent p-0.5"
|
|
5098
5189
|
}), /* @__PURE__ */ jsx(Input, {
|
|
@@ -5135,7 +5226,10 @@ function ColorPicker(props) {
|
|
|
5135
5226
|
* Two opt-in extras (off by default): the transparent ("no color") swatch
|
|
5136
5227
|
* (a theme-background circle crossed by a thin `--destructive` line —
|
|
5137
5228
|
* reports `onSelectNone`) and the custom swatch (a rainbow circle that
|
|
5138
|
-
* reports `onCustom`; the full picker it opens is built on first use).
|
|
5229
|
+
* reports `onCustom`; the full picker it opens is built on first use). A
|
|
5230
|
+
* current value that matches no entry (a non-preset custom color) shows a
|
|
5231
|
+
* small dot of its own over the custom swatch — the row's highlight shows
|
|
5232
|
+
* a preset's match only, so the dot is the custom value's marker.
|
|
5139
5233
|
*/
|
|
5140
5234
|
/** The palette's i18n scope: the package's instance stands the provider's
|
|
5141
5235
|
* seat over the palette (the primitive stands also outside the editor's
|
|
@@ -5161,6 +5255,8 @@ function ColorSwatchesInner({ colors, current, onSelect, label, allowNone = fals
|
|
|
5161
5255
|
if (currentHex === null) return false;
|
|
5162
5256
|
return (normalizeHex$1(entry) ?? resolved[index] ?? null) === currentHex;
|
|
5163
5257
|
};
|
|
5258
|
+
const currentHex = current === null || current === void 0 ? null : normalizeHex$1(current);
|
|
5259
|
+
const customCarriesCurrent = allowCustom && currentHex !== null && !colors.some((entry, index) => (normalizeHex$1(entry) ?? resolved[index] ?? null) === currentHex);
|
|
5164
5260
|
const selectSwatch = (entry) => (event) => {
|
|
5165
5261
|
const hex = resolveColorToHex(entry, event.currentTarget);
|
|
5166
5262
|
if (hex !== null) onSelect(hex);
|
|
@@ -5214,9 +5310,15 @@ function ColorSwatchesInner({ colors, current, onSelect, label, allowNone = fals
|
|
|
5214
5310
|
"data-color": "custom",
|
|
5215
5311
|
"aria-label": t("common:color.custom"),
|
|
5216
5312
|
title: t("common:color.custom"),
|
|
5217
|
-
className: "size-5 rounded-full border border-(--border) transition-none",
|
|
5313
|
+
className: "relative size-5 rounded-full border border-(--border) transition-none",
|
|
5218
5314
|
style: { background: "conic-gradient(red, orange, yellow, green, cyan, blue, magenta, red)" },
|
|
5219
|
-
onClick: onCustom
|
|
5315
|
+
onClick: onCustom,
|
|
5316
|
+
children: customCarriesCurrent && currentHex !== null && /* @__PURE__ */ jsx("span", {
|
|
5317
|
+
"aria-hidden": true,
|
|
5318
|
+
"data-slot": "docblocks/palette-custom-current",
|
|
5319
|
+
className: "\n absolute top-1/2 left-1/2 size-3 -translate-x-1/2 -translate-y-1/2 rounded-full border border-(--border)\n ",
|
|
5320
|
+
style: { background: currentHex }
|
|
5321
|
+
})
|
|
5220
5322
|
})
|
|
5221
5323
|
]
|
|
5222
5324
|
});
|
|
@@ -5244,12 +5346,13 @@ function PopoverContent({ className, align = "center", sideOffset = 4, ...props
|
|
|
5244
5346
|
pointer-events-auto z-modal flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-2.5
|
|
5245
5347
|
rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10
|
|
5246
5348
|
outline-hidden duration-100
|
|
5247
|
-
data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95
|
|
5248
|
-
data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95
|
|
5249
5349
|
data-[side=bottom]:slide-in-from-top-2
|
|
5250
5350
|
data-[side=left]:slide-in-from-right-2
|
|
5251
5351
|
data-[side=right]:slide-in-from-left-2
|
|
5252
5352
|
data-[side=top]:slide-in-from-bottom-2
|
|
5353
|
+
data-[state=closed]:pointer-events-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0
|
|
5354
|
+
data-[state=closed]:zoom-out-95
|
|
5355
|
+
data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95
|
|
5253
5356
|
`, className),
|
|
5254
5357
|
...props
|
|
5255
5358
|
}) });
|
|
@@ -11993,7 +12096,10 @@ function EditorProviderStand({ doc, provider, blockRenderers, blobResolver, blob
|
|
|
11993
12096
|
const [store] = useState(() => createEditorStore(doc, rootId));
|
|
11994
12097
|
const docRef = useRef(doc);
|
|
11995
12098
|
const rootIdRef = useRef(rootId);
|
|
11996
|
-
const selectionRef = useRef({
|
|
12099
|
+
const selectionRef = useRef({
|
|
12100
|
+
view: null,
|
|
12101
|
+
reseatUntil: { current: 0 }
|
|
12102
|
+
});
|
|
11997
12103
|
const parentContext = useContext(EditorContext);
|
|
11998
12104
|
const effectiveAccessLevel = accessLevel ?? parentContext?.accessLevel ?? "editor";
|
|
11999
12105
|
const effectiveMode = effectiveAccessLevel === "viewer" ? "published" : mode ?? parentContext?.mode ?? "edit";
|
|
@@ -16029,8 +16135,12 @@ function extendRangeFocusEnd(doc, selection, key) {
|
|
|
16029
16135
|
if (selection.kind !== "text" || selection.to === null) return false;
|
|
16030
16136
|
const domSelection = window.getSelection();
|
|
16031
16137
|
if (domSelection === null || domSelection.rangeCount === 0) return false;
|
|
16032
|
-
const { anchorNode, anchorOffset
|
|
16033
|
-
if (anchorNode === null
|
|
16138
|
+
const { anchorNode, anchorOffset } = domSelection;
|
|
16139
|
+
if (anchorNode === null) return false;
|
|
16140
|
+
const effective = effectiveFocusEnd(domSelection);
|
|
16141
|
+
if (effective === null) return false;
|
|
16142
|
+
const focusNode = effective.node;
|
|
16143
|
+
const focusOffset = effective.offset;
|
|
16034
16144
|
const scope = editorRootOf(focusNode);
|
|
16035
16145
|
const focusBlock = textBlockOfNode(focusNode);
|
|
16036
16146
|
if (focusBlock === null) return false;
|
|
@@ -16079,7 +16189,7 @@ function extendRangeFocusEnd(doc, selection, key) {
|
|
|
16079
16189
|
const target = scope.querySelector(`[data-block-id="${CSS.escape(next.blockId)}"][data-slot="docblocks/block-content"]`);
|
|
16080
16190
|
if (target === null) return false;
|
|
16081
16191
|
const position = locatePosition(target, next.offset);
|
|
16082
|
-
|
|
16192
|
+
applyDomSelection(anchorNode, anchorOffset, position.node, position.offset);
|
|
16083
16193
|
return true;
|
|
16084
16194
|
}
|
|
16085
16195
|
//#endregion
|
|
@@ -16123,6 +16233,36 @@ function mapDomSelection(root, domSelection, doc) {
|
|
|
16123
16233
|
const range = domSelection.getRangeAt(0);
|
|
16124
16234
|
const anchorBlock = textBlockOf(anchorNode, root);
|
|
16125
16235
|
const focusBlock = textBlockOf(focusNode, root);
|
|
16236
|
+
if ((anchorNode instanceof Element ? anchorNode : anchorNode.parentElement ?? null)?.closest("[data-slot=\"docblocks/canvas-view\"]") !== null && anchorBlock === null && editableOfNode(anchorNode) === null) return { kind: "none" };
|
|
16237
|
+
const startEditable = editableOfNode(range.startContainer);
|
|
16238
|
+
const endEditable = range.startContainer === range.endContainer ? startEditable : editableOfNode(range.endContainer);
|
|
16239
|
+
if (startEditable !== null && endEditable !== null && startEditable !== endEditable) {
|
|
16240
|
+
const first = {
|
|
16241
|
+
node: range.startContainer,
|
|
16242
|
+
offset: range.startOffset,
|
|
16243
|
+
textBlock: textBlockOf(range.startContainer, root)
|
|
16244
|
+
};
|
|
16245
|
+
const last = {
|
|
16246
|
+
node: range.endContainer,
|
|
16247
|
+
offset: range.endOffset,
|
|
16248
|
+
textBlock: textBlockOf(range.endContainer, root)
|
|
16249
|
+
};
|
|
16250
|
+
if (first.textBlock === null && last.textBlock === null && !rangeTouchesTextBlock(root, range)) return mapBlocksSelection(root, domSelection, anchorBlock);
|
|
16251
|
+
const from = resolveEndpoint(doc, root, first, "first");
|
|
16252
|
+
const to = resolveEndpoint(doc, root, last, "last");
|
|
16253
|
+
if (from === null || to === null) return mapBlocksSelection(root, domSelection, anchorBlock);
|
|
16254
|
+
if (from.blockId === to.blockId && from.offset === to.offset) return {
|
|
16255
|
+
kind: "text",
|
|
16256
|
+
from,
|
|
16257
|
+
to: null
|
|
16258
|
+
};
|
|
16259
|
+
return {
|
|
16260
|
+
kind: "text",
|
|
16261
|
+
from,
|
|
16262
|
+
to,
|
|
16263
|
+
...!(range.startContainer === anchorNode && range.startOffset === anchorOffset) ? { reverse: true } : {}
|
|
16264
|
+
};
|
|
16265
|
+
}
|
|
16126
16266
|
if (anchorBlock !== null && focusBlock === anchorBlock) return mapTextSelection(anchorBlock, anchorNode, anchorOffset, focusNode, focusOffset);
|
|
16127
16267
|
const anchorIsFirst = range.startContainer === anchorNode && range.startOffset === anchorOffset;
|
|
16128
16268
|
const first = {
|
|
@@ -16340,11 +16480,13 @@ function syncDomSelection(root, doc, view) {
|
|
|
16340
16480
|
* not — the mapping must not clobber a `blocks` view there.
|
|
16341
16481
|
*/
|
|
16342
16482
|
function domSelectionInEditable(domSelection) {
|
|
16343
|
-
|
|
16344
|
-
const
|
|
16345
|
-
|
|
16346
|
-
|
|
16347
|
-
|
|
16483
|
+
const anchor = editableOfNode(domSelection?.anchorNode ?? null);
|
|
16484
|
+
const focus = editableOfNode(domSelection?.focusNode ?? null);
|
|
16485
|
+
return anchor !== null && focus !== null;
|
|
16486
|
+
}
|
|
16487
|
+
/** The contenteditable that owns a node (the node's element or its parent's), or null. */
|
|
16488
|
+
function editableOfNode(node) {
|
|
16489
|
+
return (node === null ? null : node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement)?.closest("[contenteditable=\"true\"]") ?? null;
|
|
16348
16490
|
}
|
|
16349
16491
|
//#endregion
|
|
16350
16492
|
//#region src/editing/cross-range-keys.ts
|
|
@@ -16376,6 +16518,13 @@ function handleCrossRangeKey(doc, view, selection, root, event) {
|
|
|
16376
16518
|
if (moved) syncDomSelection(root, doc, view);
|
|
16377
16519
|
return true;
|
|
16378
16520
|
}
|
|
16521
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
16522
|
+
const domSelection = window.getSelection();
|
|
16523
|
+
if (domSelection === null || domSelection.rangeCount === 0 || domSelection.focusNode === null) return false;
|
|
16524
|
+
event.preventDefault();
|
|
16525
|
+
domSelection.collapse(domSelection.focusNode, domSelection.focusOffset);
|
|
16526
|
+
return true;
|
|
16527
|
+
}
|
|
16379
16528
|
if (event.key === "Delete" || event.key === "Backspace") {
|
|
16380
16529
|
event.preventDefault();
|
|
16381
16530
|
const result = doc.deleteTextRange(selection.from, selection.to);
|
|
@@ -17002,7 +17151,9 @@ function useBlockInput({ id, blockType, context, innerRef, wrapperRef, declareLo
|
|
|
17002
17151
|
const domSelection = window.getSelection();
|
|
17003
17152
|
if (selection.kind !== "blocks" && mappedRoot instanceof HTMLElement && domSelection !== null) {
|
|
17004
17153
|
const fresh = mapDomSelection(mappedRoot, domSelection, doc);
|
|
17005
|
-
if (!selectionsEqual(fresh, selection))
|
|
17154
|
+
if (!selectionsEqual(fresh, selection)) {
|
|
17155
|
+
if (!(fresh.kind !== "text" && Date.now() < selectionRef.reseatUntil.current)) view.set(fresh);
|
|
17156
|
+
}
|
|
17006
17157
|
selection = view.get();
|
|
17007
17158
|
root = mappedRoot;
|
|
17008
17159
|
}
|
|
@@ -17152,7 +17303,17 @@ function useBlockInput({ id, blockType, context, innerRef, wrapperRef, declareLo
|
|
|
17152
17303
|
const domSelection = window.getSelection();
|
|
17153
17304
|
if (domSelection === null || domSelection.rangeCount === 0 || !domSelection.isCollapsed) return;
|
|
17154
17305
|
if (domSelection.anchorNode === null || !inner.contains(domSelection.anchorNode)) return;
|
|
17155
|
-
|
|
17306
|
+
const offset = caretOffsetIn(inner, domSelection.anchorNode, domSelection.anchorOffset);
|
|
17307
|
+
if (offset !== 0) {
|
|
17308
|
+
const text = doc.getText(id);
|
|
17309
|
+
if (offset === text.length && text.endsWith("\n")) {
|
|
17310
|
+
event.preventDefault();
|
|
17311
|
+
doc.deleteText(id, offset - 1, offset);
|
|
17312
|
+
focusCaretIn(id, offset - 1, editorRootOf(inner));
|
|
17313
|
+
return;
|
|
17314
|
+
}
|
|
17315
|
+
return;
|
|
17316
|
+
}
|
|
17156
17317
|
if (inCell) return;
|
|
17157
17318
|
if ((blockType === "quote" || blockType === "callout" || blockType === "code") && doc.getTextLength(id) > 0) return;
|
|
17158
17319
|
const merged = mergeWithPrevious(doc, id, context.parentId);
|
|
@@ -17388,8 +17549,12 @@ function useBlockKeyboard({ id, blockType, context, innerRef }) {
|
|
|
17388
17549
|
if (selectionRef.view?.get().kind === "blocks") return;
|
|
17389
17550
|
const domSelection = window.getSelection();
|
|
17390
17551
|
if (domSelection === null || domSelection.rangeCount === 0) return;
|
|
17391
|
-
const { anchorNode, anchorOffset
|
|
17392
|
-
if (anchorNode === null
|
|
17552
|
+
const { anchorNode, anchorOffset } = domSelection;
|
|
17553
|
+
if (anchorNode === null) return;
|
|
17554
|
+
const effective = effectiveFocusEnd(domSelection);
|
|
17555
|
+
if (effective === null) return;
|
|
17556
|
+
const focusNode = effective.node;
|
|
17557
|
+
const focusOffset = effective.offset;
|
|
17393
17558
|
const focusBlock = textBlockOfNode(focusNode);
|
|
17394
17559
|
if (focusBlock === null) return;
|
|
17395
17560
|
const focusBlockId = focusBlock.dataset.blockId;
|
|
@@ -17580,11 +17745,14 @@ function useTextInner({ id, block, blockType, context, wrapperRef }) {
|
|
|
17580
17745
|
if (!startInside && !endInside) return;
|
|
17581
17746
|
const from = caretOffsetIn(el, range.startContainer, range.startOffset);
|
|
17582
17747
|
const to = caretOffsetIn(el, range.endContainer, range.endOffset);
|
|
17748
|
+
const farContainer = startInside ? range.endContainer : range.startContainer;
|
|
17749
|
+
const farBlockId = (farContainer.nodeType === Node.TEXT_NODE ? farContainer.parentElement : farContainer)?.closest("[data-block-id]")?.getAttribute("data-block-id") ?? null;
|
|
17583
17750
|
const crossCapture = () => ({
|
|
17584
17751
|
startIsLocal: startInside,
|
|
17585
17752
|
localOffset: startInside ? from : to,
|
|
17586
|
-
farNode:
|
|
17587
|
-
farOffset: startInside ? range.endOffset : range.startOffset
|
|
17753
|
+
farNode: farContainer,
|
|
17754
|
+
farOffset: startInside ? range.endOffset : range.startOffset,
|
|
17755
|
+
farBlockId
|
|
17588
17756
|
});
|
|
17589
17757
|
if (nextText === prevText) {
|
|
17590
17758
|
if (startInside && endInside) {
|
|
@@ -17638,16 +17806,26 @@ function useTextInner({ id, block, blockType, context, wrapperRef }) {
|
|
|
17638
17806
|
renderRunsInto(pending.runs, el);
|
|
17639
17807
|
appliedRef.current = block;
|
|
17640
17808
|
if ("cross" in pending) {
|
|
17809
|
+
const cross = pending.cross;
|
|
17810
|
+
let farNode = cross.farNode;
|
|
17811
|
+
let farOffset = cross.farOffset;
|
|
17812
|
+
if (!document.contains(farNode) && cross.farBlockId !== null) {
|
|
17813
|
+
const farInner = (el.closest("[data-slot=\"docblocks/editor\"]") ?? document).querySelector(`[data-block-id="${CSS.escape(cross.farBlockId)}"][data-slot="docblocks/block-content"]`);
|
|
17814
|
+
if (farInner !== null) {
|
|
17815
|
+
const remap = locatePosition(farInner, cross.farOffset);
|
|
17816
|
+
farNode = remap.node;
|
|
17817
|
+
farOffset = remap.offset;
|
|
17818
|
+
}
|
|
17819
|
+
}
|
|
17641
17820
|
const selection = window.getSelection();
|
|
17642
|
-
if (selection !== null && selection.rangeCount > 0 && (selection.containsNode(
|
|
17643
|
-
const local = locatePosition(el,
|
|
17644
|
-
const cross = pending.cross;
|
|
17821
|
+
if (selection !== null && selection.rangeCount > 0 && (selection.containsNode(farNode, true) || selectionOrphaned()) && document.contains(farNode)) {
|
|
17822
|
+
const local = locatePosition(el, cross.localOffset);
|
|
17645
17823
|
const range = document.createRange();
|
|
17646
17824
|
if (cross.startIsLocal) {
|
|
17647
17825
|
range.setStart(local.node, local.offset);
|
|
17648
|
-
range.setEnd(
|
|
17826
|
+
range.setEnd(farNode, farOffset);
|
|
17649
17827
|
} else {
|
|
17650
|
-
range.setStart(
|
|
17828
|
+
range.setStart(farNode, farOffset);
|
|
17651
17829
|
range.setEnd(local.node, local.offset);
|
|
17652
17830
|
}
|
|
17653
17831
|
selection.removeAllRanges();
|
|
@@ -18336,8 +18514,10 @@ function calloutIconName(icon, variant) {
|
|
|
18336
18514
|
* focus stands the gesture). The pick's stand closes the menu over the
|
|
18337
18515
|
* color and the icon's apply (the type preset stays open). The custom
|
|
18338
18516
|
* swatch's press toggles the picker's inline section over the menu's
|
|
18339
|
-
* background row;
|
|
18340
|
-
*
|
|
18517
|
+
* background row; a close without an explicit commit (the Esc / the
|
|
18518
|
+
* dismiss) applies the last scrubbed value (the picker's unmount commits
|
|
18519
|
+
* it; the grid's custom swatch carries a non-preset current value as its
|
|
18520
|
+
* own dot).
|
|
18341
18521
|
*/
|
|
18342
18522
|
function CalloutBlockMenu({ id, block }) {
|
|
18343
18523
|
const { t } = useTranslation("editor");
|
|
@@ -19378,7 +19558,11 @@ const CanvasSessionContext = createContext({
|
|
|
19378
19558
|
* `interactive` toggles the hit path: the canvas view is interactive
|
|
19379
19559
|
* (the line is selectable, the endpoints draggable), the page view's
|
|
19380
19560
|
* embedded surface stands it inert (no selection / dragging there — the
|
|
19381
|
-
* bands must not intercept the content's clicks).
|
|
19561
|
+
* bands must not intercept the content's clicks). The hit path's `d`
|
|
19562
|
+
* is returned alongside the content: the hit path renders in its own
|
|
19563
|
+
* bbox-sized svg — the webkit does not hit-test the overflow-visible
|
|
19564
|
+
* content over the 1x1 svg's box, so the line is not clickable from
|
|
19565
|
+
* there without the box.
|
|
19382
19566
|
*/
|
|
19383
19567
|
function connectorLineOf(surfaceId, connectorId, block, childIds, blocks, selected, interactive, session) {
|
|
19384
19568
|
const attrs = block.attrs;
|
|
@@ -19530,8 +19714,8 @@ function connectorLineOf(surfaceId, connectorId, block, childIds, blocks, select
|
|
|
19530
19714
|
})]
|
|
19531
19715
|
}) : null;
|
|
19532
19716
|
const pathStyle = { stroke };
|
|
19533
|
-
return
|
|
19534
|
-
/* @__PURE__ */ jsx("path", {
|
|
19717
|
+
return {
|
|
19718
|
+
content: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("path", {
|
|
19535
19719
|
"data-connector-id": connectorId,
|
|
19536
19720
|
"data-selected": selected || void 0,
|
|
19537
19721
|
d: path,
|
|
@@ -19542,18 +19726,36 @@ function connectorLineOf(surfaceId, connectorId, block, childIds, blocks, select
|
|
|
19542
19726
|
strokeDasharray: dash,
|
|
19543
19727
|
strokeLinecap: attrs.strokeStyle === "dot" ? "round" : void 0,
|
|
19544
19728
|
style: pathStyle
|
|
19545
|
-
}),
|
|
19546
|
-
interactive
|
|
19547
|
-
|
|
19548
|
-
|
|
19549
|
-
|
|
19550
|
-
|
|
19551
|
-
|
|
19552
|
-
|
|
19553
|
-
|
|
19554
|
-
|
|
19555
|
-
|
|
19556
|
-
|
|
19729
|
+
}), snapBadge] }),
|
|
19730
|
+
hitD: interactive ? path : null
|
|
19731
|
+
};
|
|
19732
|
+
}
|
|
19733
|
+
/** The path's points' bounding box (the surface's center-anchor space):
|
|
19734
|
+
* the `d`'s M/L's coordinates (the plain numbers' parse — the builder's
|
|
19735
|
+
* template's format). The hit path's svg box must contain the stroke:
|
|
19736
|
+
* the webkit hit-tests only inside the svg's own box, so the hit path
|
|
19737
|
+
* needs a bbox-sized svg over the 1x1 draw layer (draw-only there). */
|
|
19738
|
+
function hitBoxOf(d) {
|
|
19739
|
+
const numbers = d.replace(/[A-Za-z]/g, " ").split(/\s+/).map(Number.parseFloat).filter((value) => Number.isFinite(value));
|
|
19740
|
+
if (numbers.length < 4) return null;
|
|
19741
|
+
let minX = Infinity;
|
|
19742
|
+
let minY = Infinity;
|
|
19743
|
+
let maxX = -Infinity;
|
|
19744
|
+
let maxY = -Infinity;
|
|
19745
|
+
for (let i = 0; i < numbers.length; i += 2) {
|
|
19746
|
+
const x = numbers[i] ?? 0;
|
|
19747
|
+
const y = numbers[i + 1] ?? 0;
|
|
19748
|
+
minX = Math.min(minX, x);
|
|
19749
|
+
minY = Math.min(minY, y);
|
|
19750
|
+
maxX = Math.max(maxX, x);
|
|
19751
|
+
maxY = Math.max(maxY, y);
|
|
19752
|
+
}
|
|
19753
|
+
return {
|
|
19754
|
+
x: minX,
|
|
19755
|
+
y: minY,
|
|
19756
|
+
w: maxX - minX,
|
|
19757
|
+
h: maxY - minY
|
|
19758
|
+
};
|
|
19557
19759
|
}
|
|
19558
19760
|
/**
|
|
19559
19761
|
* One connector's line, the surface's Z-ORDER stand (the explicit zIndex):
|
|
@@ -19565,9 +19767,12 @@ function connectorLineOf(surfaceId, connectorId, block, childIds, blocks, select
|
|
|
19565
19767
|
* CanvasItem's wrapper stands at the surface's origin), the shared
|
|
19566
19768
|
* arrowhead defs (the surface's hidden defs svg) stand the markers.
|
|
19567
19769
|
*
|
|
19568
|
-
* The SVG
|
|
19569
|
-
* transparent stroke
|
|
19570
|
-
*
|
|
19770
|
+
* The SVG is pointer-events none: the visible path is draw-only. The
|
|
19771
|
+
* per-line hit path (the wide transparent stroke, pointer-events stroke)
|
|
19772
|
+
* renders in its own bbox-sized svg — the webkit hit-tests only inside
|
|
19773
|
+
* the svg's own box, so the 1x1 overflow-visible layer is not
|
|
19774
|
+
* click-selectable there; the chromium and the firefox reach the
|
|
19775
|
+
* overflow, so the box change alters no behavior there.
|
|
19571
19776
|
*/
|
|
19572
19777
|
function ConnectorLine({ id, interactive = true }) {
|
|
19573
19778
|
const { store } = useEditorContext();
|
|
@@ -19588,7 +19793,8 @@ function ConnectorLine({ id, interactive = true }) {
|
|
|
19588
19793
|
store
|
|
19589
19794
|
]);
|
|
19590
19795
|
if (content === null) return null;
|
|
19591
|
-
|
|
19796
|
+
const hitBox = content.hitD !== null ? hitBoxOf(content.hitD) : null;
|
|
19797
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("svg", {
|
|
19592
19798
|
"data-slot": "docblocks/connector-layer",
|
|
19593
19799
|
"data-block-id": id,
|
|
19594
19800
|
style: {
|
|
@@ -19600,8 +19806,32 @@ function ConnectorLine({ id, interactive = true }) {
|
|
|
19600
19806
|
overflow: "visible",
|
|
19601
19807
|
pointerEvents: "none"
|
|
19602
19808
|
},
|
|
19603
|
-
children: content
|
|
19604
|
-
})
|
|
19809
|
+
children: content.content
|
|
19810
|
+
}), content.hitD !== null && hitBox !== null && /* @__PURE__ */ jsx("svg", {
|
|
19811
|
+
"data-slot": "docblocks/connector-hit-layer",
|
|
19812
|
+
"data-block-id": id,
|
|
19813
|
+
style: {
|
|
19814
|
+
position: "absolute",
|
|
19815
|
+
left: `calc(50% + ${hitBox.x - 16}px)`,
|
|
19816
|
+
top: `calc(50% + ${hitBox.y - 16}px)`,
|
|
19817
|
+
width: hitBox.w + 32,
|
|
19818
|
+
height: hitBox.h + 32,
|
|
19819
|
+
overflow: "visible",
|
|
19820
|
+
pointerEvents: "none"
|
|
19821
|
+
},
|
|
19822
|
+
children: /* @__PURE__ */ jsx("g", {
|
|
19823
|
+
transform: `translate(${16 - hitBox.x}, ${16 - hitBox.y})`,
|
|
19824
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
19825
|
+
"data-connector-hit": id,
|
|
19826
|
+
d: content.hitD,
|
|
19827
|
+
stroke: "transparent",
|
|
19828
|
+
strokeWidth: 12,
|
|
19829
|
+
fill: "none",
|
|
19830
|
+
pointerEvents: "stroke",
|
|
19831
|
+
className: "cursor-pointer"
|
|
19832
|
+
})
|
|
19833
|
+
})
|
|
19834
|
+
})] });
|
|
19605
19835
|
}
|
|
19606
19836
|
//#endregion
|
|
19607
19837
|
//#region src/blocks/PlaceholderBlock.tsx
|
|
@@ -28034,7 +28264,7 @@ function crossRangeClipboardContent(doc, selection) {
|
|
|
28034
28264
|
function seatCrossSelection(anchor, position) {
|
|
28035
28265
|
const base = (anchor.node.compareDocumentPosition(position.node) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0 || anchor.offset !== 0 ? anchor : position;
|
|
28036
28266
|
const focus = base === anchor ? position : anchor;
|
|
28037
|
-
|
|
28267
|
+
applyDomSelection(base.node, base.offset, focus.node, focus.offset);
|
|
28038
28268
|
}
|
|
28039
28269
|
/**
|
|
28040
28270
|
* The text position under (x, y) inside the editor's own scope: the
|
|
@@ -28229,6 +28459,43 @@ function useCrossDrag(rootRef, doc) {
|
|
|
28229
28459
|
//#endregion
|
|
28230
28460
|
//#region src/editing/use-editing.ts
|
|
28231
28461
|
/**
|
|
28462
|
+
* The history reseat's window over the undo/redo (the remount's transient
|
|
28463
|
+
* selection loss's stand): the window's end stands the mapping's none's
|
|
28464
|
+
* suppression. The engine's remount commit + the selectionchange's settle
|
|
28465
|
+
* stands the sub-second over the load, so the window stands a margin over
|
|
28466
|
+
* it (the staleness is bounded by the window's end; the explicit paths —
|
|
28467
|
+
* the Escape, the gutter's reset — stand the real clear).
|
|
28468
|
+
*/
|
|
28469
|
+
const RESEAT_WINDOW_MS = 2e3;
|
|
28470
|
+
/**
|
|
28471
|
+
* The undo/redo's reseat over the engine's event ordering: the fast path
|
|
28472
|
+
* re-reads the view over the synchronous read (the restoration's stand
|
|
28473
|
+
* lands inside the undo/redo call), and the window's stand re-runs the
|
|
28474
|
+
* reseat when the restoration's emission lands over the transaction's
|
|
28475
|
+
* settle (the engine's ordering stands the popped's event after the
|
|
28476
|
+
* handler's return — the reseat then reads the restored state). The
|
|
28477
|
+
* reseat's own stands (the user's live selection's stop, the seated's
|
|
28478
|
+
* short-circuit) keep the re-runs from clobbering.
|
|
28479
|
+
*/
|
|
28480
|
+
function scheduleHistoryReseat(doc, view, root) {
|
|
28481
|
+
if (view === null) return;
|
|
28482
|
+
const scope = root ?? document;
|
|
28483
|
+
const run = () => {
|
|
28484
|
+
reseatHistorySelection(doc, view, scope);
|
|
28485
|
+
};
|
|
28486
|
+
run();
|
|
28487
|
+
let alive = true;
|
|
28488
|
+
const unsubscribe = view.onChange((selection) => {
|
|
28489
|
+
if (!alive) return;
|
|
28490
|
+
if (selection.kind !== "text") return;
|
|
28491
|
+
run();
|
|
28492
|
+
});
|
|
28493
|
+
window.setTimeout(() => {
|
|
28494
|
+
alive = false;
|
|
28495
|
+
unsubscribe();
|
|
28496
|
+
}, RESEAT_WINDOW_MS);
|
|
28497
|
+
}
|
|
28498
|
+
/**
|
|
28232
28499
|
* Document-level wiring owned by the editor root: `selectionchange` maps
|
|
28233
28500
|
* the DOM selection to the core Selection view, the mod+z / mod+shift+z /
|
|
28234
28501
|
* mod+y hotkeys drive doc.undo()/doc.redo(), and the copy event carries
|
|
@@ -28254,6 +28521,7 @@ function useEditing() {
|
|
|
28254
28521
|
if (view.get().kind === "blocks" && !domSelectionInEditable(window.getSelection())) return;
|
|
28255
28522
|
const anchorNode = window.getSelection()?.anchorNode ?? null;
|
|
28256
28523
|
if ((anchorNode instanceof Element ? anchorNode : anchorNode?.parentElement ?? null)?.closest("[data-slot=\"docblocks/page-card\"]") !== null) return;
|
|
28524
|
+
if (next.kind !== "text" && Date.now() < selectionRef.reseatUntil.current) return;
|
|
28257
28525
|
if (!selectionsEqual(next, view.get())) view.set(next);
|
|
28258
28526
|
};
|
|
28259
28527
|
document.addEventListener("selectionchange", onSelectionChange);
|
|
@@ -28308,11 +28576,13 @@ function useEditing() {
|
|
|
28308
28576
|
if (key === "z" && !event.shiftKey) {
|
|
28309
28577
|
event.preventDefault();
|
|
28310
28578
|
doc.undo();
|
|
28311
|
-
|
|
28579
|
+
selectionRef.reseatUntil.current = Date.now() + RESEAT_WINDOW_MS;
|
|
28580
|
+
scheduleHistoryReseat(doc, view, root);
|
|
28312
28581
|
} else if (key === "z" && event.shiftKey || key === "y") {
|
|
28313
28582
|
event.preventDefault();
|
|
28314
28583
|
doc.redo();
|
|
28315
|
-
|
|
28584
|
+
selectionRef.reseatUntil.current = Date.now() + RESEAT_WINDOW_MS;
|
|
28585
|
+
scheduleHistoryReseat(doc, view, root);
|
|
28316
28586
|
}
|
|
28317
28587
|
};
|
|
28318
28588
|
const onCopy = (event) => {
|
|
@@ -29115,11 +29385,15 @@ function clampTailSeatCrossing(next, draggedId, seatId) {
|
|
|
29115
29385
|
* event's clientY, or the last changed touch's (the touch release event
|
|
29116
29386
|
* carries no coordinate of its own). Null when the event stands no
|
|
29117
29387
|
* usable coordinate (the measurement is stale — the caller keeps the
|
|
29118
|
-
* preview or skips the commit).
|
|
29388
|
+
* preview or skips the commit). The touch's stand is duck-typed over
|
|
29389
|
+
* `changedTouches`: the global `TouchEvent` is absent on the no-touch
|
|
29390
|
+
* Safari (a desktop Safari on macOS), so the `instanceof` stand throws a
|
|
29391
|
+
* ReferenceError over every drag callback.
|
|
29119
29392
|
*/
|
|
29120
29393
|
function pointerYOf(event) {
|
|
29121
|
-
if (
|
|
29122
|
-
const
|
|
29394
|
+
if ("changedTouches" in event) {
|
|
29395
|
+
const touches = event.changedTouches;
|
|
29396
|
+
const touch = touches[touches.length - 1];
|
|
29123
29397
|
return touch === null || touch === void 0 ? null : touch.clientY;
|
|
29124
29398
|
}
|
|
29125
29399
|
return event.clientY;
|
|
@@ -997,6 +997,16 @@ interface MentionProvider {
|
|
|
997
997
|
/** The per-instance selection view; null before the mount effect ran and after unmount. */
|
|
998
998
|
interface SelectionRef {
|
|
999
999
|
view: SelectionView | null;
|
|
1000
|
+
/**
|
|
1001
|
+
* The history reseat's window end (the epoch ms): the undo/redo's remount
|
|
1002
|
+
* stands the engine's transient selection loss, and the gesture-time and
|
|
1003
|
+
* event-based mappings' none push stands the remount's residue over the
|
|
1004
|
+
* window (not the user's clear — the real clear stands the explicit
|
|
1005
|
+
* paths: the Escape, the editor's outside's focus).
|
|
1006
|
+
*/
|
|
1007
|
+
reseatUntil: {
|
|
1008
|
+
current: number;
|
|
1009
|
+
};
|
|
1000
1010
|
}
|
|
1001
1011
|
/**
|
|
1002
1012
|
* The local user's presence identity and the selection-sharing cipher
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as BlockRendererMap, r as BlockRendererProps, t as BlockRenderer } from "./index-YJcgf8Hh.mjs";
|
|
2
|
-
import { $ as ProtocolLimits, A as filterIsComplete, B as DatabaseBridge, C as DatabaseTableView, Ct as BlobSubtype, D as DatabaseTableRow, E as DatabaseTablePhase, F as BridgeTableDescription, G as IsoInstant, H as FilterConditionInput, I as BridgeValueInput, J as OperationStatus, K as MutationContext, L as CellMap, M as BatchOp, N as BridgeColumn, O as UseDatabaseTable, P as BridgeColumnAnnotation, Q as ProtocolDescriptor, R as CellValueParse, S as DatabaseTableProps, St as BlobStore, T as useDatabaseTable, Tt as createMemoryBlobStore, U as FilterInput, V as DomainEvent, W as Id, X as PageSnapshot, Y as PageQuery, Z as ProtocolCapability, _ as PresencePeer, _t as isBridgeError, a as EditorContextValue, at as SortInput, b as useTableList, bt as BlobMeta, c as EmbedSandboxPolicy, ct as SubscriptionStatus, d as PdfPreview, dt as TableEvent, et as QueryOperator, f as PdfPreviewProps, ft as TableRef, g as PresenceIdentity, gt as BridgeErrorCode, h as PresenceCrypto, ht as BridgeError, i as AccessLevel, it as RowWire, j as FilterBuilder, k as emptyFilter, l as MentionEntry, lt as TableDescription, m as SyncEmbedResolver, mt as BRIDGE_ERROR_CODES, n as DEFAULT_EMBED_SANDBOX, nt as Revision, o as EditorMode, ot as SubscriptionHandle, p as PresenceConfig, pt as TableSnapshot, q as MutationReceipt, r as EditorProvider, rt as RowPage, s as EditorProviderProps, st as SubscriptionObserver, t as useEditorContext, tt as QuerySemantics, u as MentionProvider, ut as TableEnvelopeBase, v as PresenceSharing, vt as isBridgeErrorCode, w as UseTableList, wt as blobSubtypeOf, x as DatabaseTable, xt as BlobRef, y as PresenceSharingMode, yt as toBridgeError, z as Column } from "./index-
|
|
2
|
+
import { $ as ProtocolLimits, A as filterIsComplete, B as DatabaseBridge, C as DatabaseTableView, Ct as BlobSubtype, D as DatabaseTableRow, E as DatabaseTablePhase, F as BridgeTableDescription, G as IsoInstant, H as FilterConditionInput, I as BridgeValueInput, J as OperationStatus, K as MutationContext, L as CellMap, M as BatchOp, N as BridgeColumn, O as UseDatabaseTable, P as BridgeColumnAnnotation, Q as ProtocolDescriptor, R as CellValueParse, S as DatabaseTableProps, St as BlobStore, T as useDatabaseTable, Tt as createMemoryBlobStore, U as FilterInput, V as DomainEvent, W as Id, X as PageSnapshot, Y as PageQuery, Z as ProtocolCapability, _ as PresencePeer, _t as isBridgeError, a as EditorContextValue, at as SortInput, b as useTableList, bt as BlobMeta, c as EmbedSandboxPolicy, ct as SubscriptionStatus, d as PdfPreview, dt as TableEvent, et as QueryOperator, f as PdfPreviewProps, ft as TableRef, g as PresenceIdentity, gt as BridgeErrorCode, h as PresenceCrypto, ht as BridgeError, i as AccessLevel, it as RowWire, j as FilterBuilder, k as emptyFilter, l as MentionEntry, lt as TableDescription, m as SyncEmbedResolver, mt as BRIDGE_ERROR_CODES, n as DEFAULT_EMBED_SANDBOX, nt as Revision, o as EditorMode, ot as SubscriptionHandle, p as PresenceConfig, pt as TableSnapshot, q as MutationReceipt, r as EditorProvider, rt as RowPage, s as EditorProviderProps, st as SubscriptionObserver, t as useEditorContext, tt as QuerySemantics, u as MentionProvider, ut as TableEnvelopeBase, v as PresenceSharing, vt as isBridgeErrorCode, w as UseTableList, wt as blobSubtypeOf, x as DatabaseTable, xt as BlobRef, y as PresenceSharingMode, yt as toBridgeError, z as Column } from "./index-CZEO5s2i.mjs";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { ComponentType, ReactNode } from "react";
|
|
5
5
|
import { Block, BlockId, BlockType, Doc, DocMeta, PresenceState, Selection, SelectionView, SyncEmbedRef, TextRun } from "docblocks-core";
|
|
@@ -294,7 +294,7 @@ declare function AlertDescription({ className, ...props }: React.ComponentProps<
|
|
|
294
294
|
//#endregion
|
|
295
295
|
//#region src/shared/ui/Badge.d.ts
|
|
296
296
|
declare const badgeVariants: (props?: ({
|
|
297
|
-
variant?: "default" | "
|
|
297
|
+
variant?: "default" | "destructive" | "outline" | "secondary" | "custom" | null | undefined;
|
|
298
298
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
299
299
|
declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
|
|
300
300
|
asChild?: boolean;
|
|
@@ -305,7 +305,7 @@ declare function BareSelect({ className, children, ...props }: React.ComponentPr
|
|
|
305
305
|
//#endregion
|
|
306
306
|
//#region src/shared/ui/Button.d.ts
|
|
307
307
|
declare const buttonVariants: (props?: ({
|
|
308
|
-
variant?: "link" | "default" | "
|
|
308
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
309
309
|
size?: "default" | "icon" | "sm" | "lg" | "xl" | null | undefined;
|
|
310
310
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
311
311
|
declare function Button({ className, variant, size, asChild, type, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as PopoverHeader, A as SelectTrigger, At as isBridgeErrorCode, B as DropdownMenuRadioGroup, Bt as useMeta, C as SelectContent, Ct as BASELINE_CAPABILITIES, D as SelectScrollDownButton, Dt as BRIDGE_ERROR_CODES, E as SelectLabel, Et as validateProtocolDescriptor, F as DropdownMenuContent, Ft as columnAnnotation, G as DropdownMenuSubContent, Gt as useText, H as DropdownMenuSeparator, Ht as usePresenceSharing, I as DropdownMenuGroup, It as ScrollArea, J as Separator, Jt as blobSubtypeOf, K as DropdownMenuSubTrigger, L as DropdownMenuItem, Lt as Input, M as Label, Mt as conditionMatches, N as DropdownMenu, Nt as parseCellValue, O as SelectScrollUpButton, Ot as BridgeError, P as DropdownMenuCheckboxItem, Pt as TEXT_TYPES, Q as PopoverDescription, R as DropdownMenuLabel, Rt as useBlock, S as Select, St as AlertTitle, T as SelectItem, Tt as isDatabaseBridge, U as DropdownMenuShortcut, Ut as useSelection, V as DropdownMenuRadioItem, Vt as usePresence, W as DropdownMenuSub, Wt as useSelectionView, X as PopoverAnchor, Y as Popover, Yt as createMemoryBlobStore, Z as PopoverContent, _ as FilterBuilder, _t as BareSelect, a as SHAPE_FONT_STACKS, at as CardAction, b as Skeleton, bt as Alert, ct as CardFooter, d as useEditorContext, dt as Pen, et as PopoverTitle, f as DEFAULT_EMBED_SANDBOX, ft as Eye, g as newMutationContext, gt as buttonVariants, h as cellValueEqual, ht as Button, i as SHAPE_FONT_OPTIONS, it as Card, j as SelectValue, jt as toBridgeError, k as SelectSeparator, kt as isBridgeError, l as markToggleAction, lt as CardHeader, m as useDatabaseTable, mt as BookOpen, nt as DocblocksI18nProvider, ot as CardContent, p as GridBody, q as DropdownMenuTrigger, qt as renderRuns, rt as Checkbox, s as FORMAT_PALETTE, st as CardDescription, t as Editor, tt as PopoverTrigger, u as EditorProvider, ut as CardTitle, v as emptyFilter, vt as Badge, w as SelectGroup, wt as PROTOCOL_VERSION, x as SegmentSelector, xt as AlertDescription, y as filterIsComplete, yt as badgeVariants, z as DropdownMenuPortal, zt as useDoc } from "./Editor-
|
|
1
|
+
import { $ as PopoverHeader, A as SelectTrigger, At as isBridgeErrorCode, B as DropdownMenuRadioGroup, Bt as useMeta, C as SelectContent, Ct as BASELINE_CAPABILITIES, D as SelectScrollDownButton, Dt as BRIDGE_ERROR_CODES, E as SelectLabel, Et as validateProtocolDescriptor, F as DropdownMenuContent, Ft as columnAnnotation, G as DropdownMenuSubContent, Gt as useText, H as DropdownMenuSeparator, Ht as usePresenceSharing, I as DropdownMenuGroup, It as ScrollArea, J as Separator, Jt as blobSubtypeOf, K as DropdownMenuSubTrigger, L as DropdownMenuItem, Lt as Input, M as Label, Mt as conditionMatches, N as DropdownMenu, Nt as parseCellValue, O as SelectScrollUpButton, Ot as BridgeError, P as DropdownMenuCheckboxItem, Pt as TEXT_TYPES, Q as PopoverDescription, R as DropdownMenuLabel, Rt as useBlock, S as Select, St as AlertTitle, T as SelectItem, Tt as isDatabaseBridge, U as DropdownMenuShortcut, Ut as useSelection, V as DropdownMenuRadioItem, Vt as usePresence, W as DropdownMenuSub, Wt as useSelectionView, X as PopoverAnchor, Y as Popover, Yt as createMemoryBlobStore, Z as PopoverContent, _ as FilterBuilder, _t as BareSelect, a as SHAPE_FONT_STACKS, at as CardAction, b as Skeleton, bt as Alert, ct as CardFooter, d as useEditorContext, dt as Pen, et as PopoverTitle, f as DEFAULT_EMBED_SANDBOX, ft as Eye, g as newMutationContext, gt as buttonVariants, h as cellValueEqual, ht as Button, i as SHAPE_FONT_OPTIONS, it as Card, j as SelectValue, jt as toBridgeError, k as SelectSeparator, kt as isBridgeError, l as markToggleAction, lt as CardHeader, m as useDatabaseTable, mt as BookOpen, nt as DocblocksI18nProvider, ot as CardContent, p as GridBody, q as DropdownMenuTrigger, qt as renderRuns, rt as Checkbox, s as FORMAT_PALETTE, st as CardDescription, t as Editor, tt as PopoverTrigger, u as EditorProvider, ut as CardTitle, v as emptyFilter, vt as Badge, w as SelectGroup, wt as PROTOCOL_VERSION, x as SegmentSelector, xt as AlertDescription, y as filterIsComplete, yt as badgeVariants, z as DropdownMenuPortal, zt as useDoc } from "./Editor-B1T7ytNQ.mjs";
|
|
2
2
|
import { d as cn, l as preventDefault } from "./constants-BaAj3jDA.mjs";
|
|
3
3
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
package/dist/mermaid/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Kt as INLINE_CODE_CLASS, St as AlertTitle, b as Skeleton, bt as Alert, c as textOf, d as useEditorContext, ft as Eye, n as childrenSlot, o as TextBlock, pt as CodeXml, r as CodeHeader, x as SegmentSelector, xt as AlertDescription } from "../Editor-
|
|
1
|
+
import { Kt as INLINE_CODE_CLASS, St as AlertTitle, b as Skeleton, bt as Alert, c as textOf, d as useEditorContext, ft as Eye, n as childrenSlot, o as TextBlock, pt as CodeXml, r as CodeHeader, x as SegmentSelector, xt as AlertDescription } from "../Editor-B1T7ytNQ.mjs";
|
|
2
2
|
import { r as TriangleAlert } from "../constants-BaAj3jDA.mjs";
|
|
3
3
|
import { t as LoaderCircle } from "../LoaderCircle-AGVFE4IP.mjs";
|
|
4
4
|
import { useEffect, useRef, useState } from "react";
|
package/dist/pdf/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as PdfPreviewProps } from "../index-
|
|
1
|
+
import { f as PdfPreviewProps } from "../index-CZEO5s2i.mjs";
|
|
2
2
|
//#region src/pdf/PdfFirstPage.d.ts
|
|
3
3
|
declare function PdfFirstPage({ id, url, name }: PdfPreviewProps): import("react").JSX.Element;
|
|
4
4
|
//#endregion
|
package/dist/styles.css
CHANGED
|
@@ -2394,17 +2394,6 @@
|
|
|
2394
2394
|
.aria-selected\:text-muted-foreground[aria-selected="true"] {
|
|
2395
2395
|
color: var(--muted-foreground);
|
|
2396
2396
|
}
|
|
2397
|
-
.data-closed\:animate-out[data-closed] {
|
|
2398
|
-
animation: exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
|
|
2399
|
-
}
|
|
2400
|
-
.data-closed\:fade-out-0[data-closed] {
|
|
2401
|
-
--tw-exit-opacity: calc(0/100);
|
|
2402
|
-
--tw-exit-opacity: 0;
|
|
2403
|
-
}
|
|
2404
|
-
.data-closed\:zoom-out-95[data-closed] {
|
|
2405
|
-
--tw-exit-scale: calc(95*1%);
|
|
2406
|
-
--tw-exit-scale: .95;
|
|
2407
|
-
}
|
|
2408
2397
|
.data-disabled\:pointer-events-none[data-disabled] {
|
|
2409
2398
|
pointer-events: none;
|
|
2410
2399
|
}
|
|
@@ -2417,17 +2406,6 @@
|
|
|
2417
2406
|
.data-inset\:pl-8[data-inset] {
|
|
2418
2407
|
padding-left: calc(var(--spacing) * 8);
|
|
2419
2408
|
}
|
|
2420
|
-
.data-open\:animate-in[data-open] {
|
|
2421
|
-
animation: enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
|
|
2422
|
-
}
|
|
2423
|
-
.data-open\:fade-in-0[data-open] {
|
|
2424
|
-
--tw-enter-opacity: calc(0/100);
|
|
2425
|
-
--tw-enter-opacity: 0;
|
|
2426
|
-
}
|
|
2427
|
-
.data-open\:zoom-in-95[data-open] {
|
|
2428
|
-
--tw-enter-scale: calc(95*1%);
|
|
2429
|
-
--tw-enter-scale: .95;
|
|
2430
|
-
}
|
|
2431
2409
|
.data-placeholder\:text-muted-foreground[data-placeholder] {
|
|
2432
2410
|
color: var(--muted-foreground);
|
|
2433
2411
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docblocks-react-ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "React rendering + editing layer for docblocks: hooks, block renderers, caret/selection, keyboard, clipboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"docblocks-core": "^0.2.
|
|
37
|
+
"docblocks-core": "^0.2.2",
|
|
38
38
|
"framer-motion": "^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0",
|
|
39
39
|
"mermaid": "^12.0.0",
|
|
40
40
|
"pdfjs-dist": "^6.0.0",
|