@lapismd/mira 0.0.4 → 0.0.5
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/CHANGELOG.md +9 -0
- package/dist/extensions/index.d.ts +7 -0
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/preview/editable-markdown-preview/editable-markdown-surface.svelte +20 -9
- package/dist/preview/editable-markdown-preview/editable-markdown-surface.svelte.d.ts.map +1 -1
- package/dist/preview/editable-markdown-preview/editing-session.d.ts +13 -0
- package/dist/preview/editable-markdown-preview/editing-session.d.ts.map +1 -0
- package/dist/preview/editable-markdown-preview/editing-session.js +50 -0
- package/dist/preview/editable-markdown-preview/editor-surface.svelte +13 -4
- package/dist/preview/editable-markdown-preview/editor-surface.svelte.d.ts +1 -0
- package/dist/preview/editable-markdown-preview/editor-surface.svelte.d.ts.map +1 -1
- package/dist/preview/editable-markdown-preview/serialized-writer.d.ts +5 -1
- package/dist/preview/editable-markdown-preview/serialized-writer.d.ts.map +1 -1
- package/dist/preview/editable-markdown-preview/serialized-writer.js +29 -0
- package/dist/preview/editable-markdown-preview.svelte +8 -6
- package/dist/preview/editable-markdown-preview.svelte.d.ts.map +1 -1
- package/package.json +5 -2
- package/dist/core/code-mirror.test.d.ts +0 -2
- package/dist/core/code-mirror.test.d.ts.map +0 -1
- package/dist/core/code-mirror.test.js +0 -68
- package/dist/core/images.test.d.ts +0 -2
- package/dist/core/images.test.d.ts.map +0 -1
- package/dist/core/images.test.js +0 -109
- package/dist/core/index.test.d.ts +0 -2
- package/dist/core/index.test.d.ts.map +0 -1
- package/dist/core/index.test.js +0 -11
- package/dist/core/markdown-actions.test.d.ts +0 -2
- package/dist/core/markdown-actions.test.d.ts.map +0 -1
- package/dist/core/markdown-actions.test.js +0 -182
- package/dist/extensions/doodle-dividers.test.d.ts +0 -2
- package/dist/extensions/doodle-dividers.test.d.ts.map +0 -1
- package/dist/extensions/doodle-dividers.test.js +0 -296
- package/dist/extensions/index.test.d.ts +0 -2
- package/dist/extensions/index.test.d.ts.map +0 -1
- package/dist/extensions/index.test.js +0 -283
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -64
- package/dist/internal/appearance-context.test.d.ts +0 -2
- package/dist/internal/appearance-context.test.d.ts.map +0 -1
- package/dist/internal/appearance-context.test.js +0 -31
- package/dist/internal/codemirror/base/index.test.d.ts +0 -2
- package/dist/internal/codemirror/base/index.test.d.ts.map +0 -1
- package/dist/internal/codemirror/base/index.test.js +0 -303
- package/dist/internal/codemirror/base/search.test.d.ts +0 -2
- package/dist/internal/codemirror/base/search.test.d.ts.map +0 -1
- package/dist/internal/codemirror/base/search.test.js +0 -155
- package/dist/internal/codemirror/base/selection-toolbar.test.d.ts +0 -2
- package/dist/internal/codemirror/base/selection-toolbar.test.d.ts.map +0 -1
- package/dist/internal/codemirror/base/selection-toolbar.test.js +0 -149
- package/dist/internal/codemirror/markdown/authoring.test.d.ts +0 -2
- package/dist/internal/codemirror/markdown/authoring.test.d.ts.map +0 -1
- package/dist/internal/codemirror/markdown/authoring.test.js +0 -222
- package/dist/internal/codemirror/markdown/index.test.d.ts +0 -2
- package/dist/internal/codemirror/markdown/index.test.d.ts.map +0 -1
- package/dist/internal/codemirror/markdown/index.test.js +0 -158
- package/dist/internal/codemirror/markdown/showcase-fixture.test.d.ts +0 -2
- package/dist/internal/codemirror/markdown/showcase-fixture.test.d.ts.map +0 -1
- package/dist/internal/codemirror/markdown/showcase-fixture.test.js +0 -50
- package/dist/internal/codemirror/rich/block-controls.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/block-controls.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/block-controls.test.js +0 -401
- package/dist/internal/codemirror/rich/block-ranges.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/block-ranges.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/block-ranges.test.js +0 -310
- package/dist/internal/codemirror/rich/block-toolbar.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/block-toolbar.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/block-toolbar.test.js +0 -178
- package/dist/internal/codemirror/rich/decorations/fold-indicators.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/decorations/fold-indicators.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/decorations/fold-indicators.test.js +0 -8
- package/dist/internal/codemirror/rich/index.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/index.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/index.test.js +0 -607
- package/dist/internal/codemirror/rich/utils/code-block-lines.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/code-block-lines.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/code-block-lines.test.js +0 -82
- package/dist/internal/codemirror/rich/utils/fenced-code.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/fenced-code.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/fenced-code.test.js +0 -12
- package/dist/internal/codemirror/rich/utils/headings.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/headings.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/headings.test.js +0 -27
- package/dist/internal/codemirror/rich/utils/height-estimates.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/height-estimates.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/height-estimates.test.js +0 -10
- package/dist/internal/codemirror/rich/utils/images.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/images.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/images.test.js +0 -37
- package/dist/internal/codemirror/rich/utils/indent.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/indent.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/indent.test.js +0 -169
- package/dist/internal/codemirror/rich/utils/inline-math.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/inline-math.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/inline-math.test.js +0 -15
- package/dist/internal/codemirror/rich/utils/links.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/links.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/links.test.js +0 -28
- package/dist/internal/codemirror/rich/utils/list-callouts.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/list-callouts.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/list-callouts.test.js +0 -47
- package/dist/internal/codemirror/rich/utils/measured-indent.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/measured-indent.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/measured-indent.test.js +0 -102
- package/dist/internal/codemirror/rich/utils/ranges.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/ranges.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/ranges.test.js +0 -22
- package/dist/internal/codemirror/rich/utils/tasks.test.d.ts +0 -2
- package/dist/internal/codemirror/rich/utils/tasks.test.d.ts.map +0 -1
- package/dist/internal/codemirror/rich/utils/tasks.test.js +0 -57
- package/dist/internal/task-states.test.d.ts +0 -2
- package/dist/internal/task-states.test.d.ts.map +0 -1
- package/dist/internal/task-states.test.js +0 -36
- package/dist/mira-code-editor.test.d.ts +0 -2
- package/dist/mira-code-editor.test.d.ts.map +0 -1
- package/dist/mira-code-editor.test.js +0 -93
- package/dist/mode-position.test.d.ts +0 -2
- package/dist/mode-position.test.d.ts.map +0 -1
- package/dist/mode-position.test.js +0 -60
- package/dist/preview/code-language.test.d.ts +0 -2
- package/dist/preview/code-language.test.d.ts.map +0 -1
- package/dist/preview/code-language.test.js +0 -12
- package/dist/preview/components/icon.test.d.ts +0 -2
- package/dist/preview/components/icon.test.d.ts.map +0 -1
- package/dist/preview/components/icon.test.js +0 -20
- package/dist/preview/editable-markdown-preview/behavior.test.d.ts +0 -2
- package/dist/preview/editable-markdown-preview/behavior.test.d.ts.map +0 -1
- package/dist/preview/editable-markdown-preview/behavior.test.js +0 -36
- package/dist/preview/editable-markdown-preview/serialized-writer.test.d.ts +0 -2
- package/dist/preview/editable-markdown-preview/serialized-writer.test.d.ts.map +0 -1
- package/dist/preview/editable-markdown-preview/serialized-writer.test.js +0 -59
- package/dist/preview/embed-target.test.d.ts +0 -2
- package/dist/preview/embed-target.test.d.ts.map +0 -1
- package/dist/preview/embed-target.test.js +0 -78
- package/dist/preview/frontmatter/frontmatter.test.d.ts +0 -2
- package/dist/preview/frontmatter/frontmatter.test.d.ts.map +0 -1
- package/dist/preview/frontmatter/frontmatter.test.js +0 -190
- package/dist/preview/frontmatter/suggestions.test.d.ts +0 -2
- package/dist/preview/frontmatter/suggestions.test.d.ts.map +0 -1
- package/dist/preview/frontmatter/suggestions.test.js +0 -45
- package/dist/preview/outline.test.d.ts +0 -2
- package/dist/preview/outline.test.d.ts.map +0 -1
- package/dist/preview/outline.test.js +0 -98
- package/dist/preview/portable-surfaces.test.d.ts +0 -2
- package/dist/preview/portable-surfaces.test.d.ts.map +0 -1
- package/dist/preview/portable-surfaces.test.js +0 -513
- package/dist/preview/preview.test.d.ts +0 -2
- package/dist/preview/preview.test.d.ts.map +0 -1
- package/dist/preview/preview.test.js +0 -154
- package/dist/preview/remark/callouts.test.d.ts +0 -2
- package/dist/preview/remark/callouts.test.d.ts.map +0 -1
- package/dist/preview/remark/callouts.test.js +0 -23
- package/dist/preview/remark/custom-checklists.test.d.ts +0 -2
- package/dist/preview/remark/custom-checklists.test.d.ts.map +0 -1
- package/dist/preview/remark/custom-checklists.test.js +0 -51
- package/dist/preview/remark/links.test.d.ts +0 -2
- package/dist/preview/remark/links.test.d.ts.map +0 -1
- package/dist/preview/remark/links.test.js +0 -62
- package/dist/preview/remark/parity.test.d.ts +0 -2
- package/dist/preview/remark/parity.test.d.ts.map +0 -1
- package/dist/preview/remark/parity.test.js +0 -105
- package/dist/preview/styles/frontmatter-properties.test.d.ts +0 -2
- package/dist/preview/styles/frontmatter-properties.test.d.ts.map +0 -1
- package/dist/preview/styles/frontmatter-properties.test.js +0 -57
- package/dist/preview/styles/source-mode.test.d.ts +0 -2
- package/dist/preview/styles/source-mode.test.d.ts.map +0 -1
- package/dist/preview/styles/source-mode.test.js +0 -28
- package/dist/tables/extension.test.d.ts +0 -2
- package/dist/tables/extension.test.d.ts.map +0 -1
- package/dist/tables/extension.test.js +0 -100
- package/dist/tables/grid-editor-column.test.d.ts +0 -2
- package/dist/tables/grid-editor-column.test.d.ts.map +0 -1
- package/dist/tables/grid-editor-column.test.js +0 -27
- package/dist/tables/grid-table.test.d.ts +0 -2
- package/dist/tables/grid-table.test.d.ts.map +0 -1
- package/dist/tables/grid-table.test.js +0 -140
- package/dist/tables/index.test.d.ts +0 -2
- package/dist/tables/index.test.d.ts.map +0 -1
- package/dist/tables/index.test.js +0 -7
- package/dist/tables/inline-editor-extensions.test.d.ts +0 -2
- package/dist/tables/inline-editor-extensions.test.d.ts.map +0 -1
- package/dist/tables/inline-editor-extensions.test.js +0 -23
- package/dist/tables/pipe-table.test.d.ts +0 -2
- package/dist/tables/pipe-table.test.d.ts.map +0 -1
- package/dist/tables/pipe-table.test.js +0 -13
- package/dist/tables/table-node.test.d.ts +0 -2
- package/dist/tables/table-node.test.d.ts.map +0 -1
- package/dist/tables/table-node.test.js +0 -167
- package/dist/tables/widgets/source-button.test.d.ts +0 -2
- package/dist/tables/widgets/source-button.test.d.ts.map +0 -1
- package/dist/tables/widgets/source-button.test.js +0 -11
- package/dist/themes/theme-contract.test.d.ts +0 -2
- package/dist/themes/theme-contract.test.d.ts.map +0 -1
- package/dist/themes/theme-contract.test.js +0 -88
- package/dist/ui/index.test.d.ts +0 -2
- package/dist/ui/index.test.d.ts.map +0 -1
- package/dist/ui/index.test.js +0 -7
- package/dist/ui/styles.test.d.ts +0 -2
- package/dist/ui/styles.test.d.ts.map +0 -1
- package/dist/ui/styles.test.js +0 -26
- package/dist/ui/table-dnd/table-dnd-utils.test.d.ts +0 -2
- package/dist/ui/table-dnd/table-dnd-utils.test.d.ts.map +0 -1
- package/dist/ui/table-dnd/table-dnd-utils.test.js +0 -46
package/dist/core/images.test.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { EditorView } from "@codemirror/view";
|
|
3
|
-
import { describe, expect, it, vi } from "vitest";
|
|
4
|
-
import { createImageMarkdown, createUploadingImageMarkdown, insertImageFiles, miraUploadingImageScheme, } from "./images";
|
|
5
|
-
function createView(doc = "") {
|
|
6
|
-
const parent = document.createElement("div");
|
|
7
|
-
document.body.append(parent);
|
|
8
|
-
const view = new EditorView({ doc, parent });
|
|
9
|
-
return {
|
|
10
|
-
view,
|
|
11
|
-
destroy() {
|
|
12
|
-
view.destroy();
|
|
13
|
-
parent.remove();
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
function pngFile(name = "shot.png") {
|
|
18
|
-
return new File([new Uint8Array([137, 80, 78, 71])], name, {
|
|
19
|
-
type: "image/png",
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
describe("image markdown insertion", () => {
|
|
23
|
-
it("generates reference-style image markdown by default", () => {
|
|
24
|
-
expect(createImageMarkdown({
|
|
25
|
-
alt: "Alt Text",
|
|
26
|
-
doc: "",
|
|
27
|
-
src: "data:image/png;base64,abc=",
|
|
28
|
-
syntax: "reference",
|
|
29
|
-
})).toBe("![Alt Text][alt-text]\n\n[alt-text]: data:image/png;base64,abc=");
|
|
30
|
-
});
|
|
31
|
-
it("avoids colliding with existing image reference labels", () => {
|
|
32
|
-
expect(createImageMarkdown({
|
|
33
|
-
alt: "Alt Text",
|
|
34
|
-
doc: "[alt-text]: old",
|
|
35
|
-
src: "data:image/png;base64,abc=",
|
|
36
|
-
syntax: "reference",
|
|
37
|
-
})).toContain("[alt-text-2]: data:image/png;base64,abc=");
|
|
38
|
-
});
|
|
39
|
-
it("can generate Carta-compatible inline image markdown", () => {
|
|
40
|
-
expect(createImageMarkdown({
|
|
41
|
-
alt: "Alt Text",
|
|
42
|
-
doc: "",
|
|
43
|
-
src: "data:image/png;base64,abc=",
|
|
44
|
-
syntax: "inline",
|
|
45
|
-
})).toBe("");
|
|
46
|
-
});
|
|
47
|
-
it("creates GitHub-style uploading placeholders with unique tokens", () => {
|
|
48
|
-
const placeholder = createUploadingImageMarkdown(pngFile(), "abc-123");
|
|
49
|
-
expect(placeholder).toBe(``);
|
|
50
|
-
});
|
|
51
|
-
it("inserts uploading placeholders then replaces them when upload finishes", async () => {
|
|
52
|
-
const { view, destroy } = createView("Before ");
|
|
53
|
-
view.dispatch({
|
|
54
|
-
selection: { anchor: view.state.doc.length },
|
|
55
|
-
});
|
|
56
|
-
let resolveUpload;
|
|
57
|
-
const upload = new Promise((resolve) => {
|
|
58
|
-
resolveUpload = resolve;
|
|
59
|
-
});
|
|
60
|
-
const insertPromise = insertImageFiles(view, [pngFile()], {
|
|
61
|
-
imageUpload: () => upload,
|
|
62
|
-
imageSyntax: "inline",
|
|
63
|
-
});
|
|
64
|
-
expect(view.state.doc.toString()).toMatch(/^Before !\[Uploading shot\.png…\]\(mira-uploading:[^)]+\)$/);
|
|
65
|
-
resolveUpload("https://cdn.example/shot.png");
|
|
66
|
-
await insertPromise;
|
|
67
|
-
expect(view.state.doc.toString()).toBe("Before ");
|
|
68
|
-
destroy();
|
|
69
|
-
});
|
|
70
|
-
it("removes the placeholder when upload fails", async () => {
|
|
71
|
-
const { view, destroy } = createView("Hello");
|
|
72
|
-
view.dispatch({
|
|
73
|
-
selection: { anchor: view.state.doc.length },
|
|
74
|
-
});
|
|
75
|
-
const onImageUploadError = vi.fn();
|
|
76
|
-
await insertImageFiles(view, [pngFile()], {
|
|
77
|
-
imageUpload: async () => {
|
|
78
|
-
throw new Error("upload failed");
|
|
79
|
-
},
|
|
80
|
-
imageSyntax: "inline",
|
|
81
|
-
onImageUploadError,
|
|
82
|
-
});
|
|
83
|
-
expect(view.state.doc.toString()).toBe("Hello");
|
|
84
|
-
expect(onImageUploadError).toHaveBeenCalledOnce();
|
|
85
|
-
destroy();
|
|
86
|
-
});
|
|
87
|
-
it("uploads multiple images in parallel with distinct placeholders", async () => {
|
|
88
|
-
const { view, destroy } = createView("");
|
|
89
|
-
const resolvers = [];
|
|
90
|
-
const uploads = [
|
|
91
|
-
new Promise((resolve) => resolvers.push(resolve)),
|
|
92
|
-
new Promise((resolve) => resolvers.push(resolve)),
|
|
93
|
-
];
|
|
94
|
-
let uploadIndex = 0;
|
|
95
|
-
const insertPromise = insertImageFiles(view, [pngFile("a.png"), pngFile("b.png")], {
|
|
96
|
-
imageUpload: () => uploads[uploadIndex++],
|
|
97
|
-
imageSyntax: "inline",
|
|
98
|
-
});
|
|
99
|
-
const during = view.state.doc.toString();
|
|
100
|
-
expect(during).toContain(";
|
|
101
|
-
expect(during).toContain(";
|
|
102
|
-
expect(during.match(/mira-uploading:/g)?.length).toBe(2);
|
|
103
|
-
resolvers[1]("https://cdn.example/b.png");
|
|
104
|
-
resolvers[0]("https://cdn.example/a.png");
|
|
105
|
-
await insertPromise;
|
|
106
|
-
expect(view.state.doc.toString()).toBe("\n\n");
|
|
107
|
-
destroy();
|
|
108
|
-
});
|
|
109
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/core/index.test.ts"],"names":[],"mappings":""}
|
package/dist/core/index.test.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { EditorState } from "@codemirror/state";
|
|
3
|
-
import { fromOffset, toOffset } from ".";
|
|
4
|
-
describe("position conversion", () => {
|
|
5
|
-
it("round trips offsets and editor positions", () => {
|
|
6
|
-
const state = EditorState.create({ doc: "one\ntwo" });
|
|
7
|
-
const position = fromOffset(state.doc, 5);
|
|
8
|
-
expect(position).toEqual({ line: 1, ch: 1 });
|
|
9
|
-
expect(toOffset(state.doc, position)).toBe(5);
|
|
10
|
-
});
|
|
11
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-actions.test.d.ts","sourceRoot":"","sources":["../../src/core/markdown-actions.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { EditorSelection } from "@codemirror/state";
|
|
2
|
-
import { redo, undo } from "@codemirror/commands";
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
import { createBaseCodeMirrorExtensions } from "../internal/codemirror/base";
|
|
5
|
-
import { createMarkdownCodeMirrorExtensions } from "../internal/codemirror/markdown";
|
|
6
|
-
import { isMiraMarkdownActionActive, MiraEditorController } from ".";
|
|
7
|
-
function createController(value, options = {}) {
|
|
8
|
-
return new MiraEditorController({
|
|
9
|
-
value,
|
|
10
|
-
codeMirrorExtensions: [
|
|
11
|
-
createBaseCodeMirrorExtensions({
|
|
12
|
-
lineNumbers: false,
|
|
13
|
-
readonly: options.readonly,
|
|
14
|
-
}),
|
|
15
|
-
createMarkdownCodeMirrorExtensions({ sourceMode: true }),
|
|
16
|
-
],
|
|
17
|
-
onChange(next, change) {
|
|
18
|
-
options.onChange?.(next, change.userEvent);
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
function select(controller, anchor, head = anchor) {
|
|
23
|
-
controller.view.dispatch({
|
|
24
|
-
selection: EditorSelection.single(anchor, head),
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
function apply(value, action, anchor, head = anchor) {
|
|
28
|
-
const controller = createController(value);
|
|
29
|
-
select(controller, anchor, head);
|
|
30
|
-
expect(controller.applyMarkdownAction(action)).toBe(true);
|
|
31
|
-
return {
|
|
32
|
-
controller,
|
|
33
|
-
selection: controller.view.state.selection.main,
|
|
34
|
-
value: controller.getValue(),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
describe("inline Markdown toolbar actions", () => {
|
|
38
|
-
it("reports an exact formatted selection as active", () => {
|
|
39
|
-
const controller = createController("A **bold** word");
|
|
40
|
-
select(controller, 4, 8);
|
|
41
|
-
expect(isMiraMarkdownActionActive(controller.view.state, "bold")).toBe(true);
|
|
42
|
-
expect(isMiraMarkdownActionActive(controller.view.state, "italic")).toBe(false);
|
|
43
|
-
select(controller, 5, 8);
|
|
44
|
-
expect(isMiraMarkdownActionActive(controller.view.state, "bold")).toBe(false);
|
|
45
|
-
});
|
|
46
|
-
it.each([
|
|
47
|
-
["bold", "word", "**word**"],
|
|
48
|
-
["italic", "word", "_word_"],
|
|
49
|
-
["strikethrough", "word", "~~word~~"],
|
|
50
|
-
["inlineCode", "word", "`word`"],
|
|
51
|
-
])("wraps the current word for %s", (action, source, expected) => {
|
|
52
|
-
const result = apply(source, action, 2);
|
|
53
|
-
expect(result.value).toBe(expected);
|
|
54
|
-
expect(result.selection.anchor).toBe(2 + expected.indexOf(source) - 0);
|
|
55
|
-
expect(result.selection.empty).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
it.each([
|
|
58
|
-
["bold", "**word**", 4],
|
|
59
|
-
["bold", "__word__", 4],
|
|
60
|
-
["italic", "_word_", 3],
|
|
61
|
-
["italic", "*word*", 3],
|
|
62
|
-
["strikethrough", "~~word~~", 4],
|
|
63
|
-
["inlineCode", "`word`", 3],
|
|
64
|
-
])("removes enclosing %s syntax at the caret", (action, source, caret) => {
|
|
65
|
-
const result = apply(source, action, caret);
|
|
66
|
-
expect(result.value).toBe("word");
|
|
67
|
-
expect(result.selection.anchor).toBe(caret - (source.length - 4) / 2);
|
|
68
|
-
});
|
|
69
|
-
it("wraps and unwraps an exact selection while preserving its direction", () => {
|
|
70
|
-
const wrapped = apply("one two", "bold", 7, 4);
|
|
71
|
-
expect(wrapped.value).toBe("one **two**");
|
|
72
|
-
expect(wrapped.selection.anchor).toBe(9);
|
|
73
|
-
expect(wrapped.selection.head).toBe(6);
|
|
74
|
-
expect(wrapped.controller.applyMarkdownAction("bold")).toBe(true);
|
|
75
|
-
expect(wrapped.controller.getValue()).toBe("one two");
|
|
76
|
-
expect(wrapped.controller.view.state.selection.main.anchor).toBe(7);
|
|
77
|
-
expect(wrapped.controller.view.state.selection.main.head).toBe(4);
|
|
78
|
-
});
|
|
79
|
-
it("inserts empty delimiter pairs on whitespace and places the caret inside", () => {
|
|
80
|
-
const result = apply("one two", "bold", 3);
|
|
81
|
-
expect(result.value).toBe("one**** two");
|
|
82
|
-
expect(result.selection.anchor).toBe(5);
|
|
83
|
-
});
|
|
84
|
-
it("uses a longer inline-code fence when the selection contains backticks", () => {
|
|
85
|
-
const result = apply("a`b", "inlineCode", 0, 3);
|
|
86
|
-
expect(result.value).toBe("``a`b``");
|
|
87
|
-
});
|
|
88
|
-
it("preserves nested formatting when removing only the requested syntax", () => {
|
|
89
|
-
const result = apply("**bold _and italic_**", "bold", 8);
|
|
90
|
-
expect(result.value).toBe("bold _and italic_");
|
|
91
|
-
});
|
|
92
|
-
it("wraps a current word as a link, selects its target, and unlinks from the target", () => {
|
|
93
|
-
const linked = apply("Visit Mira today", "link", 8);
|
|
94
|
-
expect(linked.value).toBe("Visit [Mira](https://example.com) today");
|
|
95
|
-
expect(linked.controller.view.state.sliceDoc(linked.selection.from, linked.selection.to)).toBe("https://example.com");
|
|
96
|
-
expect(linked.controller.applyMarkdownAction("link")).toBe(true);
|
|
97
|
-
expect(linked.controller.getValue()).toBe("Visit Mira today");
|
|
98
|
-
});
|
|
99
|
-
it("creates one undoable transaction with the formatting user event", () => {
|
|
100
|
-
const changes = [];
|
|
101
|
-
const controller = createController("word", {
|
|
102
|
-
onChange(value, event) {
|
|
103
|
-
changes.push({ value, event });
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
select(controller, 2);
|
|
107
|
-
expect(controller.applyMarkdownAction("bold")).toBe(true);
|
|
108
|
-
expect(changes).toEqual([{ value: "**word**", event: "input.format" }]);
|
|
109
|
-
expect(undo(controller.view)).toBe(true);
|
|
110
|
-
expect(controller.getValue()).toBe("word");
|
|
111
|
-
expect(redo(controller.view)).toBe(true);
|
|
112
|
-
expect(controller.getValue()).toBe("**word**");
|
|
113
|
-
});
|
|
114
|
-
it("preserves the editor scroll position while restoring focus", () => {
|
|
115
|
-
const controller = createController("word");
|
|
116
|
-
const host = document.createElement("div");
|
|
117
|
-
document.body.append(host);
|
|
118
|
-
controller.mount(host);
|
|
119
|
-
select(controller, 2);
|
|
120
|
-
controller.view.scrollDOM.scrollTop = 31;
|
|
121
|
-
controller.view.scrollDOM.scrollLeft = 7;
|
|
122
|
-
expect(controller.applyMarkdownAction("bold")).toBe(true);
|
|
123
|
-
expect(controller.view.hasFocus).toBe(true);
|
|
124
|
-
expect(controller.view.scrollDOM.scrollTop).toBe(31);
|
|
125
|
-
expect(controller.view.scrollDOM.scrollLeft).toBe(7);
|
|
126
|
-
controller.destroy();
|
|
127
|
-
host.remove();
|
|
128
|
-
});
|
|
129
|
-
it("rejects authoring actions in a readonly editor", () => {
|
|
130
|
-
const controller = createController("word", { readonly: true });
|
|
131
|
-
select(controller, 2);
|
|
132
|
-
expect(controller.applyMarkdownAction("bold")).toBe(false);
|
|
133
|
-
expect(controller.getValue()).toBe("word");
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
describe("line Markdown toolbar actions", () => {
|
|
137
|
-
it("adds and removes exactly one H1 prefix at a caret within the line", () => {
|
|
138
|
-
const result = apply("Paragraph", "heading", 4);
|
|
139
|
-
expect(result.value).toBe("# Paragraph");
|
|
140
|
-
expect(result.selection.anchor).toBe(6);
|
|
141
|
-
expect(result.controller.applyMarkdownAction("heading")).toBe(true);
|
|
142
|
-
expect(result.controller.getValue()).toBe("Paragraph");
|
|
143
|
-
expect(result.controller.view.state.selection.main.anchor).toBe(4);
|
|
144
|
-
});
|
|
145
|
-
it("removes existing ATX and Setext heading syntax", () => {
|
|
146
|
-
expect(apply("### Heading ###", "heading", 6).value).toBe("Heading");
|
|
147
|
-
expect(apply("Heading\n=======", "heading", 3).value).toBe("Heading");
|
|
148
|
-
});
|
|
149
|
-
it("normalizes mixed selected lines to H1 and excludes a final column-zero line", () => {
|
|
150
|
-
const source = "## Existing\nPlain\nUntouched";
|
|
151
|
-
const result = apply(source, "heading", 0, source.indexOf("Untouched"));
|
|
152
|
-
expect(result.value).toBe("# Existing\n# Plain\nUntouched");
|
|
153
|
-
});
|
|
154
|
-
it("normalizes mixed blockquote lines and toggles them off together", () => {
|
|
155
|
-
const source = "> Quoted\nPlain\n\nLast";
|
|
156
|
-
const result = apply(source, "quote", 0, source.length);
|
|
157
|
-
expect(result.value).toBe("> Quoted\n> Plain\n>\n> Last");
|
|
158
|
-
expect(result.controller.applyMarkdownAction("quote")).toBe(true);
|
|
159
|
-
expect(result.controller.getValue()).toBe("Quoted\nPlain\n\nLast");
|
|
160
|
-
});
|
|
161
|
-
it("preserves list and quote ancestry when adding nested block prefixes", () => {
|
|
162
|
-
expect(apply("- Item", "quote", 4).value).toBe("- > Item");
|
|
163
|
-
expect(apply("> Item", "bulletList", 4).value).toBe("> - Item");
|
|
164
|
-
expect(apply("> - Item", "heading", 6).value).toBe("> - # Item");
|
|
165
|
-
});
|
|
166
|
-
it("normalizes and converts bullet, ordered, and task markers", () => {
|
|
167
|
-
const source = "- Bullet\n2. Ordered\n* [x] Done";
|
|
168
|
-
const tasks = apply(source, "taskList", 0, source.length);
|
|
169
|
-
expect(tasks.value).toBe("- [ ] Bullet\n- [ ] Ordered\n* [x] Done");
|
|
170
|
-
const numbered = apply(tasks.value, "numberedList", 0, tasks.value.length);
|
|
171
|
-
expect(numbered.value).toBe("1. Bullet\n1. Ordered\n1. Done");
|
|
172
|
-
});
|
|
173
|
-
it("removes custom task markers without leaving checkbox delimiters", () => {
|
|
174
|
-
const source = "- [?] Question\n- [>] Forwarded";
|
|
175
|
-
const result = apply(source, "taskList", 0, source.length);
|
|
176
|
-
expect(result.value).toBe("Question\nForwarded");
|
|
177
|
-
});
|
|
178
|
-
it("skips selected blank rows for lists but starts a list on an empty caret line", () => {
|
|
179
|
-
expect(apply("One\n\nTwo", "bulletList", 0, 8).value).toBe("- One\n\n- Two");
|
|
180
|
-
expect(apply("", "numberedList", 0).value).toBe("1. ");
|
|
181
|
-
});
|
|
182
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"doodle-dividers.test.d.ts","sourceRoot":"","sources":["../../src/extensions/doodle-dividers.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
import { history, undo } from "@codemirror/commands";
|
|
2
|
-
import { EditorState, Transaction } from "@codemirror/state";
|
|
3
|
-
import { EditorView } from "@codemirror/view";
|
|
4
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { createMiraMarkdownLanguage } from "../internal/codemirror/markdown";
|
|
6
|
-
import { collectMarkdownBlockRanges, duplicateMarkdownBlockRange, } from "../internal/codemirror/rich/block-ranges";
|
|
7
|
-
import { defaultMiraDoodleDividerVariants, doodleDividersExtension, materializeDoodleDividerSeeds, } from ".";
|
|
8
|
-
import { createMiraDoodleDividerRandom, resolveMiraDoodleDividerDrawing, } from "./doodle-dividers";
|
|
9
|
-
const editors = [];
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
for (const editor of editors.splice(0)) {
|
|
12
|
-
const parent = editor.dom.parentElement;
|
|
13
|
-
editor.destroy();
|
|
14
|
-
parent?.remove();
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
describe("doodle divider seed materialization", () => {
|
|
18
|
-
it("adds seeds only to semantic unseeded thematic breaks", () => {
|
|
19
|
-
let seed = 1;
|
|
20
|
-
const markdown = `---
|
|
21
|
-
title: Demo
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
Setext heading
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
| Column |
|
|
28
|
-
| --- |
|
|
29
|
-
| Value |
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
> ---
|
|
34
|
-
|
|
35
|
-
<!-- mira-divider:v1:000000ff -->
|
|
36
|
-
***`;
|
|
37
|
-
expect(materializeDoodleDividerSeeds(markdown, {
|
|
38
|
-
createSeed: () => seed++,
|
|
39
|
-
})).toBe(`---
|
|
40
|
-
title: Demo
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
Setext heading
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
| Column |
|
|
47
|
-
| --- |
|
|
48
|
-
| Value |
|
|
49
|
-
|
|
50
|
-
<!-- mira-divider:v1:00000001 -->
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
> <!-- mira-divider:v1:00000002 -->
|
|
54
|
-
> ---
|
|
55
|
-
|
|
56
|
-
<!-- mira-divider:v1:000000ff -->
|
|
57
|
-
***`);
|
|
58
|
-
});
|
|
59
|
-
it("keeps existing, malformed, and orphan comments distinct", () => {
|
|
60
|
-
const markdown = `<!-- mira-divider:v1:0000000a -->
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
<!-- mira-divider:v2:0000000b -->
|
|
64
|
-
|
|
65
|
-
Paragraph`;
|
|
66
|
-
expect(materializeDoodleDividerSeeds(markdown, { createSeed: () => 12 })).toBe(markdown);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
describe("doodle divider authoring", () => {
|
|
70
|
-
it("seeds typed rules in the original undoable transaction", () => {
|
|
71
|
-
const createSeed = vi.fn(() => 0x2a);
|
|
72
|
-
const view = createEditor("Intro\n\n", createSeed);
|
|
73
|
-
const before = view.state.doc.toString();
|
|
74
|
-
view.dispatch({
|
|
75
|
-
changes: { from: before.length, insert: "---" },
|
|
76
|
-
annotations: Transaction.userEvent.of("input.type"),
|
|
77
|
-
});
|
|
78
|
-
expect(view.state.doc.toString()).toBe("Intro\n\n<!-- mira-divider:v1:0000002a -->\n---");
|
|
79
|
-
expect(createSeed).toHaveBeenCalledWith({
|
|
80
|
-
line: 3,
|
|
81
|
-
offset: 7,
|
|
82
|
-
reason: "authoring",
|
|
83
|
-
sourcePath: "notes/dividers.md",
|
|
84
|
-
});
|
|
85
|
-
expect(undo(view)).toBe(true);
|
|
86
|
-
expect(view.state.doc.toString()).toBe(before);
|
|
87
|
-
});
|
|
88
|
-
it("seeds multiple pasted rules and preserves a pasted authored pair", () => {
|
|
89
|
-
let seed = 1;
|
|
90
|
-
const view = createEditor("", () => seed++);
|
|
91
|
-
const pasted = `Intro
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
<!-- mira-divider:v1:000000ff -->
|
|
96
|
-
***
|
|
97
|
-
|
|
98
|
-
> ---`;
|
|
99
|
-
view.dispatch({
|
|
100
|
-
changes: { from: 0, insert: pasted },
|
|
101
|
-
annotations: Transaction.userEvent.of("input.paste"),
|
|
102
|
-
});
|
|
103
|
-
expect(view.state.doc.toString()).toBe(`Intro
|
|
104
|
-
|
|
105
|
-
<!-- mira-divider:v1:00000001 -->
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
<!-- mira-divider:v1:000000ff -->
|
|
109
|
-
***
|
|
110
|
-
|
|
111
|
-
> <!-- mira-divider:v1:00000002 -->
|
|
112
|
-
> ---`);
|
|
113
|
-
});
|
|
114
|
-
it("does not rewrite existing bare rules on mount or unrelated edits", () => {
|
|
115
|
-
const createSeed = vi.fn(() => 1);
|
|
116
|
-
const view = createEditor("---\n\nParagraph", createSeed);
|
|
117
|
-
view.dispatch({ selection: { anchor: view.state.doc.length } });
|
|
118
|
-
view.dispatch({
|
|
119
|
-
changes: { from: view.state.doc.length, insert: "!" },
|
|
120
|
-
annotations: Transaction.userEvent.of("input.type"),
|
|
121
|
-
});
|
|
122
|
-
expect(view.state.doc.toString()).toBe("---\n\nParagraph!");
|
|
123
|
-
expect(createSeed).not.toHaveBeenCalled();
|
|
124
|
-
});
|
|
125
|
-
it("gives an editor-duplicated pair a fresh seed", () => {
|
|
126
|
-
const view = createEditor("<!-- mira-divider:v1:00000001 -->\n---\n\nParagraph\n", () => 2);
|
|
127
|
-
const divider = collectMarkdownBlockRanges(view.state)[0];
|
|
128
|
-
duplicateMarkdownBlockRange(view, divider);
|
|
129
|
-
expect(view.state.doc.toString()).toBe(`<!-- mira-divider:v1:00000001 -->
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
<!-- mira-divider:v1:00000002 -->
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
Paragraph
|
|
136
|
-
`);
|
|
137
|
-
});
|
|
138
|
-
it("materializes existing rules through the contributed command", () => {
|
|
139
|
-
const extension = doodleDividersExtension({ createSeed: () => 9 });
|
|
140
|
-
const view = createEditor("Alpha\n\n---\n", () => 9);
|
|
141
|
-
const command = extension.commands?.find((candidate) => candidate.id === "mira-doodle-dividers-materialize");
|
|
142
|
-
if (!command)
|
|
143
|
-
throw new Error("Materialize command was not contributed");
|
|
144
|
-
const scrollTop = 18;
|
|
145
|
-
view.scrollDOM.scrollTop = scrollTop;
|
|
146
|
-
command.run({
|
|
147
|
-
view,
|
|
148
|
-
mode: "source",
|
|
149
|
-
readonly: false,
|
|
150
|
-
sourcePath: "notes/dividers.md",
|
|
151
|
-
getValue: () => view.state.doc.toString(),
|
|
152
|
-
setValue: () => undefined,
|
|
153
|
-
focus: () => view.focus(),
|
|
154
|
-
insertMarkdown: () => undefined,
|
|
155
|
-
});
|
|
156
|
-
expect(view.state.doc.toString()).toBe("Alpha\n\n<!-- mira-divider:v1:00000009 -->\n---\n");
|
|
157
|
-
expect(view.scrollDOM.scrollTop).toBe(scrollTop);
|
|
158
|
-
expect(undo(view)).toBe(true);
|
|
159
|
-
expect(view.state.doc.toString()).toBe("Alpha\n\n---\n");
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
describe("doodle divider rendering", () => {
|
|
163
|
-
it("enhances only a rule immediately preceded by a valid seed comment", () => {
|
|
164
|
-
const extension = doodleDividersExtension();
|
|
165
|
-
const processor = extension.postProcessors?.[0];
|
|
166
|
-
if (!processor)
|
|
167
|
-
throw new Error("Doodle postprocessor was not contributed");
|
|
168
|
-
const parent = document.createElement("div");
|
|
169
|
-
const hr = document.createElement("hr");
|
|
170
|
-
parent.append(hr);
|
|
171
|
-
const comment = {
|
|
172
|
-
type: "comment",
|
|
173
|
-
value: " mira-divider:v1:4f32a91c ",
|
|
174
|
-
};
|
|
175
|
-
const node = { type: "element", tagName: "hr" };
|
|
176
|
-
const cleanup = processor(parent.firstElementChild, node, {
|
|
177
|
-
type: "root",
|
|
178
|
-
children: [comment, node],
|
|
179
|
-
});
|
|
180
|
-
const svg = parent.querySelector(".mira-doodle-divider");
|
|
181
|
-
expect(hr.classList.contains("mira-doodle-divider__native")).toBe(true);
|
|
182
|
-
expect(hr.getAttribute("role")).toBeNull();
|
|
183
|
-
expect(svg?.getAttribute("aria-hidden")).toBe("true");
|
|
184
|
-
expect(svg?.dataset.seed).toBe("4f32a91c");
|
|
185
|
-
expect(svg?.querySelectorAll("path").length).toBeGreaterThan(0);
|
|
186
|
-
expect(parent.querySelector(".mira-doodle-divider__controls")).toBeNull();
|
|
187
|
-
expect(cleanup).toBeTypeOf("function");
|
|
188
|
-
cleanup?.();
|
|
189
|
-
expect(parent.querySelector(".mira-doodle-divider")).toBeNull();
|
|
190
|
-
expect(hr.classList.contains("mira-doodle-divider__native")).toBe(false);
|
|
191
|
-
});
|
|
192
|
-
it("falls back to the native rule for bare rules and invalid drawings", () => {
|
|
193
|
-
const bare = doodleDividersExtension().postProcessors?.[0];
|
|
194
|
-
const invalid = doodleDividersExtension({
|
|
195
|
-
variants: [{ id: "invalid", draw: () => "M NaN Infinity" }],
|
|
196
|
-
}).postProcessors?.[0];
|
|
197
|
-
if (!bare || !invalid) {
|
|
198
|
-
throw new Error("Doodle postprocessors were not contributed");
|
|
199
|
-
}
|
|
200
|
-
for (const [processor, previous] of [
|
|
201
|
-
[bare, { type: "text", value: "ordinary" }],
|
|
202
|
-
[invalid, { type: "comment", value: " mira-divider:v1:00000001 " }],
|
|
203
|
-
]) {
|
|
204
|
-
const parent = document.createElement("div");
|
|
205
|
-
const hr = document.createElement("hr");
|
|
206
|
-
parent.append(hr);
|
|
207
|
-
const node = { type: "element", tagName: "hr" };
|
|
208
|
-
processor(hr, node, { children: [previous, node] });
|
|
209
|
-
expect(hr.className).toBe("");
|
|
210
|
-
expect(parent.children).toHaveLength(1);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
it("keeps path generation stable and independent from palette changes", () => {
|
|
214
|
-
const first = resolveMiraDoodleDividerDrawing(0x4f32a91c);
|
|
215
|
-
const second = resolveMiraDoodleDividerDrawing(0x4f32a91c, {
|
|
216
|
-
palette: ["red", "blue"],
|
|
217
|
-
});
|
|
218
|
-
expect(second?.variant.id).toBe(first?.variant.id);
|
|
219
|
-
expect(second?.paths).toEqual(first?.paths);
|
|
220
|
-
expect(resolveMiraDoodleDividerDrawing(1, { variants: [] })).toBeNull();
|
|
221
|
-
expect(resolveMiraDoodleDividerDrawing(1, { palette: [] })).toBeNull();
|
|
222
|
-
});
|
|
223
|
-
it("freezes a deterministic golden path for every v1 variant", () => {
|
|
224
|
-
const paths = defaultMiraDoodleDividerVariants.map((entry) => ({
|
|
225
|
-
id: entry.id,
|
|
226
|
-
path: entry.draw({
|
|
227
|
-
seed: 0x12345678,
|
|
228
|
-
width: 1000,
|
|
229
|
-
height: 32,
|
|
230
|
-
random: createMiraDoodleDividerRandom(0x12345678),
|
|
231
|
-
}),
|
|
232
|
-
}));
|
|
233
|
-
expect(paths.map(({ id }) => id)).toEqual([
|
|
234
|
-
"scribble",
|
|
235
|
-
"waves",
|
|
236
|
-
"loop",
|
|
237
|
-
"zigzag",
|
|
238
|
-
"kink",
|
|
239
|
-
"swoop",
|
|
240
|
-
"notch",
|
|
241
|
-
"plain",
|
|
242
|
-
]);
|
|
243
|
-
expect(paths.every(({ path }) => !String(path).match(/NaN|Infinity/u))).toBe(true);
|
|
244
|
-
expect(paths.map(({ path }) => stableHash(String(path)))).toEqual([
|
|
245
|
-
1800713089, 582965354, 1904425823, 2348662072, 1740792941, 1779022722,
|
|
246
|
-
395463394, 1422833353,
|
|
247
|
-
]);
|
|
248
|
-
});
|
|
249
|
-
it("keeps the long divider tails close to horizontal", () => {
|
|
250
|
-
const paths = defaultMiraDoodleDividerVariants.slice(0, -1).map((entry) => String(entry.draw({
|
|
251
|
-
seed: 0x12345678,
|
|
252
|
-
width: 1000,
|
|
253
|
-
height: 32,
|
|
254
|
-
random: createMiraDoodleDividerRandom(0x12345678),
|
|
255
|
-
})));
|
|
256
|
-
for (const path of paths) {
|
|
257
|
-
const tail = path.match(/C [\d.]+ ([\d.]+) 748 ([\d.]+) 994 ([\d.]+)$/u);
|
|
258
|
-
expect(tail, path).not.toBeNull();
|
|
259
|
-
const controlY = Number(tail?.[1]);
|
|
260
|
-
const secondControlY = Number(tail?.[2]);
|
|
261
|
-
const endY = Number(tail?.[3]);
|
|
262
|
-
expect(Math.abs(controlY - secondControlY)).toBeLessThan(0.01);
|
|
263
|
-
expect(Math.abs(controlY - endY)).toBeLessThan(1.5);
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
function createEditor(markdown, createSeed) {
|
|
268
|
-
const extension = doodleDividersExtension({ createSeed });
|
|
269
|
-
const parent = document.createElement("div");
|
|
270
|
-
document.body.append(parent);
|
|
271
|
-
const view = new EditorView({
|
|
272
|
-
state: EditorState.create({
|
|
273
|
-
doc: markdown,
|
|
274
|
-
extensions: [
|
|
275
|
-
history(),
|
|
276
|
-
createMiraMarkdownLanguage(),
|
|
277
|
-
extension.codeMirror?.({
|
|
278
|
-
mode: "source",
|
|
279
|
-
readonly: false,
|
|
280
|
-
sourcePath: "notes/dividers.md",
|
|
281
|
-
}) ?? [],
|
|
282
|
-
],
|
|
283
|
-
}),
|
|
284
|
-
parent,
|
|
285
|
-
});
|
|
286
|
-
editors.push(view);
|
|
287
|
-
return view;
|
|
288
|
-
}
|
|
289
|
-
function stableHash(value) {
|
|
290
|
-
let hash = 2_166_136_261;
|
|
291
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
292
|
-
hash ^= value.charCodeAt(index);
|
|
293
|
-
hash = Math.imul(hash, 16_777_619);
|
|
294
|
-
}
|
|
295
|
-
return hash >>> 0;
|
|
296
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/extensions/index.test.ts"],"names":[],"mappings":""}
|