diff --git a/services/web/frontend/js/features/ide-react/components/editor/python/pyodide-worker-client.ts b/services/web/frontend/js/features/ide-react/components/editor/python/pyodide-worker-client.ts
index c31864692b..6b099cff6b 100644
--- a/services/web/frontend/js/features/ide-react/components/editor/python/pyodide-worker-client.ts
+++ b/services/web/frontend/js/features/ide-react/components/editor/python/pyodide-worker-client.ts
@@ -5,7 +5,7 @@ import type {
} from './pyodide-worker-messages'
export type OutputCallback = (
- stream: 'stdout' | 'stderr',
+ stream: 'stdout' | 'stderr' | 'info',
line: string,
fileId: string,
executionId: string
diff --git a/services/web/frontend/js/features/ide-react/components/editor/python/pyodide-worker-messages.ts b/services/web/frontend/js/features/ide-react/components/editor/python/pyodide-worker-messages.ts
index c362d0b058..181d91a5c1 100644
--- a/services/web/frontend/js/features/ide-react/components/editor/python/pyodide-worker-messages.ts
+++ b/services/web/frontend/js/features/ide-react/components/editor/python/pyodide-worker-messages.ts
@@ -28,7 +28,7 @@ export type LoadingFailedEvent = { type: 'loading-failed'; error: string }
export type OutputLineEvent = {
type: 'output-line'
- stream: 'stdout' | 'stderr'
+ stream: 'stdout' | 'stderr' | 'info'
line: string
fileId: string
executionId: string
diff --git a/services/web/frontend/js/features/ide-react/components/editor/python/python-output-pane.tsx b/services/web/frontend/js/features/ide-react/components/editor/python/python-output-pane.tsx
index f69cce748e..4ee451bdf0 100644
--- a/services/web/frontend/js/features/ide-react/components/editor/python/python-output-pane.tsx
+++ b/services/web/frontend/js/features/ide-react/components/editor/python/python-output-pane.tsx
@@ -74,9 +74,12 @@ export default function PythonOutputPane() {
{error && (
{error}
)}
- {output.map((line, index) => (
-
- {line}
+ {output.map((entry, index) => (
+
+ {entry.line}
))}
diff --git a/services/web/frontend/js/features/ide-react/components/editor/python/python-runner.ts b/services/web/frontend/js/features/ide-react/components/editor/python/python-runner.ts
index 7f126f6062..2b0162ee50 100644
--- a/services/web/frontend/js/features/ide-react/components/editor/python/python-runner.ts
+++ b/services/web/frontend/js/features/ide-react/components/editor/python/python-runner.ts
@@ -21,8 +21,13 @@ export type ExecutionContext = {
type Listener = () => void
+export type OutputLine = {
+ stream: 'stdout' | 'stderr' | 'info'
+ line: string
+}
+
export type PythonRunnerState = {
- output: string[]
+ output: OutputLine[]
status: ExecutionStatus
error: string | null
}
@@ -116,11 +121,13 @@ export class PythonRunner {
this.updateState({ status: 'finished' })
}
},
- onOutput: (_stream, line, fileId, executionId) => {
+ onOutput: (stream, line, fileId, executionId) => {
if (fileId !== this.fileId || this.activeExecutionId !== executionId) {
return
}
- this.updateState({ output: appendCapped(this.state.output, line) })
+ this.updateState({
+ output: appendCapped(this.state.output, { stream, line }),
+ })
},
})
}
@@ -185,7 +192,10 @@ export class PythonRunner {
status: 'loading',
output:
this.state.status === 'running'
- ? appendCapped(this.state.output, 'Execution interrupted')
+ ? appendCapped(this.state.output, {
+ stream: 'info',
+ line: 'Execution interrupted',
+ })
: this.state.output,
})
}
@@ -198,8 +208,11 @@ export class PythonRunner {
}
}
-function appendCapped(existing: string[], line: string): string[] {
- const updated = [...existing, line]
+function appendCapped(
+ existing: OutputLine[],
+ entry: OutputLine
+): OutputLine[] {
+ const updated = [...existing, entry]
return updated.length > MAX_OUTPUT_LINES
? updated.slice(-MAX_OUTPUT_LINES)
: updated
diff --git a/services/web/frontend/stylesheets/pages/editor/ide-redesign.scss b/services/web/frontend/stylesheets/pages/editor/ide-redesign.scss
index 2ca1600a8c..ca04e05758 100644
--- a/services/web/frontend/stylesheets/pages/editor/ide-redesign.scss
+++ b/services/web/frontend/stylesheets/pages/editor/ide-redesign.scss
@@ -92,6 +92,14 @@
word-break: break-word;
}
+.ide-redesign-python-output-pane-line-info {
+ color: var(--content-secondary);
+}
+
+.ide-redesign-python-output-pane-line-stderr {
+ color: var(--red-50);
+}
+
.ide-redesign-python-output-pane-placeholder,
.ide-redesign-python-output-pane-error {
white-space: pre-wrap;
diff --git a/services/web/test/frontend/features/ide-react/unit/editor/python-runner.spec.ts b/services/web/test/frontend/features/ide-react/unit/editor/python-runner.spec.ts
index c2aa79b5d4..a8f3aef3a3 100644
--- a/services/web/test/frontend/features/ide-react/unit/editor/python-runner.spec.ts
+++ b/services/web/test/frontend/features/ide-react/unit/editor/python-runner.spec.ts
@@ -135,7 +135,9 @@ describe('PythonRunner', function () {
executionId: runMsg.executionId,
outputs: [],
})
- expect(runner.getState().output).to.deep.equal(['first run output'])
+ expect(runner.getState().output).to.deep.equal([
+ { stream: 'stdout', line: 'first run output' },
+ ])
await runner.run()
expect(runner.getState().output).to.deep.equal([])
@@ -193,7 +195,10 @@ describe('PythonRunner', function () {
executionId: runMsg.executionId,
})
- expect(runner.getState().output).to.deep.equal(['line 1', 'line 2'])
+ expect(runner.getState().output).to.deep.equal([
+ { stream: 'stdout', line: 'line 1' },
+ { stream: 'stderr', line: 'line 2' },
+ ])
})
it('ignores output for a different fileId', async function () {
@@ -250,8 +255,8 @@ describe('PythonRunner', function () {
const output = runner.getState().output
expect(output).to.have.length(100)
- expect(output[0]).to.equal('line 10')
- expect(output[99]).to.equal('line 109')
+ expect(output[0]).to.deep.equal({ stream: 'stdout', line: 'line 10' })
+ expect(output[99]).to.deep.equal({ stream: 'stdout', line: 'line 109' })
})
})
@@ -274,8 +279,8 @@ describe('PythonRunner', function () {
expect(runner.getState().status).to.equal('loading')
expect(runner.getState().output).to.deep.equal([
- 'partial output',
- 'Execution interrupted',
+ { stream: 'stdout', line: 'partial output' },
+ { stream: 'info', line: 'Execution interrupted' },
])
})