more-compute 0.2.6__py3-none-any.whl → 0.3.1__py3-none-any.whl
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.
- frontend/app/globals.css +38 -133
- frontend/app/layout.tsx +54 -5
- frontend/components/Notebook.tsx +9 -1
- frontend/components/cell/CellButton.tsx +2 -2
- frontend/components/cell/MonacoCell.tsx +1 -15
- frontend/components/output/CellOutput.tsx +77 -17
- frontend/components/output/ErrorDisplay.tsx +3 -28
- frontend/components/popups/FilterPopup.tsx +24 -24
- frontend/components/popups/MetricsPopup.tsx +42 -7
- frontend/components/popups/PackagesPopup.tsx +2 -1
- frontend/lib/api.ts +6 -2
- frontend/lib/settings.ts +7 -0
- frontend/lib/websocket-native.ts +3 -0
- frontend/styling_README.md +15 -2
- kernel_run.py +26 -13
- {more_compute-0.2.6.dist-info → more_compute-0.3.1.dist-info}/METADATA +1 -1
- {more_compute-0.2.6.dist-info → more_compute-0.3.1.dist-info}/RECORD +29 -27
- morecompute/__version__.py +1 -1
- morecompute/execution/executor.py +12 -5
- morecompute/execution/worker.py +93 -1
- morecompute/server.py +4 -0
- morecompute/utils/cell_magics.py +713 -0
- morecompute/utils/line_magics.py +949 -0
- morecompute/utils/shell_utils.py +68 -0
- morecompute/utils/special_commands.py +106 -173
- frontend/components/Cell.tsx +0 -383
- {more_compute-0.2.6.dist-info → more_compute-0.3.1.dist-info}/WHEEL +0 -0
- {more_compute-0.2.6.dist-info → more_compute-0.3.1.dist-info}/entry_points.txt +0 -0
- {more_compute-0.2.6.dist-info → more_compute-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {more_compute-0.2.6.dist-info → more_compute-0.3.1.dist-info}/top_level.txt +0 -0
frontend/components/Cell.tsx
DELETED
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LEGACY COMPONENT - NO LONGER USED
|
|
3
|
-
*
|
|
4
|
-
* This component has been replaced by MonacoCell.tsx which provides:
|
|
5
|
-
* - Modern Monaco Editor (VSCode engine)
|
|
6
|
-
* - LSP support for Python autocomplete and hover
|
|
7
|
-
* - Better performance and features
|
|
8
|
-
*
|
|
9
|
-
* Kept for reference only. Do not use in new code.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
"use client";
|
|
13
|
-
|
|
14
|
-
import React, { useRef, useEffect, useState } from "react";
|
|
15
|
-
import { Cell as CellType } from "@/types/notebook";
|
|
16
|
-
import CellOutput from "./output/CellOutput";
|
|
17
|
-
import AddCellButton from "./cell/AddCellButton";
|
|
18
|
-
import MarkdownRenderer from "./output/MarkdownRenderer";
|
|
19
|
-
import CellButton from "./cell/CellButton";
|
|
20
|
-
import {
|
|
21
|
-
UpdateIcon,
|
|
22
|
-
LinkBreak2Icon,
|
|
23
|
-
PlayIcon,
|
|
24
|
-
ChevronUpIcon,
|
|
25
|
-
ChevronDownIcon,
|
|
26
|
-
} from "@radix-ui/react-icons";
|
|
27
|
-
import { Check, X } from "lucide-react";
|
|
28
|
-
import { fixIndentation } from "@/lib/api";
|
|
29
|
-
|
|
30
|
-
declare const CodeMirror: any;
|
|
31
|
-
|
|
32
|
-
interface CellProps {
|
|
33
|
-
cell: CellType;
|
|
34
|
-
index: number;
|
|
35
|
-
totalCells: number;
|
|
36
|
-
isActive: boolean;
|
|
37
|
-
isExecuting: boolean;
|
|
38
|
-
onExecute: (index: number) => void;
|
|
39
|
-
onInterrupt: (index: number) => void;
|
|
40
|
-
onDelete: (index: number) => void;
|
|
41
|
-
onUpdate: (index: number, source: string) => void;
|
|
42
|
-
onSetActive: (index: number) => void;
|
|
43
|
-
onAddCell: (type: "code" | "markdown", index: number) => void;
|
|
44
|
-
onMoveUp: (index: number) => void;
|
|
45
|
-
onMoveDown: (index: number) => void;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const Cell: React.FC<CellProps> = ({
|
|
49
|
-
cell,
|
|
50
|
-
index,
|
|
51
|
-
totalCells,
|
|
52
|
-
isActive,
|
|
53
|
-
isExecuting,
|
|
54
|
-
onExecute,
|
|
55
|
-
onDelete,
|
|
56
|
-
onInterrupt,
|
|
57
|
-
onUpdate,
|
|
58
|
-
onSetActive,
|
|
59
|
-
onAddCell,
|
|
60
|
-
onMoveUp,
|
|
61
|
-
onMoveDown,
|
|
62
|
-
}) => {
|
|
63
|
-
// ============================================================================
|
|
64
|
-
// REFS
|
|
65
|
-
// ============================================================================
|
|
66
|
-
const editorRef = useRef<HTMLTextAreaElement>(null);
|
|
67
|
-
const codeMirrorInstance = useRef<{
|
|
68
|
-
setValue: (value: string) => void;
|
|
69
|
-
toTextArea: () => void;
|
|
70
|
-
getValue: () => string;
|
|
71
|
-
} | null>(null);
|
|
72
|
-
const wasEditingMarkdown = useRef(false);
|
|
73
|
-
const indexRef = useRef<number>(index);
|
|
74
|
-
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
75
|
-
|
|
76
|
-
// ============================================================================
|
|
77
|
-
// STATE
|
|
78
|
-
// ============================================================================
|
|
79
|
-
const [isEditing, setIsEditing] = useState(
|
|
80
|
-
() => cell.cell_type === "code" || !cell.source?.trim()
|
|
81
|
-
);
|
|
82
|
-
const [elapsedLabel, setElapsedLabel] = useState<string | null>(
|
|
83
|
-
cell.execution_time ?? null
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
// ============================================================================
|
|
87
|
-
// UTILITIES
|
|
88
|
-
// ============================================================================
|
|
89
|
-
const formatMs = (ms: number): string => {
|
|
90
|
-
if (ms < 1000) return `${ms.toFixed(0)}ms`;
|
|
91
|
-
if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;
|
|
92
|
-
const totalSeconds = Math.floor(ms / 1000);
|
|
93
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
94
|
-
const seconds = totalSeconds % 60;
|
|
95
|
-
return `${minutes}:${seconds.toString().padStart(2, "0")}s`;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const parseExecTime = (s?: string | null): number | null => {
|
|
99
|
-
if (!s) return null;
|
|
100
|
-
if (s.endsWith("ms")) return parseFloat(s.replace("ms", ""));
|
|
101
|
-
if (s.endsWith("s")) return parseFloat(s.replace("s", "")) * 1000;
|
|
102
|
-
return null;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// ============================================================================
|
|
106
|
-
// COMPUTED VALUES
|
|
107
|
-
// ============================================================================
|
|
108
|
-
const isMarkdownWithContent =
|
|
109
|
-
cell.cell_type === "markdown" && !isEditing && cell.source?.trim();
|
|
110
|
-
|
|
111
|
-
// ============================================================================
|
|
112
|
-
// HANDLERS
|
|
113
|
-
// ============================================================================
|
|
114
|
-
const handleExecute = () => {
|
|
115
|
-
if (cell.cell_type === "markdown") {
|
|
116
|
-
onExecute(indexRef.current);
|
|
117
|
-
setIsEditing(false);
|
|
118
|
-
} else {
|
|
119
|
-
if (isExecuting) {
|
|
120
|
-
onInterrupt(indexRef.current);
|
|
121
|
-
} else {
|
|
122
|
-
onExecute(indexRef.current);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const handleCellClick = () => {
|
|
128
|
-
onSetActive(indexRef.current);
|
|
129
|
-
if (cell.cell_type === "markdown") {
|
|
130
|
-
setIsEditing(true);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const handleFixIndentation = async () => {
|
|
135
|
-
try {
|
|
136
|
-
const fixedCode = await fixIndentation(cell.source);
|
|
137
|
-
onUpdate(indexRef.current, fixedCode);
|
|
138
|
-
|
|
139
|
-
if (codeMirrorInstance.current) {
|
|
140
|
-
codeMirrorInstance.current.setValue(fixedCode);
|
|
141
|
-
}
|
|
142
|
-
} catch (err) {
|
|
143
|
-
console.error("Failed to fix indentation:", err);
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// ============================================================================
|
|
148
|
-
// EFFECTS
|
|
149
|
-
// ============================================================================
|
|
150
|
-
|
|
151
|
-
// Keep indexRef in sync
|
|
152
|
-
useEffect(() => {
|
|
153
|
-
indexRef.current = index;
|
|
154
|
-
}, [index]);
|
|
155
|
-
|
|
156
|
-
// Execution timer
|
|
157
|
-
useEffect(() => {
|
|
158
|
-
if (isExecuting) {
|
|
159
|
-
const start = Date.now();
|
|
160
|
-
setElapsedLabel("0ms");
|
|
161
|
-
if (intervalRef.current) clearInterval(intervalRef.current);
|
|
162
|
-
intervalRef.current = setInterval(() => {
|
|
163
|
-
setElapsedLabel(formatMs(Date.now() - start));
|
|
164
|
-
}, 100);
|
|
165
|
-
} else {
|
|
166
|
-
if (intervalRef.current) {
|
|
167
|
-
clearInterval(intervalRef.current);
|
|
168
|
-
intervalRef.current = null;
|
|
169
|
-
}
|
|
170
|
-
const ms = parseExecTime(cell.execution_time as any);
|
|
171
|
-
if (ms != null) setElapsedLabel(formatMs(ms));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return () => {
|
|
175
|
-
if (intervalRef.current) {
|
|
176
|
-
clearInterval(intervalRef.current);
|
|
177
|
-
intervalRef.current = null;
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
}, [isExecuting, cell.execution_time]);
|
|
181
|
-
|
|
182
|
-
// Track when user is editing markdown (for auto-save on click away)
|
|
183
|
-
useEffect(() => {
|
|
184
|
-
if (isActive && cell.cell_type === "markdown" && isEditing) {
|
|
185
|
-
wasEditingMarkdown.current = true;
|
|
186
|
-
}
|
|
187
|
-
}, [isActive, cell.cell_type]);
|
|
188
|
-
|
|
189
|
-
// Auto-save markdown when user clicks away
|
|
190
|
-
useEffect(() => {
|
|
191
|
-
if (
|
|
192
|
-
!isActive &&
|
|
193
|
-
wasEditingMarkdown.current &&
|
|
194
|
-
cell.cell_type === "markdown"
|
|
195
|
-
) {
|
|
196
|
-
if (cell.source?.trim()) {
|
|
197
|
-
onExecute(indexRef.current);
|
|
198
|
-
setIsEditing(false);
|
|
199
|
-
}
|
|
200
|
-
wasEditingMarkdown.current = false;
|
|
201
|
-
}
|
|
202
|
-
}, [isActive, cell.cell_type]);
|
|
203
|
-
|
|
204
|
-
// CodeMirror editor initialization and cleanup
|
|
205
|
-
useEffect(() => {
|
|
206
|
-
if (isEditing) {
|
|
207
|
-
if (
|
|
208
|
-
!codeMirrorInstance.current &&
|
|
209
|
-
editorRef.current &&
|
|
210
|
-
typeof CodeMirror !== "undefined"
|
|
211
|
-
) {
|
|
212
|
-
const editor = CodeMirror.fromTextArea(editorRef.current, {
|
|
213
|
-
mode: cell.cell_type === "code" ? "python" : "text/plain",
|
|
214
|
-
lineNumbers: cell.cell_type === "code",
|
|
215
|
-
theme: "default",
|
|
216
|
-
lineWrapping: true,
|
|
217
|
-
placeholder:
|
|
218
|
-
cell.cell_type === "code" ? "Enter code..." : "Enter markdown...",
|
|
219
|
-
});
|
|
220
|
-
codeMirrorInstance.current = editor;
|
|
221
|
-
|
|
222
|
-
editor.on("change", (instance: any) =>
|
|
223
|
-
onUpdate(indexRef.current, instance.getValue())
|
|
224
|
-
);
|
|
225
|
-
editor.on("focus", () => onSetActive(indexRef.current));
|
|
226
|
-
editor.on("blur", () => {
|
|
227
|
-
if (cell.cell_type === "markdown") {
|
|
228
|
-
if (cell.source?.trim()) {
|
|
229
|
-
// Auto-save on blur
|
|
230
|
-
onExecute(indexRef.current);
|
|
231
|
-
setIsEditing(false);
|
|
232
|
-
}
|
|
233
|
-
// If empty, stay in editing mode but mark as no longer editing
|
|
234
|
-
wasEditingMarkdown.current = false;
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
editor.on("keydown", (instance: any, event: KeyboardEvent) => {
|
|
238
|
-
if (event.shiftKey && event.key === "Enter") {
|
|
239
|
-
event.preventDefault();
|
|
240
|
-
handleExecute();
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
if (editor.getValue() !== cell.source) {
|
|
245
|
-
editor.setValue(cell.source);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
} else {
|
|
249
|
-
if (codeMirrorInstance.current) {
|
|
250
|
-
codeMirrorInstance.current.toTextArea();
|
|
251
|
-
codeMirrorInstance.current = null;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}, [isEditing, cell.source]);
|
|
255
|
-
|
|
256
|
-
// ============================================================================
|
|
257
|
-
// RENDER
|
|
258
|
-
// ============================================================================
|
|
259
|
-
return (
|
|
260
|
-
<div className="cell-wrapper">
|
|
261
|
-
{/* Status Indicator */}
|
|
262
|
-
{!isMarkdownWithContent && (
|
|
263
|
-
<div className="cell-status-indicator">
|
|
264
|
-
<span className="status-indicator">
|
|
265
|
-
<span className="status-bracket">[</span>
|
|
266
|
-
{isExecuting ? (
|
|
267
|
-
<UpdateIcon className="w-1 h-1" />
|
|
268
|
-
) : cell.error ? (
|
|
269
|
-
<X size={14} color="#dc2626" />
|
|
270
|
-
) : cell.execution_count != null ? (
|
|
271
|
-
<Check size={14} color="#16a34a" />
|
|
272
|
-
) : (
|
|
273
|
-
<span
|
|
274
|
-
style={{
|
|
275
|
-
width: "14px",
|
|
276
|
-
height: "14px",
|
|
277
|
-
display: "inline-block",
|
|
278
|
-
}}
|
|
279
|
-
></span>
|
|
280
|
-
)}
|
|
281
|
-
<span className="status-bracket">]</span>
|
|
282
|
-
</span>
|
|
283
|
-
{elapsedLabel && (
|
|
284
|
-
<span className="status-timer" title="Execution time">
|
|
285
|
-
{elapsedLabel}
|
|
286
|
-
</span>
|
|
287
|
-
)}
|
|
288
|
-
</div>
|
|
289
|
-
)}
|
|
290
|
-
|
|
291
|
-
{/* Add Cell Above Button */}
|
|
292
|
-
<div className="add-cell-line add-line-above">
|
|
293
|
-
<AddCellButton onAddCell={(type) => onAddCell(type, indexRef.current)} />
|
|
294
|
-
</div>
|
|
295
|
-
|
|
296
|
-
{/* Main Cell Container */}
|
|
297
|
-
<div
|
|
298
|
-
className={`cell ${isActive ? "active" : ""} ${isExecuting ? "executing" : ""} ${isMarkdownWithContent ? "markdown-display-mode" : ""}`}
|
|
299
|
-
data-cell-index={index}
|
|
300
|
-
>
|
|
301
|
-
{/* Hover Controls */}
|
|
302
|
-
{!isMarkdownWithContent && (
|
|
303
|
-
<div className="cell-hover-controls">
|
|
304
|
-
<div className="cell-actions-right">
|
|
305
|
-
<CellButton
|
|
306
|
-
icon={<PlayIcon className="w-6 h-6" />}
|
|
307
|
-
onClick={(e) => {
|
|
308
|
-
e.stopPropagation();
|
|
309
|
-
handleExecute();
|
|
310
|
-
}}
|
|
311
|
-
title={isExecuting ? "Stop execution" : "Run cell"}
|
|
312
|
-
isLoading={isExecuting}
|
|
313
|
-
/>
|
|
314
|
-
<CellButton
|
|
315
|
-
icon={<ChevronUpIcon className="w-6 h-6" />}
|
|
316
|
-
onClick={(e) => {
|
|
317
|
-
e.stopPropagation();
|
|
318
|
-
onMoveUp(indexRef.current);
|
|
319
|
-
}}
|
|
320
|
-
title="Move cell up"
|
|
321
|
-
disabled={index === 0}
|
|
322
|
-
/>
|
|
323
|
-
<CellButton
|
|
324
|
-
icon={<ChevronDownIcon className="w-6 h-6" />}
|
|
325
|
-
onClick={(e) => {
|
|
326
|
-
e.stopPropagation();
|
|
327
|
-
onMoveDown(indexRef.current);
|
|
328
|
-
}}
|
|
329
|
-
title="Move cell down"
|
|
330
|
-
disabled={index === totalCells - 1}
|
|
331
|
-
/>
|
|
332
|
-
<CellButton
|
|
333
|
-
icon={<LinkBreak2Icon className="w-5 h-5" />}
|
|
334
|
-
onClick={(e) => {
|
|
335
|
-
e.stopPropagation();
|
|
336
|
-
onDelete(indexRef.current);
|
|
337
|
-
}}
|
|
338
|
-
title="Delete cell"
|
|
339
|
-
/>
|
|
340
|
-
</div>
|
|
341
|
-
</div>
|
|
342
|
-
)}
|
|
343
|
-
|
|
344
|
-
{/* Cell Content */}
|
|
345
|
-
<div
|
|
346
|
-
className={`cell-content ${isMarkdownWithContent ? "cursor-pointer" : ""}`}
|
|
347
|
-
onClick={handleCellClick}
|
|
348
|
-
>
|
|
349
|
-
<div className="cell-input">
|
|
350
|
-
{isEditing || cell.cell_type === "code" ? (
|
|
351
|
-
<div
|
|
352
|
-
className={`cell-editor-container ${cell.cell_type === "markdown" ? "markdown-editor-container" : "code-editor-container"}`}
|
|
353
|
-
>
|
|
354
|
-
<textarea
|
|
355
|
-
ref={editorRef}
|
|
356
|
-
defaultValue={cell.source}
|
|
357
|
-
className={`cell-editor ${cell.cell_type === "markdown" ? "markdown-editor" : "code-editor"}`}
|
|
358
|
-
/>
|
|
359
|
-
</div>
|
|
360
|
-
) : (
|
|
361
|
-
<MarkdownRenderer
|
|
362
|
-
source={cell.source}
|
|
363
|
-
onClick={() => setIsEditing(true)}
|
|
364
|
-
/>
|
|
365
|
-
)}
|
|
366
|
-
</div>
|
|
367
|
-
<CellOutput
|
|
368
|
-
outputs={cell.outputs}
|
|
369
|
-
error={cell.error}
|
|
370
|
-
onFixIndentation={handleFixIndentation}
|
|
371
|
-
/>
|
|
372
|
-
</div>
|
|
373
|
-
</div>
|
|
374
|
-
|
|
375
|
-
{/* Add Cell Below Button */}
|
|
376
|
-
<div className="add-cell-line add-line-below">
|
|
377
|
-
<AddCellButton
|
|
378
|
-
onAddCell={(type) => onAddCell(type, indexRef.current + 1)}
|
|
379
|
-
/>
|
|
380
|
-
</div>
|
|
381
|
-
</div>
|
|
382
|
-
);
|
|
383
|
-
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|