more-compute 0.3.3__tar.gz → 0.4.1__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 (114) hide show
  1. {more_compute-0.3.3 → more_compute-0.4.1}/MANIFEST.in +4 -0
  2. {more_compute-0.3.3/more_compute.egg-info → more_compute-0.4.1}/PKG-INFO +35 -33
  3. more_compute-0.4.1/README.md +97 -0
  4. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/app/globals.css +9 -7
  5. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/app/layout.tsx +43 -1
  6. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/Notebook.tsx +22 -1
  7. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/cell/CellButton.tsx +5 -4
  8. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/cell/MonacoCell.tsx +42 -21
  9. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/output/ErrorDisplay.tsx +14 -1
  10. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/contexts/PodWebSocketContext.tsx +1 -1
  11. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/lib/websocket.ts +2 -2
  12. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/next.config.mjs +2 -2
  13. {more_compute-0.3.3 → more_compute-0.4.1}/kernel_run.py +107 -17
  14. {more_compute-0.3.3 → more_compute-0.4.1/more_compute.egg-info}/PKG-INFO +35 -33
  15. {more_compute-0.3.3 → more_compute-0.4.1}/more_compute.egg-info/SOURCES.txt +2 -1
  16. more_compute-0.4.1/morecompute/__version__.py +1 -0
  17. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/execution/executor.py +113 -44
  18. more_compute-0.4.1/morecompute/execution/worker.py +616 -0
  19. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/notebook.py +65 -6
  20. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/server.py +72 -40
  21. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/cell_magics.py +35 -4
  22. more_compute-0.4.1/morecompute/utils/notebook_converter.py +129 -0
  23. more_compute-0.4.1/morecompute/utils/py_percent_parser.py +190 -0
  24. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/special_commands.py +126 -49
  25. {more_compute-0.3.3 → more_compute-0.4.1}/pyproject.toml +1 -3
  26. {more_compute-0.3.3 → more_compute-0.4.1}/setup.py +1 -3
  27. more_compute-0.3.3/README.md +0 -93
  28. more_compute-0.3.3/frontend/.DS_Store +0 -0
  29. more_compute-0.3.3/morecompute/__version__.py +0 -1
  30. more_compute-0.3.3/morecompute/execution/worker.py +0 -404
  31. {more_compute-0.3.3 → more_compute-0.4.1}/LICENSE +0 -0
  32. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/.gitignore +0 -0
  33. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/README.md +0 -0
  34. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/__init__.py +0 -0
  35. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/app/favicon.ico +0 -0
  36. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/app/page.tsx +0 -0
  37. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/cell/AddCellButton.tsx +0 -0
  38. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/layout/ConnectionBanner.tsx +0 -0
  39. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/layout/Sidebar.tsx +0 -0
  40. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/modals/ConfirmModal.tsx +0 -0
  41. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/modals/ErrorModal.tsx +0 -0
  42. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/modals/SuccessModal.tsx +0 -0
  43. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/output/CellOutput.tsx +0 -0
  44. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/output/MarkdownRenderer.tsx +0 -0
  45. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/popups/ComputePopup.tsx +0 -0
  46. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/popups/FilterPopup.tsx +0 -0
  47. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/popups/FolderPopup.tsx +0 -0
  48. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/popups/MetricsPopup.tsx +0 -0
  49. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/popups/PackagesPopup.tsx +0 -0
  50. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/components/popups/SettingsPopup.tsx +0 -0
  51. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/eslint.config.mjs +0 -0
  52. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/lib/api.ts +0 -0
  53. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/lib/monaco-themes.ts +0 -0
  54. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/lib/settings.ts +0 -0
  55. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/lib/themes.json +0 -0
  56. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/lib/websocket-native.ts +0 -0
  57. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/next-env.d.ts +0 -0
  58. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/next.config.ts +0 -0
  59. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/package-lock.json +0 -0
  60. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/package.json +0 -0
  61. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/postcss.config.mjs +0 -0
  62. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/add.svg +0 -0
  63. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/check.svg +0 -0
  64. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/copy.svg +0 -0
  65. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/folder.svg +0 -0
  66. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/metric.svg +0 -0
  67. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/packages.svg +0 -0
  68. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/play.svg +0 -0
  69. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/python.svg +0 -0
  70. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/setting.svg +0 -0
  71. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/stop.svg +0 -0
  72. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/trash.svg +0 -0
  73. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/up-down.svg +0 -0
  74. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/assets/icons/x.svg +0 -0
  75. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/file.svg +0 -0
  76. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/fonts/Fira.ttf +0 -0
  77. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/fonts/Tiempos.woff2 +0 -0
  78. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/fonts/VeraMono.ttf +0 -0
  79. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/globe.svg +0 -0
  80. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/next.svg +0 -0
  81. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/vercel.svg +0 -0
  82. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/public/window.svg +0 -0
  83. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/styling_README.md +0 -0
  84. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/tailwind.config.ts +0 -0
  85. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/tsconfig.json +0 -0
  86. {more_compute-0.3.3 → more_compute-0.4.1}/frontend/types/notebook.ts +0 -0
  87. {more_compute-0.3.3 → more_compute-0.4.1}/more_compute.egg-info/dependency_links.txt +0 -0
  88. {more_compute-0.3.3 → more_compute-0.4.1}/more_compute.egg-info/entry_points.txt +0 -0
  89. {more_compute-0.3.3 → more_compute-0.4.1}/more_compute.egg-info/requires.txt +0 -0
  90. {more_compute-0.3.3 → more_compute-0.4.1}/more_compute.egg-info/top_level.txt +0 -0
  91. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/__init__.py +0 -0
  92. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/cli.py +0 -0
  93. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/execution/__init__.py +0 -0
  94. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/execution/__main__.py +0 -0
  95. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/models/__init__.py +0 -0
  96. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/models/api_models.py +0 -0
  97. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/process_worker.py +0 -0
  98. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/services/data_manager.py +0 -0
  99. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/services/lsp_service.py +0 -0
  100. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/services/pod_manager.py +0 -0
  101. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/services/pod_monitor.py +0 -0
  102. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/services/prime_intellect.py +0 -0
  103. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/static/styles.css +0 -0
  104. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/__init__.py +0 -0
  105. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/cache_util.py +0 -0
  106. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/config_util.py +0 -0
  107. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/error_utils.py +0 -0
  108. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/line_magics.py +0 -0
  109. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/notebook_util.py +0 -0
  110. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/python_environment_util.py +0 -0
  111. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/shell_utils.py +0 -0
  112. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/system_environment_util.py +0 -0
  113. {more_compute-0.3.3 → more_compute-0.4.1}/morecompute/utils/zmq_util.py +0 -0
  114. {more_compute-0.3.3 → more_compute-0.4.1}/setup.cfg +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.1
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
@@ -15,12 +15,10 @@ Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.8
19
- Classifier: Programming Language :: Python :: 3.9
20
18
  Classifier: Programming Language :: Python :: 3.10
21
19
  Classifier: Programming Language :: Python :: 3.11
22
20
  Classifier: Programming Language :: Python :: 3.12
23
- Requires-Python: >=3.8
21
+ Requires-Python: >=3.10
24
22
  Description-Content-Type: text/markdown
25
23
  License-File: LICENSE
26
24
  Requires-Dist: fastapi>=0.104.0
@@ -41,22 +39,20 @@ Dynamic: requires-python
41
39
  # more-compute
42
40
 
43
41
  [![PyPI version](https://badge.fury.io/py/more-compute.svg)](https://pypi.org/project/more-compute/)
44
- [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
42
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
45
43
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
46
44
 
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
-
45
+ An interactive Python notebook environment, similar to Marimo and Google Colab, that runs locally.
50
46
 
51
47
 
52
48
  https://github.com/user-attachments/assets/8c7ec716-dade-4de2-ad37-71d328129c97
53
49
 
54
50
 
55
-
56
-
57
51
  ## Installation
58
52
 
59
- **Prerequisites:** [Node.js](https://nodejs.org/) >= 20.10.0 required for web interface
53
+ **Prerequisites:**
54
+ - [Node.js](https://nodejs.org/) >= 20.10.0 required for web interface
55
+ - Python >= 3.10 (uv installs this automatically, pip users need to install manually)
60
56
 
61
57
  ### Using uv (Recommended)
62
58
 
@@ -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
@@ -0,0 +1,97 @@
1
+ # more-compute
2
+
3
+ [![PyPI version](https://badge.fury.io/py/more-compute.svg)](https://pypi.org/project/more-compute/)
4
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ An interactive Python notebook environment, similar to Marimo and Google Colab, that runs locally.
8
+
9
+
10
+ https://github.com/user-attachments/assets/8c7ec716-dade-4de2-ad37-71d328129c97
11
+
12
+
13
+ ## Installation
14
+
15
+ **Prerequisites:**
16
+ - [Node.js](https://nodejs.org/) >= 20.10.0 required for web interface
17
+ - Python >= 3.10 (uv installs this automatically, pip users need to install manually)
18
+
19
+ ### Using uv (Recommended)
20
+
21
+ ```bash
22
+ # macOS/Linux
23
+ curl -LsSf https://astral.sh/uv/install.sh | sh
24
+ uv tool install more-compute
25
+
26
+ # Windows
27
+ powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
28
+ uv tool install more-compute
29
+ ```
30
+
31
+ ### Using pip
32
+
33
+ ```bash
34
+ pip install more-compute
35
+
36
+ # Add to PATH if needed:
37
+ # macOS/Linux: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
38
+ # Windows: See troubleshooting below
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ```bash
44
+ more-compute notebook.py # Open existing notebook
45
+ more-compute new # Create new notebook
46
+ more-compute --debug # Show logs
47
+ ```
48
+
49
+ Opens automatically at http://localhost:3141
50
+
51
+ ### Converting Between Formats
52
+
53
+ MoreCompute uses `.py` notebooks with `# %%` cell markers, but you can convert to/from `.ipynb`:
54
+
55
+ **From .ipynb to .py:**
56
+ ```bash
57
+ # Auto-detect output name (notebook.ipynb -> notebook.py)
58
+ more-compute convert notebook.ipynb
59
+
60
+ # Or specify output
61
+ more-compute convert notebook.ipynb -o my_notebook.py
62
+
63
+ # Then open in MoreCompute
64
+ more-compute my_notebook.py
65
+ ```
66
+
67
+ The converter automatically extracts dependencies from `!pip install` commands and adds UV inline script metadata.
68
+
69
+ **From .py to .ipynb:**
70
+ ```bash
71
+ # Auto-detect output name (notebook.py -> notebook.ipynb)
72
+ more-compute convert notebook.py
73
+
74
+ # Or specify output
75
+ more-compute convert notebook.py -o colab_notebook.ipynb
76
+ ```
77
+
78
+ This makes your notebooks compatible with Google Colab, Jupyter, and other tools that require `.ipynb` format.
79
+
80
+ ## Troubleshooting
81
+
82
+ will add things here as things progress...
83
+
84
+ ## Development
85
+
86
+ ```bash
87
+ git clone https://github.com/DannyMang/MORECOMPUTE.git
88
+ cd MORECOMPUTE
89
+ uv venv && source .venv/bin/activate
90
+ uv pip install -e .
91
+ cd frontend && npm install && cd ..
92
+ more-compute notebook.py
93
+ ```
94
+
95
+ ## License
96
+
97
+ MIT - see [LICENSE](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
  },