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>
|
||||
)
|
||||
}
|
||||
@@ -50,8 +50,6 @@ export type NavigateToSessionFn = (sessionID: string) => void
|
||||
|
||||
export type SessionHrefFn = (sessionID: string) => string
|
||||
|
||||
export type SyncSessionFn = (sessionID: string) => void | Promise<void>
|
||||
|
||||
export const { use: useData, provider: DataProvider } = createSimpleContext({
|
||||
name: "Data",
|
||||
init: (props: {
|
||||
@@ -62,7 +60,6 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
||||
onQuestionReject?: QuestionRejectFn
|
||||
onNavigateToSession?: NavigateToSessionFn
|
||||
onSessionHref?: SessionHrefFn
|
||||
onSyncSession?: SyncSessionFn
|
||||
}) => {
|
||||
return {
|
||||
get store() {
|
||||
@@ -76,7 +73,6 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
||||
rejectQuestion: props.onQuestionReject,
|
||||
navigateToSession: props.onNavigateToSession,
|
||||
sessionHref: props.onSessionHref,
|
||||
syncSession: props.onSyncSession,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -82,6 +82,7 @@ export const dict = {
|
||||
"ui.common.question.other": "أسئلة",
|
||||
|
||||
"ui.common.add": "إضافة",
|
||||
"ui.common.back": "رجوع",
|
||||
"ui.common.cancel": "إلغاء",
|
||||
"ui.common.confirm": "تأكيد",
|
||||
"ui.common.dismiss": "رفض",
|
||||
@@ -97,6 +98,7 @@ export const dict = {
|
||||
"ui.message.collapse": "طي الرسالة",
|
||||
"ui.message.copy": "نسخ",
|
||||
"ui.message.copied": "تم النسخ!",
|
||||
"ui.message.interrupted": "تمت المقاطعة",
|
||||
"ui.message.attachment.alt": "مرفق",
|
||||
|
||||
"ui.patch.action.deleted": "محذوف",
|
||||
@@ -107,6 +109,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} أجيب",
|
||||
"ui.question.answer.none": "(لا توجد إجابة)",
|
||||
"ui.question.review.notAnswered": "(لم يتم الرد)",
|
||||
"ui.question.multiHint": "(حدد كل ما ينطبق)",
|
||||
"ui.question.multiHint": "حدد كل ما ينطبق",
|
||||
"ui.question.singleHint": "حدد إجابة واحدة",
|
||||
"ui.question.custom.placeholder": "اكتب إجابتك...",
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ export const dict = {
|
||||
"ui.common.question.other": "perguntas",
|
||||
|
||||
"ui.common.add": "Adicionar",
|
||||
"ui.common.back": "Voltar",
|
||||
"ui.common.cancel": "Cancelar",
|
||||
"ui.common.confirm": "Confirmar",
|
||||
"ui.common.dismiss": "Descartar",
|
||||
@@ -97,6 +98,7 @@ export const dict = {
|
||||
"ui.message.collapse": "Recolher mensagem",
|
||||
"ui.message.copy": "Copiar",
|
||||
"ui.message.copied": "Copiado!",
|
||||
"ui.message.interrupted": "Interrompido",
|
||||
"ui.message.attachment.alt": "anexo",
|
||||
|
||||
"ui.patch.action.deleted": "Excluído",
|
||||
@@ -107,6 +109,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} respondidas",
|
||||
"ui.question.answer.none": "(sem resposta)",
|
||||
"ui.question.review.notAnswered": "(não respondida)",
|
||||
"ui.question.multiHint": "(selecione todas que se aplicam)",
|
||||
"ui.question.multiHint": "Selecione todas que se aplicam",
|
||||
"ui.question.singleHint": "Selecione uma resposta",
|
||||
"ui.question.custom.placeholder": "Digite sua resposta...",
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ export const dict = {
|
||||
"ui.common.question.other": "pitanja",
|
||||
|
||||
"ui.common.add": "Dodaj",
|
||||
"ui.common.back": "Nazad",
|
||||
"ui.common.cancel": "Otkaži",
|
||||
"ui.common.confirm": "Potvrdi",
|
||||
"ui.common.dismiss": "Odbaci",
|
||||
@@ -101,6 +102,7 @@ export const dict = {
|
||||
"ui.message.collapse": "Sažmi poruku",
|
||||
"ui.message.copy": "Kopiraj",
|
||||
"ui.message.copied": "Kopirano!",
|
||||
"ui.message.interrupted": "Prekinuto",
|
||||
"ui.message.attachment.alt": "prilog",
|
||||
|
||||
"ui.patch.action.deleted": "Obrisano",
|
||||
@@ -111,6 +113,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} odgovoreno",
|
||||
"ui.question.answer.none": "(nema odgovora)",
|
||||
"ui.question.review.notAnswered": "(nije odgovoreno)",
|
||||
"ui.question.multiHint": "(odaberi sve što važi)",
|
||||
"ui.question.multiHint": "Odaberi sve što važi",
|
||||
"ui.question.singleHint": "Odaberi jedan odgovor",
|
||||
"ui.question.custom.placeholder": "Unesi svoj odgovor...",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -81,6 +81,7 @@ export const dict = {
|
||||
"ui.common.question.other": "spørgsmål",
|
||||
|
||||
"ui.common.add": "Tilføj",
|
||||
"ui.common.back": "Tilbage",
|
||||
"ui.common.cancel": "Annuller",
|
||||
"ui.common.confirm": "Bekræft",
|
||||
"ui.common.dismiss": "Afvis",
|
||||
@@ -96,6 +97,7 @@ export const dict = {
|
||||
"ui.message.collapse": "Skjul besked",
|
||||
"ui.message.copy": "Kopier",
|
||||
"ui.message.copied": "Kopieret!",
|
||||
"ui.message.interrupted": "Afbrudt",
|
||||
"ui.message.attachment.alt": "vedhæftning",
|
||||
|
||||
"ui.patch.action.deleted": "Slettet",
|
||||
@@ -106,6 +108,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} besvaret",
|
||||
"ui.question.answer.none": "(intet svar)",
|
||||
"ui.question.review.notAnswered": "(ikke besvaret)",
|
||||
"ui.question.multiHint": "(vælg alle der gælder)",
|
||||
"ui.question.multiHint": "Vælg alle der gælder",
|
||||
"ui.question.singleHint": "Vælg ét svar",
|
||||
"ui.question.custom.placeholder": "Skriv dit svar...",
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ export const dict = {
|
||||
"ui.common.question.other": "Fragen",
|
||||
|
||||
"ui.common.add": "Hinzufügen",
|
||||
"ui.common.back": "Zurück",
|
||||
"ui.common.cancel": "Abbrechen",
|
||||
"ui.common.confirm": "Bestätigen",
|
||||
"ui.common.dismiss": "Verwerfen",
|
||||
@@ -100,6 +101,7 @@ export const dict = {
|
||||
"ui.message.collapse": "Nachricht reduzieren",
|
||||
"ui.message.copy": "Kopieren",
|
||||
"ui.message.copied": "Kopiert!",
|
||||
"ui.message.interrupted": "Unterbrochen",
|
||||
"ui.message.attachment.alt": "Anhang",
|
||||
|
||||
"ui.patch.action.deleted": "Gelöscht",
|
||||
@@ -110,6 +112,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} beantwortet",
|
||||
"ui.question.answer.none": "(keine Antwort)",
|
||||
"ui.question.review.notAnswered": "(nicht beantwortet)",
|
||||
"ui.question.multiHint": "(alle zutreffenden auswählen)",
|
||||
"ui.question.multiHint": "Alle zutreffenden auswählen",
|
||||
"ui.question.singleHint": "Eine Antwort auswählen",
|
||||
"ui.question.custom.placeholder": "Geben Sie Ihre Antwort ein...",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -82,6 +82,7 @@ export const dict = {
|
||||
"ui.common.question.other": "questions",
|
||||
|
||||
"ui.common.add": "Add",
|
||||
"ui.common.back": "Back",
|
||||
"ui.common.cancel": "Cancel",
|
||||
"ui.common.confirm": "Confirm",
|
||||
"ui.common.dismiss": "Dismiss",
|
||||
@@ -96,7 +97,8 @@ export const dict = {
|
||||
"ui.message.expand": "Expand message",
|
||||
"ui.message.collapse": "Collapse message",
|
||||
"ui.message.copy": "Copy",
|
||||
"ui.message.copied": "Copied!",
|
||||
"ui.message.copied": "Copied",
|
||||
"ui.message.interrupted": "Interrupted",
|
||||
"ui.message.attachment.alt": "attachment",
|
||||
|
||||
"ui.patch.action.deleted": "Deleted",
|
||||
@@ -107,6 +109,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} answered",
|
||||
"ui.question.answer.none": "(no answer)",
|
||||
"ui.question.review.notAnswered": "(not answered)",
|
||||
"ui.question.multiHint": "(select all that apply)",
|
||||
"ui.question.multiHint": "Select all answers that apply",
|
||||
"ui.question.singleHint": "Select one answer",
|
||||
"ui.question.custom.placeholder": "Type your answer...",
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ export const dict = {
|
||||
"ui.common.question.other": "preguntas",
|
||||
|
||||
"ui.common.add": "Añadir",
|
||||
"ui.common.back": "Atrás",
|
||||
"ui.common.cancel": "Cancelar",
|
||||
"ui.common.confirm": "Confirmar",
|
||||
"ui.common.dismiss": "Descartar",
|
||||
@@ -97,6 +98,7 @@ export const dict = {
|
||||
"ui.message.collapse": "Colapsar mensaje",
|
||||
"ui.message.copy": "Copiar",
|
||||
"ui.message.copied": "¡Copiado!",
|
||||
"ui.message.interrupted": "Interrumpido",
|
||||
"ui.message.attachment.alt": "adjunto",
|
||||
|
||||
"ui.patch.action.deleted": "Eliminado",
|
||||
@@ -107,6 +109,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} respondidas",
|
||||
"ui.question.answer.none": "(sin respuesta)",
|
||||
"ui.question.review.notAnswered": "(no respondida)",
|
||||
"ui.question.multiHint": "(selecciona todas las que correspondan)",
|
||||
"ui.question.multiHint": "Selecciona todas las que correspondan",
|
||||
"ui.question.singleHint": "Selecciona una respuesta",
|
||||
"ui.question.custom.placeholder": "Escribe tu respuesta...",
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ export const dict = {
|
||||
"ui.common.question.other": "questions",
|
||||
|
||||
"ui.common.add": "Ajouter",
|
||||
"ui.common.back": "Retour",
|
||||
"ui.common.cancel": "Annuler",
|
||||
"ui.common.confirm": "Confirmer",
|
||||
"ui.common.dismiss": "Ignorer",
|
||||
@@ -97,6 +98,7 @@ export const dict = {
|
||||
"ui.message.collapse": "Réduire le message",
|
||||
"ui.message.copy": "Copier",
|
||||
"ui.message.copied": "Copié !",
|
||||
"ui.message.interrupted": "Interrompu",
|
||||
"ui.message.attachment.alt": "pièce jointe",
|
||||
|
||||
"ui.patch.action.deleted": "Supprimé",
|
||||
@@ -107,6 +109,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} répondu(s)",
|
||||
"ui.question.answer.none": "(pas de réponse)",
|
||||
"ui.question.review.notAnswered": "(non répondu)",
|
||||
"ui.question.multiHint": "(sélectionnez tout ce qui s'applique)",
|
||||
"ui.question.multiHint": "Sélectionnez tout ce qui s'applique",
|
||||
"ui.question.singleHint": "Sélectionnez une réponse",
|
||||
"ui.question.custom.placeholder": "Tapez votre réponse...",
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ export const dict = {
|
||||
"ui.common.question.other": "質問",
|
||||
|
||||
"ui.common.add": "追加",
|
||||
"ui.common.back": "戻る",
|
||||
"ui.common.cancel": "キャンセル",
|
||||
"ui.common.confirm": "確認",
|
||||
"ui.common.dismiss": "閉じる",
|
||||
@@ -96,6 +97,7 @@ export const dict = {
|
||||
"ui.message.collapse": "メッセージを折りたたむ",
|
||||
"ui.message.copy": "コピー",
|
||||
"ui.message.copied": "コピーしました!",
|
||||
"ui.message.interrupted": "中断",
|
||||
"ui.message.attachment.alt": "添付ファイル",
|
||||
|
||||
"ui.patch.action.deleted": "削除済み",
|
||||
@@ -106,6 +108,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}}件回答済み",
|
||||
"ui.question.answer.none": "(回答なし)",
|
||||
"ui.question.review.notAnswered": "(未回答)",
|
||||
"ui.question.multiHint": "(該当するものをすべて選択)",
|
||||
"ui.question.multiHint": "該当するものをすべて選択",
|
||||
"ui.question.singleHint": "1 つ選択",
|
||||
"ui.question.custom.placeholder": "回答を入力...",
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ export const dict = {
|
||||
"ui.common.question.other": "질문",
|
||||
|
||||
"ui.common.add": "추가",
|
||||
"ui.common.back": "뒤로",
|
||||
"ui.common.cancel": "취소",
|
||||
"ui.common.confirm": "확인",
|
||||
"ui.common.dismiss": "닫기",
|
||||
@@ -97,6 +98,7 @@ export const dict = {
|
||||
"ui.message.collapse": "메시지 접기",
|
||||
"ui.message.copy": "복사",
|
||||
"ui.message.copied": "복사됨!",
|
||||
"ui.message.interrupted": "중단됨",
|
||||
"ui.message.attachment.alt": "첨부 파일",
|
||||
|
||||
"ui.patch.action.deleted": "삭제됨",
|
||||
@@ -107,6 +109,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}}개 답변됨",
|
||||
"ui.question.answer.none": "(답변 없음)",
|
||||
"ui.question.review.notAnswered": "(답변되지 않음)",
|
||||
"ui.question.multiHint": "(해당하는 항목 모두 선택)",
|
||||
"ui.question.multiHint": "해당하는 항목 모두 선택",
|
||||
"ui.question.singleHint": "하나의 답변을 선택",
|
||||
"ui.question.custom.placeholder": "답변 입력...",
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ export const dict: Record<Keys, string> = {
|
||||
"ui.common.question.other": "spørsmål",
|
||||
|
||||
"ui.common.add": "Legg til",
|
||||
"ui.common.back": "Tilbake",
|
||||
"ui.common.cancel": "Avbryt",
|
||||
"ui.common.confirm": "Bekreft",
|
||||
"ui.common.dismiss": "Avvis",
|
||||
@@ -100,6 +101,7 @@ export const dict: Record<Keys, string> = {
|
||||
"ui.message.collapse": "Skjul melding",
|
||||
"ui.message.copy": "Kopier",
|
||||
"ui.message.copied": "Kopiert!",
|
||||
"ui.message.interrupted": "Avbrutt",
|
||||
"ui.message.attachment.alt": "vedlegg",
|
||||
|
||||
"ui.patch.action.deleted": "Slettet",
|
||||
@@ -110,6 +112,7 @@ export const dict: Record<Keys, string> = {
|
||||
"ui.question.subtitle.answered": "{{count}} besvart",
|
||||
"ui.question.answer.none": "(ingen svar)",
|
||||
"ui.question.review.notAnswered": "(ikke besvart)",
|
||||
"ui.question.multiHint": "(velg alle som gjelder)",
|
||||
"ui.question.multiHint": "Velg alle som gjelder",
|
||||
"ui.question.singleHint": "Velg ett svar",
|
||||
"ui.question.custom.placeholder": "Skriv svaret ditt...",
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ export const dict = {
|
||||
"ui.common.question.other": "pytania",
|
||||
|
||||
"ui.common.add": "Dodaj",
|
||||
"ui.common.back": "Wstecz",
|
||||
"ui.common.cancel": "Anuluj",
|
||||
"ui.common.confirm": "Potwierdź",
|
||||
"ui.common.dismiss": "Odrzuć",
|
||||
@@ -96,6 +97,7 @@ export const dict = {
|
||||
"ui.message.collapse": "Zwiń wiadomość",
|
||||
"ui.message.copy": "Kopiuj",
|
||||
"ui.message.copied": "Skopiowano!",
|
||||
"ui.message.interrupted": "Przerwano",
|
||||
"ui.message.attachment.alt": "załącznik",
|
||||
|
||||
"ui.patch.action.deleted": "Usunięto",
|
||||
@@ -106,6 +108,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} odpowiedzi",
|
||||
"ui.question.answer.none": "(brak odpowiedzi)",
|
||||
"ui.question.review.notAnswered": "(bez odpowiedzi)",
|
||||
"ui.question.multiHint": "(zaznacz wszystkie pasujące)",
|
||||
"ui.question.multiHint": "Zaznacz wszystkie pasujące",
|
||||
"ui.question.singleHint": "Wybierz jedną odpowiedź",
|
||||
"ui.question.custom.placeholder": "Wpisz swoją odpowiedź...",
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ export const dict = {
|
||||
"ui.common.question.other": "вопросов",
|
||||
|
||||
"ui.common.add": "Добавить",
|
||||
"ui.common.back": "Назад",
|
||||
"ui.common.cancel": "Отмена",
|
||||
"ui.common.confirm": "Подтвердить",
|
||||
"ui.common.dismiss": "Закрыть",
|
||||
@@ -96,6 +97,7 @@ export const dict = {
|
||||
"ui.message.collapse": "Свернуть сообщение",
|
||||
"ui.message.copy": "Копировать",
|
||||
"ui.message.copied": "Скопировано!",
|
||||
"ui.message.interrupted": "Прервано",
|
||||
"ui.message.attachment.alt": "вложение",
|
||||
|
||||
"ui.patch.action.deleted": "Удалено",
|
||||
@@ -106,6 +108,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} отвечено",
|
||||
"ui.question.answer.none": "(нет ответа)",
|
||||
"ui.question.review.notAnswered": "(не отвечено)",
|
||||
"ui.question.multiHint": "(выберите все подходящие)",
|
||||
"ui.question.multiHint": "Выберите все подходящие",
|
||||
"ui.question.singleHint": "Выберите один ответ",
|
||||
"ui.question.custom.placeholder": "Введите ваш ответ...",
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ export const dict = {
|
||||
"ui.common.question.other": "คำถาม",
|
||||
|
||||
"ui.common.add": "เพิ่ม",
|
||||
"ui.common.back": "ย้อนกลับ",
|
||||
"ui.common.cancel": "ยกเลิก",
|
||||
"ui.common.confirm": "ยืนยัน",
|
||||
"ui.common.dismiss": "ปิด",
|
||||
@@ -97,6 +98,7 @@ export const dict = {
|
||||
"ui.message.collapse": "ย่อข้อความ",
|
||||
"ui.message.copy": "คัดลอก",
|
||||
"ui.message.copied": "คัดลอกแล้ว!",
|
||||
"ui.message.interrupted": "ถูกขัดจังหวะ",
|
||||
"ui.message.attachment.alt": "ไฟล์แนบ",
|
||||
|
||||
"ui.patch.action.deleted": "ลบ",
|
||||
@@ -107,6 +109,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} ตอบแล้ว",
|
||||
"ui.question.answer.none": "(ไม่มีคำตอบ)",
|
||||
"ui.question.review.notAnswered": "(ไม่ได้ตอบ)",
|
||||
"ui.question.multiHint": "(เลือกทั้งหมดที่ใช้)",
|
||||
"ui.question.multiHint": "เลือกทั้งหมดที่ใช้",
|
||||
"ui.question.singleHint": "เลือกหนึ่งคำตอบ",
|
||||
"ui.question.custom.placeholder": "พิมพ์คำตอบของคุณ...",
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ export const dict = {
|
||||
"ui.common.question.other": "个问题",
|
||||
|
||||
"ui.common.add": "添加",
|
||||
"ui.common.back": "返回",
|
||||
"ui.common.cancel": "取消",
|
||||
"ui.common.confirm": "确认",
|
||||
"ui.common.dismiss": "忽略",
|
||||
@@ -101,6 +102,7 @@ export const dict = {
|
||||
"ui.message.collapse": "收起消息",
|
||||
"ui.message.copy": "复制",
|
||||
"ui.message.copied": "已复制!",
|
||||
"ui.message.interrupted": "已中断",
|
||||
"ui.message.attachment.alt": "附件",
|
||||
|
||||
"ui.patch.action.deleted": "已删除",
|
||||
@@ -111,6 +113,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} 已回答",
|
||||
"ui.question.answer.none": "(无答案)",
|
||||
"ui.question.review.notAnswered": "(未回答)",
|
||||
"ui.question.multiHint": "(可多选)",
|
||||
"ui.question.multiHint": "可多选",
|
||||
"ui.question.singleHint": "选择一个答案",
|
||||
"ui.question.custom.placeholder": "输入你的答案...",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -86,6 +86,7 @@ export const dict = {
|
||||
"ui.common.question.other": "個問題",
|
||||
|
||||
"ui.common.add": "新增",
|
||||
"ui.common.back": "返回",
|
||||
"ui.common.cancel": "取消",
|
||||
"ui.common.confirm": "確認",
|
||||
"ui.common.dismiss": "忽略",
|
||||
@@ -101,6 +102,7 @@ export const dict = {
|
||||
"ui.message.collapse": "收合訊息",
|
||||
"ui.message.copy": "複製",
|
||||
"ui.message.copied": "已複製!",
|
||||
"ui.message.interrupted": "已中斷",
|
||||
"ui.message.attachment.alt": "附件",
|
||||
|
||||
"ui.patch.action.deleted": "已刪除",
|
||||
@@ -111,6 +113,7 @@ export const dict = {
|
||||
"ui.question.subtitle.answered": "{{count}} 已回答",
|
||||
"ui.question.answer.none": "(無答案)",
|
||||
"ui.question.review.notAnswered": "(未回答)",
|
||||
"ui.question.multiHint": "(可多選)",
|
||||
"ui.question.multiHint": "可多選",
|
||||
"ui.question.singleHint": "選擇一個答案",
|
||||
"ui.question.custom.placeholder": "輸入你的答案...",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
:root {
|
||||
--animate-pulse: pulse-opacity 2s ease-in-out infinite;
|
||||
--animate-pulse-scale: pulse-scale 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-opacity {
|
||||
@@ -12,6 +13,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-scale {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.6666667);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-opacity-dim {
|
||||
0%,
|
||||
100% {
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
@import "../components/sticky-accordion-header.css" layer(components);
|
||||
@import "../components/tabs.css" layer(components);
|
||||
@import "../components/tag.css" layer(components);
|
||||
@import "../components/text-shimmer.css" layer(components);
|
||||
@import "../components/toast.css" layer(components);
|
||||
@import "../components/tooltip.css" layer(components);
|
||||
@import "../components/typewriter.css" layer(components);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
--font-size-x-large: 20px;
|
||||
--font-weight-regular: 400;
|
||||
--font-weight-medium: 500;
|
||||
--line-height-normal: 130%;
|
||||
--line-height-large: 150%;
|
||||
--line-height-x-large: 180%;
|
||||
--line-height-2x-large: 200%;
|
||||
|
||||
Reference in New Issue
Block a user