more-compute 0.2.4__tar.gz → 0.3.0__tar.gz

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.
Files changed (113) hide show
  1. {more_compute-0.2.4/more_compute.egg-info → more_compute-0.3.0}/PKG-INFO +2 -3
  2. {more_compute-0.2.4 → more_compute-0.3.0}/README.md +1 -2
  3. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/app/globals.css +38 -133
  4. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/app/layout.tsx +54 -5
  5. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/Notebook.tsx +9 -1
  6. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/cell/CellButton.tsx +2 -2
  7. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/cell/MonacoCell.tsx +1 -15
  8. more_compute-0.3.0/frontend/components/output/CellOutput.tsx +130 -0
  9. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/output/ErrorDisplay.tsx +3 -28
  10. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/popups/MetricsPopup.tsx +42 -7
  11. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/popups/PackagesPopup.tsx +2 -1
  12. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/popups/SettingsPopup.tsx +3 -0
  13. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/lib/api.ts +6 -2
  14. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/lib/settings.ts +7 -0
  15. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/lib/websocket-native.ts +3 -0
  16. more_compute-0.3.0/frontend/styling_README.md +23 -0
  17. {more_compute-0.2.4 → more_compute-0.3.0}/kernel_run.py +25 -7
  18. {more_compute-0.2.4 → more_compute-0.3.0/more_compute.egg-info}/PKG-INFO +2 -3
  19. {more_compute-0.2.4 → more_compute-0.3.0}/more_compute.egg-info/SOURCES.txt +3 -1
  20. more_compute-0.3.0/morecompute/__version__.py +1 -0
  21. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/execution/executor.py +12 -5
  22. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/execution/worker.py +93 -1
  23. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/server.py +4 -0
  24. more_compute-0.3.0/morecompute/utils/cell_magics.py +713 -0
  25. more_compute-0.3.0/morecompute/utils/line_magics.py +949 -0
  26. more_compute-0.3.0/morecompute/utils/shell_utils.py +68 -0
  27. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/utils/special_commands.py +106 -173
  28. more_compute-0.2.4/frontend/components/Cell.tsx +0 -383
  29. more_compute-0.2.4/frontend/components/output/CellOutput.tsx +0 -70
  30. more_compute-0.2.4/frontend/styling_README.md +0 -18
  31. more_compute-0.2.4/morecompute/__version__.py +0 -1
  32. {more_compute-0.2.4 → more_compute-0.3.0}/LICENSE +0 -0
  33. {more_compute-0.2.4 → more_compute-0.3.0}/MANIFEST.in +0 -0
  34. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/.DS_Store +0 -0
  35. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/.gitignore +0 -0
  36. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/README.md +0 -0
  37. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/__init__.py +0 -0
  38. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/app/favicon.ico +0 -0
  39. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/app/page.tsx +0 -0
  40. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/cell/AddCellButton.tsx +0 -0
  41. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/layout/ConnectionBanner.tsx +0 -0
  42. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/layout/Sidebar.tsx +0 -0
  43. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/modals/ConfirmModal.tsx +0 -0
  44. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/modals/ErrorModal.tsx +0 -0
  45. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/modals/SuccessModal.tsx +0 -0
  46. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/output/MarkdownRenderer.tsx +0 -0
  47. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/popups/ComputePopup.tsx +0 -0
  48. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/popups/FilterPopup.tsx +0 -0
  49. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/components/popups/FolderPopup.tsx +0 -0
  50. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/contexts/PodWebSocketContext.tsx +0 -0
  51. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/eslint.config.mjs +0 -0
  52. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/lib/monaco-themes.ts +0 -0
  53. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/lib/themes.json +0 -0
  54. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/lib/websocket.ts +0 -0
  55. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/next-env.d.ts +0 -0
  56. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/next.config.mjs +0 -0
  57. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/next.config.ts +0 -0
  58. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/package-lock.json +0 -0
  59. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/package.json +0 -0
  60. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/postcss.config.mjs +0 -0
  61. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/add.svg +0 -0
  62. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/check.svg +0 -0
  63. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/copy.svg +0 -0
  64. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/folder.svg +0 -0
  65. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/metric.svg +0 -0
  66. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/packages.svg +0 -0
  67. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/play.svg +0 -0
  68. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/python.svg +0 -0
  69. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/setting.svg +0 -0
  70. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/stop.svg +0 -0
  71. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/trash.svg +0 -0
  72. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/up-down.svg +0 -0
  73. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/assets/icons/x.svg +0 -0
  74. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/file.svg +0 -0
  75. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/fonts/Fira.ttf +0 -0
  76. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/fonts/Tiempos.woff2 +0 -0
  77. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/fonts/VeraMono.ttf +0 -0
  78. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/globe.svg +0 -0
  79. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/next.svg +0 -0
  80. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/vercel.svg +0 -0
  81. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/public/window.svg +0 -0
  82. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/tailwind.config.ts +0 -0
  83. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/tsconfig.json +0 -0
  84. {more_compute-0.2.4 → more_compute-0.3.0}/frontend/types/notebook.ts +0 -0
  85. {more_compute-0.2.4 → more_compute-0.3.0}/more_compute.egg-info/dependency_links.txt +0 -0
  86. {more_compute-0.2.4 → more_compute-0.3.0}/more_compute.egg-info/entry_points.txt +0 -0
  87. {more_compute-0.2.4 → more_compute-0.3.0}/more_compute.egg-info/requires.txt +0 -0
  88. {more_compute-0.2.4 → more_compute-0.3.0}/more_compute.egg-info/top_level.txt +0 -0
  89. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/__init__.py +0 -0
  90. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/cli.py +0 -0
  91. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/execution/__init__.py +0 -0
  92. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/execution/__main__.py +0 -0
  93. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/models/__init__.py +0 -0
  94. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/models/api_models.py +0 -0
  95. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/notebook.py +0 -0
  96. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/process_worker.py +0 -0
  97. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/services/data_manager.py +0 -0
  98. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/services/lsp_service.py +0 -0
  99. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/services/pod_manager.py +0 -0
  100. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/services/pod_monitor.py +0 -0
  101. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/services/prime_intellect.py +0 -0
  102. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/static/styles.css +0 -0
  103. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/utils/__init__.py +0 -0
  104. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/utils/cache_util.py +0 -0
  105. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/utils/config_util.py +0 -0
  106. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/utils/error_utils.py +0 -0
  107. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/utils/notebook_util.py +0 -0
  108. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/utils/python_environment_util.py +0 -0
  109. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/utils/system_environment_util.py +0 -0
  110. {more_compute-0.2.4 → more_compute-0.3.0}/morecompute/utils/zmq_util.py +0 -0
  111. {more_compute-0.2.4 → more_compute-0.3.0}/pyproject.toml +0 -0
  112. {more_compute-0.2.4 → more_compute-0.3.0}/setup.cfg +0 -0
  113. {more_compute-0.2.4 → more_compute-0.3.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: more-compute
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: An interactive notebook environment for local and GPU computing
5
5
  Home-page: https://github.com/DannyMang/MORECOMPUTE
6
6
  Author: MoreCompute Team
@@ -44,8 +44,7 @@ Dynamic: requires-python
44
44
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
45
45
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
46
46
 
47
- Interactive notebook environment for local Python development. Works with standard `.ipynb` files,
48
- similar to Jupyter Lab but more awesome.
47
+ An interactive notebook environment, similar to Marimo and Google Colab, that runs locally. It works with standard `.ipynb` files, similar to Jupyter Lab but more awesome.
49
48
 
50
49
  ## Installation
51
50
 
@@ -4,8 +4,7 @@
4
4
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
6
 
7
- Interactive notebook environment for local Python development. Works with standard `.ipynb` files,
8
- similar to Jupyter Lab but more awesome.
7
+ An interactive notebook environment, similar to Marimo and Google Colab, that runs locally. It works with standard `.ipynb` files, similar to Jupyter Lab but more awesome.
9
8
 
10
9
  ## Installation
11
10
 
@@ -210,8 +210,8 @@ body {
210
210
  .kernel-status-indicator {
211
211
  display: flex;
212
212
  align-items: center;
213
- background: rgba(255, 255, 255, 0.95);
214
- border: 1px solid #e5e7eb;
213
+ background: var(--mc-cell-background);
214
+ border: 1px solid var(--mc-border);
215
215
  border-radius: 20px;
216
216
  padding: 8px 12px;
217
217
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
@@ -252,7 +252,7 @@ body {
252
252
  }
253
253
 
254
254
  .status-text {
255
- color: #6b7280;
255
+ color: var(--mc-text-color);
256
256
  }
257
257
 
258
258
  /* Notebook Wrapper */
@@ -575,131 +575,7 @@ body {
575
575
  padding: 0;
576
576
  }
577
577
 
578
- .cell-editor-container {
579
- position: relative;
580
- }
581
-
582
- .cell-editor {
583
- width: 100%;
584
- border: none;
585
- outline: none;
586
- resize: none;
587
- font-family: 'Fira', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
588
- font-size: 14px;
589
- line-height: 1.6;
590
- padding: 16px 20px;
591
- background: transparent;
592
- color: var(--mc-text-color);
593
- min-height: 100px;
594
- }
595
-
596
- .cell-editor:focus {
597
- outline: none;
598
- }
599
-
600
- /* Markdown editor styling - uses Fira font for better readability */
601
- .markdown-editor-container {
602
- /* Inherits from cell-editor-container */
603
- }
604
-
605
- .markdown-editor {
606
- font-family: 'Fira', 'CustomFont', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
607
- /* Keep other properties from .cell-editor */
608
- }
609
-
610
- /* Code editor styling - keeps monospace font */
611
- .code-editor-container {
612
- /* Inherits from cell-editor-container */
613
- }
614
-
615
- .code-editor {
616
- /* Inherits monospace font from .cell-editor */
617
- }
618
-
619
- /* CodeMirror Styling */
620
- .CodeMirror {
621
- height: auto;
622
- font-family: 'Fira', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
623
- font-size: 14px;
624
- line-height: 1.6;
625
- border: none;
626
- background: transparent;
627
- color: var(--mc-text-color);
628
- }
629
-
630
- .CodeMirror-focused {
631
- outline: none;
632
- }
633
-
634
- .CodeMirror-scroll {
635
- /* Content-based sizing - start small and grow with content */
636
- min-height: 32px; /* Single line minimum */
637
- padding: 8px 16px;
638
- transition: min-height 0.2s ease;
639
- background: var(--mc-cell-background);
640
- }
641
-
642
- /* Auto-sizing based on content */
643
- .CodeMirror {
644
- /* Let CodeMirror size itself based on content */
645
- height: auto !important;
646
- min-height: 32px;
647
- }
648
-
649
- .CodeMirror-sizer {
650
- min-height: auto !important;
651
- }
652
-
653
- /* Smart sizing classes applied dynamically */
654
- .cell.cell-empty .CodeMirror-scroll {
655
- min-height: 32px; /* Very compact for empty cells */
656
- }
657
-
658
- .cell.cell-single-line .CodeMirror-scroll {
659
- min-height: 40px; /* Slightly more space for single line */
660
- }
661
-
662
- .cell.cell-focused .CodeMirror-scroll {
663
- min-height: 48px; /* More space when focused for editing */
664
- }
665
-
666
- .CodeMirror-cursor {
667
- border-left: 2px solid #3b82f6;
668
- }
669
-
670
- .CodeMirror-selected {
671
- background: #dbeafe;
672
- }
673
-
674
- .CodeMirror-line {
675
- padding: 0;
676
- }
677
-
678
- .CodeMirror-placeholder {
679
- color: #9ca3af !important;
680
- font-style: italic;
681
- }
682
-
683
- /* Line numbers styling */
684
- .CodeMirror-linenumbers {
685
- background: var(--mc-cell-background);
686
- border-right: 1px solid #e5e7eb;
687
- padding-right: 8px;
688
- min-width: 30px;
689
- }
690
-
691
- .CodeMirror-linenumber {
692
- color: var(--mc-line-number-color);
693
- font-size: 11px;
694
- text-align: right;
695
- padding: 0 5px 0 0;
696
- min-width: 20px;
697
- }
698
-
699
- .CodeMirror-gutters {
700
- background: var(--mc-cell-background);
701
- border-right: 1px solid #e5e7eb;
702
- }
578
+ /* Legacy textarea editor styles removed - now using Monaco Editor exclusively */
703
579
 
704
580
  /* Markdown Rendered Content - Auto-sizing */
705
581
  .markdown-rendered {
@@ -1434,6 +1310,7 @@ body {
1434
1310
  border: 1px solid var(--mc-border);
1435
1311
  border-radius: 8px;
1436
1312
  background: var(--mc-cell-background);
1313
+ overflow: hidden;
1437
1314
  }
1438
1315
 
1439
1316
  .metric-panel-header {
@@ -1450,6 +1327,7 @@ body {
1450
1327
 
1451
1328
  .metric-panel-body {
1452
1329
  padding: 10px 12px;
1330
+ overflow: hidden;
1453
1331
  }
1454
1332
 
1455
1333
  .metric-big-value .value {
@@ -1466,6 +1344,8 @@ body {
1466
1344
 
1467
1345
  .mini-chart {
1468
1346
  margin-top: 6px;
1347
+ max-width: 100%;
1348
+ height: auto;
1469
1349
  }
1470
1350
 
1471
1351
  .metrics-footer {
@@ -1577,7 +1457,7 @@ body {
1577
1457
  .packages-table {
1578
1458
  display: flex;
1579
1459
  flex-direction: column;
1580
- border: 1px solid #e5e7eb;
1460
+ border: 1px solid var(--mc-border);
1581
1461
  border-radius: 8px;
1582
1462
  overflow: hidden;
1583
1463
  background: var(--mc-cell-background);
@@ -1590,14 +1470,38 @@ body {
1590
1470
  padding: 10px 12px;
1591
1471
  font-size: 12px;
1592
1472
  font-weight: 600;
1593
- color: #6b7280;
1594
- border-bottom: 1px solid #e5e7eb;
1595
- background: #f9fafb;
1473
+ color: var(--mc-text-color);
1474
+ border-bottom: 1px solid var(--mc-border);
1475
+ background: var(--mc-secondary);
1476
+ opacity: 0.8;
1596
1477
  }
1597
1478
 
1598
1479
  .packages-list {
1599
1480
  overflow-y: auto;
1600
1481
  max-height: calc(100vh - 260px);
1482
+ /* Firefox scrollbar styling */
1483
+ scrollbar-width: thin;
1484
+ scrollbar-color: var(--mc-border) var(--mc-cell-background);
1485
+ }
1486
+
1487
+ /* Packages list scrollbar styling (Webkit browsers) */
1488
+ .packages-list::-webkit-scrollbar {
1489
+ width: 8px;
1490
+ }
1491
+
1492
+ .packages-list::-webkit-scrollbar-track {
1493
+ background: var(--mc-cell-background);
1494
+ border-radius: 4px;
1495
+ }
1496
+
1497
+ .packages-list::-webkit-scrollbar-thumb {
1498
+ background: var(--mc-border);
1499
+ border-radius: 4px;
1500
+ transition: background 0.2s ease;
1501
+ }
1502
+
1503
+ .packages-list::-webkit-scrollbar-thumb:hover {
1504
+ background: var(--mc-secondary);
1601
1505
  }
1602
1506
 
1603
1507
  .package-row {
@@ -1605,7 +1509,7 @@ body {
1605
1509
  grid-template-columns: 1fr 140px;
1606
1510
  gap: 8px;
1607
1511
  padding: 10px 12px;
1608
- border-bottom: 1px solid #f3f4f6;
1512
+ border-bottom: 1px solid var(--mc-border);
1609
1513
  }
1610
1514
 
1611
1515
  .package-row:last-child {
@@ -1694,6 +1598,7 @@ body {
1694
1598
  color: var(--mc-line-number-color) !important;
1695
1599
  font-size: 11px !important;
1696
1600
  font-family: 'Fira', 'SF Mono', Monaco, monospace;
1601
+ padding-right: 12px !important;
1697
1602
  }
1698
1603
 
1699
1604
  .monaco-editor .margin-view-overlays {
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useState, useEffect } from "react";
3
+ import { useState, useEffect, useRef } from "react";
4
4
  import Script from "next/script";
5
5
  import Sidebar from "@/components/layout/Sidebar";
6
6
  import FolderPopup from "@/components/popups/FolderPopup";
@@ -13,21 +13,65 @@ import {
13
13
  PodWebSocketProvider,
14
14
  usePodWebSocket,
15
15
  } from "@/contexts/PodWebSocketContext";
16
- import { loadSettings, applyTheme } from "@/lib/settings";
16
+ import { loadSettings, applyTheme, type NotebookSettings } from "@/lib/settings";
17
+ import { fetchMetrics, type MetricsSnapshot } from "@/lib/api";
17
18
  import "./globals.css";
18
19
 
20
+ const POLL_MS = 3000;
21
+
19
22
  function AppContent({ children }: { children: React.ReactNode }) {
20
- const [appSettings, setAppSettings] = useState({});
23
+ const [appSettings, setAppSettings] = useState<NotebookSettings>(() => loadSettings());
21
24
  const [activePopup, setActivePopup] = useState<string | null>(null);
22
25
  const { connectionState, gpuPods, connectingPodId } = usePodWebSocket();
23
26
 
27
+ // Persistent metrics collection
28
+ const [metricsHistory, setMetricsHistory] = useState<MetricsSnapshot[]>([]);
29
+ const intervalRef = useRef<number | null>(null);
30
+
24
31
  // Apply theme on initial mount
25
32
  useEffect(() => {
26
33
  const settings = loadSettings();
27
34
  applyTheme(settings.theme);
28
35
  }, []);
29
36
 
30
- const handleSettingsChange = (settings: any) => {
37
+ // Persistent metrics collection (runs when mode is 'persistent')
38
+ useEffect(() => {
39
+ if (appSettings.metricsCollectionMode !== 'persistent') {
40
+ // Stop collection if mode changes to on-demand
41
+ if (intervalRef.current) {
42
+ window.clearInterval(intervalRef.current);
43
+ intervalRef.current = null;
44
+ }
45
+ // Clear history when switching to on-demand mode
46
+ setMetricsHistory([]);
47
+ return;
48
+ }
49
+
50
+ // Start persistent collection
51
+ const load = async () => {
52
+ try {
53
+ const snap = await fetchMetrics();
54
+ setMetricsHistory((prev) => {
55
+ const arr = [...prev, snap];
56
+ return arr.slice(-100); // Keep last 100 snapshots
57
+ });
58
+ } catch {
59
+ // Silently fail if metrics API is unavailable
60
+ }
61
+ };
62
+
63
+ load(); // Initial load
64
+ intervalRef.current = window.setInterval(load, POLL_MS);
65
+
66
+ return () => {
67
+ if (intervalRef.current) {
68
+ window.clearInterval(intervalRef.current);
69
+ intervalRef.current = null;
70
+ }
71
+ };
72
+ }, [appSettings.metricsCollectionMode]);
73
+
74
+ const handleSettingsChange = (settings: NotebookSettings) => {
31
75
  console.log("Settings updated:", settings);
32
76
  setAppSettings(settings);
33
77
  };
@@ -52,7 +96,12 @@ function AppContent({ children }: { children: React.ReactNode }) {
52
96
  case "compute":
53
97
  return <ComputePopup {...props} />;
54
98
  case "metrics":
55
- return <MetricsPopup {...props} />;
99
+ return (
100
+ <MetricsPopup
101
+ {...props}
102
+ sharedHistory={appSettings.metricsCollectionMode === 'persistent' ? metricsHistory : undefined}
103
+ />
104
+ );
56
105
  case "settings":
57
106
  return (
58
107
  <SettingsPopup {...props} onSettingsChange={handleSettingsChange} />
@@ -435,6 +435,12 @@ export const Notebook: React.FC<NotebookProps> = ({
435
435
  dispatch({ type: "NOTEBOOK_UPDATED", payload: data });
436
436
  }, []);
437
437
 
438
+ const handleHeartbeat = useCallback((data: any) => {
439
+ // Heartbeat received - execution still in progress
440
+ // Cell spinner already showing via executingCells set
441
+ console.log("[Heartbeat]", data?.message || "Execution in progress");
442
+ }, []);
443
+
438
444
  const handleKernelStatusUpdate = useCallback(
439
445
  (status: "connecting" | "connected" | "disconnected") => {
440
446
  setKernelStatus(status);
@@ -485,6 +491,7 @@ export const Notebook: React.FC<NotebookProps> = ({
485
491
  ws.on("execution_complete", handleExecutionComplete);
486
492
  ws.on("execution_result", handleExecuteResult);
487
493
  ws.on("execution_error", handleExecutionError);
494
+ ws.on("heartbeat", handleHeartbeat);
488
495
 
489
496
  return () => ws.disconnect();
490
497
  }, [
@@ -497,6 +504,7 @@ export const Notebook: React.FC<NotebookProps> = ({
497
504
  handleExecuteResult,
498
505
  handleExecutionComplete,
499
506
  handleExecutionError,
507
+ handleHeartbeat,
500
508
  ]);
501
509
 
502
510
  // Simplified save management - only save on Ctrl+S or Run
@@ -528,7 +536,7 @@ export const Notebook: React.FC<NotebookProps> = ({
528
536
  if (cell.cell_type === "markdown") {
529
537
  // Save before rendering markdown
530
538
  saveNotebook();
531
- // Markdown rendering is handled locally in Cell.tsx now
539
+ // Markdown rendering is handled locally in MonacoCell.tsx now
532
540
  return;
533
541
  }
534
542
 
@@ -39,9 +39,9 @@ export const CellButton: React.FC<CellButtonProps> = ({
39
39
  style={{
40
40
  width: '28px',
41
41
  height: '28px',
42
- border: '1px solid #D9DEE5',
42
+ border: '1px solid var(--mc-border)',
43
43
  borderRadius: '4px',
44
- backgroundColor: isHovered ? '#E6E8EC' : 'rgba(243, 244, 246, 0.5)',
44
+ backgroundColor: isHovered ? 'var(--mc-secondary)' : 'var(--mc-cell-background)',
45
45
  display: 'flex',
46
46
  alignItems: 'center',
47
47
  justifyContent: 'center',
@@ -16,7 +16,6 @@ import {
16
16
  ChevronDownIcon,
17
17
  } from "@radix-ui/react-icons";
18
18
  import { Check, X } from "lucide-react";
19
- import { fixIndentation } from "@/lib/api";
20
19
  import { loadMonacoThemes } from "@/lib/monaco-themes";
21
20
  import { loadSettings } from "@/lib/settings";
22
21
 
@@ -285,18 +284,6 @@ export const MonacoCell: React.FC<CellProps> = ({
285
284
  }
286
285
  };
287
286
 
288
- const handleFixIndentation = async () => {
289
- try {
290
- const fixedCode = await fixIndentation(cell.source);
291
- onUpdate(indexRef.current, fixedCode);
292
- if (editorRef.current) {
293
- editorRef.current.setValue(fixedCode);
294
- }
295
- } catch (err) {
296
- console.error("Failed to fix indentation:", err);
297
- }
298
- };
299
-
300
287
  // ============================================================================
301
288
  // MONACO SETUP
302
289
  // ============================================================================
@@ -690,7 +677,7 @@ export const MonacoCell: React.FC<CellProps> = ({
690
677
  overviewRulerBorder: false,
691
678
  overviewRulerLanes: 0,
692
679
  // NOTE: fixedOverflowWidgets has known positioning bugs in 2024 - disabled
693
- padding: { top: 8, bottom: 8 }, // All padding managed by Monaco
680
+ padding: { top: 8, bottom: 8, left: 8 }, // All padding managed by Monaco
694
681
  scrollbar: {
695
682
  vertical: "auto",
696
683
  horizontal: "auto",
@@ -710,7 +697,6 @@ export const MonacoCell: React.FC<CellProps> = ({
710
697
  <CellOutput
711
698
  outputs={cell.outputs}
712
699
  error={cell.error}
713
- onFixIndentation={handleFixIndentation}
714
700
  />
715
701
  </div>
716
702
  </div>
@@ -0,0 +1,130 @@
1
+ 'use client';
2
+
3
+ import { FC, useState } from 'react';
4
+ import { Output } from '@/types/notebook';
5
+ import ErrorDisplay from './ErrorDisplay';
6
+ import { Copy, Check } from 'lucide-react';
7
+
8
+ interface CellOutputProps {
9
+ outputs: Output[];
10
+ error: any;
11
+ }
12
+
13
+ interface OutputWithCopyProps {
14
+ content: string;
15
+ className: string;
16
+ }
17
+
18
+ const OutputWithCopy: FC<OutputWithCopyProps> = ({ content, className }) => {
19
+ const [isCopied, setIsCopied] = useState(false);
20
+
21
+ const copyToClipboard = () => {
22
+ navigator.clipboard.writeText(content).then(() => {
23
+ setIsCopied(true);
24
+ setTimeout(() => setIsCopied(false), 2000);
25
+ });
26
+ };
27
+
28
+ return (
29
+ <div style={{ position: 'relative' }}>
30
+ {/* Copy Button */}
31
+ <button
32
+ onClick={copyToClipboard}
33
+ style={{
34
+ position: 'absolute',
35
+ top: '8px',
36
+ right: '8px',
37
+ zIndex: 10,
38
+ background: 'var(--mc-cell-background)',
39
+ border: '1px solid var(--mc-border)',
40
+ borderRadius: '4px',
41
+ padding: '6px',
42
+ cursor: 'pointer',
43
+ display: 'flex',
44
+ alignItems: 'center',
45
+ justifyContent: 'center',
46
+ transition: 'all 0.2s ease'
47
+ }}
48
+ onMouseEnter={(e) => {
49
+ e.currentTarget.style.background = 'var(--mc-secondary)';
50
+ }}
51
+ onMouseLeave={(e) => {
52
+ e.currentTarget.style.background = 'var(--mc-cell-background)';
53
+ }}
54
+ title="Copy output to clipboard"
55
+ >
56
+ {isCopied ? <Check size={14} style={{ color: 'var(--mc-primary)' }} /> : <Copy size={14} />}
57
+ </button>
58
+
59
+ {/* Output Content */}
60
+ <pre className={className}>{content}</pre>
61
+ </div>
62
+ );
63
+ };
64
+
65
+ const CellOutput: FC<CellOutputProps> = ({ outputs, error }) => {
66
+ if (error) {
67
+ return <ErrorDisplay error={error} />;
68
+ }
69
+
70
+ if (!outputs || outputs.length === 0) {
71
+ return null;
72
+ }
73
+
74
+ return (
75
+ <div className="cell-output">
76
+ <div className="output-content">
77
+ {outputs.map((output, index) => {
78
+ switch (output.output_type) {
79
+ case 'stream':
80
+ return (
81
+ <OutputWithCopy
82
+ key={index}
83
+ content={output.text}
84
+ className={`output-stream ${output.name}`}
85
+ />
86
+ );
87
+ case 'execute_result':
88
+ return (
89
+ <OutputWithCopy
90
+ key={index}
91
+ content={output.data?.['text/plain'] || ''}
92
+ className="output-result"
93
+ />
94
+ );
95
+ case 'display_data': {
96
+ const img = (output as any).data?.['image/png'];
97
+ const alt = (output as any).data?.['text/plain'] || 'image/png';
98
+ if (img) {
99
+ return (
100
+ <div key={index} className="output-result">
101
+ <img src={`data:image/png;base64,${img}`} alt={alt} />
102
+ </div>
103
+ );
104
+ }
105
+ return (
106
+ <OutputWithCopy
107
+ key={index}
108
+ content={(output as any).data?.['text/plain'] || ''}
109
+ className="output-result"
110
+ />
111
+ );
112
+ }
113
+ case 'error':
114
+ return <ErrorDisplay key={index} error={output} />;
115
+ default:
116
+ return (
117
+ <OutputWithCopy
118
+ key={index}
119
+ content={JSON.stringify(output, null, 2)}
120
+ className="output-unknown"
121
+ />
122
+ );
123
+ }
124
+ })}
125
+ </div>
126
+ </div>
127
+ );
128
+ };
129
+
130
+ export default CellOutput;
@@ -16,12 +16,10 @@ import { Output, ErrorOutput } from '@/types/notebook';
16
16
  interface ErrorDisplayProps {
17
17
  error: Output;
18
18
  maxLines?: number;
19
- onFixIndentation?: () => void;
20
19
  }
21
20
 
22
- const TypedErrorDisplay: FC<{ error: ErrorOutput; onFixIndentation?: () => void }> = ({ error, onFixIndentation }) => {
21
+ const TypedErrorDisplay: FC<{ error: ErrorOutput }> = ({ error }) => {
23
22
  const [isCopied, setIsCopied] = useState(false);
24
- const isIndentationError = error.ename === 'IndentationError';
25
23
 
26
24
  const copyToClipboard = () => {
27
25
  const errorDetails = `Error: ${error.ename}: ${error.evalue}\n\nTraceback:\n${error.traceback.join('\n')}`;
@@ -112,29 +110,6 @@ const TypedErrorDisplay: FC<{ error: ErrorOutput; onFixIndentation?: () => void
112
110
  display: 'flex',
113
111
  gap: '4px'
114
112
  }}>
115
- {/* Fix Indentation Button */}
116
- {isIndentationError && onFixIndentation && (
117
- <button
118
- onClick={onFixIndentation}
119
- style={{
120
- background: 'rgba(59, 130, 246, 0.1)',
121
- border: '1px solid #3b82f6',
122
- borderRadius: '4px',
123
- padding: '6px 10px',
124
- cursor: 'pointer',
125
- display: 'flex',
126
- alignItems: 'center',
127
- justifyContent: 'center',
128
- transition: 'all 0.2s ease',
129
- fontSize: '11px',
130
- fontWeight: 500,
131
- color: '#3b82f6'
132
- }}
133
- title="Auto-fix indentation"
134
- >
135
- Fix Indent
136
- </button>
137
- )}
138
113
  {/* Copy Button */}
139
114
  <button
140
115
  onClick={copyToClipboard}
@@ -197,12 +172,12 @@ const TypedErrorDisplay: FC<{ error: ErrorOutput; onFixIndentation?: () => void
197
172
  );
198
173
  };
199
174
 
200
- const ErrorDisplay: FC<ErrorDisplayProps> = ({ error, onFixIndentation }) => {
175
+ const ErrorDisplay: FC<ErrorDisplayProps> = ({ error }) => {
201
176
  // Type guard to ensure we have an ErrorOutput
202
177
  if (error.output_type !== 'error') {
203
178
  return null;
204
179
  }
205
- return <TypedErrorDisplay error={error} onFixIndentation={onFixIndentation} />;
180
+ return <TypedErrorDisplay error={error} />;
206
181
  };
207
182
 
208
183
  export default ErrorDisplay;