webui2/components.json 🔗
@@ -4,7 +4,7 @@
"rsc": false,
"tsx": true,
"tailwind": {
- "config": "tailwind.config.ts",
+ "config": "",
"css": "src/index.css",
"baseColor": "zinc",
"cssVariables": true,
Quentin Gliech and Claude Opus 4.6 (1M context) created
Restructure components into three clear layers:
ui/ — generic shadcn primitives (button, input, avatar, etc.)
Managed by shadcn CLI. No domain knowledge.
shared/ — app-level reusable components with domain awareness but
no data fetching. Typed against GraphQL fragments.
(IssueRow, LabelBadge, StatusBadge, CommentCard,
Pagination, QueryInput, StatusTabs, WritePreview,
EmptyState, SectionHeading)
bugs/ — feature components with GraphQL mutations and auth
(CommentBox, Timeline, TitleEditor, LabelEditor,
IssueFilters)
Also update components.json for Tailwind v4 (drop tailwind.config
reference, set config to empty string per shadcn docs).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
webui2/components.json | 2
webui2/src/components/bugs/CommentBox.tsx | 4
webui2/src/components/bugs/IssueFilters.tsx | 2
webui2/src/components/bugs/LabelEditor.tsx | 4
webui2/src/components/bugs/Timeline.tsx | 4
webui2/src/components/shared/IdentitySummary.graphql | 0
webui2/src/components/shared/IssueRow.graphql | 0
webui2/src/components/shared/IssueRow.stories.tsx | 0
webui2/src/components/shared/IssueRow.test.tsx | 0
webui2/src/components/shared/IssueRow.tsx | 0
webui2/src/components/shared/LabelBadge.graphql | 0
webui2/src/components/shared/LabelBadge.stories.tsx | 0
webui2/src/components/shared/LabelBadge.test.tsx | 0
webui2/src/components/shared/LabelBadge.tsx | 0
webui2/src/components/shared/StatusBadge.stories.tsx | 0
webui2/src/components/shared/StatusBadge.test.tsx | 0
webui2/src/components/shared/StatusBadge.tsx | 0
webui2/src/components/shared/__snapshots__/IssueRow.test.tsx.snap | 0
webui2/src/components/shared/__snapshots__/LabelBadge.test.tsx.snap | 0
webui2/src/components/shared/__snapshots__/StatusBadge.test.tsx.snap | 0
webui2/src/components/shared/__snapshots__/comment-card.test.tsx.snap | 0
webui2/src/components/shared/__snapshots__/empty-state.test.tsx.snap | 0
webui2/src/components/shared/__snapshots__/pagination.test.tsx.snap | 0
webui2/src/components/shared/__snapshots__/query-input.test.tsx.snap | 0
webui2/src/components/shared/__snapshots__/section-heading.test.tsx.snap | 0
webui2/src/components/shared/__snapshots__/status-tabs.test.tsx.snap | 0
webui2/src/components/shared/__snapshots__/write-preview.test.tsx.snap | 0
webui2/src/components/shared/comment-card.stories.tsx | 0
webui2/src/components/shared/comment-card.test.tsx | 0
webui2/src/components/shared/comment-card.tsx | 2
webui2/src/components/shared/empty-state.stories.tsx | 0
webui2/src/components/shared/empty-state.test.tsx | 0
webui2/src/components/shared/empty-state.tsx | 0
webui2/src/components/shared/pagination.stories.tsx | 0
webui2/src/components/shared/pagination.test.tsx | 0
webui2/src/components/shared/pagination.tsx | 2
webui2/src/components/shared/query-input.stories.tsx | 0
webui2/src/components/shared/query-input.test.tsx | 0
webui2/src/components/shared/query-input.tsx | 0
webui2/src/components/shared/section-heading.stories.tsx | 0
webui2/src/components/shared/section-heading.test.tsx | 0
webui2/src/components/shared/section-heading.tsx | 0
webui2/src/components/shared/status-tabs.stories.tsx | 0
webui2/src/components/shared/status-tabs.test.tsx | 0
webui2/src/components/shared/status-tabs.tsx | 0
webui2/src/components/shared/write-preview.stories.tsx | 0
webui2/src/components/shared/write-preview.test.tsx | 0
webui2/src/components/shared/write-preview.tsx | 0
webui2/src/routes/$repo/_issues/issues/$id.tsx | 6
webui2/src/routes/$repo/_issues/issues/index.tsx | 12
webui2/src/routes/$repo/_issues/issues/new.tsx | 2
webui2/src/routes/$repo/_issues/user/$id.tsx | 8
52 files changed, 24 insertions(+), 24 deletions(-)
@@ -4,7 +4,7 @@
"rsc": false,
"tsx": true,
"tailwind": {
- "config": "tailwind.config.ts",
+ "config": "",
"css": "src/index.css",
"baseColor": "zinc",
"cssVariables": true,
@@ -11,9 +11,9 @@ import {
} from "@/__generated__/graphql";
import { Markdown } from "@/components/content/Markdown";
import { Button } from "@/components/ui/button";
-import * as CommentCard from "@/components/ui/comment-card";
+import * as CommentCard from "@/components/shared/comment-card";
import { Textarea } from "@/components/ui/textarea";
-import * as WritePreview from "@/components/ui/write-preview";
+import * as WritePreview from "@/components/shared/write-preview";
import { useAuth } from "@/lib/auth";
interface CommentBoxProps {
@@ -6,7 +6,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
import { useAuth } from "@/lib/auth";
import { cn } from "@/lib/utils";
-import { LabelBadge } from "./LabelBadge";
+import { LabelBadge } from "@/components/shared/LabelBadge";
// Max authors shown in the non-searching state. We intentionally cap this to
// avoid a giant list — the current-user + recently-seen pattern covers the
@@ -2,10 +2,10 @@ import { Settings2 } from "lucide-react";
import { useBugChangeLabelsMutation, BugDetailDocument } from "@/__generated__/graphql";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
-import { SectionHeading } from "@/components/ui/section-heading";
+import { SectionHeading } from "@/components/shared/section-heading";
import { useAuth } from "@/lib/auth";
-import { LabelBadge } from "./LabelBadge";
+import { LabelBadge } from "@/components/shared/LabelBadge";
interface LabelEditorProps {
bugPrefix: string;
@@ -11,11 +11,11 @@ import {
} from "@/__generated__/graphql";
import { Markdown } from "@/components/content/Markdown";
import { Button } from "@/components/ui/button";
-import * as CommentCard from "@/components/ui/comment-card";
+import * as CommentCard from "@/components/shared/comment-card";
import { Textarea } from "@/components/ui/textarea";
import { useAuth } from "@/lib/auth";
-import { LabelBadge } from "./LabelBadge";
+import { LabelBadge } from "@/components/shared/LabelBadge";
type TimelineNode = NonNullable<
NonNullable<NonNullable<BugDetailQuery["repository"]>["bug"]>["timeline"]["nodes"][number]
@@ -1,7 +1,7 @@
import type { IdentitySummaryFragment } from "@/__generated__/graphql";
import { cn } from "@/lib/utils";
-import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
+import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
interface RootProps {
children: React.ReactNode;
@@ -4,7 +4,7 @@ import * as React from "react";
import { cn } from "@/lib/utils";
-import { buttonVariants } from "./button";
+import { buttonVariants } from "@/components/ui/button";
interface RootProps {
children: React.ReactNode;
@@ -5,13 +5,13 @@ import { formatDistanceToNow } from "date-fns";
import { type BugDetailQuery, BugDetailDocument } from "@/__generated__/graphql";
import { CommentBox } from "@/components/bugs/CommentBox";
import { LabelEditor } from "@/components/bugs/LabelEditor";
-import { StatusBadge } from "@/components/bugs/StatusBadge";
+import { StatusBadge } from "@/components/shared/StatusBadge";
import { Timeline } from "@/components/bugs/Timeline";
import { TitleEditor } from "@/components/bugs/TitleEditor";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { BackLink } from "@/components/ui/back-link";
-import { EmptyState } from "@/components/ui/empty-state";
-import { SectionHeading } from "@/components/ui/section-heading";
+import { EmptyState } from "@/components/shared/empty-state";
+import { SectionHeading } from "@/components/shared/section-heading";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
@@ -8,13 +8,13 @@ import * as v from "valibot";
import { type BugListQuery, BugListDocument } from "@/__generated__/graphql";
import { IssueFilters } from "@/components/bugs/IssueFilters";
import type { SortValue } from "@/components/bugs/IssueFilters";
-import * as IssueRow from "@/components/bugs/IssueRow";
-import { LabelBadgeLink } from "@/components/bugs/LabelBadge";
+import * as IssueRow from "@/components/shared/IssueRow";
+import { LabelBadgeLink } from "@/components/shared/LabelBadge";
import { Button } from "@/components/ui/button";
-import { EmptyState } from "@/components/ui/empty-state";
-import * as Pagination from "@/components/ui/pagination";
-import * as QueryInput from "@/components/ui/query-input";
-import type { CompletionProvider } from "@/components/ui/query-input";
+import { EmptyState } from "@/components/shared/empty-state";
+import * as Pagination from "@/components/shared/pagination";
+import * as QueryInput from "@/components/shared/query-input";
+import type { CompletionProvider } from "@/components/shared/query-input";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
@@ -8,7 +8,7 @@ import { Button } from "@/components/ui/button";
import { ButtonLink } from "@/components/ui/button-link";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
-import * as WritePreview from "@/components/ui/write-preview";
+import * as WritePreview from "@/components/shared/write-preview";
export const Route = createFileRoute("/$repo/_issues/issues/new")({
component: RouteComponent,
@@ -14,12 +14,12 @@ import {
import * as v from "valibot";
import { type UserProfileQuery, UserProfileDocument } from "@/__generated__/graphql";
-import * as IssueRow from "@/components/bugs/IssueRow";
-import { LabelBadge } from "@/components/bugs/LabelBadge";
+import * as IssueRow from "@/components/shared/IssueRow";
+import { LabelBadge } from "@/components/shared/LabelBadge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { BackLink } from "@/components/ui/back-link";
-import { EmptyState } from "@/components/ui/empty-state";
-import * as Pagination from "@/components/ui/pagination";
+import { EmptyState } from "@/components/shared/empty-state";
+import * as Pagination from "@/components/shared/pagination";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";