|
7 | 7 | import CodeBlock from './CodeBlock.svelte'; |
8 | 8 | import Icon from './Icon.svelte'; |
9 | 9 | import { tooltip } from './Tooltip.svelte'; |
10 | | - import ConsoleOutput, { type LogEntry } from './ConsoleOutput.svelte'; |
11 | 10 | import { notebookStore, type CellStatus } from '$lib/stores/notebookStore'; |
12 | 11 | import { pyodideState } from '$lib/stores/pyodideStore'; |
13 | 12 | import CellOutput from '$lib/components/notebook/CellOutput.svelte'; |
|
41 | 40 | let codeBlockRef = $state<{ getCurrentCode: () => string } | undefined>(undefined); |
42 | 41 |
|
43 | 42 | // Execution output state |
44 | | - let consoleLogs = $state<LogEntry[]>([]); |
45 | | - let nextLogId = 0; |
| 43 | + let stdout = $state(''); |
| 44 | + let stderr = $state(''); |
46 | 45 | let plots = $state<string[]>([]); |
47 | 46 | let error = $state<{ message: string; traceback?: string } | null>(null); |
48 | 47 | let duration = $state<number | null>(null); |
|
56 | 55 | // Computed states |
57 | 56 | let isRunning = $derived(cellState.status === 'running'); |
58 | 57 | let isPending = $derived(cellState.status === 'pending'); |
59 | | - let hasLiveOutput = $derived(consoleLogs.length > 0 || plots.length > 0 || error); |
| 58 | + let hasLiveOutput = $derived(stdout || stderr || plots.length > 0 || error); |
60 | 59 | let showStaticOutputs = $derived(!hasLiveOutput && staticOutputs.length > 0); |
61 | 60 | let hasOutput = $derived(hasLiveOutput || showStaticOutputs); |
62 | 61 |
|
63 | | - function addLog(message: string, level: LogEntry['level']) { |
64 | | - consoleLogs = [...consoleLogs, { id: nextLogId++, level, message }]; |
65 | | - } |
66 | | -
|
67 | | - function clearLogs() { |
68 | | - consoleLogs = []; |
69 | | - nextLogId = 0; |
70 | | - } |
71 | | -
|
72 | 62 | /** |
73 | 63 | * Execute this cell's code (called by store during prerequisite chain) |
74 | 64 | */ |
75 | 65 | async function executeCell(): Promise<void> { |
76 | 66 | const codeToRun = codeBlockRef?.getCurrentCode() ?? code; |
77 | 67 |
|
78 | 68 | // Clear previous output |
79 | | - clearLogs(); |
| 69 | + stdout = ''; |
| 70 | + stderr = ''; |
80 | 71 | plots = []; |
81 | 72 | error = null; |
82 | 73 | duration = null; |
|
93 | 84 | // Execute with streaming callbacks for real-time output |
94 | 85 | const result = await execute(codeToRun, { |
95 | 86 | onStdout: (text) => { |
96 | | - addLog(text, 'output'); |
| 87 | + stdout = stdout ? stdout + '\n' + text : text; |
97 | 88 | }, |
98 | 89 | onStderr: (text) => { |
99 | | - addLog(text, 'warning'); |
| 90 | + stderr = stderr ? stderr + '\n' + text : text; |
100 | 91 | }, |
101 | 92 | onPlot: (data) => { |
102 | 93 | plots = [...plots, data]; |
103 | 94 | } |
104 | 95 | }); |
105 | 96 |
|
106 | | - // Final result - add any output that wasn't streamed |
107 | | - if (result.stdout && consoleLogs.every((log) => log.message !== result.stdout)) { |
108 | | - addLog(result.stdout, 'output'); |
109 | | - } |
110 | | - if (result.stderr && consoleLogs.every((log) => log.message !== result.stderr)) { |
111 | | - addLog(result.stderr, 'warning'); |
112 | | - } |
| 97 | + // Final result (in case any output was missed) |
| 98 | + stdout = result.stdout; |
| 99 | + stderr = result.stderr; |
113 | 100 | plots = result.plots; |
114 | 101 | duration = result.duration; |
115 | 102 |
|
|
146 | 133 | } |
147 | 134 |
|
148 | 135 | function clearOutput() { |
149 | | - clearLogs(); |
| 136 | + stdout = ''; |
| 137 | + stderr = ''; |
150 | 138 | plots = []; |
151 | 139 | error = null; |
152 | 140 | duration = null; |
|
222 | 210 | </div> |
223 | 211 | {/if} |
224 | 212 |
|
225 | | - {#if consoleLogs.length > 0} |
| 213 | + {#if stdout} |
226 | 214 | <div class="output-panel"> |
227 | 215 | <div class="panel-header"> |
228 | 216 | <span>Output</span> |
229 | 217 | <div class="header-actions"> |
230 | 218 | {#if duration !== null} |
231 | 219 | <span class="duration">{duration}ms</span> |
232 | 220 | {/if} |
233 | | - <button class="icon-btn" onclick={clearLogs} use:tooltip={'Clear'}> |
| 221 | + <button class="icon-btn" onclick={clearOutput} use:tooltip={'Clear'}> |
| 222 | + <Icon name="x" size={14} /> |
| 223 | + </button> |
| 224 | + </div> |
| 225 | + </div> |
| 226 | + <div class="panel-body"> |
| 227 | + <div class="output-text">{stdout}</div> |
| 228 | + </div> |
| 229 | + </div> |
| 230 | + {/if} |
| 231 | + |
| 232 | + {#if stderr && !error} |
| 233 | + <div class="output-panel warning"> |
| 234 | + <div class="panel-header"> |
| 235 | + <span>Stderr</span> |
| 236 | + <div class="header-actions"> |
| 237 | + <button class="icon-btn" onclick={clearOutput} use:tooltip={'Clear'}> |
234 | 238 | <Icon name="x" size={14} /> |
235 | 239 | </button> |
236 | 240 | </div> |
237 | 241 | </div> |
238 | | - <ConsoleOutput logs={consoleLogs} maxHeight={200} /> |
| 242 | + <div class="panel-body"> |
| 243 | + <div class="output-text stderr">{stderr}</div> |
| 244 | + </div> |
239 | 245 | </div> |
240 | 246 | {/if} |
241 | 247 |
|
|
325 | 331 | padding: 0; |
326 | 332 | } |
327 | 333 |
|
| 334 | + /* output-text styles are in app.css global rules */ |
| 335 | +
|
328 | 336 | /* Duration in header */ |
329 | 337 | .output-panel .duration { |
330 | 338 | font-family: var(--font-ui); |
|
355 | 363 | border-top: 1px solid var(--border); |
356 | 364 | } |
357 | 365 |
|
| 366 | + /* Warning panel (stderr) */ |
| 367 | + .output-panel.warning { |
| 368 | + background: var(--warning-bg); |
| 369 | + } |
| 370 | +
|
| 371 | + .output-panel.warning .panel-header { |
| 372 | + background: transparent; |
| 373 | + color: var(--warning); |
| 374 | + } |
| 375 | +
|
| 376 | + .output-panel.warning .output-text { |
| 377 | + color: var(--warning); |
| 378 | + } |
| 379 | +
|
358 | 380 | /* Plots */ |
359 | 381 | .plots-body { |
360 | 382 | display: flex; |
|
369 | 391 | border-radius: var(--radius-sm); |
370 | 392 | background: transparent; |
371 | 393 | } |
| 394 | +
|
| 395 | + /* Output text styling */ |
| 396 | + .output-text { |
| 397 | + font-family: var(--font-mono); |
| 398 | + font-size: var(--font-base); |
| 399 | + font-weight: 400; |
| 400 | + line-height: 1.5; |
| 401 | + margin: 0; |
| 402 | + padding: var(--space-md); |
| 403 | + color: var(--text-muted); |
| 404 | + white-space: pre-wrap; |
| 405 | + word-break: break-word; |
| 406 | + } |
| 407 | +
|
| 408 | + .output-text.stderr { |
| 409 | + color: var(--warning); |
| 410 | + } |
372 | 411 | </style> |
0 commit comments