mrmd-editor 0.7.0 → 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/execution.js +69 -15
- package/src/frontmatter-updater.js +204 -74
- package/src/grammar.js +758 -0
- package/src/index.js +1120 -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 +218 -8
- package/src/shell/dialogs/file-picker.js +211 -0
- package/src/shell/layouts/studio.js +229 -14
- package/src/shell/orchestrator-client.js +114 -0
- package/src/shell/styles.js +62 -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 +111 -7
- package/src/term-widget.js +43 -3
- package/src/widgets/theme-utils.js +24 -16
- package/src/widgets/theme.js +1535 -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
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
import { OrchestratorClient } from '../orchestrator-client.js';
|
|
15
15
|
import { ShellStateManager } from '../state.js';
|
|
16
16
|
import { createStatusBar } from '../components/status-bar.js';
|
|
17
|
+
import { createContextPanel } from '../components/context-panel.js';
|
|
17
18
|
import { injectShellStyles } from '../styles.js';
|
|
18
19
|
import { showFilePicker, showFolderPicker } from '../dialogs/file-picker.js';
|
|
19
20
|
import { prompt, confirm } from '../dialogs/base-dialog.js';
|
|
20
21
|
import { Drive } from '../drive.js';
|
|
21
22
|
import { AiClient } from '../ai-client.js';
|
|
22
23
|
import { showAiMenu, AI_COMMANDS, injectAiMenuStyles } from '../ai-menu.js';
|
|
24
|
+
import { canImportLinkedTableFromHost } from '../../tables/index.js';
|
|
23
25
|
|
|
24
26
|
// =============================================================================
|
|
25
27
|
// STUDIO
|
|
@@ -92,6 +94,16 @@ export async function createStudio(target, options = {}) {
|
|
|
92
94
|
overflow: hidden;
|
|
93
95
|
`;
|
|
94
96
|
|
|
97
|
+
// Create main row container (editor + context rail)
|
|
98
|
+
const mainContainer = document.createElement('div');
|
|
99
|
+
mainContainer.className = 'mrmd-studio__main';
|
|
100
|
+
mainContainer.style.cssText = `
|
|
101
|
+
display: flex;
|
|
102
|
+
flex: 1;
|
|
103
|
+
min-height: 0;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
`;
|
|
106
|
+
|
|
95
107
|
// Create editor container
|
|
96
108
|
const editorContainer = document.createElement('div');
|
|
97
109
|
editorContainer.className = 'mrmd-studio__editor';
|
|
@@ -99,8 +111,19 @@ export async function createStudio(target, options = {}) {
|
|
|
99
111
|
flex: 1;
|
|
100
112
|
overflow: hidden;
|
|
101
113
|
position: relative;
|
|
114
|
+
min-width: 0;
|
|
102
115
|
`;
|
|
103
116
|
|
|
117
|
+
const contextPanelContainer = document.createElement('div');
|
|
118
|
+
contextPanelContainer.className = 'mrmd-studio__context';
|
|
119
|
+
contextPanelContainer.style.cssText = `
|
|
120
|
+
display: flex;
|
|
121
|
+
min-height: 0;
|
|
122
|
+
`;
|
|
123
|
+
|
|
124
|
+
mainContainer.appendChild(editorContainer);
|
|
125
|
+
mainContainer.appendChild(contextPanelContainer);
|
|
126
|
+
|
|
104
127
|
// Create status bar container
|
|
105
128
|
const statusBarContainer = document.createElement('div');
|
|
106
129
|
statusBarContainer.className = 'mrmd-studio__statusbar';
|
|
@@ -108,9 +131,9 @@ export async function createStudio(target, options = {}) {
|
|
|
108
131
|
// Assemble layout
|
|
109
132
|
if (statusBarConfig.position === 'top') {
|
|
110
133
|
studioEl.appendChild(statusBarContainer);
|
|
111
|
-
studioEl.appendChild(
|
|
134
|
+
studioEl.appendChild(mainContainer);
|
|
112
135
|
} else {
|
|
113
|
-
studioEl.appendChild(
|
|
136
|
+
studioEl.appendChild(mainContainer);
|
|
114
137
|
studioEl.appendChild(statusBarContainer);
|
|
115
138
|
}
|
|
116
139
|
|
|
@@ -142,6 +165,16 @@ export async function createStudio(target, options = {}) {
|
|
|
142
165
|
return () => eventHandlers.get(event).delete(handler);
|
|
143
166
|
}
|
|
144
167
|
|
|
168
|
+
function getCurrentDocumentMarkdownPath(docName = currentDocName) {
|
|
169
|
+
if (!docName) return null;
|
|
170
|
+
if (docName.startsWith('/')) return null;
|
|
171
|
+
return docName.endsWith('.md') ? docName : `${docName}.md`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function supportsLinkedTableImport() {
|
|
175
|
+
return canImportLinkedTableFromHost();
|
|
176
|
+
}
|
|
177
|
+
|
|
145
178
|
/**
|
|
146
179
|
* Detect if cursor is inside a code block and return block info
|
|
147
180
|
* @param {EditorView} view
|
|
@@ -266,6 +299,7 @@ export async function createStudio(target, options = {}) {
|
|
|
266
299
|
// Track current editor instance and preserved state
|
|
267
300
|
let editor = null;
|
|
268
301
|
let currentDocName = null;
|
|
302
|
+
let contextPanelComponent = null;
|
|
269
303
|
let preservedEditorState = {
|
|
270
304
|
theme: editorOptions.theme || null,
|
|
271
305
|
dark: editorOptions.dark ?? null,
|
|
@@ -288,6 +322,9 @@ export async function createStudio(target, options = {}) {
|
|
|
288
322
|
// Preserve theme across switches
|
|
289
323
|
theme: preservedEditorState.theme,
|
|
290
324
|
dark: preservedEditorState.dark,
|
|
325
|
+
// Linked-table host context
|
|
326
|
+
projectRoot: shellState.get('projectRoot') || editorOptions.projectRoot || null,
|
|
327
|
+
documentPath: getCurrentDocumentMarkdownPath(docName) || editorOptions.documentPath || null,
|
|
291
328
|
};
|
|
292
329
|
|
|
293
330
|
// Remove any sync options since we're providing ydoc directly
|
|
@@ -376,14 +413,17 @@ export async function createStudio(target, options = {}) {
|
|
|
376
413
|
});
|
|
377
414
|
}
|
|
378
415
|
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
juiceLevel: shellState.get('ai')?.juiceLevel || 0,
|
|
384
|
-
});
|
|
416
|
+
// Configure built-in comment syntax with the AI client.
|
|
417
|
+
// The base editor already installs the extension, so only append
|
|
418
|
+
// updated facet config here.
|
|
419
|
+
if (mrmd.default.commentSyntax?.commentConfigFacet) {
|
|
385
420
|
newEditor.view.dispatch({
|
|
386
|
-
effects: mrmd.codemirror.StateEffect.appendConfig.of(
|
|
421
|
+
effects: mrmd.codemirror.StateEffect.appendConfig.of(
|
|
422
|
+
mrmd.default.commentSyntax.commentConfigFacet.of({
|
|
423
|
+
aiClient,
|
|
424
|
+
juiceLevel: shellState.get('ai')?.juiceLevel || 0,
|
|
425
|
+
})
|
|
426
|
+
),
|
|
387
427
|
});
|
|
388
428
|
}
|
|
389
429
|
}
|
|
@@ -398,15 +438,47 @@ export async function createStudio(target, options = {}) {
|
|
|
398
438
|
* @param {Object|null} codeBlock - Code block info if cursor is in a code block
|
|
399
439
|
*/
|
|
400
440
|
async function executeAiCommand(cmd, targetEditor, codeBlock = null) {
|
|
401
|
-
if (!
|
|
441
|
+
if (!mrmd.default.ai) return;
|
|
402
442
|
|
|
403
443
|
const view = targetEditor.view;
|
|
444
|
+
|
|
445
|
+
if (cmd.action === 'insert-frontmatter-template') {
|
|
446
|
+
const handled = targetEditor.commands?.insertFrontmatterTemplate?.() || false;
|
|
447
|
+
if (handled) {
|
|
448
|
+
emit('aiCommandExecuted', { command: cmd.id, action: cmd.action });
|
|
449
|
+
} else {
|
|
450
|
+
emit('aiCommandError', { command: cmd.id, error: 'Could not insert frontmatter template' });
|
|
451
|
+
}
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (!aiClient) return;
|
|
456
|
+
|
|
404
457
|
const context = mrmd.default.ai.getAiContext(view);
|
|
405
458
|
const juiceLevel = shellState.get('ai')?.juiceLevel || 0;
|
|
406
459
|
|
|
407
460
|
// Detect language from code block, or fall back to python
|
|
408
461
|
const detectedLanguage = codeBlock?.language || 'python';
|
|
409
462
|
|
|
463
|
+
// Resolve richer context from _assets/context/*.md when available
|
|
464
|
+
let resolvedContextText = context.documentContext;
|
|
465
|
+
try {
|
|
466
|
+
if (currentDocName) {
|
|
467
|
+
const resolved = await orchestratorClient.resolveContext({
|
|
468
|
+
doc: currentDocName,
|
|
469
|
+
content: context.documentContext,
|
|
470
|
+
cursorPos: context.cursorPos,
|
|
471
|
+
selection: { from: context.selectionFrom, to: context.selectionTo },
|
|
472
|
+
ensureExists: true,
|
|
473
|
+
});
|
|
474
|
+
if (resolved?.contextText) {
|
|
475
|
+
resolvedContextText = resolved.contextText;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} catch (error) {
|
|
479
|
+
console.warn('[Studio] Failed to resolve AI context, falling back to document only:', error);
|
|
480
|
+
}
|
|
481
|
+
|
|
410
482
|
// Mark AI as active
|
|
411
483
|
shellState._set('ai.active', true);
|
|
412
484
|
|
|
@@ -419,20 +491,20 @@ export async function createStudio(target, options = {}) {
|
|
|
419
491
|
params = {
|
|
420
492
|
text_before_cursor: context.textBeforeCursor,
|
|
421
493
|
local_context: context.localContext,
|
|
422
|
-
document_context:
|
|
494
|
+
document_context: resolvedContextText,
|
|
423
495
|
};
|
|
424
496
|
} else if (cmd.program.includes('Fix') || cmd.program.includes('Correct')) {
|
|
425
497
|
params = {
|
|
426
498
|
text_to_fix: context.selectedText,
|
|
427
499
|
local_context: context.localContext,
|
|
428
|
-
document_context:
|
|
500
|
+
document_context: resolvedContextText,
|
|
429
501
|
};
|
|
430
502
|
} else if (cmd.program.includes('Code')) {
|
|
431
503
|
params = {
|
|
432
504
|
code: context.selectedText,
|
|
433
505
|
language: detectedLanguage,
|
|
434
506
|
local_context: context.localContext,
|
|
435
|
-
document_context:
|
|
507
|
+
document_context: resolvedContextText,
|
|
436
508
|
};
|
|
437
509
|
} else if (cmd.program.includes('Synonym')) {
|
|
438
510
|
params = {
|
|
@@ -440,7 +512,7 @@ export async function createStudio(target, options = {}) {
|
|
|
440
512
|
local_context: context.localContext,
|
|
441
513
|
};
|
|
442
514
|
} else if (cmd.program.includes('Document')) {
|
|
443
|
-
params = { document:
|
|
515
|
+
params = { document: resolvedContextText };
|
|
444
516
|
}
|
|
445
517
|
|
|
446
518
|
await mrmd.default.ai.executeAiOperation(view, aiClient, {
|
|
@@ -518,6 +590,18 @@ export async function createStudio(target, options = {}) {
|
|
|
518
590
|
editor = createEditorForDocument(handle, normalizedName);
|
|
519
591
|
currentDocName = normalizedName;
|
|
520
592
|
|
|
593
|
+
// Place cursor on first empty line (after frontmatter) for a clean first impression.
|
|
594
|
+
// Without this, cursor lands at position 0, showing raw YAML frontmatter.
|
|
595
|
+
if (mrmd.findInitialCursorPosition) {
|
|
596
|
+
const pos = mrmd.findInitialCursorPosition(editor.view.state.doc.toString());
|
|
597
|
+
if (pos > 0) {
|
|
598
|
+
editor.view.dispatch({
|
|
599
|
+
selection: { anchor: pos },
|
|
600
|
+
scrollIntoView: true,
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
521
605
|
// Ensure runtime attachment exists (starts monitor if needed)
|
|
522
606
|
try {
|
|
523
607
|
await orchestratorClient.createRuntimeAttachment(normalizedName, 'shared');
|
|
@@ -532,12 +616,24 @@ export async function createStudio(target, options = {}) {
|
|
|
532
616
|
path: normalizedName.endsWith('.md') ? normalizedName : `${normalizedName}.md`,
|
|
533
617
|
root: filesResult.root,
|
|
534
618
|
});
|
|
619
|
+
editor?.setLinkedTableHostContext?.({
|
|
620
|
+
projectRoot: shellState.get('projectRoot') || null,
|
|
621
|
+
documentPath: normalizedName.endsWith('.md') ? normalizedName : `${normalizedName}.md`,
|
|
622
|
+
});
|
|
535
623
|
|
|
536
624
|
// Update status bar with new editor
|
|
537
625
|
if (statusBarComponent) {
|
|
538
626
|
statusBarComponent.setEditor(editor);
|
|
539
627
|
}
|
|
540
628
|
|
|
629
|
+
if (contextPanelComponent) {
|
|
630
|
+
contextPanelComponent.setEditor(editor);
|
|
631
|
+
contextPanelComponent.setDocument(normalizedName);
|
|
632
|
+
contextPanelComponent.refresh().catch((e) => {
|
|
633
|
+
console.warn('[Studio] Failed to refresh context panel:', e);
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
|
|
541
637
|
emit('fileOpened', { doc: normalizedName });
|
|
542
638
|
|
|
543
639
|
} catch (e) {
|
|
@@ -557,6 +653,17 @@ export async function createStudio(target, options = {}) {
|
|
|
557
653
|
editor = createEditorForDocument(handle, docToOpen);
|
|
558
654
|
currentDocName = docToOpen;
|
|
559
655
|
|
|
656
|
+
// Place cursor on first empty line (after frontmatter) for a clean first impression
|
|
657
|
+
if (mrmd.findInitialCursorPosition) {
|
|
658
|
+
const pos = mrmd.findInitialCursorPosition(editor.view.state.doc.toString());
|
|
659
|
+
if (pos > 0) {
|
|
660
|
+
editor.view.dispatch({
|
|
661
|
+
selection: { anchor: pos },
|
|
662
|
+
scrollIntoView: true,
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
560
667
|
// Ensure runtime attachment exists (starts monitor if needed)
|
|
561
668
|
try {
|
|
562
669
|
await orchestratorClient.createRuntimeAttachment(docToOpen, 'shared');
|
|
@@ -570,6 +677,8 @@ export async function createStudio(target, options = {}) {
|
|
|
570
677
|
|
|
571
678
|
// Shell action handlers
|
|
572
679
|
const handlers = {
|
|
680
|
+
supportsLinkedTableImport,
|
|
681
|
+
|
|
573
682
|
async onRename() {
|
|
574
683
|
const file = shellState.get('file');
|
|
575
684
|
if (!file) return;
|
|
@@ -647,6 +756,65 @@ export async function createStudio(target, options = {}) {
|
|
|
647
756
|
});
|
|
648
757
|
},
|
|
649
758
|
|
|
759
|
+
async onImportLinkedTable() {
|
|
760
|
+
const file = shellState.get('file');
|
|
761
|
+
const projectRoot = shellState.get('projectRoot');
|
|
762
|
+
|
|
763
|
+
if (!supportsLinkedTableImport()) {
|
|
764
|
+
await confirm({
|
|
765
|
+
title: 'Linked table import unavailable',
|
|
766
|
+
message: 'This build does not expose the Electron linked-table host API yet.',
|
|
767
|
+
confirmLabel: 'OK',
|
|
768
|
+
cancelLabel: '',
|
|
769
|
+
});
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (!file || !editor) {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (!projectRoot || file.isOutsideProject || !file.path || file.path.startsWith('/')) {
|
|
778
|
+
await confirm({
|
|
779
|
+
title: 'Linked table import requires a project file',
|
|
780
|
+
message: 'Open a markdown document inside a project before importing a linked table.',
|
|
781
|
+
confirmLabel: 'OK',
|
|
782
|
+
cancelLabel: '',
|
|
783
|
+
});
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
showFilePicker({
|
|
788
|
+
mode: 'open',
|
|
789
|
+
title: 'Import Linked Table',
|
|
790
|
+
orchestratorClient,
|
|
791
|
+
initialPath: projectRoot || '~',
|
|
792
|
+
allowOutsideProject: true,
|
|
793
|
+
onSelect: async (sourceFilePath) => {
|
|
794
|
+
try {
|
|
795
|
+
const result = await editor.importLinkedTableFromHost(sourceFilePath, {
|
|
796
|
+
projectRoot,
|
|
797
|
+
documentPath: file.path,
|
|
798
|
+
cacheFormat: 'csv',
|
|
799
|
+
});
|
|
800
|
+
emit('linkedTableImported', {
|
|
801
|
+
sourceFilePath,
|
|
802
|
+
tableId: result.tableId,
|
|
803
|
+
spec: result.spec,
|
|
804
|
+
});
|
|
805
|
+
} catch (error) {
|
|
806
|
+
console.error('[Studio] Linked table import failed:', error);
|
|
807
|
+
await confirm({
|
|
808
|
+
title: 'Linked table import failed',
|
|
809
|
+
message: error.message || String(error),
|
|
810
|
+
confirmLabel: 'OK',
|
|
811
|
+
cancelLabel: '',
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
},
|
|
815
|
+
});
|
|
816
|
+
},
|
|
817
|
+
|
|
650
818
|
// NOTE: onChangeVenv, onChangeCwd, and onRestartRuntime are no longer used.
|
|
651
819
|
// The new runtime model doesn't support changing venv/cwd on running runtimes.
|
|
652
820
|
// Instead, users start new runtimes via the status bar menu and attach docs to them.
|
|
@@ -873,10 +1041,33 @@ export async function createStudio(target, options = {}) {
|
|
|
873
1041
|
path: currentDocName.endsWith('.md') ? currentDocName : `${currentDocName}.md`,
|
|
874
1042
|
root: result.root,
|
|
875
1043
|
});
|
|
1044
|
+
editor?.setLinkedTableHostContext?.({
|
|
1045
|
+
projectRoot: shellState.get('projectRoot') || null,
|
|
1046
|
+
documentPath: currentDocName.endsWith('.md') ? currentDocName : `${currentDocName}.md`,
|
|
1047
|
+
});
|
|
876
1048
|
} catch (e) {
|
|
877
1049
|
console.warn('Could not set initial file context:', e);
|
|
878
1050
|
}
|
|
879
1051
|
|
|
1052
|
+
// Create context panel
|
|
1053
|
+
contextPanelComponent = createContextPanel({
|
|
1054
|
+
container: contextPanelContainer,
|
|
1055
|
+
orchestratorClient,
|
|
1056
|
+
shellState,
|
|
1057
|
+
getCurrentDocument: () => currentDocName,
|
|
1058
|
+
getEditor: () => editor,
|
|
1059
|
+
getAiContext: mrmd.default.ai?.getAiContext,
|
|
1060
|
+
onOpenRaw: async (contextPath) => {
|
|
1061
|
+
const rawDoc = contextPath.replace(/\.md$/, '');
|
|
1062
|
+
await switchDocument(rawDoc);
|
|
1063
|
+
},
|
|
1064
|
+
});
|
|
1065
|
+
contextPanelComponent.setDocument(currentDocName);
|
|
1066
|
+
contextPanelComponent.setEditor(editor);
|
|
1067
|
+
contextPanelComponent.refresh().catch((e) => {
|
|
1068
|
+
console.warn('[Studio] Failed to initialize context panel:', e);
|
|
1069
|
+
});
|
|
1070
|
+
|
|
880
1071
|
// Create studio object
|
|
881
1072
|
const studio = {
|
|
882
1073
|
/** Current editor instance (may change on file switch) */
|
|
@@ -915,6 +1106,29 @@ export async function createStudio(target, options = {}) {
|
|
|
915
1106
|
await switchDocument(docName);
|
|
916
1107
|
},
|
|
917
1108
|
|
|
1109
|
+
/**
|
|
1110
|
+
* Import a linked table into the current document.
|
|
1111
|
+
* If no source path is provided, opens the file picker flow.
|
|
1112
|
+
*
|
|
1113
|
+
* @param {string} [sourceFilePath]
|
|
1114
|
+
*/
|
|
1115
|
+
async importLinkedTable(sourceFilePath) {
|
|
1116
|
+
if (sourceFilePath) {
|
|
1117
|
+
const file = shellState.get('file');
|
|
1118
|
+
const projectRoot = shellState.get('projectRoot');
|
|
1119
|
+
if (!editor || !file?.path || !projectRoot) {
|
|
1120
|
+
throw new Error('Linked table import requires an open project document');
|
|
1121
|
+
}
|
|
1122
|
+
return editor.importLinkedTableFromHost(sourceFilePath, {
|
|
1123
|
+
projectRoot,
|
|
1124
|
+
documentPath: file.path,
|
|
1125
|
+
cacheFormat: 'csv',
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
return handlers.onImportLinkedTable();
|
|
1130
|
+
},
|
|
1131
|
+
|
|
918
1132
|
/**
|
|
919
1133
|
* Save current file to a new location
|
|
920
1134
|
* @param {string} targetPath - Target path
|
|
@@ -995,6 +1209,7 @@ export async function createStudio(target, options = {}) {
|
|
|
995
1209
|
|
|
996
1210
|
// Destroy status bar
|
|
997
1211
|
statusBarComponent?.destroy();
|
|
1212
|
+
contextPanelComponent?.destroy();
|
|
998
1213
|
|
|
999
1214
|
// Destroy current editor
|
|
1000
1215
|
if (editor) {
|
|
@@ -218,6 +218,51 @@ export class OrchestratorClient {
|
|
|
218
218
|
return this._fetch(`/api/browse${query ? '?' + query : ''}`);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
// ===========================================================================
|
|
222
|
+
// Machine Catalog (multi-machine sync)
|
|
223
|
+
// ===========================================================================
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get catalog of files across all connected machines.
|
|
227
|
+
* @param {Object} [options]
|
|
228
|
+
* @param {string} [options.project] - Filter to a specific project
|
|
229
|
+
* @returns {Promise<{userId: string, machines: Array, cloudOnlyProjects?: string[]}>}
|
|
230
|
+
*/
|
|
231
|
+
async getCatalog(options = {}) {
|
|
232
|
+
const params = new URLSearchParams();
|
|
233
|
+
if (options.project) params.set('project', options.project);
|
|
234
|
+
const query = params.toString();
|
|
235
|
+
return this._fetch(`/api/catalog${query ? '?' + query : ''}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get list of connected machines.
|
|
240
|
+
* @returns {Promise<{userId: string, machines: Array}>}
|
|
241
|
+
*/
|
|
242
|
+
async getMachines() {
|
|
243
|
+
return this._fetch('/api/machines');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get currently active runtime machine.
|
|
248
|
+
* @returns {Promise<{activeMachineId: string|null, provider: Object|null}>}
|
|
249
|
+
*/
|
|
250
|
+
async getActiveMachine() {
|
|
251
|
+
return this._fetch('/api/machines/active');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Set active runtime machine.
|
|
256
|
+
* @param {string|null} machineId
|
|
257
|
+
* @returns {Promise<{ok: boolean, activeMachineId: string|null, provider: Object|null}>}
|
|
258
|
+
*/
|
|
259
|
+
async setActiveMachine(machineId) {
|
|
260
|
+
return this._fetch('/api/machines/active', {
|
|
261
|
+
method: 'POST',
|
|
262
|
+
body: JSON.stringify({ machineId: machineId ?? null }),
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
221
266
|
// ===========================================================================
|
|
222
267
|
// Environment Management
|
|
223
268
|
// ===========================================================================
|
|
@@ -371,6 +416,75 @@ export class OrchestratorClient {
|
|
|
371
416
|
return this.listRuntimeAttachments();
|
|
372
417
|
}
|
|
373
418
|
|
|
419
|
+
// ===========================================================================
|
|
420
|
+
// Context Management
|
|
421
|
+
// ===========================================================================
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Resolve markdown-managed AI context for a document.
|
|
425
|
+
* @param {Object} request
|
|
426
|
+
* @param {string} request.doc
|
|
427
|
+
* @param {string} [request.content]
|
|
428
|
+
* @param {number} [request.cursorPos]
|
|
429
|
+
* @param {{from: number, to: number}} [request.selection]
|
|
430
|
+
* @param {string[]} [request.codeSymbols]
|
|
431
|
+
* @param {boolean} [request.ensureExists=false]
|
|
432
|
+
*/
|
|
433
|
+
async resolveContext(request) {
|
|
434
|
+
return this._fetch('/api/context/resolve', {
|
|
435
|
+
method: 'POST',
|
|
436
|
+
body: JSON.stringify(request),
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Get context markdown for a document.
|
|
442
|
+
* @param {string} doc
|
|
443
|
+
*/
|
|
444
|
+
async getContext(doc) {
|
|
445
|
+
return this._fetch(`/api/context/${encodeURIComponent(doc)}`);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Save context markdown for a document.
|
|
450
|
+
* @param {string} doc
|
|
451
|
+
* @param {string} content
|
|
452
|
+
*/
|
|
453
|
+
async saveContext(doc, content) {
|
|
454
|
+
return this._fetch(`/api/context/${encodeURIComponent(doc)}`, {
|
|
455
|
+
method: 'PUT',
|
|
456
|
+
body: JSON.stringify({ content }),
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Initialize context markdown for a document if missing.
|
|
462
|
+
* @param {string} doc
|
|
463
|
+
*/
|
|
464
|
+
async initContext(doc) {
|
|
465
|
+
return this._fetch(`/api/context/init/${encodeURIComponent(doc)}`, {
|
|
466
|
+
method: 'POST',
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Get project default context markdown.
|
|
472
|
+
*/
|
|
473
|
+
async getDefaultContext() {
|
|
474
|
+
return this._fetch('/api/context');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Save project default context markdown.
|
|
479
|
+
* @param {string} content
|
|
480
|
+
*/
|
|
481
|
+
async saveDefaultContext(content) {
|
|
482
|
+
return this._fetch('/api/context', {
|
|
483
|
+
method: 'PUT',
|
|
484
|
+
body: JSON.stringify({ content }),
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
374
488
|
// ===========================================================================
|
|
375
489
|
// Project & Runtime Management
|
|
376
490
|
// ===========================================================================
|
package/src/shell/styles.js
CHANGED
|
@@ -160,6 +160,24 @@ export const statusBarStyles = `
|
|
|
160
160
|
margin-left: 4px;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
/* Active machine pill (simple status bar mode) */
|
|
164
|
+
.mrmd-statusbar__machine-pill {
|
|
165
|
+
display: inline-flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
padding: 1px 6px;
|
|
168
|
+
border-radius: 10px;
|
|
169
|
+
border: 1px solid var(--mrmd-border, #333);
|
|
170
|
+
background: var(--mrmd-hover-bg, rgba(255,255,255,0.06));
|
|
171
|
+
color: var(--mrmd-fg, #ccc);
|
|
172
|
+
font-size: 10px;
|
|
173
|
+
line-height: 1.2;
|
|
174
|
+
cursor: pointer;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.mrmd-statusbar__machine-pill:hover {
|
|
178
|
+
border-color: var(--mrmd-accent, #58a6ff);
|
|
179
|
+
}
|
|
180
|
+
|
|
163
181
|
/* Unified files segment - takes more space */
|
|
164
182
|
.mrmd-statusbar__segment--files {
|
|
165
183
|
min-width: 120px;
|
|
@@ -505,6 +523,50 @@ export const filePickerStyles = `
|
|
|
505
523
|
min-height: 300px;
|
|
506
524
|
}
|
|
507
525
|
|
|
526
|
+
/* Machine tab bar */
|
|
527
|
+
.mrmd-filepicker__machines {
|
|
528
|
+
display: flex;
|
|
529
|
+
gap: 4px;
|
|
530
|
+
padding: 6px 0;
|
|
531
|
+
margin-bottom: 4px;
|
|
532
|
+
border-bottom: 1px solid var(--mrmd-border, #333);
|
|
533
|
+
overflow-x: auto;
|
|
534
|
+
white-space: nowrap;
|
|
535
|
+
-webkit-overflow-scrolling: touch;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.mrmd-filepicker__machine-tab {
|
|
539
|
+
display: inline-flex;
|
|
540
|
+
align-items: center;
|
|
541
|
+
gap: 4px;
|
|
542
|
+
padding: 4px 10px;
|
|
543
|
+
border-radius: 12px;
|
|
544
|
+
border: 1px solid var(--mrmd-border, #333);
|
|
545
|
+
background: transparent;
|
|
546
|
+
color: var(--mrmd-fg-muted, #888);
|
|
547
|
+
font-size: var(--mrmd-ui-font-size-sm, 11px);
|
|
548
|
+
font-family: inherit;
|
|
549
|
+
cursor: pointer;
|
|
550
|
+
white-space: nowrap;
|
|
551
|
+
transition: background 0.1s, color 0.1s, border-color 0.1s;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.mrmd-filepicker__machine-tab:hover {
|
|
555
|
+
background: var(--mrmd-hover-bg, rgba(255, 255, 255, 0.08));
|
|
556
|
+
color: var(--mrmd-fg, #ccc);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.mrmd-filepicker__machine-tab--active {
|
|
560
|
+
background: var(--mrmd-selection-bg, rgba(0, 122, 204, 0.2));
|
|
561
|
+
color: var(--mrmd-fg, #ccc);
|
|
562
|
+
border-color: var(--mrmd-accent, #58a6ff);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.mrmd-filepicker__machine-tab--offline {
|
|
566
|
+
opacity: 0.7;
|
|
567
|
+
border-style: dashed;
|
|
568
|
+
}
|
|
569
|
+
|
|
508
570
|
/* Path bar */
|
|
509
571
|
.mrmd-filepicker__path {
|
|
510
572
|
display: flex;
|