mrmd-server 0.1.0 → 0.1.1

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.
@@ -130,6 +130,12 @@
130
130
 
131
131
  getAi: () => GET('/api/system/ai'),
132
132
 
133
+ // System info and uv management
134
+ system: {
135
+ info: () => GET('/api/system/info'),
136
+ ensureUv: () => POST('/api/system/ensure-uv', {}),
137
+ },
138
+
133
139
  // ========================================================================
134
140
  // Shell (stubs for browser)
135
141
  // ========================================================================
@@ -191,6 +197,9 @@
191
197
  // Python management
192
198
  // ========================================================================
193
199
 
200
+ createVenv: (venvPath) =>
201
+ POST('/api/system/create-venv', { venvPath }),
202
+
194
203
  installMrmdPython: (venvPath) =>
195
204
  POST('/api/system/install-mrmd-python', { venvPath }),
196
205
 
@@ -212,11 +221,24 @@
212
221
  // ========================================================================
213
222
 
214
223
  openFile: async (filePath) => {
215
- // This was used to open a file and get session info
216
- // We'll get project info and session info separately
224
+ // Get project info and session info
217
225
  const project = await window.electronAPI.project.get(filePath);
218
226
  const session = await window.electronAPI.session.forDocument(filePath);
219
- return { project, session };
227
+
228
+ // Extract filename without extension for docName
229
+ const fileName = filePath.split('/').pop();
230
+ const docName = fileName.replace(/\.md$/, '');
231
+
232
+ // Use syncPort from project response (dynamically assigned per-project)
233
+ const syncPort = project?.syncPort || 4444;
234
+
235
+ return {
236
+ success: true,
237
+ syncPort,
238
+ docName,
239
+ projectDir: project?.root || filePath.split('/').slice(0, -1).join('/'),
240
+ pythonPort: session?.pythonPort || null,
241
+ };
220
242
  },
221
243
 
222
244
  // ========================================================================
@@ -280,6 +302,144 @@
280
302
  forDocument: (documentPath) => POST('/api/bash/for-document', { documentPath }),
281
303
  },
282
304
 
305
+ // ========================================================================
306
+ // JULIA SESSION SERVICE
307
+ // ========================================================================
308
+
309
+ julia: {
310
+ list: () => GET('/api/julia'),
311
+
312
+ start: (config) => POST('/api/julia', { config }),
313
+
314
+ stop: (sessionName) => DELETE(`/api/julia/${encodeURIComponent(sessionName)}`),
315
+
316
+ restart: (sessionName) => POST(`/api/julia/${encodeURIComponent(sessionName)}/restart`, {}),
317
+
318
+ forDocument: (documentPath) => POST('/api/julia/for-document', { documentPath }),
319
+
320
+ isAvailable: () => GET('/api/julia/available').then(r => r.available),
321
+ },
322
+
323
+ // ========================================================================
324
+ // PTY SESSION SERVICE (for ```term blocks)
325
+ // ========================================================================
326
+
327
+ pty: {
328
+ list: () => GET('/api/pty'),
329
+
330
+ start: (config) => POST('/api/pty', { config }),
331
+
332
+ stop: (sessionName) => DELETE(`/api/pty/${encodeURIComponent(sessionName)}`),
333
+
334
+ restart: (sessionName) => POST(`/api/pty/${encodeURIComponent(sessionName)}/restart`, {}),
335
+
336
+ forDocument: (documentPath) => POST('/api/pty/for-document', { documentPath }),
337
+ },
338
+
339
+ // ========================================================================
340
+ // NOTEBOOK (JUPYTER) SERVICE
341
+ // ========================================================================
342
+
343
+ notebook: {
344
+ convert: (ipynbPath) => POST('/api/notebook/convert', { ipynbPath }),
345
+
346
+ startSync: (ipynbPath) => POST('/api/notebook/start-sync', { ipynbPath }),
347
+
348
+ stopSync: (ipynbPath) => POST('/api/notebook/stop-sync', { ipynbPath }),
349
+ },
350
+
351
+ // ========================================================================
352
+ // R SESSION SERVICE
353
+ // ========================================================================
354
+
355
+ r: {
356
+ list: () => GET('/api/r'),
357
+
358
+ start: (config) => POST('/api/r', { config }),
359
+
360
+ stop: (sessionName) => DELETE(`/api/r/${encodeURIComponent(sessionName)}`),
361
+
362
+ restart: (sessionName) => POST(`/api/r/${encodeURIComponent(sessionName)}/restart`, {}),
363
+
364
+ forDocument: (documentPath) => POST('/api/r/for-document', { documentPath }),
365
+
366
+ isAvailable: () => GET('/api/r/available').then(r => r.available),
367
+ },
368
+
369
+ // ========================================================================
370
+ // SETTINGS SERVICE
371
+ // ========================================================================
372
+
373
+ settings: {
374
+ getAll: () => GET('/api/settings'),
375
+
376
+ get: (key, defaultValue) =>
377
+ GET(`/api/settings/key?path=${encodeURIComponent(key)}${defaultValue !== undefined ? `&default=${encodeURIComponent(defaultValue)}` : ''}`).then(r => r.value),
378
+
379
+ set: (key, value) =>
380
+ POST('/api/settings/key', { key, value }).then(r => r.success),
381
+
382
+ update: (updates) =>
383
+ POST('/api/settings/update', { updates }).then(r => r.success),
384
+
385
+ reset: () =>
386
+ POST('/api/settings/reset', {}).then(r => r.success),
387
+
388
+ getApiKeys: (masked = true) =>
389
+ GET(`/api/settings/api-keys?masked=${masked}`),
390
+
391
+ setApiKey: (provider, key) =>
392
+ POST('/api/settings/api-key', { provider, key }).then(r => r.success),
393
+
394
+ getApiKey: (provider) =>
395
+ GET(`/api/settings/api-key/${encodeURIComponent(provider)}`).then(r => r.key),
396
+
397
+ hasApiKey: (provider) =>
398
+ GET(`/api/settings/api-key/${encodeURIComponent(provider)}/exists`).then(r => r.hasKey),
399
+
400
+ getApiProviders: () =>
401
+ GET('/api/settings/api-providers'),
402
+
403
+ getQualityLevels: () =>
404
+ GET('/api/settings/quality-levels'),
405
+
406
+ setQualityLevelModel: (level, model) =>
407
+ POST(`/api/settings/quality-level/${level}/model`, { model }).then(r => r.success),
408
+
409
+ getCustomSections: () =>
410
+ GET('/api/settings/custom-sections'),
411
+
412
+ addCustomSection: (name) =>
413
+ POST('/api/settings/custom-section', { name }),
414
+
415
+ removeCustomSection: (sectionId) =>
416
+ DELETE(`/api/settings/custom-section/${encodeURIComponent(sectionId)}`).then(r => r.success),
417
+
418
+ addCustomCommand: (sectionId, command) =>
419
+ POST('/api/settings/custom-command', { sectionId, command }),
420
+
421
+ updateCustomCommand: (sectionId, commandId, updates) =>
422
+ apiCall('PUT', '/api/settings/custom-command', { sectionId, commandId, updates }).then(r => r.success),
423
+
424
+ removeCustomCommand: (sectionId, commandId) =>
425
+ apiCall('DELETE', '/api/settings/custom-command', { sectionId, commandId }).then(r => r.success),
426
+
427
+ getAllCustomCommands: () =>
428
+ GET('/api/settings/custom-commands'),
429
+
430
+ getDefaults: () =>
431
+ GET('/api/settings/defaults'),
432
+
433
+ setDefaults: (defaults) =>
434
+ POST('/api/settings/defaults', defaults).then(r => r.success),
435
+
436
+ export: (includeKeys = false) =>
437
+ GET(`/api/settings/export?includeKeys=${includeKeys}`).then(r => r.json),
438
+
439
+ import: (json, mergeKeys = false) =>
440
+ POST('/api/settings/import', { json, mergeKeys }).then(r => r.success),
441
+ },
442
+
283
443
  // ========================================================================
284
444
  // FILE SERVICE
285
445
  // ========================================================================
@@ -353,6 +513,15 @@
353
513
  // Remove existing handlers to prevent duplicates
354
514
  eventHandlers['sync-server-died'] = [callback];
355
515
  },
516
+
517
+ /**
518
+ * Register callback for OS "open with" events.
519
+ * In browser mode, this will never be called (no OS integration).
520
+ */
521
+ onOpenWithFile: (callback) => {
522
+ // No-op in browser mode - OS file associations don't exist
523
+ // Could potentially be triggered via URL parameters in the future
524
+ },
356
525
  };
357
526
 
358
527
  // ==========================================================================
package/static/index.html CHANGED
@@ -4,6 +4,7 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>mrmd</title>
7
+ <link rel="icon" type="image/png" href="/favicon.png">
7
8
 
8
9
  <!-- Load the HTTP shim BEFORE anything else -->
9
10
  <script src="/http-shim.js"></script>