From 3cadd955079c995d82147e261f1f4766a4662583 Mon Sep 17 00:00:00 2001
From: Innei
Date: Wed, 9 Sep 2026 15:24:27 +0800
Subject: [PATCH 01/27] feat(ui): add @lody/ui with StyleX tokens and Base UI
Button (#305)
* feat(ui): add @lody/ui with StyleX tokens and Base UI Button
Introduce packages/ui as the Base UI + StyleX home for shared primitives.
It ships colour, scale, shadow and motion tokens tuned to the two palettes
the app renders (Lody Light and Vesper), squircle per-size radii, forced
light/dark themes, and a Button with primary/secondary/ghost/destructive/link
variants, mini..large sizes, tone, pill shape and icon layout.
Wire the StyleX unplugin into the Electron renderer, site-docs and Storybook,
pin one cascade layer order (theme, base, stylex, components, utilities) so
Tailwind preflight sits below component styles and utilities above them,
and inject the dev stylesheet into the TanStack Start root head.
Replace every packages/components Button import and buttonVariants user with
@lody/ui/button, delete the Radix button, and give AlertDialog action and
cancel their children back with a variant prop instead of red utilities.
Model: claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01KJL8hsY2LscFcvxHFDzwxt
* fix(ui): use button tokens for composer actions
Model: gpt-6
---------
Co-authored-by: Wibus Wu <62133302+wibus-wee@users.noreply.github.com>
---
.agents/docs/components-package.md | 6 +
...2026-09-08-ui-button-migration-takeover.md | 68 ++
README.md | 1 +
README.zh-CN.md | 1 +
apps/electron/electron.vite.config.ts | 3 +
apps/electron/package.json | 2 +
apps/electron/tsconfig.web.json | 3 +
packages/components/.storybook/main.cjs | 3 +
packages/components/package.json | 2 +
.../ai-gui/chat-failed-detail-dialog.tsx | 6 +-
.../ai-gui/mermaid-diagram-viewer.tsx | 18 +-
.../components/ai-gui/message-selection.tsx | 8 +-
.../ai-gui/session-file-preview-dialog.tsx | 20 +-
.../components/ai-gui/user-message-editor.tsx | 16 +-
.../components/src/components/ai-gui/view.tsx | 45 +-
.../src/components/archive/archive-view.tsx | 27 +-
.../components/archive/web-archive-screen.tsx | 22 +-
.../bug-report/bug-report-dialog.tsx | 4 +-
.../components/chat/attachment-add-menu.tsx | 6 +-
.../src/components/chat/chat-composer.tsx | 12 +-
.../src/components/chat/chat-landing-view.tsx | 14 +-
.../src/components/chat/chat-landing.tsx | 26 +-
.../chat/machine-pairing-dialog.tsx | 12 +-
.../codex-reset-forecast-dialog.tsx | 4 +-
.../components/error-boundary-fallback.tsx | 16 +-
.../add-local-project-dialog.tsx | 34 +-
.../mobile-add-local-project-flow.tsx | 7 +-
.../components/src/components/login-page.tsx | 34 +-
.../src/components/loro-app-sidebar.tsx | 4 +-
.../src/components/loro-sidebar.tsx | 4 +-
.../mobile/mobile-about-settings.tsx | 10 +-
.../mobile/mobile-account-settings.tsx | 72 +-
.../mobile/mobile-acp-history-sheet.tsx | 15 +-
.../mobile/mobile-archive-screen.tsx | 28 +-
.../mobile/mobile-chat-landing-screen.tsx | 12 +-
.../components/mobile/mobile-chat-list.tsx | 2 +-
.../mobile/mobile-create-workspace-sheet.tsx | 4 +-
.../mobile/mobile-delete-workspace-sheet.tsx | 15 +-
.../mobile/mobile-file-viewer-drawer.tsx | 6 +-
.../mobile/mobile-integrations-settings.tsx | 10 +-
.../mobile-remove-local-project-sheet.tsx | 4 +-
.../components/src/components/not-found.tsx | 6 +-
.../onboarding/ceremony/intro-sequence.tsx | 6 +-
.../onboarding/onboarding-shell.tsx | 8 +-
.../onboarding/screens/first-task-screen.tsx | 9 +-
.../onboarding/screens/invite-screen.tsx | 14 +-
.../onboarding/screens/login-screen.tsx | 4 +-
.../onboarding/screens/projects-screen.tsx | 9 +-
.../onboarding/screens/providers-screen.tsx | 37 +-
.../onboarding/screens/summary-screen.tsx | 7 +-
.../onboarding/screens/workspace-screen.tsx | 14 +-
.../src/components/organization-switcher.tsx | 11 +-
.../components/page-headers/base-header.tsx | 4 +-
.../pages/accept-invitation-page.tsx | 14 +-
.../components/pages/complete-email-page.tsx | 4 +-
.../pages/create-workspace-page.tsx | 8 +-
.../pages/desktop-checkout-return-page.tsx | 11 +-
.../pages/desktop-github-install-page.tsx | 8 +-
.../src/components/pages/device-auth-page.tsx | 2 +-
.../components/pages/email-verified-page.tsx | 2 +-
.../components/pages/forgot-password-page.tsx | 2 +-
.../components/pages/reset-password-page.tsx | 2 +-
.../pages/workspace-join-request-page.tsx | 11 +-
.../visual-annotation-draft-composer.tsx | 14 +-
.../sessions/ask-user-question-card.tsx | 28 +-
.../components/sessions/auto-review-info.tsx | 10 +-
.../sessions/auto-review-status.tsx | 27 +-
.../sessions/chat-share-image-dialog.tsx | 4 +-
.../sessions/components/file-tree-view.tsx | 2 +-
.../sessions/floating-permission-request.tsx | 6 +-
.../sessions/managed-preview-surface.tsx | 9 +-
.../notification-permission-prompt.tsx | 13 +-
.../components/sessions/paged-file-viewer.tsx | 8 +-
.../components/sessions/pr-merge-button.tsx | 15 +-
.../src/components/sessions/pr-tab-view.tsx | 173 ++---
.../sessions/rename-session-dialog.tsx | 6 +-
.../sessions/session-browser-panel.tsx | 62 +-
.../sessions/session-browser-toolbar.tsx | 6 +-
.../sessions/session-chat-input-area.tsx | 27 +-
.../sessions/session-chat-interface.tsx | 36 +-
.../components/sessions/session-detail.tsx | 31 +-
.../sessions/session-file-content-view.tsx | 24 +-
.../sessions/session-file-error-state.tsx | 8 +-
.../sessions/session-goal-banner.tsx | 17 +-
.../sessions/session-info-chips.tsx | 7 +-
.../components/sessions/session-not-found.tsx | 4 +-
.../src/components/sessions/session-pin.tsx | 9 +-
.../sessions/session-usage-popover.tsx | 9 +-
.../src/components/settings/about-setting.tsx | 19 +-
.../settings/account-machines-overview.tsx | 22 +-
.../settings/account-setting-pure.tsx | 56 +-
.../settings/acp-authentication-panel.tsx | 24 +-
.../settings/agent-config-dialog.tsx | 32 +-
.../components/settings/agent-role-form.tsx | 16 +-
.../settings/agent-roles-setting.tsx | 20 +-
.../settings/billing-setting-pure.tsx | 16 +-
.../components/settings/billing-setting.tsx | 3 +-
.../settings/change-password-button.tsx | 39 +-
.../src/components/settings/clear-cache.tsx | 2 +-
.../settings/cli-daemon-setting.tsx | 13 +-
.../settings/device-resource-monitor.tsx | 12 +-
.../components/settings/general-setting.tsx | 4 +-
.../settings/integrations-setting.tsx | 17 +-
.../settings/invite-member-dialog.tsx | 10 +-
.../settings/join-community-dialog.tsx | 2 +-
.../settings/keyboard-shortcuts-setting.tsx | 24 +-
.../settings/linked-accounts-list.tsx | 34 +-
.../settings/machine-agent-settings.tsx | 9 +-
.../settings/machine-detail-pane.tsx | 58 +-
.../components/settings/machine-tab-list.tsx | 15 +-
.../settings/mcp-connection-form.tsx | 29 +-
.../src/components/settings/mcp-setting.tsx | 16 +-
.../my-machine-connected-resources.tsx | 4 +-
.../open-source-attributions-dialog.tsx | 5 +-
.../settings/path-launchers-setting.tsx | 12 +-
.../components/settings/project-settings.tsx | 25 +-
.../settings/project-skills-tab.tsx | 14 +-
.../settings/provider-progress-button.tsx | 11 +-
.../src/components/settings/provider-row.tsx | 13 +-
.../settings/provider-setup-row.tsx | 14 +-
.../settings/review-policy-setting.tsx | 12 +-
.../components/settings/settings-header.tsx | 4 +-
.../settings/usage-calendar-visualization.tsx | 23 +-
.../workspace-join-requests-settings.tsx | 21 +-
.../settings/workspace-ownership-transfer.tsx | 8 +-
.../components/shared/acp-session-select.tsx | 11 +-
.../src/components/shared/option-selector.tsx | 4 +-
.../shared/session-relation-card.tsx | 8 +-
.../shared/workdir-mode-selector.tsx | 13 +-
.../src/components/sidebar-filter-popover.tsx | 7 +-
.../src/components/sidebar-update-banner.tsx | 6 +-
.../components/stuck-connection-banner.tsx | 13 +-
.../tasks/task-attach-session-dialog.tsx | 12 +-
.../tasks/task-body-editor-fallback.tsx | 6 +-
.../tasks/task-body-editor-surface.tsx | 4 +-
.../tasks/task-detail-actions-menu.tsx | 9 +-
.../src/components/tasks/task-detail-view.tsx | 6 +-
.../components/tasks/task-launch-controls.tsx | 15 +-
.../tasks/task-properties-panel.tsx | 37 +-
.../components/tasks/task-proposal-notice.tsx | 8 +-
.../tasks/task-quick-add-dialog.tsx | 19 +-
.../src/components/tasks/task-thread.tsx | 14 +-
.../src/components/tasks/tasks-workspace.tsx | 15 +-
.../components/update-changelog-dialog.tsx | 4 +-
.../stories/AddLocalProjectDialog.stories.tsx | 2 +-
.../AssistantTurnAlignment.stories.tsx | 9 -
.../src/stories/BaseHeader.stories.tsx | 22 +-
.../src/stories/ChatComposer.stories.tsx | 28 +-
.../ChatFailedDetailDialog.stories.tsx | 1 -
.../ChatLandingAttachments.stories.tsx | 14 +-
.../src/stories/ChatLandingView.stories.tsx | 3 -
.../stories/ChatMessageSelection.stories.tsx | 8 +-
.../CodeCollabMarkdownPreview.stories.tsx | 12 -
.../CodeCollabMonacoEditor.stories.tsx | 2 -
...rsationOutlineArrivalIntentLab.stories.tsx | 30 +-
.../DesktopSessionDetailLayout.stories.tsx | 6 +-
.../stories/ErrorBoundaryFallback.stories.tsx | 1 -
.../src/stories/FileTreeStates.stories.tsx | 4 +-
.../stories/JoinCommunityDialog.stories.tsx | 4 +-
.../stories/LocalProjectRemovalUX.stories.tsx | 8 +-
.../src/stories/MarkdownRenderer.stories.tsx | 101 ---
.../MobileChatLandingScreen.stories.tsx | 9 +-
.../stories/MobileInlinePicker.stories.tsx | 20 +-
.../stories/MobileNewChatSheet.stories.tsx | 9 +-
.../stories/MobileProjectSkills.stories.tsx | 2 -
.../stories/MobileSidebarDrawer.stories.tsx | 2 +-
.../stories/MobileWorkspaceLayout.stories.tsx | 10 +-
.../MobileWorkspaceSwitcherSheet.stories.tsx | 2 +-
.../src/stories/OnboardingShell.stories.tsx | 12 +-
.../src/stories/PrTabView.stories.tsx | 5 -
.../src/stories/ProjectSkillsTab.stories.tsx | 4 -
.../ReviewAgentSetupDialog.stories.tsx | 2 +-
.../src/stories/SessionChatSearch.stories.tsx | 1 -
.../SessionConversationPage.stories.tsx | 13 -
.../SessionForkDestinationPopover.stories.tsx | 10 +-
.../SessionSidePanelTabBar.stories.tsx | 6 +-
.../src/stories/SettingsHeader.stories.tsx | 6 +-
.../stories/SkillMarkdownFallback.stories.tsx | 10 -
.../src/stories/TaskTabBar.stories.tsx | 4 +-
.../src/stories/TerminalComponent.stories.tsx | 1 -
.../src/stories/Toaster.stories.tsx | 22 +-
.../stories/UpdateChangelogDialog.stories.tsx | 3 -
.../src/stories/VSCodeThemeProbe.stories.tsx | 6 +-
...isualAnnotationCommentsOverlay.stories.tsx | 8 +-
.../VisualAnnotationInspector.stories.tsx | 37 +-
.../stories/ZoomableImageViewer.stories.tsx | 2 +-
packages/components/src/tailwind/index.css | 1 +
packages/components/src/theme-provider.tsx | 7 +-
packages/components/src/ui/alert-dialog.tsx | 24 +-
packages/components/src/ui/button.tsx | 50 --
packages/components/src/ui/calendar.tsx | 14 +-
.../components/src/ui/collapsible-card.tsx | 9 +-
packages/components/src/ui/copy-button.tsx | 6 +-
.../src/ui/diff-viewer/diff-viewer.tsx | 8 +-
.../ui/diff-viewer/github-comment-thread.tsx | 11 +-
.../ui/diff-viewer/session-comment-draft.tsx | 10 +-
packages/components/src/ui/index.ts | 3 +-
packages/components/src/ui/pagination.tsx | 46 +-
packages/components/src/ui/sidebar.tsx | 9 +-
packages/components/src/ui/workspace-list.tsx | 13 +-
packages/components/vitest.config.ts | 10 +-
packages/ui/AGENTS.md | 28 +
packages/ui/CLAUDE.md | 1 +
packages/ui/README.md | 25 +
packages/ui/package.json | 38 +
.../ui/src/button/button.tokens.stylex.ts | 28 +
packages/ui/src/button/button.tsx | 178 +++++
packages/ui/src/index.ts | 10 +
packages/ui/src/theme/theme.tsx | 33 +
packages/ui/src/tokens/RULES.md | 62 ++
packages/ui/src/tokens/colors.stylex.ts | 129 ++++
packages/ui/src/tokens/scales.stylex.ts | 62 ++
packages/ui/stylex-options.ts | 10 +
packages/ui/test/button.test.tsx | 38 +
packages/ui/test/tokens.test.tsx | 36 +
packages/ui/tsconfig.json | 7 +
packages/ui/vitest.config.ts | 11 +
pnpm-lock.yaml | 655 ++++++++++++------
site-docs/app/global.css | 1 +
site-docs/components/landing-app-preview.tsx | 66 +-
.../components/landing-mobile-preview.tsx | 275 ++++----
.../components/landing-preview-panel.tsx | 24 +-
site-docs/package.json | 2 +
site-docs/src/routes/__root.tsx | 1 +
site-docs/types/lody-app-components.d.ts | 17 -
site-docs/vite.config.ts | 3 +
specs/ui-primitives.md | 51 ++
227 files changed, 2677 insertions(+), 2019 deletions(-)
create mode 100644 .agents/notes/implemented/architecture/2026-09-08-ui-button-migration-takeover.md
delete mode 100644 packages/components/src/ui/button.tsx
create mode 100644 packages/ui/AGENTS.md
create mode 120000 packages/ui/CLAUDE.md
create mode 100644 packages/ui/README.md
create mode 100644 packages/ui/package.json
create mode 100644 packages/ui/src/button/button.tokens.stylex.ts
create mode 100644 packages/ui/src/button/button.tsx
create mode 100644 packages/ui/src/index.ts
create mode 100644 packages/ui/src/theme/theme.tsx
create mode 100644 packages/ui/src/tokens/RULES.md
create mode 100644 packages/ui/src/tokens/colors.stylex.ts
create mode 100644 packages/ui/src/tokens/scales.stylex.ts
create mode 100644 packages/ui/stylex-options.ts
create mode 100644 packages/ui/test/button.test.tsx
create mode 100644 packages/ui/test/tokens.test.tsx
create mode 100644 packages/ui/tsconfig.json
create mode 100644 packages/ui/vitest.config.ts
create mode 100644 specs/ui-primitives.md
diff --git a/.agents/docs/components-package.md b/.agents/docs/components-package.md
index a370ca77c..5abb0ed09 100644
--- a/.agents/docs/components-package.md
+++ b/.agents/docs/components-package.md
@@ -58,6 +58,12 @@ button for a file the machine will refuse, and the user would lose the edit at s
## Theming and fields
+Reusable visual primitives and semantic design tokens are owned by
+[`@lody/ui`](../../packages/ui/README.md). `@lody/components` composes those
+primitives into product surfaces and may add layout or interaction constraints,
+while the primitive's props remain the owner of its visual variant, size, tone,
+and shape.
+
`--input` is the theme's raw `input.background` and doubles as a muted chip/composer
slab, so in a light theme it can sit below the page color and read as a disabled field.
`--input-field` is derived in `lib/vscode-theme/vscode-theme-css.ts` as the lighter of
diff --git a/.agents/notes/implemented/architecture/2026-09-08-ui-button-migration-takeover.md b/.agents/notes/implemented/architecture/2026-09-08-ui-button-migration-takeover.md
new file mode 100644
index 000000000..30fc939bd
--- /dev/null
+++ b/.agents/notes/implemented/architecture/2026-09-08-ui-button-migration-takeover.md
@@ -0,0 +1,68 @@
+# UI Button migration takeover
+
+Status: implemented
+Translation: pending
+Issue: https://github.com/LodyAI/Lody/issues/304
+Original PR: https://github.com/LodyAI/Lody/pull/305
+
+## Abstract
+
+PR #305 redesigns Lody buttons around `@lody/ui`, Base UI, and StyleX tokens,
+but its September 2 branch no longer merged cleanly with current `main`. The
+takeover keeps that redesign as the visual authority, preserves business behavior
+added on `main`, and migrates newer Button consumers to the author's public API.
+It does not provide a compatibility layer for the deleted Button or restore its
+appearance through caller classes. Surface-specific accessibility and layout
+requirements remain explicit at their owning call sites.
+
+## Decision
+
+Update the author's branch by merging current `main` into its existing commit.
+Resolve overlapping product files by retaining current navigation, session,
+onboarding, machine, and Skills behavior, then express their controls using the
+new Button variants, sizes, tones, shapes, and icon flag.
+
+New Button consumers added after the PR base follow the same API used by the
+author's migration. They do not select sizes by matching the removed component's
+pixel dimensions. Mermaid viewer controls keep their existing 44 px touch target
+because that surface has an independent mobile accessibility requirement; the
+new Button still owns their appearance.
+
+`@lody/ui` owns primitive visuals and semantic tokens. Product packages compose
+those primitives and may pass classes for layout or interaction constraints.
+Caller classes must not recreate the deleted Button's colors, typography,
+shadows, radii, or state styling.
+
+Composer submit and stop actions use the Button's `primary`, `medium`, `pill`,
+and icon choices directly. Their former `ghost` variant plus Tailwind color,
+size, radius, shadow, and press-state overrides bypassed the primitive and could
+leave the stop glyph with insufficient contrast. Landing, session, mobile-sheet,
+and Storybook compositions now select the same token-owned control treatment.
+
+## Integration evidence
+
+The original PR is commit `11ef421f86c29fe50773c6c101bf129839368c3c`,
+based on `947affd0741e35b3644622c841e8457a26133a95`. Current `main` is merged as
+the second parent so the author's commit and authorship remain intact. Conflict
+resolution retains the current package dependencies and application handlers,
+adds `@lody/ui` where needed, and regenerates the lockfile from the merged
+manifests.
+
+Verification used the repository's supported Node 22 runtime. The complete
+`pnpm check` suite passed after disabling commit signing for its synthetic Git
+repository, and the focused `@lody/ui` test suite passed all eight tests. The
+Electron application, documentation site, and component Storybook production
+builds completed with StyleX compiling the package source. A source scan found no
+remaining imports of the deleted Button module or uses of its old size and variant
+props.
+
+Interactive browser screenshot comparison was unavailable in the takeover
+environment. The production application and Storybook builds provide compiler
+and integration coverage, but a maintainer should still inspect the PR preview as
+the final visual acceptance step.
+
+## Limits
+
+This migration establishes the Button and token foundation described in the
+linked draft Spec. Other primitives remain in `@lody/components` until a later
+migration gives each one an explicit `@lody/ui` contract.
diff --git a/README.md b/README.md
index 113350c05..6eaf09bf3 100644
--- a/README.md
+++ b/README.md
@@ -150,6 +150,7 @@ Lody is still moving toward full local-first support.
- `apps/cli` — Connect machines and run coding agents
- `apps/electron` — Lody desktop app
- `packages/components` — Shared workspace UI
+- `packages/ui` — Base UI primitives and StyleX design tokens
- `packages/platform` — Platform capabilities and integrations
- `packages/shared` — Shared schemas, protocols, and utilities
- `packages/cloud-api` — Optional-cloud protocol names and DTOs
diff --git a/README.zh-CN.md b/README.zh-CN.md
index c492777e9..262ce6988 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -161,6 +161,7 @@ Lody 仍在走向完整的 local-first 支持。
- `apps/cli` — 连接机器并运行 Coding Agents
- `apps/electron` — Lody 桌面应用
- `packages/components` — 工作空间共享 UI
+- `packages/ui` — Base UI 基础组件与 StyleX 设计令牌
- `packages/platform` — 平台能力与集成
- `packages/shared` — 共享 Schema、协议与工具
- `site-docs` — 官网、文档与博客
diff --git a/apps/electron/electron.vite.config.ts b/apps/electron/electron.vite.config.ts
index e5cd6bbe5..295002520 100644
--- a/apps/electron/electron.vite.config.ts
+++ b/apps/electron/electron.vite.config.ts
@@ -6,6 +6,7 @@ import type { Plugin } from 'vite'
import react from '@vitejs/plugin-react'
import wasm from 'vite-plugin-wasm'
import tailwindcss from '@tailwindcss/vite'
+import stylex from '@stylexjs/unplugin'
import {
loroCrdtBundlerAlias,
loroCrdtWasmUrlWorkaround
@@ -17,6 +18,7 @@ import {
rendererBundleAliasPlugin,
rendererBundleAliases
} from '../../packages/components/vite-renderer-bundle-aliases'
+import { stylexOptions } from '../../packages/ui/stylex-options'
import { emojibaseAssetsPlugin } from '../../packages/components/vite-emojibase-assets'
function getGitCommitHash(): string {
@@ -187,6 +189,7 @@ export default defineConfig(({ mode }) => {
plugins: [
previewPublicBaseDomainHtmlPlugin(previewPublicBaseDomain),
tailwindcss(),
+ stylex.vite(stylexOptions),
loroCrdtWasmUrlWorkaround(),
react(),
wasm(),
diff --git a/apps/electron/package.json b/apps/electron/package.json
index 6ed410786..33fbf4544 100644
--- a/apps/electron/package.json
+++ b/apps/electron/package.json
@@ -54,10 +54,12 @@
"react-dom": "catalog:"
},
"devDependencies": {
+ "@stylexjs/unplugin": "0.19.0",
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
"@electron-toolkit/eslint-config-ts": "^3.1.0",
"@electron-toolkit/tsconfig": "^2.0.0",
"@lody/components": "workspace:*",
+ "@lody/ui": "workspace:*",
"@lody/configs": "workspace:*",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/vite": "^4.3.0",
diff --git a/apps/electron/tsconfig.web.json b/apps/electron/tsconfig.web.json
index 80f817eb0..75e57ef6e 100644
--- a/apps/electron/tsconfig.web.json
+++ b/apps/electron/tsconfig.web.json
@@ -19,6 +19,9 @@
"react/jsx-dev-runtime": ["./node_modules/@types/react/jsx-dev-runtime"],
"@lody/components": ["../../packages/components/src"],
"@lody/components/*": ["../../packages/components/src/*"],
+ "@lody/ui": ["../../packages/ui/src"],
+ "@lody/ui/tokens/*": ["../../packages/ui/src/tokens/*"],
+ "@lody/ui/theme": ["../../packages/ui/src/theme/theme"],
"@/ui": ["../../packages/components/src/ui"],
"@/ui/*": ["../../packages/components/src/ui/*"],
"@/components": ["../../packages/components/src/components"],
diff --git a/packages/components/.storybook/main.cjs b/packages/components/.storybook/main.cjs
index 251e4bdcf..342284c8b 100644
--- a/packages/components/.storybook/main.cjs
+++ b/packages/components/.storybook/main.cjs
@@ -1,4 +1,6 @@
const tailwindcss = require('@tailwindcss/vite').default;
+const stylex = require('@stylexjs/unplugin').default;
+const { stylexOptions } = require('../../ui/stylex-options.ts');
const { loadEnv } = require('vite');
const { resolve } = require('node:path');
const wasm = require('vite-plugin-wasm').default;
@@ -33,6 +35,7 @@ const config = {
return true;
});
viteConfig.plugins.push(tailwindcss());
+ viteConfig.plugins.push(stylex.vite(stylexOptions));
viteConfig.worker = {
...(viteConfig.worker ?? {}),
diff --git a/packages/components/package.json b/packages/components/package.json
index 9023024de..80e9f6af6 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -70,6 +70,7 @@
"build-storybook": "storybook build"
},
"dependencies": {
+ "@lody/ui": "workspace:*",
"@zumer/snapdom": "^2.24.15",
"@assistant-ui/react": "^0.10.39",
"@assistant-ui/react-markdown": "^0.10.8",
@@ -196,6 +197,7 @@
"zod": "catalog:"
},
"devDependencies": {
+ "@stylexjs/unplugin": "0.19.0",
"@lody/configs": "workspace:*",
"@playwright/test": "^1.58.2",
"@radix-ui/react-avatar": "^1.1.10",
diff --git a/packages/components/src/components/ai-gui/chat-failed-detail-dialog.tsx b/packages/components/src/components/ai-gui/chat-failed-detail-dialog.tsx
index fe3ee3a39..0ec23890c 100644
--- a/packages/components/src/components/ai-gui/chat-failed-detail-dialog.tsx
+++ b/packages/components/src/components/ai-gui/chat-failed-detail-dialog.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';
import { AlertCircle, Check, Copy } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
Dialog,
DialogClose,
@@ -128,11 +128,11 @@ export function ChatFailedDetailDialog({
-
+
{t('common.close', 'Close')}
-
+
{copied ? (
) : (
diff --git a/packages/components/src/components/ai-gui/mermaid-diagram-viewer.tsx b/packages/components/src/components/ai-gui/mermaid-diagram-viewer.tsx
index 6143ba0cc..c8688e0a7 100644
--- a/packages/components/src/components/ai-gui/mermaid-diagram-viewer.tsx
+++ b/packages/components/src/components/ai-gui/mermaid-diagram-viewer.tsx
@@ -9,7 +9,7 @@ import {
import { createPortal } from 'react-dom';
import { Maximize, X, ZoomIn, ZoomOut } from 'lucide-react';
import { useTranslation } from 'react-i18next';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
/**
@@ -41,9 +41,8 @@ const MERMAID_DIAGRAM_MAX_INITIAL_ZOOM = 3;
const MERMAID_DIAGRAM_ZOOM_STEP = 1.25;
/**
- * `size="icon"` is 36px square. The viewer's controls sit at the top edge of a
- * phone screen, where the shared size is under the 44px touch-target floor
- * (iOS HIG / WCAG 2.5.5) that this whole fix is about.
+ * The shared Button sizes are under this viewer's 44px touch-target floor, so
+ * the toolbar keeps an explicit size at the top edge of a phone screen.
*/
const CONTROL_CLASS_NAME = 'h-11 w-11 shrink-0 text-muted-foreground';
@@ -274,7 +273,8 @@ function OpenMermaidDiagramViewer({
zoomBy(1 / MERMAID_DIAGRAM_ZOOM_STEP)}
disabled={zoom !== null && zoom <= MERMAID_DIAGRAM_MIN_ZOOM}
@@ -286,7 +286,7 @@ function OpenMermaidDiagramViewer({
zoomBy(MERMAID_DIAGRAM_ZOOM_STEP)}
disabled={zoom !== null && zoom >= MERMAID_DIAGRAM_MAX_ZOOM}
@@ -310,7 +311,8 @@ function OpenMermaidDiagramViewer({
ref={closeRef}
type="button"
variant="ghost"
- size="icon"
+ size="small"
+ icon
data-testid="mermaid-diagram-viewer-close"
className={CONTROL_CLASS_NAME}
onClick={onClose}
diff --git a/packages/components/src/components/ai-gui/message-selection.tsx b/packages/components/src/components/ai-gui/message-selection.tsx
index 3a10db4f5..2f3020c49 100644
--- a/packages/components/src/components/ai-gui/message-selection.tsx
+++ b/packages/components/src/components/ai-gui/message-selection.tsx
@@ -12,7 +12,7 @@ import {
import { useTranslation } from 'react-i18next';
import { ImageIcon } from 'lucide-react';
import type { ConversationMessage } from '@lody/shared';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Checkbox } from '@/ui/checkbox';
import { ConversationColumn } from '@/components/shared/conversation-column';
@@ -366,15 +366,15 @@ export function MessageSelectionToolbar({
count: selection.messages.length,
})}
-
+
{selection.allSelected
? t('sessions.shareImage.clearSelection', 'Clear selection')
: t('sessions.shareImage.selectAll', 'Select all')}
-
+
{t('common.cancel', 'Cancel')}
-
+
{t('sessions.shareImage.preview', 'Preview image')}
diff --git a/packages/components/src/components/ai-gui/session-file-preview-dialog.tsx b/packages/components/src/components/ai-gui/session-file-preview-dialog.tsx
index 945649250..bc40d5f66 100644
--- a/packages/components/src/components/ai-gui/session-file-preview-dialog.tsx
+++ b/packages/components/src/components/ai-gui/session-file-preview-dialog.tsx
@@ -4,7 +4,7 @@ import { toast } from 'sonner';
import { Check, Copy, Download, FileWarning, Loader2, X } from 'lucide-react';
import type { SessionFilePayload } from '@lody/shared';
import { Dialog, DialogClose, DialogContentWithoutClose, DialogTitle } from '@/ui/dialog';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Tabs, TabsList, TabsTrigger } from '@/ui/tabs';
import { formatFileSize } from '@/lib/session-file-presentation';
import { MarkdownRenderer } from './markdown-renderer';
@@ -100,8 +100,8 @@ export function SessionFilePreviewPanel({
onDownload(file)}
disabled={isDownloading}
aria-label={t('sessions.fileDownload', 'Download')}
@@ -131,9 +131,9 @@ export function SessionFilePreviewPanel({
@@ -153,8 +153,8 @@ export function SessionFilePreviewPanel({
onDownload(file)}
disabled={isDownloading}
>
@@ -177,7 +177,7 @@ export function SessionFilePreviewPanel({
{status.message}
- onDownload(file)}>
+ onDownload(file)}>
{t('sessions.fileDownload', 'Download')}
diff --git a/packages/components/src/components/ai-gui/user-message-editor.tsx b/packages/components/src/components/ai-gui/user-message-editor.tsx
index 553d64824..bdc57e6a0 100644
--- a/packages/components/src/components/ai-gui/user-message-editor.tsx
+++ b/packages/components/src/components/ai-gui/user-message-editor.tsx
@@ -2,7 +2,7 @@ import { useCallback, useLayoutEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { Loader2 } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Textarea } from '@/ui/textarea';
import { cn } from '@/lib/utils';
import type { ConversationFontSize } from '@/atoms/settings';
@@ -101,24 +101,20 @@ export function UserMessageEditor({
{t('common.cancel', 'Cancel')}
{isSaving ? : null}
{t('sessions.send', 'Send')}
diff --git a/packages/components/src/components/ai-gui/view.tsx b/packages/components/src/components/ai-gui/view.tsx
index 1d967a6df..fd2d42701 100644
--- a/packages/components/src/components/ai-gui/view.tsx
+++ b/packages/components/src/components/ai-gui/view.tsx
@@ -187,7 +187,7 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from '@/ui/alert-dialog';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
AgentActivityIndicator,
type AgentActivityTone,
@@ -1794,8 +1794,9 @@ export const SessionChatStreamView = forwardRef<
@@ -2923,9 +2924,9 @@ const UserMessageRowView = ({
onFork(turnId, 'shared')}
disabled={isForking}
aria-label={t('sessions.forkSession', 'Fork session')}
@@ -3756,8 +3757,8 @@ const AssistantTurnFooter = ({
{
void handleCopy();
}}
@@ -3817,14 +3818,8 @@ const AssistantTurnFooter = ({
@@ -5252,8 +5247,7 @@ const UserPlainTextBlock = ({
setIsExpanded((prev) => !prev)}
>
{isExpanded ? 'Show less' : 'Show more'}
@@ -5531,8 +5525,9 @@ const PlanPanel = ({
{
void handleCopy();
}}
diff --git a/packages/components/src/components/archive/archive-view.tsx b/packages/components/src/components/archive/archive-view.tsx
index c3c428566..bdc97ebae 100644
--- a/packages/components/src/components/archive/archive-view.tsx
+++ b/packages/components/src/components/archive/archive-view.tsx
@@ -25,7 +25,7 @@ import {
} from 'lucide-react';
import type { LucideIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Checkbox } from '@/ui/checkbox';
import {
Dialog,
@@ -1489,12 +1489,7 @@ export function ArchiveView() {
{isMobile ? (
-
+
{scopeLabel}
@@ -1520,12 +1515,7 @@ export function ArchiveView() {
-
+
{groupMode === 'flat' ? (
) : (
@@ -1554,12 +1544,7 @@ export function ArchiveView() {
-
+
{sortMode === 'title' ? (
) : sortMode === 'oldest' ? (
@@ -1661,7 +1646,7 @@ export function ArchiveView() {
- setDeleteConfirmSession(null)}>
+ setDeleteConfirmSession(null)}>
{t('common.cancel', 'Cancel')}
setBulkDeleteConfirmOpen(false)}
>
diff --git a/packages/components/src/components/archive/web-archive-screen.tsx b/packages/components/src/components/archive/web-archive-screen.tsx
index 385450976..aeb51243e 100644
--- a/packages/components/src/components/archive/web-archive-screen.tsx
+++ b/packages/components/src/components/archive/web-archive-screen.tsx
@@ -7,7 +7,7 @@ import { navigationSidebarHiddenAtom, showNavigationSidebarAtom } from '@/atoms/
import { isMacOSElectronRenderer, useElectronFullscreen } from '@/lib/electron';
import { useWindowDragRegionClass, useWindowsCaptionPadClass } from '@/ui/window-drag-region';
import { isNativeAppShell } from '@/lib/native-platform';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
@@ -76,22 +76,18 @@ export function WebArchiveScreen({
showNavigationSidebar()}
aria-label={t('sessions.leftSidebar.show', 'Show navigation sidebar')}
- className="-ml-1 h-7 w-7 shrink-0 text-muted-foreground"
+ className="-ml-1 shrink-0"
>
) : null}
{isMultiSelectMode ? (
<>
-
+
{t('archive.multiSelect.exit', 'Exit selection')}
@@ -102,22 +98,20 @@ export function WebArchiveScreen({
{t('archive.multiSelect.restore', 'Restore')}
{t('archive.multiSelect.delete', 'Delete')}
diff --git a/packages/components/src/components/bug-report/bug-report-dialog.tsx b/packages/components/src/components/bug-report/bug-report-dialog.tsx
index b96126184..c849e0be3 100644
--- a/packages/components/src/components/bug-report/bug-report-dialog.tsx
+++ b/packages/components/src/components/bug-report/bug-report-dialog.tsx
@@ -10,7 +10,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/ui/dialog';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Label } from '@/ui/label';
import { Textarea } from '@/ui/textarea';
import { CopyButton } from '@/ui/copy-button';
@@ -196,7 +196,7 @@ export function BugReportDialog({
) : null}
-
+
{t('common.cancel', 'Cancel')}
diff --git a/packages/components/src/components/chat/chat-composer.tsx b/packages/components/src/components/chat/chat-composer.tsx
index 37a098c62..05a4452ff 100644
--- a/packages/components/src/components/chat/chat-composer.tsx
+++ b/packages/components/src/components/chat/chat-composer.tsx
@@ -45,7 +45,7 @@ import type { Mention as MentionRange } from '@/ui/mention/index';
import type { PersistedMentionRange } from '@/components/mentions/mention-persistence';
import { toIntlLocale } from '@/lib/intl-locale';
import { useIsMobile } from '@/hooks/use-mobile';
-import { Button, type ButtonProps } from '@/ui/button';
+import { Button, type ButtonProps } from '@lody/ui/button';
import {
Dialog,
DialogClose,
@@ -773,8 +773,9 @@ export function ChatComposer({
onImageRemove?.(image.id)}
aria-label={removeImageLabel}
>
@@ -869,8 +870,9 @@ export function ChatComposer({
onFileRemove?.(file.id)}
aria-label={removeAttachmentLabel}
>
diff --git a/packages/components/src/components/chat/chat-landing-view.tsx b/packages/components/src/components/chat/chat-landing-view.tsx
index 5bf0ca9d0..322d2cabd 100644
--- a/packages/components/src/components/chat/chat-landing-view.tsx
+++ b/packages/components/src/components/chat/chat-landing-view.tsx
@@ -7,7 +7,7 @@ import type { PersistedMentionRange } from '@/components/mentions/mention-persis
import type { AcpCommandSummary, AgentConfigCliType } from '@lody/shared';
import { cn } from '@/lib/utils';
import { useSessionMentionDropZone } from '@/hooks/use-session-mention-drag';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
ChatComposer,
type ChatComposerFileItem,
@@ -270,11 +270,6 @@ export function ChatLandingView({
'border-border bg-background/70 text-foreground hover:bg-muted/60'
);
- const primaryActionButtonClassName = cn(
- 'h-8 w-8 rounded-full shadow-xs transition-all',
- 'bg-foreground text-background hover:bg-foreground/90 hover:text-background active:translate-y-[1px] focus-visible:ring-ring focus-visible:ring-offset-background'
- );
-
// No-agent-config hint shown in scrollable area (not as overlay)
const agentConfigHintNode =
hintType === 'no-agent-config' ? (
@@ -356,12 +351,13 @@ export function ChatLandingView({
{submissionPending ? (
diff --git a/packages/components/src/components/chat/chat-landing.tsx b/packages/components/src/components/chat/chat-landing.tsx
index 65111d598..004150a11 100644
--- a/packages/components/src/components/chat/chat-landing.tsx
+++ b/packages/components/src/components/chat/chat-landing.tsx
@@ -53,7 +53,7 @@ import {
RefreshCw,
X,
} from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
type AgentSelection,
@@ -3417,12 +3417,13 @@ function WorkspaceChatLanding({
-
+
@@ -6118,19 +6119,15 @@ function WorkspaceChatLanding({
primaryAction={
{
void handleSubmit();
}}
disabled={submitDisabled}
aria-label={submitDisabled ? t('chat.submitting') : t('chat.send')}
- /* Match the in-session mobile composer send face: solid
- foreground disc + ArrowUp (not the old primary-tint chip). */
- className={cn(
- 'h-8 w-8 rounded-full shadow-xs transition-all',
- 'bg-foreground text-background hover:bg-foreground/90 hover:text-background active:translate-y-[1px]'
- )}
>
{submitting ? (
@@ -6589,10 +6586,11 @@ function WorkspaceChatLanding({
showNavigationSidebar()}
aria-label={t('chat.leftSidebar.show', 'Show navigation sidebar')}
- className="h-7 w-7 shrink-0 text-muted-foreground"
+ className="shrink-0"
>
diff --git a/packages/components/src/components/chat/machine-pairing-dialog.tsx b/packages/components/src/components/chat/machine-pairing-dialog.tsx
index a07585b36..3cc2b0308 100644
--- a/packages/components/src/components/chat/machine-pairing-dialog.tsx
+++ b/packages/components/src/components/chat/machine-pairing-dialog.tsx
@@ -15,7 +15,7 @@ import { toast } from 'sonner';
import { useConvexErrorMessage } from '@/hooks/use-convex-error-message';
import { writeTextToClipboard } from '@/lib/clipboard';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
Dialog,
DialogContent,
@@ -164,7 +164,7 @@ export function MachinePairingDialog({
{t('machinePairing.createFailed', 'Could not create a connection request.')}
-
+
{t('common.retry', 'Retry')}
@@ -187,7 +187,7 @@ export function MachinePairingDialog({
-
+
{t('machinePairing.downloadDesktop', 'Download Lody Desktop')}
@@ -223,8 +223,8 @@ export function MachinePairingDialog({
void copyCommand()}
aria-label={t('machinePairing.copyCommand', 'Copy command')}
>
@@ -269,7 +269,7 @@ export function MachinePairingDialog({
? t('machinePairing.cancelled', 'This connection request was cancelled.')
: t('machinePairing.expired', 'This connection request has expired.')}
-
+
{t('machinePairing.createNew', 'Create a new request')}
diff --git a/packages/components/src/components/codex-reset/codex-reset-forecast-dialog.tsx b/packages/components/src/components/codex-reset/codex-reset-forecast-dialog.tsx
index 2bc1b30e5..0ba6bd648 100644
--- a/packages/components/src/components/codex-reset/codex-reset-forecast-dialog.tsx
+++ b/packages/components/src/components/codex-reset/codex-reset-forecast-dialog.tsx
@@ -5,7 +5,7 @@ import { enUS, zhCN } from 'date-fns/locale';
import { ExternalLink, Loader2, TimerReset } from 'lucide-react';
import { Badge } from '@/ui/badge';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/ui/dialog';
import { openExternalUrl } from '@/lib/native-browser';
import { cn } from '@/lib/utils';
@@ -139,7 +139,7 @@ function RetryButton({ onRetry }: { onRetry: () => void }) {
const { t } = useTranslation();
return (
-
+
{t('codexReset.retry', 'Try again')}
);
diff --git a/packages/components/src/components/error-boundary-fallback.tsx b/packages/components/src/components/error-boundary-fallback.tsx
index 6fa46a989..3cfc02042 100644
--- a/packages/components/src/components/error-boundary-fallback.tsx
+++ b/packages/components/src/components/error-boundary-fallback.tsx
@@ -10,7 +10,7 @@ import {
Trash2,
} from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
AlertDialog,
AlertDialogAction,
@@ -207,14 +207,14 @@ export function ErrorBoundaryFallback({
) : null}
-
+
{t('errorBoundary.tryAgain', 'Try again')}
{
reloadApp();
}}
@@ -222,7 +222,7 @@ export function ErrorBoundaryFallback({
{t('errorBoundary.reload', 'Reload Lody')}
-
+
{copied ? (
) : (
@@ -301,9 +301,9 @@ export function ErrorBoundaryFallback({
setHardResetOpen(true)}
>
@@ -368,7 +368,7 @@ export function HardResetConfirmDialog({
onConfirm();
}}
disabled={isResetting}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{isResetting
diff --git a/packages/components/src/components/local-projects/add-local-project-dialog.tsx b/packages/components/src/components/local-projects/add-local-project-dialog.tsx
index 0bb379f5c..e94343953 100644
--- a/packages/components/src/components/local-projects/add-local-project-dialog.tsx
+++ b/packages/components/src/components/local-projects/add-local-project-dialog.tsx
@@ -24,7 +24,7 @@ import type {
LocalProjectBrowseRootsResult,
} from '@lody/shared';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Skeleton } from '@/ui/skeleton';
import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@/ui/dialog';
@@ -329,8 +329,9 @@ function BrowseStep({
@@ -380,8 +381,9 @@ function BrowseStep({
onNavigate(drive)}
>
@@ -422,13 +423,7 @@ function BrowseStep({
title={errorView.title}
description={errorView.description}
action={
-
+
{t('common.retry', 'Retry')}
@@ -472,8 +467,8 @@ function BrowseStep({
@@ -519,13 +514,12 @@ function BrowseStep({
)}
-
+
{t('common.cancel', 'Cancel')}
diff --git a/packages/components/src/components/local-projects/mobile-add-local-project-flow.tsx b/packages/components/src/components/local-projects/mobile-add-local-project-flow.tsx
index 81271cd3a..3a4bfd67f 100644
--- a/packages/components/src/components/local-projects/mobile-add-local-project-flow.tsx
+++ b/packages/components/src/components/local-projects/mobile-add-local-project-flow.tsx
@@ -19,7 +19,7 @@ import {
} from 'lucide-react';
import type { LocalProjectBrowseDirectoryEntry } from '@lody/shared';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Skeleton } from '@/ui/skeleton';
import {
@@ -327,7 +327,7 @@ function MobileBrowse({ controller: c }: { controller: RemoteDirectoryPickerCont
title={errorView.title}
description={errorView.description}
action={
-
+
{t('common.retry', 'Retry')}
@@ -386,7 +386,8 @@ function MobileBrowse({ controller: c }: { controller: RemoteDirectoryPickerCont
) : null}
void c.addCurrentFolder()}
>
diff --git a/packages/components/src/components/login-page.tsx b/packages/components/src/components/login-page.tsx
index 284115a32..5e4172fe5 100644
--- a/packages/components/src/components/login-page.tsx
+++ b/packages/components/src/components/login-page.tsx
@@ -6,7 +6,7 @@ import { usePostHog } from '@posthog/react';
import { useAtomValue, useSetAtom } from 'jotai';
import isEmail from 'validator/lib/isEmail';
import { electronDeepLinkSignInInProgressAtom, nativeSignInInProgressAtom } from '@/atoms';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { PasswordInput } from '@/ui/password-input';
@@ -482,35 +482,35 @@ const PROVIDER_CONFIG: {
icon: React.ComponentType;
labelKey: string;
labelDefault: string;
- variant: 'default' | 'outline';
+ variant: 'primary' | 'secondary';
}[] = [
{
id: 'github',
icon: GitHubIcon,
labelKey: 'login.githubSignIn',
labelDefault: 'Continue with GitHub',
- variant: 'default',
+ variant: 'primary',
},
{
id: 'google',
icon: GoogleIcon,
labelKey: 'login.googleSignIn',
labelDefault: 'Continue with Google',
- variant: 'outline',
+ variant: 'secondary',
},
{
id: 'apple',
icon: AppleIcon,
labelKey: 'login.appleSignIn',
labelDefault: 'Continue with Apple',
- variant: 'outline',
+ variant: 'secondary',
},
{
id: 'discord',
icon: DiscordIcon,
labelKey: 'login.discordSignIn',
labelDefault: 'Continue with Discord',
- variant: 'outline',
+ variant: 'secondary',
},
];
@@ -1291,10 +1291,11 @@ export function LoginPage({
return (
{
void handleSocialLogin(provider.id);
}}
- className="h-10 w-full"
+ className="w-full"
disabled={isButtonsDisabled}
variant={provider.variant}
>
@@ -1317,15 +1318,16 @@ export function LoginPage({
-
+
}
label={emailEntryLabel}
@@ -1515,7 +1517,7 @@ export function LoginPage({
) : null}
-
+
{isEmailSubmitting ? (
<>
@@ -1603,8 +1605,9 @@ export function LoginPage({
className="flex flex-col gap-3"
>
void handleElectronBrowserLogin()}
- className="h-10 w-full"
+ className="w-full"
disabled={isButtonsDisabled}
>
-
+
}
label={emailEntryLabel}
diff --git a/packages/components/src/components/loro-app-sidebar.tsx b/packages/components/src/components/loro-app-sidebar.tsx
index ee63a9b00..b6cdb4870 100644
--- a/packages/components/src/components/loro-app-sidebar.tsx
+++ b/packages/components/src/components/loro-app-sidebar.tsx
@@ -105,7 +105,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/ui/dialog';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Checkbox } from '@/ui/checkbox';
import {
ContextMenu,
@@ -423,7 +423,7 @@ export function RemoveLocalProjectDialog({
- onOpenChange(false)} disabled={isRemoving}>
+ onOpenChange(false)} disabled={isRemoving}>
{t('common.cancel', 'Cancel')}
(function IconB
ref={ref}
type="button"
variant="ghost"
- size="icon"
+ icon
className={cn(getLoroSidebarFooterIconButtonClassName(isMobile, active), className)}
{...buttonProps}
>
diff --git a/packages/components/src/components/mobile/mobile-about-settings.tsx b/packages/components/src/components/mobile/mobile-about-settings.tsx
index 30b747900..cd67a2d37 100644
--- a/packages/components/src/components/mobile/mobile-about-settings.tsx
+++ b/packages/components/src/components/mobile/mobile-about-settings.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { Loader2, CheckCircle2, AlertCircle, Download, ExternalLink } from 'lucide-react';
import type { ElectronUpdaterPhase } from '@lody/shared';
import { useAtom } from 'jotai';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Switch } from '@/ui/switch';
import {
developerModeEnabledAtom,
@@ -230,8 +230,7 @@ export function MobileAboutSettings() {
>
{isDownloaded ? (
{
void handleQuitAndInstall();
}}
@@ -246,9 +245,8 @@ export function MobileAboutSettings() {
) : (
{
void handleCheckForUpdates();
}}
diff --git a/packages/components/src/components/mobile/mobile-account-settings.tsx b/packages/components/src/components/mobile/mobile-account-settings.tsx
index 74f0498d0..7af124a74 100644
--- a/packages/components/src/components/mobile/mobile-account-settings.tsx
+++ b/packages/components/src/components/mobile/mobile-account-settings.tsx
@@ -16,7 +16,7 @@ import {
Pencil,
X,
} from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { Badge } from '@/ui/badge';
@@ -452,8 +452,8 @@ export function MobileAccountSettings({
) : null}
{
void onSignOut();
}}
@@ -529,12 +529,7 @@ export function MobileAccountSettings({
title={t('workspace.members.title')}
actions={
hasAdminPermission ? (
- setInviteDialogOpen(true)}
- >
+ setInviteDialogOpen(true)}>
{t('workspace.members.invite')}
@@ -615,9 +610,10 @@ export function MobileAccountSettings({
{isEditable && (
{
setUserToDelete(member.id);
setDeleteUserDialogOpen(true);
@@ -664,9 +660,9 @@ export function MobileAccountSettings({
{
void onCopyInviteLink(getInviteLink(invitation));
}}
@@ -676,9 +672,10 @@ export function MobileAccountSettings({
{hasAdminPermission && (
{
void (async () => {
@@ -730,8 +727,7 @@ export function MobileAccountSettings({
description={t('settings.account.cliAuth.description')}
actions={
{
setCliApiKeyDialogOpen(true);
}}
@@ -804,9 +800,9 @@ export function MobileAccountSettings({
setCliApiKeyToRevoke(apiKey)}
disabled={revokingCliApiKeyId === apiKey.id}
>
@@ -835,9 +831,9 @@ export function MobileAccountSettings({
stack
>
setLeaveDialogOpen(true)}
>
{t('workspace.danger.leaveWorkspace.button')}
@@ -851,9 +847,9 @@ export function MobileAccountSettings({
stack
>
{
// A live subscription blocks deletion; explain instead of
// letting the backend reject the delete.
@@ -879,9 +875,9 @@ export function MobileAccountSettings({
stack
>
setDeleteAccountDialogOpen(true)}
>
@@ -910,8 +906,8 @@ export function MobileAccountSettings({
{t('settings.account.cliAuth.usageHint')}
{
void onCopyGeneratedCliApiKey?.();
}}
@@ -938,8 +934,8 @@ export function MobileAccountSettings({
)}
handleCliApiKeyDialogOpenChange(false)}
disabled={isCreatingCliApiKey}
>
@@ -947,7 +943,7 @@ export function MobileAccountSettings({
{!hasGeneratedCliApiKey && (
{
void handleCreateCliApiKey();
}}
@@ -991,7 +987,7 @@ export function MobileAccountSettings({
})();
}}
disabled={!cliApiKeyToRevoke || Boolean(revokingCliApiKeyId)}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{revokingCliApiKeyId ? (
@@ -1036,7 +1032,7 @@ export function MobileAccountSettings({
onClick={() => {
void handleRemoveMember();
}}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{t('common.remove')}
@@ -1070,7 +1066,7 @@ export function MobileAccountSettings({
})();
}}
disabled={isLeaving}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{isLeaving ? (
<>
@@ -1157,7 +1153,7 @@ export function MobileAccountSettings({
{
setDeleteAccountDialogOpen(false);
setDeleteAccountConfirmText('');
diff --git a/packages/components/src/components/mobile/mobile-acp-history-sheet.tsx b/packages/components/src/components/mobile/mobile-acp-history-sheet.tsx
index 9a15cb703..7f2005165 100644
--- a/packages/components/src/components/mobile/mobile-acp-history-sheet.tsx
+++ b/packages/components/src/components/mobile/mobile-acp-history-sheet.tsx
@@ -7,7 +7,7 @@ import { AlertCircle, Check, Download, Loader2, RefreshCw, X } from 'lucide-reac
import type { LocalProjectHistoryCatalogItem, LocalProjectHistoryProvider } from '@lody/shared';
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerTitle } from '@/ui/drawer';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
AlertDialog,
AlertDialogAction,
@@ -390,9 +390,9 @@ export function MobileAcpHistorySheet({
{
if (!canResolveConflict) return;
@@ -431,8 +431,8 @@ export function MobileAcpHistorySheet({
>
{
void onImportHistory(row, state.provider);
@@ -477,8 +477,7 @@ export function MobileAcpHistorySheet({
{t('common.cancel', 'Cancel')}
{t('workspace.projects.resolveHistoryConflict', 'Re-import')}
diff --git a/packages/components/src/components/mobile/mobile-archive-screen.tsx b/packages/components/src/components/mobile/mobile-archive-screen.tsx
index 3e1a0a280..ff86aab3f 100644
--- a/packages/components/src/components/mobile/mobile-archive-screen.tsx
+++ b/packages/components/src/components/mobile/mobile-archive-screen.tsx
@@ -1,8 +1,7 @@
import type { ReactNode } from 'react';
import { Archive, PanelLeft, Trash2, Undo2, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
-import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { TooltipProvider } from '@/ui/tooltip';
export type MobileArchiveScreenProps = {
@@ -45,12 +44,7 @@ export function MobileArchiveScreen({
{isMultiSelectMode ? (
<>
-
+
{t('archive.multiSelect.exit', 'Exit selection')}
@@ -61,22 +55,20 @@ export function MobileArchiveScreen({
{t('archive.multiSelect.restore', 'Restore')}
{t('archive.multiSelect.delete', 'Delete')}
@@ -84,15 +76,7 @@ export function MobileArchiveScreen({
>
) : (
<>
-
+
Open sidebar
diff --git a/packages/components/src/components/mobile/mobile-chat-landing-screen.tsx b/packages/components/src/components/mobile/mobile-chat-landing-screen.tsx
index eccf8afc4..5140d72d6 100644
--- a/packages/components/src/components/mobile/mobile-chat-landing-screen.tsx
+++ b/packages/components/src/components/mobile/mobile-chat-landing-screen.tsx
@@ -1,6 +1,6 @@
import type { ReactNode } from 'react';
import { PanelLeft } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
export type MobileChatLandingScreenProps = {
@@ -31,14 +31,12 @@ export function MobileChatLandingScreen({
-
+
Open sidebar
{title}
diff --git a/packages/components/src/components/mobile/mobile-chat-list.tsx b/packages/components/src/components/mobile/mobile-chat-list.tsx
index e78604f37..8f6fdc409 100644
--- a/packages/components/src/components/mobile/mobile-chat-list.tsx
+++ b/packages/components/src/components/mobile/mobile-chat-list.tsx
@@ -1383,7 +1383,7 @@ export function MobileChatList({
void handleDelete();
}}
disabled={isDeleting}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{selectionLabels?.confirmDelete ?? '删除'}
diff --git a/packages/components/src/components/mobile/mobile-create-workspace-sheet.tsx b/packages/components/src/components/mobile/mobile-create-workspace-sheet.tsx
index 2fc411cc4..730d392ce 100644
--- a/packages/components/src/components/mobile/mobile-create-workspace-sheet.tsx
+++ b/packages/components/src/components/mobile/mobile-create-workspace-sheet.tsx
@@ -5,7 +5,7 @@ import { cloudOperations } from '@/lib/cloud-api-operations';
import { useCloudQuery } from '@lody/platform/react';
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerTitle } from '@/ui/drawer';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { useOrganization } from '../../hooks/useOrganization';
@@ -244,7 +244,7 @@ export function MobileCreateWorkspaceSheet({
>
handleClose(false)}
disabled={creating}
diff --git a/packages/components/src/components/mobile/mobile-delete-workspace-sheet.tsx b/packages/components/src/components/mobile/mobile-delete-workspace-sheet.tsx
index 590e0a764..e86aea3d1 100644
--- a/packages/components/src/components/mobile/mobile-delete-workspace-sheet.tsx
+++ b/packages/components/src/components/mobile/mobile-delete-workspace-sheet.tsx
@@ -2,14 +2,8 @@ import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { AlertTriangle, Loader2, X } from 'lucide-react';
-import {
- Drawer,
- DrawerClose,
- DrawerContent,
- DrawerDescription,
- DrawerTitle,
-} from '@/ui/drawer';
-import { Button } from '@/ui/button';
+import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerTitle } from '@/ui/drawer';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { cn } from '@/lib/utils';
@@ -117,8 +111,7 @@ export function MobileDeleteWorkspaceSheet({
{t('workspace.danger.deleteWorkspace.confirmDescription', {
workspace: workspaceName,
- defaultValue:
- 'Permanently delete "{{workspace}}". This action cannot be undone.',
+ defaultValue: 'Permanently delete "{{workspace}}". This action cannot be undone.',
})}
@@ -179,7 +172,7 @@ export function MobileDeleteWorkspaceSheet({
>
onOpenChange(false)}
disabled={isDeleting}
diff --git a/packages/components/src/components/mobile/mobile-file-viewer-drawer.tsx b/packages/components/src/components/mobile/mobile-file-viewer-drawer.tsx
index 8553b4434..3a2eb6479 100644
--- a/packages/components/src/components/mobile/mobile-file-viewer-drawer.tsx
+++ b/packages/components/src/components/mobile/mobile-file-viewer-drawer.tsx
@@ -12,7 +12,7 @@ import { VaulDrawerBody } from '@/components/mobile/vaul-drawer-edge-back-zone';
import { getSessionDetailTouchIconButtonClassName } from '@/lib/session-detail-a11y';
import { getBasename } from '@/lib';
import { isNativeAppShell } from '@/lib/native-platform';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Drawer, DrawerContent, DrawerTitle } from '@/ui/drawer';
const MOBILE_FILE_DRAWER_HEADER_INSET = 'calc(3.5rem + var(--safe-area-top))';
@@ -90,7 +90,7 @@ export function MobileFileViewerDrawer({
onOpenChange(false)}
aria-label={t('common.back', 'Back')}
@@ -104,7 +104,7 @@ export function MobileFileViewerDrawer({
setMenuOpen(true)}
aria-label={t('sessions.fileViewer.moreActions', 'File actions')}
diff --git a/packages/components/src/components/mobile/mobile-integrations-settings.tsx b/packages/components/src/components/mobile/mobile-integrations-settings.tsx
index 36e583772..b5009b65b 100644
--- a/packages/components/src/components/mobile/mobile-integrations-settings.tsx
+++ b/packages/components/src/components/mobile/mobile-integrations-settings.tsx
@@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Badge } from '@/ui/badge';
import { ArrowUpRight, Book, Github, Loader2, Search } from 'lucide-react';
import { useCloudAction, useCloudMutation } from '@lody/platform/react';
@@ -88,7 +88,9 @@ function MobileCloudIntegrationsSettings() {
const setPersonalOperationPreference = useCloudMutation(
cloudOperations.github.setPersonalOperationPreference
);
- const refreshPersonalGitHubProfile = useCloudAction(cloudOperations.github.refreshPersonalGitHubProfile);
+ const refreshPersonalGitHubProfile = useCloudAction(
+ cloudOperations.github.refreshPersonalGitHubProfile
+ );
const [connectingToGitHub, setConnectingToGitHub] = useState(false);
const [updatingPersonalPreference, setUpdatingPersonalPreference] = useState(false);
const [authorizingPersonalGitHub, setAuthorizingPersonalGitHub] = useState(false);
@@ -349,9 +351,7 @@ function MobileCloudIntegrationsSettings() {
>
{canManage ? (
{
void handleConnectGitHub();
}}
diff --git a/packages/components/src/components/mobile/mobile-remove-local-project-sheet.tsx b/packages/components/src/components/mobile/mobile-remove-local-project-sheet.tsx
index 217545f54..0916696ad 100644
--- a/packages/components/src/components/mobile/mobile-remove-local-project-sheet.tsx
+++ b/packages/components/src/components/mobile/mobile-remove-local-project-sheet.tsx
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
import { Loader2, X } from 'lucide-react';
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerTitle } from '@/ui/drawer';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Checkbox } from '@/ui/checkbox';
import { cn } from '@/lib/utils';
@@ -265,7 +265,7 @@ export function MobileRemoveLocalProjectSheet({
>
onOpenChange(false)}
disabled={isRemoving}
diff --git a/packages/components/src/components/not-found.tsx b/packages/components/src/components/not-found.tsx
index a98e98573..9ffd5d47b 100644
--- a/packages/components/src/components/not-found.tsx
+++ b/packages/components/src/components/not-found.tsx
@@ -3,7 +3,7 @@
* 当用户访问不存在的页面时显示
*/
import { useNavigate } from '@tanstack/react-router';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Home, ArrowLeft } from 'lucide-react';
import { useTranslation } from 'react-i18next';
@@ -48,12 +48,12 @@ export function NotFound() {
-
+
{t('notFound.goBack', 'Go Back')}
-
+
{t('notFound.goHome', 'Go Home')}
diff --git a/packages/components/src/components/onboarding/ceremony/intro-sequence.tsx b/packages/components/src/components/onboarding/ceremony/intro-sequence.tsx
index 941785ff1..d03fec258 100644
--- a/packages/components/src/components/onboarding/ceremony/intro-sequence.tsx
+++ b/packages/components/src/components/onboarding/ceremony/intro-sequence.tsx
@@ -5,7 +5,7 @@ import quietWork from '@/assets/onboarding/intro/quiet-work.png';
import continuousScroll from '@/assets/onboarding/intro/continuous-scroll.png';
import readyToBegin from '@/assets/onboarding/intro/ready-to-begin.png';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { WINDOW_DRAG_EXEMPT_CLASS } from '@/ui/window-drag-region';
import type { AudioLayers } from './use-onboarding-audio';
import { playClick, playCut, playReveal, playSelect } from './ui-sounds';
@@ -395,10 +395,10 @@ export function IntroSequence({
{beat.final ? (
diff --git a/packages/components/src/components/onboarding/onboarding-shell.tsx b/packages/components/src/components/onboarding/onboarding-shell.tsx
index 76cfe6072..0bc1a28d0 100644
--- a/packages/components/src/components/onboarding/onboarding-shell.tsx
+++ b/packages/components/src/components/onboarding/onboarding-shell.tsx
@@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next';
import { PlatformContext } from '@lody/platform/react';
import { ArrowLeft, Check, Loader2 } from 'lucide-react';
import { AnimatePresence, motion } from 'framer-motion';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { TourStill, type TourStillDebugOptions } from './tour/tour-still';
import { DEFAULT_TOUR_IDENTITY, type TourIdentity } from './tour/tour-fixtures';
import type { TourAppTracks, TourConfigurationState } from './tour/tour-app';
@@ -595,15 +595,14 @@ export function OnboardingBackButton({ onClick, disabled, label }: OnboardingBac
return (
playHover()}
+ size="large"
onClick={() => {
// Going back falls in pitch, as it does everywhere else in the flow.
playBack();
onClick();
}}
disabled={disabled}
- className="gap-2"
>
{label ?? t('common.back', 'Back')}
@@ -634,8 +633,8 @@ export function OnboardingNextButton({
const text = label ?? (isFinish ? t('common.finish', 'Finish') : t('common.next', 'Next'));
return (
playHover()}
+ size="large"
onClick={() => {
// Advancing rises; the last step gets the confirming click instead.
if (isFinish) playClick();
@@ -643,7 +642,6 @@ export function OnboardingNextButton({
onClick();
}}
disabled={disabled}
- className="gap-2"
>
{loading === true ? (
diff --git a/packages/components/src/components/onboarding/screens/first-task-screen.tsx b/packages/components/src/components/onboarding/screens/first-task-screen.tsx
index b55f12cde..db137c527 100644
--- a/packages/components/src/components/onboarding/screens/first-task-screen.tsx
+++ b/packages/components/src/components/onboarding/screens/first-task-screen.tsx
@@ -21,7 +21,7 @@ import { useSessionActions } from '@/hooks/use-session-actions';
import { buildAgentPrompt } from '@/lib';
import { cn } from '@/lib/utils';
import { AgentIcon } from '@/components/icons/agent-icon';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/ui/select';
import { Textarea } from '@/ui/textarea';
import { getFirstTaskPrimaryAction } from '../first-task-primary-action';
@@ -248,12 +248,7 @@ export function FirstTaskScreen({
secondaryAction={ }
primaryAction={
-
+
{t('onboarding.firstTask.skip', 'Skip for now')}
}
primaryAction={
-
+
{t('onboarding.invite.skip', 'Skip')}
{sending ? : }
@@ -100,7 +99,7 @@ export function InviteScreenView({
disabled={sending}
/>
-
+
{t('onboarding.invite.add', 'Add')}
@@ -132,9 +131,10 @@ export function InviteScreenView({
) : (
onRemove(invite.id)}
disabled={sending}
>
diff --git a/packages/components/src/components/onboarding/screens/login-screen.tsx b/packages/components/src/components/onboarding/screens/login-screen.tsx
index 39dc2acdc..129fc0e1d 100644
--- a/packages/components/src/components/onboarding/screens/login-screen.tsx
+++ b/packages/components/src/components/onboarding/screens/login-screen.tsx
@@ -3,7 +3,7 @@ import { useRouter } from '@tanstack/react-router';
import { useTranslation } from 'react-i18next';
import { usePlatformSession } from '@lody/platform/react';
import { ExternalLink, Loader2, LogIn } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { OnboardingBackButton, OnboardingShell } from '../onboarding-shell';
import { useOnboardingAnalytics } from '../onboarding-analytics';
@@ -96,7 +96,7 @@ export function LoginScreen({ onBack, onNext }: { onBack: () => void; onNext: ()
)}
secondaryAction={ }
primaryAction={
-
+
{locked ? : }
{openingBrowser
? t('onboarding.login.openBrowserAgain', 'Open browser again')
diff --git a/packages/components/src/components/onboarding/screens/projects-screen.tsx b/packages/components/src/components/onboarding/screens/projects-screen.tsx
index 9caaf8884..4fa5abac6 100644
--- a/packages/components/src/components/onboarding/screens/projects-screen.tsx
+++ b/packages/components/src/components/onboarding/screens/projects-screen.tsx
@@ -23,7 +23,7 @@ import { isElectronRenderer } from '@/lib/electron';
import { getIpcServices } from '@/lib/electron-ipc-client';
import { selectAndWriteLocalProject } from '@/lib/local-project-import';
import { openExternalUrl } from '@/lib/native-browser';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { OnboardingShell, OnboardingBackButton, OnboardingNextButton } from '../onboarding-shell';
import { useOnboardingAnalytics } from '../onboarding-analytics';
@@ -114,12 +114,7 @@ export function ProjectsScreenView({
secondaryAction={ }
primaryAction={
-
+
{t('onboarding.projects.skip', 'Skip for now')}
}
primaryAction={
-
+
{t('onboarding.providers.skip', 'Skip for now')}
onEdit(config)}
>
{t('common.edit', 'Edit')}
@@ -456,17 +451,13 @@ export function ProvidersScreenView({
{activity ? (
) : status !== 'needs-auth' ? (
- // Always outline, passed or not. Ghosting the
- // button once a test succeeded emptied the slot of
- // everything but a word, so the one row that had
- // been verified read as a hole in the column while
- // its neighbours kept a bordered control. A fixed
- // slot only holds the column if what sits in it
- // keeps its shape too.
+ // Keep the same visual role after success. Changing
+ // the action to a ghost made the verified row read
+ // as a hole in this fixed-width column.
onTest(config)}
>
@@ -478,9 +469,11 @@ export function ProvidersScreenView({
onDelete(config)}
>
@@ -1326,7 +1319,7 @@ export function ProvidersScreen({
event.preventDefault();
void handleConfirmDelete();
}}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{deleting && }
{t('common.delete', 'Delete')}
diff --git a/packages/components/src/components/onboarding/screens/summary-screen.tsx b/packages/components/src/components/onboarding/screens/summary-screen.tsx
index 2bc531287..ffda12631 100644
--- a/packages/components/src/components/onboarding/screens/summary-screen.tsx
+++ b/packages/components/src/components/onboarding/screens/summary-screen.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { Check, Clock3, Loader2, Minus, RotateCcw, XCircle } from 'lucide-react';
import type { ProviderSetupFailureCode } from '@lody/shared';
import { Table, TableBody, TableCell, TableRow } from '@/ui/table';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { OnboardingBackButton, OnboardingNextButton, OnboardingShell } from '../onboarding-shell';
import { useOnboardingAnalytics } from '../onboarding-analytics';
@@ -113,9 +113,8 @@ export function SummaryScreen({
{
if (retryingAgent) return;
diff --git a/packages/components/src/components/onboarding/screens/workspace-screen.tsx b/packages/components/src/components/onboarding/screens/workspace-screen.tsx
index 1926cb0a5..91f1189e8 100644
--- a/packages/components/src/components/onboarding/screens/workspace-screen.tsx
+++ b/packages/components/src/components/onboarding/screens/workspace-screen.tsx
@@ -8,7 +8,7 @@ import { setWorkspaceContextAtom } from '@/atoms/workspace-context';
import { cloudOperations } from '@/lib/cloud-api-operations';
import { toast } from 'sonner';
import { useCloudQuery, usePlatform, usePlatformWorkspaces } from '@lody/platform/react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { cn } from '@/lib/utils';
@@ -216,7 +216,7 @@ export function WorkspaceScreenView({
}
primaryAction={
creating ? (
-
+
{saving ? : null}
{createError
? t('common.retry', 'Retry')
@@ -312,9 +312,8 @@ export function WorkspaceScreenView({
{newSlugCheckError}
@@ -404,11 +403,10 @@ export function WorkspaceScreenView({
{t('common.retry', 'Retry')}
diff --git a/packages/components/src/components/organization-switcher.tsx b/packages/components/src/components/organization-switcher.tsx
index 86512bd94..578b3eac4 100644
--- a/packages/components/src/components/organization-switcher.tsx
+++ b/packages/components/src/components/organization-switcher.tsx
@@ -14,7 +14,7 @@ import {
DropdownMenuTrigger,
} from '@/ui/dropdown-menu';
import { Badge } from '@/ui/badge';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
Dialog,
DialogContent,
@@ -82,10 +82,7 @@ function CloudOrganizationSwitcher() {
const isMobile = useIsMobile();
// Paid workspaces only (free ones are omitted by the query); keyed by the
// better-auth organization id, which is what billing uses as workspaceId.
- const paidPlanTiers = useCloudQuery(
- cloudOperations.billing.getMyPaidWorkspacePlanTiers,
- {}
- );
+ const paidPlanTiers = useCloudQuery(cloudOperations.billing.getMyPaidWorkspacePlanTiers, {});
const planTierByWorkspaceId = useMemo(
() => new Map((paidPlanTiers ?? []).map((entry) => [entry.workspaceId, entry.planTier])),
[paidPlanTiers]
@@ -151,7 +148,7 @@ function CloudOrganizationSwitcher() {
-
+
- setDialogOpen(false)} disabled={creating}>
+ setDialogOpen(false)} disabled={creating}>
{t('common.cancel')}
{
// Remove focus to avoid aria-hidden warnings in some browsers.
diff --git a/packages/components/src/components/pages/accept-invitation-page.tsx b/packages/components/src/components/pages/accept-invitation-page.tsx
index c148f54cd..bd19418ef 100644
--- a/packages/components/src/components/pages/accept-invitation-page.tsx
+++ b/packages/components/src/components/pages/accept-invitation-page.tsx
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Loader2 } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
// Placeholder workspace avatar until per-workspace avatars are supported.
import workspaceAvatarPlaceholder from '@/assets/icon-transparent.png';
@@ -105,7 +105,7 @@ export function AcceptInvitationPage({
)}
{roleLabel ? : null}
-
+
{t('invite.idle.accept', 'Accept invitation')}
>
@@ -142,7 +142,7 @@ export function AcceptInvitationPage({
/>
) : null}
-
+
{t('invite.authRequired.continue', 'Continue with invited email')}
>
@@ -168,10 +168,10 @@ export function AcceptInvitationPage({
/>
{roleLabel ? : null}
-
+
{t('invite.accountMismatch.switch', 'Switch account')}
-
+
{t('invite.error.backButton', 'Back to home')}
>
@@ -186,7 +186,7 @@ export function AcceptInvitationPage({
'For security, verify the email address named by this invitation before accepting it.'
)}
-
+
{t('invite.verificationRequired.continue', 'Continue to verification')}
>
@@ -212,7 +212,7 @@ export function AcceptInvitationPage({
{errorMessage || t('invite.error.notFound', 'Invitation not found or expired.')}
-
+
{t('invite.error.backButton', 'Back to home')}
>
diff --git a/packages/components/src/components/pages/complete-email-page.tsx b/packages/components/src/components/pages/complete-email-page.tsx
index d6df6cca1..7557df940 100644
--- a/packages/components/src/components/pages/complete-email-page.tsx
+++ b/packages/components/src/components/pages/complete-email-page.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { Loader2, Mail } from 'lucide-react';
import { Alert, AlertDescription } from '@/ui/alert';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/ui/card';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
@@ -42,7 +42,7 @@ export function CompleteEmailPage({
diff --git a/packages/components/src/components/pages/create-workspace-page.tsx b/packages/components/src/components/pages/create-workspace-page.tsx
index 323c5b7f6..c0a0e4216 100644
--- a/packages/components/src/components/pages/create-workspace-page.tsx
+++ b/packages/components/src/components/pages/create-workspace-page.tsx
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next';
import { ArrowLeft, ArrowRight, Building2, CreditCard } from 'lucide-react';
import { Loader2 } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/ui/card';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
@@ -67,10 +67,10 @@ export function CreateWorkspacePage({
{t('organization.backToPreviousWorkspace', 'Back to previous workspace')}
@@ -226,6 +226,7 @@ export function CreateWorkspacePage({
) : null}
{creating ? (
diff --git a/packages/components/src/components/pages/desktop-checkout-return-page.tsx b/packages/components/src/components/pages/desktop-checkout-return-page.tsx
index ada2bafce..97359610f 100644
--- a/packages/components/src/components/pages/desktop-checkout-return-page.tsx
+++ b/packages/components/src/components/pages/desktop-checkout-return-page.tsx
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/ui/card';
import lodyLogo from '@/assets/lody-icon.png';
@@ -46,8 +46,13 @@ export function DesktopCheckoutReturnPage({
-
- {deepLink ? {openLabel} : {openLabel} }
+ : undefined}
+ size="large"
+ className="w-full"
+ disabled={!deepLink}
+ >
+ {openLabel}
diff --git a/packages/components/src/components/pages/desktop-github-install-page.tsx b/packages/components/src/components/pages/desktop-github-install-page.tsx
index 922641e42..5d9303411 100644
--- a/packages/components/src/components/pages/desktop-github-install-page.tsx
+++ b/packages/components/src/components/pages/desktop-github-install-page.tsx
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next';
import { motion } from 'framer-motion';
import { Github, Loader2 } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/ui/card';
import lodyLogo from '@/assets/lody-icon.png';
@@ -41,11 +41,11 @@ export function DesktopGithubInstallPage({ deepLink }: DesktopGithubInstallPageP
{deepLink ? (
-
- {openLabel}
+ } size="large" className="w-full">
+ {openLabel}
) : (
-
+
{openLabel}
)}
diff --git a/packages/components/src/components/pages/device-auth-page.tsx b/packages/components/src/components/pages/device-auth-page.tsx
index e696ca3d0..7d2330180 100644
--- a/packages/components/src/components/pages/device-auth-page.tsx
+++ b/packages/components/src/components/pages/device-auth-page.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { AlertCircle, CheckCircle2, Loader2 } from 'lucide-react';
import { Alert, AlertDescription } from '@/ui/alert';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/ui/card';
import { Input } from '@/ui/input';
diff --git a/packages/components/src/components/pages/email-verified-page.tsx b/packages/components/src/components/pages/email-verified-page.tsx
index 142a0311d..d5101faf9 100644
--- a/packages/components/src/components/pages/email-verified-page.tsx
+++ b/packages/components/src/components/pages/email-verified-page.tsx
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/ui/card';
import lodyLogo from '@/assets/lody-icon.png';
diff --git a/packages/components/src/components/pages/forgot-password-page.tsx b/packages/components/src/components/pages/forgot-password-page.tsx
index 2c4095ba5..36dbe772d 100644
--- a/packages/components/src/components/pages/forgot-password-page.tsx
+++ b/packages/components/src/components/pages/forgot-password-page.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { CheckCircle2, Loader2, Mail } from 'lucide-react';
import { Alert, AlertDescription } from '@/ui/alert';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/ui/card';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
diff --git a/packages/components/src/components/pages/reset-password-page.tsx b/packages/components/src/components/pages/reset-password-page.tsx
index 4281f83fa..f05603776 100644
--- a/packages/components/src/components/pages/reset-password-page.tsx
+++ b/packages/components/src/components/pages/reset-password-page.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { CheckCircle2, KeyRound, Loader2 } from 'lucide-react';
import { Alert, AlertDescription } from '@/ui/alert';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/ui/card';
import { Label } from '@/ui/label';
import { PasswordInput } from '@/ui/password-input';
diff --git a/packages/components/src/components/pages/workspace-join-request-page.tsx b/packages/components/src/components/pages/workspace-join-request-page.tsx
index a9eb0b38c..a2b4b0a4c 100644
--- a/packages/components/src/components/pages/workspace-join-request-page.tsx
+++ b/packages/components/src/components/pages/workspace-join-request-page.tsx
@@ -1,6 +1,6 @@
import { Loader2 } from 'lucide-react';
import { useTranslation } from 'react-i18next';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Textarea } from '@/ui/textarea';
import workspaceAvatarPlaceholder from '@/assets/icon-transparent.png';
@@ -86,7 +86,7 @@ export function WorkspaceJoinRequestPage({
'Sign in or create an account to request access. You can use any verified email.'
)}
-
+
{t('joinRequest.continue', 'Continue')}
>
@@ -98,7 +98,7 @@ export function WorkspaceJoinRequestPage({
email: currentEmail || '',
})}
-
+
{t('joinRequest.verifyAction', 'Verify email')}
>
@@ -134,7 +134,8 @@ export function WorkspaceJoinRequestPage({
})}
@@ -156,7 +157,7 @@ export function WorkspaceJoinRequestPage({
? t('joinRequest.approved', 'Your request was approved.')
: t('joinRequest.alreadyMember', 'You are already a member of this workspace.')}
-
+
{t('joinRequest.openWorkspace', 'Open workspace')}
>
diff --git a/packages/components/src/components/preview/visual-annotation-draft-composer.tsx b/packages/components/src/components/preview/visual-annotation-draft-composer.tsx
index b9cc30f49..eebd54fca 100644
--- a/packages/components/src/components/preview/visual-annotation-draft-composer.tsx
+++ b/packages/components/src/components/preview/visual-annotation-draft-composer.tsx
@@ -3,7 +3,7 @@ import { Loader2, Send, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Textarea } from '@/ui/textarea';
export type VisualAnnotationDraftComposerProps = {
@@ -50,8 +50,9 @@ export function VisualAnnotationDraftComposer({
@@ -66,7 +67,12 @@ export function VisualAnnotationDraftComposer({
autoFocus={autoFocus}
/>
-
+
{submitting ? (
) : (
diff --git a/packages/components/src/components/sessions/ask-user-question-card.tsx b/packages/components/src/components/sessions/ask-user-question-card.tsx
index bb473cf17..95a7ffc2e 100644
--- a/packages/components/src/components/sessions/ask-user-question-card.tsx
+++ b/packages/components/src/components/sessions/ask-user-question-card.tsx
@@ -20,7 +20,7 @@ import {
getServerNow,
} from '@lody/shared';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/ui/dialog';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/ui/tooltip';
import { cn } from '@/lib/utils';
@@ -494,12 +494,13 @@ export function AskUserQuestionCard({ meta, mode, className }: AskUserQuestionCa
{!isReadonly && mode.kind === 'interactive' ? (
{mode.isPendingCancel ? (
@@ -640,11 +641,10 @@ export function AskUserQuestionCard({ meta, mode, className }: AskUserQuestionCa
mode.kind === 'interactive' && submit(drafts)}
- className="h-8 gap-1.5 px-3 text-xs"
>
{mode.kind === 'interactive' && mode.isPendingSubmit ? (
@@ -661,11 +661,11 @@ export function AskUserQuestionCard({ meta, mode, className }: AskUserQuestionCa
{t('sessions.askQuestion.prev', 'Prev')}
@@ -676,11 +676,11 @@ export function AskUserQuestionCard({ meta, mode, className }: AskUserQuestionCa
{isReadonly ? (
{t('sessions.askQuestion.next', 'Next')}
@@ -688,11 +688,10 @@ export function AskUserQuestionCard({ meta, mode, className }: AskUserQuestionCa
) : isLast ? (
mode.kind === 'interactive' && submit(drafts)}
- className="h-6 gap-1.5 px-3 text-xs"
>
{mode.kind === 'interactive' && mode.isPendingSubmit ? (
@@ -704,14 +703,11 @@ export function AskUserQuestionCard({ meta, mode, className }: AskUserQuestionCa
) : (
{t('sessions.askQuestion.next', 'Next')}
diff --git a/packages/components/src/components/sessions/auto-review-info.tsx b/packages/components/src/components/sessions/auto-review-info.tsx
index 499e9666d..e18cbafea 100644
--- a/packages/components/src/components/sessions/auto-review-info.tsx
+++ b/packages/components/src/components/sessions/auto-review-info.tsx
@@ -8,7 +8,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/ui/dialog';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
/**
* The contract the user reads once.
@@ -129,11 +129,11 @@ export function AutoReviewConfirmDialog({
- onOpenChange(false)}>
+ onOpenChange(false)}>
{t('common.cancel', 'Cancel')}
{
onConfirm();
onOpenChange(false);
@@ -179,11 +179,11 @@ export function ReviewAgentSetupDialog({
- onOpenChange(false)}>
+ onOpenChange(false)}>
{t('common.cancel', 'Cancel')}
{
onOpenChange(false);
onOpenSettings();
diff --git a/packages/components/src/components/sessions/auto-review-status.tsx b/packages/components/src/components/sessions/auto-review-status.tsx
index 7cbdd9438..8e584030c 100644
--- a/packages/components/src/components/sessions/auto-review-status.tsx
+++ b/packages/components/src/components/sessions/auto-review-status.tsx
@@ -6,7 +6,7 @@ import {
type ReviewRun,
type ReviewRunState,
} from '@lody/shared';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
/**
@@ -85,8 +85,7 @@ export function AutoReviewStatus({
{busy ? (
- ) : run.state === 'merged' ||
- (run.state === 'reviewed' && blocking.length === 0) ? (
+ ) : run.state === 'merged' || (run.state === 'reviewed' && blocking.length === 0) ? (
) : (
@@ -108,8 +107,9 @@ export function AutoReviewStatus({
- {run.blocked ? (
- {run.blocked.summary}
- ) : null}
+ {run.blocked ? {run.blocked.summary}
: null}
{/* The confirmation prompt has exactly one exit, and this is it. Without
the button the run waits forever: the workspace-level flag that would
satisfy the gate is only ever written by the merge it gates. */}
{run.state === 'awaiting_merge_confirmation' && onConfirmMerge ? (
-
+
{t('sessions.autoReview.confirmMerge', 'Merge now')}
@@ -141,12 +139,7 @@ export function AutoReviewStatus({
{run.state === 'paused' && onResume ? (
-
+
{t('sessions.autoReview.resume', 'Resume')}
@@ -170,8 +163,8 @@ export function AutoReviewStatus({
{onFixFinding && canFixManually ? (
onFixFinding(finding)}
>
{t('sessions.autoReview.fixThis', 'Fix this')}
diff --git a/packages/components/src/components/sessions/chat-share-image-dialog.tsx b/packages/components/src/components/sessions/chat-share-image-dialog.tsx
index 8854488f8..3b7bf9571 100644
--- a/packages/components/src/components/sessions/chat-share-image-dialog.tsx
+++ b/packages/components/src/components/sessions/chat-share-image-dialog.tsx
@@ -8,7 +8,7 @@ import { cn } from '@/lib/utils';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/ui/dialog';
import { Label } from '@/ui/label';
import { Input } from '@/ui/input';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { copyChatShareImage, exportChatShareImage } from '@/lib/chat-share-image-export';
import { Switch } from '@/ui/switch';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/ui/select';
@@ -462,7 +462,7 @@ export function ChatShareImageDialog({
) : null}
void handleCopy()}
disabled={exporting || !assetsReady || messages.length === 0}
>
diff --git a/packages/components/src/components/sessions/components/file-tree-view.tsx b/packages/components/src/components/sessions/components/file-tree-view.tsx
index d943a53a9..31b4dc1a2 100644
--- a/packages/components/src/components/sessions/components/file-tree-view.tsx
+++ b/packages/components/src/components/sessions/components/file-tree-view.tsx
@@ -864,7 +864,7 @@ const AutoFileTreeView = ({
localError ?? t('sessions.localProject.files.loadFailed', 'Failed to load files.')
}
action={
-
+
{t('sessions.codeSession.files.retry', 'Try again')}
diff --git a/packages/components/src/components/sessions/floating-permission-request.tsx b/packages/components/src/components/sessions/floating-permission-request.tsx
index 6b6b34634..1657993d6 100644
--- a/packages/components/src/components/sessions/floating-permission-request.tsx
+++ b/packages/components/src/components/sessions/floating-permission-request.tsx
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ChevronDown, ChevronRight, Loader2 } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/ui/card';
import { ScrollArea } from '@/ui/scroll-area';
import { cn } from '@/lib/utils';
@@ -275,12 +275,12 @@ export function PermissionRequestCard({
return (
onSelect(option.optionId)}
diff --git a/packages/components/src/components/sessions/managed-preview-surface.tsx b/packages/components/src/components/sessions/managed-preview-surface.tsx
index ea47ede47..bbec6a0ad 100644
--- a/packages/components/src/components/sessions/managed-preview-surface.tsx
+++ b/packages/components/src/components/sessions/managed-preview-surface.tsx
@@ -44,7 +44,7 @@ import {
} from '@lody/shared/preview-comment-types';
import { userAtom } from '@/atoms';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Textarea } from '@/ui/textarea';
import { toast } from 'sonner';
import { VisualAnnotationCommentsOverlay } from '@/components/preview/visual-annotation-comments-overlay';
@@ -741,8 +741,9 @@ export function ManagedPreviewSurface({
{
setSelectedTarget(null);
@@ -762,7 +763,7 @@ export function ManagedPreviewSurface({
void submitDraft()}
>
diff --git a/packages/components/src/components/sessions/notification-permission-prompt.tsx b/packages/components/src/components/sessions/notification-permission-prompt.tsx
index c48ce7fd2..65b441f92 100644
--- a/packages/components/src/components/sessions/notification-permission-prompt.tsx
+++ b/packages/components/src/components/sessions/notification-permission-prompt.tsx
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Bell, X } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { useTranslation } from 'react-i18next';
import { useAtom, useAtomValue } from 'jotai';
import { notificationPromptDismissedAtom, userAtom, currentWorkspaceSlugAtom } from '@/atoms';
@@ -133,14 +133,14 @@ export function NotificationPermissionPrompt({
{t('notifications.prompt.description', 'Get notified when your AI tasks complete.')}
-
+
{t('notifications.prompt.enable', 'Enable')}
{t('notifications.prompt.dontRemind', "Don't remind me")}
@@ -148,9 +148,10 @@ export function NotificationPermissionPrompt({
diff --git a/packages/components/src/components/sessions/paged-file-viewer.tsx b/packages/components/src/components/sessions/paged-file-viewer.tsx
index 3541a67ba..278ab9549 100644
--- a/packages/components/src/components/sessions/paged-file-viewer.tsx
+++ b/packages/components/src/components/sessions/paged-file-viewer.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { VList } from 'virtua';
import type { PagedFileSource } from '@/lib/paged-file-source';
import { Input } from '@/ui/input';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
/** One bounded page, virtual rows, no editor/LSP/HTML execution or accumulated cache. */
export function PagedFileViewer({
@@ -45,7 +45,7 @@ export function PagedFileViewer({
{t('sessions.fileViewer.paged', 'Large file · read-only')}
setPage(current - 1)}
>
@@ -70,14 +70,14 @@ export function PagedFileViewer({
= pages}
onClick={() => setPage(current + 1)}
>
{t('sessions.fileViewer.nextPage', 'Next page')}
{onOpenExternal ? (
-
+
{t('sessions.fileActions.openInDefaultApp', 'Open in default app')}
) : null}
diff --git a/packages/components/src/components/sessions/pr-merge-button.tsx b/packages/components/src/components/sessions/pr-merge-button.tsx
index 8de173de4..e07e22a43 100644
--- a/packages/components/src/components/sessions/pr-merge-button.tsx
+++ b/packages/components/src/components/sessions/pr-merge-button.tsx
@@ -2,7 +2,7 @@ import { Check, ChevronDown, GitMerge, Loader2 } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import type { GitHubMergeMethod } from '@lody/shared';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
@@ -71,7 +71,7 @@ export function PrMergeButton({
const { t } = useTranslation();
const isDisabled = disabled || isMerging || !onMerge;
const buttonVariant =
- tone === 'ready' ? 'default' : tone === 'conflict' ? 'destructive' : 'outline';
+ tone === 'ready' ? 'primary' : tone === 'conflict' ? 'destructive' : 'secondary';
// The full (non-compact) ready button uses GitHub's green so "merge" reads as
// the positive terminal action, matching the compact info-bar merge control.
const readyGreen = tone === 'ready' && !compact;
@@ -108,11 +108,11 @@ export function PrMergeButton({
) : (
void onMerge?.(method)}
- className={cn('h-8 gap-1 rounded-r-none border-transparent', readyGreen && greenClasses)}
+ className={cn(readyGreen && greenClasses)}
>
{mainContent}
@@ -131,14 +131,11 @@ export function PrMergeButton({
) : (
diff --git a/packages/components/src/components/sessions/pr-tab-view.tsx b/packages/components/src/components/sessions/pr-tab-view.tsx
index dd1efa071..26100c5eb 100644
--- a/packages/components/src/components/sessions/pr-tab-view.tsx
+++ b/packages/components/src/components/sessions/pr-tab-view.tsx
@@ -40,7 +40,7 @@ import {
isPullRequestMergeabilityPending,
} from '@/lib/github-pr-details-state';
import { Avatar, AvatarFallback, AvatarImage } from '@/ui/avatar';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { ScrollArea } from '@/ui/scroll-area';
import { Skeleton } from '@/ui/skeleton';
import { Textarea } from '@/ui/textarea';
@@ -314,13 +314,7 @@ function ChecksPermissionNotice({
{onGrantChecksPermission && (
-
+
{t('sessions.prTab.checksPermissionCta', 'Update permissions')}
@@ -342,7 +336,7 @@ function MergeStatusNotice({ kind }: { kind: 'conflict' | 'blocked' | 'checking'
{t(
'sessions.prTab.mergeConflictNotice',
- "This branch has conflicts with the base branch — resolve them before it can be merged."
+ 'This branch has conflicts with the base branch — resolve them before it can be merged.'
)}
@@ -700,10 +694,10 @@ function PrHeaderActionButton({
void onMerge(mergeMethod)}
disabled={busy}
- className={cn(PR_ACTION_BTN, PR_MERGE_BTN_GREEN, 'rounded-r-none border-transparent')}
+ className={cn(PR_ACTION_BTN, PR_MERGE_BTN_GREEN)}
>
{isMerging ? (
@@ -716,10 +710,10 @@ function PrHeaderActionButton({
@@ -733,7 +727,11 @@ function PrHeaderActionButton({
onValueChange={(value) => onSelectMergeMethod?.(value as GitHubMergeMethod)}
>
{HEADER_MERGE_METHODS.map((method) => (
-
+
{t(method.labelKey, method.labelFallback)}
@@ -771,12 +769,12 @@ function PrHeaderActionButton({
{resolving ? (
@@ -790,11 +788,10 @@ function PrHeaderActionButton({
@@ -821,11 +818,11 @@ function PrHeaderActionButton({
{kind === 'checking' ? (
@@ -839,11 +836,10 @@ function PrHeaderActionButton({
@@ -863,10 +859,10 @@ function PrHeaderActionButton({
void onMarkReadyForReview()}
disabled={busy}
- className={cn(PR_ACTION_BTN, canClose && 'rounded-r-none')}
+ className={cn(PR_ACTION_BTN, canClose && '')}
>
{isMarkingReady ? (
@@ -880,10 +876,10 @@ function PrHeaderActionButton({
@@ -902,8 +898,8 @@ function PrHeaderActionButton({
return (
void onSetState?.('open')}
disabled={busy}
className={PR_ACTION_BTN}
@@ -926,8 +922,8 @@ function PrHeaderActionButton({
return (
void onDeleteBranch?.()}
disabled={busy}
className={PR_ACTION_BTN}
@@ -947,8 +943,8 @@ function PrHeaderActionButton({
return (
void onSetState?.('closed')}
disabled={busy}
className={cn(PR_ACTION_BTN, 'text-status-danger')}
@@ -1048,13 +1044,7 @@ function Composer({
disabled={isPending}
/>
-
void submit()}
- disabled={!canSubmit}
- className="gap-1"
- >
+ void submit()} disabled={!canSubmit}>
{isPending && }
{t('sessions.prTab.composerSubmit', 'Comment')}
@@ -1081,31 +1071,31 @@ function BranchRow({ pr }: { pr: GitHubPullRequestDetails }) {
-
- {t('sessions.prTab.base', 'base')}
+
+ {t('sessions.prTab.base', 'base')}
+
+
+ {pr.baseRef}
+
+
-
- {pr.baseRef}
-
-
-
-
- ←
-
-
-
- {t('sessions.prTab.head', 'head')}
+
+ ←
-
- {pr.headRef}
-
-
+
+
+ {t('sessions.prTab.head', 'head')}
+
+
+ {pr.headRef}
+
+
@@ -1176,13 +1166,7 @@ export const PrTabView = memo(function PrTabView({
{onRefresh && (
-
+
{t('sessions.prTab.retry', 'Retry')}
)}
@@ -1267,9 +1251,9 @@ export const PrTabView = memo(function PrTabView({
data &&
)}
- {(mergeKind === 'conflict' ||
- mergeKind === 'blocked' ||
- mergeKind === 'checking') && }
+ {(mergeKind === 'conflict' || mergeKind === 'blocked' || mergeKind === 'checking') && (
+
+ )}
{conversation.length > 0 && (
@@ -1326,19 +1310,12 @@ export const PrTabView = memo(function PrTabView({
onDeleteBranch={onDeleteBranch}
onResolveConflicts={onResolveConflicts}
isResolvingConflicts={isResolvingConflicts}
- menuContentClassName={
- embedded ? 'lody-app-preview-portal-dark' : undefined
- }
+ menuContentClassName={embedded ? 'lody-app-preview-portal-dark' : undefined}
/>
) : null;
return (
-
+
{embedded ? (
/* Landing: slim bar — badge + merge only (no branch row / github chrome). */
@@ -1363,8 +1340,8 @@ export const PrTabView = memo(function PrTabView({
)}
+ }
variant="ghost"
- size="icon"
- className="h-7 w-7 text-muted-foreground"
title={t('sessions.prTab.openOnGitHub', 'Open on GitHub')}
+ size="small"
+ icon
>
-
-
-
+
{mergeAction}
diff --git a/packages/components/src/components/sessions/rename-session-dialog.tsx b/packages/components/src/components/sessions/rename-session-dialog.tsx
index 7c39385ed..d57ee1358 100644
--- a/packages/components/src/components/sessions/rename-session-dialog.tsx
+++ b/packages/components/src/components/sessions/rename-session-dialog.tsx
@@ -10,7 +10,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/ui/dialog';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Textarea } from '@/ui/textarea';
import { useSessionActions } from '@/hooks/use-session-actions';
import { isImeComposingKeyboardEvent } from '@/lib/ime';
@@ -159,11 +159,11 @@ export function RenameSessionDialogView({
-
+
{t('common.cancel', 'Cancel')}
{
void handleSubmit();
}}
diff --git a/packages/components/src/components/sessions/session-browser-panel.tsx b/packages/components/src/components/sessions/session-browser-panel.tsx
index 8a85de14f..baa536b61 100644
--- a/packages/components/src/components/sessions/session-browser-panel.tsx
+++ b/packages/components/src/components/sessions/session-browser-panel.tsx
@@ -23,7 +23,7 @@ import {
import { activeWorkspaceRuntimeAtom, userAtom } from '@/atoms';
import { getMachineMetaByIdAtomFamily } from '@/atoms/machines';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
AlertDialog,
AlertDialogAction,
@@ -713,12 +713,14 @@ function SessionBrowserPanelController({
publicState?.canGoBack &&
getPublicBrowserBridge()
) {
- void getPublicBrowserBridge()?.back(`session-browser-${session.id}`).then(
- (result) => {
- if (!result.ok) setError(result.error);
- },
- (commandError: unknown) => setError(errorMessage(commandError))
- );
+ void getPublicBrowserBridge()
+ ?.back(`session-browser-${session.id}`)
+ .then(
+ (result) => {
+ if (!result.ok) setError(result.error);
+ },
+ (commandError: unknown) => setError(errorMessage(commandError))
+ );
return;
}
if (currentAddress?.engine === 'managed-preview' && managedState?.canGoBack) {
@@ -741,12 +743,14 @@ function SessionBrowserPanelController({
publicState?.canGoForward &&
getPublicBrowserBridge()
) {
- void getPublicBrowserBridge()?.forward(`session-browser-${session.id}`).then(
- (result) => {
- if (!result.ok) setError(result.error);
- },
- (commandError: unknown) => setError(errorMessage(commandError))
- );
+ void getPublicBrowserBridge()
+ ?.forward(`session-browser-${session.id}`)
+ .then(
+ (result) => {
+ if (!result.ok) setError(result.error);
+ },
+ (commandError: unknown) => setError(errorMessage(commandError))
+ );
return;
}
if (currentAddress?.engine === 'managed-preview' && managedState?.canGoForward) {
@@ -765,12 +769,14 @@ function SessionBrowserPanelController({
const handleReload = useCallback(() => {
if (currentAddress?.engine === 'public-web' && getPublicBrowserBridge()) {
- void getPublicBrowserBridge()?.reload(`session-browser-${session.id}`).then(
- (result) => {
- if (!result.ok) setError(result.error);
- },
- (commandError: unknown) => setError(errorMessage(commandError))
- );
+ void getPublicBrowserBridge()
+ ?.reload(`session-browser-${session.id}`)
+ .then(
+ (result) => {
+ if (!result.ok) setError(result.error);
+ },
+ (commandError: unknown) => setError(errorMessage(commandError))
+ );
return;
}
if (viewerUrl) {
@@ -788,12 +794,14 @@ function SessionBrowserPanelController({
const handleStop = useCallback(() => {
if (currentAddress?.engine === 'public-web' && getPublicBrowserBridge()) {
- void getPublicBrowserBridge()?.stop(`session-browser-${session.id}`).then(
- (result) => {
- if (!result.ok) setError(result.error);
- },
- (commandError: unknown) => setError(errorMessage(commandError))
- );
+ void getPublicBrowserBridge()
+ ?.stop(`session-browser-${session.id}`)
+ .then(
+ (result) => {
+ if (!result.ok) setError(result.error);
+ },
+ (commandError: unknown) => setError(errorMessage(commandError))
+ );
return;
}
if (currentAddress?.engine === 'managed-preview' && annotationAvailable) {
@@ -1004,8 +1012,8 @@ function SessionBrowserPanelController({
setError(null)}
>
{t('common.dismiss', 'Dismiss')}
diff --git a/packages/components/src/components/sessions/session-browser-toolbar.tsx b/packages/components/src/components/sessions/session-browser-toolbar.tsx
index 1ba2f46d6..e7edcd86f 100644
--- a/packages/components/src/components/sessions/session-browser-toolbar.tsx
+++ b/packages/components/src/components/sessions/session-browser-toolbar.tsx
@@ -12,7 +12,7 @@ import {
} from 'lucide-react';
import { useTranslation } from 'react-i18next';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/ui/tooltip';
import { cn } from '@/lib/utils';
@@ -54,8 +54,8 @@ function ToolbarButton({
diff --git a/packages/components/src/components/sessions/session-chat-input-area.tsx b/packages/components/src/components/sessions/session-chat-input-area.tsx
index 0e8046ff9..41c16f814 100644
--- a/packages/components/src/components/sessions/session-chat-input-area.tsx
+++ b/packages/components/src/components/sessions/session-chat-input-area.tsx
@@ -13,7 +13,7 @@ import {
} from 'react';
import { useAtomValue } from 'jotai';
import { ArrowUp, Loader2 } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import type { AcpSessionSelectOption } from '@/components/shared/acp-session-select';
import { useSessionAgentRole, type SessionAgentRoleControl } from '@/hooks/use-session-agent-role';
import { buildAgentRoleFormValueFromRunConfig } from '@/lib/agent-role-form';
@@ -2392,21 +2392,17 @@ export const SessionChatInputArea = memo(
) : null}
) : null;
- /* Keep desktop actions compact while preserving the mobile touch target. */
- const primaryActionSizeClassName = isMobile ? 'h-8 w-8' : 'h-7 w-7';
const primaryActionNode = showStopButton ? (
{
void onStop();
}}
- variant="ghost"
- size="icon"
aria-label={t('sessions.stop')}
- className={cn(
- primaryActionSizeClassName,
- 'rounded-full shadow-xs transition-all',
- 'bg-foreground text-background hover:bg-foreground/90 hover:text-background active:translate-y-[1px]'
- )}
>
void sendMessage()}
disabled={!hasSendableContent || isSendActionDisabled}
aria-label={
@@ -2425,11 +2423,6 @@ export const SessionChatInputArea = memo(
? externalHistorySyncLabel
: t('sessions.send')
}
- className={cn(
- primaryActionSizeClassName,
- 'rounded-full shadow-xs transition-all',
- 'bg-foreground text-background hover:bg-foreground/90 hover:text-background active:translate-y-[1px]'
- )}
>
{submissionPending || hasBlockingImages || isExternalHistoryRefreshing ? (
diff --git a/packages/components/src/components/sessions/session-chat-interface.tsx b/packages/components/src/components/sessions/session-chat-interface.tsx
index 5ddcdf5fd..ec6db3c7c 100644
--- a/packages/components/src/components/sessions/session-chat-interface.tsx
+++ b/packages/components/src/components/sessions/session-chat-interface.tsx
@@ -48,7 +48,7 @@ import {
Users,
X,
} from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { isMacOSElectronRenderer, useElectronFullscreen } from '@/lib/electron';
import { getIpcServices } from '@/lib/electron-ipc-client';
import { isMac } from '@/lib/commands/platform';
@@ -814,9 +814,10 @@ export function SessionHistoryButton({
const trigger = (
@@ -1156,8 +1157,9 @@ export function SessionHeaderMenu({
@@ -1621,8 +1623,9 @@ export function SessionSearchBar({
@@ -5657,22 +5661,16 @@ export const SessionChatInterface = memo(
<>
{shouldShowOpenInIdeButton && isElectronRendererForPathLaunch && (
-
+
{selectedPathLauncher.label}
diff --git a/packages/components/src/components/sessions/session-detail.tsx b/packages/components/src/components/sessions/session-detail.tsx
index 13639ab0b..8832a38cc 100644
--- a/packages/components/src/components/sessions/session-detail.tsx
+++ b/packages/components/src/components/sessions/session-detail.tsx
@@ -23,7 +23,7 @@ import {
Users,
} from 'lucide-react';
import { useTranslation } from 'react-i18next';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { useRouter } from '@tanstack/react-router';
import { useComposerNavigationFocus } from '../chat/submission/use-composer-navigation-focus';
import { usePostHog } from '@posthog/react';
@@ -673,12 +673,13 @@ const TerminalDockToggleButton = memo(function TerminalDockToggleButton() {
store.get(terminalControllerAtom)?.toggleOpen()}
aria-label={label}
title={label}
- className={cn('h-7 w-7 shrink-0 text-muted-foreground', isOpen && 'text-foreground')}
+ className={cn('shrink-0', isOpen && '')}
>
@@ -4572,7 +4573,7 @@ const SessionDetail = ({
{t('sessions.tabWaitingForSync', 'Waiting for this conversation to sync…')}
- handleSessionTabSelect(sessionId)}>
+ handleSessionTabSelect(sessionId)}>
{t('sessions.tabBackToMain', 'Back to main conversation')}
>
@@ -4598,7 +4599,7 @@ const SessionDetail = ({
- setDeleteConfirmOpen(false)}>
+ setDeleteConfirmOpen(false)}>
{t('common.cancel', 'Cancel')}
- setArchiveConfirmOpen(false)}>
+ setArchiveConfirmOpen(false)}>
{t('common.cancel', 'Cancel')}
-
+
{t('common.cancel', 'Cancel')}
@@ -5660,10 +5662,11 @@ const SessionDetail = ({
showNavigationSidebar()}
aria-label={t('sessions.leftSidebar.show', 'Show navigation sidebar')}
- className="h-7 w-7 shrink-0 text-muted-foreground"
+ className="shrink-0"
>
diff --git a/packages/components/src/components/sessions/session-file-content-view.tsx b/packages/components/src/components/sessions/session-file-content-view.tsx
index 9fcc7d9a1..7856c3e77 100644
--- a/packages/components/src/components/sessions/session-file-content-view.tsx
+++ b/packages/components/src/components/sessions/session-file-content-view.tsx
@@ -81,7 +81,7 @@ import {
type SessionFileLiveSyncStatus,
type SessionFileSaveStatus,
} from '@/hooks/use-code-collab-save-text';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/ui/tooltip';
import { useCodeCollabLsp, type CodeCollabLspState } from '@/hooks/use-code-collab-lsp';
import { useLatestRef } from '@/hooks/use-latest-ref';
@@ -1983,18 +1983,12 @@ export function SessionFileConflictActionRow({
{pendingOverride ? (
<>
- setPendingOverride(false)}
- >
+ setPendingOverride(false)}>
{t('common.cancel', 'Cancel')}
{
setPendingOverride(false);
void onResolveConflict('override');
@@ -2006,37 +2000,33 @@ export function SessionFileConflictActionRow({
) : (
<>
void onResolveConflict('discard')}
>
{t('sessions.fileSave.conflictDiscard', 'Discard my edits')}
>>>>>> conflict markers so you can resolve by hand.'
)}
+ size="small"
onClick={() => void onResolveConflict('load_with_conflicts')}
>
{t('sessions.fileSave.conflictMarkers', 'Insert conflict markers')}
setPendingOverride(true)}
>
{t('sessions.fileSave.conflictOverride', 'Overwrite disk')}
@@ -2079,7 +2069,7 @@ function SessionFileLspPanel({
{actionLabel}
-
+
{t('sessions.lsp.dismiss', 'Dismiss')}
diff --git a/packages/components/src/components/sessions/session-file-error-state.tsx b/packages/components/src/components/sessions/session-file-error-state.tsx
index 7b69bc8f6..bf284f1ee 100644
--- a/packages/components/src/components/sessions/session-file-error-state.tsx
+++ b/packages/components/src/components/sessions/session-file-error-state.tsx
@@ -5,7 +5,7 @@ import { cn } from '@/lib/utils';
// The action model is shared with the file tree context menu and the side
// panel ⋯ menu; the card is one consumer of it, not its owner.
import type { SessionFileErrorActions } from '@/lib/session-file-actions';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
type Translation = (key: string, defaultValue: string, options?: Record
) => string;
@@ -304,7 +304,7 @@ export function SessionFileErrorState({
{localHost ? (
<>
) : null}
{loading ? (
@@ -311,11 +307,12 @@ export const SessionGoalBanner = memo(function SessionGoalBanner({
onDismiss(goal)}
aria-label={t('sessions.goal.actions.dismiss', 'Dismiss goal banner')}
title={t('sessions.goal.actions.dismiss', 'Dismiss goal banner')}
- className="h-7 w-7 shrink-0 text-muted-foreground hover:text-foreground"
+ className="shrink-0"
>
diff --git a/packages/components/src/components/sessions/session-info-chips.tsx b/packages/components/src/components/sessions/session-info-chips.tsx
index 8b63ac331..dc6f1bdc1 100644
--- a/packages/components/src/components/sessions/session-info-chips.tsx
+++ b/packages/components/src/components/sessions/session-info-chips.tsx
@@ -32,7 +32,7 @@ import {
type SessionPullRequestMeta,
} from '@lody/shared';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { writeTextToClipboard } from '@/lib/clipboard';
import { getGoalStatusPresentation } from '@/lib/session-goal-status';
import { formatDurationCompact, type DurationUnitLabels } from '@/lib/format-duration';
@@ -273,11 +273,12 @@ export function GoalChip({
onDismiss?.(goal)}
aria-label={t('sessions.goal.actions.dismiss', 'Dismiss goal banner')}
title={t('sessions.goal.actions.dismiss', 'Dismiss goal banner')}
- className="h-7 w-7 shrink-0 text-muted-foreground hover:text-foreground"
+ className="shrink-0"
>
diff --git a/packages/components/src/components/sessions/session-not-found.tsx b/packages/components/src/components/sessions/session-not-found.tsx
index 66139c885..6e532d19f 100644
--- a/packages/components/src/components/sessions/session-not-found.tsx
+++ b/packages/components/src/components/sessions/session-not-found.tsx
@@ -1,5 +1,5 @@
import { useRouter } from '@tanstack/react-router';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { ArrowLeft, MessageSquareOff } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useAtomValue } from 'jotai';
@@ -54,7 +54,7 @@ export function SessionNotFound({ onBack }: SessionNotFoundProps) {
)}
-
+
{t('sessions.notFound.backToList', 'Back to Sessions')}
diff --git a/packages/components/src/components/sessions/session-pin.tsx b/packages/components/src/components/sessions/session-pin.tsx
index 33d054a7b..59544dce3 100644
--- a/packages/components/src/components/sessions/session-pin.tsx
+++ b/packages/components/src/components/sessions/session-pin.tsx
@@ -1,7 +1,7 @@
import { memo, useCallback, useMemo } from 'react';
import { Pin, X } from 'lucide-react';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import type { SessionHistoryParsed } from '@lody/shared';
import { useTranslation } from 'react-i18next';
import { ConversationColumn } from '@/components/shared/conversation-column';
@@ -61,7 +61,7 @@ export const SessionPin = memo(function SessionPin({
@@ -70,8 +70,9 @@ export const SessionPin = memo(function SessionPin({
diff --git a/packages/components/src/components/sessions/session-usage-popover.tsx b/packages/components/src/components/sessions/session-usage-popover.tsx
index 614dc4aa4..109e0c096 100644
--- a/packages/components/src/components/sessions/session-usage-popover.tsx
+++ b/packages/components/src/components/sessions/session-usage-popover.tsx
@@ -5,7 +5,7 @@ import { enUS, zhCN } from 'date-fns/locale';
import { getServerNow, type SessionContextWindowUsage } from '@lody/shared';
import { Loader2 } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
CodexResetForecastDialogHost,
CodexResetForecastUsageRow,
@@ -124,11 +124,8 @@ export const SessionUsagePopover = memo(function SessionUsagePopover({
diff --git a/packages/components/src/components/settings/about-setting.tsx b/packages/components/src/components/settings/about-setting.tsx
index 821969f69..31bfd5352 100644
--- a/packages/components/src/components/settings/about-setting.tsx
+++ b/packages/components/src/components/settings/about-setting.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { useAtom } from 'jotai';
import { Loader2, CheckCircle2, AlertCircle, Download, ExternalLink } from 'lucide-react';
import type { ElectronUpdaterPhase } from '@lody/shared';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Switch } from '@/ui/switch';
import { BetaFeaturesSection } from './beta-features-setting';
import { CompactRow, CompactSection } from './compact-layout';
@@ -147,18 +147,13 @@ export function AboutSettingsComponent() {
-
+
{t('settings.about.openDownloadPage', 'Open download page')}
-
+
{t('settings.about.visitWebsite', 'Visit website')}
@@ -202,8 +197,7 @@ export function AboutSettingsComponent() {
)}
{isDownloaded ? (
{
void handleQuitAndInstall();
}}
@@ -218,9 +212,8 @@ export function AboutSettingsComponent() {
) : (
{
void handleCheckForUpdates();
}}
diff --git a/packages/components/src/components/settings/account-machines-overview.tsx b/packages/components/src/components/settings/account-machines-overview.tsx
index c58fd3c45..2ad5dca6b 100644
--- a/packages/components/src/components/settings/account-machines-overview.tsx
+++ b/packages/components/src/components/settings/account-machines-overview.tsx
@@ -25,7 +25,7 @@ import { useVisibleMachineMetas } from '@/hooks/use-visible-machine-metas';
import { isElectronRenderer } from '@/lib/electron';
import { cn } from '@/lib/utils';
import { Badge } from '@/ui/badge';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/ui/tooltip';
export type AccountMachineDirectory = {
@@ -177,8 +177,9 @@ export function AccountMachinesOverviewView({
@@ -268,8 +269,8 @@ export function AccountMachinesOverviewView({
toggleDirectories(item.id)}
aria-expanded={expanded}
>
@@ -295,8 +296,8 @@ export function AccountMachinesOverviewView({
onManageMachine(item.id)}
aria-label={t('settings.account.machines.manageMachine', {
name: item.name,
@@ -324,8 +325,7 @@ export function AccountMachinesOverviewView({
onOpenDirectories(item.id)}
>
{t('settings.account.machines.openProjects', 'Open Projects')}
@@ -387,8 +387,8 @@ function AgentStackButton({ agents, onClick }: { agents: AgentConfigMeta[]; onCl
diff --git a/packages/components/src/components/settings/account-setting-pure.tsx b/packages/components/src/components/settings/account-setting-pure.tsx
index e7e893601..aebddae14 100644
--- a/packages/components/src/components/settings/account-setting-pure.tsx
+++ b/packages/components/src/components/settings/account-setting-pure.tsx
@@ -17,7 +17,7 @@ import {
Pencil,
X,
} from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { Badge } from '@/ui/badge';
@@ -607,8 +607,7 @@ export function AccountSettingsPure({
{
void onSignOut();
}}
@@ -694,8 +693,8 @@ export function AccountSettingsPure({
{t('settings.account.cliAuth.usageHint')}
{
void onCopyGeneratedCliApiKey?.();
}}
@@ -722,8 +721,8 @@ export function AccountSettingsPure({
)}
handleCliApiKeyDialogOpenChange(false)}
disabled={isCreatingCliApiKey}
>
@@ -731,7 +730,7 @@ export function AccountSettingsPure({
{!hasGeneratedCliApiKey && (
{
void handleCreateCliApiKey();
}}
@@ -775,7 +774,7 @@ export function AccountSettingsPure({
})();
}}
disabled={!cliApiKeyToRevoke || Boolean(revokingCliApiKeyId)}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{revokingCliApiKeyId ? (
@@ -796,9 +795,9 @@ export function AccountSettingsPure({
actions={
hasAdminPermission && (
setInviteDialogOpen(true)}
>
@@ -872,8 +871,9 @@ export function AccountSettingsPure({
{isEditable && (
{
setUserToDelete(member.id);
setDeleteUserDialogOpen(true);
@@ -911,8 +911,7 @@ export function AccountSettingsPure({
{
void onCopyInviteLink(getInviteLink(invitation));
}}
@@ -923,8 +922,9 @@ export function AccountSettingsPure({
{hasAdminPermission && (
{
void (async () => {
@@ -973,8 +973,8 @@ export function AccountSettingsPure({
actions={
{
setCliApiKeyDialogOpen(true);
}}
@@ -1045,8 +1045,8 @@ export function AccountSettingsPure({
setCliApiKeyToRevoke(apiKey)}
disabled={revokingCliApiKeyId === apiKey.id}
>
@@ -1075,8 +1075,8 @@ export function AccountSettingsPure({
>
setLeaveDialogOpen(true)}
>
{t('workspace.danger.leaveWorkspace.button')}
@@ -1090,8 +1090,8 @@ export function AccountSettingsPure({
>
{
// A live subscription blocks deletion outright; surface the
// guidance dialog instead of the type-to-confirm flow (the
@@ -1142,7 +1142,7 @@ export function AccountSettingsPure({
onClick={() => {
void handleRemoveMember();
}}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{t('common.remove')}
@@ -1176,7 +1176,7 @@ export function AccountSettingsPure({
})();
}}
disabled={isLeaving}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{isLeaving ? (
<>
@@ -1263,7 +1263,7 @@ export function AccountSettingsPure({
{
setDeleteDialogOpen(false);
setDeleteConfirmText('');
@@ -1348,7 +1348,7 @@ export function AccountSettingsPure({
{
setDeleteAccountDialogOpen(false);
setDeleteAccountConfirmText('');
diff --git a/packages/components/src/components/settings/acp-authentication-panel.tsx b/packages/components/src/components/settings/acp-authentication-panel.tsx
index 4b8bada7e..50a6bf3f5 100644
--- a/packages/components/src/components/settings/acp-authentication-panel.tsx
+++ b/packages/components/src/components/settings/acp-authentication-panel.tsx
@@ -23,7 +23,7 @@ import {
type MachineAcpAuthenticationArgs,
} from '@/hooks/use-machine-acp-authentication';
import { resyncMachineFlockRows } from '@/hooks/use-machine-flock-rows';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { isElectronRenderer } from '@/lib/electron';
@@ -574,13 +574,13 @@ export function AcpAuthenticationPanel({
{phase === 'running' ? (
<>
-
+
{t('agents.authentication.waiting', 'Waiting for {{provider}} sign-in', {
provider,
})}
-
+
{t('common.cancel', 'Cancel')}
@@ -588,8 +588,8 @@ export function AcpAuthenticationPanel({
) : (
@@ -696,8 +696,8 @@ export function AcpAuthenticationAuthorizationView({
@@ -729,7 +729,7 @@ export function AcpAuthenticationAuthorizationView({
{authorization.userCode}
-
+
{userCodeCopied ? (
) : (
@@ -778,7 +778,7 @@ export function AcpAuthenticationAuthorizationView({
/>
onSubmit({ action: 'accept', methodId: method.id })}
>
@@ -916,7 +916,7 @@ export function AcpAuthenticationInteractionView({
))}
onSubmit({ action: 'accept', content: values })}
>
diff --git a/packages/components/src/components/settings/agent-config-dialog.tsx b/packages/components/src/components/settings/agent-config-dialog.tsx
index 44f95733f..75e398f20 100644
--- a/packages/components/src/components/settings/agent-config-dialog.tsx
+++ b/packages/components/src/components/settings/agent-config-dialog.tsx
@@ -65,7 +65,7 @@ import { cn } from '@/lib/utils';
import { useKeyboardAwareScrollIntoView } from '@/hooks/use-keyboard-aware-scroll-into-view';
import { useMachineAcpBinaryProgress } from '@/hooks/use-machine-acp-binary-progress';
import { activeWorkspaceRuntimeAtom } from '@/atoms/runtime';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@/ui/dialog';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/ui/collapsible';
import { Input } from '@/ui/input';
@@ -2095,8 +2095,8 @@ export function AgentConfigDialog(props: AgentConfigDialogProps) {
void runCustomProbe()}
>
@@ -2169,8 +2169,8 @@ export function AgentConfigDialog(props: AgentConfigDialogProps) {
{
setProbeError(null);
@@ -2302,8 +2302,8 @@ export function AgentConfigDialog(props: AgentConfigDialogProps) {
(binaryStatus === 'not-installed' || binaryStatus === 'error') ? (
void handleInstallBinary()}
>
@@ -2441,10 +2441,10 @@ export function AgentConfigDialog(props: AgentConfigDialogProps) {
onOpenChange(false)}
disabled={submitting}
- size="sm"
>
{t('common.cancel', 'Cancel')}
@@ -2452,11 +2452,11 @@ export function AgentConfigDialog(props: AgentConfigDialogProps) {
void submit()}
disabled={
!!disableReason || submitting || (builtinCreationPending && !probeError)
}
- size="sm"
>
{(submitting || (builtinCreationPending && !authRequired && !probeError)) && (
@@ -2714,9 +2714,9 @@ function ProbeStatus({
return (
@@ -2763,8 +2763,7 @@ function ProbeStatus({
@@ -3173,8 +3172,9 @@ function InlineCopyButton({ value, ariaLabel }: { value: string; ariaLabel: stri
{
event.stopPropagation();
@@ -3214,8 +3214,8 @@ function TitleGenerationFields({
{sel.label}
onChange(sel.configId, !isEnabled)}
aria-pressed={isEnabled}
>
diff --git a/packages/components/src/components/settings/agent-role-form.tsx b/packages/components/src/components/settings/agent-role-form.tsx
index 400ee6c44..11983ae34 100644
--- a/packages/components/src/components/settings/agent-role-form.tsx
+++ b/packages/components/src/components/settings/agent-role-form.tsx
@@ -18,7 +18,7 @@ import {
type AgentRoleRunConfigIssue,
} from '@/lib/agent-role-form';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { Popover, PopoverContent, PopoverTrigger } from '@/ui/popover';
@@ -313,10 +313,16 @@ export function AgentRoleForm({
-
+
{t('common.cancel')}
- 0}>
+ 0}>
{submitting ? : null}
{isEditing ? t('common.save') : t('settings.agentRoles.form.create')}
@@ -390,8 +396,8 @@ function AgentRoleEmojiField({
{
onChange('');
setOpen(false);
diff --git a/packages/components/src/components/settings/agent-roles-setting.tsx b/packages/components/src/components/settings/agent-roles-setting.tsx
index 55a38edeb..de31acc44 100644
--- a/packages/components/src/components/settings/agent-roles-setting.tsx
+++ b/packages/components/src/components/settings/agent-roles-setting.tsx
@@ -34,7 +34,7 @@ import {
AlertDialogTitle,
} from '@/ui/alert-dialog';
import { Badge } from '@/ui/badge';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/ui/tooltip';
import { settingContainerClass } from '.';
import {
@@ -126,13 +126,7 @@ export function AgentRolesSetting() {
-
+
@@ -144,7 +138,7 @@ export function AgentRolesSetting() {
{t('settings.agentRoles.empty')}
-
+
{addLabel}
@@ -203,8 +197,7 @@ export function AgentRolesSetting() {
{t('common.cancel')}
{
event.preventDefault();
void confirmRemoval();
@@ -300,10 +293,11 @@ export function AgentRoleRow({
{canManage ? (
diff --git a/packages/components/src/components/settings/billing-setting-pure.tsx b/packages/components/src/components/settings/billing-setting-pure.tsx
index 6a8f1c95e..b37f8ecb8 100644
--- a/packages/components/src/components/settings/billing-setting-pure.tsx
+++ b/packages/components/src/components/settings/billing-setting-pure.tsx
@@ -299,7 +299,7 @@ export function BillingSettingsView({
{onCancelExternalCheckout ? (
-
+
{t('billing.externalCheckoutDismiss')}
) : null}
@@ -463,10 +463,10 @@ export function BillingSettingsView({
onIntervalChange(interval === 'year' ? 'month' : 'year')}
>
@@ -612,8 +612,8 @@ export function BillingSettingsView({
spellCheck={false}
/>
onRedeemCode(code.trim())}
>
@@ -714,7 +714,7 @@ export function BillingSettingsView({
{invoicesError ? (
{t('billing.historyError')}
-
+
{t('billing.historyRetry')}
@@ -792,8 +792,8 @@ export function BillingSettingsView({
{t('billing.cancelDialogKeep')}
- void handleSetCancelAtPeriodEnd(true)}
>
{t('billing.cancelDialogConfirm')}
diff --git a/packages/components/src/components/settings/change-password-button.tsx b/packages/components/src/components/settings/change-password-button.tsx
index 4b45115c9..65794f5f6 100644
--- a/packages/components/src/components/settings/change-password-button.tsx
+++ b/packages/components/src/components/settings/change-password-button.tsx
@@ -4,7 +4,7 @@ import { AnimatePresence, motion } from 'framer-motion';
import { ArrowLeft, Loader2 } from 'lucide-react';
import { toast } from 'sonner';
import { validateNewPassword } from '@lody/shared';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Label } from '@/ui/label';
import { PasswordInput } from '@/ui/password-input';
import {
@@ -154,13 +154,7 @@ export function ChangePasswordButton({
return (
<>
- setOpen(true)}
- >
+ setOpen(true)}>
{hasPassword
? t('settings.profile.password.changeButton')
: t('settings.profile.password.setupButton')}
@@ -266,40 +260,45 @@ export function ChangePasswordButton({
{step === 0 ? (
<>
handleOpenChange(false)}
disabled={isVerifying}
>
{t('common.cancel')}
{
void goToNewStep();
}}
disabled={!currentPassword || isVerifying}
>
- {isVerifying ? (
-
- ) : null}
+ {isVerifying ? : null}
{t('settings.profile.password.continueButton')}
>
) : (
<>
-
+
{t('common.back')}
{
void handleSubmit();
}}
disabled={isSubmitting || !newPassword || !confirmPassword}
>
- {isSubmitting ? : null}
+ {isSubmitting ? (
+
+ ) : null}
{t('settings.profile.password.submitButton')}
>
@@ -316,15 +315,15 @@ export function ChangePasswordButton({
handleOpenChange(false)}
disabled={isSubmitting}
>
{t('common.cancel')}
{
void handleSetup();
}}
diff --git a/packages/components/src/components/settings/clear-cache.tsx b/packages/components/src/components/settings/clear-cache.tsx
index d3807d68f..b35acb46d 100644
--- a/packages/components/src/components/settings/clear-cache.tsx
+++ b/packages/components/src/components/settings/clear-cache.tsx
@@ -78,7 +78,7 @@ export function ClearCacheConfirmDialog({
onConfirm();
}}
disabled={isClearing}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{isClearing ? (
diff --git a/packages/components/src/components/settings/cli-daemon-setting.tsx b/packages/components/src/components/settings/cli-daemon-setting.tsx
index a6285d820..492c72c77 100644
--- a/packages/components/src/components/settings/cli-daemon-setting.tsx
+++ b/packages/components/src/components/settings/cli-daemon-setting.tsx
@@ -2,7 +2,7 @@ import { Loader2, Play, RotateCcw, Square } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import type { ElectronCliState } from '@lody/shared';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { useElectronCliDaemon } from '@/hooks/use-electron-cli-daemon';
import { CompactRow } from './compact-layout';
@@ -58,9 +58,8 @@ export function CliDaemonSetting() {
void restart()}
>
@@ -75,9 +74,9 @@ export function CliDaemonSetting() {
void terminate()}
>
diff --git a/packages/components/src/components/settings/device-resource-monitor.tsx b/packages/components/src/components/settings/device-resource-monitor.tsx
index afb9b2cee..9270427cb 100644
--- a/packages/components/src/components/settings/device-resource-monitor.tsx
+++ b/packages/components/src/components/settings/device-resource-monitor.tsx
@@ -18,7 +18,7 @@ import {
Loader2,
TerminalSquare,
} from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Card } from '@/ui/card';
import {
AlertDialog,
@@ -434,8 +434,7 @@ function SessionTable({
{t('common.cancel', 'Cancel')}
- {
const session = confirmSession;
@@ -470,12 +469,9 @@ function SessionActionButton({
- clearCache.setDialogOpen(true)}>
+ clearCache.setDialogOpen(true)}>
{t('settings.cache.clearCache.button')}
diff --git a/packages/components/src/components/settings/integrations-setting.tsx b/packages/components/src/components/settings/integrations-setting.tsx
index 3cbc197e1..a48fa06fc 100644
--- a/packages/components/src/components/settings/integrations-setting.tsx
+++ b/packages/components/src/components/settings/integrations-setting.tsx
@@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
AlertCircle,
Book,
@@ -144,9 +144,9 @@ export function GitHubPersonalIdentitySettingsCard({
{!authorizationReady && canAuthorize && (
@@ -240,9 +240,9 @@ export function GitHubPersonalIdentitySettingsCard({
{!authorizationReady && canAuthorize && (
@@ -309,7 +309,9 @@ function CloudIntegrationsSettings() {
const setPersonalOperationPreference = useCloudMutation(
cloudOperations.github.setPersonalOperationPreference
);
- const refreshPersonalGitHubProfile = useCloudAction(cloudOperations.github.refreshPersonalGitHubProfile);
+ const refreshPersonalGitHubProfile = useCloudAction(
+ cloudOperations.github.refreshPersonalGitHubProfile
+ );
const [connectingToGitHub, setConnectingToGitHub] = useState(false);
const [updatingPersonalPreference, setUpdatingPersonalPreference] = useState(false);
const [authorizingPersonalGitHub, setAuthorizingPersonalGitHub] = useState(false);
@@ -563,9 +565,8 @@ function CloudIntegrationsSettings() {
{canManage && (
{
void handleConnectGitHub();
}}
diff --git a/packages/components/src/components/settings/invite-member-dialog.tsx b/packages/components/src/components/settings/invite-member-dialog.tsx
index 95ce87604..a1df72a81 100644
--- a/packages/components/src/components/settings/invite-member-dialog.tsx
+++ b/packages/components/src/components/settings/invite-member-dialog.tsx
@@ -1,7 +1,7 @@
import { useEffect, useState, type KeyboardEvent } from 'react';
import { useTranslation } from 'react-i18next';
import { CreditCard, Loader2, Shield, User } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/ui/select';
@@ -181,8 +181,8 @@ export function InviteMemberDialog({
onOpenChange(false)}
disabled={inviting}
>
@@ -193,7 +193,7 @@ export function InviteMemberDialog({
billingUiAvailable &&
onOpenBilling && (
{
onOpenChange(false);
onOpenBilling();
@@ -204,7 +204,7 @@ export function InviteMemberDialog({
)
) : (
-
+
{inviting && }
{inviting ? t('common.inviting') : t('common.invite')}
diff --git a/packages/components/src/components/settings/join-community-dialog.tsx b/packages/components/src/components/settings/join-community-dialog.tsx
index 41efa3bcf..5f16ae101 100644
--- a/packages/components/src/components/settings/join-community-dialog.tsx
+++ b/packages/components/src/components/settings/join-community-dialog.tsx
@@ -100,7 +100,7 @@ export function JoinCommunityButton() {
return (
<>
- setOpen(true)}>
+ setOpen(true)}>
{t('settings.about.joinCommunity', 'Join community')}
diff --git a/packages/components/src/components/settings/keyboard-shortcuts-setting.tsx b/packages/components/src/components/settings/keyboard-shortcuts-setting.tsx
index 09d178a9a..a1e7e8eac 100644
--- a/packages/components/src/components/settings/keyboard-shortcuts-setting.tsx
+++ b/packages/components/src/components/settings/keyboard-shortcuts-setting.tsx
@@ -6,7 +6,7 @@ import {
type GlobalShortcutId,
type GlobalShortcutSetError,
} from '@lody/shared';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
import {
canonicalizeBinding,
@@ -93,13 +93,7 @@ export function KeyboardShortcutsSetting() {
{t('settings.keyboardShortcuts.description')}
-
+
{t('settings.keyboardShortcuts.resetAll')}
@@ -227,15 +221,16 @@ function GlobalShortcutRow({
{!recording && binding && (
{
setError(null);
void onSet(id, null);
}}
title={t('settings.keyboardShortcuts.unbindTooltip')}
>
-
+
)}
@@ -355,12 +350,13 @@ function ShortcutRow({
{!recording && primary && (
-
+
)}
diff --git a/packages/components/src/components/settings/linked-accounts-list.tsx b/packages/components/src/components/settings/linked-accounts-list.tsx
index c90041d87..d8366fd7f 100644
--- a/packages/components/src/components/settings/linked-accounts-list.tsx
+++ b/packages/components/src/components/settings/linked-accounts-list.tsx
@@ -6,7 +6,7 @@ import type { IconType } from 'react-icons';
import { SiApple, SiDiscord, SiGithub } from 'react-icons/si';
import { FcGoogle } from 'react-icons/fc';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
Dialog,
DialogContent,
@@ -49,10 +49,25 @@ const PROVIDERS: {
* Kept as literal strings so Tailwind's JIT emits them. */
hoverClassName?: string;
}[] = [
- { id: 'github', Icon: SiGithub, boundClassName: 'text-foreground', hoverClassName: 'group-hover:text-foreground' },
+ {
+ id: 'github',
+ Icon: SiGithub,
+ boundClassName: 'text-foreground',
+ hoverClassName: 'group-hover:text-foreground',
+ },
{ id: 'google', Icon: FcGoogle, flat: true },
- { id: 'apple', Icon: SiApple, boundClassName: 'text-foreground', hoverClassName: 'group-hover:text-foreground' },
- { id: 'discord', Icon: SiDiscord, boundClassName: 'text-[#5865F2]', hoverClassName: 'group-hover:text-[#5865F2]' },
+ {
+ id: 'apple',
+ Icon: SiApple,
+ boundClassName: 'text-foreground',
+ hoverClassName: 'group-hover:text-foreground',
+ },
+ {
+ id: 'discord',
+ Icon: SiDiscord,
+ boundClassName: 'text-[#5865F2]',
+ hoverClassName: 'group-hover:text-[#5865F2]',
+ },
];
export function LinkedAccountsList({
@@ -67,7 +82,10 @@ export function LinkedAccountsList({
const boundProviders = new Set(accounts.map((account) => account.providerId));
const providerLabel = (providerId: string): string =>
- t(`settings.profile.providers.${providerId}`, providerId.charAt(0).toUpperCase() + providerId.slice(1));
+ t(
+ `settings.profile.providers.${providerId}`,
+ providerId.charAt(0).toUpperCase() + providerId.slice(1)
+ );
const iconClassName = (
bound: boolean,
@@ -185,15 +203,15 @@ export function LinkedAccountsList({
setPendingProviderId(null)}
disabled={isConnecting}
>
{t('common.cancel')}
{
void handleConfirmConnect();
}}
diff --git a/packages/components/src/components/settings/machine-agent-settings.tsx b/packages/components/src/components/settings/machine-agent-settings.tsx
index df1885bc9..8c4083521 100644
--- a/packages/components/src/components/settings/machine-agent-settings.tsx
+++ b/packages/components/src/components/settings/machine-agent-settings.tsx
@@ -59,8 +59,8 @@ import { formatSessionTabSearch } from '@/lib/session-tab-url';
import { useMachineMonitor } from '@/hooks/use-machine-monitor';
import { useMachineLifecycleCapability } from '@/hooks/use-machine-lifecycle-capability';
import { useOpenSettings } from '@/hooks/use-open-settings';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
import { Drawer, DrawerContent, DrawerDescription, DrawerTitle } from '@/ui/drawer';
import { MobileSettingsRow, MobileSettingsSection } from '@/components/mobile/mobile-settings-row';
import {
@@ -1451,8 +1451,7 @@ export function MachineAgentSettings({
setFilter({ onlineOnly: false, mineOnly: false })}
>
{t('settings.agent.machineTabs.filter.reset', 'Clear filter')}
@@ -1569,8 +1568,8 @@ function OwnPrivateMachines({
onOpen(item.machine.id)}
>
{item.machine.name || item.machine.id}
diff --git a/packages/components/src/components/settings/machine-detail-pane.tsx b/packages/components/src/components/settings/machine-detail-pane.tsx
index 2835b8436..4f9cef775 100644
--- a/packages/components/src/components/settings/machine-detail-pane.tsx
+++ b/packages/components/src/components/settings/machine-detail-pane.tsx
@@ -26,7 +26,7 @@ import {
Users,
} from 'lucide-react';
import { Badge } from '@/ui/badge';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Switch } from '@/ui/switch';
import {
@@ -99,8 +99,8 @@ export function MachineProvidersSection({
@@ -123,7 +123,7 @@ export function MachineProvidersSection({
{t('settings.agent.provider.empty', 'No providers on this machine yet.')}
-
+
{t('settings.agent.provider.addProvider', 'Add provider')}
@@ -447,12 +447,7 @@ export function MachineDetailPane(props: MachineDetailPaneProps) {
/>
) : externalAccordionHeader ? (
manageableOwnMachine ? (
- setRenaming(true)}
- >
+ setRenaming(true)}>
{t('workspace.machines.editName', 'Edit machine name')}
@@ -471,8 +466,9 @@ export function MachineDetailPane(props: MachineDetailPaneProps) {
{!isMobile && manageableOwnMachine && (
setRenaming(true)}
>
@@ -491,8 +487,7 @@ export function MachineDetailPane(props: MachineDetailPaneProps) {
>
void handlePing()}
>
@@ -536,8 +531,9 @@ export function MachineDetailPane(props: MachineDetailPaneProps) {
{sharing ? (
@@ -767,8 +767,9 @@ export function MachineDetailPane(props: MachineDetailPaneProps) {
void handleUpgradeDaemon()}
>
@@ -896,7 +896,7 @@ export function MachineDetailPane(props: MachineDetailPaneProps) {
event.preventDefault();
void handleDelete();
}}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{deleting && }
{t('workspace.machines.removeAction', 'Remove')}
@@ -936,7 +936,7 @@ export function MachineDetailPane(props: MachineDetailPaneProps) {
})
.finally(() => setRevoking(false));
}}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ variant="destructive"
>
{revoking ? : null}
{t('settings.devices.credentials.disconnect', 'Revoke machine access')}
@@ -961,7 +961,7 @@ function EmptyProviders({ onAdd, flush = false }: { onAdd: () => void; flush?: b
{t('settings.agent.provider.empty', 'No providers on this machine yet.')}
-
+
{t('settings.agent.provider.addProvider', 'Add provider')}
diff --git a/packages/components/src/components/settings/machine-tab-list.tsx b/packages/components/src/components/settings/machine-tab-list.tsx
index b5d4041d8..380cf7d06 100644
--- a/packages/components/src/components/settings/machine-tab-list.tsx
+++ b/packages/components/src/components/settings/machine-tab-list.tsx
@@ -4,7 +4,7 @@ import { ChevronRight, ListFilter, LockKeyhole, Users } from 'lucide-react';
import { type MachineId, type MachineViewMeta } from '@lody/shared';
import type { MachineSettingsFilter } from '@/atoms/settings-machine-tab';
import type { MachineVisibilityAccess } from '@/hooks/use-visible-machine-metas';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
DropdownMenu,
DropdownMenuCheckboxItem,
@@ -120,8 +120,7 @@ export function MachineTabList({
onFilterChange({ onlineOnly: false, mineOnly: false })}
>
{t('settings.agent.machineTabs.filter.reset', 'Clear filter')}
@@ -150,12 +149,10 @@ export function MachineListFilterButton({
diff --git a/packages/components/src/components/settings/mcp-connection-form.tsx b/packages/components/src/components/settings/mcp-connection-form.tsx
index c6da3f28e..7a7a699e8 100644
--- a/packages/components/src/components/settings/mcp-connection-form.tsx
+++ b/packages/components/src/components/settings/mcp-connection-form.tsx
@@ -8,7 +8,7 @@ import {
McpTransportIcon,
} from '@/components/shared/mcp-transport';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import { Switch } from '@/ui/switch';
@@ -49,9 +49,7 @@ const emptyConnectionFields = (transport: McpTransport) => ({
headers: [] as KeyValueDraft[],
});
-const createMcpConnectionFormDraft = (
- entry?: WorkspaceMcpServerMeta
-): McpConnectionFormDraft => {
+const createMcpConnectionFormDraft = (entry?: WorkspaceMcpServerMeta): McpConnectionFormDraft => {
const transport = entry?.transport ?? 'stdio';
const connection = entry?.connection;
return {
@@ -328,10 +326,16 @@ export function McpConnectionForm({
-
+
{t('common.cancel')}
-
+
{submitting ? : null}
{submitting ? t('settings.mcp.form.saving') : t('common.save')}
@@ -401,13 +405,7 @@ function ListEditor({
{children}
-
+
{addLabel}
@@ -421,9 +419,10 @@ function RemoveRowButton({ onClick }: { onClick: () => void }) {
return (
diff --git a/packages/components/src/components/settings/mcp-setting.tsx b/packages/components/src/components/settings/mcp-setting.tsx
index 6eb162c43..e5ee39004 100644
--- a/packages/components/src/components/settings/mcp-setting.tsx
+++ b/packages/components/src/components/settings/mcp-setting.tsx
@@ -29,7 +29,7 @@ import {
AlertDialogTitle,
} from '@/ui/alert-dialog';
import { Badge } from '@/ui/badge';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@/ui/dialog';
import { Switch } from '@/ui/switch';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/ui/tooltip';
@@ -151,9 +151,9 @@ export function McpSetting() {
openEditor({ mode: 'add' })}
>
@@ -167,7 +167,7 @@ export function McpSetting() {
{t('settings.mcp.empty')}
-
openEditor({ mode: 'add' })}>
+ openEditor({ mode: 'add' })}>
{addLabel}
@@ -247,8 +247,7 @@ export function McpSetting() {
{t('common.cancel')}
{
event.preventDefault();
void confirmRemoval();
@@ -326,10 +325,11 @@ export function McpServerRow({
diff --git a/packages/components/src/components/settings/my-machine-connected-resources.tsx b/packages/components/src/components/settings/my-machine-connected-resources.tsx
index 730685ca0..1fab5c565 100644
--- a/packages/components/src/components/settings/my-machine-connected-resources.tsx
+++ b/packages/components/src/components/settings/my-machine-connected-resources.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { Bot, Folder } from 'lucide-react';
import type { AgentConfigMeta, MachineId } from '@lody/shared';
import { useLocalProjectsAdmin } from '@/hooks/use-local-projects-admin';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Switch } from '@/ui/switch';
import type { ProjectSettingsRow } from './project-settings';
@@ -166,7 +166,7 @@ function MachineConnectedResourcesContent({
{!readOnly ? (
-
+
{t('settings.machines.manageAgents', 'Manage in Agents')}
) : null}
diff --git a/packages/components/src/components/settings/open-source-attributions-dialog.tsx b/packages/components/src/components/settings/open-source-attributions-dialog.tsx
index bc9324213..733281eb2 100644
--- a/packages/components/src/components/settings/open-source-attributions-dialog.tsx
+++ b/packages/components/src/components/settings/open-source-attributions-dialog.tsx
@@ -167,9 +167,8 @@ export function OpenSourceAttributionsDialog({
>
{
event.preventDefault();
clearOpenTimer();
diff --git a/packages/components/src/components/settings/path-launchers-setting.tsx b/packages/components/src/components/settings/path-launchers-setting.tsx
index bd0037bd1..0eb3b27b0 100644
--- a/packages/components/src/components/settings/path-launchers-setting.tsx
+++ b/packages/components/src/components/settings/path-launchers-setting.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import type { TFunction } from 'i18next';
import { usePostHog } from '@posthog/react';
import { Check, Pencil, Plus, Trash2 } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import {
@@ -220,9 +220,10 @@ export function PathLaunchersSettings({
{
@@ -386,8 +387,9 @@ function LauncherFormDialog({
@@ -674,10 +674,9 @@ function ProjectSettingsDesktop({
{addToSelectedMachine ? (
@@ -794,7 +793,8 @@ function ProjectAddMenu({
{
@@ -1615,7 +1615,7 @@ export function ProjectHistoryImportPanel({
{
@@ -1829,7 +1829,7 @@ export function ProjectHistoryImportPanel({
{
@@ -1877,8 +1877,7 @@ export function ProjectHistoryImportPanel({
{t('common.cancel', 'Cancel')}
{t('workspace.projects.resolveHistoryConflict', 'Re-import')}
diff --git a/packages/components/src/components/settings/project-skills-tab.tsx b/packages/components/src/components/settings/project-skills-tab.tsx
index bfca58d15..2481c84da 100644
--- a/packages/components/src/components/settings/project-skills-tab.tsx
+++ b/packages/components/src/components/settings/project-skills-tab.tsx
@@ -21,7 +21,7 @@ import {
type ProjectSkillsSource,
type ProjectSkillsStatus,
} from '@/hooks/use-project-skills';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { cn } from '@/lib/utils';
@@ -109,7 +109,13 @@ export function ProjectSkillsView({
title={t('workspace.projects.skills.errorTitle', "Couldn't load skills")}
body={error}
action={
-
+
{t('workspace.projects.skills.retry', 'Retry')}
@@ -170,8 +176,8 @@ export function ProjectSkillsView({
diff --git a/packages/components/src/components/settings/provider-progress-button.tsx b/packages/components/src/components/settings/provider-progress-button.tsx
index a533e4dc6..bf54a0134 100644
--- a/packages/components/src/components/settings/provider-progress-button.tsx
+++ b/packages/components/src/components/settings/provider-progress-button.tsx
@@ -1,6 +1,6 @@
import type { ReactNode } from 'react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
export function ProviderProgressButton({
@@ -31,16 +31,13 @@ export function ProviderProgressButton({
return (
{boundedPercent !== null ? (
{
event.stopPropagation();
setDeleteOpen(true);
@@ -277,7 +278,7 @@ export function ProviderRow({
event.preventDefault();
void handleDelete();
}}
- className={cn('bg-destructive text-destructive-foreground hover:bg-destructive/90')}
+ variant="destructive"
>
{deleting && }
{t('common.delete', 'Delete')}
diff --git a/packages/components/src/components/settings/provider-setup-row.tsx b/packages/components/src/components/settings/provider-setup-row.tsx
index 5d583a68a..ff0e6112e 100644
--- a/packages/components/src/components/settings/provider-setup-row.tsx
+++ b/packages/components/src/components/settings/provider-setup-row.tsx
@@ -10,7 +10,7 @@ import {
import { Loader2, RotateCcw, Trash2, XCircle } from 'lucide-react';
import { AgentReadinessMark, type AgentReadiness } from '@/components/shared/agent-readiness-mark';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
import { activeWorkspaceRuntimeAtom } from '@/atoms/runtime';
import { useMachineAcpBinaryProgress } from '@/hooks/use-machine-acp-binary-progress';
@@ -181,9 +181,9 @@ export function ProviderSetupRow({
) : setup.status === 'failed' ? (
void runAction('retry', onRetry)}
>
@@ -199,8 +199,10 @@ export function ProviderSetupRow({
void runAction('delete', onDelete)}
diff --git a/packages/components/src/components/settings/review-policy-setting.tsx b/packages/components/src/components/settings/review-policy-setting.tsx
index 50ccb21a9..20b2db38c 100644
--- a/packages/components/src/components/settings/review-policy-setting.tsx
+++ b/packages/components/src/components/settings/review-policy-setting.tsx
@@ -40,7 +40,7 @@ import {
DesktopRunConfigMenu,
} from '@/components/sessions/desktop-run-config-menu';
import { MobileSettingsRow, MobileSettingsSection } from '@/components/mobile/mobile-settings-row';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Switch } from '@/ui/switch';
import { Textarea } from '@/ui/textarea';
@@ -199,20 +199,20 @@ function ReviewerMachineRow({
{t('settings.review.noAgentsOnMachine', 'No agents are configured on this machine.')}
-
+
{t('settings.review.configureAgents', 'Configure agents')}
{reviewerConfig ? (
onDelete(machine.id)}
>
@@ -299,14 +299,14 @@ function ReviewerMachineRow({
{reviewerConfig ? (
onDelete(machine.id)}
>
diff --git a/packages/components/src/components/settings/settings-header.tsx b/packages/components/src/components/settings/settings-header.tsx
index cc17e7df2..02f7d5d67 100644
--- a/packages/components/src/components/settings/settings-header.tsx
+++ b/packages/components/src/components/settings/settings-header.tsx
@@ -1,6 +1,6 @@
import type { ReactNode } from 'react';
import { ArrowLeft } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { useRouter } from '@tanstack/react-router';
import { cn } from '@/lib/utils';
import { useAtomValue } from 'jotai';
@@ -43,7 +43,7 @@ export function SettingsHeader({ title, onBack, actions, className }: SettingsHe
>
{/* 返回按钮 */}
-
+
diff --git a/packages/components/src/components/settings/usage-calendar-visualization.tsx b/packages/components/src/components/settings/usage-calendar-visualization.tsx
index 6bc3ac7b4..df8f69297 100644
--- a/packages/components/src/components/settings/usage-calendar-visualization.tsx
+++ b/packages/components/src/components/settings/usage-calendar-visualization.tsx
@@ -28,7 +28,7 @@ import i18next from 'i18next';
import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';
import { Avatar, AvatarFallback, AvatarImage } from '@/ui/avatar';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Popover, PopoverContent, PopoverTrigger } from '@/ui/popover';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/ui/tooltip';
import { formatCompactNumber, formatUsdAmount } from '@/lib/format-compact-number';
@@ -1655,10 +1655,11 @@ function UsageDayDetailPanel({
className="relative rounded-lg bg-muted/40 p-4"
>
@@ -2300,11 +2301,11 @@ export function UsageCalendarVisualization({
-
+
@@ -2340,8 +2341,8 @@ export function UsageCalendarVisualization({
void shareCard()}
>
@@ -2488,21 +2489,21 @@ export function UsageCalendarVisualization({
void copyAscii()}
aria-label={t('workspace.usage.skyline.copyAscii')}
>
-
+
{t('workspace.usage.skyline.copyAscii')}
-
+
{t('workspace.usage.skyline.downloadAscii')}
-
+
{t('workspace.usage.skyline.downloadBinaryStl')}
diff --git a/packages/components/src/components/settings/workspace-join-requests-settings.tsx b/packages/components/src/components/settings/workspace-join-requests-settings.tsx
index f2c537af7..2ff3a5d52 100644
--- a/packages/components/src/components/settings/workspace-join-requests-settings.tsx
+++ b/packages/components/src/components/settings/workspace-join-requests-settings.tsx
@@ -5,7 +5,7 @@ import { toast } from 'sonner';
import { useCloudMutation, useCloudQuery } from '@lody/platform/react';
import { cloudOperations } from '@/lib/cloud-api-operations';
import { getAppShareUrl } from '@/lib/app-location';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/ui/select';
export function WorkspaceJoinRequestsSettings({ workspaceId }: { workspaceId: string }) {
@@ -66,8 +66,8 @@ export function WorkspaceJoinRequestsSettings({ workspaceId }: { workspaceId: st
void run('rotate', () =>
@@ -100,10 +100,10 @@ export function WorkspaceJoinRequestsSettings({ workspaceId }: { workspaceId: st
})}
{
void navigator.clipboard
.writeText(joinUrl)
@@ -113,9 +113,9 @@ export function WorkspaceJoinRequestsSettings({ workspaceId }: { workspaceId: st
void run('revoke', () => revokeLink({ workspaceId, linkId: activeLink.id }))
@@ -154,10 +154,10 @@ export function WorkspaceJoinRequestsSettings({ workspaceId }: { workspaceId: st
void run(`reject:${request.id}`, () =>
@@ -172,9 +172,8 @@ export function WorkspaceJoinRequestsSettings({ workspaceId }: { workspaceId: st
)}
void run(`approve:${request.id}`, () =>
diff --git a/packages/components/src/components/settings/workspace-ownership-transfer.tsx b/packages/components/src/components/settings/workspace-ownership-transfer.tsx
index d5cedb5ce..c0572f737 100644
--- a/packages/components/src/components/settings/workspace-ownership-transfer.tsx
+++ b/packages/components/src/components/settings/workspace-ownership-transfer.tsx
@@ -1,7 +1,7 @@
import { useId, useRef, useState } from 'react';
import { ConvexError } from 'convex/values';
import { useTranslation } from 'react-i18next';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Label } from '@/ui/label';
import {
@@ -89,8 +89,8 @@ export function WorkspaceOwnershipTransfer({
changeOpen(true)}
>
@@ -145,7 +145,7 @@ export function WorkspaceOwnershipTransfer({
) : null}
- changeOpen(false)}>
+ changeOpen(false)}>
{t('common.cancel')}
void submit()}>
diff --git a/packages/components/src/components/shared/acp-session-select.tsx b/packages/components/src/components/shared/acp-session-select.tsx
index 04ff24594..00cef3c77 100644
--- a/packages/components/src/components/shared/acp-session-select.tsx
+++ b/packages/components/src/components/shared/acp-session-select.tsx
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
import { Check, ChevronDown } from 'lucide-react';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/ui';
import {
DropdownMenu,
@@ -73,14 +73,11 @@ export function AcpSessionSelect({
({
type="button"
variant="ghost"
className={cn(
- 'w-auto select-none justify-between gap-2 rounded-1 border border-transparent px-3 font-medium text-foreground hover:foreground/70',
+ 'w-auto select-none justify-between rounded-1 hover:foreground/70',
sizeClassMap[size],
disabled && 'opacity-50 pointer-events-none',
className
diff --git a/packages/components/src/components/shared/session-relation-card.tsx b/packages/components/src/components/shared/session-relation-card.tsx
index cb911f759..e68273c79 100644
--- a/packages/components/src/components/shared/session-relation-card.tsx
+++ b/packages/components/src/components/shared/session-relation-card.tsx
@@ -1,7 +1,7 @@
import type { ElementType, ReactNode } from 'react';
import { ArrowUpRight, GitBranchPlus } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
export function SessionRelationCard({
@@ -45,9 +45,9 @@ export function SessionRelationCard({
{status}
diff --git a/packages/components/src/components/shared/workdir-mode-selector.tsx b/packages/components/src/components/shared/workdir-mode-selector.tsx
index 3c0c02206..40c2b865d 100644
--- a/packages/components/src/components/shared/workdir-mode-selector.tsx
+++ b/packages/components/src/components/shared/workdir-mode-selector.tsx
@@ -2,7 +2,7 @@ import { Check, ChevronDown, Folder, GitBranch } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
@@ -88,7 +88,6 @@ const modeIcon = {
} as const;
export function WorkdirModeSelector({
- tone,
mode,
onModeChange,
worktreeAvailable,
@@ -99,7 +98,6 @@ export function WorkdirModeSelector({
const readOnly = !onModeChange;
const selectedMode = mode === 'worktree' && worktreeAvailable ? 'worktree' : 'local';
const SelectedIcon = modeIcon[selectedMode];
- const isDark = tone === 'dark';
const options: Array<{
value: WorkdirMode;
label: string;
@@ -126,14 +124,13 @@ export function WorkdirModeSelector({
diff --git a/packages/components/src/components/sidebar-filter-popover.tsx b/packages/components/src/components/sidebar-filter-popover.tsx
index 6c786a496..ab4ddf88d 100644
--- a/packages/components/src/components/sidebar-filter-popover.tsx
+++ b/packages/components/src/components/sidebar-filter-popover.tsx
@@ -5,7 +5,7 @@ import type { LucideIcon } from 'lucide-react';
import { CarbonSettingsAdjust } from '@/components/icons/carbon-settings-adjust';
import { cn } from '@/lib/utils';
import { Popover, PopoverContent, PopoverTrigger } from '@/ui/popover';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import type { SidebarOrganizeMode } from '@/atoms/sidebar-state';
import type { SidebarChatScope } from '@/atoms/sidebar-state';
@@ -123,13 +123,14 @@ export function SidebarFilterPopover({
-
+
{t('sidebar.updateReady.later', 'Later')}
{isDownloading ? null : (
-
+
{isRestarting ? : null}
{t('sidebar.updateReady.restart', 'Update & Restart')}
diff --git a/packages/components/src/components/stuck-connection-banner.tsx b/packages/components/src/components/stuck-connection-banner.tsx
index 8c1062eae..795ef9ee8 100644
--- a/packages/components/src/components/stuck-connection-banner.tsx
+++ b/packages/components/src/components/stuck-connection-banner.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { usePlatform } from '@lody/platform/react';
import { motion } from 'framer-motion';
import { Loader2, X } from 'lucide-react';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { useStuckConnectionHint } from '@/hooks/use-stuck-connection';
import { ClearCacheConfirmDialog, useClearCache } from './settings/clear-cache';
@@ -61,8 +61,9 @@ export function StuckConnectionBanner({
{labels.title}
@@ -76,11 +77,7 @@ export function StuckConnectionBanner({
{labels.description}
-
+
{labels.clearCache}
diff --git a/packages/components/src/components/tasks/task-attach-session-dialog.tsx b/packages/components/src/components/tasks/task-attach-session-dialog.tsx
index e55a29b85..331539359 100644
--- a/packages/components/src/components/tasks/task-attach-session-dialog.tsx
+++ b/packages/components/src/components/tasks/task-attach-session-dialog.tsx
@@ -2,14 +2,8 @@ import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { MessagesSquare, Search } from 'lucide-react';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogHeader,
- DialogTitle,
-} from '@/ui/dialog';
+import { Button } from '@lody/ui/button';
+import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/ui/dialog';
import { Input } from '@/ui/input';
import { ScrollArea } from '@/ui/scroll-area';
import { TASKS_SURFACE_CLASS } from './tasks-surface';
@@ -135,7 +129,7 @@ export function TaskAttachSessionDialog({
-
+
{t('common.cancel', 'Cancel')}
diff --git a/packages/components/src/components/tasks/task-body-editor-fallback.tsx b/packages/components/src/components/tasks/task-body-editor-fallback.tsx
index 79d64fea4..07e804d13 100644
--- a/packages/components/src/components/tasks/task-body-editor-fallback.tsx
+++ b/packages/components/src/components/tasks/task-body-editor-fallback.tsx
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Eye, Pencil, Quote } from 'lucide-react';
import { toast } from 'sonner';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Textarea } from '@/ui/textarea';
import { MarkdownRenderer } from '@/components/ai-gui/markdown-renderer';
import type { TaskBodyEditorProps } from './task-body-editor';
@@ -185,14 +185,14 @@ export default function TaskBodyEditorFallback({
{mode === 'edit' && onQuoteSelection ? (
-
+
{t('tasks.body.quote', 'Quote selection')}
) : null}
setMode(mode === 'edit' ? 'preview' : 'edit')}
>
{mode === 'edit' ? (
diff --git a/packages/components/src/components/tasks/task-body-editor-surface.tsx b/packages/components/src/components/tasks/task-body-editor-surface.tsx
index c05ab1a04..7a4213799 100644
--- a/packages/components/src/components/tasks/task-body-editor-surface.tsx
+++ b/packages/components/src/components/tasks/task-body-editor-surface.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { Quote } from 'lucide-react';
import { MeowdownEditor, type EditorHandle } from '@meowdown/react';
import { toast } from 'sonner';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
import { useKeyScope } from '@/lib/commands';
import { shouldAdoptRemoteBody, type TaskBodyEditorProps } from './task-body-editor';
@@ -406,7 +406,7 @@ export default function TaskBodyEditorSurface({
menu also exposes Quote when the host provides it. */}
{onQuoteSelection && hasSelection ? (
-
+
{t('tasks.body.quote', 'Quote selection')}
diff --git a/packages/components/src/components/tasks/task-detail-actions-menu.tsx b/packages/components/src/components/tasks/task-detail-actions-menu.tsx
index 54c1716ab..50f8dac97 100644
--- a/packages/components/src/components/tasks/task-detail-actions-menu.tsx
+++ b/packages/components/src/components/tasks/task-detail-actions-menu.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { Check, Copy, Link2, MoreHorizontal } from 'lucide-react';
import type { TaskId } from '@lody/shared';
import { useTaskDoc } from '@/hooks/use-task-doc';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
@@ -66,12 +66,7 @@ export function TaskDetailActionsMenu({
-
+
diff --git a/packages/components/src/components/tasks/task-detail-view.tsx b/packages/components/src/components/tasks/task-detail-view.tsx
index 331abfd39..1ed168412 100644
--- a/packages/components/src/components/tasks/task-detail-view.tsx
+++ b/packages/components/src/components/tasks/task-detail-view.tsx
@@ -51,7 +51,7 @@ import { useOpenSettings } from '@/hooks/use-open-settings';
import { cloudOperations } from '@/lib/cloud-api-operations';
import type { UnifiedLocalProjectOption } from '@/components/chat/unified-project-selector';
import { getChatLandingProjectRecency } from '@/components/chat/chat-landing-derived';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { ScrollArea } from '@/ui/scroll-area';
import { TooltipProvider } from '@/ui/tooltip';
import {
@@ -711,7 +711,7 @@ export function TaskDetailView({ embedded = false }: TaskDetailViewProps) {
const mobileStatusMenu = (
-
+
{t(presentation.labelKey, presentation.labelFallback)}
@@ -753,9 +753,9 @@ export function TaskDetailView({ embedded = false }: TaskDetailViewProps) {
leading={
isMobile ? null : (
{
if (workspaceSlug) {
void router.navigate({
diff --git a/packages/components/src/components/tasks/task-launch-controls.tsx b/packages/components/src/components/tasks/task-launch-controls.tsx
index 9c00491f7..9dadaf4ad 100644
--- a/packages/components/src/components/tasks/task-launch-controls.tsx
+++ b/packages/components/src/components/tasks/task-launch-controls.tsx
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next';
import { AlertTriangle, Bot, ChevronRight, FolderGit2, Play } from 'lucide-react';
import type { MachineOnlineStatus } from '@/atoms/presence';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
@@ -89,9 +89,7 @@ function SlotChip({
>
{label}
- {secondary ? (
- · {secondary}
- ) : null}
+ {secondary ? · {secondary} : null}
{children}
@@ -129,11 +127,11 @@ export function TaskLaunchControls({
return (
@@ -164,7 +162,10 @@ export function TaskLaunchControls({
onClick={() => onSelectAgent(option.agentConfigId)}
>
{option.name}
diff --git a/packages/components/src/components/tasks/task-properties-panel.tsx b/packages/components/src/components/tasks/task-properties-panel.tsx
index fcdce2ea0..34534b3b2 100644
--- a/packages/components/src/components/tasks/task-properties-panel.tsx
+++ b/packages/components/src/components/tasks/task-properties-panel.tsx
@@ -11,7 +11,7 @@ import {
import type { MachineOnlineStatus } from '@/atoms/presence';
import type { UnifiedLocalProjectOption } from '@/components/chat/unified-project-selector';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
@@ -20,10 +20,7 @@ import {
} from '@/ui/dropdown-menu';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/ui/tooltip';
import { CachedAvatarImg } from '@/components/cached-avatar-img';
-import {
- getTaskStatusPresentation,
- TASK_STATUS_PRESENTATION,
-} from './task-status-presentation';
+import { getTaskStatusPresentation, TASK_STATUS_PRESENTATION } from './task-status-presentation';
import {
getTaskPriorityPresentation,
TASK_PRIORITY_PRESENTATION,
@@ -149,10 +146,7 @@ function PropertyButton({
{leading ?? }
{valueContent != null ? (
@@ -288,10 +282,7 @@ export function TaskPropertiesPanel({
style={tasksMenuSurfaceStyle}
>
{TASK_STATUS_PRESENTATION.map((option) => (
- onStatusChange(option.status)}
- >
+ onStatusChange(option.status)}>
{t(option.labelKey, option.labelFallback)}
@@ -304,11 +295,7 @@ export function TaskPropertiesPanel({
value={ownerName}
placeholder={t('tasks.properties.owner', 'Owner')}
disabled={disabled}
- leading={
- owner ? (
-
- ) : undefined
- }
+ leading={owner ? : undefined}
>
diff --git a/packages/components/src/components/tasks/task-proposal-notice.tsx b/packages/components/src/components/tasks/task-proposal-notice.tsx
index 635123775..ba8f8c77d 100644
--- a/packages/components/src/components/tasks/task-proposal-notice.tsx
+++ b/packages/components/src/components/tasks/task-proposal-notice.tsx
@@ -7,7 +7,7 @@ import { ListTodo } from 'lucide-react';
import type { SessionId, TaskProposalMeta } from '@lody/shared';
import { currentWorkspaceSlugAtom } from '@/atoms';
import { useTaskActions } from '@/hooks/use-task-actions';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { MarkdownRenderer } from '@/components/ai-gui/markdown-renderer';
export type TaskProposalNoticeProps = {
@@ -100,7 +100,7 @@ export function TaskProposalNotice({
{t('tasks.proposal.created', 'Task created')}
{meta.title}
{meta.taskId ? (
- openTask(meta.taskId as string)}>
+ openTask(meta.taskId as string)}>
{t('tasks.proposal.openTask', 'Open task')}
) : null}
@@ -136,10 +136,10 @@ export function TaskProposalNotice({
) : null}
-
+
{t('tasks.proposal.dismiss', 'Ignore')}
-
+
{t('tasks.proposal.create', 'Create task')}
diff --git a/packages/components/src/components/tasks/task-quick-add-dialog.tsx b/packages/components/src/components/tasks/task-quick-add-dialog.tsx
index 0ba7b1d5f..9ad0fb7ad 100644
--- a/packages/components/src/components/tasks/task-quick-add-dialog.tsx
+++ b/packages/components/src/components/tasks/task-quick-add-dialog.tsx
@@ -15,7 +15,7 @@ import {
import { taskLabelPillStyle } from './task-label-presentation';
import type { UnifiedLocalProjectOption } from '@/components/chat/unified-project-selector';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Checkbox } from '@/ui/checkbox';
import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@/ui/dialog';
import {
@@ -284,7 +284,9 @@ export function TaskQuickAddDialog({
>
{t(presentation.labelKey, presentation.labelFallback)}
- {presentation.status === status ? : null}
+ {presentation.status === status ? (
+
+ ) : null}
))}
@@ -310,7 +312,10 @@ export function TaskQuickAddDialog({
style={tasksMenuSurfaceStyle}
>
{TASK_PRIORITY_PRESENTATION.map((option) => (
- setPriority(option.priority)}>
+ setPriority(option.priority)}
+ >
{t(option.labelKey, option.labelFallback)}
{option.priority === priority ? : null}
@@ -326,9 +331,7 @@ export function TaskQuickAddDialog({
disabled={submitting}
icon={ }
>
- {labels.length > 0
- ? labels.join(', ')
- : t('tasks.properties.labels', 'Labels')}
+ {labels.length > 0 ? labels.join(', ') : t('tasks.properties.labels', 'Labels')}
-
+
{t('common.cancel', 'Cancel')}
-
+
{t('tasks.quickAdd.create', 'Create')}
⌘↵
diff --git a/packages/components/src/components/tasks/task-thread.tsx b/packages/components/src/components/tasks/task-thread.tsx
index c05cbe1e9..29b2ac257 100644
--- a/packages/components/src/components/tasks/task-thread.tsx
+++ b/packages/components/src/components/tasks/task-thread.tsx
@@ -18,7 +18,7 @@ import {
} from '@lody/shared';
import { toast } from 'sonner';
import { cn } from '@/lib/utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Textarea } from '@/ui/textarea';
import { MarkdownRenderer } from '@/components/ai-gui/markdown-renderer';
import {
@@ -414,9 +414,10 @@ export function TaskThread({
) : null}
imageInputRef.current?.click()}
aria-label={t('tasks.thread.addImage', 'Add image')}
@@ -428,9 +429,10 @@ export function TaskThread({
)}
-
+
@@ -516,13 +511,13 @@ export function TasksListBody({
const mobileActions = (
setNeedsYouOnly(!needsYouOnly)}
>
{t('tasks.needsYou', 'Needs you')}
-
handleQuickAdd()}>
+ handleQuickAdd()}>
{t('tasks.newTask', 'New task')}
diff --git a/packages/components/src/components/update-changelog-dialog.tsx b/packages/components/src/components/update-changelog-dialog.tsx
index f987deaab..0f6d23468 100644
--- a/packages/components/src/components/update-changelog-dialog.tsx
+++ b/packages/components/src/components/update-changelog-dialog.tsx
@@ -80,12 +80,12 @@ export function UpdateChangelogDialog({
)}
{!notes ? (
-
+
{t('updates.changelog.openWebsite', 'Open changelog website')}
) : null}
- onOpenChange(false)}>
+ onOpenChange(false)}>
{t('common.close', 'Close')}
diff --git a/packages/components/src/stories/AddLocalProjectDialog.stories.tsx b/packages/components/src/stories/AddLocalProjectDialog.stories.tsx
index 6b5a0e9fb..560fab119 100644
--- a/packages/components/src/stories/AddLocalProjectDialog.stories.tsx
+++ b/packages/components/src/stories/AddLocalProjectDialog.stories.tsx
@@ -7,7 +7,7 @@ import {
type RemoteDirectoryOps,
type RemoteDirectoryPickerMachine,
} from '@/components/local-projects/add-local-project-dialog';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
type FakeEntry = {
name: string;
diff --git a/packages/components/src/stories/AssistantTurnAlignment.stories.tsx b/packages/components/src/stories/AssistantTurnAlignment.stories.tsx
index 042b75b39..b7eee19b3 100644
--- a/packages/components/src/stories/AssistantTurnAlignment.stories.tsx
+++ b/packages/components/src/stories/AssistantTurnAlignment.stories.tsx
@@ -358,11 +358,8 @@ const planModeTurn: SessionHistoryParsed = {
isLatest: true,
markdown: [
'## Goal',
- '',
'Keep every top-level row of a plan-mode turn on one left rail.',
- '',
'## Steps',
- '',
'1. No per-shell horizontal pad on the `switch_mode` card.',
'2. The resolved permission card sits on the rail, not on its own `ml-4`.',
].join('\n'),
@@ -488,11 +485,8 @@ const planAwaitingDecisionTurn: SessionHistoryParsed = {
isLatest: true,
markdown: [
'## Goal',
- '',
'Keep every top-level row of a plan-mode turn on one left rail.',
- '',
'## Steps',
- '',
'1. No per-shell horizontal pad on the `switch_mode` card.',
'2. The resolved permission card sits on the rail.',
'3. Attachments sort below the plan.',
@@ -530,11 +524,8 @@ const planAwaitingDecisionTurn: SessionHistoryParsed = {
*/
const PARITY_PLAN = [
'## Goal',
- '',
'Render the same plan panel whichever adapter produced it.',
- '',
'## Steps',
- '',
'1. Resolve the carrier in `plan-surface.ts`.',
'2. Feed every carrier into one `PlanPanel`.',
].join('\n');
diff --git a/packages/components/src/stories/BaseHeader.stories.tsx b/packages/components/src/stories/BaseHeader.stories.tsx
index b0591056b..62517ef41 100644
--- a/packages/components/src/stories/BaseHeader.stories.tsx
+++ b/packages/components/src/stories/BaseHeader.stories.tsx
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react';
import { BaseHeader } from '@/components/page-headers/base-header';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Plus, Filter, Search } from 'lucide-react';
import { Input } from '@/ui/input';
@@ -42,11 +42,11 @@ export const Default: Story = {
title: 'Page Title',
actions: (
-
+
Filter
-
+
New Session
New
@@ -126,11 +126,11 @@ export const Tablet: Story = {
title: 'Projects',
actions: (
-
+
Filter
-
+
New Project
New
@@ -153,11 +153,11 @@ export const DarkMode: Story = {
title: 'Dark Mode Header',
actions: (
-
+
Filter
-
+
New Item
diff --git a/packages/components/src/stories/ChatComposer.stories.tsx b/packages/components/src/stories/ChatComposer.stories.tsx
index e3c4642a9..1a13b75ab 100644
--- a/packages/components/src/stories/ChatComposer.stories.tsx
+++ b/packages/components/src/stories/ChatComposer.stories.tsx
@@ -10,7 +10,7 @@ import {
import type { OptionSelectorOption } from '@/components/shared/option-selector';
import { OptionSelector } from '@/components/shared/option-selector';
import { Card, CardContent } from '@/ui/card';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
import { getPastedTextCharacterCount, type PastedTextDraft } from '@/lib/pasted-text-draft';
import { registerBuiltInCommands } from '@/lib/commands';
@@ -78,10 +78,8 @@ const repoOptions: OptionSelectorOption[] = [
const samplePastedText = [
'Design brief:',
- '',
'Users often paste full logs or PR descriptions into the composer before asking a focused question.',
'We should keep that context available without letting the textarea grow so tall that the actual prompt disappears.',
- '',
'Requirements:',
'- Show a compact summary block with the pasted character count.',
'- Let users inspect the full content on hover or tap.',
@@ -194,17 +192,11 @@ function DemoComposer({
: []
);
- const primaryActionClassName = cn(
- variant === 'dialog'
- ? 'border font-semibold transition-all focus-visible:ring-2 focus-visible:ring-offset-2 h-10 rounded-lg px-5 text-sm'
- : 'h-8 w-8 rounded-md border shadow-xs transition-all',
+ const dialogPrimaryActionClassName = cn(
+ 'border font-semibold transition-all focus-visible:ring-2 focus-visible:ring-offset-2 h-10 rounded-lg px-5 text-sm',
tone === 'dark'
- ? variant !== 'dialog'
- ? 'border-sky-200/20 bg-sky-300/15 text-white hover:bg-sky-300/25 active:translate-y-[1px] focus-visible:ring-white/40 focus-visible:ring-offset-[#050b1d]'
- : 'border-white/25 bg-white/10 text-white hover:bg-white/15 active:translate-y-[1px] focus-visible:ring-white/30 focus-visible:ring-offset-[#050b1d]'
- : variant !== 'dialog'
- ? 'border-input-border/70 bg-input/70 text-input-foreground hover:bg-muted/60 active:translate-y-[1px] focus-visible:ring-ring'
- : 'border-input-border/70 bg-input/60 text-input-foreground hover:bg-muted/60 active:translate-y-[1px] focus-visible:ring-ring'
+ ? 'border-white/25 bg-white/10 text-white hover:bg-white/15 active:translate-y-[1px] focus-visible:ring-white/30 focus-visible:ring-offset-[#050b1d]'
+ : 'border-input-border/70 bg-input/60 text-input-foreground hover:bg-muted/60 active:translate-y-[1px] focus-visible:ring-ring'
);
const selectorNode =
@@ -262,17 +254,11 @@ function DemoComposer({
const primaryActionNode =
variant !== 'dialog' ? (
-
+
) : (
-
+
Send
);
diff --git a/packages/components/src/stories/ChatFailedDetailDialog.stories.tsx b/packages/components/src/stories/ChatFailedDetailDialog.stories.tsx
index 4677565d2..d9aed6528 100644
--- a/packages/components/src/stories/ChatFailedDetailDialog.stories.tsx
+++ b/packages/components/src/stories/ChatFailedDetailDialog.stories.tsx
@@ -44,7 +44,6 @@ export const LongRawPayload: Story = {
summary: 'Unhandled exception while starting the agent',
message: [
'Internal error: Unhandled exception while starting the agent',
- '',
...Array.from(
{ length: 40 },
(_, index) =>
diff --git a/packages/components/src/stories/ChatLandingAttachments.stories.tsx b/packages/components/src/stories/ChatLandingAttachments.stories.tsx
index 7fcbeb59e..7782aef00 100644
--- a/packages/components/src/stories/ChatLandingAttachments.stories.tsx
+++ b/packages/components/src/stories/ChatLandingAttachments.stories.tsx
@@ -16,8 +16,7 @@ import {
MobileInlinePickerCoordinator,
MobileInlinePickerRowSlot,
} from '@/components/mobile/mobile-inline-picker';
-import { Button } from '@/ui/button';
-import { cn } from '@/lib/utils';
+import { Button } from '@lody/ui/button';
import { registerBuiltInCommands } from '@/lib/commands';
// So the composer's ⌘L focus hint has a command binding to read.
@@ -182,16 +181,7 @@ function MobileNewChatDemo() {
claude-opus-4-8 · Thinking
}
primaryAction={
-
+
}
diff --git a/packages/components/src/stories/ChatLandingView.stories.tsx b/packages/components/src/stories/ChatLandingView.stories.tsx
index 10acb4d0f..aa8481796 100644
--- a/packages/components/src/stories/ChatLandingView.stories.tsx
+++ b/packages/components/src/stories/ChatLandingView.stories.tsx
@@ -199,19 +199,16 @@ const zhCnPastedText = [
'[错误摘要]',
'在切换到中文后,用户把一整段日志直接粘贴进输入框。',
'之前的行为是整段文本全部撑开输入区域,真正的问题描述会被顶到折叠线以下。',
- '',
'[期望]',
'1. 输入框里只保留一段内联提示。',
'2. 用户仍然知道自己已经粘贴了大量上下文。',
'3. 点击这段提示后,可以看到完整内容并继续发送。',
- '',
'[原始日志片段]',
'RangeError: Invalid language tag: zh_CN',
' at new NumberFormat ()',
' at ChatLanding (chat-landing.tsx:219:27)',
' at renderWithHooks (react-dom-client.development.js:5529:22)',
' at updateFunctionComponent (react-dom-client.development.js:8897:19)',
- '',
'补充说明:',
'这类日志在实际使用里通常会更长,里面还会包含堆栈、请求参数、环境信息和用户自己的描述。',
].join('\n');
diff --git a/packages/components/src/stories/ChatMessageSelection.stories.tsx b/packages/components/src/stories/ChatMessageSelection.stories.tsx
index 7c5d8ee77..ab331eeba 100644
--- a/packages/components/src/stories/ChatMessageSelection.stories.tsx
+++ b/packages/components/src/stories/ChatMessageSelection.stories.tsx
@@ -25,7 +25,7 @@ import {
SessionConversationPageHeader,
} from '@/components/sessions/session-conversation-page';
import { ChatShareImageDialog } from '@/components/sessions/chat-share-image-dialog';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
const sessionId = 'share-selection-story' as SessionId;
const session = {
@@ -126,7 +126,11 @@ function SelectionHarness({ long = false }: { long?: boolean }) {
selection.start(candidates, confirm)}>
+ selection.start(candidates, confirm)}
+ >
{t('sessions.shareImage.dialogTitle', 'Share as image')}
diff --git a/packages/components/src/stories/CodeCollabMarkdownPreview.stories.tsx b/packages/components/src/stories/CodeCollabMarkdownPreview.stories.tsx
index 1ed3d1c36..fae9ee048 100644
--- a/packages/components/src/stories/CodeCollabMarkdownPreview.stories.tsx
+++ b/packages/components/src/stories/CodeCollabMarkdownPreview.stories.tsx
@@ -18,41 +18,29 @@ const MARKDOWN_FILE: SessionFileProviderEntry = {
const MARKDOWN_SOURCE = [
'# Loro Code Collab',
- '',
'Host-backed file browsing and digest-checked editing for your repository.',
- '',
'> Markdown files open as rendered documents, with the source editor one toggle away.',
- '',
'## Highlights',
- '',
'- Host RPC open/refresh/save with digest conflict checks',
'- Path-keyed file tree and current All Changes state',
'- Rendered previews for `.svg` and `.md` files',
- '',
'## Quick start',
- '',
'```ts',
'const file = await provider.openFile("README.md");',
'await provider.saveFile("README.md", file.digest, nextText);',
'```',
- '',
'## Supported file states',
- '',
'| State | Editable | Notes |',
'| ------------------ | :------: | ------------------------------ |',
'| live-collaborative | ✅ | Host RPC read/write |',
'| live-readonly | ❌ | Host RPC read-only |',
'| historical-turn | ❌ | Unavailable in v2 |',
- '',
'### Checklist',
- '',
'1. Open a workspace',
'2. Pick a file from the tree',
'3. Toggle **Code** to edit the Markdown source',
- '',
'- [x] Code/Rendered toggle',
'- [ ] Side-by-side preview',
- '',
'See the [docs](https://example.com) for more.',
].join('\n');
diff --git a/packages/components/src/stories/CodeCollabMonacoEditor.stories.tsx b/packages/components/src/stories/CodeCollabMonacoEditor.stories.tsx
index fd10b4c08..e79f1140a 100644
--- a/packages/components/src/stories/CodeCollabMonacoEditor.stories.tsx
+++ b/packages/components/src/stories/CodeCollabMonacoEditor.stories.tsx
@@ -38,14 +38,12 @@ const sampleText = [
' path: file.path.replace(/\\\\/g, "/"),',
' sizeBytes: file.sizeBytes ?? 0,',
' }));',
- '',
' return {',
' meta,',
' files,',
' builtAt: Date.now(),',
' };',
'}',
- '',
].join('\n');
const FILE_ID = 'monaco-story-file-1';
diff --git a/packages/components/src/stories/ConversationOutlineArrivalIntentLab.stories.tsx b/packages/components/src/stories/ConversationOutlineArrivalIntentLab.stories.tsx
index 719cf9bf2..c8e65b114 100644
--- a/packages/components/src/stories/ConversationOutlineArrivalIntentLab.stories.tsx
+++ b/packages/components/src/stories/ConversationOutlineArrivalIntentLab.stories.tsx
@@ -8,7 +8,7 @@ import {
} from '@/components/ai-gui/conversation-outline-rail';
import { DEFAULT_ARRIVAL_INTENT_CONFIG } from '@/components/ai-gui/conversation-outline-arrival-intent';
import type { ConversationOutlineEntry } from '@/lib/conversation-outline';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
const meta = {
title: 'Sessions/ConversationOutlineRail/ArrivalIntentLab',
@@ -304,49 +304,49 @@ function ArrivalIntentLab() {
{recording ? (
-
- Stop
+
+ Stop
) : (
-
- Record
+
+ Record
)}
labelLatestTrial('intended')}
>
- Intended
+ Intended
labelLatestTrial('accidental')}
>
- Accidental
+ Accidental
-
+
-
+
diff --git a/packages/components/src/stories/DesktopSessionDetailLayout.stories.tsx b/packages/components/src/stories/DesktopSessionDetailLayout.stories.tsx
index be6e836ae..d1af7ca39 100644
--- a/packages/components/src/stories/DesktopSessionDetailLayout.stories.tsx
+++ b/packages/components/src/stories/DesktopSessionDetailLayout.stories.tsx
@@ -1,7 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { DesktopSessionDetailLayout } from '@/components/sessions/desktop-session-detail-layout';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
/* The layout persists panel sizes under this key (autoSaveId); clear it so
every story starts from its declared defaultSizes, not a previous drag. */
@@ -70,10 +70,10 @@ function LayoutHarness({
return (
-
+
Open PR tab
- setSidebarOpen(false)}>
+ setSidebarOpen(false)}>
Close sidebar
container: {containerWidthPx}px
diff --git a/packages/components/src/stories/ErrorBoundaryFallback.stories.tsx b/packages/components/src/stories/ErrorBoundaryFallback.stories.tsx
index a61ed79e8..67e2eafc9 100644
--- a/packages/components/src/stories/ErrorBoundaryFallback.stories.tsx
+++ b/packages/components/src/stories/ErrorBoundaryFallback.stories.tsx
@@ -13,7 +13,6 @@ function crashError(): Error {
}
const COMPONENT_STACK = [
- '',
' at SessionChatHeader (session-chat-interface.tsx:5192:21)',
' at ErrorBoundary (error-boundary.tsx:116:1)',
' at RootOutlet',
diff --git a/packages/components/src/stories/FileTreeStates.stories.tsx b/packages/components/src/stories/FileTreeStates.stories.tsx
index 2795b946f..26d25beec 100644
--- a/packages/components/src/stories/FileTreeStates.stories.tsx
+++ b/packages/components/src/stories/FileTreeStates.stories.tsx
@@ -4,7 +4,7 @@ import {
FileTreeSkeleton,
FileTreeStatePanel,
} from '@/components/sessions/components/file-tree-states';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
const meta = {
title: 'Sessions/FileTreeStates',
@@ -66,7 +66,7 @@ export const ErrorWithRetry: Story = {
title="Couldn't load files"
description="Permission denied while reading the project directory."
action={
-
+
Try again
diff --git a/packages/components/src/stories/JoinCommunityDialog.stories.tsx b/packages/components/src/stories/JoinCommunityDialog.stories.tsx
index 26cd712bd..502500aba 100644
--- a/packages/components/src/stories/JoinCommunityDialog.stories.tsx
+++ b/packages/components/src/stories/JoinCommunityDialog.stories.tsx
@@ -47,13 +47,13 @@ export const SettingsAboutRow: StoryObj = {
-
+
Open download page
-
+
Visit website
diff --git a/packages/components/src/stories/LocalProjectRemovalUX.stories.tsx b/packages/components/src/stories/LocalProjectRemovalUX.stories.tsx
index 0c789c704..f0105f49e 100644
--- a/packages/components/src/stories/LocalProjectRemovalUX.stories.tsx
+++ b/packages/components/src/stories/LocalProjectRemovalUX.stories.tsx
@@ -19,7 +19,7 @@ import { LoroSidebar, type LoroSidebarNavKey } from '@/components/loro-sidebar';
import { MobileHomeScreen } from '@/components/mobile/mobile-home-screen';
import { MobileRemoveLocalProjectSheet } from '@/components/mobile/mobile-remove-local-project-sheet';
import { SidebarSectionHeader } from '@/components/sidebar-row-shared';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { Input } from '@/ui/input';
import { Toaster } from '@/ui/sonner';
@@ -198,11 +198,11 @@ function ProjectConversationPane() {
继续讨论这个项目…
-
+
Lody Desktop
- 发送
+ 发送
@@ -409,7 +409,7 @@ function RemovedProjectArchive() {
-
+
diff --git a/packages/components/src/stories/MarkdownRenderer.stories.tsx b/packages/components/src/stories/MarkdownRenderer.stories.tsx
index e1f9b6e33..c2e4bd4b7 100644
--- a/packages/components/src/stories/MarkdownRenderer.stories.tsx
+++ b/packages/components/src/stories/MarkdownRenderer.stories.tsx
@@ -37,9 +37,7 @@ export const Paragraphs: Story = {
size: 'default',
text: [
'This is the first paragraph with some **bold** and _italic_ text.',
- '',
'This is the second paragraph. It should have comfortable vertical spacing.',
- '',
'A third paragraph to check last-child spacing.',
].join('\n'),
},
@@ -51,14 +49,11 @@ export const ListsAndBlockquote: Story = {
size: 'default',
text: [
'A list:',
- '',
'- First item',
'- Second item with a nested list:',
' - Nested item A',
' - Nested item B',
- '',
'A blockquote:',
- '',
'> This is a blockquote.',
'> It should have a left border and spacing.',
].join('\n'),
@@ -71,21 +66,13 @@ export const LooseListWithMultiParagraphItems: Story = {
size: 'default',
text: [
'Quint 会让你更轻松的任务',
- '',
'1. **描述状态机/协议行为**',
- '',
' 例如账户转账、共识投票、两阶段提交、缓存一致性、权限流转。你写 `init`、`step`、`action`、`nondet`,不用手工展开 `s0, s1, ..., sk` 和转移约束。',
- '',
'2. **检查 safety invariant**',
- '',
' 比如"余额不会为负""不会两个 leader 同时被提交""锁不会被两个进程同时持有"。Quint 的主路径就是写模型、写 invariant,然后 `quint run` 或 `quint verify`。',
- '',
'3. **寻找可复现反例 trace**',
- '',
' 直接用 Z3 通常给你一个 model;Quint 会把结果还原成状态序列,更像"重现步骤"。这对调试协议设计很有价值。',
- '',
'4. **表达 nondeterminism 和 interleaving**',
- '',
' 网络延迟、消息丢失、随机选一个节点、任意执行某个 actor,这些在 Quint 中比较自然;直接 Z3 要自己编码选择变量、路径和约束。',
].join('\n'),
},
@@ -105,15 +92,11 @@ export const CodeBlocks: Story = {
size: 'default',
text: [
'Inline code: `pnpm --filter @lody/components storybook`',
- '',
'Fenced code with language:',
- '',
'```ts',
'export const hello = (name: string) => `Hello, ${name}`;',
'```',
- '',
'Fenced code without language:',
- '',
'```',
'echo "hello"',
'```',
@@ -173,31 +156,23 @@ export const CodeBlockPaddingSymmetry: Story = {
text: [
'Single-line code blocks stay compact and vertically centered, with the',
'language label floating in the top-right corner.',
- '',
'```ts',
'const answer = 42;',
'```',
- '',
'```bash',
'pnpm --filter @lody/components storybook',
'```',
- '',
'A short multi-line block keeps the same compact frame:',
- '',
'```tsx',
'export const Hello = ({ name }: { name: string }) => (',
' Hello, {name} ',
');',
'```',
- '',
'A block without a language fence has no label and keeps the same compact body:',
- '',
'```',
'echo "no language"',
'```',
- '',
'A long first line scrolls the full width and slides beneath the floating label:',
- '',
'```ts',
"const command = 'pnpm --filter @lody/components exec vitest run tests/markdown-streaming-reparse.test.ts --reporter=verbose';",
'```',
@@ -216,29 +191,17 @@ export const CodeBlockPaddingSymmetry: Story = {
const allMarkdownFormatsText = [
'# Markdown coverage',
- '',
'A dense fixture for checking typography, spacing, GFM, code highlighting, math, Mermaid, links, and long content in one pass.',
- '',
'## Inline formatting',
- '',
'Plain text with **bold**, _italic_, ***bold italic***, ~~strikethrough~~, `inline code`, [named links](https://example.com/docs), autolinks https://example.com/a/b?x=1, email test@example.com, and trailing punctuation https://example.com/path).',
- '',
'Escaped markdown characters: \\*not italic\\*, \\`not code\\`, and \\[not a link\\].',
- '',
'## Headings',
- '',
'### Heading level 3',
- '',
'#### Heading level 4',
- '',
'##### Heading level 5',
- '',
'###### Heading level 6',
- '',
'---',
- '',
'## Lists',
- '',
'- Unordered item one',
'- Unordered item two with nested content:',
' - Nested bullet A',
@@ -248,49 +211,36 @@ const allMarkdownFormatsText = [
'- Task list states:',
' - [x] Finished task',
' - [ ] Pending task',
- '',
'1. Ordered item one',
'2. Ordered item two',
'3. Ordered item three',
- '',
'## Blockquotes',
- '',
'> A blockquote with **formatting** and a link https://example.com.',
'>',
'> - Quoted list item',
'> - Another quoted list item',
- '',
'## Code',
- '',
'Inline code should stay compact: `pnpm --filter @lody/components storybook`.',
- '',
'TypeScript fenced code should preserve line breaks and syntax highlighting:',
- '',
'```tsx',
'type Message = {',
' id: string;',
' text: string;',
' metadata?: Record;',
'};',
- '',
'export function renderMessage(message: Message) {',
' if (!message.text.trim()) {',
' return null;',
' }',
- '',
' return {message.text} ;',
'}',
'```',
- '',
'Shell fenced code:',
- '',
'```bash',
'pnpm --filter @lody/components typecheck',
'pnpm --filter @lody/components exec vitest run tests/markdown-streaming-reparse.test.ts',
'```',
- '',
'JSON fenced code:',
- '',
'```json',
'{',
' "name": "lody",',
@@ -298,46 +248,33 @@ const allMarkdownFormatsText = [
' "enabled": true',
'}',
'```',
- '',
'A long code line should scroll instead of destroying layout:',
- '',
'```ts',
'const veryLongLine = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";',
'```',
- '',
'Fenced code without language:',
- '',
'```',
'first line',
'second line',
'third line',
'```',
- '',
'## Tables',
- '',
'| Feature | Status | Notes |',
'| --- | --- | --- |',
'| GFM table | Supported | Should have borders and readable spacing |',
'| Inline code | `ok` | Code inside cells should remain inline |',
'| Links | [docs](https://example.com) | Links remain clickable |',
- '',
'A wide table should be horizontally scrollable:',
- '',
'| ID | File | Description | Long value | Status | Owner |',
'| --- | --- | --- | --- | --- | --- |',
'| 1 | packages/components/src/components/ai-gui/markdown-renderer.tsx | Markdown renderer fixture | abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz | Active | UI |',
'| 2 | packages/components/src/stories/MarkdownRenderer.stories.tsx | Storybook coverage | 1234567890-1234567890-1234567890-1234567890-1234567890 | Review | Components |',
- '',
'## Math',
- '',
'Inline math: $E = mc^2$ and $a^2 + b^2 = c^2$.',
- '',
'$$',
'\\frac{\\partial L}{\\partial q} - \\frac{d}{dt}\\frac{\\partial L}{\\partial \\dot q} = 0',
'$$',
- '',
'## Mermaid',
- '',
'```mermaid',
'sequenceDiagram',
' participant U as User',
@@ -347,13 +284,9 @@ const allMarkdownFormatsText = [
' M->>S: stable blocks',
' S-->>U: formatted output',
'```',
- '',
'## Raw HTML',
- '',
'Raw HTML should stay escaped unless allowHtml is enabled. ',
- '',
'## Images',
- '',
'',
].join('\n');
@@ -383,17 +316,13 @@ export const AllMarkdownFormats: Story = {
const streamdownDemoChunks = [
[
'### Streamdown native rendering',
- '',
'This story exercises Mermaid, LaTeX, and word-level streaming animation in one Markdown response.',
- '',
].join('\n'),
[
'Inline math is rendered by KaTeX: $E = mc^2$, and display math works too:',
- '',
'$$',
'\\int_0^\\infty e^{-x^2}\\,dx = \\frac{\\sqrt{\\pi}}{2}',
'$$',
- '',
].join('\n'),
[
'```mermaid',
@@ -405,7 +334,6 @@ const streamdownDemoChunks = [
' E --> F[SVG with zoom / copy / download controls]',
' D --> G[Animated new text settles]',
'```',
- '',
].join('\n'),
[
'| Feature | Expected behavior |',
@@ -477,7 +405,6 @@ const mermaidStyleReviewText = [
' Theme --> Svg["Readable SVG"]',
' Svg --> Layout["Single frame + natural height"]',
'```',
- '',
'```mermaid',
'sequenceDiagram',
' participant U as User',
@@ -519,24 +446,16 @@ export const TablesAndHeadings: Story = {
size: 'default',
text: [
'# Heading 1',
- '',
'Some text under heading 1.',
- '',
'## Heading 2',
- '',
'| Column A | Column B |',
'| --- | --- |',
'| 1 | 2 |',
'| 3 | 4 |',
- '',
'### Heading 3',
- '',
'A markdown link: [example](https://example.com)',
- '',
'An autolink: https://example.com',
- '',
'A www link: www.example.com',
- '',
'An autolink with punctuation: https://example.com).',
].join('\n'),
},
@@ -548,7 +467,6 @@ export const ConfigurationTable: Story = {
size: 'default',
text: [
'`jh` 当前配置如下:',
- '',
'| 项目 | 配置 |',
'|---|---|',
'| 状态 | `RUNNING` |',
@@ -569,15 +487,12 @@ export const ConfigurationTable: Story = {
'| Secure Boot | 未开启 |',
'| vTPM / 完整性监控 | 已开启 |',
'| 标签 | 无 |',
- '',
'远程访问方面:',
- '',
'- SSH `22` 对 `0.0.0.0/0` 开放',
'- RDP `3389` 对 `0.0.0.0/0` 开放',
'- 两个端口均实测可连接',
'- 使用默认 Compute Engine 服务账号',
'- 当前本地 SSH key 无法通过认证',
- '',
'实例是今天约 `19:47`(北京时间)启动的。',
].join('\n'),
},
@@ -597,7 +512,6 @@ export const WideTableScrollable: Story = {
size: 'default',
text: [
'A wide table that overflows on narrow screens:',
- '',
'| ID | Name | Email | Department | Role | Location | Start Date | Salary | Status | Notes |',
'| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |',
'| 1 | Alice Johnson | alice.johnson@example.com | Engineering | Senior Engineer | San Francisco, CA | 2020-01-15 | $150,000 | Active | Team lead for infrastructure |',
@@ -617,19 +531,12 @@ export const GitHubLinks: Story = {
size: 'default',
text: [
'GitHub links should be auto-linked:',
- '',
'A PR link: https://github.com/loro-dev/lody/pull/602',
- '',
'An issue link: https://github.com/loro-dev/lody/issues/123',
- '',
'A repo link: https://github.com/loro-dev/lody',
- '',
'Multiple links in one line: Check https://github.com/loro-dev/lody/pull/602 and https://github.com/loro-dev/lody/pull/603',
- '',
'Link with trailing punctuation: See https://github.com/loro-dev/lody/pull/602.',
- '',
'Inline code link: `https://github.com/loro-dev/lody/pull/602`',
- '',
'Mixed inline code: Check `https://github.com/loro-dev/lody/pull/602` for details.',
].join('\n'),
},
@@ -641,16 +548,13 @@ export const InlineCodeDensity: Story = {
size: 'default',
text: [
'When consecutive lines have inline code, the borders should not overlap:',
- '',
'Use `useState` for local state management.',
'Use `useEffect` for side effects and subscriptions.',
'Use `useMemo` for expensive computations.',
'Use `useCallback` for stable function references.',
'Use `useRef` for mutable values that persist across renders.',
- '',
'Mixed density paragraph with `inline code` on the first line',
'and `more code` on the second line and `even more` on the third.',
- '',
'A paragraph with multiple inline codes: `pnpm install`, then `pnpm build`, and finally `pnpm check` to verify.',
].join('\n'),
},
@@ -662,15 +566,10 @@ export const AgentFileLinks: Story = {
size: 'default',
text: [
'Agent filesystem links should not navigate inside the web app:',
- '',
'A labeled worktree file: [markdown-renderer.tsx](/home/agent/.lody/repos/github---example---project/worktrees/5110aa94-b18b-43cf-afa7-369905c2515a/packages/components/src/components/ai-gui/markdown-renderer.tsx)',
- '',
'A repo-relative GitHub line anchor: [README.md#L100](README.md#L100)',
- '',
'A repo-relative line reference: [README.md:100](README.md:100)',
- '',
'A raw path label: [/tmp/lody-output.log](/tmp/lody-output.log)',
- '',
'A protocol-relative web URL stays a normal link: [CDN script](//cdn.example.com/app.js)',
].join('\n'),
},
diff --git a/packages/components/src/stories/MobileChatLandingScreen.stories.tsx b/packages/components/src/stories/MobileChatLandingScreen.stories.tsx
index 20201e88f..b259ee20f 100644
--- a/packages/components/src/stories/MobileChatLandingScreen.stories.tsx
+++ b/packages/components/src/stories/MobileChatLandingScreen.stories.tsx
@@ -3,7 +3,7 @@ import { fn } from 'storybook/test';
import { ArrowUp } from 'lucide-react';
import { MobileChatLandingScreen } from '@/components/mobile/mobile-chat-landing-screen';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
function MockComposer() {
return (
@@ -17,10 +17,11 @@ function MockComposer() {
claude-3.5-sonnet · zx-macbook
diff --git a/packages/components/src/stories/MobileInlinePicker.stories.tsx b/packages/components/src/stories/MobileInlinePicker.stories.tsx
index 6f5019af9..2ec644e8e 100644
--- a/packages/components/src/stories/MobileInlinePicker.stories.tsx
+++ b/packages/components/src/stories/MobileInlinePicker.stories.tsx
@@ -9,12 +9,18 @@ import {
MobileInlineMenu,
type MobileInlinePickerOption,
} from '@/components/mobile/mobile-inline-picker';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
const machineOptions: MobileInlinePickerOption[] = [
{ value: 'zx-macbook', label: 'zx-macbook', icon: },
{ value: 'lab-m2', label: 'lab-m2', icon: },
- { value: 'mini-offline', label: 'mini-offline', icon: , disabled: true, disabledReason: 'Offline' },
+ {
+ value: 'mini-offline',
+ label: 'mini-offline',
+ icon: ,
+ disabled: true,
+ disabledReason: 'Offline',
+ },
];
const repoOptions: MobileInlinePickerOption[] = Array.from({ length: 12 }).map((_, i) => ({
@@ -26,7 +32,11 @@ const repoOptions: MobileInlinePickerOption[] = Array.from({ length: 12 }).map((
const branchOptions: MobileInlinePickerOption[] = [
{ value: 'main', label: 'main', icon: },
- { value: 'feat/x', label: 'feat/audit-mobile-coupling', icon: },
+ {
+ value: 'feat/x',
+ label: 'feat/audit-mobile-coupling',
+ icon: ,
+ },
];
function StandaloneShell() {
@@ -128,7 +138,9 @@ function CoordinatorRowShell({ searchable = false }: { searchable?: boolean }) {
triggerContent={
<>
- {repoOptions.find((o) => o.value === repo)?.label}
+
+ {repoOptions.find((o) => o.value === repo)?.label}
+
>
}
/>
diff --git a/packages/components/src/stories/MobileNewChatSheet.stories.tsx b/packages/components/src/stories/MobileNewChatSheet.stories.tsx
index b28dc8900..75bd07d01 100644
--- a/packages/components/src/stories/MobileNewChatSheet.stories.tsx
+++ b/packages/components/src/stories/MobileNewChatSheet.stories.tsx
@@ -22,7 +22,7 @@ import {
MobileModelPickerLabel,
mobileModelPickerTriggerClassName,
} from '@/components/mobile/mobile-session-composer-footer';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
import { cn } from '@/lib/utils';
import type { AcpConfigOptionValue } from '@/components/shared/acp-selector-options';
import { Tabs, TabsList, TabsTrigger } from '@/ui/tabs';
@@ -228,10 +228,11 @@ function MockComposer({
diff --git a/packages/components/src/stories/MobileProjectSkills.stories.tsx b/packages/components/src/stories/MobileProjectSkills.stories.tsx
index 660b9167b..c243a532d 100644
--- a/packages/components/src/stories/MobileProjectSkills.stories.tsx
+++ b/packages/components/src/stories/MobileProjectSkills.stories.tsx
@@ -31,9 +31,7 @@ const LOADED_GROUPS: ProjectSkillResolvedGroup[] = [
isSymlink: false,
content: [
'## When to use',
- '',
'Run this before opening a PR.',
- '',
'1. Diff the branch against `main`.',
'2. Flag **correctness** issues first.',
].join('\n'),
diff --git a/packages/components/src/stories/MobileSidebarDrawer.stories.tsx b/packages/components/src/stories/MobileSidebarDrawer.stories.tsx
index 5fe1839f4..3b0ba7657 100644
--- a/packages/components/src/stories/MobileSidebarDrawer.stories.tsx
+++ b/packages/components/src/stories/MobileSidebarDrawer.stories.tsx
@@ -3,7 +3,7 @@ import { useState } from 'react';
import { fn } from 'storybook/test';
import { MobileSidebarDrawer } from '@/components/mobile/mobile-sidebar-drawer';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
function StoryShell({
open,
diff --git a/packages/components/src/stories/MobileWorkspaceLayout.stories.tsx b/packages/components/src/stories/MobileWorkspaceLayout.stories.tsx
index e04617679..1065c924c 100644
--- a/packages/components/src/stories/MobileWorkspaceLayout.stories.tsx
+++ b/packages/components/src/stories/MobileWorkspaceLayout.stories.tsx
@@ -6,7 +6,7 @@ import {
getMobileMainLayoutContentClassName,
getMobileMainLayoutRootClassName,
} from '@/components/workspace-layout-utils';
-import { Button } from '@/ui/button';
+import { Button } from '@lody/ui/button';
/* MobileWorkspaceLayout in production wires `` directly,
which depends on workspace / user / connection atoms not available in
@@ -35,7 +35,7 @@ function StoryShell({ initialOpen = false }: { initialOpen?: boolean }) {