@panphora/clayjs 1.25.1 → 1.26.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/dist/all.js +1 -1
- package/dist/clay-dom.js +1 -1
- package/dist/clay-events.js +1 -1
- package/dist/clay-internals.js +1 -1
- package/dist/clay-options.js +1 -1
- package/dist/clay-router.js +1 -1
- package/dist/clay-ui.js +1 -1
- package/dist/clay-utils.js +1 -1
- package/dist/clay.js +1 -1
- package/dist/clay.standalone.js +1141 -345
- package/dist/m/c-2EPHRF47.js +1 -0
- package/dist/m/{c-2GB34XL4.js → c-2SUFMUZX.js} +2 -2
- package/dist/m/{c-BHLYQGTA.js → c-3S3647J6.js} +1 -1
- package/dist/m/{c-QL4NYM2L.js → c-3S5TNRZA.js} +1 -1
- package/dist/m/{c-3Y234AUB.js → c-3XGLJAS7.js} +2 -2
- package/dist/m/{c-O4YIDULY.js → c-42WBOKFM.js} +1 -1
- package/dist/m/{c-42RLKGEY.js → c-4TP3UXBT.js} +1 -1
- package/dist/m/{c-ASGHHLFX.js → c-5ELELG2F.js} +1 -1
- package/dist/m/{c-T5IWJOMW.js → c-5ZYLTN3Q.js} +1 -1
- package/dist/m/{c-DDCYVLGG.js → c-67TBU56I.js} +1 -1
- package/dist/m/{c-WNOLMHI2.js → c-6BGJAJ67.js} +1 -1
- package/dist/m/{c-XS7G6U3Z.js → c-ABOBD7F3.js} +1 -1
- package/dist/m/{c-5IIEOPIV.js → c-ARO6CPMA.js} +1 -1
- package/dist/m/{c-XUZMRQ7O.js → c-BCS3AZQZ.js} +1 -1
- package/dist/m/c-BOS7GONO.js +1 -0
- package/dist/m/{c-NICICT4P.js → c-DEYHCYJB.js} +1 -1
- package/dist/m/{c-T7LOTEWO.js → c-DZT6W2NG.js} +1 -1
- package/dist/m/c-EHJWIGWN.js +1 -0
- package/dist/m/{c-V2V5FKXX.js → c-EIUKME3W.js} +1 -1
- package/dist/m/{c-ASMLERFA.js → c-EXZOA6XY.js} +1 -1
- package/dist/m/{c-STAPQ2X6.js → c-EYFMAR3M.js} +1 -1
- package/dist/m/{c-DVMDYRAE.js → c-FUN6AKFE.js} +1 -1
- package/dist/m/{c-XMIDHEKF.js → c-GY36NO7C.js} +1 -1
- package/dist/m/{c-TWB5ZQU4.js → c-H47C6PJZ.js} +1 -1
- package/dist/m/c-KD2ME5UN.js +2 -0
- package/dist/m/{c-GO635CRU.js → c-LAKBBJOU.js} +2 -2
- package/dist/m/{c-GNFDX6SU.js → c-LETPPG2F.js} +1 -1
- package/dist/m/{c-LS7IVQVW.js → c-MMV5JULA.js} +1 -1
- package/dist/m/{c-NEQRFC4O.js → c-MVVEIJUU.js} +1 -1
- package/dist/m/{c-XCOQNISP.js → c-N362HNGO.js} +1 -1
- package/dist/m/{c-7K4SCQB7.js → c-ND3W7D5U.js} +1 -1
- package/dist/m/{c-4NAYDOWH.js → c-NNHNXD6M.js} +1 -1
- package/dist/m/{c-DAUQ6JZ3.js → c-NYJ577NL.js} +1 -1
- package/dist/m/{c-3AAQJAKJ.js → c-PWILUN4T.js} +1 -1
- package/dist/m/{c-7Z2625LX.js → c-QA3Q3MDI.js} +1 -1
- package/dist/m/{c-2A2ATCIH.js → c-QKOKMQXA.js} +1 -1
- package/dist/m/{c-TGIMBAWE.js → c-RBWVF4N5.js} +1 -1
- package/dist/m/{c-A5IYL7JI.js → c-SPOEDIMP.js} +1 -1
- package/dist/m/{c-ZJ7726FO.js → c-SRW5OBCF.js} +1 -1
- package/dist/m/{c-A3OKSYRI.js → c-SYOA4T7V.js} +1 -1
- package/dist/m/{c-MHTO3QAD.js → c-TJ5PFOJ2.js} +1 -1
- package/dist/m/{c-NW4ZFKCY.js → c-TKV273YU.js} +1 -1
- package/dist/m/{c-D4N4UJCQ.js → c-UEKJWXCB.js} +2 -2
- package/dist/m/{c-44WITKWQ.js → c-V4DBV5ZV.js} +1 -1
- package/dist/m/c-WHMZCRQD.js +6 -0
- package/dist/m/{c-WMRJKQ2V.js → c-WHQUZM2V.js} +1 -1
- package/dist/m/{c-C27EVUQ6.js → c-XHD7XJTE.js} +1 -1
- package/dist/m/{c-PP3CUF3U.js → c-XONFK3WC.js} +1 -1
- package/dist/m/c-YYIN4NAF.js +30 -0
- package/dist/m/c-ZFAPQQIB.js +1 -0
- package/dist/m/core/{conflict-notice-Y2NTMDGJ.js → conflict-notice-UNDOD4BL.js} +1 -1
- package/dist/m/core/site-edits-C22O53GS.js +1 -0
- package/dist/m/loader-DCKCZKY6.js +1 -0
- package/dist/m/{manifest-A27A4278.js → manifest-7954CBE3.js} +1 -1
- package/dist/m/{manifest-edit-E8A61EB4.js → manifest-edit-93AA79D7.js} +1 -1
- package/dist/m/manifest.json +442 -441
- package/dist/m/page/{editor-HS2CAV3W.js → editor-UF3CIA6V.js} +2 -2
- package/dist/m/plugins/{cms-4KET6RZT.js → cms-KG6TCTR3.js} +1 -1
- package/dist/m/plugins/{live-CO4VJS46.js → live-DF5YS5AH.js} +1 -1
- package/dist/m/plugins/page-GZSU77JS.js +2 -0
- package/dist/m/plugins/{page-view-6KNTXABH.js → page-view-7MM3FEAR.js} +1 -1
- package/dist/m/plugins/{quickcrop-VNRXGRPC.js → quickcrop-UHURKA2F.js} +2 -2
- package/dist/m/plugins/richclay-MXH2GFAZ.js +549 -0
- package/dist/m/plugins/undo-KLHAGV75.js +1 -0
- package/dist/m/sync/{live-sync-DX4K5GDB.js → live-sync-RZQSDMZW.js} +1 -1
- package/dist/offline-manifest.json +360 -360
- package/package.json +1 -1
- package/src/core/site-edits.js +1 -1
- package/src/loader-logic.js +0 -13
- package/src/page/boot-pack.js +1 -0
- package/src/page/history-intent.js +111 -4
- package/src/page/ybind/bridge.js +14 -7
- package/src/page/ybind/moves.js +437 -3
- package/src/page/ybind/origins.js +1 -1
- package/src/page/ybind/recovery.js +1 -1
- package/src/plugins/page.js +48 -4
- package/src/plugins/richclay.js +136 -0
- package/src/plugins/undo.js +169 -7
- package/src/recovery/undo.js +14 -7
- package/src/sync/live-sync.js +24 -0
- package/src/ui/vendor-skin.js +9 -4
- package/dist/m/c-3XNTQQEJ.js +0 -1
- package/dist/m/c-4OZZ7ENT.js +0 -30
- package/dist/m/c-D2OPWOWO.js +0 -2
- package/dist/m/c-LUBZU4AZ.js +0 -6
- package/dist/m/c-LYNLY3MR.js +0 -1
- package/dist/m/c-MLMJUNRV.js +0 -1
- package/dist/m/c-V5L6HWVL.js +0 -1
- package/dist/m/core/site-edits-TIJDPG6H.js +0 -1
- package/dist/m/loader-7ORQVRCK.js +0 -1
- package/dist/m/plugins/page-L3VKIQU2.js +0 -2
- package/dist/m/plugins/richclay-V6WKJTUF.js +0 -549
- package/dist/m/plugins/undo-WUK2NLBA.js +0 -1
- /package/dist/m/plugins/{quickcrop-VNRXGRPC.js.LEGAL.txt → quickcrop-UHURKA2F.js.LEGAL.txt} +0 -0
- /package/dist/m/plugins/{richclay-V6WKJTUF.js.LEGAL.txt → richclay-MXH2GFAZ.js.LEGAL.txt} +0 -0
package/package.json
CHANGED
package/src/core/site-edits.js
CHANGED
|
@@ -104,7 +104,7 @@ function takeCurrent(current, sent, refused) {
|
|
|
104
104
|
if (refused.has(key) || !rule || !Object.hasOwn(rule, key) || live[key] !== sent[key]) continue;
|
|
105
105
|
// Outside undo history and the edit observers, the way a sync frame lands: an undo
|
|
106
106
|
// must not bring back the value this tab was behind on, and then declare it.
|
|
107
|
-
const fields = new Map([...root.querySelectorAll("[editable]")].map((field) => [field, field.innerHTML]));
|
|
107
|
+
const fields = new Map([...root.querySelectorAll("[data-richclay], [richclay], [editable], [clay-editable]")].map((field) => [field, field.innerHTML]));
|
|
108
108
|
Mutation.pause();
|
|
109
109
|
try {
|
|
110
110
|
engine.applyData(root, { site: { [key]: value } }, { site: { [key]: rule[key] } });
|
package/src/loader-logic.js
CHANGED
|
@@ -195,10 +195,6 @@ const IMPLIES = { cms: ["quickcrop", "upload", "menu"], "ai-edit": ["wire"], blo
|
|
|
195
195
|
// A Document's editor owns its article's rich text, so richclay (for [editable]) has
|
|
196
196
|
// nothing to do there; plugins=blockeditor,richclay still loads it.
|
|
197
197
|
const DISPLACES = { blockeditor: ["richclay"] };
|
|
198
|
-
// A Document's editor keeps its own history, the same one Cmd+Z in the editor uses, and
|
|
199
|
-
// hyper-undo takes Cmd+Z for the whole window: loaded together the two would fight over
|
|
200
|
-
// every press. This is the one pair where a name the page asked for by hand still loses.
|
|
201
|
-
const CANNOT_RUN_WITH = { blockeditor: ["undo"] };
|
|
202
198
|
// No first paint or keystroke waits for these. source keeps the file's bytes outside
|
|
203
199
|
// the edited parts stable; a save before it installs is the ordinary full serialization.
|
|
204
200
|
// upgrade only asks the host for a newer version once `clay.ready` resolves.
|
|
@@ -296,15 +292,6 @@ export function resolveModules(params, isEditMode, { isDraftMode = false, hasSap
|
|
|
296
292
|
if (enabled.has(owner)) for (const name of names) if (!explicit.has(name)) enabled.delete(name);
|
|
297
293
|
}
|
|
298
294
|
|
|
299
|
-
for (const [owner, names] of Object.entries(CANNOT_RUN_WITH)) {
|
|
300
|
-
if (!enabled.has(owner)) continue;
|
|
301
|
-
for (const name of names) {
|
|
302
|
-
if (!enabled.has(name)) continue;
|
|
303
|
-
enabled.delete(name);
|
|
304
|
-
console.warn(`clayjs: plugin "${name}" cannot run with "${owner}" and was left out`);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
295
|
const plugins = [];
|
|
309
296
|
const late = [];
|
|
310
297
|
for (const name of PLUGIN_ORDER) {
|
package/src/page/boot-pack.js
CHANGED
|
@@ -20,6 +20,7 @@ export { createYPageBinding } from "./ybind/binding.js";
|
|
|
20
20
|
export { yValueOf } from "./ybind/provider.js";
|
|
21
21
|
export { seedPageFromArticle } from "./ybind/migrate.js";
|
|
22
22
|
export { holdSlashRange, heldSlashRange } from "./chrome.js";
|
|
23
|
+
export { createHistoryBridge } from "./history-intent.js";
|
|
23
24
|
export { createRecoverList, focusBlock } from "./recover.js";
|
|
24
25
|
export { observeDocumentTitle } from "./document-title-runtime.js";
|
|
25
26
|
export { createFeatureLoader } from "./feature-loader.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Typing joins one undo item the
|
|
2
|
-
//
|
|
1
|
+
// Typing joins one undo item per sentence, the same as every other field on the page:
|
|
2
|
+
// Mod-z removes the last sentence typed, not the last letter or word. Only the editor's own input path marks a
|
|
3
3
|
// transaction; a command or a programmatic dispatch stays one undo item by itself.
|
|
4
4
|
import { state } from "../vendor/prosemirror.vendor.js";
|
|
5
5
|
import { STEP } from "./blocks.js";
|
|
@@ -14,15 +14,28 @@ const blockIdAt = (doc, pos) => {
|
|
|
14
14
|
|
|
15
15
|
const markKey = (st) => (st.storedMarks ?? st.selection.$from.marks()).map((m) => `${m.type.name}:${JSON.stringify(m.attrs)}`).sort().join(",");
|
|
16
16
|
|
|
17
|
+
// A sentence ends at a space typed right after ., ? or ! (closing quotes and brackets
|
|
18
|
+
// allowed between). The space belongs to the sentence it ends, so the step closes after it.
|
|
19
|
+
// A second space, or a space after a space, is not a new sentence. The rule is hyper-undo's
|
|
20
|
+
// `endsSentence`, kept here so the block editor does not load the engine.
|
|
21
|
+
const SENTENCE_END = /[.?!]["'\u201D\u2019)\]]*$/;
|
|
22
|
+
|
|
23
|
+
const endsSentence = (st, from, text) => {
|
|
24
|
+
if (!/\s$/.test(text)) return false;
|
|
25
|
+
const $from = st.doc.resolve(from);
|
|
26
|
+
const head = $from.parent.textBetween(0, $from.parentOffset) + text.replace(/\s+$/, "");
|
|
27
|
+
return !/\s$/.test(head) && SENTENCE_END.test(head);
|
|
28
|
+
};
|
|
29
|
+
|
|
17
30
|
export function typingIntent(st, from, to, text) {
|
|
18
|
-
return { kind: "insert", blockId: blockIdAt(st.doc, from), from, end: from + text.length, replaced: from !== to, text, marks: markKey(st), at: Date.now() };
|
|
31
|
+
return { kind: "insert", blockId: blockIdAt(st.doc, from), from, end: from + text.length, replaced: from !== to, text, marks: markKey(st), breakAfter: endsSentence(st, from, text), at: Date.now() };
|
|
19
32
|
}
|
|
20
33
|
|
|
21
34
|
export function continuesGroup(prev, next) {
|
|
22
35
|
if (!prev || !next || prev.kind !== next.kind) return false;
|
|
23
36
|
if (next.kind === "compose") return prev.id === next.id;
|
|
24
37
|
if (prev.blockId !== next.blockId || next.at - prev.at > GROUP_MS) return false;
|
|
25
|
-
if (next.kind === "insert") return !next.replaced && next.from === prev.end && next.marks === prev.marks
|
|
38
|
+
if (next.kind === "insert") return !prev.breakAfter && !next.replaced && next.from === prev.end && next.marks === prev.marks;
|
|
26
39
|
return next.direction === prev.direction && (next.direction < 0 ? next.to === prev.from : next.from === prev.from);
|
|
27
40
|
}
|
|
28
41
|
|
|
@@ -76,3 +89,97 @@ export function historyIntentPlugin() {
|
|
|
76
89
|
},
|
|
77
90
|
});
|
|
78
91
|
}
|
|
92
|
+
|
|
93
|
+
// Each page entry owns one current Yjs StackItem, replaced by its inverse after replay.
|
|
94
|
+
export function createHistoryBridge({ um, undo, redo, article, focus }) {
|
|
95
|
+
const page = () => window.clay?.undo
|
|
96
|
+
const epoch = Math.random().toString(36).slice(2)
|
|
97
|
+
let next = 0
|
|
98
|
+
let running = false
|
|
99
|
+
let recording = false
|
|
100
|
+
let disposed = false
|
|
101
|
+
|
|
102
|
+
const replay = (entry, direction) => {
|
|
103
|
+
const fromKey = direction === 'undo' ? 'undoStack' : 'redoStack'
|
|
104
|
+
const toKey = direction === 'undo' ? 'redoStack' : 'undoStack'
|
|
105
|
+
const source = um[fromKey], destination = um[toKey]
|
|
106
|
+
const index = source.indexOf(entry.item)
|
|
107
|
+
if (index < 0) throw new Error('blockeditor: history item is no longer available')
|
|
108
|
+
const remaining = source.filter((_, at) => at !== index)
|
|
109
|
+
// Yjs scans BOTH stacks' deletion sets when checking map conflicts. Keep those
|
|
110
|
+
// sets visible on the opposite stack while limiting the pop loop to one item.
|
|
111
|
+
const temporary = [...destination, ...remaining]
|
|
112
|
+
const boundary = temporary.length
|
|
113
|
+
um[fromKey] = [entry.item]
|
|
114
|
+
um[toKey] = temporary
|
|
115
|
+
running = true
|
|
116
|
+
let changed
|
|
117
|
+
let inverse
|
|
118
|
+
try {
|
|
119
|
+
changed = direction === 'undo' ? undo() : redo()
|
|
120
|
+
} finally {
|
|
121
|
+
inverse = temporary.slice(boundary)
|
|
122
|
+
um[fromKey] = remaining
|
|
123
|
+
um[toKey] = [...destination, ...inverse]
|
|
124
|
+
running = false
|
|
125
|
+
um.stopCapturing()
|
|
126
|
+
}
|
|
127
|
+
if (!changed || !inverse.length) {
|
|
128
|
+
entry.item = null
|
|
129
|
+
throw new Error('blockeditor: history item was emptied by another edit')
|
|
130
|
+
}
|
|
131
|
+
entry.item = inverse[0]
|
|
132
|
+
entry.applied = direction === 'redo'
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const onAdded = (event) => {
|
|
136
|
+
if (running || event.origin === um || event.type !== 'undo' || page()?.pageHistory !== true) return
|
|
137
|
+
const entry = { item: event.stackItem, applied: true }
|
|
138
|
+
const id = `${epoch}:${++next}`
|
|
139
|
+
const oldValue = `${id}:undone`, newValue = `${id}:applied`
|
|
140
|
+
const primitive = {
|
|
141
|
+
kind: 'value',
|
|
142
|
+
target: article,
|
|
143
|
+
prop: `blockeditor-step:${id}`,
|
|
144
|
+
oldValue,
|
|
145
|
+
newValue,
|
|
146
|
+
read: () => !disposed && entry.item && (entry.applied ? um.undoStack : um.redoStack).includes(entry.item)
|
|
147
|
+
? entry.applied ? newValue : oldValue
|
|
148
|
+
: null,
|
|
149
|
+
write: (target, wanted) => {
|
|
150
|
+
const typing = !!document.activeElement?.closest?.('[contenteditable]:not([contenteditable="false"])')
|
|
151
|
+
const before = article.textContent
|
|
152
|
+
replay(entry, wanted === oldValue ? 'undo' : 'redo')
|
|
153
|
+
if (typing && article.textContent !== before && typeof focus === 'function') focus()
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
recording = true
|
|
157
|
+
try {
|
|
158
|
+
try { page().recordStep('Edit', primitive) } catch { page().record(primitive) }
|
|
159
|
+
} finally {
|
|
160
|
+
recording = false
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const onCommit = () => { if (!recording) um.stopCapturing() }
|
|
165
|
+
um.on('stack-item-added', onAdded)
|
|
166
|
+
let subscribed = null
|
|
167
|
+
const subscribe = () => {
|
|
168
|
+
const current = page()?.pageHistory === true ? page() : null
|
|
169
|
+
if (current === subscribed) return
|
|
170
|
+
subscribed?.off?.('commit', onCommit)
|
|
171
|
+
subscribed = current
|
|
172
|
+
subscribed?.on?.('commit', onCommit)
|
|
173
|
+
}
|
|
174
|
+
subscribe()
|
|
175
|
+
document.addEventListener('clay:undo-session', subscribe)
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
dispose() {
|
|
179
|
+
disposed = true
|
|
180
|
+
um.off('stack-item-added', onAdded)
|
|
181
|
+
subscribed?.off?.('commit', onCommit)
|
|
182
|
+
document.removeEventListener('clay:undo-session', subscribe)
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
}
|
package/src/page/ybind/bridge.js
CHANGED
|
@@ -10,7 +10,7 @@ import { state as pmState } from "../../vendor/prosemirror.vendor.js";
|
|
|
10
10
|
import { repairSiblings, isKey } from "../model.js";
|
|
11
11
|
import { createOriginIndex, installUndoOrigins } from "./origins.js";
|
|
12
12
|
import { reduceRecovery, reduceRecoverable, canonicalRevision, restoreKey, physicalKeyOf, authorKey, RECOVERED_PREFIX, parsePhysicalKey, parseGoneIntents } from "./recovery.js";
|
|
13
|
-
import { moveBlocks } from "./moves.js";
|
|
13
|
+
import { moveBlocks, createMoveCarry } from "./moves.js";
|
|
14
14
|
import { createBoardCommands } from "./board-commands.js";
|
|
15
15
|
import { continuesGroup, intentOf } from "../history-intent.js";
|
|
16
16
|
import { MOVE, BlockSelection } from "../blocks.js";
|
|
@@ -320,7 +320,9 @@ export function createBridge({ pd, onNotice = () => {}, ownedTokens = [], author
|
|
|
320
320
|
if (!d) { adopt(view.state.doc); return; }
|
|
321
321
|
const tr = view.state.tr.replace(d.start, d.endA, derived.slice(d.start, d.endB)).setMeta(REMOTE_META, true).setMeta("addToHistory", false);
|
|
322
322
|
if (own) tr.setMeta("yundo", true);
|
|
323
|
-
if (
|
|
323
|
+
if (own && view.state.doc.textContent !== derived.textContent && document.activeElement?.closest?.('[contenteditable]:not([contenteditable="false"])')) {
|
|
324
|
+
tr.setSelection(pmState.Selection.near(tr.doc.resolve(Math.min(d.endB, tr.doc.content.size)), -1));
|
|
325
|
+
} else if (keep) {
|
|
324
326
|
const hiddenUnit = (y) => y >= 0 && w.holes.some((hole) => y >= yBefore(w.holes, hole.at) && y < yAfter(w.holes, hole.at));
|
|
325
327
|
const placeIn = (id, offset) => {
|
|
326
328
|
let found = null;
|
|
@@ -359,12 +361,14 @@ export function createBridge({ pd, onNotice = () => {}, ownedTokens = [], author
|
|
|
359
361
|
return true;
|
|
360
362
|
};
|
|
361
363
|
const stale = (t) => { began.set(t, docVersion); docVersion++; };
|
|
364
|
+
const carry = createMoveCarry({ pd, origins, um, walked: () => currentWalk() ?? walk(pd, origins) });
|
|
362
365
|
const onTransaction = (t) => {
|
|
363
366
|
if (unchanged(t)) {
|
|
364
367
|
idle.add(t);
|
|
365
368
|
if (walkedAt === began.get(t) && docVersion === walkedAt + 1) walkedAt = docVersion;
|
|
366
369
|
return;
|
|
367
370
|
}
|
|
371
|
+
if (t.origin === um) carry.note(t);
|
|
368
372
|
deltas++;
|
|
369
373
|
delta = deltas === 1 ? captureDelta(pd, t, w) : null;
|
|
370
374
|
};
|
|
@@ -385,7 +389,9 @@ export function createBridge({ pd, onNotice = () => {}, ownedTokens = [], author
|
|
|
385
389
|
saved = { anchor: end(sel.$anchor), head: end(sel.$head) };
|
|
386
390
|
};
|
|
387
391
|
const onAfter = (t) => {
|
|
388
|
-
|
|
392
|
+
// A transaction that wrote nothing (Yjs's cleanup after an undo) is no peer's edit, so
|
|
393
|
+
// it leaves an open typing run open.
|
|
394
|
+
if (t.origin !== LOCAL && !unchanged(t)) { lastIntent = null; groupOpen = false; }
|
|
389
395
|
};
|
|
390
396
|
// One render per batch Yjs settles (a transaction and those its observers queued), after
|
|
391
397
|
// its garbage collection and merges, so the walk it leaves is current for the next keystroke.
|
|
@@ -398,9 +404,10 @@ export function createBridge({ pd, onNotice = () => {}, ownedTokens = [], author
|
|
|
398
404
|
else if (t.origin === um) own = true;
|
|
399
405
|
}
|
|
400
406
|
if (!changed) { saved = null; return; }
|
|
401
|
-
if (!view) { bumpRecovery(); return; }
|
|
407
|
+
if (!view) { bumpRecovery(); carry.received(transactions); return; }
|
|
402
408
|
if (local) { saved = null; return; }
|
|
403
409
|
render(own);
|
|
410
|
+
carry.received(transactions);
|
|
404
411
|
};
|
|
405
412
|
const portMove = (tr, move) => {
|
|
406
413
|
const result = moveBlocks({ pd, view, um, origins, render, walked: currentWalk() }, move);
|
|
@@ -486,7 +493,7 @@ export function createBridge({ pd, onNotice = () => {}, ownedTokens = [], author
|
|
|
486
493
|
registerAuthor = null;
|
|
487
494
|
const intent = history ? intentOf(local, view) : null;
|
|
488
495
|
const join = groupOpen && continuesGroup(lastIntent, intent);
|
|
489
|
-
lastIntent = intent;
|
|
496
|
+
lastIntent = !join && intent?.breakAfter && !intent.text.trim() ? { ...intent, breakAfter: false } : intent;
|
|
490
497
|
joining = join;
|
|
491
498
|
try {
|
|
492
499
|
localGroup(pd, local, w, stats, um, history, register, { join, open: intent !== null });
|
|
@@ -546,7 +553,7 @@ export function createBridge({ pd, onNotice = () => {}, ownedTokens = [], author
|
|
|
546
553
|
moveBlocks(args) {
|
|
547
554
|
return moveBlocks({ pd, view, um, origins, render, walked: currentWalk() }, args);
|
|
548
555
|
},
|
|
549
|
-
undo() { return
|
|
550
|
-
redo() { return
|
|
556
|
+
undo() { return carry.replay("undo"); },
|
|
557
|
+
redo() { return carry.replay("redo"); },
|
|
551
558
|
};
|
|
552
559
|
}
|