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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ LICENSE
*.pem
*.tsbuildinfo
public/uploads
public/demo-assets
*-before.png
design-qa-*.png
canvas-rollback-empty.png
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ package-lock.json

# Runtime uploads and local QA artifacts
/public/uploads/
/public/demo-assets/
/*-before.png
/design-qa-*.png
/canvas-rollback-empty.png
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Do not add authentication, accounts, payments, subscriptions, credits, API-key i
- Storage entitlement follows billing. BeatAPI managed R2/Files is allowed only with the official `https://api.beatapi.io` billing endpoint; any custom API host must use an operator-owned R2/S3-compatible bucket. File selection stays local and upload is allowed only after generation precheck. Never commit shared storage credentials.
- The model catalog lives in `src/core/effects/effect-registry.ts`.
- BeatAPI request mapping lives in `src/core/adapters/beatapi-adapter.ts`.
- Studio and Canvas share projects, tasks, and assets.
- Studio, Canvas, and Assets share projects, tasks, and assets.
- Add message keys to both `messages/en.json` and `messages/zh.json`.
- Do not edit `src/routeTree.gen.ts` manually.

Expand Down
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Projects
+ Storage configuration
```

Studio and Canvas are two views over the same project, generation, and asset services. Switching modes never creates a second project or backend.
Studio, Canvas, and Assets are three views over the same project, generation, and asset services. Switching modes never creates a second project or backend.

## Runtime flow

Expand Down
25 changes: 13 additions & 12 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Design System — BeatAPI SaaS Template
# Design System — BeatDesign

## Product Context

- **What this is:** An open-source AI SaaS starter and a working demonstration of building on BeatAPI.
- **What this is:** An open-source, local-first AI creative workbench and a working demonstration of building on BeatAPI.
- **Who it is for:** Independent developers and small product teams shipping AI media products for an international audience.
- **Space:** Developer tools, AI creative software, and SaaS infrastructure.
- **Project type:** Marketing site, authenticated SaaS dashboard, guided studio, and node canvas.
- **Project type:** Marketing site, local project workspace, guided Studio, node Canvas, and shared Assets view.

## Aesthetic Direction

- **Direction:** Warm technical editorial in marketing; cinematic black creative operating system across Studio, Canvas, and PromptWise-structured Pricing.
- **Decoration:** Intentional. Typography, product media, fine grid lines, dark glass controls, and restrained highlights create character without ornamental UI noise.
- **Layout:** Hybrid. Marketing uses editorial scale and asymmetry; authenticated product surfaces use a strict, predictable grid.
- **Layout:** Hybrid. Marketing uses editorial scale and asymmetry; local project surfaces use a strict, predictable grid.
- **Mood:** Capable, cinematic, and crafted. Creation surfaces should feel like focused production tools, while the marketing site stays warmer and more editorial.
- **Reference language:** PromptWise is the visual reference, not a copied page or a separately selectable theme.

Expand All @@ -30,7 +30,7 @@
- **Brand 600:** `#E9550B` — hover and pressed states.
- **Brand soft:** `#FFF0E5` — selected surfaces and subtle callouts.
- **Marketing canvas:** `#F6F0E7` — warm cream.
- **Studio / Canvas canvas:** `#08090A` — near-black production workspace.
- **Studio / Canvas / Assets canvas:** `#08090A` — near-black production workspace.
- **Pricing canvas:** `#08090A` with graphite `#141517` plan cards, warm-white type, and PromptWise's original plan geometry.
- **Dark surface:** `#111214`; raised surface `#17181B`; control surface `#202126`.
- **Dark ink:** `#F6F6F4`; secondary `rgba(246,246,244,.58)`; faint `rgba(246,246,244,.34)`.
Expand All @@ -39,7 +39,7 @@
- **Borders:** `#DDDED8`; strong `#C7C9C0`.
- **Highlight:** `#C7F36B` — sparse proof or status accent, never a primary CTA.
- **Success:** `#208A55`; warning `#B66A09`; error `#C23B32`; info `#2563A9`.
- **Dark product surfaces:** Near-black canvas with charcoal cards. Orange stays saturated because it is the only brand action color; purple is limited to Canvas graph semantics. Pricing keeps PromptWise's commercial hierarchy while using the same dark product vocabulary as Studio and Canvas.
- **Dark product surfaces:** Near-black canvas with charcoal cards. Orange stays saturated because it is the only brand action color; cold blue is limited to Canvas graph semantics. Pricing keeps PromptWise's commercial hierarchy while using the same dark product vocabulary as Studio, Canvas, and Assets.

## Spacing and Shape

Expand All @@ -52,9 +52,10 @@
## Layout

- **Marketing grid:** 12 columns, max width 1280px, 24–32px gutters.
- **App grid:** Creative project routes use a 56px top bar and fluid main region without a persistent sidebar. Administrative routes may keep their own navigation.
- **Studio:** No sidebar. A full-bleed media wall supports a bottom floating composer; media type, model, ratio, credits, and Generate live in that composer. Content max width is 1380px and composer max width is 1180px.
- **App grid:** Creative project routes use a 56px top bar and fluid main region without a persistent sidebar.
- **Studio:** No sidebar. A full-bleed media wall supports a bottom floating composer; media type, model, ratio, parameters, and Generate live in that composer. Content max width is 1380px and composer max width is 1180px.
- **Canvas:** Full-bleed workspace inside the same AppShell. Floating toolbars use the shared panel tokens.
- **Assets:** A project-scoped media library shares the same AppShell and content width as Studio.
- **Responsive:** Sidebar becomes a drawer below 960px. Canvas remains usable with touch-safe controls and a collapsible inspector.

## Component Language
Expand All @@ -69,11 +70,11 @@ MarketingHeader, HeroStatement, HeroDemoFrame, ProofStrip, FeatureStory, MediaSh

### Product

AppShell, AppSidebar, AppTopbar, PageHeader, MetricCard, DataTable, EmptyState, ErrorState, LoadingState, UsageMeter, CreditBalance, and StatusBadge.
AppShell, AppTopbar, PageHeader, MediaLibrary, EmptyState, ErrorState, LoadingState, and StatusBadge.

### Creative workspace

WorkspaceSwitcher, PromptComposer, MediaUploader, ModelPicker, EffectPicker, GenerationSettings, GenerationCard, TaskProgress, ResultViewer, CreditEstimate, APIRequestPreview, WebhookStatus, CanvasNode, CanvasEdge, CanvasToolbar, and InspectorPanel.
WorkspaceSwitcher, PromptComposer, MediaUploader, ModelPicker, EffectPicker, GenerationSettings, GenerationCard, GenerationHistory, TaskProgress, ResultViewer, CanvasNode, CanvasEdge, CanvasToolbar, and InspectorPanel.

Do not create a universal `Section` component controlled by many presentation props. Reuse small primitives and keep each product section semantically owned.

Expand All @@ -83,7 +84,7 @@ React Flow remains the interaction engine. PromptWise changes its visual express

- Canvas background uses the same near-black canvas with a restrained two-scale dot grid.
- Nodes are dark, thin-bordered cards with a narrow semantic header and clear input/output ports.
- Purple marks graph selection and connection semantics; orange remains reserved for brand and purchase actions.
- Cold blue marks graph selection and connection semantics; orange remains reserved for brand actions.
- Toolbars and inspectors match AppShell surfaces, typography, radius, and focus states.
- Zoom, pan, connect, multi-select, keyboard shortcuts, undo/redo, persistence, and accessibility behavior are regression-tested independently of styling.

Expand Down Expand Up @@ -116,7 +117,7 @@ Homepage emphasis is approximately 70% what can be built with BeatAPI, 20% what
| Date | Decision | Rationale |
| --- | --- | --- |
| 2026-08-12 | One PromptWise-derived design language | A single recognizable system is more valuable than several shallow themes. |
| 2026-08-12 | Shared AppShell with Studio and Canvas modes | Preserves both workflows without doubling dashboard maintenance. |
| 2026-08-12 | Shared AppShell with Studio, Canvas, and Assets modes | Preserves every project view without duplicating workspace chrome. |
| 2026-08-12 | Orange primary, cream marketing, neutral app | Connects the brand across marketing and product while preserving workspace clarity. |
| 2026-08-12 | Figtree + Geist Mono | Keeps creative brand expression and developer readability in one family of interfaces. |
| 2026-08-15 | Unified black Studio, Canvas, and Pricing | Pricing retains the preferred PromptWise structure while matching the cinematic product environment. |
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ There is no login, account system, payment flow, subscription, local credit ledg

- Studio for prompt-first image and video generation.
- React Flow Canvas for connected, multi-step creative workflows.
- A project-scoped Assets view shared by Studio and Canvas.
- Local projects, snapshots, generation history, and asset indexing.
- One code-defined catalog of supported BeatAPI image and video models.
- BeatAPI task submission and status polling.
Expand All @@ -37,7 +38,7 @@ Open `http://localhost:3020`. Home is the default route. Opening Studio or Canva
## How data flows

```text
Studio / Canvas
Studio / Canvas / Assets
-> local server routes
-> imported files saved under data/project-assets and indexed in SQLite
-> generation precheck and a one-time SQLite generation intent
Expand Down
3 changes: 2 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

- Studio:以提示词和参数为主的快速生成体验。
- Canvas:基于 React Flow 的多步骤节点画布。
- Assets:Studio 与 Canvas 共用的项目素材库。
- 本地项目、画布快照、生成历史和素材索引。
- 由代码统一维护的 BeatAPI 图片/视频模型目录。
- BeatAPI 任务提交、状态轮询和结果回填。
Expand All @@ -35,7 +36,7 @@ pnpm dev
## 数据逻辑

```text
Studio / Canvas
Studio / Canvas / Assets
-> 本地服务端接口
-> 导入文件立即保存到 data/project-assets,并写入 SQLite 素材索引
-> 生成预检与 SQLite 一次性生成意图
Expand Down
6 changes: 4 additions & 2 deletions WORKSPACE_MODES.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Workspace modes

Each project can be opened in either mode:
Each project can be opened in three modes:

- **Studio** is a guided, form-first surface for producing a result quickly.
- **Canvas** is a node-based surface for connected references and multi-step workflows.
- **Assets** is the project-scoped library shared by Studio and Canvas.

Both modes share the same project ID, assets, model catalog, provider connection, generation history, and persistence layer. The last opened mode is stored on the project so the project list can resume in place.
All three modes share the same project ID, assets, model catalog, provider connection, generation history, and persistence layer. The last opened mode is stored on the project so the project list can resume in place.

Routes:

- `/studio/:projectId`
- `/canvas/:projectId`
- `/assets/:projectId`
34 changes: 26 additions & 8 deletions messages/en.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"product.shell.home": "Home",
"product.shell.create": "Create",
"product.shell.studio": "Studio",
"product.shell.canvas": "Canvas",
"product.shell.pricing": "Pricing",
Expand All @@ -21,10 +22,10 @@
"product.home.createInStudio": "Create in Studio",
"product.home.sectionTitle": "Choose your workspace",
"product.home.viewProjects": "View projects",
"product.home.studioDescription": "Generate and compare images or videos in a focused workspace.",
"product.home.canvasDescription": "Arrange shared assets and connect them into a visual workflow.",
"product.home.studioDescription": "Generate images and videos in one workspace.",
"product.home.canvasDescription": "Connect assets in a visual workflow.",
"product.home.projectsTitle": "Projects",
"product.home.projectsDescription": "Continue recent work with Studio and Canvas state kept together.",
"product.home.projectsDescription": "Continue recent Studio and Canvas work.",
"product.home.openWorkspace": "Open workspace",
"product.home.mediaImage": "Image",
"product.home.mediaVideo": "Video",
Expand Down Expand Up @@ -387,7 +388,8 @@
"status": {
"queued": "Queued",
"generating": "Generating",
"ready": "Ready",
"ready": "Success",
"success": "Success",
"failed": "Failed"
},
"fromCanvas": "From canvas",
Expand Down Expand Up @@ -432,6 +434,23 @@
"freeGenerate": "Free generation",
"uploadStart": "Upload image"
},
"feed": {
"history": "History",
"result": "Generation",
"generating": "Generating...",
"failed": "Failed",
"ready": "Ready",
"success": "Success",
"noPrompt": "No prompt",
"openPreview": "Open preview",
"closePreview": "Close preview",
"prompt": "Prompt",
"model": "Model",
"params": "Parameters",
"image": "Image",
"video": "Video",
"references": "References"
},
"textNote": {
"template": "Text note\n\nCapture composition ideas, shot notes, or production reminders here."
},
Expand Down Expand Up @@ -624,10 +643,9 @@
"retry": "Try again",
"backToHistory": "Back to history",
"startTitle": "Start a new project",
"startStudioDescription": "A Studio project is created only after you choose to start.",
"startCanvasDescription": "A Canvas project is created only after you choose to start.",
"startStudio": "Start Studio project",
"startCanvas": "Start Canvas project",
"startDescription": "Choose Studio or Canvas to create this project.",
"startStudio": "Studio",
"startCanvas": "Canvas",
"defaultName": "Untitled project"
}
},
Expand Down
32 changes: 25 additions & 7 deletions messages/zh.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"product.shell.home": "首页",
"product.shell.create": "创作",
"product.shell.studio": "Studio",
"product.shell.canvas": "Canvas",
"product.shell.pricing": "定价",
Expand All @@ -22,10 +23,10 @@
"product.home.createInStudio": "在 Studio 中创作",
"product.home.sectionTitle": "选择你的工作空间",
"product.home.viewProjects": "查看项目",
"product.home.studioDescription": "在专注的工作区中生成和比较图片或视频。",
"product.home.canvasDescription": "排列共享素材,并把它们连接成可视化工作流。",
"product.home.studioDescription": "在专注工作区生成图片和视频。",
"product.home.canvasDescription": "把素材连成可视化工作流。",
"product.home.projectsTitle": "项目",
"product.home.projectsDescription": "继续最近的创作,Studio 与 Canvas 状态会保存在同一个项目中。",
"product.home.projectsDescription": "继续最近的 Studio 与 Canvas 创作。",
"product.home.openWorkspace": "打开工作空间",
"product.home.mediaImage": "图片",
"product.home.mediaVideo": "视频",
Expand Down Expand Up @@ -389,6 +390,7 @@
"queued": "排队中",
"generating": "生成中",
"ready": "已就绪",
"success": "成功",
"failed": "失败"
},
"fromCanvas": "来自画布",
Expand Down Expand Up @@ -433,6 +435,23 @@
"freeGenerate": "自由生成",
"uploadStart": "上传图片"
},
"feed": {
"history": "历史",
"result": "生成结果",
"generating": "生成中...",
"failed": "失败",
"ready": "成功",
"success": "成功",
"noPrompt": "无提示词",
"openPreview": "打开预览",
"closePreview": "关闭预览",
"prompt": "提示词",
"model": "模型",
"params": "参数",
"image": "图片",
"video": "视频",
"references": "参考图"
},
"textNote": {
"template": "文本备注\n\n在这里记录构图想法、镜头说明或制作备注。"
},
Expand Down Expand Up @@ -625,10 +644,9 @@
"retry": "重试",
"backToHistory": "返回历史",
"startTitle": "开始新项目",
"startStudioDescription": "只有确认开始后,才会创建 Studio 项目。",
"startCanvasDescription": "只有确认开始后,才会创建 Canvas 项目。",
"startStudio": "开始 Studio 项目",
"startCanvas": "开始 Canvas 项目",
"startDescription": "选择 Studio 或 Canvas 来创建这个项目。",
"startStudio": "Studio",
"startCanvas": "Canvas",
"defaultName": "未命名项目"
}
},
Expand Down
4 changes: 3 additions & 1 deletion src/components/app/create-project-route-page.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ const source = readFileSync(

test('visiting Studio or Canvas does not create a project automatically', () => {
assert.doesNotMatch(source, /useEffect/);
assert.match(source, /onClick=\{\(\) => void createProject\(\)\}/);
assert.match(source, /createProject\('studio'\)/);
assert.match(source, /createProject\('canvas'\)/);
assert.match(source, /envConfigs\.app_logo/);
assert.match(source, /'\/api\/app\/projects'/);
});

Expand Down
Loading
Loading