Compare commits

..

8 Commits

Author SHA1 Message Date
9aad52347b refactor(tracers): 统一命令字段名并重构数组追踪器创建逻辑
- 将所有命令的 `params` 字段重命名为 `payload`,以保持命名一致性
- 将 `ArrayTracerCreateOptions` 从联合类型简化为单一接口,移除 `array` 参数,新增 `initial` 和 `walker` 参数
- 引入 `buildInitial` 函数集中处理初始数组的构建逻辑
- 使用 `metadata` 对象管理数组长度状态,替代独立的 `arrayLength` 变量
- 更新错误消息格式,移除方括号前缀
- 调整泛型参数,使 `createArrayTracer` 更通用
2026-03-04 02:34:12 +08:00
2dfc4ced67 docs: 优化开发规则与项目介绍文档的表述
- 移除关于禁止修改源代码的临时限制说明,该限制已不再适用
- 统一并优化多处中文标点与空格的格式,使文档更整洁
- 调整项目介绍中关于协议设计的表述,使其更清晰准确
- 合并`tracer-type.md`中的分隔线,使列表结构更紧凑
2026-03-02 21:06:36 +08:00
19d4b97669 fix(tracer): 修复异步scope中异常处理后的状态设置问题
发生ScopeError时不执行flush,否则即便抛出异常也会执行flush
2026-02-24 20:33:59 +08:00
8a2399e890 docs(tracer-context): 添加详细的中文注释说明scope的设计原理和实现细节。 2026-02-23 13:03:22 +08:00
9f08c34225 refactor(tracer-context): 引入ScopeError类并优化错误处理逻辑
- 新增ScopeError自定义错误类,用于区分范围相关错误
- 将error字段重命名为exception以更准确描述其用途
- 优化flush方法,仅在有必要时输出指令序列
- 简化错误处理逻辑,移除冗余的console.error调用
- 确保ScopeError能正确向上抛出而不被捕获
2026-02-23 03:20:13 +08:00
134d4e2b19 refactor(tracer-context): 改进 scope 状态管理以支持嵌套检测
- 引入 SCOPE_STATE 枚举明确区分初始、执行中和已完成状态
- 将 scoping 布尔标志替换为 state 枚举,支持检测嵌套调用
- 分离同步和异步错误处理流程,确保状态正确更新
- 修复 flush 方法中 commands 和 error 的清理逻辑
2026-02-23 02:05:58 +08:00
8894458f4c feat(tracer-context): 添加scope方法以支持追踪异步错误
新增scope方法用于包裹异步或同步代码块,自动捕获错误并记录到上下文中。同时将dump方法重命名为flush以更准确描述其行为(清空并输出指令序列)。重构内部数据结构,将commands和error统一管理在scopeResult对象中。
2026-02-22 22:37:06 +08:00
0e61e4f5b9 docs: 新增并更新产品战略、技术设计与商业化规划文档
- 新增 AI 时代下的 StructRail 战略定位与发展路线图,明确 AI 集成方向
- 更新前端可视化库设计方案,细化架构、技术挑战与商业化结合点
- 重构商业化策略文档为 Q&A 形式,深入探讨市场、产品与增长策略
2026-02-22 22:36:48 +08:00
9 changed files with 466 additions and 181 deletions

View File

@@ -0,0 +1,65 @@
# AI 时代下的 StructRail 战略定位与发展路线图
## 1. 背景与趋势分析:从 Coding 到 Reasoning
在 AI 时代2024-2025+),中国软件开发职业环境对算法技能的要求发生了质的转变:
* **Coding 能力贬值**AI如 Copilot, GPT-4已能秒生成绝大多数标准算法代码。
* **Reasoning 能力升值**:面试官不再纠结于“手撕代码”的语法细节,转而考察:
* **代码审计**:能否一眼看出 AI 生成代码的逻辑漏洞(如边界条件、内存泄漏)。
* **过程推演**:能否清晰解释代码在特定数据输入下的执行轨迹。
* **底层理解**:能否跨越语言语法,理解数据结构在内存中的真实布局与操作(指针、引用、所有权)。
**结论**AI 极大地降低了“编写代码”的门槛,但极大地提高了**“理解与调试复杂逻辑”**的门槛。
## 2. StructRail 的新定位
StructRail 不仅仅是一个算法学习工具,更应定位为 **AI 生成代码的“验证引擎”与“调试器”**
* **测谎仪Verifier**:验证 AI 生成的复杂算法(如图算法、红黑树)逻辑是否正确。
* **白盒解释器Explainer**:将 AI 的“黑盒代码”转化为可视化的“白盒流程”,帮助人类理解。
* **逻辑调试器Visual Debugger**:通过 **Time Travel时间旅行** 功能,让开发者回溯到 Bug 发生的瞬间,直观看到拓扑结构错误(如链表断裂、图连接错误),而非枯燥的内存值。
## 3. AI 深度集成路线图
为了实现上述定位StructRail 需要在协议层、SDK 层和平台层进行 AI 深度集成。
### 第一阶段:开发辅助 (AI-Assisted Development)
*目标:降低用户接入 SDK 的门槛,让 Tracer 代码“自动生长”。*
1. **智能插桩助手 (Auto-Instrumentation Copilot)**
* **功能**:用户提供原始算法代码(如 C++ 快排AI 自动识别关键操作swap, compare并插入对应的 SDK 调用代码。
* **示例**:将 `swap(arr[i], arr[j])` 自动转换为 `tracer.pick(i); tracer.pick(j); tracer.patch(...); tracer.drop(...)`
2. **自然语言生成 (Text-to-Tracer)**
* **功能**用户输入自然语言描述“展示二叉树插入过程”AI 直接生成可运行的 Tracer 代码,用于快速演示。
### 第二阶段:运行时智能增强 (AI-Enhanced Runtime)
*目标:利用 Tracer 产生的数据流,进行实时的逻辑分析和解释。*
1. **实时算法解说员 (Live Narrator)**
* **功能**:平台后端接收 Tracer 指令流AI 实时分析并生成自然语言解说。
* **效果**当动画播放到“分区”步骤时AI 弹幕解释:“这里选取了 5 作为基准,将小于 5 的元素移到左侧”。
2. **逻辑错误诊断 (Logic Doctor)**
* **功能**AI 分析 Tracer 指令流,识别逻辑错误(如死循环、链表环路、非法的图操作)。
* **交互**:在时间轴上直接标记“逻辑错误点”,并给出修复建议。
### 第三阶段:协议层面的进化 (Protocol Evolution)
*目标:为 AI 提供语义信息,使其能理解操作意图。*
1. **协议扩展Meta 字段**
* **建议**:在 `TracerCommand` 接口中增加可选的 `meta` 字段,用于存储操作的语义信息。
* **示例**
```typescript
// 修改前:仅表示选中
tracer.pick(5);
// 修改后携带语义AI 可读取
tracer.pick(5, { meta: { role: 'pivot', intent: 'partition_base' } });
```
* **价值**AI 读取 `meta` 后,能生成更精准的解说词(“选中基准点” vs “选中下标 5”
## 4. 关键行动建议
1. **协议升级**:优先在 `Command` 接口中增加 `meta?: Record<string, any>` 字段。
2. **SDK 优化**:在设计 API 时,考虑如何方便地让 AI 生成代码(例如保持 API 的原子性和语义清晰)。
3. **Prompt 工程**:建立一套标准的 Prompt 模板,指导用户如何使用 LLM 将现有代码转换为 StructRail SDK 代码。

View File

@@ -1,51 +1,187 @@
# StructRail 商业化策略与产品规划 # StructRail 产品商业化与战略规划 Q&A
## 1. 核心愿景 本文档以问答形式记录了关于 StructRail 平台在商业化路径、技术策略、AI 时代定位等核心问题的深度探讨。
打造一个支持主流编程语言、具备“时间旅行”调试能力的算法可视化生态平台。不仅仅是展示算法,更是理解和调试算法的工具。
## 2. 商业模式:基于运行成本的 Freemium 模型 ***
鉴于 Web 平台的技术特性,采用“低成本服务免费引流,高成本服务付费增值”的策略。
### 2.1 免费层 (Client-side / Web Native) ### Q1: 对于整个平台,如果希望它成为一个产生商业价值的产品,应该怎么做?
利用浏览器端计算能力Web Worker / WASM提供零边际成本的服务。
- **支持语言**JavaScript, TypeScript, 以及未来通过 WASM 支持的 Python/C++ (Web版)。
- **运行环境**:用户浏览器本地运行,无服务器计算成本。
- **目标用户**:初学者、前端开发者、开源社区贡献者。
- **价值**:作为“引流款”,通过开源和免费使用获取最大流量和技术声誉。
### 2.2 会员/增值层 (Server-side / Cloud Native) **A:**
利用云端沙箱Docker/K8s提供高性能、多语言的运行环境。 要产生商业价值StructRail 不能只做一个“库”,而必须做一个“平台”。建议从 **用户价值**、**商业模式** 和 **产品形态** 三个维度进行规划:
- **支持语言**Java, Go, C#, C++ (完整版) 等重型或依赖特定运行时环境的语言。
- **运行环境**:云端高性能沙箱,保障安全与隔离。
- **服务限制**
- **普通用户**:每日有限次数的云端运行额度(如 10 次/天)。
- **会员用户**:无限次云端运行,优先调度高性能实例。
- **高级功能**
- **时间旅行调试 (Time Travel Debugging)**:针对复杂算法的深度调试能力。
- **云端存储**:保存并分享私有算法库。
- **高清导出**:生成 60fps 算法演示视频(面向内容创作者)。
## 3. SDK 开源策略 1. **核心价值主张**:不仅仅是“看”算法,而是“懂”和“调试”。
**策略核心SDK 全面开源,不卖代码,卖“运行环境”与“服务”。**
- **全语言开源 (MIT License)**:将 TS, Java, C++, Python 等所有语言的 SDK 代码开源 * **可调试性 (Time Travel Debugging)**:支持逐行调试用户自己的代码,查看数据结构变化
- **目的**
- **简历与声誉**:展示多语言架构设计能力和协议通用性,提升项目技术含金量。
- **生态共建**:降低社区参与门槛,鼓励开发者修复 Bug 或贡献新语言支持。
- **标准确立**:推动 Tracer Protocol 成为事实上的算法可视化标准。
## 4. 目标用户与场景分层 * **多语言支持**:打破语言壁垒,适合作为通用的教学工具。
| 用户群体 | 核心痛点 | 解决方案 | 商业化路径 | * **创作自由度**:不仅支持标准算法,更支持用户自定义逻辑的可视化。
| :--- | :--- | :--- | :--- |
| **学生/求职者** | 理解算法难,刷题调试难 | 可视化题解 + 调试器 | 会员订阅 (解锁 Java/C++ 调试) |
| **教育机构/高校** | 教学抽象,作业批改难 | 交互式课件 + 作业系统 | 企业版 SaaS (ToB) |
| **内容创作者** | 制作动画成本高 | 脚本生成视频 + 自定义皮肤 | 工具付费/素材付费 |
## 5. 技术壁垒 (Moat) 2. **商业化场景 (Product-Market Fit)**
- **协议生态**:语言无关的 Tracer Protocol形成内容护城河。
- **混合运行时架构 (Hybrid Runtime)**:前端轻量级运行 + 后端重型沙箱,优化成本结构。
- **WebAssembly 应用**:探索将 Python/C++ 移植到前端运行,进一步降低服务端压力并提升免费用户体验。
--- * **ToC (学生/求职者)**:提供“可视化题解”和“调试会员”,解决刷题痛点。
*本文档基于 2026-02-16 的对话讨论整理。*
* **ToB (教育机构)**:提供交互式课件和可视化作业系统,解决教学抽象和批改难的问题。
* **ToC (创作者)**:提供高清视频导出工具,服务技术博主。
3. **技术壁垒**
* 建立 Tracer Protocol 协议标准生态。
* 构建高性能的安全沙箱和渲染引擎。
***
### Q2: 在 ToC 市场,推荐在语言层面做会员功能差异化吗?
**A:**
**非常推荐**,这是一种符合技术成本逻辑的 Freemium 策略。
* **策略核心**
* **前端语言 (JS/TS/WASM)****完全免费**。利用浏览器端计算能力(零边际成本),作为引流款和开源展示。
* **后端语言 (Java/C++/Go)****云端运行付费**。利用云端沙箱(高成本),提供高性能、免配置的运行环境作为增值服务。
* **风险与对策**
* **风险**:直接锁死 Java/C++ 可能流失核心学生用户(算法主力语言)。
* **对策**:采用“混合运行时”策略。
* **云端一键运行**:仅限会员(或新用户试用),筛选付费意愿强的用户。
* **本地运行 (Local Runner)**:提供免费 CLI 工具,允许用户在本地运行重型语言并上传数据。给“白嫖党”留活路,同时不消耗服务器资源。
***
### Q3: SDK 调用代码混杂在算法中,会不会产生严重的视觉噪音?
**A:**
**会,而且影响很大。** 但这是“可编程性”的代价,可以通过分级策略解决:
1. **Level 1: 隐式追踪 (零侵入)**
* 对于 JS/TS (Proxy)、C++ (Operator Overloading)、Python (Magic Methods),利用语言特性实现**全自动追踪**。用户只需初始化,后续写原生代码即可触发可视化。
2. **Level 2: 显式调用 (精细控制)**
* 对于 Java 原生数组或 C 指针,无法隐式拦截,必须保留显式 SDK 调用。
* 这是为了保留“可编程性”,允许用户精确控制动画时机。
3. **Level 3: IDE 视觉降噪**
* 在 Web IDE 中,通过 **“幽灵文本” (Ghost Text)** 技术将 SDK 代码渲染为灰色/半透明。
* 提供 **“专注模式”**,一键折叠所有可视化相关代码,只展示算法逻辑。
***
### Q4: 既然有免费额度,会不会出现用户刷号(刷额度)的情况?应该一开始就筛选付费用户吗?
**A:**
刷号问题客观存在,但通过技术手段(手机号/微信登录)可控。对于中国市场,建议采取 **“漏斗筛选”** 策略:
1. **不要一上来就收费**:直接付费门槛太高,用户无法体验产品价值。
2. **用“麻烦”筛选用户**
* 提供免费的 **Local Runner**(本地运行+上传),路径繁琐,适合无付费能力但有时间折腾的学生。
* 提供付费的 **Cloud Sandbox**(云端一键运行),路径极致便捷,适合追求效率、愿意付费的用户。
3. **付费点设计**
* **效率**:免配置、一键运行。
* **数据**:云端存储、多端同步。
* **功能**Time Travel 深度调试。
***
### Q5: 我们的业务策略应该是 ToB/G 还是 ToC
**A:**
**ToC 树品牌、磨产品ToB/ToG 赚利润、上规模。** 建议采用 **Product-Led Growth (PLG)** 模式:
1. **起步期 (ToC)**:全力打磨 C 端体验,积累海量用户和社区口碑(题解/教程)。**目标是 DAU 和品牌声量。**
2. **成长期 (ToC -> ToB)**:推出“教师版/团队版”,通过 C 端用户(学生/助教)自下而上渗透高校。
3. **成熟期 (ToB/ToG)**:基于已有的教学渗透率,向高校/政府销售私有化部署或定制课程服务。**目标是营收和壁垒。**
***
### Q6: AI 时代下,如果 AI 能直接生成可视化,我们的产品痛点还存在吗?
**A:**
这是一个极度危险但也充满机遇的转折点。
* **威胁**:如果只做“手动写代码的库”,会被 AI 颠覆。因为 AI 可以生成 D3.js 代码。
* **机会**AI 生成的可视化通常是 **“黑盒”、“不可调试”、“不可交互”** 的。
**StructRail 的新定位AI-Powered Algorithm Visualization Engine**
我们不做 AI 的对手,做 AI 的 **“后端引擎”**
1. **自动插桩 (Auto-Instrumentation)**:利用 AI 分析用户代码,自动插入 StructRail SDK 指令。用户得到的是 **“可调试、可交互”** 的可视化,而非 AI 生成的死动画。
2. **精确性与标准化**StructRail 提供标准化的 Tracer Protocol保证可视化的精确性和可维护性这是 AI 生成的代码无法比拟的。
3. **AI 讲解 (AI Tutor)**:利用 SDK 产生的精确数据流,喂给 AI 生成实时语音旁白。
**结论**:痛点依然存在,但解法变了。从“帮用户画图”转变为“提供可被 AI 操控的标准可视化运行时”。
***
### Q7: 是否应该将前端渲染逻辑沉淀为独立的可视化库?它在商业化中扮演什么角色?
**A:**
**必须这样做。** 建议将前端渲染逻辑剥离为独立的开源库(暂定名 `StructRail Viz`),定位为 **“算法领域的 ECharts”**。
1. **技术解耦与复用**
* 将 Tracer SDK数据生成与渲染层数据展示分离中间通过标准化的 Tracer Protocol 连接。
* 该库本质上是一个 **确定性状态机 (Player)**,支持“时间旅行”和“进度拖拽”。
* 可以被嵌入到任何 Web 环境中,如 VS Code 插件、Hexo/Hugo 博客、在线文档、电子书等。
2. **生态扩展 (特洛伊木马策略)**
* 通过开源库进入开发者的工具链和内容生态,让 StructRail 的水印和链接遍布互联网。
* 建立 Tracer Protocol 的事实标准,任何生成该协议的工具(包括 AI都可以使用该库渲染。
3. **商业化变现点**
* **主题市场 (Theme Store)**:售卖高级皮肤(赛博朋克、手绘风、像素风)。
* **高级渲染器**:提供基于 WebGL/WebGPU 的高性能渲染引擎(用于大规模图数据)或 3D 渲染器。
* **导出服务**提供基于云端的高清视频渲染服务4K/60fps服务于内容创作者。
***
### Q8: 我们的平台如果面向全球市场开展商业化行动,相较于面向中国国内市场会有什么区别?
**A:**
我们的商业化策略在两个市场呈现出明显的**“分而治之”**特征:国内市场侧重于**“筛选与防守”**,全球市场侧重于**“增长与生态”**。
| 维度 | 全球市场 (Global Market) | 中国国内市场 (Domestic Chinese Market) |
| :--- | :--- | :--- |
| **1. 用户画像** | **CS 学生、开源贡献者、自学者**<br>侧重计算机科学基础教育、直观理解算法原理。 | **求职刷题党、考研学生、技术博主**<br>侧重“面试突击”、“算法竞赛”及“题解制作”,功利性更强。 |
| **2. 支付习惯** | **订阅制 (Subscription)**<br>习惯为工具和效率付费SaaS接受自动扣费。<br>**支付工具**Stripe (信用卡), PayPal。 | **买断/季卡 + 漏斗筛选**<br>付费意愿相对较低,需通过门槛筛选用户。<br>**支付工具**:微信支付、支付宝(需深度集成)。 |
| **3. 推广渠道** | **技术社区驱动 (Community Driven)**<br>主战场GitHub, Product Hunt, Reddit, Twitter。<br>策略:通过开源可视化库 (`@structrail/viz`) 进入开发者工具链。 | **内容创作者驱动 (Creator Economy)**<br>主战场Bilibili, 知乎, 掘金, 公众号。<br>策略:利用**“高清视频导出”**功能赋能技术博主,让他们的算法讲解视频成为平台的免费广告。 |
| **4. 基础设施** | **云端优先 (Cloud First)**<br>用户习惯 Web IDE 的无缝体验AWS/Cloudflare 等基础设施成熟,成本可控。 | **混合运行 (Hybrid Runtime)**<br>鉴于国内服务器带宽昂贵且用户量大,大力推广 **Local Runner**(本地运行+上传),将计算和流量成本转嫁给用户端。 |
| **5. 法律合规** | **数据隐私 (GDPR)**<br>重点关注用户数据(代码、日志)的隐私保护和跨境传输合规。 | **实名认证 (KYC)**<br>必须集成手机号/微信一键登录既是合规要求ICP备案也是防止“刷号/白嫖”的技术手段。 |
**核心行动差异:**
1. **中国市场 - 漏斗筛选与创作者经济**
* **漏斗筛选**:免费用户推荐使用 **Local Runner**(本地运行),筛选出动手能力强或预算有限的用户;付费用户享受 **Cloud Sandbox**(云端一键运行)。
* **视频破圈**:提供 **4K/60fps 高清视频导出** 功能成为技术博主B站/抖音)制作算法讲解视频的“生产力工具”,实现病毒式传播。
2. **全球市场 - PLG 与开源生态**
* **PLG 增长**:直接开放云端体验,通过极致的 **Time Travel Debugging** 体验留住用户。
* **开源渗透**:利用开源的 `@structrail/viz` 库建立技术标准,让全球开发者在博客/文档中嵌入组件,形成网络效应。
3. **共同战略 - AI 可视化引擎**
* 无论市场如何,定位均为 **“AI 的可视化引擎”**。开发 IDE 插件,让 AI 生成的代码自动插入 StructRail SDK解决 AI 代码“不可解释、难以调试”的痛点。

View File

@@ -1,91 +1,94 @@
# 前端可视化库设计案 (@structrail/viz) # 前端可视化库设计案 (`@structrail/viz`)
本文档描述了将平台前端渲染逻辑沉淀为独立可视化库的设计构想。 ## 1. 核心目标
- **解耦**:将 Tracer SDK数据生成与前端渲染逻辑数据展示完全分离。
- **复用**:打造一个通用的、可嵌入的算法可视化库,服务于 StructRail 平台及第三方场景博客、文档、IDE 插件)。
- **标准**:作为 Tracer Protocol 的标准消费者,建立可视化渲染的事实标准。
## 1. 项目定位 ## 2. 架构设计:确定性状态机 (Deterministic State Machine)
我们将“数据生成”Tracer SDK与“数据展示”Visualization Lib完全解耦旨在沉淀一套标准化的、可嵌入的前端可视化引擎 采用 **Player + Store + Renderer** 的分层架构确保时间旅行Time Travel的可靠性
* **Tracer SDK (后端/客户端)**:负责生成标准化的、语言无关的 JSON 指令流Trace Protocol
* **Visualization Lib (前端)**:负责解析指令流,重建数据状态,并提供交互式的可视化组件。
这套库不仅服务于 StructRail 平台,未来也可被嵌入到博客、电子书或教学文档中,成为算法可视化的基础设施。
## 2. 核心架构 (Architecture)
库的核心遵循 **Player + Store + Renderer** 的分层架构本质上是一个支持时间旅行Time Travel的确定性状态机。
### 2.1 Player (播放器/控制器) ### 2.1 Player (播放器/控制器)
* **职责**:负责解析 Tracer 生成的 JSON 指令流控制播放进度Frame/Step)。 - **职责**:解析 JSON 指令流控制时间轴Play, Pause, Seek, Speed)。
* **功能**:提供 `play()`, `pause()`, `next()`, `prev()`, `seek(index)` 等 API。 - **特性**
* **特性**:它不关心具体怎么画,只关心“当前是第几步”以及“播放速度” - 维护当前帧索引 `currentIndex`
- 支持步进Step和连续播放。
- 处理“自动暂停”指令(如 `tracer.wait()`)。
### 2.2 Store (状态仓库/沙盒) ### 2.2 Store (状态仓库/沙盒)
* **职责**:负责根据指令重构数据结构状态。 - **职责**维护“影子内存”Shadow Memory根据指令计算数据结构状态。
* **机制**维护一个“影子内存”Shadow Memory - **机制**
* 当 Player 处于第 0 步时Store 是空的 - **增量计算**:基于 immer.js 或类似机制只存储指令序列和初始状态通过重放Replay或关键帧Keyframe技术快速计算任意时刻的状态
* 当收到 `ArrayTracer.create` 指令时Store 里建立一个数组模型 - **快照 (Snapshot)**:向渲染层提供只读的、标准化的数据快照
* 当收到 `patch` 指令时Store 更新对应数组的元素值。 - **State(t) = Apply(InitialState, Events[0...t])**
* 当收到 `pick` 指令时Store 标记对应元素为“高亮状态”。
* **输出**:它能在任何时间点,输出一份**只读的、标准化的数据快照 (Snapshot)** 给渲染层。
### 2.3 Renderer (渲染器/组件库) ### 2.3 Renderer (渲染器/组件库)
* **职责**:纯粹的 UI 展示层View遵循 `f(state) => UI` 原则。 - **职责**:纯 UI 展示层,遵循 `UI = Render(State)` 原则。
* **实现**:可以使用 React/Vue 组件,也可以是 Canvas/WebGL针对大规模数据 - **实现**
* **特点**:它完全不知道“指令”的存在,它只接收 `Store` 给它的快照 - **DOM/SVG 渲染器**:用于常规数据结构(数组、链表、树),支持 CSS 动画和高质量文本渲染
* 例如:`<ArrayVisualizer data={[1, 2, 3]} highlights={[1]} />` - **Canvas/WebGL 渲染器**:用于大规模数据(如 10w+ 节点的图、矩阵热力图)。
- **组件化**:提供 `<ArrayVisualizer>`, `<GraphVisualizer>` 等独立组件。
## 3. 库的形态设计 (API Preview) ## 3. 关键技术挑战与策略 (Advanced Technical Considerations)
如果这个库被开发出来,它在前端项目中的使用方式可能如下(以 React 为例) 为了确保该库能适应复杂的互联网环境(移动端、嵌入式、高性能场景),需重点关注以下方面
### 3.1 响应式与移动端适配 (Responsive & Mobile First)
- **自适应布局**Renderer 需具备流式布局能力。例如 `ArrayVisualizer` 在窄屏(手机/VS Code 侧边栏)下应自动切换为“折行显示”或“垂直列表模式”,避免横向滚动。
- **触控交互**:设计移动端友好的交互模式(如长按代替 Hover双指缩放代替滚轮
### 3.2 样式隔离与嵌入性 (Style Isolation)
- **Shadow DOM**:推荐使用 Web Components (Shadow DOM) 封装渲染器,彻底隔离宿主页面(如博客全局 CSS的样式干扰。
- **CSS Modules / CSS-in-JS**:作为备选方案,确保类名哈希化,防止污染宿主环境。
### 3.3 性能降级与 LOD (Level of Detail)
- **自动后端切换**:根据数据规模动态选择渲染后端。
- 节点 < 500使用 SVG交互性好调试方便
- 节点 > 500自动降级为 Canvas/WebGL高性能
- **LOD 策略**:在缩放或快速播放时,自动隐藏非关键细节(如节点文本、阴影),保证渲染帧率。
### 3.4 状态序列化与分享 (Serialization)
- **State Export**:支持将当前 Store 的状态快照导出为 JSON。
- **Deep Link**:支持将状态编码为 URL 参数(如 `?state=base64...`),实现“分享当前步骤”功能。
### 3.5 插件系统与扩展性 (Plugin System)
- **Renderer Provider**:设计开放的注册接口,允许第三方开发者注册自定义渲染器。
```typescript
// 允许用户注册自定义渲染器,支持“主题市场”商业化
VizRegistry.register('GraphTracer', CyberpunkGraphRenderer);
```
- **中间件机制**:允许拦截和修改指令流,实现自定义的逻辑增强(如增加声音音效)。
## 4. 预期形态
该库将发布为 NPM 包,提供如下使用方式:
```typescript ```typescript
import { Player, ArrayVisualizer, GraphVisualizer } from '@structrail/viz'; import { Player, ArrayVisualizer } from '@structrail/viz';
import '@structrail/viz/dist/style.css'; // 或自动注入 Shadow DOM 样式
// 1. 载入 Tracer 生成的 JSON 数据 // 1. 初始化播放器
const events = await fetch('algorithm-trace.json');
const player = new Player(events); const player = new Player(events);
// 2. 绑定到 UI // 2. 渲染组件 (React 示例)
function AlgorithmDemo() { function App() {
// 使用 player 的 hook 获取当前帧的数据快照
const snapshot = usePlayerSnapshot(player); const snapshot = usePlayerSnapshot(player);
return ( return (
<div> <div className="viz-container">
{/* 播放控制栏 */} <ArrayVisualizer
<ControlBar player={player} /> model={snapshot.tracers['array-1']}
theme="cyberpunk" // 支持主题切换
{/* 渲染区域:根据 snapshot 中的数据自动渲染 */} />
<div className="canvas"> <Controls player={player} />
{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> </div>
); );
} }
``` ```
## 4. 关键技术挑战 (Technical Challenges) ## 5. 商业化结合点
- **开源核心**:建立协议标准,获取流量。
### 4.1 增量计算与快照管理 - **付费扩展**
* **问题**:如果算法有 100 万步,不能每一步都存一个深拷贝的快照 - **高级主题包**:赛博朋克、手绘风、企业定制皮肤
* **策略**:使用类似 Git 的机制或 immer.js。Store 只记录关键帧Keyframe的快照中间步骤通过重放指令Replay动态计算 - **高性能渲染器**3D 图算法渲染、大数据分析引擎
- **云端服务**:高清视频导出 (MP4/GIF)。
### 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 树布局

View File

@@ -1,7 +1,6 @@
# 开发规则 # 开发规则
- **严禁主动修改任何项目源代码文件(如 .ts, .js, .c, .h 等)。当前工作仅限于文档编写(.md和方案讨论。所有代码示例仅能在对话中展示不得写入文件。** - 在设计协议和API时我们需要考虑API的移植性尤其需要特别关注C语言因为C语言的语法表达力不算强。当然各类主流的编程语言都需要纳入考虑范围包括但不限于
- 在考虑API的移植性时需要特别考虑C语言当然各类主流的编程语言要需要纳入考虑范围包括但不限于
- C++ - C++
- Java - Java
- C# - C#

View File

@@ -6,10 +6,10 @@
基于上述设想,我肯定无法接受从编译技术的角度去适配每一种编程语言(例如代码插桩甚至改造编译器),因为这会导致平台的开发变得非常复杂,维护成本也会非常高。因此我的想法是设计一套语言无关的协议,用来描述各种数据结构的变化(跟踪器),因此这套协议会定义需要支持的数据结构类型,以及每一种数据结构类型需要支持的操作指令,并且我倾向于将指令设计得更加“底层”一些,类似于“原语”的概念。 基于上述设想,我肯定无法接受从编译技术的角度去适配每一种编程语言(例如代码插桩甚至改造编译器),因为这会导致平台的开发变得非常复杂,维护成本也会非常高。因此我的想法是设计一套语言无关的协议,用来描述各种数据结构的变化(跟踪器),因此这套协议会定义需要支持的数据结构类型,以及每一种数据结构类型需要支持的操作指令,并且我倾向于将指令设计得更加“底层”一些,类似于“原语”的概念。
> 例如针对顺序表结构,我可能会定义 pick(获取元素)、drop(取消获取)、patch(修改元素) 指令来替代 swap(交换元素) 操作,因为交换两个元素的本质就是 获取元素 + 修改元素,而我在其中添加 drop 指令的目的则是使可视化过程更加具体,例如 pick 指令会将其选定的元素高亮,那么 drop 指令就可以取消该元素的高亮行为。 > 例如针对顺序表结构我可能会定义pick(获取元素)、drop(取消获取)、patch(修改元素)指令来替代swap(交换元素)操作,因为交换两个元素的本质就是获取元素+修改元素而我在其中添加rop指令的目的则是使可视化过程更加具体例如pick指令会将其选定的元素高亮那么drop指令就可以取消该元素的高亮行为。
我们会先选取一种编程语言(例如 TypeScript)来实现这套协议,在实现的过程中,需要考虑到未来将添加其他编程语言的支持,因此需要考虑到不同编程语言之间的差异,例如语法/类型系统差异,进而反过来优化这套协议,以使其能够更好地支持不同的编程语言,这是一个双向的过程。 我们会先选取一种编程语言(例如TypeScript)来实现这套协议,在实现的过程中,需要考虑到未来将添加其他编程语言的支持,因此需要考虑到不同编程语言之间的差异,例如语法/类型系统差异,进而反过来优化这套协议,以使其能够更好地支持不同的编程语言,这是一个双向的过程。
当我们实现了这套协议后,就形成了一套 SDK用户可以在平台中标记想要记录的数据结构(注册跟踪器),并调用 SDK 提供的 API 来记录数据结构的变化,当代码被编译运行后,就能够自然地获取数据结构的变化过程(序列化为 JSON)。 当我们实现了这套协议后就形成了一套SDK用户可以在平台中标记想要记录的数据结构(注册跟踪器)并调用SDK提供的API来记录数据结构的变化当代码被编译运行后就能够自然地获取数据结构的变化过程(序列化为JSON)。
所以本项目的最终目标就是设计这一套语言无关的协议,并在多个主流编程语言上实现该协议。 所以本项目的最终目标就是设计这一套语言无关的协议,并在多个主流编程语言上实现该协议。

View File

@@ -1,6 +1,6 @@
## Tracer 是什么? ## Tracer 是什么?
Tracer 代表一个数据结构变化的跟踪器,用户可以通过调用 Tracer 提供的 API 来记录数据结构的变化。每当用户想要记录一个数据结构的变化时,就需要创建一个对应的 Tracer。这种将数据结构变化的记录留给用户的设计使得用户可以在算法的实现中灵活地记录数据结构的变化从而降低对原有算法代码逻辑的侵入性同时也在一定程度上提高了可视化效果的灵活度用户可以自由控制记录数据结构变化的时机。 Tracer代表一个数据结构变化的跟踪器用户可以通过调用Tracer提供的API来记录数据结构的变化。每当用户想要记录一个数据结构的变化时就需要创建一个对应的Tracer。这种将数据结构变化的记录留给用户的设计使得用户可以在算法的实现中灵活地记录数据结构的变化从而降低对原有算法代码逻辑的侵入性同时也在一定程度上提高了可视化效果的灵活度用户可以自由控制记录数据结构变化的时机。
## Tracer 类型 ## Tracer 类型
@@ -12,8 +12,5 @@ Tracer 代表一个数据结构变化的跟踪器,用户可以通过调用 Tra
- LinkTracer: 链表 - LinkTracer: 链表
- TreeTracer: 树 - TreeTracer: 树
- GraphTracer: 图 - GraphTracer: 图
---
- LogTracer: 日志 - LogTracer: 日志
- ControlTracer: 控制 - ControlTracer: 控制

View File

@@ -1,35 +1,113 @@
import type { TracerCommand } from '../types'; import type { TracerCommand } from '../types';
const createTracerContext = () => { class ScopeError extends Error {
const commands: TracerCommand[] = []; constructor(message?: string) {
super(message);
this.name = 'ScopeError';
}
}
// if (typeof process !== 'undefined' && typeof process.on === 'function') { const SCOPE_STATE = {
// process.on('exit', () => { INITIAL: 'initial',
// if (commands.length > 0) { SCOPING: 'scoping',
// console.log(commands); SCOPED: 'scoped',
// } } as const;
// });
// } type ScopeState = (typeof SCOPE_STATE)[keyof typeof SCOPE_STATE];
type Error = {
stack?: string;
};
type ScopeResult = {
// 记录指令序列
commands: TracerCommand[];
// 记录异常信息
exception: Error;
// 检查scope的执行状态
state: ScopeState;
};
const createTracerContext = () => {
const scopeResult: ScopeResult = {
commands: [],
exception: {},
state: SCOPE_STATE.INITIAL,
};
const command = (command: TracerCommand) => {
scopeResult.commands.push(command);
};
// TODO: 输出指令序列和错误信息
const flush = () => {
const { commands, exception } = scopeResult;
if (commands.length > 0 || !!exception.stack) {
console.log(JSON.stringify({ commands, exception }, null, 2));
}
scopeResult.commands = [];
scopeResult.exception = {};
};
const getTracerContext = () => { const getTracerContext = () => {
const command = (command: TracerCommand) => {
commands.push(command);
};
// TODO: 输出指令序列
const dump = () => {
return commands;
};
return { return {
command, command,
dump, flush,
}; };
}; };
// 由于JavaScript/TypeScript在语言层面缺乏统一的“程序退出钩子”机制
// 所以我们无法在算法执行结束后自动地输出指令序列和异常信息,
// 尽管像Node.js这样的运行时提供了process.on('exit')但是我们希望保持SDK的环境无关性
// 所以为了不依赖于特定运行时的API我们设计了scope方法来创建一个显式的生命周期
// 算法逻辑将会被一个回调函数包裹作为routine参数
// 在routine执行完毕后scope会向外输出指令序列和异常信息。
const scope = (routine: () => void | Promise<void>) => {
if (scopeResult.state === SCOPE_STATE.SCOPED) {
throw new ScopeError('ScopeError: Detect multiple scope.');
}
if (scopeResult.state === SCOPE_STATE.SCOPING) {
throw new ScopeError('ScopeError: Detect nested scope.');
}
scopeResult.state = SCOPE_STATE.SCOPING;
// 如果routine是同步函数那可能会抛出同步错误所以需要用try-catch包裹
try {
const result = routine();
// 如果routine是异步函数需要用catch捕获异步错误
if (result instanceof Promise) {
result
.then(() => {
scopeResult.state = SCOPE_STATE.SCOPED;
flush();
})
.catch((error) => {
// 如果是ScopeError需要抛出否则会在嵌套调用scope时被外层scope捕获
if (error instanceof ScopeError) {
throw error;
}
scopeResult.exception.stack = (error as Error).stack;
scopeResult.state = SCOPE_STATE.SCOPED;
flush();
});
} else {
// 如果routine是同步函数执行完成后需要设置scope状态
scopeResult.state = SCOPE_STATE.SCOPED;
flush();
}
} catch (error) {
if (error instanceof ScopeError) {
throw error;
}
scopeResult.exception.stack = (error as Error).stack;
scopeResult.state = SCOPE_STATE.SCOPED;
flush();
}
};
return { return {
getTracerContext, getTracerContext,
scope,
}; };
}; };
export const { getTracerContext } = createTracerContext(); export const { getTracerContext, scope } = createTracerContext();

View File

@@ -1,44 +1,51 @@
import { getTracerContext } from '../context'; import { getTracerContext } from '../context';
import type { JsonValue } from '../types'; import type { JsonValue } from '../types';
interface BaseArrayTracerCreateOptions { interface ArrayTracerCreateOptions<T extends JsonValue> {
description?: string; description?: string;
initial?: T[];
walker?: (builder: { add: (item: T) => void }) => void;
} }
interface ArrayTracerCreateOptionsFromArray< type ArrayTracerMetadata = {
T extends JsonValue[], // initial: JsonValue[];
> extends BaseArrayTracerCreateOptions { length: number;
array: T; };
walker?: never;
}
interface ArrayTracerCreateOptionsFromWalker< export const createArrayTracer = <T extends JsonValue = JsonValue>(
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[]>(
options: ArrayTracerCreateOptions<T>, options: ArrayTracerCreateOptions<T>,
) => { ) => {
const { description, array, walker } = options; const { description, initial, walker } = options;
const tracer = crypto.randomUUID(); const tracer = crypto.randomUUID();
// 优化:仅维护数组长度作为影子状态,这在 C++/Java 等强类型语言中也极易实现(仅需一个 int 变量) const metadata: ArrayTracerMetadata = {
// 这种“最小必要状态”策略既能实现越界校验,又避免了在强类型语言中处理泛型存储的复杂性,且内存开销极低。 length: 0,
let arrayLength = array ? array.length : 0; };
const buildInitial = () => {
if (!!initial) {
return [...initial];
}
if (!!walker) {
const initial: T[] = [];
walker({
add: (item) => {
initial.push(item);
},
});
return initial;
}
return [];
};
// console.log(_initial);
const { command } = getTracerContext(); const { command } = getTracerContext();
const validateIndex = (index: number) => { const validateIndex = (index: number) => {
if (index < 0 || index >= arrayLength) { if (index < 0 || index >= metadata.length) {
throw new Error( throw new Error(
`[ArrayTracer] Index out of bounds: index ${index} is not within [0, ${arrayLength})`, `ArrayTracer: Index out of bounds: index ${index} is not within [0, ${metadata.length})`,
); );
} }
}; };
@@ -47,18 +54,18 @@ export const createArrayTracer = <T extends JsonValue[]>(
type: 'ArrayTracer', type: 'ArrayTracer',
tracer: tracer, tracer: tracer,
action: 'create', action: 'create',
params: { payload: {
description: description ?? 'ArrayTracer', description: description ?? 'ArrayTracer',
array: array ?? [], initial: buildInitial(),
}, },
}); });
// size 为正时,数组长度增加;为负时,数组长度减少(但不能小于 0 // size 为正时,数组长度增加;为负时,数组长度减少(但不能小于 0
const scale = (size: number) => { const scale = (size: number) => {
arrayLength += size; metadata.length += size;
if (arrayLength < 0) { if (metadata.length < 0) {
throw new Error( throw new Error(
`[ArrayTracer] Invalid size: ${size}, array length cannot be negative`, `ArrayTracer: Invalid size: ${size}, array length cannot be negative`,
); );
} }
@@ -66,7 +73,7 @@ export const createArrayTracer = <T extends JsonValue[]>(
type: 'ArrayTracer', type: 'ArrayTracer',
tracer: tracer, tracer: tracer,
action: 'scale', action: 'scale',
params: { payload: {
size: size, size: size,
}, },
}); });
@@ -79,7 +86,7 @@ export const createArrayTracer = <T extends JsonValue[]>(
type: 'ArrayTracer', type: 'ArrayTracer',
tracer: tracer, tracer: tracer,
action: 'pick', action: 'pick',
params: { payload: {
index: index, index: index,
}, },
}); });
@@ -92,20 +99,20 @@ export const createArrayTracer = <T extends JsonValue[]>(
type: 'ArrayTracer', type: 'ArrayTracer',
tracer: tracer, tracer: tracer,
action: 'drop', action: 'drop',
params: { payload: {
index: index, index: index,
}, },
}); });
}; };
const patch = (index: number, value: T[number]) => { const patch = (index: number, value: T) => {
validateIndex(index); validateIndex(index);
command({ command({
type: 'ArrayTracer', type: 'ArrayTracer',
tracer: tracer, tracer: tracer,
action: 'patch', action: 'patch',
params: { payload: {
index: index, index: index,
value: value, value: value,
}, },
@@ -119,7 +126,7 @@ export const createArrayTracer = <T extends JsonValue[]>(
type: 'ArrayTracer', type: 'ArrayTracer',
tracer: tracer, tracer: tracer,
action: 'unset', action: 'unset',
params: { payload: {
index: index, index: index,
}, },
}); });

View File

@@ -6,36 +6,36 @@ type BaseArrayTracerCommand = BaseTracerCommand & {
type ArrayTracerCreateCommand = BaseArrayTracerCommand & { type ArrayTracerCreateCommand = BaseArrayTracerCommand & {
action: 'create'; action: 'create';
params: { payload: {
description: string; description: string;
array: JsonValue[]; initial: JsonValue[];
}; };
}; };
type ArrayTracerScaleCommand = BaseArrayTracerCommand & { type ArrayTracerScaleCommand = BaseArrayTracerCommand & {
action: 'scale'; action: 'scale';
params: { payload: {
size: number; size: number;
}; };
}; };
type ArrayTracerPickCommand = BaseArrayTracerCommand & { type ArrayTracerPickCommand = BaseArrayTracerCommand & {
action: 'pick'; action: 'pick';
params: { payload: {
index: number; index: number;
}; };
}; };
type ArrayTracerDropCommand = BaseArrayTracerCommand & { type ArrayTracerDropCommand = BaseArrayTracerCommand & {
action: 'drop'; action: 'drop';
params: { payload: {
index: number; index: number;
}; };
}; };
type ArrayTracerPatchCommand = BaseArrayTracerCommand & { type ArrayTracerPatchCommand = BaseArrayTracerCommand & {
action: 'patch'; action: 'patch';
params: { payload: {
index: number; index: number;
value: JsonValue; value: JsonValue;
}; };
@@ -43,7 +43,7 @@ type ArrayTracerPatchCommand = BaseArrayTracerCommand & {
type ArrayTracerUnsetCommand = BaseArrayTracerCommand & { type ArrayTracerUnsetCommand = BaseArrayTracerCommand & {
action: 'unset'; action: 'unset';
params: { payload: {
index: number; index: number;
}; };
}; };