@wonderwhy-er/desktop-commander 0.2.39 → 0.2.40
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/dist/server.js +1 -1
- package/dist/ui/file-preview/preview-runtime.js +204 -153
- package/dist/ui/file-preview/src/markdown/controller.d.ts +7 -1
- package/dist/ui/file-preview/src/markdown/controller.js +135 -16
- package/dist/ui/file-preview/src/markdown/editor.d.ts +97 -1
- package/dist/ui/file-preview/src/markdown/editor.js +814 -26
- package/dist/ui/file-preview/src/model.d.ts +2 -1
- package/dist/utils/capture.js +1 -1
- package/dist/utils/toolHistory.d.ts +13 -0
- package/dist/utils/toolHistory.js +65 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -1
- package/dist/ui/config-editor/app.js +0 -840
- package/dist/ui/config-editor/array-modal.d.ts +0 -19
- package/dist/ui/config-editor/array-modal.js +0 -185
- package/dist/ui/config-editor/main.d.ts +0 -1
- package/dist/ui/config-editor/main.js +0 -2
- package/dist/ui/config-editor/src/App.d.ts +0 -43
- package/dist/ui/config-editor/src/components/layout.d.ts +0 -4
- package/dist/ui/config-editor/src/components/layout.js +0 -83
- package/dist/ui/config-editor/src/components/toolbar.d.ts +0 -1
- package/dist/ui/config-editor/src/components/toolbar.js +0 -21
- package/dist/ui/config-editor/src/config-values.d.ts +0 -6
- package/dist/ui/config-editor/src/config-values.js +0 -61
- package/dist/ui/config-editor/src/contracts.d.ts +0 -14
- package/dist/ui/config-editor/src/contracts.js +0 -3
- package/dist/ui/config-editor/src/directory-browser.d.ts +0 -6
- package/dist/ui/config-editor/src/directory-browser.js +0 -71
- package/dist/ui/config-editor/src/layout.d.ts +0 -5
- package/dist/ui/config-editor/src/layout.js +0 -90
- package/dist/ui/config-editor/src/parsing.d.ts +0 -5
- package/dist/ui/config-editor/src/parsing.js +0 -50
- package/dist/ui/config-editor/src/toolbar.d.ts +0 -1
- package/dist/ui/config-editor/src/toolbar.js +0 -18
- package/dist/ui/config-editor/src/types.d.ts +0 -17
- package/dist/ui/config-editor/src/types.js +0 -3
- package/dist/ui/config-editor/src/utils/config-values.d.ts +0 -9
- package/dist/ui/config-editor/src/utils/config-values.js +0 -61
- package/dist/ui/config-editor/src/utils/directory-browser.d.ts +0 -31
- package/dist/ui/config-editor/src/utils/directory-browser.js +0 -201
- package/dist/ui/config-editor/src/utils/parsing.d.ts +0 -8
- package/dist/ui/config-editor/src/utils/parsing.js +0 -50
- package/dist/ui/file-preview/app.d.ts +0 -8
- package/dist/ui/file-preview/app.js +0 -2020
- package/dist/ui/file-preview/components/code-viewer.d.ts +0 -6
- package/dist/ui/file-preview/components/code-viewer.js +0 -73
- package/dist/ui/file-preview/components/highlighting.d.ts +0 -2
- package/dist/ui/file-preview/components/highlighting.js +0 -54
- package/dist/ui/file-preview/components/html-renderer.d.ts +0 -5
- package/dist/ui/file-preview/components/html-renderer.js +0 -47
- package/dist/ui/file-preview/components/markdown-renderer.d.ts +0 -1
- package/dist/ui/file-preview/components/markdown-renderer.js +0 -67
- package/dist/ui/file-preview/components/toolbar.d.ts +0 -6
- package/dist/ui/file-preview/components/toolbar.js +0 -75
- package/dist/ui/file-preview/image-preview.d.ts +0 -3
- package/dist/ui/file-preview/image-preview.js +0 -21
- package/dist/ui/file-preview/main.d.ts +0 -1
- package/dist/ui/file-preview/main.js +0 -5
- package/dist/ui/file-preview/markdown/editor.d.ts +0 -36
- package/dist/ui/file-preview/markdown/editor.js +0 -643
- package/dist/ui/file-preview/markdown/linking.d.ts +0 -9
- package/dist/ui/file-preview/markdown/linking.js +0 -210
- package/dist/ui/file-preview/markdown/outline.d.ts +0 -7
- package/dist/ui/file-preview/markdown/outline.js +0 -40
- package/dist/ui/file-preview/markdown/preview.d.ts +0 -8
- package/dist/ui/file-preview/markdown/preview.js +0 -33
- package/dist/ui/file-preview/markdown/slugify.d.ts +0 -3
- package/dist/ui/file-preview/markdown/slugify.js +0 -31
- package/dist/ui/file-preview/markdown/toc.d.ts +0 -11
- package/dist/ui/file-preview/markdown/toc.js +0 -75
- package/dist/ui/file-preview/markdown/utils.d.ts +0 -1
- package/dist/ui/file-preview/markdown/utils.js +0 -15
- package/dist/ui/file-preview/markdown/workspace-controller.d.ts +0 -25
- package/dist/ui/file-preview/markdown/workspace-controller.js +0 -40
- package/dist/ui/file-preview/src/components/CodeViewer.d.ts +0 -6
- package/dist/ui/file-preview/src/components/CodeViewer.js +0 -60
- package/dist/ui/file-preview/src/components/HtmlRenderer.d.ts +0 -8
- package/dist/ui/file-preview/src/components/HtmlRenderer.js +0 -45
- package/dist/ui/file-preview/src/components/MarkdownRenderer.d.ts +0 -1
- package/dist/ui/file-preview/src/components/MarkdownRenderer.js +0 -15
- package/dist/ui/file-preview/src/components/Toolbar.d.ts +0 -6
- package/dist/ui/file-preview/src/components/Toolbar.js +0 -75
- package/dist/ui/file-preview/src/components/editor-toolbar.d.ts +0 -15
- package/dist/ui/file-preview/src/components/editor-toolbar.js +0 -384
- package/dist/ui/file-preview/src/components/markdown-editor.d.ts +0 -29
- package/dist/ui/file-preview/src/components/markdown-editor.js +0 -535
- package/dist/ui/file-preview/src/markdown/block-merge.d.ts +0 -25
- package/dist/ui/file-preview/src/markdown/block-merge.js +0 -86
- package/dist/ui/file-preview/src/markdown/link-modal.d.ts +0 -13
- package/dist/ui/file-preview/src/markdown/link-modal.js +0 -213
- package/dist/ui/file-preview/src/markdown/raw-editor.d.ts +0 -8
- package/dist/ui/file-preview/src/markdown/raw-editor.js +0 -61
- package/dist/ui/file-preview/src/markdown/selection-toolbar.d.ts +0 -14
- package/dist/ui/file-preview/src/markdown/selection-toolbar.js +0 -128
- package/dist/ui/file-preview/src/markdown/toc.d.ts +0 -11
- package/dist/ui/file-preview/src/markdown/toc.js +0 -75
- package/dist/ui/file-preview/src/markdown-workspace/editor.d.ts +0 -36
- package/dist/ui/file-preview/src/markdown-workspace/editor.js +0 -643
- package/dist/ui/file-preview/src/markdown-workspace/linking.d.ts +0 -9
- package/dist/ui/file-preview/src/markdown-workspace/linking.js +0 -210
- package/dist/ui/file-preview/src/markdown-workspace/outline.d.ts +0 -7
- package/dist/ui/file-preview/src/markdown-workspace/outline.js +0 -40
- package/dist/ui/file-preview/src/markdown-workspace/preview.d.ts +0 -8
- package/dist/ui/file-preview/src/markdown-workspace/preview.js +0 -33
- package/dist/ui/file-preview/src/markdown-workspace/slugify.d.ts +0 -3
- package/dist/ui/file-preview/src/markdown-workspace/slugify.js +0 -31
- package/dist/ui/file-preview/src/markdown-workspace/toc.d.ts +0 -11
- package/dist/ui/file-preview/src/markdown-workspace/toc.js +0 -75
- package/dist/ui/file-preview/src/markdown-workspace/utils.d.ts +0 -1
- package/dist/ui/file-preview/src/markdown-workspace/utils.js +0 -15
- package/dist/ui/file-preview/src/markdown-workspace/workspace-controller.d.ts +0 -25
- package/dist/ui/file-preview/src/markdown-workspace/workspace-controller.js +0 -40
- package/dist/ui/file-preview/types.d.ts +0 -1
- package/dist/ui/file-preview/types.js +0 -1
- package/dist/ui/server-integration.d.ts +0 -13
- package/dist/ui/server-integration.js +0 -31
- package/dist/ui/shared/ToolHeader.d.ts +0 -9
- package/dist/ui/shared/ToolHeader.js +0 -29
- package/dist/ui/shared/app-bootstrap.d.ts +0 -9
- package/dist/ui/shared/app-bootstrap.js +0 -15
- package/dist/ui/shared/guards.d.ts +0 -1
- package/dist/ui/shared/guards.js +0 -3
- package/dist/ui/shared/host-lifecycle.d.ts +0 -17
- package/dist/ui/shared/host-lifecycle.js +0 -41
- package/dist/ui/shared/rpc-client.d.ts +0 -14
- package/dist/ui/shared/rpc-client.js +0 -72
- package/dist/ui/shared/theme-adaptation.d.ts +0 -10
- package/dist/ui/shared/theme-adaptation.js +0 -118
- package/dist/ui/shared/tool-header.d.ts +0 -9
- package/dist/ui/shared/tool-header.js +0 -25
- package/dist/utils/ui-call-context.d.ts +0 -8
- package/dist/utils/ui-call-context.js +0 -72
- /package/dist/ui/config-editor/{app.d.ts → src/app.d.ts} +0 -0
- /package/dist/ui/config-editor/src/{App.js → app.js} +0 -0
- /package/dist/ui/file-preview/src/{App.d.ts → app.d.ts} +0 -0
- /package/dist/ui/file-preview/src/{App.js → app.js} +0 -0
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
const SEARCH_DEBOUNCE_MS = 150;
|
|
2
|
-
export function renderLinkModalHtml() {
|
|
3
|
-
return `
|
|
4
|
-
<div id="markdown-link-modal" class="markdown-link-modal" hidden>
|
|
5
|
-
<div class="markdown-link-modal-card">
|
|
6
|
-
<div class="markdown-link-mode-tabs">
|
|
7
|
-
<button type="button" id="markdown-link-mode-file" class="markdown-link-mode-tab is-active">File</button>
|
|
8
|
-
<button type="button" id="markdown-link-mode-url" class="markdown-link-mode-tab">URL</button>
|
|
9
|
-
</div>
|
|
10
|
-
<div id="markdown-link-file-fields">
|
|
11
|
-
<label class="markdown-link-modal-label" for="markdown-link-search">Find note</label>
|
|
12
|
-
<input id="markdown-link-search" class="markdown-link-modal-input" type="text" placeholder="Search files..." />
|
|
13
|
-
<div id="markdown-link-results" class="markdown-link-results"></div>
|
|
14
|
-
<label class="markdown-link-modal-label" for="markdown-link-heading">Heading</label>
|
|
15
|
-
<select id="markdown-link-heading" class="markdown-link-modal-input markdown-link-modal-select">
|
|
16
|
-
<option value="">None</option>
|
|
17
|
-
</select>
|
|
18
|
-
<label class="markdown-link-modal-label" for="markdown-link-alias">Alias</label>
|
|
19
|
-
<input id="markdown-link-alias" class="markdown-link-modal-input" type="text" placeholder="Optional label" />
|
|
20
|
-
</div>
|
|
21
|
-
<div id="markdown-link-url-fields" hidden>
|
|
22
|
-
<label class="markdown-link-modal-label" for="markdown-link-input">URL</label>
|
|
23
|
-
<input id="markdown-link-input" class="markdown-link-modal-input" type="url" placeholder="https://example.com" />
|
|
24
|
-
<label class="markdown-link-modal-label" for="markdown-link-label">Label</label>
|
|
25
|
-
<input id="markdown-link-label" class="markdown-link-modal-input" type="text" placeholder="Optional label" />
|
|
26
|
-
</div>
|
|
27
|
-
<div class="markdown-link-modal-actions">
|
|
28
|
-
<button type="button" id="markdown-link-cancel" class="markdown-link-modal-button">Cancel</button>
|
|
29
|
-
<button type="button" id="markdown-link-apply" class="markdown-link-modal-button markdown-link-modal-button--primary">Insert</button>
|
|
30
|
-
</div>
|
|
31
|
-
</div>
|
|
32
|
-
</div>
|
|
33
|
-
`;
|
|
34
|
-
}
|
|
35
|
-
export function attachLinkModal(options) {
|
|
36
|
-
const modalEl = options.root.querySelector('#markdown-link-modal');
|
|
37
|
-
if (!modalEl) {
|
|
38
|
-
return { open: () => undefined, close: () => undefined, destroy: () => undefined };
|
|
39
|
-
}
|
|
40
|
-
const modal = modalEl;
|
|
41
|
-
const tabFile = modal.querySelector('#markdown-link-mode-file');
|
|
42
|
-
const tabUrl = modal.querySelector('#markdown-link-mode-url');
|
|
43
|
-
const fileFields = modal.querySelector('#markdown-link-file-fields');
|
|
44
|
-
const urlFields = modal.querySelector('#markdown-link-url-fields');
|
|
45
|
-
const searchInput = modal.querySelector('#markdown-link-search');
|
|
46
|
-
const resultsEl = modal.querySelector('#markdown-link-results');
|
|
47
|
-
const headingSelect = modal.querySelector('#markdown-link-heading');
|
|
48
|
-
const aliasInput = modal.querySelector('#markdown-link-alias');
|
|
49
|
-
const urlInput = modal.querySelector('#markdown-link-input');
|
|
50
|
-
const labelInput = modal.querySelector('#markdown-link-label');
|
|
51
|
-
const applyBtn = modal.querySelector('#markdown-link-apply');
|
|
52
|
-
const cancelBtn = modal.querySelector('#markdown-link-cancel');
|
|
53
|
-
let mode = 'file';
|
|
54
|
-
let selectedItem = null;
|
|
55
|
-
let searchRequestId = 0;
|
|
56
|
-
let headingRequestId = 0;
|
|
57
|
-
let searchDebounce = null;
|
|
58
|
-
function setMode(next) {
|
|
59
|
-
mode = next;
|
|
60
|
-
tabFile.classList.toggle('is-active', mode === 'file');
|
|
61
|
-
tabUrl.classList.toggle('is-active', mode === 'url');
|
|
62
|
-
fileFields.hidden = mode !== 'file';
|
|
63
|
-
urlFields.hidden = mode !== 'url';
|
|
64
|
-
}
|
|
65
|
-
function renderResults(items, message) {
|
|
66
|
-
if (items.length === 0) {
|
|
67
|
-
resultsEl.innerHTML = `<div class="markdown-link-result markdown-link-result--empty">${message ?? 'No results'}</div>`;
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
resultsEl.innerHTML = items.map((item, idx) => `
|
|
71
|
-
<button type="button" class="markdown-link-result${selectedItem?.path === item.path ? ' is-selected' : ''}" data-idx="${idx}">
|
|
72
|
-
<span class="markdown-link-result-title">${escapeHtml(item.title)}</span>
|
|
73
|
-
<span class="markdown-link-result-path">${escapeHtml(item.relativePath)}</span>
|
|
74
|
-
</button>
|
|
75
|
-
`).join('');
|
|
76
|
-
Array.from(resultsEl.querySelectorAll('[data-idx]')).forEach((btn) => {
|
|
77
|
-
btn.addEventListener('click', async () => {
|
|
78
|
-
const idx = Number(btn.dataset.idx);
|
|
79
|
-
selectedItem = items[idx] ?? null;
|
|
80
|
-
Array.from(resultsEl.querySelectorAll('.markdown-link-result')).forEach((el) => {
|
|
81
|
-
el.classList.toggle('is-selected', el === btn);
|
|
82
|
-
});
|
|
83
|
-
await loadHeadingsFor(selectedItem);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
async function loadHeadingsFor(item) {
|
|
88
|
-
headingSelect.innerHTML = '<option value="">None</option>';
|
|
89
|
-
if (!item || !options.loadHeadings)
|
|
90
|
-
return;
|
|
91
|
-
const myRequest = ++headingRequestId;
|
|
92
|
-
try {
|
|
93
|
-
const headings = await options.loadHeadings(item.path);
|
|
94
|
-
if (myRequest !== headingRequestId)
|
|
95
|
-
return;
|
|
96
|
-
for (const heading of headings) {
|
|
97
|
-
const option = document.createElement('option');
|
|
98
|
-
option.value = heading.id;
|
|
99
|
-
option.textContent = heading.text;
|
|
100
|
-
headingSelect.appendChild(option);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
// ignore — heading load failures shouldn't block insertion
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
async function runSearch(query) {
|
|
108
|
-
if (!options.searchLinks) {
|
|
109
|
-
renderResults([], 'Search unavailable');
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const myRequest = ++searchRequestId;
|
|
113
|
-
try {
|
|
114
|
-
const items = await options.searchLinks(query);
|
|
115
|
-
if (myRequest !== searchRequestId)
|
|
116
|
-
return;
|
|
117
|
-
renderResults(items, query ? 'No files match' : 'Start typing to search');
|
|
118
|
-
}
|
|
119
|
-
catch (err) {
|
|
120
|
-
if (myRequest !== searchRequestId)
|
|
121
|
-
return;
|
|
122
|
-
renderResults([], `Search failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
function apply() {
|
|
126
|
-
if (mode === 'url') {
|
|
127
|
-
const url = urlInput.value.trim();
|
|
128
|
-
if (!url)
|
|
129
|
-
return;
|
|
130
|
-
const label = labelInput.value.trim();
|
|
131
|
-
options.onInsert(label ? `[${label}](${url})` : `<${url}>`);
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
if (!selectedItem)
|
|
135
|
-
return;
|
|
136
|
-
const heading = headingSelect.value.trim();
|
|
137
|
-
const alias = aliasInput.value.trim();
|
|
138
|
-
let text = `[[${selectedItem.wikiPath}`;
|
|
139
|
-
if (heading)
|
|
140
|
-
text += `#${heading}`;
|
|
141
|
-
if (alias)
|
|
142
|
-
text += `|${alias}`;
|
|
143
|
-
text += ']]';
|
|
144
|
-
options.onInsert(text);
|
|
145
|
-
}
|
|
146
|
-
close();
|
|
147
|
-
}
|
|
148
|
-
function reset() {
|
|
149
|
-
setMode('file');
|
|
150
|
-
selectedItem = null;
|
|
151
|
-
searchInput.value = '';
|
|
152
|
-
aliasInput.value = '';
|
|
153
|
-
urlInput.value = '';
|
|
154
|
-
labelInput.value = '';
|
|
155
|
-
headingSelect.innerHTML = '<option value="">None</option>';
|
|
156
|
-
renderResults([], 'Start typing to search');
|
|
157
|
-
}
|
|
158
|
-
function open(selectedText) {
|
|
159
|
-
reset();
|
|
160
|
-
if (selectedText && /^https?:\/\//i.test(selectedText)) {
|
|
161
|
-
setMode('url');
|
|
162
|
-
urlInput.value = selectedText;
|
|
163
|
-
}
|
|
164
|
-
else if (selectedText) {
|
|
165
|
-
aliasInput.value = selectedText;
|
|
166
|
-
labelInput.value = selectedText;
|
|
167
|
-
}
|
|
168
|
-
modal.hidden = false;
|
|
169
|
-
(mode === 'file' ? searchInput : urlInput).focus();
|
|
170
|
-
}
|
|
171
|
-
function close() {
|
|
172
|
-
modal.hidden = true;
|
|
173
|
-
}
|
|
174
|
-
const onSearchInput = () => {
|
|
175
|
-
if (searchDebounce)
|
|
176
|
-
clearTimeout(searchDebounce);
|
|
177
|
-
searchDebounce = setTimeout(() => {
|
|
178
|
-
void runSearch(searchInput.value);
|
|
179
|
-
}, SEARCH_DEBOUNCE_MS);
|
|
180
|
-
};
|
|
181
|
-
const onKeyDown = (event) => {
|
|
182
|
-
if (event.key === 'Escape') {
|
|
183
|
-
event.preventDefault();
|
|
184
|
-
close();
|
|
185
|
-
}
|
|
186
|
-
else if (event.key === 'Enter' && !event.shiftKey) {
|
|
187
|
-
event.preventDefault();
|
|
188
|
-
apply();
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
tabFile.addEventListener('click', () => setMode('file'));
|
|
192
|
-
tabUrl.addEventListener('click', () => setMode('url'));
|
|
193
|
-
searchInput.addEventListener('input', onSearchInput);
|
|
194
|
-
applyBtn.addEventListener('click', apply);
|
|
195
|
-
cancelBtn.addEventListener('click', close);
|
|
196
|
-
modal.addEventListener('keydown', onKeyDown);
|
|
197
|
-
return {
|
|
198
|
-
open,
|
|
199
|
-
close,
|
|
200
|
-
destroy: () => {
|
|
201
|
-
if (searchDebounce)
|
|
202
|
-
clearTimeout(searchDebounce);
|
|
203
|
-
close();
|
|
204
|
-
},
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
function escapeHtml(value) {
|
|
208
|
-
return value
|
|
209
|
-
.replace(/&/g, '&')
|
|
210
|
-
.replace(/</g, '<')
|
|
211
|
-
.replace(/>/g, '>')
|
|
212
|
-
.replace(/"/g, '"');
|
|
213
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { MarkdownEditorHandle } from './editor.js';
|
|
2
|
-
export declare function mountCodeMirrorRaw(options: {
|
|
3
|
-
target: HTMLElement;
|
|
4
|
-
value: string;
|
|
5
|
-
initialScrollTop?: number;
|
|
6
|
-
onChange: (value: string) => void;
|
|
7
|
-
onBlur?: () => void;
|
|
8
|
-
}): MarkdownEditorHandle;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
|
|
2
|
-
import { markdown } from '@codemirror/lang-markdown';
|
|
3
|
-
import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
4
|
-
import { EditorState } from '@codemirror/state';
|
|
5
|
-
import { oneDark } from '@codemirror/theme-one-dark';
|
|
6
|
-
import { EditorView, keymap, lineNumbers } from '@codemirror/view';
|
|
7
|
-
export function mountCodeMirrorRaw(options) {
|
|
8
|
-
const state = EditorState.create({
|
|
9
|
-
doc: options.value,
|
|
10
|
-
extensions: [
|
|
11
|
-
lineNumbers(),
|
|
12
|
-
history(),
|
|
13
|
-
markdown(),
|
|
14
|
-
oneDark,
|
|
15
|
-
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
|
16
|
-
keymap.of([...defaultKeymap, ...historyKeymap]),
|
|
17
|
-
EditorView.lineWrapping,
|
|
18
|
-
EditorView.updateListener.of((update) => {
|
|
19
|
-
if (update.docChanged) {
|
|
20
|
-
options.onChange(update.state.doc.toString());
|
|
21
|
-
}
|
|
22
|
-
}),
|
|
23
|
-
EditorView.domEventHandlers({
|
|
24
|
-
blur: () => {
|
|
25
|
-
options.onBlur?.();
|
|
26
|
-
return false;
|
|
27
|
-
},
|
|
28
|
-
}),
|
|
29
|
-
],
|
|
30
|
-
});
|
|
31
|
-
const view = new EditorView({ state, parent: options.target });
|
|
32
|
-
if (typeof options.initialScrollTop === 'number') {
|
|
33
|
-
view.scrollDOM.scrollTop = options.initialScrollTop;
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
destroy: () => view.destroy(),
|
|
37
|
-
focus: () => view.focus(),
|
|
38
|
-
getValue: () => view.state.doc.toString(),
|
|
39
|
-
setValue: (value) => {
|
|
40
|
-
const current = view.state.doc.toString();
|
|
41
|
-
if (current === value)
|
|
42
|
-
return;
|
|
43
|
-
view.dispatch({
|
|
44
|
-
changes: { from: 0, to: view.state.doc.length, insert: value },
|
|
45
|
-
});
|
|
46
|
-
},
|
|
47
|
-
revealLine: (lineNumber) => {
|
|
48
|
-
const total = view.state.doc.lines;
|
|
49
|
-
const clamped = Math.max(1, Math.min(lineNumber, total));
|
|
50
|
-
const line = view.state.doc.line(clamped);
|
|
51
|
-
view.dispatch({
|
|
52
|
-
selection: { anchor: line.from },
|
|
53
|
-
scrollIntoView: true,
|
|
54
|
-
});
|
|
55
|
-
view.focus();
|
|
56
|
-
},
|
|
57
|
-
setScrollTop: (scrollTop) => {
|
|
58
|
-
view.scrollDOM.scrollTop = scrollTop;
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface SelectionToolbarHandle {
|
|
2
|
-
destroy(): void;
|
|
3
|
-
}
|
|
4
|
-
export interface SelectionToolbarOptions {
|
|
5
|
-
editorRoot: HTMLElement;
|
|
6
|
-
onLinkRequest?: (selectedText: string) => void;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Floating selection toolbar that appears above non-empty text selections
|
|
10
|
-
* inside the editor root. Uses `document.execCommand` for formatting so it
|
|
11
|
-
* works uniformly with ProseMirror (which intercepts beforeinput) and plain
|
|
12
|
-
* contenteditable.
|
|
13
|
-
*/
|
|
14
|
-
export declare function attachSelectionToolbar(options: SelectionToolbarOptions): SelectionToolbarHandle;
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Floating selection toolbar that appears above non-empty text selections
|
|
3
|
-
* inside the editor root. Uses `document.execCommand` for formatting so it
|
|
4
|
-
* works uniformly with ProseMirror (which intercepts beforeinput) and plain
|
|
5
|
-
* contenteditable.
|
|
6
|
-
*/
|
|
7
|
-
export function attachSelectionToolbar(options) {
|
|
8
|
-
const toolbar = document.createElement('div');
|
|
9
|
-
toolbar.className = 'md-selection-toolbar';
|
|
10
|
-
toolbar.setAttribute('role', 'toolbar');
|
|
11
|
-
toolbar.hidden = true;
|
|
12
|
-
toolbar.innerHTML = `
|
|
13
|
-
<button type="button" class="md-selection-toolbar-btn" data-action="bold" title="Bold"><strong>B</strong></button>
|
|
14
|
-
<button type="button" class="md-selection-toolbar-btn" data-action="italic" title="Italic"><em>I</em></button>
|
|
15
|
-
<button type="button" class="md-selection-toolbar-btn" data-action="strike" title="Strikethrough"><span style="text-decoration:line-through">S</span></button>
|
|
16
|
-
<span class="md-selection-toolbar-sep" aria-hidden="true"></span>
|
|
17
|
-
<button type="button" class="md-selection-toolbar-btn" data-action="heading-1" title="Heading 1">H1</button>
|
|
18
|
-
<button type="button" class="md-selection-toolbar-btn" data-action="heading-2" title="Heading 2">H2</button>
|
|
19
|
-
<button type="button" class="md-selection-toolbar-btn" data-action="heading-3" title="Heading 3">H3</button>
|
|
20
|
-
<span class="md-selection-toolbar-sep" aria-hidden="true"></span>
|
|
21
|
-
<button type="button" class="md-selection-toolbar-btn" data-action="code" title="Inline code">‹›</button>
|
|
22
|
-
<button type="button" class="md-selection-toolbar-btn" data-action="link" title="Link">🔗</button>
|
|
23
|
-
`;
|
|
24
|
-
document.body.appendChild(toolbar);
|
|
25
|
-
function positionToToolbar(rect) {
|
|
26
|
-
const toolbarRect = toolbar.getBoundingClientRect();
|
|
27
|
-
const margin = 8;
|
|
28
|
-
let top = rect.top - toolbarRect.height - margin;
|
|
29
|
-
if (top < margin) {
|
|
30
|
-
top = rect.bottom + margin;
|
|
31
|
-
}
|
|
32
|
-
let left = rect.left + rect.width / 2 - toolbarRect.width / 2;
|
|
33
|
-
const viewportWidth = document.documentElement.clientWidth;
|
|
34
|
-
left = Math.max(margin, Math.min(left, viewportWidth - toolbarRect.width - margin));
|
|
35
|
-
toolbar.style.position = 'fixed';
|
|
36
|
-
toolbar.style.top = `${top}px`;
|
|
37
|
-
toolbar.style.left = `${left}px`;
|
|
38
|
-
}
|
|
39
|
-
function hide() {
|
|
40
|
-
toolbar.hidden = true;
|
|
41
|
-
}
|
|
42
|
-
function selectionIsInsideEditor(selection) {
|
|
43
|
-
const anchorNode = selection.anchorNode;
|
|
44
|
-
if (!anchorNode)
|
|
45
|
-
return false;
|
|
46
|
-
return options.editorRoot.contains(anchorNode.nodeType === Node.ELEMENT_NODE ? anchorNode : anchorNode.parentElement);
|
|
47
|
-
}
|
|
48
|
-
function update() {
|
|
49
|
-
const selection = window.getSelection();
|
|
50
|
-
if (!selection || selection.isCollapsed || selection.rangeCount === 0) {
|
|
51
|
-
hide();
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (!selectionIsInsideEditor(selection)) {
|
|
55
|
-
hide();
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const range = selection.getRangeAt(0);
|
|
59
|
-
const rect = range.getBoundingClientRect();
|
|
60
|
-
if (rect.width === 0 && rect.height === 0) {
|
|
61
|
-
hide();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
toolbar.hidden = false;
|
|
65
|
-
positionToToolbar(rect);
|
|
66
|
-
}
|
|
67
|
-
const onSelectionChange = () => {
|
|
68
|
-
// Debounce to next frame so getBoundingClientRect is stable.
|
|
69
|
-
requestAnimationFrame(update);
|
|
70
|
-
};
|
|
71
|
-
const onScrollOrResize = () => {
|
|
72
|
-
if (!toolbar.hidden)
|
|
73
|
-
update();
|
|
74
|
-
};
|
|
75
|
-
document.addEventListener('selectionchange', onSelectionChange);
|
|
76
|
-
window.addEventListener('scroll', onScrollOrResize, true);
|
|
77
|
-
window.addEventListener('resize', onScrollOrResize);
|
|
78
|
-
toolbar.addEventListener('mousedown', (event) => {
|
|
79
|
-
// Keep the editor's selection while interacting with the toolbar.
|
|
80
|
-
event.preventDefault();
|
|
81
|
-
});
|
|
82
|
-
toolbar.addEventListener('click', (event) => {
|
|
83
|
-
const button = event.target.closest('[data-action]');
|
|
84
|
-
if (!button)
|
|
85
|
-
return;
|
|
86
|
-
const action = button.dataset.action;
|
|
87
|
-
if (!action)
|
|
88
|
-
return;
|
|
89
|
-
const selectedText = window.getSelection()?.toString() ?? '';
|
|
90
|
-
switch (action) {
|
|
91
|
-
case 'bold':
|
|
92
|
-
document.execCommand('bold');
|
|
93
|
-
break;
|
|
94
|
-
case 'italic':
|
|
95
|
-
document.execCommand('italic');
|
|
96
|
-
break;
|
|
97
|
-
case 'strike':
|
|
98
|
-
document.execCommand('strikeThrough');
|
|
99
|
-
break;
|
|
100
|
-
case 'code': {
|
|
101
|
-
const wrapped = `\`${selectedText || 'code'}\``;
|
|
102
|
-
document.execCommand('insertText', false, wrapped);
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
case 'heading-1':
|
|
106
|
-
document.execCommand('formatBlock', false, 'h1');
|
|
107
|
-
break;
|
|
108
|
-
case 'heading-2':
|
|
109
|
-
document.execCommand('formatBlock', false, 'h2');
|
|
110
|
-
break;
|
|
111
|
-
case 'heading-3':
|
|
112
|
-
document.execCommand('formatBlock', false, 'h3');
|
|
113
|
-
break;
|
|
114
|
-
case 'link':
|
|
115
|
-
options.onLinkRequest?.(selectedText);
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
hide();
|
|
119
|
-
});
|
|
120
|
-
return {
|
|
121
|
-
destroy: () => {
|
|
122
|
-
document.removeEventListener('selectionchange', onSelectionChange);
|
|
123
|
-
window.removeEventListener('scroll', onScrollOrResize, true);
|
|
124
|
-
window.removeEventListener('resize', onScrollOrResize);
|
|
125
|
-
toolbar.remove();
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { MarkdownOutlineItem } from './outline.js';
|
|
2
|
-
export interface MarkdownTocHandle {
|
|
3
|
-
dispose: () => void;
|
|
4
|
-
}
|
|
5
|
-
export declare function renderMarkdownToc(outline: MarkdownOutlineItem[], activeHeadingId?: string | null): string;
|
|
6
|
-
export declare function attachMarkdownToc(options: {
|
|
7
|
-
shell: HTMLElement;
|
|
8
|
-
outline: MarkdownOutlineItem[];
|
|
9
|
-
scrollContainer: HTMLElement;
|
|
10
|
-
onSelect: (headingId: string) => void;
|
|
11
|
-
}): MarkdownTocHandle | null;
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
function escapeHtml(value) {
|
|
2
|
-
return value
|
|
3
|
-
.replace(/&/g, '&')
|
|
4
|
-
.replace(/</g, '<')
|
|
5
|
-
.replace(/>/g, '>')
|
|
6
|
-
.replace(/"/g, '"')
|
|
7
|
-
.replace(/'/g, ''');
|
|
8
|
-
}
|
|
9
|
-
function setActiveItem(nav, activeId) {
|
|
10
|
-
const buttons = Array.from(nav.querySelectorAll('[data-toc-id]'));
|
|
11
|
-
buttons.forEach((button) => {
|
|
12
|
-
const isActive = button.dataset.tocId === activeId;
|
|
13
|
-
button.classList.toggle('is-active', isActive);
|
|
14
|
-
button.setAttribute('aria-current', isActive ? 'location' : 'false');
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
export function renderMarkdownToc(outline, activeHeadingId) {
|
|
18
|
-
if (outline.length === 0) {
|
|
19
|
-
return '';
|
|
20
|
-
}
|
|
21
|
-
const items = outline.map((item) => {
|
|
22
|
-
const activeClass = item.id === activeHeadingId ? ' is-active' : '';
|
|
23
|
-
return `<button class="markdown-toc-link${activeClass}" type="button" data-toc-id="${escapeHtml(item.id)}" data-level="${item.level}" aria-current="${item.id === activeHeadingId ? 'location' : 'false'}">${escapeHtml(item.text)}</button>`;
|
|
24
|
-
}).join('');
|
|
25
|
-
return `
|
|
26
|
-
<aside class="markdown-toc-shell" aria-label="Table of contents">
|
|
27
|
-
<div class="markdown-toc-title">Contents</div>
|
|
28
|
-
<nav class="markdown-toc-nav">${items}</nav>
|
|
29
|
-
</aside>
|
|
30
|
-
`;
|
|
31
|
-
}
|
|
32
|
-
export function attachMarkdownToc(options) {
|
|
33
|
-
const nav = options.shell.querySelector('.markdown-toc-nav');
|
|
34
|
-
if (!nav) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
const handleClick = (event) => {
|
|
38
|
-
const target = event.target;
|
|
39
|
-
const button = target?.closest('[data-toc-id]');
|
|
40
|
-
const headingId = button?.dataset.tocId;
|
|
41
|
-
if (!headingId) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
options.onSelect(headingId);
|
|
45
|
-
setActiveItem(nav, headingId);
|
|
46
|
-
};
|
|
47
|
-
const updateActiveHeading = () => {
|
|
48
|
-
const headings = options.outline
|
|
49
|
-
.map((item) => {
|
|
50
|
-
const element = document.getElementById(item.id);
|
|
51
|
-
return element ? { item, element } : null;
|
|
52
|
-
})
|
|
53
|
-
.filter((entry) => entry !== null);
|
|
54
|
-
if (headings.length === 0) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const scrollTop = options.scrollContainer.scrollTop;
|
|
58
|
-
const nextActive = headings.reduce((activeId, current) => {
|
|
59
|
-
if (current.element.offsetTop - scrollTop <= 96) {
|
|
60
|
-
return current.item.id;
|
|
61
|
-
}
|
|
62
|
-
return activeId;
|
|
63
|
-
}, headings[0].item.id);
|
|
64
|
-
setActiveItem(nav, nextActive);
|
|
65
|
-
};
|
|
66
|
-
nav.addEventListener('click', handleClick);
|
|
67
|
-
options.scrollContainer.addEventListener('scroll', updateActiveHeading, { passive: true });
|
|
68
|
-
updateActiveHeading();
|
|
69
|
-
return {
|
|
70
|
-
dispose: () => {
|
|
71
|
-
nav.removeEventListener('click', handleClick);
|
|
72
|
-
options.scrollContainer.removeEventListener('scroll', updateActiveHeading);
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export type MarkdownEditorView = 'raw' | 'markdown';
|
|
2
|
-
export interface MarkdownLinkSearchItem {
|
|
3
|
-
path: string;
|
|
4
|
-
title: string;
|
|
5
|
-
wikiPath: string;
|
|
6
|
-
relativePath: string;
|
|
7
|
-
}
|
|
8
|
-
export interface MarkdownLinkHeading {
|
|
9
|
-
id: string;
|
|
10
|
-
text: string;
|
|
11
|
-
}
|
|
12
|
-
export interface MarkdownEditorHandle {
|
|
13
|
-
destroy: () => void;
|
|
14
|
-
focus: () => void;
|
|
15
|
-
getValue: () => string;
|
|
16
|
-
setValue: (value: string) => void;
|
|
17
|
-
revealLine: (lineNumber: number, headingId?: string) => void;
|
|
18
|
-
setScrollTop: (scrollTop: number) => void;
|
|
19
|
-
}
|
|
20
|
-
export declare function renderMarkdownCopyButton(): string;
|
|
21
|
-
export declare function renderMarkdownModeToggle(view: MarkdownEditorView): string;
|
|
22
|
-
export declare function renderMarkdownEditorShell(options: {
|
|
23
|
-
content: string;
|
|
24
|
-
view: MarkdownEditorView;
|
|
25
|
-
}): string;
|
|
26
|
-
export declare function mountMarkdownEditor(options: {
|
|
27
|
-
target: HTMLElement;
|
|
28
|
-
value: string;
|
|
29
|
-
view: MarkdownEditorView;
|
|
30
|
-
initialScrollTop?: number;
|
|
31
|
-
currentFilePath: string;
|
|
32
|
-
searchLinks?: (query: string) => Promise<MarkdownLinkSearchItem[]>;
|
|
33
|
-
loadHeadings?: (filePath: string) => Promise<MarkdownLinkHeading[]>;
|
|
34
|
-
onChange: (value: string) => void;
|
|
35
|
-
onBlur?: () => void;
|
|
36
|
-
}): MarkdownEditorHandle;
|