mrmd-editor 0.5.0 → 0.7.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 +1 -1
- package/src/cell-controls/widgets.js +30 -0
- package/src/cells.js +9 -9
- package/src/config/schema.js +4 -3
- package/src/ctrl-k-modal.js +190 -14
- package/src/document-languages.js +105 -0
- package/src/execution.js +50 -13
- package/src/frontmatter-updater.js +224 -0
- package/src/index.js +162 -97
- package/src/markdown/block-decorations.js +103 -0
- package/src/markdown/renderer.js +52 -3
- package/src/markdown/styles.js +126 -0
- package/src/markdown/widgets/frontmatter.js +438 -0
- package/src/monitor-coordination.js +1 -3
- package/src/mrp-client.js +36 -169
- package/src/mrp-types.js +1 -37
- package/src/output-widget.js +818 -123
- package/src/runtime-codelens/index.js +3 -3
- package/src/shell/ai-menu.js +70 -0
- package/src/shell/components/menu.js +39 -1
- package/src/shell/components/status-bar.js +5 -5
- package/src/shell/dialogs/file-picker.js +167 -6
- package/src/shell/layouts/studio.js +8 -9
- package/src/shell/orchestrator-client.js +60 -18
- package/src/shell/state.js +63 -42
- package/src/shell/styles.js +266 -0
package/src/index.js
CHANGED
|
@@ -304,55 +304,55 @@ function parseProgress(output) {
|
|
|
304
304
|
function createJavaScriptRuntime(options = {}) {
|
|
305
305
|
const rt = createMrmdJsRuntime(options);
|
|
306
306
|
|
|
307
|
-
// Track named
|
|
308
|
-
//
|
|
309
|
-
// null/undefined/'default'/'main'/'none' →
|
|
307
|
+
// Track named execution contexts - each can have different isolation
|
|
308
|
+
// Context naming:
|
|
309
|
+
// null/undefined/'default'/'main'/'none' → configured default isolation
|
|
310
310
|
// 'sandbox'/'iframe' → sandboxed iframe
|
|
311
311
|
// other names → sandboxed iframe with separate scope
|
|
312
|
-
const
|
|
312
|
+
const contexts = new Map();
|
|
313
313
|
const defaultIsolation = options.defaultIsolation || 'iframe';
|
|
314
314
|
|
|
315
315
|
/**
|
|
316
|
-
* Get or create a
|
|
317
|
-
* @param {string|null}
|
|
316
|
+
* Get or create a context by name
|
|
317
|
+
* @param {string|null} contextName
|
|
318
318
|
* @returns {Session}
|
|
319
319
|
*/
|
|
320
|
-
function
|
|
321
|
-
// Normalize
|
|
322
|
-
const name =
|
|
320
|
+
function getOrCreateContext(contextName) {
|
|
321
|
+
// Normalize context name
|
|
322
|
+
const name = contextName || 'default';
|
|
323
323
|
|
|
324
|
-
// Return existing
|
|
325
|
-
if (
|
|
326
|
-
return
|
|
324
|
+
// Return existing context
|
|
325
|
+
if (contexts.has(name)) {
|
|
326
|
+
return contexts.get(name);
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
// Determine isolation mode based on
|
|
329
|
+
// Determine isolation mode based on context name
|
|
330
330
|
let isolation;
|
|
331
|
-
if (!
|
|
332
|
-
// Default
|
|
331
|
+
if (!contextName || contextName === 'default' || contextName === 'main' || contextName === 'none') {
|
|
332
|
+
// Default context uses the configured default isolation
|
|
333
333
|
isolation = defaultIsolation;
|
|
334
|
-
} else if (
|
|
334
|
+
} else if (contextName === 'sandbox' || contextName === 'iframe') {
|
|
335
335
|
// Explicit sandbox request
|
|
336
336
|
isolation = 'iframe';
|
|
337
337
|
} else {
|
|
338
|
-
// Named
|
|
338
|
+
// Named contexts are sandboxed by default (separate scope)
|
|
339
339
|
isolation = 'iframe';
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
// Create new
|
|
343
|
-
const
|
|
342
|
+
// Create new execution context with appropriate isolation
|
|
343
|
+
const context = rt.createSession({
|
|
344
344
|
language: 'javascript',
|
|
345
345
|
isolation,
|
|
346
346
|
id: name,
|
|
347
347
|
});
|
|
348
348
|
|
|
349
|
-
|
|
350
|
-
console.log(`[JS Runtime] Created
|
|
351
|
-
return
|
|
349
|
+
contexts.set(name, context);
|
|
350
|
+
console.log(`[JS Runtime] Created context '${name}' with isolation: ${isolation}`);
|
|
351
|
+
return context;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
// Create default
|
|
355
|
-
const
|
|
354
|
+
// Create default context eagerly
|
|
355
|
+
const defaultContext = getOrCreateContext('default');
|
|
356
356
|
|
|
357
357
|
// Languages supported by mrmd-js
|
|
358
358
|
const supportedLanguages = {
|
|
@@ -379,8 +379,9 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
379
379
|
/** Execute code (non-streaming) */
|
|
380
380
|
async execute(code, language, execOptions = {}) {
|
|
381
381
|
const lang = supportedLanguages[language.toLowerCase()] || 'javascript';
|
|
382
|
-
const
|
|
383
|
-
const
|
|
382
|
+
const contextName = execOptions.context ?? execOptions.session;
|
|
383
|
+
const context = getOrCreateContext(contextName);
|
|
384
|
+
const result = await context.execute(code, { language: lang });
|
|
384
385
|
return {
|
|
385
386
|
success: result.success,
|
|
386
387
|
stdout: result.stdout || '',
|
|
@@ -394,8 +395,9 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
394
395
|
/** Execute code with streaming output */
|
|
395
396
|
async executeStreaming(code, language, onChunk, onStdinRequest, execOptions = {}) {
|
|
396
397
|
const lang = supportedLanguages[language.toLowerCase()] || 'javascript';
|
|
397
|
-
const
|
|
398
|
-
const
|
|
398
|
+
const contextName = execOptions.context ?? execOptions.session;
|
|
399
|
+
const context = getOrCreateContext(contextName);
|
|
400
|
+
const result = await context.execute(code, { language: lang });
|
|
399
401
|
|
|
400
402
|
// Handle different output types
|
|
401
403
|
let output = result.stdout || '';
|
|
@@ -407,6 +409,9 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
407
409
|
// Prefer HTML representation
|
|
408
410
|
if (display.data['text/html']) {
|
|
409
411
|
output = display.data['text/html'];
|
|
412
|
+
} else if (display.data['text/css']) {
|
|
413
|
+
// Preserve CSS source so the output widget can show selector impact
|
|
414
|
+
output = display.data['text/css'];
|
|
410
415
|
} else if (display.data['text/plain']) {
|
|
411
416
|
output = display.data['text/plain'];
|
|
412
417
|
}
|
|
@@ -431,18 +436,18 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
431
436
|
};
|
|
432
437
|
},
|
|
433
438
|
|
|
434
|
-
/** Reset a
|
|
435
|
-
reset(
|
|
436
|
-
const
|
|
437
|
-
if (
|
|
438
|
-
|
|
439
|
+
/** Reset a context (clear all variables) */
|
|
440
|
+
reset(contextName) {
|
|
441
|
+
const context = contexts.get(contextName || 'default');
|
|
442
|
+
if (context) {
|
|
443
|
+
context.reset();
|
|
439
444
|
}
|
|
440
445
|
},
|
|
441
446
|
|
|
442
|
-
/** Reset all
|
|
447
|
+
/** Reset all contexts */
|
|
443
448
|
resetAll() {
|
|
444
|
-
for (const
|
|
445
|
-
|
|
449
|
+
for (const context of contexts.values()) {
|
|
450
|
+
context.reset();
|
|
446
451
|
}
|
|
447
452
|
},
|
|
448
453
|
|
|
@@ -451,23 +456,32 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
451
456
|
return rt;
|
|
452
457
|
},
|
|
453
458
|
|
|
454
|
-
/** Get a
|
|
455
|
-
|
|
456
|
-
return
|
|
459
|
+
/** Get a context by name (default if not specified) */
|
|
460
|
+
getContext(contextName) {
|
|
461
|
+
return getOrCreateContext(contextName);
|
|
462
|
+
},
|
|
463
|
+
|
|
464
|
+
/** List all context names */
|
|
465
|
+
listContexts() {
|
|
466
|
+
return Array.from(contexts.keys());
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
// Legacy aliases (kept for compatibility inside monorepo)
|
|
470
|
+
getSession(contextName) {
|
|
471
|
+
return getOrCreateContext(contextName);
|
|
457
472
|
},
|
|
458
473
|
|
|
459
|
-
/** List all session names */
|
|
460
474
|
listSessions() {
|
|
461
|
-
return Array.from(
|
|
475
|
+
return Array.from(contexts.keys());
|
|
462
476
|
},
|
|
463
477
|
|
|
464
|
-
/** Destroy the runtime and all
|
|
478
|
+
/** Destroy the runtime and all contexts */
|
|
465
479
|
destroy() {
|
|
466
480
|
rt.destroy();
|
|
467
481
|
},
|
|
468
482
|
|
|
469
483
|
// =========================================================================
|
|
470
|
-
// LSP Features (powered by mrmd-js
|
|
484
|
+
// LSP Features (powered by mrmd-js default context)
|
|
471
485
|
// =========================================================================
|
|
472
486
|
|
|
473
487
|
/**
|
|
@@ -479,7 +493,7 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
479
493
|
* @returns {{found: boolean, name?: string, type?: string, value?: string, signature?: string}|null}
|
|
480
494
|
*/
|
|
481
495
|
hover(code, cursor) {
|
|
482
|
-
return
|
|
496
|
+
return defaultContext.hover(code, cursor);
|
|
483
497
|
},
|
|
484
498
|
|
|
485
499
|
/**
|
|
@@ -491,7 +505,7 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
491
505
|
* @returns {{matches: Array, cursorStart: number, cursorEnd: number}}
|
|
492
506
|
*/
|
|
493
507
|
complete(code, cursor) {
|
|
494
|
-
return
|
|
508
|
+
return defaultContext.complete(code, cursor);
|
|
495
509
|
},
|
|
496
510
|
|
|
497
511
|
/**
|
|
@@ -503,16 +517,18 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
503
517
|
* @returns {Object|null}
|
|
504
518
|
*/
|
|
505
519
|
inspect(code, cursor, options = {}) {
|
|
506
|
-
return
|
|
520
|
+
return defaultContext.inspect(code, cursor, options);
|
|
507
521
|
},
|
|
508
522
|
|
|
509
523
|
/**
|
|
510
|
-
* List all variables in
|
|
524
|
+
* List all variables in a context namespace.
|
|
511
525
|
*
|
|
526
|
+
* @param {Object} [filter]
|
|
527
|
+
* @param {string} [contextName='default']
|
|
512
528
|
* @returns {Array<{name: string, type: string, value: string, expandable?: boolean}>}
|
|
513
529
|
*/
|
|
514
|
-
listVariables() {
|
|
515
|
-
return
|
|
530
|
+
listVariables(filter = {}, contextName = 'default') {
|
|
531
|
+
return getOrCreateContext(contextName).listVariables(filter);
|
|
516
532
|
},
|
|
517
533
|
|
|
518
534
|
/**
|
|
@@ -523,7 +539,7 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
523
539
|
* @returns {Object}
|
|
524
540
|
*/
|
|
525
541
|
getVariable(name, options = {}) {
|
|
526
|
-
return
|
|
542
|
+
return defaultContext.getVariable(name, options);
|
|
527
543
|
},
|
|
528
544
|
|
|
529
545
|
/**
|
|
@@ -533,7 +549,7 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
533
549
|
* @returns {{status: 'complete'|'incomplete'|'invalid'|'unknown', indent?: string}}
|
|
534
550
|
*/
|
|
535
551
|
isComplete(code) {
|
|
536
|
-
return
|
|
552
|
+
return defaultContext.isComplete(code);
|
|
537
553
|
},
|
|
538
554
|
|
|
539
555
|
/**
|
|
@@ -543,7 +559,7 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
543
559
|
* @returns {Promise<{formatted: string, changed: boolean}>}
|
|
544
560
|
*/
|
|
545
561
|
format(code) {
|
|
546
|
-
return
|
|
562
|
+
return defaultContext.format(code);
|
|
547
563
|
},
|
|
548
564
|
|
|
549
565
|
/**
|
|
@@ -551,7 +567,7 @@ function createJavaScriptRuntime(options = {}) {
|
|
|
551
567
|
* @returns {import('./runtime-lsp.js').RuntimeLSPProvider}
|
|
552
568
|
*/
|
|
553
569
|
getLSPProvider() {
|
|
554
|
-
return adaptMrmdJsSession(
|
|
570
|
+
return adaptMrmdJsSession(defaultContext);
|
|
555
571
|
},
|
|
556
572
|
};
|
|
557
573
|
}
|
|
@@ -726,6 +742,15 @@ const codeBlockStyles = EditorView.theme({
|
|
|
726
742
|
'.cm-codeblock-fence::selection, .cm-codeblock-fence *::selection': {
|
|
727
743
|
backgroundColor: 'var(--editor-selection, #264f78) !important',
|
|
728
744
|
},
|
|
745
|
+
// Mobile: code blocks need to be larger and scroll horizontally
|
|
746
|
+
'@media (max-width: 768px)': {
|
|
747
|
+
'.cm-codeblock-line': {
|
|
748
|
+
fontSize: 'max(var(--code-font-size, 0.8em), 13px)',
|
|
749
|
+
},
|
|
750
|
+
'.cm-codeblock-fence': {
|
|
751
|
+
fontSize: '0.6em', // Slightly larger than desktop's 0.5em for visibility
|
|
752
|
+
},
|
|
753
|
+
},
|
|
729
754
|
});
|
|
730
755
|
// #endregion CODE_BLOCK_BACKGROUND
|
|
731
756
|
|
|
@@ -823,8 +848,6 @@ function create(target, options = {}) {
|
|
|
823
848
|
// MRP and builtin types are handled separately below
|
|
824
849
|
}
|
|
825
850
|
|
|
826
|
-
// JavaScript runtime option (backward compat)
|
|
827
|
-
const javascript = options.javascript !== undefined ? options.javascript : true;
|
|
828
851
|
// JavaScript isolation mode: 'iframe' (default, sandboxed) or 'none' (main window context)
|
|
829
852
|
const javascriptIsolation = options.javascriptIsolation || 'iframe';
|
|
830
853
|
|
|
@@ -893,6 +916,11 @@ function create(target, options = {}) {
|
|
|
893
916
|
'.cm-gutters': { display: 'none' },
|
|
894
917
|
'.cm-activeLineGutter': { backgroundColor: 'transparent' },
|
|
895
918
|
'&.cm-focused': { outline: 'none' },
|
|
919
|
+
// Mobile: slightly larger text, comfortable line-height
|
|
920
|
+
'@media (max-width: 768px)': {
|
|
921
|
+
'&': { fontSize: '17px' },
|
|
922
|
+
'.cm-scroller': { lineHeight: '1.7' },
|
|
923
|
+
},
|
|
896
924
|
});
|
|
897
925
|
|
|
898
926
|
// Inject CSS styles
|
|
@@ -1021,6 +1049,7 @@ function create(target, options = {}) {
|
|
|
1021
1049
|
// Event handlers
|
|
1022
1050
|
const changeHandlers = [];
|
|
1023
1051
|
const saveHandlers = [];
|
|
1052
|
+
const frontmatterTitleCommitHandlers = [];
|
|
1024
1053
|
const viewSourceHandlers = [];
|
|
1025
1054
|
const cellRunHandlers = [];
|
|
1026
1055
|
const cellOutputHandlers = [];
|
|
@@ -1036,6 +1065,19 @@ function create(target, options = {}) {
|
|
|
1036
1065
|
}
|
|
1037
1066
|
});
|
|
1038
1067
|
|
|
1068
|
+
const handleFrontmatterTitleCommit = (event) => {
|
|
1069
|
+
const title = event?.detail?.title;
|
|
1070
|
+
if (!title) return;
|
|
1071
|
+
for (const handler of frontmatterTitleCommitHandlers) {
|
|
1072
|
+
try {
|
|
1073
|
+
handler(title, event.detail || {});
|
|
1074
|
+
} catch (err) {
|
|
1075
|
+
console.warn('[mrmd] frontmatter title commit handler failed:', err);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
element.addEventListener('mrmd:frontmatter-title-commit', handleFrontmatterTitleCommit);
|
|
1080
|
+
|
|
1039
1081
|
// Create runtime registry
|
|
1040
1082
|
const registry = createRuntimeRegistry();
|
|
1041
1083
|
|
|
@@ -1044,19 +1086,17 @@ function create(target, options = {}) {
|
|
|
1044
1086
|
registry.register(name, runtime);
|
|
1045
1087
|
}
|
|
1046
1088
|
|
|
1047
|
-
//
|
|
1048
|
-
//
|
|
1049
|
-
// - false: disable JavaScript execution
|
|
1050
|
-
// - object: use custom runtime (must implement supports/execute/executeStreaming)
|
|
1089
|
+
// JavaScript runtime for editor + LSP features.
|
|
1090
|
+
// Use normalized config so options.runtimes.javascript is not shadowed by a second runtime.
|
|
1051
1091
|
let jsRuntime = null;
|
|
1052
|
-
|
|
1092
|
+
const jsRuntimeConfig = config.runtimes.javascript;
|
|
1093
|
+
if (jsRuntimeConfig?.type === 'builtin') {
|
|
1053
1094
|
jsRuntime = createJavaScriptRuntime({ defaultIsolation: javascriptIsolation });
|
|
1054
1095
|
registry.register('javascript', jsRuntime);
|
|
1055
|
-
} else if (
|
|
1056
|
-
|
|
1057
|
-
registry.register('javascript', javascript);
|
|
1096
|
+
} else if (jsRuntimeConfig?.type === 'custom' && jsRuntimeConfig.instance) {
|
|
1097
|
+
jsRuntime = jsRuntimeConfig.instance;
|
|
1058
1098
|
}
|
|
1059
|
-
// javascript
|
|
1099
|
+
// If javascript is disabled, jsRuntime remains null.
|
|
1060
1100
|
|
|
1061
1101
|
// =========================================================================
|
|
1062
1102
|
// RUNTIME LSP PROVIDERS
|
|
@@ -1066,7 +1106,7 @@ function create(target, options = {}) {
|
|
|
1066
1106
|
const runtimeLspProviders = new Map();
|
|
1067
1107
|
|
|
1068
1108
|
// Add JS runtime LSP provider if available
|
|
1069
|
-
if (jsRuntime) {
|
|
1109
|
+
if (jsRuntime?.getLSPProvider) {
|
|
1070
1110
|
const jsProvider = jsRuntime.getLSPProvider();
|
|
1071
1111
|
runtimeLspProviders.set('javascript', jsProvider);
|
|
1072
1112
|
}
|
|
@@ -2031,17 +2071,16 @@ function create(target, options = {}) {
|
|
|
2031
2071
|
* Refresh variables from all MRP runtimes
|
|
2032
2072
|
* Fetches current variable state and updates state.variables
|
|
2033
2073
|
*
|
|
2034
|
-
* @param {string} [sessionId] - Specific session to refresh (optional)
|
|
2035
2074
|
* @returns {Promise<void>}
|
|
2036
2075
|
*/
|
|
2037
|
-
async refreshVariables(
|
|
2076
|
+
async refreshVariables() {
|
|
2038
2077
|
for (const [name, runtime] of registry.runtimes) {
|
|
2039
2078
|
// Check if runtime is an MRP client (has getVariables method)
|
|
2040
2079
|
if (typeof runtime.getVariables === 'function') {
|
|
2041
2080
|
try {
|
|
2042
|
-
const result = await runtime.getVariables(
|
|
2081
|
+
const result = await runtime.getVariables();
|
|
2043
2082
|
if (result && result.variables) {
|
|
2044
|
-
const session =
|
|
2083
|
+
const session = 'default';
|
|
2045
2084
|
const variables = {};
|
|
2046
2085
|
for (const v of result.variables) {
|
|
2047
2086
|
variables[v.name] = {
|
|
@@ -2106,6 +2145,14 @@ function create(target, options = {}) {
|
|
|
2106
2145
|
};
|
|
2107
2146
|
},
|
|
2108
2147
|
|
|
2148
|
+
onFrontmatterTitleCommit(callback) {
|
|
2149
|
+
frontmatterTitleCommitHandlers.push(callback);
|
|
2150
|
+
return () => {
|
|
2151
|
+
const idx = frontmatterTitleCommitHandlers.indexOf(callback);
|
|
2152
|
+
if (idx >= 0) frontmatterTitleCommitHandlers.splice(idx, 1);
|
|
2153
|
+
};
|
|
2154
|
+
},
|
|
2155
|
+
|
|
2109
2156
|
onCellRun(callback) {
|
|
2110
2157
|
return this.execution.on('cellRun', callback);
|
|
2111
2158
|
},
|
|
@@ -2179,6 +2226,7 @@ function create(target, options = {}) {
|
|
|
2179
2226
|
}
|
|
2180
2227
|
// Clean up theme watcher
|
|
2181
2228
|
unwatchTheme();
|
|
2229
|
+
element.removeEventListener('mrmd:frontmatter-title-commit', handleFrontmatterTitleCommit);
|
|
2182
2230
|
// Clean up undo manager
|
|
2183
2231
|
undoManager.destroy();
|
|
2184
2232
|
view.destroy();
|
|
@@ -2522,12 +2570,12 @@ function create(target, options = {}) {
|
|
|
2522
2570
|
}
|
|
2523
2571
|
// #endregion CREATE
|
|
2524
2572
|
|
|
2525
|
-
// #region
|
|
2573
|
+
// #region RUNTIME
|
|
2526
2574
|
/**
|
|
2527
|
-
* Create an editor
|
|
2575
|
+
* Create an editor runtime attachment via orchestrator.
|
|
2528
2576
|
*
|
|
2529
2577
|
* This is the simplest way to use mrmd with full features:
|
|
2530
|
-
* - Automatically creates
|
|
2578
|
+
* - Automatically creates/attaches runtime with orchestrator
|
|
2531
2579
|
* - Connects to sync server
|
|
2532
2580
|
* - Sets up Python runtime (shared or dedicated)
|
|
2533
2581
|
* - Enables monitor mode for long-running executions
|
|
@@ -2538,28 +2586,28 @@ function create(target, options = {}) {
|
|
|
2538
2586
|
* @param {string} options.doc - Document name (required)
|
|
2539
2587
|
* @param {string} [options.python='shared'] - 'shared' or 'dedicated'
|
|
2540
2588
|
* @param {Object} [options.editor] - Additional editor options
|
|
2541
|
-
* @returns {Promise<Object>} Editor instance with
|
|
2589
|
+
* @returns {Promise<Object>} Editor instance with destroyRuntime() method
|
|
2542
2590
|
*
|
|
2543
2591
|
* @example
|
|
2544
2592
|
* // Basic usage
|
|
2545
|
-
* const editor = await mrmd.
|
|
2593
|
+
* const editor = await mrmd.runtime('http://localhost:8080', '#editor', {
|
|
2546
2594
|
* doc: 'my-notebook',
|
|
2547
2595
|
* });
|
|
2548
2596
|
*
|
|
2549
2597
|
* // With dedicated Python runtime
|
|
2550
|
-
* const editor = await mrmd.
|
|
2598
|
+
* const editor = await mrmd.runtime('http://localhost:8080', '#editor', {
|
|
2551
2599
|
* doc: 'my-notebook',
|
|
2552
2600
|
* python: 'dedicated',
|
|
2553
2601
|
* });
|
|
2554
2602
|
*
|
|
2555
2603
|
* // Clean up when done
|
|
2556
|
-
* await editor.
|
|
2604
|
+
* await editor.destroyRuntime();
|
|
2557
2605
|
*/
|
|
2558
|
-
async function
|
|
2606
|
+
async function runtime(orchestratorUrl, target, options = {}) {
|
|
2559
2607
|
const { doc, python = 'shared', editor: editorOptions = {} } = options;
|
|
2560
2608
|
|
|
2561
2609
|
if (!doc) {
|
|
2562
|
-
throw new Error('mrmd.
|
|
2610
|
+
throw new Error('mrmd.runtime: doc option is required');
|
|
2563
2611
|
}
|
|
2564
2612
|
|
|
2565
2613
|
// Normalize orchestrator URL
|
|
@@ -2568,24 +2616,33 @@ async function session(orchestratorUrl, target, options = {}) {
|
|
|
2568
2616
|
baseUrl = baseUrl.slice(0, -1);
|
|
2569
2617
|
}
|
|
2570
2618
|
|
|
2571
|
-
// Create
|
|
2572
|
-
console.log(`[mrmd.
|
|
2619
|
+
// Create runtime attachment with orchestrator
|
|
2620
|
+
console.log(`[mrmd.runtime] Creating runtime for '${doc}' (python=${python})`);
|
|
2573
2621
|
|
|
2574
|
-
|
|
2622
|
+
// Prefer /api/runtimes, fall back to /api/sessions for legacy orchestrators
|
|
2623
|
+
let response = await fetch(`${baseUrl}/api/runtimes`, {
|
|
2575
2624
|
method: 'POST',
|
|
2576
2625
|
headers: { 'Content-Type': 'application/json' },
|
|
2577
2626
|
body: JSON.stringify({ doc, python }),
|
|
2578
2627
|
});
|
|
2579
2628
|
|
|
2629
|
+
if (!response.ok && response.status === 404) {
|
|
2630
|
+
response = await fetch(`${baseUrl}/api/sessions`, {
|
|
2631
|
+
method: 'POST',
|
|
2632
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2633
|
+
body: JSON.stringify({ doc, python }),
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2580
2637
|
if (!response.ok) {
|
|
2581
2638
|
const error = await response.text();
|
|
2582
|
-
throw new Error(`Failed to create
|
|
2639
|
+
throw new Error(`Failed to create runtime attachment: ${error}`);
|
|
2583
2640
|
}
|
|
2584
2641
|
|
|
2585
2642
|
const sessionInfo = await response.json();
|
|
2586
|
-
console.log('[mrmd.
|
|
2643
|
+
console.log('[mrmd.runtime] Runtime created:', sessionInfo);
|
|
2587
2644
|
|
|
2588
|
-
// Extract URLs from
|
|
2645
|
+
// Extract URLs from response
|
|
2589
2646
|
const syncUrl = sessionInfo.sync;
|
|
2590
2647
|
const runtimeUrl = sessionInfo.runtimes?.python?.url;
|
|
2591
2648
|
|
|
@@ -2610,17 +2667,16 @@ async function session(orchestratorUrl, target, options = {}) {
|
|
|
2610
2667
|
ydoc: editor.ydoc,
|
|
2611
2668
|
runtimeUrl,
|
|
2612
2669
|
awareness: editor.awareness,
|
|
2613
|
-
session: doc,
|
|
2614
2670
|
});
|
|
2615
2671
|
}
|
|
2616
2672
|
|
|
2617
|
-
// Store
|
|
2673
|
+
// Store runtime info on editor
|
|
2618
2674
|
editor._sessionInfo = sessionInfo;
|
|
2619
2675
|
editor._orchestratorUrl = baseUrl;
|
|
2620
2676
|
|
|
2621
|
-
// Add
|
|
2622
|
-
editor.
|
|
2623
|
-
console.log(`[mrmd.
|
|
2677
|
+
// Add destroyRuntime method
|
|
2678
|
+
editor.destroyRuntime = async function() {
|
|
2679
|
+
console.log(`[mrmd.runtime] Destroying runtime for '${doc}'`);
|
|
2624
2680
|
|
|
2625
2681
|
// Disconnect from sync
|
|
2626
2682
|
if (editor.disconnect) {
|
|
@@ -2629,28 +2685,33 @@ async function session(orchestratorUrl, target, options = {}) {
|
|
|
2629
2685
|
|
|
2630
2686
|
// Call orchestrator to clean up
|
|
2631
2687
|
try {
|
|
2632
|
-
|
|
2688
|
+
let resp = await fetch(`${baseUrl}/api/runtimes/${encodeURIComponent(doc)}`, {
|
|
2633
2689
|
method: 'DELETE',
|
|
2634
2690
|
});
|
|
2691
|
+
if (!resp.ok && resp.status === 404) {
|
|
2692
|
+
resp = await fetch(`${baseUrl}/api/sessions/${encodeURIComponent(doc)}`, {
|
|
2693
|
+
method: 'DELETE',
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2635
2696
|
if (!resp.ok) {
|
|
2636
|
-
console.warn(`[mrmd.
|
|
2697
|
+
console.warn(`[mrmd.runtime] Failed to destroy runtime: ${resp.statusText}`);
|
|
2637
2698
|
}
|
|
2638
2699
|
} catch (err) {
|
|
2639
|
-
console.warn(`[mrmd.
|
|
2700
|
+
console.warn(`[mrmd.runtime] Failed to destroy runtime:`, err);
|
|
2640
2701
|
}
|
|
2641
2702
|
|
|
2642
2703
|
// Destroy editor
|
|
2643
2704
|
editor.destroy();
|
|
2644
2705
|
};
|
|
2645
2706
|
|
|
2646
|
-
// Add method to get
|
|
2647
|
-
editor.
|
|
2707
|
+
// Add method to get runtime info
|
|
2708
|
+
editor.getRuntimeInfo = function() {
|
|
2648
2709
|
return this._sessionInfo;
|
|
2649
2710
|
};
|
|
2650
2711
|
|
|
2651
2712
|
return editor;
|
|
2652
2713
|
}
|
|
2653
|
-
// #endregion
|
|
2714
|
+
// #endregion RUNTIME
|
|
2654
2715
|
|
|
2655
2716
|
// #region DRIVE
|
|
2656
2717
|
/**
|
|
@@ -3014,7 +3075,7 @@ const mrmd = {
|
|
|
3014
3075
|
version: VERSION,
|
|
3015
3076
|
create,
|
|
3016
3077
|
drive,
|
|
3017
|
-
|
|
3078
|
+
runtime,
|
|
3018
3079
|
yjs,
|
|
3019
3080
|
codemirror,
|
|
3020
3081
|
terminal,
|
|
@@ -3072,7 +3133,7 @@ export default mrmd;
|
|
|
3072
3133
|
export {
|
|
3073
3134
|
create,
|
|
3074
3135
|
drive,
|
|
3075
|
-
|
|
3136
|
+
runtime,
|
|
3076
3137
|
yjs,
|
|
3077
3138
|
codemirror,
|
|
3078
3139
|
terminal,
|
|
@@ -3208,4 +3269,8 @@ export {
|
|
|
3208
3269
|
|
|
3209
3270
|
// Re-export shell components for direct imports
|
|
3210
3271
|
export const { createStudio, OrchestratorClient, Drive, createDrive, ShellStateManager, injectShellStyles } = shellModule;
|
|
3272
|
+
|
|
3273
|
+
// Document language detection and frontmatter updater
|
|
3274
|
+
export { getDocumentLanguages, getLanguageDisplay, isExecutableLanguage } from './document-languages.js';
|
|
3275
|
+
export { parseFrontmatter, updateFrontmatterSession, readFrontmatterSession, getEffectiveSessionConfig } from './frontmatter-updater.js';
|
|
3211
3276
|
// #endregion EXPORTS
|