Skip to content

Commit be2e176

Browse files
staredclaude
andcommitted
Improve console output with proper R message handling and warning styling (v0.1.4)
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent c4ccec5 commit be2e176

File tree

4 files changed

+115
-13
lines changed

4 files changed

+115
-13
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "webr-ggplot2-demo",
33
"private": true,
4-
"version": "0.1.3",
4+
"version": "0.1.4",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src/App.vue

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ const hasErrors = computed(() => {
6060
return textMessages.value.some(msg => msg.type === 'error' || msg.type === 'stderr')
6161
})
6262
63+
const hasWarnings = computed(() => {
64+
return textMessages.value.some(msg => msg.type === 'warning')
65+
})
66+
6367
const runCode = async () => {
6468
if (code.value.trim()) {
6569
clearMessages() // Clear console and charts before running
@@ -295,9 +299,8 @@ onMounted(async () => {
295299
</div>
296300
<div class="bottom-bar-right">
297301
<button
298-
v-if="textMessages.length > 0"
299302
class="console-toggle"
300-
:class="{ 'has-errors': hasErrors }"
303+
:class="{ 'has-errors': hasErrors, 'has-warnings': hasWarnings && !hasErrors }"
301304
@click="showConsole = !showConsole"
302305
>
303306
Console ({{ textMessages.length }})
@@ -582,6 +585,10 @@ onMounted(async () => {
582585
color: #2563eb;
583586
}
584587
588+
.console-message.warning .message-label {
589+
color: #d97706;
590+
}
591+
585592
.message-text {
586593
flex: 1;
587594
margin: 0;
@@ -651,6 +658,12 @@ onMounted(async () => {
651658
color: #dc2626;
652659
}
653660
661+
.console-toggle.has-warnings {
662+
background: #fefbf2;
663+
border-color: #fed7aa;
664+
color: #d97706;
665+
}
666+
654667
.toggle-arrow {
655668
font-size: 0.75rem;
656669
transition: transform 0.3s ease;

src/composables/useWebR.ts

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,60 @@
11
import { ref, reactive } from 'vue'
2-
import type { WebRMessage, CsvData } from '@/types'
2+
import type { WebRMessage, CsvData, WebROutputItem, WebRCharacterObject, WebRListObject, WebRProxy, WebRExecutionResult } from '@/types'
3+
4+
// WebR output type mapping - immutable and reusable
5+
const WEBR_TYPE_MAP = {
6+
stdout: 'stdout',
7+
stderr: 'stderr',
8+
message: 'info',
9+
warning: 'warning',
10+
error: 'error'
11+
} as const satisfies Record<string, WebRMessage['type']>
12+
13+
// Type guards for WebR objects
14+
const isWebRCharacterObject = (obj: unknown): obj is WebRCharacterObject => {
15+
return typeof obj === 'object' && obj !== null &&
16+
'type' in obj && obj.type === 'character' &&
17+
'values' in obj && Array.isArray(obj.values)
18+
}
19+
20+
const isWebRListObject = (obj: unknown): obj is WebRListObject => {
21+
return typeof obj === 'object' && obj !== null &&
22+
'type' in obj && obj.type === 'list' &&
23+
'values' in obj && Array.isArray(obj.values)
24+
}
25+
26+
// Extract content from WebR output with proper type checking
27+
const extractContentFromWebROutput = async (output: WebROutputItem): Promise<string> => {
28+
if (typeof output.data === 'string') {
29+
return output.data
30+
}
31+
32+
// Convert WebR proxy to JavaScript object
33+
const proxy = output.data as WebRProxy
34+
const jsResult = await proxy.toJs()
35+
36+
if (isWebRCharacterObject(jsResult)) {
37+
return jsResult.values.join('\n')
38+
}
39+
40+
if (isWebRListObject(jsResult)) {
41+
// Validate assumption: we expect only 1 message in the list
42+
if (jsResult.values.length > 1) {
43+
console.error(`ASSUMPTION VIOLATION: List has ${jsResult.values.length} values, expected 1:`, jsResult)
44+
}
45+
46+
const messageObject = jsResult.values[0]
47+
if (isWebRCharacterObject(messageObject) && messageObject.values.length > 0) {
48+
return messageObject.values.join('\n')
49+
}
50+
51+
console.error('Unexpected list structure:', jsResult)
52+
return '[Unable to extract message from list]'
53+
}
54+
55+
console.error('Unknown WebR object type:', jsResult)
56+
return `[Unknown WebR type: ${(jsResult as any)?.type || 'unknown'}]`
57+
}
358

459
export const useWebR = () => {
560
const isReady = ref(false)
@@ -88,19 +143,25 @@ export const useWebR = () => {
88143
const result = await shelter.captureR(code, {
89144
withAutoprint: true,
90145
captureStreams: true,
91-
captureConditions: false,
146+
captureConditions: true,
92147
captureGraphics: true,
93148
env: webR.objs.globalEnv,
94149
})
95150

96-
// Process stdout
97-
if (result.output && result.output.length > 0) {
98-
for (const output of result.output) {
99-
if (output.type === 'stdout' && output.data) {
100-
addMessage('stdout', output.data)
101-
} else if (output.type === 'stderr' && output.data) {
102-
addMessage('stderr', output.data)
151+
// Process all output (streams and conditions)
152+
const typedResult = result as WebRExecutionResult
153+
if (typedResult.output && typedResult.output.length > 0) {
154+
for (const output of typedResult.output) {
155+
const content = await extractContentFromWebROutput(output)
156+
const messageType = WEBR_TYPE_MAP[output.type as keyof typeof WEBR_TYPE_MAP]
157+
158+
if (!messageType) {
159+
console.error(`Unknown WebR output type: ${output.type}`)
160+
addMessage('error', `Unknown output type: ${output.type}`)
161+
continue
103162
}
163+
164+
addMessage(messageType, content)
104165
}
105166
}
106167

src/types/index.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export interface RExample {
77
}
88

99
export interface WebRMessage {
10-
type: 'stdout' | 'stderr' | 'error' | 'success' | 'info' | 'plot'
10+
type: 'stdout' | 'stderr' | 'error' | 'success' | 'info' | 'warning' | 'plot'
1111
content: string
1212
}
1313

@@ -17,4 +17,32 @@ export interface CsvData {
1717
rows: number
1818
columns: number
1919
columnNames: string[]
20+
}
21+
22+
// WebR output types
23+
export interface WebRCharacterObject {
24+
type: 'character'
25+
names: string[] | null
26+
values: string[]
27+
}
28+
29+
export interface WebRListObject {
30+
type: 'list'
31+
names: string[]
32+
values: WebRCharacterObject[]
33+
}
34+
35+
export interface WebRProxy {
36+
toJs(): Promise<WebRCharacterObject | WebRListObject>
37+
}
38+
39+
export interface WebROutputItem {
40+
type: 'stdout' | 'stderr' | 'message' | 'warning' | 'error'
41+
data: string | WebRProxy
42+
}
43+
44+
export interface WebRExecutionResult {
45+
output?: WebROutputItem[]
46+
images?: HTMLImageElement[]
47+
result?: unknown
2048
}

0 commit comments

Comments
 (0)