refactor(ui): promote current design system (#43200)
This commit is contained in:
@@ -279,7 +279,7 @@
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
[data-component="icon"] {
|
||||
[data-slot="icon-svg"] {
|
||||
color: var(--task-agent-color, var(--v2-icon-icon-base));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export const lineCommentStyles = `
|
||||
border: none;
|
||||
}
|
||||
|
||||
[data-component="line-comment"] [data-component="icon"] {
|
||||
[data-component="line-comment"] [data-slot="icon-svg"] {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
@@ -411,7 +411,7 @@ export const LineCommentEditor = (props: LineCommentEditorProps) => {
|
||||
<Button size="small" variant="ghost" onClick={split.onCancel}>
|
||||
{split.cancelLabel ?? i18n.t("ui.common.cancel")}
|
||||
</Button>
|
||||
<Button size="small" variant="primary" disabled={split.value.trim().length === 0} onClick={submit}>
|
||||
<Button size="small" variant="contrast" disabled={split.value.trim().length === 0} onClick={submit}>
|
||||
{split.submitLabel ?? i18n.t("ui.lineComment.submit")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
@@ -12,9 +12,9 @@ import {
|
||||
splitProps,
|
||||
} from "solid-js"
|
||||
import { isServer, render } from "solid-js/web"
|
||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||
import { canReusePendingBlock, completedProjection } from "./markdown-projection"
|
||||
import type { Block, Projection } from "./markdown-stream"
|
||||
import {
|
||||
@@ -133,20 +133,20 @@ function createCopyButton(labels: CopyLabels) {
|
||||
function MarkdownCopyButton(props: { labels: CopyLabels; copied: boolean }) {
|
||||
const label = () => (props.copied ? props.labels.copied : props.labels.copy)
|
||||
return (
|
||||
<TooltipV2 placement="top" value={label()}>
|
||||
<IconButtonV2
|
||||
<Tooltip placement="top" value={label()}>
|
||||
<IconButton
|
||||
type="button"
|
||||
size="normal"
|
||||
variant="ghost-muted"
|
||||
aria-label={label()}
|
||||
icon={
|
||||
<>
|
||||
<IconV2 name="outline-copy" data-copy-icon />
|
||||
<IconV2 name="check" data-check-icon />
|
||||
<Icon name="outline-copy" data-copy-icon />
|
||||
<Icon name="check" data-check-icon />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,13 @@
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
[data-slot="message-nav-diff-bars"] {
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
[data-slot="message-nav-title-preview"] {
|
||||
font-size: 14px; /* text-14-regular */
|
||||
color: var(--text-base);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { UserMessage } from "../presentation"
|
||||
import { HoverCard } from "@kobalte/core/hover-card"
|
||||
import { ComponentProps, For, Match, Show, createSignal, splitProps, Switch } from "solid-js"
|
||||
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
||||
import { ComponentProps, For, Match, Show, createMemo, createSignal, splitProps, Switch } from "solid-js"
|
||||
import { useI18n } from "@opencode-ai/ui/context/i18n"
|
||||
|
||||
export function MessageNav(
|
||||
@@ -51,7 +50,7 @@ export function MessageNav(
|
||||
</Match>
|
||||
<Match when={local.size === "normal"}>
|
||||
<button data-slot="message-nav-message-button" onClick={handleClick} onKeyDown={handleKeyPress}>
|
||||
<DiffChanges changes={message.summary?.diffs ?? []} variant="bars" />
|
||||
<MessageDiffBars changes={message.summary?.diffs ?? []} />
|
||||
<div
|
||||
data-slot="message-nav-title-preview"
|
||||
data-active={message.id === local.current?.id || undefined}
|
||||
@@ -100,3 +99,47 @@ export function MessageNav(
|
||||
</Switch>
|
||||
)
|
||||
}
|
||||
|
||||
function MessageDiffBars(props: { changes: { additions: number; deletions: number }[] }) {
|
||||
const additions = createMemo(() => props.changes.reduce((total, diff) => total + diff.additions, 0))
|
||||
const deletions = createMemo(() => props.changes.reduce((total, diff) => total + diff.deletions, 0))
|
||||
const colors = createMemo(() => {
|
||||
const added = additions()
|
||||
const deleted = deletions()
|
||||
if (added === 0 && deleted === 0) return Array(5).fill("var(--icon-weak-base)")
|
||||
|
||||
if (added + deleted < 5) {
|
||||
return [
|
||||
...Array(added > 0 ? 1 : 0).fill("var(--icon-diff-add-base)"),
|
||||
...Array(deleted > 0 ? 1 : 0).fill("var(--icon-diff-delete-base)"),
|
||||
...Array(5 - (added > 0 ? 1 : 0) - (deleted > 0 ? 1 : 0)).fill("var(--icon-weak-base)"),
|
||||
]
|
||||
}
|
||||
|
||||
const total = added + deleted
|
||||
const ratio = added > deleted ? added / deleted : deleted / added
|
||||
const colored = total < 20 || ratio < 4 ? 4 : 5
|
||||
const addedRaw = (added / total) * colored
|
||||
const deletedRaw = (deleted / total) * colored
|
||||
const addedBars =
|
||||
added === 0 ? 0 : Math.min(added <= 5 ? 1 : added <= 10 ? 2 : colored, Math.max(1, Math.round(addedRaw)))
|
||||
const deletedBars =
|
||||
deleted === 0 ? 0 : Math.min(deleted <= 5 ? 1 : deleted <= 10 ? 2 : colored, Math.max(1, Math.round(deletedRaw)))
|
||||
const overflow = Math.max(0, addedBars + deletedBars - colored)
|
||||
const adjustedAdded = overflow > 0 && addedRaw > deletedRaw ? addedBars - overflow : addedBars
|
||||
const adjustedDeleted = overflow > 0 && addedRaw <= deletedRaw ? deletedBars - overflow : deletedBars
|
||||
return [
|
||||
...Array(adjustedAdded).fill("var(--icon-diff-add-base)"),
|
||||
...Array(adjustedDeleted).fill("var(--icon-diff-delete-base)"),
|
||||
...Array(5 - adjustedAdded - adjustedDeleted).fill("var(--icon-weak-base)"),
|
||||
]
|
||||
})
|
||||
|
||||
return (
|
||||
<svg data-slot="message-nav-diff-bars" viewBox="0 0 18 14" aria-hidden="true">
|
||||
<For each={colors().slice(0, 5)}>
|
||||
{(color, index) => <rect x={index() * 4} width="2" height="14" rx="1" fill={color} />}
|
||||
</For>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
justify-content: center;
|
||||
color: var(--icon-weak);
|
||||
|
||||
[data-component="icon"] {
|
||||
[data-slot="icon-svg"] {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
@@ -172,7 +172,7 @@
|
||||
transition: opacity 0.15s ease;
|
||||
will-change: opacity;
|
||||
|
||||
[data-component="tooltip-trigger"] {
|
||||
[data-component="tooltip-v2-trigger"] {
|
||||
display: inline-flex;
|
||||
width: fit-content;
|
||||
}
|
||||
@@ -240,7 +240,7 @@
|
||||
transition: opacity 0.15s ease;
|
||||
will-change: opacity;
|
||||
|
||||
[data-component="tooltip-trigger"] {
|
||||
[data-component="tooltip-v2-trigger"] {
|
||||
display: inline-flex;
|
||||
width: fit-content;
|
||||
}
|
||||
@@ -766,7 +766,7 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[data-slot="permission-icon"] [data-component="icon"] {
|
||||
[data-slot="permission-icon"] [data-slot="icon-svg"] {
|
||||
color: var(--icon-warning-base);
|
||||
}
|
||||
|
||||
@@ -833,7 +833,7 @@
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
[data-component="button"] {
|
||||
[data-component="button-v2"] {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
@@ -1024,7 +1024,7 @@
|
||||
background-color 0.15s ease,
|
||||
border-color 0.15s ease;
|
||||
|
||||
[data-component="icon"] {
|
||||
[data-slot="icon-svg"] {
|
||||
opacity: 0;
|
||||
color: var(--icon-base);
|
||||
}
|
||||
@@ -1044,7 +1044,7 @@
|
||||
&[data-picked="true"] {
|
||||
border-color: var(--icon-interactive-base);
|
||||
|
||||
[data-component="icon"] {
|
||||
[data-slot="icon-svg"] {
|
||||
opacity: 1;
|
||||
color: var(--icon-invert-base);
|
||||
}
|
||||
@@ -1170,7 +1170,7 @@
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
[data-component="icon"] {
|
||||
[data-slot="icon-svg"] {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
@@ -1331,7 +1331,7 @@
|
||||
line-height: var(--line-height-large);
|
||||
color: var(--v2-text-text-muted);
|
||||
|
||||
[data-component="icon"] {
|
||||
[data-slot="icon-svg"] {
|
||||
flex-shrink: 0;
|
||||
color: var(--icon-weak);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import { Accordion } from "@opencode-ai/ui/accordion"
|
||||
import { StickyAccordionHeader } from "@opencode-ai/ui/sticky-accordion-header"
|
||||
import { Collapsible } from "@opencode-ai/ui/collapsible"
|
||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { Icon, type IconProps } from "@opencode-ai/ui/icon"
|
||||
import { ToolErrorCard } from "./tool-error-card"
|
||||
import { Checkbox } from "@opencode-ai/ui/checkbox"
|
||||
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
||||
@@ -51,10 +51,7 @@ import { CommentCardV2 } from "../v2/components/comment-card-v2"
|
||||
import { checksum } from "@opencode-ai/util/encode"
|
||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { TextShimmer } from "@opencode-ai/ui/text-shimmer"
|
||||
import { AnimatedCountList } from "./tool-count-summary"
|
||||
import { ToolStatusTitle } from "./tool-status-title"
|
||||
@@ -213,34 +210,17 @@ function MessageActionButton(
|
||||
) {
|
||||
const icon = () => (props.icon === "copy" ? "outline-copy" : props.icon)
|
||||
return (
|
||||
<Show
|
||||
when={props.useV2}
|
||||
fallback={
|
||||
<Tooltip value={props.label} placement="top" gutter={4}>
|
||||
<IconButton
|
||||
icon={props.icon}
|
||||
size="normal"
|
||||
variant="ghost"
|
||||
disabled={props.disabled}
|
||||
onMouseDown={props.onMouseDown}
|
||||
onClick={props.onClick}
|
||||
aria-label={props["aria-label"]}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<TooltipV2 value={props.label} placement="top" gutter={4}>
|
||||
<IconButtonV2
|
||||
icon={<IconV2 name={icon()} size="small" />}
|
||||
size="normal"
|
||||
variant="ghost-muted"
|
||||
disabled={props.disabled}
|
||||
onMouseDown={props.onMouseDown}
|
||||
onClick={props.onClick}
|
||||
aria-label={props["aria-label"]}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Show>
|
||||
<Tooltip appearance={props.useV2 ? "compact" : "standard"} value={props.label} placement="top" gutter={4}>
|
||||
<IconButton
|
||||
icon={<Icon name={icon()} size="small" />}
|
||||
size="normal"
|
||||
variant="ghost-muted"
|
||||
disabled={props.disabled}
|
||||
onMouseDown={props.onMouseDown}
|
||||
onClick={props.onClick}
|
||||
aria-label={props["aria-label"]}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -363,7 +343,6 @@ function displayDirectory(path: string | undefined) {
|
||||
return relativizeProjectPath(getDirectory(path), data.directory)
|
||||
}
|
||||
|
||||
import type { IconProps } from "@opencode-ai/ui/icon"
|
||||
import { normalize, resolveFileDiff } from "./session-diff"
|
||||
|
||||
export type ToolInfo = {
|
||||
@@ -1191,9 +1170,9 @@ function UserMessageComments(props: { comments: UserMessageComment[]; bounded: b
|
||||
)}
|
||||
</For>
|
||||
<Show when={props.bounded && props.comments.length > 5 && !state.expanded}>
|
||||
<ButtonV2 size="small" variant="ghost-muted" onClick={() => setState("expanded", true)}>
|
||||
<Button size="small" variant="ghost-muted" onClick={() => setState("expanded", true)}>
|
||||
{i18n.t("ui.common.showMore")}
|
||||
</ButtonV2>
|
||||
</Button>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
@@ -2144,16 +2123,16 @@ function ConsoleOutput(props: { copy: string; children: JSX.Element }) {
|
||||
return (
|
||||
<div data-component="bash-output" dir="ltr">
|
||||
<div data-slot="bash-copy">
|
||||
<TooltipV2 value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")} placement="top">
|
||||
<IconButtonV2
|
||||
icon={<IconV2 name={copied() ? "check" : "outline-copy"} size="small" />}
|
||||
<Tooltip value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")} placement="top">
|
||||
<IconButton
|
||||
icon={<Icon name={copied() ? "check" : "outline-copy"} size="small" />}
|
||||
size="normal"
|
||||
variant="ghost-muted"
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={copy}
|
||||
aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
data-slot="bash-scroll"
|
||||
@@ -2350,7 +2329,7 @@ ToolRegistry.register({
|
||||
</div>
|
||||
<div data-slot="message-part-actions">
|
||||
<Show when={!pending() && props.metadata.filediff}>
|
||||
<DiffChanges changes={props.metadata.filediff} />
|
||||
<DiffChanges appearance="standard" changes={props.metadata.filediff} />
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2361,7 +2340,7 @@ ToolRegistry.register({
|
||||
path={path()}
|
||||
actions={
|
||||
<Show when={!pending() && props.metadata.filediff}>
|
||||
<DiffChanges changes={props.metadata.filediff!} />
|
||||
<DiffChanges appearance="standard" changes={props.metadata.filediff!} />
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
@@ -2543,7 +2522,10 @@ ToolRegistry.register({
|
||||
</span>
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<DiffChanges changes={{ additions: file.additions, deletions: file.deletions }} />
|
||||
<DiffChanges
|
||||
appearance="standard"
|
||||
changes={{ additions: file.additions, deletions: file.deletions }}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
<Icon name="chevron-grabber-vertical" size="small" />
|
||||
@@ -2599,7 +2581,10 @@ ToolRegistry.register({
|
||||
</div>
|
||||
<div data-slot="message-part-actions">
|
||||
<Show when={!pending()}>
|
||||
<DiffChanges changes={{ additions: single()!.additions, deletions: single()!.deletions }} />
|
||||
<DiffChanges
|
||||
appearance="standard"
|
||||
changes={{ additions: single()!.additions, deletions: single()!.deletions }}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2625,7 +2610,10 @@ ToolRegistry.register({
|
||||
</span>
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<DiffChanges changes={{ additions: single()!.additions, deletions: single()!.deletions }} />
|
||||
<DiffChanges
|
||||
appearance="standard"
|
||||
changes={{ additions: single()!.additions, deletions: single()!.deletions }}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export function SessionRetry(props: { status: SessionStatus; show?: boolean }) {
|
||||
<Spinner class="size-4 mt-0.5" />
|
||||
<div class="min-w-0">
|
||||
<Show when={truncated()} fallback={<div data-slot="session-turn-retry-message">{message()}</div>}>
|
||||
<Tooltip value={retry()?.message ?? ""} placement="top">
|
||||
<Tooltip appearance="standard" value={retry()?.message ?? ""} placement="top">
|
||||
<div data-slot="session-turn-retry-message" class="cursor-help truncate">
|
||||
{message()}
|
||||
</div>
|
||||
|
||||
@@ -45,20 +45,6 @@
|
||||
column-gap: 12px;
|
||||
}
|
||||
|
||||
[data-slot="session-review-actions"] [data-component="radio-group"] {
|
||||
[data-slot="radio-group-wrapper"],
|
||||
[data-slot="radio-group-indicator"],
|
||||
[data-slot="radio-group-item-control"] {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
[data-slot="radio-group-item-input"]:not([data-checked], [data-disabled])
|
||||
+ [data-slot="radio-group-item-label"]:hover
|
||||
[data-slot="radio-group-item-control"] {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="sticky-accordion-header"] {
|
||||
--sticky-accordion-top: 0px;
|
||||
}
|
||||
@@ -237,3 +223,14 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="segmented-control-v2"].session-review-diff-style {
|
||||
width: fit-content;
|
||||
height: 24px;
|
||||
|
||||
[data-slot="segmented-control-v2-item"] {
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Accordion } from "@opencode-ai/ui/accordion"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
||||
import { RadioGroup } from "@opencode-ai/ui/radio-group"
|
||||
import { Menu } from "@opencode-ai/ui/menu"
|
||||
import { SegmentedControl, SegmentedControlItem } from "@opencode-ai/ui/segmented-control"
|
||||
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
@@ -127,26 +127,22 @@ function ReviewCommentMenu(props: {
|
||||
}) {
|
||||
return (
|
||||
<div onMouseDown={(event) => event.stopPropagation()} onClick={(event) => event.stopPropagation()}>
|
||||
<DropdownMenu gutter={4} placement="bottom-end">
|
||||
<DropdownMenu.Trigger
|
||||
<Menu appearance="standard" gutter={4} placement="bottom-end">
|
||||
<Menu.Trigger
|
||||
as={IconButton}
|
||||
icon="dot-grid"
|
||||
icon={<Icon name="dot-grid" />}
|
||||
variant="ghost"
|
||||
size="small"
|
||||
class="size-6 rounded-md"
|
||||
aria-label={props.labels.moreLabel}
|
||||
/>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.Item onSelect={props.onEdit}>
|
||||
<DropdownMenu.ItemLabel>{props.labels.editLabel}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onSelect={props.onDelete}>
|
||||
<DropdownMenu.ItemLabel>{props.labels.deleteLabel}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu>
|
||||
<Menu.Portal>
|
||||
<Menu.Content>
|
||||
<Menu.Item onSelect={props.onEdit}>{props.labels.editLabel}</Menu.Item>
|
||||
<Menu.Item onSelect={props.onDelete}>{props.labels.deleteLabel}</Menu.Item>
|
||||
</Menu.Content>
|
||||
</Menu.Portal>
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -345,16 +341,24 @@ export const SessionReview = (props: SessionReviewProps) => {
|
||||
</div>
|
||||
<div data-slot="session-review-actions">
|
||||
<Show when={hasDiffs() && props.onDiffStyleChange}>
|
||||
<RadioGroup
|
||||
options={["unified", "split"] as const}
|
||||
current={diffStyle()}
|
||||
size="small"
|
||||
value={(style) => style}
|
||||
label={(style) =>
|
||||
i18n.t(style === "unified" ? "ui.sessionReview.diffStyle.unified" : "ui.sessionReview.diffStyle.split")
|
||||
}
|
||||
onSelect={(style) => style && props.onDiffStyleChange?.(style)}
|
||||
/>
|
||||
<SegmentedControl
|
||||
class="session-review-diff-style"
|
||||
value={diffStyle()}
|
||||
onChange={(style) => {
|
||||
if (style !== "unified" && style !== "split") return
|
||||
props.onDiffStyleChange?.(style)
|
||||
}}
|
||||
>
|
||||
<For each={["unified", "split"] as const}>
|
||||
{(style) => (
|
||||
<SegmentedControlItem value={style}>
|
||||
{i18n.t(
|
||||
style === "unified" ? "ui.sessionReview.diffStyle.unified" : "ui.sessionReview.diffStyle.split",
|
||||
)}
|
||||
</SegmentedControlItem>
|
||||
)}
|
||||
</For>
|
||||
</SegmentedControl>
|
||||
</Show>
|
||||
<Show when={hasDiffs()}>
|
||||
<Button
|
||||
@@ -521,7 +525,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
||||
</Show>
|
||||
<span data-slot="session-review-filename">{getFilename(file)}</span>
|
||||
<Show when={props.onViewFile && diffCanRender()}>
|
||||
<Tooltip value={openFileLabel()} placement="top" gutter={4}>
|
||||
<Tooltip appearance="standard" value={openFileLabel()} placement="top" gutter={4}>
|
||||
<button
|
||||
data-slot="session-review-view-button"
|
||||
type="button"
|
||||
@@ -544,7 +548,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
||||
<span data-slot="session-review-change" data-type="added">
|
||||
{i18n.t("ui.sessionReview.change.added")}
|
||||
</span>
|
||||
<DiffChanges changes={diff()} />
|
||||
<DiffChanges appearance="standard" changes={diff()} />
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={isDeleted()}>
|
||||
@@ -558,7 +562,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
||||
</span>
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<DiffChanges changes={diff()} />
|
||||
<DiffChanges appearance="standard" changes={diff()} />
|
||||
</Match>
|
||||
</Switch>
|
||||
<Show when={diffCanRender()}>
|
||||
@@ -602,7 +606,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
||||
<div data-slot="session-review-large-diff-actions">
|
||||
<Button
|
||||
size="normal"
|
||||
variant="secondary"
|
||||
variant="neutral"
|
||||
onClick={() => setStore("force", file, true)}
|
||||
>
|
||||
{i18n.t("ui.sessionReview.largeDiff.renderAnyway")}
|
||||
|
||||
@@ -437,7 +437,7 @@ export function SessionTurn(
|
||||
<span data-slot="session-turn-diffs-label">
|
||||
{i18n.plural("ui.sessionTurn.diffs.changed", edited())}
|
||||
</span>
|
||||
<DiffChanges changes={diffs()} />
|
||||
<DiffChanges appearance="standard" changes={diffs()} />
|
||||
<Show when={overflow() > 0}>
|
||||
<span data-slot="session-turn-diffs-toggle" onClick={toggleAll}>
|
||||
{showAll() ? i18n.t("ui.sessionTurn.diffs.showLess") : i18n.t("ui.sessionTurn.diffs.showAll")}
|
||||
@@ -490,7 +490,7 @@ export function SessionTurn(
|
||||
</span>
|
||||
<div data-slot="session-turn-diff-meta">
|
||||
<span data-slot="session-turn-diff-changes">
|
||||
<DiffChanges changes={diff} />
|
||||
<DiffChanges appearance="standard" changes={diff} />
|
||||
</span>
|
||||
<span data-slot="session-turn-diff-chevron">
|
||||
<Icon name="chevron-down" size="small" />
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
[data-component="tool-error-card-icon"] [data-component="icon"] {
|
||||
[data-component="tool-error-card-icon"] [data-slot="icon-svg"] {
|
||||
color: var(--card-accent);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,12 +136,13 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||
<Show when={open()}>
|
||||
<div data-slot="tool-error-card-copy">
|
||||
<Tooltip
|
||||
appearance="standard"
|
||||
value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.toolErrorCard.copyError")}
|
||||
placement="top"
|
||||
gutter={4}
|
||||
>
|
||||
<IconButton
|
||||
icon={copied() ? "check" : "copy"}
|
||||
icon={<Icon name={copied() ? "check" : "copy"} />}
|
||||
size="normal"
|
||||
variant="ghost"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Collapsible } from "@kobalte/core/collapsible"
|
||||
import { type ComponentProps, type JSX, For, Show, createMemo, splitProps } from "solid-js"
|
||||
import { DiffChanges } from "@opencode-ai/ui/v2/diff-changes-v2"
|
||||
import { TextShimmerV2 } from "@opencode-ai/ui/v2/text-shimmer-v2"
|
||||
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
||||
import { TextShimmer } from "@opencode-ai/ui/text-shimmer"
|
||||
import "./basic-tool-v2.css"
|
||||
|
||||
function ChevronIcon() {
|
||||
@@ -91,7 +91,7 @@ export function BasicToolV2(props: BasicToolV2Props) {
|
||||
{(title) => (
|
||||
<>
|
||||
<span data-slot="basic-tool-v2-title">
|
||||
<TextShimmerV2 text={title().title} active={pending()} />
|
||||
<TextShimmer text={title().title} active={pending()} />
|
||||
</span>
|
||||
<Show when={!pending() && title().subtitle}>
|
||||
<span data-slot="basic-tool-v2-sep" aria-hidden="true">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createSignal, onCleanup, onMount, Show } from "solid-js"
|
||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||
import { getFilenameTruncated } from "@opencode-ai/util/path"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||
import { AttachmentCardV2 } from "./attachment-card-v2"
|
||||
|
||||
export function CommentCardV2(props: {
|
||||
@@ -30,7 +30,7 @@ export function CommentCardV2(props: {
|
||||
})
|
||||
|
||||
return (
|
||||
<TooltipV2
|
||||
<Tooltip
|
||||
placement="top"
|
||||
openDelay={1000}
|
||||
value={props.title ?? props.comment}
|
||||
@@ -59,6 +59,6 @@ export function CommentCardV2(props: {
|
||||
</Show>
|
||||
</span>
|
||||
</AttachmentCardV2>
|
||||
</TooltipV2>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ import {
|
||||
} from "../../components/line-comment-annotations"
|
||||
import { useI18n } from "@opencode-ai/ui/context/i18n"
|
||||
import { cloneSelectedLineRange, formatSelectedLineLabel } from "../../pierre/selection-bridge"
|
||||
import { LineCommentEditorV2, LineCommentV2 } from "@opencode-ai/ui/v2/line-comment-v2"
|
||||
import type { LineCommentEditorV2Mention } from "@opencode-ai/ui/v2/line-comment-v2"
|
||||
import { LineCommentEditor, LineComment, type LineCommentEditorMention } from "@opencode-ai/ui/line-comment"
|
||||
|
||||
type LineCommentControllerV2Props<T extends LineCommentShape> = {
|
||||
comments: Accessor<T[]>
|
||||
@@ -24,7 +23,7 @@ type LineCommentControllerV2Props<T extends LineCommentShape> = {
|
||||
onDelete?: (comment: T) => void
|
||||
renderCommentActions?: (comment: T, controls: { edit: VoidFunction; remove: VoidFunction }) => JSX.Element
|
||||
editSubmitLabel?: string
|
||||
mention?: LineCommentEditorV2Mention
|
||||
mention?: LineCommentEditorMention
|
||||
}
|
||||
|
||||
type CommentProps = {
|
||||
@@ -45,7 +44,7 @@ type DraftProps = {
|
||||
onSubmit: (value: string) => void
|
||||
cancelLabel?: string
|
||||
submitLabel?: string
|
||||
mention?: LineCommentEditorV2Mention
|
||||
mention?: LineCommentEditorMention
|
||||
}
|
||||
|
||||
function lineCommentElementV2(view: Accessor<CommentProps>) {
|
||||
@@ -60,12 +59,12 @@ function lineCommentElementV2(view: Accessor<CommentProps>) {
|
||||
onClick={view().onClick}
|
||||
onMouseEnter={view().onMouseEnter}
|
||||
>
|
||||
<LineCommentV2 comment={view().comment} selection={view().selection} actions={view().actions} />
|
||||
<LineComment comment={view().comment} selection={view().selection} actions={view().actions} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div data-prevent-autofocus="" data-comment-id={view().id} onMouseDown={(event) => event.stopPropagation()}>
|
||||
<LineCommentEditorV2
|
||||
<LineCommentEditor
|
||||
value={view().editor!.value}
|
||||
selection={view().editor!.selection}
|
||||
onInput={view().editor!.onInput}
|
||||
@@ -83,7 +82,7 @@ function lineCommentElementV2(view: Accessor<CommentProps>) {
|
||||
function lineCommentDraftElementV2(view: Accessor<DraftProps>) {
|
||||
return (
|
||||
<div data-prevent-autofocus="" onMouseDown={(event) => event.stopPropagation()}>
|
||||
<LineCommentEditorV2
|
||||
<LineCommentEditor
|
||||
value={view().value}
|
||||
selection={view().selection}
|
||||
onInput={view().onInput}
|
||||
|
||||
@@ -4,12 +4,10 @@ import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
||||
import { useI18n } from "@opencode-ai/ui/context/i18n"
|
||||
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
|
||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
|
||||
import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { Keybind } from "@opencode-ai/ui/keybind"
|
||||
import { Menu } from "@opencode-ai/ui/menu"
|
||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||
import { AttachmentCardV2 } from "../attachment-card-v2"
|
||||
import { CommentCardV2 } from "../comment-card-v2"
|
||||
import { typeLabel } from "../../../components/message-file"
|
||||
@@ -403,7 +401,7 @@ export function PromptInputV2Attachments(props: {
|
||||
<For each={props.comments ?? []}>
|
||||
{(comment) => (
|
||||
<div class="relative group shrink-0">
|
||||
<TooltipV2
|
||||
<Tooltip
|
||||
value={comment.comment}
|
||||
placement="top"
|
||||
openDelay={800}
|
||||
@@ -416,14 +414,14 @@ export function PromptInputV2Attachments(props: {
|
||||
active={comment.key === props.activeCommentID}
|
||||
onClick={() => props.onCommentClick?.(comment)}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Tooltip>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => props.onCommentRemove?.(comment)}
|
||||
class="absolute -top-1 -end-1 size-4 rounded-full bg-v2-icon-icon-muted outline-solid outline-1 outline-v2-icon-icon-contrast flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
aria-label={props.removeLabel}
|
||||
>
|
||||
<IconV2 name="outline-xmark" class="text-v2-icon-icon-contrast" />
|
||||
<Icon name="outline-xmark" class="text-v2-icon-icon-contrast" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -431,7 +429,7 @@ export function PromptInputV2Attachments(props: {
|
||||
<For each={props.attachments}>
|
||||
{(attachment) => (
|
||||
<div class="relative group shrink-0">
|
||||
<TooltipV2 value={attachment.filename} placement="top" contentClass="break-all">
|
||||
<Tooltip value={attachment.filename} placement="top" contentClass="break-all">
|
||||
<Show
|
||||
when={attachment.mime.startsWith("image/")}
|
||||
fallback={
|
||||
@@ -448,14 +446,14 @@ export function PromptInputV2Attachments(props: {
|
||||
/>
|
||||
<div class="absolute inset-0 rounded-[6px] shadow-[inset_0_0_0_0.5px_var(--v2-border-border-base)] pointer-events-none" />
|
||||
</Show>
|
||||
</TooltipV2>
|
||||
</Tooltip>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => props.onAttachmentRemove(attachment)}
|
||||
class="absolute -top-1 -end-1 size-4 rounded-full bg-v2-icon-icon-muted outline-solid outline-1 outline-v2-icon-icon-contrast flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
aria-label={props.removeLabel}
|
||||
>
|
||||
<IconV2 name="outline-xmark" class="text-v2-icon-icon-contrast" />
|
||||
<Icon name="outline-xmark" class="text-v2-icon-icon-contrast" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -489,45 +487,45 @@ export function PromptInputV2AddMenu(props: {
|
||||
onShell: () => void
|
||||
}) {
|
||||
return (
|
||||
<TooltipV2
|
||||
<Tooltip
|
||||
placement="top"
|
||||
value={
|
||||
<>
|
||||
{props.title}
|
||||
<KeybindV2 keys={props.keybind ?? []} variant="neutral" />
|
||||
<Keybind keys={props.keybind ?? []} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<MenuV2 gutter={6} modal={false} placement="top-start">
|
||||
<MenuV2.Trigger
|
||||
as={IconButtonV2}
|
||||
<Menu gutter={6} modal={false} placement="top-start">
|
||||
<Menu.Trigger
|
||||
as={IconButton}
|
||||
data-action="prompt-attach"
|
||||
type="button"
|
||||
icon={<IconV2 name="plus" />}
|
||||
icon={<Icon name="plus" />}
|
||||
variant="ghost-muted"
|
||||
size="large"
|
||||
disabled={props.disabled}
|
||||
aria-label={props.title}
|
||||
/>
|
||||
<MenuV2.Portal>
|
||||
<MenuV2.Content style={{ "min-width": "180px" }}>
|
||||
<MenuV2.Item onSelect={props.onAttach} shortcut={props.attachShortcut}>
|
||||
<Menu.Portal>
|
||||
<Menu.Content style={{ "min-width": "180px" }}>
|
||||
<Menu.Item onSelect={props.onAttach} shortcut={props.attachShortcut}>
|
||||
{props.attachLabel}
|
||||
</MenuV2.Item>
|
||||
<MenuV2.Separator />
|
||||
<MenuV2.Item onSelect={props.onCommands} shortcut="/">
|
||||
</Menu.Item>
|
||||
<Menu.Separator />
|
||||
<Menu.Item onSelect={props.onCommands} shortcut="/">
|
||||
{props.commandsLabel}
|
||||
</MenuV2.Item>
|
||||
<MenuV2.Item onSelect={props.onContext} shortcut="@">
|
||||
</Menu.Item>
|
||||
<Menu.Item onSelect={props.onContext} shortcut="@">
|
||||
{props.contextLabel}
|
||||
</MenuV2.Item>
|
||||
<MenuV2.Item onSelect={props.onShell} shortcut="!">
|
||||
</Menu.Item>
|
||||
<Menu.Item onSelect={props.onShell} shortcut="!">
|
||||
{props.shellLabel}
|
||||
</MenuV2.Item>
|
||||
</MenuV2.Content>
|
||||
</MenuV2.Portal>
|
||||
</MenuV2>
|
||||
</TooltipV2>
|
||||
</Menu.Item>
|
||||
</Menu.Content>
|
||||
</Menu.Portal>
|
||||
</Menu>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -566,18 +564,18 @@ export function PromptInputV2Select(props: {
|
||||
onSelect: (id: string) => void
|
||||
}) {
|
||||
return (
|
||||
<TooltipV2
|
||||
<Tooltip
|
||||
placement="top"
|
||||
value={
|
||||
<>
|
||||
{props.title}
|
||||
<KeybindV2 keys={props.keybind ?? []} variant="neutral" />
|
||||
<Keybind keys={props.keybind ?? []} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<MenuV2 gutter={6} modal={false} placement="top-start" onOpenChange={props.onOpenChange}>
|
||||
<MenuV2.Trigger
|
||||
as={ButtonV2}
|
||||
<Menu gutter={6} modal={false} placement="top-start" onOpenChange={props.onOpenChange}>
|
||||
<Menu.Trigger
|
||||
as={Button}
|
||||
variant="ghost-muted"
|
||||
size="normal"
|
||||
class={`max-w-[220px] justify-start ![font-weight:440] ${props.class ?? ""}`}
|
||||
@@ -588,24 +586,24 @@ export function PromptInputV2Select(props: {
|
||||
{props.options.find((option) => option.id === props.current)?.label ?? props.current}
|
||||
</span>
|
||||
<span class="-ms-0.5 -me-1 flex shrink-0">
|
||||
<IconV2 name="chevron-down" />
|
||||
<Icon name="chevron-down" />
|
||||
</span>
|
||||
</MenuV2.Trigger>
|
||||
<MenuV2.Portal>
|
||||
<MenuV2.Content>
|
||||
<MenuV2.RadioGroup value={props.current} onChange={props.onSelect}>
|
||||
</Menu.Trigger>
|
||||
<Menu.Portal>
|
||||
<Menu.Content>
|
||||
<Menu.RadioGroup value={props.current} onChange={props.onSelect}>
|
||||
<For each={props.options}>
|
||||
{(option) => (
|
||||
<MenuV2.RadioItem value={option.id} class="capitalize" closeOnSelect>
|
||||
<Menu.RadioItem value={option.id} class="capitalize" closeOnSelect>
|
||||
{option.label}
|
||||
</MenuV2.RadioItem>
|
||||
</Menu.RadioItem>
|
||||
)}
|
||||
</For>
|
||||
</MenuV2.RadioGroup>
|
||||
</MenuV2.Content>
|
||||
</MenuV2.Portal>
|
||||
</MenuV2>
|
||||
</TooltipV2>
|
||||
</Menu.RadioGroup>
|
||||
</Menu.Content>
|
||||
</Menu.Portal>
|
||||
</Menu>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -687,7 +685,7 @@ export function PromptInputV2SubmitButton(props: {
|
||||
onStop: () => void
|
||||
}) {
|
||||
return (
|
||||
<TooltipV2
|
||||
<Tooltip
|
||||
placement="top"
|
||||
inactive={!props.stopping && props.disabled}
|
||||
value={props.stopping ? props.stopLabel : props.sendLabel}
|
||||
@@ -697,8 +695,8 @@ export function PromptInputV2SubmitButton(props: {
|
||||
type="button"
|
||||
disabled={!props.stopping && props.disabled}
|
||||
tabIndex={props.mode === "normal" ? undefined : -1}
|
||||
icon={props.stopping ? "stop" : props.mode === "shell" ? "arrow-undo-down" : "arrow-up"}
|
||||
variant="primary"
|
||||
icon={<Icon name={props.stopping ? "stop" : props.mode === "shell" ? "arrow-undo-down" : "arrow-up"} />}
|
||||
variant="contrast"
|
||||
class="size-7 rounded-md p-[6px] shadow-[var(--v2-elevation-button-contrast)] disabled:opacity-50"
|
||||
classList={{
|
||||
"text-v2-text-text-contrast": !!props.accent && !props.stopping && !props.disabled,
|
||||
@@ -721,7 +719,7 @@ export function PromptInputV2SubmitButton(props: {
|
||||
props.onSubmit()
|
||||
}}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useI18n } from "@opencode-ai/ui/context/i18n"
|
||||
import { Icon } from "@opencode-ai/ui/v2/icon"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import "./session-review-v2.css"
|
||||
|
||||
export function SessionReviewEmptyChangesV2() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||
import { useI18n } from "@opencode-ai/ui/context/i18n"
|
||||
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import "./session-review-v2.css"
|
||||
|
||||
export type SessionReviewEmptyNoGitV2Props = {
|
||||
@@ -18,11 +18,11 @@ export function SessionReviewEmptyNoGitV2(props: SessionReviewEmptyNoGitV2Props)
|
||||
<div data-slot="session-review-v2-empty-no-git-description">
|
||||
{i18n.t("ui.sessionReviewV2.empty.noGit.description")}
|
||||
</div>
|
||||
<ButtonV2 variant="neutral" size="normal" disabled={props.pending} onClick={props.onInitGit}>
|
||||
<Button variant="neutral" size="normal" disabled={props.pending} onClick={props.onInitGit}>
|
||||
{props.pending
|
||||
? i18n.t("ui.sessionReviewV2.empty.noGit.actionLoading")
|
||||
: i18n.t("ui.sessionReviewV2.empty.noGit.action")}
|
||||
</ButtonV2>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
||||
import type { SelectedLineRange } from "@pierre/diffs"
|
||||
import { DiffChanges } from "@opencode-ai/ui/v2/diff-changes-v2"
|
||||
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||
import { useFileComponent } from "@opencode-ai/ui/context/file"
|
||||
import { useI18n } from "@opencode-ai/ui/context/i18n"
|
||||
@@ -24,8 +24,8 @@ import type {
|
||||
import type { SessionReviewExpandMode } from "./session-review-v2"
|
||||
import { createLineCommentControllerV2 } from "./line-comment-annotations-v2"
|
||||
import { shouldVirtualizeReviewDiff } from "./session-review-file-preview-v2-virtualize"
|
||||
import { LineCommentV2OverflowIcon } from "@opencode-ai/ui/v2/line-comment-v2"
|
||||
import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2"
|
||||
import { LineCommentOverflowIcon } from "@opencode-ai/ui/line-comment"
|
||||
import { Menu } from "@opencode-ai/ui/menu"
|
||||
import "./session-review-v2.css"
|
||||
|
||||
type ReviewDiff = (PresentationFileDiff & { file: string }) | FileDiffInfo
|
||||
@@ -68,29 +68,29 @@ function selectionPreview(diff: ViewDiff, range: SelectedLineRange) {
|
||||
return previewSelectedLines(contents, range)
|
||||
}
|
||||
|
||||
function ReviewCommentMenuV2(props: {
|
||||
function ReviewCommentMenu(props: {
|
||||
labels: SessionReviewCommentActions
|
||||
onEdit: VoidFunction
|
||||
onDelete: VoidFunction
|
||||
}) {
|
||||
return (
|
||||
<div onMouseDown={(event) => event.stopPropagation()} onClick={(event) => event.stopPropagation()}>
|
||||
<MenuV2 gutter={4}>
|
||||
<MenuV2.Trigger
|
||||
<Menu gutter={4}>
|
||||
<Menu.Trigger
|
||||
as="button"
|
||||
type="button"
|
||||
data-slot="line-comment-v2-overflow"
|
||||
aria-label={props.labels.moreLabel}
|
||||
>
|
||||
<LineCommentV2OverflowIcon />
|
||||
</MenuV2.Trigger>
|
||||
<MenuV2.Portal>
|
||||
<MenuV2.Content>
|
||||
<MenuV2.Item onSelect={props.onEdit}>{props.labels.editLabel}</MenuV2.Item>
|
||||
<MenuV2.Item onSelect={props.onDelete}>{props.labels.deleteLabel}</MenuV2.Item>
|
||||
</MenuV2.Content>
|
||||
</MenuV2.Portal>
|
||||
</MenuV2>
|
||||
<LineCommentOverflowIcon />
|
||||
</Menu.Trigger>
|
||||
<Menu.Portal>
|
||||
<Menu.Content>
|
||||
<Menu.Item onSelect={props.onEdit}>{props.labels.editLabel}</Menu.Item>
|
||||
<Menu.Item onSelect={props.onDelete}>{props.labels.deleteLabel}</Menu.Item>
|
||||
</Menu.Content>
|
||||
</Menu.Portal>
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -156,7 +156,7 @@ export function SessionReviewFilePreviewV2(props: SessionReviewFilePreviewV2Prop
|
||||
editSubmitLabel: props.lineCommentActions?.saveLabel,
|
||||
renderCommentActions: props.lineCommentActions
|
||||
? (comment, controls) => (
|
||||
<ReviewCommentMenuV2 labels={props.lineCommentActions!} onEdit={controls.edit} onDelete={controls.remove} />
|
||||
<ReviewCommentMenu labels={props.lineCommentActions!} onEdit={controls.edit} onDelete={controls.remove} />
|
||||
)
|
||||
: undefined,
|
||||
})
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { useI18n } from "@opencode-ai/ui/context/i18n"
|
||||
import { SegmentedControlItemV2, SegmentedControlV2 } from "@opencode-ai/ui/v2/segmented-control-v2"
|
||||
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
|
||||
import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
|
||||
import { Icon } from "@opencode-ai/ui/v2/icon"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { SegmentedControlItem, SegmentedControl } from "@opencode-ai/ui/segmented-control"
|
||||
import { TextInput } from "@opencode-ai/ui/text-input"
|
||||
import { Keybind } from "@opencode-ai/ui/keybind"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||
import type { SessionReviewDiffStyle } from "../../components/session-review"
|
||||
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
||||
import { ScrollView } from "@opencode-ai/ui/scroll-view"
|
||||
@@ -87,7 +86,7 @@ export function SessionReviewV2Sidebar(props: SessionReviewV2SidebarProps) {
|
||||
{props.stats}
|
||||
</div>
|
||||
<div data-slot="session-review-v2-sidebar-filter">
|
||||
<TextInputV2
|
||||
<TextInput
|
||||
type="search"
|
||||
value={props.filter}
|
||||
onInput={(event) => props.onFilterChange(event.currentTarget.value)}
|
||||
@@ -219,18 +218,18 @@ export function SessionReviewV2(props: SessionReviewV2Props) {
|
||||
</div>
|
||||
</Show>
|
||||
<div class="flex items-center">
|
||||
<TooltipV2
|
||||
<Tooltip
|
||||
openDelay={2000}
|
||||
inactive={!prev()}
|
||||
value={
|
||||
<>
|
||||
{i18n.t("ui.sessionReviewV2.previousFile")}
|
||||
<KeybindV2 keys={[locale.direction() === "rtl" ? "→" : "←"]} variant="neutral" />
|
||||
<Keybind keys={[locale.direction() === "rtl" ? "→" : "←"]} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
icon="arrow-left"
|
||||
icon={<Icon name="arrow-left" />}
|
||||
variant="ghost"
|
||||
size="small"
|
||||
class="session-review-v2-file-nav-button"
|
||||
@@ -238,19 +237,19 @@ export function SessionReviewV2(props: SessionReviewV2Props) {
|
||||
onClick={() => cycle(prev())}
|
||||
aria-label={i18n.t("ui.sessionReviewV2.previousFile")}
|
||||
/>
|
||||
</TooltipV2>
|
||||
<TooltipV2
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
openDelay={2000}
|
||||
inactive={!next()}
|
||||
value={
|
||||
<>
|
||||
{i18n.t("ui.sessionReviewV2.nextFile")}
|
||||
<KeybindV2 keys={[locale.direction() === "rtl" ? "←" : "→"]} variant="neutral" />
|
||||
<Keybind keys={[locale.direction() === "rtl" ? "←" : "→"]} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
icon="arrow-right"
|
||||
icon={<Icon name="arrow-right" />}
|
||||
variant="ghost"
|
||||
size="small"
|
||||
class="session-review-v2-file-nav-button"
|
||||
@@ -258,14 +257,14 @@ export function SessionReviewV2(props: SessionReviewV2Props) {
|
||||
onClick={() => cycle(next())}
|
||||
aria-label={i18n.t("ui.sessionReviewV2.nextFile")}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
const toolbarEnd = () => (
|
||||
<>
|
||||
<SegmentedControlV2
|
||||
<SegmentedControl
|
||||
value={props.expandMode}
|
||||
onChange={(value) => {
|
||||
if (value !== "expand" && value !== "collapse") return
|
||||
@@ -274,19 +273,19 @@ export function SessionReviewV2(props: SessionReviewV2Props) {
|
||||
class="session-review-v2-segmented-control session-review-v2-segmented-control--icon"
|
||||
aria-label={i18n.t("ui.sessionReviewV2.expandMode")}
|
||||
>
|
||||
<TooltipV2 openDelay={2000} value={i18n.t("ui.sessionReviewV2.showAllLines")}>
|
||||
<SegmentedControlItemV2 value="expand" aria-label={i18n.t("ui.sessionReviewV2.showAllLines")}>
|
||||
<Tooltip openDelay={2000} value={i18n.t("ui.sessionReviewV2.showAllLines")}>
|
||||
<SegmentedControlItem value="expand" aria-label={i18n.t("ui.sessionReviewV2.showAllLines")}>
|
||||
<Icon name="expand" />
|
||||
</SegmentedControlItemV2>
|
||||
</TooltipV2>
|
||||
<TooltipV2 openDelay={2000} value={i18n.t("ui.sessionReviewV2.hideNonDiffLines")}>
|
||||
<SegmentedControlItemV2 value="collapse" aria-label={i18n.t("ui.sessionReviewV2.hideNonDiffLines")}>
|
||||
</SegmentedControlItem>
|
||||
</Tooltip>
|
||||
<Tooltip openDelay={2000} value={i18n.t("ui.sessionReviewV2.hideNonDiffLines")}>
|
||||
<SegmentedControlItem value="collapse" aria-label={i18n.t("ui.sessionReviewV2.hideNonDiffLines")}>
|
||||
<Icon name="collapse" />
|
||||
</SegmentedControlItemV2>
|
||||
</TooltipV2>
|
||||
</SegmentedControlV2>
|
||||
</SegmentedControlItem>
|
||||
</Tooltip>
|
||||
</SegmentedControl>
|
||||
<Show when={props.onDiffStyleChange}>
|
||||
<SegmentedControlV2
|
||||
<SegmentedControl
|
||||
value={props.diffStyle}
|
||||
onChange={(value) => {
|
||||
if (value !== "unified" && value !== "split") return
|
||||
@@ -295,17 +294,17 @@ export function SessionReviewV2(props: SessionReviewV2Props) {
|
||||
class="session-review-v2-segmented-control session-review-v2-segmented-control--icon"
|
||||
aria-label={i18n.t("ui.sessionReviewV2.diffView")}
|
||||
>
|
||||
<TooltipV2 openDelay={2000} value={i18n.t("ui.sessionReviewV2.unifiedDiff")}>
|
||||
<SegmentedControlItemV2 value="unified" aria-label={i18n.t("ui.sessionReviewV2.unifiedDiff")}>
|
||||
<Tooltip openDelay={2000} value={i18n.t("ui.sessionReviewV2.unifiedDiff")}>
|
||||
<SegmentedControlItem value="unified" aria-label={i18n.t("ui.sessionReviewV2.unifiedDiff")}>
|
||||
<Icon name="unified" />
|
||||
</SegmentedControlItemV2>
|
||||
</TooltipV2>
|
||||
<TooltipV2 openDelay={2000} value={i18n.t("ui.sessionReviewV2.splitDiff")}>
|
||||
<SegmentedControlItemV2 value="split" aria-label={i18n.t("ui.sessionReviewV2.splitDiff")}>
|
||||
</SegmentedControlItem>
|
||||
</Tooltip>
|
||||
<Tooltip openDelay={2000} value={i18n.t("ui.sessionReviewV2.splitDiff")}>
|
||||
<SegmentedControlItem value="split" aria-label={i18n.t("ui.sessionReviewV2.splitDiff")}>
|
||||
<Icon name="split" />
|
||||
</SegmentedControlItemV2>
|
||||
</TooltipV2>
|
||||
</SegmentedControlV2>
|
||||
</SegmentedControlItem>
|
||||
</Tooltip>
|
||||
</SegmentedControl>
|
||||
</Show>
|
||||
</>
|
||||
)
|
||||
@@ -330,8 +329,8 @@ export function SessionReviewV2SidebarToggle(props: { opened: boolean; disabled?
|
||||
const i18n = useI18n()
|
||||
|
||||
return (
|
||||
<TooltipV2 value={i18n.t("ui.sessionReviewV2.toggleSidebar")}>
|
||||
<IconButtonV2
|
||||
<Tooltip value={i18n.t("ui.sessionReviewV2.toggleSidebar")}>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="small"
|
||||
class="session-review-v2-sidebar-toggle"
|
||||
@@ -342,6 +341,6 @@ export function SessionReviewV2SidebarToggle(props: { opened: boolean; disabled?
|
||||
onClick={props.onToggle}
|
||||
icon={<Icon name="filetree" />}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createSignal } from "solid-js"
|
||||
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { ToolErrorCardV2, type ToolErrorCardV2Props } from "./tool-error-card-v2"
|
||||
|
||||
const docs = `### Overview
|
||||
@@ -75,9 +75,9 @@ export const Controlled = {
|
||||
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)}>
|
||||
<Button type="button" classList={{ "w-fit": true }} onClick={() => setOpen((o) => !o)}>
|
||||
Toggle from outside: {open() ? "Open" : "Closed"}
|
||||
</ButtonV2>
|
||||
</Button>
|
||||
<ToolErrorCardV2
|
||||
title="Grep"
|
||||
subtitle="Timeout"
|
||||
|
||||
Reference in New Issue
Block a user