mrmd-editor 0.6.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/ctrl-k-modal.js +190 -14
- package/src/document-languages.js +105 -0
- package/src/execution.js +4 -10
- package/src/frontmatter-updater.js +224 -0
- package/src/index.js +127 -86
- package/src/markdown/renderer.js +52 -3
- package/src/markdown/styles.js +126 -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 +54 -0
- 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
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontmatter Session Updater
|
|
3
|
+
*
|
|
4
|
+
* Programmatically updates session/runtime configuration in a document's
|
|
5
|
+
* YAML frontmatter. Used by the runtimes panel UI to persist configuration
|
|
6
|
+
* changes back to the document.
|
|
7
|
+
*
|
|
8
|
+
* @module frontmatter-updater
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import yaml from 'yaml';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} SessionConfig
|
|
15
|
+
* @property {string} [name] - Session name
|
|
16
|
+
* @property {string} [venv] - Virtual environment path (Python)
|
|
17
|
+
* @property {string} [cwd] - Working directory
|
|
18
|
+
* @property {boolean} [auto_start] - Auto-start on project open
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse existing frontmatter from document content.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} content - Full document content
|
|
25
|
+
* @returns {{ exists: boolean, yaml: object|null, range: {start: number, end: number}|null, raw: string|null }}
|
|
26
|
+
*/
|
|
27
|
+
export function parseFrontmatter(content) {
|
|
28
|
+
if (!content.startsWith('---')) {
|
|
29
|
+
return { exists: false, yaml: null, range: null, raw: null };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const endIdx = content.indexOf('\n---', 3);
|
|
33
|
+
if (endIdx === -1) {
|
|
34
|
+
return { exists: false, yaml: null, range: null, raw: null };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const rawYaml = content.slice(4, endIdx); // Skip opening ---\n
|
|
38
|
+
const endOfClosing = endIdx + 4; // Include \n---
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const parsed = yaml.parse(rawYaml) || {};
|
|
42
|
+
return {
|
|
43
|
+
exists: true,
|
|
44
|
+
yaml: parsed,
|
|
45
|
+
range: { start: 0, end: endOfClosing },
|
|
46
|
+
raw: rawYaml,
|
|
47
|
+
};
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.warn('[frontmatter-updater] Failed to parse YAML:', e.message);
|
|
50
|
+
return { exists: true, yaml: null, range: { start: 0, end: endOfClosing }, raw: rawYaml };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Build frontmatter YAML string from an object.
|
|
56
|
+
*
|
|
57
|
+
* @param {object} data - Frontmatter data
|
|
58
|
+
* @returns {string} Complete frontmatter block including --- delimiters
|
|
59
|
+
*/
|
|
60
|
+
function buildFrontmatter(data) {
|
|
61
|
+
// Remove empty/null values
|
|
62
|
+
const cleaned = cleanObject(data);
|
|
63
|
+
|
|
64
|
+
if (!cleaned || Object.keys(cleaned).length === 0) {
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const yamlStr = yaml.stringify(cleaned, {
|
|
69
|
+
indent: 2,
|
|
70
|
+
lineWidth: 0, // Don't wrap lines
|
|
71
|
+
}).trimEnd();
|
|
72
|
+
|
|
73
|
+
return `---\n${yamlStr}\n---`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Recursively remove null, undefined, and empty object values.
|
|
78
|
+
* @param {any} obj
|
|
79
|
+
* @returns {any}
|
|
80
|
+
*/
|
|
81
|
+
function cleanObject(obj) {
|
|
82
|
+
if (obj === null || obj === undefined) return undefined;
|
|
83
|
+
if (typeof obj !== 'object' || Array.isArray(obj)) return obj;
|
|
84
|
+
|
|
85
|
+
const result = {};
|
|
86
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
87
|
+
if (value === null || value === undefined) continue;
|
|
88
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
89
|
+
const cleaned = cleanObject(value);
|
|
90
|
+
if (cleaned && Object.keys(cleaned).length > 0) {
|
|
91
|
+
result[key] = cleaned;
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
result[key] = value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Update session configuration in document frontmatter.
|
|
102
|
+
*
|
|
103
|
+
* If frontmatter doesn't exist, creates it.
|
|
104
|
+
* If values match project defaults, omits them (keeps frontmatter clean).
|
|
105
|
+
*
|
|
106
|
+
* @param {import('@codemirror/view').EditorView} view - CodeMirror editor view
|
|
107
|
+
* @param {string} language - Runtime language (e.g. 'python', 'bash', 'r', 'julia')
|
|
108
|
+
* @param {SessionConfig} config - Session configuration to set
|
|
109
|
+
* @param {SessionConfig} [projectDefaults] - Defaults from mrmd.md to diff against
|
|
110
|
+
*/
|
|
111
|
+
export function updateFrontmatterSession(view, language, config, projectDefaults = {}) {
|
|
112
|
+
const doc = view.state.doc;
|
|
113
|
+
const content = doc.toString();
|
|
114
|
+
const fm = parseFrontmatter(content);
|
|
115
|
+
|
|
116
|
+
// Start with existing frontmatter data or empty object
|
|
117
|
+
const data = fm.yaml ? { ...fm.yaml } : {};
|
|
118
|
+
|
|
119
|
+
// Build session config, omitting values that match project defaults
|
|
120
|
+
const sessionConfig = {};
|
|
121
|
+
|
|
122
|
+
if (config.name !== undefined && config.name !== (projectDefaults.name || 'default')) {
|
|
123
|
+
sessionConfig.name = config.name;
|
|
124
|
+
}
|
|
125
|
+
if (config.venv !== undefined && config.venv !== (projectDefaults.venv || '.venv')) {
|
|
126
|
+
sessionConfig.venv = config.venv;
|
|
127
|
+
}
|
|
128
|
+
if (config.cwd !== undefined && config.cwd !== (projectDefaults.cwd || '.')) {
|
|
129
|
+
sessionConfig.cwd = config.cwd;
|
|
130
|
+
}
|
|
131
|
+
if (config.auto_start !== undefined && config.auto_start !== true) {
|
|
132
|
+
sessionConfig.auto_start = config.auto_start;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Update the session section
|
|
136
|
+
if (Object.keys(sessionConfig).length > 0) {
|
|
137
|
+
if (!data.session) data.session = {};
|
|
138
|
+
data.session[language] = {
|
|
139
|
+
...(data.session[language] || {}),
|
|
140
|
+
...sessionConfig,
|
|
141
|
+
};
|
|
142
|
+
} else {
|
|
143
|
+
// All values match defaults — remove the language key if it exists
|
|
144
|
+
if (data.session && data.session[language]) {
|
|
145
|
+
delete data.session[language];
|
|
146
|
+
if (Object.keys(data.session).length === 0) {
|
|
147
|
+
delete data.session;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Build new frontmatter string
|
|
153
|
+
const newFrontmatter = buildFrontmatter(data);
|
|
154
|
+
|
|
155
|
+
// Apply the change to the editor
|
|
156
|
+
if (fm.exists && fm.range) {
|
|
157
|
+
// Replace existing frontmatter
|
|
158
|
+
if (newFrontmatter) {
|
|
159
|
+
view.dispatch({
|
|
160
|
+
changes: { from: fm.range.start, to: fm.range.end, insert: newFrontmatter },
|
|
161
|
+
});
|
|
162
|
+
} else {
|
|
163
|
+
// Remove frontmatter entirely (and trailing newline if present)
|
|
164
|
+
let removeEnd = fm.range.end;
|
|
165
|
+
if (content[removeEnd] === '\n') removeEnd++;
|
|
166
|
+
view.dispatch({
|
|
167
|
+
changes: { from: fm.range.start, to: removeEnd, insert: '' },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
} else if (newFrontmatter) {
|
|
171
|
+
// Insert new frontmatter at the top
|
|
172
|
+
view.dispatch({
|
|
173
|
+
changes: { from: 0, to: 0, insert: newFrontmatter + '\n\n' },
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Read current session configuration from document frontmatter.
|
|
180
|
+
*
|
|
181
|
+
* @param {string} content - Document content
|
|
182
|
+
* @param {string} language - Runtime language
|
|
183
|
+
* @returns {SessionConfig} Current session config (may be empty object if using defaults)
|
|
184
|
+
*/
|
|
185
|
+
export function readFrontmatterSession(content, language) {
|
|
186
|
+
const fm = parseFrontmatter(content);
|
|
187
|
+
if (!fm.yaml) return {};
|
|
188
|
+
|
|
189
|
+
// Check verbose syntax: session.python.venv
|
|
190
|
+
if (fm.yaml.session && fm.yaml.session[language]) {
|
|
191
|
+
return { ...fm.yaml.session[language] };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Check minimal syntax: python: ".venv"
|
|
195
|
+
if (fm.yaml[language]) {
|
|
196
|
+
const value = fm.yaml[language];
|
|
197
|
+
if (typeof value === 'string') {
|
|
198
|
+
if (language === 'python') return { venv: value };
|
|
199
|
+
return { cwd: value };
|
|
200
|
+
}
|
|
201
|
+
if (typeof value === 'object') return { ...value };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get the effective session configuration for a document,
|
|
209
|
+
* merging project defaults with document-level overrides.
|
|
210
|
+
*
|
|
211
|
+
* @param {string} content - Document content
|
|
212
|
+
* @param {string} language - Runtime language
|
|
213
|
+
* @param {SessionConfig} projectDefaults - Defaults from mrmd.md
|
|
214
|
+
* @returns {SessionConfig} Effective configuration
|
|
215
|
+
*/
|
|
216
|
+
export function getEffectiveSessionConfig(content, language, projectDefaults = {}) {
|
|
217
|
+
const docConfig = readFrontmatterSession(content, language);
|
|
218
|
+
return {
|
|
219
|
+
name: docConfig.name || projectDefaults.name || 'default',
|
|
220
|
+
venv: docConfig.venv || projectDefaults.venv || '.venv',
|
|
221
|
+
cwd: docConfig.cwd || projectDefaults.cwd || '.',
|
|
222
|
+
auto_start: docConfig.auto_start ?? projectDefaults.auto_start ?? true,
|
|
223
|
+
};
|
|
224
|
+
}
|