@lapismd/mira-editor 0.0.1

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 (46) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE.md +4 -0
  3. package/README.md +70 -0
  4. package/dist/assets/logo.png +0 -0
  5. package/dist/assets.d.ts +4 -0
  6. package/dist/features.d.ts +164 -0
  7. package/dist/features.d.ts.map +1 -0
  8. package/dist/features.js +348 -0
  9. package/dist/index.d.ts +12 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +7 -0
  12. package/dist/index.test.d.ts +2 -0
  13. package/dist/index.test.d.ts.map +1 -0
  14. package/dist/index.test.js +179 -0
  15. package/dist/mira-editor-about-dialog.svelte +28 -0
  16. package/dist/mira-editor-about-dialog.svelte.d.ts +7 -0
  17. package/dist/mira-editor-about-dialog.svelte.d.ts.map +1 -0
  18. package/dist/mira-editor-toolbar.svelte +805 -0
  19. package/dist/mira-editor-toolbar.svelte.d.ts +6 -0
  20. package/dist/mira-editor-toolbar.svelte.d.ts.map +1 -0
  21. package/dist/mira-editor-toolbar.test.d.ts +2 -0
  22. package/dist/mira-editor-toolbar.test.d.ts.map +1 -0
  23. package/dist/mira-editor-toolbar.test.js +199 -0
  24. package/dist/mira-editor.svelte +492 -0
  25. package/dist/mira-editor.svelte.d.ts +24 -0
  26. package/dist/mira-editor.svelte.d.ts.map +1 -0
  27. package/dist/mira-editor.test.d.ts +2 -0
  28. package/dist/mira-editor.test.d.ts.map +1 -0
  29. package/dist/mira-editor.test.js +208 -0
  30. package/dist/styles.css +165 -0
  31. package/dist/test-setup.d.ts +8 -0
  32. package/dist/test-setup.d.ts.map +1 -0
  33. package/dist/test-setup.js +31 -0
  34. package/dist/toolbar-model.d.ts +32 -0
  35. package/dist/toolbar-model.d.ts.map +1 -0
  36. package/dist/toolbar-model.js +148 -0
  37. package/dist/toolbar-model.test.d.ts +2 -0
  38. package/dist/toolbar-model.test.d.ts.map +1 -0
  39. package/dist/toolbar-model.test.js +46 -0
  40. package/dist/types.d.ts +88 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +1 -0
  43. package/dist/version.d.ts +3 -0
  44. package/dist/version.d.ts.map +1 -0
  45. package/dist/version.js +2 -0
  46. package/package.json +56 -0
@@ -0,0 +1,24 @@
1
+ import { type MiraHandle } from "@lapismd/mira";
2
+ import type { MiraEditorSelection, MiraMarkdownActionId } from "@lapismd/mira/core";
3
+ import { type MiraMode, type MiraTemplateSelection } from "@lapismd/mira/extensions";
4
+ import { type Component } from "svelte";
5
+ import type { MiraEditorProps } from "./types";
6
+ declare const MiraEditor: Component<MiraEditorProps, {
7
+ focus: () => void;
8
+ executeCommand: (commandId: string) => boolean;
9
+ getCommands: () => ReturnType<MiraHandle["getCommands"]>;
10
+ isCommandEnabled: (commandId: string) => boolean;
11
+ getMarkdown: () => string;
12
+ setMarkdown: (markdown: string) => void;
13
+ getMode: () => MiraMode;
14
+ setMode: (nextMode: MiraMode) => void;
15
+ setReadonly: (nextReadonly: boolean) => void;
16
+ getSelection: () => MiraEditorSelection | null;
17
+ setSelection: (selection: MiraEditorSelection) => void;
18
+ insertMarkdown: (markdown: string, selection?: MiraTemplateSelection) => void;
19
+ applyMarkdownAction: (action: MiraMarkdownActionId) => boolean;
20
+ insertImage: () => void;
21
+ }, "mode" | "value">;
22
+ type MiraEditor = ReturnType<typeof MiraEditor>;
23
+ export default MiraEditor;
24
+ //# sourceMappingURL=mira-editor.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mira-editor.svelte.d.ts","sourceRoot":"","sources":["../src/mira-editor.svelte.ts"],"names":[],"mappings":"AAcA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EACR,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAC9B,OAAO,EAKH,KAAK,QAAQ,EACb,KAAK,qBAAqB,EAE3B,MAAM,0BAA0B,CAAC;AACpC,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAsBxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA6Z/C,QAAA,MAAM,UAAU;iBA5JK,IAAI;gCAIa,MAAM,KAAG,OAAO;uBAI3B,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;kCAIxB,MAAM,KAAG,OAAO;uBAI7B,MAAM;4BAIC,MAAM,KAAG,IAAI;mBAKxB,QAAQ;wBAID,QAAQ,KAAG,IAAI;gCAQP,OAAO,KAAG,IAAI;wBAKxB,mBAAmB,GAAG,IAAI;8BAIlB,mBAAmB,KAAG,IAAI;+BAKhD,MAAM,cACJ,qBAAqB,KAChC,IAAI;kCAI+B,oBAAoB,KAAG,OAAO;uBAO3C,IAAI;oBA4FyB,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mira-editor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mira-editor.test.d.ts","sourceRoot":"","sources":["../src/mira-editor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,208 @@
1
+ import { mount, tick, unmount } from "svelte";
2
+ import BoldIcon from "@lucide/svelte/icons/bold";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import MiraEditor from "./mira-editor.svelte";
5
+ describe("MiraEditor extension contributions", () => {
6
+ it("normalizes framework block-menu actions with active block context", async () => {
7
+ const target = document.createElement("div");
8
+ document.body.append(target);
9
+ const run = vi.fn();
10
+ const component = mount(MiraEditor, {
11
+ target,
12
+ props: {
13
+ featureConfigs: {
14
+ "block-controls": { toolbar: true },
15
+ },
16
+ mode: "source",
17
+ theme: "obsidian company-brand",
18
+ colorMode: "dark",
19
+ toolbarActions: [
20
+ {
21
+ id: "inspect-block",
22
+ label: "Inspect block",
23
+ icon: BoldIcon,
24
+ placements: ["block-menu"],
25
+ shortcut: "⌘I",
26
+ run,
27
+ },
28
+ ],
29
+ value: "Paragraph",
30
+ },
31
+ });
32
+ await tick();
33
+ await new Promise((resolve) => requestAnimationFrame(resolve));
34
+ expect(target.querySelector('[aria-label="Inspect block"]')).toBeNull();
35
+ target
36
+ .querySelector(".mira-block-toolbar-trigger")
37
+ ?.click();
38
+ await new Promise((resolve) => requestAnimationFrame(resolve));
39
+ const action = document.body.querySelector('[data-block-toolbar-item="mira-editor-inspect-block"]');
40
+ expect(action?.textContent).toContain("Inspect block");
41
+ expect(action?.textContent).toContain("⌘I");
42
+ expect(action?.querySelector("svg")).not.toBeNull();
43
+ const portal = action?.closest(".mira-block-toolbar-portal");
44
+ expect(portal?.getAttribute("data-mira-theme")).toBe("obsidian company-brand");
45
+ expect(portal?.getAttribute("data-mira-color-mode")).toBe("dark");
46
+ action?.click();
47
+ expect(run).toHaveBeenCalledWith(expect.objectContaining({
48
+ block: expect.objectContaining({ text: "Paragraph" }),
49
+ handle: expect.objectContaining({ role: "block" }),
50
+ replaceRange: expect.any(Function),
51
+ }));
52
+ await unmount(component);
53
+ target.remove();
54
+ });
55
+ it("exposes context-aware Markdown actions on the public handle", async () => {
56
+ const target = document.createElement("div");
57
+ document.body.append(target);
58
+ const component = mount(MiraEditor, {
59
+ target,
60
+ props: { value: "word", mode: "source" },
61
+ });
62
+ await tick();
63
+ component.setSelection({
64
+ anchor: { line: 0, ch: 2 },
65
+ head: { line: 0, ch: 2 },
66
+ });
67
+ expect(component.applyMarkdownAction("bold")).toBe(true);
68
+ expect(component.getMarkdown()).toBe("**word**");
69
+ component.setMode("preview");
70
+ expect(component.applyMarkdownAction("italic")).toBe(false);
71
+ expect(component.getMarkdown()).toBe("**word**");
72
+ await unmount(component);
73
+ target.remove();
74
+ });
75
+ it("applies targeted appearance to the shell and portaled menus", async () => {
76
+ const target = document.createElement("div");
77
+ document.body.append(target);
78
+ const component = mount(MiraEditor, {
79
+ target,
80
+ props: {
81
+ value: "# Targeted appearance",
82
+ theme: "obsidian company-brand",
83
+ colorMode: "dark",
84
+ },
85
+ });
86
+ await tick();
87
+ const shell = target.querySelector(".mira-editor");
88
+ expect(shell?.dataset.miraTheme).toBe("obsidian company-brand");
89
+ expect(shell?.dataset.miraColorMode).toBe("dark");
90
+ target
91
+ .querySelector('[aria-label="View options"]')
92
+ ?.click();
93
+ await tick();
94
+ const menu = document.body.querySelector('[data-slot="dropdown-menu-content"]');
95
+ expect(menu?.dataset.miraTheme).toBe("obsidian company-brand");
96
+ expect(menu?.dataset.miraColorMode).toBe("dark");
97
+ expect(menu?.hasAttribute("data-mira-overlay")).toBe(true);
98
+ await unmount(component);
99
+ target.remove();
100
+ });
101
+ it("runs extension commands from the toolbar and mounts extension styles", async () => {
102
+ const target = document.createElement("div");
103
+ document.body.append(target);
104
+ const run = vi.fn((context) => {
105
+ context.insertMarkdown("Extension command", 9);
106
+ });
107
+ const component = mount(MiraEditor, {
108
+ target,
109
+ props: {
110
+ extensions: [
111
+ {
112
+ name: "command-example",
113
+ commands: [
114
+ {
115
+ id: "insert-extension-command",
116
+ label: "Insert extension command",
117
+ run,
118
+ },
119
+ ],
120
+ toolbarItems: [
121
+ {
122
+ id: "insert-extension-command",
123
+ label: "Insert extension command",
124
+ command: "insert-extension-command",
125
+ icon: "sparkles",
126
+ },
127
+ ],
128
+ styles: [
129
+ {
130
+ id: "command-example-style",
131
+ cssText: ".command-example { color: rebeccapurple; }",
132
+ },
133
+ ],
134
+ },
135
+ ],
136
+ value: "",
137
+ },
138
+ });
139
+ await tick();
140
+ expect(component.getCommands().map((command) => command.id)).toContain("insert-extension-command");
141
+ expect(component.isCommandEnabled("insert-extension-command")).toBe(true);
142
+ expect(document.head.querySelector('[data-mira-extension-style="id:command-example-style"]')).toBeTruthy();
143
+ document.body
144
+ .querySelector('[aria-label="Insert extension command"]')
145
+ ?.click();
146
+ await tick();
147
+ expect(run).toHaveBeenCalledTimes(1);
148
+ expect(component.getMarkdown()).toBe("Extension command");
149
+ expect(component.getSelection()).toEqual({
150
+ anchor: { line: 0, ch: 9 },
151
+ head: { line: 0, ch: 9 },
152
+ });
153
+ unmount(component);
154
+ target.remove();
155
+ expect(document.head.querySelector('[data-mira-extension-style="id:command-example-style"]')).toBeNull();
156
+ });
157
+ it("places portable extension toolbar commands in the block menu explicitly", async () => {
158
+ const target = document.createElement("div");
159
+ document.body.append(target);
160
+ const run = vi.fn();
161
+ const component = mount(MiraEditor, {
162
+ target,
163
+ props: {
164
+ extensions: [
165
+ {
166
+ name: "block-command-example",
167
+ commands: [
168
+ {
169
+ id: "inspect-extension-block",
170
+ label: "Inspect extension block",
171
+ run,
172
+ },
173
+ ],
174
+ toolbarItems: [
175
+ {
176
+ id: "inspect-extension-block",
177
+ label: "Inspect extension block",
178
+ command: "inspect-extension-block",
179
+ icon: "sparkles",
180
+ placements: ["block-menu"],
181
+ shortcut: "⌘E",
182
+ },
183
+ ],
184
+ },
185
+ ],
186
+ featureConfigs: { "block-controls": { toolbar: true } },
187
+ mode: "source",
188
+ value: "Portable block",
189
+ },
190
+ });
191
+ await tick();
192
+ await new Promise((resolve) => requestAnimationFrame(resolve));
193
+ expect(target.querySelector('[aria-label="Inspect extension block"]')).toBeNull();
194
+ target
195
+ .querySelector(".mira-block-toolbar-trigger")
196
+ ?.click();
197
+ await new Promise((resolve) => requestAnimationFrame(resolve));
198
+ document.body
199
+ .querySelector('[data-block-toolbar-item="toolbar-inspect-extension-block"]')
200
+ ?.click();
201
+ expect(run).toHaveBeenCalledWith(expect.objectContaining({
202
+ block: expect.objectContaining({ text: "Portable block" }),
203
+ handle: expect.objectContaining({ role: "block" }),
204
+ }));
205
+ await unmount(component);
206
+ target.remove();
207
+ });
208
+ });
@@ -0,0 +1,165 @@
1
+ @import "@lapismd/mira/styles.css";
2
+
3
+ .mira-editor {
4
+ background: var(--mira-background);
5
+ border: 1px solid var(--mira-border);
6
+ border-radius: var(--mira-radius);
7
+ box-sizing: border-box;
8
+ color: var(--mira-foreground);
9
+ display: flex;
10
+ flex-direction: column;
11
+ font-family: var(--mira-font-sans);
12
+ height: 100%;
13
+ min-height: 360px;
14
+ overflow: hidden;
15
+ }
16
+
17
+ .mira-editor__toolbar {
18
+ align-items: center;
19
+ background: var(--mira-background);
20
+ border-bottom: 1px solid var(--mira-border);
21
+ flex-wrap: nowrap;
22
+ gap: 0.25rem;
23
+ min-height: 36px;
24
+ overflow-x: auto;
25
+ overflow-y: hidden;
26
+ overscroll-behavior-x: contain;
27
+ padding: 0.2rem 0.35rem;
28
+ scrollbar-width: none;
29
+ touch-action: pan-x;
30
+ -webkit-overflow-scrolling: touch;
31
+ }
32
+
33
+ .mira-editor__toolbar::-webkit-scrollbar {
34
+ display: none;
35
+ height: 0;
36
+ width: 0;
37
+ }
38
+
39
+ .mira-editor__separator {
40
+ align-self: center;
41
+ flex: 0 0 auto;
42
+ height: 1.25rem;
43
+ min-height: 1.25rem;
44
+ }
45
+
46
+ .mira-editor__toolbar-section {
47
+ align-items: center;
48
+ flex: 0 0 auto;
49
+ display: flex;
50
+ gap: 0.15rem;
51
+ }
52
+
53
+ .mira-editor-toolbar__event-root {
54
+ display: contents;
55
+ }
56
+
57
+ .mira-editor-toolbar__spacer {
58
+ flex: 1 1 auto;
59
+ min-width: 0.5rem;
60
+ }
61
+
62
+ .mira-editor__icon {
63
+ height: 0.95rem;
64
+ width: 0.95rem;
65
+ }
66
+
67
+ .mira-editor-toolbar__menu-icon {
68
+ flex: 0 0 1rem;
69
+ height: 1rem;
70
+ width: 1rem;
71
+ }
72
+
73
+ .mira-editor-toolbar__menu-icon-placeholder {
74
+ flex: 0 0 1rem;
75
+ height: 1rem;
76
+ width: 1rem;
77
+ }
78
+
79
+ .mira-editor-toolbar__menu-check {
80
+ height: 0.9rem;
81
+ margin-left: auto;
82
+ width: 0.9rem;
83
+ }
84
+
85
+ @media (hover: none) and (pointer: coarse) {
86
+ .mira-editor__toolbar {
87
+ min-height: 52px;
88
+ }
89
+
90
+ .mira-editor__toolbar
91
+ :is(.mira-toolbar__button, .mira-toolbar__group-item, .mira-toolbar__link) {
92
+ height: 2.75rem;
93
+ width: 2.75rem;
94
+ }
95
+ }
96
+
97
+ .mira-editor-about-dialog {
98
+ width: min(32rem, calc(100% - 2rem));
99
+ }
100
+
101
+ .mira-editor-about-dialog__header {
102
+ text-align: center;
103
+ }
104
+
105
+ .mira-editor-about-dialog__logo-viewport {
106
+ aspect-ratio: 480 / 337;
107
+ display: block;
108
+ margin: 0 auto;
109
+ max-width: 15rem;
110
+ overflow: hidden;
111
+ width: 100%;
112
+ }
113
+
114
+ .mira-editor-about-dialog__logo-image {
115
+ display: block;
116
+ height: auto;
117
+ max-width: none;
118
+ transform: translateX(0);
119
+ width: 200%;
120
+ }
121
+
122
+ @media (prefers-color-scheme: dark) {
123
+ .mira-editor-about-dialog__logo-image {
124
+ transform: translateX(-50%);
125
+ }
126
+ }
127
+
128
+ :where(.dark, .theme-dark)
129
+ .mira-editor-about-dialog:not([data-mira-color-mode])
130
+ .mira-editor-about-dialog__logo-image,
131
+ .mira-editor-about-dialog:where(.dark, .theme-dark)
132
+ .mira-editor-about-dialog__logo-image,
133
+ .mira-editor-about-dialog[data-mira-color-mode="dark"]
134
+ .mira-editor-about-dialog__logo-image {
135
+ transform: translateX(-50%);
136
+ }
137
+
138
+ :where(.light, .theme-light)
139
+ .mira-editor-about-dialog:not([data-mira-color-mode])
140
+ .mira-editor-about-dialog__logo-image,
141
+ .mira-editor-about-dialog:where(.light, .theme-light)
142
+ .mira-editor-about-dialog__logo-image,
143
+ .mira-editor-about-dialog[data-mira-color-mode="light"]
144
+ .mira-editor-about-dialog__logo-image {
145
+ transform: translateX(0);
146
+ }
147
+
148
+ .mira-editor-about-dialog__version {
149
+ color: var(--mira-muted-foreground);
150
+ font-size: 0.875rem;
151
+ margin: 0;
152
+ text-align: center;
153
+ }
154
+
155
+ .mira-editor__editor {
156
+ flex: 1;
157
+ min-height: 0;
158
+ }
159
+
160
+ .mira-editor__editor > .mira {
161
+ border: 0;
162
+ border-radius: 0;
163
+ height: 100%;
164
+ min-height: 0;
165
+ }
@@ -0,0 +1,8 @@
1
+ declare class TestResizeObserver {
2
+ observe(): void;
3
+ unobserve(): void;
4
+ disconnect(): void;
5
+ }
6
+ declare function emptyDomRect(): DOMRect;
7
+ declare function emptyDomRectList(): DOMRectList;
8
+ //# sourceMappingURL=test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAAA,cAAM,kBAAkB;IACtB,OAAO,IAAI,IAAI;IACf,SAAS,IAAI,IAAI;IACjB,UAAU,IAAI,IAAI;CACnB;AAID,iBAAS,YAAY,IAAI,OAAO,CAY/B;AAED,iBAAS,gBAAgB,IAAI,WAAW,CAMvC"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ class TestResizeObserver {
3
+ observe() { }
4
+ unobserve() { }
5
+ disconnect() { }
6
+ }
7
+ globalThis.ResizeObserver ??= TestResizeObserver;
8
+ function emptyDomRect() {
9
+ return {
10
+ bottom: 0,
11
+ height: 0,
12
+ left: 0,
13
+ right: 0,
14
+ top: 0,
15
+ width: 0,
16
+ x: 0,
17
+ y: 0,
18
+ toJSON: () => ({}),
19
+ };
20
+ }
21
+ function emptyDomRectList() {
22
+ return {
23
+ length: 0,
24
+ item: () => null,
25
+ [Symbol.iterator]: function* iterator() { },
26
+ };
27
+ }
28
+ if (typeof Range !== "undefined") {
29
+ Range.prototype.getClientRects ??= emptyDomRectList;
30
+ Range.prototype.getBoundingClientRect ??= emptyDomRect;
31
+ }
@@ -0,0 +1,32 @@
1
+ import type { MiraMarkdownActionId } from "@lapismd/mira/core";
2
+ import type { MiraMode } from "@lapismd/mira/extensions";
3
+ import type { MiraEditorEditMode, MiraEditorToolbarItem } from "./features";
4
+ export type MiraViewModeMenuItem = {
5
+ mode: MiraMode;
6
+ label: string;
7
+ checked: boolean;
8
+ };
9
+ export declare const miraEditorModeLabels: Record<MiraMode, string>;
10
+ export declare const miraEditorToolbarItemLabels: Record<MiraEditorToolbarItem, string>;
11
+ export declare function isMiraEditMode(mode: MiraMode): mode is "live-preview" | "source";
12
+ export declare function resolveMiraViewModeMenuItems({ mode, modeOptions, resolvedDefaultEditMode, }: {
13
+ mode: MiraMode;
14
+ modeOptions: MiraMode[];
15
+ resolvedDefaultEditMode: MiraEditorEditMode;
16
+ }): MiraViewModeMenuItem[];
17
+ export declare function resolveMiraAlternateEditMode({ mode, modeOptions, resolvedDefaultEditMode, }: {
18
+ mode: MiraMode;
19
+ modeOptions: MiraMode[];
20
+ resolvedDefaultEditMode: MiraEditorEditMode;
21
+ }): MiraMode | null;
22
+ export declare function resolveMiraModeAfterSplit({ lastNonSplitMode, modeOptions, resolvedDefaultEditMode, }: {
23
+ lastNonSplitMode: MiraMode | null | undefined;
24
+ modeOptions: MiraMode[];
25
+ resolvedDefaultEditMode: MiraEditorEditMode;
26
+ }): MiraMode;
27
+ export declare function resolveMiraViewToggleMode(mode: MiraMode, resolvedDefaultEditMode: MiraEditorEditMode): MiraMode;
28
+ export declare function miraViewToggleLabel(mode: MiraMode): string;
29
+ export declare const miraViewOptionsLabel = "View options";
30
+ export declare function templateForMiraToolbarItem(item: MiraEditorToolbarItem): string;
31
+ export declare function markdownActionForMiraToolbarItem(item: MiraEditorToolbarItem): MiraMarkdownActionId | null;
32
+ //# sourceMappingURL=toolbar-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-model.d.ts","sourceRoot":"","sources":["../src/toolbar-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE5E,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAKzD,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAC9C,qBAAqB,EACrB,MAAM,CAkBP,CAAC;AAEF,wBAAgB,cAAc,CAC5B,IAAI,EAAE,QAAQ,GACb,IAAI,IAAI,cAAc,GAAG,QAAQ,CAEnC;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,IAAI,EACJ,WAAW,EACX,uBAAuB,GACxB,EAAE;IACD,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,QAAQ,EAAE,CAAC;IACxB,uBAAuB,EAAE,kBAAkB,CAAC;CAC7C,GAAG,oBAAoB,EAAE,CA6BzB;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,IAAI,EACJ,WAAW,EACX,uBAAuB,GACxB,EAAE;IACD,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,QAAQ,EAAE,CAAC;IACxB,uBAAuB,EAAE,kBAAkB,CAAC;CAC7C,GAAG,QAAQ,GAAG,IAAI,CAkBlB;AAED,wBAAgB,yBAAyB,CAAC,EACxC,gBAAgB,EAChB,WAAW,EACX,uBAAuB,GACxB,EAAE;IACD,gBAAgB,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,WAAW,EAAE,QAAQ,EAAE,CAAC;IACxB,uBAAuB,EAAE,kBAAkB,CAAC;CAC7C,GAAG,QAAQ,CAiBX;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,QAAQ,EACd,uBAAuB,EAAE,kBAAkB,GAC1C,QAAQ,CAEV;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAE1D;AAED,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AAEnD,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,qBAAqB,GAC1B,MAAM,CAmCR;AAED,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,qBAAqB,GAC1B,oBAAoB,GAAG,IAAI,CAgB7B"}
@@ -0,0 +1,148 @@
1
+ export const miraEditorModeLabels = {
2
+ source: "Source mode",
3
+ "live-preview": "Live edit",
4
+ preview: "Reading view",
5
+ split: "Split",
6
+ };
7
+ export const miraEditorToolbarItemLabels = {
8
+ heading: "Heading",
9
+ bold: "Bold",
10
+ italic: "Italic",
11
+ strikethrough: "Strikethrough",
12
+ inlineCode: "Inline code",
13
+ quote: "Blockquote",
14
+ bulletList: "Bullet list",
15
+ numberedList: "Numbered list",
16
+ taskList: "Task list",
17
+ link: "Link",
18
+ image: "Image",
19
+ table: "Table",
20
+ gridTable: "Grid table",
21
+ code: "Code block",
22
+ math: "Math",
23
+ mermaid: "Mermaid diagram",
24
+ };
25
+ export function isMiraEditMode(mode) {
26
+ return mode === "live-preview" || mode === "source";
27
+ }
28
+ export function resolveMiraViewModeMenuItems({ mode, modeOptions, resolvedDefaultEditMode, }) {
29
+ const itemModes = [];
30
+ if (mode === "preview") {
31
+ if (modeOptions.includes("live-preview")) {
32
+ itemModes.push("live-preview");
33
+ }
34
+ if (modeOptions.includes("source")) {
35
+ itemModes.push("source");
36
+ }
37
+ }
38
+ else {
39
+ const alternateEditMode = resolveMiraAlternateEditMode({
40
+ mode,
41
+ modeOptions,
42
+ resolvedDefaultEditMode,
43
+ });
44
+ if (modeOptions.includes("preview")) {
45
+ itemModes.push("preview");
46
+ }
47
+ if (alternateEditMode) {
48
+ itemModes.push(alternateEditMode);
49
+ }
50
+ }
51
+ return itemModes.map((modeOption) => ({
52
+ checked: mode === modeOption,
53
+ label: miraEditorModeLabels[modeOption],
54
+ mode: modeOption,
55
+ }));
56
+ }
57
+ export function resolveMiraAlternateEditMode({ mode, modeOptions, resolvedDefaultEditMode, }) {
58
+ if (mode === "live-preview" && modeOptions.includes("source")) {
59
+ return "source";
60
+ }
61
+ if (mode === "source" && modeOptions.includes("live-preview")) {
62
+ return "live-preview";
63
+ }
64
+ if (mode === "split") {
65
+ const defaultAlternate = resolvedDefaultEditMode === "source" ? "live-preview" : "source";
66
+ if (modeOptions.includes(defaultAlternate)) {
67
+ return defaultAlternate;
68
+ }
69
+ if (modeOptions.includes(resolvedDefaultEditMode)) {
70
+ return resolvedDefaultEditMode;
71
+ }
72
+ }
73
+ return null;
74
+ }
75
+ export function resolveMiraModeAfterSplit({ lastNonSplitMode, modeOptions, resolvedDefaultEditMode, }) {
76
+ if (lastNonSplitMode &&
77
+ lastNonSplitMode !== "split" &&
78
+ modeOptions.includes(lastNonSplitMode)) {
79
+ return lastNonSplitMode;
80
+ }
81
+ if (modeOptions.includes(resolvedDefaultEditMode)) {
82
+ return resolvedDefaultEditMode;
83
+ }
84
+ if (modeOptions.includes("preview")) {
85
+ return "preview";
86
+ }
87
+ return (modeOptions.find((modeOption) => modeOption !== "split") ?? "live-preview");
88
+ }
89
+ export function resolveMiraViewToggleMode(mode, resolvedDefaultEditMode) {
90
+ return mode === "preview" ? resolvedDefaultEditMode : "preview";
91
+ }
92
+ export function miraViewToggleLabel(mode) {
93
+ return mode === "preview" ? "Edit" : "Reading view";
94
+ }
95
+ export const miraViewOptionsLabel = "View options";
96
+ export function templateForMiraToolbarItem(item) {
97
+ switch (item) {
98
+ case "heading":
99
+ return "# Heading";
100
+ case "bold":
101
+ return "**strong**";
102
+ case "italic":
103
+ return "_emphasis_";
104
+ case "strikethrough":
105
+ return "~~strikethrough~~";
106
+ case "inlineCode":
107
+ return "`code`";
108
+ case "quote":
109
+ return "> Quote";
110
+ case "bulletList":
111
+ return "- List item";
112
+ case "numberedList":
113
+ return "1. List item";
114
+ case "taskList":
115
+ return "- [ ] Task";
116
+ case "link":
117
+ return "[label](https://example.com)";
118
+ case "image":
119
+ return "![alt](https://example.com/image.png)";
120
+ case "table":
121
+ return "\n| Column | Value |\n| --- | --- |\n| Item | Detail |\n";
122
+ case "gridTable":
123
+ return "\n+--------+--------+\n| Column | Value |\n+========+========+\n| Item | Detail |\n+--------+--------+\n";
124
+ case "code":
125
+ return '\n```ts\nconsole.log("hello");\n```\n';
126
+ case "math":
127
+ return "$E = mc^2$";
128
+ case "mermaid":
129
+ return "\n```mermaid\nflowchart TD\n A[Start] --> B[Done]\n```\n";
130
+ }
131
+ }
132
+ export function markdownActionForMiraToolbarItem(item) {
133
+ switch (item) {
134
+ case "heading":
135
+ case "bold":
136
+ case "italic":
137
+ case "strikethrough":
138
+ case "inlineCode":
139
+ case "quote":
140
+ case "bulletList":
141
+ case "numberedList":
142
+ case "taskList":
143
+ case "link":
144
+ return item;
145
+ default:
146
+ return null;
147
+ }
148
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=toolbar-model.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-model.test.d.ts","sourceRoot":"","sources":["../src/toolbar-model.test.ts"],"names":[],"mappings":""}