From 3c25511f4791fd41f9cb5a4f6f9205f53c19dafb Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Mon, 21 Sep 2026 14:54:20 +0800 Subject: [PATCH 1/3] =?UTF-8?q?build(ports):=20=E6=9C=AC=E5=9C=B0=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E6=94=B9=E7=94=A8=207380/7381=20=E9=81=BF=E5=BC=80?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 5 +-- AGENTS.md | 70 ++++++++++++++++++++++++++++++++++++++ README.md | 6 ++-- backend/cmd/server/main.go | 3 +- docker-compose.dev.yml | 2 +- docker-compose.local.yml | 4 +-- docker-compose.yml | 4 +-- scripts/start-local.ps1 | 8 ++--- web/package.json | 4 +-- web/vite.config.ts | 4 ++- 10 files changed, 93 insertions(+), 17 deletions(-) diff --git a/.env.example b/.env.example index 7c774204a..16647091c 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,7 @@ CANVAS_REGISTRATION_ENABLED=false CANVAS_ALLOW_PRIVATE_UPSTREAMS=false # 多个主机使用英文逗号分隔,例如:gateway.internal,192.168.1.10 CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS= -CANVAS_CORS_ORIGINS=http://localhost:3000 +CANVAS_CORS_ORIGINS=http://localhost:7380 # 服务器对模型上游可访问的地址:本地存储的短时签名链接(Agent 看图、生成任务参考图)由它拼出, # 内网部署还要把该主机放进 CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS。留空则本地存储无法签出链接。 CANVAS_PUBLIC_BASE_URL= @@ -34,4 +34,5 @@ CANVAS_UPDATER_SOCKET_DIR=/run/open-ai-canvas-updater # REDIS_URL=redis://redis:6379/0 # CANVAS_POSTGRES_DATA_PATH=./postgres-data # CANVAS_REDIS_DATA_PATH=./redis-data -# CANVAS_HTTP_PORT=3000 +# 宿主机访问端口;本项目使用 7380 以避免与同机其它工程(默认 3000)冲突 +# CANVAS_HTTP_PORT=7380 diff --git a/AGENTS.md b/AGENTS.md index 2cce78452..6dbf0d0bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -138,3 +138,73 @@ - Git 提交说明使用 `(): <业务模块> - <变更摘要>`,`type` 为 `feat|fix|refactor|perf|docs|test|build|ci|chore|revert`。 交付前至少检查:改动是否聚焦、调用方和类型是否同步、错误/权限/数据归属是否完整、必要文档是否同步、验证是否如实说明、是否留下密钥或本地数据。 + +--- + +# 本项目附加约定 + +以上第 1–9 章继承自上游 `ddcat-ai/open-ai-canvas`。以下为本仓库(`ttstd-infinite-canvas`)在底座之上叠加的约定;**与上游冲突时以本章为准**。 + +## A. 底座与上游同步 + +本仓库以 `ddcat-ai/open-ai-canvas` 为底座,通过 Git remote 保持可追溯的上游关系: + +- `upstream` → `https://github.com/ddcat-ai/open-ai-canvas.git`(开发机上另存有一份克隆,可作离线 fetch 源)。 +- 同步新功能:`git fetch upstream && git merge upstream/main`。冲突优先保留上游实现,本仓库的差异应集中在 B~D 三项。 +- **功能一律优先使用上游源码实现**,不要在本仓库另起一套。确需偏离上游时,先在本章登记原因再改代码。 +- `libtv.html` 与 `libtv-前端技术分析.md` 是竞品运行时快照与分析资料,属于本地参考,不要当作上游代码改动。 +- 分支:本仓库工作分支为 `master`,上游为 `main`;`git pull upstream` 后按需合并。 + +## B. 端口约定 + +**不要改回上游默认的 3000 / 8080**——同机上 `infinite-canvas`、`open-ai-canvas`、`open-canvas` 都占用 3000,会互相抢占。 + +| 场景 | 前端 | 后端 | +| --- | --- | --- | +| 本地开发(宿主机) | **7380** | **7381** | +| Docker(宿主机映射) | **7380** → 容器 3000 | 不暴露(容器内仍 8080) | + +容器内部端口保持上游原样(nginx `listen 3000`、后端 `:8080`),以减小与上游的差异面。改端口时必须同步: + +- `web/package.json` — `dev` / `start` 的 `--port` +- `web/vite.config.ts` — `apiProxyTarget` 默认值 +- `backend/cmd/server/main.go` — `CANVAS_BACKEND_ADDR` 默认值 +- `scripts/start-local.ps1` — 后端地址、代理目标、提示文案 +- `docker-compose.yml`、`docker-compose.local.yml` — 宿主机端口映射与 `CANVAS_CORS_ORIGINS` +- `.env.example` — `CANVAS_CORS_ORIGINS`、`CANVAS_HTTP_PORT` +- `README.md` 快速开始 + +`docker-compose.deploy.yml` / `docker-compose.server.yml` 与 `scripts/install-server*.sh` 面向独立服务器部署,保留上游的 3000 默认值,不做本地化改动。 + +`scripts/launch-canvas.ps1` 是上游维护者的个人脚本(硬编码 `D:\yingce\...`、`Administrator`、cloudflared 隧道),在本机不可用,**保持原样不要改**。 + +## C. 许可与商标红线 + +- 本仓库与上游均为 **MIT**。复制上游代码必须保留其版权声明与 `NOTICE`(声明 fork 自 `basketikun/infinite-canvas`,上游作者权利保留)。 +- **`tldraw` 许可非 MIT**:生产环境商用需购买 license key,否则必须保留 "Made with tldraw" 水印。上游已预留 `VITE_TLDRAW_LICENSE_KEY`(构建期)与 `tldraw_license_key`(运行时设置),对外商用前必须就此明确决策。 +- **不得新增他人 logo / 商标**:`web/public/icons/` 下的模型厂商图标、根 README 的赞助商与社群素材仅在上游语境下存在,对外分发前必须清理或替换为纯文字。 +- **禁止把带 GPL 组件的 ffmpeg 打进分发镜像**:服务端 ffmpeg 使用 LGPL 构建或由用户挂载(`CANVAS_FFMPEG_PATH`)。 +- 面向国内公众提供服务时,需处理 AI 生成内容标识、生成式 AI 服务备案与深度合成管理要求。 + +## D. 进程清理(强制) + +验收或调试结束后清理进程时,**只允许按精确 PID 操作**,严禁用命令名、路径片段或宽正则批量匹配后终止。 + +正确做法: + +1. `netstat -ano | Select-String 'LISTENING' | Select-String ':<端口>'` 拿到占用端口的 PID; +2. `Get-Process -Id ` 确认进程名与可执行文件路径确属本项目; +3. `Stop-Process -Id -Force`;需要连带子进程时用 `taskkill /PID /T /F`。 + +禁止的做法(已有实际事故): + +- ❌ `Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'server\.exe' } | Stop-Process` + 曾匹配到 IDE 的 `embeddings-server.exe`,导致误杀开发环境进程。 +- ❌ `-match 'vite'` / `-match 'node'` / `-match 'concurrently'` / `-match 'go'` + 这些字符串同样会命中编辑器本体、语言服务器,以及同机上其它工程。 + +补充要求: + +- 需要批量清理时,**先只打印**匹配结果(PID、进程名、完整命令行)供人工确认,确认后另起一步执行终止;不要在同一条命令里「查完即杀」。 +- 无法确认归属的进程一律不动,交回用户处理。同机存在多个同类工程时尤其要区分(例如 `infinite-canvas/web/.../vite.js` 与本项目的 `vite.js`)。 +- 误杀非本项目进程时,必须立即如实告知用户,说明进程名、可能影响与恢复方式,不要轻描淡写。 diff --git a/README.md b/README.md index c2a7a138e..b15d3c0d6 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ mkdir -p .local/project-workbench-debug .local/cache/go-build .local/cache/go-mo # 终端一:后端 cd backend -CANVAS_BACKEND_ADDR=127.0.0.1:8080 \ +CANVAS_BACKEND_ADDR=127.0.0.1:7381 \ CANVAS_BACKEND_DATA_DIR=../.local/project-workbench-debug \ GOCACHE=../.local/cache/go-build \ GOMODCACHE=../.local/cache/go-mod \ @@ -65,7 +65,9 @@ bun install --frozen-lockfile bun run dev ``` -打开 。首次使用时注册管理员账号,并在设置中配置模型渠道。前端默认将 `/api` 代理到 `http://127.0.0.1:8080`;如需修改代理目标,可设置 `VITE_API_PROXY_TARGET`。 +打开 。首次使用时注册管理员账号,并在设置中配置模型渠道。前端默认将 `/api` 代理到 `http://127.0.0.1:7381`;如需修改代理目标,可设置 `VITE_API_PROXY_TARGET`。 + +> **本项目端口约定**:本地开发使用前端 **7380** / 后端 **7381**,Docker 宿主机端口为 **7380**(容器内仍为上游默认的 3000/8080)。这样可与同机上的 `infinite-canvas`、`open-ai-canvas` 等工程并存。详见 [AGENTS.md](./AGENTS.md) 的「本项目附加约定」。 Windows PowerShell 用户可在仓库根目录执行: diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index d6f6e2c6e..62380516e 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -67,7 +67,8 @@ func run(ctx context.Context) error { } repo := repository.New(db) - addr := env("CANVAS_BACKEND_ADDR", ":8080") + // 本项目本地开发端口 7381,避开上游默认的 8080(见 AGENTS.md「本项目附加约定」)。 + addr := env("CANVAS_BACKEND_ADDR", ":7381") svc := service.New(repo, dataDir) if updaterToken := strings.TrimSpace(os.Getenv("CANVAS_UPDATER_TOKEN")); updaterToken != "" { svc.ConfigureUpdateManager(updaterclient.New(env("CANVAS_UPDATER_SOCKET", "/run/open-ai-canvas-updater/updater.sock"), updaterToken)) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index fcf16dc56..86ef0a539 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -25,7 +25,7 @@ services: CANVAS_REGISTRATION_ENABLED: ${CANVAS_REGISTRATION_ENABLED:-false} CANVAS_ALLOW_PRIVATE_UPSTREAMS: ${CANVAS_ALLOW_PRIVATE_UPSTREAMS:-false} CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS: ${CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS:-} - CANVAS_CORS_ORIGINS: ${CANVAS_CORS_ORIGINS:-http://localhost:3000} + CANVAS_CORS_ORIGINS: ${CANVAS_CORS_ORIGINS:-http://localhost:7380} # 服务器访问地址:Agent 看图与生成参考图都靠它签发上游可取到的短时链接。 CANVAS_PUBLIC_BASE_URL: ${CANVAS_PUBLIC_BASE_URL:-} GIN_MODE: debug diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 7dd0a5504..75d8402f4 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -18,7 +18,7 @@ services: CANVAS_REGISTRATION_ENABLED: ${CANVAS_REGISTRATION_ENABLED:-false} CANVAS_ALLOW_PRIVATE_UPSTREAMS: ${CANVAS_ALLOW_PRIVATE_UPSTREAMS:-false} CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS: ${CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS:-} - CANVAS_CORS_ORIGINS: ${CANVAS_CORS_ORIGINS:-http://localhost:3000} + CANVAS_CORS_ORIGINS: ${CANVAS_CORS_ORIGINS:-http://localhost:7380} # 服务器访问地址:Agent 看图与生成参考图都靠它签发上游可取到的短时链接。 CANVAS_PUBLIC_BASE_URL: ${CANVAS_PUBLIC_BASE_URL:-} ENABLE_PROVIDER_PLUGINS: ${ENABLE_PROVIDER_PLUGINS:-false} @@ -40,7 +40,7 @@ services: context: . dockerfile: Dockerfile ports: - - "3000:3000" + - "7380:3000" depends_on: backend: condition: service_healthy diff --git a/docker-compose.yml b/docker-compose.yml index 763cc847a..8b0407c08 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: CANVAS_REGISTRATION_ENABLED: ${CANVAS_REGISTRATION_ENABLED:-false} CANVAS_ALLOW_PRIVATE_UPSTREAMS: ${CANVAS_ALLOW_PRIVATE_UPSTREAMS:-false} CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS: ${CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS:-} - CANVAS_CORS_ORIGINS: ${CANVAS_CORS_ORIGINS:-http://localhost:3000} + CANVAS_CORS_ORIGINS: ${CANVAS_CORS_ORIGINS:-http://localhost:7380} # 服务器访问地址:Agent 看图与生成参考图都靠它签发上游可取到的短时链接。 CANVAS_PUBLIC_BASE_URL: ${CANVAS_PUBLIC_BASE_URL:-} GIN_MODE: release @@ -33,7 +33,7 @@ services: context: . dockerfile: Dockerfile ports: - - "3000:3000" + - "7380:3000" depends_on: backend: condition: service_healthy diff --git a/scripts/start-local.ps1 b/scripts/start-local.ps1 index 0eeb4e186..c41ad427a 100644 --- a/scripts/start-local.ps1 +++ b/scripts/start-local.ps1 @@ -70,19 +70,19 @@ $dataDirLiteral = ConvertTo-PowerShellLiteral $dataDir $backendCommand = @" `$ErrorActionPreference = 'Stop' Set-Location -LiteralPath $backendDirLiteral -`$env:CANVAS_BACKEND_ADDR = '127.0.0.1:8080' +`$env:CANVAS_BACKEND_ADDR = '127.0.0.1:7381' `$env:CANVAS_BACKEND_DATA_DIR = $dataDirLiteral `$env:GOCACHE = $(ConvertTo-PowerShellLiteral $goBuildCache) `$env:GOMODCACHE = $(ConvertTo-PowerShellLiteral $goModuleCache) -Write-Host '影策后端:http://127.0.0.1:8080' -ForegroundColor Cyan +Write-Host '影策后端:http://127.0.0.1:7381' -ForegroundColor Cyan go run ./cmd/server "@ $webCommand = @" `$ErrorActionPreference = 'Stop' Set-Location -LiteralPath $webDirLiteral -`$env:VITE_API_PROXY_TARGET = 'http://127.0.0.1:8080' -Write-Host '影策前端:http://localhost:3000' -ForegroundColor Cyan +`$env:VITE_API_PROXY_TARGET = 'http://127.0.0.1:7381' +Write-Host '影策前端:http://localhost:7380' -ForegroundColor Cyan bun run dev "@ diff --git a/web/package.json b/web/package.json index 992ac4ec4..2536f3057 100644 --- a/web/package.json +++ b/web/package.json @@ -4,14 +4,14 @@ "private": true, "type": "module", "scripts": { - "dev": "vite --host 0.0.0.0 --port 3000", + "dev": "vite --host 0.0.0.0 --port 7380", "build": "tsc --noEmit && vite build", "pretest": "bun test test/tiptap-dependency-versions.test.ts test/admin-ui-regressions.test.ts test/announcement-prompt-regressions.test.ts test/password-reset.test.ts test/oss-settings.test.ts test/channel-model-editor-form.test.ts test/channel-model-price-tier-form.test.ts test/admin-logical-models.test.ts && bun test test/canvas-grid-viewport.test.tsx", "typecheck": "tsc --noEmit", "test:canvas": "bun test test/canvas-media-performance.test.ts test/canvas-node-title-interaction.test.ts test/canvas-spatial-index.test.ts", "test": "bun test", "test:director:e2e": "bun scripts/director-p0-chrome-e2e.mjs", - "start": "vite preview --host 0.0.0.0 --port 3000", + "start": "vite preview --host 0.0.0.0 --port 7380", "format": "prettier --write .", "format:check": "prettier --check .", "lint": "eslint src test" diff --git a/web/vite.config.ts b/web/vite.config.ts index 3824d4657..fd9d1c632 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -7,7 +7,9 @@ import { defineConfig } from "vite"; const webDir = dirname(fileURLToPath(import.meta.url)); const appVersion = process.env.CANVAS_BUILD_VERSION?.trim() || readFileSync(resolve(webDir, "../VERSION"), "utf8").trim(); const appChangelog = readFileSync(resolve(webDir, "../CHANGELOG.md"), "utf8"); -const apiProxyTarget = process.env.VITE_API_PROXY_TARGET?.trim() || "http://127.0.0.1:8080"; +// 本项目本地开发端口:前端 7380、后端 7381(见 AGENTS.md「本项目附加约定」)。 +// 避开上游默认的 3000 / 8080,以便与 infinite-canvas、open-ai-canvas 等同机工程并存。 +const apiProxyTarget = process.env.VITE_API_PROXY_TARGET?.trim() || "http://127.0.0.1:7381"; export default defineConfig({ plugins: [react()], From c8cde54c41337f818f244ff564dc9d559d7dd035 Mon Sep 17 00:00:00 2001 From: TongTongStudio Date: Tue, 22 Sep 2026 13:17:56 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat(canvas):=20=E6=96=B0=E5=A2=9E=E7=94=BB?= =?UTF-8?q?=E5=B8=83=E8=AE=BE=E7=BD=AE=E5=85=A5=E5=8F=A3=E3=80=81=E5=8C=BA?= =?UTF-8?q?=E5=9F=9F=E9=80=89=E6=8B=A9=E6=BB=9A=E8=BD=AE=E5=B9=B3=E7=A7=BB?= =?UTF-8?q?=E4=B8=8E=E6=8F=90=E4=BA=A4=E6=B5=81=E7=A8=8B=E5=BF=AB=E6=8D=B7?= =?UTF-8?q?=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/content/docs/progress/pending-test.mdx | 4 ++++ .../canvas/canvas-node-prompt-panel.tsx | 1 + .../canvas/canvas-workspace-overlays.tsx | 16 +++++++++++++++- web/src/components/canvas/infinite-canvas.tsx | 14 +++++++++++++- web/src/lib/canvas/canvas-shortcuts.ts | 4 ++-- web/src/pages/canvas/canvas-project-top-bar.tsx | 6 +++++- 6 files changed, 40 insertions(+), 5 deletions(-) diff --git a/docs/content/docs/progress/pending-test.mdx b/docs/content/docs/progress/pending-test.mdx index f4c5c98a6..abc6c993b 100644 --- a/docs/content/docs/progress/pending-test.mdx +++ b/docs/content/docs/progress/pending-test.mdx @@ -2589,3 +2589,7 @@ SSE 现在按 revision 变化发送紧凑快照,无变化只 heartbeat;客 入口精简与工具草稿复测:移除左侧工具菜单及图片节点提示词模板入口,保留图片风格 / 预设和其他节点模板入口。工具引用列表读取草稿改用空值判断,明确清空时不再从原始生成提示词回读旧标签。风格和预设的选择、替换、连续编辑、标签移除、全文清空及保存重载覆盖有图/空节点两种情况;5 个专项文件共 58 项通过,TypeScript 类型检查通过。实际页面仍需确认普通/放大编辑器均无图片提示词模板入口,以及风格和预设清空后重新聚焦不回填。 主分支提交前复测:前端 `bun run test` 全量 1925 项通过,生产构建、类型检查、UI 退场 lint 通过;构建仍有大分块提示。同步了技能/工具标签结构与多行连接菜单的源码守护断言。后端 `go test ./...` 全量通过,工具、迁移、资源引用、handler 及工具预览权限/项目封面专项通过。本轮未执行真实模型调用、PostgreSQL 实库迁移或登录态浏览器验收,这些项目继续保留待验收。 + +画布页本轮改动:区域选择(底部工具栏胶囊切到「区域选择」)下滚轮改为上下平移画布,缩放保留给 Command/Ctrl + 滚轮(含触控板捏合);带 `data-canvas-wheel-scroll` 的节点内部区域仍保持自身滚动。顶栏分享按钮右侧新增「账户与设置」按钮,点击复用画布内「去设置」的 `navigateToSettings({ section: "channels", continueCreation: true })`,进入 `/settings?section=channels&continue=1`(顶部有「返回创作 / 保存并返回」,与模型缺失时的跳转一致,不新增全局配置弹窗)。曾尝试在画布内用抽屉内嵌设置页,因嵌套 Router 报错已移除。类型检查通过;待登录态浏览器验收:区域选择的滚轮方向与实际滚动手感,设置按钮跳转后返回画布时画布内容与滚动位置是否保持,以及设置项保存后画布生成默认值立即生效。 + +节点面板快捷键:`[data-canvas-node-panel]` 面板内任意位置按 ⌘/Ctrl + Enter 触发面板主按钮(目前只有创作面板的生成按钮带 `data-canvas-panel-submit`),生成中或提示词为空时按钮禁用,快捷键不拦截按键。待验收:文本框内按下不插入换行、输入法组合中不触发、面板内其他输入框与配置面板不被误触发。 diff --git a/web/src/components/canvas/canvas-node-prompt-panel.tsx b/web/src/components/canvas/canvas-node-prompt-panel.tsx index 8e628c197..d634074d0 100644 --- a/web/src/components/canvas/canvas-node-prompt-panel.tsx +++ b/web/src/components/canvas/canvas-node-prompt-panel.tsx @@ -387,6 +387,7 @@ export function CanvasNodePromptPanel({ projectId, node, isRunning, onPromptChan