diff --git a/bun.lock b/bun.lock index ccdd079236..525904f5cf 100644 --- a/bun.lock +++ b/bun.lock @@ -79,6 +79,7 @@ "eslint": "^9.36.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-tailwindcss": "4.0.0-beta.0", "jest": "^30.1.3", "mermaid": "^11.12.0", "playwright": "^1.56.0", @@ -1517,6 +1518,8 @@ "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="], + "eslint-plugin-tailwindcss": ["eslint-plugin-tailwindcss@4.0.0-beta.0", "", { "dependencies": { "fast-glob": "^3.2.5", "postcss": "^8.4.4", "synckit": "^0.11.4", "tailwind-api-utils": "^1.0.3" }, "peerDependencies": { "tailwindcss": "^3.4.0 || ^4.0.0" } }, "sha512-WWCajZgQu38Sd67ZCl2W6i3MRzqB0d+H8s4qV9iB6lBJbsDOIpIlj6R1Fj2FXkoWErbo05pZnZYbCGIU9o/DsA=="], + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], @@ -2693,6 +2696,8 @@ "synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="], + "tailwind-api-utils": ["tailwind-api-utils@1.0.3", "", { "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "local-pkg": "^1.1.1" }, "peerDependencies": { "tailwindcss": "^3.3.0 || ^4.0.0 || ^4.0.0-beta" } }, "sha512-KpzUHkH1ug1sq4394SLJX38ZtpeTiqQ1RVyFTTSY2XuHsNSTWUkRo108KmyyrMWdDbQrLYkSHaNKj/a3bmA4sQ=="], + "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], "tailwindcss": ["tailwindcss@4.1.15", "", {}, "sha512-k2WLnWkYFkdpRv+Oby3EBXIyQC8/s1HOFMBUViwtAh6Z5uAozeUSMQlIsn/c6Q2iJzqG6aJT3wdPaRNj70iYxQ=="], diff --git a/eslint.config.mjs b/eslint.config.mjs index 15ab04f766..664ef02d06 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -2,6 +2,7 @@ import js from "@eslint/js"; import { defineConfig } from "eslint/config"; import react from "eslint-plugin-react"; import reactHooks from "eslint-plugin-react-hooks"; +import tailwindcss from "eslint-plugin-tailwindcss"; import tseslint from "typescript-eslint"; /** @@ -154,12 +155,21 @@ export default defineConfig([ plugins: { react, "react-hooks": reactHooks, + tailwindcss, local: localPlugin, }, settings: { react: { version: "detect", }, + tailwindcss: { + // Don't try to load Tailwind config (v4 doesn't export resolveConfig) + config: false, + // CSS files to check + cssFiles: ["**/*.css", "!**/node_modules", "!**/.*", "!**/dist", "!**/build"], + // Disable callees check to avoid resolving config + callees: [], + }, }, rules: { ...react.configs.recommended.rules, @@ -240,6 +250,15 @@ export default defineConfig([ "react/react-in-jsx-scope": "off", "react/prop-types": "off", + // Tailwind CSS + "tailwindcss/classnames-order": "warn", + "tailwindcss/enforces-negative-arbitrary-values": "warn", + "tailwindcss/enforces-shorthand": "warn", + "tailwindcss/migration-from-tailwind-2": "warn", + "tailwindcss/no-arbitrary-value": "off", + "tailwindcss/no-contradicting-classname": "error", + "tailwindcss/no-custom-classname": "off", + // Safe Node.js patterns "local/no-unsafe-child-process": "error", "local/no-sync-fs-methods": "error", diff --git a/package.json b/package.json index bf78009bb9..c327bec835 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "eslint": "^9.36.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-tailwindcss": "4.0.0-beta.0", "jest": "^30.1.3", "mermaid": "^11.12.0", "playwright": "^1.56.0", diff --git a/src/App.tsx b/src/App.tsx index b74b928a11..972ce225f4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -662,7 +662,7 @@ function AppInner() { return ( <> -
+
-
-
+
+
{selectedWorkspace ? ( ) : (
= ({ )} style={{ containerType: "inline-size" }} > -
+

Loading workspace...

@@ -291,7 +291,7 @@ const AIViewInner: React.FC = ({ )} style={{ containerType: "inline-size" }} > -
+

Loading workspace...

@@ -307,7 +307,7 @@ const AIViewInner: React.FC = ({ )} style={{ containerType: "inline-size" }} > -
+

No Workspace Selected

Select a workspace from the sidebar to view and interact with Claude @@ -327,10 +327,10 @@ const AIViewInner: React.FC = ({ >

-
-
+
+
= ({ workspaceId={workspaceId} tooltipPosition="bottom" /> - + {projectName} / {branch} - + {namedWorkspacePath}
-
+
= ({ aria-busy={canInterrupt} aria-label="Conversation transcript" tabIndex={0} - className="h-full overflow-y-auto p-[15px] whitespace-pre-wrap break-words leading-[1.5]" + className="h-full overflow-y-auto p-[15px] leading-[1.5] break-words whitespace-pre-wrap" > {mergedMessages.length === 0 ? ( -
+

No Messages Yet

Send a message below to begin

@@ -404,7 +404,7 @@ const AIViewInner: React.FC = ({
{isAtCutoff && (
= ({ @@ -93,7 +93,7 @@ export const ChatInputToast: React.FC = ({ toast, onDismiss // Regular toast for simple messages and success return ( -
+
= ({ toast, onDismiss > {toast.type === "success" ? "✓" : "⚠"}
- {toast.title &&
{toast.title}
} + {toast.title &&
{toast.title}
}
{toast.message}
{toast.type === "error" && ( diff --git a/src/components/CommandPalette.stories.tsx b/src/components/CommandPalette.stories.tsx index a72b301865..a43a539695 100644 --- a/src/components/CommandPalette.stories.tsx +++ b/src/components/CommandPalette.stories.tsx @@ -132,7 +132,7 @@ const PaletteDemo: React.FC<{ autoOpen?: boolean }> = ({ autoOpen = true }) => { <> @@ -174,8 +174,8 @@ type Story = StoryObj; export const Default: Story = { render: () => ( -
-
+
+
Command Palette

diff --git a/src/components/CommandPalette.tsx b/src/components/CommandPalette.tsx index 658db47156..06da96b9cf 100644 --- a/src/components/CommandPalette.tsx +++ b/src/components/CommandPalette.tsx @@ -355,7 +355,7 @@ export const CommandPalette: React.FC = ({ getSlashContext return (
{ setActivePrompt(null); setPromptError(null); @@ -364,12 +364,12 @@ export const CommandPalette: React.FC = ({ getSlashContext }} > e.stopPropagation()} shouldFilter={shouldUseCmdkFilter} > = ({ getSlashContext {group.items.map((item) => ( { if ("prompt" in item && item.prompt) { addRecent(item.id); @@ -440,7 +440,7 @@ export const CommandPalette: React.FC = ({ getSlashContext )}
{"shortcutHint" in item && item.shortcutHint && ( - + {item.shortcutHint} )} @@ -449,7 +449,7 @@ export const CommandPalette: React.FC = ({ getSlashContext ))} {!hasAnyItems && ( -
{emptyText ?? "No results"}
+
{emptyText ?? "No results"}
)} diff --git a/src/components/CommandSuggestions.tsx b/src/components/CommandSuggestions.tsx index a6d7696b38..fd607c3ea7 100644 --- a/src/components/CommandSuggestions.tsx +++ b/src/components/CommandSuggestions.tsx @@ -93,7 +93,7 @@ export const CommandSuggestions: React.FC = ({ activeSuggestion ? `${resolvedListId}-option-${activeSuggestion.id}` : undefined } data-command-suggestions - className="absolute bottom-full left-0 right-0 mb-2 bg-separator border border-border-light rounded shadow-[0_-4px_12px_rgba(0,0,0,0.4)] max-h-[200px] overflow-y-auto z-[100] flex flex-col" + className="bg-separator border-border-light absolute right-0 bottom-full left-0 z-[100] mb-2 flex max-h-[200px] flex-col overflow-y-auto rounded border shadow-[0_-4px_12px_rgba(0,0,0,0.4)]" > {suggestions.map((suggestion, index) => (
= ({ index === selectedIndex ? "bg-accent-darker" : "bg-transparent" )} > -
- {suggestion.display} -
-
+
{suggestion.display}
+
{suggestion.description}
))} -
+
Tab to complete • ↑↓ to navigate • Esc to dismiss
diff --git a/src/components/Context1MCheckbox.tsx b/src/components/Context1MCheckbox.tsx index d9eb4df0cb..ea474c2088 100644 --- a/src/components/Context1MCheckbox.tsx +++ b/src/components/Context1MCheckbox.tsx @@ -16,18 +16,18 @@ export const Context1MCheckbox: React.FC = ({ modelStrin } return ( -
-