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
package/src/shell/state.js
CHANGED
|
@@ -69,12 +69,12 @@ function getInitialState() {
|
|
|
69
69
|
runtimes: {
|
|
70
70
|
// Legacy: single Python runtime info (for backward compat)
|
|
71
71
|
python: null,
|
|
72
|
-
//
|
|
72
|
+
// Runtime registry
|
|
73
73
|
sessions: {
|
|
74
74
|
// 'shared': { id, url, status, venv, cwd, ... }
|
|
75
75
|
// 'python-8001': { id, url, status, venv, cwd, dedicated: true, port: 8001 }
|
|
76
76
|
},
|
|
77
|
-
// Document →
|
|
77
|
+
// Document → runtime attachment
|
|
78
78
|
attachments: {
|
|
79
79
|
// 'my-notebook': 'shared'
|
|
80
80
|
// 'data-analysis': 'python-8001'
|
|
@@ -464,55 +464,55 @@ export class ShellStateManager {
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
// ===========================================================================
|
|
467
|
-
// Runtime
|
|
467
|
+
// Runtime Attachment Management
|
|
468
468
|
// ===========================================================================
|
|
469
469
|
|
|
470
470
|
/**
|
|
471
|
-
* Get the
|
|
471
|
+
* Get the runtime attached to a document
|
|
472
472
|
* @param {string} docName - Document name
|
|
473
|
-
* @returns {string}
|
|
473
|
+
* @returns {string} Runtime ID (defaults to 'shared')
|
|
474
474
|
*/
|
|
475
|
-
|
|
475
|
+
getDocumentRuntime(docName) {
|
|
476
476
|
return this.get(`runtimes.attachments.${docName}`) || 'shared';
|
|
477
477
|
}
|
|
478
478
|
|
|
479
479
|
/**
|
|
480
|
-
* Get
|
|
481
|
-
* @param {string}
|
|
480
|
+
* Get runtime info
|
|
481
|
+
* @param {string} runtimeId - Runtime ID
|
|
482
482
|
* @returns {Object|null}
|
|
483
483
|
*/
|
|
484
|
-
|
|
485
|
-
return this.get(`runtimes.sessions.${
|
|
484
|
+
getRuntime(runtimeId) {
|
|
485
|
+
return this.get(`runtimes.sessions.${runtimeId}`) || null;
|
|
486
486
|
}
|
|
487
487
|
|
|
488
488
|
/**
|
|
489
|
-
* Get all available
|
|
489
|
+
* Get all available runtimes
|
|
490
490
|
* @returns {Array<{id: string, info: Object}>}
|
|
491
491
|
*/
|
|
492
|
-
|
|
493
|
-
const
|
|
494
|
-
return Object.entries(
|
|
492
|
+
getRuntimes() {
|
|
493
|
+
const runtimes = this.get('runtimes.sessions') || {};
|
|
494
|
+
return Object.entries(runtimes).map(([id, info]) => ({ id, info }));
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
/**
|
|
498
|
-
* Attach a document to a
|
|
498
|
+
* Attach a document to a runtime
|
|
499
499
|
* @param {string} docName - Document name
|
|
500
|
-
* @param {string}
|
|
500
|
+
* @param {string} runtimeId - Runtime ID to attach to
|
|
501
501
|
*/
|
|
502
|
-
attachDocument(docName,
|
|
503
|
-
this._set(`runtimes.attachments.${docName}`,
|
|
502
|
+
attachDocument(docName, runtimeId) {
|
|
503
|
+
this._set(`runtimes.attachments.${docName}`, runtimeId);
|
|
504
504
|
}
|
|
505
505
|
|
|
506
506
|
/**
|
|
507
|
-
* Register
|
|
508
|
-
* @param {string}
|
|
509
|
-
* @param {Object} info -
|
|
507
|
+
* Register runtime metadata
|
|
508
|
+
* @param {string} runtimeId - Runtime ID
|
|
509
|
+
* @param {Object} info - Runtime info (url, status, venv, cwd, etc.)
|
|
510
510
|
*/
|
|
511
|
-
|
|
512
|
-
this._set(`runtimes.sessions.${
|
|
511
|
+
registerRuntime(runtimeId, info) {
|
|
512
|
+
this._set(`runtimes.sessions.${runtimeId}`, info);
|
|
513
513
|
|
|
514
|
-
// Also update legacy python state if this is the shared
|
|
515
|
-
if (
|
|
514
|
+
// Also update legacy python state if this is the shared runtime
|
|
515
|
+
if (runtimeId === 'shared' && info.language === 'python') {
|
|
516
516
|
this._set('runtimes.python', {
|
|
517
517
|
language: 'python',
|
|
518
518
|
version: info.version,
|
|
@@ -527,20 +527,20 @@ export class ShellStateManager {
|
|
|
527
527
|
}
|
|
528
528
|
|
|
529
529
|
/**
|
|
530
|
-
* Create
|
|
531
|
-
* @param {string} docName - Document to attach
|
|
530
|
+
* Create and attach a runtime
|
|
531
|
+
* @param {string} docName - Document to attach runtime to
|
|
532
532
|
* @param {'shared'|'dedicated'} mode - Runtime mode
|
|
533
533
|
* @param {string} [venv] - Path to virtual environment (for dedicated runtimes)
|
|
534
|
-
* @returns {Promise<Object>}
|
|
534
|
+
* @returns {Promise<Object>} Runtime info
|
|
535
535
|
*/
|
|
536
|
-
async
|
|
536
|
+
async createRuntime(docName, mode = 'dedicated', venv = null) {
|
|
537
537
|
try {
|
|
538
|
-
const result = await this._client.
|
|
538
|
+
const result = await this._client.createRuntimeAttachment(docName, mode, venv);
|
|
539
539
|
|
|
540
|
-
const
|
|
540
|
+
const runtimeId = result.id || (mode === 'dedicated' ? `python-${result.runtimes?.python?.port || Date.now()}` : 'shared');
|
|
541
541
|
|
|
542
|
-
const
|
|
543
|
-
id:
|
|
542
|
+
const runtimeInfo = {
|
|
543
|
+
id: runtimeId,
|
|
544
544
|
url: result.runtimes?.python?.url || result.sync,
|
|
545
545
|
status: 'ready',
|
|
546
546
|
dedicated: mode === 'dedicated',
|
|
@@ -550,15 +550,15 @@ export class ShellStateManager {
|
|
|
550
550
|
docName,
|
|
551
551
|
};
|
|
552
552
|
|
|
553
|
-
// Register
|
|
554
|
-
this.
|
|
553
|
+
// Register runtime
|
|
554
|
+
this.registerRuntime(runtimeId, runtimeInfo);
|
|
555
555
|
|
|
556
|
-
// Attach
|
|
557
|
-
this.attachDocument(docName,
|
|
556
|
+
// Attach document to runtime
|
|
557
|
+
this.attachDocument(docName, runtimeId);
|
|
558
558
|
|
|
559
|
-
return
|
|
559
|
+
return runtimeInfo;
|
|
560
560
|
} catch (error) {
|
|
561
|
-
console.error('Failed to create
|
|
561
|
+
console.error('Failed to create runtime:', error);
|
|
562
562
|
throw error;
|
|
563
563
|
}
|
|
564
564
|
}
|
|
@@ -569,9 +569,30 @@ export class ShellStateManager {
|
|
|
569
569
|
* @returns {string|null} Runtime URL
|
|
570
570
|
*/
|
|
571
571
|
getRuntimeUrl(docName) {
|
|
572
|
-
const
|
|
573
|
-
const
|
|
574
|
-
return
|
|
572
|
+
const runtimeId = this.getDocumentRuntime(docName);
|
|
573
|
+
const runtime = this.getRuntime(runtimeId);
|
|
574
|
+
return runtime?.url || null;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Legacy aliases
|
|
578
|
+
getDocumentSession(docName) {
|
|
579
|
+
return this.getDocumentRuntime(docName);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
getSession(sessionId) {
|
|
583
|
+
return this.getRuntime(sessionId);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
getSessions() {
|
|
587
|
+
return this.getRuntimes();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
registerSession(sessionId, info) {
|
|
591
|
+
this.registerRuntime(sessionId, info);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
async createSession(docName, mode = 'dedicated', venv = null) {
|
|
595
|
+
return this.createRuntime(docName, mode, venv);
|
|
575
596
|
}
|
|
576
597
|
|
|
577
598
|
// ===========================================================================
|
package/src/shell/styles.js
CHANGED
|
@@ -601,6 +601,271 @@ export const filePickerStyles = `
|
|
|
601
601
|
}
|
|
602
602
|
`;
|
|
603
603
|
|
|
604
|
+
// =============================================================================
|
|
605
|
+
// MOBILE RESPONSIVE STYLES
|
|
606
|
+
// =============================================================================
|
|
607
|
+
|
|
608
|
+
export const mobileStyles = `
|
|
609
|
+
/* ---- Status Bar: mobile-friendly ---- */
|
|
610
|
+
@media (max-width: 768px) {
|
|
611
|
+
.mrmd-statusbar {
|
|
612
|
+
height: auto;
|
|
613
|
+
min-height: 44px;
|
|
614
|
+
padding: 8px 12px;
|
|
615
|
+
gap: 4px;
|
|
616
|
+
font-size: 13px;
|
|
617
|
+
flex-wrap: wrap;
|
|
618
|
+
padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.mrmd-statusbar__segment {
|
|
622
|
+
padding: 8px 12px;
|
|
623
|
+
min-height: 44px;
|
|
624
|
+
font-size: 14px;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.mrmd-statusbar__segment--files {
|
|
628
|
+
min-width: 0;
|
|
629
|
+
flex: 1;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.mrmd-statusbar__icon {
|
|
633
|
+
width: 18px;
|
|
634
|
+
height: 18px;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.mrmd-statusbar__dot {
|
|
638
|
+
width: 10px;
|
|
639
|
+
height: 10px;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.mrmd-statusbar__badge {
|
|
643
|
+
font-size: 10px;
|
|
644
|
+
padding: 2px 6px;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.mrmd-statusbar__separator {
|
|
648
|
+
display: none;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.mrmd-statusbar__spacer {
|
|
652
|
+
display: none;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/* ---- Menus → Bottom sheets on mobile ---- */
|
|
657
|
+
@media (max-width: 768px) {
|
|
658
|
+
.mrmd-menu {
|
|
659
|
+
position: fixed !important;
|
|
660
|
+
top: auto !important;
|
|
661
|
+
left: 0 !important;
|
|
662
|
+
right: 0 !important;
|
|
663
|
+
bottom: 0 !important;
|
|
664
|
+
max-width: 100% !important;
|
|
665
|
+
min-width: 100% !important;
|
|
666
|
+
max-height: 70vh;
|
|
667
|
+
border-radius: 16px 16px 0 0;
|
|
668
|
+
box-shadow: 0 -4px 32px rgba(0, 0, 0, 0.4);
|
|
669
|
+
padding: 8px 0;
|
|
670
|
+
padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
|
|
671
|
+
animation: mrmd-slide-up 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
@keyframes mrmd-slide-up {
|
|
675
|
+
from { transform: translateY(100%); }
|
|
676
|
+
to { transform: translateY(0); }
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/* Drag handle at top of bottom sheet */
|
|
680
|
+
.mrmd-menu::before {
|
|
681
|
+
content: '';
|
|
682
|
+
display: block;
|
|
683
|
+
width: 36px;
|
|
684
|
+
height: 4px;
|
|
685
|
+
background: var(--mrmd-fg-muted, #666);
|
|
686
|
+
border-radius: 2px;
|
|
687
|
+
margin: 4px auto 8px;
|
|
688
|
+
opacity: 0.4;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.mrmd-menu__header {
|
|
692
|
+
padding: 12px 20px 8px;
|
|
693
|
+
font-size: 12px;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.mrmd-menu__item {
|
|
697
|
+
padding: 14px 20px;
|
|
698
|
+
min-height: 48px;
|
|
699
|
+
font-size: 15px;
|
|
700
|
+
gap: 14px;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.mrmd-menu__item-icon {
|
|
704
|
+
width: 22px;
|
|
705
|
+
height: 22px;
|
|
706
|
+
font-size: 18px;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.mrmd-menu__item-shortcut {
|
|
710
|
+
display: none; /* Keyboard shortcuts aren't relevant on touch */
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.mrmd-menu__info {
|
|
714
|
+
padding: 10px 20px;
|
|
715
|
+
font-size: 12px;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.mrmd-menu__divider {
|
|
719
|
+
margin: 6px 16px;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.mrmd-menu__file {
|
|
723
|
+
padding: 12px 20px;
|
|
724
|
+
min-height: 48px;
|
|
725
|
+
font-size: 15px;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/* ---- Dialogs: full-screen on mobile ---- */
|
|
730
|
+
@media (max-width: 768px) {
|
|
731
|
+
.mrmd-dialog-overlay {
|
|
732
|
+
padding: 0;
|
|
733
|
+
align-items: flex-end;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.mrmd-dialog {
|
|
737
|
+
min-width: 100%;
|
|
738
|
+
max-width: 100%;
|
|
739
|
+
max-height: 90vh;
|
|
740
|
+
border-radius: 16px 16px 0 0;
|
|
741
|
+
border: none;
|
|
742
|
+
border-top: 1px solid var(--mrmd-dialog-border, var(--mrmd-border, #454545));
|
|
743
|
+
animation: mrmd-slide-up 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.mrmd-dialog__header {
|
|
747
|
+
padding: 16px 20px;
|
|
748
|
+
position: relative;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/* Drag handle */
|
|
752
|
+
.mrmd-dialog__header::before {
|
|
753
|
+
content: '';
|
|
754
|
+
position: absolute;
|
|
755
|
+
top: 8px;
|
|
756
|
+
left: 50%;
|
|
757
|
+
transform: translateX(-50%);
|
|
758
|
+
width: 36px;
|
|
759
|
+
height: 4px;
|
|
760
|
+
background: var(--mrmd-fg-muted, #888);
|
|
761
|
+
border-radius: 2px;
|
|
762
|
+
opacity: 0.4;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.mrmd-dialog__title {
|
|
766
|
+
font-size: 16px;
|
|
767
|
+
padding-top: 8px;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.mrmd-dialog__close {
|
|
771
|
+
width: 44px;
|
|
772
|
+
height: 44px;
|
|
773
|
+
font-size: 22px;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.mrmd-dialog__body {
|
|
777
|
+
padding: 16px 20px;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.mrmd-dialog__footer {
|
|
781
|
+
padding: 16px 20px;
|
|
782
|
+
padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
|
|
783
|
+
gap: 12px;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
.mrmd-input {
|
|
787
|
+
padding: 12px 14px;
|
|
788
|
+
font-size: 16px; /* Prevents iOS zoom on focus */
|
|
789
|
+
border-radius: 8px;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.mrmd-button {
|
|
793
|
+
padding: 12px 20px;
|
|
794
|
+
font-size: 15px;
|
|
795
|
+
border-radius: 8px;
|
|
796
|
+
min-height: 44px;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/* ---- File Picker: full-height on mobile ---- */
|
|
801
|
+
@media (max-width: 768px) {
|
|
802
|
+
.mrmd-filepicker {
|
|
803
|
+
min-height: 50vh;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
.mrmd-filepicker__path {
|
|
807
|
+
padding: 12px 0;
|
|
808
|
+
gap: 2px;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.mrmd-filepicker__path-segment {
|
|
812
|
+
padding: 6px 10px;
|
|
813
|
+
font-size: 14px;
|
|
814
|
+
min-height: 36px;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.mrmd-filepicker__item {
|
|
818
|
+
padding: 14px 12px;
|
|
819
|
+
min-height: 52px;
|
|
820
|
+
font-size: 15px;
|
|
821
|
+
gap: 14px;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
.mrmd-filepicker__item-icon {
|
|
825
|
+
font-size: 20px;
|
|
826
|
+
width: 28px;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.mrmd-filepicker__item-info {
|
|
830
|
+
font-size: 12px;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/* ---- Touch-specific: larger targets for coarse pointers ---- */
|
|
835
|
+
@media (pointer: coarse) {
|
|
836
|
+
.mrmd-statusbar__segment {
|
|
837
|
+
min-height: 44px;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.mrmd-menu__item {
|
|
841
|
+
min-height: 48px;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.mrmd-dialog__close {
|
|
845
|
+
min-width: 44px;
|
|
846
|
+
min-height: 44px;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.mrmd-button {
|
|
850
|
+
min-height: 44px;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
.mrmd-filepicker__item {
|
|
854
|
+
min-height: 48px;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
.mrmd-filepicker__path-segment {
|
|
858
|
+
min-height: 36px;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/* ---- Shared mobile animation keyframes ---- */
|
|
863
|
+
@keyframes mrmd-fade-in {
|
|
864
|
+
from { opacity: 0; }
|
|
865
|
+
to { opacity: 1; }
|
|
866
|
+
}
|
|
867
|
+
`;
|
|
868
|
+
|
|
604
869
|
// =============================================================================
|
|
605
870
|
// ALL STYLES COMBINED
|
|
606
871
|
// =============================================================================
|
|
@@ -672,6 +937,7 @@ ${menuStyles}
|
|
|
672
937
|
${dialogStyles}
|
|
673
938
|
${filePickerStyles}
|
|
674
939
|
${studioStyles}
|
|
940
|
+
${mobileStyles}
|
|
675
941
|
`;
|
|
676
942
|
|
|
677
943
|
// =============================================================================
|