@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.
- package/CHANGELOG.md +7 -0
- package/LICENSE.md +4 -0
- package/README.md +70 -0
- package/dist/assets/logo.png +0 -0
- package/dist/assets.d.ts +4 -0
- package/dist/features.d.ts +164 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/features.js +348 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +179 -0
- package/dist/mira-editor-about-dialog.svelte +28 -0
- package/dist/mira-editor-about-dialog.svelte.d.ts +7 -0
- package/dist/mira-editor-about-dialog.svelte.d.ts.map +1 -0
- package/dist/mira-editor-toolbar.svelte +805 -0
- package/dist/mira-editor-toolbar.svelte.d.ts +6 -0
- package/dist/mira-editor-toolbar.svelte.d.ts.map +1 -0
- package/dist/mira-editor-toolbar.test.d.ts +2 -0
- package/dist/mira-editor-toolbar.test.d.ts.map +1 -0
- package/dist/mira-editor-toolbar.test.js +199 -0
- package/dist/mira-editor.svelte +492 -0
- package/dist/mira-editor.svelte.d.ts +24 -0
- package/dist/mira-editor.svelte.d.ts.map +1 -0
- package/dist/mira-editor.test.d.ts +2 -0
- package/dist/mira-editor.test.d.ts.map +1 -0
- package/dist/mira-editor.test.js +208 -0
- package/dist/styles.css +165 -0
- package/dist/test-setup.d.ts +8 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +31 -0
- package/dist/toolbar-model.d.ts +32 -0
- package/dist/toolbar-model.d.ts.map +1 -0
- package/dist/toolbar-model.js +148 -0
- package/dist/toolbar-model.test.d.ts +2 -0
- package/dist/toolbar-model.test.d.ts.map +1 -0
- package/dist/toolbar-model.test.js +46 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- 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 @@
|
|
|
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
|
+
});
|
package/dist/styles.css
ADDED
|
@@ -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 @@
|
|
|
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 "";
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"toolbar-model.test.d.ts","sourceRoot":"","sources":["../src/toolbar-model.test.ts"],"names":[],"mappings":""}
|