Initial commit: img-infinite-canvas AI design workbench MVP

Moteva-style AI design workbench replica. Home prompt creates a project;
the project page provides an infinite canvas with node drag, zoom/pan,
a design chat panel, history replay, image asset upload, and project
save/regenerate.

- Backend: go-zero, DDD layering, sqlc/pgx, optional PostgreSQL; memory
  or Redis cache; asynq job queue; MinIO/S3/R2/OSS object storage;
  sky-valley/pi agent runtime adapter.
- Frontend: Next.js App Router + Vite artifact build, TypeScript, i18n,
  shadcn/ui components, auth (OTP/Turnstile/Google/WeChat).
- Deploy: Docker Compose and k3s manifests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-07 23:15:37 +08:00
commit 44406b72db
349 changed files with 73265 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
"use client";
import type { ButtonHTMLAttributes } from "react";
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
export function Button({ className = "", type = "button", ...props }: ButtonProps) {
return <button type={type} className={["ui-button", className].filter(Boolean).join(" ")} {...props} />;
}
@@ -0,0 +1,31 @@
"use client";
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
import type { ComponentProps } from "react";
export const ContextMenu = ContextMenuPrimitive.Root;
export const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
export const ContextMenuSub = ContextMenuPrimitive.Sub;
export const ContextMenuSubTrigger = ContextMenuPrimitive.SubTrigger;
export const ContextMenuPortal = ContextMenuPrimitive.Portal;
export const ContextMenuSeparator = ContextMenuPrimitive.Separator;
export function ContextMenuContent({ className = "", ...props }: ComponentProps<typeof ContextMenuPrimitive.Content>) {
return (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content className={["ui-context-menu-content", className].filter(Boolean).join(" ")} {...props} />
</ContextMenuPrimitive.Portal>
);
}
export function ContextMenuItem({ className = "", ...props }: ComponentProps<typeof ContextMenuPrimitive.Item>) {
return <ContextMenuPrimitive.Item className={["ui-context-menu-item", className].filter(Boolean).join(" ")} {...props} />;
}
export function ContextMenuSubContent({ className = "", ...props }: ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
return (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.SubContent className={["ui-context-menu-content", className].filter(Boolean).join(" ")} {...props} />
</ContextMenuPrimitive.Portal>
);
}
@@ -0,0 +1,35 @@
"use client";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import type { ComponentProps } from "react";
export const DropdownMenu = DropdownMenuPrimitive.Root;
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
export const DropdownMenuRadioItem = DropdownMenuPrimitive.RadioItem;
export const DropdownMenuCheckboxItem = DropdownMenuPrimitive.CheckboxItem;
export const DropdownMenuSub = DropdownMenuPrimitive.Sub;
export const DropdownMenuSubTrigger = DropdownMenuPrimitive.SubTrigger;
export const DropdownMenuSubContent = DropdownMenuPrimitive.SubContent;
export function DropdownMenuContent({ className = "", align = "center", sideOffset = 8, ...props }: ComponentProps<typeof DropdownMenuPrimitive.Content>) {
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
align={align}
sideOffset={sideOffset}
className={["ui-dropdown-menu-content", className].filter(Boolean).join(" ")}
{...props}
/>
</DropdownMenuPrimitive.Portal>
);
}
export function DropdownMenuItem({ className = "", ...props }: ComponentProps<typeof DropdownMenuPrimitive.Item>) {
return <DropdownMenuPrimitive.Item className={["ui-dropdown-menu-item", className].filter(Boolean).join(" ")} {...props} />;
}
export function DropdownMenuSeparator({ className = "", ...props }: ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
return <DropdownMenuPrimitive.Separator className={["ui-dropdown-menu-separator", className].filter(Boolean).join(" ")} {...props} />;
}
+22
View File
@@ -0,0 +1,22 @@
"use client";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import type { ComponentProps } from "react";
export const Popover = PopoverPrimitive.Root;
export const PopoverTrigger = PopoverPrimitive.Trigger;
export const PopoverAnchor = PopoverPrimitive.Anchor;
export const PopoverClose = PopoverPrimitive.Close;
export function PopoverContent({ className = "", align = "center", sideOffset = 8, ...props }: ComponentProps<typeof PopoverPrimitive.Content>) {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
align={align}
sideOffset={sideOffset}
className={["ui-popover-content", className].filter(Boolean).join(" ")}
{...props}
/>
</PopoverPrimitive.Portal>
);
}
@@ -0,0 +1,16 @@
"use client";
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import type { ComponentProps } from "react";
export function ScrollArea({ className = "", children, ...props }: ComponentProps<typeof ScrollAreaPrimitive.Root>) {
return (
<ScrollAreaPrimitive.Root className={["ui-scroll-area", className].filter(Boolean).join(" ")} {...props}>
<ScrollAreaPrimitive.Viewport className="ui-scroll-area-viewport">{children}</ScrollAreaPrimitive.Viewport>
<ScrollAreaPrimitive.Scrollbar className="ui-scroll-area-scrollbar" orientation="vertical">
<ScrollAreaPrimitive.Thumb className="ui-scroll-area-thumb" />
</ScrollAreaPrimitive.Scrollbar>
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
);
}
+12
View File
@@ -0,0 +1,12 @@
"use client";
import * as SwitchPrimitive from "@radix-ui/react-switch";
import type { ComponentProps } from "react";
export function Switch({ className = "", ...props }: ComponentProps<typeof SwitchPrimitive.Root>) {
return (
<SwitchPrimitive.Root className={["ui-switch", className].filter(Boolean).join(" ")} {...props}>
<SwitchPrimitive.Thumb className="ui-switch-thumb" />
</SwitchPrimitive.Root>
);
}
+18
View File
@@ -0,0 +1,18 @@
"use client";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import type { ComponentProps } from "react";
export const Tabs = TabsPrimitive.Root;
export function TabsList({ className = "", ...props }: ComponentProps<typeof TabsPrimitive.List>) {
return <TabsPrimitive.List className={["ui-tabs-list", className].filter(Boolean).join(" ")} {...props} />;
}
export function TabsTrigger({ className = "", ...props }: ComponentProps<typeof TabsPrimitive.Trigger>) {
return <TabsPrimitive.Trigger className={["ui-tabs-trigger", className].filter(Boolean).join(" ")} {...props} />;
}
export function TabsContent({ className = "", ...props }: ComponentProps<typeof TabsPrimitive.Content>) {
return <TabsPrimitive.Content className={["ui-tabs-content", className].filter(Boolean).join(" ")} {...props} />;
}