feat(app): refine expanded shell output (#44368)
This commit is contained in:
@@ -54,6 +54,7 @@
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
"@dnd-kit/dom": "0.5.0",
|
||||
|
||||
@@ -358,7 +358,8 @@ test.describe("smoke: session timeline", () => {
|
||||
const shellTrigger = shell.locator('[data-slot="collapsible-trigger"]')
|
||||
const shellSubtitle = shell.locator('[data-slot="basic-tool-tool-subtitle"]')
|
||||
await expect(shellSubtitle).toHaveCount(0)
|
||||
await expect(shell.locator('[data-slot="bash-pre"]')).toContainText("$ bun typecheck")
|
||||
await expect(shell.locator('[data-slot="bash-command"]')).toHaveText("bun typecheck")
|
||||
await expect(shell.locator('[data-slot="bash-result"]')).not.toContainText("bun typecheck")
|
||||
await shellTrigger.click()
|
||||
await expect(shellTrigger).toHaveAttribute("aria-expanded", "false")
|
||||
await expect(shellSubtitle).toHaveText("bun typecheck")
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"vite-plugin-solid": "2.11.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
"@dnd-kit/dom": "0.5.0",
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IBM Plex Mono";
|
||||
src: url("@ibm/plex/IBM-Plex-Mono/fonts/complete/woff2/IBMPlexMono-Text.woff2") format("woff2");
|
||||
font-weight: 440;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@media (display-mode: standalone) {
|
||||
/* WebKit excludes safe-area insets from dvh in installed apps. */
|
||||
#root {
|
||||
|
||||
@@ -43,6 +43,7 @@ export default defineConfig({
|
||||
"web-app-manifest*",
|
||||
"assets/index-*.{js,css}",
|
||||
"assets/session-*.js",
|
||||
"assets/IBMPlexMono-Text-*.woff2",
|
||||
"assets/Inter.ttf",
|
||||
"assets/JetBrainsMonoNerdFontMono-Regular.woff2",
|
||||
],
|
||||
|
||||
@@ -419,11 +419,44 @@
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
[data-slot="bash-prompt"] {
|
||||
user-select: none;
|
||||
&[data-variant="shell"] {
|
||||
background: var(--v2-background-bg-base);
|
||||
|
||||
[data-slot="bash-pre"] {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[data-slot="bash-pre"] code {
|
||||
font-family: var(--v2-font-family-code);
|
||||
font-weight: 440;
|
||||
line-height: var(--line-height-base);
|
||||
letter-spacing: -0.04px;
|
||||
}
|
||||
|
||||
[data-slot="bash-command"],
|
||||
[data-slot="bash-result"] {
|
||||
display: block;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
[data-slot="bash-result"] {
|
||||
border-block-start: 0.5px solid var(--v2-border-border-base);
|
||||
background: var(--v2-background-bg-layer-01);
|
||||
color: var(--v2-text-text-muted);
|
||||
}
|
||||
|
||||
[data-slot="bash-command"] {
|
||||
color: var(--v2-text-text-base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="collapsible"].tool-collapsible:has(
|
||||
> [data-slot="collapsible-content"] [data-component="bash-output"][data-variant="shell"]
|
||||
) {
|
||||
--tool-content-gap: 6px;
|
||||
}
|
||||
|
||||
[data-component="edit-trigger"],
|
||||
[data-component="write-trigger"] {
|
||||
display: flex;
|
||||
|
||||
@@ -507,6 +507,29 @@ export const terminalPassedDocument = document([
|
||||
}),
|
||||
] satisfies SessionMessageInfo[])
|
||||
|
||||
export const expandedShellDocument = document([
|
||||
user("msg_user_shell_expanded", "Inspect the active service and its available feature surface.", 52_100),
|
||||
assistant({
|
||||
id: "msg_assistant_shell_expanded",
|
||||
offset: 52_200,
|
||||
completed: 52_900,
|
||||
content: [
|
||||
completedTool({
|
||||
id: "tool_shell_expanded",
|
||||
name: "shell",
|
||||
offset: 52_300,
|
||||
args: {
|
||||
command:
|
||||
"opencode2 api get /openapi.json > /private/var/folders/j/gd69b2|16y91666jzf3p9g22asdasc0000gn/T/opencode/temp/tes",
|
||||
},
|
||||
output:
|
||||
'{"location":{"directory":"/Users/usrnk1","project":{"id":"global","directory":"/","canonical":"/"}},"data":[{"id":"opencode","name":"OpenCode","description":"Use this skill for any question about OpenCode itself, including how OpenCode works, using or configuring it, migrating from V1 to V2, troubleshooting it, developing plugins or integrations, using the OpenCode SDK, clients, server, or API, and contributing to the OpenCode codebase."}]}',
|
||||
metadata: { exit: 0 },
|
||||
}),
|
||||
],
|
||||
}),
|
||||
] satisfies SessionMessageInfo[])
|
||||
|
||||
export const terminalFailedDocument = document([
|
||||
user("msg_user_terminal_failed", "Run the focused Session UI tests.", 53_000),
|
||||
assistant({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CurrentSessionTimelineStory } from "../storybook/current-session-story"
|
||||
import {
|
||||
expandedShellDocument,
|
||||
recoveryDocument,
|
||||
standaloneShellCompletedDocument,
|
||||
standaloneShellRunningDocument,
|
||||
@@ -72,6 +73,18 @@ export const TestsPassed = {
|
||||
),
|
||||
}
|
||||
|
||||
export const ExpandedShell = {
|
||||
render: () => (
|
||||
<CurrentSessionTimelineStory
|
||||
title="Expanded shell"
|
||||
description="The expanded shell separates the command from its output in a full-width card without a detail rail."
|
||||
document={expandedShellDocument}
|
||||
width="786px"
|
||||
shellToolDefaultOpen
|
||||
/>
|
||||
),
|
||||
}
|
||||
|
||||
export const TestFailed = {
|
||||
render: () => (
|
||||
<CurrentSessionTimelineStory
|
||||
|
||||
@@ -1182,7 +1182,7 @@ ToolRegistry.register({
|
||||
},
|
||||
})
|
||||
|
||||
function ConsoleOutput(props: { copy: string; children: JSX.Element }) {
|
||||
function ConsoleOutput(props: { copy: string; children: JSX.Element; variant?: "shell" }) {
|
||||
const i18n = useI18n()
|
||||
const [copied, setCopied] = createSignal(false)
|
||||
|
||||
@@ -1194,7 +1194,7 @@ function ConsoleOutput(props: { copy: string; children: JSX.Element }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-component="bash-output" dir="ltr">
|
||||
<div data-component="bash-output" data-variant={props.variant} dir="ltr">
|
||||
<div data-slot="bash-copy">
|
||||
<Tooltip value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")} placement="top">
|
||||
<IconButton
|
||||
@@ -1273,10 +1273,7 @@ ToolRegistry.register({
|
||||
if (typeof props.metadata.command === "string") return props.metadata.command
|
||||
return ""
|
||||
}
|
||||
const text = createMemo(() => {
|
||||
const out = stripAnsi(props.output ?? "").replace(/\r\n?/g, "\n")
|
||||
return `${command()}${out ? "\n\n" + out : ""}`
|
||||
})
|
||||
const output = createMemo(() => stripAnsi(props.output ?? "").replace(/\r\n?/g, "\n"))
|
||||
return (
|
||||
<BasicTool
|
||||
{...props}
|
||||
@@ -1288,10 +1285,7 @@ ToolRegistry.register({
|
||||
<div data-slot="basic-tool-tool-info-structured">
|
||||
<div data-slot="basic-tool-tool-info-main">
|
||||
<span data-slot="basic-tool-tool-title">
|
||||
<TextShimmer
|
||||
text={i18n.t("ui.tool.shell")}
|
||||
active={pending()}
|
||||
/>
|
||||
<TextShimmer text={i18n.t("ui.tool.shell")} active={pending()} />
|
||||
</span>
|
||||
<Show when={!open()}>
|
||||
<Show
|
||||
@@ -1309,11 +1303,9 @@ ToolRegistry.register({
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<ConsoleOutput copy={command()}>
|
||||
<span data-slot="bash-prompt" aria-hidden="true">
|
||||
{"$ "}
|
||||
</span>
|
||||
{text()}
|
||||
<ConsoleOutput copy={command()} variant="shell">
|
||||
<span data-slot="bash-command">{command()}</span>
|
||||
<Show when={output()}>{(value) => <span data-slot="bash-result">{value()}</span>}</Show>
|
||||
</ConsoleOutput>
|
||||
</BasicTool>
|
||||
)
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
|
||||
--font-family-text: "Inter", sans-serif;
|
||||
--v2-font-family-sans: "Inter", sans-serif;
|
||||
--v2-font-family-code: "IBM Plex Mono", var(--font-family-mono);
|
||||
--line-height-tight: 12px;
|
||||
--line-height-compact: 16px;
|
||||
--line-height-base: 20px;
|
||||
|
||||
Reference in New Issue
Block a user