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.
@@ -69,12 +69,12 @@ function getInitialState() {
69
69
  runtimes: {
70
70
  // Legacy: single Python runtime info (for backward compat)
71
71
  python: null,
72
- // New: multiple runtime sessions
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 → session attachment
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 Session Management
467
+ // Runtime Attachment Management
468
468
  // ===========================================================================
469
469
 
470
470
  /**
471
- * Get the session attached to a document
471
+ * Get the runtime attached to a document
472
472
  * @param {string} docName - Document name
473
- * @returns {string} Session ID (defaults to 'shared')
473
+ * @returns {string} Runtime ID (defaults to 'shared')
474
474
  */
475
- getDocumentSession(docName) {
475
+ getDocumentRuntime(docName) {
476
476
  return this.get(`runtimes.attachments.${docName}`) || 'shared';
477
477
  }
478
478
 
479
479
  /**
480
- * Get session info
481
- * @param {string} sessionId - Session ID
480
+ * Get runtime info
481
+ * @param {string} runtimeId - Runtime ID
482
482
  * @returns {Object|null}
483
483
  */
484
- getSession(sessionId) {
485
- return this.get(`runtimes.sessions.${sessionId}`) || null;
484
+ getRuntime(runtimeId) {
485
+ return this.get(`runtimes.sessions.${runtimeId}`) || null;
486
486
  }
487
487
 
488
488
  /**
489
- * Get all available sessions
489
+ * Get all available runtimes
490
490
  * @returns {Array<{id: string, info: Object}>}
491
491
  */
492
- getSessions() {
493
- const sessions = this.get('runtimes.sessions') || {};
494
- return Object.entries(sessions).map(([id, info]) => ({ id, info }));
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 session
498
+ * Attach a document to a runtime
499
499
  * @param {string} docName - Document name
500
- * @param {string} sessionId - Session ID to attach to
500
+ * @param {string} runtimeId - Runtime ID to attach to
501
501
  */
502
- attachDocument(docName, sessionId) {
503
- this._set(`runtimes.attachments.${docName}`, sessionId);
502
+ attachDocument(docName, runtimeId) {
503
+ this._set(`runtimes.attachments.${docName}`, runtimeId);
504
504
  }
505
505
 
506
506
  /**
507
- * Register a runtime session
508
- * @param {string} sessionId - Session ID
509
- * @param {Object} info - Session info (url, status, venv, cwd, etc.)
507
+ * Register runtime metadata
508
+ * @param {string} runtimeId - Runtime ID
509
+ * @param {Object} info - Runtime info (url, status, venv, cwd, etc.)
510
510
  */
511
- registerSession(sessionId, info) {
512
- this._set(`runtimes.sessions.${sessionId}`, info);
511
+ registerRuntime(runtimeId, info) {
512
+ this._set(`runtimes.sessions.${runtimeId}`, info);
513
513
 
514
- // Also update legacy python state if this is the shared session
515
- if (sessionId === 'shared' && info.language === 'python') {
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 a new dedicated runtime session
531
- * @param {string} docName - Document to attach the session to
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>} Session info
534
+ * @returns {Promise<Object>} Runtime info
535
535
  */
536
- async createSession(docName, mode = 'dedicated', venv = null) {
536
+ async createRuntime(docName, mode = 'dedicated', venv = null) {
537
537
  try {
538
- const result = await this._client.createSession(docName, mode, venv);
538
+ const result = await this._client.createRuntimeAttachment(docName, mode, venv);
539
539
 
540
- const sessionId = result.id || (mode === 'dedicated' ? `python-${result.runtimes?.python?.port || Date.now()}` : 'shared');
540
+ const runtimeId = result.id || (mode === 'dedicated' ? `python-${result.runtimes?.python?.port || Date.now()}` : 'shared');
541
541
 
542
- const sessionInfo = {
543
- id: sessionId,
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 the session
554
- this.registerSession(sessionId, sessionInfo);
553
+ // Register runtime
554
+ this.registerRuntime(runtimeId, runtimeInfo);
555
555
 
556
- // Attach the document to this session
557
- this.attachDocument(docName, sessionId);
556
+ // Attach document to runtime
557
+ this.attachDocument(docName, runtimeId);
558
558
 
559
- return sessionInfo;
559
+ return runtimeInfo;
560
560
  } catch (error) {
561
- console.error('Failed to create session:', error);
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 sessionId = this.getDocumentSession(docName);
573
- const session = this.getSession(sessionId);
574
- return session?.url || null;
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
  // ===========================================================================
@@ -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
  // =============================================================================