Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4284abf
feat(ui): fold hidden prompt sections in chat history
pascalandr May 8, 2026
fdf6aab
Merge branch 'dev' into feat/hidden-prompt-sections
pascalandr May 8, 2026
bba876f
fix(ui): narrow hidden prompt persistence
pascalandr May 8, 2026
55d14f8
feat: TASK-059 collapse pasted prompt text in history
pascalandr May 21, 2026
6933695
Merge branch 'dev' into feat/hidden-prompt-sections
pascalandr May 21, 2026
080ee6c
fix: TASK-059 preserve pasted collapse through prompt submission
pascalandr May 21, 2026
acff400
fix: TASK-059 persist and lazy-render pasted history blocks
pascalandr May 21, 2026
2e0f686
fix: TASK-059 align v3 prompt display cleanup semantics
pascalandr May 21, 2026
8fc2c2e
Merge branch 'dev' into feat/hidden-prompt-sections
pascalandr May 21, 2026
383739f
fix: TASK-059 polish pasted text collapse shell
pascalandr May 30, 2026
3200953
Merge branch 'dev' into feat/hidden-prompt-sections
pascalandr May 30, 2026
231e573
fix: task-078 restore UI typecheck on integrated batch
pascalandr Jun 5, 2026
8ecd898
Merge branch 'dev' into feat/hidden-prompt-sections
pascalandr Jun 5, 2026
224b6bd
fix: TASK-079 address hidden prompt section feedback
pascalandr Jun 6, 2026
9ee69be
Merge branch 'dev' into feat/hidden-prompt-sections
pascalandr Jun 6, 2026
da57f98
Merge branch 'dev' into feat/hidden-prompt-sections
pascalandr Jun 6, 2026
4ae7baa
Support OpenCode SDK 1.16 runtime APIs (#526)
shantur Jun 7, 2026
97dcc0a
feat(ui): show the session title in the header bar (#340)
pascalandr Jun 7, 2026
f913fb6
merge: task-086 refresh PR407 onto upstream dev
pascalandr Jun 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dependencies": {
"@git-diff-view/solid": "^0.0.8",
"@kobalte/core": "0.13.11",
"@opencode-ai/sdk": "1.15.13",
"@opencode-ai/sdk": "1.16.0",
"@solidjs/router": "^0.13.0",
"@suid/icons-material": "^0.9.0",
"@suid/material": "^0.19.0",
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ const App: Component = () => {
binaryPath: string
instanceId: string
} | null>(null)

const phoneQuery = useMediaQuery("(max-width: 767px)")
const isPhoneLayout = createMemo(() => phoneQuery())

Expand Down Expand Up @@ -418,7 +417,7 @@ const App: Component = () => {
clearActiveParentSession(instanceId)

try {
await fetchSessions(instanceId, { start: 0, limit: getSessionFetchLimit(instanceId) })
await fetchSessions(instanceId, { reset: true, limit: getSessionFetchLimit(instanceId) })
} catch (error) {
log.error("Failed to refresh sessions after closing", error)
}
Expand Down
219 changes: 120 additions & 99 deletions packages/ui/src/components/instance/instance-shell2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,23 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
</div>
)

const renderPreviewToggleButton = () => (
<Show when={!showingInfoView()}>
<IconButton
color="inherit"
onClick={handlePreviewButtonClick}
aria-label={previewToggleLabel()}
title={previewToggleLabel()}
size="small"
>
{(() => {
const Icon = PreviewToggleIcon()
return <Icon class="w-5 h-5" aria-hidden="true" />
})()}
</IconButton>
</Show>
)

const handleCommandPaletteClick = () => {
showCommandPalette(props.instance.id)
}
Expand Down Expand Up @@ -834,6 +851,50 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
}

const showingInfoView = createMemo(() => activeSessionIdForInstance() === "info")
const activeSessionTitle = createMemo(() => {
if (showingInfoView()) return null
const title = activeSessionForInstance()?.title?.trim()
return title || t("sessionList.session.untitled")
})
const showHeaderLeftSlot = createMemo(() => !leftPinned())
const showHeaderSessionTitle = createMemo(() => !compactHeaderLayout() && showHeaderLeftSlot() && Boolean(activeSessionTitle()))
const headerToolbarHorizontalInset = createMemo(() => (isPhoneLayout() ? 16 : 24))
const headerLeftSlotWidth = createMemo(() => Math.max(0, sessionSidebarWidth() - headerToolbarHorizontalInset()))
const headerLeftSlotStyle = createMemo(() =>
leftDrawerState() === "floating-open" || showHeaderSessionTitle() ? { width: `${headerLeftSlotWidth()}px` } : undefined,
)

const renderActiveSessionHeaderTitle = () => (
<Show when={showHeaderSessionTitle()}>
<div
class="session-header-active-title"
dir="auto"
title={activeSessionTitle() ?? undefined}
>
<span class="session-header-active-title-text">{activeSessionTitle()}</span>
</div>
</Show>
)

const renderHeaderLeftSlot = () => (
<Show when={showHeaderLeftSlot()}>
<div class="session-header-left-slot" style={headerLeftSlotStyle()}>
<Show when={leftDrawerState() === "floating-closed"}>
<IconButton
ref={setLeftToggleButtonEl}
color="inherit"
onClick={handleLeftAppBarButtonClick}
aria-label={leftAppBarButtonLabel()}
size="small"
aria-expanded={leftDrawerState() !== "floating-closed"}
>
{leftAppBarButtonIcon()}
</IconButton>
</Show>
{renderActiveSessionHeaderTitle()}
</div>
</Show>
)

const isLaunching = createMemo(() => props.instance.status === "starting")

Expand Down Expand Up @@ -933,94 +994,76 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
fallback={
<div class="flex flex-col w-full gap-1.5">
<div class="flex flex-wrap items-center justify-between gap-2 w-full">
<Show when={leftDrawerState() === "floating-closed"}>
<IconButton
ref={setLeftToggleButtonEl}
color="inherit"
onClick={handleLeftAppBarButtonClick}
aria-label={leftAppBarButtonLabel()}
size="small"
aria-expanded={leftDrawerState() !== "floating-closed"}
>
{leftAppBarButtonIcon()}
</IconButton>
</Show>
{renderHeaderLeftSlot()}

<div class="flex-1 flex items-center justify-center min-w-0">
{renderSessionHeaderIndicators()}
</div>
<div class="flex-1 flex items-center justify-center min-w-0">
{renderSessionHeaderIndicators()}
</div>

<div class="flex flex-wrap items-center justify-center gap-1">
<Show when={!showingInfoView()}>
<div class="flex flex-wrap items-center justify-center gap-1">
<Show when={!showingInfoView()}>
<IconButton
color="inherit"
onClick={handleChatSearchClick}
aria-label={t("instanceShell.chatSearch.openAriaLabel")}
title={t("instanceShell.chatSearch.openAriaLabel")}
size="small"
>
<Search class="w-5 h-5" aria-hidden="true" />
</IconButton>
</Show>
<button
type="button"
class="connection-status-button command-palette-button"
onClick={handleCommandPaletteClick}
aria-label={t("instanceShell.commandPalette.openAriaLabel")}
style={{ flex: "0 0 auto", width: "auto" }}
>
{t("instanceShell.commandPalette.button")}
</button>
<span class="connection-status-shortcut-hint kbd-hint">
<Kbd shortcut="cmd+shift+p" />
</span>
</div>

<div class="flex-1 flex items-center justify-center min-w-0">
<span
class={`status-indicator ${connectionStatusClass()}`}
aria-label={t("instanceShell.connection.ariaLabel", { status: connectionStatusLabel() })}
>
<span class="status-dot" />
</span>
</div>

<Show when={!isPhoneLayout()}>
{renderPreviewToggleButton()}
</Show>

<Show when={isPhoneLayout() && !props.mobileFullscreenMode}>
<IconButton
color="inherit"
onClick={handleChatSearchClick}
aria-label={t("instanceShell.chatSearch.openAriaLabel")}
title={t("instanceShell.chatSearch.openAriaLabel")}
onClick={props.onEnterMobileFullscreen}
aria-label={t("instanceShell.fullscreen.enter")}
title={t("instanceShell.fullscreen.enter")}
size="small"
>
<Search class="w-5 h-5" aria-hidden="true" />
<Maximize2 class="w-5 h-5" aria-hidden="true" />
</IconButton>
{renderPreviewToggleButton()}
</Show>

<Show when={rightDrawerState() === "floating-closed"}>
<IconButton
ref={setRightToggleButtonEl}
color="inherit"
onClick={handlePreviewButtonClick}
aria-label={previewToggleLabel()}
title={previewToggleLabel()}
onClick={handleRightAppBarButtonClick}
aria-label={rightAppBarButtonLabel()}
size="small"
aria-expanded={rightDrawerState() !== "floating-closed"}
>
{(() => {
const Icon = PreviewToggleIcon()
return <Icon class="w-5 h-5" aria-hidden="true" />
})()}
{rightAppBarButtonIcon()}
</IconButton>
</Show>
<button
type="button"
class="connection-status-button command-palette-button"
onClick={handleCommandPaletteClick}
aria-label={t("instanceShell.commandPalette.openAriaLabel")}
style={{ flex: "0 0 auto", width: "auto" }}
>
{t("instanceShell.commandPalette.button")}
</button>
<span class="connection-status-shortcut-hint kbd-hint">
<Kbd shortcut="cmd+shift+p" />
</span>
</div>

<div class="flex-1 flex items-center justify-center min-w-0">
<span
class={`status-indicator ${connectionStatusClass()}`}
aria-label={t("instanceShell.connection.ariaLabel", { status: connectionStatusLabel() })}
>
<span class="status-dot" />
</span>
</div>

<Show when={isPhoneLayout() && !props.mobileFullscreenMode}>
<IconButton
color="inherit"
onClick={props.onEnterMobileFullscreen}
aria-label={t("instanceShell.fullscreen.enter")}
title={t("instanceShell.fullscreen.enter")}
size="small"
>
<Maximize2 class="w-5 h-5" aria-hidden="true" />
</IconButton>
</Show>

<Show when={rightDrawerState() === "floating-closed"}>
<IconButton
ref={setRightToggleButtonEl}
color="inherit"
onClick={handleRightAppBarButtonClick}
aria-label={rightAppBarButtonLabel()}
size="small"
aria-expanded={rightDrawerState() !== "floating-closed"}
>
{rightAppBarButtonIcon()}
</IconButton>
</Show>
</div>

<div class="flex flex-wrap items-center justify-center gap-2 pb-1">
Expand All @@ -1038,18 +1081,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
}
>
<div class="session-toolbar-left flex-1 flex items-center gap-3 min-w-0">
<Show when={leftDrawerState() === "floating-closed"}>
<IconButton
ref={setLeftToggleButtonEl}
color="inherit"
onClick={handleLeftAppBarButtonClick}
aria-label={leftAppBarButtonLabel()}
size="small"
aria-expanded={leftDrawerState() !== "floating-closed"}
>
{leftAppBarButtonIcon()}
</IconButton>
</Show>
{renderHeaderLeftSlot()}

<Show when={!showingInfoView()}>
<ContextMeter
Expand Down Expand Up @@ -1095,18 +1127,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
>
<Search class="w-5 h-5" aria-hidden="true" />
</IconButton>
<IconButton
color="inherit"
onClick={handlePreviewButtonClick}
aria-label={previewToggleLabel()}
title={previewToggleLabel()}
size="small"
>
{(() => {
const Icon = PreviewToggleIcon()
return <Icon class="w-5 h-5" aria-hidden="true" />
})()}
</IconButton>
{renderPreviewToggleButton()}
</Show>
<Show when={connectionStatus() === "connected"}>
<span class="status-indicator connected">
Expand Down
6 changes: 6 additions & 0 deletions packages/ui/src/components/message-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ export default function MessageItem(props: MessageItemProps) {
return typeof firstText?.id === "string" ? firstText.id : null
}

const primaryUserPromptDisplayMetadata = () => {
if (!isUser()) return undefined
return props.record.clientPromptDisplayMetadata
}

const fileAttachments = () =>
messageParts().filter((part): part is FilePart => part?.type === "file" && typeof (part as FilePart).url === "string")

Expand Down Expand Up @@ -688,6 +693,7 @@ export default function MessageItem(props: MessageItemProps) {
instanceId={props.instanceId}
sessionId={props.sessionId}
primaryUserTextPartId={primaryUserTextPartId()}
displayMetadataOverride={part.id === primaryUserTextPartId() ? primaryUserPromptDisplayMetadata() : undefined}
onRendered={props.onContentRendered}
/>
</div>
Expand Down
Loading
Loading