mrmd-editor 0.7.1 → 0.8.0
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/package.json +3 -1
- package/src/commands.js +112 -4
- package/src/comment-syntax.js +364 -39
- package/src/config/handlers.js +1 -2
- package/src/config/schema.js +46 -4
- package/src/document-template.js +2236 -0
- package/src/frontmatter-updater.js +204 -74
- package/src/grammar.js +758 -0
- package/src/index.js +1074 -55
- package/src/keymap.js +11 -2
- package/src/markdown/block-decorations.js +108 -5
- package/src/markdown/facets.js +37 -0
- package/src/markdown/html-inline.js +9 -5
- package/src/markdown/index.js +13 -3
- package/src/markdown/inline-commands.js +256 -0
- package/src/markdown/inline-model.js +578 -0
- package/src/markdown/inline-state.js +103 -0
- package/src/markdown/renderer.js +219 -12
- package/src/markdown/styles.js +290 -3
- package/src/markdown/widgets/alert-title.js +10 -8
- package/src/markdown/widgets/frontmatter.js +0 -6
- package/src/markdown/widgets/index.js +1 -0
- package/src/markdown/widgets/list-marker.js +29 -0
- package/src/markdown/wysiwyg.js +1158 -0
- package/src/mrp-types.js +2 -0
- package/src/output-widget.js +532 -18
- package/src/page-view-pagination.js +127 -0
- package/src/runtime-lsp.js +1757 -150
- package/src/section-controls/commands.js +617 -0
- package/src/section-controls/index.js +63 -0
- package/src/section-controls/plugin.js +165 -0
- package/src/section-controls/widgets.js +936 -0
- package/src/shell/ai-menu.js +11 -0
- package/src/shell/components/context-panel.js +572 -0
- package/src/shell/components/status-bar.js +10 -2
- package/src/shell/layouts/studio.js +206 -14
- package/src/shell/orchestrator-client.js +69 -0
- package/src/spellcheck.js +166 -0
- package/src/tables/README.md +97 -0
- package/src/tables/commands/insert-linked-table.js +122 -0
- package/src/tables/commands/open-table-workspace.js +43 -0
- package/src/tables/index.js +24 -0
- package/src/tables/jobs/client.js +158 -0
- package/src/tables/parsing/anchors.js +82 -0
- package/src/tables/parsing/linked-table-blocks.js +61 -0
- package/src/tables/state/linked-table-state.js +68 -0
- package/src/tables/widgets/linked-table-source-banner.js +77 -0
- package/src/tables/widgets/linked-table-widget.js +256 -0
- package/src/tables/workspace/controller.js +616 -0
- package/src/term-pty-client.js +51 -2
- package/src/term-widget.js +43 -3
- package/src/widgets/theme-utils.js +24 -16
- package/src/widgets/theme.js +1015 -1
- package/src/runtime-codelens/detector.js +0 -279
- package/src/runtime-codelens/index.js +0 -76
- package/src/runtime-codelens/plugin.js +0 -142
- package/src/runtime-codelens/styles.js +0 -184
- package/src/runtime-codelens/widgets.js +0 -216
package/src/term-pty-client.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* @property {string} [cwd] - Working directory
|
|
16
16
|
* @property {string} [venv] - Virtual environment path
|
|
17
17
|
* @property {string} [filePath] - Associated file path
|
|
18
|
+
* @property {string} [shell] - Preferred shell (e.g. powershell, wsl, cmd)
|
|
18
19
|
* @property {Function} [onData] - Callback for data from PTY
|
|
19
20
|
* @property {Function} [onConnect] - Callback when connected
|
|
20
21
|
* @property {Function} [onDisconnect] - Callback when disconnected
|
|
@@ -36,6 +37,7 @@ export class PtyClient {
|
|
|
36
37
|
cwd: config.cwd || null,
|
|
37
38
|
venv: config.venv || null,
|
|
38
39
|
filePath: config.filePath || null,
|
|
40
|
+
shell: config.shell || null,
|
|
39
41
|
onData: config.onData || (() => {}),
|
|
40
42
|
onConnect: config.onConnect || (() => {}),
|
|
41
43
|
onDisconnect: config.onDisconnect || (() => {}),
|
|
@@ -56,6 +58,10 @@ export class PtyClient {
|
|
|
56
58
|
this.maxReconnectAttempts = 10;
|
|
57
59
|
this.baseReconnectDelay = 1000;
|
|
58
60
|
this.reconnectTimeout = null;
|
|
61
|
+
|
|
62
|
+
// Small-input batching (typing) to reduce WS frame overhead on tunneled/mobile connections.
|
|
63
|
+
this._writeBuffer = '';
|
|
64
|
+
this._writeTimer = null;
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
/**
|
|
@@ -88,6 +94,9 @@ export class PtyClient {
|
|
|
88
94
|
if (this.config.filePath) {
|
|
89
95
|
params.set('file_path', this.config.filePath);
|
|
90
96
|
}
|
|
97
|
+
if (this.config.shell) {
|
|
98
|
+
params.set('shell', this.config.shell);
|
|
99
|
+
}
|
|
91
100
|
|
|
92
101
|
return `${protocol}//${host}/api/pty?${params.toString()}`;
|
|
93
102
|
}
|
|
@@ -149,6 +158,11 @@ export class PtyClient {
|
|
|
149
158
|
console.log('[PtyClient] Disconnected, code:', event.code);
|
|
150
159
|
this.connected = false;
|
|
151
160
|
this.ws = null;
|
|
161
|
+
if (this._writeTimer) {
|
|
162
|
+
clearTimeout(this._writeTimer);
|
|
163
|
+
this._writeTimer = null;
|
|
164
|
+
}
|
|
165
|
+
this._writeBuffer = '';
|
|
152
166
|
this.config.onDisconnect(event.code, event.reason);
|
|
153
167
|
|
|
154
168
|
// Attempt reconnection if not intentionally closed
|
|
@@ -184,6 +198,21 @@ export class PtyClient {
|
|
|
184
198
|
}, delay);
|
|
185
199
|
}
|
|
186
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Flush any buffered small-input writes.
|
|
203
|
+
*/
|
|
204
|
+
_flushWriteBuffer() {
|
|
205
|
+
if (!this._writeBuffer) return;
|
|
206
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
207
|
+
this._writeBuffer = '';
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const buffered = this._writeBuffer;
|
|
212
|
+
this._writeBuffer = '';
|
|
213
|
+
this.ws.send(JSON.stringify({ type: 'input', data: buffered }));
|
|
214
|
+
}
|
|
215
|
+
|
|
187
216
|
/**
|
|
188
217
|
* Send data to the PTY (user input).
|
|
189
218
|
* Large payloads (pastes) are chunked to avoid overwhelming the terminal
|
|
@@ -194,12 +223,26 @@ export class PtyClient {
|
|
|
194
223
|
write(data) {
|
|
195
224
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
|
|
196
225
|
|
|
197
|
-
// Small inputs (normal typing
|
|
226
|
+
// Small inputs (normal typing/control sequences): buffer briefly so
|
|
227
|
+
// rapid keypresses are batched into fewer WS frames.
|
|
198
228
|
if (data.length <= 4096) {
|
|
199
|
-
this.
|
|
229
|
+
this._writeBuffer += data;
|
|
230
|
+
if (!this._writeTimer) {
|
|
231
|
+
this._writeTimer = setTimeout(() => {
|
|
232
|
+
this._writeTimer = null;
|
|
233
|
+
this._flushWriteBuffer();
|
|
234
|
+
}, 8);
|
|
235
|
+
}
|
|
200
236
|
return;
|
|
201
237
|
}
|
|
202
238
|
|
|
239
|
+
// Preserve input ordering: flush any pending small-input batch first.
|
|
240
|
+
if (this._writeTimer) {
|
|
241
|
+
clearTimeout(this._writeTimer);
|
|
242
|
+
this._writeTimer = null;
|
|
243
|
+
}
|
|
244
|
+
this._flushWriteBuffer();
|
|
245
|
+
|
|
203
246
|
// Large paste: chunk it to prevent UI freeze.
|
|
204
247
|
// xterm.js may wrap pastes in bracketed paste markers (\x1b[200~ ... \x1b[201~).
|
|
205
248
|
// We preserve those: send a leading marker once, chunk the body, send trailing marker.
|
|
@@ -266,6 +309,12 @@ export class PtyClient {
|
|
|
266
309
|
this.reconnectTimeout = null;
|
|
267
310
|
}
|
|
268
311
|
|
|
312
|
+
if (this._writeTimer) {
|
|
313
|
+
clearTimeout(this._writeTimer);
|
|
314
|
+
this._writeTimer = null;
|
|
315
|
+
}
|
|
316
|
+
this._writeBuffer = '';
|
|
317
|
+
|
|
269
318
|
if (this.ws) {
|
|
270
319
|
this.ws.close();
|
|
271
320
|
this.ws = null;
|
package/src/term-widget.js
CHANGED
|
@@ -146,15 +146,23 @@ class TerminalWidget extends WidgetType {
|
|
|
146
146
|
|
|
147
147
|
// Get theme from CSS variables
|
|
148
148
|
const theme = this._getThemeFromCSS();
|
|
149
|
+
const isMobile = /iPhone|iPad|Android|Mobile/i.test(navigator.userAgent);
|
|
149
150
|
|
|
150
151
|
// Create terminal
|
|
151
152
|
const term = new Terminal({
|
|
152
153
|
cursorBlink: true,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
cursorStyle: 'block',
|
|
155
|
+
cursorInactiveStyle: 'outline',
|
|
156
|
+
fontSize: isMobile ? 13 : 14,
|
|
157
|
+
fontFamily: '"Monaspace Neon Var", "SF Mono", "Fira Code", "Monaco", "Inconsolata", monospace',
|
|
158
|
+
scrollback: isMobile ? 5000 : 50000,
|
|
156
159
|
convertEol: true,
|
|
157
160
|
theme: theme,
|
|
161
|
+
drawBoldTextInBrightColors: !isMobile,
|
|
162
|
+
allowTransparency: false,
|
|
163
|
+
smoothScrollDuration: 0,
|
|
164
|
+
fastScrollModifier: 'alt',
|
|
165
|
+
fastScrollSensitivity: isMobile ? 3 : 5,
|
|
158
166
|
});
|
|
159
167
|
|
|
160
168
|
this.xtermInstance = term;
|
|
@@ -176,6 +184,37 @@ class TerminalWidget extends WidgetType {
|
|
|
176
184
|
// Open terminal
|
|
177
185
|
term.open(container);
|
|
178
186
|
|
|
187
|
+
// Renderer addons: prefer WebGL on desktop, fallback to Canvas, then DOM.
|
|
188
|
+
let rendererLoaded = false;
|
|
189
|
+
if (!isMobile && typeof WebglAddon !== 'undefined') {
|
|
190
|
+
try {
|
|
191
|
+
const webgl = new WebglAddon.WebglAddon();
|
|
192
|
+
webgl.onContextLoss(() => {
|
|
193
|
+
try { webgl.dispose(); } catch (e) {}
|
|
194
|
+
console.warn('[term] WebGL context lost, falling back to canvas/DOM');
|
|
195
|
+
if (typeof CanvasAddon !== 'undefined') {
|
|
196
|
+
try {
|
|
197
|
+
term.loadAddon(new CanvasAddon.CanvasAddon());
|
|
198
|
+
} catch (canvasErr) {
|
|
199
|
+
console.warn('[term] Canvas addon failed, using DOM fallback:', canvasErr);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
term.loadAddon(webgl);
|
|
204
|
+
rendererLoaded = true;
|
|
205
|
+
} catch (e) {
|
|
206
|
+
console.warn('[term] WebGL failed:', e);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!rendererLoaded && typeof CanvasAddon !== 'undefined') {
|
|
211
|
+
try {
|
|
212
|
+
term.loadAddon(new CanvasAddon.CanvasAddon());
|
|
213
|
+
} catch (e) {
|
|
214
|
+
console.warn('[term] Canvas addon failed, using DOM fallback:', e);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
179
218
|
// Fit to container
|
|
180
219
|
if (this.fitAddon) {
|
|
181
220
|
setTimeout(() => {
|
|
@@ -266,6 +305,7 @@ class TerminalWidget extends WidgetType {
|
|
|
266
305
|
cwd: this.config.cwd,
|
|
267
306
|
venv: this.config.venv,
|
|
268
307
|
filePath: this.block.filePath,
|
|
308
|
+
shell: this.config.shell,
|
|
269
309
|
onData: (data) => {
|
|
270
310
|
term.write(data);
|
|
271
311
|
},
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 1. Explicit config (config.appearance.widgetTheme)
|
|
10
10
|
* 2. CodeMirror theme class (.cm-theme-dark)
|
|
11
11
|
* 3. System preference (prefers-color-scheme)
|
|
12
|
-
* 4. Default: '
|
|
12
|
+
* 4. Default: 'plain-light'
|
|
13
13
|
*
|
|
14
14
|
* ## Watching for Changes
|
|
15
15
|
*
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* @module widgets/theme-utils
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import { getTheme } from './theme.js';
|
|
23
|
+
import { getTheme, getDefaultTokens } from './theme.js';
|
|
24
24
|
|
|
25
25
|
// #region DETECTION
|
|
26
26
|
|
|
@@ -31,12 +31,12 @@ import { getTheme } from './theme.js';
|
|
|
31
31
|
* 1. Explicit theme name passed as parameter
|
|
32
32
|
* 2. CodeMirror theme class on the editor element
|
|
33
33
|
* 3. System color scheme preference
|
|
34
|
-
* 4. Default: '
|
|
34
|
+
* 4. Default: 'plain-light'
|
|
35
35
|
*
|
|
36
36
|
* @param {Object} [options]
|
|
37
37
|
* @param {string} [options.themeName] - Explicit theme name (highest priority)
|
|
38
38
|
* @param {HTMLElement} [options.editorElement] - Editor DOM element to check for .cm-theme-dark
|
|
39
|
-
* @returns {string} Theme name ('
|
|
39
|
+
* @returns {string} Theme name ('plain-light', 'plain-dark', etc.)
|
|
40
40
|
*
|
|
41
41
|
* @example
|
|
42
42
|
* // Detect based on CodeMirror and system preference
|
|
@@ -57,25 +57,25 @@ export function detectTheme({ themeName, editorElement } = {}) {
|
|
|
57
57
|
// CodeMirror adds this class when using oneDark or similar dark themes
|
|
58
58
|
const hasDarkTheme = editorElement.closest('.cm-theme-dark') !== null;
|
|
59
59
|
if (hasDarkTheme) {
|
|
60
|
-
return '
|
|
60
|
+
return 'plain-dark';
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// If there's any cm-theme class but not dark, assume light
|
|
64
64
|
const hasAnyTheme = editorElement.matches('[class*="cm-theme"]') ||
|
|
65
65
|
editorElement.closest('[class*="cm-theme"]') !== null;
|
|
66
66
|
if (hasAnyTheme) {
|
|
67
|
-
return '
|
|
67
|
+
return 'plain-light';
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// 3. System preference
|
|
72
72
|
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
73
73
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
74
|
-
return prefersDark ? '
|
|
74
|
+
return prefersDark ? 'plain-dark' : 'plain-light';
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// 4. Default
|
|
78
|
-
return '
|
|
78
|
+
return 'plain-light';
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
@@ -208,7 +208,7 @@ const THEME_FONTS_ID = 'mrmd-widget-theme-fonts';
|
|
|
208
208
|
*
|
|
209
209
|
* @example
|
|
210
210
|
* // Apply by name
|
|
211
|
-
* applyTheme('
|
|
211
|
+
* applyTheme('plain-light');
|
|
212
212
|
*
|
|
213
213
|
* // Apply custom theme object
|
|
214
214
|
* applyTheme({
|
|
@@ -224,12 +224,14 @@ export function applyTheme(themeOrName, { target, useStyleTag = true } = {}) {
|
|
|
224
224
|
: themeOrName;
|
|
225
225
|
|
|
226
226
|
if (!theme) {
|
|
227
|
-
console.warn(`Theme "${themeOrName}" not found, using
|
|
228
|
-
return applyTheme('
|
|
227
|
+
console.warn(`Theme "${themeOrName}" not found, using plain-light`);
|
|
228
|
+
return applyTheme('plain-light', { target, useStyleTag });
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
// Get token values (exclude name, description, fontFace, isDark)
|
|
232
|
-
|
|
232
|
+
// Merge with token defaults so newly added tokens are always present,
|
|
233
|
+
// even for older themes that don't define them yet.
|
|
234
|
+
const tokens = getDefaultTokens();
|
|
233
235
|
for (const [key, value] of Object.entries(theme)) {
|
|
234
236
|
if (key.startsWith('--')) {
|
|
235
237
|
tokens[key] = value;
|
|
@@ -330,7 +332,7 @@ export function removeThemeStyles() {
|
|
|
330
332
|
* @returns {string} CSS string with :root variables and optional font-face
|
|
331
333
|
*
|
|
332
334
|
* @example
|
|
333
|
-
* const css = generateThemeCSS('
|
|
335
|
+
* const css = generateThemeCSS('plain-light');
|
|
334
336
|
* // :root {
|
|
335
337
|
* // --widget-surface: rgba(0, 0, 0, 0.35);
|
|
336
338
|
* // ...
|
|
@@ -346,11 +348,17 @@ export function generateThemeCSS(themeOrName, { includeFontFace = true } = {}) {
|
|
|
346
348
|
: themeOrName;
|
|
347
349
|
|
|
348
350
|
if (!theme) {
|
|
349
|
-
return generateThemeCSS('
|
|
351
|
+
return generateThemeCSS('plain-light', { includeFontFace });
|
|
350
352
|
}
|
|
351
353
|
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
+
const tokens = getDefaultTokens();
|
|
355
|
+
for (const [key, value] of Object.entries(theme)) {
|
|
356
|
+
if (key.startsWith('--')) {
|
|
357
|
+
tokens[key] = value;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const vars = Object.entries(tokens)
|
|
354
362
|
.map(([k, v]) => ` ${k}: ${v};`)
|
|
355
363
|
.join('\n');
|
|
356
364
|
|