Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .cursor/rules/minimalist-ui-styling.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
description: UI styling conventions for a minimalistic, flat, and rounded aesthetic
globs: src/renderer/src/**/*.{css,tsx}
alwaysApply: false
---

# Minimalistic UI Styling

When styling UI components in this application, adhere to a flat, clean, and rounded aesthetic. Avoid sharp edges, heavy borders, and prominent solid backgrounds for secondary elements.

## 1. Rounded Edges over Sharp Cuts
Do not use `clip-path` polygons to create sharp, angled cuts (e.g., "blades" or "notches"). Instead, use smooth `border-radius` values (e.g., `10px`, `12px`, `18px`, or fully rounded).

```css
/* ❌ BAD */
.button {
clip-path: polygon(12px 0, calc(100% - 12px) 0, 100% 50%, calc(100% - 12px) 100%, 12px 100%, 0 50%);
}

/* ✅ GOOD */
.button {
border-radius: 10px; /* or 12px, 18px, etc. */
}
```

## 2. Flat Controls with Hover Highlights
Secondary buttons, pills, and toggle controls should sit flat against their container's background. Remove static borders and solid backgrounds. Instead, use transparent backgrounds that reveal a subtle highlight only on hover.

```tsx
// ❌ BAD
<button className="border border-panel-border bg-surface-btn text-fg px-3 py-1">
Action
</button>

// ✅ GOOD
<button className="bg-transparent text-muted hover:text-fg hover:bg-[var(--surface-item-hover-bg)] transition-colors rounded-[10px] px-3 py-1">
Action
</button>
```

## 3. Active States
When indicating an active state (like a selected workspace pill), avoid full borders or heavy background fills. Use minimalistic indicators, such as a single colored line or a subtle text color change.

```css
/* ❌ BAD */
.item-active {
border: 1px solid var(--active-border);
background-color: var(--active-bg);
}

/* ✅ GOOD */
.item-active {
border-color: transparent;
background-color: transparent;
color: var(--color-fg);
/* Rely on a pseudo-element or separate indicator line for the active state */
}
```
18 changes: 13 additions & 5 deletions src/renderer/src/components/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,23 +183,31 @@ export function Canvas(props: CanvasProps) {
: "bg-muted";

const segmentBtn =
"min-w-[44px] min-h-9 px-3.5 border-0 border-r border-panel-border bg-transparent cursor-pointer text-fg hover:bg-[var(--surface-item-hover-bg)] last:border-r-0 font-medium tracking-wide max-md:min-h-10";
"min-w-[44px] min-h-9 px-3.5 border-0 bg-transparent cursor-pointer text-muted hover:text-fg hover:bg-[var(--surface-item-hover-bg)] font-medium tracking-wide max-md:min-h-10 rounded-[10px] transition-colors";

return (
<section className="relative flex-1 min-w-0 min-h-0 grid grid-rows-[1fr]">
<div
className="absolute top-4 left-4 z-20 flex items-center flex-wrap gap-2.5 pointer-events-auto max-md:left-[70px] max-md:right-2.5 max-md:top-2.5 max-md:gap-2"
aria-label="Canvas controls"
>
<div className="inline-flex items-center min-h-8 gap-2.5 px-3.5 mr-2 max-md:hidden">
<strong className="text-sm font-semibold tracking-[0.01em] text-fg">
Baton
</strong>
<span className="text-muted text-[10px] tracking-[0.24em] uppercase">
Orchestrate terminals
</span>
</div>
<button
className="btn-primary blade min-h-9 px-3.5 text-[12px] tracking-[0.02em] whitespace-nowrap max-md:min-h-10"
className="flex items-center justify-center gap-2 text-muted hover:text-fg hover:bg-[var(--surface-item-hover-bg)] transition-colors min-h-9 rounded-[10px] px-3.5 text-[12px] tracking-[0.02em] whitespace-nowrap max-md:min-h-10"
type="button"
onClick={addTerminalAtCenter}
>
Spawn Terminal
</button>
<div
className="stone-frame blade flex items-center overflow-hidden"
className="flex items-center gap-0.5"
role="group"
aria-label="Zoom controls"
>
Expand Down Expand Up @@ -228,7 +236,7 @@ export function Canvas(props: CanvasProps) {
</button>
</div>
<span
className={`stone-frame chamfer inline-flex items-center min-h-8 gap-2 px-3.5 text-[11px] tracking-[0.02em] font-medium max-md:hidden ${modePillTone}`}
className={`inline-flex items-center min-h-8 gap-2 px-3.5 text-[11px] tracking-[0.02em] font-medium max-md:hidden ${modePillTone}`}
>
<span className={`w-1.5 h-1.5 rounded-full ${modeDot}`} aria-hidden />
{props.terminalMode === "electron"
Expand Down Expand Up @@ -288,7 +296,7 @@ export function Canvas(props: CanvasProps) {
)}
</div>

<div className="stone-frame chamfer-l absolute left-[18px] bottom-4 z-20 px-3.5 py-2 text-muted text-[11px] tracking-[0.02em] pointer-events-none max-md:hidden">
<div className="absolute left-[18px] bottom-4 z-20 px-3.5 py-2 text-muted text-[11px] tracking-[0.02em] pointer-events-none max-md:hidden">
Drag to pan · scroll to pan · ⌘/ctrl/alt + scroll to zoom · double-click
workspace to rename
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/components/TerminalPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function TerminalPane({ terminalId }: TerminalPaneProps) {
cursorStyle: "block",
convertEol: false,
fontFamily:
'"Terminess Nerd Font Mono", "JetBrainsMono Nerd Font Mono", "FiraCode Nerd Font Mono", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace',
'"FiraCode Nerd Font Mono", "JetBrainsMono Nerd Font Mono", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace',
fontSize: 14,
lineHeight: 1.2,
scrollback: 10000,
Expand Down
65 changes: 18 additions & 47 deletions src/renderer/src/components/WorkspaceSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,20 @@ interface WorkspaceSidebarProps {
}

const SIDEBAR_BASE =
"surface-sidebar flex flex-col h-full grow-0 shrink-0 z-30 border-r border-panel-border backdrop-blur-2xl transition-[width,flex-basis] duration-150 ease-out max-md:absolute max-md:inset-y-0 max-md:left-0 max-md:basis-auto max-md:shadow-[24px_0_80px_rgba(0,0,0,0.25)]";
"relative surface-sidebar flex flex-col h-full grow-0 shrink-0 z-30 border-r border-panel-border backdrop-blur-2xl transition-[width,flex-basis] duration-150 ease-out max-md:absolute max-md:inset-y-0 max-md:left-0 max-md:basis-auto max-md:shadow-[24px_0_80px_rgba(0,0,0,0.25)]";

const SIDEBAR_COLLAPSED = "w-[66px] basis-[66px] max-md:w-[58px]";
const SIDEBAR_COLLAPSED_MAC = "w-[96px] basis-[96px] max-md:w-[88px]";
const SIDEBAR_EXPANDED = "w-[286px] basis-[286px] max-md:w-[min(82vw,286px)]";

const ICON_BUTTON =
"app-region-no-drag chamfer surface-btn-secondary inline-flex items-center justify-center w-9 h-9 min-h-9 px-0 cursor-pointer";

const SECONDARY_BUTTON =
"app-region-no-drag chamfer surface-btn-secondary min-h-9 min-w-0 px-1.5 text-[10px] font-medium tracking-[0.01em] text-center cursor-pointer overflow-hidden whitespace-nowrap text-ellipsis";
"app-region-no-drag rounded-[10px] min-h-9 min-w-0 px-1.5 text-[10px] font-medium tracking-[0.01em] text-center cursor-pointer overflow-hidden whitespace-nowrap text-ellipsis text-muted hover:text-fg hover:bg-[var(--surface-item-hover-bg)] transition-colors";

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SECONDARY_BUTTON styling no longer includes any disabled-state treatment, but the "Delete" button is still rendered with disabled={props.workspaces.length === 1}. As a result, the disabled button may look enabled (no opacity/cursor change). Add explicit disabled styling (e.g., opacity/cursor and suppress hover) or keep using a class that applies consistent [disabled] styles.

Suggested change
"app-region-no-drag rounded-[10px] min-h-9 min-w-0 px-1.5 text-[10px] font-medium tracking-[0.01em] text-center cursor-pointer overflow-hidden whitespace-nowrap text-ellipsis text-muted hover:text-fg hover:bg-[var(--surface-item-hover-bg)] transition-colors";
"app-region-no-drag rounded-[10px] min-h-9 min-w-0 px-1.5 text-[10px] font-medium tracking-[0.01em] text-center cursor-pointer overflow-hidden whitespace-nowrap text-ellipsis text-muted hover:text-fg hover:bg-[var(--surface-item-hover-bg)] transition-colors disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:text-muted disabled:hover:bg-transparent";

Copilot uses AI. Check for mistakes.

const ITEM_BASE =
"app-region-no-drag pill-rounded w-full flex items-center gap-3 p-2.5 mb-2 text-left border relative";
"app-region-no-drag pill-rounded w-full flex items-center gap-3 p-2.5 mb-2 text-left relative";

const ITEM_COLLAPSED =
"app-region-no-drag pill-rounded flex items-center justify-center w-11 h-11 mx-auto mb-2 border";
"app-region-no-drag pill-rounded flex items-center justify-center w-11 h-11 mx-auto mb-2 relative";

const ITEM_INACTIVE = "surface-item-inactive";
const ITEM_ACTIVE = "surface-item-active";
Expand Down Expand Up @@ -76,41 +73,12 @@ export function WorkspaceSidebar(props: WorkspaceSidebarProps) {
props.collapsed ? collapsedSidebarClass : SIDEBAR_EXPANDED
}`}
>
<div
className="absolute right-0 top-0 bottom-0 w-2 cursor-col-resize z-50 hover:bg-fg/10 transition-colors app-region-no-drag"
onClick={props.onToggleCollapsed}
Comment on lines +76 to +78

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new collapse/expand control is an absolutely-positioned <div> with onClick. This is not keyboard-accessible and has no role/ARIA label, so it’s not reachable for screen-reader/keyboard users. Consider using a <button> (or adding role="button", tabIndex={0}, keyboard handlers, and an aria-label) and ensure the hit target remains comfortably large.

Suggested change
<div
className="absolute right-0 top-0 bottom-0 w-2 cursor-col-resize z-50 hover:bg-fg/10 transition-colors app-region-no-drag"
onClick={props.onToggleCollapsed}
<button
type="button"
className="absolute right-0 top-0 bottom-0 w-2 cursor-col-resize z-50 hover:bg-fg/10 transition-colors app-region-no-drag"
onClick={props.onToggleCollapsed}
aria-label={props.collapsed ? "Expand sidebar" : "Collapse sidebar"}

Copilot uses AI. Check for mistakes.
title={props.collapsed ? "Expand sidebar" : "Collapse sidebar"}
/>
<div className={headerClass} style={headerStyle}>
<button
className={ICON_BUTTON}
type="button"
onClick={props.onToggleCollapsed}
aria-label="Toggle workspace panel"
>
<svg
aria-hidden="true"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.25"
strokeLinecap="round"
strokeLinejoin="round"
style={{
transform: props.collapsed ? "rotate(0deg)" : "rotate(180deg)",
transition: "transform 150ms ease-out",
}}
>
<polyline points="9 6 15 12 9 18" />
</svg>
</button>
{!props.collapsed && (
<div className="grid gap-0.5 min-w-0">
<strong className="text-sm font-semibold tracking-[0.01em] text-fg">
Baton
</strong>
<span className="text-muted text-[10px] tracking-[0.24em] uppercase">
Orchestrate terminals
</span>
</div>
)}
</div>

<div
Expand All @@ -132,7 +100,7 @@ export function WorkspaceSidebar(props: WorkspaceSidebarProps) {
onDoubleClick={() => props.onRenameWorkspace(workspace.id)}
title={workspace.name}
>
{selected && !props.collapsed && (
{selected && (
<span
aria-hidden
className="absolute left-1 top-1/2 -translate-y-1/2 h-6 w-[2px] bg-fg"
Expand Down Expand Up @@ -163,16 +131,19 @@ export function WorkspaceSidebar(props: WorkspaceSidebarProps) {
}`}
>
<button
className={`app-region-no-drag btn-primary ${
className={`app-region-no-drag flex items-center justify-center gap-2 text-muted hover:text-fg hover:bg-[var(--surface-item-hover-bg)] transition-colors ${
props.collapsed
? "blade-compact text-lg leading-none w-11 max-w-full"
: "blade px-3 text-[12px] tracking-[0.02em] w-full"
} min-h-9 overflow-hidden whitespace-nowrap`}
? "w-8 h-8 rounded-full"
: "w-full min-h-9 rounded-[10px] px-3 text-[12px] tracking-[0.02em]"
} overflow-hidden whitespace-nowrap`}
type="button"
onClick={props.onAddWorkspace}
title="New workspace"
>
{props.collapsed ? "+" : "New workspace"}
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 5v14M5 12h14"/>
</svg>
{!props.collapsed && "New workspace"}
</button>
{!props.collapsed && activeWorkspace && (
<div className="grid grid-cols-3 gap-2">
Expand Down
57 changes: 19 additions & 38 deletions src/renderer/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@
/*
* Typography
* UI: Bricolage Grotesque (Google Fonts)
* Terminal: Terminess Nerd Font Mono (Nerd Fonts CDN via jsDelivr)
* Terminal: FiraCode Nerd Font Mono (Nerd Fonts CDN via jsDelivr)
*/

@font-face {
font-family: "Terminess Nerd Font Mono";
font-family: "FiraCode Nerd Font Mono";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@master/patched-fonts/Terminus/Regular/TerminessNerdFontMono-Regular.ttf") format("truetype");
src: url("https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@master/patched-fonts/FiraCode/Regular/FiraCodeNerdFontMono-Regular.ttf") format("truetype");
}

@font-face {
font-family: "Terminess Nerd Font Mono";
font-family: "FiraCode Nerd Font Mono";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@master/patched-fonts/Terminus/Bold/TerminessNerdFontMono-Bold.ttf") format("truetype");
src: url("https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@master/patched-fonts/FiraCode/Bold/FiraCodeNerdFontMono-Bold.ttf") format("truetype");
}

@theme {
Expand All @@ -41,7 +41,7 @@
--color-warning: #e7e5e4;
--color-danger: #fafafa;

--surface-sidebar: rgba(10, 10, 10, 0.90);
--surface-sidebar: linear-gradient(180deg, rgba(10, 10, 10, 0.90), rgba(18, 18, 17, 0.92));
--surface-window: rgba(10, 10, 10, 0.95);
--surface-window-border: rgba(231, 229, 228, 0.15);
--surface-window-shadow: 0 24px 80px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(245, 245, 244, 0.04);
Expand Down Expand Up @@ -86,7 +86,7 @@
--terminal-selection: #404040;

--font-sans: "Bricolage Grotesque", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-mono: "Terminess Nerd Font Mono", "JetBrainsMono Nerd Font Mono", "FiraCode Nerd Font Mono", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
--font-mono: "FiraCode Nerd Font Mono", "JetBrainsMono Nerd Font Mono", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}

/*
Expand All @@ -106,7 +106,7 @@
--color-warning: #b45309;
--color-danger: #b91c1c;

--surface-sidebar: rgba(250, 249, 246, 0.92);
--surface-sidebar: linear-gradient(180deg, rgba(250, 249, 246, 0.92), rgba(245, 245, 244, 0.94));
--surface-window: rgba(255, 254, 251, 0.96);
--surface-window-border: rgba(28, 25, 23, 0.14);
--surface-window-shadow: 0 18px 60px rgba(28, 25, 23, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.8);
Expand Down Expand Up @@ -207,6 +207,7 @@ input {

.terminal-header-grad {
background: var(--surface-header-grad);
backdrop-filter: blur(12px);
}

/*
Expand All @@ -220,6 +221,7 @@ input {

.surface-window {
border: 1px solid var(--surface-window-border);
border-radius: 12px;
background: var(--surface-window);
box-shadow: var(--surface-window-shadow);
}
Expand Down Expand Up @@ -280,8 +282,8 @@ input {

.surface-item-active {
color: var(--color-fg);
border-color: var(--surface-item-active-border);
background-color: var(--surface-item-active-bg);
border-color: transparent;
background-color: transparent;
}

.surface-item-avatar {
Expand Down Expand Up @@ -309,42 +311,21 @@ input {
* cannot collide with the angled notches when the element is narrow.
*/
.blade {
clip-path: polygon(
12px 0,
calc(100% - 12px) 0,
100% 50%,
calc(100% - 12px) 100%,
12px 100%,
0 50%
);
border-radius: 18px;
padding-left: max(1.125rem, 18px);
padding-right: max(1.125rem, 18px);
}

/* When a blade wraps a single icon/glyph we still need the point cuts
* but can collapse the textual side padding to stay compact. */
.blade-compact {
clip-path: polygon(
10px 0,
calc(100% - 10px) 0,
100% 50%,
calc(100% - 10px) 100%,
10px 100%,
0 50%
);
border-radius: 18px;
padding-left: 14px;
padding-right: 14px;
}

.notch {
clip-path: polygon(
0 0,
calc(100% - 14px) 0,
100% 14px,
100% 100%,
14px 100%,
0 calc(100% - 14px)
);
border-radius: 12px;
}

/*
Expand Down Expand Up @@ -497,8 +478,8 @@ input {
gap: 0;
padding: 2px;
border-radius: 7px;
border: 1px solid var(--surface-btn-secondary-border);
background: var(--surface-btn-secondary-bg);
border: 1px solid transparent;
background: transparent;
}

.theme-toggle[data-compact='true'] {
Expand All @@ -515,8 +496,8 @@ input {
left: 2px;
width: calc((100% - 4px) / var(--theme-toggle-count));
border-radius: 5px;
background: var(--surface-item-active-bg);
box-shadow: inset 0 0 0 1px var(--surface-item-active-border);
background: var(--surface-item-hover-bg);
box-shadow: none;
transform: translateX(calc(var(--theme-toggle-index) * 100%));
transition: transform 220ms cubic-bezier(0.4, 0.0, 0.2, 1);
pointer-events: none;
Expand Down
Loading