summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoradamelmore <[email protected]>2026-01-27 08:28:55 -0600
committeradamelmore <[email protected]>2026-01-27 08:43:37 -0600
commit099ab929dbfcbe9e167e1afd46487e039e46b1c2 (patch)
tree28540d7a1dd0d4fe480b48668fad359ba75d817a
parenteac2d4c6999f79085ac72dd4812125674030ef16 (diff)
downloadopencode-099ab929dbfcbe9e167e1afd46487e039e46b1c2.tar.gz
opencode-099ab929dbfcbe9e167e1afd46487e039e46b1c2.zip
chore(app): cleanup tailwind vs pure css
-rw-r--r--packages/app/src/components/dialog-edit-project.tsx39
-rw-r--r--packages/app/src/components/dialog-fork.tsx8
-rw-r--r--packages/app/src/components/dialog-release-notes.tsx134
-rw-r--r--packages/app/src/components/dialog-select-server.tsx4
-rw-r--r--packages/app/src/components/prompt-input.tsx10
-rw-r--r--packages/app/src/components/session/session-new-view.tsx5
-rw-r--r--packages/app/src/components/session/session-sortable-terminal-tab.tsx4
-rw-r--r--packages/app/src/components/settings-general.tsx10
-rw-r--r--packages/app/src/components/settings-keybinds.tsx10
-rw-r--r--packages/app/src/components/settings-models.tsx10
-rw-r--r--packages/app/src/components/settings-permissions.tsx8
-rw-r--r--packages/app/src/components/settings-providers.tsx2
-rw-r--r--packages/app/src/components/status-popover.tsx23
-rw-r--r--packages/app/src/index.css83
-rw-r--r--packages/app/src/pages/layout.tsx13
-rw-r--r--packages/ui/src/components/avatar.css8
-rw-r--r--packages/ui/src/components/avatar.tsx2
-rw-r--r--packages/ui/src/components/message-part.css8
-rw-r--r--packages/ui/src/components/message-part.tsx15
-rw-r--r--packages/ui/src/components/session-turn.css4
-rw-r--r--packages/ui/src/components/session-turn.tsx2
-rw-r--r--packages/ui/src/components/tabs.tsx2
-rw-r--r--packages/ui/src/styles/tailwind/utilities.css33
23 files changed, 158 insertions, 279 deletions
diff --git a/packages/app/src/components/dialog-edit-project.tsx b/packages/app/src/components/dialog-edit-project.tsx
index 2ab25ceeb..490753e62 100644
--- a/packages/app/src/components/dialog-edit-project.tsx
+++ b/packages/app/src/components/dialog-edit-project.tsx
@@ -145,8 +145,7 @@ export function DialogEditProject(props: { project: LocalProject }) {
<Avatar
fallback={store.name || defaultName()}
{...getAvatarColors(store.color)}
- class="size-full"
- style={{ "font-size": "32px" }}
+ class="size-full text-[32px]"
/>
</div>
}
@@ -159,39 +158,19 @@ export function DialogEditProject(props: { project: LocalProject }) {
</Show>
</div>
<div
- style={{
- position: "absolute",
- top: 0,
- left: 0,
- width: "64px",
- height: "64px",
- background: "rgba(0,0,0,0.6)",
- "border-radius": "6px",
- "z-index": 10,
- "pointer-events": "none",
- opacity: store.iconHover && !store.iconUrl ? 1 : 0,
- display: "flex",
- "align-items": "center",
- "justify-content": "center",
+ class="absolute inset-0 size-16 bg-black/60 rounded-[6px] z-10 pointer-events-none flex items-center justify-center transition-opacity"
+ classList={{
+ "opacity-100": store.iconHover && !store.iconUrl,
+ "opacity-0": !(store.iconHover && !store.iconUrl),
}}
>
<Icon name="cloud-upload" size="large" class="text-icon-invert-base" />
</div>
<div
- style={{
- position: "absolute",
- top: 0,
- left: 0,
- width: "64px",
- height: "64px",
- background: "rgba(0,0,0,0.6)",
- "border-radius": "6px",
- "z-index": 10,
- "pointer-events": "none",
- opacity: store.iconHover && store.iconUrl ? 1 : 0,
- display: "flex",
- "align-items": "center",
- "justify-content": "center",
+ class="absolute inset-0 size-16 bg-black/60 rounded-[6px] z-10 pointer-events-none flex items-center justify-center transition-opacity"
+ classList={{
+ "opacity-100": store.iconHover && !!store.iconUrl,
+ "opacity-0": !(store.iconHover && !!store.iconUrl),
}}
>
<Icon name="trash" size="large" class="text-icon-invert-base" />
diff --git a/packages/app/src/components/dialog-fork.tsx b/packages/app/src/components/dialog-fork.tsx
index 17782f5ab..09d62021f 100644
--- a/packages/app/src/components/dialog-fork.tsx
+++ b/packages/app/src/components/dialog-fork.tsx
@@ -90,12 +90,8 @@ export const DialogFork: Component = () => {
>
{(item) => (
<div class="w-full flex items-center gap-2">
- <span class="truncate flex-1 min-w-0 text-left" style={{ "font-weight": "400" }}>
- {item.text}
- </span>
- <span class="text-text-weak shrink-0" style={{ "font-weight": "400" }}>
- {item.time}
- </span>
+ <span class="truncate flex-1 min-w-0 text-left font-normal">{item.text}</span>
+ <span class="text-text-weak shrink-0 font-normal">{item.time}</span>
</div>
)}
</List>
diff --git a/packages/app/src/components/dialog-release-notes.tsx b/packages/app/src/components/dialog-release-notes.tsx
index d3ee7e201..c6f2f3930 100644
--- a/packages/app/src/components/dialog-release-notes.tsx
+++ b/packages/app/src/components/dialog-release-notes.tsx
@@ -73,80 +73,86 @@ export function DialogReleaseNotes(props: { highlights: Highlight[] }) {
})
return (
- <Dialog class="dialog-release-notes">
+ <Dialog
+ size="large"
+ fit
+ class="w-[min(calc(100vw-40px),720px)] h-[min(calc(100vh-40px),400px)] -mt-20 min-h-0 overflow-hidden"
+ >
{/* Hidden element to capture initial focus and handle escape */}
<div ref={focusTrap} tabindex="0" class="absolute opacity-0 pointer-events-none" />
- {/* Left side - Text content */}
- <div class="flex flex-col flex-1 min-w-0 p-8">
- {/* Top section - feature content (fixed position from top) */}
- <div class="flex flex-col gap-2 pt-22">
- <div class="flex items-center gap-2">
- <h1 class="text-16-medium text-text-strong">{feature()?.title ?? ""}</h1>
+ <div class="flex flex-1 min-w-0 min-h-0">
+ {/* Left side - Text content */}
+ <div class="flex flex-col flex-1 min-w-0 p-8">
+ {/* Top section - feature content (fixed position from top) */}
+ <div class="flex flex-col gap-2 pt-22">
+ <div class="flex items-center gap-2">
+ <h1 class="text-16-medium text-text-strong">{feature()?.title ?? ""}</h1>
+ </div>
+ <p class="text-14-regular text-text-base">{feature()?.description ?? ""}</p>
</div>
- <p class="text-14-regular text-text-base">{feature()?.description ?? ""}</p>
- </div>
-
- {/* Spacer to push buttons to bottom */}
- <div class="flex-1" />
- {/* Bottom section - buttons and indicators (fixed position) */}
- <div class="flex flex-col gap-12">
- <div class="flex flex-col items-start gap-3">
- {isLast() ? (
- <Button variant="primary" size="large" onClick={handleClose}>
- Get started
- </Button>
- ) : (
- <Button variant="secondary" size="large" onClick={handleNext}>
- Next
+ {/* Spacer to push buttons to bottom */}
+ <div class="flex-1" />
+
+ {/* Bottom section - buttons and indicators (fixed position) */}
+ <div class="flex flex-col gap-12">
+ <div class="flex flex-col items-start gap-3">
+ {isLast() ? (
+ <Button variant="primary" size="large" onClick={handleClose}>
+ Get started
+ </Button>
+ ) : (
+ <Button variant="secondary" size="large" onClick={handleNext}>
+ Next
+ </Button>
+ )}
+
+ <Button variant="ghost" size="small" onClick={handleDisable}>
+ Don't show these in the future
</Button>
- )}
-
- <Button variant="ghost" size="small" onClick={handleDisable}>
- Don't show these in the future
- </Button>
- </div>
+ </div>
- {paged() && (
- <div class="flex items-center gap-1.5 -my-2.5">
- {props.highlights.map((_, i) => (
- <button
- type="button"
- class="h-6 flex items-center cursor-pointer bg-transparent border-none p-0 transition-all duration-200"
- classList={{
- "w-8": i === index(),
- "w-3": i !== index(),
- }}
- onClick={() => setIndex(i)}
- >
- <div
- class="w-full h-0.5 rounded-[1px] transition-colors duration-200"
+ {paged() && (
+ <div class="flex items-center gap-1.5 -my-2.5">
+ {props.highlights.map((_, i) => (
+ <button
+ type="button"
+ class="h-6 flex items-center cursor-pointer bg-transparent border-none p-0 transition-all duration-200"
classList={{
- "bg-icon-strong-base": i === index(),
- "bg-icon-weak-base": i !== index(),
+ "w-8": i === index(),
+ "w-3": i !== index(),
}}
- />
- </button>
- ))}
- </div>
- )}
+ onClick={() => setIndex(i)}
+ >
+ <div
+ class="w-full h-0.5 rounded-[1px] transition-colors duration-200"
+ classList={{
+ "bg-icon-strong-base": i === index(),
+ "bg-icon-weak-base": i !== index(),
+ }}
+ />
+ </button>
+ ))}
+ </div>
+ )}
+ </div>
</div>
- </div>
- {/* Right side - Media content (edge to edge) */}
- {feature()?.media && (
- <div class="flex-1 min-w-0 bg-surface-base overflow-hidden rounded-r-xl">
- {feature()!.media!.type === "image" ? (
- <img
- src={feature()!.media!.src}
- alt={feature()!.media!.alt ?? feature()?.title ?? "Release preview"}
- class="w-full h-full object-cover"
- />
- ) : (
- <video src={feature()!.media!.src} autoplay loop muted playsinline class="w-full h-full object-cover" />
- )}
- </div>
- )}
+ {/* Right side - Media content (edge to edge) */}
+ {feature()?.media && (
+ <div class="flex-1 min-w-0 bg-surface-base overflow-hidden rounded-r-xl">
+ {feature()!.media!.type === "image" ? (
+ <img
+ src={feature()!.media!.src}
+ alt={feature()!.media!.alt ?? feature()?.title ?? "Release preview"}
+ class="w-full h-full object-cover"
+ />
+ ) : (
+ <video src={feature()!.media!.src} autoplay loop muted playsinline class="w-full h-full object-cover" />
+ )}
+ </div>
+ )}
+ </div>
</Dialog>
)
}
diff --git a/packages/app/src/components/dialog-select-server.tsx b/packages/app/src/components/dialog-select-server.tsx
index 910b05ad4..e9e7646d5 100644
--- a/packages/app/src/components/dialog-select-server.tsx
+++ b/packages/app/src/components/dialog-select-server.tsx
@@ -59,18 +59,16 @@ function AddRow(props: AddRowProps) {
<div class="flex-1 min-w-0 [&_[data-slot=input-wrapper]]:relative">
<div
classList={{
- "size-1.5 rounded-full absolute left-3 z-10 pointer-events-none": true,
+ "size-1.5 rounded-full absolute left-3 top-1/2 -translate-y-1/2 z-10 pointer-events-none": true,
"bg-icon-success-base": props.status === true,
"bg-icon-critical-base": props.status === false,
"bg-border-weak-base": props.status === undefined,
}}
- style={{ top: "50%", transform: "translateY(-50%)" }}
ref={(el) => {
// Position relative to input-wrapper
requestAnimationFrame(() => {
const wrapper = el.parentElement?.querySelector('[data-slot="input-wrapper"]')
if (wrapper instanceof HTMLElement) {
- wrapper.style.position = "relative"
wrapper.appendChild(el)
}
})
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx
index 84f16d67e..c736ef0f1 100644
--- a/packages/app/src/components/prompt-input.tsx
+++ b/packages/app/src/components/prompt-input.tsx
@@ -1746,10 +1746,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<Tooltip
value={
<span class="flex max-w-[300px]">
- <span
- class="text-text-invert-base truncate min-w-0"
- style={{ direction: "rtl", "text-align": "left", "unicode-bidi": "plaintext" }}
- >
+ <span class="text-text-invert-base truncate-start [unicode-bidi:plaintext] min-w-0">
{getDirectory(item.path)}
</span>
<span class="shrink-0">{getFilename(item.path)}</span>
@@ -1772,10 +1769,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
>
<div class="flex items-center gap-1.5">
<FileIcon node={{ path: item.path, type: "file" }} class="shrink-0 size-3.5" />
- <div
- class="flex items-center text-11-regular min-w-0"
- style={{ "font-weight": "var(--font-weight-medium)" }}
- >
+ <div class="flex items-center text-11-regular min-w-0 font-medium">
<span class="text-text-strong whitespace-nowrap">{getFilenameTruncated(item.path, 14)}</span>
<Show when={item.selection}>
{(sel) => (
diff --git a/packages/app/src/components/session/session-new-view.tsx b/packages/app/src/components/session/session-new-view.tsx
index 2b79c47df..9306e8acb 100644
--- a/packages/app/src/components/session/session-new-view.tsx
+++ b/packages/app/src/components/session/session-new-view.tsx
@@ -45,10 +45,7 @@ export function NewSessionView(props: NewSessionViewProps) {
}
return (
- <div
- class="size-full flex flex-col pb-45 justify-end items-start gap-4 flex-[1_0_0] self-stretch max-w-200 mx-auto px-6"
- style={{ "padding-bottom": "calc(var(--prompt-height, 11.25rem) + 64px)" }}
- >
+ <div class="size-full flex flex-col justify-end items-start gap-4 flex-[1_0_0] self-stretch max-w-200 mx-auto px-6 pb-[calc(var(--prompt-height,11.25rem)+64px)]">
<div class="text-20-medium text-text-weaker">{language.t("command.session.new")}</div>
<div class="flex justify-center items-center gap-3">
<Icon name="folder" size="small" />
diff --git a/packages/app/src/components/session/session-sortable-terminal-tab.tsx b/packages/app/src/components/session/session-sortable-terminal-tab.tsx
index 75e9b22f9..aedf67876 100644
--- a/packages/app/src/components/session/session-sortable-terminal-tab.tsx
+++ b/packages/app/src/components/session/session-sortable-terminal-tab.tsx
@@ -146,7 +146,7 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () =>
/>
}
>
- <span onDblClick={edit} style={{ visibility: store.editing ? "hidden" : "visible" }}>
+ <span onDblClick={edit} classList={{ invisible: store.editing }}>
{label()}
</span>
</Tabs.Trigger>
@@ -167,8 +167,8 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () =>
<DropdownMenu open={store.menuOpen} onOpenChange={(open) => setStore("menuOpen", open)}>
<DropdownMenu.Portal>
<DropdownMenu.Content
+ class="fixed"
style={{
- position: "fixed",
left: `${store.menuPosition.x}px`,
top: `${store.menuPosition.y}px`,
}}
diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx
index e7e5b67f3..57efcfdfa 100644
--- a/packages/app/src/components/settings-general.tsx
+++ b/packages/app/src/components/settings-general.tsx
@@ -67,14 +67,8 @@ export const SettingsGeneral: Component = () => {
const soundOptions = [...SOUND_OPTIONS]
return (
- <div class="flex flex-col h-full overflow-y-auto no-scrollbar" style={{ padding: "0 40px 40px 40px" }}>
- <div
- class="sticky top-0 z-10"
- style={{
- background:
- "linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha) calc(100% - 24px), transparent)",
- }}
- >
+ <div class="flex flex-col h-full overflow-y-auto no-scrollbar px-10 pb-10">
+ <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
<div class="flex flex-col gap-1 pt-6 pb-8">
<h2 class="text-16-medium text-text-strong">{language.t("settings.tab.general")}</h2>
</div>
diff --git a/packages/app/src/components/settings-keybinds.tsx b/packages/app/src/components/settings-keybinds.tsx
index 7ff3425ab..a2cd3280c 100644
--- a/packages/app/src/components/settings-keybinds.tsx
+++ b/packages/app/src/components/settings-keybinds.tsx
@@ -352,14 +352,8 @@ export const SettingsKeybinds: Component = () => {
})
return (
- <div class="flex flex-col h-full overflow-y-auto no-scrollbar" style={{ padding: "0 40px 40px 40px" }}>
- <div
- class="sticky top-0 z-10"
- style={{
- background:
- "linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha) calc(100% - 24px), transparent)",
- }}
- >
+ <div class="flex flex-col h-full overflow-y-auto no-scrollbar px-10 pb-10">
+ <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
<div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
<div class="flex items-center justify-between gap-4">
<h2 class="text-16-medium text-text-strong">{language.t("settings.shortcuts.title")}</h2>
diff --git a/packages/app/src/components/settings-models.tsx b/packages/app/src/components/settings-models.tsx
index 3a73a42de..0cb6deee1 100644
--- a/packages/app/src/components/settings-models.tsx
+++ b/packages/app/src/components/settings-models.tsx
@@ -39,14 +39,8 @@ export const SettingsModels: Component = () => {
})
return (
- <div class="flex flex-col h-full overflow-y-auto no-scrollbar" style={{ padding: "0 40px 40px 40px" }}>
- <div
- class="sticky top-0 z-10"
- style={{
- background:
- "linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha) calc(100% - 24px), transparent)",
- }}
- >
+ <div class="flex flex-col h-full overflow-y-auto no-scrollbar px-10 pb-10">
+ <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
<div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
<h2 class="text-16-medium text-text-strong">{language.t("settings.models.title")}</h2>
<div class="flex items-center gap-2 px-3 h-9 rounded-lg bg-surface-base">
diff --git a/packages/app/src/components/settings-permissions.tsx b/packages/app/src/components/settings-permissions.tsx
index 1381515f5..bcbf7b68f 100644
--- a/packages/app/src/components/settings-permissions.tsx
+++ b/packages/app/src/components/settings-permissions.tsx
@@ -175,13 +175,7 @@ export const SettingsPermissions: Component = () => {
return (
<div class="flex flex-col h-full overflow-y-auto no-scrollbar">
- <div
- class="sticky top-0 z-10"
- style={{
- background:
- "linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha) calc(100% - 24px), transparent)",
- }}
- >
+ <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
<div class="flex flex-col gap-1 p-8 max-w-[720px]">
<h2 class="text-16-medium text-text-strong">{language.t("settings.permissions.title")}</h2>
<p class="text-14-regular text-text-weak">{language.t("settings.permissions.description")}</p>
diff --git a/packages/app/src/components/settings-providers.tsx b/packages/app/src/components/settings-providers.tsx
index d098db279..86a393b98 100644
--- a/packages/app/src/components/settings-providers.tsx
+++ b/packages/app/src/components/settings-providers.tsx
@@ -68,7 +68,7 @@ export const SettingsProviders: Component = () => {
}
return (
- <div class="flex flex-col h-full overflow-y-auto no-scrollbar" style={{ padding: "0 40px 40px 40px" }}>
+ <div class="flex flex-col h-full overflow-y-auto no-scrollbar px-10 pb-10">
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
<div class="flex flex-col gap-1 pt-6 pb-8 max-w-[720px]">
<h2 class="text-16-medium text-text-strong">{language.t("settings.providers.title")}</h2>
diff --git a/packages/app/src/components/status-popover.tsx b/packages/app/src/components/status-popover.tsx
index 79511ef04..102c477a1 100644
--- a/packages/app/src/components/status-popover.tsx
+++ b/packages/app/src/components/status-popover.tsx
@@ -176,33 +176,16 @@ export function StatusPopover() {
placement="bottom-end"
shift={-136}
>
- <div
- class="flex items-center gap-1 w-[360px] rounded-xl"
- style={{ "box-shadow": "var(--shadow-lg-border-base)" }}
- >
+ <div class="flex items-center gap-1 w-[360px] rounded-xl shadow-[var(--shadow-lg-border-base)]">
<Tabs
aria-label={language.t("status.popover.ariaLabel")}
- class="tabs"
+ class="tabs bg-background-strong rounded-xl overflow-hidden"
data-component="tabs"
data-active="servers"
defaultValue="servers"
variant="alt"
- style={{
- "background-color": "var(--background-strong)",
- "border-radius": "12px",
- overflow: "hidden",
- }}
>
- <Tabs.List
- data-slot="tablist"
- style={{
- "background-color": "transparent",
- "border-bottom": "none",
- padding: "8px 16px 0",
- gap: "16px",
- height: "40px",
- }}
- >
+ <Tabs.List data-slot="tablist" class="bg-transparent border-b-0 px-4 pt-2 pb-0 gap-4 h-10">
<Tabs.Trigger value="servers" data-slot="tab" class="text-12-regular">
{serverCount() > 0 ? `${serverCount()} ` : ""}
{language.t("status.popover.tab.servers")}
diff --git a/packages/app/src/index.css b/packages/app/src/index.css
index c0c7da858..4af87bca6 100644
--- a/packages/app/src/index.css
+++ b/packages/app/src/index.css
@@ -1,84 +1 @@
@import "@opencode-ai/ui/styles/tailwind";
-
-:root {
- a {
- cursor: default;
- }
-}
-
-[data-component="markdown"] ul {
- list-style: disc outside;
- padding-left: 1.5rem;
-}
-
-[data-component="markdown"] ol {
- list-style: decimal outside;
- padding-left: 1.5rem;
-}
-
-[data-component="markdown"] li > p:first-child {
- display: inline;
- margin: 0;
-}
-
-[data-component="markdown"] li > p + p {
- display: block;
- margin-top: 0.5rem;
-}
-
-*[data-tauri-drag-region] {
- app-region: drag;
-}
-
-.session-scroller::-webkit-scrollbar {
- width: 10px !important;
- height: 10px !important;
-}
-
-.session-scroller::-webkit-scrollbar-track {
- background: transparent !important;
- border-radius: 5px !important;
-}
-
-.session-scroller::-webkit-scrollbar-thumb {
- background: var(--border-weak-base) !important;
- border-radius: 5px !important;
- border: 3px solid transparent !important;
- background-clip: padding-box !important;
-}
-
-.session-scroller::-webkit-scrollbar-thumb:hover {
- background: var(--border-weak-base) !important;
-}
-
-.session-scroller {
- scrollbar-width: thin !important;
- scrollbar-color: var(--border-weak-base) transparent !important;
-}
-
-/* Wider dialog variant for release notes modal */
-[data-component="dialog"]:has(.dialog-release-notes) {
- padding: 20px;
- box-sizing: border-box;
-
- [data-slot="dialog-container"] {
- width: min(100%, 720px);
- height: min(100%, 400px);
- margin-top: -80px;
-
- [data-slot="dialog-content"] {
- min-height: auto;
- overflow: hidden;
- height: 100%;
- border: none;
- box-shadow: var(--shadow-lg-border-base);
- }
-
- [data-slot="dialog-body"] {
- overflow: hidden;
- height: 100%;
- display: flex;
- flex-direction: row;
- }
- }
-}
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 97eed72d7..15557dedb 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -1563,7 +1563,6 @@ export default function Layout(props: ParentProps) {
const notifications = createMemo(() => notification.project.unseen(props.project.worktree))
const hasError = createMemo(() => notifications().some((n) => n.type === "error"))
const name = createMemo(() => props.project.name || getFilename(props.project.worktree))
- const mask = "radial-gradient(circle 5px at calc(100% - 4px) 4px, transparent 5px, black 5.5px)"
const opencode = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750"
return (
@@ -1574,11 +1573,7 @@ export default function Layout(props: ParentProps) {
src={props.project.id === opencode ? "https://opencode.ai/favicon.svg" : props.project.icon?.override}
{...getAvatarColors(props.project.icon?.color)}
class="size-full rounded"
- style={
- notifications().length > 0 && props.notify
- ? { "-webkit-mask-image": mask, "mask-image": mask }
- : undefined
- }
+ classList={{ "badge-mask": notifications().length > 0 && props.notify }}
/>
</div>
<Show when={notifications().length > 0 && props.notify}>
@@ -2348,8 +2343,7 @@ export default function Layout(props: ParentProps) {
ref={(el) => {
if (!props.mobile) scrollContainerRef = el
}}
- class="size-full flex flex-col py-2 overflow-y-auto no-scrollbar"
- style={{ "overflow-anchor": "none" }}
+ class="size-full flex flex-col py-2 overflow-y-auto no-scrollbar [overflow-anchor:none]"
>
<nav class="flex flex-col gap-1 px-2">
<Show when={loading()}>
@@ -2575,8 +2569,7 @@ export default function Layout(props: ParentProps) {
ref={(el) => {
if (!panelProps.mobile) scrollContainerRef = el
}}
- class="size-full flex flex-col py-2 gap-4 overflow-y-auto no-scrollbar"
- style={{ "overflow-anchor": "none" }}
+ class="size-full flex flex-col py-2 gap-4 overflow-y-auto no-scrollbar [overflow-anchor:none]"
>
<SortableProvider ids={workspaces()}>
<For each={workspaces()}>
diff --git a/packages/ui/src/components/avatar.css b/packages/ui/src/components/avatar.css
index 87be9a50a..587216077 100644
--- a/packages/ui/src/components/avatar.css
+++ b/packages/ui/src/components/avatar.css
@@ -39,3 +39,11 @@
font-size: 1.25rem;
line-height: 2rem;
}
+
+[data-component="avatar"] [data-slot="avatar-image"] {
+ width: 100%;
+ height: 100%;
+ display: block;
+ object-fit: cover;
+ border-radius: inherit;
+}
diff --git a/packages/ui/src/components/avatar.tsx b/packages/ui/src/components/avatar.tsx
index 9d124b56a..76bde1e15 100644
--- a/packages/ui/src/components/avatar.tsx
+++ b/packages/ui/src/components/avatar.tsx
@@ -37,7 +37,7 @@ export function Avatar(props: AvatarProps) {
}}
>
<Show when={src} fallback={split.fallback?.[0]}>
- {(src) => <img src={src()} draggable={false} class="size-full object-cover rounded-[inherit]" />}
+ {(src) => <img src={src()} draggable={false} data-slot="avatar-image" />}
</Show>
</div>
)
diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css
index d47a3a79b..56fe9cf41 100644
--- a/packages/ui/src/components/message-part.css
+++ b/packages/ui/src/components/message-part.css
@@ -81,6 +81,14 @@
padding: 8px 12px;
border-radius: 4px;
+ [data-highlight="file"] {
+ color: var(--syntax-property);
+ }
+
+ [data-highlight="agent"] {
+ color: var(--syntax-type);
+ }
+
[data-slot="user-message-copy-wrapper"] {
position: absolute;
top: 7px;
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx
index 194d5148a..3fde255c8 100644
--- a/packages/ui/src/components/message-part.tsx
+++ b/packages/ui/src/components/message-part.tsx
@@ -477,20 +477,7 @@ function HighlightedText(props: { text: string; references: FilePart[]; agents:
return result
})
- return (
- <For each={segments()}>
- {(segment) => (
- <span
- classList={{
- "text-syntax-property": segment.type === "file",
- "text-syntax-type": segment.type === "agent",
- }}
- >
- {segment.text}
- </span>
- )}
- </For>
- )
+ return <For each={segments()}>{(segment) => <span data-highlight={segment.type}>{segment.text}</span>}</For>
}
export function Part(props: MessagePartProps) {
diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css
index 4a2714c0e..d1ade879e 100644
--- a/packages/ui/src/components/session-turn.css
+++ b/packages/ui/src/components/session-turn.css
@@ -499,6 +499,10 @@
gap: 8px;
color: var(--text-weak);
+ [data-slot="session-turn-trigger-icon"] {
+ color: var(--icon-base);
+ }
+
[data-component="spinner"] {
width: 12px;
height: 12px;
diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx
index 5aa79e0b9..8b20a73b4 100644
--- a/packages/ui/src/components/session-turn.tsx
+++ b/packages/ui/src/components/session-turn.tsx
@@ -565,7 +565,7 @@ export function SessionTurn(
viewBox="0 0 10 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
- class="text-icon-base"
+ data-slot="session-turn-trigger-icon"
>
<path
d="M8.125 1.875H1.875L5 8.125L8.125 1.875Z"
diff --git a/packages/ui/src/components/tabs.tsx b/packages/ui/src/components/tabs.tsx
index ddd22ec51..4836a0864 100644
--- a/packages/ui/src/components/tabs.tsx
+++ b/packages/ui/src/components/tabs.tsx
@@ -75,7 +75,7 @@ function TabsTrigger(props: ParentProps<TabsTriggerProps>) {
<Kobalte.Trigger
{...rest}
data-slot="tabs-trigger"
- classList={{ "group/tab": true, [split.classes?.button ?? ""]: split.classes?.button }}
+ classList={{ [split.classes?.button ?? ""]: split.classes?.button }}
>
{split.children}
</Kobalte.Trigger>
diff --git a/packages/ui/src/styles/tailwind/utilities.css b/packages/ui/src/styles/tailwind/utilities.css
index 8194aeffb..be305b4cb 100644
--- a/packages/ui/src/styles/tailwind/utilities.css
+++ b/packages/ui/src/styles/tailwind/utilities.css
@@ -8,6 +8,39 @@
}
}
+@utility session-scroller {
+ &::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: transparent;
+ border-radius: 5px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: var(--border-weak-base);
+ border-radius: 5px;
+ border: 3px solid transparent;
+ background-clip: padding-box;
+ }
+
+ &::-webkit-scrollbar-thumb:hover {
+ background: var(--border-weak-base);
+ }
+
+ & {
+ scrollbar-width: thin;
+ scrollbar-color: var(--border-weak-base) transparent;
+ }
+}
+
+@utility badge-mask {
+ -webkit-mask-image: radial-gradient(circle 5px at calc(100% - 4px) 4px, transparent 5px, black 5.5px);
+ mask-image: radial-gradient(circle 5px at calc(100% - 4px) 4px, transparent 5px, black 5.5px);
+}
+
@utility truncate-start {
text-overflow: ellipsis;
overflow: hidden;