noteloom 0.3.3 → 0.5.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/README.md +173 -808
- package/bin/noteloom.mjs +139 -0
- package/dist/canvas.cjs +2 -0
- package/dist/canvas.cjs.map +1 -0
- package/dist/canvas.d.ts +2 -0
- package/dist/canvas.js +5 -0
- package/dist/canvas.js.map +1 -0
- package/dist/collab.cjs +2 -0
- package/dist/collab.cjs.map +1 -0
- package/dist/collab.d.ts +19 -0
- package/dist/collab.js +379 -0
- package/dist/collab.js.map +1 -0
- package/dist/comments.cjs +2 -0
- package/dist/comments.cjs.map +1 -0
- package/dist/comments.d.ts +17 -0
- package/dist/comments.js +19 -0
- package/dist/comments.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +374 -34
- package/dist/index.js +3983 -0
- package/dist/index.js.map +1 -0
- package/dist/persistence.cjs +2 -0
- package/dist/persistence.cjs.map +1 -0
- package/dist/persistence.d.ts +11 -0
- package/dist/persistence.js +12 -0
- package/dist/persistence.js.map +1 -0
- package/dist/shared/CommentAvatar-TKqsM8vG.js +53 -0
- package/dist/shared/CommentAvatar-TKqsM8vG.js.map +1 -0
- package/dist/shared/CommentAvatar-V457IgoQ.cjs +2 -0
- package/dist/shared/CommentAvatar-V457IgoQ.cjs.map +1 -0
- package/dist/shared/CommentThreadCard-CwKd6fp3.js +213 -0
- package/dist/shared/CommentThreadCard-CwKd6fp3.js.map +1 -0
- package/dist/shared/CommentThreadCard-YziIa8My.cjs +2 -0
- package/dist/shared/CommentThreadCard-YziIa8My.cjs.map +1 -0
- package/dist/shared/CommentsPanel-DezejDdE.cjs +2 -0
- package/dist/shared/CommentsPanel-DezejDdE.cjs.map +1 -0
- package/dist/shared/CommentsPanel-dx7IaoqS.js +76 -0
- package/dist/shared/CommentsPanel-dx7IaoqS.js.map +1 -0
- package/dist/shared/VersionHistory-DhYb8C2e.js +1120 -0
- package/dist/shared/VersionHistory-DhYb8C2e.js.map +1 -0
- package/dist/shared/VersionHistory-DuSxis_7.cjs +12 -0
- package/dist/shared/VersionHistory-DuSxis_7.cjs.map +1 -0
- package/dist/shared/VoiceListeningIndicator-BXByLZwO.cjs +2 -0
- package/dist/shared/VoiceListeningIndicator-BXByLZwO.cjs.map +1 -0
- package/dist/shared/VoiceListeningIndicator-bv285FSY.js +338 -0
- package/dist/shared/VoiceListeningIndicator-bv285FSY.js.map +1 -0
- package/dist/shared/fieldRegistry-BoYyW3GH.cjs +2 -0
- package/dist/shared/fieldRegistry-BoYyW3GH.cjs.map +1 -0
- package/dist/shared/fieldRegistry-PjJWXFoB.js +223 -0
- package/dist/shared/fieldRegistry-PjJWXFoB.js.map +1 -0
- package/dist/shared/icons-BCseh2pt.cjs +3531 -0
- package/dist/shared/icons-BCseh2pt.cjs.map +1 -0
- package/dist/shared/icons-D5Ctow8R.js +4279 -0
- package/dist/shared/icons-D5Ctow8R.js.map +1 -0
- package/dist/shared/index-CbIBsFmI.cjs +2 -0
- package/dist/shared/index-CbIBsFmI.cjs.map +1 -0
- package/dist/shared/index-D_8hJpI7.js +2145 -0
- package/dist/shared/index-D_8hJpI7.js.map +1 -0
- package/dist/shared/indexedDbPersistence-BKlGPFTP.js +112 -0
- package/dist/shared/indexedDbPersistence-BKlGPFTP.js.map +1 -0
- package/dist/shared/indexedDbPersistence-BWJ1h5Vp.cjs +2 -0
- package/dist/shared/indexedDbPersistence-BWJ1h5Vp.cjs.map +1 -0
- package/dist/shared/leafBlockFactory-CaLWUcfa.cjs +2 -0
- package/dist/shared/leafBlockFactory-CaLWUcfa.cjs.map +1 -0
- package/dist/shared/leafBlockFactory-DSa1XCox.js +335 -0
- package/dist/shared/leafBlockFactory-DSa1XCox.js.map +1 -0
- package/dist/shared/serialize-BqKU9EZy.js +86 -0
- package/dist/shared/serialize-BqKU9EZy.js.map +1 -0
- package/dist/shared/serialize-CqwX55uK.cjs +3 -0
- package/dist/shared/serialize-CqwX55uK.cjs.map +1 -0
- package/dist/shared/starter-kit-DyX1U84W.cjs +27 -0
- package/dist/shared/starter-kit-DyX1U84W.cjs.map +1 -0
- package/dist/shared/starter-kit-mfeNxqFn.js +4980 -0
- package/dist/shared/starter-kit-mfeNxqFn.js.map +1 -0
- package/dist/shared/usePopoverEdgeClamp-CPRWQtZV.cjs +2 -0
- package/dist/shared/usePopoverEdgeClamp-CPRWQtZV.cjs.map +1 -0
- package/dist/shared/usePopoverEdgeClamp-D_hB5t60.js +109 -0
- package/dist/shared/usePopoverEdgeClamp-D_hB5t60.js.map +1 -0
- package/dist/shared/useServiceWorkerUpdate-BduabXv6.js +94 -0
- package/dist/shared/useServiceWorkerUpdate-BduabXv6.js.map +1 -0
- package/dist/shared/useServiceWorkerUpdate-DQboGesi.cjs +2 -0
- package/dist/shared/useServiceWorkerUpdate-DQboGesi.cjs.map +1 -0
- package/dist/starter-kit.cjs +2 -0
- package/dist/starter-kit.cjs.map +1 -0
- package/dist/starter-kit.d.ts +9 -0
- package/dist/starter-kit.js +10 -0
- package/dist/starter-kit.js.map +1 -0
- package/dist/style.css +3529 -3283
- package/dist/versions.cjs +2 -0
- package/dist/versions.cjs.map +1 -0
- package/dist/versions.d.ts +11 -0
- package/dist/versions.js +13 -0
- package/dist/versions.js.map +1 -0
- package/dist/voice.cjs +2 -0
- package/dist/voice.cjs.map +1 -0
- package/dist/voice.d.ts +7 -0
- package/dist/voice.js +8 -0
- package/dist/voice.js.map +1 -0
- package/package.json +159 -92
- package/dist/noteloom.cjs +0 -3292
- package/dist/noteloom.cjs.map +0 -1
- package/dist/noteloom.es.js +0 -15047
- package/dist/noteloom.es.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VersionHistory-DuSxis_7.cjs","sources":["../../src/crdt/textDiff.js","../../src/store/EditorStore.js","../../src/templates/blockTemplates.js","../../src/clipboard/exportDocument.js","../../src/versions/autoVersionHistory.js","../../src/versions/diffVersions.js","../../src/react/useDocumentVersions.js","../../src/react/VersionHistory.jsx"],"sourcesContent":["/**\n * Finds the common-prefix/common-suffix boundary between two strings,\n * operating on Unicode code points (via `Array.from`) rather than raw\n * UTF-16 units, so a surrogate pair can never be split across two\n * character-CRDT slots. Returns the changed region as code-point indices:\n * `oldChars[start, oldEnd)` was removed, `newChars[start, newEnd)` was\n * inserted in its place — everything outside `[start, max(oldEnd,newEnd))`\n * is unchanged. Same algorithm as History's own `diffValueOffsets` (used\n * there purely for caret-position cosmetics on a UTF-16 string), reused\n * here for the actual data-merge boundary, which is why it needs to be\n * code-point aware rather than just close enough.\n */\nexport function diffCodePoints(oldValue, newValue) {\n const oldChars = Array.from(oldValue ?? '');\n const newChars = Array.from(newValue ?? '');\n let start = 0;\n const maxStart = Math.min(oldChars.length, newChars.length);\n while (start < maxStart && oldChars[start] === newChars[start]) start += 1;\n\n let oldEnd = oldChars.length;\n let newEnd = newChars.length;\n while (oldEnd > start && newEnd > start && oldChars[oldEnd - 1] === newChars[newEnd - 1]) {\n oldEnd -= 1;\n newEnd -= 1;\n }\n\n return { start, oldEnd, newEnd, oldChars, newChars };\n}\n","import {\n OP,\n insertBlock,\n removeBlock,\n moveBlock,\n changeBlockType,\n updateBlockProps,\n updateRun,\n editRunChars,\n setBlockContentIds,\n replaceRunSpan,\n setBlockRuns,\n addFieldType,\n updateFieldType,\n removeFieldType,\n addCommentThread,\n removeCommentThread,\n addCommentReply,\n removeCommentReply,\n resolveComment,\n addPerson,\n updatePerson,\n removePerson,\n} from './operations.js';\nimport { ListCrdtState } from '../crdt/listCrdt.js';\nimport { HLC, genPeerId } from '../crdt/clock.js';\nimport { FieldClockRegistry } from '../crdt/fieldRegistry.js';\nimport { diffCodePoints } from '../crdt/textDiff.js';\nimport { genId } from '../utils/idGen.js';\n\n/** Sentinel subscribe/notify key for \"the fieldTypes collection changed\" — see useFieldTypes. */\nconst FIELD_TYPES_KEY = '$fieldTypes';\n\n/** Sentinel subscribe/notify key for \"the comments collection changed\" — see useComments. */\nconst COMMENTS_KEY = '$comments';\n\n/** Sentinel subscribe/notify key for \"the document's people list changed\" — see usePeople. */\nconst PEOPLE_KEY = '$people';\n\n/**\n * Flat, normalized document store with per-id pub-sub.\n *\n * Every write replaces only the object(s) whose data actually changed, so\n * `getBlock(id)`/`getRun(id)` return a referentially stable value across any\n * update that doesn't touch that exact id. That stability is what lets\n * `useSyncExternalStore` (in the React bindings) skip re-rendering anything\n * that wasn't part of a given operation.\n *\n * Whether a given child id in `contentIds` refers to another Block or to a\n * Run is never encoded on the store — it's discovered by membership (ids are\n * unique across both maps), which keeps the store decoupled from the block\n * type registry.\n */\nexport class EditorStore {\n constructor(doc) {\n this.blocks = new Map((doc?.blocks ?? []).map((b) => [b.id, b]));\n this.runs = new Map((doc?.runs ?? []).map((r) => [r.id, r]));\n this.rootId = doc?.rootId ?? null;\n this.fieldTypes = new Map((doc?.fieldTypes ?? []).map((f) => [f.id, f]));\n this._fieldTypesSnapshot = null; // invalidated (set to null) on every fieldTypes mutation — see getFieldTypes\n this.comments = new Map((doc?.comments ?? []).map((c) => [c.id, c]));\n this._commentsSnapshot = null; // invalidated (set to null) on every comments mutation — see getComments\n this.people = new Map((doc?.people ?? []).map((p) => [p.id, p]));\n this._peopleSnapshot = null; // invalidated (set to null) on every people mutation — see getPeople\n this._listeners = new Map(); // id -> Set<() => void>\n this._globalListeners = new Set(); // fired on every mutation regardless of which id(s) changed -- see subscribeAll\n\n // Collaborative-editing merge state. Not yet reachable from any remote\n // transport (that's a later phase) -- for now this only tracks logical\n // clocks and CRDT-safe ordering locally, so `type`/props/run edits and\n // block insert/remove/move are all merge-ready without changing any\n // observable behavior for a single local user. Session-scoped: not\n // persisted by toJSON/fromJSON, since there's no remote peer yet that\n // could need it to survive a reload.\n this._peerId = genPeerId();\n this._clock = new HLC(this._peerId);\n this._fieldClocks = new FieldClockRegistry();\n this._orders = new Map(); // parent block id -> ListCrdtState over its contentIds, lazily seeded on first touch — see _getOrder\n\n // Character-level text merge state — same lazy-seed-from-existing-data\n // relationship to `runs` that `_orders` has to `blocks`. `_runOrders`\n // orders a run's characters (ListCrdtState is fully generic over\n // whatever ids it's given, so it's reused as-is here, just one\n // instance per RUN instead of per parent BLOCK); `_runChars` holds the\n // actual character payload for each slot (ListCrdtState's slots carry\n // no value of their own); `_runValueSnapshots` memoizes the\n // materialized string per run (see _materializeRunValue), the same\n // invalidate-on-mutation convention `getFieldTypes()` already uses for\n // `_fieldTypesSnapshot`.\n this._runOrders = new Map();\n this._runChars = new Map();\n this._runValueSnapshots = new Map();\n }\n\n /** Lazily seeds a block's ordering CRDT state from its current (plain-array) contentIds the first time it's touched. */\n _getOrder(parentId) {\n let order = this._orders.get(parentId);\n if (!order) {\n const block = this.blocks.get(parentId);\n order = ListCrdtState.fromArray(block?.contentIds ?? [], { peerId: 'legacy' });\n this._orders.set(parentId, order);\n }\n return order;\n }\n\n /**\n * Lazily seeds a run's character-ordering CRDT state from its current\n * (plain-string) value the first time it's touched. Unlike blocks,\n * individual characters have no pre-existing stable id of their own —\n * BUT a fresh *random* one (genId()) can't be minted here the way\n * `_getRunOrder`'s block equivalent doesn't need to, because a block's\n * seed ids (its real `.id`s) are already identical on every peer (the\n * document itself was already synced); a run's *characters* aren't\n * separately synced at all, only its plain `value` string is. Two peers\n * independently lazy-seeding the same pre-existing text with random ids\n * would each invent a DIFFERENT id for \"the same\" character, so a\n * remote edit's `afterId` (anchored to the sender's random id) would\n * never resolve on the receiving side. Fix: ids for this initial seed\n * are DETERMINISTIC — `` `${runId}:legacyChar:${index}` `` — so any peer\n * seeding from the same starting string (guaranteed, since a peer only\n * ever needs to seed a run it has already received the full document\n * for) always mints the identical ids independently, with no\n * coordination required. Only ever used for this one-time seed; every\n * character inserted after that (by typing, on any peer) gets a real\n * `genId()`.\n */\n _getRunOrder(runId) {\n let order = this._runOrders.get(runId);\n if (!order) {\n const run = this.runs.get(runId);\n const chars = new Map();\n order = new ListCrdtState();\n let prev = null;\n let index = 0;\n for (const ch of Array.from(run?.value ?? '')) {\n const charId = `${runId}:legacyChar:${index}`;\n chars.set(charId, ch);\n order.insert(charId, prev, { wallTime: 0, counter: 0, peerId: 'legacy' }, 'legacy');\n prev = charId;\n index += 1;\n }\n this._runOrders.set(runId, order);\n this._runChars.set(runId, chars);\n }\n return order;\n }\n\n /** Materializes a run's current text from its character CRDT, memoized until that run's char order next changes. */\n _materializeRunValue(runId) {\n let value = this._runValueSnapshots.get(runId);\n if (value === undefined) {\n const order = this._getRunOrder(runId);\n const chars = this._runChars.get(runId);\n value = order\n .toArray()\n .map((charId) => chars.get(charId))\n .join('');\n this._runValueSnapshots.set(runId, value);\n }\n return value;\n }\n\n /**\n * Diffs `newValue` against the run's current materialized value (common\n * prefix/suffix, code-point aware — see diffCodePoints) and translates\n * only the changed region into character-CRDT insert/delete ops. One\n * shared clock covers the whole edit — matching how a single\n * INSERT_BLOCK/REMOVE_BLOCK op already uses one tick() each — since\n * these characters are never independently reordered relative to each\n * other regardless of how many ticks they'd otherwise get.\n */\n _editRunText(runId, newValue) {\n const oldValue = this._materializeRunValue(runId);\n const { start, oldEnd, newEnd, newChars } = diffCodePoints(oldValue, newValue);\n const order = this._getRunOrder(runId);\n const chars = this._runChars.get(runId);\n const currentIds = order.toArray();\n const clock = this._clock.tick();\n\n const deletedCharIds = [];\n for (let i = start; i < oldEnd; i += 1) {\n const id = currentIds[i];\n order.delete(id, clock);\n deletedCharIds.push({ id, clock });\n }\n\n const insertedChars = [];\n let afterId = start > 0 ? currentIds[start - 1] : null;\n for (let i = start; i < newEnd; i += 1) {\n const id = genId();\n const ch = newChars[i];\n chars.set(id, ch);\n order.insert(id, afterId, clock, this._peerId);\n insertedChars.push({ id, char: ch, afterId, clock });\n afterId = id;\n }\n\n this._runValueSnapshots.delete(runId);\n return { oldValue, insertedChars, deletedCharIds, clock };\n }\n\n /**\n * Replays a previously-resolved text edit's EXACT character ids (from\n * `op._charEdit`, cached on first application — see the UPDATE_RUN case)\n * instead of re-diffing and minting fresh ones. Needed for `History`'s\n * redo, which replays the same op OBJECT a second time: re-diffing would\n * mint a brand-new set of ids rather than restoring the ones the\n * matching undo had only tombstoned, breaking a subsequent undo/redo\n * cycle (mirrors how INSERT_BLOCK's own handler restores an existing\n * slot via `order.restore()` on redo instead of creating a new one,\n * since a block's id is already stable across replay — characters have\n * no such built-in stable id, so this cache is what gives them one).\n */\n _replayRunTextEdit(runId, charEdit) {\n const order = this._getRunOrder(runId);\n const chars = this._runChars.get(runId);\n for (const c of charEdit.insertedChars) {\n if (order.has(c.id)) order.restore(c.id);\n else {\n chars.set(c.id, c.char);\n order.insert(c.id, c.afterId, c.clock, this._peerId);\n }\n }\n for (const d of charEdit.deletedCharIds) order.delete(d.id, d.clock);\n this._runValueSnapshots.delete(runId);\n }\n\n getBlock(id) {\n return this.blocks.get(id);\n }\n\n getRun(id) {\n return this.runs.get(id);\n }\n\n /**\n * Persisted, user-created custom select field types (static only) —\n * insertion order. Returns the SAME array reference across calls unless\n * a fieldTypes op ran in between — required for useSyncExternalStore\n * (see useFieldTypes), same reference-stability contract as\n * getBlock/getRun.\n */\n getFieldTypes() {\n if (!this._fieldTypesSnapshot) this._fieldTypesSnapshot = [...this.fieldTypes.values()];\n return this._fieldTypesSnapshot;\n }\n\n getFieldType(id) {\n return this.fieldTypes.get(id);\n }\n\n /**\n * Every comment thread — insertion order. Returns the SAME array\n * reference across calls unless a comments op ran in between — required\n * for useSyncExternalStore (see useComments), same contract as\n * getFieldTypes.\n */\n getComments() {\n if (!this._commentsSnapshot) this._commentsSnapshot = [...this.comments.values()];\n return this._commentsSnapshot;\n }\n\n getComment(id) {\n return this.comments.get(id);\n }\n\n /**\n * The document's own people list (see src/people/people.js's addPerson —\n * the documented entry point, and createSelectFieldType's \"Assignee\"\n * usage, which sources its options from this instead of any device-local\n * list, so every collaborator on the same document sees the same\n * assignable people). Same reference-stability contract as getFieldTypes/\n * getComments — required for useSyncExternalStore (see usePeople).\n */\n getPeople() {\n if (!this._peopleSnapshot) this._peopleSnapshot = [...this.people.values()];\n return this._peopleSnapshot;\n }\n\n getPerson(id) {\n return this.people.get(id);\n }\n\n /**\n * Every run id in the whole document, regardless of reachability from\n * the root — used by removeCommentMarkEverywhere (see\n * src/comments/commentMarks.js) to strip a deleted comment's id from\n * any run that still carries it, without needing a separate index.\n */\n getAllRunIds() {\n return [...this.runs.keys()];\n }\n\n getRootId() {\n return this.rootId;\n }\n\n subscribe(id, listener) {\n let set = this._listeners.get(id);\n if (!set) {\n set = new Set();\n this._listeners.set(id, set);\n }\n set.add(listener);\n return () => {\n set.delete(listener);\n if (set.size === 0) this._listeners.delete(id);\n };\n }\n\n /**\n * Fires on every mutation, local or remote (applyOperation and\n * applyRemoteOperation both funnel through _notify), regardless of\n * which specific id(s) changed — unlike subscribe(id, ...), which is\n * scoped to one id for render-isolation purposes. Meant for\n * whole-document concerns like auto-persistence, not for UI rendering\n * (a component re-rendering on every edit anywhere defeats the point of\n * per-id subscriptions).\n */\n subscribeAll(listener) {\n this._globalListeners.add(listener);\n return () => this._globalListeners.delete(listener);\n }\n\n _notify(touchedIds) {\n for (const id of touchedIds) {\n const set = this._listeners.get(id);\n if (!set) continue;\n // copy before iterating: a listener may unsubscribe during notification\n for (const listener of [...set]) listener();\n }\n if (this._globalListeners.size > 0) {\n for (const listener of [...this._globalListeners]) listener();\n }\n }\n\n /** Recursively collects a block (or run) and everything reachable under it. */\n _captureSubtree(rootId) {\n const blocks = [];\n const runs = [];\n const walk = (id) => {\n if (this.runs.has(id)) {\n runs.push(this.runs.get(id));\n return;\n }\n const block = this.blocks.get(id);\n if (!block) return;\n blocks.push(block);\n for (const childId of block.contentIds) walk(childId);\n // A block type (e.g. list_item) may hold its own inline runs in\n // props.titleRunIds alongside nested child blocks in contentIds.\n // Walk that too, generically, so nothing gets orphaned on delete.\n for (const runId of block.props?.titleRunIds ?? []) walk(runId);\n };\n walk(rootId);\n return { blocks, runs };\n }\n\n _deleteSubtree(rootId) {\n const walk = (id) => {\n if (this.runs.has(id)) {\n this.runs.delete(id);\n return;\n }\n const block = this.blocks.get(id);\n if (!block) return;\n for (const childId of block.contentIds) walk(childId);\n for (const runId of block.props?.titleRunIds ?? []) walk(runId);\n this.blocks.delete(id);\n };\n walk(rootId);\n }\n\n /**\n * Applies one operation, mutating only the affected map entries, and\n * returns the inverse operation (computed from state captured *before* the\n * mutation, per-op — never reconstructed afterward).\n */\n applyOperation(op) {\n switch (op.type) {\n case OP.UPDATE_RUN: {\n const run = this.runs.get(op.id);\n const patchKeys = Object.keys(op.patch);\n\n // The overwhelmingly common (and, per every real call site in this\n // codebase, the ONLY observed) shape: a lone `value` key, from\n // typing. Routed through this run's own character CRDT instead of\n // whole-value LWW, so concurrent edits to the same run interleave\n // instead of one clobbering the other, and undo can precisely\n // reverse only the characters THIS actor touched (via\n // EDIT_RUN_CHARS below) instead of replaying a stale whole-string\n // snapshot that can clobber a peer's concurrent edit. Any other\n // patch shape (marks, an atomic chip's `data`, or — never observed\n // in practice — `value` mixed with another key) keeps the\n // original whole-field LWW path below.\n if (patchKeys.length === 1 && patchKeys[0] === 'value') {\n let charEdit = op._charEdit;\n if (charEdit) {\n this._replayRunTextEdit(op.id, charEdit); // redo: reuse the exact ids from the first application\n } else {\n const { oldValue, insertedChars, deletedCharIds, clock } = this._editRunText(\n op.id,\n op.patch.value,\n );\n charEdit = { oldValue, insertedChars, deletedCharIds, clock };\n op._charEdit = charEdit; // cached on the op object itself so a future redo replays these same ids -- see _replayRunTextEdit\n }\n const newValue = this._materializeRunValue(op.id);\n this.runs.set(op.id, { ...run, value: newValue });\n this._lastEnvelope = {\n kind: 'runTextEdit',\n runId: op.id,\n insertedChars: charEdit.insertedChars,\n deletedCharIds: charEdit.deletedCharIds,\n clock: charEdit.clock,\n };\n this._notify([op.id]);\n return editRunChars(\n op.id,\n charEdit.insertedChars.map((c) => c.id), // undo: tombstone what was just inserted...\n charEdit.deletedCharIds.map((c) => c.id), // ...and restore what was just deleted\n charEdit.oldValue, // cosmetic-only, for History's caret restoration/change log -- see editRunChars's own doc comment\n );\n }\n\n const previousPatch = {};\n const clocks = {};\n for (const key of patchKeys) {\n previousPatch[key] = run[key];\n clocks[key] = this._clock.tick();\n this._fieldClocks.recordLocal(op.id, key, clocks[key]);\n }\n this.runs.set(op.id, { ...run, ...op.patch });\n this._lastEnvelope = {\n kind: 'fieldWrite',\n target: 'run',\n id: op.id,\n patch: op.patch,\n clocks,\n };\n this._notify([op.id]);\n return updateRun(op.id, previousPatch);\n }\n\n case OP.EDIT_RUN_CHARS: {\n const order = this._getRunOrder(op.id);\n const clock = this._clock.tick();\n for (const charId of op.tombstoneIds) order.delete(charId, clock);\n for (const charId of op.restoreIds) order.restore(charId);\n this._runValueSnapshots.delete(op.id);\n const run = this.runs.get(op.id);\n const newValue = this._materializeRunValue(op.id);\n this.runs.set(op.id, { ...run, value: newValue });\n this._lastEnvelope = {\n kind: 'runCharEdit',\n runId: op.id,\n tombstoneIds: op.tombstoneIds,\n restoreIds: op.restoreIds,\n clock,\n };\n this._notify([op.id]);\n return editRunChars(op.id, op.restoreIds, op.tombstoneIds); // swapped -- this op is its own inverse shape\n }\n\n case OP.UPDATE_BLOCK_PROPS: {\n const block = this.blocks.get(op.id);\n const previousPatch = {};\n const clocks = {};\n for (const key of Object.keys(op.patch)) {\n previousPatch[key] = block.props[key];\n clocks[key] = this._clock.tick();\n this._fieldClocks.recordLocal(op.id, `props.${key}`, clocks[key]);\n }\n this.blocks.set(op.id, { ...block, props: { ...block.props, ...op.patch } });\n this._lastEnvelope = {\n kind: 'fieldWrite',\n target: 'blockProps',\n id: op.id,\n patch: op.patch,\n clocks,\n };\n this._notify([op.id]);\n return updateBlockProps(op.id, previousPatch);\n }\n\n case OP.CHANGE_BLOCK_TYPE: {\n const block = this.blocks.get(op.id);\n const previousType = block.type;\n const previousProps = block.props;\n const clock = this._clock.tick();\n this._fieldClocks.recordLocal(op.id, 'type', clock);\n this.blocks.set(op.id, { ...block, type: op.blockType, props: op.props });\n this._lastEnvelope = {\n kind: 'fieldWrite',\n target: 'blockType',\n id: op.id,\n blockType: op.blockType,\n props: op.props,\n clock,\n };\n this._notify([op.id]);\n return changeBlockType(op.id, previousType, previousProps);\n }\n\n case OP.INSERT_BLOCK: {\n if (op.subtree) {\n for (const b of op.subtree.blocks) this.blocks.set(b.id, b);\n for (const r of op.subtree.runs) this.runs.set(r.id, r);\n } else {\n this.blocks.set(op.block.id, op.block);\n }\n const parent = this.blocks.get(op.parentId);\n const order = this._getOrder(op.parentId);\n if (order.has(op.block.id)) {\n // Already has a slot here (undo of a REMOVE_BLOCK, or duplicate\n // delivery) -- restore its original position instead of trying\n // to create a second slot for the same id.\n order.restore(op.block.id);\n } else {\n const currentArray = order.toArray();\n const index = op.index ?? currentArray.length;\n const afterId = index > 0 ? currentArray[index - 1] : null;\n order.insert(op.block.id, afterId, this._clock.tick(), this._peerId);\n }\n this.blocks.set(op.parentId, { ...parent, contentIds: order.toArray() });\n this._lastEnvelope = {\n kind: 'insertSlot',\n parentId: op.parentId,\n blockId: op.block.id,\n slot: order.getSlot(op.block.id),\n subtree: op.subtree ?? { blocks: [op.block], runs: [] },\n };\n this._notify([op.block.id, op.parentId]);\n return removeBlock(op.block.id);\n }\n\n case OP.REMOVE_BLOCK: {\n const target = this.blocks.get(op.id);\n const parentId = target.parentId;\n const parent = this.blocks.get(parentId);\n const order = this._getOrder(parentId);\n const index = order.toArray().indexOf(op.id);\n const subtree = this._captureSubtree(op.id);\n this._deleteSubtree(op.id);\n const deleteClock = this._clock.tick();\n order.delete(op.id, deleteClock);\n this.blocks.set(parentId, { ...parent, contentIds: order.toArray() });\n this._lastEnvelope = { kind: 'deleteSlot', parentId, blockId: op.id, clock: deleteClock };\n this._notify([parentId]);\n return { type: OP.INSERT_BLOCK, block: target, parentId, index, subtree };\n }\n\n case OP.MOVE_BLOCK: {\n const target = this.blocks.get(op.id);\n const fromParentId = target.parentId;\n const fromOrder = this._getOrder(fromParentId);\n const fromIndex = fromOrder.toArray().indexOf(op.id);\n\n // Tombstone the slot at its old position first -- for a\n // same-parent reorder this is the SAME ListCrdtState as toOrder\n // below, and the array it produces afterward is what toIndex is\n // interpreted against (matches the original index-based\n // contract, where toIndex already meant \"position in the\n // resulting list\").\n fromOrder.delete(op.id, this._clock.tick());\n this.blocks.set(fromParentId, {\n ...this.blocks.get(fromParentId),\n contentIds: fromOrder.toArray(),\n });\n\n const toOrder = this._getOrder(op.toParentId);\n const toArray = toOrder.toArray();\n const toIndex = op.toIndex ?? toArray.length;\n const afterId = toIndex > 0 ? toArray[toIndex - 1] : null;\n const ts = this._clock.tick();\n if (toOrder.has(op.id)) {\n // Reposition the existing slot (same-parent reorder, or moving\n // back to a parent this id was in — and tombstoned — before).\n toOrder.move(op.id, afterId, ts, this._peerId);\n } else {\n toOrder.insert(op.id, afterId, ts, this._peerId);\n }\n this.blocks.set(op.toParentId, {\n ...this.blocks.get(op.toParentId),\n contentIds: toOrder.toArray(),\n });\n\n this.blocks.set(op.id, { ...this.blocks.get(op.id), parentId: op.toParentId });\n\n this._lastEnvelope = {\n kind: 'moveSlot',\n fromParentId,\n toParentId: op.toParentId,\n blockId: op.id,\n slot: toOrder.getSlot(op.id),\n };\n const touched = new Set([op.id, fromParentId, op.toParentId]);\n this._notify(touched);\n return moveBlock(op.id, fromParentId, fromIndex);\n }\n\n case OP.SET_BLOCK_CONTENT_IDS: {\n const block = this.blocks.get(op.blockId);\n const previousContentIds = block.contentIds;\n this.blocks.set(op.blockId, { ...block, contentIds: op.contentIds });\n // This op wholesale-replaces the array (a resync escape hatch, not\n // an incremental edit) -- reseed the ordering CRDT to match rather\n // than leave it stale relative to the new authoritative contents.\n this._orders.set(op.blockId, ListCrdtState.fromArray(op.contentIds, { peerId: 'legacy' }));\n this._lastEnvelope = null; // no CRDT-safe wire representation for a wholesale resync -- not broadcastable in Phase C\n this._notify([op.blockId]);\n return setBlockContentIds(op.blockId, previousContentIds);\n }\n\n case OP.REPLACE_RUN_SPAN: {\n const block = this.blocks.get(op.blockId);\n // The run span lives in contentIds for a plain leaf block (paragraph,\n // heading, tableCell), or in props.titleRunIds for a listItem (whose\n // contentIds instead holds nested child list items).\n const inContentIds = block.contentIds.indexOf(op.oldRunIds[0]) !== -1;\n const sourceArray = inContentIds ? block.contentIds : (block.props?.titleRunIds ?? []);\n const startIndex = sourceArray.indexOf(op.oldRunIds[0]);\n\n const removedRuns = op.oldRunIds.map((id) => this.runs.get(id));\n for (const id of op.oldRunIds) this.runs.delete(id);\n for (const r of op.newRuns) this.runs.set(r.id, r);\n\n const newArray = [...sourceArray];\n newArray.splice(startIndex, op.oldRunIds.length, ...op.newRuns.map((r) => r.id));\n\n if (inContentIds) {\n this.blocks.set(op.blockId, { ...block, contentIds: newArray });\n } else {\n this.blocks.set(op.blockId, {\n ...block,\n props: { ...block.props, titleRunIds: newArray },\n });\n }\n this._lastEnvelope = null; // no CRDT-safe wire representation yet -- not broadcastable in Phase C\n this._notify([op.blockId, ...op.oldRunIds, ...op.newRuns.map((r) => r.id)]);\n return replaceRunSpan(\n op.blockId,\n op.newRuns.map((r) => r.id),\n removedRuns,\n );\n }\n\n case OP.SET_BLOCK_RUNS: {\n const block = this.blocks.get(op.blockId);\n // Same dual-path convention as REPLACE_RUN_SPAN: a listItem's own\n // runs live in props.titleRunIds (contentIds holds nested child\n // items instead); every other run-bearing leaf uses contentIds.\n const usesTitleRunIds = block.props && 'titleRunIds' in block.props;\n const previousRunIds = usesTitleRunIds ? block.props.titleRunIds : block.contentIds;\n const previousRuns = previousRunIds.map((id) => this.runs.get(id)).filter(Boolean);\n\n for (const id of previousRunIds) this.runs.delete(id);\n for (const r of op.runs) this.runs.set(r.id, r);\n const newRunIds = op.runs.map((r) => r.id);\n\n if (usesTitleRunIds) {\n this.blocks.set(op.blockId, {\n ...block,\n props: { ...block.props, titleRunIds: newRunIds },\n });\n } else {\n this.blocks.set(op.blockId, { ...block, contentIds: newRunIds });\n }\n this._lastEnvelope = null; // no CRDT-safe wire representation yet -- not broadcastable in Phase C\n this._notify([op.blockId, ...previousRunIds, ...newRunIds]);\n return setBlockRuns(op.blockId, previousRuns);\n }\n\n case OP.ADD_FIELD_TYPE: {\n this.fieldTypes.set(op.fieldType.id, op.fieldType);\n this._fieldTypesSnapshot = null;\n this._notify([FIELD_TYPES_KEY]);\n return removeFieldType(op.fieldType.id);\n }\n\n case OP.UPDATE_FIELD_TYPE: {\n const existing = this.fieldTypes.get(op.id);\n const previousPatch = {};\n for (const key of Object.keys(op.patch)) previousPatch[key] = existing[key];\n this.fieldTypes.set(op.id, { ...existing, ...op.patch });\n this._fieldTypesSnapshot = null;\n this._notify([FIELD_TYPES_KEY]);\n return updateFieldType(op.id, previousPatch);\n }\n\n case OP.REMOVE_FIELD_TYPE: {\n const existing = this.fieldTypes.get(op.id);\n this.fieldTypes.delete(op.id);\n this._fieldTypesSnapshot = null;\n this._notify([FIELD_TYPES_KEY]);\n return addFieldType(existing);\n }\n\n case OP.ADD_COMMENT_THREAD: {\n this.comments.set(op.thread.id, op.thread);\n this._commentsSnapshot = null;\n this._lastEnvelope = { kind: 'commentWrite', op: 'addThread', thread: op.thread };\n this._notify([COMMENTS_KEY]);\n return removeCommentThread(op.thread.id);\n }\n\n case OP.REMOVE_COMMENT_THREAD: {\n const existing = this.comments.get(op.commentId);\n this.comments.delete(op.commentId);\n this._commentsSnapshot = null;\n this._lastEnvelope = { kind: 'commentWrite', op: 'removeThread', commentId: op.commentId };\n this._notify([COMMENTS_KEY]);\n return addCommentThread(existing);\n }\n\n case OP.ADD_COMMENT_REPLY: {\n const thread = this.comments.get(op.commentId);\n this.comments.set(op.commentId, { ...thread, messages: [...thread.messages, op.message] });\n this._commentsSnapshot = null;\n this._lastEnvelope = {\n kind: 'commentWrite',\n op: 'addReply',\n commentId: op.commentId,\n message: op.message,\n };\n this._notify([COMMENTS_KEY]);\n return removeCommentReply(op.commentId, op.message.id);\n }\n\n case OP.REMOVE_COMMENT_REPLY: {\n const thread = this.comments.get(op.commentId);\n const removedMessage = thread.messages.find((m) => m.id === op.messageId);\n this.comments.set(op.commentId, {\n ...thread,\n messages: thread.messages.filter((m) => m.id !== op.messageId),\n });\n this._commentsSnapshot = null;\n this._lastEnvelope = {\n kind: 'commentWrite',\n op: 'removeReply',\n commentId: op.commentId,\n messageId: op.messageId,\n };\n this._notify([COMMENTS_KEY]);\n return addCommentReply(op.commentId, removedMessage);\n }\n\n case OP.RESOLVE_COMMENT: {\n const thread = this.comments.get(op.commentId);\n const previousResolved = thread.resolved;\n this.comments.set(op.commentId, { ...thread, resolved: op.resolved });\n this._commentsSnapshot = null;\n this._lastEnvelope = {\n kind: 'commentWrite',\n op: 'resolve',\n commentId: op.commentId,\n resolved: op.resolved,\n };\n this._notify([COMMENTS_KEY]);\n return resolveComment(op.commentId, previousResolved);\n }\n\n case OP.ADD_PERSON: {\n this.people.set(op.person.id, op.person);\n this._peopleSnapshot = null;\n this._lastEnvelope = { kind: 'peopleWrite', op: 'add', person: op.person };\n this._notify([PEOPLE_KEY]);\n return removePerson(op.person.id);\n }\n\n case OP.UPDATE_PERSON: {\n const existing = this.people.get(op.id);\n const previousPatch = {};\n for (const key of Object.keys(op.patch)) previousPatch[key] = existing[key];\n this.people.set(op.id, { ...existing, ...op.patch });\n this._peopleSnapshot = null;\n this._lastEnvelope = { kind: 'peopleWrite', op: 'update', id: op.id, patch: op.patch };\n this._notify([PEOPLE_KEY]);\n return updatePerson(op.id, previousPatch);\n }\n\n case OP.REMOVE_PERSON: {\n const existing = this.people.get(op.id);\n this.people.delete(op.id);\n this._peopleSnapshot = null;\n this._lastEnvelope = { kind: 'peopleWrite', op: 'remove', id: op.id };\n this._notify([PEOPLE_KEY]);\n return addPerson(existing);\n }\n\n default:\n throw new Error(`Unknown operation type: ${op.type}`);\n }\n }\n\n /** Applies a batch as one transaction: all mutations first, one notify pass, one combined inverse list. */\n applyOperations(ops) {\n return ops.map((op) => this.applyOperation(op));\n }\n\n /**\n * The CRDT-native wire envelope for whatever `applyOperation` just did —\n * read this immediately after each call to broadcast the change to\n * other peers. `null` for op types with no CRDT-safe representation yet\n * (the coarse resync ops: setBlockContentIds/replaceRunSpan/\n * setBlockRuns/fieldType ops) — those remain local-only in this phase.\n */\n getLastEnvelope() {\n return this._lastEnvelope;\n }\n\n /**\n * Applies a change received from another peer. Deliberately separate\n * from `applyOperation`/`History.perform` — a remote change must never\n * enter the local undo stack (only `perform`/`performBatch` push onto\n * it), so this method is the only way for a remote envelope to reach\n * the store. Idempotent and order-independent per envelope kind: safe\n * to call more than once, or with envelopes arriving out of order.\n */\n applyRemoteOperation(envelope) {\n if (!envelope) return;\n switch (envelope.kind) {\n case 'insertSlot': {\n for (const b of envelope.subtree.blocks)\n if (!this.blocks.has(b.id)) this.blocks.set(b.id, b);\n for (const r of envelope.subtree.runs) if (!this.runs.has(r.id)) this.runs.set(r.id, r);\n const order = this._getOrder(envelope.parentId);\n order.merge([envelope.slot]);\n const parent = this.blocks.get(envelope.parentId);\n this.blocks.set(envelope.parentId, { ...parent, contentIds: order.toArray() });\n this._clock.receive(envelope.slot.clock);\n this._notify([envelope.blockId, envelope.parentId]);\n return;\n }\n\n case 'deleteSlot': {\n const order = this._getOrder(envelope.parentId);\n if (this.blocks.has(envelope.blockId)) this._deleteSubtree(envelope.blockId);\n order.delete(envelope.blockId, envelope.clock);\n if (envelope.clock) this._clock.receive(envelope.clock);\n const parent = this.blocks.get(envelope.parentId);\n this.blocks.set(envelope.parentId, { ...parent, contentIds: order.toArray() });\n this._notify([envelope.parentId]);\n return;\n }\n\n case 'moveSlot': {\n const fromOrder = this._getOrder(envelope.fromParentId);\n // The move's own clock (envelope.slot.clock) is a reasonable proxy\n // for \"when the old position was vacated\" -- there's no separate\n // timestamp for that specific fact, and it's close enough in time\n // to be a safe (if not perfectly precise) tombstone age.\n fromOrder.delete(envelope.blockId, envelope.slot?.clock);\n this.blocks.set(envelope.fromParentId, {\n ...this.blocks.get(envelope.fromParentId),\n contentIds: fromOrder.toArray(),\n });\n\n const toOrder = this._getOrder(envelope.toParentId);\n const hadSlot = toOrder.has(envelope.blockId);\n toOrder.merge([envelope.slot]);\n // merge() only creates a slot or un-deletes one — it never\n // repositions an existing slot (that's not what merging two\n // independently-evolved lists means). A remote MOVE needs the\n // actual reposition, applied only when the slot already existed\n // here before this merge.\n if (hadSlot)\n toOrder.move(\n envelope.blockId,\n envelope.slot.originId,\n envelope.slot.clock,\n envelope.slot.peerId,\n );\n this.blocks.set(envelope.toParentId, {\n ...this.blocks.get(envelope.toParentId),\n contentIds: toOrder.toArray(),\n });\n\n if (this.blocks.has(envelope.blockId)) {\n this.blocks.set(envelope.blockId, {\n ...this.blocks.get(envelope.blockId),\n parentId: envelope.toParentId,\n });\n }\n this._clock.receive(envelope.slot.clock);\n this._notify([envelope.blockId, envelope.fromParentId, envelope.toParentId]);\n return;\n }\n\n case 'fieldWrite': {\n if (envelope.target === 'run') {\n const run = this.runs.get(envelope.id);\n if (run) {\n const patch = {};\n for (const key of Object.keys(envelope.patch)) {\n const remoteClock = envelope.clocks[key];\n if (!this._fieldClocks.shouldApplyRemote(envelope.id, key, remoteClock)) continue;\n patch[key] = envelope.patch[key];\n this._fieldClocks.recordRemote(envelope.id, key, remoteClock);\n }\n if (Object.keys(patch).length > 0) {\n this.runs.set(envelope.id, { ...run, ...patch });\n this._notify([envelope.id]);\n }\n }\n } else if (envelope.target === 'blockProps') {\n const block = this.blocks.get(envelope.id);\n if (block) {\n const patch = {};\n for (const key of Object.keys(envelope.patch)) {\n const remoteClock = envelope.clocks[key];\n if (!this._fieldClocks.shouldApplyRemote(envelope.id, `props.${key}`, remoteClock))\n continue;\n patch[key] = envelope.patch[key];\n this._fieldClocks.recordRemote(envelope.id, `props.${key}`, remoteClock);\n }\n if (Object.keys(patch).length > 0) {\n this.blocks.set(envelope.id, { ...block, props: { ...block.props, ...patch } });\n this._notify([envelope.id]);\n }\n }\n } else if (envelope.target === 'blockType') {\n const block = this.blocks.get(envelope.id);\n if (block && this._fieldClocks.shouldApplyRemote(envelope.id, 'type', envelope.clock)) {\n this._fieldClocks.recordRemote(envelope.id, 'type', envelope.clock);\n this.blocks.set(envelope.id, {\n ...block,\n type: envelope.blockType,\n props: envelope.props,\n });\n this._notify([envelope.id]);\n }\n }\n for (const ts of Object.values(envelope.clocks ?? {})) this._clock.receive(ts);\n if (envelope.clock) this._clock.receive(envelope.clock);\n return;\n }\n\n case 'runTextEdit': {\n const order = this._getRunOrder(envelope.runId);\n const chars = this._runChars.get(envelope.runId);\n for (const c of envelope.insertedChars) {\n if (!order.has(c.id)) chars.set(c.id, c.char);\n order.insert(c.id, c.afterId, c.clock, c.clock?.peerId); // idempotent -- safe on duplicate delivery, same as block inserts\n }\n for (const d of envelope.deletedCharIds) order.delete(d.id, d.clock);\n this._runValueSnapshots.delete(envelope.runId);\n const run = this.runs.get(envelope.runId);\n if (run) {\n this.runs.set(envelope.runId, {\n ...run,\n value: this._materializeRunValue(envelope.runId),\n });\n this._notify([envelope.runId]);\n }\n this._clock.receive(envelope.clock);\n return;\n }\n\n case 'runCharEdit': {\n const order = this._getRunOrder(envelope.runId);\n for (const charId of envelope.tombstoneIds) order.delete(charId, envelope.clock);\n for (const charId of envelope.restoreIds) order.restore(charId);\n this._runValueSnapshots.delete(envelope.runId);\n const run = this.runs.get(envelope.runId);\n if (run) {\n this.runs.set(envelope.runId, {\n ...run,\n value: this._materializeRunValue(envelope.runId),\n });\n this._notify([envelope.runId]);\n }\n this._clock.receive(envelope.clock);\n return;\n }\n\n case 'commentWrite': {\n // Simple, no-per-field-clock handling -- comment thread metadata\n // sees low write concurrency in practice (see the plan's own\n // rationale), so each sub-kind just applies directly rather than\n // comparing HLC clocks the way run/block field writes do above.\n // Existence checks guard only against out-of-order delivery (e.g.\n // a reply arriving before its thread's own add), not against real\n // conflicts -- a later full resync reconciles anything this misses.\n if (envelope.op === 'addThread') {\n if (!this.comments.has(envelope.thread.id)) {\n this.comments.set(envelope.thread.id, envelope.thread);\n this._commentsSnapshot = null;\n this._notify([COMMENTS_KEY]);\n }\n } else if (envelope.op === 'removeThread') {\n if (this.comments.delete(envelope.commentId)) {\n this._commentsSnapshot = null;\n this._notify([COMMENTS_KEY]);\n }\n } else if (envelope.op === 'addReply') {\n const thread = this.comments.get(envelope.commentId);\n if (thread && !thread.messages.some((m) => m.id === envelope.message.id)) {\n this.comments.set(envelope.commentId, {\n ...thread,\n messages: [...thread.messages, envelope.message],\n });\n this._commentsSnapshot = null;\n this._notify([COMMENTS_KEY]);\n }\n } else if (envelope.op === 'removeReply') {\n const thread = this.comments.get(envelope.commentId);\n if (thread && thread.messages.some((m) => m.id === envelope.messageId)) {\n this.comments.set(envelope.commentId, {\n ...thread,\n messages: thread.messages.filter((m) => m.id !== envelope.messageId),\n });\n this._commentsSnapshot = null;\n this._notify([COMMENTS_KEY]);\n }\n } else if (envelope.op === 'resolve') {\n const thread = this.comments.get(envelope.commentId);\n if (thread) {\n this.comments.set(envelope.commentId, { ...thread, resolved: envelope.resolved });\n this._commentsSnapshot = null;\n this._notify([COMMENTS_KEY]);\n }\n }\n return;\n }\n\n case 'peopleWrite': {\n // Same simple existence-check merge as commentWrite above, for the\n // same reason: a document's people list sees low write concurrency\n // in practice (adding/renaming/removing a collaborator's name is\n // not something two people do to the same entry at the same\n // instant), so there's no need for the per-field HLC comparison\n // run/block field writes use.\n if (envelope.op === 'add') {\n if (!this.people.has(envelope.person.id)) {\n this.people.set(envelope.person.id, envelope.person);\n this._peopleSnapshot = null;\n this._notify([PEOPLE_KEY]);\n }\n } else if (envelope.op === 'update') {\n const existing = this.people.get(envelope.id);\n if (existing) {\n this.people.set(envelope.id, { ...existing, ...envelope.patch });\n this._peopleSnapshot = null;\n this._notify([PEOPLE_KEY]);\n }\n } else if (envelope.op === 'remove') {\n if (this.people.delete(envelope.id)) {\n this._peopleSnapshot = null;\n this._notify([PEOPLE_KEY]);\n }\n }\n return;\n }\n\n default:\n throw new Error(`Unknown remote envelope kind: ${envelope.kind}`);\n }\n }\n\n /** Total tombstoned (deleted but still retained) slots across every parent's ordering CRDT and every run's character CRDT — a cheap signal for whether pruneTombstones() is worth calling, or just for observing growth over a long session. */\n getTombstoneCount() {\n let count = 0;\n for (const order of this._orders.values()) count += order.tombstoneCount();\n for (const order of this._runOrders.values()) count += order.tombstoneCount();\n return count;\n }\n\n /**\n * Permanently removes tombstones older than `maxAgeMs` (default 24h)\n * across every parent's ordering CRDT — see ListCrdtState.pruneTombstones\n * for the safety argument this relies on (this package's reconnect model\n * always resyncs via a full snapshot, never a replayed op log, which is\n * what makes a time-based threshold safe here specifically). Opt-in:\n * never called automatically by anything in this store — call it\n * yourself on whatever schedule fits (see createPeriodicTombstoneGC for\n * a ready-made \"just handle it\" helper). Returns the number of slots\n * removed.\n */\n pruneTombstones({ maxAgeMs = 24 * 60 * 60 * 1000, now = Date.now() } = {}) {\n const threshold = { wallTime: now - maxAgeMs, counter: 0, peerId: '' };\n let removed = 0;\n for (const order of this._orders.values()) removed += order.pruneTombstones(threshold);\n // Character payloads for pruned run-char slots (_runChars) are\n // deliberately left in place rather than cleaned up here:\n // ListCrdtState.pruneTombstones only returns a removed *count*, not\n // which ids were removed (a shared contract with the block-level GC\n // above, which has no equivalent payload map to clean), and a few\n // orphaned single-character map entries per prune is a far smaller\n // concern than the whole-subtree leaks tombstone GC exists to prevent\n // in the first place.\n for (const order of this._runOrders.values()) removed += order.pruneTombstones(threshold);\n return removed;\n }\n\n toJSON() {\n return {\n blocks: [...this.blocks.values()],\n runs: [...this.runs.values()],\n rootId: this.rootId,\n fieldTypes: [...this.fieldTypes.values()],\n comments: [...this.comments.values()],\n people: [...this.people.values()],\n };\n }\n\n static fromJSON(doc) {\n return new EditorStore(doc);\n }\n}\n","import { captureSubtree, remapSubtreeIds } from '../clipboard/serialize.js';\nimport { insertBlock, updateRun } from '../store/operations.js';\n\n/** Batches ops if the store supports it (a History instance), else applies them one at a time — same idiom as blocks/shared/mergeCommands.js. */\nfunction applyOps(store, ops) {\n if (typeof store.performBatch === 'function') store.performBatch(ops);\n else for (const op of ops) store.applyOperation(op);\n}\n\n/**\n * Captures one or more sibling blocks (each with its own descendants) as a\n * reusable, storable template — the block-level counterpart to\n * `exportDocumentJSON` for whole documents. Ids are kept as-is (matching the\n * app clipboard's own JSON payload); `insertBlockTemplate` remaps them fresh\n * at insert time, so the same template can be inserted any number of times\n * without ever colliding with existing content.\n */\nexport function captureBlockTemplate(store, blockIds) {\n return { roots: blockIds.map((id) => captureSubtree(store, id)) };\n}\n\n/**\n * Inserts a captured block template as new sibling blocks under `parentId`,\n * starting at `index` — the same \"remap ids, then insert\" loop\n * useClipboardHandlers.js's onPaste uses for multi-block paste, reused here\n * instead of duplicated as a one-off.\n */\nexport function insertBlockTemplate(store, template, { parentId, index }) {\n const ops = [];\n let nextIndex = index;\n for (const root of template.roots) {\n const { block, runs, subtreeBlocks } = remapSubtreeIds(root);\n block.parentId = parentId;\n ops.push(insertBlock(block, parentId, nextIndex, { blocks: [block, ...subtreeBlocks], runs }));\n nextIndex += 1;\n }\n applyOps(store, ops);\n}\n\n/**\n * Wholesale-replaces an already-mounted editor's content with a document\n * template — the \"apply this starter document to a live editor\" counterpart\n * to passing `doc` to `useEditor()` at creation time. Same Map-reassignment\n * approach usePersistedDocument.js uses for loading a persisted document\n * (written fresh here rather than shared, to avoid touching that file).\n */\nexport function applyDocumentTemplate(store, doc) {\n const rawStore = store.store ?? store; // unwrap a History instance to the underlying EditorStore\n rawStore.blocks = new Map((doc.blocks ?? []).map((b) => [b.id, b]));\n rawStore.runs = new Map((doc.runs ?? []).map((r) => [r.id, r]));\n rawStore.rootId = doc.rootId ?? null;\n rawStore.fieldTypes = new Map((doc.fieldTypes ?? []).map((f) => [f.id, f]));\n rawStore._fieldTypesSnapshot = null;\n rawStore.comments = new Map((doc.comments ?? []).map((c) => [c.id, c]));\n rawStore._commentsSnapshot = null;\n rawStore._orders = new Map();\n // '$fieldTypes'/'$comments' are the same sentinel notify keys\n // useFieldTypes/useComments subscribe to -- without them, a restored\n // template/version's field types or comments wouldn't refresh a\n // currently-mounted list for either, even though the underlying Maps did change.\n rawStore._notify([\n ...rawStore.blocks.keys(),\n ...rawStore.runs.keys(),\n '$fieldTypes',\n '$comments',\n ]);\n}\n\n/**\n * Registers block-level templates as slash commands — discoverable and\n * insertable via \"/\" alongside every built-in block, with no changes needed\n * to SlashMenu/useSlashMenuTrigger/BlockRegistry, since those already just\n * read whatever `listSlashCommands()` returns. Each template is registered\n * under a synthetic key that's never used as a real block's `type` (no\n * block of that type is ever inserted — only the template's own captured\n * blocks, which already carry their own real types), so `component`/\n * `isLeaf` here are never actually reached.\n */\nexport function registerBlockTemplates(registry, templates) {\n for (const template of templates) {\n registry.register(`__blockTemplate:${template.id}`, {\n component: () => null,\n isLeaf: true,\n slashCommand: {\n label: template.label,\n icon: template.icon,\n keywords: template.keywords ?? [template.label],\n run(store, { blockId, runId, sliceStart, sliceEnd }) {\n // clear the \"/query\" text that triggered this, same as every other block's slashCommand\n const run = store.getRun(runId);\n const value = run?.value ?? '';\n store.applyOperation(\n updateRun(runId, { value: value.slice(0, sliceStart) + value.slice(sliceEnd) }),\n );\n\n const current = store.getBlock(blockId);\n const parent = store.getBlock(current.parentId);\n const index = parent.contentIds.indexOf(blockId) + 1;\n insertBlockTemplate(store, template, { parentId: current.parentId, index });\n },\n },\n });\n }\n}\n","import { captureSubtree } from './serialize.js';\nimport { serializeBlockRange } from './serialize.js';\n\n/**\n * Whole-document JSON export — the exact `{ rootId, blocks, runs }` shape\n * `new EditorStore({...})` itself accepts, so this round-trips: export a\n * document, and the result can be handed straight back into a fresh\n * EditorStore to reconstruct it. Wraps captureSubtree (already used for\n * duplicate/copy) starting from the document's own root rather than one\n * top-level block, so it walks the *entire* tree in one pass.\n */\nexport function exportDocumentJSON(store, { pretty = true } = {}) {\n const rootId = store.getRootId();\n const { blocks, runs } = captureSubtree(store, rootId);\n const payload = { version: 1, rootId, blocks, runs };\n return JSON.stringify(payload, null, pretty ? 2 : 0);\n}\n\n/** Whole-document HTML export — every top-level block's own toHTML, joined (see serializeBlockRange). */\nexport function exportDocumentHTML(store, registry, inlineRegistry) {\n const root = store.getBlock(store.getRootId());\n if (!root) return '';\n return serializeBlockRange(store, registry, root.contentIds, inlineRegistry).html;\n}\n\n/** Whole-document plain-text export — every top-level block's own toPlainText, joined (see serializeBlockRange). */\nexport function exportDocumentText(store, registry, inlineRegistry) {\n const root = store.getBlock(store.getRootId());\n if (!root) return '';\n return serializeBlockRange(store, registry, root.contentIds, inlineRegistry).text;\n}\n\n/**\n * Whole-document HTML wrapped for Microsoft Word — the well-known trick of\n * giving Word-flavored HTML (an `xmlns:w=\"urn:schemas-microsoft-com:office:\n * word\"` namespace + a small `<!--[if gte mso 9]>` block) a `.doc`\n * extension, which Word opens directly via MIME sniffing without needing a\n * real `.docx` (OOXML zip) writer — no runtime dependency needed for\n * either the zip container or the XML schema a genuine `.docx` would\n * require, staying true to this package's zero-dependency design. The\n * `<body>` content is exactly `exportDocumentHTML`'s own output — nothing\n * about the per-block HTML changes for Word specifically.\n */\nexport function exportDocumentWordHTML(store, registry, inlineRegistry) {\n const body = exportDocumentHTML(store, registry, inlineRegistry);\n return `<html xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" xmlns=\"http://www.w3.org/TR/REC-html40\">\n<head>\n<meta charset=\"utf-8\">\n<title>Document</title>\n<!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:DoNotOptimizeForBrowser/></w:WordDocument></xml><![endif]-->\n</head>\n<body>${body}</body>\n</html>`;\n}\n\n/**\n * Whole-document Markdown (CommonMark/GFM) export. Each top-level block\n * calls its own `toMarkdown(block, ctx)`; a block type with no\n * `toMarkdown` of its own (a custom host-registered block type, or one of\n * this package's own types with no meaningful Markdown form — canvas\n * drawings, in particular) falls back to its required `toPlainText`\n * instead, same graceful-degradation choice every per-block `toMarkdown`\n * above makes for ITS OWN children.\n *\n * Consecutive sibling blocks are joined with a blank line EXCEPT between\n * two list items of the same kind (both `listItem`, both `ordered` or both\n * not) or two consecutive `blockquote` lines — those need to stay\n * adjacent, with no blank line between them, or Markdown parses them as\n * separate lists/quotes instead of one continuous one. Mirrors\n * `serializeHTML`'s own consecutive-sibling grouping in `serialize.js`,\n * just without an actual wrapping tag to emit (Markdown lists/quotes need\n * no container syntax, only adjacency).\n */\nexport function exportDocumentMarkdown(store, registry, inlineRegistry) {\n const root = store.getBlock(store.getRootId());\n if (!root) return '';\n const ctx = { store, registry, inlineRegistry };\n const ids = root.contentIds;\n\n let markdown = '';\n let prevBlock = null;\n for (const id of ids) {\n const block = store.getBlock(id);\n const entry = registry.get(block.type);\n const blockMarkdown = (entry.toMarkdown ?? entry.toPlainText)(block, ctx);\n\n if (prevBlock) {\n const sameList =\n block.type === 'listItem' &&\n prevBlock.type === 'listItem' &&\n Boolean(block.props.ordered) === Boolean(prevBlock.props.ordered);\n const sameQuote = block.type === 'blockquote' && prevBlock.type === 'blockquote';\n markdown += sameList || sameQuote ? '\\n' : '\\n\\n';\n }\n markdown += blockMarkdown;\n prevBlock = block;\n }\n return markdown;\n}\n","import { exportDocumentJSON } from '../clipboard/exportDocument.js';\nimport {\n saveDocumentVersion,\n listDocumentVersions,\n deleteDocumentVersion,\n} from '../persistence/indexedDbPersistence.js';\n\nconst DEFAULT_IDLE_MS = 5 * 60 * 1000; // 5 minutes of inactivity closes a version, roughly matching how Google Docs groups edits into one revision\nconst DEFAULT_MAX_VERSIONS = 200;\n\n/**\n * Automatic, Google Docs-style version history — no \"type a label and save\"\n * step. Listens to `store.subscribeToHistory()` (`store` must be a\n * `History` instance, not a plain `EditorStore` — this needs `getHistoryLog()`)\n * and, after `idleMs` of no further edits, closes the \"window\" of edits\n * that just happened and saves one snapshot for it: who made them\n * (`authorId`/`authorIds`, read straight off each entry's `actorId` — see\n * History's `defaultActorId`/`useEditor`'s `currentUserId`, no separate\n * identity plumbing needed here), roughly what changed (`summary`, a\n * lightweight count of distinct blocks touched — not a full diff), and the\n * full document snapshot itself for restore/preview.\n *\n * A version is never named/labeled up front — `DocumentVersion.label`\n * still exists for callers who want to rename one afterward, but nothing\n * in this module asks for one.\n */\nexport function createAutoVersionHistory({\n store,\n docId,\n idleMs = DEFAULT_IDLE_MS,\n maxVersions = DEFAULT_MAX_VERSIONS,\n onSnapshot,\n onError,\n}) {\n let idleTimer = null;\n let windowStart = store.getHistoryLog().length; // index into historyLog where the current open window begins\n let hasPendingActivity = false;\n\n function clearIdleTimer() {\n if (idleTimer) {\n clearTimeout(idleTimer);\n idleTimer = null;\n }\n }\n\n async function finalizeWindow() {\n clearIdleTimer();\n if (!hasPendingActivity) return;\n hasPendingActivity = false;\n\n const log = store.getHistoryLog();\n const entries = log.slice(windowStart);\n windowStart = log.length;\n if (entries.length === 0) return; // e.g. only an undo/redo happened, nothing new was appended to the log\n\n const touchedIds = new Set(entries.map((e) => e.id).filter(Boolean));\n const authorIds = [...new Set(entries.map((e) => e.actorId).filter(Boolean))];\n const authorId = entries[entries.length - 1].actorId ?? null; // whoever made the most recent edit in this window\n\n try {\n const doc = JSON.parse(exportDocumentJSON(store)); // exportDocumentJSON returns a JSON *string* -- parse it into the plain object DocumentVersion.doc expects\n const version = {\n id: crypto.randomUUID(),\n docId,\n timestamp: Date.now(),\n authorId,\n authorIds,\n summary: `${touchedIds.size} block${touchedIds.size === 1 ? '' : 's'} changed`,\n doc,\n };\n await saveDocumentVersion(version);\n onSnapshot?.(version);\n\n const all = await listDocumentVersions(docId); // newest first\n if (all.length > maxVersions) {\n const excess = all.slice(maxVersions);\n await Promise.all(excess.map((v) => deleteDocumentVersion(v.id)));\n }\n } catch (err) {\n onError?.(err);\n }\n }\n\n const unsubscribe = store.subscribeToHistory(() => {\n hasPendingActivity = true;\n clearIdleTimer();\n idleTimer = setTimeout(finalizeWindow, idleMs);\n });\n\n return {\n /** Stops listening. Does NOT flush a final in-progress window -- call `flush()` first if you want that (e.g. right before navigating away). */\n stop() {\n clearIdleTimer();\n unsubscribe();\n },\n /** Closes and saves the current window immediately, without waiting for `idleMs` of inactivity. A no-op if nothing's happened since the last save. */\n flush: finalizeWindow,\n };\n}\n","/**\n * Word-level LCS diff between two strings. Not a full Myers diff, but\n * version-history edits are typically a handful of scattered word changes\n * inside otherwise-unchanged paragraphs, so this is enough to render\n * accurate insertions/deletions instead of just \"this whole block changed.\"\n * Whitespace runs are kept as their own tokens so re-joining segments\n * reproduces the original spacing exactly.\n */\nexport function diffWords(oldText, newText) {\n const oldTokens = tokenize(oldText);\n const newTokens = tokenize(newText);\n const n = oldTokens.length;\n const m = newTokens.length;\n\n const dp = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));\n for (let i = n - 1; i >= 0; i -= 1) {\n for (let j = m - 1; j >= 0; j -= 1) {\n dp[i][j] =\n oldTokens[i] === newTokens[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);\n }\n }\n\n const segments = [];\n const push = (type, text) => {\n const last = segments[segments.length - 1];\n if (last && last.type === type) last.text += text;\n else segments.push({ type, text });\n };\n\n let i = 0;\n let j = 0;\n while (i < n && j < m) {\n if (oldTokens[i] === newTokens[j]) {\n push('equal', oldTokens[i]);\n i += 1;\n j += 1;\n } else if (dp[i + 1][j] >= dp[i][j + 1]) {\n push('removed', oldTokens[i]);\n i += 1;\n } else {\n push('added', newTokens[j]);\n j += 1;\n }\n }\n while (i < n) {\n push('removed', oldTokens[i]);\n i += 1;\n }\n while (j < m) {\n push('added', newTokens[j]);\n j += 1;\n }\n return segments;\n}\n\nfunction tokenize(text) {\n return (text ?? '').match(/\\s+|[^\\s]+/g) ?? [];\n}\n\nconst ESCAPE_MAP = { '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' };\nfunction escapeHtml(text) {\n return text.replace(/[&<>\"']/g, (ch) => ESCAPE_MAP[ch]);\n}\n\n/**\n * Flattens a DocumentJSON into an ordered list of its text-bearing blocks\n * (paragraphs, headings, list items, ...), keyed by block id. Only blocks\n * whose content resolves directly to text runs are collected -- container\n * blocks (page, columns, ...) are walked through but not themselves\n * represented, matching how they render (no text of their own to diff).\n */\nfunction extractTextBlocks(doc) {\n const blockById = new Map(doc.blocks.map((b) => [b.id, b]));\n const runById = new Map(doc.runs.map((r) => [r.id, r]));\n const result = [];\n\n const textFromRunIds = (ids) => ids.map((id) => runById.get(id)?.value ?? '').join('');\n\n const walk = (id) => {\n const block = blockById.get(id);\n if (!block) return;\n const titleRunIds = block.props?.titleRunIds;\n if (Array.isArray(titleRunIds) && titleRunIds.length > 0) {\n result.push({ id, text: textFromRunIds(titleRunIds) });\n } else if (block.contentIds.length > 0 && block.contentIds.every((cid) => runById.has(cid))) {\n result.push({ id, text: textFromRunIds(block.contentIds) });\n }\n for (const childId of block.contentIds) {\n if (!runById.has(childId)) walk(childId);\n }\n };\n walk(doc.rootId);\n return result;\n}\n\nconst EMPTY_DOC = {\n rootId: 'root',\n blocks: [{ id: 'root', type: 'page', parentId: null, contentIds: [], props: {} }],\n runs: [],\n};\n\n/**\n * Renders an HTML diff of `nextDoc` against `prevDoc` (Google Docs-style\n * \"show changes\" view) -- blocks that only exist in `nextDoc` render fully\n * highlighted as added, blocks that only existed in `prevDoc` render fully\n * struck-through as removed (appended after the surviving blocks, since\n * this is about surfacing *what* changed, not reproducing exact block\n * reordering), and blocks present in both get word-level diffed. Pass\n * `null`/`undefined` as `prevDoc` for \"this is the first version\" (everything\n * shows as added).\n */\nexport function diffDocumentsHTML(prevDoc, nextDoc) {\n const prevBlocks = extractTextBlocks(prevDoc ?? EMPTY_DOC);\n const nextBlocks = extractTextBlocks(nextDoc ?? EMPTY_DOC);\n const prevById = new Map(prevBlocks.map((b) => [b.id, b]));\n const nextIds = new Set(nextBlocks.map((b) => b.id));\n\n const parts = [];\n\n for (const block of nextBlocks) {\n const prevBlock = prevById.get(block.id);\n if (!prevBlock) {\n const text = escapeHtml(block.text) || ' ';\n parts.push(\n `<p class=\"be-version-diff-block\"><span class=\"be-version-diff-added\">${text}</span></p>`,\n );\n continue;\n }\n if (prevBlock.text === block.text) {\n parts.push(`<p class=\"be-version-diff-block\">${escapeHtml(block.text) || ' '}</p>`);\n continue;\n }\n const segments = diffWords(prevBlock.text, block.text);\n const html = segments\n .map((seg) => {\n const escaped = escapeHtml(seg.text);\n if (seg.type === 'added') return `<span class=\"be-version-diff-added\">${escaped}</span>`;\n if (seg.type === 'removed')\n return `<span class=\"be-version-diff-removed\">${escaped}</span>`;\n return escaped;\n })\n .join('');\n parts.push(`<p class=\"be-version-diff-block\">${html}</p>`);\n }\n\n for (const block of prevBlocks) {\n if (!nextIds.has(block.id)) {\n const text = escapeHtml(block.text) || ' ';\n parts.push(\n `<p class=\"be-version-diff-block\"><span class=\"be-version-diff-removed\">${text}</span></p>`,\n );\n }\n }\n\n if (parts.length === 0) return '<p class=\"be-version-diff-empty\">No text changes.</p>';\n return parts.join('');\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { listDocumentVersions } from '../persistence/indexedDbPersistence.js';\n\n/**\n * Reactive access to the stored version history for one document (see\n * saveDocumentVersion/loadDocumentVersion/deleteDocumentVersion/\n * listDocumentVersions) — for building a version list UI backed by\n * IndexedDB. Loads on mount and whenever `docId` changes; `refresh()`\n * re-queries on demand (e.g. right after a saveDocumentVersion/\n * deleteDocumentVersion call made elsewhere, since nothing here subscribes\n * to storage writes automatically). Restoring a version is not part of\n * this hook -- pass the loaded version's `doc` to `applyDocumentTemplate`.\n */\nexport function useDocumentVersions(docId) {\n const [versions, setVersions] = useState([]);\n const [isLoaded, setIsLoaded] = useState(false);\n\n const refresh = useCallback(() => {\n if (!docId) return Promise.resolve();\n setIsLoaded(false);\n return listDocumentVersions(docId).then((all) => {\n setVersions(all);\n setIsLoaded(true);\n });\n }, [docId]);\n\n useEffect(() => {\n if (!docId) {\n setVersions([]);\n setIsLoaded(true);\n return undefined;\n }\n let cancelled = false;\n setIsLoaded(false);\n listDocumentVersions(docId).then((all) => {\n if (cancelled) return;\n setVersions(all);\n setIsLoaded(true);\n });\n return () => {\n cancelled = true;\n };\n }, [docId]);\n\n return { versions, isLoaded, refresh };\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useEditorStore, useBlockRegistry, useInlineRegistry } from './EditorProvider.jsx';\nimport { useDocumentVersions } from './useDocumentVersions.js';\nimport { createAutoVersionHistory } from '../versions/autoVersionHistory.js';\nimport { diffDocumentsHTML } from '../versions/diffVersions.js';\nimport { applyDocumentTemplate } from '../templates/blockTemplates.js';\nimport { exportDocumentHTML } from '../clipboard/exportDocument.js';\nimport { EditorStore } from '../store/EditorStore.js';\nimport { CommentAvatar } from './CommentAvatar.jsx';\nimport { formatRelativeTime } from './commentFormatting.js';\nimport { ClockHistoryIcon, RestoreIcon, XIcon } from './icons.jsx';\n\nconst FOCUSABLE_SELECTOR =\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])';\n\nfunction formatDayLabel(timestamp) {\n const d = new Date(timestamp);\n const now = new Date();\n const startOfDay = (date) =>\n new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();\n const diffDays = Math.round((startOfDay(now) - startOfDay(d)) / (24 * 60 * 60 * 1000));\n if (diffDays === 0) return 'Today';\n if (diffDays === 1) return 'Yesterday';\n return d.toLocaleDateString(undefined, {\n month: 'long',\n day: 'numeric',\n year: d.getFullYear() !== now.getFullYear() ? 'numeric' : undefined,\n });\n}\n\n/**\n * Google Docs-style version history — no \"name it and save\" step. Mount\n * this once anywhere under an `<EditorProvider>` (`store` must be a\n * `History` instance, not a plain `EditorStore` — see `createAutoVersionHistory`)\n * and it both quietly captures automatic snapshots (one saved after each\n * burst of edits settles, attributed to whoever made them via History's\n * `defaultActorId`/`useEditor`'s `currentUserId`, no separate identity\n * plumbing needed here) AND renders the \"Version history\" button + drawer\n * UI for browsing/previewing/restoring them.\n *\n * The drawer's preview is a static HTML render (`exportDocumentHTML` against\n * a throwaway `EditorStore` built from that version's snapshot) — read-only\n * by construction, not a second live nested editor.\n */\nexport function VersionHistory({ docId, idleMs, maxVersions }) {\n const store = useEditorStore();\n const registry = useBlockRegistry();\n const inlineRegistry = useInlineRegistry();\n const { versions, isLoaded, refresh } = useDocumentVersions(docId);\n const [isOpen, setIsOpen] = useState(false);\n const [selectedId, setSelectedId] = useState(null);\n const [viewMode, setViewMode] = useState('changes'); // 'changes' | 'preview'\n const drawerRef = useRef(null);\n const previouslyFocusedRef = useRef(null);\n\n useEffect(() => {\n if (typeof store.getHistoryLog !== 'function') return undefined; // needs a History instance, not a plain EditorStore\n const autoHistory = createAutoVersionHistory({\n store,\n docId,\n idleMs,\n maxVersions,\n onSnapshot: refresh,\n });\n return () => autoHistory.stop();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [store, docId]);\n\n function close() {\n setIsOpen(false);\n setSelectedId(null);\n setViewMode('changes');\n }\n\n useEffect(() => {\n if (!isOpen) return undefined;\n const handleKeyDown = (event) => {\n if (event.key === 'Escape') close();\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n // Same one-time move-in-on-open / move-back-out-on-close as Modal.jsx —\n // this drawer is hand-rolled rather than built on Modal (it's a\n // side-drawer, not a centered dialog), so it needs its own copy of the\n // same focus handling instead of inheriting it for free.\n useEffect(() => {\n if (isOpen) {\n previouslyFocusedRef.current = document.activeElement;\n const drawer = drawerRef.current;\n const focusable = drawer?.querySelector(FOCUSABLE_SELECTOR);\n (focusable ?? drawer)?.focus();\n return undefined;\n }\n previouslyFocusedRef.current?.focus?.();\n previouslyFocusedRef.current = null;\n return undefined;\n }, [isOpen]);\n\n const selectedIndex = versions.findIndex((v) => v.id === selectedId);\n const selected = selectedIndex >= 0 ? versions[selectedIndex] : null;\n // versions is newest-first, so the version right before this one chronologically is the next array entry.\n const previousVersion = selectedIndex >= 0 ? (versions[selectedIndex + 1] ?? null) : null;\n\n const previewHtml = useMemo(() => {\n if (!selected) return '';\n const previewStore = new EditorStore(selected.doc);\n return exportDocumentHTML(previewStore, registry, inlineRegistry);\n }, [selected, registry, inlineRegistry]);\n\n const diffHtml = useMemo(() => {\n if (!selected) return '';\n return diffDocumentsHTML(previousVersion?.doc ?? null, selected.doc);\n }, [selected, previousVersion]);\n\n function handleRestore() {\n if (!selected) return;\n if (!window.confirm('Restore this version? This replaces the current document.')) return;\n applyDocumentTemplate(store, selected.doc);\n close();\n }\n\n // Groups by day, preserving useDocumentVersions' own newest-first order.\n const groups = [];\n for (const version of versions) {\n const label = formatDayLabel(version.timestamp);\n let group = groups[groups.length - 1];\n if (!group || group.label !== label) {\n group = { label, versions: [] };\n groups.push(group);\n }\n group.versions.push(version);\n }\n\n return (\n <>\n <button\n type=\"button\"\n className=\"be-version-history-trigger\"\n onClick={() => setIsOpen(true)}\n title=\"Version history\"\n >\n <ClockHistoryIcon size={15} />\n Version history\n </button>\n {isOpen &&\n createPortal(\n <>\n <div className=\"be-version-history-backdrop\" onClick={close} />\n <div\n ref={drawerRef}\n className=\"be-version-history-drawer\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Version history\"\n tabIndex={-1}\n >\n <div className=\"be-version-history-header\">\n <span>{selected ? 'Preview' : 'Version history'}</span>\n <button\n type=\"button\"\n className=\"be-version-history-close\"\n onClick={close}\n aria-label=\"Close\"\n >\n <XIcon size={14} />\n </button>\n </div>\n\n {selected ? (\n <div className=\"be-version-history-preview\">\n <div className=\"be-version-history-preview-meta\">\n <CommentAvatar authorId={selected.authorId} size={24} />\n <div>\n <div className=\"be-version-history-preview-author\">\n {selected.authorId ?? 'Unknown'}\n </div>\n <div className=\"be-version-history-preview-time\">\n {new Date(selected.timestamp).toLocaleString()} · {selected.summary}\n </div>\n </div>\n </div>\n\n <div\n className=\"be-version-history-view-toggle\"\n role=\"tablist\"\n aria-label=\"View mode\"\n >\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={viewMode === 'changes'}\n className={viewMode === 'changes' ? 'is-active' : ''}\n onClick={() => setViewMode('changes')}\n >\n Changes\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={viewMode === 'preview'}\n className={viewMode === 'preview' ? 'is-active' : ''}\n onClick={() => setViewMode('preview')}\n >\n Preview\n </button>\n </div>\n\n {viewMode === 'changes' && (\n <>\n <div className=\"be-version-history-diff-legend\">\n <span className=\"be-version-diff-added\">Added</span>\n <span className=\"be-version-diff-removed\">Removed</span>\n {!previousVersion && (\n <span className=\"be-version-history-diff-legend-note\">\n First saved version — everything shown is new.\n </span>\n )}\n </div>\n {/* Word-level diff against the previous version, rendered from plain text -- deliberately simpler than the live editor's own formatting (see diffDocumentsHTML). */}\n <div\n className=\"be-version-history-preview-body be-version-history-diff-body\"\n dangerouslySetInnerHTML={{ __html: diffHtml }}\n />\n </>\n )}\n {viewMode === 'preview' && (\n // Static HTML from a throwaway store (see previewHtml above) -- inherently read-only, not a second live editor.\n <div\n className=\"be-version-history-preview-body\"\n dangerouslySetInnerHTML={{ __html: previewHtml }}\n />\n )}\n\n <div className=\"be-version-history-preview-actions\">\n <button\n type=\"button\"\n onClick={() => {\n setSelectedId(null);\n setViewMode('changes');\n }}\n >\n Back\n </button>\n <button\n type=\"button\"\n className=\"be-version-history-restore\"\n onClick={handleRestore}\n >\n <RestoreIcon size={13} />\n Restore this version\n </button>\n </div>\n </div>\n ) : (\n <div className=\"be-version-history-list\">\n {!isLoaded && <p className=\"be-version-history-empty\">Loading…</p>}\n {isLoaded && versions.length === 0 && (\n <p className=\"be-version-history-empty\">\n No versions yet — one is saved automatically after you make some edits and\n pause.\n </p>\n )}\n {groups.map((group) => (\n <div key={group.label} className=\"be-version-history-group\">\n <div className=\"be-version-history-group-label\">{group.label}</div>\n {group.versions.map((version) => (\n <button\n key={version.id}\n type=\"button\"\n className=\"be-version-history-item\"\n onClick={() => setSelectedId(version.id)}\n >\n <CommentAvatar authorId={version.authorId} size={24} />\n <div className=\"be-version-history-item-body\">\n <div className=\"be-version-history-item-author\">\n {version.authorId ?? 'Unknown'}\n </div>\n <div className=\"be-version-history-item-meta\">\n {formatRelativeTime(version.timestamp)} · {version.summary}\n </div>\n </div>\n </button>\n ))}\n </div>\n ))}\n </div>\n )}\n </div>\n </>,\n document.body,\n )}\n </>\n );\n}\n"],"names":["diffCodePoints","oldValue","newValue","oldChars","newChars","start","maxStart","oldEnd","newEnd","FIELD_TYPES_KEY","COMMENTS_KEY","PEOPLE_KEY","EditorStore","doc","b","f","c","p","genPeerId","HLC","FieldClockRegistry","parentId","order","block","ListCrdtState","runId","run","chars","prev","index","ch","charId","value","currentIds","clock","deletedCharIds","i","id","insertedChars","afterId","genId","charEdit","d","listener","set","touchedIds","rootId","blocks","runs","walk","childId","_a","op","OP","patchKeys","editRunChars","previousPatch","clocks","key","updateRun","updateBlockProps","previousType","previousProps","changeBlockType","r","parent","currentArray","removeBlock","target","subtree","deleteClock","fromParentId","fromOrder","fromIndex","toOrder","toArray","toIndex","ts","touched","moveBlock","previousContentIds","setBlockContentIds","inContentIds","sourceArray","startIndex","removedRuns","newArray","replaceRunSpan","usesTitleRunIds","previousRunIds","previousRuns","newRunIds","setBlockRuns","removeFieldType","existing","updateFieldType","addFieldType","removeCommentThread","addCommentThread","thread","removeCommentReply","removedMessage","m","addCommentReply","previousResolved","resolveComment","removePerson","updatePerson","addPerson","ops","envelope","hadSlot","patch","remoteClock","_b","count","maxAgeMs","now","threshold","removed","applyOps","store","captureBlockTemplate","blockIds","captureSubtree","insertBlockTemplate","template","nextIndex","root","subtreeBlocks","remapSubtreeIds","insertBlock","applyDocumentTemplate","rawStore","registerBlockTemplates","registry","templates","blockId","sliceStart","sliceEnd","current","exportDocumentJSON","pretty","exportDocumentHTML","inlineRegistry","serializeBlockRange","exportDocumentText","exportDocumentWordHTML","exportDocumentMarkdown","ctx","ids","markdown","prevBlock","entry","blockMarkdown","sameList","sameQuote","DEFAULT_IDLE_MS","DEFAULT_MAX_VERSIONS","createAutoVersionHistory","docId","idleMs","maxVersions","onSnapshot","onError","idleTimer","windowStart","hasPendingActivity","clearIdleTimer","finalizeWindow","log","entries","e","authorIds","authorId","version","saveDocumentVersion","all","listDocumentVersions","excess","v","deleteDocumentVersion","err","unsubscribe","diffWords","oldText","newText","oldTokens","tokenize","newTokens","n","dp","j","segments","push","type","text","last","ESCAPE_MAP","escapeHtml","extractTextBlocks","blockById","runById","result","textFromRunIds","titleRunIds","cid","EMPTY_DOC","diffDocumentsHTML","prevDoc","nextDoc","prevBlocks","nextBlocks","prevById","nextIds","parts","html","seg","escaped","useDocumentVersions","versions","setVersions","useState","isLoaded","setIsLoaded","refresh","useCallback","useEffect","cancelled","FOCUSABLE_SELECTOR","formatDayLabel","timestamp","startOfDay","date","diffDays","VersionHistory","useEditorStore","useBlockRegistry","useInlineRegistry","isOpen","setIsOpen","selectedId","setSelectedId","viewMode","setViewMode","drawerRef","useRef","previouslyFocusedRef","autoHistory","close","handleKeyDown","event","drawer","_c","selectedIndex","selected","previousVersion","previewHtml","useMemo","previewStore","diffHtml","handleRestore","groups","label","group","jsxs","Fragment","jsx","ClockHistoryIcon","createPortal","XIcon","CommentAvatar","RestoreIcon","formatRelativeTime"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";const c=require("react"),q=require("react-dom"),L=require("./icons-BCseh2pt.cjs"),g=require("./leafBlockFactory-CaLWUcfa.cjs"),V=require("./usePopoverEdgeClamp-CPRWQtZV.cjs"),b=require("react/jsx-runtime");function N(e){var u,l;const s=(u=e.getPendingSelection)==null?void 0:u.call(e);if(s){g.focusRunAtOffset(s.runId,s.offset);return}const i=(((l=e.getPendingAffectedBlockIds)==null?void 0:l.call(e))??[]).find(a=>e.getBlock(a));i&&g.focusBlockStart(e,i)}const F=[{phrases:["new paragraph","next paragraph"],description:"Start a new paragraph",action:{type:"insertParagraph"}},{phrases:["heading one","heading 1"],description:"Convert the current block to a heading 1",action:{type:"convertBlock",blockType:"heading",props:{level:1}}},{phrases:["heading two","heading 2"],description:"Convert the current block to a heading 2",action:{type:"convertBlock",blockType:"heading",props:{level:2}}},{phrases:["heading three","heading 3"],description:"Convert the current block to a heading 3",action:{type:"convertBlock",blockType:"heading",props:{level:3}}},{phrases:["bulleted list","bullet point","bullet list"],description:"Convert the current block to a bulleted list item",action:{type:"convertBlock",blockType:"listItem",props:{ordered:!1,titleRunIds:[]}}},{phrases:["numbered list","ordered list"],description:"Convert the current block to a numbered list item",action:{type:"convertBlock",blockType:"listItem",props:{ordered:!0,titleRunIds:[]}}},{phrases:["checklist","to-do","to do","to-do list","to do list"],description:"Convert the current block to a to-do item",action:{type:"convertBlock",blockType:"listItem",props:{ordered:!1,checked:!1,titleRunIds:[]}}},{phrases:["quote"],description:"Convert the current block to a quote",action:{type:"convertBlock",blockType:"blockquote",props:{}}},{phrases:["code block","code"],description:"Convert the current block to a code block",action:{type:"convertBlock",blockType:"code",props:{language:"plaintext"}}},{phrases:["undo"],description:"Undo the last edit",action:{type:"undo"}},{phrases:["redo"],description:"Redo the last undone edit",action:{type:"redo"}},{phrases:["stop dictation","stop listening","stop"],description:"Stop dictation",action:{type:"stopDictation"}}],H=new Map;for(const{phrases:e,action:s}of F)for(const n of e)H.set(n,s);function $(e){return(e??"").toLowerCase().trim().replace(/\s+/g," ").replace(/[.,!?]+$/,"")}function G(e){return H.get($(e))??null}function J(){return F.map(({phrases:e,description:s})=>({phrases:[...e],description:s}))}function Q(e,s){if(typeof e.performBatch=="function")e.performBatch(s);else for(const n of s)e.applyOperation(n)}function W(e){var s;return((s=e==null?void 0:e.props)==null?void 0:s.titleRunIds)??(e==null?void 0:e.contentIds)??[]}function X(e,s,n){var i,u;if(n.type==="insertParagraph"){g.insertSiblingAfterAndFocus(e,s,g.createTextLeafBlock("paragraph"));return}if(n.type==="convertBlock"){const l=e.getBlock(s);if(!l)return;const a=W(l),{ops:E}=V.convertBlockType(e,s,n.blockType,n.props,a);Q(e,E),a[0]&&g.focusRunStart(a[0]);return}if(n.type==="undo"){(i=e.undo)!=null&&i.call(e)&&N(e);return}n.type==="redo"&&(u=e.redo)!=null&&u.call(e)&&N(e)}function _(){return typeof window>"u"?null:window.SpeechRecognition??window.webkitSpeechRecognition??null}const Y="Microphone access is blocked. Enable it in your browser's site settings, then try again.";function Z({shortcut:e=!0,engine:s}={}){const n=L.useEditorStore(),[i,u]=c.useState(!1),[l,a]=c.useState("idle"),[E,k]=c.useState("unknown"),[v,C]=c.useState(null),p=c.useRef(null),d=c.useRef(null),I=c.useRef(null),m=c.useRef(null),O=c.useRef(0),x=s!=null||_()!==null;c.useEffect(()=>{var o;if(!((o=navigator.permissions)!=null&&o.query))return;let t,r=!1;return navigator.permissions.query({name:"microphone"}).then(f=>{r||(t=f,k(f.state),t.onchange=()=>k(t.state))}).catch(()=>{}),()=>{r=!0,t&&(t.onchange=null)}},[]);const T=c.useCallback(()=>{if(!d.current){const t=g.resolveCollapsedCaret();if(!t)return null;d.current=t}return{runId:d.current.runId,blockId:d.current.blockId,startOffset:d.current.offset,insertedLength:0}},[]),A=c.useCallback((t,r)=>{const o=n.getRun(t.runId);if(!o||o.type!=="text")return;const f=o.value??"",y=f.slice(0,t.startOffset);if(t.needsLeadingSpace===void 0){const w=y[y.length-1];t.needsLeadingSpace=t.startOffset>0&&w!==void 0&&!/\s/.test(w)}const R=(t.needsLeadingSpace?" ":"")+r,h=f.slice(t.startOffset+t.insertedLength);q.flushSync(()=>n.applyOperation(L.updateRun(t.runId,{value:y+R+h}))),t.insertedLength=R.length;const S=t.startOffset+R.length;g.setCaretSync(t.runId,S),d.current={runId:t.runId,blockId:t.blockId,offset:S},I.current={runId:t.runId,offset:S}},[n]),D=c.useCallback(t=>{const r=n.getRun(t.runId);if(!r||r.type!=="text")return;const o=r.value??"",f=o.slice(0,t.startOffset)+o.slice(t.startOffset+t.insertedLength);q.flushSync(()=>n.applyOperation(L.updateRun(t.runId,{value:f}))),g.setCaretSync(t.runId,t.startOffset),d.current={runId:t.runId,blockId:t.blockId,offset:t.startOffset},I.current={runId:t.runId,offset:t.startOffset}},[n]),z=c.useCallback(t=>{const r=n.getRun(t.runId);if(!r||r.type!=="text")return;const o=r.value??"",f=o.slice(0,t.startOffset)+o.slice(t.startOffset+t.insertedLength);n.applyOperation(L.updateRun(t.runId,{value:f}))},[n]),K=c.useCallback(t=>{var y,R;let r="",o=!1;const f=Math.max(t.resultIndex,O.current);for(let h=f;h<t.results.length;h+=1){const S=t.results[h],w=((y=S[0])==null?void 0:y.transcript)??"";if(!S.isFinal){r+=(r?" ":"")+w,o=!0;continue}const B=m.current??T();if(m.current=null,O.current=h+1,!B)continue;const j=G(w);j?(D(B),j.type==="stopDictation"?(R=p.current)==null||R.stop():(X(n,B.blockId,j),d.current=null,I.current=null,a("listening"))):(A(B,w),a("listening"))}if(o){const h=m.current??T();h&&(m.current=h,A(h,r))}},[n,T,A,D]);c.useEffect(()=>{if(!i)return;const t=()=>{const r=I.current;if(!r)return;const o=g.resolveCollapsedCaret();o&&(o.runId===r.runId&&o.offset===r.offset||(m.current&&(z(m.current),m.current=null),d.current=o,I.current={runId:o.runId,offset:o.offset}))};return document.addEventListener("selectionchange",t),()=>document.removeEventListener("selectionchange",t)},[i,z]);const M=c.useCallback(()=>{if(!x||p.current)return;C(null);const t=g.resolveCollapsedCaret();d.current=t,I.current=t?{runId:t.runId,offset:t.offset}:null,m.current=null,O.current=0;let r;if(s)r=s();else{const o=_();r=new o}r.continuous=!0,r.interimResults=!0,r.onresult=K,r.onstart=()=>a("listening"),r.onspeechstart=()=>a("listening"),r.onspeechend=()=>a("processing"),r.onerror=o=>{o.error==="not-allowed"||o.error==="service-not-allowed"?(k("denied"),C(Y)):C(o.error??"Voice typing stopped unexpectedly.")},r.onend=()=>{p.current=null,d.current=null,I.current=null,m.current=null,O.current=0,u(!1),a("idle")},p.current=r,r.start(),u(!0)},[x,K,s]),P=c.useCallback(()=>{var t;(t=p.current)==null||t.stop()},[]);return c.useEffect(()=>()=>{var t;return(t=p.current)==null?void 0:t.stop()},[]),c.useEffect(()=>{if(!e||!x)return;const t=r=>{!(r.metaKey||r.ctrlKey)||!r.shiftKey||r.key.toLowerCase()!=="m"||(r.preventDefault(),p.current?P():M())};return document.addEventListener("keydown",t),()=>document.removeEventListener("keydown",t)},[e,x,M,P]),{isSupported:x,isListening:i,status:l,permissionState:E,error:v,start:M,stop:P}}function ee({voice:e}){const[s,n]=c.useState(!1);c.useEffect(()=>{n(!1)},[e==null?void 0:e.error]);const i=(e==null?void 0:e.permissionState)==="denied"&&!s;return b.jsxs(V.Modal,{isOpen:i,onClose:()=>n(!0),title:"Microphone access blocked",children:[b.jsx("p",{children:(e==null?void 0:e.error)||"Microphone access is blocked. Enable it in your browser's site settings, then try again."}),b.jsxs("div",{className:"be-modal-actions",children:[b.jsx("button",{type:"button",className:"be-modal-cancel",onClick:()=>n(!0),children:"Dismiss"}),b.jsx("button",{type:"button",className:"be-modal-save",onClick:()=>{var u;return(u=e==null?void 0:e.start)==null?void 0:u.call(e)},children:"Try again"})]})]})}function U(e){const[s,n]=c.useState(null);return c.useEffect(()=>{if(!e){n(null);return}const i=()=>{var k,v,C,p;const u=(k=window.getSelection)==null?void 0:k.call(window);if(!u||u.rangeCount===0||!u.isCollapsed){n(null);return}const l=u.anchorNode,a=(l==null?void 0:l.nodeType)===1?l:l==null?void 0:l.parentElement;if(!((v=a==null?void 0:a.closest)!=null&&v.call(a,"[data-run-id]"))){n(null);return}const E=((p=(C=u.getRangeAt(0)).getBoundingClientRect)==null?void 0:p.call(C))??{top:0,left:0,right:0,bottom:0,width:0,height:0};n(E)};return i(),document.addEventListener("selectionchange",i),window.addEventListener("scroll",i,!0),window.addEventListener("resize",i),()=>{document.removeEventListener("selectionchange",i),window.removeEventListener("scroll",i,!0),window.removeEventListener("resize",i)}},[e]),s}function te({voice:e}){const s=!!(e!=null&&e.isListening),n=U(s),i=c.useRef(null),u=V.useAutoAdjustedCenteredLeft(i,!!(s&&n),n==null?void 0:n.left);if(!s||!n||u==null)return null;const l=e.status==="processing";return q.createPortal(b.jsxs("div",{ref:i,className:`be-voice-indicator${l?" be-voice-indicator-processing":""}`,contentEditable:!1,style:{position:"fixed",top:n.top-8,left:u,transform:"translate(-50%, -100%)"},children:[b.jsx("span",{className:"be-voice-indicator-dot"}),b.jsx(L.MicIcon,{size:12}),b.jsx("span",{className:"be-voice-indicator-label",children:l?"Processing…":"Listening…"})]}),document.body)}exports.VoiceListeningIndicator=te;exports.VoicePermissionModal=ee;exports.listVoiceCommands=J;exports.restoreSelectionAfterHistoryChange=N;exports.useCaretRect=U;exports.useVoiceTyping=Z;
|
|
2
|
+
//# sourceMappingURL=VoiceListeningIndicator-BXByLZwO.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoiceListeningIndicator-BXByLZwO.cjs","sources":["../../src/react/restoreHistorySelection.js","../../src/voice/voiceCommands.js","../../src/voice/applyVoiceAction.js","../../src/react/useVoiceTyping.js","../../src/react/VoicePermissionModal.jsx","../../src/react/useCaretRect.js","../../src/react/VoiceListeningIndicator.jsx"],"sourcesContent":["import { focusRunAtOffset } from './focusRun.js';\nimport { focusBlockStart } from '../blocks/shared/navigationCommands.js';\n\n/**\n * Moves the caret to wherever the undo/redo that was just performed should\n * leave it — call this right after a truthy store.undo()/store.redo().\n * Prefers the precise {runId, offset} History derives for a plain text-run\n * edit; falls back to the start of the first still-existing block the\n * entry touched for structural edits (insert/remove/move block, etc.),\n * which don't reduce to one offset but still shouldn't leave focus\n * dangling on whatever had it before (usually a toolbar button, or a DOM\n * node a delete/undo just replaced outright).\n */\nexport function restoreSelectionAfterHistoryChange(store) {\n const selection = store.getPendingSelection?.();\n if (selection) {\n focusRunAtOffset(selection.runId, selection.offset);\n return;\n }\n\n const affectedIds = store.getPendingAffectedBlockIds?.() ?? [];\n const targetBlockId = affectedIds.find((id) => store.getBlock(id));\n if (targetBlockId) focusBlockStart(store, targetBlockId);\n}\n","/**\n * The spoken-phrase -> editor-action table driving voice typing (see\n * useVoiceTyping.js). Deliberately data-driven and pure (no store/DOM\n * access here at all) so the \"does this phrase count as a command\"\n * decision is directly unit-testable without a real SpeechRecognition\n * engine — matchVoiceCommand is exercised with plain strings.\n *\n * Only a *complete, isolated* recognized utterance is ever checked against\n * this table (see useVoiceTyping's isFinal handling) — a command phrase\n * spoken as part of a longer sentence while dictating never matches,\n * since the caller only calls this with one finalized speech segment at a\n * time, normalized as its own whole utterance.\n *\n * Each entry's `description` is shown to end users (see listVoiceCommands)\n * — not just a code comment — so keep it short and phrased as what saying\n * it does, not how it's implemented.\n */\nconst COMMANDS = [\n {\n phrases: ['new paragraph', 'next paragraph'],\n description: 'Start a new paragraph',\n action: { type: 'insertParagraph' },\n },\n {\n phrases: ['heading one', 'heading 1'],\n description: 'Convert the current block to a heading 1',\n action: { type: 'convertBlock', blockType: 'heading', props: { level: 1 } },\n },\n {\n phrases: ['heading two', 'heading 2'],\n description: 'Convert the current block to a heading 2',\n action: { type: 'convertBlock', blockType: 'heading', props: { level: 2 } },\n },\n {\n phrases: ['heading three', 'heading 3'],\n description: 'Convert the current block to a heading 3',\n action: { type: 'convertBlock', blockType: 'heading', props: { level: 3 } },\n },\n {\n phrases: ['bulleted list', 'bullet point', 'bullet list'],\n description: 'Convert the current block to a bulleted list item',\n action: {\n type: 'convertBlock',\n blockType: 'listItem',\n props: { ordered: false, titleRunIds: [] },\n },\n },\n {\n phrases: ['numbered list', 'ordered list'],\n description: 'Convert the current block to a numbered list item',\n action: {\n type: 'convertBlock',\n blockType: 'listItem',\n props: { ordered: true, titleRunIds: [] },\n },\n },\n {\n phrases: ['checklist', 'to-do', 'to do', 'to-do list', 'to do list'],\n description: 'Convert the current block to a to-do item',\n action: {\n type: 'convertBlock',\n blockType: 'listItem',\n props: { ordered: false, checked: false, titleRunIds: [] },\n },\n },\n {\n phrases: ['quote'],\n description: 'Convert the current block to a quote',\n action: { type: 'convertBlock', blockType: 'blockquote', props: {} },\n },\n {\n phrases: ['code block', 'code'],\n description: 'Convert the current block to a code block',\n action: { type: 'convertBlock', blockType: 'code', props: { language: 'plaintext' } },\n },\n { phrases: ['undo'], description: 'Undo the last edit', action: { type: 'undo' } },\n { phrases: ['redo'], description: 'Redo the last undone edit', action: { type: 'redo' } },\n {\n phrases: ['stop dictation', 'stop listening', 'stop'],\n description: 'Stop dictation',\n action: { type: 'stopDictation' },\n },\n];\n\nconst PHRASE_TO_ACTION = new Map();\nfor (const { phrases, action } of COMMANDS) {\n for (const phrase of phrases) PHRASE_TO_ACTION.set(phrase, action);\n}\n\n/**\n * Lowercases, trims, collapses internal whitespace, and strips trailing\n * sentence punctuation a speech engine commonly appends to a finalized\n * utterance (\".\", \",\", \"!\", \"?\") — none of that punctuation is meaningful\n * for matching a short command phrase.\n */\nexport function normalizeUtterance(text) {\n return (text ?? '')\n .toLowerCase()\n .trim()\n .replace(/\\s+/g, ' ')\n .replace(/[.,!?]+$/, '');\n}\n\n/**\n * Returns the matching command action for a normalized, complete utterance,\n * or null if it's plain dictation (no command matched). Exact-match only —\n * deliberately not a substring/contains check, so a sentence that merely\n * *mentions* a command phrase while dictating prose is never misread as a\n * command (matches only when the whole finalized utterance IS the phrase).\n */\nexport function matchVoiceCommand(text) {\n return PHRASE_TO_ACTION.get(normalizeUtterance(text)) ?? null;\n}\n\n/**\n * Read-only `[{ phrases, description }]` list for a host to render as an\n * in-app \"what can I say\" help list — pure data, no rendering opinion (see\n * examples/basic's own \"Voice commands\" button for one way to display it).\n */\nexport function listVoiceCommands() {\n return COMMANDS.map(({ phrases, description }) => ({ phrases: [...phrases], description }));\n}\n","import { convertBlockType } from '../blocks/shared/convertBlockType.js';\nimport { insertSiblingAfterAndFocus } from '../blocks/shared/blockCommands.js';\nimport { createTextLeafBlock } from '../blocks/shared/leafBlockFactory.js';\nimport { focusRunStart } from '../react/focusRun.js';\nimport { restoreSelectionAfterHistoryChange } from '../react/restoreHistorySelection.js';\n\nfunction applyOps(store, ops) {\n if (typeof store.performBatch === 'function') store.performBatch(ops);\n else for (const op of ops) store.applyOperation(op);\n}\n\nfunction getOwnRunIds(block) {\n return block?.props?.titleRunIds ?? block?.contentIds ?? [];\n}\n\n/**\n * Executes one matched voice command (see voiceCommands.js) against the\n * store. `blockId` is wherever the caret currently is — resolved by the\n * caller (useVoiceTyping) via resolveCollapsedCaret, the same way every\n * other command in this codebase locates \"the current block\".\n *\n * 'convertBlock' reuses convertBlockType directly (not the slash-command\n * factory path) specifically so any text already dictated into the\n * current block survives the conversion — the same reason\n * markdownShortcuts.js uses it, and doubly important here since a live\n * dictation session is far more likely to have real typed content in the\n * block at the moment a structural command is spoken.\n */\nexport function applyVoiceAction(store, blockId, action) {\n if (action.type === 'insertParagraph') {\n insertSiblingAfterAndFocus(store, blockId, createTextLeafBlock('paragraph'));\n return;\n }\n\n if (action.type === 'convertBlock') {\n const block = store.getBlock(blockId);\n if (!block) return;\n const runIds = getOwnRunIds(block);\n const { ops } = convertBlockType(store, blockId, action.blockType, action.props, runIds);\n applyOps(store, ops);\n if (runIds[0]) focusRunStart(runIds[0]);\n return;\n }\n\n if (action.type === 'undo') {\n if (store.undo?.()) restoreSelectionAfterHistoryChange(store);\n return;\n }\n\n if (action.type === 'redo') {\n if (store.redo?.()) restoreSelectionAfterHistoryChange(store);\n }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { flushSync } from 'react-dom';\nimport { useEditorStore } from './EditorProvider.jsx';\nimport { resolveCollapsedCaret } from './selectionResolve.js';\nimport { setCaretSync } from './focusRun.js';\nimport { updateRun } from '../store/operations.js';\nimport { matchVoiceCommand } from '../voice/voiceCommands.js';\nimport { applyVoiceAction } from '../voice/applyVoiceAction.js';\n\nfunction getSpeechRecognitionCtor() {\n if (typeof window === 'undefined') return null;\n return window.SpeechRecognition ?? window.webkitSpeechRecognition ?? null;\n}\n\nconst PERMISSION_DENIED_MESSAGE =\n \"Microphone access is blocked. Enable it in your browser's site settings, then try again.\";\n\n/**\n * Native-browser (Web Speech API) voice typing: live, continuous dictation\n * into the current caret position (text appears and self-corrects as you\n * speak, not just after each pause), plus spoken structural commands\n * (\"heading one\", \"new paragraph\", \"undo\", \"stop dictation\", ...)\n * recognized via voiceCommands.js — see that file for the full command\n * table and matching rules (also exported there as listVoiceCommands() for\n * an in-app \"what can I say\" help list).\n *\n * Deliberately built on the browser's own SpeechRecognition rather than a\n * bundled speech-to-text SDK: real speech recognition needs either a\n * network round-trip to a vendor's servers or a genuinely heavy on-device\n * model, either of which conflicts with this package staying zero-runtime-\n * dependency. `isSupported` is false (start()/stop() are no-ops) wherever\n * the API doesn't exist (Firefox, most notably) — a host app decides how\n * to surface that (hide the mic button, show a note, etc.), this hook just\n * degrades safely rather than throwing.\n *\n * ---- Never trust SpeechRecognition's resultIndex for phrase identity —\n * this is the part two earlier versions of this hook got wrong ----\n *\n * `sessionAnchorRef` (`{ runId, blockId, offset }`) is this hook's own,\n * single source of truth for \"where does the next chunk of dictated text\n * go\" — resolved from the real caret when `start()` is called, and after\n * that updated only by this hook's own writes OR by the `selectionchange`\n * listener below noticing the user manually moved the caret somewhere else\n * (click, arrow keys, Tab into another block) — it is never blindly\n * re-read from `window.getSelection()` on some other trigger (e.g. every\n * `onresult` event), which is what the two broken attempts below actually\n * ran into.\n *\n * `liveEntryRef` is the *one* mutable \"currently being revised, not yet\n * final\" text buffer — on every `onresult` event, every non-final result\n * in that event is concatenated into one string and used to *fully\n * replace* this single buffer's contents, regardless of which\n * `resultIndex`(es) the browser happened to report them under.\n * `committedCountRef` is a separate, monotonically-increasing \"how many\n * result indices have I already permanently committed\" counter, advanced\n * only when *this hook* finalizes something — used purely as a \"don't\n * reprocess an index I already committed\" guard, never for matching a new\n * event's index back to an existing in-progress phrase.\n *\n * This design replaced two earlier, both-broken attempts at using\n * `resultIndex` as a phrase identifier:\n * 1. Tracking a `Map` of every \"in-flight\" phrase keyed by resultIndex,\n * re-resolving the live caret each time a new index appeared. Real\n * Chrome doesn't keep resultIndex behaving cleanly enough for that —\n * a later index could get matched against a stale map entry, and\n * text landed out of order (\"11234567 89 10 11 ...\" for numbers\n * spoken in order).\n * 2. Comparing consecutive events' resultIndex to decide \"is this a\n * revision of the same phrase, or a new one\" (one phrase at a time,\n * no Map). Real Chrome can advance resultIndex on what is still\n * conceptually the same in-progress phrase, which made this treat\n * every revision as a brand-new phrase, anchored after the\n * previous (wrongly-separate) one's text — producing runaway,\n * compounding duplication (\"test for the\" -> \"test for the life\" ->\n * \"test for the life desc\" -> ... each revision re-typing the whole\n * growing sentence from scratch instead of replacing the last guess).\n *\n * Concatenate-all-non-final-into-one-buffer is the standard pattern real\n * speech-to-text integrations use (e.g. how the `react-speech-recognition`\n * library and most Web Speech API tutorials build up \"interim transcript\"\n * + \"final transcript\" separately) — it needs no assumption whatsoever\n * about how the browser indexes results across events.\n *\n * Every write still goes through `flushSync` (react-dom) before\n * `setCaretSync` (the non-rAF variant of focusRunAtOffset, see\n * focusRun.js) moves the *visible* caret to match — `TextRunSpan`\n * (EditableBlockContent.jsx) fully destroys and recreates the run's DOM\n * text node on every value change, which resets the browser's native\n * Selection, and a plain `store.applyOperation(...)` call is not\n * guaranteed to have committed that DOM change by the time it returns\n * (React may batch it) — `flushSync` forces the commit first. This part\n * only affects what the user visually sees the caret doing; the actual\n * text-insertion math never depends on it, since `sessionAnchorRef` is the\n * authority, not the DOM.\n *\n * Text is inserted/updated plain, with no \"tentative\" styling while\n * interim — it just grows and occasionally self-corrects as the engine\n * hears more context, the same way Google's own voice typing behaves.\n *\n * Command detection vs. plain dictation: only a *finalized* segment is\n * ever checked against the command table (matchVoiceCommand) — natural\n * pauses in speech are what the Speech API itself uses to decide a segment\n * is \"final\", so a command spoken as its own isolated utterance is\n * detected reliably, while the same words embedded in a longer dictated\n * sentence never match (the whole segment has to equal a known phrase,\n * not just contain one). If a phrase that was already live-inserted as\n * interim text finalizes as a command, the interim text is reverted\n * (removed) before the command runs — so e.g. saying \"heading one\" never\n * leaves \"heading one\" sitting in the paragraph before converting it, even\n * though \"heading\" alone may have briefly appeared while still mid-\n * utterance.\n *\n * `status` (`'idle' | 'listening' | 'processing'`) is driven by the Speech\n * API's own `onspeechstart`/`onspeechend` events, not a fabricated timer:\n * `onspeechend` fires when the engine detects you've stopped talking and is\n * about to finalize what it heard ('processing'), and the next committed\n * final result (or a fresh `onspeechstart`) returns to 'listening'. Neither\n * event is guaranteed on every engine — if they never fire, status simply\n * stays 'listening' for the whole session, a safe degrade, not a crash.\n * `VoiceListeningIndicator` (also exported) renders a small badge that\n * follows the live caret off this state.\n *\n * Mic permission: `recognition.onerror` reporting 'not-allowed' or\n * 'service-not-allowed' is the one cross-browser-reliable signal that\n * permission was denied (the Permissions API's 'microphone' descriptor\n * isn't supported in Firefox at all, and is inconsistent in Safari) — that\n * error sets `permissionState: 'denied'`. Where the Permissions API *is*\n * available, it's also queried opportunistically on mount (and its own\n * `change` event subscribed to) purely so a previously-denied permission\n * shows up in `permissionState` before the user even presses the mic\n * button, rather than only ever finding out after a failed start().\n * `VoicePermissionModal` (also exported) renders itself automatically off\n * this same state.\n *\n * `options.shortcut` (default true) wires one native, document-level\n * Ctrl/Cmd+Shift+M keydown listener that toggles start()/stop() — pass\n * `{ shortcut: false }` to opt out if a host wants its own binding.\n *\n * `options.engine` lets a host app supply its own recognition engine\n * instead of relying on `window.SpeechRecognition`/`webkitSpeechRecognition`\n * — needed because that API is commonly absent or non-functional in\n * embedded webviews (e.g. Tauri's WebView2/WKWebView), which have no Web\n * Speech API implementation, or a Chromium-derived one that's missing the\n * private Google API key real Chrome ships with (constructor exists, but\n * every session fails immediately with a `'network'` error). `engine` is a\n * zero-arg factory, `() => SpeechRecognitionLike`, called once per `start()`\n * to produce a fresh instance implementing the same surface this hook\n * already drives on the native constructor:\n * - properties: `continuous`, `interimResults` (set by this hook before\n * starting; the engine should honor them)\n * - event handlers this hook assigns: `onresult`, `onstart`,\n * `onspeechstart`, `onspeechend`, `onerror`, `onend`\n * - methods: `start()`, `stop()`\n * - `onresult`'s event shape: `{ resultIndex, results }` where `results`\n * is array-like of `results[i][0].transcript` (string) and\n * `results[i].isFinal` (boolean) — exactly the native SpeechRecognition\n * result shape, since `handleRecognitionResult` above reads it that way\n * regardless of what produced it (native browser engine, a native/OS\n * bridge, or a cloud STT service streamed back into this shape).\n * - `onerror`'s event shape: `{ error: string }` — pass `'not-allowed'` or\n * `'service-not-allowed'` for a permission denial (drives\n * `permissionState`/`VoicePermissionModal`), anything else is surfaced\n * as-is via `error`.\n * When `engine` is provided, `isSupported` is unconditionally `true` (the\n * host has already decided its engine is usable) — the browser-detection\n * path (`getSpeechRecognitionCtor`) is only consulted when `engine` is\n * omitted, so existing callers are unaffected.\n */\nexport function useVoiceTyping({ shortcut = true, engine } = {}) {\n const store = useEditorStore();\n const [isListening, setIsListening] = useState(false);\n const [status, setStatus] = useState('idle');\n const [permissionState, setPermissionState] = useState('unknown');\n const [error, setError] = useState(null);\n const recognitionRef = useRef(null);\n // { runId, blockId, offset } | null — this hook's single, internally-\n // owned source of truth for \"where does dictation write next.\" See the\n // class comment for why this is never re-read from the live DOM\n // mid-session EXCEPT via the selectionchange listener below, which only\n // reacts to a caret move this hook didn't itself just cause.\n const sessionAnchorRef = useRef(null);\n // { runId, offset } | null — wherever this hook itself most recently\n // placed the caret (via setCaretSync in writeEntryText/revertEntryText).\n // The selectionchange listener compares against this to tell \"the user\n // just clicked/arrow-keyed somewhere else\" apart from \"the selection\n // changed because WE just rewrote this run's text and moved the caret to\n // match\" — without it, every dictated word would look indistinguishable\n // from a manual caret move and constantly re-anchor to nowhere useful.\n const expectedCaretRef = useRef(null);\n // The one, single \"still being revised\" text buffer — replaced wholesale\n // on every onresult event, never matched against a prior event by index.\n // { runId, blockId, startOffset, insertedLength, needsLeadingSpace } | null\n const liveEntryRef = useRef(null);\n // How many result indices this hook has already permanently committed —\n // only ever advanced by this hook itself; used solely to avoid\n // reprocessing an index already committed, never to identify a phrase.\n const committedCountRef = useRef(0);\n const isSupported = engine != null || getSpeechRecognitionCtor() !== null;\n\n useEffect(() => {\n if (!navigator.permissions?.query) return undefined;\n let permissionStatus;\n let cancelled = false;\n navigator.permissions\n .query({ name: 'microphone' })\n .then((result) => {\n if (cancelled) return;\n permissionStatus = result;\n setPermissionState(result.state);\n permissionStatus.onchange = () => setPermissionState(permissionStatus.state);\n })\n .catch(() => {}); // unsupported descriptor name (e.g. Safari) — fall back to onerror-only detection\n return () => {\n cancelled = true;\n if (permissionStatus) permissionStatus.onchange = null;\n };\n }, []);\n\n const createEntryAtAnchor = useCallback(() => {\n if (!sessionAnchorRef.current) {\n const caret = resolveCollapsedCaret();\n if (!caret) return null;\n sessionAnchorRef.current = caret;\n }\n return {\n runId: sessionAnchorRef.current.runId,\n blockId: sessionAnchorRef.current.blockId,\n startOffset: sessionAnchorRef.current.offset,\n insertedLength: 0,\n };\n }, []);\n\n const writeEntryText = useCallback(\n (entry, text) => {\n const run = store.getRun(entry.runId);\n if (!run || run.type !== 'text') return;\n const value = run.value ?? '';\n const before = value.slice(0, entry.startOffset);\n // The leading-space decision is made once, against whatever sat\n // right before the anchor when this phrase began, and reused for\n // every revision of this same phrase — recomputing it against the\n // phrase's *own* still-growing text on each revision would flip it\n // back and forth as that text changes shape.\n if (entry.needsLeadingSpace === undefined) {\n const beforeChar = before[before.length - 1];\n entry.needsLeadingSpace =\n entry.startOffset > 0 && beforeChar !== undefined && !/\\s/.test(beforeChar);\n }\n const insertedText = (entry.needsLeadingSpace ? ' ' : '') + text;\n const after = value.slice(entry.startOffset + entry.insertedLength);\n // flushSync forces React to synchronously commit this store write\n // (including TextRunSpan's DOM-rewriting layout effect) before it\n // returns, so setCaretSync right after is guaranteed to see the\n // freshly-rewritten text node rather than a stale one.\n flushSync(() =>\n store.applyOperation(updateRun(entry.runId, { value: before + insertedText + after })),\n );\n entry.insertedLength = insertedText.length;\n const nextOffset = entry.startOffset + insertedText.length;\n setCaretSync(entry.runId, nextOffset);\n sessionAnchorRef.current = { runId: entry.runId, blockId: entry.blockId, offset: nextOffset };\n expectedCaretRef.current = { runId: entry.runId, offset: nextOffset };\n },\n [store],\n );\n\n const revertEntryText = useCallback(\n (entry) => {\n const run = store.getRun(entry.runId);\n if (!run || run.type !== 'text') return;\n const value = run.value ?? '';\n const restored =\n value.slice(0, entry.startOffset) + value.slice(entry.startOffset + entry.insertedLength);\n flushSync(() => store.applyOperation(updateRun(entry.runId, { value: restored })));\n setCaretSync(entry.runId, entry.startOffset);\n sessionAnchorRef.current = {\n runId: entry.runId,\n blockId: entry.blockId,\n offset: entry.startOffset,\n };\n expectedCaretRef.current = { runId: entry.runId, offset: entry.startOffset };\n },\n [store],\n );\n\n /**\n * Same text removal as `revertEntryText`, but WITHOUT touching the real\n * caret/selection — used when the user has just manually moved focus\n * away from `entry`'s own block (see the selectionchange listener below),\n * where `setCaretSync`ing back to it would fight the move the user just\n * made. Leaving the interim guess in place instead (an earlier version of\n * this fix did exactly that) caused a duplication bug: the NEXT interim\n * update for the same still-in-progress utterance writes the full\n * accumulated transcript again at the new anchor, since SpeechRecognition\n * interim results are always the whole phrase-so-far, not just the new\n * part — so the old block was left with a stale partial guess and the new\n * block got the same words all over again.\n */\n const removeEntryTextSilently = useCallback(\n (entry) => {\n const run = store.getRun(entry.runId);\n if (!run || run.type !== 'text') return;\n const value = run.value ?? '';\n const restored =\n value.slice(0, entry.startOffset) + value.slice(entry.startOffset + entry.insertedLength);\n store.applyOperation(updateRun(entry.runId, { value: restored }));\n },\n [store],\n );\n\n const handleRecognitionResult = useCallback(\n (event) => {\n let interimText = '';\n let hasInterim = false;\n const start = Math.max(event.resultIndex, committedCountRef.current);\n\n for (let i = start; i < event.results.length; i += 1) {\n const result = event.results[i];\n const transcript = result[0]?.transcript ?? '';\n\n if (!result.isFinal) {\n interimText += (interimText ? ' ' : '') + transcript;\n hasInterim = true;\n continue;\n }\n\n // Finalizing: reuse the live buffer if one exists (the common\n // case — this text was almost certainly interim a moment ago),\n // otherwise create a fresh one (a short utterance can finalize\n // with no prior interim event at all).\n const entry = liveEntryRef.current ?? createEntryAtAnchor();\n liveEntryRef.current = null;\n committedCountRef.current = i + 1;\n if (!entry) continue; // nowhere to anchor — drop this one\n\n const command = matchVoiceCommand(transcript);\n if (command) {\n revertEntryText(entry); // the interim guess was never real content\n if (command.type === 'stopDictation') {\n recognitionRef.current?.stop();\n } else {\n applyVoiceAction(store, entry.blockId, command);\n // The command may have moved focus into a different block/run\n // (e.g. a new paragraph) via this codebase's usual rAF-deferred\n // focus helpers — force the next phrase to re-resolve the live\n // caret rather than trusting a position that's now stale.\n // expectedCaretRef is cleared too, so the selectionchange\n // listener below doesn't mistake the command's own (rAF-\n // deferred, so not yet reflected here) focus move for a manual\n // one and race it.\n sessionAnchorRef.current = null;\n expectedCaretRef.current = null;\n setStatus('listening');\n }\n } else {\n writeEntryText(entry, transcript);\n setStatus('listening');\n }\n }\n\n if (hasInterim) {\n const entry = liveEntryRef.current ?? createEntryAtAnchor();\n if (entry) {\n liveEntryRef.current = entry;\n writeEntryText(entry, interimText);\n }\n }\n },\n [store, createEntryAtAnchor, writeEntryText, revertEntryText],\n );\n\n // Detects the user manually moving the caret (click, arrow keys, Tab into\n // another block, ...) WHILE dictation is listening, and redirects the\n // next dictated phrase there instead of silently continuing to write at\n // the stale session anchor — this is the fix for a real bug: without it,\n // clicking into a different block mid-dictation had no effect on where\n // the next phrase landed, and `setCaretSync` would keep snapping the\n // visible caret back to the old spot on every subsequent word, making it\n // look like focus was being fought over.\n //\n // Only reacts when the resolved caret differs from `expectedCaretRef` —\n // this hook's own record of wherever IT most recently placed the caret —\n // so it never mistakes its own writes (which also move the real DOM\n // selection) for a manual move. `expectedCaretRef` being null (right\n // after a voice command moved focus, before the next phrase has\n // re-resolved yet) means there's no baseline to compare against yet, so\n // the listener stays passive rather than racing that in-flight move.\n useEffect(() => {\n if (!isListening) return undefined;\n const handleSelectionChange = () => {\n const expected = expectedCaretRef.current;\n if (!expected) return;\n const caret = resolveCollapsedCaret();\n if (!caret) return; // selection isn't a collapsed caret inside a run right now — nothing to redirect to\n if (caret.runId === expected.runId && caret.offset === expected.offset) return; // our own write\n if (liveEntryRef.current) {\n // Discard the not-yet-finalized guess left behind in the old block —\n // see removeEntryTextSilently's own doc comment for why leaving it\n // in place duplicated text instead.\n removeEntryTextSilently(liveEntryRef.current);\n liveEntryRef.current = null;\n }\n sessionAnchorRef.current = caret;\n expectedCaretRef.current = { runId: caret.runId, offset: caret.offset };\n };\n document.addEventListener('selectionchange', handleSelectionChange);\n return () => document.removeEventListener('selectionchange', handleSelectionChange);\n }, [isListening, removeEntryTextSilently]);\n\n const start = useCallback(() => {\n if (!isSupported || recognitionRef.current) return;\n setError(null);\n const initialCaret = resolveCollapsedCaret();\n sessionAnchorRef.current = initialCaret;\n expectedCaretRef.current = initialCaret\n ? { runId: initialCaret.runId, offset: initialCaret.offset }\n : null;\n liveEntryRef.current = null;\n committedCountRef.current = 0;\n let recognition;\n if (engine) {\n recognition = engine();\n } else {\n const Ctor = getSpeechRecognitionCtor();\n recognition = new Ctor();\n }\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.onresult = handleRecognitionResult;\n recognition.onstart = () => setStatus('listening');\n recognition.onspeechstart = () => setStatus('listening');\n recognition.onspeechend = () => setStatus('processing');\n recognition.onerror = (event) => {\n if (event.error === 'not-allowed' || event.error === 'service-not-allowed') {\n setPermissionState('denied');\n setError(PERMISSION_DENIED_MESSAGE);\n } else {\n setError(event.error ?? 'Voice typing stopped unexpectedly.');\n }\n };\n recognition.onend = () => {\n recognitionRef.current = null;\n sessionAnchorRef.current = null;\n expectedCaretRef.current = null;\n liveEntryRef.current = null;\n committedCountRef.current = 0;\n setIsListening(false);\n setStatus('idle');\n };\n recognitionRef.current = recognition;\n recognition.start();\n setIsListening(true);\n }, [isSupported, handleRecognitionResult, engine]);\n\n const stop = useCallback(() => {\n recognitionRef.current?.stop();\n }, []);\n\n useEffect(() => () => recognitionRef.current?.stop(), []);\n\n useEffect(() => {\n if (!shortcut || !isSupported) return undefined;\n const handleKeyDown = (event) => {\n const mod = event.metaKey || event.ctrlKey;\n if (!mod || !event.shiftKey || event.key.toLowerCase() !== 'm') return;\n event.preventDefault();\n if (recognitionRef.current) stop();\n else start();\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [shortcut, isSupported, start, stop]);\n\n return { isSupported, isListening, status, permissionState, error, start, stop };\n}\n","import { useEffect, useState } from 'react';\nimport { Modal } from './Modal.jsx';\n\n/**\n * Ready-made \"microphone access is blocked\" dialog for useVoiceTyping —\n * mount it once anywhere alongside a mic button: `<VoicePermissionModal\n * voice={voice} />`. Driven by `voice.permissionState`, so there's no\n * separate isOpen prop to wire — it opens itself the instant permission is\n * detected as denied (whether that's from a failed start() attempt or the\n * opportunistic Permissions API check useVoiceTyping does on mount).\n *\n * Manually dismissible (Escape/backdrop click, via Modal's own built-in\n * handling) rather than forced to stay open until permission is fixed —\n * `dismissed` tracks that locally and resets the instant a *new* denial\n * comes in (voice.error changing is what \"a new denial just happened\"\n * means here, since start() always clears error first) so the dialog\n * reappears on the next real attempt even after being dismissed once,\n * rather than staying silently suppressed forever.\n */\nexport function VoicePermissionModal({ voice }) {\n const [dismissed, setDismissed] = useState(false);\n\n useEffect(() => {\n setDismissed(false);\n }, [voice?.error]);\n\n const isOpen = voice?.permissionState === 'denied' && !dismissed;\n\n return (\n <Modal isOpen={isOpen} onClose={() => setDismissed(true)} title=\"Microphone access blocked\">\n <p>\n {voice?.error ||\n \"Microphone access is blocked. Enable it in your browser's site settings, then try again.\"}\n </p>\n <div className=\"be-modal-actions\">\n <button type=\"button\" className=\"be-modal-cancel\" onClick={() => setDismissed(true)}>\n Dismiss\n </button>\n <button type=\"button\" className=\"be-modal-save\" onClick={() => voice?.start?.()}>\n Try again\n </button>\n </div>\n </Modal>\n );\n}\n","import { useEffect, useState } from 'react';\n\n/**\n * Tracks the live collapsed caret's bounding rect, recomputed via the same\n * event set `useFloatingToolbarTrigger` uses for its own (non-collapsed)\n * selection tracking — `document`'s `selectionchange`, plus `window`\n * scroll (capture phase)/resize, since a `position: fixed` box anchored to\n * a rect needs to stay correct across page scroll. `selectionchange` fires\n * even for *programmatic* `Selection.addRange()` calls, which is what\n * `setCaretSync` (focusRun.js) already does on every voice-dictation text\n * insertion — so this reliably follows the caret while dictating without\n * useVoiceTyping needing to know anything about this hook at all.\n *\n * Only active while `isActive` is true (pass `voice.isListening`) — a\n * no-op with zero listeners attached otherwise, so this costs nothing when\n * not dictating. Returns `null` whenever there's no collapsed caret inside\n * an editable run (`[data-run-id]`), rather than a stale/wrong position.\n */\nexport function useCaretRect(isActive) {\n const [rect, setRect] = useState(null);\n\n useEffect(() => {\n if (!isActive) {\n setRect(null);\n return undefined;\n }\n\n const recompute = () => {\n const selection = window.getSelection?.();\n if (!selection || selection.rangeCount === 0 || !selection.isCollapsed) {\n setRect(null);\n return;\n }\n const node = selection.anchorNode;\n const el = node?.nodeType === 1 ? node : node?.parentElement;\n if (!el?.closest?.('[data-run-id]')) {\n setRect(null);\n return;\n }\n\n // jsdom (this package's own test suite) implements\n // Element.prototype.getBoundingClientRect but not\n // Range.prototype.getBoundingClientRect — falling back to a zero\n // rect keeps this testable without a real layout engine, same\n // convention as useFloatingToolbarTrigger.\n const nextRect = selection.getRangeAt(0).getBoundingClientRect?.() ?? {\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: 0,\n height: 0,\n };\n setRect(nextRect);\n };\n\n recompute();\n document.addEventListener('selectionchange', recompute);\n window.addEventListener('scroll', recompute, true);\n window.addEventListener('resize', recompute);\n return () => {\n document.removeEventListener('selectionchange', recompute);\n window.removeEventListener('scroll', recompute, true);\n window.removeEventListener('resize', recompute);\n };\n }, [isActive]);\n\n return rect;\n}\n","import { useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useCaretRect } from './useCaretRect.js';\nimport { useAutoAdjustedCenteredLeft } from './usePopoverEdgeClamp.js';\nimport { MicIcon } from './icons.jsx';\n\n/**\n * Ready-made \"voice typing is actively working\" indicator for\n * useVoiceTyping — mount it once anywhere: `<VoiceListeningIndicator\n * voice={voice} />`. A toolbar-only status label is easy to miss since\n * attention is on the text being dictated, not the toolbar, so this\n * renders a small pulsing badge anchored right above the live caret\n * instead — the same `getBoundingClientRect()`-off-a-Range positioning\n * `FloatingToolbar`/`LinkHoverCard` already use elsewhere in this\n * codebase (see useCaretRect.js), so it visibly follows the caret as\n * dictated text streams in and moves it.\n *\n * Renders nothing when not listening, or when there's currently no\n * resolvable caret position (useCaretRect returns null) — never shows a\n * stale/wrong-position badge.\n */\nexport function VoiceListeningIndicator({ voice }) {\n const isActive = Boolean(voice?.isListening);\n const rect = useCaretRect(isActive);\n const badgeRef = useRef(null);\n // Only the horizontal axis is clamped — the badge sits right above the\n // caret (`translateY(-100%)`), which stays within the viewport in\n // practice since the caret itself must be visible to type into; the\n // centered horizontal offset is the one that can push it off-screen near\n // the left/right edge of a narrow viewport.\n const centerLeft = useAutoAdjustedCenteredLeft(badgeRef, Boolean(isActive && rect), rect?.left);\n\n if (!isActive || !rect || centerLeft == null) return null;\n\n const isProcessing = voice.status === 'processing';\n\n return createPortal(\n <div\n ref={badgeRef}\n className={`be-voice-indicator${isProcessing ? ' be-voice-indicator-processing' : ''}`}\n contentEditable={false}\n style={{\n position: 'fixed',\n top: rect.top - 8,\n left: centerLeft,\n transform: 'translate(-50%, -100%)',\n }}\n >\n <span className=\"be-voice-indicator-dot\" />\n <MicIcon size={12} />\n <span className=\"be-voice-indicator-label\">\n {isProcessing ? 'Processing…' : 'Listening…'}\n </span>\n </div>,\n document.body,\n );\n}\n"],"names":["restoreSelectionAfterHistoryChange","store","selection","_a","focusRunAtOffset","targetBlockId","_b","id","focusBlockStart","COMMANDS","PHRASE_TO_ACTION","phrases","action","phrase","normalizeUtterance","text","matchVoiceCommand","listVoiceCommands","description","applyOps","ops","op","getOwnRunIds","block","applyVoiceAction","blockId","insertSiblingAfterAndFocus","createTextLeafBlock","runIds","convertBlockType","focusRunStart","getSpeechRecognitionCtor","PERMISSION_DENIED_MESSAGE","useVoiceTyping","shortcut","engine","useEditorStore","isListening","setIsListening","useState","status","setStatus","permissionState","setPermissionState","error","setError","recognitionRef","useRef","sessionAnchorRef","expectedCaretRef","liveEntryRef","committedCountRef","isSupported","useEffect","permissionStatus","cancelled","result","createEntryAtAnchor","useCallback","caret","resolveCollapsedCaret","writeEntryText","entry","run","value","before","beforeChar","insertedText","after","flushSync","updateRun","nextOffset","setCaretSync","revertEntryText","restored","removeEntryTextSilently","handleRecognitionResult","event","interimText","hasInterim","start","i","transcript","command","handleSelectionChange","expected","initialCaret","recognition","Ctor","stop","handleKeyDown","VoicePermissionModal","voice","dismissed","setDismissed","isOpen","jsxs","Modal","jsx","useCaretRect","isActive","rect","setRect","recompute","node","el","nextRect","_d","_c","VoiceListeningIndicator","badgeRef","centerLeft","useAutoAdjustedCenteredLeft","isProcessing","createPortal","MicIcon"],"mappings":"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"}
|