Compare commits
5 Commits
5cc4f96b46
...
a9eaaa6023
| Author | SHA1 | Date | |
|---|---|---|---|
| a9eaaa6023 | |||
| 4929ca496b | |||
| 9d5a46129e | |||
| 305a9c7dc3 | |||
| a3328b8617 |
47
.trae/documents/brand-naming.md
Normal file
47
.trae/documents/brand-naming.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Structrail 品牌命名解读
|
||||||
|
|
||||||
|
## 1. 命名构成
|
||||||
|
|
||||||
|
- **项目名称**:Structrail
|
||||||
|
- **构词方式**:`Struct` + `t` + `rail` (共享字母 `t` 的混成词)
|
||||||
|
- **核心双关**:
|
||||||
|
- Struct **Trail** (数据轨迹)
|
||||||
|
- Struct **Rail** (结构轨道)
|
||||||
|
|
||||||
|
## 2. 核心释义:Struct Trail (数据轨迹)
|
||||||
|
|
||||||
|
这是最贴切且最具动态感的解释,直接映射了项目的核心价值——**可视化数据结构的演变过程**。
|
||||||
|
|
||||||
|
* **与 Tracer (跟踪器) 的呼应**:
|
||||||
|
* 核心组件 `Tracer` 的作用是记录数据结构的变化。
|
||||||
|
* 每一次变化都在时间维度上留下了一个 `Trail` (痕迹/足迹)。
|
||||||
|
* 用户通过 `Tracer` 追踪到了数据的 `Trail`。
|
||||||
|
|
||||||
|
* **Time Travel (时间旅行) 的隐喻**:
|
||||||
|
* `Trail` 暗示了一条可以沿着往返的小径。
|
||||||
|
* 支持“上一步”、“下一步”的操作,就像沿着留下的面包屑 (breadcrumbs) 在历史轨迹中穿梭。
|
||||||
|
* 用户可以随时回溯,查看数据在某一时刻的状态。
|
||||||
|
|
||||||
|
* **Debugging (调试) 的本质**:
|
||||||
|
* 算法执行的过程,本质上就是数据状态在时间流中留下的一条长长的轨迹。
|
||||||
|
* 调试就是沿着这条轨迹寻找异常点。
|
||||||
|
|
||||||
|
## 3. 双关释义:Struct Rail (结构轨道)
|
||||||
|
|
||||||
|
作为第二层含义,`Rail` 为项目增添了稳固感和基础设施的韵味。
|
||||||
|
|
||||||
|
* **规范与引导 (On the Rails)**:
|
||||||
|
* `Rail` 暗示了“轨道”,意味着代码在既定的逻辑轨道上运行。
|
||||||
|
* 可视化的目的之一是确保算法逻辑“在正轨上” (Stay on the rails),帮助用户发现何时“脱轨” (Off the rails)。
|
||||||
|
|
||||||
|
* **基础设施感 (Infrastructure)**:
|
||||||
|
* 像 *Ruby on Rails* 一样,`Rail` 给人一种坚实底座的感觉。
|
||||||
|
* 暗示 Structrail 是承载算法运行、支撑可视化功能的坚实平台。
|
||||||
|
|
||||||
|
## 4. 结论与建议
|
||||||
|
|
||||||
|
建议以 **Struct Trail** 作为品牌叙事的核心,强调“追踪”、“轨迹”和“时间旅行”的概念,同时保留 **Struct Rail** 作为一种有趣的双关解读,暗示平台的稳定性和规范性。
|
||||||
|
|
||||||
|
**Slogan 构思:**
|
||||||
|
> **Structrail**: Visualize the **trail** of your data structures.
|
||||||
|
> (Structrail:可视化你数据结构的轨迹。)
|
||||||
51
.trae/documents/commercialization-strategy.md
Normal file
51
.trae/documents/commercialization-strategy.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# StructRail 商业化策略与产品规划
|
||||||
|
|
||||||
|
## 1. 核心愿景
|
||||||
|
打造一个支持主流编程语言、具备“时间旅行”调试能力的算法可视化生态平台。不仅仅是展示算法,更是理解和调试算法的工具。
|
||||||
|
|
||||||
|
## 2. 商业模式:基于运行成本的 Freemium 模型
|
||||||
|
鉴于 Web 平台的技术特性,采用“低成本服务免费引流,高成本服务付费增值”的策略。
|
||||||
|
|
||||||
|
### 2.1 免费层 (Client-side / Web Native)
|
||||||
|
利用浏览器端计算能力(Web Worker / WASM),提供零边际成本的服务。
|
||||||
|
- **支持语言**:JavaScript, TypeScript, 以及未来通过 WASM 支持的 Python/C++ (Web版)。
|
||||||
|
- **运行环境**:用户浏览器本地运行,无服务器计算成本。
|
||||||
|
- **目标用户**:初学者、前端开发者、开源社区贡献者。
|
||||||
|
- **价值**:作为“引流款”,通过开源和免费使用获取最大流量和技术声誉。
|
||||||
|
|
||||||
|
### 2.2 会员/增值层 (Server-side / Cloud Native)
|
||||||
|
利用云端沙箱(Docker/K8s)提供高性能、多语言的运行环境。
|
||||||
|
- **支持语言**:Java, Go, C#, C++ (完整版) 等重型或依赖特定运行时环境的语言。
|
||||||
|
- **运行环境**:云端高性能沙箱,保障安全与隔离。
|
||||||
|
- **服务限制**:
|
||||||
|
- **普通用户**:每日有限次数的云端运行额度(如 10 次/天)。
|
||||||
|
- **会员用户**:无限次云端运行,优先调度高性能实例。
|
||||||
|
- **高级功能**:
|
||||||
|
- **时间旅行调试 (Time Travel Debugging)**:针对复杂算法的深度调试能力。
|
||||||
|
- **云端存储**:保存并分享私有算法库。
|
||||||
|
- **高清导出**:生成 60fps 算法演示视频(面向内容创作者)。
|
||||||
|
|
||||||
|
## 3. SDK 开源策略
|
||||||
|
**策略核心:SDK 全面开源,不卖代码,卖“运行环境”与“服务”。**
|
||||||
|
|
||||||
|
- **全语言开源 (MIT License)**:将 TS, Java, C++, Python 等所有语言的 SDK 代码开源。
|
||||||
|
- **目的**:
|
||||||
|
- **简历与声誉**:展示多语言架构设计能力和协议通用性,提升项目技术含金量。
|
||||||
|
- **生态共建**:降低社区参与门槛,鼓励开发者修复 Bug 或贡献新语言支持。
|
||||||
|
- **标准确立**:推动 Tracer Protocol 成为事实上的算法可视化标准。
|
||||||
|
|
||||||
|
## 4. 目标用户与场景分层
|
||||||
|
|
||||||
|
| 用户群体 | 核心痛点 | 解决方案 | 商业化路径 |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **学生/求职者** | 理解算法难,刷题调试难 | 可视化题解 + 调试器 | 会员订阅 (解锁 Java/C++ 调试) |
|
||||||
|
| **教育机构/高校** | 教学抽象,作业批改难 | 交互式课件 + 作业系统 | 企业版 SaaS (ToB) |
|
||||||
|
| **内容创作者** | 制作动画成本高 | 脚本生成视频 + 自定义皮肤 | 工具付费/素材付费 |
|
||||||
|
|
||||||
|
## 5. 技术壁垒 (Moat)
|
||||||
|
- **协议生态**:语言无关的 Tracer Protocol,形成内容护城河。
|
||||||
|
- **混合运行时架构 (Hybrid Runtime)**:前端轻量级运行 + 后端重型沙箱,优化成本结构。
|
||||||
|
- **WebAssembly 应用**:探索将 Python/C++ 移植到前端运行,进一步降低服务端压力并提升免费用户体验。
|
||||||
|
|
||||||
|
---
|
||||||
|
*本文档基于 2026-02-16 的对话讨论整理。*
|
||||||
53
.trae/documents/sdk-visual-noise-handling.md
Normal file
53
.trae/documents/sdk-visual-noise-handling.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# SDK 视觉噪音处理策略
|
||||||
|
|
||||||
|
## 1. 问题背景
|
||||||
|
在算法可视化场景中,为了记录数据结构变化,需要在用户算法代码中插入 SDK 调用(如 `tracer.patch()`, `tracer.pick()`)。过多的 SDK 调用代码会产生“视觉噪音”,破坏算法逻辑的连贯性,增加认知负担,与平台“低侵入性”和“原生体验”的设计目标冲突。
|
||||||
|
|
||||||
|
## 2. 核心原则
|
||||||
|
- **原生优先 (Native First)**:尽量利用编程语言特性实现隐式追踪,避免引入非标准的封装数据结构(如避免强制用户使用 `MyArray.set()` 替代 `arr[]`)。
|
||||||
|
- **显式降噪 (Explicit Reduction)**:在无法隐式追踪的场景下,通过 IDE 的视觉设计弱化 SDK 代码的存在感。
|
||||||
|
- **可编程性保留**:承认并保留显式调用 SDK 的必要性(用于精细控制动画或处理不支持的语言特性),将其视为“高级功能”而非“噪音”。
|
||||||
|
|
||||||
|
## 3. 分级解决方案
|
||||||
|
|
||||||
|
### 3.1 Level 1: 隐式追踪 (Implicit Tracing) —— 零侵入
|
||||||
|
利用现代编程语言的高级特性(代理、重载、魔术方法),拦截原生数据结构的读写操作,自动触发可视化事件。
|
||||||
|
- **适用场景**:
|
||||||
|
- **JavaScript / TypeScript**: 使用 `Proxy` 对象拦截数组/对象操作。
|
||||||
|
- **C++**: 使用运算符重载(`operator[]`, `operator=`)封装 `std::vector` 或自定义容器。
|
||||||
|
- **Python**: 使用魔术方法(`__getitem__`, `__setitem__`)封装 `list`。
|
||||||
|
- **用户体验**:
|
||||||
|
```typescript
|
||||||
|
// 用户只需在初始化时声明一次
|
||||||
|
const tracer = new ArrayTracer().watch(arr);
|
||||||
|
// 后续全是原生代码,无感知
|
||||||
|
arr[i] = arr[j];
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Level 2: 显式调用 (Explicit Call) —— 精细控制
|
||||||
|
对于不支持隐式拦截的语言特性(如 Java 原生数组、C 语言指针),或者用户需要插入非数据结构操作的指令(如 `tracer.delay()`, `tracer.log()`),必须使用显式 SDK 调用。
|
||||||
|
- **适用场景**:
|
||||||
|
- Java 原生数组 (`int[]`)。
|
||||||
|
- C 语言指针操作。
|
||||||
|
- 算法逻辑之外的控制指令(暂停、日志、自定义高亮)。
|
||||||
|
- **代码规范**:
|
||||||
|
- 保持 SDK 调用独立成行,避免嵌套在复杂逻辑中。
|
||||||
|
- 命名保持简洁(如 `tracer.patch` 而非 `Visualizer.getInstance().updateElement`)。
|
||||||
|
|
||||||
|
### 3.3 Level 3: IDE 视觉降噪 (Visual Noise Reduction)
|
||||||
|
在 Web IDE 层面通过 UI/UX 设计,将显式 SDK 调用代码“隐形”或“弱化”。
|
||||||
|
- **幽灵文本 (Ghost Text)**:将检测到的 SDK 调用行渲染为低对比度颜色(如浅灰色,opacity: 0.5),使视线自然聚焦于算法逻辑。
|
||||||
|
- **代码折叠 (Code Folding)**:
|
||||||
|
- 提供“专注模式”开关,一键折叠所有 SDK 调用行。
|
||||||
|
- 在行号区域标记 SDK 调用,鼠标悬停时才高亮显示。
|
||||||
|
- **分离视图 (Split View) [可选]**:
|
||||||
|
- **算法视图**:仅显示纯算法逻辑(隐藏 SDK 调用)。
|
||||||
|
- **完整视图**:显示包含 Tracer 调用的完整代码,供调试使用。
|
||||||
|
|
||||||
|
## 4. 结论
|
||||||
|
不应为了消除噪音而发明一套蹩脚的封装 API。正确的路径是:
|
||||||
|
1. **能隐式则隐式**(JS/Py/C++ Proxy)。
|
||||||
|
2. **不能隐式则显式**(Java/C 原生操作)。
|
||||||
|
3. **显式噪音交给 IDE 处理**(变灰/折叠)。
|
||||||
|
|
||||||
|
这种策略既保留了代码的“可编程性”和“原生手感”,又解决了视觉干扰问题。
|
||||||
109
.trae/documents/system-architecture.md
Normal file
109
.trae/documents/system-architecture.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# Structrail 系统架构设计与技术选型
|
||||||
|
|
||||||
|
本文档总结了 Structrail 平台的技术架构决策,包括 Monorepo 策略、服务拆分原则以及前后端架构方案的对比分析。
|
||||||
|
|
||||||
|
## 1. 总体架构策略:Monorepo (单体仓库)
|
||||||
|
|
||||||
|
考虑到 Structrail 项目涉及多语言 SDK、统一的协议定义、前端可视化组件以及后端执行服务,我们决定采用 **Monorepo** 架构。
|
||||||
|
|
||||||
|
### 核心收益
|
||||||
|
1. **单一事实来源 (Single Source of Truth)**:协议定义 (`@structrail/protocol`) 作为核心资产,被前端、后端、SDK 共同引用。协议变更时,所有依赖方均可即时感知(类型检查报错),避免版本割裂。
|
||||||
|
2. **原子化提交 (Atomic Commits)**:由于协议变更往往涉及多端修改(如 ArrayTracer 新增指令 -> 前端渲染逻辑更新 -> SDK 实现更新),Monorepo 允许在一个 Commit 中完成所有相关修改,保证系统一致性。
|
||||||
|
3. **统一版本管理**:便于统一管理多语言 SDK 的版本发布节奏。
|
||||||
|
4. **开发体验**:利用 `pnpm workspace` 和 `Turborepo` 等工具,实现高效的依赖管理和增量构建。
|
||||||
|
|
||||||
|
## 2. 服务架构设计
|
||||||
|
|
||||||
|
Structrail 的业务特性决定了其架构必须采用 **动静分离** 的策略,将轻量级的业务逻辑与重量级的代码执行隔离。
|
||||||
|
|
||||||
|
### 2.1 核心服务拆分
|
||||||
|
|
||||||
|
| 服务模块 | 职责描述 | 技术特征 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| **Web (业务层)** | 用户界面、题目管理、社区互动、鉴权 | I/O 密集型,重交互,适合快速开发 |
|
||||||
|
| **API (接口层)** | 业务逻辑处理、数据转发、RPC 服务 | 高并发,低延迟,连接 Web 与 Runner |
|
||||||
|
| **Runner (执行层)** | 代码编译、沙箱运行、Trace 数据生成 | CPU 密集型,高风险,需独立部署与资源隔离 |
|
||||||
|
| **Protocol (共享层)** | 核心协议定义、类型声明、常量 | 纯代码库,无运行时依赖 |
|
||||||
|
|
||||||
|
### 2.2 架构方案对比
|
||||||
|
|
||||||
|
针对 Web 与 API 的实现,我们探讨了两种主流方案。两种方案均可行,且在 Monorepo 下都能实现 **100% 的端到端类型安全**。
|
||||||
|
|
||||||
|
#### 方案 A:Next.js 全栈架构 (SSR 优先)
|
||||||
|
* **架构描述**:Web 与 API 合二为一,直接在 Next.js 的 Server Actions / Route Handlers 中处理业务逻辑,并调用 Runner 服务。
|
||||||
|
* **优势**:
|
||||||
|
* **开发效率极致**:无 API 胶水层,前后端代码在同一文件中,调用后端函数像调用本地函数一样自然。
|
||||||
|
* **SSR 支持**:对 SEO 友好,首屏加载快(适合题目详情页、文档页)。
|
||||||
|
* **生态丰富**:Vercel/Next.js 生态拥有大量现成组件。
|
||||||
|
* **劣势**:
|
||||||
|
* **部署绑定**:较强依赖 Node.js Runtime 或 Vercel 平台。
|
||||||
|
* **边界模糊**:Server Actions 可能导致前后端代码耦合,心智负担较重。
|
||||||
|
|
||||||
|
#### 方案 B:React (SPA) + Hono (API) 前后端分离 (CSR + Edge 优先)
|
||||||
|
* **架构描述**:
|
||||||
|
* **Web**: 纯 React SPA,通过 CDN 分发。
|
||||||
|
* **API**: 独立 Hono 服务,提供 RPC 接口。
|
||||||
|
* **通信**: 通过 `hono/client` 实现类型安全的 RPC 调用。
|
||||||
|
* **优势**:
|
||||||
|
* **物理边界清晰**:前端关注 UI 交互,后端关注数据处理,职责分明。
|
||||||
|
* **部署极其灵活**:前端可部署至任何静态托管(OSS/Netlify),后端可部署至 Edge(Cloudflare Workers/Deno)或容器。
|
||||||
|
* **高性能**:Hono 基于 Web Standards,冷启动快,资源占用极低。
|
||||||
|
* **类型安全**:通过 Monorepo 导出 API 类型 (`AppType`),前端直接引用,实现与 tRPC 类似的开发体验。
|
||||||
|
* **劣势**:
|
||||||
|
* **SEO 略弱**:纯 SPA 对 SEO 不如 SSR 友好(虽然可以通过预渲染解决)。
|
||||||
|
* **初期搭建成本**:需配置两个独立应用及通信链路。
|
||||||
|
|
||||||
|
### 2.3 结论与推荐
|
||||||
|
|
||||||
|
**推荐采用 Monorepo 架构,并根据团队偏好选择方案 A 或 B。**
|
||||||
|
|
||||||
|
* 如果更看重 **SEO** 和 **快速验证原型**,推荐 **方案 A (Next.js)**。
|
||||||
|
* 如果更看重 **架构清晰度**、**部署灵活性 (Edge)** 和 **前后端解耦**,推荐 **方案 B (React + Hono)**。
|
||||||
|
* *注:考虑到 Structrail 的编辑器交互极其复杂(重客户端逻辑),且 Runner 服务天然适合独立部署,**方案 B** 在长期维护和扩展性上可能更具优势。*
|
||||||
|
|
||||||
|
## 3. 建议的项目目录结构
|
||||||
|
|
||||||
|
无论选择哪种方案,Monorepo 的基础结构应保持一致:
|
||||||
|
|
||||||
|
```text
|
||||||
|
structrail/
|
||||||
|
├── apps/ # 应用程序(业务层)
|
||||||
|
│ ├── web/ # [React/Next.js] 平台前端
|
||||||
|
│ │ └── ... (Visualizer 组件调用)
|
||||||
|
│ ├── api/ # [Hono/Node] 业务后端 (如果是方案 B)
|
||||||
|
│ ├── runner/ # [Go/Rust] 代码执行沙箱服务
|
||||||
|
│ └── docs/ # [VitePress] 文档站点
|
||||||
|
│
|
||||||
|
├── packages/ # 共享库(核心资产)
|
||||||
|
│ ├── protocol/ # 核心协议定义 (JSON Schema, TS Interfaces)
|
||||||
|
│ │ ├── index.ts # 导出 Tracer 类型、指令定义
|
||||||
|
│ │ └── schema.json # 用于校验 Trace 数据的 Schema
|
||||||
|
│ ├── sdk-ts/ # TypeScript SDK 实现
|
||||||
|
│ ├── ui/ # 通用 UI 组件库 (可选)
|
||||||
|
│ └── eslint-config/ # 统一的代码规范配置
|
||||||
|
│
|
||||||
|
├── sdks/ # 多语言 SDK (非 JS/TS 生态)
|
||||||
|
│ ├── c/ # C 语言 SDK (Makefile)
|
||||||
|
│ ├── python/ # Python SDK (Poetry)
|
||||||
|
│ ├── java/ # Java SDK (Maven/Gradle)
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── tools/ # 工程化脚本
|
||||||
|
│ └── scripts/ # 批量发布、协议生成脚本
|
||||||
|
│
|
||||||
|
├── package.json # Root package.json
|
||||||
|
├── pnpm-workspace.yaml # pnpm workspace 配置
|
||||||
|
└── turbo.json # Turborepo 构建编排配置
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 关键技术栈建议
|
||||||
|
|
||||||
|
* **包管理器与运行时**: `Bun` (All-in-One 工具链,提供极致的安装速度和高性能运行时)
|
||||||
|
* 作为包管理器替代 `npm/pnpm`
|
||||||
|
* 作为脚本执行器替代 `ts-node`
|
||||||
|
* 作为测试运行器替代 `Jest/Vitest`
|
||||||
|
* **构建系统**: `Turborepo` (任务编排、远程缓存)
|
||||||
|
* **前端框架**: `React` (配合 Vite)
|
||||||
|
* **后端框架**: `Hono` (完美适配 Bun 运行时,性能卓越)
|
||||||
|
* **Runner 语言**: `Go` 或 `Rust` (高性能、并发强、安全性好)
|
||||||
|
* **API 通信**: `Hono RPC` (如果分离) 或 `Server Actions` (如果全栈)
|
||||||
91
.trae/documents/visualization-library-design.md
Normal file
91
.trae/documents/visualization-library-design.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# 前端可视化库设计草案 (@structrail/viz)
|
||||||
|
|
||||||
|
本文档描述了将平台前端渲染逻辑沉淀为独立可视化库的设计构想。
|
||||||
|
|
||||||
|
## 1. 项目定位
|
||||||
|
|
||||||
|
我们将“数据生成”(Tracer SDK)与“数据展示”(Visualization Lib)完全解耦,旨在沉淀一套标准化的、可嵌入的前端可视化引擎。
|
||||||
|
|
||||||
|
* **Tracer SDK (后端/客户端)**:负责生成标准化的、语言无关的 JSON 指令流(Trace Protocol)。
|
||||||
|
* **Visualization Lib (前端)**:负责解析指令流,重建数据状态,并提供交互式的可视化组件。
|
||||||
|
|
||||||
|
这套库不仅服务于 StructRail 平台,未来也可被嵌入到博客、电子书或教学文档中,成为算法可视化的基础设施。
|
||||||
|
|
||||||
|
## 2. 核心架构 (Architecture)
|
||||||
|
|
||||||
|
库的核心遵循 **Player + Store + Renderer** 的分层架构,本质上是一个支持时间旅行(Time Travel)的确定性状态机。
|
||||||
|
|
||||||
|
### 2.1 Player (播放器/控制器)
|
||||||
|
* **职责**:负责解析 Tracer 生成的 JSON 指令流,控制播放进度(Frame/Step)。
|
||||||
|
* **功能**:提供 `play()`, `pause()`, `next()`, `prev()`, `seek(index)` 等 API。
|
||||||
|
* **特性**:它不关心具体怎么画,只关心“当前是第几步”以及“播放速度”。
|
||||||
|
|
||||||
|
### 2.2 Store (状态仓库/沙盒)
|
||||||
|
* **职责**:负责根据指令重构数据结构的状态。
|
||||||
|
* **机制**:维护一个“影子内存”(Shadow Memory)。
|
||||||
|
* 当 Player 处于第 0 步时,Store 是空的。
|
||||||
|
* 当收到 `ArrayTracer.create` 指令时,Store 里建立一个数组模型。
|
||||||
|
* 当收到 `patch` 指令时,Store 更新对应数组的元素值。
|
||||||
|
* 当收到 `pick` 指令时,Store 标记对应元素为“高亮状态”。
|
||||||
|
* **输出**:它能在任何时间点,输出一份**只读的、标准化的数据快照 (Snapshot)** 给渲染层。
|
||||||
|
|
||||||
|
### 2.3 Renderer (渲染器/组件库)
|
||||||
|
* **职责**:纯粹的 UI 展示层(View),遵循 `f(state) => UI` 的原则。
|
||||||
|
* **实现**:可以使用 React/Vue 组件,也可以是 Canvas/WebGL(针对大规模数据)。
|
||||||
|
* **特点**:它完全不知道“指令”的存在,它只接收 `Store` 给它的快照。
|
||||||
|
* 例如:`<ArrayVisualizer data={[1, 2, 3]} highlights={[1]} />`
|
||||||
|
|
||||||
|
## 3. 库的形态设计 (API Preview)
|
||||||
|
|
||||||
|
如果这个库被开发出来,它在前端项目中的使用方式可能如下(以 React 为例):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Player, ArrayVisualizer, GraphVisualizer } from '@structrail/viz';
|
||||||
|
|
||||||
|
// 1. 载入 Tracer 生成的 JSON 数据
|
||||||
|
const events = await fetch('algorithm-trace.json');
|
||||||
|
const player = new Player(events);
|
||||||
|
|
||||||
|
// 2. 绑定到 UI
|
||||||
|
function AlgorithmDemo() {
|
||||||
|
// 使用 player 的 hook 获取当前帧的数据快照
|
||||||
|
const snapshot = usePlayerSnapshot(player);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* 播放控制栏 */}
|
||||||
|
<ControlBar player={player} />
|
||||||
|
|
||||||
|
{/* 渲染区域:根据 snapshot 中的数据自动渲染 */}
|
||||||
|
<div className="canvas">
|
||||||
|
{snapshot.tracers.map(tracer => {
|
||||||
|
if (tracer.type === 'array') {
|
||||||
|
return <ArrayVisualizer key={tracer.id} model={tracer} />
|
||||||
|
}
|
||||||
|
if (tracer.type === 'graph') {
|
||||||
|
return <GraphVisualizer key={tracer.id} model={tracer} />
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 关键技术挑战 (Technical Challenges)
|
||||||
|
|
||||||
|
### 4.1 增量计算与快照管理
|
||||||
|
* **问题**:如果算法有 100 万步,不能每一步都存一个深拷贝的快照。
|
||||||
|
* **策略**:使用类似 Git 的机制或 immer.js。Store 只记录关键帧(Keyframe)的快照,中间步骤通过重放指令(Replay)动态计算。
|
||||||
|
|
||||||
|
### 4.2 动画过渡 (Animation)
|
||||||
|
* **问题**:当从“第 1 步”跳到“第 2 步”时,如果只是数据的突变(1 -> 2),体验很生硬。
|
||||||
|
* **策略**:渲染层需要比较 `PrevSnapshot` 和 `CurrentSnapshot`(Diff)。
|
||||||
|
* 如果发现数组 index 0 的元素位置变了,它应该生成一个移动动画。
|
||||||
|
* 这就是为什么我们的 `patch` / `swap` 指令语义很重要,它们对应了不同的动画原语。
|
||||||
|
|
||||||
|
### 4.3 自动布局 (Auto-Layout)
|
||||||
|
* **问题**:后端 Tracer 通常不包含坐标信息(除非我们以后添加坐标指令,但通常不建议耦合 UI 细节)。
|
||||||
|
* **策略**:前端库需要内置强大的自动布局算法。
|
||||||
|
* Graph: Force-directed graph (力导向图)
|
||||||
|
* Tree: Reingold-Tilford 树布局
|
||||||
13
.trae/rules/development-rules.md
Normal file
13
.trae/rules/development-rules.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 开发规则
|
||||||
|
|
||||||
|
- **严禁主动修改任何项目源代码文件(如 .ts, .js, .c, .h 等)。当前工作仅限于文档编写(.md)和方案讨论。所有代码示例仅能在对话中展示,不得写入文件。**
|
||||||
|
- 在考虑API的移植性时,需要特别考虑C语言,当然,各类主流的编程语言要需要纳入考虑范围,包括但不限于:
|
||||||
|
- C++
|
||||||
|
- Java
|
||||||
|
- C#
|
||||||
|
- Kotlin
|
||||||
|
- Python
|
||||||
|
- Rust
|
||||||
|
- Go
|
||||||
|
- Swift
|
||||||
|
- Dart
|
||||||
@@ -3,22 +3,27 @@ import type { TracerCommand } from '../types';
|
|||||||
const createTracerContext = () => {
|
const createTracerContext = () => {
|
||||||
const commands: TracerCommand[] = [];
|
const commands: TracerCommand[] = [];
|
||||||
|
|
||||||
if (typeof process !== 'undefined' && typeof process.on === 'function') {
|
// if (typeof process !== 'undefined' && typeof process.on === 'function') {
|
||||||
process.on('exit', () => {
|
// process.on('exit', () => {
|
||||||
if (commands.length > 0) {
|
// if (commands.length > 0) {
|
||||||
console.log(commands);
|
// console.log(commands);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
const getTracerContext = () => {
|
const getTracerContext = () => {
|
||||||
const command = (command: TracerCommand) => {
|
const command = (command: TracerCommand) => {
|
||||||
commands.push(command);
|
commands.push(command);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: 输出指令序列
|
||||||
|
const dump = () => {
|
||||||
|
return commands;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
commands,
|
|
||||||
command,
|
command,
|
||||||
|
dump,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,12 @@ const controlTracer = createControlTracer({ description: 'ControlTracer' });
|
|||||||
|
|
||||||
const arrayTracer = createArrayTracer({
|
const arrayTracer = createArrayTracer({
|
||||||
description: 'ArrayTracer',
|
description: 'ArrayTracer',
|
||||||
array: [1, 2, 3],
|
// array: [1, 2, 3],
|
||||||
|
walker: (commit) => {
|
||||||
|
commit(1);
|
||||||
|
commit(2);
|
||||||
|
commit(3);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
arrayTracer.patch(0, 100);
|
arrayTracer.patch(0, 100);
|
||||||
|
|||||||
@@ -1,15 +1,32 @@
|
|||||||
import { getTracerContext } from '../context';
|
import { getTracerContext } from '../context';
|
||||||
import type { JsonValue } from '../types';
|
import type { JsonValue } from '../types';
|
||||||
|
|
||||||
interface ArrayTracerCreateOptions<T extends JsonValue[]> {
|
interface BaseArrayTracerCreateOptions {
|
||||||
description?: string;
|
description?: string;
|
||||||
array?: T;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ArrayTracerCreateOptionsFromArray<
|
||||||
|
T extends JsonValue[],
|
||||||
|
> extends BaseArrayTracerCreateOptions {
|
||||||
|
array: T;
|
||||||
|
walker?: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ArrayTracerCreateOptionsFromWalker<
|
||||||
|
T extends JsonValue[],
|
||||||
|
> extends BaseArrayTracerCreateOptions {
|
||||||
|
array?: never;
|
||||||
|
walker: (commit: (item: T[number]) => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArrayTracerCreateOptions<T extends JsonValue[]> =
|
||||||
|
| ArrayTracerCreateOptionsFromArray<T>
|
||||||
|
| ArrayTracerCreateOptionsFromWalker<T>;
|
||||||
|
|
||||||
export const createArrayTracer = <T extends JsonValue[]>(
|
export const createArrayTracer = <T extends JsonValue[]>(
|
||||||
options: ArrayTracerCreateOptions<T>,
|
options: ArrayTracerCreateOptions<T>,
|
||||||
) => {
|
) => {
|
||||||
const { description = 'ArrayTracer', array } = options;
|
const { description, array, walker } = options;
|
||||||
const tracer = crypto.randomUUID();
|
const tracer = crypto.randomUUID();
|
||||||
|
|
||||||
// 优化:仅维护数组长度作为影子状态,这在 C++/Java 等强类型语言中也极易实现(仅需一个 int 变量)
|
// 优化:仅维护数组长度作为影子状态,这在 C++/Java 等强类型语言中也极易实现(仅需一个 int 变量)
|
||||||
@@ -31,8 +48,8 @@ export const createArrayTracer = <T extends JsonValue[]>(
|
|||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
action: 'create',
|
action: 'create',
|
||||||
params: {
|
params: {
|
||||||
description: description,
|
description: description ?? 'ArrayTracer',
|
||||||
array: array,
|
array: array ?? [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
33
tracers.ts/src/tracers/graph-tracer.ts
Normal file
33
tracers.ts/src/tracers/graph-tracer.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { getTracerContext } from '../context';
|
||||||
|
import type { GraphTracerGraph } from '../types';
|
||||||
|
|
||||||
|
interface GraphTracerCreateOptions {
|
||||||
|
description?: string;
|
||||||
|
graph?: GraphTracerGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不要了,改为 walker/commit 方案
|
||||||
|
// TODO: 后续我们会添加创建图的辅助函数
|
||||||
|
// export const createGraphTracerHelper = () => {};
|
||||||
|
|
||||||
|
export const createGraphTracer = (options: GraphTracerCreateOptions) => {
|
||||||
|
const { description, graph } = options;
|
||||||
|
const tracer = crypto.randomUUID();
|
||||||
|
|
||||||
|
const { command } = getTracerContext();
|
||||||
|
|
||||||
|
command({
|
||||||
|
type: 'GraphTracer',
|
||||||
|
tracer: tracer,
|
||||||
|
action: 'create',
|
||||||
|
params: {
|
||||||
|
description: description ?? 'GraphTracer',
|
||||||
|
graph: graph ?? {
|
||||||
|
directed: false,
|
||||||
|
weighted: false,
|
||||||
|
nodes: [],
|
||||||
|
edges: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from './array-tracer';
|
export * from './array-tracer';
|
||||||
export * from './control-tracer';
|
export * from './control-tracer';
|
||||||
|
export * from './graph-tracer';
|
||||||
export * from './log-tracer';
|
export * from './log-tracer';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ type ArrayTracerCreateCommand = BaseArrayTracerCommand & {
|
|||||||
action: 'create';
|
action: 'create';
|
||||||
params: {
|
params: {
|
||||||
description: string;
|
description: string;
|
||||||
array?: JsonValue[];
|
array: JsonValue[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import type { ArrayTracerCommand } from './array-tracer';
|
import type { ArrayTracerCommand } from './array-tracer';
|
||||||
import type { ControlTracerCommand } from './control-tracer';
|
import type { ControlTracerCommand } from './control-tracer';
|
||||||
|
import type { GraphTracerCommand } from './graph-tracer';
|
||||||
import type { LogTracerCommand } from './log-tracer';
|
import type { LogTracerCommand } from './log-tracer';
|
||||||
|
|
||||||
export type TracerCommand =
|
export type TracerCommand =
|
||||||
| ArrayTracerCommand
|
|
||||||
| LogTracerCommand
|
| LogTracerCommand
|
||||||
| ControlTracerCommand;
|
| ControlTracerCommand
|
||||||
|
| ArrayTracerCommand
|
||||||
|
| GraphTracerCommand;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export type JsonValue =
|
|||||||
| JsonValue[]
|
| JsonValue[]
|
||||||
| { [key: string]: JsonValue };
|
| { [key: string]: JsonValue };
|
||||||
|
|
||||||
export type TracerType = 'ArrayTracer' | 'LogTracer' | 'ControlTracer';
|
export type TracerType = 'LogTracer' | 'ControlTracer' | 'ArrayTracer' | 'GraphTracer';
|
||||||
|
|
||||||
export type TracerId = ReturnType<typeof crypto.randomUUID>;
|
export type TracerId = ReturnType<typeof crypto.randomUUID>;
|
||||||
|
|
||||||
|
|||||||
94
tracers.ts/src/types/graph-tracer.ts
Normal file
94
tracers.ts/src/types/graph-tracer.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import type { BaseTracerCommand, JsonValue } from './common';
|
||||||
|
|
||||||
|
export type GraphTracerGraphNode = {
|
||||||
|
id: string;
|
||||||
|
value: JsonValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GraphTracerGraphEdge = {
|
||||||
|
source: string;
|
||||||
|
target: string;
|
||||||
|
weight: JsonValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GraphTracerGraph = {
|
||||||
|
directed: boolean;
|
||||||
|
weighted: boolean;
|
||||||
|
nodes: GraphTracerGraphNode[];
|
||||||
|
edges: GraphTracerGraphEdge[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseGraphTracerCommand = BaseTracerCommand & {
|
||||||
|
type: 'GraphTracer';
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerCreateCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'create';
|
||||||
|
params: {
|
||||||
|
description: string;
|
||||||
|
graph: GraphTracerGraph;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerInsertCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'insert';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerRemoveCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'remove';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerUpdateCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'update';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerConnectCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'connect';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerDisconnectCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'disconnect';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerWeightCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'weight';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerVisitCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'visit';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerLeaveCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'leave';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerPickCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'pick';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
type GraphTracerDropCommand = BaseGraphTracerCommand & {
|
||||||
|
action: 'drop';
|
||||||
|
params: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GraphTracerCommand =
|
||||||
|
| GraphTracerCreateCommand
|
||||||
|
| GraphTracerInsertCommand
|
||||||
|
| GraphTracerRemoveCommand
|
||||||
|
| GraphTracerUpdateCommand
|
||||||
|
| GraphTracerConnectCommand
|
||||||
|
| GraphTracerDisconnectCommand
|
||||||
|
| GraphTracerWeightCommand
|
||||||
|
| GraphTracerVisitCommand
|
||||||
|
| GraphTracerLeaveCommand
|
||||||
|
| GraphTracerPickCommand
|
||||||
|
| GraphTracerDropCommand;
|
||||||
@@ -2,4 +2,5 @@ export * from './array-tracer';
|
|||||||
export * from './command';
|
export * from './command';
|
||||||
export * from './common';
|
export * from './common';
|
||||||
export * from './control-tracer';
|
export * from './control-tracer';
|
||||||
|
export * from './graph-tracer';
|
||||||
export * from './log-tracer';
|
export * from './log-tracer';
|
||||||
|
|||||||
Reference in New Issue
Block a user