feat(app): session timeline/turn rework (#13196)
Co-authored-by: David Hill <iamdavidhill@gmail.com>
This commit is contained in:
@@ -4,15 +4,44 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
gap: 20px;
|
||||
justify-content: space-between;
|
||||
gap: 0px;
|
||||
justify-content: flex-start;
|
||||
|
||||
[data-slot="basic-tool-tool-trigger-content"] {
|
||||
width: 100%;
|
||||
width: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
gap: 20px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-indicator"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
[data-component="spinner"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-spinner"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
color: var(--text-weak);
|
||||
|
||||
[data-component="spinner"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="icon-svg"] {
|
||||
@@ -20,16 +49,17 @@
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-info"] {
|
||||
flex-grow: 1;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-info-structured"] {
|
||||
width: 100%;
|
||||
width: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-info-main"] {
|
||||
@@ -43,16 +73,21 @@
|
||||
[data-slot="basic-tool-tool-title"] {
|
||||
flex-shrink: 0;
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-small);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-large);
|
||||
letter-spacing: var(--letter-spacing-normal);
|
||||
color: var(--text-base);
|
||||
color: var(--text-strong);
|
||||
|
||||
&.capitalize {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
&.agent-title {
|
||||
color: var(--text-strong);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-subtitle"] {
|
||||
@@ -62,12 +97,12 @@
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-small);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-large);
|
||||
letter-spacing: var(--letter-spacing-normal);
|
||||
color: var(--text-weak);
|
||||
color: var(--text-base);
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
@@ -78,6 +113,26 @@
|
||||
color: var(--text-base);
|
||||
}
|
||||
}
|
||||
|
||||
&.subagent-link {
|
||||
color: var(--text-interactive-base);
|
||||
text-decoration: none;
|
||||
text-underline-offset: 2px;
|
||||
font-weight: var(--font-weight-regular);
|
||||
|
||||
&:hover {
|
||||
color: var(--text-interactive-base);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: var(--text-interactive-base);
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: var(--text-interactive-base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-tool-arg"] {
|
||||
@@ -87,11 +142,11 @@
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-small);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-large);
|
||||
letter-spacing: var(--letter-spacing-normal);
|
||||
color: var(--text-weak);
|
||||
color: var(--text-base);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createEffect, createSignal, For, Match, Show, Switch, type JSX } from "solid-js"
|
||||
import { Collapsible } from "./collapsible"
|
||||
import { Icon, IconProps } from "./icon"
|
||||
import type { IconProps } from "./icon"
|
||||
import { TextShimmer } from "./text-shimmer"
|
||||
|
||||
export type TriggerTitle = {
|
||||
title: string
|
||||
@@ -22,6 +23,7 @@ export interface BasicToolProps {
|
||||
icon: IconProps["name"]
|
||||
trigger: TriggerTitle | JSX.Element
|
||||
children?: JSX.Element
|
||||
status?: string
|
||||
hideDetails?: boolean
|
||||
defaultOpen?: boolean
|
||||
forceOpen?: boolean
|
||||
@@ -31,22 +33,23 @@ export interface BasicToolProps {
|
||||
|
||||
export function BasicTool(props: BasicToolProps) {
|
||||
const [open, setOpen] = createSignal(props.defaultOpen ?? false)
|
||||
const pending = () => props.status === "pending" || props.status === "running"
|
||||
|
||||
createEffect(() => {
|
||||
if (props.forceOpen) setOpen(true)
|
||||
})
|
||||
|
||||
const handleOpenChange = (value: boolean) => {
|
||||
if (pending()) return
|
||||
if (props.locked && !value) return
|
||||
setOpen(value)
|
||||
}
|
||||
|
||||
return (
|
||||
<Collapsible open={open()} onOpenChange={handleOpenChange}>
|
||||
<Collapsible open={open()} onOpenChange={handleOpenChange} class="tool-collapsible">
|
||||
<Collapsible.Trigger>
|
||||
<div data-component="tool-trigger">
|
||||
<div data-slot="basic-tool-tool-trigger-content">
|
||||
<Icon name={props.icon} size="small" />
|
||||
<div data-slot="basic-tool-tool-info">
|
||||
<Switch>
|
||||
<Match when={isTriggerTitle(props.trigger) && props.trigger}>
|
||||
@@ -59,41 +62,45 @@ export function BasicTool(props: BasicToolProps) {
|
||||
[trigger().titleClass ?? ""]: !!trigger().titleClass,
|
||||
}}
|
||||
>
|
||||
{trigger().title}
|
||||
<Show when={pending()} fallback={trigger().title}>
|
||||
<TextShimmer text={trigger().title} />
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={trigger().subtitle}>
|
||||
<span
|
||||
data-slot="basic-tool-tool-subtitle"
|
||||
classList={{
|
||||
[trigger().subtitleClass ?? ""]: !!trigger().subtitleClass,
|
||||
clickable: !!props.onSubtitleClick,
|
||||
}}
|
||||
onClick={(e) => {
|
||||
if (props.onSubtitleClick) {
|
||||
e.stopPropagation()
|
||||
props.onSubtitleClick()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{trigger().subtitle}
|
||||
</span>
|
||||
</Show>
|
||||
<Show when={trigger().args?.length}>
|
||||
<For each={trigger().args}>
|
||||
{(arg) => (
|
||||
<span
|
||||
data-slot="basic-tool-tool-arg"
|
||||
classList={{
|
||||
[trigger().argsClass ?? ""]: !!trigger().argsClass,
|
||||
}}
|
||||
>
|
||||
{arg}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
<Show when={!pending()}>
|
||||
<Show when={trigger().subtitle}>
|
||||
<span
|
||||
data-slot="basic-tool-tool-subtitle"
|
||||
classList={{
|
||||
[trigger().subtitleClass ?? ""]: !!trigger().subtitleClass,
|
||||
clickable: !!props.onSubtitleClick,
|
||||
}}
|
||||
onClick={(e) => {
|
||||
if (props.onSubtitleClick) {
|
||||
e.stopPropagation()
|
||||
props.onSubtitleClick()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{trigger().subtitle}
|
||||
</span>
|
||||
</Show>
|
||||
<Show when={trigger().args?.length}>
|
||||
<For each={trigger().args}>
|
||||
{(arg) => (
|
||||
<span
|
||||
data-slot="basic-tool-tool-arg"
|
||||
classList={{
|
||||
[trigger().argsClass ?? ""]: !!trigger().argsClass,
|
||||
}}
|
||||
>
|
||||
{arg}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={trigger().action}>{trigger().action}</Show>
|
||||
<Show when={!pending() && trigger().action}>{trigger().action}</Show>
|
||||
</div>
|
||||
)}
|
||||
</Match>
|
||||
@@ -101,7 +108,7 @@ export function BasicTool(props: BasicToolProps) {
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={props.children && !props.hideDetails && !props.locked}>
|
||||
<Show when={props.children && !props.hideDetails && !props.locked && !pending()}>
|
||||
<Collapsible.Arrow />
|
||||
</Show>
|
||||
</div>
|
||||
@@ -113,6 +120,6 @@ export function BasicTool(props: BasicToolProps) {
|
||||
)
|
||||
}
|
||||
|
||||
export function GenericTool(props: { tool: string; hideDetails?: boolean }) {
|
||||
return <BasicTool icon="mcp" trigger={{ title: props.tool }} hideDetails={props.hideDetails} />
|
||||
export function GenericTool(props: { tool: string; status?: string; hideDetails?: boolean }) {
|
||||
return <BasicTool icon="mcp" status={props.status} trigger={{ title: props.tool }} hideDetails={props.hideDetails} />
|
||||
}
|
||||
|
||||
@@ -170,3 +170,15 @@
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="button"].titlebar-icon[data-variant="ghost"]:hover:not(:disabled) {
|
||||
background-color: var(--surface-raised-base-active);
|
||||
}
|
||||
|
||||
[data-component="button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] {
|
||||
background-color: var(--surface-base-active);
|
||||
}
|
||||
|
||||
[data-component="button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"]:hover:not(:disabled) {
|
||||
background-color: var(--surface-base-active);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[data-component="checkbox"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: var(--checkbox-align, center);
|
||||
gap: 12px;
|
||||
cursor: default;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 2px;
|
||||
margin-top: var(--checkbox-offset, 0px);
|
||||
aspect-ratio: 1;
|
||||
flex-shrink: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
|
||||
@@ -2,23 +2,44 @@
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--surface-inset-base);
|
||||
border: 1px solid var(--border-weaker-base);
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
transition: background-color 0.15s ease;
|
||||
border-radius: var(--radius-md);
|
||||
overflow: clip;
|
||||
|
||||
&.tool-collapsible {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"] {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 32px;
|
||||
padding: 6px 8px 6px 12px;
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
color: var(--text-base);
|
||||
|
||||
[data-slot="collapsible-arrow"] {
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
[data-slot="collapsible-arrow-icon"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-slot="collapsible-arrow-icon"][data-direction="right"] {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
&:hover [data-slot="collapsible-arrow"] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* text-12-medium */
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-small);
|
||||
@@ -48,6 +69,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="collapsible-trigger"][aria-expanded="true"] {
|
||||
[data-slot="collapsible-arrow"] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[data-slot="collapsible-arrow-icon"][data-direction="right"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-slot="collapsible-arrow-icon"][data-direction="down"] {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="collapsible-content"] {
|
||||
overflow: hidden;
|
||||
/* animation: slideUp 250ms ease-out; */
|
||||
|
||||
@@ -34,7 +34,12 @@ function CollapsibleContent(props: ComponentProps<typeof Kobalte.Content>) {
|
||||
function CollapsibleArrow(props?: ComponentProps<"div">) {
|
||||
return (
|
||||
<div data-slot="collapsible-arrow" {...(props || {})}>
|
||||
<Icon name="chevron-grabber-vertical" size="small" />
|
||||
<span data-slot="collapsible-arrow-icon" data-direction="right">
|
||||
<Icon name="chevron-right" size="small" />
|
||||
</span>
|
||||
<span data-slot="collapsible-arrow-icon" data-direction="down">
|
||||
<Icon name="chevron-down" size="small" />
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[data-slot="diff-changes-additions"] {
|
||||
font-family: var(--font-family-mono);
|
||||
font-feature-settings: var(--font-family-mono--font-feature-settings);
|
||||
font-size: var(--font-size-small);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-large);
|
||||
@@ -19,7 +19,7 @@
|
||||
[data-slot="diff-changes-deletions"] {
|
||||
font-family: var(--font-family-mono);
|
||||
font-feature-settings: var(--font-family-mono--font-feature-settings);
|
||||
font-size: var(--font-size-small);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-large);
|
||||
@@ -31,11 +31,12 @@
|
||||
|
||||
[data-component="diff-changes"][data-variant="bars"] {
|
||||
width: 18px;
|
||||
height: 14px;
|
||||
flex-shrink: 0;
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,10 +96,10 @@ export function DiffChanges(props: {
|
||||
<div data-component="diff-changes" data-variant={variant()} classList={{ [props.class ?? ""]: true }}>
|
||||
<Switch>
|
||||
<Match when={variant() === "bars"}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 12" fill="none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 14" fill="none">
|
||||
<g>
|
||||
<For each={visibleBlocks()}>
|
||||
{(color, i) => <rect x={i() * 4} width="2" height="12" rx="1" fill={color} />}
|
||||
{(color, i) => <rect x={i() * 4} width="2" height="14" rx="1" fill={color} />}
|
||||
</For>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
@@ -143,3 +143,39 @@
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
[data-component="icon-button"][data-icon="stop"] [data-slot="icon-svg"] rect {
|
||||
transform-origin: center;
|
||||
transform-box: fill-box;
|
||||
animation: stop-pulse 1.8s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes stop-pulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.12);
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="icon-button"].titlebar-icon {
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
aspect-ratio: auto;
|
||||
}
|
||||
|
||||
[data-component="icon-button"].titlebar-icon[data-variant="ghost"]:hover:not(:disabled) {
|
||||
background-color: var(--surface-raised-base-active);
|
||||
}
|
||||
|
||||
[data-component="icon-button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] {
|
||||
background-color: var(--surface-base-active);
|
||||
}
|
||||
|
||||
[data-component="icon-button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"]:hover:not(:disabled) {
|
||||
background-color: var(--surface-base-active);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export function IconButton(props: ComponentProps<"button"> & IconButtonProps) {
|
||||
<Kobalte
|
||||
{...rest}
|
||||
data-component="icon-button"
|
||||
data-icon={props.icon}
|
||||
data-size={split.size || "normal"}
|
||||
data-variant={split.variant || "secondary"}
|
||||
classList={{
|
||||
|
||||
@@ -7,11 +7,13 @@ const icons = {
|
||||
"arrow-right": `<path d="M11.6654 4.58398L17.082 10.0007L11.6654 15.4173M16.6654 10.0007H2.91536" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
archive: `<path d="M16.8747 6.24935H17.3747V5.74935H16.8747V6.24935ZM16.8747 16.8743V17.3743H17.3747V16.8743H16.8747ZM3.12467 16.8743H2.62467V17.3743H3.12467V16.8743ZM3.12467 6.24935V5.74935H2.62467V6.24935H3.12467ZM2.08301 2.91602V2.41602H1.58301V2.91602H2.08301ZM17.9163 2.91602H18.4163V2.41602H17.9163V2.91602ZM17.9163 6.24935V6.74935H18.4163V6.24935H17.9163ZM2.08301 6.24935H1.58301V6.74935H2.08301V6.24935ZM8.33301 9.08268H7.83301V10.0827H8.33301V9.58268V9.08268ZM11.6663 10.0827H12.1663V9.08268H11.6663V9.58268V10.0827ZM16.8747 6.24935H16.3747V16.8743H16.8747H17.3747V6.24935H16.8747ZM16.8747 16.8743V16.3743H3.12467V16.8743V17.3743H16.8747V16.8743ZM3.12467 16.8743H3.62467V6.24935H3.12467H2.62467V16.8743H3.12467ZM3.12467 6.24935V6.74935H16.8747V6.24935V5.74935H3.12467V6.24935ZM2.08301 2.91602V3.41602H17.9163V2.91602V2.41602H2.08301V2.91602ZM17.9163 2.91602H17.4163V6.24935H17.9163H18.4163V2.91602H17.9163ZM17.9163 6.24935V5.74935H2.08301V6.24935V6.74935H17.9163V6.24935ZM2.08301 6.24935H2.58301V2.91602H2.08301H1.58301V6.24935H2.08301ZM8.33301 9.58268V10.0827H11.6663V9.58268V9.08268H8.33301V9.58268Z" fill="currentColor"/>`,
|
||||
"bubble-5": `<path d="M18.3327 9.99935C18.3327 5.57227 15.0919 2.91602 9.99935 2.91602C4.90676 2.91602 1.66602 5.57227 1.66602 9.99935C1.66602 11.1487 2.45505 13.1006 2.57637 13.3939C2.58707 13.4197 2.59766 13.4434 2.60729 13.4697C2.69121 13.6987 3.04209 14.9354 1.66602 16.7674C3.51787 17.6528 5.48453 16.1973 5.48453 16.1973C6.84518 16.9193 8.46417 17.0827 9.99935 17.0827C15.0919 17.0827 18.3327 14.4264 18.3327 9.99935Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
prompt: `<path d="M14.5841 12.0807H17.9193V2.91406H5.6276V6.2474M14.5859 6.2474H2.08594V15.4141H5.0026V17.4974L8.7526 15.4141H14.5859V6.2474Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
brain: `<path d="M13.332 8.7487C11.4911 8.7487 9.9987 7.25631 9.9987 5.41536M6.66536 11.2487C8.50631 11.2487 9.9987 12.7411 9.9987 14.582M9.9987 2.78209L9.9987 17.0658M16.004 15.0475C17.1255 14.5876 17.9154 13.4849 17.9154 12.1978C17.9154 11.3363 17.5615 10.5575 16.9913 9.9987C17.5615 9.43991 17.9154 8.66108 17.9154 7.79962C17.9154 6.21199 16.7136 4.90504 15.1702 4.73878C14.7858 3.21216 13.4039 2.08203 11.758 2.08203C11.1171 2.08203 10.5162 2.25337 9.9987 2.55275C9.48117 2.25337 8.88032 2.08203 8.23944 2.08203C6.59353 2.08203 5.21157 3.21216 4.82722 4.73878C3.28377 4.90504 2.08203 6.21199 2.08203 7.79962C2.08203 8.66108 2.43585 9.43991 3.00609 9.9987C2.43585 10.5575 2.08203 11.3363 2.08203 12.1978C2.08203 13.4849 2.87191 14.5876 3.99339 15.0475C4.46688 16.7033 5.9917 17.9154 7.79962 17.9154C8.61335 17.9154 9.36972 17.6698 9.9987 17.2488C10.6277 17.6698 11.384 17.9154 12.1978 17.9154C14.0057 17.9154 15.5305 16.7033 16.004 15.0475Z" stroke="currentColor"/>`,
|
||||
"bullet-list": `<path d="M9.58329 13.7497H17.0833M9.58329 6.24967H17.0833M6.24996 6.24967C6.24996 7.17015 5.50377 7.91634 4.58329 7.91634C3.66282 7.91634 2.91663 7.17015 2.91663 6.24967C2.91663 5.3292 3.66282 4.58301 4.58329 4.58301C5.50377 4.58301 6.24996 5.3292 6.24996 6.24967ZM6.24996 13.7497C6.24996 14.6701 5.50377 15.4163 4.58329 15.4163C3.66282 15.4163 2.91663 14.6701 2.91663 13.7497C2.91663 12.8292 3.66282 12.083 4.58329 12.083C5.50377 12.083 6.24996 12.8292 6.24996 13.7497Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"check-small": `<path d="M6.5 11.4412L8.97059 13.5L13.5 6.5" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"chevron-down": `<path d="M6.6665 8.33325L9.99984 11.6666L13.3332 8.33325" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"chevron-right": `<path d="M8.33301 13.3327L11.6663 9.99935L8.33301 6.66602" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"chevron-left": `<path d="M12 15L7 10L12 5" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"chevron-right": `<path d="M8 15L13 10L8 5" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"chevron-grabber-vertical": `<path d="M6.66675 12.4998L10.0001 15.8332L13.3334 12.4998M6.66675 7.49984L10.0001 4.1665L13.3334 7.49984" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"chevron-double-right": `<path d="M11.6654 13.3346L14.9987 10.0013L11.6654 6.66797M5.83203 13.3346L9.16536 10.0013L5.83203 6.66797" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"circle-x": `<path fill-rule="evenodd" clip-rule="evenodd" d="M1.6665 10.0003C1.6665 5.39795 5.39746 1.66699 9.99984 1.66699C14.6022 1.66699 18.3332 5.39795 18.3332 10.0003C18.3332 14.6027 14.6022 18.3337 9.99984 18.3337C5.39746 18.3337 1.6665 14.6027 1.6665 10.0003ZM7.49984 6.91107L6.91058 7.50033L9.41058 10.0003L6.91058 12.5003L7.49984 13.0896L9.99984 10.5896L12.4998 13.0896L13.0891 12.5003L10.5891 10.0003L13.0891 7.50033L12.4998 6.91107L9.99984 9.41107L7.49984 6.91107Z" fill="currentColor"/>`,
|
||||
@@ -28,9 +30,12 @@ const icons = {
|
||||
eye: `<path d="M10 4.58325C5.83333 4.58325 2.5 9.99992 2.5 9.99992C2.5 9.99992 5.83333 15.4166 10 15.4166C14.1667 15.4166 17.5 9.99992 17.5 9.99992C17.5 9.99992 14.1667 4.58325 10 4.58325Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><circle cx="10" cy="10" r="2.5" stroke="currentColor"/>`,
|
||||
enter: `<path d="M5.83333 15.8334L2.5 12.5L5.83333 9.16671M3.33333 12.5H17.9167V4.58337H10" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
folder: `<path d="M2.08301 2.91675V16.2501H17.9163V5.41675H9.99967L8.33301 2.91675H2.08301Z" stroke="currentColor" stroke-linecap="round"/>`,
|
||||
"file-tree": `<path d="M4.58203 16.6693L6.66536 9.58594H17.082M4.58203 16.6693H16.457L18.5404 9.58594H17.082M4.58203 16.6693H2.08203V3.33594H8.33203L9.9987 5.83594H17.082V9.58594" stroke="currentColor" stroke-linecap="round"/>`,
|
||||
"file-tree-active": `<path d="M6.66536 9.58594L4.58203 16.6693H16.457L18.5404 9.58594H17.082H6.66536Z" fill="currentColor" fill-opacity="40%"/><path d="M4.58203 16.6693L6.66536 9.58594H17.082M4.58203 16.6693H16.457L18.5404 9.58594H17.082M4.58203 16.6693H2.08203V3.33594H8.33203L9.9987 5.83594H17.082V9.58594" stroke="currentColor" stroke-linecap="round"/>`,
|
||||
"magnifying-glass": `<path d="M15.8332 15.8337L13.0819 13.0824M14.6143 9.39088C14.6143 12.2759 12.2755 14.6148 9.39039 14.6148C6.50532 14.6148 4.1665 12.2759 4.1665 9.39088C4.1665 6.5058 6.50532 4.16699 9.39039 4.16699C12.2755 4.16699 14.6143 6.5058 14.6143 9.39088Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"plus-small": `<path d="M9.99984 5.41699V10.0003M9.99984 10.0003V14.5837M9.99984 10.0003H5.4165M9.99984 10.0003H14.5832" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
plus: `<path d="M9.9987 2.20703V9.9987M9.9987 9.9987V17.7904M9.9987 9.9987H2.20703M9.9987 9.9987H17.7904" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"new-session": `<path d="M17.0827 17.0807V17.5807H17.5827V17.0807H17.0827ZM2.91602 17.0807H2.41602L2.41602 17.5807H2.91602L2.91602 17.0807ZM2.91602 2.91406V2.41406H2.41602V2.91406H2.91602ZM9.58268 3.41406H10.0827V2.41406L9.58268 2.41406V2.91406V3.41406ZM17.5827 10.4141V9.91406L16.5827 9.91406V10.4141H17.0827H17.5827ZM6.24935 11.2474L5.8958 10.8938L5.74935 11.0403V11.2474H6.24935ZM6.24935 13.7474H5.74935V14.2474H6.24935V13.7474ZM8.74935 13.7474V14.2474H8.95646L9.1029 14.101L8.74935 13.7474ZM15.2077 2.28906L15.5612 1.93551L15.2077 1.58196L14.8541 1.93551L15.2077 2.28906ZM17.7077 4.78906L18.0612 5.14262L18.4148 4.78906L18.0612 4.43551L17.7077 4.78906ZM17.0827 17.0807V16.5807H2.91602V17.0807L2.91602 17.5807H17.0827V17.0807ZM2.91602 17.0807H3.41602L3.41602 2.91406H2.91602H2.41602L2.41602 17.0807H2.91602ZM2.91602 2.91406V3.41406L9.58268 3.41406V2.91406V2.41406L2.91602 2.41406V2.91406ZM17.0827 10.4141H16.5827V17.0807H17.0827H17.5827V10.4141H17.0827ZM6.24935 11.2474H5.74935V13.7474H6.24935H6.74935V11.2474H6.24935ZM6.24935 13.7474V14.2474L8.74935 14.2474V13.7474V13.2474L6.24935 13.2474V13.7474ZM6.24935 11.2474L6.6029 11.6009L15.5612 2.64262L15.2077 2.28906L14.8541 1.93551L5.8958 10.8938L6.24935 11.2474ZM15.2077 2.28906L14.8541 2.64262L17.3541 5.14262L17.7077 4.78906L18.0612 4.43551L15.5612 1.93551L15.2077 2.28906ZM17.7077 4.78906L17.3541 4.43551L8.3958 13.3938L8.74935 13.7474L9.1029 14.101L18.0612 5.14262L17.7077 4.78906Z" fill="currentColor"/>`,
|
||||
"pencil-line": `<path d="M9.58301 17.9166H17.9163M17.9163 5.83325L14.1663 2.08325L2.08301 14.1666V17.9166H5.83301L17.9163 5.83325Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
mcp: `<g><path d="M0.972656 9.37176L9.5214 1.60019C10.7018 0.527151 12.6155 0.527151 13.7957 1.60019C14.9761 2.67321 14.9761 4.41295 13.7957 5.48599L7.3397 11.3552" stroke="currentColor" stroke-linecap="round"/><path d="M7.42871 11.2747L13.7957 5.48643C14.9761 4.41338 16.8898 4.41338 18.0702 5.48643L18.1147 5.52688C19.2951 6.59993 19.2951 8.33966 18.1147 9.4127L10.3831 16.4414C9.98966 16.7991 9.98966 17.379 10.3831 17.7366L11.9707 19.1799" stroke="currentColor" stroke-linecap="round"/><path d="M11.6587 3.54346L5.33619 9.29119C4.15584 10.3642 4.15584 12.1039 5.33619 13.177C6.51649 14.25 8.43019 14.25 9.61054 13.177L15.9331 7.42923" stroke="currentColor" stroke-linecap="round"/></g>`,
|
||||
glasses: `<path d="M0.416626 7.91667H1.66663M19.5833 7.91667H18.3333M11.866 7.57987C11.3165 7.26398 10.6793 7.08333 9.99996 7.08333C9.32061 7.08333 8.68344 7.26398 8.13389 7.57987M8.74996 10C8.74996 12.0711 7.07103 13.75 4.99996 13.75C2.92889 13.75 1.24996 12.0711 1.24996 10C1.24996 7.92893 2.92889 6.25 4.99996 6.25C7.07103 6.25 8.74996 7.92893 8.74996 10ZM18.75 10C18.75 12.0711 17.071 13.75 15 13.75C12.9289 13.75 11.25 12.0711 11.25 10C11.25 7.92893 12.9289 6.25 15 6.25C17.071 6.25 18.75 7.92893 18.75 10Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
@@ -41,9 +46,9 @@ const icons = {
|
||||
"layout-left": `<path d="M2.91675 2.91699L2.91675 2.41699L2.41675 2.41699L2.41675 2.91699L2.91675 2.91699ZM17.0834 2.91699L17.5834 2.91699L17.5834 2.41699L17.0834 2.41699L17.0834 2.91699ZM17.0834 17.0837L17.0834 17.5837L17.5834 17.5837L17.5834 17.0837L17.0834 17.0837ZM2.91675 17.0837L2.41675 17.0837L2.41675 17.5837L2.91675 17.5837L2.91675 17.0837ZM7.41674 17.0837L7.41674 17.5837L8.41674 17.5837L8.41674 17.0837L7.91674 17.0837L7.41674 17.0837ZM8.41674 2.91699L8.41674 2.41699L7.41674 2.41699L7.41674 2.91699L7.91674 2.91699L8.41674 2.91699ZM2.91675 2.91699L2.91675 3.41699L17.0834 3.41699L17.0834 2.91699L17.0834 2.41699L2.91675 2.41699L2.91675 2.91699ZM17.0834 2.91699L16.5834 2.91699L16.5834 17.0837L17.0834 17.0837L17.5834 17.0837L17.5834 2.91699L17.0834 2.91699ZM17.0834 17.0837L17.0834 16.5837L2.91675 16.5837L2.91675 17.0837L2.91675 17.5837L17.0834 17.5837L17.0834 17.0837ZM2.91675 17.0837L3.41675 17.0837L3.41675 2.91699L2.91675 2.91699L2.41675 2.91699L2.41675 17.0837L2.91675 17.0837ZM7.91674 17.0837L8.41674 17.0837L8.41674 2.91699L7.91674 2.91699L7.41674 2.91699L7.41674 17.0837L7.91674 17.0837Z" fill="currentColor"/>`,
|
||||
"layout-left-partial": `<path d="M2.91732 2.91602L7.91732 2.91602L7.91732 17.0827H2.91732L2.91732 2.91602Z" fill="currentColor" fill-opacity="40%" /><path d="M2.91732 2.91602L17.084 2.91602M2.91732 2.91602L2.91732 17.0827M2.91732 2.91602L7.91732 2.91602M17.084 2.91602L17.084 17.0827M17.084 2.91602L7.91732 2.91602M17.084 17.0827L2.91732 17.0827M17.084 17.0827L7.91732 17.0827M2.91732 17.0827H7.91732M7.91732 17.0827L7.91732 2.91602" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"layout-left-full": `<path d="M2.91732 2.91602L7.91732 2.91602L7.91732 17.0827H2.91732L2.91732 2.91602Z" fill="currentColor"/><path d="M2.91732 2.91602L17.084 2.91602M2.91732 2.91602L2.91732 17.0827M2.91732 2.91602L7.91732 2.91602M17.084 2.91602L17.084 17.0827M17.084 2.91602L7.91732 2.91602M17.084 17.0827L2.91732 17.0827M17.084 17.0827L7.91732 17.0827M2.91732 17.0827H7.91732M7.91732 17.0827L7.91732 2.91602" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"layout-right": `<path d="M17.0832 2.91699L17.5832 2.91699L17.5832 2.41699L17.0832 2.41699L17.0832 2.91699ZM2.91651 2.91699L2.91651 2.41699L2.41651 2.41699L2.41651 2.91699L2.91651 2.91699ZM2.9165 17.0837L2.4165 17.0837L2.4165 17.5837L2.9165 17.5837L2.9165 17.0837ZM17.0832 17.0837L17.0832 17.5837L17.5832 17.5837L17.5832 17.0837L17.0832 17.0837ZM11.5832 17.0837L11.5832 17.5837L12.5832 17.5837L12.5832 17.0837L12.0832 17.0837L11.5832 17.0837ZM12.5832 2.91699L12.5832 2.41699L11.5832 2.41699L11.5832 2.91699L12.0832 2.91699L12.5832 2.91699ZM17.0832 2.91699L17.0832 2.41699L2.91651 2.41699L2.91651 2.91699L2.91651 3.41699L17.0832 3.41699L17.0832 2.91699ZM2.91651 2.91699L2.41651 2.91699L2.4165 17.0837L2.9165 17.0837L3.4165 17.0837L3.41651 2.91699L2.91651 2.91699ZM2.9165 17.0837L2.9165 17.5837L17.0832 17.5837L17.0832 17.0837L17.0832 16.5837L2.9165 16.5837L2.9165 17.0837ZM17.0832 17.0837L17.5832 17.0837L17.5832 2.91699L17.0832 2.91699L16.5832 2.91699L16.5832 17.0837L17.0832 17.0837ZM12.0832 17.0837L12.5832 17.0837L12.5832 2.91699L12.0832 2.91699L11.5832 2.91699L11.5832 17.0837L12.0832 17.0837Z" fill="currentColor"/>`,
|
||||
"layout-right-partial": `<path d="M12.0827 2.91602L2.91602 2.91602L2.91602 17.0827L12.0827 17.0827L12.0827 2.91602Z" fill="currentColor" fill-opacity="40%" /><path d="M2.91602 2.91602L17.0827 2.91602L17.0827 17.0827L2.91602 17.0827M2.91602 2.91602L2.91602 17.0827M2.91602 2.91602L12.0827 2.91602L12.0827 17.0827L2.91602 17.0827" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"layout-right-full": `<path d="M12.0827 2.91602L2.91602 2.91602L2.91602 17.0827L12.0827 17.0827L12.0827 2.91602Z" fill="currentColor"/><path d="M2.91602 2.91602L17.0827 2.91602L17.0827 17.0827L2.91602 17.0827M2.91602 2.91602L2.91602 17.0827M2.91602 2.91602L12.0827 2.91602L12.0827 17.0827L2.91602 17.0827" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"layout-right": `<path d="M2.91536 2.91406H2.36536V2.36406H2.91536V2.91406ZM2.91536 17.0807V17.6307H2.36536V17.0807H2.91536ZM17.082 17.0807H17.632V17.6307H17.082V17.0807ZM17.082 2.91406V2.36406H17.632V2.91406H17.082ZM6.9987 2.91406H6.4487V2.36406H6.9987V2.91406ZM6.9987 17.0807V17.6307H6.4487V17.0807H6.9987ZM2.91536 2.91406H3.46536V17.0807H2.91536H2.36536V2.91406H2.91536ZM2.91536 17.0807V16.5307H17.082V17.0807V17.6307H2.91536V17.0807ZM17.082 17.0807H16.532V2.91406H17.082H17.632V17.0807H17.082ZM17.082 2.91406V3.46406H2.91536V2.91406V2.36406H17.082V2.91406ZM6.9987 2.91406H7.5487V17.0807H6.9987H6.4487V2.91406H6.9987ZM17.082 17.0807L17.082 17.6307L6.9987 17.6307V17.0807V16.5307L17.082 16.5307L17.082 17.0807ZM6.9987 2.91406V2.36406H17.082V2.91406V3.46406H6.9987V2.91406Z" fill="currentColor"/>`,
|
||||
"layout-right-partial": `<path d="M17.082 17.0807L6.9987 17.0807V2.91406H17.082V17.0807Z" fill="currentColor" fill-opacity="40%" /><path d="M2.91536 2.91406H2.36536V2.36406H2.91536V2.91406ZM2.91536 17.0807V17.6307H2.36536V17.0807H2.91536ZM17.082 17.0807H17.632V17.6307H17.082V17.0807ZM17.082 2.91406V2.36406H17.632V2.91406H17.082ZM6.9987 2.91406H6.4487V2.36406H6.9987V2.91406ZM6.9987 17.0807V17.6307H6.4487V17.0807H6.9987ZM2.91536 2.91406H3.46536V17.0807H2.91536H2.36536V2.91406H2.91536ZM2.91536 17.0807V16.5307H17.082V17.0807V17.6307H2.91536V17.0807ZM17.082 17.0807H16.532V2.91406H17.082H17.632V17.0807H17.082ZM17.082 2.91406V3.46406H2.91536V2.91406V2.36406H17.082V2.91406ZM6.9987 2.91406H7.5487V17.0807H6.9987H6.4487V2.91406H6.9987ZM17.082 17.0807L17.082 17.6307L6.9987 17.6307V17.0807V16.5307L17.082 16.5307L17.082 17.0807ZM6.9987 2.91406V2.36406H17.082V2.91406V3.46406H6.9987V2.91406Z" fill="currentColor" />`,
|
||||
"layout-right-full": `<path d="M17.082 17.0807L6.9987 17.0807V2.91406H17.082V17.0807Z" fill="currentColor" /><path d="M2.91536 2.91406H2.36536V2.36406H2.91536V2.91406ZM2.91536 17.0807V17.6307H2.36536V17.0807H2.91536ZM17.082 17.0807H17.632V17.6307H17.082V17.0807ZM17.082 2.91406V2.36406H17.632V2.91406H17.082ZM6.9987 2.91406H6.4487V2.36406H6.9987V2.91406ZM6.9987 17.0807V17.6307H6.4487V17.0807H6.9987ZM2.91536 2.91406H3.46536V17.0807H2.91536H2.36536V2.91406H2.91536ZM2.91536 17.0807V16.5307H17.082V17.0807V17.6307H2.91536V17.0807ZM17.082 17.0807H16.532V2.91406H17.082H17.632V17.0807H17.082ZM17.082 2.91406V3.46406H2.91536V2.91406V2.36406H17.082V2.91406ZM6.9987 2.91406H7.5487V17.0807H6.9987H6.4487V2.91406H6.9987ZM17.082 17.0807L17.082 17.6307L6.9987 17.6307V17.0807V16.5307L17.082 16.5307L17.082 17.0807ZM6.9987 2.91406V2.36406H17.082V2.91406V3.46406H6.9987V2.91406Z" fill="currentColor" />`,
|
||||
"square-arrow-top-right": `<path d="M7.91675 2.9165H2.91675V17.0832H17.0834V12.0832M12.0834 2.9165H17.0834V7.9165M9.58342 10.4165L16.6667 3.33317" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
"speech-bubble": `<path d="M18.3334 10.0003C18.3334 5.57324 15.0927 2.91699 10.0001 2.91699C4.90749 2.91699 1.66675 5.57324 1.66675 10.0003C1.66675 11.1497 2.45578 13.1016 2.5771 13.3949C2.5878 13.4207 2.59839 13.4444 2.60802 13.4706C2.69194 13.6996 3.04282 14.9364 1.66675 16.7684C3.5186 17.6538 5.48526 16.1982 5.48526 16.1982C6.84592 16.9202 8.46491 17.0837 10.0001 17.0837C15.0927 17.0837 18.3334 14.4274 18.3334 10.0003Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
comment: `<path d="M16.25 3.75H3.75V16.25L6.875 14.4643H16.25V3.75Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px var(--border-interactive-focus);
|
||||
box-shadow: var(--inline-input-shadow, 0 0 0 1px var(--border-interactive-focus));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,17 @@ export type InlineInputProps = ComponentProps<"input"> & {
|
||||
}
|
||||
|
||||
export function InlineInput(props: InlineInputProps) {
|
||||
const [local, others] = splitProps(props, ["class", "width"])
|
||||
return <input data-component="inline-input" class={local.class} style={{ width: local.width }} {...others} />
|
||||
const [local, others] = splitProps(props, ["class", "width", "style"])
|
||||
|
||||
const style = () => {
|
||||
if (!local.style) return { width: local.width }
|
||||
if (typeof local.style === "string") {
|
||||
if (!local.width) return local.style
|
||||
return `${local.style};width:${local.width}`
|
||||
}
|
||||
if (!local.width) return local.style
|
||||
return { ...local.style, width: local.width }
|
||||
}
|
||||
|
||||
return <input data-component="inline-input" class={local.class} style={style()} {...others} />
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
overflow-wrap: break-word;
|
||||
color: var(--text-base);
|
||||
color: var(--text-strong);
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-base); /* 14px */
|
||||
line-height: var(--line-height-x-large);
|
||||
@@ -117,7 +117,7 @@
|
||||
.shiki {
|
||||
font-size: 13px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
border-radius: 6px;
|
||||
border: 0.5px solid var(--border-weak-base);
|
||||
}
|
||||
|
||||
@@ -127,11 +127,55 @@
|
||||
|
||||
[data-slot="markdown-copy-button"] {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
z-index: 1;
|
||||
|
||||
&::after {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: calc(100% + 4px);
|
||||
transform: translateX(-50%);
|
||||
z-index: 1000;
|
||||
|
||||
max-width: 320px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface-float-base);
|
||||
color: var(--text-invert-strong);
|
||||
padding: 2px 8px;
|
||||
border: 1px solid var(--border-weak-base, rgba(0, 0, 0, 0.07));
|
||||
box-shadow: var(--shadow-md);
|
||||
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-small);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-large);
|
||||
letter-spacing: var(--letter-spacing-normal);
|
||||
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="markdown-copy-button"]:hover::after,
|
||||
[data-slot="markdown-copy-button"]:focus-visible::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[data-slot="markdown-copy-button"][data-variant="secondary"] {
|
||||
box-shadow: none;
|
||||
border: 1px solid var(--border-weak-base);
|
||||
}
|
||||
|
||||
[data-slot="markdown-copy-button"][data-variant="secondary"] [data-slot="icon-svg"] {
|
||||
color: var(--icon-base);
|
||||
}
|
||||
|
||||
[data-component="markdown-code"]:hover [data-slot="markdown-copy-button"] {
|
||||
|
||||
@@ -85,7 +85,7 @@ function createCopyButton(labels: CopyLabels) {
|
||||
button.setAttribute("data-size", "small")
|
||||
button.setAttribute("data-slot", "markdown-copy-button")
|
||||
button.setAttribute("aria-label", labels.copy)
|
||||
button.setAttribute("title", labels.copy)
|
||||
button.setAttribute("data-tooltip", labels.copy)
|
||||
button.appendChild(createIcon(iconPaths.copy, "copy-icon"))
|
||||
button.appendChild(createIcon(iconPaths.check, "check-icon"))
|
||||
return button
|
||||
@@ -95,12 +95,12 @@ function setCopyState(button: HTMLButtonElement, labels: CopyLabels, copied: boo
|
||||
if (copied) {
|
||||
button.setAttribute("data-copied", "true")
|
||||
button.setAttribute("aria-label", labels.copied)
|
||||
button.setAttribute("title", labels.copied)
|
||||
button.setAttribute("data-tooltip", labels.copied)
|
||||
return
|
||||
}
|
||||
button.removeAttribute("data-copied")
|
||||
button.setAttribute("aria-label", labels.copy)
|
||||
button.setAttribute("title", labels.copy)
|
||||
button.setAttribute("data-tooltip", labels.copy)
|
||||
}
|
||||
|
||||
function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) {
|
||||
|
||||
@@ -14,15 +14,27 @@
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-large);
|
||||
letter-spacing: var(--letter-spacing-normal);
|
||||
color: var(--text-base);
|
||||
color: var(--text-strong);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
gap: 8px;
|
||||
|
||||
&[data-interrupted] {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="user-message-attachments"] {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
width: fit-content;
|
||||
max-width: min(82%, 64ch);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
[data-slot="user-message-attachment"] {
|
||||
@@ -71,15 +83,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="user-message-body"] {
|
||||
width: fit-content;
|
||||
max-width: min(82%, 64ch);
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
[data-slot="user-message-text"] {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
background: var(--surface-weak);
|
||||
background: var(--surface-base);
|
||||
border: 1px solid var(--border-weak-base);
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
border-radius: 6px;
|
||||
|
||||
[data-highlight="file"] {
|
||||
color: var(--syntax-property);
|
||||
@@ -89,19 +110,36 @@
|
||||
color: var(--syntax-type);
|
||||
}
|
||||
|
||||
[data-slot="user-message-copy-wrapper"] {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 7px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&:hover [data-slot="user-message-copy-wrapper"] {
|
||||
opacity: 1;
|
||||
[data-slot="user-message-copy-wrapper"] {
|
||||
min-height: 24px;
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.15s ease;
|
||||
will-change: opacity;
|
||||
|
||||
[data-component="tooltip-trigger"] {
|
||||
display: inline-flex;
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="user-message-copy-wrapper"][data-interrupted] {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
&:hover [data-slot="user-message-copy-wrapper"],
|
||||
&:focus-within [data-slot="user-message-copy-wrapper"] {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.text-text-strong {
|
||||
color: var(--text-strong);
|
||||
}
|
||||
@@ -115,21 +153,36 @@
|
||||
width: 100%;
|
||||
|
||||
[data-slot="text-part-body"] {
|
||||
position: relative;
|
||||
margin-top: 32px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
[data-slot="text-part-copy-wrapper"] {
|
||||
position: absolute;
|
||||
top: -28px;
|
||||
right: 8px;
|
||||
min-height: 24px;
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.15s ease;
|
||||
z-index: 1;
|
||||
will-change: opacity;
|
||||
|
||||
[data-component="tooltip-trigger"] {
|
||||
display: inline-flex;
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="text-part-body"]:hover [data-slot="text-part-copy-wrapper"] {
|
||||
[data-slot="text-part-copy-wrapper"][data-interrupted] {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
&:hover [data-slot="text-part-copy-wrapper"],
|
||||
&:focus-within [data-slot="text-part-copy-wrapper"] {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
[data-component="markdown"] {
|
||||
@@ -146,7 +199,7 @@
|
||||
|
||||
[data-component="markdown"] {
|
||||
margin-top: 24px;
|
||||
font-style: italic !important;
|
||||
font-style: normal;
|
||||
|
||||
p:has(strong) {
|
||||
margin-top: 24px;
|
||||
@@ -196,7 +249,8 @@
|
||||
|
||||
[data-component="tool-output"] {
|
||||
white-space: pre;
|
||||
padding: 8px 12px;
|
||||
padding: 0;
|
||||
margin-bottom: 24px;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -238,6 +292,79 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="collapsible-content"]:has([data-component="edit-content"]),
|
||||
[data-slot="collapsible-content"]:has([data-component="write-content"]),
|
||||
[data-slot="collapsible-content"]:has([data-component="apply-patch-files"]) {
|
||||
border: 1px solid var(--border-weak-base);
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] {
|
||||
width: 100%;
|
||||
border: 1px solid var(--border-weak-base);
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
[data-slot="bash-copy"] {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
&:hover [data-slot="bash-copy"],
|
||||
&:focus-within [data-slot="bash-copy"] {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
[data-slot="bash-copy"] [data-component="icon-button"][data-variant="secondary"] {
|
||||
box-shadow: none;
|
||||
border: 1px solid var(--border-weak-base);
|
||||
}
|
||||
|
||||
[data-slot="bash-copy"] [data-component="icon-button"][data-variant="secondary"] [data-slot="icon-svg"] {
|
||||
color: var(--icon-base);
|
||||
}
|
||||
|
||||
[data-slot="bash-scroll"] {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 240px;
|
||||
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="bash-pre"] {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
[data-slot="bash-pre"] code {
|
||||
font-family: var(--font-family-mono);
|
||||
font-feature-settings: var(--font-family-mono--font-feature-settings);
|
||||
font-size: 13px;
|
||||
line-height: var(--line-height-large);
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="collapsible-content"]:has([data-component="edit-content"]) [data-component="edit-content"],
|
||||
[data-slot="collapsible-content"]:has([data-component="write-content"]) [data-component="write-content"] {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
[data-component="edit-trigger"],
|
||||
[data-component="write-trigger"] {
|
||||
display: flex;
|
||||
@@ -258,9 +385,9 @@
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 8px;
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-base);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-large);
|
||||
@@ -268,18 +395,37 @@
|
||||
color: var(--text-base);
|
||||
}
|
||||
|
||||
[data-slot="message-part-title-spinner"] {
|
||||
margin-left: 4px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
color: var(--text-weak);
|
||||
|
||||
[data-component="spinner"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="message-part-title-text"] {
|
||||
text-transform: capitalize;
|
||||
color: var(--text-strong);
|
||||
}
|
||||
|
||||
[data-slot="message-part-title-filename"] {
|
||||
/* No text-transform - preserve original filename casing */
|
||||
font-weight: var(--font-weight-regular);
|
||||
}
|
||||
|
||||
[data-slot="message-part-path"] {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
font-weight: var(--font-weight-regular);
|
||||
}
|
||||
|
||||
[data-slot="message-part-directory"] {
|
||||
@@ -344,12 +490,19 @@
|
||||
}
|
||||
|
||||
[data-component="todos"] {
|
||||
padding: 10px 12px 24px 48px;
|
||||
padding: 10px 0 24px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
[data-component="checkbox"] {
|
||||
--checkbox-align: flex-start;
|
||||
--checkbox-offset: 0.5px;
|
||||
}
|
||||
|
||||
[data-slot="message-part-todo-content"] {
|
||||
line-height: var(--line-height-normal);
|
||||
|
||||
&[data-completed="completed"] {
|
||||
text-decoration: line-through;
|
||||
color: var(--text-weaker);
|
||||
@@ -357,41 +510,55 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="task-tools"] {
|
||||
padding: 8px 12px;
|
||||
[data-component="context-tool-group-trigger"] {
|
||||
width: 100%;
|
||||
min-height: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 0px;
|
||||
cursor: pointer;
|
||||
|
||||
[data-slot="task-tool-item"] {
|
||||
[data-slot="context-tool-group-title"] {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--text-weak);
|
||||
|
||||
[data-slot="icon-svg"] {
|
||||
flex-shrink: 0;
|
||||
color: var(--icon-weak);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="task-tool-title"] {
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-small);
|
||||
font-size: 14px;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-large);
|
||||
color: var(--text-weak);
|
||||
color: var(--text-strong);
|
||||
}
|
||||
|
||||
[data-slot="task-tool-subtitle"] {
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-large);
|
||||
color: var(--text-weaker);
|
||||
[data-slot="context-tool-group-label"] {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
[data-slot="context-tool-group-summary"] {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: var(--font-weight-regular);
|
||||
color: var(--text-base);
|
||||
}
|
||||
|
||||
[data-slot="collapsible-arrow"] {
|
||||
color: var(--icon-weaker);
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="context-tool-group-list"] {
|
||||
padding: 6px 0 4px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
||||
[data-slot="context-tool-group-item"] {
|
||||
min-width: 0;
|
||||
padding: 6px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,170 +716,322 @@
|
||||
}
|
||||
|
||||
[data-component="question-prompt"] {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 12px;
|
||||
background-color: var(--surface-inset-base);
|
||||
border-radius: 0 0 6px 6px;
|
||||
gap: 12px;
|
||||
gap: 0;
|
||||
min-height: 0;
|
||||
max-height: var(--question-prompt-max-height, 100dvh);
|
||||
|
||||
[data-slot="question-tabs"] {
|
||||
[data-slot="question-body"] {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 8px 8px 0;
|
||||
background-color: var(--surface-raised-stronger-non-alpha);
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--shadow-xs-border);
|
||||
overflow: clip;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
[data-slot="question-tab"] {
|
||||
padding: 4px 12px;
|
||||
font-size: 13px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--surface-base);
|
||||
color: var(--text-base);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
color 0.15s,
|
||||
background-color 0.15s;
|
||||
[data-slot="question-header"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--surface-base-hover);
|
||||
}
|
||||
[data-slot="question-header-title"] {
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: 14px;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-large);
|
||||
color: var(--text-strong);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&[data-active="true"] {
|
||||
background-color: var(--surface-raised-base);
|
||||
}
|
||||
[data-slot="question-progress"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&[data-answered="true"] {
|
||||
color: var(--text-strong);
|
||||
}
|
||||
[data-slot="question-progress-segment"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
border-radius: 999px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
width: 16px;
|
||||
height: 2px;
|
||||
border-radius: 999px;
|
||||
background-color: var(--icon-weak-base);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
&[data-active="true"]::after {
|
||||
background-color: var(--icon-strong-base);
|
||||
}
|
||||
|
||||
&[data-answered="true"]::after {
|
||||
background-color: var(--icon-interactive-base);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="question-content"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 4px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
[data-slot="question-text"] {
|
||||
font-size: 14px;
|
||||
color: var(--text-base);
|
||||
line-height: 1.5;
|
||||
}
|
||||
[data-slot="question-text"] {
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: 14px;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-large);
|
||||
color: var(--text-strong);
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
[data-slot="question-hint"] {
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: 13px;
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-large);
|
||||
color: var(--text-weak);
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
[data-slot="question-options"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
margin-top: 12px;
|
||||
padding: 1px 1px 8px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
[data-slot="question-option"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 2px;
|
||||
padding: 8px 12px;
|
||||
background-color: var(--surface-base);
|
||||
border: 1px solid var(--border-weaker-base);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
transition:
|
||||
background-color 0.15s,
|
||||
border-color 0.15s;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--surface-base-hover);
|
||||
border-color: var(--border-default);
|
||||
}
|
||||
|
||||
&[data-picked="true"] {
|
||||
[data-component="icon"] {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--text-strong);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="option-label"] {
|
||||
font-size: 14px;
|
||||
color: var(--text-base);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
[data-slot="option-description"] {
|
||||
font-size: 12px;
|
||||
color: var(--text-weak);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="custom-input-form"] {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px 0;
|
||||
align-items: stretch;
|
||||
|
||||
[data-slot="custom-input"] {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: 6px;
|
||||
background-color: var(--surface-base);
|
||||
color: var(--text-base);
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--border-focus);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="button"] {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="question-review"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
[data-slot="review-title"] {
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="review-item"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
font-size: 13px;
|
||||
[data-slot="question-option"] {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 8px 8px 8px 10px;
|
||||
background-color: var(--surface-raised-stronger-non-alpha);
|
||||
border: 1px solid var(--border-weak-base);
|
||||
border-radius: 6px;
|
||||
box-shadow: none;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 0.15s ease,
|
||||
border-color 0.15s ease,
|
||||
box-shadow 0.15s ease;
|
||||
|
||||
[data-slot="review-label"] {
|
||||
color: var(--text-weak);
|
||||
&:hover:not([data-picked="true"]) {
|
||||
background-color: var(--background-base);
|
||||
}
|
||||
|
||||
&[data-picked="true"] {
|
||||
background-color: var(--surface-interactive-weak);
|
||||
border-color: transparent;
|
||||
box-shadow: var(--shadow-xs-border-hover);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="question-option-check"] {
|
||||
display: inline-flex;
|
||||
transform: translateY(2px);
|
||||
}
|
||||
|
||||
[data-slot="question-option-box"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 2px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border-weak-base);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
background-color: transparent;
|
||||
transition:
|
||||
background-color 0.15s ease,
|
||||
border-color 0.15s ease;
|
||||
|
||||
[data-component="icon"] {
|
||||
opacity: 0;
|
||||
color: var(--icon-base);
|
||||
}
|
||||
|
||||
&[data-type="radio"] {
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
[data-slot="question-option-radio-dot"] {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background-color: var(--background-stronger);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&[data-picked="true"] {
|
||||
border-color: var(--icon-interactive-base);
|
||||
|
||||
[data-component="icon"] {
|
||||
opacity: 1;
|
||||
color: var(--icon-invert-base);
|
||||
}
|
||||
|
||||
[data-slot="review-value"] {
|
||||
color: var(--text-strong);
|
||||
&[data-type="checkbox"] {
|
||||
background-color: var(--icon-interactive-base);
|
||||
}
|
||||
|
||||
&[data-answered="false"] {
|
||||
color: var(--text-weak);
|
||||
&[data-type="radio"] {
|
||||
background-color: var(--icon-interactive-base);
|
||||
[data-slot="question-option-radio-dot"] {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="question-actions"] {
|
||||
[data-slot="question-option-main"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
[data-slot="option-label"] {
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: 14px;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-large);
|
||||
color: var(--text-strong);
|
||||
}
|
||||
|
||||
[data-slot="option-description"] {
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: 14px;
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-large);
|
||||
color: var(--text-base);
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-slot="question-option"][data-custom="true"] {
|
||||
[data-slot="option-description"] {
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="question-custom"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
[data-slot="question-custom-input-wrap"] {
|
||||
padding-left: 36px;
|
||||
}
|
||||
|
||||
[data-slot="question-custom-input"] {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: 14px;
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-large);
|
||||
color: var(--text-base);
|
||||
min-width: 0;
|
||||
cursor: text;
|
||||
resize: none;
|
||||
overflow: hidden;
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 1px solid var(--border-interactive-base);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--radius-xs);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="question-footer"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
padding: 32px 8px 8px;
|
||||
background-color: var(--background-base);
|
||||
border: 1px solid var(--border-weak-base);
|
||||
border-radius: 12px;
|
||||
overflow: clip;
|
||||
margin-top: -24px;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
[data-slot="question-footer-actions"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -720,7 +1039,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 8px 12px;
|
||||
padding: 8px 0;
|
||||
|
||||
[data-slot="question-answer-item"] {
|
||||
display: flex;
|
||||
@@ -746,18 +1065,13 @@
|
||||
[data-component="apply-patch-file"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-top: 1px solid var(--border-weaker-base);
|
||||
|
||||
&:first-child {
|
||||
border-top: 1px solid var(--border-weaker-base);
|
||||
}
|
||||
|
||||
[data-slot="apply-patch-file-header"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
background-color: var(--surface-inset-base);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
[data-slot="apply-patch-file-action"] {
|
||||
@@ -799,7 +1113,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="apply-patch-file"] + [data-component="apply-patch-file"] {
|
||||
border-top: 1px solid var(--border-weaker-base);
|
||||
}
|
||||
|
||||
[data-component="apply-patch-file-diff"] {
|
||||
border-top: 1px solid var(--border-weaker-base);
|
||||
max-height: 420px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
||||
[data-component="session-turn"] {
|
||||
--session-turn-sticky-height: 0px;
|
||||
--sticky-header-height: calc(var(--session-title-height, 0px) + var(--session-turn-sticky-height, 0px) + 24px);
|
||||
/* flex: 1; */
|
||||
--sticky-header-height: calc(var(--session-title-height, 0px) + 24px);
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
@@ -30,525 +28,30 @@
|
||||
min-width: 0;
|
||||
gap: 18px;
|
||||
overflow-anchor: none;
|
||||
|
||||
[data-slot="session-turn-badge"] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: var(--font-family-mono);
|
||||
font-size: var(--font-size-x-small);
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-normal);
|
||||
white-space: nowrap;
|
||||
color: var(--text-base);
|
||||
background: var(--surface-raised-base);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="session-turn-attachments"] {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-sticky"] {
|
||||
width: calc(100% + 9px);
|
||||
position: sticky;
|
||||
top: var(--session-title-height, 0px);
|
||||
z-index: 20;
|
||||
background-color: var(--background-stronger);
|
||||
margin-left: -9px;
|
||||
padding-left: 9px;
|
||||
/* padding-bottom: 12px; */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: var(--background-stronger);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 32px;
|
||||
background: linear-gradient(to bottom, var(--background-stronger), transparent);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="session-turn-message-header"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-message-content"] {
|
||||
margin-top: 0;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
[data-component="user-message"] [data-slot="user-message-text"] {
|
||||
max-height: var(--user-message-collapsed-height, 64px);
|
||||
}
|
||||
|
||||
[data-component="user-message"][data-expanded="true"] [data-slot="user-message-text"] {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
[data-component="user-message"][data-can-expand="true"] [data-slot="user-message-text"] {
|
||||
padding-right: 36px;
|
||||
padding-bottom: 28px;
|
||||
}
|
||||
|
||||
[data-component="user-message"][data-can-expand="true"]:not([data-expanded="true"])
|
||||
[data-slot="user-message-text"]::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 8px;
|
||||
bottom: 0px;
|
||||
background:
|
||||
linear-gradient(to bottom, transparent, var(--surface-weak)),
|
||||
linear-gradient(to bottom, transparent, var(--surface-weak));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[data-component="user-message"] [data-slot="user-message-text"] [data-slot="user-message-expand"] {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
right: 6px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[data-component="user-message"][data-can-expand="true"]
|
||||
[data-slot="user-message-text"]
|
||||
[data-slot="user-message-expand"],
|
||||
[data-component="user-message"][data-expanded="true"]
|
||||
[data-slot="user-message-text"]
|
||||
[data-slot="user-message-expand"] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
color: var(--text-weak);
|
||||
|
||||
[data-slot="icon-svg"] {
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="user-message"][data-expanded="true"]
|
||||
[data-slot="user-message-text"]
|
||||
[data-slot="user-message-expand"]
|
||||
[data-slot="icon-svg"] {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
[data-component="user-message"] [data-slot="user-message-text"] [data-slot="user-message-expand"]:hover {
|
||||
background: var(--surface-raised-base);
|
||||
color: var(--text-base);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-user-badges"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-message-title"] {
|
||||
width: 100%;
|
||||
font-size: var(--font-size-large);
|
||||
font-weight: 500;
|
||||
color: var(--text-strong);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-message-title"] h1 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-typewriter"] {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-summary-section"] {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-summary-header"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
|
||||
[data-slot="session-turn-summary-title-row"] {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-response"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-response-copy-wrapper"] {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
&:hover [data-slot="session-turn-response-copy-wrapper"],
|
||||
&:focus-within [data-slot="session-turn-response-copy-wrapper"] {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: var(--font-size-base);
|
||||
line-height: var(--line-height-x-large);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="session-turn-summary-title"] {
|
||||
font-size: 13px;
|
||||
/* text-12-medium */
|
||||
font-weight: 500;
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-markdown"],
|
||||
[data-slot="session-turn-accordion"] [data-slot="accordion-content"] {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-markdown"] {
|
||||
&[data-diffs="true"] {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
&[data-fade="true"] > * {
|
||||
animation: fadeUp 0.4s ease-out forwards;
|
||||
opacity: 0;
|
||||
|
||||
&:nth-child(1) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
&:nth-child(5) {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
&:nth-child(6) {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
&:nth-child(7) {
|
||||
animation-delay: 0.7s;
|
||||
}
|
||||
|
||||
&:nth-child(8) {
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
&:nth-child(9) {
|
||||
animation-delay: 0.9s;
|
||||
}
|
||||
|
||||
&:nth-child(10) {
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
&:nth-child(11) {
|
||||
animation-delay: 1.1s;
|
||||
}
|
||||
|
||||
&:nth-child(12) {
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
|
||||
&:nth-child(13) {
|
||||
animation-delay: 1.3s;
|
||||
}
|
||||
|
||||
&:nth-child(14) {
|
||||
animation-delay: 1.4s;
|
||||
}
|
||||
|
||||
&:nth-child(15) {
|
||||
animation-delay: 1.5s;
|
||||
}
|
||||
|
||||
&:nth-child(16) {
|
||||
animation-delay: 1.6s;
|
||||
}
|
||||
|
||||
&:nth-child(17) {
|
||||
animation-delay: 1.7s;
|
||||
}
|
||||
|
||||
&:nth-child(18) {
|
||||
animation-delay: 1.8s;
|
||||
}
|
||||
|
||||
&:nth-child(19) {
|
||||
animation-delay: 1.9s;
|
||||
}
|
||||
|
||||
&:nth-child(20) {
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
&:nth-child(21) {
|
||||
animation-delay: 2.1s;
|
||||
}
|
||||
|
||||
&:nth-child(22) {
|
||||
animation-delay: 2.2s;
|
||||
}
|
||||
|
||||
&:nth-child(23) {
|
||||
animation-delay: 2.3s;
|
||||
}
|
||||
|
||||
&:nth-child(24) {
|
||||
animation-delay: 2.4s;
|
||||
}
|
||||
|
||||
&:nth-child(25) {
|
||||
animation-delay: 2.5s;
|
||||
}
|
||||
|
||||
&:nth-child(26) {
|
||||
animation-delay: 2.6s;
|
||||
}
|
||||
|
||||
&:nth-child(27) {
|
||||
animation-delay: 2.7s;
|
||||
}
|
||||
|
||||
&:nth-child(28) {
|
||||
animation-delay: 2.8s;
|
||||
}
|
||||
|
||||
&:nth-child(29) {
|
||||
animation-delay: 2.9s;
|
||||
}
|
||||
|
||||
&:nth-child(30) {
|
||||
animation-delay: 3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="session-turn-summary-section"] {
|
||||
position: relative;
|
||||
|
||||
[data-slot="session-turn-summary-copy"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
&:hover [data-slot="session-turn-summary-copy"] {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="session-turn-accordion"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
[data-component="sticky-accordion-header"] {
|
||||
top: var(--sticky-header-height, 0px);
|
||||
}
|
||||
|
||||
[data-component="sticky-accordion-header"][data-expanded]::before,
|
||||
[data-slot="accordion-item"][data-expanded] [data-component="sticky-accordion-header"]::before {
|
||||
top: calc(-1 * var(--sticky-header-height, 0px));
|
||||
}
|
||||
|
||||
[data-slot="session-turn-accordion-trigger-content"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
gap: 20px;
|
||||
|
||||
[data-expandable="false"] {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="session-turn-file-info"] {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-file-icon"] {
|
||||
flex-shrink: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-file-path"] {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-directory"] {
|
||||
color: var(--text-base);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
direction: rtl;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-filename"] {
|
||||
color: var(--text-strong);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-accordion-actions"] {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-accordion-content"] {
|
||||
max-height: 240px;
|
||||
/* max-h-60 */
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-accordion-content"]::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-response-section"] {
|
||||
width: calc(100% + 9px);
|
||||
min-width: 0;
|
||||
margin-left: -9px;
|
||||
padding-left: 9px;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-collapsible"] {
|
||||
gap: 32px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-collapsible-trigger-content"] {
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
[data-slot="session-turn-thinking"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--text-weak);
|
||||
|
||||
[data-slot="session-turn-trigger-icon"] {
|
||||
color: var(--icon-base);
|
||||
}
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-large);
|
||||
min-height: 20px;
|
||||
|
||||
[data-component="spinner"] {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
[data-component="icon"] {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="session-turn-retry-message"] {
|
||||
font-weight: 500;
|
||||
color: var(--syntax-critical);
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-retry-seconds"] {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-retry-attempt"] {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-status-text"] {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-details-text"] {
|
||||
font-size: 13px;
|
||||
/* text-12-medium */
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.error-card {
|
||||
@@ -560,50 +63,112 @@
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.retry-error-link,
|
||||
.error-card-link {
|
||||
color: var(--text-strong);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-collapsible-content-inner"] {
|
||||
[data-slot="session-turn-assistant-content"] {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: stretch;
|
||||
gap: 12px;
|
||||
margin-left: 12px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
border-left: 1px solid var(--border-base);
|
||||
|
||||
> :first-child > [data-component="markdown"]:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="session-turn-permission-parts"] {
|
||||
[data-slot="session-turn-diffs"] {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
[data-component="session-turn-diffs-trigger"] {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-diffs-title"] {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-diffs-label"] {
|
||||
color: var(--text-strong);
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-large);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-diffs-count"] {
|
||||
color: var(--text-base);
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-x-large);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-diffs-meta"] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
|
||||
[data-component="diff-changes"][data-variant="bars"] {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-turn-diffs-content"] {
|
||||
padding-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-question-parts"] {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
[data-component="session-turn-diff"] {
|
||||
border: 1px solid var(--border-weaker-base);
|
||||
border-radius: var(--radius-md);
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
[data-slot="session-turn-answered-question-parts"] {
|
||||
[data-slot="session-turn-diff-header"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 6px 10px;
|
||||
border-bottom: 1px solid var(--border-weaker-base);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-diff-path"] {
|
||||
display: inline-flex;
|
||||
min-width: 0;
|
||||
align-items: baseline;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-small);
|
||||
line-height: var(--line-height-large);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-diff-directory"] {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-diff-filename"] {
|
||||
color: var(--text-strong);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
[data-slot="session-turn-diff-view"] {
|
||||
background-color: var(--surface-inset-base);
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,18 @@
|
||||
import {
|
||||
AssistantMessage,
|
||||
FilePart,
|
||||
Message as MessageType,
|
||||
Part as PartType,
|
||||
type PermissionRequest,
|
||||
type QuestionRequest,
|
||||
TextPart,
|
||||
ToolPart,
|
||||
} from "@opencode-ai/sdk/v2/client"
|
||||
import { AssistantMessage, type FileDiff, Message as MessageType, Part as PartType } from "@opencode-ai/sdk/v2/client"
|
||||
import { useData } from "../context"
|
||||
import { type UiI18nKey, type UiI18nParams, useI18n } from "../context/i18n"
|
||||
import { useDiffComponent } from "../context/diff"
|
||||
|
||||
import { Binary } from "@opencode-ai/util/binary"
|
||||
import { createEffect, createMemo, createSignal, For, Match, on, onCleanup, ParentProps, Show, Switch } from "solid-js"
|
||||
import { Message, Part } from "./message-part"
|
||||
import { Markdown } from "./markdown"
|
||||
import { IconButton } from "./icon-button"
|
||||
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
||||
import { createMemo, createSignal, For, ParentProps, Show } from "solid-js"
|
||||
import { Dynamic } from "solid-js/web"
|
||||
import { Message } from "./message-part"
|
||||
import { Card } from "./card"
|
||||
import { Button } from "./button"
|
||||
import { Spinner } from "./spinner"
|
||||
import { Tooltip } from "./tooltip"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { DateTime, DurationUnit, Interval } from "luxon"
|
||||
import { Collapsible } from "./collapsible"
|
||||
import { DiffChanges } from "./diff-changes"
|
||||
import { TextShimmer } from "./text-shimmer"
|
||||
import { createAutoScroll } from "../hooks"
|
||||
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
||||
|
||||
type Translator = (key: UiI18nKey, params?: UiI18nParams) => string
|
||||
import { useI18n } from "../context/i18n"
|
||||
|
||||
function record(value: unknown): value is Record<string, unknown> {
|
||||
return !!value && typeof value === "object" && !Array.isArray(value)
|
||||
@@ -80,117 +67,42 @@ function unwrap(message: string) {
|
||||
return message
|
||||
}
|
||||
|
||||
function computeStatusFromPart(part: PartType | undefined, t: Translator): string | undefined {
|
||||
if (!part) return undefined
|
||||
|
||||
if (part.type === "tool") {
|
||||
switch (part.tool) {
|
||||
case "task":
|
||||
return t("ui.sessionTurn.status.delegating")
|
||||
case "todowrite":
|
||||
case "todoread":
|
||||
return t("ui.sessionTurn.status.planning")
|
||||
case "read":
|
||||
return t("ui.sessionTurn.status.gatheringContext")
|
||||
case "list":
|
||||
case "grep":
|
||||
case "glob":
|
||||
return t("ui.sessionTurn.status.searchingCodebase")
|
||||
case "webfetch":
|
||||
return t("ui.sessionTurn.status.searchingWeb")
|
||||
case "edit":
|
||||
case "write":
|
||||
return t("ui.sessionTurn.status.makingEdits")
|
||||
case "bash":
|
||||
return t("ui.sessionTurn.status.runningCommands")
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
if (part.type === "reasoning") {
|
||||
const text = part.text ?? ""
|
||||
const match = text.trimStart().match(/^\*\*(.+?)\*\*/)
|
||||
if (match) return t("ui.sessionTurn.status.thinkingWithTopic", { topic: match[1].trim() })
|
||||
return t("ui.sessionTurn.status.thinking")
|
||||
}
|
||||
if (part.type === "text") {
|
||||
return t("ui.sessionTurn.status.gatheringThoughts")
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function same<T>(a: readonly T[], b: readonly T[]) {
|
||||
if (a === b) return true
|
||||
if (a.length !== b.length) return false
|
||||
return a.every((x, i) => x === b[i])
|
||||
}
|
||||
|
||||
function isAttachment(part: PartType | undefined) {
|
||||
if (part?.type !== "file") return false
|
||||
const mime = (part as FilePart).mime ?? ""
|
||||
return mime.startsWith("image/") || mime === "application/pdf"
|
||||
}
|
||||
|
||||
function list<T>(value: T[] | undefined | null, fallback: T[]) {
|
||||
if (Array.isArray(value)) return value
|
||||
return fallback
|
||||
}
|
||||
|
||||
function AssistantMessageItem(props: {
|
||||
message: AssistantMessage
|
||||
responsePartId: string | undefined
|
||||
hideResponsePart: boolean
|
||||
hideReasoning: boolean
|
||||
hidden?: () => readonly { messageID: string; callID: string }[]
|
||||
}) {
|
||||
const hidden = new Set(["todowrite", "todoread"])
|
||||
|
||||
function visible(part: PartType) {
|
||||
if (part.type === "tool") {
|
||||
if (hidden.has(part.tool)) return false
|
||||
if (part.tool === "question") return part.state.status !== "pending" && part.state.status !== "running"
|
||||
return true
|
||||
}
|
||||
if (part.type === "text") return !!part.text?.trim()
|
||||
if (part.type === "reasoning") return !!part.text?.trim()
|
||||
return false
|
||||
}
|
||||
|
||||
function AssistantMessageItem(props: { message: AssistantMessage; showAssistantCopyPartID?: string | null }) {
|
||||
const data = useData()
|
||||
const emptyParts: PartType[] = []
|
||||
const msgParts = createMemo(() => list(data.store.part?.[props.message.id], emptyParts))
|
||||
const lastTextPart = createMemo(() => {
|
||||
const parts = msgParts()
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
const part = parts[i]
|
||||
if (part?.type === "text") return part as TextPart
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
|
||||
const filteredParts = createMemo(() => {
|
||||
let parts = msgParts()
|
||||
|
||||
if (props.hideReasoning) {
|
||||
parts = parts.filter((part) => part?.type !== "reasoning")
|
||||
}
|
||||
|
||||
if (props.hideResponsePart) {
|
||||
const responsePartId = props.responsePartId
|
||||
if (responsePartId && responsePartId === lastTextPart()?.id) {
|
||||
parts = parts.filter((part) => part?.id !== responsePartId)
|
||||
}
|
||||
}
|
||||
|
||||
const hidden = props.hidden?.() ?? []
|
||||
if (hidden.length === 0) return parts
|
||||
|
||||
const id = props.message.id
|
||||
return parts.filter((part) => {
|
||||
if (part?.type !== "tool") return true
|
||||
const tool = part as ToolPart
|
||||
return !hidden.some((h) => h.messageID === id && h.callID === tool.callID)
|
||||
})
|
||||
})
|
||||
|
||||
return <Message message={props.message} parts={filteredParts()} />
|
||||
return <Message message={props.message} parts={msgParts()} showAssistantCopyPartID={props.showAssistantCopyPartID} />
|
||||
}
|
||||
|
||||
export function SessionTurn(
|
||||
props: ParentProps<{
|
||||
sessionID: string
|
||||
sessionTitle?: string
|
||||
messageID: string
|
||||
lastUserMessageID?: string
|
||||
stepsExpanded?: boolean
|
||||
onStepsExpandedToggle?: () => void
|
||||
onUserInteracted?: () => void
|
||||
classes?: {
|
||||
root?: string
|
||||
@@ -199,16 +111,14 @@ export function SessionTurn(
|
||||
}
|
||||
}>,
|
||||
) {
|
||||
const i18n = useI18n()
|
||||
const data = useData()
|
||||
const i18n = useI18n()
|
||||
const diffComponent = useDiffComponent()
|
||||
|
||||
const emptyMessages: MessageType[] = []
|
||||
const emptyParts: PartType[] = []
|
||||
const emptyFiles: FilePart[] = []
|
||||
const emptyAssistant: AssistantMessage[] = []
|
||||
const emptyPermissions: PermissionRequest[] = []
|
||||
const emptyQuestions: QuestionRequest[] = []
|
||||
const emptyQuestionParts: { part: ToolPart; message: AssistantMessage }[] = []
|
||||
const emptyDiffs: FileDiff[] = []
|
||||
const idle = { type: "idle" as const }
|
||||
|
||||
const allMessages = createMemo(() => list(data.store.message?.[props.sessionID], emptyMessages))
|
||||
@@ -256,18 +166,22 @@ export function SessionTurn(
|
||||
return list(data.store.part?.[msg.id], emptyParts)
|
||||
})
|
||||
|
||||
const attachmentParts = createMemo(() => {
|
||||
const msgParts = parts()
|
||||
if (msgParts.length === 0) return emptyFiles
|
||||
return msgParts.filter((part) => isAttachment(part)) as FilePart[]
|
||||
})
|
||||
const diffs = createMemo(() => {
|
||||
const files = message()?.summary?.diffs
|
||||
if (!files?.length) return emptyDiffs
|
||||
|
||||
const stickyParts = createMemo(() => {
|
||||
const msgParts = parts()
|
||||
if (msgParts.length === 0) return emptyParts
|
||||
if (attachmentParts().length === 0) return msgParts
|
||||
return msgParts.filter((part) => !isAttachment(part))
|
||||
const seen = new Set<string>()
|
||||
return files
|
||||
.reduceRight<FileDiff[]>((result, diff) => {
|
||||
if (seen.has(diff.file)) return result
|
||||
seen.add(diff.file)
|
||||
result.push(diff)
|
||||
return result
|
||||
}, [])
|
||||
.reverse()
|
||||
})
|
||||
const edited = createMemo(() => diffs().length)
|
||||
const [open, setOpen] = createSignal(false)
|
||||
|
||||
const assistantMessages = createMemo(
|
||||
() => {
|
||||
@@ -291,9 +205,27 @@ export function SessionTurn(
|
||||
{ equals: same },
|
||||
)
|
||||
|
||||
const lastAssistantMessage = createMemo(() => assistantMessages().at(-1))
|
||||
const interrupted = createMemo(() => assistantMessages().some((m) => m.error?.name === "MessageAbortedError"))
|
||||
const error = createMemo(
|
||||
() => assistantMessages().find((m) => m.error && m.error.name !== "MessageAbortedError")?.error,
|
||||
)
|
||||
const showAssistantCopyPartID = createMemo(() => {
|
||||
const messages = assistantMessages()
|
||||
|
||||
const error = createMemo(() => assistantMessages().find((m) => m.error)?.error)
|
||||
for (let i = messages.length - 1; i >= 0; i--) {
|
||||
const message = messages[i]
|
||||
if (!message) continue
|
||||
|
||||
const parts = list(data.store.part?.[message.id], emptyParts)
|
||||
for (let j = parts.length - 1; j >= 0; j--) {
|
||||
const part = parts[j]
|
||||
if (!part || part.type !== "text" || !part.text?.trim()) continue
|
||||
return part.id
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
})
|
||||
const errorText = createMemo(() => {
|
||||
const msg = error()?.data?.message
|
||||
if (typeof msg === "string") return unwrap(msg)
|
||||
@@ -301,314 +233,29 @@ export function SessionTurn(
|
||||
return unwrap(String(msg))
|
||||
})
|
||||
|
||||
const lastTextPart = createMemo(() => {
|
||||
const msgs = assistantMessages()
|
||||
for (let mi = msgs.length - 1; mi >= 0; mi--) {
|
||||
const msgParts = list(data.store.part?.[msgs[mi].id], emptyParts)
|
||||
for (let pi = msgParts.length - 1; pi >= 0; pi--) {
|
||||
const part = msgParts[pi]
|
||||
if (part?.type === "text") return part as TextPart
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
|
||||
const hasSteps = createMemo(() => {
|
||||
for (const m of assistantMessages()) {
|
||||
const msgParts = list(data.store.part?.[m.id], emptyParts)
|
||||
for (const p of msgParts) {
|
||||
if (p?.type === "tool") return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
const permissions = createMemo(() => list(data.store.permission?.[props.sessionID], emptyPermissions))
|
||||
const nextPermission = createMemo(() => permissions()[0])
|
||||
|
||||
const questions = createMemo(() => list(data.store.question?.[props.sessionID], emptyQuestions))
|
||||
const nextQuestion = createMemo(() => questions()[0])
|
||||
|
||||
const hidden = createMemo(() => {
|
||||
const out: { messageID: string; callID: string }[] = []
|
||||
const perm = nextPermission()
|
||||
if (perm?.tool) out.push(perm.tool)
|
||||
const question = nextQuestion()
|
||||
if (question?.tool) out.push(question.tool)
|
||||
return out
|
||||
})
|
||||
|
||||
const answeredQuestionParts = createMemo(() => {
|
||||
if (props.stepsExpanded) return emptyQuestionParts
|
||||
if (questions().length > 0) return emptyQuestionParts
|
||||
|
||||
const result: { part: ToolPart; message: AssistantMessage }[] = []
|
||||
|
||||
for (const msg of assistantMessages()) {
|
||||
const parts = list(data.store.part?.[msg.id], emptyParts)
|
||||
for (const part of parts) {
|
||||
if (part?.type !== "tool") continue
|
||||
const tool = part as ToolPart
|
||||
if (tool.tool !== "question") continue
|
||||
// @ts-expect-error metadata may not exist on all tool states
|
||||
const answers = tool.state?.metadata?.answers
|
||||
if (answers && answers.length > 0) {
|
||||
result.push({ part: tool, message: msg })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const shellModePart = createMemo(() => {
|
||||
const p = parts()
|
||||
if (p.length === 0) return
|
||||
if (!p.every((part) => part?.type === "text" && part?.synthetic)) return
|
||||
|
||||
const msgs = assistantMessages()
|
||||
if (msgs.length !== 1) return
|
||||
|
||||
const msgParts = list(data.store.part?.[msgs[0].id], emptyParts)
|
||||
if (msgParts.length !== 1) return
|
||||
|
||||
const assistantPart = msgParts[0]
|
||||
if (assistantPart?.type === "tool" && assistantPart.tool === "bash") return assistantPart
|
||||
})
|
||||
|
||||
const isShellMode = createMemo(() => !!shellModePart())
|
||||
|
||||
const rawStatus = createMemo(() => {
|
||||
const msgs = assistantMessages()
|
||||
let last: PartType | undefined
|
||||
let currentTask: ToolPart | undefined
|
||||
|
||||
for (let mi = msgs.length - 1; mi >= 0; mi--) {
|
||||
const msgParts = list(data.store.part?.[msgs[mi].id], emptyParts)
|
||||
for (let pi = msgParts.length - 1; pi >= 0; pi--) {
|
||||
const part = msgParts[pi]
|
||||
if (!part) continue
|
||||
if (!last) last = part
|
||||
|
||||
if (
|
||||
part.type === "tool" &&
|
||||
part.tool === "task" &&
|
||||
part.state &&
|
||||
"metadata" in part.state &&
|
||||
part.state.metadata?.sessionId &&
|
||||
part.state.status === "running"
|
||||
) {
|
||||
currentTask = part as ToolPart
|
||||
break
|
||||
}
|
||||
}
|
||||
if (currentTask) break
|
||||
}
|
||||
|
||||
const taskSessionId =
|
||||
currentTask?.state && "metadata" in currentTask.state
|
||||
? (currentTask.state.metadata?.sessionId as string | undefined)
|
||||
: undefined
|
||||
|
||||
if (taskSessionId) {
|
||||
const taskMessages = list(data.store.message?.[taskSessionId], emptyMessages)
|
||||
for (let mi = taskMessages.length - 1; mi >= 0; mi--) {
|
||||
const msg = taskMessages[mi]
|
||||
if (!msg || msg.role !== "assistant") continue
|
||||
|
||||
const msgParts = list(data.store.part?.[msg.id], emptyParts)
|
||||
for (let pi = msgParts.length - 1; pi >= 0; pi--) {
|
||||
const part = msgParts[pi]
|
||||
if (part) return computeStatusFromPart(part, i18n.t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return computeStatusFromPart(last, i18n.t)
|
||||
})
|
||||
|
||||
const status = createMemo(() => data.store.session_status[props.sessionID] ?? idle)
|
||||
const working = createMemo(() => status().type !== "idle" && isLastUserMessage())
|
||||
const retry = createMemo(() => {
|
||||
// session_status is session-scoped; only show retry on the active (last) turn
|
||||
if (!isLastUserMessage()) return
|
||||
const s = status()
|
||||
if (s.type !== "retry") return
|
||||
return s
|
||||
|
||||
const assistantCopyPartID = createMemo(() => {
|
||||
if (!isLastUserMessage()) return null
|
||||
if (status().type !== "idle") return null
|
||||
return showAssistantCopyPartID() ?? null
|
||||
})
|
||||
const isRetryFreeUsageLimitError = createMemo(() => {
|
||||
const r = retry()
|
||||
if (!r) return false
|
||||
return r.message.includes("Free usage exceeded")
|
||||
})
|
||||
|
||||
const response = createMemo(() => lastTextPart()?.text)
|
||||
const responsePartId = createMemo(() => lastTextPart()?.id)
|
||||
const hasDiffs = createMemo(() => (message()?.summary?.diffs?.length ?? 0) > 0)
|
||||
const hideResponsePart = createMemo(() => !working() && !!responsePartId())
|
||||
|
||||
const [copied, setCopied] = createSignal(false)
|
||||
|
||||
const handleCopy = async () => {
|
||||
const content = response() ?? ""
|
||||
if (!content) return
|
||||
await navigator.clipboard.writeText(content)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
|
||||
const [rootRef, setRootRef] = createSignal<HTMLDivElement | undefined>()
|
||||
const [stickyRef, setStickyRef] = createSignal<HTMLDivElement | undefined>()
|
||||
|
||||
const updateStickyHeight = (height: number) => {
|
||||
const root = rootRef()
|
||||
if (!root) return
|
||||
const next = Math.ceil(height)
|
||||
root.style.setProperty("--session-turn-sticky-height", `${next}px`)
|
||||
}
|
||||
|
||||
function duration() {
|
||||
const msg = message()
|
||||
if (!msg) return ""
|
||||
const completed = lastAssistantMessage()?.time.completed
|
||||
const from = DateTime.fromMillis(msg.time.created)
|
||||
const to = completed ? DateTime.fromMillis(completed) : DateTime.now()
|
||||
const interval = Interval.fromDateTimes(from, to)
|
||||
const unit: DurationUnit[] = interval.length("seconds") > 60 ? ["minutes", "seconds"] : ["seconds"]
|
||||
|
||||
const locale = i18n.locale()
|
||||
const human = interval.toDuration(unit).normalize().reconfigure({ locale }).toHuman({
|
||||
notation: "compact",
|
||||
unitDisplay: "narrow",
|
||||
compactDisplay: "short",
|
||||
showZeros: false,
|
||||
})
|
||||
return locale.startsWith("zh") ? human.replaceAll("、", "") : human
|
||||
}
|
||||
const assistantVisible = createMemo(() =>
|
||||
assistantMessages().reduce((count, message) => {
|
||||
const parts = list(data.store.part?.[message.id], emptyParts)
|
||||
return count + parts.filter(visible).length
|
||||
}, 0),
|
||||
)
|
||||
|
||||
const autoScroll = createAutoScroll({
|
||||
working,
|
||||
onUserInteracted: props.onUserInteracted,
|
||||
overflowAnchor: "auto",
|
||||
})
|
||||
|
||||
createResizeObserver(
|
||||
() => stickyRef(),
|
||||
({ height }) => {
|
||||
updateStickyHeight(height)
|
||||
},
|
||||
)
|
||||
|
||||
createEffect(() => {
|
||||
const root = rootRef()
|
||||
if (!root) return
|
||||
const sticky = stickyRef()
|
||||
if (!sticky) {
|
||||
root.style.setProperty("--session-turn-sticky-height", "0px")
|
||||
return
|
||||
}
|
||||
updateStickyHeight(sticky.getBoundingClientRect().height)
|
||||
})
|
||||
|
||||
const [store, setStore] = createStore({
|
||||
retrySeconds: 0,
|
||||
status: rawStatus(),
|
||||
duration: duration(),
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const r = retry()
|
||||
if (!r) {
|
||||
setStore("retrySeconds", 0)
|
||||
return
|
||||
}
|
||||
const updateSeconds = () => {
|
||||
const next = r.next
|
||||
if (next) setStore("retrySeconds", Math.max(0, Math.round((next - Date.now()) / 1000)))
|
||||
}
|
||||
updateSeconds()
|
||||
const timer = setInterval(updateSeconds, 1000)
|
||||
onCleanup(() => clearInterval(timer))
|
||||
})
|
||||
|
||||
let retryLog = ""
|
||||
createEffect(() => {
|
||||
const r = retry()
|
||||
if (!r) return
|
||||
const key = `${r.attempt}:${r.next}:${r.message}`
|
||||
if (key === retryLog) return
|
||||
retryLog = key
|
||||
console.warn("[session-turn] retry", {
|
||||
sessionID: props.sessionID,
|
||||
messageID: props.messageID,
|
||||
attempt: r.attempt,
|
||||
next: r.next,
|
||||
raw: r.message,
|
||||
parsed: unwrap(r.message),
|
||||
})
|
||||
})
|
||||
|
||||
let errorLog = ""
|
||||
createEffect(() => {
|
||||
const value = error()?.data?.message
|
||||
if (value === undefined || value === null) return
|
||||
const raw = typeof value === "string" ? value : String(value)
|
||||
if (!raw) return
|
||||
if (raw === errorLog) return
|
||||
errorLog = raw
|
||||
console.warn("[session-turn] assistant-error", {
|
||||
sessionID: props.sessionID,
|
||||
messageID: props.messageID,
|
||||
raw,
|
||||
parsed: unwrap(raw),
|
||||
})
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const update = () => {
|
||||
setStore("duration", duration())
|
||||
}
|
||||
|
||||
update()
|
||||
|
||||
// Only keep ticking while the active (in-progress) turn is running.
|
||||
if (!working()) return
|
||||
|
||||
const timer = setInterval(update, 1000)
|
||||
onCleanup(() => clearInterval(timer))
|
||||
})
|
||||
|
||||
let lastStatusChange = Date.now()
|
||||
let statusTimeout: number | undefined
|
||||
createEffect(() => {
|
||||
const newStatus = rawStatus()
|
||||
if (newStatus === store.status || !newStatus) return
|
||||
|
||||
const timeSinceLastChange = Date.now() - lastStatusChange
|
||||
if (timeSinceLastChange >= 2500) {
|
||||
setStore("status", newStatus)
|
||||
lastStatusChange = Date.now()
|
||||
if (statusTimeout) {
|
||||
clearTimeout(statusTimeout)
|
||||
statusTimeout = undefined
|
||||
}
|
||||
} else {
|
||||
if (statusTimeout) clearTimeout(statusTimeout)
|
||||
statusTimeout = setTimeout(() => {
|
||||
setStore("status", rawStatus())
|
||||
lastStatusChange = Date.now()
|
||||
statusTimeout = undefined
|
||||
}, 2500 - timeSinceLastChange) as unknown as number
|
||||
}
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
if (!statusTimeout) return
|
||||
clearTimeout(statusTimeout)
|
||||
overflowAnchor: "dynamic",
|
||||
})
|
||||
|
||||
return (
|
||||
<div data-component="session-turn" class={props.classes?.root} ref={setRootRef}>
|
||||
<div data-component="session-turn" class={props.classes?.root}>
|
||||
<div
|
||||
ref={autoScroll.scrollRef}
|
||||
onScroll={autoScroll.handleScroll}
|
||||
@@ -624,197 +271,83 @@ export function SessionTurn(
|
||||
data-slot="session-turn-message-container"
|
||||
class={props.classes?.container}
|
||||
>
|
||||
<Switch>
|
||||
<Match when={isShellMode()}>
|
||||
<Part part={shellModePart()!} message={msg()} defaultOpen />
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<Show when={attachmentParts().length > 0}>
|
||||
<div data-slot="session-turn-attachments" aria-live="off">
|
||||
<Message message={msg()} parts={attachmentParts()} />
|
||||
</div>
|
||||
</Show>
|
||||
<div data-slot="session-turn-sticky" ref={setStickyRef}>
|
||||
{/* User Message */}
|
||||
<div data-slot="session-turn-message-content" aria-live="off">
|
||||
<Message message={msg()} parts={stickyParts()} />
|
||||
</div>
|
||||
|
||||
{/* Trigger (sticky) */}
|
||||
<Show when={working() || hasSteps()}>
|
||||
<div data-slot="session-turn-response-trigger">
|
||||
<Button
|
||||
data-expandable={assistantMessages().length > 0}
|
||||
data-slot="session-turn-collapsible-trigger-content"
|
||||
variant="ghost"
|
||||
size="small"
|
||||
onClick={props.onStepsExpandedToggle ?? (() => {})}
|
||||
aria-expanded={props.stepsExpanded}
|
||||
>
|
||||
<Switch>
|
||||
<Match when={working()}>
|
||||
<Spinner />
|
||||
</Match>
|
||||
<Match when={!props.stepsExpanded}>
|
||||
<svg
|
||||
width="10"
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
data-slot="session-turn-trigger-icon"
|
||||
>
|
||||
<path
|
||||
d="M8.125 1.875H1.875L5 8.125L8.125 1.875Z"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Match>
|
||||
<Match when={props.stepsExpanded}>
|
||||
<svg
|
||||
width="10"
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-icon-base"
|
||||
>
|
||||
<path
|
||||
d="M8.125 8.125H1.875L5 1.875L8.125 8.125Z"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Match>
|
||||
</Switch>
|
||||
<Switch>
|
||||
<Match when={retry()}>
|
||||
<span data-slot="session-turn-retry-message">
|
||||
{(() => {
|
||||
const r = retry()
|
||||
if (!r) return ""
|
||||
const msg = isRetryFreeUsageLimitError()
|
||||
? i18n.t("ui.sessionTurn.error.freeUsageExceeded")
|
||||
: unwrap(r.message)
|
||||
return msg.length > 60 ? msg.slice(0, 60) + "..." : msg
|
||||
})()}
|
||||
</span>
|
||||
<Show when={isRetryFreeUsageLimitError()}>
|
||||
<a
|
||||
href="https://opencode.ai/zen"
|
||||
target="_blank"
|
||||
class="retry-error-link"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{i18n.t("ui.sessionTurn.error.addCredits")}
|
||||
</a>
|
||||
</Show>
|
||||
<span data-slot="session-turn-retry-seconds">
|
||||
· {i18n.t("ui.sessionTurn.retry.retrying")}
|
||||
{store.retrySeconds > 0
|
||||
? " " + i18n.t("ui.sessionTurn.retry.inSeconds", { seconds: store.retrySeconds })
|
||||
: ""}
|
||||
</span>
|
||||
<span data-slot="session-turn-retry-attempt">(#{retry()?.attempt})</span>
|
||||
</Match>
|
||||
<Match when={working()}>
|
||||
<span data-slot="session-turn-status-text">
|
||||
{store.status ?? i18n.t("ui.sessionTurn.status.consideringNextSteps")}
|
||||
</span>
|
||||
</Match>
|
||||
<Match when={props.stepsExpanded}>
|
||||
<span data-slot="session-turn-status-text">{i18n.t("ui.sessionTurn.steps.hide")}</span>
|
||||
</Match>
|
||||
<Match when={!props.stepsExpanded}>
|
||||
<span data-slot="session-turn-status-text">{i18n.t("ui.sessionTurn.steps.show")}</span>
|
||||
</Match>
|
||||
</Switch>
|
||||
<span aria-hidden="true">·</span>
|
||||
<span aria-live="off">{store.duration}</span>
|
||||
</Button>
|
||||
<div data-slot="session-turn-message-content" aria-live="off">
|
||||
<Message message={msg()} parts={parts()} interrupted={interrupted()} />
|
||||
</div>
|
||||
<Show when={working() && assistantVisible() === 0 && !error()}>
|
||||
<div data-slot="session-turn-thinking">
|
||||
<TextShimmer text={i18n.t("ui.sessionTurn.status.thinking")} />
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={assistantMessages().length > 0}>
|
||||
<div data-slot="session-turn-assistant-content" aria-hidden={working()}>
|
||||
<For each={assistantMessages()}>
|
||||
{(assistantMessage) => (
|
||||
<AssistantMessageItem
|
||||
message={assistantMessage}
|
||||
showAssistantCopyPartID={assistantCopyPartID()}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={edited() > 0}>
|
||||
<div data-slot="session-turn-diffs">
|
||||
<Collapsible open={open()} onOpenChange={setOpen} variant="ghost">
|
||||
<Collapsible.Trigger>
|
||||
<div data-component="session-turn-diffs-trigger">
|
||||
<div data-slot="session-turn-diffs-title">
|
||||
<span data-slot="session-turn-diffs-label">
|
||||
{i18n.t("ui.sessionReview.change.modified")}
|
||||
</span>
|
||||
<span data-slot="session-turn-diffs-count">
|
||||
{edited()} {i18n.t(edited() === 1 ? "ui.common.file.one" : "ui.common.file.other")}
|
||||
</span>
|
||||
<div data-slot="session-turn-diffs-meta">
|
||||
<DiffChanges changes={diffs()} variant="bars" />
|
||||
<Collapsible.Arrow />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
{/* Response */}
|
||||
<Show when={props.stepsExpanded && assistantMessages().length > 0}>
|
||||
<div data-slot="session-turn-collapsible-content-inner" aria-hidden={working()}>
|
||||
<For each={assistantMessages()}>
|
||||
{(assistantMessage) => (
|
||||
<AssistantMessageItem
|
||||
message={assistantMessage}
|
||||
responsePartId={responsePartId()}
|
||||
hideResponsePart={hideResponsePart()}
|
||||
hideReasoning={!working()}
|
||||
hidden={hidden}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
<Show when={error()}>
|
||||
<Card variant="error" class="error-card">
|
||||
{errorText()}
|
||||
</Card>
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
<Show when={open()}>
|
||||
<div data-component="session-turn-diffs-content">
|
||||
<For each={diffs()}>
|
||||
{(diff) => (
|
||||
<div data-component="session-turn-diff">
|
||||
<div data-slot="session-turn-diff-header">
|
||||
<span data-slot="session-turn-diff-path">
|
||||
<Show when={diff.file.includes("/")}>
|
||||
<span data-slot="session-turn-diff-directory">{getDirectory(diff.file)}</span>
|
||||
</Show>
|
||||
<span data-slot="session-turn-diff-filename">{getFilename(diff.file)}</span>
|
||||
</span>
|
||||
<span data-slot="session-turn-diff-changes">
|
||||
<DiffChanges changes={diff} />
|
||||
</span>
|
||||
</div>
|
||||
<div data-slot="session-turn-diff-view">
|
||||
<Dynamic
|
||||
component={diffComponent}
|
||||
before={{ name: diff.file, contents: diff.before }}
|
||||
after={{ name: diff.file, contents: diff.after }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={!props.stepsExpanded && answeredQuestionParts().length > 0}>
|
||||
<div data-slot="session-turn-answered-question-parts">
|
||||
<For each={answeredQuestionParts()}>
|
||||
{({ part, message }) => <Part part={part} message={message} />}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
{/* Response */}
|
||||
<div class="sr-only" aria-live="polite">
|
||||
{!working() && response() ? response() : ""}
|
||||
</div>
|
||||
<Show when={!working() && response()}>
|
||||
<div data-slot="session-turn-summary-section">
|
||||
<div data-slot="session-turn-summary-header">
|
||||
<div data-slot="session-turn-summary-title-row">
|
||||
<h2 data-slot="session-turn-summary-title">{i18n.t("ui.sessionTurn.summary.response")}</h2>
|
||||
<Show when={response()}>
|
||||
<div data-slot="session-turn-response-copy-wrapper">
|
||||
<Tooltip
|
||||
value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")}
|
||||
placement="top"
|
||||
gutter={8}
|
||||
>
|
||||
<IconButton
|
||||
icon={copied() ? "check" : "copy"}
|
||||
size="small"
|
||||
variant="secondary"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
handleCopy()
|
||||
}}
|
||||
aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<div data-slot="session-turn-response">
|
||||
<Markdown
|
||||
data-slot="session-turn-markdown"
|
||||
data-diffs={hasDiffs()}
|
||||
text={response() ?? ""}
|
||||
cacheKey={responsePartId()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={error() && !props.stepsExpanded}>
|
||||
<Card variant="error" class="error-card">
|
||||
{errorText()}
|
||||
</Card>
|
||||
</Show>
|
||||
</Match>
|
||||
</Switch>
|
||||
</Collapsible.Content>
|
||||
</Collapsible>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={error()}>
|
||||
<Card variant="error" class="error-card">
|
||||
{errorText()}
|
||||
</Card>
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
|
||||
43
packages/ui/src/components/text-shimmer.css
Normal file
43
packages/ui/src/components/text-shimmer.css
Normal file
@@ -0,0 +1,43 @@
|
||||
[data-component="text-shimmer"] {
|
||||
--text-shimmer-step: 45ms;
|
||||
--text-shimmer-duration: 1200ms;
|
||||
}
|
||||
|
||||
[data-component="text-shimmer"] [data-slot="text-shimmer-char"] {
|
||||
white-space: pre;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
[data-component="text-shimmer"][data-active="true"] [data-slot="text-shimmer-char"] {
|
||||
animation-name: text-shimmer-char;
|
||||
animation-duration: var(--text-shimmer-duration);
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-delay: calc(var(--text-shimmer-step) * var(--text-shimmer-index));
|
||||
}
|
||||
|
||||
@keyframes text-shimmer-char {
|
||||
0%,
|
||||
100% {
|
||||
color: var(--text-weaker);
|
||||
}
|
||||
|
||||
30% {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
55% {
|
||||
color: var(--text-base);
|
||||
}
|
||||
|
||||
75% {
|
||||
color: var(--text-strong);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
[data-component="text-shimmer"] [data-slot="text-shimmer-char"] {
|
||||
animation: none !important;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
36
packages/ui/src/components/text-shimmer.tsx
Normal file
36
packages/ui/src/components/text-shimmer.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { For, createMemo, type ValidComponent } from "solid-js"
|
||||
import { Dynamic } from "solid-js/web"
|
||||
|
||||
export const TextShimmer = <T extends ValidComponent = "span">(props: {
|
||||
text: string
|
||||
class?: string
|
||||
as?: T
|
||||
active?: boolean
|
||||
stepMs?: number
|
||||
durationMs?: number
|
||||
}) => {
|
||||
const chars = createMemo(() => Array.from(props.text))
|
||||
const active = () => props.active ?? true
|
||||
|
||||
return (
|
||||
<Dynamic
|
||||
component={props.as || "span"}
|
||||
data-component="text-shimmer"
|
||||
data-active={active()}
|
||||
class={props.class}
|
||||
aria-label={props.text}
|
||||
style={{
|
||||
"--text-shimmer-step": `${props.stepMs ?? 45}ms`,
|
||||
"--text-shimmer-duration": `${props.durationMs ?? 1200}ms`,
|
||||
}}
|
||||
>
|
||||
<For each={chars()}>
|
||||
{(char, index) => (
|
||||
<span data-slot="text-shimmer-char" aria-hidden="true" style={{ "--text-shimmer-index": `${index()}` }}>
|
||||
{char}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</Dynamic>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user