@k2b/ui 0.40.0 → 0.42.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.
Files changed (135) hide show
  1. package/dist/base.css +22 -0
  2. package/dist/browser/actions/Dropdown.js +220 -110
  3. package/dist/browser/actions/Dropdown.js.map +1 -1
  4. package/dist/browser/actions/index.js.map +1 -1
  5. package/dist/browser/actions/menu-search.js +166 -0
  6. package/dist/browser/actions/menu-search.js.map +1 -0
  7. package/dist/browser/actions/menu-sheet.js +107 -13
  8. package/dist/browser/actions/menu-sheet.js.map +1 -1
  9. package/dist/browser/chat/ChatComposer.js +759 -570
  10. package/dist/browser/chat/ChatComposer.js.map +1 -1
  11. package/dist/browser/content/CalendarWeekView.js +13 -1
  12. package/dist/browser/content/CalendarWeekView.js.map +1 -1
  13. package/dist/browser/inputs/ChoiceGroups.js +30 -18
  14. package/dist/browser/inputs/ChoiceGroups.js.map +1 -1
  15. package/dist/browser/inputs/IconChoices.js +121 -0
  16. package/dist/browser/inputs/IconChoices.js.map +1 -0
  17. package/dist/browser/inputs/Select.js +32 -19
  18. package/dist/browser/inputs/Select.js.map +1 -1
  19. package/dist/browser/inputs/SpecialInputs.js +54 -25
  20. package/dist/browser/inputs/SpecialInputs.js.map +1 -1
  21. package/dist/browser/inputs/choice.js +4 -1
  22. package/dist/browser/inputs/choice.js.map +1 -1
  23. package/dist/browser/inputs/dictation/Dictation.js +546 -0
  24. package/dist/browser/inputs/dictation/Dictation.js.map +1 -0
  25. package/dist/browser/inputs/dictation/create-dictation.js +324 -0
  26. package/dist/browser/inputs/dictation/create-dictation.js.map +1 -0
  27. package/dist/browser/inputs/dictation/dictation-messages.js +115 -0
  28. package/dist/browser/inputs/dictation/dictation-messages.js.map +1 -0
  29. package/dist/browser/inputs/dictation/text-dictation.js +152 -0
  30. package/dist/browser/inputs/dictation/text-dictation.js.map +1 -0
  31. package/dist/browser/inputs/dictation/types.js +2 -0
  32. package/dist/browser/inputs/dictation/types.js.map +1 -0
  33. package/dist/browser/inputs/icon-messages.js +29 -2
  34. package/dist/browser/inputs/icon-messages.js.map +1 -1
  35. package/dist/browser/inputs/icon-texts.js +172 -0
  36. package/dist/browser/inputs/icon-texts.js.map +1 -0
  37. package/dist/browser/inputs/index.js +3 -0
  38. package/dist/browser/inputs/index.js.map +1 -1
  39. package/dist/browser/intl/messages.js +6 -0
  40. package/dist/browser/intl/messages.js.map +1 -1
  41. package/dist/browser/layout/AnchoredPopover.js +1 -1
  42. package/dist/browser/layout/AnchoredPopover.js.map +1 -1
  43. package/dist/browser/layout/AppWorkspace.js +118 -93
  44. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  45. package/dist/browser/layout/SettingsModal.js +10 -5
  46. package/dist/browser/layout/SettingsModal.js.map +1 -1
  47. package/dist/browser/text-editor/TextEditor.js +74 -25
  48. package/dist/browser/text-editor/TextEditor.js.map +1 -1
  49. package/dist/browser/text-editor/dictation.js +166 -0
  50. package/dist/browser/text-editor/dictation.js.map +1 -0
  51. package/dist/browser/text-editor/format.js +11 -7
  52. package/dist/browser/text-editor/format.js.map +1 -1
  53. package/dist/browser/text-editor/table.js +39 -11
  54. package/dist/browser/text-editor/table.js.map +1 -1
  55. package/dist/browser/text-editor.js +2 -1
  56. package/dist/browser/text-editor.js.map +1 -1
  57. package/dist/ssr/actions/Dropdown.js +118 -40
  58. package/dist/ssr/actions/Dropdown.js.map +1 -1
  59. package/dist/ssr/actions/index.js.map +1 -1
  60. package/dist/ssr/actions/menu-search.js +113 -0
  61. package/dist/ssr/actions/menu-search.js.map +1 -0
  62. package/dist/ssr/actions/menu-sheet.js +82 -13
  63. package/dist/ssr/actions/menu-sheet.js.map +1 -1
  64. package/dist/ssr/chat/ChatComposer.js +271 -99
  65. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  66. package/dist/ssr/content/CalendarWeekView.js +13 -1
  67. package/dist/ssr/content/CalendarWeekView.js.map +1 -1
  68. package/dist/ssr/inputs/ChoiceGroups.js +12 -3
  69. package/dist/ssr/inputs/ChoiceGroups.js.map +1 -1
  70. package/dist/ssr/inputs/IconChoices.js +59 -0
  71. package/dist/ssr/inputs/IconChoices.js.map +1 -0
  72. package/dist/ssr/inputs/Select.js +8 -2
  73. package/dist/ssr/inputs/Select.js.map +1 -1
  74. package/dist/ssr/inputs/SpecialInputs.js +54 -25
  75. package/dist/ssr/inputs/SpecialInputs.js.map +1 -1
  76. package/dist/ssr/inputs/choice.js +4 -1
  77. package/dist/ssr/inputs/choice.js.map +1 -1
  78. package/dist/ssr/inputs/dictation/Dictation.js +386 -0
  79. package/dist/ssr/inputs/dictation/Dictation.js.map +1 -0
  80. package/dist/ssr/inputs/dictation/create-dictation.js +324 -0
  81. package/dist/ssr/inputs/dictation/create-dictation.js.map +1 -0
  82. package/dist/ssr/inputs/dictation/dictation-messages.js +115 -0
  83. package/dist/ssr/inputs/dictation/dictation-messages.js.map +1 -0
  84. package/dist/ssr/inputs/dictation/text-dictation.js +152 -0
  85. package/dist/ssr/inputs/dictation/text-dictation.js.map +1 -0
  86. package/dist/ssr/inputs/dictation/types.js +2 -0
  87. package/dist/ssr/inputs/dictation/types.js.map +1 -0
  88. package/dist/ssr/inputs/icon-messages.js +29 -2
  89. package/dist/ssr/inputs/icon-messages.js.map +1 -1
  90. package/dist/ssr/inputs/icon-texts.js +172 -0
  91. package/dist/ssr/inputs/icon-texts.js.map +1 -0
  92. package/dist/ssr/inputs/index.js +3 -0
  93. package/dist/ssr/inputs/index.js.map +1 -1
  94. package/dist/ssr/intl/messages.js +6 -0
  95. package/dist/ssr/intl/messages.js.map +1 -1
  96. package/dist/ssr/layout/AnchoredPopover.js +1 -1
  97. package/dist/ssr/layout/AnchoredPopover.js.map +1 -1
  98. package/dist/ssr/layout/AppWorkspace.js +40 -20
  99. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  100. package/dist/ssr/layout/SettingsModal.js +10 -5
  101. package/dist/ssr/layout/SettingsModal.js.map +1 -1
  102. package/dist/ssr/text-editor/TextEditor.js +74 -25
  103. package/dist/ssr/text-editor/TextEditor.js.map +1 -1
  104. package/dist/ssr/text-editor/dictation.js +166 -0
  105. package/dist/ssr/text-editor/dictation.js.map +1 -0
  106. package/dist/ssr/text-editor/format.js +11 -7
  107. package/dist/ssr/text-editor/format.js.map +1 -1
  108. package/dist/ssr/text-editor/table.js +39 -11
  109. package/dist/ssr/text-editor/table.js.map +1 -1
  110. package/dist/ssr/text-editor.js +2 -1
  111. package/dist/ssr/text-editor.js.map +1 -1
  112. package/dist/styles.css +1 -1
  113. package/dist/types/actions/Dropdown.d.ts +34 -3
  114. package/dist/types/actions/index.d.ts +1 -1
  115. package/dist/types/actions/menu-search.d.ts +27 -0
  116. package/dist/types/actions/menu-sheet.d.ts +5 -1
  117. package/dist/types/chat/ChatComposer.d.ts +9 -0
  118. package/dist/types/inputs/IconChoices.d.ts +19 -0
  119. package/dist/types/inputs/SpecialInputs.d.ts +8 -0
  120. package/dist/types/inputs/dictation/Dictation.d.ts +26 -0
  121. package/dist/types/inputs/dictation/create-dictation.d.ts +77 -0
  122. package/dist/types/inputs/dictation/dictation-messages.d.ts +54 -0
  123. package/dist/types/inputs/dictation/text-dictation.d.ts +36 -0
  124. package/dist/types/inputs/dictation/types.d.ts +74 -0
  125. package/dist/types/inputs/icon-messages.d.ts +14 -0
  126. package/dist/types/inputs/icon-texts.d.ts +13 -0
  127. package/dist/types/inputs/index.d.ts +7 -0
  128. package/dist/types/intl/messages.d.ts +3 -0
  129. package/dist/types/layout/AnchoredPopover.d.ts +5 -0
  130. package/dist/types/layout/AppWorkspace.d.ts +6 -1
  131. package/dist/types/text-editor/TextEditor.d.ts +10 -1
  132. package/dist/types/text-editor/dictation.d.ts +20 -0
  133. package/dist/types/text-editor/table.d.ts +2 -0
  134. package/dist/types/text-editor.d.ts +2 -1
  135. package/package.json +3 -1
@@ -0,0 +1,166 @@
1
+ import { isolateHistory } from "@codemirror/commands";
2
+ import { StateEffect, StateField, Transaction } from "@codemirror/state";
3
+ import { Decoration, EditorView, WidgetType } from "@codemirror/view";
4
+ const dictatedMark = Decoration.mark({
5
+ class: "k2b-dictation-dictated"
6
+ });
7
+ const freshMark = at => Decoration.mark({
8
+ class: "k2b-dictation-fresh",
9
+ at
10
+ });
11
+ class MarkWidget extends WidgetType {
12
+ eq() {
13
+ return true;
14
+ }
15
+ toDOM() {
16
+ const mark = document.createElement("span");
17
+ mark.className = "k2b-dictation-mark";
18
+ mark.setAttribute("aria-hidden", "true");
19
+ return mark;
20
+ }
21
+ ignoreEvent() {
22
+ return true;
23
+ }
24
+ }
25
+ const open = StateEffect.define();
26
+ const inserted = StateEffect.define();
27
+ /** Removes the tint of words older than the given time. */
28
+ const settle = StateEffect.define();
29
+ const close = StateEffect.define();
30
+ const empty = {
31
+ mark: null,
32
+ dictated: Decoration.none,
33
+ fresh: Decoration.none
34
+ };
35
+ const dictationField = StateField.define({
36
+ create: () => empty,
37
+ update(value, tr) {
38
+ let next = tr.docChanged ? {
39
+ // Edits elsewhere move the mark along; text typed exactly at the mark stays before the next words.
40
+ mark: value.mark === null ? null : tr.changes.mapPos(value.mark, 1),
41
+ dictated: value.dictated.map(tr.changes),
42
+ fresh: value.fresh.map(tr.changes)
43
+ } : value;
44
+ for (const effect of tr.effects) {
45
+ if (effect.is(open)) next = {
46
+ ...next,
47
+ mark: effect.value
48
+ };else if (effect.is(inserted)) {
49
+ const {
50
+ from,
51
+ to,
52
+ mark
53
+ } = effect.value;
54
+ next = {
55
+ mark,
56
+ dictated: next.dictated.update({
57
+ add: [dictatedMark.range(from, to)]
58
+ }),
59
+ fresh: next.fresh.update({
60
+ add: [freshMark(Date.now()).range(from, to)]
61
+ })
62
+ };
63
+ } else if (effect.is(settle)) {
64
+ const before = effect.value;
65
+ next = {
66
+ ...next,
67
+ fresh: next.fresh.update({
68
+ filter: (_from, _to, value) => value.spec.at > before
69
+ })
70
+ };
71
+ } else if (effect.is(close)) next = empty;
72
+ }
73
+ return next;
74
+ },
75
+ provide: field => [EditorView.decorations.from(field, state => state.fresh), EditorView.decorations.from(field, state => state.mark === null ? Decoration.none : Decoration.set([Decoration.widget({
76
+ widget: new MarkWidget(),
77
+ side: 1
78
+ }).range(state.mark)]))]
79
+ });
80
+ const space = /\s/;
81
+ const freshMs = 1_500;
82
+
83
+ /** The editor state dictation needs; add it to the view that `codeMirrorDictationTarget` writes into. */
84
+ export const dictationExtension = () => dictationField;
85
+
86
+ /**
87
+ * A dictation target for a CodeMirror view. Words go into the document at once, as typed text does, so a shared
88
+ * document shows them to everyone and they last as long as the host saves typed text. The mark follows local and
89
+ * remote edits; Discard removes exactly the dictated ranges in one step, including what the person typed inside them.
90
+ */
91
+ export function codeMirrorDictationTarget(editorView) {
92
+ /** The first words of each session get one space from earlier text, never two. */
93
+ let sessionStart = false;
94
+ const view = () => {
95
+ const current = editorView();
96
+ return current?.state.field(dictationField, false) ? current : undefined;
97
+ };
98
+ return {
99
+ begin: () => {
100
+ const current = view();
101
+ if (!current) return;
102
+ sessionStart = true;
103
+ // A new session of the same dictation writes on at its mark.
104
+ if (current.state.field(dictationField).mark !== null) return;
105
+ current.dispatch({
106
+ effects: open.of(current.state.selection.main.head),
107
+ annotations: isolateHistory.of("before")
108
+ });
109
+ },
110
+ insert: text => {
111
+ const current = view();
112
+ const mark = current?.state.field(dictationField).mark;
113
+ // A view that turned read-only takes no more words; the host ends the dictation.
114
+ if (!current || mark === null || mark === undefined || current.state.readOnly) return;
115
+ let chunk = text;
116
+ if (sessionStart) {
117
+ const before = current.state.doc.sliceString(Math.max(0, mark - 1), mark);
118
+ if (!before || space.test(before)) chunk = chunk.replace(/^[^\S\n]+/, "");else if (chunk && !space.test(chunk[0])) chunk = ` ${chunk}`;
119
+ }
120
+ if (!chunk) return;
121
+ // The first words right before existing text keep one space to it; the mark stays before that space.
122
+ const after = current.state.doc.sliceString(mark, mark + 1);
123
+ const gap = sessionStart && after && !space.test(after) && !space.test(chunk[chunk.length - 1]) ? " " : "";
124
+ sessionStart = false;
125
+ current.dispatch({
126
+ changes: {
127
+ from: mark,
128
+ insert: chunk + gap
129
+ },
130
+ effects: inserted.of({
131
+ from: mark,
132
+ to: mark + chunk.length + gap.length,
133
+ mark: mark + chunk.length
134
+ }),
135
+ annotations: [Transaction.userEvent.of("input.dictate"), Transaction.addToHistory.of(true)]
136
+ });
137
+ // Each chunk keeps its tint for the same time, also while speech goes on.
138
+ const at = Date.now();
139
+ setTimeout(() => view()?.dispatch({
140
+ effects: settle.of(at)
141
+ }), freshMs);
142
+ },
143
+ discard: () => {
144
+ const current = view();
145
+ if (!current) return;
146
+ const ranges = [];
147
+ const cursor = current.state.field(dictationField).dictated.iter();
148
+ for (; cursor.value; cursor.next()) ranges.push(cursor.value.range(cursor.from, cursor.to));
149
+ current.dispatch({
150
+ changes: ranges.filter(range => range.to > range.from).map(range => ({
151
+ from: range.from,
152
+ to: range.to
153
+ })),
154
+ effects: close.of(null),
155
+ annotations: [Transaction.userEvent.of("delete.dictate"), isolateHistory.of("full")]
156
+ });
157
+ },
158
+ end: () => {
159
+ view()?.dispatch({
160
+ effects: close.of(null),
161
+ annotations: isolateHistory.of("after")
162
+ });
163
+ }
164
+ };
165
+ }
166
+ //# sourceMappingURL=dictation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["text-editor/dictation.ts"],"sourcesContent":["import { isolateHistory } from \"@codemirror/commands\";\nimport { type Range, type RangeSet, StateEffect, StateField, Transaction } from \"@codemirror/state\";\nimport { Decoration, type DecorationSet, EditorView, WidgetType } from \"@codemirror/view\";\nimport type { DictationTarget } from \"../inputs/dictation/types\";\n\ntype DictationState = {\n /** Where the next words land; `null` while no dictation is open. */\n mark: number | null;\n /** Every range dictation inserted, mapped through all later edits. */\n dictated: RangeSet<Decoration>;\n /** The newest words, tinted for a moment. */\n fresh: DecorationSet;\n};\n\nconst dictatedMark = Decoration.mark({ class: \"k2b-dictation-dictated\" });\nconst freshMark = (at: number) => Decoration.mark({ class: \"k2b-dictation-fresh\", at });\n\nclass MarkWidget extends WidgetType {\n override eq() {\n return true;\n }\n override toDOM() {\n const mark = document.createElement(\"span\");\n mark.className = \"k2b-dictation-mark\";\n mark.setAttribute(\"aria-hidden\", \"true\");\n return mark;\n }\n override ignoreEvent() {\n return true;\n }\n}\n\nconst open = StateEffect.define<number>();\nconst inserted = StateEffect.define<{ from: number; to: number; mark: number }>();\n/** Removes the tint of words older than the given time. */\nconst settle = StateEffect.define<number>();\nconst close = StateEffect.define<null>();\n\nconst empty: DictationState = { mark: null, dictated: Decoration.none, fresh: Decoration.none };\n\nconst dictationField = StateField.define<DictationState>({\n create: () => empty,\n update(value, tr) {\n let next: DictationState = tr.docChanged\n ? {\n // Edits elsewhere move the mark along; text typed exactly at the mark stays before the next words.\n mark: value.mark === null ? null : tr.changes.mapPos(value.mark, 1),\n dictated: value.dictated.map(tr.changes),\n fresh: value.fresh.map(tr.changes),\n }\n : value;\n for (const effect of tr.effects) {\n if (effect.is(open)) next = { ...next, mark: effect.value };\n else if (effect.is(inserted)) {\n const { from, to, mark } = effect.value;\n next = {\n mark,\n dictated: next.dictated.update({ add: [dictatedMark.range(from, to)] }),\n fresh: next.fresh.update({ add: [freshMark(Date.now()).range(from, to)] }),\n };\n } else if (effect.is(settle)) {\n const before = effect.value;\n next = { ...next, fresh: next.fresh.update({ filter: (_from, _to, value) => (value.spec.at as number) > before }) };\n } else if (effect.is(close)) next = empty;\n }\n return next;\n },\n provide: (field) => [\n EditorView.decorations.from(field, (state) => state.fresh),\n EditorView.decorations.from(field, (state) =>\n state.mark === null ? Decoration.none : Decoration.set([Decoration.widget({ widget: new MarkWidget(), side: 1 }).range(state.mark)]),\n ),\n ],\n});\n\nconst space = /\\s/;\nconst freshMs = 1_500;\n\n/** The editor state dictation needs; add it to the view that `codeMirrorDictationTarget` writes into. */\nexport const dictationExtension = () => dictationField;\n\n/**\n * A dictation target for a CodeMirror view. Words go into the document at once, as typed text does, so a shared\n * document shows them to everyone and they last as long as the host saves typed text. The mark follows local and\n * remote edits; Discard removes exactly the dictated ranges in one step, including what the person typed inside them.\n */\nexport function codeMirrorDictationTarget(editorView: () => EditorView | undefined): DictationTarget {\n /** The first words of each session get one space from earlier text, never two. */\n let sessionStart = false;\n const view = () => {\n const current = editorView();\n return current?.state.field(dictationField, false) ? current : undefined;\n };\n return {\n begin: () => {\n const current = view();\n if (!current) return;\n sessionStart = true;\n // A new session of the same dictation writes on at its mark.\n if (current.state.field(dictationField).mark !== null) return;\n current.dispatch({ effects: open.of(current.state.selection.main.head), annotations: isolateHistory.of(\"before\") });\n },\n insert: (text) => {\n const current = view();\n const mark = current?.state.field(dictationField).mark;\n // A view that turned read-only takes no more words; the host ends the dictation.\n if (!current || mark === null || mark === undefined || current.state.readOnly) return;\n let chunk = text;\n if (sessionStart) {\n const before = current.state.doc.sliceString(Math.max(0, mark - 1), mark);\n if (!before || space.test(before)) chunk = chunk.replace(/^[^\\S\\n]+/, \"\");\n else if (chunk && !space.test(chunk[0]!)) chunk = ` ${chunk}`;\n }\n if (!chunk) return;\n // The first words right before existing text keep one space to it; the mark stays before that space.\n const after = current.state.doc.sliceString(mark, mark + 1);\n const gap = sessionStart && after && !space.test(after) && !space.test(chunk[chunk.length - 1]!) ? \" \" : \"\";\n sessionStart = false;\n current.dispatch({\n changes: { from: mark, insert: chunk + gap },\n effects: inserted.of({ from: mark, to: mark + chunk.length + gap.length, mark: mark + chunk.length }),\n annotations: [Transaction.userEvent.of(\"input.dictate\"), Transaction.addToHistory.of(true)],\n });\n // Each chunk keeps its tint for the same time, also while speech goes on.\n const at = Date.now();\n setTimeout(() => view()?.dispatch({ effects: settle.of(at) }), freshMs);\n },\n discard: () => {\n const current = view();\n if (!current) return;\n const ranges: Range<Decoration>[] = [];\n const cursor = current.state.field(dictationField).dictated.iter();\n for (; cursor.value; cursor.next()) ranges.push(cursor.value.range(cursor.from, cursor.to));\n current.dispatch({\n changes: ranges.filter((range) => range.to > range.from).map((range) => ({ from: range.from, to: range.to })),\n effects: close.of(null),\n annotations: [Transaction.userEvent.of(\"delete.dictate\"), isolateHistory.of(\"full\")],\n });\n },\n end: () => {\n view()?.dispatch({ effects: close.of(null), annotations: isolateHistory.of(\"after\") });\n },\n };\n}\n"],"mappings":"AAAA,SAAS,cAAc,QAAQ,sBAAsB;AACrD,SAAoC,WAAW,EAAE,UAAU,EAAE,WAAW,QAAQ,mBAAmB;AACnG,SAAS,UAAU,EAAsB,UAAU,EAAE,UAAU,QAAQ,kBAAkB;AAYzF,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC;EAAE,KAAK,EAAE;AAAyB,CAAC,CAAC;AACzE,MAAM,SAAS,GAAI,EAAU,IAAK,UAAU,CAAC,IAAI,CAAC;EAAE,KAAK,EAAE,qBAAqB;EAAE;AAAG,CAAC,CAAC;AAEvF,MAAM,UAAU,SAAS,UAAU,CAAC;EACzB,EAAE,GAAG;IACZ,OAAO,IAAI;EACb;EACS,KAAK,GAAG;IACf,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC;IAC3C,IAAI,CAAC,SAAS,GAAG,oBAAoB;IACrC,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;IACxC,OAAO,IAAI;EACb;EACS,WAAW,GAAG;IACrB,OAAO,IAAI;EACb;AACF;AAEA,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAS,CAAC;AACzC,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAA6C,CAAC;AACjF;AACA,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAS,CAAC;AAC3C,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAO,CAAC;AAExC,MAAM,KAAqB,GAAG;EAAE,IAAI,EAAE,IAAI;EAAE,QAAQ,EAAE,UAAU,CAAC,IAAI;EAAE,KAAK,EAAE,UAAU,CAAC;AAAK,CAAC;AAE/F,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAiB;EACvD,MAAM,EAAE,MAAM,KAAK;EACnB,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE;IAChB,IAAI,IAAoB,GAAG,EAAE,CAAC,UAAU,GACpC;MACE;MACA,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;MACnE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC;MACxC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO;IACnC,CAAC,GACD,KAAK;IACT,KAAK,MAAM,MAAM,IAAI,EAAE,CAAC,OAAO,EAAE;MAC/B,IAAI,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG;QAAE,GAAG,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;MAAM,CAAC,CAAC,KACvD,IAAI,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE;QAC5B,MAAM;UAAE,IAAI;UAAE,EAAE;UAAE;QAAK,CAAC,GAAG,MAAM,CAAC,KAAK;QACvC,IAAI,GAAG;UACL,IAAI;UACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;UAAE,CAAC,CAAC;UACvE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;UAAE,CAAC;QAC3E,CAAC;MACH,CAAC,MAAM,IAAI,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE;QAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK;QAC3B,IAAI,GAAG;UAAE,GAAG,IAAI;UAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,KAAM,KAAK,CAAC,IAAI,CAAC,EAAE,GAAc;UAAO,CAAC;QAAE,CAAC;MACrH,CAAC,MAAM,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,GAAG,KAAK;IAC3C;IACA,OAAO,IAAI;EACb,CAAC;EACD,OAAO,EAAG,KAAK,IAAK,CAClB,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,EAAG,KAAK,IAAK,KAAK,CAAC,KAAK,CAAC,EAC1D,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,EAAG,KAAK,IACvC,KAAK,CAAC,IAAI,KAAK,IAAI,GAAG,UAAU,CAAC,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;IAAE,MAAM,EAAE,IAAI,UAAU,CAAC,CAAC;IAAE,IAAI,EAAE;EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CACrI,CAAC;AAEL,CAAC,CAAC;AAEF,MAAM,KAAK,GAAG,IAAI;AAClB,MAAM,OAAO,GAAG,KAAK;;AAErB;AACA,OAAO,MAAM,kBAAkB,GAAG,MAAM,cAAc;;AAEtD;AACA;AACA;AACA;AACA;AACA,OAAO,SAAS,yBAAyB,CAAC,UAAwC,EAAmB;EACnG;EACA,IAAI,YAAY,GAAG,KAAK;EACxB,MAAM,IAAI,GAAG,MAAM;IACjB,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC;IAC5B,OAAO,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,GAAG,OAAO,GAAG,SAAS;EAC1E,CAAC;EACD,OAAO;IACL,KAAK,EAAE,MAAM;MACX,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC;MACtB,IAAI,CAAC,OAAO,EAAE;MACd,YAAY,GAAG,IAAI;MACnB;MACA,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE;MACvD,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,WAAW,EAAE,cAAc,CAAC,EAAE,CAAC,QAAQ;MAAE,CAAC,CAAC;IACrH,CAAC;IACD,MAAM,EAAG,IAAI,IAAK;MAChB,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC;MACtB,MAAM,IAAI,GAAG,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI;MACtD;MACA,IAAI,CAAC,OAAO,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE;MAC/E,IAAI,KAAK,GAAG,IAAI;MAChB,IAAI,YAAY,EAAE;QAChB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC;QACzE,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,KACrE,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,EAAE;MAC/D;MACA,IAAI,CAAC,KAAK,EAAE;MACZ;MACA,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC;MAC3D,MAAM,GAAG,GAAG,YAAY,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,GAAG,GAAG,GAAG,EAAE;MAC3G,YAAY,GAAG,KAAK;MACpB,OAAO,CAAC,QAAQ,CAAC;QACf,OAAO,EAAE;UAAE,IAAI,EAAE,IAAI;UAAE,MAAM,EAAE,KAAK,GAAG;QAAI,CAAC;QAC5C,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC;UAAE,IAAI,EAAE,IAAI;UAAE,EAAE,EAAE,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM;UAAE,IAAI,EAAE,IAAI,GAAG,KAAK,CAAC;QAAO,CAAC,CAAC;QACrG,WAAW,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,CAAC;MAC5F,CAAC,CAAC;MACF;MACA,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;MACrB,UAAU,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE;MAAE,CAAC,CAAC,EAAE,OAAO,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,MAAM;MACb,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC;MACtB,IAAI,CAAC,OAAO,EAAE;MACd,MAAM,MAA2B,GAAG,EAAE;MACtC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;MAClE,OAAO,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;MAC3F,OAAO,CAAC,QAAQ,CAAC;QACf,OAAO,EAAE,MAAM,CAAC,MAAM,CAAE,KAAK,IAAK,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAE,KAAK,KAAM;UAAE,IAAI,EAAE,KAAK,CAAC,IAAI;UAAE,EAAE,EAAE,KAAK,CAAC;QAAG,CAAC,CAAC,CAAC;QAC7G,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC;QACvB,WAAW,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,gBAAgB,CAAC,EAAE,cAAc,CAAC,EAAE,CAAC,MAAM,CAAC;MACrF,CAAC,CAAC;IACJ,CAAC;IACD,GAAG,EAAE,MAAM;MACT,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC;QAAE,WAAW,EAAE,cAAc,CAAC,EAAE,CAAC,OAAO;MAAE,CAAC,CAAC;IACxF;EACF,CAAC;AACH","ignoreList":[]}
@@ -81,7 +81,7 @@ const linePrefix = (pattern, prefix) => view => {
81
81
  const lines = new Set();
82
82
  for (const r of state.selection.ranges) for (let n = state.doc.lineAt(r.from).number; n <= state.doc.lineAt(r.to).number; n++) lines.add(n);
83
83
  const all = [...lines].every(n => pattern.test(state.doc.line(n).text));
84
- const changes = [...lines].map(n => {
84
+ const changes = state.changes([...lines].map(n => {
85
85
  const line = state.doc.line(n);
86
86
  const m = pattern.exec(line.text);
87
87
  return all && m ? {
@@ -91,9 +91,11 @@ const linePrefix = (pattern, prefix) => view => {
91
91
  from: line.from,
92
92
  insert: prefix(state, n)
93
93
  };
94
- });
94
+ }));
95
+ // A caret at the line start follows the new prefix, so the next typed letter lands after it.
95
96
  view.dispatch({
96
97
  changes,
98
+ selection: state.selection.map(changes, 1),
97
99
  userEvent: "input.format"
98
100
  });
99
101
  view.focus();
@@ -109,12 +111,14 @@ const heading = level => view => {
109
111
  const prefix = `${"#".repeat(level)} `;
110
112
  const lines = [...numbers].sort((a, b) => a - b).map(number => state.doc.line(number));
111
113
  const remove = lines.every(line => line.text.startsWith(prefix));
114
+ const changes = state.changes(lines.map(line => ({
115
+ from: line.from,
116
+ to: line.from + (/^#{1,6} /.exec(line.text)?.[0].length ?? 0),
117
+ insert: remove ? "" : prefix
118
+ })));
112
119
  view.dispatch({
113
- changes: lines.map(line => ({
114
- from: line.from,
115
- to: line.from + (/^#{1,6} /.exec(line.text)?.[0].length ?? 0),
116
- insert: remove ? "" : prefix
117
- })),
120
+ changes,
121
+ selection: state.selection.map(changes, 1),
118
122
  userEvent: "input.format"
119
123
  });
120
124
  view.focus();
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["text-editor/format.ts"],"sourcesContent":["/** Toolbar and keyboard commands. Each one is a plain Markdown edit of the selection. */\nimport { EditorSelection, type EditorState, Facet, type TransactionSpec } from \"@codemirror/state\";\nimport type { EditorView } from \"@codemirror/view\";\n\n/** Insertion text follows the editor's inherited locale, including keyboard commands. */\nexport const formatLabels = Facet.define<\n { link: string; column: (column: number) => string },\n { link: string; column: (column: number) => string }\n>({\n combine: (values) => values[0] ?? { link: \"Link\", column: (column) => `Column ${column}` },\n});\n\n/** Named Markdown marks or an app's single-line paired delimiter (for example Notebook's `==`). */\nexport type InlineFormat = \"bold\" | \"italic\" | \"code\" | \"strike\" | \"link\" | { marker: string };\n/** Pure transaction spec, also used when a missing table cell must be materialized atomically. */\nexport const inlineFormat = (state: EditorState, name: InlineFormat): TransactionSpec => {\n if (name === \"link\") {\n const { from, to } = state.selection.main;\n const label = state.sliceDoc(from, to) || state.facet(formatLabels).link;\n return {\n changes: { from, to, insert: `[${label}](https://)` },\n selection: { anchor: from + label.length + 3, head: from + label.length + 11 },\n userEvent: \"input.format\",\n };\n }\n const marker = typeof name === \"string\" ? { bold: \"**\", italic: \"*\", code: \"`\", strike: \"~~\" }[name] : name.marker;\n // A host inline delimiter must not split a cell or introduce another row.\n if (!marker || /[|\\r\\n]/.test(marker)) return {};\n return {\n ...state.changeByRange((range) => {\n const text = state.sliceDoc(range.from, range.to);\n const before = state.sliceDoc(Math.max(0, range.from - marker.length), range.from);\n const after = state.sliceDoc(range.to, Math.min(state.doc.length, range.to + marker.length));\n const italicBoundary =\n name !== \"italic\" ||\n ((/\\*+$/.exec(state.sliceDoc(state.doc.lineAt(range.from).from, range.from))?.[0].length ?? 0) % 2 === 1 &&\n (/^\\*+/.exec(state.sliceDoc(range.to, state.doc.lineAt(range.to).to))?.[0].length ?? 0) % 2 === 1);\n if (before === marker && after === marker && italicBoundary)\n return {\n changes: [\n { from: range.from - marker.length, to: range.from },\n { from: range.to, to: range.to + marker.length },\n ],\n range: EditorSelection.range(range.from - marker.length, range.to - marker.length),\n };\n return {\n changes: { from: range.from, to: range.to, insert: `${marker}${text}${marker}` },\n range: EditorSelection.range(range.from + marker.length, range.to + marker.length),\n };\n }),\n userEvent: \"input.format\",\n };\n};\nconst inlineCommand = (name: InlineFormat) => (view: EditorView) => {\n if (view.state.readOnly) return false;\n view.dispatch(inlineFormat(view.state, name));\n view.focus();\n return true;\n};\n\nconst linePrefix = (pattern: RegExp, prefix: (state: EditorState, n: number) => string) => (view: EditorView) => {\n if (view.state.readOnly) return false;\n const { state } = view;\n const lines = new Set<number>();\n for (const r of state.selection.ranges)\n for (let n = state.doc.lineAt(r.from).number; n <= state.doc.lineAt(r.to).number; n++) lines.add(n);\n const all = [...lines].every((n) => pattern.test(state.doc.line(n).text));\n const changes = [...lines].map((n) => {\n const line = state.doc.line(n);\n const m = pattern.exec(line.text);\n return all && m ? { from: line.from, to: line.from + m[0].length } : { from: line.from, insert: prefix(state, n) };\n });\n view.dispatch({ changes, userEvent: \"input.format\" });\n view.focus();\n return true;\n};\n\nconst heading = (level: number) => (view: EditorView) => {\n if (view.state.readOnly) return false;\n const { state } = view;\n const numbers = new Set<number>();\n for (const range of state.selection.ranges)\n for (let n = state.doc.lineAt(range.from).number; n <= state.doc.lineAt(range.to).number; n++) numbers.add(n);\n const prefix = `${\"#\".repeat(level)} `;\n const lines = [...numbers].sort((a, b) => a - b).map((number) => state.doc.line(number));\n const remove = lines.every((line) => line.text.startsWith(prefix));\n view.dispatch({\n changes: lines.map((line) => ({\n from: line.from,\n to: line.from + (/^#{1,6} /.exec(line.text)?.[0].length ?? 0),\n insert: remove ? \"\" : prefix,\n })),\n userEvent: \"input.format\",\n });\n view.focus();\n return true;\n};\n\nexport const commands = {\n bold: inlineCommand(\"bold\"),\n italic: inlineCommand(\"italic\"),\n code: inlineCommand(\"code\"),\n strike: inlineCommand(\"strike\"),\n heading: heading(2),\n heading1: heading(1),\n heading2: heading(2),\n heading3: heading(3),\n codeBlock: (view: EditorView) => {\n if (view.state.readOnly) return false;\n const { state } = view;\n const from = state.doc.lineAt(state.selection.main.from).from;\n const to = state.doc.lineAt(state.selection.main.to).to;\n const text = state.sliceDoc(from, to);\n let fenceLength = 3;\n for (const match of text.matchAll(/`+/g)) fenceLength = Math.max(fenceLength, match[0].length + 1);\n const fence = \"`\".repeat(fenceLength);\n const lead = from > 0 && state.doc.lineAt(from - 1).length ? \"\\n\" : \"\";\n const tail = to < state.doc.length && state.doc.lineAt(to + 1).length ? \"\\n\" : \"\";\n const start = from + lead.length + fence.length + 1;\n view.dispatch({\n changes: { from, to, insert: `${lead}${fence}\\n${text}\\n${fence}${tail}` },\n selection: { anchor: start, head: start + text.length },\n userEvent: \"input.format\",\n });\n view.focus();\n return true;\n },\n bulletList: linePrefix(/^\\s*[-*] /, () => \"- \"),\n orderedList: linePrefix(/^\\s*\\d+\\. /, () => \"1. \"),\n quote: linePrefix(/^> ?/, () => \"> \"),\n link: inlineCommand(\"link\"),\n table: (view: EditorView) => {\n if (view.state.readOnly) return false;\n const { from } = view.state.selection.main;\n const line = view.state.doc.lineAt(from);\n const lead = line.length ? \"\\n\\n\" : \"\";\n const labels = view.state.facet(formatLabels);\n const first = labels.column(1);\n const insert = `${lead}| ${first} | ${labels.column(2)} |\\n| --- | --- |\\n| | |\\n`;\n const anchor = line.to + lead.length + 2;\n view.dispatch({ changes: { from: line.to, insert }, selection: { anchor, head: anchor + first.length }, userEvent: \"input.format\" });\n view.focus();\n queueMicrotask(() => {\n if (!view.dom.isConnected) return;\n const table = [...view.contentDOM.querySelectorAll<HTMLElement>(\".k2b-ste-table\")].find(\n (element) => view.posAtDOM(element) === line.to + lead.length,\n );\n const cell = table?.querySelector<HTMLElement>('.k2b-ste-cell[data-r=\"0\"][data-c=\"0\"]');\n if (!cell) return;\n cell.focus();\n const selection = cell.ownerDocument.getSelection();\n const range = cell.ownerDocument.createRange();\n range.selectNodeContents(cell);\n selection?.removeAllRanges();\n selection?.addRange(range);\n });\n return true;\n },\n rule: (view: EditorView) => {\n if (view.state.readOnly) return false;\n const line = view.state.doc.lineAt(view.state.selection.main.from);\n const previousHasText = line.number > 1 && view.state.doc.line(line.number - 1).text.trim().length > 0;\n const lead = line.length ? \"\\n\\n\" : previousHasText ? \"\\n\" : \"\";\n view.dispatch({ changes: { from: line.to, insert: `${lead}---\\n` }, userEvent: \"input.format\" });\n view.focus();\n return true;\n },\n};\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["text-editor/format.ts"],"sourcesContent":["/** Toolbar and keyboard commands. Each one is a plain Markdown edit of the selection. */\nimport { EditorSelection, type EditorState, Facet, type TransactionSpec } from \"@codemirror/state\";\nimport type { EditorView } from \"@codemirror/view\";\n\n/** Insertion text follows the editor's inherited locale, including keyboard commands. */\nexport const formatLabels = Facet.define<\n { link: string; column: (column: number) => string },\n { link: string; column: (column: number) => string }\n>({\n combine: (values) => values[0] ?? { link: \"Link\", column: (column) => `Column ${column}` },\n});\n\n/** Named Markdown marks or an app's single-line paired delimiter (for example Notebook's `==`). */\nexport type InlineFormat = \"bold\" | \"italic\" | \"code\" | \"strike\" | \"link\" | { marker: string };\n/** Pure transaction spec, also used when a missing table cell must be materialized atomically. */\nexport const inlineFormat = (state: EditorState, name: InlineFormat): TransactionSpec => {\n if (name === \"link\") {\n const { from, to } = state.selection.main;\n const label = state.sliceDoc(from, to) || state.facet(formatLabels).link;\n return {\n changes: { from, to, insert: `[${label}](https://)` },\n selection: { anchor: from + label.length + 3, head: from + label.length + 11 },\n userEvent: \"input.format\",\n };\n }\n const marker = typeof name === \"string\" ? { bold: \"**\", italic: \"*\", code: \"`\", strike: \"~~\" }[name] : name.marker;\n // A host inline delimiter must not split a cell or introduce another row.\n if (!marker || /[|\\r\\n]/.test(marker)) return {};\n return {\n ...state.changeByRange((range) => {\n const text = state.sliceDoc(range.from, range.to);\n const before = state.sliceDoc(Math.max(0, range.from - marker.length), range.from);\n const after = state.sliceDoc(range.to, Math.min(state.doc.length, range.to + marker.length));\n const italicBoundary =\n name !== \"italic\" ||\n ((/\\*+$/.exec(state.sliceDoc(state.doc.lineAt(range.from).from, range.from))?.[0].length ?? 0) % 2 === 1 &&\n (/^\\*+/.exec(state.sliceDoc(range.to, state.doc.lineAt(range.to).to))?.[0].length ?? 0) % 2 === 1);\n if (before === marker && after === marker && italicBoundary)\n return {\n changes: [\n { from: range.from - marker.length, to: range.from },\n { from: range.to, to: range.to + marker.length },\n ],\n range: EditorSelection.range(range.from - marker.length, range.to - marker.length),\n };\n return {\n changes: { from: range.from, to: range.to, insert: `${marker}${text}${marker}` },\n range: EditorSelection.range(range.from + marker.length, range.to + marker.length),\n };\n }),\n userEvent: \"input.format\",\n };\n};\nconst inlineCommand = (name: InlineFormat) => (view: EditorView) => {\n if (view.state.readOnly) return false;\n view.dispatch(inlineFormat(view.state, name));\n view.focus();\n return true;\n};\n\nconst linePrefix = (pattern: RegExp, prefix: (state: EditorState, n: number) => string) => (view: EditorView) => {\n if (view.state.readOnly) return false;\n const { state } = view;\n const lines = new Set<number>();\n for (const r of state.selection.ranges)\n for (let n = state.doc.lineAt(r.from).number; n <= state.doc.lineAt(r.to).number; n++) lines.add(n);\n const all = [...lines].every((n) => pattern.test(state.doc.line(n).text));\n const changes = state.changes(\n [...lines].map((n) => {\n const line = state.doc.line(n);\n const m = pattern.exec(line.text);\n return all && m ? { from: line.from, to: line.from + m[0].length } : { from: line.from, insert: prefix(state, n) };\n }),\n );\n // A caret at the line start follows the new prefix, so the next typed letter lands after it.\n view.dispatch({ changes, selection: state.selection.map(changes, 1), userEvent: \"input.format\" });\n view.focus();\n return true;\n};\n\nconst heading = (level: number) => (view: EditorView) => {\n if (view.state.readOnly) return false;\n const { state } = view;\n const numbers = new Set<number>();\n for (const range of state.selection.ranges)\n for (let n = state.doc.lineAt(range.from).number; n <= state.doc.lineAt(range.to).number; n++) numbers.add(n);\n const prefix = `${\"#\".repeat(level)} `;\n const lines = [...numbers].sort((a, b) => a - b).map((number) => state.doc.line(number));\n const remove = lines.every((line) => line.text.startsWith(prefix));\n const changes = state.changes(\n lines.map((line) => ({\n from: line.from,\n to: line.from + (/^#{1,6} /.exec(line.text)?.[0].length ?? 0),\n insert: remove ? \"\" : prefix,\n })),\n );\n view.dispatch({ changes, selection: state.selection.map(changes, 1), userEvent: \"input.format\" });\n view.focus();\n return true;\n};\n\nexport const commands = {\n bold: inlineCommand(\"bold\"),\n italic: inlineCommand(\"italic\"),\n code: inlineCommand(\"code\"),\n strike: inlineCommand(\"strike\"),\n heading: heading(2),\n heading1: heading(1),\n heading2: heading(2),\n heading3: heading(3),\n codeBlock: (view: EditorView) => {\n if (view.state.readOnly) return false;\n const { state } = view;\n const from = state.doc.lineAt(state.selection.main.from).from;\n const to = state.doc.lineAt(state.selection.main.to).to;\n const text = state.sliceDoc(from, to);\n let fenceLength = 3;\n for (const match of text.matchAll(/`+/g)) fenceLength = Math.max(fenceLength, match[0].length + 1);\n const fence = \"`\".repeat(fenceLength);\n const lead = from > 0 && state.doc.lineAt(from - 1).length ? \"\\n\" : \"\";\n const tail = to < state.doc.length && state.doc.lineAt(to + 1).length ? \"\\n\" : \"\";\n const start = from + lead.length + fence.length + 1;\n view.dispatch({\n changes: { from, to, insert: `${lead}${fence}\\n${text}\\n${fence}${tail}` },\n selection: { anchor: start, head: start + text.length },\n userEvent: \"input.format\",\n });\n view.focus();\n return true;\n },\n bulletList: linePrefix(/^\\s*[-*] /, () => \"- \"),\n orderedList: linePrefix(/^\\s*\\d+\\. /, () => \"1. \"),\n quote: linePrefix(/^> ?/, () => \"> \"),\n link: inlineCommand(\"link\"),\n table: (view: EditorView) => {\n if (view.state.readOnly) return false;\n const { from } = view.state.selection.main;\n const line = view.state.doc.lineAt(from);\n const lead = line.length ? \"\\n\\n\" : \"\";\n const labels = view.state.facet(formatLabels);\n const first = labels.column(1);\n const insert = `${lead}| ${first} | ${labels.column(2)} |\\n| --- | --- |\\n| | |\\n`;\n const anchor = line.to + lead.length + 2;\n view.dispatch({ changes: { from: line.to, insert }, selection: { anchor, head: anchor + first.length }, userEvent: \"input.format\" });\n view.focus();\n queueMicrotask(() => {\n if (!view.dom.isConnected) return;\n const table = [...view.contentDOM.querySelectorAll<HTMLElement>(\".k2b-ste-table\")].find(\n (element) => view.posAtDOM(element) === line.to + lead.length,\n );\n const cell = table?.querySelector<HTMLElement>('.k2b-ste-cell[data-r=\"0\"][data-c=\"0\"]');\n if (!cell) return;\n cell.focus();\n const selection = cell.ownerDocument.getSelection();\n const range = cell.ownerDocument.createRange();\n range.selectNodeContents(cell);\n selection?.removeAllRanges();\n selection?.addRange(range);\n });\n return true;\n },\n rule: (view: EditorView) => {\n if (view.state.readOnly) return false;\n const line = view.state.doc.lineAt(view.state.selection.main.from);\n const previousHasText = line.number > 1 && view.state.doc.line(line.number - 1).text.trim().length > 0;\n const lead = line.length ? \"\\n\\n\" : previousHasText ? \"\\n\" : \"\";\n view.dispatch({ changes: { from: line.to, insert: `${lead}---\\n` }, userEvent: \"input.format\" });\n view.focus();\n return true;\n },\n};\n"],"mappings":"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","ignoreList":[]}
@@ -123,7 +123,7 @@ const escapeHtml = s => s.replace(/[&<>"]/g, ch => `&${{
123
123
  '"': "quot"
124
124
  }[ch]};`);
125
125
  /** Render the same Markdown tokens as prose, keeping code and escaped markers literal. */
126
- const renderInline = raw => {
126
+ export const renderInline = raw => {
127
127
  const text = raw;
128
128
  const tree = markdownLanguage.parser.parse(text);
129
129
  const render = node => {
@@ -189,6 +189,9 @@ const hasHeader = t => t.lines[0].cells.some(cell => cell.text.length > 0);
189
189
  const firstRow = t => hasHeader(t) ? 0 : 1;
190
190
  const shapeOf = t => `${T.rowCount(t)}x${width(t)}:${firstRow(t)}`;
191
191
  const renderedDocuments = new WeakMap();
192
+ /** The height a table was last drawn with, handed from widget to widget while its shape stays. */
193
+
194
+ const drawnHeights = new WeakMap();
192
195
  /**
193
196
  * How far the grid may reach left of the text: its grip gutter and the first
194
197
  * cell's padding sit there, so cell text lines up with the paragraphs. It
@@ -213,25 +216,31 @@ const measureScroll = (view, wrap) => view.requestMeasure({
213
216
  left: wrap.scrollLeft,
214
217
  width: wrap.clientWidth,
215
218
  total: wrap.scrollWidth,
216
- bleed: bleed(view, wrap)
219
+ bleed: bleed(view, wrap),
220
+ height: wrap.getBoundingClientRect().height / view.scaleY
217
221
  }),
218
222
  write: ({
219
223
  left,
220
224
  width,
221
225
  total,
222
- bleed
226
+ bleed,
227
+ height
223
228
  }) => {
224
229
  if (!wrap.isConnected) return;
230
+ const drawn = drawnHeights.get(wrap);
231
+ // A hidden editor has no height to remember.
232
+ if (drawn && height > 0) drawn.height = height;
225
233
  wrap.toggleAttribute("data-scrolled", left > 0);
226
234
  wrap.toggleAttribute("data-more", left + width < total - 1);
227
235
  wrap.style.setProperty("--k2b-ste-bleed", `${bleed}px`);
228
236
  }
229
237
  });
230
238
  class GridWidget extends WidgetType {
231
- constructor(table, readOnly) {
239
+ constructor(table, readOnly, drawn) {
232
240
  super();
233
241
  this.table = table;
234
242
  this.readOnly = readOnly;
243
+ this.drawn = drawn;
235
244
  }
236
245
  // Always update the existing DOM on document/refresh transactions. A shape
237
246
  // deferred during composition must be patched even when the next table value
@@ -250,12 +259,14 @@ class GridWidget extends WidgetType {
250
259
  wrap.toggleAttribute("data-more", wrap.scrollLeft + wrap.clientWidth < wrap.scrollWidth - 1);
251
260
  });
252
261
  renderedDocuments.set(wrap, view.state.doc);
262
+ drawnHeights.set(wrap, this.drawn);
253
263
  setEditable(wrap, !this.readOnly, view);
254
264
  measureScroll(view, wrap);
255
265
  return wrap;
256
266
  }
257
267
  updateDOM(dom, view) {
258
268
  const act = active.get(view);
269
+ drawnHeights.set(dom, this.drawn);
259
270
  measureScroll(view, dom);
260
271
  if (dom.dataset.shape !== shapeOf(this.table)) {
261
272
  const focused = dom.contains(document.activeElement) ? document.activeElement : null;
@@ -311,8 +322,10 @@ class GridWidget extends WidgetType {
311
322
  ignoreEvent() {
312
323
  return true;
313
324
  }
325
+ // Every rebuild replaces the widget, and CodeMirror then takes this height for tables outside the drawn range too.
326
+ // A guess there would shift every line below them, so a table that was drawn keeps its real height.
314
327
  get estimatedHeight() {
315
- return (T.rowCount(this.table) - firstRow(this.table)) * 41 + 16;
328
+ return this.drawn.height ?? (T.rowCount(this.table) - firstRow(this.table)) * 41 + 16;
316
329
  }
317
330
  }
318
331
  const sources = new WeakMap();
@@ -1756,10 +1769,25 @@ export const htmlTableGrid = html => {
1756
1769
  const rows = [...table.querySelectorAll("tr")].map(tr => [...tr.querySelectorAll("th,td")].map(cell => (cell.textContent ?? "").replace(/\s+/g, " ").trim()));
1757
1770
  return rows.filter(row => row.length).length ? rows.filter(row => row.length) : null;
1758
1771
  };
1759
- const decorate = state => Decoration.set(tablesIn(state).map(t => Decoration.replace({
1760
- widget: new GridWidget(t, state.readOnly),
1761
- block: true
1762
- }).range(t.from, t.to)), true);
1772
+ const decorate = (state, previous = Decoration.none, changes) => {
1773
+ const drawn = new Map();
1774
+ for (const cursor = previous.iter(); cursor.value; cursor.next()) {
1775
+ const widget = cursor.value.spec.widget;
1776
+ if (widget instanceof GridWidget) drawn.set(changes ? changes.mapPos(cursor.from, 1) : cursor.from, widget.drawn);
1777
+ }
1778
+ return Decoration.set(tablesIn(state).map(t => {
1779
+ const shape = shapeOf(t);
1780
+ const known = drawn.get(t.from);
1781
+ const widget = new GridWidget(t, state.readOnly, known?.shape === shape ? known : {
1782
+ shape,
1783
+ height: null
1784
+ });
1785
+ return Decoration.replace({
1786
+ widget,
1787
+ block: true
1788
+ }).range(t.from, t.to);
1789
+ }), true);
1790
+ };
1763
1791
  export const tableGrid = () => [clipboardLoading(view => view.state.facet(tableHost), syncCellSelection), Prec.highest(keymap.of([{
1764
1792
  key: "ArrowDown",
1765
1793
  run: view => enterTable(view, true)
@@ -1790,8 +1818,8 @@ export const tableGrid = () => [clipboardLoading(view => view.state.facet(tableH
1790
1818
  }
1791
1819
  };
1792
1820
  }), StateField.define({
1793
- create: decorate,
1794
- update: (value, tr) => tr.docChanged || tr.effects.some(e => e.is(refreshTables)) || tr.startState.readOnly !== tr.state.readOnly || syntaxTree(tr.startState) !== syntaxTree(tr.state) ? decorate(tr.state) : value,
1821
+ create: state => decorate(state),
1822
+ update: (value, tr) => tr.docChanged || tr.effects.some(e => e.is(refreshTables)) || tr.startState.readOnly !== tr.state.readOnly || syntaxTree(tr.startState) !== syntaxTree(tr.state) ? decorate(tr.state, value, tr.changes) : value,
1795
1823
  provide: field => [EditorView.decorations.from(field), EditorView.atomicRanges.of(view => view.state.field(field))]
1796
1824
  }), followChanges];
1797
1825
  //# sourceMappingURL=table.js.map