Skip to content

Commit 2bed16d

Browse files
committed
fix: fix program output text formatting
1 parent cce5b62 commit 2bed16d

File tree

3 files changed

+70
-60
lines changed

3 files changed

+70
-60
lines changed

web/src/components/features/inspector/FallbackOutput/FallbackOutput.tsx

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import React from 'react'
22

33
import { mergeStyleSets, useTheme } from '@fluentui/react'
44
import type { StatusState } from '~/store'
5-
import { OutputLine } from './OutputLine'
5+
import { EvalEventKind } from '~/services/api'
6+
import { splitImageAndText } from './utils'
67

78
interface Props {
89
status?: StatusState
@@ -16,11 +17,27 @@ interface Props {
1617
export const FallbackOutput: React.FC<Props> = ({ fontFamily, fontSize, status }) => {
1718
const theme = useTheme()
1819
const styles = mergeStyleSets({
19-
container: {
20+
root: {
2021
flex: '1 1 auto',
2122
boxSizing: 'border-box',
2223
padding: '0, 15px',
2324
},
25+
content: {
26+
whiteSpace: 'pre-wrap',
27+
display: 'table',
28+
width: '100%',
29+
30+
font: 'inherit',
31+
border: 'none',
32+
margin: 0,
33+
float: 'left',
34+
},
35+
stderr: {
36+
color: theme.palette.red,
37+
},
38+
image: {
39+
display: 'block',
40+
},
2441
programExitMsg: {
2542
marginTop: '1rem',
2643
display: 'inline-block',
@@ -29,8 +46,29 @@ export const FallbackOutput: React.FC<Props> = ({ fontFamily, fontSize, status }
2946
})
3047

3148
return (
32-
<div className={styles.container} style={{ fontFamily, fontSize: `${fontSize}px` }}>
33-
{status?.events?.map((event, i) => <OutputLine key={i} event={event} />)}
49+
<div className={styles.root} style={{ fontFamily, fontSize: `${fontSize}px` }}>
50+
<div className={styles.content}>
51+
{status?.events?.map(({ Kind: kind, Message: msg }, i) => {
52+
if (kind === EvalEventKind.Stderr) {
53+
return (
54+
<span key={i} className={styles.stderr}>
55+
{msg}
56+
</span>
57+
)
58+
}
59+
60+
// Image content and text can come mixed due to output buffering
61+
return splitImageAndText(msg).map(({ isImage, data }, j) => (
62+
<React.Fragment key={`${i}.${j}`}>
63+
{isImage ? (
64+
<img className={styles.image} key={i} src={`data:image;base64,${data}`} alt="Image output" />
65+
) : (
66+
data
67+
)}
68+
</React.Fragment>
69+
))
70+
})}
71+
</div>
3472
{!status?.running && <span className={styles.programExitMsg}>Program exited.</span>}
3573
</div>
3674
)

web/src/components/features/inspector/FallbackOutput/OutputLine.tsx

Lines changed: 0 additions & 56 deletions
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const imageSectionPrefix = 'IMAGE:'
2+
const base64RegEx = /[A-Za-z0-9+/]+={0,2}$/
3+
const imageRegEx = /(IMAGE:[A-Za-z0-9+/]+={0,2})/
4+
5+
export const isImageLine = (message: string): [boolean, string] => {
6+
if (!message.startsWith(imageSectionPrefix)) {
7+
return [false, message]
8+
}
9+
10+
const payload = message.substring(imageSectionPrefix.length).trim()
11+
return [base64RegEx.test(payload), payload]
12+
}
13+
14+
interface TextChunk {
15+
isImage?: boolean
16+
data: string
17+
}
18+
19+
/**
20+
* Finds all embedded base64 image segments and tokenizes content.
21+
*
22+
* Used to support embedded images in Go program stdout like go.dev/play does.
23+
*/
24+
export const splitImageAndText = (data: string): TextChunk[] =>
25+
data.split(imageRegEx).map((str) => {
26+
const [isImage, data] = isImageLine(str)
27+
return { isImage, data }
28+
})

0 commit comments

Comments
 (0)