foldkit-richtext-dom 0.9.0 → 0.10.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 +41 -2
- package/dist/editor-bundle.d.mts.map +1 -1
- package/dist/editor-bundle.mjs +57 -26
- package/dist/editor-bundle.mjs.map +1 -1
- package/dist/input.d.mts.map +1 -1
- package/dist/input.mjs +6 -2
- package/dist/input.mjs.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -185,11 +185,17 @@ the `transactions` the edit applied, which is what `RichText.Replicated.translat
|
|
|
185
185
|
`foldkit-richtext-dom/input` puts the editor in a `foldkit-form` form: a key whose value is a
|
|
186
186
|
`RichText.Document`. It is the one entry that imports `foldkit-form`, an optional peer.
|
|
187
187
|
|
|
188
|
+
<!-- docs-check
|
|
189
|
+
import { Schema } from 'effect'
|
|
190
|
+
import { Entity } from 'foldkit-entity'
|
|
191
|
+
import * as RichText from 'foldkit-richtext'
|
|
192
|
+
const Post = Entity.define('Post', Schema.Struct({ id: Schema.String, body: RichText.Document }))
|
|
193
|
+
const PostInput = Entity.input(Post, Schema.Struct({ body: Post.fields.body.schema }))
|
|
194
|
+
-->
|
|
188
195
|
```ts
|
|
189
196
|
import { Form } from 'foldkit-form'
|
|
190
197
|
import { richTextInput } from 'foldkit-richtext-dom/input'
|
|
191
198
|
|
|
192
|
-
// PostInput is an `Entity.input(...)` whose `body` field is a `RichText.Document`.
|
|
193
199
|
const PostForm = Form.make('PostForm', PostInput, {
|
|
194
200
|
inputs: { body: richTextInput('post-body', { placeholder: 'Write the post…' }) },
|
|
195
201
|
})
|
|
@@ -232,6 +238,18 @@ buttons.
|
|
|
232
238
|
|
|
233
239
|
The same buttons, in an element `selectionAnchor` places over the selection:
|
|
234
240
|
|
|
241
|
+
<!-- docs-check standalone
|
|
242
|
+
import type { HtmlBuilder } from 'foldkit/html'
|
|
243
|
+
import type * as RichText from 'foldkit-richtext'
|
|
244
|
+
import { Message } from 'foldkit-richtext-dom/editor'
|
|
245
|
+
import type { ToolbarState } from 'foldkit-richtext-dom/toolbar'
|
|
246
|
+
declare const model: {
|
|
247
|
+
readonly editor: Omit<ToolbarState, 'document'>
|
|
248
|
+
readonly document: RichText.Document
|
|
249
|
+
}
|
|
250
|
+
declare const h: HtmlBuilder<Message>
|
|
251
|
+
declare const edited: (message: Message) => Message
|
|
252
|
+
-->
|
|
235
253
|
```ts
|
|
236
254
|
import * as RichText from 'foldkit-richtext'
|
|
237
255
|
import { marksToolbar, selectionAnchor } from 'foldkit-richtext-dom/toolbar'
|
|
@@ -339,7 +357,16 @@ them.
|
|
|
339
357
|
it dispatches nothing and owns no DOM, so it serves a visitor's page, a preview, or an email as
|
|
340
358
|
it is.
|
|
341
359
|
|
|
360
|
+
<!-- docs-check standalone
|
|
361
|
+
import * as RichText from 'foldkit-richtext'
|
|
362
|
+
declare const document: RichText.Document
|
|
363
|
+
declare const slice: { readonly blocks: ReadonlyArray<RichText.Block> }
|
|
364
|
+
declare const renderer: RichText.Rendering
|
|
365
|
+
declare const decorations: RichText.DecorationSet
|
|
366
|
+
-->
|
|
342
367
|
```ts
|
|
368
|
+
import { renderBlocks, renderDocument } from 'foldkit-richtext-dom/view'
|
|
369
|
+
|
|
343
370
|
const html = renderDocument(document) // a div of block elements
|
|
344
371
|
renderBlocks(slice.blocks) // one element per block
|
|
345
372
|
renderDocument(document, renderer) // a declared Link as a real <a href>
|
|
@@ -376,6 +403,8 @@ mounts, and the editor adopts it rather than drawing the document again. The ser
|
|
|
376
403
|
when the placement says the render is the server's:
|
|
377
404
|
|
|
378
405
|
```ts
|
|
406
|
+
import * as RichText from 'foldkit-richtext'
|
|
407
|
+
import { editorAt } from 'foldkit-richtext-dom/editor-bundle'
|
|
379
408
|
import { SSR } from 'foldkit-ssr'
|
|
380
409
|
|
|
381
410
|
editorAt('body', { rendering: RichText.standardRendering, serverRendered: SSR.serving })
|
|
@@ -437,7 +466,7 @@ Message.
|
|
|
437
466
|
Everything above is built from these. Reach for them to embed the editable subtree without
|
|
438
467
|
the Bundle, or to write another host.
|
|
439
468
|
|
|
440
|
-
```ts
|
|
469
|
+
```ts sketch
|
|
441
470
|
import { mount } from 'foldkit-richtext-dom'
|
|
442
471
|
import { attach } from 'foldkit-richtext-dom/events'
|
|
443
472
|
import * as RichText from 'foldkit-richtext'
|
|
@@ -513,6 +542,16 @@ A Foldkit view owns the host element; the interpreter owns what goes inside it.
|
|
|
513
542
|
renders into the host and records the attachment there, so a patch Command that has only the
|
|
514
543
|
element can find it:
|
|
515
544
|
|
|
545
|
+
<!-- docs-check standalone
|
|
546
|
+
import type * as RichText from 'foldkit-richtext'
|
|
547
|
+
declare const host: HTMLElement
|
|
548
|
+
declare const content: RichText.Document
|
|
549
|
+
declare const onIntent: (command: RichText.Command) => void
|
|
550
|
+
declare const onSelection: (selection: RichText.Selection | null) => void
|
|
551
|
+
declare const renderer: RichText.Rendering
|
|
552
|
+
declare const state: RichText.EditorState
|
|
553
|
+
declare const changeSet: RichText.ChangeSet
|
|
554
|
+
-->
|
|
516
555
|
```ts
|
|
517
556
|
import { attachmentIn, mountInto, releaseMount } from 'foldkit-richtext-dom/host'
|
|
518
557
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-bundle.d.mts","names":[],"sources":["../src/editor-bundle.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"editor-bundle.d.mts","names":[],"sources":["../src/editor-bundle.ts"],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAoOV,QAAM,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCjB;qBAIW,yCAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YACd,uBAAuB,cAAc;;;;;iBAuChC;;WAEN,YAAY,SAAS;;WAErB,aAAa;;WAEb,aAAa,cAAc,SAAS;;;;;WAKpC,WAAW;;WAEX;;;;;WAKA;;;;;;WAMA,SAAS,cAAc;;;;;;WAMvB,eAAe,cAAc,WAAW;;;;;;WAMxC;;;;;;qBAOE,cAAW,gBAAkB,WAAa;;;;;;qBAqB1C,WAAQ,gBAAkB,YAAa,6CAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAqBtD,iCAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAEN,aAAW,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAGX,SAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAGN,SAAM,SAAa,YAAU;qBAC7B,QAAK,iBAAmB;qBACxB,UAAO,oDAAsD;qBAE7D,UAAO,iBAAmB;qBAC1B,UAAO,MAAU,SAAS,YAAU;qBAEpC,UAAO,iBAAmB;qBAC1B,UAAO,OAAW,SAAS,cAAY;qBAEvC,UAAO,YAAgB,cAAc,SAAS,eAAa;qBAE3D,YAAS,IAAQ,SAAS,cAAY;qBAEtC,cAAa;qBACb,QAAK,MAAU,SAAS,QAAM,IAAM,SAAS,WAAS;qBAEtD,WAAQ,OAAW,SAAS,OAAK,KAAO,SAAS,WAAS;qBAE1D,UAAO,MAAU,SAAS,WAAS;qBAEnC,eAAY,MAAU,SAAS,QAAM,OAAS,OAAO,eAAa;qBAElE,WAAQ,WAAe,SAAS,qBAAmB;qBAEnD,cAAa;qBACb,cAAa;qBACb,eAAc"}
|
package/dist/editor-bundle.mjs
CHANGED
|
@@ -382,6 +382,9 @@ const editorView = Submodel.defineView((model, h) => {
|
|
|
382
382
|
h.OnMount(events({ content: model.document }))
|
|
383
383
|
], serverRendered ? [renderEditable(model.document, renderingFor(model.hostId), decorationsFor(model.hostId)(model.document))] : []);
|
|
384
384
|
});
|
|
385
|
+
const isEntered = Match.type().pipe(Match.tag("Entered", () => true), Match.orElse(() => false));
|
|
386
|
+
const typedOf = Match.type().pipe(Match.tag("Typed", (typed) => Option.some(typed)), Match.orElse(() => Option.none()));
|
|
387
|
+
const toggledMarkOf = Match.type().pipe(Match.tag("ToggledMark", ({ mark }) => Option.some(mark)), Match.orElse(() => Option.none()));
|
|
385
388
|
/** What a Message does to the editor; `Editor.update` carries the host's key on a commit. */
|
|
386
389
|
const transition = (model, incoming) => {
|
|
387
390
|
const state = {
|
|
@@ -389,7 +392,7 @@ const transition = (model, incoming) => {
|
|
|
389
392
|
selection: model.selection
|
|
390
393
|
};
|
|
391
394
|
const vocabulary = vocabularyFor(model.hostId);
|
|
392
|
-
const menu = incoming
|
|
395
|
+
const menu = isEntered(incoming) ? slashMenu(slashEntriesFor(model.hostId) ?? slashEntries, menuTextOf(model), model.menuIndex) : void 0;
|
|
393
396
|
const message = menu?.highlighted?.message ?? incoming;
|
|
394
397
|
return Match.value(message).pipe(Match.withReturnType(), Match.tags({
|
|
395
398
|
Patched: () => ({ model }),
|
|
@@ -404,10 +407,12 @@ const transition = (model, incoming) => {
|
|
|
404
407
|
Undone: (undone) => travel(model, state, undone),
|
|
405
408
|
Redone: (redone) => travel(model, state, redone)
|
|
406
409
|
}), Match.orElse((message) => {
|
|
407
|
-
const
|
|
410
|
+
const caretMoved = message._tag === "Selected";
|
|
411
|
+
const answering = caretMoved ? [] : [answer(model.hostId)];
|
|
408
412
|
let nextId = model.nextId;
|
|
409
413
|
const collapsed = model.selection?.type === "Range" && model.selection.anchor.node === model.selection.focus.node && model.selection.anchor.offset === model.selection.focus.offset;
|
|
410
|
-
|
|
414
|
+
const toggling = toggledMarkOf(message);
|
|
415
|
+
if (Option.isSome(toggling) && collapsed && !(vocabulary.marks ?? RichText.shippedRegistry).accepts(toggling.value)) return {
|
|
411
416
|
model,
|
|
412
417
|
outMessage: {
|
|
413
418
|
_tag: "Rejected",
|
|
@@ -415,23 +420,38 @@ const transition = (model, incoming) => {
|
|
|
415
420
|
},
|
|
416
421
|
commands: answering
|
|
417
422
|
};
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
423
|
+
const toggled = (mark) => {
|
|
424
|
+
if (!collapsed) return model.storedMarks;
|
|
425
|
+
if (model.storedMarks?.includes(mark)) return model.storedMarks.filter((held) => held !== mark);
|
|
426
|
+
return [...model.storedMarks ?? [], mark];
|
|
427
|
+
};
|
|
428
|
+
const storedMarks = Option.match(toggling, {
|
|
429
|
+
onNone: () => model.storedMarks,
|
|
430
|
+
onSome: toggled
|
|
431
|
+
});
|
|
432
|
+
const typing = typedOf(message);
|
|
433
|
+
const command = Option.match(typing, {
|
|
434
|
+
onNone: () => toCommand(message),
|
|
435
|
+
onSome: (typed) => storedMarks === null ? toCommand(typed) : {
|
|
436
|
+
type: "InsertText",
|
|
437
|
+
text: typed.text,
|
|
438
|
+
marks: storedMarks
|
|
439
|
+
}
|
|
440
|
+
});
|
|
424
441
|
const queryRange = menu === void 0 || menu.highlighted === void 0 || model.selection?.type !== "Range" ? void 0 : RichText.textRangeBefore(model.document, model.selection.anchor, menu.query.length + 1);
|
|
425
442
|
const runAction = (action) => RichText.runAction(state, action, { mint: () => `e${nextId++}` }, {
|
|
426
443
|
marks: vocabulary.marks,
|
|
427
444
|
nodes: vocabulary.nodes
|
|
428
445
|
});
|
|
429
|
-
const ruled =
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
446
|
+
const ruled = Option.match(typing, {
|
|
447
|
+
onNone: () => void 0,
|
|
448
|
+
onSome: (typed) => queryRange === void 0 ? RichText.resolveInputRules(inputRulesFor(model.hostId), {
|
|
449
|
+
textBefore: textBeforeOf(model),
|
|
450
|
+
text: typed.text,
|
|
451
|
+
insertion: command,
|
|
452
|
+
within: withinOf(model)
|
|
453
|
+
}) : void 0
|
|
454
|
+
});
|
|
435
455
|
const attempt = runAction(queryRange !== void 0 ? [
|
|
436
456
|
{
|
|
437
457
|
type: "SetSelection",
|
|
@@ -461,7 +481,7 @@ const transition = (model, incoming) => {
|
|
|
461
481
|
...model,
|
|
462
482
|
selection: result.state.selection,
|
|
463
483
|
nextId,
|
|
464
|
-
storedMarks:
|
|
484
|
+
storedMarks: caretMoved ? null : (fallback ? void 0 : ruled?.storedMarks) ?? storedMarks,
|
|
465
485
|
history: contentChanged ? RichText.commit(model.history, state, { group: RichText.groupFor(command) }) : model.history
|
|
466
486
|
},
|
|
467
487
|
outMessage: {
|
|
@@ -476,12 +496,18 @@ const transition = (model, incoming) => {
|
|
|
476
496
|
};
|
|
477
497
|
/** Undo or redo: history restores content and selection, and the host is told as a replace. */
|
|
478
498
|
const travel = (model, state, message) => {
|
|
479
|
-
const restored =
|
|
499
|
+
const restored = Match.valueTags(message, {
|
|
500
|
+
Undone: () => RichText.undo(model.history, state),
|
|
501
|
+
Redone: () => RichText.redo(model.history, state)
|
|
502
|
+
});
|
|
480
503
|
if (restored === void 0) return {
|
|
481
504
|
model,
|
|
482
505
|
outMessage: {
|
|
483
506
|
_tag: "Rejected",
|
|
484
|
-
error: message
|
|
507
|
+
error: Match.valueTags(message, {
|
|
508
|
+
Undone: () => "NothingToUndo",
|
|
509
|
+
Redone: () => "NothingToRedo"
|
|
510
|
+
})
|
|
485
511
|
}
|
|
486
512
|
};
|
|
487
513
|
const changeSet = replaceChangeSet(model.document, restored.state.document);
|
|
@@ -584,16 +610,21 @@ const placeEditor = (hostId, placement) => {
|
|
|
584
610
|
*/
|
|
585
611
|
const editorAt = (hostId, placement = {}) => {
|
|
586
612
|
placeEditor(hostId, placement);
|
|
613
|
+
const adopted = (parent, state) => ({ model: {
|
|
614
|
+
...parent,
|
|
615
|
+
document: state.document,
|
|
616
|
+
editor: {
|
|
617
|
+
...parent.editor,
|
|
618
|
+
selection: state.selection
|
|
619
|
+
}
|
|
620
|
+
} });
|
|
587
621
|
return Editor.at(editorLink, {
|
|
588
622
|
args: { hostId },
|
|
589
|
-
onOut: (out) => (parent) =>
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
selection: out.state.selection
|
|
595
|
-
}
|
|
596
|
-
} } : { model: parent }
|
|
623
|
+
onOut: (out) => (parent) => Match.valueTags(out, {
|
|
624
|
+
Edited: ({ state }) => adopted(parent, state),
|
|
625
|
+
Replaced: ({ state }) => adopted(parent, state),
|
|
626
|
+
Rejected: () => ({ model: parent })
|
|
627
|
+
})
|
|
597
628
|
});
|
|
598
629
|
};
|
|
599
630
|
const editor = editorAt("richtext-editor");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-bundle.mjs","names":[],"sources":["../src/editor-bundle.ts"],"sourcesContent":["/**\n * The controlled-editor feasibility harness (§27): the authoritative Document\n * lives in the parent Model, the editor Bundle reads it through its Link, and a\n * single parent transition commits both the document and the interaction state.\n *\n * No DOM, no persistence, no collaboration: this exists to answer whether Bundle\n * ownership can hold a rich-text editor without a second synchronized document\n * copy or a Command that commits half the transition.\n */\nimport { Effect, Equal, Match, Option, Schema } from 'effect'\nimport { define } from 'foldkit/customElement'\nimport { defineMessageUnion } from 'foldkit/message'\nimport { Bundle, Link, type Wrapped } from 'foldkit-bundle'\nimport * as RichText from 'foldkit-richtext'\nimport * as Submodel from 'foldkit/submodel'\nimport type * as Command from 'foldkit/command'\nimport type * as Update from 'foldkit/update'\nimport {\n heldDocument,\n events,\n focusEditor,\n Message,\n answerEditor,\n patchEditor,\n redecorateEditor,\n slashEntries,\n slashMenu,\n slashQuery,\n type EditorEvent,\n type SlashEntry,\n} from './editor.js'\nimport {\n decorationsFor,\n inputRulesFor,\n placeDecorations,\n placeInputRules,\n placeOverlay,\n placePlaceholders,\n placeRendering,\n placeServerRendered,\n placeKeymap,\n placeSlashEntries,\n placeVocabulary,\n renderingFor,\n serverRenderedFor,\n slashEntriesFor,\n type Vocabulary,\n vocabularyFor,\n} from './host.js'\nimport type { Decorate, KeyBinding } from './events.js'\nimport { renderEditable } from './view.js'\n\n/** Interaction state the parent owns beside the document. */\nexport const EditorState = Schema.Struct({\n selection: Schema.NullOr(RichText.Selection),\n /** Caller-owned identity source: a live edit mints, replay never does. */\n nextId: Schema.Number,\n /** Local undo history: snapshots of document plus selection. */\n history: RichText.History,\n /** Null inherits neighboring marks; an array explicitly sets them, even when empty. */\n storedMarks: Schema.NullOr(Schema.Array(Schema.String)),\n /**\n * The highlighted entry in a live slash menu (§123). The document says whether the\n * caret is in a query and what matches; this is the one thing the menu owns, and\n * `update` resolves Enter against it.\n */\n menuIndex: Schema.Number,\n /** The slash of the query whose menu Escape closed; it stays closed until that query ends. */\n slashDismissed: Schema.OptionFromNullOr(RichText.Position),\n /**\n * The host element the view renders and the patch Command finds (§118). It is\n * per-placement, so it arrives as the Bundle's args and is carried here\n * because `read` re-projects the whole child from the parent.\n */\n hostId: Schema.String,\n})\nexport type EditorState = typeof EditorState.Type\n\nexport const Model = Schema.Struct({\n document: RichText.Document,\n editor: EditorState,\n})\nexport type Model = typeof Model.Type\n\n/** What the child reads: the parent's document passes through, never stored. */\nexport const EditorView = Schema.Struct({\n document: RichText.Document,\n selection: Schema.NullOr(RichText.Selection),\n nextId: Schema.Number,\n history: RichText.History,\n storedMarks: Schema.NullOr(Schema.Array(Schema.String)),\n menuIndex: Schema.Number,\n slashDismissed: Schema.OptionFromNullOr(RichText.Position),\n hostId: Schema.String,\n})\nexport type EditorView = typeof EditorView.Type\n\n/** The committed edit, or the diagnostic that refused it. */\nexport type OutMessage =\n | {\n readonly _tag: 'Edited'\n readonly state: RichText.EditorState\n readonly changeSet: RichText.ChangeSet\n /** What the edit applied, in order, as `RichText.run` returns them. */\n readonly transactions: ReadonlyArray<RichText.Transaction>\n }\n | {\n /** Undo and redo replace the document wholesale; nothing was incremental. */\n readonly _tag: 'Replaced'\n readonly state: RichText.EditorState\n readonly changeSet: RichText.ChangeSet\n }\n | { readonly _tag: 'Rejected'; readonly error: string }\n\n/** Where a node stands: the block holding it (null at the top), and its index there. */\ninterface Placed {\n readonly node: RichText.Block | RichText.Text\n readonly parent: RichText.NodeId | null\n readonly index: number\n}\n\n/** Every block and run of a document by id, nested ones included, with where it stands. */\nconst nodesOf = (content: RichText.Document): ReadonlyMap<RichText.NodeId, Placed> => {\n const nodes = new Map<RichText.NodeId, Placed>()\n const visit = (blocks: ReadonlyArray<RichText.Block>, parent: RichText.NodeId | null): void => {\n for (const [index, block] of blocks.entries()) {\n nodes.set(block.id, { node: block, parent, index })\n for (const [at, run] of block.children.entries()) {\n nodes.set(run.id, { node: run, parent: block.id, index: at })\n }\n if (block.type === 'Node' && block.blocks !== undefined) visit(block.blocks, block.id)\n }\n }\n visit(content.children, null)\n return nodes\n}\n\nconst sameMarks = (left: RichText.Text, right: RichText.Text): boolean =>\n left.marks.length === right.marks.length &&\n left.marks.every((mark, index) => RichText.sameMark(mark, right.marks[index]!))\n\n/** A block's own fields, apart from what it holds: its type, level, kind and props. */\nconst ownFields = ({ children: _runs, ...block }: RichText.Block): unknown => ({\n ...block,\n blocks: undefined,\n})\n\n/**\n * What differs between two whole documents, for a patch from one to the other, in a\n * transaction's terms: the runs whose text or marks changed and the blocks holding them,\n * blocks whose own fields changed, what came and went, and whether any block moved.\n */\nexport const replaceChangeSet = (\n previous: RichText.Document,\n next: RichText.Document,\n): RichText.ChangeSet => {\n const before = nodesOf(previous)\n const after = nodesOf(next)\n const dirtyNodes = new Set<RichText.NodeId>()\n const textChanged = new Set<RichText.NodeId>()\n let structureChanged = false\n for (const [id, placed] of after) {\n const was = before.get(id)\n if (placed.node.type === 'Text') {\n const run = placed.node\n const old = was?.node.type === 'Text' ? was.node : undefined\n if (old === undefined || old.text !== run.text) textChanged.add(id)\n if (old === undefined || old.text !== run.text || !sameMarks(old, run)) {\n dirtyNodes.add(id)\n if (placed.parent !== null) dirtyNodes.add(placed.parent)\n }\n // A run moved to another block, or to another place in its own, changes both blocks'\n // runs though the run itself did not change.\n if (was !== undefined && (was.parent !== placed.parent || was.index !== placed.index)) {\n if (placed.parent !== null) dirtyNodes.add(placed.parent)\n if (was.parent !== null && after.has(was.parent)) dirtyNodes.add(was.parent)\n }\n continue\n }\n if (was === undefined || was.parent !== placed.parent || was.index !== placed.index) {\n structureChanged = true\n }\n if (\n was === undefined ||\n !Equal.equals(ownFields(was.node as RichText.Block), ownFields(placed.node))\n ) {\n dirtyNodes.add(id)\n }\n }\n const removedNodes = new Set([...before.keys()].filter(id => !after.has(id)))\n for (const id of removedNodes) {\n const was = before.get(id)!\n if (was.node.type !== 'Text') structureChanged = true\n else if (was.parent !== null && after.has(was.parent)) dirtyNodes.add(was.parent)\n }\n return {\n dirtyNodes,\n insertedNodes: new Set([...after.keys()].filter(id => !before.has(id))),\n removedNodes,\n textChanged,\n structureChanged,\n selectionChanged: false,\n }\n}\n\ntype CommandMessage = Exclude<\n Message,\n { readonly _tag: 'Undone' | 'Redone' | 'Patched' | 'ActivatedAdornment' | 'DismissedSlash' }\n>\n\nconst toCommand = (message: CommandMessage): RichText.Command => {\n switch (message._tag) {\n case 'Typed':\n return { type: 'InsertText', text: message.text }\n case 'Backspace':\n return { type: 'DeleteBackward' }\n case 'DeletedForward':\n return { type: 'DeleteForward' }\n case 'Entered':\n return { type: 'SplitBlock' }\n case 'ToggledMark':\n return { type: 'ToggleMark', mark: message.mark }\n case 'AppliedMark':\n return { type: 'SetMark', mark: message.mark }\n case 'ClearedMark':\n return { type: 'ClearMark', mark: message.mark }\n case 'RetypedBlock':\n return { type: 'RetypeBlock', to: message.block }\n case 'WrappedBlock':\n return { type: 'WrapBlock', containers: message.containers }\n case 'ConvertedBlock':\n return { type: 'ConvertBlock', to: message.to }\n case 'LiftedBlock':\n return { type: 'LiftBlock' }\n case 'MovedBlock':\n return { type: 'MoveBlock', node: message.node, to: message.to }\n case 'InsertedBlock':\n return { type: 'InsertBlock', block: message.block, to: message.to }\n case 'DeletedBlock':\n return { type: 'DeleteBlock', node: message.node }\n case 'ChangedProps':\n return { type: 'SetProps', node: message.node, props: message.props }\n case 'InsertedRow':\n return { type: 'InsertRow', where: message.where }\n case 'InsertedColumn':\n return { type: 'InsertColumn', where: message.where }\n case 'DeletedRow':\n return { type: 'DeleteRow' }\n case 'DeletedColumn':\n return { type: 'DeleteColumn' }\n case 'Indented':\n return { type: 'Indent' }\n case 'ReplacedText':\n return {\n type: 'Replace',\n query: message.query,\n replacement: message.replacement,\n caseSensitive: message.caseSensitive,\n at: message.at,\n }\n case 'InsertedFootnote':\n return { type: 'InsertFootnote', text: message.text }\n case 'RenamedFootnote':\n return { type: 'RenameFootnote', from: message.from, to: message.to }\n case 'Outdented':\n return { type: 'Outdent' }\n case 'Selected':\n return { type: 'SetSelection', selection: message.selection }\n case 'Pasted':\n return { type: 'Paste', slice: message.slice }\n }\n}\n\n/**\n * The rendering effect (§118): patch the host the view rendered, once the\n * parent's transition has committed. It commits nothing, so the one-transition\n * property the proof established is untouched.\n */\nconst patch = (hostId: string, state: RichText.EditorState, changeSet: RichText.ChangeSet) => ({\n name: 'RichText.patch',\n effect: Effect.as(\n Effect.sync(() => {\n patchEditor(hostId, state, changeSet)\n }),\n Message.Patched(),\n ),\n})\n\n/**\n * Answers an edit the editor asked for (`Attachment.answered`), last among the Commands of\n * its transition: the runtime runs them in order, so the answer follows the draw, or stands\n * alone for an edit refused or changing nothing. An application that replaces the patch\n * with its own keeps the answer after it.\n */\nconst answer = (hostId: string) => ({\n name: 'RichText.answer',\n effect: Effect.as(\n Effect.sync(() => answerEditor(hostId)),\n Message.Patched(),\n ),\n})\n\n/**\n * The text between the start of the caret's block and the caret, or `''` when the\n * selection names no caret. A menu and the input rules read this; nothing about the query\n * is stored. A range is read from its start, which is where what is typed over it lands,\n * whichever way it was dragged.\n */\nconst textBeforeOf = (model: EditorView): string => {\n if (model.selection?.type !== 'Range') return ''\n const start = RichText.rangeStart(model.document, model.selection)\n return start === undefined ? '' : RichText.textBefore(model.document, start)\n}\n\n/** Where the slash of the caret's live query is: none outside a query. */\nconst slashOf = (model: EditorView): Option.Option<RichText.Position> => {\n const query = slashQuery(textBeforeOf(model))\n if (query === undefined || model.selection?.type !== 'Range') return Option.none()\n const start = RichText.rangeStart(model.document, model.selection)\n const range =\n start === undefined\n ? undefined\n : RichText.textRangeBefore(model.document, start, query.length + 1)\n return range?.type === 'Range' ? Option.some(range.anchor) : Option.none()\n}\n\nconst dismissed = (model: EditorView): boolean =>\n Option.exists(model.slashDismissed, slash =>\n Option.exists(slashOf(model), live => live.node === slash.node && live.offset === slash.offset),\n )\n\n/**\n * The text a slash menu reads: the text before the caret, or nothing once Escape has closed\n * the menu of the query the caret is in. Enter resolves against it, and an application's menu\n * view reads it too, so the two agree on whether a menu is open.\n */\nexport const menuTextOf = (model: EditorView): string =>\n dismissed(model) ? '' : textBeforeOf(model)\n\n/**\n * The kinds of the node blocks around the caret's block, innermost first; a range is read\n * from its start, as `textBeforeOf` reads it.\n */\nconst withinOf = (model: EditorView): ReadonlyArray<string> => {\n if (model.selection?.type !== 'Range') return []\n const start = RichText.rangeStart(model.document, model.selection)\n const run = start === undefined ? undefined : RichText.locateRun(model.document, start.node)\n if (run === undefined) return []\n const kinds: Array<string> = []\n for (let depth = run.path.length - 1; depth > 0; depth--) {\n const block = RichText.blockAtPath(model.document, run.path.slice(0, depth))\n if (block?.type === 'Node') kinds.push(block.kind)\n }\n return kinds\n}\n\nconst Host = define({ tag: 'foldkit-richtext', properties: {}, events: {} })\n\n/**\n * Each host id's keys, by the document shown. The host's Mount reads its document once, so a\n * document that did not come from an edit here (a form's fill or reset, an entry opened, a\n * revision restored) needs a fresh host. A committed edit moves its document's key to the next\n * document (`carryKey`), and the document it leaves keeps none, so returning to it is a new host\n * too. The first document a host id shows has no key, as the server's render has none.\n */\nconst lineages = new Map<\n string,\n { next: number; readonly keys: WeakMap<RichText.Document, number> }\n>()\n\nconst lineageOf = (hostId: string) => {\n const found = lineages.get(hostId)\n if (found !== undefined) return found\n const created = { next: 0, keys: new WeakMap<RichText.Document, number>() }\n lineages.set(hostId, created)\n return created\n}\n\nconst hostKey = (hostId: string, document: RichText.Document): string | undefined => {\n const lineage = lineageOf(hostId)\n const key = lineage.keys.get(document) ?? lineage.next++\n lineage.keys.set(document, key)\n return key === 0 ? undefined : `${hostId}-${key}`\n}\n\nconst carryKey = (hostId: string, from: RichText.Document, to: RichText.Document): void => {\n const keys = lineageOf(hostId).keys\n const key = keys.get(from)\n if (key === undefined || from === to) return\n keys.delete(from)\n keys.set(to, key)\n}\n\n/**\n * Patches the editor at `hostId` to `next`, a document the parent put in place of\n * `previous`, keeping the host element instead of mounting a new one. A parent that\n * replaces its document outside an edit here (another replica's change arriving) returns\n * this, in the same transition as the replacement.\n *\n * The host is found by the document the view renders, so `previous` is the object it\n * rendered and `next.document` the object it will render; building the Command hands the\n * host on from one to the other, so build it only to return it. The patch itself starts\n * from the latest state the editor holds (what it has drawn, or a state waiting on a\n * composition), and `next.selection` becomes the browser selection;\n * a replacement that changes nothing drawn leaves the DOM alone, selection included, as an\n * exchange that only confirms edits already shown does.\n */\nexport const patchTo = (\n hostId: string,\n previous: RichText.Document,\n next: RichText.EditorState,\n): Command.Command<Message> => {\n carryKey(hostId, previous, next.document)\n return {\n name: 'RichText.patch',\n effect: Effect.as(\n Effect.sync(() => {\n const held = heldDocument(hostId)\n // No editor is attached there: it went away while the transition was in flight.\n if (held === undefined) return\n const changeSet = replaceChangeSet(held, next.document)\n const unchanged =\n !changeSet.structureChanged &&\n changeSet.dirtyNodes.size === 0 &&\n changeSet.removedNodes.size === 0\n if (!unchanged) patchEditor(hostId, next, changeSet)\n }),\n Message.Patched(),\n ),\n }\n}\n\n/**\n * Draws `decorations` over the editor at `hostId`, beside what its placement draws, until the\n * next overlay replaces them: decorations an application derives from its own state, such as\n * other people's carets, which a placement's `decorate` cannot see. Their positions name the\n * document shown, so an application sets them again when that changes. The caret stays put.\n */\nexport const overlay = (\n hostId: string,\n decorations: RichText.DecorationSet,\n): Command.Command<Message> => ({\n name: 'RichText.overlay',\n effect: Effect.as(\n Effect.sync(() => {\n placeOverlay(hostId, decorations)\n redecorateEditor(hostId)\n }),\n Message.Patched(),\n ),\n})\n\n/**\n * Focuses the editor at `hostId` with `selection` in the browser, brought into view: for a\n * control outside the editor that sends the reader into the text, such as a table of contents.\n * The browser reports the selection back as `Selected`, so the Model follows it either way.\n */\nexport const focus = (hostId: string, selection: RichText.Selection): Command.Command<Message> => ({\n name: 'RichText.focus',\n effect: Effect.as(\n Effect.sync(() => focusEditor(hostId, selection)),\n Message.Patched(),\n ),\n})\n\n/**\n * The host element the editor mounts into, and whose id the patch Command finds (§118). It is a\n * custom element so hydration leaves what is inside it alone (§145): on the server it holds the\n * document's markup, which the editor adopts; in the browser the view declares it empty, and the\n * editor owns everything below.\n */\nexport const editorView = Submodel.defineView<EditorView, Message>((model, h) => {\n const serverRendered = serverRenderedFor(model.hostId)()\n const key = serverRendered ? undefined : hostKey(model.hostId, model.document)\n return Host.withMessage(h)(\n [\n h.Id(model.hostId),\n ...(key === undefined ? [] : [h.Key(key)]),\n // A custom element is inline until styled, and the editor inside it is a block.\n h.Style({ display: 'block' }),\n h.OnMount(events({ content: model.document })),\n ],\n serverRendered\n ? [\n renderEditable(\n model.document,\n renderingFor(model.hostId),\n decorationsFor(model.hostId)(model.document),\n ),\n ]\n : [],\n )\n})\n\n/** What a Message does to the editor; `Editor.update` carries the host's key on a commit. */\nconst transition = (\n model: EditorView,\n incoming: Message,\n): Update.ReturnWithOutMessage<EditorView, Message, OutMessage> => {\n const state: RichText.EditorState = { document: model.document, selection: model.selection }\n // The vocabulary this placement resolves edits against; both fields may be absent.\n const vocabulary = vocabularyFor(model.hostId)\n // A live query decides what Enter means before anything else reads the message\n // (§123): the highlighted entry applies as the Message a click would send, instead\n // of splitting, and a query that chooses nothing falls through to `incoming` and\n // still splits. Substituting the Message here rather than recursing is what makes a\n // mark entry update the caret's stored marks through the path a toggle already uses.\n const menu =\n incoming._tag === 'Entered'\n ? slashMenu(slashEntriesFor(model.hostId) ?? slashEntries, menuTextOf(model), model.menuIndex)\n : undefined\n const message = menu?.highlighted?.message ?? incoming\n return Match.value(message).pipe(\n Match.withReturnType<Update.ReturnWithOutMessage<EditorView, Message, OutMessage>>(),\n Match.tags({\n // The patch Command's own completion: the render already happened.\n Patched: () => ({ model }),\n // A click asking the application for something, not an edit: nothing changes here.\n ActivatedAdornment: () => ({ model }),\n DismissedSlash: () => {\n const slash = slashOf(model)\n return Option.isNone(slash) || dismissed(model)\n ? { model }\n : { model: { ...model, slashDismissed: slash } }\n },\n Undone: undone => travel(model, state, undone),\n Redone: redone => travel(model, state, redone),\n }),\n Match.orElse(message => {\n // Every edit the editor asks for is answered, once drawn or refused; a reported caret\n // is not an ask.\n const answering = message._tag === 'Selected' ? [] : [answer(model.hostId)]\n let nextId = model.nextId\n // With nothing selected, a mark toggle is the caret's own state: the next\n // typed text lands with it. A caret move ends the format it was carrying.\n const collapsed =\n model.selection?.type === 'Range' &&\n model.selection.anchor.node === model.selection.focus.node &&\n model.selection.anchor.offset === model.selection.focus.offset\n // The caret never carries a mark the vocabulary cannot type — an unknown one, or one\n // like Link whose props a bare name lacks — so it is refused here rather than at the\n // first keystroke after it.\n if (\n message._tag === 'ToggledMark' &&\n collapsed &&\n !(vocabulary.marks ?? RichText.shippedRegistry).accepts(message.mark)\n ) {\n return {\n model,\n outMessage: { _tag: 'Rejected', error: 'InvalidInput' },\n commands: answering,\n }\n }\n const storedMarks =\n message._tag === 'ToggledMark' && collapsed\n ? model.storedMarks?.includes(message.mark)\n ? model.storedMarks.filter(mark => mark !== message.mark)\n : [...(model.storedMarks ?? []), message.mark]\n : model.storedMarks\n const command =\n message._tag === 'Typed' && storedMarks !== null\n ? ({ type: 'InsertText', text: message.text, marks: storedMarks } as const)\n : toCommand(message)\n // Choosing an entry removes the query it was typed into and applies the choice as\n // one action (§124 §5), so one transition and one undo step cover both. The range is\n // a read, not state: `menu.query` is the text the document already holds, and the\n // `+ 1` is the slash that opened it.\n const queryRange =\n menu === undefined || menu.highlighted === undefined || model.selection?.type !== 'Range'\n ? undefined\n : RichText.textRangeBefore(model.document, model.selection.anchor, menu.query.length + 1)\n const runAction = (action: RichText.Action) =>\n RichText.runAction(\n state,\n action,\n { mint: () => `e${nextId++}` },\n { marks: vocabulary.marks, nodes: vocabulary.nodes },\n )\n // What is typed can be a block marker (§124 §4): the rules are the placement's own, so\n // the editor carries none of any syntax's vocabulary itself.\n const ruled =\n queryRange === undefined && message._tag === 'Typed'\n ? RichText.resolveInputRules(inputRulesFor(model.hostId), {\n textBefore: textBeforeOf(model),\n text: message.text,\n insertion: command,\n within: withinOf(model),\n })\n : undefined\n const attempt = runAction(\n queryRange !== undefined\n ? [{ type: 'SetSelection', selection: queryRange }, { type: 'DeleteBackward' }, command]\n : (ruled?.action ?? [command]),\n )\n // A rule the vocabulary refuses — a fence typed into bold text, say — leaves the marker\n // as the text it is, rather than refusing the keystroke along with it. The refused\n // attempt's identities are given back first.\n const fallback = !attempt.ok && ruled !== undefined\n if (fallback) nextId = model.nextId\n const result = fallback ? runAction([command]) : attempt\n if (!result.ok) {\n // A refused command changes nothing, so it does not burn identities.\n return { model, outMessage: { _tag: 'Rejected', error: result.error }, commands: answering }\n }\n // `runAction` returns the state it was given when nothing changed: a caret the browser\n // echoes back, or a command with nothing to act on. Keeping the Model keeps the page from\n // rendering, and a caret that did not move keeps the format it carries.\n if (result.state === state && storedMarks === model.storedMarks) {\n return { model, commands: answering }\n }\n // History holds content, not cursor movement: a selection change keeps the\n // redo stack, and a no-op edit adds no step to undo.\n const contentChanged = result.state.document !== model.document\n return {\n model: {\n ...model,\n selection: result.state.selection,\n nextId,\n // A caret move ends the format it was carrying; a rule that applied may set the next.\n storedMarks:\n message._tag === 'Selected'\n ? null\n : ((fallback ? undefined : ruled?.storedMarks) ?? storedMarks),\n history: contentChanged\n ? RichText.commit(model.history, state, { group: RichText.groupFor(command) })\n : model.history,\n },\n outMessage: {\n _tag: 'Edited',\n state: result.state,\n changeSet: result.changeSet,\n transactions: result.transactions,\n },\n commands: [patch(model.hostId, result.state, result.changeSet), ...answering],\n }\n }),\n )\n}\n\n/** Undo or redo: history restores content and selection, and the host is told as a replace. */\nconst travel = (\n model: EditorView,\n state: RichText.EditorState,\n message: Extract<Message, { readonly _tag: 'Undone' | 'Redone' }>,\n): Update.ReturnWithOutMessage<EditorView, Message, OutMessage> => {\n const restored =\n message._tag === 'Undone'\n ? RichText.undo(model.history, state)\n : RichText.redo(model.history, state)\n if (restored === undefined) {\n return {\n model,\n outMessage: {\n _tag: 'Rejected',\n error: message._tag === 'Undone' ? 'NothingToUndo' : 'NothingToRedo',\n },\n }\n }\n const changeSet = replaceChangeSet(model.document, restored.state.document)\n return {\n model: {\n ...model,\n selection: restored.state.selection,\n history: restored.history,\n },\n outMessage: { _tag: 'Replaced', state: restored.state, changeSet },\n commands: [patch(model.hostId, restored.state, changeSet)],\n }\n}\n\nexport const Editor = Bundle.make({\n name: 'RichTextEditor',\n Model: EditorView,\n Message,\n args: Schema.Struct({ hostId: Schema.String }),\n // A read-only projection has no initial content of its own; the placement\n // writes only the interaction fields back, so this never becomes the document.\n // The host id is the one thing the child seeds: it is per-placement, and `read`\n // re-projects everything else.\n init: args => ({\n model: {\n document: RichText.Document.make({ version: 1, children: [] }),\n selection: null,\n nextId: 0,\n history: RichText.emptyHistory,\n storedMarks: null,\n menuIndex: 0,\n slashDismissed: Option.none(),\n hostId: args.hostId,\n },\n }),\n update: (model, incoming): Update.ReturnWithOutMessage<EditorView, Message, OutMessage> => {\n const result = transition(model, incoming)\n const out = result.outMessage\n // An edit or a replacement carries the state it committed; a rejection, none.\n const committed = out !== undefined && 'state' in out ? out.state : undefined\n if (committed !== undefined) carryKey(model.hostId, model.document, committed.document)\n // A dismissal ends with the query it closed, so a slash typed later opens a menu again.\n // The child's document is the parent's, so the edit is read from what it committed.\n const after = { ...result.model, document: committed?.document ?? result.model.document }\n return Option.isSome(result.model.slashDismissed) && !dismissed(after)\n ? { ...result, model: { ...result.model, slashDismissed: Option.none() } }\n : result\n },\n // The host element belongs to the view; everything below it belongs to the\n // interpreter the mount attaches there.\n view: editorView,\n})\n\nconst GotEditor = Link.wrapper('GotEditorMessage', Message)\n\nexport const ParentMessage = defineMessageUnion({ ...GotEditor.cases })\nexport type ParentMessage = typeof ParentMessage.Type\n\nconst editorLink: Link<\n Model,\n Wrapped<'GotEditorMessage', Message>,\n EditorView,\n Message\n> = Link.make({\n // The child sees the parent's document; the parent owns it.\n read: parent =>\n Option.some({\n document: parent.document,\n selection: parent.editor.selection,\n nextId: parent.editor.nextId,\n history: parent.editor.history,\n storedMarks: parent.editor.storedMarks,\n menuIndex: parent.editor.menuIndex,\n slashDismissed: parent.editor.slashDismissed,\n hostId: parent.editor.hostId,\n }),\n // Only interaction state is written back: the document is not the child's. `read` builds a\n // new view each time, so the Bundle cannot see that a child returned what it read; a child\n // whose fields are the parent's own leaves the parent as it was. The placement's `init`\n // writes into a parent that has no `editor` yet.\n write: (parent, { document: _document, ...editor }) => {\n const current: EditorState | undefined = parent.editor\n return current !== undefined &&\n (Object.keys(editor) as Array<keyof EditorState>).every(key => editor[key] === current[key])\n ? parent\n : { ...parent, editor }\n },\n wrapper: GotEditor,\n path: ['editor'],\n})\n\n/**\n * What a placement gives its editor. Each is placed by host id rather than passed as an arg\n * (§122): each holds functions or schemas, which a schema-decoded arg cannot describe.\n */\nexport interface EditorPlacement {\n /** How this editor's marks and node kinds render (§121). Defaults to `noRendering`. */\n readonly rendering?: RichText.Rendering | undefined\n /** The vocabulary its edits resolve against (§125). Defaults to none. */\n readonly vocabulary?: Vocabulary | undefined\n /** The rules applied to what is typed (§128). Defaults to none. */\n readonly inputRules?: ReadonlyArray<RichText.InputRule> | undefined\n /**\n * What is drawn over the document (§129), derived from it on every patch: code\n * highlighting, for one. Defaults to nothing.\n */\n readonly decorate?: Decorate | undefined\n /** What the editor shows while its document is blank (`RichText.isBlank`). Defaults to none. */\n readonly placeholder?: string | undefined\n /**\n * What the empty paragraph the caret is in shows, such as \"Type / for blocks\", as\n * `data-hint` on it. Defaults to none.\n */\n readonly lineHint?: string | undefined\n /**\n * Chords beside the built-in ones (Bold, Italic, Code, Strikethrough, undo and redo), checked\n * first: `{ chord: 'Mod-u', command: { type: 'ToggleMark', mark: 'Underline' } }` for a mark\n * of the application's own. Defaults to none.\n */\n readonly keymap?: ReadonlyArray<KeyBinding> | undefined\n /**\n * What its slash menu offers, which Enter resolves against: an application's own entries\n * (an image, a table, a callout) beside or instead of `slashEntries`. The application's menu\n * view draws the same list. Defaults to `slashEntries`.\n */\n readonly slashEntries?: ReadonlyArray<SlashEntry<EditorEvent>> | undefined\n /**\n * Whether this render is the server's (§145), such as `foldkit-ssr`'s `SSR.serving`. While it\n * is, the host carries the document's markup, which the browser's editor adopts instead of\n * drawing again. Defaults to never, so the host is empty until the editor mounts.\n */\n readonly serverRendered?: (() => boolean) | undefined\n}\n\n/**\n * Records everything a placement names for its host id, replacing what the id had: one call,\n * so a placement cannot half-apply. `editorAt` and the form control both place through it.\n */\nexport const placeEditor = (hostId: string, placement: EditorPlacement): void => {\n placeRendering(hostId, placement.rendering ?? RichText.noRendering)\n placeVocabulary(hostId, placement.vocabulary ?? {})\n placeInputRules(hostId, placement.inputRules ?? [])\n placeDecorations(hostId, placement.decorate ?? (() => []))\n // The overlay is set by Commands after placing, so a placement starts without the last one's.\n placeOverlay(hostId, [])\n placePlaceholders(hostId, {\n placeholder: placement.placeholder,\n lineHint: placement.lineHint,\n })\n placeSlashEntries(hostId, placement.slashEntries)\n placeKeymap(hostId, placement.keymap ?? [])\n placeServerRendered(hostId, placement.serverRendered ?? (() => false))\n}\n\n/**\n * Places one editor, bound to the host element the view renders and the patch Command\n * finds. Each placement picks its own id and names what it places; one call records all\n * of it, so a placement cannot half-apply.\n */\nexport const editorAt = (hostId: string, placement: EditorPlacement = {}) => {\n placeEditor(hostId, placement)\n return Editor.at(editorLink, {\n args: { hostId },\n // Runs with the child already written back, in the same parent transition.\n onOut: (out: OutMessage) => (parent: Model) =>\n out._tag === 'Edited' || out._tag === 'Replaced'\n ? {\n model: {\n ...parent,\n document: out.state.document,\n editor: { ...parent.editor, selection: out.state.selection },\n },\n }\n : { model: parent },\n })\n}\n\nexport const editor = editorAt('richtext-editor')\n\nexport const application = Bundle.assemble<Model, ParentMessage>()([editor])\n\n/** The parent's update: placement Messages route to the editor, others stand still. */\nexport const update = application.update()\n\n/** Wraps one editor Message as the parent Message that carries it. */\nexport const edited = (message: Message): ParentMessage => GotEditor.make(message)\nexport const typed = (text: string): ParentMessage => edited(Message.Typed({ text }))\nexport const pressed = (tag: 'Backspace' | 'DeletedForward' | 'Entered'): ParentMessage =>\n edited(Message[tag]())\nexport const toggled = (mark: string): ParentMessage => edited(Message.ToggledMark({ mark }))\nexport const applied = (mark: RichText.RunMark): ParentMessage =>\n edited(Message.AppliedMark({ mark }))\nexport const cleared = (mark: string): ParentMessage => edited(Message.ClearedMark({ mark }))\nexport const retyped = (block: RichText.TextBlock): ParentMessage =>\n edited(Message.RetypedBlock({ block }))\nexport const wrapped = (containers: ReadonlyArray<RichText.Container>): ParentMessage =>\n edited(Message.WrappedBlock({ containers }))\nexport const converted = (to: RichText.Container): ParentMessage =>\n edited(Message.ConvertedBlock({ to }))\nexport const lifted = (): ParentMessage => edited(Message.LiftedBlock())\nexport const moved = (node: RichText.NodeId, to: RichText.Beside): ParentMessage =>\n edited(Message.MovedBlock({ node, to }))\nexport const inserted = (block: RichText.Block, to?: RichText.Beside): ParentMessage =>\n edited(Message.InsertedBlock(to === undefined ? { block } : { block, to }))\nexport const deleted = (node: RichText.NodeId): ParentMessage =>\n edited(Message.DeletedBlock({ node }))\nexport const changedProps = (node: RichText.NodeId, props: Schema.JsonObject): ParentMessage =>\n edited(Message.ChangedProps({ node, props }))\nexport const selected = (selection: RichText.Selection | null): ParentMessage =>\n edited(Message.Selected({ selection }))\nexport const undone = (): ParentMessage => edited(Message.Undone())\nexport const redone = (): ParentMessage => edited(Message.Redone())\nexport const patched = (): ParentMessage => edited(Message.Patched())\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"editor-bundle.mjs","names":[],"sources":["../src/editor-bundle.ts"],"sourcesContent":["/**\n * The controlled-editor feasibility harness (§27): the authoritative Document\n * lives in the parent Model, the editor Bundle reads it through its Link, and a\n * single parent transition commits both the document and the interaction state.\n *\n * No DOM, no persistence, no collaboration: this exists to answer whether Bundle\n * ownership can hold a rich-text editor without a second synchronized document\n * copy or a Command that commits half the transition.\n */\nimport { Effect, Equal, Match, Option, Schema } from 'effect'\nimport { define } from 'foldkit/customElement'\nimport { defineMessageUnion } from 'foldkit/message'\nimport { Bundle, Link, type Wrapped } from 'foldkit-bundle'\nimport * as RichText from 'foldkit-richtext'\nimport * as Submodel from 'foldkit/submodel'\nimport type * as Command from 'foldkit/command'\nimport type * as Update from 'foldkit/update'\nimport {\n heldDocument,\n events,\n focusEditor,\n Message,\n answerEditor,\n patchEditor,\n redecorateEditor,\n slashEntries,\n slashMenu,\n slashQuery,\n type EditorEvent,\n type SlashEntry,\n} from './editor.js'\nimport {\n decorationsFor,\n inputRulesFor,\n placeDecorations,\n placeInputRules,\n placeOverlay,\n placePlaceholders,\n placeRendering,\n placeServerRendered,\n placeKeymap,\n placeSlashEntries,\n placeVocabulary,\n renderingFor,\n serverRenderedFor,\n slashEntriesFor,\n type Vocabulary,\n vocabularyFor,\n} from './host.js'\nimport type { Decorate, KeyBinding } from './events.js'\nimport { renderEditable } from './view.js'\n\n/** Interaction state the parent owns beside the document. */\nexport const EditorState = Schema.Struct({\n selection: Schema.NullOr(RichText.Selection),\n /** Caller-owned identity source: a live edit mints, replay never does. */\n nextId: Schema.Number,\n /** Local undo history: snapshots of document plus selection. */\n history: RichText.History,\n /** Null inherits neighboring marks; an array explicitly sets them, even when empty. */\n storedMarks: Schema.NullOr(Schema.Array(Schema.String)),\n /**\n * The highlighted entry in a live slash menu (§123). The document says whether the\n * caret is in a query and what matches; this is the one thing the menu owns, and\n * `update` resolves Enter against it.\n */\n menuIndex: Schema.Number,\n /** The slash of the query whose menu Escape closed; it stays closed until that query ends. */\n slashDismissed: Schema.OptionFromNullOr(RichText.Position),\n /**\n * The host element the view renders and the patch Command finds (§118). It is\n * per-placement, so it arrives as the Bundle's args and is carried here\n * because `read` re-projects the whole child from the parent.\n */\n hostId: Schema.String,\n})\nexport type EditorState = typeof EditorState.Type\n\nexport const Model = Schema.Struct({\n document: RichText.Document,\n editor: EditorState,\n})\nexport type Model = typeof Model.Type\n\n/** What the child reads: the parent's document passes through, never stored. */\nexport const EditorView = Schema.Struct({\n document: RichText.Document,\n selection: Schema.NullOr(RichText.Selection),\n nextId: Schema.Number,\n history: RichText.History,\n storedMarks: Schema.NullOr(Schema.Array(Schema.String)),\n menuIndex: Schema.Number,\n slashDismissed: Schema.OptionFromNullOr(RichText.Position),\n hostId: Schema.String,\n})\nexport type EditorView = typeof EditorView.Type\n\n/** The committed edit, or the diagnostic that refused it. */\nexport type OutMessage =\n | {\n readonly _tag: 'Edited'\n readonly state: RichText.EditorState\n readonly changeSet: RichText.ChangeSet\n /** What the edit applied, in order, as `RichText.run` returns them. */\n readonly transactions: ReadonlyArray<RichText.Transaction>\n }\n | {\n /** Undo and redo replace the document wholesale; nothing was incremental. */\n readonly _tag: 'Replaced'\n readonly state: RichText.EditorState\n readonly changeSet: RichText.ChangeSet\n }\n | { readonly _tag: 'Rejected'; readonly error: string }\n\n/** Where a node stands: the block holding it (null at the top), and its index there. */\ninterface Placed {\n readonly node: RichText.Block | RichText.Text\n readonly parent: RichText.NodeId | null\n readonly index: number\n}\n\n/** Every block and run of a document by id, nested ones included, with where it stands. */\nconst nodesOf = (content: RichText.Document): ReadonlyMap<RichText.NodeId, Placed> => {\n const nodes = new Map<RichText.NodeId, Placed>()\n const visit = (blocks: ReadonlyArray<RichText.Block>, parent: RichText.NodeId | null): void => {\n for (const [index, block] of blocks.entries()) {\n nodes.set(block.id, { node: block, parent, index })\n for (const [at, run] of block.children.entries()) {\n nodes.set(run.id, { node: run, parent: block.id, index: at })\n }\n if (block.type === 'Node' && block.blocks !== undefined) visit(block.blocks, block.id)\n }\n }\n visit(content.children, null)\n return nodes\n}\n\nconst sameMarks = (left: RichText.Text, right: RichText.Text): boolean =>\n left.marks.length === right.marks.length &&\n left.marks.every((mark, index) => RichText.sameMark(mark, right.marks[index]!))\n\n/** A block's own fields, apart from what it holds: its type, level, kind and props. */\nconst ownFields = ({ children: _runs, ...block }: RichText.Block): unknown => ({\n ...block,\n blocks: undefined,\n})\n\n/**\n * What differs between two whole documents, for a patch from one to the other, in a\n * transaction's terms: the runs whose text or marks changed and the blocks holding them,\n * blocks whose own fields changed, what came and went, and whether any block moved.\n */\nexport const replaceChangeSet = (\n previous: RichText.Document,\n next: RichText.Document,\n): RichText.ChangeSet => {\n const before = nodesOf(previous)\n const after = nodesOf(next)\n const dirtyNodes = new Set<RichText.NodeId>()\n const textChanged = new Set<RichText.NodeId>()\n let structureChanged = false\n for (const [id, placed] of after) {\n const was = before.get(id)\n if (placed.node.type === 'Text') {\n const run = placed.node\n const old = was?.node.type === 'Text' ? was.node : undefined\n if (old === undefined || old.text !== run.text) textChanged.add(id)\n if (old === undefined || old.text !== run.text || !sameMarks(old, run)) {\n dirtyNodes.add(id)\n if (placed.parent !== null) dirtyNodes.add(placed.parent)\n }\n // A run moved to another block, or to another place in its own, changes both blocks'\n // runs though the run itself did not change.\n if (was !== undefined && (was.parent !== placed.parent || was.index !== placed.index)) {\n if (placed.parent !== null) dirtyNodes.add(placed.parent)\n if (was.parent !== null && after.has(was.parent)) dirtyNodes.add(was.parent)\n }\n continue\n }\n if (was === undefined || was.parent !== placed.parent || was.index !== placed.index) {\n structureChanged = true\n }\n if (\n was === undefined ||\n !Equal.equals(ownFields(was.node as RichText.Block), ownFields(placed.node))\n ) {\n dirtyNodes.add(id)\n }\n }\n const removedNodes = new Set([...before.keys()].filter(id => !after.has(id)))\n for (const id of removedNodes) {\n const was = before.get(id)!\n if (was.node.type !== 'Text') structureChanged = true\n else if (was.parent !== null && after.has(was.parent)) dirtyNodes.add(was.parent)\n }\n return {\n dirtyNodes,\n insertedNodes: new Set([...after.keys()].filter(id => !before.has(id))),\n removedNodes,\n textChanged,\n structureChanged,\n selectionChanged: false,\n }\n}\n\ntype CommandMessage = Exclude<\n Message,\n { readonly _tag: 'Undone' | 'Redone' | 'Patched' | 'ActivatedAdornment' | 'DismissedSlash' }\n>\n\nconst toCommand = (message: CommandMessage): RichText.Command => {\n switch (message._tag) {\n case 'Typed':\n return { type: 'InsertText', text: message.text }\n case 'Backspace':\n return { type: 'DeleteBackward' }\n case 'DeletedForward':\n return { type: 'DeleteForward' }\n case 'Entered':\n return { type: 'SplitBlock' }\n case 'ToggledMark':\n return { type: 'ToggleMark', mark: message.mark }\n case 'AppliedMark':\n return { type: 'SetMark', mark: message.mark }\n case 'ClearedMark':\n return { type: 'ClearMark', mark: message.mark }\n case 'RetypedBlock':\n return { type: 'RetypeBlock', to: message.block }\n case 'WrappedBlock':\n return { type: 'WrapBlock', containers: message.containers }\n case 'ConvertedBlock':\n return { type: 'ConvertBlock', to: message.to }\n case 'LiftedBlock':\n return { type: 'LiftBlock' }\n case 'MovedBlock':\n return { type: 'MoveBlock', node: message.node, to: message.to }\n case 'InsertedBlock':\n return { type: 'InsertBlock', block: message.block, to: message.to }\n case 'DeletedBlock':\n return { type: 'DeleteBlock', node: message.node }\n case 'ChangedProps':\n return { type: 'SetProps', node: message.node, props: message.props }\n case 'InsertedRow':\n return { type: 'InsertRow', where: message.where }\n case 'InsertedColumn':\n return { type: 'InsertColumn', where: message.where }\n case 'DeletedRow':\n return { type: 'DeleteRow' }\n case 'DeletedColumn':\n return { type: 'DeleteColumn' }\n case 'Indented':\n return { type: 'Indent' }\n case 'ReplacedText':\n return {\n type: 'Replace',\n query: message.query,\n replacement: message.replacement,\n caseSensitive: message.caseSensitive,\n at: message.at,\n }\n case 'InsertedFootnote':\n return { type: 'InsertFootnote', text: message.text }\n case 'RenamedFootnote':\n return { type: 'RenameFootnote', from: message.from, to: message.to }\n case 'Outdented':\n return { type: 'Outdent' }\n case 'Selected':\n return { type: 'SetSelection', selection: message.selection }\n case 'Pasted':\n return { type: 'Paste', slice: message.slice }\n }\n}\n\n/**\n * The rendering effect (§118): patch the host the view rendered, once the\n * parent's transition has committed. It commits nothing, so the one-transition\n * property the proof established is untouched.\n */\nconst patch = (hostId: string, state: RichText.EditorState, changeSet: RichText.ChangeSet) => ({\n name: 'RichText.patch',\n effect: Effect.as(\n Effect.sync(() => {\n patchEditor(hostId, state, changeSet)\n }),\n Message.Patched(),\n ),\n})\n\n/**\n * Answers an edit the editor asked for (`Attachment.answered`), last among the Commands of\n * its transition: the runtime runs them in order, so the answer follows the draw, or stands\n * alone for an edit refused or changing nothing. An application that replaces the patch\n * with its own keeps the answer after it.\n */\nconst answer = (hostId: string) => ({\n name: 'RichText.answer',\n effect: Effect.as(\n Effect.sync(() => answerEditor(hostId)),\n Message.Patched(),\n ),\n})\n\n/**\n * The text between the start of the caret's block and the caret, or `''` when the\n * selection names no caret. A menu and the input rules read this; nothing about the query\n * is stored. A range is read from its start, which is where what is typed over it lands,\n * whichever way it was dragged.\n */\nconst textBeforeOf = (model: EditorView): string => {\n if (model.selection?.type !== 'Range') return ''\n const start = RichText.rangeStart(model.document, model.selection)\n return start === undefined ? '' : RichText.textBefore(model.document, start)\n}\n\n/** Where the slash of the caret's live query is: none outside a query. */\nconst slashOf = (model: EditorView): Option.Option<RichText.Position> => {\n const query = slashQuery(textBeforeOf(model))\n if (query === undefined || model.selection?.type !== 'Range') return Option.none()\n const start = RichText.rangeStart(model.document, model.selection)\n const range =\n start === undefined\n ? undefined\n : RichText.textRangeBefore(model.document, start, query.length + 1)\n return range?.type === 'Range' ? Option.some(range.anchor) : Option.none()\n}\n\nconst dismissed = (model: EditorView): boolean =>\n Option.exists(model.slashDismissed, slash =>\n Option.exists(slashOf(model), live => live.node === slash.node && live.offset === slash.offset),\n )\n\n/**\n * The text a slash menu reads: the text before the caret, or nothing once Escape has closed\n * the menu of the query the caret is in. Enter resolves against it, and an application's menu\n * view reads it too, so the two agree on whether a menu is open.\n */\nexport const menuTextOf = (model: EditorView): string =>\n dismissed(model) ? '' : textBeforeOf(model)\n\n/**\n * The kinds of the node blocks around the caret's block, innermost first; a range is read\n * from its start, as `textBeforeOf` reads it.\n */\nconst withinOf = (model: EditorView): ReadonlyArray<string> => {\n if (model.selection?.type !== 'Range') return []\n const start = RichText.rangeStart(model.document, model.selection)\n const run = start === undefined ? undefined : RichText.locateRun(model.document, start.node)\n if (run === undefined) return []\n const kinds: Array<string> = []\n for (let depth = run.path.length - 1; depth > 0; depth--) {\n const block = RichText.blockAtPath(model.document, run.path.slice(0, depth))\n if (block?.type === 'Node') kinds.push(block.kind)\n }\n return kinds\n}\n\nconst Host = define({ tag: 'foldkit-richtext', properties: {}, events: {} })\n\n/**\n * Each host id's keys, by the document shown. The host's Mount reads its document once, so a\n * document that did not come from an edit here (a form's fill or reset, an entry opened, a\n * revision restored) needs a fresh host. A committed edit moves its document's key to the next\n * document (`carryKey`), and the document it leaves keeps none, so returning to it is a new host\n * too. The first document a host id shows has no key, as the server's render has none.\n */\nconst lineages = new Map<\n string,\n { next: number; readonly keys: WeakMap<RichText.Document, number> }\n>()\n\nconst lineageOf = (hostId: string) => {\n const found = lineages.get(hostId)\n if (found !== undefined) return found\n const created = { next: 0, keys: new WeakMap<RichText.Document, number>() }\n lineages.set(hostId, created)\n return created\n}\n\nconst hostKey = (hostId: string, document: RichText.Document): string | undefined => {\n const lineage = lineageOf(hostId)\n const key = lineage.keys.get(document) ?? lineage.next++\n lineage.keys.set(document, key)\n return key === 0 ? undefined : `${hostId}-${key}`\n}\n\nconst carryKey = (hostId: string, from: RichText.Document, to: RichText.Document): void => {\n const keys = lineageOf(hostId).keys\n const key = keys.get(from)\n if (key === undefined || from === to) return\n keys.delete(from)\n keys.set(to, key)\n}\n\n/**\n * Patches the editor at `hostId` to `next`, a document the parent put in place of\n * `previous`, keeping the host element instead of mounting a new one. A parent that\n * replaces its document outside an edit here (another replica's change arriving) returns\n * this, in the same transition as the replacement.\n *\n * The host is found by the document the view renders, so `previous` is the object it\n * rendered and `next.document` the object it will render; building the Command hands the\n * host on from one to the other, so build it only to return it. The patch itself starts\n * from the latest state the editor holds (what it has drawn, or a state waiting on a\n * composition), and `next.selection` becomes the browser selection;\n * a replacement that changes nothing drawn leaves the DOM alone, selection included, as an\n * exchange that only confirms edits already shown does.\n */\nexport const patchTo = (\n hostId: string,\n previous: RichText.Document,\n next: RichText.EditorState,\n): Command.Command<Message> => {\n carryKey(hostId, previous, next.document)\n return {\n name: 'RichText.patch',\n effect: Effect.as(\n Effect.sync(() => {\n const held = heldDocument(hostId)\n // No editor is attached there: it went away while the transition was in flight.\n if (held === undefined) return\n const changeSet = replaceChangeSet(held, next.document)\n const unchanged =\n !changeSet.structureChanged &&\n changeSet.dirtyNodes.size === 0 &&\n changeSet.removedNodes.size === 0\n if (!unchanged) patchEditor(hostId, next, changeSet)\n }),\n Message.Patched(),\n ),\n }\n}\n\n/**\n * Draws `decorations` over the editor at `hostId`, beside what its placement draws, until the\n * next overlay replaces them: decorations an application derives from its own state, such as\n * other people's carets, which a placement's `decorate` cannot see. Their positions name the\n * document shown, so an application sets them again when that changes. The caret stays put.\n */\nexport const overlay = (\n hostId: string,\n decorations: RichText.DecorationSet,\n): Command.Command<Message> => ({\n name: 'RichText.overlay',\n effect: Effect.as(\n Effect.sync(() => {\n placeOverlay(hostId, decorations)\n redecorateEditor(hostId)\n }),\n Message.Patched(),\n ),\n})\n\n/**\n * Focuses the editor at `hostId` with `selection` in the browser, brought into view: for a\n * control outside the editor that sends the reader into the text, such as a table of contents.\n * The browser reports the selection back as `Selected`, so the Model follows it either way.\n */\nexport const focus = (hostId: string, selection: RichText.Selection): Command.Command<Message> => ({\n name: 'RichText.focus',\n effect: Effect.as(\n Effect.sync(() => focusEditor(hostId, selection)),\n Message.Patched(),\n ),\n})\n\n/**\n * The host element the editor mounts into, and whose id the patch Command finds (§118). It is a\n * custom element so hydration leaves what is inside it alone (§145): on the server it holds the\n * document's markup, which the editor adopts; in the browser the view declares it empty, and the\n * editor owns everything below.\n */\nexport const editorView = Submodel.defineView<EditorView, Message>((model, h) => {\n const serverRendered = serverRenderedFor(model.hostId)()\n const key = serverRendered ? undefined : hostKey(model.hostId, model.document)\n return Host.withMessage(h)(\n [\n h.Id(model.hostId),\n ...(key === undefined ? [] : [h.Key(key)]),\n // A custom element is inline until styled, and the editor inside it is a block.\n h.Style({ display: 'block' }),\n h.OnMount(events({ content: model.document })),\n ],\n serverRendered\n ? [\n renderEditable(\n model.document,\n renderingFor(model.hostId),\n decorationsFor(model.hostId)(model.document),\n ),\n ]\n : [],\n )\n})\n\n// Built once: a matcher made inside `transition` would be rebuilt on every keystroke.\ntype TypedMessage = Extract<Message, { readonly _tag: 'Typed' }>\nconst isEntered = Match.type<Message>().pipe(\n Match.tag('Entered', () => true),\n Match.orElse(() => false),\n)\nconst typedOf = Match.type<Message>().pipe(\n Match.tag('Typed', typed => Option.some(typed)),\n Match.orElse((): Option.Option<TypedMessage> => Option.none()),\n)\nconst toggledMarkOf = Match.type<Message>().pipe(\n Match.tag('ToggledMark', ({ mark }) => Option.some(mark)),\n Match.orElse((): Option.Option<string> => Option.none()),\n)\n\n/** What a Message does to the editor; `Editor.update` carries the host's key on a commit. */\nconst transition = (\n model: EditorView,\n incoming: Message,\n): Update.ReturnWithOutMessage<EditorView, Message, OutMessage> => {\n const state: RichText.EditorState = { document: model.document, selection: model.selection }\n // The vocabulary this placement resolves edits against; both fields may be absent.\n const vocabulary = vocabularyFor(model.hostId)\n // A live query decides what Enter means before anything else reads the message\n // (§123): the highlighted entry applies as the Message a click would send, instead\n // of splitting, and a query that chooses nothing falls through to `incoming` and\n // still splits. Substituting the Message here rather than recursing is what makes a\n // mark entry update the caret's stored marks through the path a toggle already uses.\n const menu = isEntered(incoming)\n ? slashMenu(slashEntriesFor(model.hostId) ?? slashEntries, menuTextOf(model), model.menuIndex)\n : undefined\n const message = menu?.highlighted?.message ?? incoming\n return Match.value(message).pipe(\n Match.withReturnType<Update.ReturnWithOutMessage<EditorView, Message, OutMessage>>(),\n Match.tags({\n // The patch Command's own completion: the render already happened.\n Patched: () => ({ model }),\n // A click asking the application for something, not an edit: nothing changes here.\n ActivatedAdornment: () => ({ model }),\n DismissedSlash: () => {\n const slash = slashOf(model)\n return Option.isNone(slash) || dismissed(model)\n ? { model }\n : { model: { ...model, slashDismissed: slash } }\n },\n Undone: undone => travel(model, state, undone),\n Redone: redone => travel(model, state, redone),\n }),\n Match.orElse(message => {\n // Every edit the editor asks for is answered, once drawn or refused; a reported caret\n // is not an ask.\n const caretMoved = message._tag === 'Selected'\n const answering = caretMoved ? [] : [answer(model.hostId)]\n let nextId = model.nextId\n // With nothing selected, a mark toggle is the caret's own state: the next\n // typed text lands with it. A caret move ends the format it was carrying.\n const collapsed =\n model.selection?.type === 'Range' &&\n model.selection.anchor.node === model.selection.focus.node &&\n model.selection.anchor.offset === model.selection.focus.offset\n // The caret never carries a mark the vocabulary cannot type — an unknown one, or one\n // like Link whose props a bare name lacks — so it is refused here rather than at the\n // first keystroke after it.\n const toggling = toggledMarkOf(message)\n if (\n Option.isSome(toggling) &&\n collapsed &&\n !(vocabulary.marks ?? RichText.shippedRegistry).accepts(toggling.value)\n ) {\n return {\n model,\n outMessage: { _tag: 'Rejected', error: 'InvalidInput' },\n commands: answering,\n }\n }\n const toggled = (mark: string) => {\n if (!collapsed) return model.storedMarks\n if (model.storedMarks?.includes(mark))\n return model.storedMarks.filter(held => held !== mark)\n return [...(model.storedMarks ?? []), mark]\n }\n const storedMarks = Option.match(toggling, {\n onNone: () => model.storedMarks,\n onSome: toggled,\n })\n const typing = typedOf(message)\n const command = Option.match(typing, {\n onNone: () => toCommand(message),\n onSome: typed =>\n storedMarks === null\n ? toCommand(typed)\n : ({ type: 'InsertText', text: typed.text, marks: storedMarks } as const),\n })\n // Choosing an entry removes the query it was typed into and applies the choice as\n // one action (§124 §5), so one transition and one undo step cover both. The range is\n // a read, not state: `menu.query` is the text the document already holds, and the\n // `+ 1` is the slash that opened it.\n const queryRange =\n menu === undefined || menu.highlighted === undefined || model.selection?.type !== 'Range'\n ? undefined\n : RichText.textRangeBefore(model.document, model.selection.anchor, menu.query.length + 1)\n const runAction = (action: RichText.Action) =>\n RichText.runAction(\n state,\n action,\n { mint: () => `e${nextId++}` },\n { marks: vocabulary.marks, nodes: vocabulary.nodes },\n )\n // What is typed can be a block marker (§124 §4): the rules are the placement's own, so\n // the editor carries none of any syntax's vocabulary itself.\n const ruled = Option.match(typing, {\n onNone: () => undefined,\n onSome: typed =>\n queryRange === undefined\n ? RichText.resolveInputRules(inputRulesFor(model.hostId), {\n textBefore: textBeforeOf(model),\n text: typed.text,\n insertion: command,\n within: withinOf(model),\n })\n : undefined,\n })\n const attempt = runAction(\n queryRange !== undefined\n ? [{ type: 'SetSelection', selection: queryRange }, { type: 'DeleteBackward' }, command]\n : (ruled?.action ?? [command]),\n )\n // A rule the vocabulary refuses — a fence typed into bold text, say — leaves the marker\n // as the text it is, rather than refusing the keystroke along with it. The refused\n // attempt's identities are given back first.\n const fallback = !attempt.ok && ruled !== undefined\n if (fallback) nextId = model.nextId\n const result = fallback ? runAction([command]) : attempt\n if (!result.ok) {\n // A refused command changes nothing, so it does not burn identities.\n return { model, outMessage: { _tag: 'Rejected', error: result.error }, commands: answering }\n }\n // `runAction` returns the state it was given when nothing changed: a caret the browser\n // echoes back, or a command with nothing to act on. Keeping the Model keeps the page from\n // rendering, and a caret that did not move keeps the format it carries.\n if (result.state === state && storedMarks === model.storedMarks) {\n return { model, commands: answering }\n }\n // History holds content, not cursor movement: a selection change keeps the\n // redo stack, and a no-op edit adds no step to undo.\n const contentChanged = result.state.document !== model.document\n return {\n model: {\n ...model,\n selection: result.state.selection,\n nextId,\n // A caret move ends the format it was carrying; a rule that applied may set the next.\n storedMarks: caretMoved\n ? null\n : ((fallback ? undefined : ruled?.storedMarks) ?? storedMarks),\n history: contentChanged\n ? RichText.commit(model.history, state, { group: RichText.groupFor(command) })\n : model.history,\n },\n outMessage: {\n _tag: 'Edited',\n state: result.state,\n changeSet: result.changeSet,\n transactions: result.transactions,\n },\n commands: [patch(model.hostId, result.state, result.changeSet), ...answering],\n }\n }),\n )\n}\n\n/** Undo or redo: history restores content and selection, and the host is told as a replace. */\nconst travel = (\n model: EditorView,\n state: RichText.EditorState,\n message: Extract<Message, { readonly _tag: 'Undone' | 'Redone' }>,\n): Update.ReturnWithOutMessage<EditorView, Message, OutMessage> => {\n const restored = Match.valueTags(message, {\n Undone: () => RichText.undo(model.history, state),\n Redone: () => RichText.redo(model.history, state),\n })\n if (restored === undefined) {\n return {\n model,\n outMessage: {\n _tag: 'Rejected',\n error: Match.valueTags(message, {\n Undone: () => 'NothingToUndo' as const,\n Redone: () => 'NothingToRedo' as const,\n }),\n },\n }\n }\n const changeSet = replaceChangeSet(model.document, restored.state.document)\n return {\n model: {\n ...model,\n selection: restored.state.selection,\n history: restored.history,\n },\n outMessage: { _tag: 'Replaced', state: restored.state, changeSet },\n commands: [patch(model.hostId, restored.state, changeSet)],\n }\n}\n\nexport const Editor = Bundle.make({\n name: 'RichTextEditor',\n Model: EditorView,\n Message,\n args: Schema.Struct({ hostId: Schema.String }),\n // A read-only projection has no initial content of its own; the placement\n // writes only the interaction fields back, so this never becomes the document.\n // The host id is the one thing the child seeds: it is per-placement, and `read`\n // re-projects everything else.\n init: args => ({\n model: {\n document: RichText.Document.make({ version: 1, children: [] }),\n selection: null,\n nextId: 0,\n history: RichText.emptyHistory,\n storedMarks: null,\n menuIndex: 0,\n slashDismissed: Option.none(),\n hostId: args.hostId,\n },\n }),\n update: (model, incoming): Update.ReturnWithOutMessage<EditorView, Message, OutMessage> => {\n const result = transition(model, incoming)\n const out = result.outMessage\n // An edit or a replacement carries the state it committed; a rejection, none.\n const committed = out !== undefined && 'state' in out ? out.state : undefined\n if (committed !== undefined) carryKey(model.hostId, model.document, committed.document)\n // A dismissal ends with the query it closed, so a slash typed later opens a menu again.\n // The child's document is the parent's, so the edit is read from what it committed.\n const after = { ...result.model, document: committed?.document ?? result.model.document }\n return Option.isSome(result.model.slashDismissed) && !dismissed(after)\n ? { ...result, model: { ...result.model, slashDismissed: Option.none() } }\n : result\n },\n // The host element belongs to the view; everything below it belongs to the\n // interpreter the mount attaches there.\n view: editorView,\n})\n\nconst GotEditor = Link.wrapper('GotEditorMessage', Message)\n\nexport const ParentMessage = defineMessageUnion({ ...GotEditor.cases })\nexport type ParentMessage = typeof ParentMessage.Type\n\nconst editorLink: Link<\n Model,\n Wrapped<'GotEditorMessage', Message>,\n EditorView,\n Message\n> = Link.make({\n // The child sees the parent's document; the parent owns it.\n read: parent =>\n Option.some({\n document: parent.document,\n selection: parent.editor.selection,\n nextId: parent.editor.nextId,\n history: parent.editor.history,\n storedMarks: parent.editor.storedMarks,\n menuIndex: parent.editor.menuIndex,\n slashDismissed: parent.editor.slashDismissed,\n hostId: parent.editor.hostId,\n }),\n // Only interaction state is written back: the document is not the child's. `read` builds a\n // new view each time, so the Bundle cannot see that a child returned what it read; a child\n // whose fields are the parent's own leaves the parent as it was. The placement's `init`\n // writes into a parent that has no `editor` yet.\n write: (parent, { document: _document, ...editor }) => {\n const current: EditorState | undefined = parent.editor\n return current !== undefined &&\n (Object.keys(editor) as Array<keyof EditorState>).every(key => editor[key] === current[key])\n ? parent\n : { ...parent, editor }\n },\n wrapper: GotEditor,\n path: ['editor'],\n})\n\n/**\n * What a placement gives its editor. Each is placed by host id rather than passed as an arg\n * (§122): each holds functions or schemas, which a schema-decoded arg cannot describe.\n */\nexport interface EditorPlacement {\n /** How this editor's marks and node kinds render (§121). Defaults to `noRendering`. */\n readonly rendering?: RichText.Rendering | undefined\n /** The vocabulary its edits resolve against (§125). Defaults to none. */\n readonly vocabulary?: Vocabulary | undefined\n /** The rules applied to what is typed (§128). Defaults to none. */\n readonly inputRules?: ReadonlyArray<RichText.InputRule> | undefined\n /**\n * What is drawn over the document (§129), derived from it on every patch: code\n * highlighting, for one. Defaults to nothing.\n */\n readonly decorate?: Decorate | undefined\n /** What the editor shows while its document is blank (`RichText.isBlank`). Defaults to none. */\n readonly placeholder?: string | undefined\n /**\n * What the empty paragraph the caret is in shows, such as \"Type / for blocks\", as\n * `data-hint` on it. Defaults to none.\n */\n readonly lineHint?: string | undefined\n /**\n * Chords beside the built-in ones (Bold, Italic, Code, Strikethrough, undo and redo), checked\n * first: `{ chord: 'Mod-u', command: { type: 'ToggleMark', mark: 'Underline' } }` for a mark\n * of the application's own. Defaults to none.\n */\n readonly keymap?: ReadonlyArray<KeyBinding> | undefined\n /**\n * What its slash menu offers, which Enter resolves against: an application's own entries\n * (an image, a table, a callout) beside or instead of `slashEntries`. The application's menu\n * view draws the same list. Defaults to `slashEntries`.\n */\n readonly slashEntries?: ReadonlyArray<SlashEntry<EditorEvent>> | undefined\n /**\n * Whether this render is the server's (§145), such as `foldkit-ssr`'s `SSR.serving`. While it\n * is, the host carries the document's markup, which the browser's editor adopts instead of\n * drawing again. Defaults to never, so the host is empty until the editor mounts.\n */\n readonly serverRendered?: (() => boolean) | undefined\n}\n\n/**\n * Records everything a placement names for its host id, replacing what the id had: one call,\n * so a placement cannot half-apply. `editorAt` and the form control both place through it.\n */\nexport const placeEditor = (hostId: string, placement: EditorPlacement): void => {\n placeRendering(hostId, placement.rendering ?? RichText.noRendering)\n placeVocabulary(hostId, placement.vocabulary ?? {})\n placeInputRules(hostId, placement.inputRules ?? [])\n placeDecorations(hostId, placement.decorate ?? (() => []))\n // The overlay is set by Commands after placing, so a placement starts without the last one's.\n placeOverlay(hostId, [])\n placePlaceholders(hostId, {\n placeholder: placement.placeholder,\n lineHint: placement.lineHint,\n })\n placeSlashEntries(hostId, placement.slashEntries)\n placeKeymap(hostId, placement.keymap ?? [])\n placeServerRendered(hostId, placement.serverRendered ?? (() => false))\n}\n\n/**\n * Places one editor, bound to the host element the view renders and the patch Command\n * finds. Each placement picks its own id and names what it places; one call records all\n * of it, so a placement cannot half-apply.\n */\nexport const editorAt = (hostId: string, placement: EditorPlacement = {}) => {\n placeEditor(hostId, placement)\n const adopted = (parent: Model, state: RichText.EditorState) => ({\n model: {\n ...parent,\n document: state.document,\n editor: { ...parent.editor, selection: state.selection },\n },\n })\n return Editor.at(editorLink, {\n args: { hostId },\n // Runs with the child already written back, in the same parent transition.\n onOut: (out: OutMessage) => (parent: Model) =>\n Match.valueTags(out, {\n Edited: ({ state }) => adopted(parent, state),\n Replaced: ({ state }) => adopted(parent, state),\n Rejected: () => ({ model: parent }),\n }),\n })\n}\n\nexport const editor = editorAt('richtext-editor')\n\nexport const application = Bundle.assemble<Model, ParentMessage>()([editor])\n\n/** The parent's update: placement Messages route to the editor, others stand still. */\nexport const update = application.update()\n\n/** Wraps one editor Message as the parent Message that carries it. */\nexport const edited = (message: Message): ParentMessage => GotEditor.make(message)\nexport const typed = (text: string): ParentMessage => edited(Message.Typed({ text }))\nexport const pressed = (tag: 'Backspace' | 'DeletedForward' | 'Entered'): ParentMessage =>\n edited(Message[tag]())\nexport const toggled = (mark: string): ParentMessage => edited(Message.ToggledMark({ mark }))\nexport const applied = (mark: RichText.RunMark): ParentMessage =>\n edited(Message.AppliedMark({ mark }))\nexport const cleared = (mark: string): ParentMessage => edited(Message.ClearedMark({ mark }))\nexport const retyped = (block: RichText.TextBlock): ParentMessage =>\n edited(Message.RetypedBlock({ block }))\nexport const wrapped = (containers: ReadonlyArray<RichText.Container>): ParentMessage =>\n edited(Message.WrappedBlock({ containers }))\nexport const converted = (to: RichText.Container): ParentMessage =>\n edited(Message.ConvertedBlock({ to }))\nexport const lifted = (): ParentMessage => edited(Message.LiftedBlock())\nexport const moved = (node: RichText.NodeId, to: RichText.Beside): ParentMessage =>\n edited(Message.MovedBlock({ node, to }))\nexport const inserted = (block: RichText.Block, to?: RichText.Beside): ParentMessage =>\n edited(Message.InsertedBlock(to === undefined ? { block } : { block, to }))\nexport const deleted = (node: RichText.NodeId): ParentMessage =>\n edited(Message.DeletedBlock({ node }))\nexport const changedProps = (node: RichText.NodeId, props: Schema.JsonObject): ParentMessage =>\n edited(Message.ChangedProps({ node, props }))\nexport const selected = (selection: RichText.Selection | null): ParentMessage =>\n edited(Message.Selected({ selection }))\nexport const undone = (): ParentMessage => edited(Message.Undone())\nexport const redone = (): ParentMessage => edited(Message.Redone())\nexport const patched = (): ParentMessage => edited(Message.Patched())\n"],"mappings":"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"}
|
package/dist/input.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.mts","names":[],"sources":["../src/input.ts"],"mappings":";;;;;qBAyCa,aAAW,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"input.d.mts","names":[],"sources":["../src/input.ts"],"mappings":";;;;;qBAyCa,aAAW,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAiCX,gBAAa,gBAAkB,YAAa,2CAAe"}
|
package/dist/input.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Message } from "./editor.mjs";
|
|
2
2
|
import { Editor, EditorView, editorView, placeEditor } from "./editor-bundle.mjs";
|
|
3
3
|
import * as RichText from "foldkit-richtext";
|
|
4
|
-
import { Option, Schema } from "effect";
|
|
4
|
+
import { Match, Option, Schema } from "effect";
|
|
5
5
|
import { Bundle } from "foldkit-bundle";
|
|
6
6
|
import { Input } from "foldkit-form";
|
|
7
7
|
// src/input.ts
|
|
@@ -45,7 +45,11 @@ const EditorInput = Bundle.make({
|
|
|
45
45
|
} }),
|
|
46
46
|
update: (model, message, args) => {
|
|
47
47
|
const { model: next, outMessage, commands } = Editor.update(model, message, args);
|
|
48
|
-
const committed = outMessage
|
|
48
|
+
const committed = outMessage === void 0 ? void 0 : Match.valueTags(outMessage, {
|
|
49
|
+
Edited: ({ state }) => state,
|
|
50
|
+
Replaced: ({ state }) => state,
|
|
51
|
+
Rejected: () => void 0
|
|
52
|
+
});
|
|
49
53
|
return {
|
|
50
54
|
model: committed === void 0 ? next : {
|
|
51
55
|
...next,
|
package/dist/input.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.mjs","names":[],"sources":["../src/input.ts"],"sourcesContent":["/**\n * The rich-text editor as a form control (§124 §12, §139): a document a `foldkit-form` key\n * validates and submits.\n *\n * The editor Bundle leaves the document to its parent and reports each edit as an\n * OutMessage. A form key has no parent to report to — `Input.bundle` refuses an OutMessage,\n * which would otherwise be dropped — so this Bundle is the same editor holding its document\n * in its own Model: its `update` is the editor's, with each committed edit folded back in.\n * The form owns that Model as the key's draft, and the document is the key's value.\n */\nimport { Option, Schema } from 'effect'\nimport { Bundle } from 'foldkit-bundle'\nimport { Input } from 'foldkit-form'\nimport * as RichText from 'foldkit-richtext'\nimport { Message } from './editor.js'\nimport {\n Editor,\n EditorView,\n editorView,\n placeEditor,\n type EditorPlacement,\n} from './editor-bundle.js'\n\n/**\n * A document with one empty paragraph: what a new record's editor starts with, since a\n * caret needs a block to sit in. Its identities are fixed, and the editor mints `e`-prefixed\n * ones after it, so they never meet.\n */\nconst blank = (): RichText.Document =>\n RichText.decodeDocument({\n version: 1,\n children: [\n {\n type: 'Paragraph',\n id: 'blank',\n children: [{ type: 'Text', id: 'blank-t', text: '', marks: [] }],\n },\n ],\n })\n\n/** The editor with its document in its own Model: no parent, so no OutMessage. */\nexport const EditorInput = Bundle.make({\n name: 'RichTextInput',\n Model: EditorView,\n Message,\n args: Schema.Struct({ hostId: Schema.String }),\n init: args => ({ model: { ...Editor.init(args).model, document: blank() } }),\n update: (model, message, args) => {\n const { model: next, outMessage, commands } = Editor.update(model, message, args)\n // An edit and an undo carry the state to commit; a refusal changes nothing to keep.\n const committed =\n outMessage
|
|
1
|
+
{"version":3,"file":"input.mjs","names":[],"sources":["../src/input.ts"],"sourcesContent":["/**\n * The rich-text editor as a form control (§124 §12, §139): a document a `foldkit-form` key\n * validates and submits.\n *\n * The editor Bundle leaves the document to its parent and reports each edit as an\n * OutMessage. A form key has no parent to report to — `Input.bundle` refuses an OutMessage,\n * which would otherwise be dropped — so this Bundle is the same editor holding its document\n * in its own Model: its `update` is the editor's, with each committed edit folded back in.\n * The form owns that Model as the key's draft, and the document is the key's value.\n */\nimport { Match, Option, Schema } from 'effect'\nimport { Bundle } from 'foldkit-bundle'\nimport { Input } from 'foldkit-form'\nimport * as RichText from 'foldkit-richtext'\nimport { Message } from './editor.js'\nimport {\n Editor,\n EditorView,\n editorView,\n placeEditor,\n type EditorPlacement,\n} from './editor-bundle.js'\n\n/**\n * A document with one empty paragraph: what a new record's editor starts with, since a\n * caret needs a block to sit in. Its identities are fixed, and the editor mints `e`-prefixed\n * ones after it, so they never meet.\n */\nconst blank = (): RichText.Document =>\n RichText.decodeDocument({\n version: 1,\n children: [\n {\n type: 'Paragraph',\n id: 'blank',\n children: [{ type: 'Text', id: 'blank-t', text: '', marks: [] }],\n },\n ],\n })\n\n/** The editor with its document in its own Model: no parent, so no OutMessage. */\nexport const EditorInput = Bundle.make({\n name: 'RichTextInput',\n Model: EditorView,\n Message,\n args: Schema.Struct({ hostId: Schema.String }),\n init: args => ({ model: { ...Editor.init(args).model, document: blank() } }),\n update: (model, message, args) => {\n const { model: next, outMessage, commands } = Editor.update(model, message, args)\n // An edit and an undo carry the state to commit; a refusal changes nothing to keep.\n const committed =\n outMessage === undefined\n ? undefined\n : Match.valueTags(outMessage, {\n Edited: ({ state }) => state,\n Replaced: ({ state }) => state,\n Rejected: () => undefined,\n })\n return {\n model:\n committed === undefined\n ? next\n : { ...next, document: committed.document, selection: committed.selection },\n ...(commands === undefined ? {} : { commands }),\n }\n },\n view: editorView,\n})\n\n/**\n * A form control editing a document, placed at `hostId` with everything `editorAt` places\n * (`EditorPlacement`). A blank\n * document (`RichText.isBlank`) is nothing entered, so a required key refuses it.\n */\nexport const richTextInput = (hostId: string, placement: EditorPlacement = {}) => {\n placeEditor(hostId, placement)\n return Input.bundle('RichText', {\n bundle: EditorInput,\n args: { hostId },\n value: model => (RichText.isBlank(model.document) ? undefined : model.document),\n // A given document starts its own history: undoing past it would reach the form's blank.\n fill: (model, document: RichText.Document) => ({\n ...model,\n document,\n selection: null,\n history: RichText.emptyHistory,\n storedMarks: null,\n hostId,\n }),\n // A stored draft shown again keeps its caret, format, and history (§124 §12); only the slash\n // menu's highlight and dismissal belong to the moment it was typed in. The host is this placement's, not\n // whichever one the draft was stored from.\n settled: model => ({ ...model, menuIndex: 0, slashDismissed: Option.none(), hostId }),\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAM,cACJ,SAAS,eAAe;CACtB,SAAS;CACT,UAAU,CACR;EACE,MAAM;EACN,IAAI;EACJ,UAAU,CAAC;GAAE,MAAM;GAAQ,IAAI;GAAW,MAAM;GAAI,OAAO,CAAC;EAAE,CAAC;CACjE,CACF;AACF,CAAC;;AAGH,MAAa,cAAc,OAAO,KAAK;CACrC,MAAM;CACN,OAAO;CACP;CACA,MAAM,OAAO,OAAO,EAAE,QAAQ,OAAO,OAAO,CAAC;CAC7C,OAAM,UAAS,EAAE,OAAO;EAAE,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC;EAAO,UAAU,MAAM;CAAE,EAAE;CAC1E,SAAS,OAAO,SAAS,SAAS;EAChC,MAAM,EAAE,OAAO,MAAM,YAAY,aAAa,OAAO,OAAO,OAAO,SAAS,IAAI;EAEhF,MAAM,YACJ,eAAe,KAAA,IACX,KAAA,IACA,MAAM,UAAU,YAAY;GAC1B,SAAS,EAAE,YAAY;GACvB,WAAW,EAAE,YAAY;GACzB,gBAAgB,KAAA;EAClB,CAAC;EACP,OAAO;GACL,OACE,cAAc,KAAA,IACV,OACA;IAAE,GAAG;IAAM,UAAU,UAAU;IAAU,WAAW,UAAU;GAAU;GAC9E,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAC/C;CACF;CACA,MAAM;AACR,CAAC;;;;;;AAOD,MAAa,iBAAiB,QAAgB,YAA6B,CAAC,MAAM;CAChF,YAAY,QAAQ,SAAS;CAC7B,OAAO,MAAM,OAAO,YAAY;EAC9B,QAAQ;EACR,MAAM,EAAE,OAAO;EACf,QAAO,UAAU,SAAS,QAAQ,MAAM,QAAQ,IAAI,KAAA,IAAY,MAAM;EAEtE,OAAO,OAAO,cAAiC;GAC7C,GAAG;GACH;GACA,WAAW;GACX,SAAS,SAAS;GAClB,aAAa;GACb;EACF;EAIA,UAAS,WAAU;GAAE,GAAG;GAAO,WAAW;GAAG,gBAAgB,OAAO,KAAK;GAAG;EAAO;CACrF,CAAC;AACH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foldkit-richtext-dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "The DOM interpreter for a foldkit-richtext editable subtree.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"foldkit",
|
|
@@ -80,13 +80,13 @@
|
|
|
80
80
|
"LICENSE"
|
|
81
81
|
],
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"foldkit-richtext": "0.
|
|
83
|
+
"foldkit-richtext": "0.9.0"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
86
|
"effect": "^4.1.0",
|
|
87
87
|
"foldkit": "^0.170.0",
|
|
88
|
-
"foldkit-bundle": "^0.
|
|
89
|
-
"foldkit-form": "^0.
|
|
88
|
+
"foldkit-bundle": "^0.12.0",
|
|
89
|
+
"foldkit-form": "^0.12.0"
|
|
90
90
|
},
|
|
91
91
|
"peerDependenciesMeta": {
|
|
92
92
|
"foldkit-form": {
|
|
@@ -99,9 +99,9 @@
|
|
|
99
99
|
"jsdom": "30.1.3",
|
|
100
100
|
"tsdown": "^0.23.1",
|
|
101
101
|
"typescript": "7.0.2",
|
|
102
|
-
"foldkit-bundle": "0.
|
|
103
|
-
"foldkit-
|
|
104
|
-
"foldkit-
|
|
102
|
+
"foldkit-bundle": "0.12.0",
|
|
103
|
+
"foldkit-entity": "0.17.0",
|
|
104
|
+
"foldkit-form": "0.12.0"
|
|
105
105
|
},
|
|
106
106
|
"scripts": {
|
|
107
107
|
"build": "node ../../scripts/declarations.mjs . && tsdown && node ../../scripts/declared-any.mjs .",
|