diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 268acff..f09e88d 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -4,13 +4,13 @@ "name": "BeatAPI" }, "description": "Official BeatDesign plugins for Claude Code.", - "version": "0.2.4", + "version": "0.2.5", "plugins": [ { "name": "beatdesign", "source": "./integrations/claude-code/beatdesign", "description": "Operate a local BeatDesign Canvas and video Editor through MCP.", - "version": "0.2.4", + "version": "0.2.5", "author": { "name": "BeatAPI" } diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b1641e5..b138b35 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -15,10 +15,13 @@ Use Node.js 22+ and pnpm 10+. ```bash pnpm install pnpm db:push +# Generate route and translation modules before checking a fresh clone. +pnpm build pnpm typecheck pnpm test pnpm i18n:check -pnpm build +pnpm exec playwright install chromium +pnpm test:e2e ``` Add or update tests for behavior changes. Add user-facing copy to both `messages/en.json` and `messages/zh.json`. Never include API keys, local databases, generated uploads, or storage credentials. diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index b7c1608..acb7ca6 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -10,15 +10,19 @@ permissions: jobs: verify: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, windows-2025] + runs-on: ${{ matrix.os }} timeout-minutes: 20 steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 with: version: 10.32.1 run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 with: node-version: 22 cache: pnpm @@ -32,6 +36,8 @@ jobs: - run: pnpm typecheck - run: pnpm test - run: pnpm i18n:check - - run: pnpm exec playwright install --with-deps chromium - - run: pnpm test:e2e + - if: runner.os == 'Linux' + run: pnpm exec playwright install --with-deps chromium + - if: runner.os == 'Linux' + run: pnpm test:e2e - run: pnpm audit --prod --audit-level high diff --git a/CHANGELOG.md b/CHANGELOG.md index dfd2a5a..66bd93a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to BeatDesign are documented in this file. -## Unreleased +## Unreleased — v0.2.5 candidate ### Added @@ -14,6 +14,7 @@ All notable changes to BeatDesign are documented in this file. - Align Canvas previews and Take selection with the latest usable successful generation, while preserving explicit selections and pinned timeline Assets (#26). - Preserve timeline clip gains in MCP audio exports and limit overlapping peaks without makeup gain (#24). - Commit project settings and idempotency receipts atomically; retain edits and show errors when header renames conflict. +- Keep Canvas creation, upload/history controls and keyboard shortcuts inactive until the editor and saved project are ready, preserving the first user action even when the editor chunk loads slowly. - Persist undo/redo back to an intentionally empty Canvas. Extract Windows test portability, Studio empty-state translations, and narrow-screen hero layout improvements from #18 without its branding, Provider, or hosted deployment profile. ### Security @@ -21,8 +22,17 @@ All notable changes to BeatDesign are documented in this file. - Isolate BeatAPI adapter construction, readiness checks and input validation with TanStack server-only functions so browser model discovery never imports local configuration or SQLite. - Update TanStack Start/Router, the MCP SDK, Vite, Seroval and source-map-js to patched versions; production dependency audit reports no known vulnerabilities. +### Maintenance + +- Prepare v0.2.5 application and integration metadata; publication remains pending. +- Replace Unix-only postinstall commands with a Node.js setup script. Verify clean installation, schema setup, build, types, tests and translations on Linux and Windows in CI. +- Run browser/MCP checks against the production build so development reloads and Vite error overlays cannot interrupt release validation. +- Pin Node.js 24-compatible GitHub Actions and explicit runner images; adopt the current server-function validator API and explicit Vite config import extensions. +- Correct product website links in application, package, plugin and policy-page metadata. + ### Documentation +- Document source upgrade steps and project-setting revision/cover handling in Codex, Claude Code and WorkBuddy Skills. - Highlight current source updates in English, Chinese and Japanese READMEs, while distinguishing them from the v0.2.4 release and package. Update the MCP catalog and current product status. ## [0.2.4] - 2026-09-23 diff --git a/README.ja.md b/README.ja.md index 5f87b16..d879451 100644 --- a/README.ja.md +++ b/README.ja.md @@ -34,6 +34,8 @@ BeatDesignは、AI画像・動画制作のための独立したオープンソ 以下は`main`の更新です。最新の正式Releaseとnpmパッケージは引き続き **v0.2.4** です。利用するにはソースを更新してください。既存のパッケージへ自動配信されるものではありません。 +リポジトリと連携用マニフェストは **v0.2.5** に更新済みですが、公開は保留中です。ソース版を更新する場合はBeatDesignを停止し、`git pull --ff-only`、`pnpm install --frozen-lockfile`、`pnpm db:push` の順に実行してから、`pnpm dev:agent`(または通常のUI/MCP起動コマンド)を再起動してください。ローカルデータを保持し、Projectを初期化しないでください。 + - **Project設定とカバー:** 名前と説明の編集、画像カバーのアップロード・選択、自動カバーへのリセットに対応。手動カバーは後の読み込みや生成で置き換わりません。Projectカードまたはワークスペース上部の設定アイコンから開きます。 - **生成バージョンの一致:** CanvasのプレビューとTake選択が最新の成功結果を示します。手動選択とTimelineの固定Assetは保持されます。 - **Projectへ直接移動:** CanvasまたはStudioを選ぶとProjectを作成して直接開きます。再試行と連打防止にも対応します。 diff --git a/README.md b/README.md index 3a73004..4f3c62a 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ BeatDesign is an independent, open-source, local-first workspace for AI image an Available on `main`; the latest tagged release and npm package remain **v0.2.4**. Pull the source to use these updates; existing packaged installations do not receive them automatically. +The repository and integration manifests are prepared for **v0.2.5**; publication is pending. For an existing source checkout, stop BeatDesign, run `git pull --ff-only`, `pnpm install --frozen-lockfile`, `pnpm db:push`, then restart `pnpm dev:agent` (or your usual UI/MCP commands). Keep your local data directory; no reinstall or project reset is needed. + - **Project settings:** edit names and descriptions, upload or select a cover, and reset to an automatic image or local video poster. A manual cover stays selected after later imports and generations. Open the settings icon on a project card or workspace header. - **Consistent generation versions:** Canvas previews and Take selection agree on the latest successful output; manual selections and concrete timeline Assets are preserved. - **Faster project entry:** choosing Canvas or Studio creates one project and opens it directly, with retry protection. diff --git a/README.zh-CN.md b/README.zh-CN.md index 3488500..d3960e1 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -34,6 +34,8 @@ BeatDesign 是一个独立、开源、本地优先的 AI 图片与视频创作 以下更新位于 `main` 源码;最新正式 Release 和 npm 包仍为 **v0.2.4**。拉取源码后即可使用,现有安装包不会自动获得这些更新。 +仓库与接入包元数据已准备为 **v0.2.5**,尚未发布。已有源码用户请先停止 BeatDesign,再依次运行 `git pull --ff-only`、`pnpm install --frozen-lockfile`、`pnpm db:push`,随后重启 `pnpm dev:agent`(或原有 UI/MCP 启动命令)。保留本地数据目录,无需重装或重置项目。 + - **项目设置与稳定封面:** 修改项目名称和简介,上传或选择封面,或恢复自动封面。手动封面不会被后续导入、生成的素材替换。入口在项目卡片和工作区顶栏的设置图标。 - **生成版本保持一致:** 画布预览和 Take 选择默认指向最新成功结果,同时保留手动选择和时间线锁定的具体素材。 - **创建项目一步直达:** 选择画布或创作台后直接进入新项目,支持失败重试和重复点击保护。 diff --git a/docs/PRODUCT_PLAN_AND_STATUS.md b/docs/PRODUCT_PLAN_AND_STATUS.md index 63e2ff2..19dbbe8 100644 --- a/docs/PRODUCT_PLAN_AND_STATUS.md +++ b/docs/PRODUCT_PLAN_AND_STATUS.md @@ -4,14 +4,14 @@ - 当前基线:BeatDesign v0.2 Phase 1 本地实现 - 最后核对:2026-10-22 - 事实边界:只有代码、测试和本文“已完成”栏目共同证明的能力才算完成;规划项不等于已发布 -- 发布状态:GitHub 最新正式 Release 为 [v0.2.4](https://github.com/BeatAPI/BeatDesign/releases/tag/v0.2.4);npm 最新版本为 `@beatapi/beatdesign-workbuddy@0.2.4`(2026-09-23 核验)。WorkBuddy 开放平台中,原有连接器 `oc_321d021032343aea` 的 v0.2.3 已发布,v0.2.4 已提交更新并显示“审核中”;新版通过审核和公开市场可检索性均未核验。 +- 发布状态:GitHub 最新正式 Release 为 [v0.2.4](https://github.com/BeatAPI/BeatDesign/releases/tag/v0.2.4);npm 最新版本为 `@beatapi/beatdesign-workbuddy@0.2.4`(2026-10-22 核验)。仓库与接入包元数据已准备为 v0.2.5,尚未发布。WorkBuddy 开放平台中,原有连接器 `oc_321d021032343aea` 的 v0.2.3 已发布,v0.2.4 已提交更新并显示“审核中”;新版通过审核和公开市场可检索性均未核验。 ### 2026-10 主分支更新(尚未发布新版本) - 整合社区 PR #22、#24、#26、#28:直接创建项目、MCP 导出音量修复、最新成功生成版本统一选择、项目设置与手动/自动封面。 - `bdesign_project_update` 将 MCP 目录扩展为 30 个工具;项目设置更新与幂等回执在同一 SQLite 事务提交,回执失败时回滚设置,保留旧项目封面。 - 从 PR #18 提取 Windows 测试兼容、Studio 首屏翻译、空画布撤销/重做保存及窄屏布局修复;第三方品牌、Provider 和托管部署配置未纳入上游。 -- README 分开标注源码更新与 v0.2.4 安装包。官方 Skill 目录仍为空,Issue #29 的持久分组、素材文件夹和复用模板仍待实现。 +- v0.2.5 待发布候选补齐跨平台安装脚本、Linux/Windows CI、接入包版本和项目设置 Skill 指引;README 分开标注源码更新与 v0.2.4 安装包。官方 Skill 目录仍为空,Issue #29 的持久分组、素材文件夹和复用模板仍待实现。 ### v0.2.4 已发布改进 diff --git a/docs/RELEASE_SCOPE.md b/docs/RELEASE_SCOPE.md index e20ea13..9182d2e 100644 --- a/docs/RELEASE_SCOPE.md +++ b/docs/RELEASE_SCOPE.md @@ -6,7 +6,23 @@ Included: homepage, projects, Studio, Canvas, provider and upload-storage config Excluded: authentication, login, accounts, payments, subscriptions, credits, API-key issuing, invitations, RBAC, admin, support tickets, CMS, email delivery, and unrelated AI-provider adapters. -Release verification requires a clean install, schema creation, production build (which generates Paraglide and route types), typecheck, test, i18n check, MCP stdio handshake, and local route smoke test. A real paid BeatAPI generation is a separate credentialed end-to-end check. +Release verification requires a clean install, schema creation, production build (which generates Paraglide and route types), typecheck, test, i18n check, MCP stdio handshake, and local route smoke test. Browser/MCP checks run against that production build; run `pnpm build` before `pnpm test:e2e`. A real paid BeatAPI generation is a separate credentialed end-to-end check. + +## v0.2.5 candidate (not published) + +- [x] Align application, Codex, Claude Code, Claude marketplace, LobeHub and WorkBuddy metadata on `0.2.5`. +- [x] Verify clean installation and schema setup on Linux and Windows in CI ([candidate run](https://github.com/BeatAPI/BeatDesign/actions/runs/39647018659), source commit `cbc6951`). +- [x] Run typecheck, tests (432 passing), i18n validation, production build and focused UI/MCP checks (14 passing). +- [x] Build and inspect the Connector ZIP and npm runtime tarball; install the tarball in a clean temporary directory, complete MCP handshake (30 tools), and smoke-test the production workspace. +- Merge and main-branch CI status: [maintenance PR #31](https://github.com/BeatAPI/BeatDesign/pull/31) and its linked Quality checks. Recheck the exact merge commit before publishing. +- [ ] Publish GitHub Release `v0.2.5` with verified assets. +- [ ] Publish `@beatapi/beatdesign-workbuddy@0.2.5`; independently verify npm `latest`. +- [ ] Submit the updated Connector to WorkBuddy, then record review and public availability separately. + +The version in repository metadata identifies the next candidate, not a published +release. Latest public GitHub Release and npm package remain `v0.2.4` (checked +2026-10-22). WorkBuddy's last observed review state remains the dated v0.2.4 +submission below. ## v0.2.4 release and WorkBuddy update diff --git a/integrations/claude-code/beatdesign/.claude-plugin/plugin.json b/integrations/claude-code/beatdesign/.claude-plugin/plugin.json index 959e46e..7af7d7f 100644 --- a/integrations/claude-code/beatdesign/.claude-plugin/plugin.json +++ b/integrations/claude-code/beatdesign/.claude-plugin/plugin.json @@ -1,13 +1,13 @@ { "name": "beatdesign", "displayName": "BeatDesign", - "version": "0.2.4", + "version": "0.2.5", "description": "Operate a local BeatDesign Canvas, shared Assets, generation workspace, and video Editor through MCP.", "author": { "name": "BeatAPI", "url": "https://beatapi.io" }, - "homepage": "https://design.beatapi.io", + "homepage": "https://beatapi.io/design", "repository": "https://github.com/BeatAPI/BeatDesign", "license": "Apache-2.0", "keywords": ["beatdesign", "canvas", "video-editor", "generation", "mcp"] diff --git a/integrations/claude-code/beatdesign/README.md b/integrations/claude-code/beatdesign/README.md index 70e753c..883ccef 100644 --- a/integrations/claude-code/beatdesign/README.md +++ b/integrations/claude-code/beatdesign/README.md @@ -31,7 +31,7 @@ Inside Claude Code, check `/mcp` for the `beatdesign` server and ask: ## Install from the repository marketplace -After this repository is available on GitHub: +Add the repository marketplace: ```text /plugin marketplace add BeatAPI/BeatDesign diff --git a/integrations/claude-code/beatdesign/skills/beatdesign-workspace/SKILL.md b/integrations/claude-code/beatdesign/skills/beatdesign-workspace/SKILL.md index a45090c..c16f0e6 100644 --- a/integrations/claude-code/beatdesign/skills/beatdesign-workspace/SKILL.md +++ b/integrations/claude-code/beatdesign/skills/beatdesign-workspace/SKILL.md @@ -37,6 +37,20 @@ opened when the host cannot open it. - Treat generation as asset-first: output becomes a Project Asset before it is placed on Canvas or Editor. +## Update project settings + +- Call `bdesign_project_get` and use `settings.settingsRevision` as the expected + revision for `bdesign_project_update`. Settings revisions are separate from + Canvas and Editor revisions. +- Update only the requested name, description or cover. A manual cover must be + an image Asset in this Project; import a local image first when needed. Use + `cover: { "mode": "auto" }` to restore automatic cover selection. +- Reuse the command ID and idempotency key when delivery is uncertain. On a + revision conflict, read the latest settings and resolve the conflicting edit + before submitting again; do not overwrite another change blindly. +- Read the saved settings back and inspect the project card or workspace header. + Project settings refresh while visible within about five seconds. + ## Import captions - Prefer `bdesign_editor_import_srt` for a file or complete SRT text. Use diff --git a/integrations/codex/beatdesign/.codex-plugin/plugin.json b/integrations/codex/beatdesign/.codex-plugin/plugin.json index 6eca5af..ab97f12 100644 --- a/integrations/codex/beatdesign/.codex-plugin/plugin.json +++ b/integrations/codex/beatdesign/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "beatdesign", - "version": "0.2.4", + "version": "0.2.5", "description": "Control a local BeatDesign Canvas, generation workspace, and video Editor through MCP.", "author": { "name": "BeatAPI" diff --git a/integrations/codex/beatdesign/skills/beatdesign-workspace/SKILL.md b/integrations/codex/beatdesign/skills/beatdesign-workspace/SKILL.md index 5785a2a..17e7f90 100644 --- a/integrations/codex/beatdesign/skills/beatdesign-workspace/SKILL.md +++ b/integrations/codex/beatdesign/skills/beatdesign-workspace/SKILL.md @@ -24,6 +24,20 @@ For Canvas-specific review, call `bdesign_canvas_view` with `cardId` so the work - Import local media through `bdesign_asset_import` before referencing it from Canvas or Editor. - Treat generation as asset-first: generated output becomes a Project Asset before placement. +## Update project settings + +- Call `bdesign_project_get` and use `settings.settingsRevision` as the expected + revision for `bdesign_project_update`. Settings revisions are separate from + Canvas and Editor revisions. +- Update only the requested name, description or cover. A manual cover must be + an image Asset in this Project; import a local image first when needed. Use + `cover: { "mode": "auto" }` to restore automatic cover selection. +- Reuse the command ID and idempotency key when delivery is uncertain. On a + revision conflict, read the latest settings and resolve the conflicting edit + before submitting again; do not overwrite another change blindly. +- Read the saved settings back and inspect the project card or workspace header. + Project settings refresh while visible within about five seconds. + ## Import captions - Prefer `bdesign_editor_import_srt` for an SRT file or complete SRT text. Use `bdesign_editor_edit` with `upsert_caption` only for targeted cue changes; do not represent captions as generic media clips. diff --git a/integrations/workbuddy/beatdesign/connector-meta.json b/integrations/workbuddy/beatdesign/connector-meta.json index 52b7629..da5dc50 100644 --- a/integrations/workbuddy/beatdesign/connector-meta.json +++ b/integrations/workbuddy/beatdesign/connector-meta.json @@ -7,7 +7,7 @@ "description_en": "Operate a local BeatDesign Canvas, Assets, generation tasks, video timeline, and captions with natural language.", "source": "beatapi-beatdesign", "type": "mcp", - "version": "0.2.4", + "version": "0.2.5", "minWorkbuddyVersion": "5.0.0", "examples_zh": [ "打开我最近的 BeatDesign 项目并总结画布内容", diff --git a/integrations/workbuddy/beatdesign/mcp.json b/integrations/workbuddy/beatdesign/mcp.json index bf83a9d..6672e2f 100644 --- a/integrations/workbuddy/beatdesign/mcp.json +++ b/integrations/workbuddy/beatdesign/mcp.json @@ -3,7 +3,7 @@ "beatdesign": { "type": "stdio", "command": "npx", - "args": ["--yes", "@beatapi/beatdesign-workbuddy@0.2.4"], + "args": ["--yes", "@beatapi/beatdesign-workbuddy@0.2.5"], "runtime": { "type": "node", "version": "22" diff --git a/integrations/workbuddy/beatdesign/skills/beatdesign-workspace/SKILL.md b/integrations/workbuddy/beatdesign/skills/beatdesign-workspace/SKILL.md index 48de0d6..22df6d2 100644 --- a/integrations/workbuddy/beatdesign/skills/beatdesign-workspace/SKILL.md +++ b/integrations/workbuddy/beatdesign/skills/beatdesign-workspace/SKILL.md @@ -43,6 +43,16 @@ BeatDesign MCP 是结构化控制层,浏览器中的 BeatDesign 是用户审 - 本地媒体先通过 `bdesign_asset_import` 导入,再放进 Canvas 或 Editor。 - 生成结果先成为 Project Asset;是否放进 Canvas/Editor 是下一步操作。 +## 项目设置 + +- 先调用 `bdesign_project_get`,把 `settings.settingsRevision` 用作 + `bdesign_project_update` 的 expected revision;不要使用画布或时间线的 revision。 +- 只修改用户要求的名称、简介或封面。手动封面必须是本项目的图片 Asset;本地图片先导入。 + 使用 `cover: { "mode": "auto" }` 恢复自动封面。 +- 结果不确定时复用同一个 command ID 和幂等键;遇到 revision 冲突先读最新设置, + 解决冲突后再提交,不直接覆盖他人的修改。 +- 写入后读回设置,并在项目卡片或工作区标题处检查。设置通常在页面可见时约五秒内刷新。 + ## 字幕 - 完整 SRT 文件或文本优先使用 `bdesign_editor_import_srt`;单条调整才使用 diff --git a/lhm.plugin.json b/lhm.plugin.json index af16900..9539b07 100644 --- a/lhm.plugin.json +++ b/lhm.plugin.json @@ -1,7 +1,7 @@ { "identifier": "beatapi-beatdesign", "name": "BeatDesign", - "version": "0.2.4", + "version": "0.2.5", "description": "The open-source, local-first AI canvas for image and video creation and analysis.", "homepage": "https://github.com/BeatAPI/BeatDesign" } diff --git a/package.json b/package.json index 8fc4d5c..6bc84b0 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "beatdesign", - "version": "0.2.4", + "version": "0.2.5", "private": true, "description": "The open-source, local-first AI canvas for image and video creation and analysis", "license": "Apache-2.0", - "homepage": "https://design.beatapi.io", + "homepage": "https://beatapi.io/design", "repository": { "type": "git", "url": "git+https://github.com/BeatAPI/BeatDesign.git" @@ -36,7 +36,7 @@ "prebuild": "node scripts/db-setup.mjs", "build": "tsx scripts/prepare-paraglide.ts && vite build && node scripts/sanitize-output.mjs", "start": "node .output/server/index.mjs", - "postinstall": "mkdir -p data && (test -f src/config/db/schema.ts || node scripts/db-setup.mjs)", + "postinstall": "node scripts/postinstall.mjs", "db:setup": "node scripts/db-setup.mjs", "db:push": "drizzle-kit push --config=drizzle.config.ts", "db:generate": "drizzle-kit generate --config=drizzle.config.ts", diff --git a/scripts/build-workbuddy-runtime-package.ts b/scripts/build-workbuddy-runtime-package.ts index b445125..8b2e698 100644 --- a/scripts/build-workbuddy-runtime-package.ts +++ b/scripts/build-workbuddy-runtime-package.ts @@ -22,7 +22,7 @@ const runtimePackage = { description: 'One-click local BeatDesign Canvas and video Editor runtime for WorkBuddy', license: 'Apache-2.0', - homepage: 'https://design.beatapi.io', + homepage: 'https://beatapi.io/design', repository: { type: 'git', url: 'git+https://github.com/BeatAPI/BeatDesign.git', diff --git a/scripts/postinstall.mjs b/scripts/postinstall.mjs new file mode 100644 index 0000000..d5aa608 --- /dev/null +++ b/scripts/postinstall.mjs @@ -0,0 +1,7 @@ +// Keep fresh-clone setup compatible with Node.js on Windows, macOS and Linux. +import { existsSync, mkdirSync } from 'node:fs'; + +mkdirSync('data', { recursive: true }); +if (!existsSync('src/config/db/schema.ts')) { + await import('./db-setup.mjs'); +} diff --git a/scripts/probe-workbuddy-runtime-package.ts b/scripts/probe-workbuddy-runtime-package.ts index 6dd5806..2cddb86 100644 --- a/scripts/probe-workbuddy-runtime-package.ts +++ b/scripts/probe-workbuddy-runtime-package.ts @@ -156,6 +156,13 @@ try { assert.equal(health.ok, true); assert.equal((await health.json()).message, 'pong'); + for (const path of ['/', '/zh', '/ja', '/projects', '/privacy-policy', '/terms-of-service']) { + const page = await fetch(`http://127.0.0.1:3020${path}`); + assert.equal(page.ok, true, `Packaged workspace route ${path} must load`); + assert.match(page.headers.get('content-type') ?? '', /text\/html/); + assert.match(await page.text(), /BeatDesign/); + } + const conflictingRuntime = spawn(process.execPath, [launcher], { cwd: probeRoot, env: { diff --git a/scripts/start-e2e.mjs b/scripts/start-e2e.mjs index 6f66ab5..74dd58e 100644 --- a/scripts/start-e2e.mjs +++ b/scripts/start-e2e.mjs @@ -1,13 +1,19 @@ -import { mkdtemp, rm } from 'node:fs/promises'; +import { access, mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { spawn, execFileSync } from 'node:child_process'; +try { + await access('.output/server/index.mjs'); +} catch { + throw new Error('Browser/MCP checks require a production build. Run pnpm build first.'); +} const directory = await mkdtemp(join(tmpdir(), 'beatdesign-e2e-')); -const env = { ...process.env, BEATDESIGN_DATA_DIR: directory, BEATDESIGN_MCP_HTTP_PORT: '3043', BEATDESIGN_MCP_TOKEN: 'e2e-local-only' }; +const env = { ...process.env, BEATDESIGN_DATA_DIR: directory, NITRO_HOST: '127.0.0.1', NITRO_PORT: '3042', BEATDESIGN_MCP_HTTP_PORT: '3043', BEATDESIGN_MCP_TOKEN: 'e2e-local-only' }; execFileSync('pnpm', ['db:push'], { env, stdio: 'inherit' }); const children = [ - spawn(process.execPath, ['node_modules/vite/bin/vite.js', '--host', '127.0.0.1', '--port', '3042', '--strictPort'], { env, stdio: 'inherit' }), + // Exercise the distributed workspace rather than Vite's HMR/error overlay. + spawn(process.execPath, ['.output/server/index.mjs'], { env, stdio: 'inherit' }), spawn(process.execPath, ['--import', 'tsx', 'scripts/mcp-server.ts', '--http'], { env, stdio: 'inherit' }), ]; let stopping = false; diff --git a/src/components/beatcanvas/beatcanvas-shell.tsx b/src/components/beatcanvas/beatcanvas-shell.tsx index 92b7ad2..9415ae1 100644 --- a/src/components/beatcanvas/beatcanvas-shell.tsx +++ b/src/components/beatcanvas/beatcanvas-shell.tsx @@ -1383,7 +1383,7 @@ export function BeatCanvasShell({ toast.error(studioT('messages.snapshotConflict')); }, [studioT]); - useProjectSnapshotLifecycle({ + const isWorkspaceReady = useProjectSnapshotLifecycle({ projectId, projectPath, initialProjectSnapshot, @@ -1484,6 +1484,7 @@ export function BeatCanvasShell({ useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { + if (!isWorkspaceReady) return; const targetContext = getCanvasShortcutTargetContext(event.target); const key = event.key.toLowerCase(); const hasModifier = event.metaKey || event.ctrlKey; @@ -1573,6 +1574,7 @@ export function BeatCanvasShell({ window.removeEventListener('keydown', handleKeyDown, { capture: true }); }; }, [ + isWorkspaceReady, copySelectedCanvasCards, handleCreatePromptDraft, openUploadPicker, @@ -1677,6 +1679,7 @@ export function BeatCanvasShell({ {/* Floating Left Toolbar */} handleCreatePromptDraft('image')} onInsertHistoryAsset={(asset) => diff --git a/src/components/beatcanvas/beatcanvas-sidebar.tsx b/src/components/beatcanvas/beatcanvas-sidebar.tsx index 1682335..1ec739c 100644 --- a/src/components/beatcanvas/beatcanvas-sidebar.tsx +++ b/src/components/beatcanvas/beatcanvas-sidebar.tsx @@ -22,6 +22,7 @@ const HistoryPanel = lazy(() => export type BeatCanvasSidebarProps = { projectId?: string | null; + disabled?: boolean; onUploadMedia: () => void; onCreateImageDraft: () => void; onInsertHistoryAsset: ( @@ -32,6 +33,7 @@ export type BeatCanvasSidebarProps = { export default function BeatCanvasSidebar({ projectId, + disabled = false, onUploadMedia, onCreateImageDraft, onInsertHistoryAsset, @@ -62,13 +64,14 @@ export default function BeatCanvasSidebar({ icon={} title={t('toolbar.generationNode')} active={false} + disabled={disabled} onClick={handleCreateGenerationNode} /> } title={t('toolbar.uploadNode')} active={uploadIntent !== null} - disabled={uploadIntent !== null} + disabled={disabled || uploadIntent !== null} onClick={onUploadMedia} /> @@ -76,6 +79,7 @@ export default function BeatCanvasSidebar({ icon={} title={t('sidebar.historyAssets')} active={activePanel === 'history'} + disabled={disabled} onClick={() => handleTogglePanel('history')} /> diff --git a/src/components/beatcanvas/use-project-snapshot-lifecycle.ts b/src/components/beatcanvas/use-project-snapshot-lifecycle.ts index 4ebd138..6f58923 100644 --- a/src/components/beatcanvas/use-project-snapshot-lifecycle.ts +++ b/src/components/beatcanvas/use-project-snapshot-lifecycle.ts @@ -574,4 +574,6 @@ export function useProjectSnapshotLifecycle({ isHydratedFromProject, projectId, ]); + + return isCanvasReady && isHydratedFromProject && isHydratedFromQuery; } diff --git a/src/content/pages/privacy-policy.en.mdx b/src/content/pages/privacy-policy.en.mdx index f9fed8c..4650aff 100644 --- a/src/content/pages/privacy-policy.en.mdx +++ b/src/content/pages/privacy-policy.en.mdx @@ -91,7 +91,7 @@ BeatDesign is a general-purpose creative tool and is not directed to children wh ## 13. Changes to this Policy -We may update this Privacy Policy as BeatDesign evolves. We will update the date above when changes are published. Material changes may also be announced through the official repository or [BeatDesign website](https://design.beatapi.io). +We may update this Privacy Policy as BeatDesign evolves. We will update the date above when changes are published. Material changes may also be announced through the official repository or [BeatDesign website](https://beatapi.io/design). ## 14. Contact diff --git a/src/content/pages/privacy-policy.ja.mdx b/src/content/pages/privacy-policy.ja.mdx index 394b717..6464565 100644 --- a/src/content/pages/privacy-policy.ja.mdx +++ b/src/content/pages/privacy-policy.ja.mdx @@ -91,7 +91,7 @@ BeatDesignは汎用的なクリエイティブツールであり、その法域 ## 13. 本ポリシーの変更 -BeatDesignの進化に伴い、本プライバシーポリシーを更新する場合があります。変更の公開時には上記の日付を更新します。重要な変更は、公式リポジトリまたは[BeatDesignウェブサイト](https://design.beatapi.io)で告知する場合もあります。 +BeatDesignの進化に伴い、本プライバシーポリシーを更新する場合があります。変更の公開時には上記の日付を更新します。重要な変更は、公式リポジトリまたは[BeatDesignウェブサイト](https://beatapi.io/design)で告知する場合もあります。 ## 14. お問い合わせ diff --git a/src/content/pages/privacy-policy.zh.mdx b/src/content/pages/privacy-policy.zh.mdx index 63f3003..f806d3b 100644 --- a/src/content/pages/privacy-policy.zh.mdx +++ b/src/content/pages/privacy-policy.zh.mdx @@ -91,7 +91,7 @@ BeatDesign 是通用创作工具,并非面向在其所在司法辖区无法自 ## 13. 政策变更 -随着 BeatDesign 演进,我们可能更新本《隐私政策》。发布更新时,我们会修改页面顶部日期;重大变更还可能通过官方仓库或 [BeatDesign 网站](https://design.beatapi.io/zh/)公告。 +随着 BeatDesign 演进,我们可能更新本《隐私政策》。发布更新时,我们会修改页面顶部日期;重大变更还可能通过官方仓库或 [BeatDesign 网站](https://beatapi.io/design)公告。 ## 14. 联系方式 diff --git a/src/content/pages/terms-of-service.en.mdx b/src/content/pages/terms-of-service.en.mdx index 8c6b695..e3652ee 100644 --- a/src/content/pages/terms-of-service.en.mdx +++ b/src/content/pages/terms-of-service.en.mdx @@ -4,7 +4,7 @@ export const meta = { updated_at: '2026-08-22', } -These Terms of Use govern your use of the [official BeatDesign website](https://design.beatapi.io), distribution, and open-source workspace. By using BeatDesign, you agree to these Terms. If you do not agree, do not use the official website or services. +These Terms of Use govern your use of the [official BeatDesign website](https://beatapi.io/design), distribution, and open-source workspace. By using BeatDesign, you agree to these Terms. If you do not agree, do not use the official website or services. BeatDesign is an open-source, local-first AI canvas maintained by BeatAPI. In these Terms, “BeatDesign” means the workspace software and official BeatDesign surfaces. “BeatAPI” means the separate hosted API service available at [beatapi.io](https://beatapi.io). @@ -89,7 +89,7 @@ You may stop using BeatDesign at any time. In a local installation, you control ## 13. Changes to these Terms -We may update these Terms as BeatDesign evolves. We will update the date above when changes are published. Material changes may also be announced through the project repository or [official website](https://design.beatapi.io). Your continued use after an update means you accept the revised Terms to the extent permitted by law. +We may update these Terms as BeatDesign evolves. We will update the date above when changes are published. Material changes may also be announced through the project repository or [official website](https://beatapi.io/design). Your continued use after an update means you accept the revised Terms to the extent permitted by law. ## 14. Contact diff --git a/src/content/pages/terms-of-service.ja.mdx b/src/content/pages/terms-of-service.ja.mdx index 8a86f11..dbd11fa 100644 --- a/src/content/pages/terms-of-service.ja.mdx +++ b/src/content/pages/terms-of-service.ja.mdx @@ -4,7 +4,7 @@ export const meta = { updated_at: '2026-08-22', } -本利用規約は、[BeatDesign公式ウェブサイト](https://design.beatapi.io)、配布物、およびオープンソースワークスペースの利用に適用されます。BeatDesignを利用することで、本規約に同意したものとみなされます。同意しない場合は、公式ウェブサイトまたはサービスを利用しないでください。 +本利用規約は、[BeatDesign公式ウェブサイト](https://beatapi.io/design)、配布物、およびオープンソースワークスペースの利用に適用されます。BeatDesignを利用することで、本規約に同意したものとみなされます。同意しない場合は、公式ウェブサイトまたはサービスを利用しないでください。 BeatDesignは、BeatAPIが管理するオープンソースでローカルファーストなAI Canvasです。本規約において「BeatDesign」とは、ワークスペースソフトウェアとBeatDesign公式の提供画面を指します。「BeatAPI」とは、[beatapi.io](https://beatapi.io)で提供される別個のホスト型APIサービスを指します。 @@ -89,7 +89,7 @@ BeatDesign、そのプロバイダー、生成出力が意図する用途に適 ## 13. 本規約の変更 -BeatDesignの進化に伴い、本規約を更新する場合があります。変更の公開時には上記の日付を更新します。重要な変更は、プロジェクトリポジトリまたは[公式ウェブサイト](https://design.beatapi.io)で告知する場合もあります。更新後も利用を継続した場合、法令で認められる範囲で改定後の規約に同意したものとみなされます。 +BeatDesignの進化に伴い、本規約を更新する場合があります。変更の公開時には上記の日付を更新します。重要な変更は、プロジェクトリポジトリまたは[公式ウェブサイト](https://beatapi.io/design)で告知する場合もあります。更新後も利用を継続した場合、法令で認められる範囲で改定後の規約に同意したものとみなされます。 ## 14. お問い合わせ diff --git a/src/content/pages/terms-of-service.zh.mdx b/src/content/pages/terms-of-service.zh.mdx index eecc152..b40fb44 100644 --- a/src/content/pages/terms-of-service.zh.mdx +++ b/src/content/pages/terms-of-service.zh.mdx @@ -4,7 +4,7 @@ export const meta = { updated_at: '2026-08-22', } -本《使用条款》适用于 [BeatDesign 官方网站](https://design.beatapi.io/zh/)、官方发行版本及开源工作台。使用 BeatDesign 即表示您同意本条款;如果您不同意,请不要使用官方网站或相关服务。 +本《使用条款》适用于 [BeatDesign 官方网站](https://beatapi.io/design)、官方发行版本及开源工作台。使用 BeatDesign 即表示您同意本条款;如果您不同意,请不要使用官方网站或相关服务。 BeatDesign 是由 BeatAPI 维护的开源、本地优先 AI 画布。在本条款中,“BeatDesign”指工作台软件及 BeatDesign 官方产品界面;“BeatAPI”指通过 [beatapi.io](https://beatapi.io) 提供的独立托管 API 服务。 @@ -89,7 +89,7 @@ BeatDesign 及其集成会持续演进。功能、模型可用性、限制、存 ## 13. 条款变更 -随着 BeatDesign 演进,我们可能更新本条款。发布更新时,我们会修改页面顶部日期;重大变更还可能通过项目仓库或[官方网站](https://design.beatapi.io/zh/)公告。在法律允许的范围内,更新后继续使用即表示您接受修订后的条款。 +随着 BeatDesign 演进,我们可能更新本条款。发布更新时,我们会修改页面顶部日期;重大变更还可能通过项目仓库或[官方网站](https://beatapi.io/design)公告。在法律允许的范围内,更新后继续使用即表示您接受修订后的条款。 ## 14. 联系方式 diff --git a/src/core/projects/server-functions.ts b/src/core/projects/server-functions.ts index 4d8590f..fb12039 100644 --- a/src/core/projects/server-functions.ts +++ b/src/core/projects/server-functions.ts @@ -10,7 +10,7 @@ const loadProjectWithLatestSnapshotSchema = z.object({ }); export const loadSerializedProjectsFn = createServerFn() - .inputValidator(loadProjectsSchema) + .validator(loadProjectsSchema) .handler(async ({ data }) => { const [{ loadProjects }, { serializeProjectCenterCard }] = await Promise.all([ @@ -22,7 +22,7 @@ export const loadSerializedProjectsFn = createServerFn() }); export const loadProjectWithLatestSnapshotFn = createServerFn() - .inputValidator(loadProjectWithLatestSnapshotSchema) + .validator(loadProjectWithLatestSnapshotSchema) .handler(async ({ data }) => { const { loadProjectWithLatestSnapshot } = await import('./projects'); return loadProjectWithLatestSnapshot({ diff --git a/tests/e2e/project-settings.spec.ts b/tests/e2e/project-settings.spec.ts index cc0385f..1de56b8 100644 --- a/tests/e2e/project-settings.spec.ts +++ b/tests/e2e/project-settings.spec.ts @@ -111,9 +111,18 @@ test('home does not overflow narrow screens', async ({ page }) => { test('undo and redo persist an explicitly empty Canvas across reload', async ({ page, request }) => { const id = await createProject(request, 'Empty undo QA'); - await page.goto(`/canvas/${id}`); + let releaseEditor!: () => void; + const editorDownload = new Promise((resolve) => { releaseEditor = resolve; }); + await page.route('**/assets/react-flow-editor-*.js', async (route) => { + await editorDownload; + await route.continue(); + }); + await page.goto(`/canvas/${id}`, { waitUntil: 'domcontentloaded' }); await expect(page.getByRole('button', { name: 'Project settings', exact: true })).toBeEnabled(); - await page.getByRole('button', { name: 'Generation node', exact: true }).click(); + const createNode = page.getByRole('button', { name: 'Generation node', exact: true }); + await expect(createNode).toBeDisabled(); + releaseEditor(); + await createNode.click(); const cards = async () => (await (await request.get(`/api/app/projects/${id}/snapshot`)).json()).document.cards; await expect.poll(async () => (await cards()).length).toBe(1); await page.getByRole('button', { name: 'Undo', exact: true }).click(); diff --git a/tsconfig.json b/tsconfig.json index f5bb148..4d4bcdf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "esnext" ], "allowJs": true, + "allowImportingTsExtensions": true, "skipLibCheck": true, "strict": true, "noEmit": true, diff --git a/vite.config.ts b/vite.config.ts index 81c7ff6..b1a3b3b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,8 +6,8 @@ import viteReact from '@vitejs/plugin-react'; import { nitro } from 'nitro/vite'; import { defineConfig } from 'vite'; -import { paraglideCompilerOptions } from './src/config/paraglide'; -import { shouldNormalizeProjectAssetMediaRequest } from './src/lib/project-asset-media-request'; +import { paraglideCompilerOptions } from './src/config/paraglide.ts'; +import { shouldNormalizeProjectAssetMediaRequest } from './src/lib/project-asset-media-request.ts'; export default defineConfig({ server: {