more-compute 0.3.2__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 (115) hide show
  1. {more_compute-0.3.2 → more_compute-0.4.0}/MANIFEST.in +4 -0
  2. {more_compute-0.3.2/more_compute.egg-info → more_compute-0.4.0}/PKG-INFO +34 -24
  3. more_compute-0.4.0/README.md +95 -0
  4. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/app/globals.css +10 -7
  5. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/app/layout.tsx +43 -1
  6. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/Notebook.tsx +22 -1
  7. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/cell/CellButton.tsx +5 -4
  8. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/cell/MonacoCell.tsx +42 -21
  9. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/output/ErrorDisplay.tsx +14 -1
  10. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/contexts/PodWebSocketContext.tsx +1 -1
  11. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/lib/websocket.ts +2 -2
  12. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/next.config.mjs +2 -2
  13. {more_compute-0.3.2 → more_compute-0.4.0}/kernel_run.py +153 -18
  14. {more_compute-0.3.2 → more_compute-0.4.0/more_compute.egg-info}/PKG-INFO +34 -24
  15. {more_compute-0.3.2 → 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.2 → more_compute-0.4.0}/morecompute/execution/executor.py +114 -44
  18. more_compute-0.4.0/morecompute/execution/worker.py +616 -0
  19. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/notebook.py +65 -6
  20. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/server.py +76 -44
  21. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/services/pod_manager.py +2 -2
  22. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/cell_magics.py +35 -4
  23. more_compute-0.4.0/morecompute/utils/config_util.py +75 -0
  24. more_compute-0.4.0/morecompute/utils/notebook_converter.py +129 -0
  25. more_compute-0.4.0/morecompute/utils/py_percent_parser.py +190 -0
  26. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/special_commands.py +126 -49
  27. more_compute-0.3.2/README.md +0 -85
  28. more_compute-0.3.2/frontend/.DS_Store +0 -0
  29. more_compute-0.3.2/morecompute/__version__.py +0 -1
  30. more_compute-0.3.2/morecompute/execution/worker.py +0 -404
  31. more_compute-0.3.2/morecompute/utils/config_util.py +0 -59
  32. {more_compute-0.3.2 → more_compute-0.4.0}/LICENSE +0 -0
  33. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/.gitignore +0 -0
  34. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/README.md +0 -0
  35. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/__init__.py +0 -0
  36. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/app/favicon.ico +0 -0
  37. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/app/page.tsx +0 -0
  38. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/cell/AddCellButton.tsx +0 -0
  39. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/layout/ConnectionBanner.tsx +0 -0
  40. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/layout/Sidebar.tsx +0 -0
  41. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/modals/ConfirmModal.tsx +0 -0
  42. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/modals/ErrorModal.tsx +0 -0
  43. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/modals/SuccessModal.tsx +0 -0
  44. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/output/CellOutput.tsx +0 -0
  45. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/output/MarkdownRenderer.tsx +0 -0
  46. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/popups/ComputePopup.tsx +0 -0
  47. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/popups/FilterPopup.tsx +0 -0
  48. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/popups/FolderPopup.tsx +0 -0
  49. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/popups/MetricsPopup.tsx +0 -0
  50. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/popups/PackagesPopup.tsx +0 -0
  51. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/components/popups/SettingsPopup.tsx +0 -0
  52. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/eslint.config.mjs +0 -0
  53. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/lib/api.ts +0 -0
  54. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/lib/monaco-themes.ts +0 -0
  55. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/lib/settings.ts +0 -0
  56. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/lib/themes.json +0 -0
  57. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/lib/websocket-native.ts +0 -0
  58. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/next-env.d.ts +0 -0
  59. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/next.config.ts +0 -0
  60. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/package-lock.json +0 -0
  61. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/package.json +0 -0
  62. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/postcss.config.mjs +0 -0
  63. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/add.svg +0 -0
  64. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/check.svg +0 -0
  65. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/copy.svg +0 -0
  66. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/folder.svg +0 -0
  67. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/metric.svg +0 -0
  68. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/packages.svg +0 -0
  69. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/play.svg +0 -0
  70. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/python.svg +0 -0
  71. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/setting.svg +0 -0
  72. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/stop.svg +0 -0
  73. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/trash.svg +0 -0
  74. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/up-down.svg +0 -0
  75. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/assets/icons/x.svg +0 -0
  76. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/file.svg +0 -0
  77. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/fonts/Fira.ttf +0 -0
  78. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/fonts/Tiempos.woff2 +0 -0
  79. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/fonts/VeraMono.ttf +0 -0
  80. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/globe.svg +0 -0
  81. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/next.svg +0 -0
  82. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/vercel.svg +0 -0
  83. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/public/window.svg +0 -0
  84. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/styling_README.md +0 -0
  85. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/tailwind.config.ts +0 -0
  86. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/tsconfig.json +0 -0
  87. {more_compute-0.3.2 → more_compute-0.4.0}/frontend/types/notebook.ts +0 -0
  88. {more_compute-0.3.2 → more_compute-0.4.0}/more_compute.egg-info/dependency_links.txt +0 -0
  89. {more_compute-0.3.2 → more_compute-0.4.0}/more_compute.egg-info/entry_points.txt +0 -0
  90. {more_compute-0.3.2 → more_compute-0.4.0}/more_compute.egg-info/requires.txt +0 -0
  91. {more_compute-0.3.2 → more_compute-0.4.0}/more_compute.egg-info/top_level.txt +0 -0
  92. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/__init__.py +0 -0
  93. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/cli.py +0 -0
  94. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/execution/__init__.py +0 -0
  95. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/execution/__main__.py +0 -0
  96. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/models/__init__.py +0 -0
  97. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/models/api_models.py +0 -0
  98. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/process_worker.py +0 -0
  99. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/services/data_manager.py +0 -0
  100. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/services/lsp_service.py +0 -0
  101. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/services/pod_monitor.py +0 -0
  102. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/services/prime_intellect.py +0 -0
  103. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/static/styles.css +0 -0
  104. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/__init__.py +0 -0
  105. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/cache_util.py +0 -0
  106. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/error_utils.py +0 -0
  107. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/line_magics.py +0 -0
  108. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/notebook_util.py +0 -0
  109. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/python_environment_util.py +0 -0
  110. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/shell_utils.py +0 -0
  111. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/system_environment_util.py +0 -0
  112. {more_compute-0.3.2 → more_compute-0.4.0}/morecompute/utils/zmq_util.py +0 -0
  113. {more_compute-0.3.2 → more_compute-0.4.0}/pyproject.toml +0 -0
  114. {more_compute-0.3.2 → more_compute-0.4.0}/setup.cfg +0 -0
  115. {more_compute-0.3.2 → 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.2
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,7 +44,11 @@ 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.
47
+ An interactive Python notebook environment, similar to Marimo and Google Colab, that runs locally.
48
+
49
+
50
+ https://github.com/user-attachments/assets/8c7ec716-dade-4de2-ad37-71d328129c97
51
+
48
52
 
49
53
  ## Installation
50
54
 
@@ -75,40 +79,46 @@ pip install more-compute
75
79
  ## Usage
76
80
 
77
81
  ```bash
78
- more-compute notebook.ipynb # Open existing notebook
79
- more-compute # Create and open new notebook
82
+ more-compute notebook.py # Open existing notebook
83
+ more-compute new # Create new notebook
80
84
  more-compute --debug # Show logs
81
85
  ```
82
86
 
83
- Opens automatically at http://localhost:8000
87
+ Opens automatically at http://localhost:3141
84
88
 
85
- ## Troubleshooting
89
+ ### Converting Between Formats
86
90
 
87
- **Command not found:**
88
- ```bash
89
- uv tool update-shell # Fixes PATH automatically
90
- ```
91
+ MoreCompute uses `.py` notebooks with `# %%` cell markers, but you can convert to/from `.ipynb`:
91
92
 
92
- **Manual PATH fix (macOS/Linux):**
93
+ **From .ipynb to .py:**
93
94
  ```bash
94
- echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
95
- source ~/.bashrc
96
- ```
95
+ # Auto-detect output name (notebook.ipynb -> notebook.py)
96
+ more-compute convert notebook.ipynb
97
97
 
98
- **Manual PATH fix (Windows):**
99
- ```powershell
100
- $pythonScripts = python -c "import site; print(site.USER_BASE)"
101
- $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
102
- [Environment]::SetEnvironmentVariable("Path", "$userPath;$pythonScripts\Scripts", "User")
103
- # 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
104
103
  ```
105
104
 
106
- **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:**
107
108
  ```bash
108
- export MORECOMPUTE_PORT=8080 # macOS/Linux
109
- $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
110
114
  ```
111
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
+
112
122
  ## Development
113
123
 
114
124
  ```bash
@@ -117,7 +127,7 @@ cd MORECOMPUTE
117
127
  uv venv && source .venv/bin/activate
118
128
  uv pip install -e .
119
129
  cd frontend && npm install && cd ..
120
- python kernel_run.py notebook.ipynb
130
+ more-compute notebook.py
121
131
  ```
122
132
 
123
133
  ## License
@@ -0,0 +1,95 @@
1
+ # more-compute
2
+
3
+ [![PyPI version](https://badge.fury.io/py/more-compute.svg)](https://pypi.org/project/more-compute/)
4
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-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:** [Node.js](https://nodejs.org/) >= 20.10.0 required for web interface
16
+
17
+ ### Using uv (Recommended)
18
+
19
+ ```bash
20
+ # macOS/Linux
21
+ curl -LsSf https://astral.sh/uv/install.sh | sh
22
+ uv tool install more-compute
23
+
24
+ # Windows
25
+ powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
26
+ uv tool install more-compute
27
+ ```
28
+
29
+ ### Using pip
30
+
31
+ ```bash
32
+ pip install more-compute
33
+
34
+ # Add to PATH if needed:
35
+ # macOS/Linux: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
36
+ # Windows: See troubleshooting below
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ ```bash
42
+ more-compute notebook.py # Open existing notebook
43
+ more-compute new # Create new notebook
44
+ more-compute --debug # Show logs
45
+ ```
46
+
47
+ Opens automatically at http://localhost:3141
48
+
49
+ ### Converting Between Formats
50
+
51
+ MoreCompute uses `.py` notebooks with `# %%` cell markers, but you can convert to/from `.ipynb`:
52
+
53
+ **From .ipynb to .py:**
54
+ ```bash
55
+ # Auto-detect output name (notebook.ipynb -> notebook.py)
56
+ more-compute convert notebook.ipynb
57
+
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
63
+ ```
64
+
65
+ The converter automatically extracts dependencies from `!pip install` commands and adds UV inline script metadata.
66
+
67
+ **From .py to .ipynb:**
68
+ ```bash
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
74
+ ```
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
+
82
+ ## Development
83
+
84
+ ```bash
85
+ git clone https://github.com/DannyMang/MORECOMPUTE.git
86
+ cd MORECOMPUTE
87
+ uv venv && source .venv/bin/activate
88
+ uv pip install -e .
89
+ cd frontend && npm install && cd ..
90
+ more-compute notebook.py
91
+ ```
92
+
93
+ ## License
94
+
95
+ MIT - see [LICENSE](LICENSE)
@@ -111,6 +111,7 @@ body {
111
111
  display: flex;
112
112
  align-items: center;
113
113
  gap: 6px;
114
+ pointer-events: none; /* Allow clicks to pass through */
114
115
  }
115
116
 
116
117
  .connection-banner.provisioning {
@@ -642,7 +643,8 @@ body {
642
643
  }
643
644
 
644
645
  .markdown-rendered code {
645
- background: #f3f4f6;
646
+ background: var(--mc-secondary);
647
+ color: var(--mc-text-color);
646
648
  padding: 2px 4px;
647
649
  border-radius: 3px;
648
650
  font-family: 'Fira', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
@@ -650,7 +652,7 @@ body {
650
652
  }
651
653
 
652
654
  .markdown-rendered pre {
653
- background: #f9fafb;
655
+ background: var(--mc-secondary);
654
656
  padding: 12px;
655
657
  border-radius: 6px;
656
658
  margin: 12px 0;
@@ -664,7 +666,7 @@ body {
664
666
  }
665
667
 
666
668
  .markdown-rendered blockquote {
667
- border-left: 3px solid #d1d5db;
669
+ border-left: 3px solid var(--mc-border);
668
670
  padding-left: 12px;
669
671
  margin: 12px 0;
670
672
  color: var(--mc-markdown-paragraph-color);
@@ -683,7 +685,7 @@ body {
683
685
  }
684
686
 
685
687
  .markdown-rendered .empty-markdown {
686
- color: #9ca3af;
688
+ color: var(--mc-line-number-color);
687
689
  font-style: italic;
688
690
  }
689
691
 
@@ -696,19 +698,20 @@ body {
696
698
  flex-direction: column;
697
699
  align-items: center;
698
700
  font-size: 11px;
699
- color: #6b7280;
701
+ color: var(--mc-line-number-color);
700
702
  width: 30px;
701
703
  }
702
704
 
703
705
  .execution-count {
704
706
  font-family: 'Fira', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
705
707
  font-size: 10px;
706
- color: #9ca3af;
708
+ color: var(--mc-line-number-color);
707
709
  }
708
710
 
709
711
  .execution-time {
710
712
  font-size: 9px;
711
- color: #d1d5db;
713
+ color: var(--mc-line-number-color);
714
+ opacity: 0.7;
712
715
  margin-top: 2px;
713
716
  }
714
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
  },