more-compute 0.3.3__tar.gz → 0.4.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.3.3 → more_compute-0.4.0}/MANIFEST.in +4 -0
  2. {more_compute-0.3.3/more_compute.egg-info → more_compute-0.4.0}/PKG-INFO +30 -28
  3. {more_compute-0.3.3 → more_compute-0.4.0}/README.md +29 -27
  4. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/app/globals.css +9 -7
  5. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/app/layout.tsx +43 -1
  6. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/Notebook.tsx +22 -1
  7. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/cell/CellButton.tsx +5 -4
  8. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/cell/MonacoCell.tsx +42 -21
  9. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/output/ErrorDisplay.tsx +14 -1
  10. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/contexts/PodWebSocketContext.tsx +1 -1
  11. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/lib/websocket.ts +2 -2
  12. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/next.config.mjs +2 -2
  13. {more_compute-0.3.3 → more_compute-0.4.0}/kernel_run.py +107 -17
  14. {more_compute-0.3.3 → more_compute-0.4.0/more_compute.egg-info}/PKG-INFO +30 -28
  15. {more_compute-0.3.3 → more_compute-0.4.0}/more_compute.egg-info/SOURCES.txt +2 -1
  16. more_compute-0.4.0/morecompute/__version__.py +1 -0
  17. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/execution/executor.py +113 -44
  18. more_compute-0.4.0/morecompute/execution/worker.py +616 -0
  19. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/notebook.py +65 -6
  20. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/server.py +72 -40
  21. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/cell_magics.py +35 -4
  22. more_compute-0.4.0/morecompute/utils/notebook_converter.py +129 -0
  23. more_compute-0.4.0/morecompute/utils/py_percent_parser.py +190 -0
  24. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/special_commands.py +126 -49
  25. more_compute-0.3.3/frontend/.DS_Store +0 -0
  26. more_compute-0.3.3/morecompute/__version__.py +0 -1
  27. more_compute-0.3.3/morecompute/execution/worker.py +0 -404
  28. {more_compute-0.3.3 → more_compute-0.4.0}/LICENSE +0 -0
  29. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/.gitignore +0 -0
  30. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/README.md +0 -0
  31. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/__init__.py +0 -0
  32. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/app/favicon.ico +0 -0
  33. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/app/page.tsx +0 -0
  34. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/cell/AddCellButton.tsx +0 -0
  35. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/layout/ConnectionBanner.tsx +0 -0
  36. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/layout/Sidebar.tsx +0 -0
  37. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/modals/ConfirmModal.tsx +0 -0
  38. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/modals/ErrorModal.tsx +0 -0
  39. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/modals/SuccessModal.tsx +0 -0
  40. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/output/CellOutput.tsx +0 -0
  41. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/output/MarkdownRenderer.tsx +0 -0
  42. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/popups/ComputePopup.tsx +0 -0
  43. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/popups/FilterPopup.tsx +0 -0
  44. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/popups/FolderPopup.tsx +0 -0
  45. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/popups/MetricsPopup.tsx +0 -0
  46. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/popups/PackagesPopup.tsx +0 -0
  47. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/components/popups/SettingsPopup.tsx +0 -0
  48. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/eslint.config.mjs +0 -0
  49. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/lib/api.ts +0 -0
  50. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/lib/monaco-themes.ts +0 -0
  51. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/lib/settings.ts +0 -0
  52. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/lib/themes.json +0 -0
  53. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/lib/websocket-native.ts +0 -0
  54. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/next-env.d.ts +0 -0
  55. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/next.config.ts +0 -0
  56. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/package-lock.json +0 -0
  57. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/package.json +0 -0
  58. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/postcss.config.mjs +0 -0
  59. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/add.svg +0 -0
  60. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/check.svg +0 -0
  61. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/copy.svg +0 -0
  62. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/folder.svg +0 -0
  63. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/metric.svg +0 -0
  64. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/packages.svg +0 -0
  65. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/play.svg +0 -0
  66. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/python.svg +0 -0
  67. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/setting.svg +0 -0
  68. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/stop.svg +0 -0
  69. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/trash.svg +0 -0
  70. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/up-down.svg +0 -0
  71. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/assets/icons/x.svg +0 -0
  72. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/file.svg +0 -0
  73. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/fonts/Fira.ttf +0 -0
  74. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/fonts/Tiempos.woff2 +0 -0
  75. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/fonts/VeraMono.ttf +0 -0
  76. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/globe.svg +0 -0
  77. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/next.svg +0 -0
  78. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/vercel.svg +0 -0
  79. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/public/window.svg +0 -0
  80. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/styling_README.md +0 -0
  81. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/tailwind.config.ts +0 -0
  82. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/tsconfig.json +0 -0
  83. {more_compute-0.3.3 → more_compute-0.4.0}/frontend/types/notebook.ts +0 -0
  84. {more_compute-0.3.3 → more_compute-0.4.0}/more_compute.egg-info/dependency_links.txt +0 -0
  85. {more_compute-0.3.3 → more_compute-0.4.0}/more_compute.egg-info/entry_points.txt +0 -0
  86. {more_compute-0.3.3 → more_compute-0.4.0}/more_compute.egg-info/requires.txt +0 -0
  87. {more_compute-0.3.3 → more_compute-0.4.0}/more_compute.egg-info/top_level.txt +0 -0
  88. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/__init__.py +0 -0
  89. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/cli.py +0 -0
  90. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/execution/__init__.py +0 -0
  91. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/execution/__main__.py +0 -0
  92. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/models/__init__.py +0 -0
  93. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/models/api_models.py +0 -0
  94. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/process_worker.py +0 -0
  95. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/services/data_manager.py +0 -0
  96. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/services/lsp_service.py +0 -0
  97. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/services/pod_manager.py +0 -0
  98. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/services/pod_monitor.py +0 -0
  99. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/services/prime_intellect.py +0 -0
  100. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/static/styles.css +0 -0
  101. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/__init__.py +0 -0
  102. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/cache_util.py +0 -0
  103. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/config_util.py +0 -0
  104. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/error_utils.py +0 -0
  105. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/line_magics.py +0 -0
  106. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/notebook_util.py +0 -0
  107. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/python_environment_util.py +0 -0
  108. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/shell_utils.py +0 -0
  109. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/system_environment_util.py +0 -0
  110. {more_compute-0.3.3 → more_compute-0.4.0}/morecompute/utils/zmq_util.py +0 -0
  111. {more_compute-0.3.3 → more_compute-0.4.0}/pyproject.toml +0 -0
  112. {more_compute-0.3.3 → more_compute-0.4.0}/setup.cfg +0 -0
  113. {more_compute-0.3.3 → more_compute-0.4.0}/setup.py +0 -0
@@ -1,5 +1,9 @@
1
1
  include README.md
2
+ include LICENSE
2
3
  recursive-include morecompute/static *
3
4
  recursive-include frontend *
4
5
  prune frontend/node_modules
5
6
  prune frontend/.next
7
+ global-exclude .DS_Store
8
+ global-exclude *.pyc
9
+ global-exclude __pycache__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: more-compute
3
- Version: 0.3.3
3
+ Version: 0.4.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,16 +44,12 @@ 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
- 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.
48
-
49
-
47
+ An interactive Python notebook environment, similar to Marimo and Google Colab, that runs locally.
50
48
 
51
49
 
52
50
  https://github.com/user-attachments/assets/8c7ec716-dade-4de2-ad37-71d328129c97
53
51
 
54
52
 
55
-
56
-
57
53
  ## Installation
58
54
 
59
55
  **Prerequisites:** [Node.js](https://nodejs.org/) >= 20.10.0 required for web interface
@@ -83,40 +79,46 @@ pip install more-compute
83
79
  ## Usage
84
80
 
85
81
  ```bash
86
- more-compute notebook.ipynb # Open existing notebook
87
- more-compute # Create and open new notebook
82
+ more-compute notebook.py # Open existing notebook
83
+ more-compute new # Create new notebook
88
84
  more-compute --debug # Show logs
89
85
  ```
90
86
 
91
- Opens automatically at http://localhost:8000
87
+ Opens automatically at http://localhost:3141
92
88
 
93
- ## Troubleshooting
89
+ ### Converting Between Formats
94
90
 
95
- **Command not found:**
96
- ```bash
97
- uv tool update-shell # Fixes PATH automatically
98
- ```
91
+ MoreCompute uses `.py` notebooks with `# %%` cell markers, but you can convert to/from `.ipynb`:
99
92
 
100
- **Manual PATH fix (macOS/Linux):**
93
+ **From .ipynb to .py:**
101
94
  ```bash
102
- echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
103
- source ~/.bashrc
104
- ```
95
+ # Auto-detect output name (notebook.ipynb -> notebook.py)
96
+ more-compute convert notebook.ipynb
105
97
 
106
- **Manual PATH fix (Windows):**
107
- ```powershell
108
- $pythonScripts = python -c "import site; print(site.USER_BASE)"
109
- $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
110
- [Environment]::SetEnvironmentVariable("Path", "$userPath;$pythonScripts\Scripts", "User")
111
- # Restart PowerShell
98
+ # Or specify output
99
+ more-compute convert notebook.ipynb -o my_notebook.py
100
+
101
+ # Then open in MoreCompute
102
+ more-compute my_notebook.py
112
103
  ```
113
104
 
114
- **Port in use:**
105
+ The converter automatically extracts dependencies from `!pip install` commands and adds UV inline script metadata.
106
+
107
+ **From .py to .ipynb:**
115
108
  ```bash
116
- export MORECOMPUTE_PORT=8080 # macOS/Linux
117
- $env:MORECOMPUTE_PORT = "8080" # Windows
109
+ # Auto-detect output name (notebook.py -> notebook.ipynb)
110
+ more-compute convert notebook.py
111
+
112
+ # Or specify output
113
+ more-compute convert notebook.py -o colab_notebook.ipynb
118
114
  ```
119
115
 
116
+ This makes your notebooks compatible with Google Colab, Jupyter, and other tools that require `.ipynb` format.
117
+
118
+ ## Troubleshooting
119
+
120
+ will add things here as things progress...
121
+
120
122
  ## Development
121
123
 
122
124
  ```bash
@@ -125,7 +127,7 @@ cd MORECOMPUTE
125
127
  uv venv && source .venv/bin/activate
126
128
  uv pip install -e .
127
129
  cd frontend && npm install && cd ..
128
- python kernel_run.py notebook.ipynb
130
+ more-compute notebook.py
129
131
  ```
130
132
 
131
133
  ## License
@@ -4,16 +4,12 @@
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
- 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.
8
-
9
-
7
+ An interactive Python notebook environment, similar to Marimo and Google Colab, that runs locally.
10
8
 
11
9
 
12
10
  https://github.com/user-attachments/assets/8c7ec716-dade-4de2-ad37-71d328129c97
13
11
 
14
12
 
15
-
16
-
17
13
  ## Installation
18
14
 
19
15
  **Prerequisites:** [Node.js](https://nodejs.org/) >= 20.10.0 required for web interface
@@ -43,40 +39,46 @@ pip install more-compute
43
39
  ## Usage
44
40
 
45
41
  ```bash
46
- more-compute notebook.ipynb # Open existing notebook
47
- more-compute # Create and open new notebook
42
+ more-compute notebook.py # Open existing notebook
43
+ more-compute new # Create new notebook
48
44
  more-compute --debug # Show logs
49
45
  ```
50
46
 
51
- Opens automatically at http://localhost:8000
47
+ Opens automatically at http://localhost:3141
52
48
 
53
- ## Troubleshooting
49
+ ### Converting Between Formats
54
50
 
55
- **Command not found:**
56
- ```bash
57
- uv tool update-shell # Fixes PATH automatically
58
- ```
51
+ MoreCompute uses `.py` notebooks with `# %%` cell markers, but you can convert to/from `.ipynb`:
59
52
 
60
- **Manual PATH fix (macOS/Linux):**
53
+ **From .ipynb to .py:**
61
54
  ```bash
62
- echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
63
- source ~/.bashrc
64
- ```
55
+ # Auto-detect output name (notebook.ipynb -> notebook.py)
56
+ more-compute convert notebook.ipynb
65
57
 
66
- **Manual PATH fix (Windows):**
67
- ```powershell
68
- $pythonScripts = python -c "import site; print(site.USER_BASE)"
69
- $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
70
- [Environment]::SetEnvironmentVariable("Path", "$userPath;$pythonScripts\Scripts", "User")
71
- # Restart PowerShell
58
+ # Or specify output
59
+ more-compute convert notebook.ipynb -o my_notebook.py
60
+
61
+ # Then open in MoreCompute
62
+ more-compute my_notebook.py
72
63
  ```
73
64
 
74
- **Port in use:**
65
+ The converter automatically extracts dependencies from `!pip install` commands and adds UV inline script metadata.
66
+
67
+ **From .py to .ipynb:**
75
68
  ```bash
76
- export MORECOMPUTE_PORT=8080 # macOS/Linux
77
- $env:MORECOMPUTE_PORT = "8080" # Windows
69
+ # Auto-detect output name (notebook.py -> notebook.ipynb)
70
+ more-compute convert notebook.py
71
+
72
+ # Or specify output
73
+ more-compute convert notebook.py -o colab_notebook.ipynb
78
74
  ```
79
75
 
76
+ This makes your notebooks compatible with Google Colab, Jupyter, and other tools that require `.ipynb` format.
77
+
78
+ ## Troubleshooting
79
+
80
+ will add things here as things progress...
81
+
80
82
  ## Development
81
83
 
82
84
  ```bash
@@ -85,7 +87,7 @@ cd MORECOMPUTE
85
87
  uv venv && source .venv/bin/activate
86
88
  uv pip install -e .
87
89
  cd frontend && npm install && cd ..
88
- python kernel_run.py notebook.ipynb
90
+ more-compute notebook.py
89
91
  ```
90
92
 
91
93
  ## License
@@ -643,7 +643,8 @@ body {
643
643
  }
644
644
 
645
645
  .markdown-rendered code {
646
- background: #f3f4f6;
646
+ background: var(--mc-secondary);
647
+ color: var(--mc-text-color);
647
648
  padding: 2px 4px;
648
649
  border-radius: 3px;
649
650
  font-family: 'Fira', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
@@ -651,7 +652,7 @@ body {
651
652
  }
652
653
 
653
654
  .markdown-rendered pre {
654
- background: #f9fafb;
655
+ background: var(--mc-secondary);
655
656
  padding: 12px;
656
657
  border-radius: 6px;
657
658
  margin: 12px 0;
@@ -665,7 +666,7 @@ body {
665
666
  }
666
667
 
667
668
  .markdown-rendered blockquote {
668
- border-left: 3px solid #d1d5db;
669
+ border-left: 3px solid var(--mc-border);
669
670
  padding-left: 12px;
670
671
  margin: 12px 0;
671
672
  color: var(--mc-markdown-paragraph-color);
@@ -684,7 +685,7 @@ body {
684
685
  }
685
686
 
686
687
  .markdown-rendered .empty-markdown {
687
- color: #9ca3af;
688
+ color: var(--mc-line-number-color);
688
689
  font-style: italic;
689
690
  }
690
691
 
@@ -697,19 +698,20 @@ body {
697
698
  flex-direction: column;
698
699
  align-items: center;
699
700
  font-size: 11px;
700
- color: #6b7280;
701
+ color: var(--mc-line-number-color);
701
702
  width: 30px;
702
703
  }
703
704
 
704
705
  .execution-count {
705
706
  font-family: 'Fira', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
706
707
  font-size: 10px;
707
- color: #9ca3af;
708
+ color: var(--mc-line-number-color);
708
709
  }
709
710
 
710
711
  .execution-time {
711
712
  font-size: 9px;
712
- color: #d1d5db;
713
+ color: var(--mc-line-number-color);
714
+ opacity: 0.7;
713
715
  margin-top: 2px;
714
716
  }
715
717
 
@@ -8,6 +8,7 @@ import PackagesPopup from "@/components/popups/PackagesPopup";
8
8
  import ComputePopup from "@/components/popups/ComputePopup";
9
9
  import MetricsPopup from "@/components/popups/MetricsPopup";
10
10
  import SettingsPopup from "@/components/popups/SettingsPopup";
11
+ import ConfirmModal from "@/components/modals/ConfirmModal";
11
12
  import { ConnectionBanner } from "@/components/layout/ConnectionBanner";
12
13
  import {
13
14
  PodWebSocketProvider,
@@ -22,6 +23,7 @@ const POLL_MS = 3000;
22
23
  function AppContent({ children }: { children: React.ReactNode }) {
23
24
  const [appSettings, setAppSettings] = useState<NotebookSettings>(() => loadSettings());
24
25
  const [activePopup, setActivePopup] = useState<string | null>(null);
26
+ const [showRestartModal, setShowRestartModal] = useState(false);
25
27
  const { connectionState, gpuPods, connectingPodId } = usePodWebSocket();
26
28
 
27
29
  // Persistent metrics collection
@@ -84,6 +86,15 @@ function AppContent({ children }: { children: React.ReactNode }) {
84
86
  setActivePopup(null);
85
87
  };
86
88
 
89
+ const handleRestartKernel = () => {
90
+ // Send reset kernel command via WebSocket
91
+ const ws = new WebSocket('ws://127.0.0.1:3141/ws');
92
+ ws.onopen = () => {
93
+ ws.send(JSON.stringify({ type: 'reset_kernel' }));
94
+ setTimeout(() => ws.close(), 100);
95
+ };
96
+ };
97
+
87
98
  const renderPopup = () => {
88
99
  if (!activePopup) return null;
89
100
 
@@ -176,7 +187,28 @@ function AppContent({ children }: { children: React.ReactNode }) {
176
187
  id="kernel-status-dot"
177
188
  className="status-dot connecting"
178
189
  ></span>
179
- <span id="kernel-status-text" className="status-text">
190
+ <span
191
+ id="kernel-status-text"
192
+ className="status-text"
193
+ data-original-text="Connecting..."
194
+ style={{
195
+ cursor: 'pointer',
196
+ transition: 'all 0.15s ease',
197
+ }}
198
+ onMouseEnter={(e) => {
199
+ const originalText = e.currentTarget.textContent || '';
200
+ e.currentTarget.setAttribute('data-original-text', originalText);
201
+ e.currentTarget.textContent = 'Restart Kernel';
202
+ e.currentTarget.style.color = 'var(--mc-primary)';
203
+ }}
204
+ onMouseLeave={(e) => {
205
+ const originalText = e.currentTarget.getAttribute('data-original-text') || 'Connecting...';
206
+ e.currentTarget.textContent = originalText;
207
+ e.currentTarget.style.color = '';
208
+ }}
209
+ onClick={() => setShowRestartModal(true)}
210
+ title="Click to restart kernel"
211
+ >
180
212
  Connecting...
181
213
  </span>
182
214
  </div>
@@ -197,6 +229,16 @@ function AppContent({ children }: { children: React.ReactNode }) {
197
229
  />
198
230
  </div>
199
231
  </div>
232
+ <ConfirmModal
233
+ isOpen={showRestartModal}
234
+ onClose={() => setShowRestartModal(false)}
235
+ onConfirm={handleRestartKernel}
236
+ title="Restart Kernel"
237
+ message="Are you sure you want to restart the kernel? All variables will be lost."
238
+ confirmLabel="Restart"
239
+ cancelLabel="Cancel"
240
+ isDangerous={true}
241
+ />
200
242
  </>
201
243
  );
202
244
  }
@@ -276,10 +276,18 @@ function notebookReducer(
276
276
  case "EXECUTION_COMPLETE": {
277
277
  const payload = action.payload || {};
278
278
  const cell_index = payload.cell_index;
279
+
280
+ console.log(`[EXECUTION_COMPLETE] Processing completion for cell ${cell_index}`, payload);
281
+
279
282
  // Support both shapes: { result: {...} } and flat payload {...}
280
283
  const result = payload && payload.result ? payload.result : payload || {};
284
+
285
+ console.log(`[EXECUTION_COMPLETE] result.status=${result.status}, result.error=`, result.error);
286
+
287
+ // ALWAYS remove cell from executingCells when completion arrives
281
288
  const newExecuting = new Set(state.executingCells);
282
289
  newExecuting.delete(cell_index);
290
+
283
291
  return {
284
292
  ...state,
285
293
  executingCells: newExecuting,
@@ -310,8 +318,13 @@ function notebookReducer(
310
318
 
311
319
  case "EXECUTION_ERROR": {
312
320
  const { cell_index, error } = action.payload;
321
+
322
+ console.log(`[EXECUTION_ERROR] Processing error for cell ${cell_index}`);
323
+
324
+ // ALWAYS remove cell from executingCells when error arrives
313
325
  const newExecuting = new Set(state.executingCells);
314
326
  newExecuting.delete(cell_index);
327
+
315
328
  const normalizedError = normalizeError(error);
316
329
  return {
317
330
  ...state,
@@ -348,6 +361,8 @@ function notebookReducer(
348
361
  ...cell,
349
362
  outputs: [],
350
363
  execution_count: null,
364
+ execution_time: null,
365
+ error: null,
351
366
  })),
352
367
  };
353
368
 
@@ -435,6 +450,10 @@ export const Notebook: React.FC<NotebookProps> = ({
435
450
  dispatch({ type: "NOTEBOOK_UPDATED", payload: data });
436
451
  }, []);
437
452
 
453
+ const handleKernelRestarted = useCallback(() => {
454
+ dispatch({ type: "RESET_KERNEL" });
455
+ }, []);
456
+
438
457
  const handleHeartbeat = useCallback((data: any) => {
439
458
  // Heartbeat received - execution still in progress
440
459
  // Cell spinner already showing via executingCells set
@@ -473,7 +492,7 @@ export const Notebook: React.FC<NotebookProps> = ({
473
492
  wsRef.current = ws;
474
493
  handleKernelStatusUpdate("connecting");
475
494
 
476
- ws.connect("ws://127.0.0.1:8000/ws")
495
+ ws.connect("ws://127.0.0.1:3141/ws")
477
496
  .then(() => {
478
497
  ws.loadNotebook(notebookName || "default");
479
498
  })
@@ -486,6 +505,7 @@ export const Notebook: React.FC<NotebookProps> = ({
486
505
  ws.on("disconnect", () => handleKernelStatusUpdate("disconnected"));
487
506
  ws.on("notebook_loaded", handleNotebookLoaded);
488
507
  ws.on("notebook_updated", handleNotebookUpdate);
508
+ ws.on("kernel_restarted", handleKernelRestarted);
489
509
  ws.on("execution_start", handleExecutionStart);
490
510
  ws.on("stream_output", handleStreamOutput);
491
511
  ws.on("execution_complete", handleExecutionComplete);
@@ -499,6 +519,7 @@ export const Notebook: React.FC<NotebookProps> = ({
499
519
  handleKernelStatusUpdate,
500
520
  handleNotebookLoaded,
501
521
  handleNotebookUpdate,
522
+ handleKernelRestarted,
502
523
  handleExecutionStart,
503
524
  handleStreamOutput,
504
525
  handleExecuteResult,
@@ -22,7 +22,8 @@ export const CellButton: React.FC<CellButtonProps> = ({
22
22
  const [isHovered, setIsHovered] = useState(false);
23
23
 
24
24
  const handleClick = (e: React.MouseEvent) => {
25
- if (onClick && !disabled && !isLoading) {
25
+ if (onClick && !disabled) {
26
+ // Allow clicks even when loading (for stop/interrupt functionality)
26
27
  onClick(e);
27
28
  }
28
29
  };
@@ -34,7 +35,7 @@ export const CellButton: React.FC<CellButtonProps> = ({
34
35
  onClick={handleClick}
35
36
  onMouseEnter={() => setIsHovered(true)}
36
37
  onMouseLeave={() => setIsHovered(false)}
37
- disabled={disabled || isLoading}
38
+ disabled={disabled}
38
39
  title={title}
39
40
  style={{
40
41
  width: '28px',
@@ -45,9 +46,9 @@ export const CellButton: React.FC<CellButtonProps> = ({
45
46
  display: 'flex',
46
47
  alignItems: 'center',
47
48
  justifyContent: 'center',
48
- cursor: disabled || isLoading ? 'not-allowed' : 'pointer',
49
+ cursor: disabled ? 'not-allowed' : 'pointer',
49
50
  transition: 'background-color 0.15s ease',
50
- opacity: disabled ? 0.5 : 1
51
+ opacity: disabled ? 0.5 : isLoading ? 0.8 : 1
51
52
  }}
52
53
  >
53
54
  {icon}
@@ -12,6 +12,7 @@ import {
12
12
  UpdateIcon,
13
13
  LinkBreak2Icon,
14
14
  PlayIcon,
15
+ StopIcon,
15
16
  ChevronUpIcon,
16
17
  ChevronDownIcon,
17
18
  } from "@radix-ui/react-icons";
@@ -269,13 +270,13 @@ export const MonacoCell: React.FC<CellProps> = ({
269
270
  onExecute(indexRef.current);
270
271
  setIsEditing(false);
271
272
  } else {
272
- if (isExecuting) {
273
- onInterrupt(indexRef.current);
274
- } else {
275
- onExecute(indexRef.current);
276
- }
273
+ onExecute(indexRef.current);
277
274
  }
278
- }, [cell.cell_type, isExecuting, onExecute, onInterrupt]);
275
+ }, [cell.cell_type, onExecute]);
276
+
277
+ const handleInterrupt = useCallback(() => {
278
+ onInterrupt(indexRef.current);
279
+ }, [onInterrupt]);
279
280
 
280
281
  const handleCellClick = () => {
281
282
  onSetActive(indexRef.current);
@@ -583,15 +584,26 @@ export const MonacoCell: React.FC<CellProps> = ({
583
584
  <div className="cell-hover-controls">
584
585
  <div className="cell-actions-right">
585
586
  {!isMarkdownWithContent && (
586
- <CellButton
587
- icon={<PlayIcon className="w-6 h-6" />}
588
- onClick={(e) => {
589
- e.stopPropagation();
590
- handleExecute();
591
- }}
592
- title={isExecuting ? "Stop execution" : "Run cell"}
593
- isLoading={isExecuting}
594
- />
587
+ <>
588
+ <CellButton
589
+ icon={<PlayIcon className="w-6 h-6" />}
590
+ onClick={(e) => {
591
+ e.stopPropagation();
592
+ handleExecute();
593
+ }}
594
+ title="Run cell"
595
+ disabled={isExecuting}
596
+ />
597
+ <CellButton
598
+ icon={<StopIcon className="w-6 h-6" />}
599
+ onClick={(e) => {
600
+ e.stopPropagation();
601
+ handleInterrupt();
602
+ }}
603
+ title="Stop execution"
604
+ disabled={!isExecuting}
605
+ />
606
+ </>
595
607
  )}
596
608
  <CellButton
597
609
  icon={<ChevronUpIcon className="w-6 h-6" />}
@@ -599,8 +611,8 @@ export const MonacoCell: React.FC<CellProps> = ({
599
611
  e.stopPropagation();
600
612
  onMoveUp(indexRef.current);
601
613
  }}
602
- title="Move cell up"
603
- disabled={index === 0}
614
+ title={isExecuting ? "Cannot move while executing" : "Move cell up"}
615
+ disabled={isExecuting || index === 0}
604
616
  />
605
617
  <CellButton
606
618
  icon={<ChevronDownIcon className="w-6 h-6" />}
@@ -608,16 +620,25 @@ export const MonacoCell: React.FC<CellProps> = ({
608
620
  e.stopPropagation();
609
621
  onMoveDown(indexRef.current);
610
622
  }}
611
- title="Move cell down"
612
- disabled={index === totalCells - 1}
623
+ title={isExecuting ? "Cannot move while executing" : "Move cell down"}
624
+ disabled={isExecuting || index === totalCells - 1}
613
625
  />
614
626
  <CellButton
615
627
  icon={<LinkBreak2Icon className="w-5 h-5" />}
616
628
  onClick={(e) => {
617
629
  e.stopPropagation();
618
- onDelete(indexRef.current);
630
+ // If cell is executing, interrupt it first
631
+ if (isExecuting) {
632
+ onInterrupt(indexRef.current);
633
+ // Wait a bit for interrupt to complete, then delete
634
+ setTimeout(() => {
635
+ onDelete(indexRef.current);
636
+ }, 500);
637
+ } else {
638
+ onDelete(indexRef.current);
639
+ }
619
640
  }}
620
- title="Delete cell"
641
+ title={isExecuting ? "Stop and delete cell" : "Delete cell"}
621
642
  />
622
643
  </div>
623
644
  </div>
@@ -165,7 +165,20 @@ const TypedErrorDisplay: FC<{ error: ErrorOutput }> = ({ error }) => {
165
165
  margin: 0
166
166
  }}
167
167
  >
168
- {error.traceback?.join('\n') || ''}
168
+ {/* Always show error name and message */}
169
+ {error.ename && (
170
+ <div style={{ fontWeight: 600, marginBottom: '4px' }}>
171
+ {error.ename}: {error.evalue}
172
+ </div>
173
+ )}
174
+ {/* Show traceback if available */}
175
+ {error.traceback && error.traceback.length > 0 && (
176
+ <div>{error.traceback.join('\n')}</div>
177
+ )}
178
+ {/* Fallback if no traceback */}
179
+ {(!error.traceback || error.traceback.length === 0) && !error.ename && (
180
+ <div>An unknown error occurred</div>
181
+ )}
169
182
  </div>
170
183
  </div>
171
184
  </div>
@@ -95,7 +95,7 @@ export const PodWebSocketProvider: React.FC<PodWebSocketProviderProps> = ({ chil
95
95
  wsRef.current = null;
96
96
  }
97
97
 
98
- const wsUrl = 'ws://127.0.0.1:8000/ws';
98
+ const wsUrl = 'ws://127.0.0.1:3141/ws';
99
99
  const ws = new WebSocket(wsUrl);
100
100
 
101
101
  ws.onopen = () => {
@@ -53,12 +53,12 @@ export class WebSocketService {
53
53
  };
54
54
  }
55
55
 
56
- connect(url: string = 'ws://localhost:8000'): Promise<void> {
56
+ connect(url: string = 'ws://localhost:3141'): Promise<void> {
57
57
  return new Promise((resolve, reject) => {
58
58
  // For development, connect directly to the backend WebSocket
59
59
  const wsUrl = process.env.NODE_ENV === 'production'
60
60
  ? '/ws'
61
- : 'ws://localhost:8000/ws';
61
+ : 'ws://localhost:3141/ws';
62
62
 
63
63
  // Use native WebSocket for FastAPI compatibility
64
64
  const ws = new WebSocket(wsUrl);
@@ -4,11 +4,11 @@ const nextConfig = {
4
4
  return [
5
5
  {
6
6
  source: '/ws/:path*',
7
- destination: 'http://localhost:8000/ws/:path*',
7
+ destination: 'http://localhost:3141/ws/:path*',
8
8
  },
9
9
  {
10
10
  source: '/api/:path*',
11
- destination: 'http://localhost:8000/api/:path*',
11
+ destination: 'http://localhost:3141/api/:path*',
12
12
  },
13
13
  ];
14
14
  },