@prosemark/core 0.0.3 → 0.0.4

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 (3) hide show
  1. package/dist/main.d.ts +52 -12
  2. package/dist/main.js +311 -83
  3. package/package.json +11 -2
package/dist/main.d.ts CHANGED
@@ -1,16 +1,12 @@
1
- import { DOMEventHandlers, DOMEventMap, Decoration, DecorationSet } from "@codemirror/view";
1
+ import { DOMEventHandlers, DOMEventMap, Decoration, DecorationSet, EditorView, ViewPlugin, ViewUpdate } from "@codemirror/view";
2
2
  import * as _codemirror_state0 from "@codemirror/state";
3
- import { EditorState, Extension, Facet, Range, StateField } from "@codemirror/state";
3
+ import { EditorState, Extension, Facet, Line, Range, RangeSet, StateField } from "@codemirror/state";
4
4
  import { Tag } from "@lezer/highlight";
5
5
  import * as _lezer_markdown0 from "@lezer/markdown";
6
6
  import { MarkdownConfig } from "@lezer/markdown";
7
7
  import * as _lezer_common0 from "@lezer/common";
8
8
  import { SyntaxNodeRef } from "@lezer/common";
9
9
 
10
- //#region lib/hide/index.d.ts
11
- declare const defaultHideExtensions: _codemirror_state0.Extension[];
12
- declare const escapeMarkdownSyntaxExtension: MarkdownConfig;
13
- //#endregion
14
10
  //#region lib/utils.d.ts
15
11
  interface RangeLike {
16
12
  from: number;
@@ -19,13 +15,21 @@ interface RangeLike {
19
15
  type ClassBasedEventHandlers<This> = { [event in keyof DOMEventMap]?: Record<string, DOMEventHandlers<This>[event]> };
20
16
  declare function eventHandlersWithClass<This>(handlers: ClassBasedEventHandlers<This>): DOMEventHandlers<This>;
21
17
  //#endregion
18
+ //#region lib/hide/core.d.ts
19
+ declare const hideExtension: StateField<DecorationSet>;
20
+ //#endregion
21
+ //#region lib/hide/index.d.ts
22
+ declare const defaultHideExtensions: _codemirror_state0.Extension[];
23
+ declare const escapeMarkdownSyntaxExtension: MarkdownConfig;
24
+ //#endregion
22
25
  //#region lib/fold/core.d.ts
23
- declare const foldDecorationExtension: StateField<DecorationSet>;
26
+ declare const foldExtension: StateField<DecorationSet>;
24
27
  interface FoldableSyntaxSpec {
25
28
  nodePath: string | string[] | ((nodePath: string) => boolean);
26
- onFold?: (state: EditorState, node: SyntaxNodeRef) => Range<Decoration> | Range<Decoration>[] | undefined;
29
+ buildDecorations?: (state: EditorState, node: SyntaxNodeRef, selectionTouchesRange: boolean) => Range<Decoration> | Range<Decoration>[] | undefined;
27
30
  unfoldZone?: (state: EditorState, node: SyntaxNodeRef) => RangeLike;
28
31
  eventHandlers?: DOMEventHandlers<void>;
32
+ keepDecorationOnUnfold?: boolean;
29
33
  }
30
34
  declare const foldableSyntaxFacet: Facet<FoldableSyntaxSpec, FoldableSyntaxSpec[]>;
31
35
  declare const selectAllDecorationsOnSelectExtension: (widgetClass: string) => Extension;
@@ -37,6 +41,10 @@ declare const bulletListExtension: _codemirror_state0.Extension;
37
41
  declare const emojiMarkdownSyntaxExtension: MarkdownConfig;
38
42
  declare const emojiExtension: _codemirror_state0.Extension;
39
43
  //#endregion
44
+ //#region lib/fold/dashes.d.ts
45
+ declare const dashMarkdownSyntaxExtension: MarkdownConfig;
46
+ declare const dashExtension: _codemirror_state0.Extension;
47
+ //#endregion
40
48
  //#region lib/fold/horizontalRule.d.ts
41
49
  declare const horizonalRuleExtension: _codemirror_state0.Extension[];
42
50
  //#endregion
@@ -46,8 +54,26 @@ declare const imageExtension: _codemirror_state0.Extension[];
46
54
  //#region lib/fold/task.d.ts
47
55
  declare const taskExtension: _codemirror_state0.Extension[];
48
56
  //#endregion
49
- //#region lib/fold/blockQuote.d.ts
50
- declare const blockQuoteExtension: _codemirror_state0.Extension;
57
+ //#region lib/blockQuote.d.ts
58
+ interface MeasureData {
59
+ lineFroms: number[];
60
+ nestedBorders: {
61
+ pos: number;
62
+ offset: number;
63
+ }[];
64
+ }
65
+ declare const blockQuoteExtension: ViewPlugin<{
66
+ decorations: DecorationSet;
67
+ update(u: ViewUpdate): void;
68
+ requestMeasure(view: EditorView): void;
69
+ applyMeasure(data: MeasureData, view: EditorView): void;
70
+ }, undefined>;
71
+ //#endregion
72
+ //#region lib/fold/index.d.ts
73
+ declare const defaultFoldableSyntaxExtensions: _codemirror_state0.Extension[];
74
+ //#endregion
75
+ //#region lib/revealBlockOnArrow.d.ts
76
+ declare const revealBlockOnArrowExtension: _codemirror_state0.Extension[];
51
77
  //#endregion
52
78
  //#region lib/syntaxHighlighting.d.ts
53
79
  declare const additionalMarkdownSyntaxTags: {
@@ -69,6 +95,7 @@ declare const markdownTags: {
69
95
  emoji: Tag;
70
96
  emojiMark: Tag;
71
97
  listMark: Tag;
98
+ dash: Tag;
72
99
  };
73
100
  //#endregion
74
101
  //#region lib/clickLink.d.ts
@@ -77,6 +104,20 @@ declare const clickLinkHandler: Facet<ClickLinkHandler, readonly ClickLinkHandle
77
104
  declare const defaultClickLinkHandler: _codemirror_state0.Extension;
78
105
  declare const clickLinkExtension: _codemirror_state0.Extension[];
79
106
  //#endregion
107
+ //#region lib/softIndentExtension.d.ts
108
+ interface IndentData {
109
+ line: Line;
110
+ indentWidth: number;
111
+ }
112
+ declare const softIndentExtension: ViewPlugin<{
113
+ decorations: DecorationSet;
114
+ update(u: ViewUpdate): void;
115
+ requestMeasure(view: EditorView): void;
116
+ measureIndents(view: EditorView): IndentData[];
117
+ buildDecorations(indents: IndentData[]): RangeSet<Decoration>;
118
+ applyIndents(indents: IndentData[], view: EditorView): void;
119
+ }, undefined>;
120
+ //#endregion
80
121
  //#region lib/codeFenceExtension.d.ts
81
122
  declare const codeBlockDecorationsExtension: Extension;
82
123
  declare const codeFenceTheme: Extension;
@@ -86,9 +127,8 @@ declare const prosemarkMarkdownSyntaxExtensions: (_lezer_markdown0.MarkdownConfi
86
127
  defineNodes: never[];
87
128
  props: _lezer_common0.NodePropSource[];
88
129
  })[];
89
- declare const defaultFoldableSyntaxExtensions: Extension[];
90
130
  declare const prosemarkBasicSetup: () => Extension;
91
131
  declare const prosemarkBaseThemeSetup: () => Extension;
92
132
  declare const prosemarkLightThemeSetup: () => Extension;
93
133
  //#endregion
94
- export { ClickLinkHandler, additionalMarkdownSyntaxTags, baseSyntaxHighlights, baseTheme, blockQuoteExtension, bulletListExtension, clickLinkExtension, clickLinkHandler, codeBlockDecorationsExtension, codeFenceTheme, defaultClickLinkHandler, defaultFoldableSyntaxExtensions, defaultHideExtensions, emojiExtension, emojiMarkdownSyntaxExtension, escapeMarkdownSyntaxExtension, eventHandlersWithClass, foldDecorationExtension, foldableSyntaxFacet, generalSyntaxHighlights, horizonalRuleExtension, imageExtension, lightTheme, markdownTags, prosemarkBaseThemeSetup, prosemarkBasicSetup, prosemarkLightThemeSetup, prosemarkMarkdownSyntaxExtensions, selectAllDecorationsOnSelectExtension, taskExtension };
134
+ export { ClickLinkHandler, additionalMarkdownSyntaxTags, baseSyntaxHighlights, baseTheme, blockQuoteExtension, bulletListExtension, clickLinkExtension, clickLinkHandler, codeBlockDecorationsExtension, codeFenceTheme, dashExtension, dashMarkdownSyntaxExtension, defaultClickLinkHandler, defaultFoldableSyntaxExtensions, defaultHideExtensions, emojiExtension, emojiMarkdownSyntaxExtension, escapeMarkdownSyntaxExtension, eventHandlersWithClass, foldExtension, foldableSyntaxFacet, generalSyntaxHighlights, hideExtension, horizonalRuleExtension, imageExtension, lightTheme, markdownTags, prosemarkBaseThemeSetup, prosemarkBasicSetup, prosemarkLightThemeSetup, prosemarkMarkdownSyntaxExtensions, revealBlockOnArrowExtension, selectAllDecorationsOnSelectExtension, softIndentExtension, taskExtension };
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
- import { Decoration, EditorView, EditorView as EditorView$1, ViewPlugin, WidgetType, dropCursor, keymap } from "@codemirror/view";
2
- import { CharCategory, EditorSelection, EditorState, Facet, RangeSetBuilder, StateField, findClusterBreak } from "@codemirror/state";
1
+ import { Decoration, EditorView, EditorView as EditorView$1, ViewPlugin, ViewUpdate, WidgetType, dropCursor, keymap } from "@codemirror/view";
2
+ import { Annotation, CharCategory, EditorSelection, EditorState, Facet, Line, RangeSet, RangeSetBuilder, StateField, findClusterBreak } from "@codemirror/state";
3
3
  import { HighlightStyle, bracketMatching, foldGutter, foldKeymap, indentOnInput, syntaxHighlighting, syntaxTree } from "@codemirror/language";
4
4
  import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
5
5
  import { searchKeymap } from "@codemirror/search";
@@ -51,12 +51,17 @@ function eventHandlersWithClass(handlers) {
51
51
 
52
52
  //#endregion
53
53
  //#region lib/hide/core.ts
54
- const hideTheme = EditorView.theme({ ".cm-hidden-token": { fontSize: "0px" } });
54
+ const hideTheme = EditorView.theme({
55
+ ".cm-hidden-token": { fontSize: "0px" },
56
+ ".cm-transparent-token": { opacity: 0 }
57
+ });
55
58
  const hideInlineDecoration = Decoration.mark({ class: "cm-hidden-token" });
59
+ const hideInlineKeepSpaceDecoration = Decoration.mark({ class: "cm-transparent-token" });
56
60
  const hideBlockDecoration = Decoration.replace({ block: true });
57
61
  const buildDecorations$1 = (state) => {
58
62
  const decorations = [];
59
63
  const specs = state.facet(hidableNodeFacet);
64
+ specs.map(checkSpec);
60
65
  syntaxTree(state).iterate({ enter: (node) => {
61
66
  if (state.selection.ranges.some((range) => rangeTouchesRange(node, range))) return;
62
67
  for (const spec of specs) {
@@ -78,11 +83,9 @@ const buildDecorations$1 = (state) => {
78
83
  let names;
79
84
  if (!Array.isArray(spec.subNodeNameToHide)) names = [spec.subNodeNameToHide];
80
85
  else names = spec.subNodeNameToHide;
81
- const cursor = node.node.cursor();
82
- console.assert(cursor.firstChild(), "A hide node must have children");
83
- do
84
- if (names.includes(cursor.type.name)) decorations.push((spec.block ? hideBlockDecoration : hideInlineDecoration).range(cursor.from, cursor.to));
85
- while (cursor.nextSibling());
86
+ node.node.cursor().iterate((node$1) => {
87
+ if (names.includes(node$1.type.name)) decorations.push((spec.block ? hideBlockDecoration : spec.keepSpace ? hideInlineKeepSpaceDecoration : hideInlineDecoration).range(node$1.from, node$1.to));
88
+ });
86
89
  }
87
90
  }
88
91
  } });
@@ -98,6 +101,9 @@ const hideExtension = StateField.define({
98
101
  },
99
102
  provide: (f) => [EditorView.decorations.from(f), hideTheme]
100
103
  });
104
+ const checkSpec = (spec) => {
105
+ if (spec.block && spec.keepSpace) console.warn("Only inline hide nodes can maintain space currently, but `block` and `keepSpace` are set in:", spec);
106
+ };
101
107
  const hidableNodeFacet = Facet.define({
102
108
  combine(value) {
103
109
  return [...value];
@@ -115,7 +121,8 @@ const markdownTags = {
115
121
  escapeMark: Tag.define(),
116
122
  emoji: Tag.define(),
117
123
  emojiMark: Tag.define(),
118
- listMark: Tag.define()
124
+ listMark: Tag.define(),
125
+ dash: Tag.define()
119
126
  };
120
127
 
121
128
  //#endregion
@@ -168,7 +175,8 @@ const defaultHidableSpecs = [
168
175
  },
169
176
  {
170
177
  nodeName: "Blockquote",
171
- subNodeNameToHide: "QuoteMark"
178
+ subNodeNameToHide: "QuoteMark",
179
+ keepSpace: true
172
180
  }
173
181
  ];
174
182
  const defaultHideExtensions = defaultHidableSpecs.map((spec) => hidableNodeFacet.of(spec));
@@ -187,31 +195,102 @@ const escapeMarkdownSyntaxExtension = {
187
195
  }]
188
196
  };
189
197
 
198
+ //#endregion
199
+ //#region lib/blockQuote.ts
200
+ var NestedBlockQuoteBorder = class extends WidgetType {
201
+ constructor(offset) {
202
+ super();
203
+ this.offset = offset;
204
+ }
205
+ toDOM() {
206
+ const span = document.createElement("span");
207
+ span.className = "cm-nested-blockquote-border";
208
+ span.style = `--blockquote-border-offset: ${this.offset.toString()}px`;
209
+ return span;
210
+ }
211
+ ignoreEvent(_event) {
212
+ return false;
213
+ }
214
+ };
215
+ function measureBlockQuotes(view) {
216
+ const lineFroms = [];
217
+ const nestedBorders = [];
218
+ syntaxTree(view.state).iterate({ enter(node) {
219
+ if (node.type.name != "Blockquote") return;
220
+ const startLine = view.state.doc.lineAt(node.from).number;
221
+ const endLine = view.state.doc.lineAt(node.to).number;
222
+ for (let i = startLine; i <= endLine; i++) {
223
+ const line = view.state.doc.line(i);
224
+ lineFroms.push(line.from);
225
+ }
226
+ node.node.cursor().iterate((child) => {
227
+ if (child.type.name !== "QuoteMark") return;
228
+ const line = view.state.doc.lineAt(child.from);
229
+ if (child.from == line.from) return;
230
+ const offset = (view.coordsAtPos(child.from)?.left ?? 0) - (view.coordsAtPos(line.from)?.left ?? 0);
231
+ nestedBorders.push({
232
+ pos: child.from,
233
+ offset
234
+ });
235
+ });
236
+ return false;
237
+ } });
238
+ return {
239
+ lineFroms,
240
+ nestedBorders
241
+ };
242
+ }
243
+ function buildDecorationsFromMeasure(_view, data) {
244
+ const decos = [];
245
+ for (const from of data.lineFroms) decos.push(Decoration.line({ attributes: { class: "cm-blockquote-line" } }).range(from));
246
+ for (const { pos, offset } of data.nestedBorders) decos.push(Decoration.widget({ widget: new NestedBlockQuoteBorder(offset) }).range(pos));
247
+ return RangeSet.of(decos, true);
248
+ }
249
+ const blockQuoteExtension = ViewPlugin.fromClass(class {
250
+ decorations = Decoration.none;
251
+ constructor(view) {
252
+ this.requestMeasure(view);
253
+ }
254
+ update(u) {
255
+ if (u.docChanged || u.viewportChanged) this.requestMeasure(u.view);
256
+ }
257
+ requestMeasure(view) {
258
+ view.requestMeasure({
259
+ read: (v) => measureBlockQuotes(v),
260
+ write: (data, v) => {
261
+ this.applyMeasure(data, v);
262
+ }
263
+ });
264
+ }
265
+ applyMeasure(data, view) {
266
+ this.decorations = buildDecorationsFromMeasure(view, data);
267
+ }
268
+ }, { decorations: (v) => v.decorations });
269
+
190
270
  //#endregion
191
271
  //#region lib/fold/core.ts
192
272
  const buildDecorations = (state) => {
193
273
  const decorations = [];
194
274
  const specs = state.facet(foldableSyntaxFacet);
195
275
  syntaxTree(state).iterate({ enter: (node) => {
196
- if (selectionTouchesRange(state.selection.ranges, node)) return;
276
+ const selectionTouchesNodeRange = selectionTouchesRange(state.selection.ranges, node);
277
+ const lineage = [];
278
+ let node_ = node;
279
+ while (node_) {
280
+ lineage.push(node_.name);
281
+ node_ = node_.node.parent;
282
+ }
283
+ const path = lineage.reverse().join("/");
197
284
  for (const spec of specs) {
198
- const lineage = [];
199
- let node_ = node;
200
- while (node_) {
201
- lineage.push(node_.name);
202
- node_ = node_.node.parent;
203
- }
204
- const path = lineage.reverse().join("/");
205
285
  if (spec.nodePath instanceof Function) {
206
286
  if (!spec.nodePath(path)) continue;
207
287
  } else if (spec.nodePath instanceof Array) {
208
288
  if (!spec.nodePath.some((testPath) => path.endsWith(testPath))) continue;
209
289
  } else if (!path.endsWith(spec.nodePath)) continue;
210
- if (spec.unfoldZone) {
211
- if (selectionTouchesRange(state.selection.ranges, spec.unfoldZone(state, node))) return;
212
- }
213
- if (spec.onFold) {
214
- const res = spec.onFold(state, node);
290
+ const selectionTouchesRange_ = spec.unfoldZone ? selectionTouchesRange(state.selection.ranges, spec.unfoldZone(state, node)) : selectionTouchesNodeRange;
291
+ if (!spec.keepDecorationOnUnfold && selectionTouchesRange_) return;
292
+ if (spec.buildDecorations) {
293
+ const res = spec.buildDecorations(state, node, selectionTouchesRange_);
215
294
  if (res instanceof Array) decorations.push(...res);
216
295
  else if (res) decorations.push(res);
217
296
  }
@@ -219,7 +298,7 @@ const buildDecorations = (state) => {
219
298
  } });
220
299
  return Decoration.set(decorations, true);
221
300
  };
222
- const foldDecorationExtension = StateField.define({
301
+ const foldExtension = StateField.define({
223
302
  create(state) {
224
303
  return buildDecorations(state);
225
304
  },
@@ -229,7 +308,6 @@ const foldDecorationExtension = StateField.define({
229
308
  },
230
309
  provide: (f) => [EditorView.decorations.from(f)]
231
310
  });
232
- const foldExtension = [foldDecorationExtension];
233
311
  const foldableSyntaxFacet = Facet.define({
234
312
  combine(value) {
235
313
  return [...value];
@@ -241,7 +319,7 @@ const selectAllDecorationsOnSelectExtension = (widgetClass) => EditorView.domEve
241
319
  if (ranges.length === 0 || ranges[0]?.anchor !== ranges[0]?.head) return;
242
320
  const target = e.target;
243
321
  const pos = view.posAtDOM(target);
244
- view.state.field(foldDecorationExtension).between(pos, pos, (from, to) => {
322
+ view.state.field(foldExtension).between(pos, pos, (from, to) => {
245
323
  setTimeout(() => {
246
324
  view.dispatch({ selection: EditorSelection.single(to, from) });
247
325
  }, 0);
@@ -264,13 +342,55 @@ var BulletPoint = class extends WidgetType {
264
342
  };
265
343
  const bulletListExtension = foldableSyntaxFacet.of({
266
344
  nodePath: "BulletList/ListItem/ListMark",
267
- onFold: (_state, node) => {
345
+ buildDecorations: (_state, node) => {
268
346
  const cursor = node.node.cursor();
269
347
  if (cursor.nextSibling() && cursor.name === "Task") return;
270
348
  return Decoration.replace({ widget: new BulletPoint() }).range(node.from, node.to);
271
349
  }
272
350
  });
273
351
 
352
+ //#endregion
353
+ //#region lib/fold/dashes.ts
354
+ const dashMarkdownSyntaxExtension = {
355
+ defineNodes: [{
356
+ name: "Dash",
357
+ style: markdownTags.dash
358
+ }],
359
+ parseInline: [{
360
+ name: "Dash",
361
+ parse: (cx, next, pos) => {
362
+ if (next !== 45 || pos > 1 && cx.char(pos - 1) == 45) return -1;
363
+ let i;
364
+ for (i = pos; i < cx.end && cx.char(i) === 45; i++);
365
+ if (i - pos > 3) return -1;
366
+ return cx.addElement(cx.elt("Dash", pos, i));
367
+ },
368
+ before: "Emphasis"
369
+ }]
370
+ };
371
+ var DashWidget = class extends WidgetType {
372
+ constructor(dashCount) {
373
+ super();
374
+ this.dashCount = dashCount;
375
+ }
376
+ toDOM() {
377
+ const span = document.createElement("span");
378
+ span.className = "cm-dash";
379
+ if (this.dashCount === 2) span.innerHTML = "&#8211;";
380
+ else if (this.dashCount === 3) span.innerHTML = "&#8212;";
381
+ else span.innerHTML = "-".repeat(this.dashCount);
382
+ return span;
383
+ }
384
+ };
385
+ const dashExtension = foldableSyntaxFacet.of({
386
+ nodePath: "Dash",
387
+ buildDecorations: (_state, node) => {
388
+ const dashCount = node.to - node.from;
389
+ if (dashCount < 2 || dashCount > 3) return;
390
+ return Decoration.replace({ widget: new DashWidget(dashCount) }).range(node.from, node.to);
391
+ }
392
+ });
393
+
274
394
  //#endregion
275
395
  //#region lib/fold/emoji.ts
276
396
  const emojiDelimiter = {
@@ -311,7 +431,7 @@ var EmojiWidget = class extends WidgetType {
311
431
  };
312
432
  const emojiExtension = foldableSyntaxFacet.of({
313
433
  nodePath: "Emoji",
314
- onFold: (state, node) => {
434
+ buildDecorations: (state, node) => {
315
435
  const emojiName = state.doc.sliceString(node.from + 1, node.to - 1);
316
436
  const emoji_ = emoji.get(emojiName);
317
437
  if (!emoji_) return;
@@ -336,16 +456,19 @@ var HorizontalRuleWidget = class extends WidgetType {
336
456
  dom.remove();
337
457
  }
338
458
  };
339
- const horizontalRuleTheme = EditorView.theme({ ".cm-horizontal-rule-container": {
340
- height: "1.4em",
341
- display: "flow-root",
342
- "align-items": "center",
343
- padding: "0 2px 0 6px"
344
- } });
459
+ const horizontalRuleTheme = EditorView.theme({
460
+ ".cm-horizontal-rule-container": {
461
+ height: "1.4em",
462
+ display: "flex",
463
+ "align-items": "center",
464
+ padding: "0 2px 0 6px"
465
+ },
466
+ ".cm-horizontal-rule-container hr": { width: "100%" }
467
+ });
345
468
  const horizonalRuleExtension = [
346
469
  foldableSyntaxFacet.of({
347
470
  nodePath: "HorizontalRule",
348
- onFold: (_state, node) => {
471
+ buildDecorations: (_state, node) => {
349
472
  return Decoration.replace({
350
473
  widget: new HorizontalRuleWidget(),
351
474
  block: true,
@@ -360,15 +483,17 @@ const horizonalRuleExtension = [
360
483
  //#endregion
361
484
  //#region lib/fold/image.ts
362
485
  var ImageWidget = class extends WidgetType {
363
- constructor(url) {
486
+ constructor(url, block) {
364
487
  super();
365
488
  this.url = url;
489
+ this.block = block;
366
490
  }
367
491
  toDOM() {
368
- const span = document.createElement("span");
369
- span.className = "cm-image";
370
- if (this.url) span.innerHTML = `<img src="${this.url}" />`;
371
- return span;
492
+ const elem = document.createElement(this.block ? "div" : "span");
493
+ elem.className = "cm-image";
494
+ if (this.block) elem.className += " cm-image-block";
495
+ elem.innerHTML = `<img src="${this.url}" />`;
496
+ return elem;
372
497
  }
373
498
  ignoreEvent(_event) {
374
499
  return false;
@@ -376,12 +501,25 @@ var ImageWidget = class extends WidgetType {
376
501
  };
377
502
  const imageExtension = [foldableSyntaxFacet.of({
378
503
  nodePath: "Image",
379
- onFold: (state, node) => {
504
+ keepDecorationOnUnfold: true,
505
+ buildDecorations: (state, node, selectionTouchesRange$1) => {
380
506
  let imageUrl;
381
507
  iterChildren(node.node.cursor(), (node$1) => {
382
508
  if (node$1.name === "URL") imageUrl = state.doc.sliceString(node$1.from, node$1.to);
383
509
  });
384
- if (imageUrl) return Decoration.replace({ widget: new ImageWidget(imageUrl) }).range(node.from, node.to);
510
+ if (imageUrl) {
511
+ const line = state.doc.lineAt(node.from);
512
+ const block = node.from == line.from && node.to == line.to;
513
+ const widget = new ImageWidget(imageUrl, block);
514
+ if (selectionTouchesRange$1) return Decoration.widget({
515
+ widget,
516
+ block
517
+ }).range(node.to, node.to);
518
+ else return Decoration.replace({
519
+ widget,
520
+ block
521
+ }).range(node.from, node.to);
522
+ }
385
523
  }
386
524
  }), selectAllDecorationsOnSelectExtension("cm-image")];
387
525
 
@@ -406,7 +544,7 @@ var Checkbox = class extends WidgetType {
406
544
  };
407
545
  const taskExtension = [foldableSyntaxFacet.of({
408
546
  nodePath: "BulletList/ListItem/Task/TaskMarker",
409
- onFold: (state, node) => {
547
+ buildDecorations: (state, node) => {
410
548
  const value = state.doc.sliceString(node.from + 1, node.to - 1).toLowerCase() === "x";
411
549
  return Decoration.replace({ widget: new Checkbox(value) }).range(node.from - 2, node.to);
412
550
  },
@@ -426,23 +564,39 @@ const taskExtension = [foldableSyntaxFacet.of({
426
564
  } } }))];
427
565
 
428
566
  //#endregion
429
- //#region lib/fold/blockQuote.ts
430
- var BlockQuoteVerticalLine = class extends WidgetType {
431
- toDOM() {
432
- const span = document.createElement("span");
433
- span.className = "cm-blockquote-vertical-line";
434
- return span;
435
- }
436
- ignoreEvent(_event) {
437
- return false;
567
+ //#region lib/fold/index.ts
568
+ const defaultFoldableSyntaxExtensions = [
569
+ blockQuoteExtension,
570
+ bulletListExtension,
571
+ taskExtension,
572
+ imageExtension,
573
+ emojiExtension,
574
+ horizonalRuleExtension,
575
+ dashExtension
576
+ ];
577
+
578
+ //#endregion
579
+ //#region lib/revealBlockOnArrow.ts
580
+ const maybeReveal = (decorationField, view, direction) => {
581
+ const decorations = view.state.field(decorationField);
582
+ const cursorAt = view.state.selection.main.head;
583
+ for (let iter = decorations.iter(); iter.value; iter.next()) if (direction === "down" && cursorAt == iter.from - 1) {
584
+ view.dispatch({ selection: EditorSelection.single(iter.from) });
585
+ return true;
586
+ } else if (direction === "up" && cursorAt == iter.to + 1) {
587
+ view.dispatch({ selection: EditorSelection.single(iter.to) });
588
+ return true;
438
589
  }
590
+ return false;
439
591
  };
440
- const blockQuoteExtension = foldableSyntaxFacet.of({
441
- nodePath: (nodePath) => nodePath.endsWith("Blockquote/QuoteMark") || nodePath.endsWith("Blockquote/Paragraph/QuoteMark"),
442
- onFold: (_state, node) => {
443
- return Decoration.replace({ widget: new BlockQuoteVerticalLine() }).range(node.from, node.to);
444
- }
445
- });
592
+ const revealBlockOnArrowExtension_ = (decorationField) => keymap.of([{
593
+ key: "ArrowUp",
594
+ run: (view) => maybeReveal(decorationField, view, "up")
595
+ }, {
596
+ key: "ArrowDown",
597
+ run: (view) => maybeReveal(decorationField, view, "down")
598
+ }]);
599
+ const revealBlockOnArrowExtension = [hideExtension, foldExtension].map(revealBlockOnArrowExtension_);
446
600
 
447
601
  //#endregion
448
602
  //#region lib/syntaxHighlighting.ts
@@ -538,15 +692,29 @@ const baseTheme = EditorView.theme({
538
692
  color: "var(--pm-muted-color)",
539
693
  margin: "0 0.2em"
540
694
  },
541
- ".cm-blockquote-vertical-line": {
542
- display: "inline-block",
543
- width: "4px",
544
- marginRight: "4px",
545
- marginLeft: "4px",
546
- height: "1.4em",
547
- verticalAlign: "bottom",
548
- backgroundColor: "var(--pm-blockquote-vertical-line-background-color)"
549
- }
695
+ ".cm-blockquote-line": {
696
+ position: "relative",
697
+ "&:before": {
698
+ content: "\"\"",
699
+ display: "block",
700
+ borderLeft: "solid 0.3em var(--pm-blockquote-vertical-line-background-color)",
701
+ position: "absolute",
702
+ top: "0px",
703
+ bottom: "0px",
704
+ insetInlineStart: "6px",
705
+ zIndex: -10
706
+ }
707
+ },
708
+ ".cm-nested-blockquote-border": {
709
+ display: "block",
710
+ borderLeft: "solid 0.3em var(--pm-blockquote-vertical-line-background-color)",
711
+ position: "absolute",
712
+ top: "0px",
713
+ bottom: "0px",
714
+ insetInlineStart: "calc(6px + var(--blockquote-border-offset))",
715
+ zIndex: -10
716
+ },
717
+ ".cm-image-block": { paddingLeft: "6px" }
550
718
  });
551
719
  const generalSyntaxHighlights = syntaxHighlighting(HighlightStyle.define([
552
720
  {
@@ -709,6 +877,70 @@ const clickLinkExtension = [
709
877
  clickRawUrlExtension
710
878
  ];
711
879
 
880
+ //#endregion
881
+ //#region lib/softIndentExtension.ts
882
+ const softIndentPattern = /^(> )*(\s*)?(([-*+]?|\d[.)])\s)?(\[.\]\s)?/;
883
+ const softIndentRefresh = Annotation.define();
884
+ const softIndentExtension = ViewPlugin.fromClass(class {
885
+ decorations = Decoration.none;
886
+ constructor(view) {
887
+ this.requestMeasure(view);
888
+ }
889
+ update(u) {
890
+ if (u.docChanged || u.viewportChanged || u.selectionSet) this.requestMeasure(u.view);
891
+ if (u.transactions.some((tr) => tr.annotation(softIndentRefresh))) this.requestMeasure(u.view);
892
+ }
893
+ requestMeasure(view) {
894
+ view.requestMeasure({
895
+ read: (view$1) => this.measureIndents(view$1),
896
+ write: (indents, view$1) => {
897
+ this.applyIndents(indents, view$1);
898
+ }
899
+ });
900
+ }
901
+ measureIndents(view) {
902
+ const indents = [];
903
+ for (const { from, to } of view.visibleRanges) {
904
+ const start = view.state.doc.lineAt(from);
905
+ const end = view.state.doc.lineAt(to);
906
+ for (let i = start.number; i <= end.number; i++) {
907
+ const line = view.state.doc.line(i);
908
+ const text = view.state.sliceDoc(line.from, line.to);
909
+ const matches = softIndentPattern.exec(text);
910
+ if (!matches) continue;
911
+ const nonContent = matches[0];
912
+ const indentWidth = (view.coordsAtPos(line.from + nonContent.length)?.left ?? 0) - (view.coordsAtPos(line.from)?.left ?? 0);
913
+ if (!indentWidth) continue;
914
+ indents.push({
915
+ line,
916
+ indentWidth
917
+ });
918
+ }
919
+ }
920
+ return indents;
921
+ }
922
+ buildDecorations(indents) {
923
+ const builder = new RangeSetBuilder();
924
+ for (const { line, indentWidth } of indents) {
925
+ const deco = Decoration.line({ attributes: { style: `padding-inline-start: ${(indentWidth + 6).toString()}px; text-indent: -${indentWidth.toString()}px;` } });
926
+ builder.add(line.from, line.from, deco);
927
+ }
928
+ return builder.finish();
929
+ }
930
+ applyIndents(indents, view) {
931
+ const newDecos = this.buildDecorations(indents);
932
+ let changed = false;
933
+ for (const { from, to } of view.visibleRanges) if (!RangeSet.eq([this.decorations], [newDecos], from, to)) {
934
+ changed = true;
935
+ break;
936
+ }
937
+ if (changed) queueMicrotask(() => {
938
+ view.dispatch({ annotations: [softIndentRefresh.of(true)] });
939
+ });
940
+ this.decorations = newDecos;
941
+ }
942
+ }, { decorations: (v) => v.decorations });
943
+
712
944
  //#endregion
713
945
  //#region lib/codeFenceExtension.ts
714
946
  const codeBlockDecorations = (view) => {
@@ -829,17 +1061,17 @@ const codeFenceTheme = EditorView.theme({
829
1061
  const prosemarkMarkdownSyntaxExtensions = [
830
1062
  additionalMarkdownSyntaxTags,
831
1063
  escapeMarkdownSyntaxExtension,
832
- emojiMarkdownSyntaxExtension
833
- ];
834
- const defaultFoldableSyntaxExtensions = [
835
- blockQuoteExtension,
836
- bulletListExtension,
837
- taskExtension,
838
- imageExtension,
839
- emojiExtension,
840
- horizonalRuleExtension
1064
+ emojiMarkdownSyntaxExtension,
1065
+ dashMarkdownSyntaxExtension
841
1066
  ];
842
1067
  const prosemarkBasicSetup = () => [
1068
+ defaultHideExtensions,
1069
+ defaultFoldableSyntaxExtensions,
1070
+ revealBlockOnArrowExtension,
1071
+ clickLinkExtension,
1072
+ defaultClickLinkHandler,
1073
+ softIndentExtension,
1074
+ codeBlockDecorationsExtension,
843
1075
  history(),
844
1076
  dropCursor(),
845
1077
  indentOnInput(),
@@ -857,11 +1089,7 @@ const prosemarkBasicSetup = () => [
857
1089
  indentWithTab
858
1090
  ]),
859
1091
  foldGutter(),
860
- EditorView.lineWrapping,
861
- defaultHideExtensions,
862
- defaultFoldableSyntaxExtensions,
863
- clickLinkExtension,
864
- codeBlockDecorationsExtension
1092
+ EditorView.lineWrapping
865
1093
  ];
866
1094
  const prosemarkBaseThemeSetup = () => [
867
1095
  baseSyntaxHighlights,
@@ -869,7 +1097,7 @@ const prosemarkBaseThemeSetup = () => [
869
1097
  baseTheme,
870
1098
  codeFenceTheme
871
1099
  ];
872
- const prosemarkLightThemeSetup = () => lightTheme;
1100
+ const prosemarkLightThemeSetup = () => [prosemarkBaseThemeSetup(), lightTheme];
873
1101
 
874
1102
  //#endregion
875
- export { additionalMarkdownSyntaxTags, baseSyntaxHighlights, baseTheme, blockQuoteExtension, bulletListExtension, clickLinkExtension, clickLinkHandler, codeBlockDecorationsExtension, codeFenceTheme, defaultClickLinkHandler, defaultFoldableSyntaxExtensions, defaultHideExtensions, emojiExtension, emojiMarkdownSyntaxExtension, escapeMarkdownSyntaxExtension, eventHandlersWithClass, foldDecorationExtension, foldableSyntaxFacet, generalSyntaxHighlights, horizonalRuleExtension, imageExtension, lightTheme, markdownTags, prosemarkBaseThemeSetup, prosemarkBasicSetup, prosemarkLightThemeSetup, prosemarkMarkdownSyntaxExtensions, selectAllDecorationsOnSelectExtension, taskExtension };
1103
+ export { additionalMarkdownSyntaxTags, baseSyntaxHighlights, baseTheme, blockQuoteExtension, bulletListExtension, clickLinkExtension, clickLinkHandler, codeBlockDecorationsExtension, codeFenceTheme, dashExtension, dashMarkdownSyntaxExtension, defaultClickLinkHandler, defaultFoldableSyntaxExtensions, defaultHideExtensions, emojiExtension, emojiMarkdownSyntaxExtension, escapeMarkdownSyntaxExtension, eventHandlersWithClass, foldExtension, foldableSyntaxFacet, generalSyntaxHighlights, hideExtension, horizonalRuleExtension, imageExtension, lightTheme, markdownTags, prosemarkBaseThemeSetup, prosemarkBasicSetup, prosemarkLightThemeSetup, prosemarkMarkdownSyntaxExtensions, revealBlockOnArrowExtension, selectAllDecorationsOnSelectExtension, softIndentExtension, taskExtension };
package/package.json CHANGED
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "name": "@prosemark/core",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
+ "repository": {
5
+ "url": "https://github.com/jsimonrichard/ProseMark",
6
+ "type": "github"
7
+ },
8
+ "license": "MIT",
9
+ "author": {
10
+ "name": "J. Simon Richard",
11
+ "url": "https://jsimonrichard.com"
12
+ },
4
13
  "type": "module",
5
14
  "files": [
6
15
  "dist"
@@ -19,7 +28,7 @@
19
28
  "preview": "vite preview",
20
29
  "lint": "eslint --max-warnings=0",
21
30
  "check-types": "tsc",
22
- "ci:publish": "bun publish --tolerate-republish"
31
+ "ci:publish": "bash ../../scripts/ci-publish.sh"
23
32
  },
24
33
  "dependencies": {
25
34
  "@lezer/common": "^1.2.3",