refactor(ui): isolate session components (#33670)
This commit is contained in:
parent
1ef0fd5d01
commit
04c1730c90
126 changed files with 294 additions and 142 deletions
|
|
@ -1,163 +0,0 @@
|
|||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: geometricPrecision;
|
||||
}
|
||||
|
||||
[data-component="basic-tool-v2"] {
|
||||
--bt-title: var(--v2-text-text-base);
|
||||
--bt-sep: var(--v2-text-text-muted);
|
||||
--bt-subtitle: var(--v2-text-text-muted);
|
||||
--bt-args: var(--v2-text-text-muted);
|
||||
--bt-chevron: var(--v2-text-text-faint);
|
||||
--bt-content: var(--v2-text-text-muted);
|
||||
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding: 4px 0;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
font-variant-numeric: tabular-nums;
|
||||
|
||||
[data-slot="basic-tool-v2-trigger"] {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 20px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-trigger"]:focus-visible {
|
||||
outline: 2px solid var(--v2-border-border-focus);
|
||||
outline-offset: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-labels"] {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-title"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.04px;
|
||||
color: var(--bt-title);
|
||||
font-variation-settings: "slnt" 0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-sep"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
font-size: 11px;
|
||||
font-weight: 530;
|
||||
line-height: 12px;
|
||||
letter-spacing: 0.05px;
|
||||
color: var(--bt-sep);
|
||||
font-variation-settings: "slnt" 0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-subtitle"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
flex: 0 1 auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.04px;
|
||||
color: var(--bt-subtitle);
|
||||
font-variation-settings: "slnt" 0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-arg"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.04px;
|
||||
color: var(--bt-args);
|
||||
font-variation-settings: "slnt" 0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-diff"] {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-diff"] [data-component="diff-changes"] {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-chevron-wrap"] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 2px;
|
||||
border-radius: 4px;
|
||||
color: var(--bt-chevron);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-chevron"] {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transition: transform 0.15s ease-out;
|
||||
}
|
||||
|
||||
&[data-expanded] [data-slot="basic-tool-v2-chevron"] {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-content"] {
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[data-slot="basic-tool-v2-content-inner"] {
|
||||
font-size: 15px;
|
||||
font-weight: 440;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0;
|
||||
color: var(--bt-content);
|
||||
font-variation-settings: "slnt" 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
import { createSignal } from "solid-js"
|
||||
import { BasicToolV2 } from "./basic-tool-v2"
|
||||
|
||||
const docs = `### Overview
|
||||
Compact collapsible tool row showing title, subtitle, args, and diff changes, with an expand/collapse chevron.
|
||||
|
||||
### API
|
||||
- \`BasicToolV2\` wraps Kobalte \`Collapsible\`. Pass \`open\`, \`defaultOpen\`, and \`onOpenChange\` for controlled/uncontrolled disclosure.
|
||||
- \`trigger\` accepts either a \`BasicToolV2TriggerTitle\` object (title, subtitle, args, changes) or arbitrary JSX.
|
||||
- When \`status\` is \`"pending"\` or \`"running"\`, subtitle/args/chevron hide and the title shows a shimmer animation.
|
||||
- Pass \`children\` for expandable detail content.
|
||||
|
||||
### Theming
|
||||
- Uses \`data-component="basic-tool-v2"\` and slot attributes; colors via \`--bt-*\` CSS variables.
|
||||
`
|
||||
|
||||
export default {
|
||||
title: "UI V2/BasicTool",
|
||||
id: "components-basic-tool-v2",
|
||||
component: BasicToolV2,
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
frameBackground: "#fff",
|
||||
layout: "padded",
|
||||
docs: {
|
||||
description: {
|
||||
component: docs,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const Default = {
|
||||
render: () => (
|
||||
<BasicToolV2
|
||||
trigger={{
|
||||
title: "Read",
|
||||
subtitle: "src/index.ts",
|
||||
args: ["lines=1-50"],
|
||||
changes: { additions: 12, deletions: 3 },
|
||||
}}
|
||||
defaultOpen={false}
|
||||
>
|
||||
File content appears here.
|
||||
</BasicToolV2>
|
||||
),
|
||||
}
|
||||
|
||||
export const Expanded = {
|
||||
render: () => (
|
||||
<BasicToolV2
|
||||
trigger={{
|
||||
title: "Read",
|
||||
subtitle: "src/index.ts",
|
||||
args: ["lines=1-50"],
|
||||
changes: { additions: 12, deletions: 3 },
|
||||
}}
|
||||
defaultOpen={true}
|
||||
>
|
||||
File content appears here.
|
||||
</BasicToolV2>
|
||||
),
|
||||
}
|
||||
|
||||
export const Pending = {
|
||||
render: () => (
|
||||
<BasicToolV2
|
||||
trigger={{
|
||||
title: "Read",
|
||||
subtitle: "src/index.ts",
|
||||
args: ["lines=1-50"],
|
||||
changes: { additions: 12, deletions: 3 },
|
||||
}}
|
||||
status="pending"
|
||||
/>
|
||||
),
|
||||
}
|
||||
|
||||
export const NoChildren = {
|
||||
render: () => (
|
||||
<BasicToolV2
|
||||
trigger={{
|
||||
title: "Grep",
|
||||
subtitle: "pattern=TODO",
|
||||
args: ["recursive=true"],
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}
|
||||
|
||||
export const CustomTrigger = {
|
||||
render: () => (
|
||||
<BasicToolV2
|
||||
trigger={
|
||||
<span style={{ color: "#161616", "font-size": "13px", "font-weight": "440" }}>Custom trigger content</span>
|
||||
}
|
||||
>
|
||||
Expandable detail for custom trigger.
|
||||
</BasicToolV2>
|
||||
),
|
||||
}
|
||||
|
||||
export const Controlled = {
|
||||
render: () => {
|
||||
const [open, setOpen] = createSignal(false)
|
||||
return (
|
||||
<div style={{ display: "flex", "flex-direction": "column", gap: "16px", "max-width": "420px" }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
style={{
|
||||
padding: "4px 10px",
|
||||
"font-size": "12px",
|
||||
"border-radius": "6px",
|
||||
border: "1px solid rgba(0,0,0,0.15)",
|
||||
background: "#fff",
|
||||
color: "#161616",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Toggle from outside: {open() ? "Open" : "Closed"}
|
||||
</button>
|
||||
<BasicToolV2
|
||||
trigger={{
|
||||
title: "Write",
|
||||
subtitle: "src/utils.ts",
|
||||
changes: { additions: 8, deletions: 2 },
|
||||
}}
|
||||
open={open()}
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
Controlled content.
|
||||
</BasicToolV2>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
import { Collapsible } from "@kobalte/core/collapsible"
|
||||
import { type ComponentProps, type JSX, For, Show, createMemo, splitProps } from "solid-js"
|
||||
import { DiffChanges } from "./diff-changes-v2"
|
||||
import { TextShimmerV2 } from "./text-shimmer-v2"
|
||||
import "./basic-tool-v2.css"
|
||||
|
||||
function ChevronIcon() {
|
||||
return (
|
||||
<svg
|
||||
data-slot="basic-tool-v2-chevron"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M6.75194 10.6243C6.41861 10.8187 6 10.5783 6 10.1924V5.80837C6 5.42247 6.41861 5.18204 6.75194 5.37648L10.5096 7.56846C10.8404 7.7614 10.8404 8.2393 10.5096 8.43224L6.75194 10.6243Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export interface BasicToolV2TriggerTitle {
|
||||
title: string
|
||||
subtitle?: string
|
||||
args?: string[]
|
||||
changes?: { additions: number; deletions: number } | { additions: number; deletions: number }[]
|
||||
action?: JSX.Element
|
||||
}
|
||||
|
||||
const isTriggerTitle = (val: unknown): val is BasicToolV2TriggerTitle =>
|
||||
typeof val === "object" && val !== null && "title" in val && (typeof Node === "undefined" || !(val instanceof Node))
|
||||
|
||||
export interface BasicToolV2Props extends Omit<ComponentProps<"div">, "children" | "title"> {
|
||||
trigger: BasicToolV2TriggerTitle | JSX.Element
|
||||
children?: JSX.Element
|
||||
status?: string
|
||||
open?: boolean
|
||||
defaultOpen?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
onSubtitleClick?: () => void
|
||||
}
|
||||
|
||||
export function BasicToolV2(props: BasicToolV2Props) {
|
||||
const [local, rest] = splitProps(props, [
|
||||
"trigger",
|
||||
"children",
|
||||
"status",
|
||||
"open",
|
||||
"defaultOpen",
|
||||
"onOpenChange",
|
||||
"onSubtitleClick",
|
||||
"class",
|
||||
"classList",
|
||||
])
|
||||
|
||||
const pending = createMemo(() => local.status === "pending" || local.status === "running")
|
||||
|
||||
const hasChildren = createMemo(() => {
|
||||
const c = local.children
|
||||
if (c == null) return false
|
||||
return true
|
||||
})
|
||||
|
||||
const canExpand = createMemo(() => hasChildren() && !pending())
|
||||
|
||||
const handleOpenChange = (value: boolean) => {
|
||||
if (pending()) return
|
||||
local.onOpenChange?.(value)
|
||||
}
|
||||
|
||||
return (
|
||||
<Collapsible
|
||||
{...rest}
|
||||
data-component="basic-tool-v2"
|
||||
open={local.open}
|
||||
defaultOpen={local.defaultOpen}
|
||||
onOpenChange={handleOpenChange}
|
||||
disabled={!canExpand()}
|
||||
classList={{
|
||||
...local.classList,
|
||||
[local.class ?? ""]: !!local.class,
|
||||
}}
|
||||
>
|
||||
<Collapsible.Trigger as="div" role="button" data-slot="basic-tool-v2-trigger">
|
||||
<div data-slot="basic-tool-v2-labels">
|
||||
<Show when={isTriggerTitle(local.trigger) && local.trigger} fallback={local.trigger as JSX.Element}>
|
||||
{(title) => (
|
||||
<>
|
||||
<span data-slot="basic-tool-v2-title">
|
||||
<TextShimmerV2 text={title().title} active={pending()} />
|
||||
</span>
|
||||
<Show when={!pending() && title().subtitle}>
|
||||
<span data-slot="basic-tool-v2-sep" aria-hidden="true">
|
||||
·
|
||||
</span>
|
||||
<span
|
||||
data-slot="basic-tool-v2-subtitle"
|
||||
style={local.onSubtitleClick ? { cursor: "pointer" } : undefined}
|
||||
onClick={(e) => {
|
||||
if (local.onSubtitleClick) {
|
||||
e.stopPropagation()
|
||||
local.onSubtitleClick()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{title().subtitle}
|
||||
</span>
|
||||
</Show>
|
||||
<Show when={!pending() && title().args?.length}>
|
||||
<For each={title().args}>{(arg) => <span data-slot="basic-tool-v2-arg">{arg}</span>}</For>
|
||||
</Show>
|
||||
<Show when={!pending() && title().changes}>
|
||||
<span data-slot="basic-tool-v2-diff">
|
||||
<DiffChanges changes={title().changes!} />
|
||||
</span>
|
||||
</Show>
|
||||
<Show when={!pending() && title().action}>{(action) => action()}</Show>
|
||||
</>
|
||||
)}
|
||||
</Show>
|
||||
<Show when={canExpand()}>
|
||||
<span data-slot="basic-tool-v2-chevron-wrap">
|
||||
<ChevronIcon />
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
</Collapsible.Trigger>
|
||||
<Show when={canExpand()}>
|
||||
<Collapsible.Content data-slot="basic-tool-v2-content">
|
||||
<div data-slot="basic-tool-v2-content-inner">{local.children}</div>
|
||||
</Collapsible.Content>
|
||||
</Show>
|
||||
</Collapsible>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,875 +0,0 @@
|
|||
[data-component="session-progress-indicator-v2"] {
|
||||
--_duration: 1200ms;
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
color: var(--v2-icon-icon-muted, #808080);
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot] {
|
||||
fill: currentColor;
|
||||
opacity: 0.2;
|
||||
animation-duration: var(--_duration);
|
||||
animation-timing-function: ease-out;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-0 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="0"] {
|
||||
animation-name: session-progress-indicator-v2-dot-0;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-5 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="5"] {
|
||||
animation-name: session-progress-indicator-v2-dot-5;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-10 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 1;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="10"] {
|
||||
animation-name: session-progress-indicator-v2-dot-10;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-15 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="15"] {
|
||||
animation-name: session-progress-indicator-v2-dot-15;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-20 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="20"] {
|
||||
animation-name: session-progress-indicator-v2-dot-20;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-1 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="1"] {
|
||||
animation-name: session-progress-indicator-v2-dot-1;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-6 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="6"] {
|
||||
animation-name: session-progress-indicator-v2-dot-6;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-11 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 1;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="11"] {
|
||||
animation-name: session-progress-indicator-v2-dot-11;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-16 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="16"] {
|
||||
animation-name: session-progress-indicator-v2-dot-16;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-21 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="21"] {
|
||||
animation-name: session-progress-indicator-v2-dot-21;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-2 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="2"] {
|
||||
animation-name: session-progress-indicator-v2-dot-2;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-7 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="7"] {
|
||||
animation-name: session-progress-indicator-v2-dot-7;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-12 {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
25% {
|
||||
opacity: 1;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
75% {
|
||||
opacity: 1;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="12"] {
|
||||
animation-name: session-progress-indicator-v2-dot-12;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-17 {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="17"] {
|
||||
animation-name: session-progress-indicator-v2-dot-17;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-22 {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="22"] {
|
||||
animation-name: session-progress-indicator-v2-dot-22;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-3 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="3"] {
|
||||
animation-name: session-progress-indicator-v2-dot-3;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-8 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="8"] {
|
||||
animation-name: session-progress-indicator-v2-dot-8;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-13 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 1;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="13"] {
|
||||
animation-name: session-progress-indicator-v2-dot-13;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-18 {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="18"] {
|
||||
animation-name: session-progress-indicator-v2-dot-18;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-23 {
|
||||
0% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="23"] {
|
||||
animation-name: session-progress-indicator-v2-dot-23;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-4 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="4"] {
|
||||
animation-name: session-progress-indicator-v2-dot-4;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-9 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="9"] {
|
||||
animation-name: session-progress-indicator-v2-dot-9;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-14 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 1;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="14"] {
|
||||
animation-name: session-progress-indicator-v2-dot-14;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-19 {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="19"] {
|
||||
animation-name: session-progress-indicator-v2-dot-19;
|
||||
}
|
||||
|
||||
@keyframes session-progress-indicator-v2-dot-24 {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
12.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
37.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
62.5% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
87.5% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="24"] {
|
||||
animation-name: session-progress-indicator-v2-dot-24;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
[data-component="session-progress-indicator-v2"] [data-dot] {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
[data-component="session-progress-indicator-v2"] [data-dot="12"] {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
// @ts-nocheck
|
||||
import { SessionProgressIndicatorV2 } from "./session-progress-indicator-v2"
|
||||
|
||||
const docs = `### Overview
|
||||
Animated 5×5 dot grid loader for in-progress session state.
|
||||
|
||||
Derived from Figma \`_sessionProgressIndicator\` with 8-frame rotation.
|
||||
|
||||
### API
|
||||
- Accepts standard SVG props.
|
||||
|
||||
### Behavior
|
||||
- CSS keyframes drive per-dot opacity across 8 frames (1.2s loop).
|
||||
- Center dot stays at full opacity throughout the cycle.
|
||||
|
||||
### Accessibility
|
||||
- Sets \`aria-hidden="true"\` by default.
|
||||
|
||||
### Theming
|
||||
- Uses \`currentColor\` via \`--v2-icon-icon-muted\`.
|
||||
`
|
||||
|
||||
export default {
|
||||
title: "UI V2/SessionProgressIndicator",
|
||||
id: "components-session-progress-indicator-v2",
|
||||
component: SessionProgressIndicatorV2,
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: docs,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const Basic = {
|
||||
render: () => <SessionProgressIndicatorV2 />,
|
||||
}
|
||||
|
||||
export const Sizes = {
|
||||
render: () => (
|
||||
<div style={{ display: "flex", gap: "16px", "align-items": "center" }}>
|
||||
<SessionProgressIndicatorV2 width={12} height={12} />
|
||||
<SessionProgressIndicatorV2 />
|
||||
<SessionProgressIndicatorV2 width={24} height={24} />
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
||||
export const OnDark = {
|
||||
render: () => (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "16px",
|
||||
"align-items": "center",
|
||||
padding: "16px",
|
||||
"background-color": "#171717",
|
||||
color: "#c7c7c7",
|
||||
}}
|
||||
>
|
||||
<SessionProgressIndicatorV2 />
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import { For, splitProps, type ComponentProps } from "solid-js"
|
||||
import "./session-progress-indicator-v2.css"
|
||||
|
||||
const grid = 5
|
||||
const dot = 2
|
||||
const gap = 1
|
||||
const origin = 1.5
|
||||
const dots = Array.from({ length: grid * grid }, (_, index) => ({
|
||||
index,
|
||||
x: origin + (index % grid) * (dot + gap),
|
||||
y: origin + Math.floor(index / grid) * (dot + gap),
|
||||
}))
|
||||
|
||||
export function SessionProgressIndicatorV2(props: ComponentProps<"svg">) {
|
||||
const [local, rest] = splitProps(props, ["class", "classList", "width", "height"])
|
||||
return (
|
||||
<svg
|
||||
{...rest}
|
||||
class={local.class}
|
||||
classList={local.classList}
|
||||
width={local.width ?? 16}
|
||||
height={local.height ?? 16}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
data-component="session-progress-indicator-v2"
|
||||
aria-hidden={rest["aria-hidden"] ?? "true"}
|
||||
>
|
||||
<For each={dots}>{(cell) => <rect data-dot={cell.index} x={cell.x} y={cell.y} width={dot} height={dot} />}</For>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,200 +0,0 @@
|
|||
[data-component="tool-error-card"] {
|
||||
--tec-border: var(--v2-state-fg-danger);
|
||||
--tec-title: var(--v2-text-text-base);
|
||||
--tec-sep: var(--v2-text-text-muted);
|
||||
--tec-subtitle: var(--v2-text-text-muted);
|
||||
--tec-suffix: var(--v2-text-text-faint);
|
||||
--tec-chevron: var(--v2-text-text-faint);
|
||||
--tec-icon: var(--v2-icon-icon-base);
|
||||
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
padding: 0 0 0 10px;
|
||||
gap: 8px;
|
||||
border-left: 2px solid var(--tec-border);
|
||||
font-variant-numeric: tabular-nums;
|
||||
|
||||
[data-slot="tool-error-card-trigger"] {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 24px;
|
||||
padding: 2px 0;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-trigger"]:focus-visible {
|
||||
outline: 2px solid var(--v2-border-border-focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-icon-wrap"] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 16px;
|
||||
height: 20px;
|
||||
box-sizing: border-box;
|
||||
color: var(--tec-icon);
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-ban"] {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-loader"] {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
animation: tool-error-card-spin 0.65s linear infinite;
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-main"] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
min-width: 0;
|
||||
min-height: 20px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-labels"] {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-title"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
max-width: 100%;
|
||||
height: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 530;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.04px;
|
||||
color: var(--tec-title);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-sep"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
height: 20px;
|
||||
font-size: 11px;
|
||||
font-weight: 530;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.05px;
|
||||
color: var(--tec-sep);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-subtitle"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
height: 20px;
|
||||
flex: 0 1 auto;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.04px;
|
||||
color: var(--tec-subtitle);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
a[data-slot="tool-error-card-subtitle"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
height: 20px;
|
||||
flex: 0 1 auto;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.04px;
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-chevron-wrap"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 14px;
|
||||
height: 20px;
|
||||
color: var(--tec-chevron);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-chevron"] {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: var(--tec-chevron);
|
||||
transition: transform 0.15s ease-out;
|
||||
}
|
||||
|
||||
&[data-expanded] [data-slot="tool-error-card-chevron"] {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-content"] {
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
[data-slot="tool-error-card-suffix"] {
|
||||
padding: 0 0 0 24px;
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.04px;
|
||||
color: var(--tec-suffix);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes tool-error-card-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
import { createSignal } from "solid-js"
|
||||
import { ButtonV2 } from "./button-v2"
|
||||
import { ToolErrorCardV2, type ToolErrorCardV2Props } from "./tool-error-card-v2"
|
||||
|
||||
const docs = `### Overview
|
||||
Compact tool error row with optional expandable detail, aligned to the OpenCode design system spec.
|
||||
|
||||
### API
|
||||
- \`ToolErrorCardV2\` wraps Kobalte \`Collapsible\` directly. Pass \`open\`, \`defaultOpen\`, and \`onOpenChange\` like any disclosure (controlled when \`open\` is defined).
|
||||
- Without a non-empty \`suffix\`, the card is not expandable (\`disabled\` on the collapsible root).
|
||||
|
||||
### Theming
|
||||
- Uses \`data-component="tool-error-card"\` and slot attributes; colors are CSS variables on the root (\`--tec-*\`).
|
||||
`
|
||||
|
||||
export default {
|
||||
title: "UI V2/ToolErrorCard",
|
||||
id: "components-tool-error-card-v2",
|
||||
component: ToolErrorCardV2,
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
frameBackground: "#fff",
|
||||
layout: "padded",
|
||||
docs: {
|
||||
description: {
|
||||
component: docs,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const Default = {
|
||||
args: {
|
||||
title: "Read",
|
||||
subtitle: "Permission denied",
|
||||
suffix: "The tool could not access the requested path.",
|
||||
defaultOpen: false,
|
||||
} satisfies ToolErrorCardV2Props,
|
||||
render: (args: ToolErrorCardV2Props) => <ToolErrorCardV2 {...args} />,
|
||||
}
|
||||
|
||||
export const Loading = {
|
||||
args: {
|
||||
title: "Read",
|
||||
subtitle: "Working",
|
||||
suffix: "Details appear when the tool finishes.",
|
||||
loading: true,
|
||||
defaultOpen: false,
|
||||
} satisfies ToolErrorCardV2Props,
|
||||
render: (args: ToolErrorCardV2Props) => <ToolErrorCardV2 {...args} />,
|
||||
}
|
||||
|
||||
export const SubtitleLink = {
|
||||
args: {
|
||||
title: "Task",
|
||||
subtitle: "View logs",
|
||||
subtitleHref: "https://example.com",
|
||||
suffix: "Subagent exited with code 1.",
|
||||
defaultOpen: false,
|
||||
} satisfies ToolErrorCardV2Props,
|
||||
render: (args: ToolErrorCardV2Props) => <ToolErrorCardV2 {...args} />,
|
||||
}
|
||||
|
||||
export const NoSuffixDisabled = {
|
||||
args: {
|
||||
title: "List",
|
||||
subtitle: "No detail",
|
||||
defaultOpen: false,
|
||||
} satisfies ToolErrorCardV2Props,
|
||||
render: (args: ToolErrorCardV2Props) => <ToolErrorCardV2 {...args} />,
|
||||
}
|
||||
|
||||
export const Controlled = {
|
||||
render: () => {
|
||||
const [open, setOpen] = createSignal(false)
|
||||
return (
|
||||
<div style={{ display: "flex", "flex-direction": "column", gap: "24px", "max-width": "420px" }}>
|
||||
<ButtonV2 type="button" classList={{ "w-fit": true }} onClick={() => setOpen((o) => !o)}>
|
||||
Toggle from outside: {open() ? "Open" : "Closed"}
|
||||
</ButtonV2>
|
||||
<ToolErrorCardV2
|
||||
title="Grep"
|
||||
subtitle="Timeout"
|
||||
suffix="Operation exceeded 30s."
|
||||
open={open()}
|
||||
onOpenChange={setOpen}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
import { Collapsible } from "@kobalte/core/collapsible"
|
||||
import { type ComponentProps, type JSX, Show, createMemo, splitProps } from "solid-js"
|
||||
import "./tool-error-card-v2.css"
|
||||
|
||||
function BanIcon() {
|
||||
return (
|
||||
<svg
|
||||
data-slot="tool-error-card-ban"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M3.44283 12.5575L12.5495 3.45081M14.4446 8.00011C14.4446 11.5593 11.5593 14.4446 8.00011 14.4446C4.44094 14.4446 1.55566 11.5593 1.55566 8.00011C1.55566 4.44094 4.44094 1.55566 8.00011 1.55566C11.5593 1.55566 14.4446 4.44094 14.4446 8.00011Z"
|
||||
stroke="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
/** duo-progress-25: faint track ring + ~25% solid arc (Figma OpenCode DS) */
|
||||
function LoaderIcon() {
|
||||
const r = 5.9
|
||||
return (
|
||||
<svg
|
||||
data-slot="tool-error-card-loader"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<g transform="translate(8 8)">
|
||||
<circle
|
||||
r={r}
|
||||
fill="none"
|
||||
stroke="var(--v2-icon-icon-base)"
|
||||
stroke-width="1"
|
||||
stroke-opacity="0.3"
|
||||
transform="rotate(-90)"
|
||||
/>
|
||||
<circle
|
||||
r={r}
|
||||
fill="none"
|
||||
stroke="var(--v2-icon-icon-base)"
|
||||
stroke-width="1"
|
||||
pathLength="100"
|
||||
stroke-dasharray="25 75"
|
||||
transform="rotate(-90)"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function ChevronIcon() {
|
||||
return (
|
||||
<svg
|
||||
data-slot="tool-error-card-chevron"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M5.90795 9.62425C5.61628 9.81865 5.25 9.57825 5.25 9.19235V4.80837C5.25 4.42247 5.61628 4.18204 5.90795 4.37648L9.1959 6.56846C9.48535 6.7614 9.48535 7.2393 9.1959 7.43224L5.90795 9.62425Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export interface ToolErrorCardV2Props extends Omit<ComponentProps<"div">, "children" | "title"> {
|
||||
title: JSX.Element | string
|
||||
subtitle: JSX.Element | string
|
||||
suffix?: JSX.Element | string
|
||||
loading?: boolean
|
||||
open?: boolean
|
||||
defaultOpen?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
/** When set, subtitle renders as a link (clicks do not toggle expand). */
|
||||
subtitleHref?: string
|
||||
}
|
||||
|
||||
export function ToolErrorCardV2(props: ToolErrorCardV2Props) {
|
||||
const [local, rest] = splitProps(props, [
|
||||
"title",
|
||||
"subtitle",
|
||||
"suffix",
|
||||
"loading",
|
||||
"open",
|
||||
"defaultOpen",
|
||||
"onOpenChange",
|
||||
"subtitleHref",
|
||||
"class",
|
||||
"classList",
|
||||
])
|
||||
|
||||
const hasSuffix = createMemo(() => {
|
||||
const s = local.suffix
|
||||
if (s == null) return false
|
||||
if (typeof s === "string") return s.length > 0
|
||||
return true
|
||||
})
|
||||
|
||||
return (
|
||||
<Collapsible
|
||||
{...rest}
|
||||
data-component="tool-error-card"
|
||||
open={local.open}
|
||||
defaultOpen={local.defaultOpen}
|
||||
onOpenChange={local.onOpenChange}
|
||||
disabled={!hasSuffix()}
|
||||
aria-busy={local.loading ? true : undefined}
|
||||
classList={{
|
||||
...local.classList,
|
||||
[local.class ?? ""]: !!local.class,
|
||||
}}
|
||||
>
|
||||
<Collapsible.Trigger as="div" role="button" data-slot="tool-error-card-trigger">
|
||||
<span data-slot="tool-error-card-icon-wrap">
|
||||
<Show when={local.loading} fallback={<BanIcon />}>
|
||||
<LoaderIcon />
|
||||
</Show>
|
||||
</span>
|
||||
<div data-slot="tool-error-card-main">
|
||||
<div data-slot="tool-error-card-labels">
|
||||
<span data-slot="tool-error-card-title">{local.title}</span>
|
||||
<span data-slot="tool-error-card-sep" aria-hidden="true">
|
||||
·
|
||||
</span>
|
||||
<Show
|
||||
when={local.subtitleHref}
|
||||
fallback={<span data-slot="tool-error-card-subtitle">{local.subtitle}</span>}
|
||||
>
|
||||
<a
|
||||
data-slot="tool-error-card-subtitle"
|
||||
href={local.subtitleHref!}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
{local.subtitle}
|
||||
</a>
|
||||
</Show>
|
||||
<Show when={hasSuffix()}>
|
||||
<span data-slot="tool-error-card-chevron-wrap">
|
||||
<ChevronIcon />
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</Collapsible.Trigger>
|
||||
<Show when={hasSuffix()}>
|
||||
<Collapsible.Content data-slot="tool-error-card-content">
|
||||
<div data-slot="tool-error-card-suffix">{local.suffix}</div>
|
||||
</Collapsible.Content>
|
||||
</Show>
|
||||
</Collapsible>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue