啡跃 Endoleap

本地 Next.js App Router + TypeScript + Tailwind CSS + shadcn/ui 项目。首页采用留白、黑色主按钮、衬线标题与线稿画布预览;账户页保留原有布局。工作台使用合并后的单侧栏。

项目目录:/Users/zzyang/endoleap。

启动

npm ci
npm run dev

默认仅监听本机 localhost:17321。生产模式:npm run build && npm start。

  • 首页:/(未登录显示 landing page;有效登录会话跳转 /dashboard)
  • 登录、注册:/login
  • 登录回调:/auth/callback
  • 真实工作台:/dashboard
  • 本地演示:/dashboard?demo=1
  • 无限图谱:/dashboard/{workspace}/{board}

路由结构

  • src/app/layout.tsx:根布局、中文语言标记、全局样式与页面元信息。
  • src/app/page.tsx:首页。
  • src/app/login/page.tsx、src/app/auth/callback/page.tsx:账户页与登录回调。
  • src/app/dashboard/layout.tsx:共享单侧栏;page.tsx 保留空白工作区。
  • src/app/dashboard/dashboard.tsx:侧栏收起/展开、页面搜索、新建图谱与底部账户弹出框。
  • src/app/dashboard/[workspace]/[board]/page.tsx:按 workspace 和图谱 ID 打开页面,重新读取后端资料。
  • src/components/ui/icon-button.tsx:Lucide 图标按钮,图标为 16×16px,上下 4px、左右 6px 内边距,总尺寸为 28×24px。
  • src/app/api/**/route.ts:Foundry 代理与 OAuth 跳转,使用 App Router Route Handlers。

配置

复制 .env.example 为 .env.local,按实际部署填写:

  • FOUNDRY_API_ORIGIN:仅供 Next 服务端请求后端,默认 https://api.foundry.entroia.com。
  • NEXT_PUBLIC_FOUNDRY_PROJECT:项目标识,默认 endoleap。

Slack Client Secret、Signing Secret、Verification Token、用户令牌与 app token 均不需要放入此前端。浏览器调用同源 Next API,由服务端转发到 Foundry。Foundry 会话保存在 HttpOnly / SameSite=Lax Cookie,生产 HTTPS 反向代理需传 X-Forwarded-Proto=https。不要将原始会话写入客户端存储。

Google / Slack 的提供方回调仍是 Foundry 后端地址;后端的项目回调映射必须指向本前端 /auth/callback。协议、主机名、端口必须与启动第三方登录时一致,否则 sessionStorage 中的 verifier 无法恢复。正式账号授权、邮件验证与 OAuth 全流程尚未完成真人验收;HTTP 302 跳转成功不能代表登录成功。

当前功能与范围

  • 响应式首页、锚点导航与 FAQ。
  • 邮箱注册 / 登录 / 邮件验证兑换,Google 和 Slack 的 PKCE 登录入口及回调处理。
  • 真实用户资料、空间列表与创建、画布列表与创建。资料和空间并行读取;首次创建画布时自动建立个人空间。
  • 单侧栏第一行提供完全收起/恢复与新建页面,第二行为当前空间页面列表搜索。画布列表包含可展开的父子画布目录,搜索保留祖先路径,直接打开子图时自动展开所在目录。侧栏使用 Motion for React 14.9.1 实现 240ms 开关动画,尊重系统减少动态效果偏好,收起后保留左上角快捷浮框。
  • 账户菜单分为工作空间概览、账户设置、用户资料与空间切换、新建工作空间、退出登录;账户设置和新建空间均接入 Foundry。菜单文字和行距参考 Notion 收紧,工作空间分组标题保留浅灰色。
  • 底部按钮、账户菜单和设置面板使用 Foundry profile.avatar_url 提供的 Google 头像;无头像或图片加载失败时显示姓名首字。
  • 新建页面使用 Foundry Core 的 POST /workspaces/{workspace}/materials,提交 kind: board、默认名称“未命名图谱”和内容版本;由后端生成 ID。没有空间时先创建“个人空间”,成功后跳转到图谱的独立地址。
  • 本地演示提供示例图谱、搜索、新建和浏览器保存,支持图谱独立地址与刷新。
  • 图谱页面使用 React Flow 12.12.1,支持空白处平移、节点拖动、连线、多选、缩放和文本编辑。文字、脚本、图片节点和创作模板可从底部悬浮工具栏插入。
  • 右上角悬浮框提供撤销、重做、缩放、恢复 100%、适应画布和 JSON 导出;底部支持 JSON 导入及操作帮助。
  • 真实图谱先读取 Core Canvas 快照,再开放编辑;节点、样式、连线与视图以独立版本串行自动保存。旧本地草稿需用户明确选择恢复;版本冲突停止覆盖并保留恢复记录。演示模式仅本地保存。支持 Core Canvas V1/V2;V2 分区是真实子画布,内部内容按所属画布独立保存。
  • 分享、Agent、收藏、自由绘图及多人协同尚未接入。画布覆盖整个视口,侧栏和快捷浮框覆盖其上;演示数据不写入线上。
  • 收起侧栏后,左上角浮框显示 Logo 和用户头像;头像与展开侧栏底部的账户按钮打开同一个菜单。
  • 本地演示仅用于查看界面,不提供后端认证与权限。

Figma 原始 Logo、人物插画与图标已下载到 public/assets,不依赖临时 Figma URL。品牌保持啡跃 Endoleap;shadcn Button / Dialog 使用官方 registry 源码。依赖版本固定,package-lock.json 可用于 npm ci。

验证

npm run build
node tests/api-smoke.mjs

API 测试使用本机临时后端和独立前端进程,检查会话 Cookie、令牌不泄漏、同源校验和路径白名单,不访问线上账号。构建需先完成。冒烟测试还覆盖所有页面地址、元信息、OAuth 跳转、请求体限制及退出会话。此前的浏览器验收覆盖登录/注册切换、演示画布创建、搜索空状态、笔记保存、桌面与 390px 手机布局;本次迁移的验证结果见 VALIDATION.md。

若执行环境限制 Turbopack 的 CSS 工作进程绑定本地端口,可使用官方 Webpack 模式:npm run build -- --webpack。

项目已同步至 ssh://git@gitea.entroia.com:22222/Zayden-Jung/Endoleap.git;尚未部署。

性能分析、实测耗时和修复说明见 PERFORMANCE.md。 流程回归可在支持 TypeScript 类型擦除的 Node 版本执行:node --experimental-strip-types --test tests/workspace.test.mjs。

品牌资源

public/brand/ 原样复用旧 PROMPT 仓库 Zayden-Jung/probably-rendering-one-more-pixel-tonight 的 packages/brand/assets/,来源提交 47467874841ea7f7b4b362d4ec0e66f236c6a7d6。

共 88 个原始资源:6 套 PNG(logo/icon/mono 的 light 与 dark 版本,每套 16、24、32、48、64、128、180、192、256、512、1024、2048px),8 个 SVG,以及 8 个应用图标文件(含 3 种 ICO、Apple Touch 和 PWA PNG)。所有文件与来源 Git blob 哈希一致。

收起侧栏后的浮框使用 Logo 返回 /;展开侧栏后仍保留新建页面按钮。首页与账户页品牌组件使用原始 SVG,根布局声明 ICO、深浅色 PNG favicon 和 Apple Touch 图标。

画布操作

  • 单击选择,双击编辑文本或卡片名称。纯文本与图片可整块拖动;选中时四侧连接点可拖线。
  • 触控板滚动平移,Ctrl / ⌘ 加滚动或捏合缩放。V 选择,H 抓手,空格临时平移,Shift 框选。
  • 上方编辑栏提供文字样式、旋转和八向拉伸模式。旋转时 Shift 吸附 15°;八向拉伸时 Shift 固定比例,Esc 退出。图标保留等比缩放预设。
  • 连线可单独选择:颜色、粗细、透明度、实/虚/点线、箭头与箭尾形状/大小/填充均可设置。双击线或点击添加按钮插入曲线点,拖动锚点和控制柄制作多段贝塞尔链;支持平滑、镜像和独立控制柄。
  • 连线动画可关闭、始终播放或仅执行时播放,包含沿完整曲线的流光、流动虚线和脉冲;方向、颜色和时长可设。执行动画由真实任务边事件驱动。
  • 底部 AI 模式可保持开启;选择文本/图片/卡片后,上方为编辑栏,下方为固定屏幕尺寸的 AI 面板。模型从后端目录读取,支持原地生成、取消及可选下游传播;冲突结果保留供明确应用。
  • 新选框通过服务端事务创建并关联真实子画布。标签用三个图标切换隐藏/固定字号/随缩放,悬停查看解释。选框可收起、展开及调整概览比例,子图真实内容尺寸不变。
  • 展开内容使用子画布投影,编辑保存到实际 owner;不会在父图复制一份。拖入/移出使用多画布事务;新跨图连线按后端能力通过 connections 命令创建端口链。删除选框解散并保留内容。
  • 原地生成与用户拖动可合并;内容冲突保留本地草稿。网络结果不确定时暂停编辑,用相同幂等请求重试或重新读取。每个画布有独立版本与串行保存器。
  • 图标面板支持 2313 个 Lucide 和 24 个 Tabler 图标试用、分类/搜索、虚拟列表及本地图形分片加载。未打开面板不加载目录。
  • 有子图时导出完整 owner 文档集合;关系型子图备份不直接覆盖导入到其他画布。历史 V1/关系不兼容草稿可导出,不能覆盖 V2 子图关系。

Canvas V2 接口与检查

后端基线由用户交付的部署 b4e537b、CI #912/#913 与指定测试画布迁移摘要确认。跨图 connections 扩展以线上 capabilities.subgraphs.connections 为准;客户端先检查能力,再发事务。前端未重复运行迁移或调用线上付费模型。

同源代理转发 V2 Accept、If-Match、Idempotency-Key 和事件分页查询,保留 ETag/schema/Retry-After;受管图片通过带会话的二进制代理读取。快照上限 5 MiB,图片上传支持受限 multipart;服务令牌不会发给浏览器。

验证命令:

npm run typecheck
node --experimental-strip-types --test tests/*.test.mjs
CANVAS_UI_TOOLS=/path/to/isolated-tools node tests/canvas-ui-check.mjs

DOM 工具仅需在临时目录安装 esbuild/jsdom,见测试文件开头;不启动浏览器。生产构建与 tests/api-smoke.mjs 使用隔离副本和临时本地服务,避免影响现有开发服务器。实际结果及限制见 VALIDATION.md。

Headless Canvas API 的完整方案保留在本地忽略的 docs/HEADLESS_CANVAS_API_DRAFT.md。当前工作分支已接入纯 core/runtime、ledger/queue、IndexedDB 恢复、渐进导入/深复制、canonical Foundry 协议和私有 ESM 包预览;使用方式见 headless/README.md,尚未发布 npm registry,也不代表完整产品验收或后端部署完成。

Workspace AssetDepot 支持重新授权后的跨项目采用、持久绑定与共享字节。浏览器数据库 v4 将已确认上传的项目文件转换为共享字节指针,保留原 File MIME 和逻辑读取接口;绑定使用恢复格式13。共享回收要求所有项目的持久引用清单完整且与 Root 同步;关闭项目仍受保护。源文件转换、字节、引用清单和 Root 在同一个事务中保存,未确认上传继续保留原 Blob。

应用提示使用 Sonner,图标说明使用 Radix Tooltip;根布局统一安装 provider。画布通用操作错误与归属完成使用固定 ID 通知,保存冲突、失败上传和重试操作仍保留在相关内容处。消息和浮层依赖仅属于应用 UI,不作为未来 headless core 的依赖。

会话期限

会话为可撤销的随机令牌。30 天闲置续期、90 天绝对期限由 Core 控制,代理按剩余期限更新 HttpOnly Cookie;既有过期会话不恢复。详细本地说明为 docs/SESSION_LIFETIME.md。

图片附件与加载

真实画布新上传的图片存入 Foundry 附件 API,快照只保存原始附件引用,避免 Base64 随每次打开和保存传输。原图保留;画布按需读取单一最长边 1024px 的预览,放大后可读取原图。预览权限与原图一致,Core 的配套预览功能已随 PR #15 合并并部署(b2ae48e);小图或 GIF 可能直接返回原始文件。

指定测试画布已完成一次性旧图迁移,原图、节点与连线的哈希核对一致;临时迁移入口及执行逻辑已移除。打开页面不会自动迁移或 PATCH。只改变视角保存在本机;真实内容修改才自动保存到服务器。

跨分区拖放同步

拖放后元素保持落位,旁边显示局部同步状态;其他元素仍可编辑和拖动。移动中的节点及分区几何暂时受保护,归属事务排队使用最新版本。期间的本地编辑在响应到达后合并,再自动保存。响应丢失时重试复用同一请求和幂等键;明确版本冲突则先重读版本,避免重复移动或覆盖其他编辑。

Core subgraphs.member_positions 能力启用时,最终落点(源画布局部坐标)与归属通过一次 members POST 原子保存,单纯拖放不再先 PATCH 坐标。旧后端仍使用先保存坐标再迁移的兼容流程,但不显示整屏等待遮罩。Core 配套 PR #16 已合并并部署为 2207c85;刷新页面后会重新检测并启用新接口能力。

上传图片时立即在画布显示本地预览,并在后台上传原始文件;上传成功才把稳定附件引用加入快照。服务端预览加载完成前继续显示本地图片,避免网络等待期间空白。失败保留文件供重试/取消,重试沿用同一幂等键;本地 blob URL 不保存到服务端。

上传中的图片也是正常本地节点,可立即选中、拖动、缩放、改名、删除和撤销;上传完成只补齐附件引用,不重置节点。上传前改变分区归属会先在本地完成,文件准备好后保存到当时的实际所属画布。临时上传标记和 blob 地址由传输层排除,不发送给 Core;文件仍未可用时,依赖服务端文件的 AI 生成需等待上传完成。

配置与生成数据阅读说明

本节为不适合内嵌注释的配置和严格 JSON 提供旁注;实际值仍以对应文件为准。源码、脚本和测试中的函数说明保留在各自文件内。包版本、TypeScript 输出目标、服务协议版本、文档格式版本和组件数据版本是不同概念。

应用包与执行入口

根 package.json 的 private: true 防止常规 npm 发布,version: 0.1.0 是应用包版本。dependencies 是应用运行/构建引用的库,devDependencies 包含编译器、类型声明和 Tailwind 工具;它们的精确版本及传递依赖由 package-lock.json 锁定。lock 文件由包管理器维护,不在其中手写说明或改某个嵌套版本。

scripts 项 执行内容与边界
dev 在 localhost:17321 启动 Next 开发服务器,会按需编译。
build / start 前者执行 Next 生产构建,后者在 localhost:17321 运行已有构建。
typecheck tsc --noEmit 检查类型,不生成应用 JS。
test:headless 运行 tests/headless/*.test.mjs;不自动执行所有 .integration.mjs 或浏览器场景。
test:headless:browser 浏览器存储、Worker 等适配器场景入口,环境装配见 browser-storage.mjs。
test:headless:proxy Foundry 同源代理的隔离集成入口,启动与替身链路见 proxy.integration.mjs。
test:headless:renderer / test:headless:performance 运行页面集成入口;后者附加 --performance 选择性能场景。
build:headless 清理并重建 .headless-dist 的 ESM、声明和 Worker 引用,再复制包清单/文档。
test:headless:package 构建、打包到隔离目录并运行消费方及浏览器验收,不是单纯语法检查。

集成入口可能编译或启动本地进程,具体动作见各脚本模块注释;本节列出命令不表示这些检查已经运行或通过。

应用 TypeScript 配置

tsconfig.json 面向应用检查;以下选项共同作用,并非运行时开关:

配置项 用途
target: ES2020 / lib 声明语言输出目标和可用类型库;dom、dom.iterable、esnext 提供 API 类型,不会安装浏览器 polyfill。
allowJs / skipLibCheck 允许 JS 进入项目;跳过声明文件内部的完整检查。它们不关闭项目 TS 源码的检查。
strict / noEmit 启用严格类型检查,默认只检查而不生成 JS/声明。
allowImportingTsExtensions 允许源码相对导入使用 .ts 等 TS 扩展名;独立包输出由另一配置重写扩展名。
esModuleInterop 为 CommonJS/ESM 互操作提供 TypeScript 的兼容处理,不把整个项目改成 CommonJS。
module: esnext / moduleResolution: bundler 使用 ESM 语法并按 bundler 模式解析依赖;应用最终交由 Next 构建链处理。
resolveJsonModule 允许 TS 引用并推断 JSON 模块,例如图标 manifest。
isolatedModules / jsx: react-jsx 按单文件转换约束检查源码,并使用 React 自动 JSX 转换形式。
incremental 允许 TypeScript 使用增量检查缓存;生成的 *.tsbuildinfo 被忽略。
paths @/* 在 TS 解析时指向 src/*,这不是把字符串路径写进业务文档。
plugins 登记 Next TypeScript 插件供支持它的工具使用。
include / exclude 纳入应用 TS/TSX、next-env.d.ts 与 Next 生成类型入口;排除依赖和独立包输出目录。由这些源码导入的依赖仍可参与类型解析。

next-env.d.ts 是 Next 维护的类型入口,引用框架/图片和生成路由类型。其路径会随开发/构建环境变化,不在这个生成文件中追加维护注释。

独立 headless 包

tsconfig.headless.json 继承应用配置,显式覆盖输出部分:target/module 为 ES2022,rootDir 为 src/lib,outDir 为 .headless-dist/modules;noEmit: false 与 declaration: true 生成 JS 和 .d.ts。incremental: false 不使用这次编译的增量缓存,rewriteRelativeImportExtensions 将可重写的相对 TS 导入转成输出扩展名。types: [] 和 plugins: [] 清空自动纳入的类型包列表与继承插件条目,不禁止源码显式导入类型;未覆盖的 lib 等项仍继承。四组 include 从 core/runtime/service/bindings 的 .ts 入口收集源码,导入关系还会带入所需依赖。

headless/package.json 是复制到产物根的包模板,源目录本身没有生成后的 modules。它的 type: module 声明 ESM,Node 引擎要求为 >=22。exports 为每个公开入口分别指定类型声明和 ESM 文件:根及 /core 指向 core,另有 /runtime、/browser、/service、/bindings、/react-flow 与 /react。这些是产物目录中的路径,不是指向 src 的别名,也没有声明 CommonJS require 入口。

files 限定包收录的模块、消费示例和验收文档。React、React DOM、React Flow 以带版本范围的可选 peer 声明,供选择相关适配入口的宿主提供;“可选”不表示调用 UI 适配器时可以缺少它依赖的框架。便携入口的使用和依赖边界见 headless/README.md。private: true 保持预览包私有,不代表已经发布到 registry。

组件生成器配置

components.json 是 UI 组件生成器读取的配置,不是应用运行时组件 registry。$schema 指向配置的 schema;style: new-york、tsx: true 和 rsc: false 记录生成风格、TSX 和 RSC 选项,不改变整个 App Router 的路由/渲染模式。tailwind.css 指向 src/styles/globals.css,空 tailwind.config 没有指定独立配置路径,baseColor: neutral 与 cssVariables: true 是生成器的配色约定。aliases 指示生成文件的组件、工具、UI、lib 和 hooks 导入位置;其中 @/hooks 不保证已经存在对应目录。

图标生成数据

src/components/canvas/icon-catalog-data.json 是检索目录数组,每项包含 name(持久引用的图标 ID)、slug、source、label、categories、common 和 keywords;不包含 SVG 路径。icon-catalog.ts 用它构建名称 Map 并执行筛选,选择器的虚拟窗口另由 UI 控制。

src/lib/canvas-icon-assets.json 保存 root URL 前缀与 assets 桶名到摘要文件名的映射。iconAsset() 对 Lucide 名称做 31 进制滚动散列并模 96,Tabler 名称统一使用 tabler 桶。public/canvas-icons/<版本目录>/*.json 每个分片以图标名索引 [tag, props][],消费端 validateIconShard() 再检查支持的图形与属性。

这三类数据由 scripts/generate-canvas-icons.mjs 同批生成,中文标注来自 scripts/canvas-icon-labels.mjs。文件名摘要是紧凑 JSON 内容的 SHA-256 前 12 位,写盘时另加换行;它与分桶散列用途不同。分片变更须同步目录/manifest 并更新摘要名;脚本不会自动删除旧分片。不要在 JSON 中插入注释或只手改其中一份,否则会破坏读取、引用或缓存命名约定。

协议样例与忽略规则

tests/headless/fixtures/foundry-model-operation-v2.json 是服务适配器单测的完整请求样例。协议 2 的 request_id 标识冻结请求,expected_versions 是参与 canvas 的已确认版本,operations[].id 是逻辑操作 ID。样例把第一代元素 a 的 data.title 从 a 改为 renamed;beforePresent/afterPresent 区分字段缺失与有值。字段旁注与断言在 tests/headless/service.test.mjs,静态样例不代表真实服务已执行。

.gitignore 排除依赖、Next/headless 产物、环境变量、增量缓存和系统文件;仅放行 .env.example 作为模板。根 /docs/ 当前作为本地研究目录忽略。忽略规则只影响未跟踪文件,不会把已经跟踪的文件自动移出 Git。.env.example 的公开项目标识与服务端源站说明写在模板内;实际凭据不应写进示例或公开构建变量。

S
Description
No description provided
Readme 15 MiB
Languages
TypeScript 49.1%
JavaScript 48.4%
CSS 2.5%