diff --git a/AGENT.md b/AGENT.md index 0d4898c..70e7389 100644 --- a/AGENT.md +++ b/AGENT.md @@ -56,6 +56,13 @@ esp-emulator wasm 能力边界见 `docs/EMU-API.md` 与 `docs/FEASIBILITY.md`(M0 6. 状态推送有 33ms 节流 + **尾随定时器**(保证最后一次 dirty 必达)——别删掉尾随逻辑, 那是修过的真实缺陷(状态静默丢失)。 7. sigbus 按批做 gpio_state 差分,**联合掩码**(out|oe)每引脚每批至多通知一次。 +8. **同语义代码必须复用,禁止复制一份再改**:同一逻辑只允许一份实现,所有 + 入口(行内 chip / 详情编辑器 / 行图标 / 新插件)调用同一函数—— + GPIO 可选项= `config-editor.ts fillPinOptions()`;V·G 徽章推导=`rowChips()`; + 插件多视觉元素(如 LED 灯泡+行图标)共用同一 setter/render,不许给图标 + 单写一份样式逻辑;面板对 View 固定 **先 `rowIcon` 后 `setConfig`**, + 保证配置式样能覆盖到图标。改行为前先找有没有既有函数,有就复用; + review 发现两处雷同代码时,先抽公共函数再改。 ## 4. 曾踩过的 bug(防止回潮) @@ -69,6 +76,8 @@ esp-emulator wasm 能力边界见 `docs/EMU-API.md` 与 `docs/FEASIBILITY.md`(M0 | 按键 activeLow 电平写反 | idle=高、pressed=低(active-low 上拉接法);单测固定语义 | | Worker URL 相对**页面**解析,不是相对 import 的模块 | worker-client 里写页面相对路径 `build/core/worker.js` | | 节流窗口丢最后一次状态 | 尾随 flush(见 §3.7) | +| LED 行图标颜色与详情灯泡不一致(0.3.0) | 面板曾先 setConfig 后 rowIcon,且插件两处各自设色;统一 setColour + 固定调用顺序(§3.8) | +| 换芯片加载固件后,详情编辑器 GPIO 选项仍是旧芯片(0.3.0) | 编辑器把 validPins 快照在构建时;芯片变化必须重建编辑器;选项构建统一走 fillPinOptions(§3.8) | ## 5. 测试与验证约定 diff --git a/README.md b/README.md index 867061a..6cc0a1b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ 为乐鑫 [esp-emulator](https://github.com/espressif/esp-emulator) 浏览器形态(wasm)做的**模拟外设面板**:LED、按键、触摸按键、拨动开关……以插件方式不断添加,不需要改动官方 `www/` 里的任何基础文件。 ``` -esp-emu-peris.html 入口页面(UART0 控制台 + 右侧 Peripherals/Debug 双标签) +esp-emu-peris.html 入口页面(UART0 控制台 | 外设行式面板 | Debug,三栏同屏) src/ TypeScript 源码(npm run build → build/) core/ worker 侧框架:version/types/protocol/chips/mmio/sigbus/registry/runtime/worker ui/ 页面侧模块:app/worker-client/terminal/chart/debug-panels/peris-panel/config-editor @@ -37,8 +37,10 @@ npm run serve # http://127.0.0.1:8080/esp-emu-peris.html # 浏览器打开上面的地址,选择芯片、拖入 merged flash image(.bin) # 注意:必须是 idf.py merge-bin 生成的完整镜像,裸 app bin 无法启动 -# 右侧 Peripherals 标签:Add 添加 LED/按键等,选好 GPIO; -# Export/Import 导出/导入接线配置(JSON,自动存 localStorage) +# 外设栏(控制台右侧):Add 添加 LED/按键等;每外设一行——行内图标可交互 +# (LED 点击换色/按键按压),名称、GPIO 方框、V/G 电平徽章点击即改, +# ▸ 展开详情与高级配置,⠿ 拖动排序;Export/Import 导出/导入接线配置 +# (JSON,自动存 localStorage) ``` 把外设面板**发布**进官方 www 目录(只增不改;先 build): diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6c4491e..fff1db9 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -5,11 +5,13 @@ ``` ┌───────────────────────────────── 页面(page)─────────────────────────────────┐ │ esp-emu-peris.html 上游样式原块(便于升级 diff)+ │ +│ 三栏同屏:UART0 控制台 │ 外设面板 │ Debug(0.3.0 起不再标签切换) │ │ build/ui/* (TypeScript 编译产物) │ │ app.ts 编排器:机器状态、Run/Pause/固件/网络控件、消息路由 │ │ worker-client Worker 唯一入口(其它模块一律经由它收发消息) │ │ terminal / chart / debug-panels UART 控制台 / 性能图 / Debug 只读面板 │ -│ peris-panel + config-editor Peripherals 面板与通用配置编辑器 │ +│ peris-panel + config-editor 行式外设列表(图标/名称/GPIO/V·G 徽章 │ +│ + 展开/删除/拖动)与详情配置编辑器 │ └───────────────▲───────────────────────────────────────────────▲────────────┘ postMessage(协议见 core/protocol.ts) │ DOM ┌───────────────┴───────────────────────────────────────────────▼────────────┐ @@ -80,9 +82,11 @@ npm run serve # python serve.py(合并服务仓库与 www/pkg) npm run sync # 发布进官方 www(先 build) ``` -测试分层:**单元**(纯逻辑:位运算/电平语义/校验——快、定位准)→ **集成**(真实 -wasm+固件全链路)→ **GUI**(手动清单:页面加载、加设备、载固件、Run 后 LED 翻转、 -配置导入导出往返;浏览器自动化待网络环境允许时引入 Playwright, +测试分层:**单元**(纯逻辑:位运算/电平语义/校验/行式徽章推导——快、定位准)→ +**集成**(真实 wasm+固件全链路)→ **GUI**(手动清单:页面加载、三栏同屏可见、加设备 +后行式渲染、行内改名/改 GPIO/翻 V·G、行内图标交互(点灯换色/按压/开关)、展开详情 +与配置编辑、拖动排序、载固件、Run 后 LED 翻转、配置导入导出往返;浏览器自动化待 +网络环境允许时引入 Playwright, `PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright`)。 ## 6. 升级流程(摘要,详见 UPGRADE.md) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1f13b6a..e86fcfd 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -3,6 +3,41 @@ 版本三处同步:`src/core/version.ts`、`package.json`、本文件。 规则:主版本=协议/架构破坏性变更;次版本=新能力;修订=修复与小改。 +## peris 0.3.1 + +- **修复**:LED 行内图标颜色不随配置(与详情灯泡不一致)。根因有二:面板先 + `setConfig` 后 `rowIcon`(图标元素尚不存在,颜色没应用到它),插件里两处各自 + 设色。现 led 插件统一 `setColour()` 同时应用到灯泡与行图标,面板固定 + **先 rowIcon 后 setConfig**。 +- **修复**:加载固件换芯片后,展开详情的 GPIO 选项仍停留在旧芯片(行内 chip + 一直正确)。引脚选项构建抽取为 `fillPinOptions()` 单一实现(详情编辑器与 + 行内 chip 共用);`PerisPanel.refreshForChip()` 在固件加载/复位重载后检测 + 芯片变化并重建详情编辑器。 +- **AGENT.md 新增铁律 §3.8**:"同语义代码必须复用"——以上两个 bug 皆为复制 + 代码只改一处所致;同时登记两条 bug 记录(§4)。 +- `npm test`/`npm run test:unit` 改为显式传 .test.mjs 文件列表(AGENT.md §1 + 记录的 Windows/Git Bash 目录解析坑,目录形式在本机直接 MODULE_NOT_FOUND)。 + +## peris 0.3.0 + +- **三栏同屏布局**:外设面板独立成栏,置于 UART 控制台与 Debug 之间,与 Debug + 同宽(360px,窄屏自适应收窄/堆叠);移除 Peripherals/Debug 标签切换,三栏同时可见。 + 外设面板改为内部滚动(工具栏常驻),Debug 面板加标题条。 +- **行式外设列表**(类 Photoshop 图层):每外设一行——左侧依次为交互图标、名称 + (点击原地改名)、GPIO 号方框(点击弹出选单)、V/G 闲置电平徽章(点击翻转, + V=上拉到 VCC、G=下拉到 GND);右侧靠齐展开按钮、删除按钮、拖动排序柄。 + 展开后的详情区(类 Blender 属性分组)显示设备控件主体 + 全量配置编辑器 + (一条属性一行);行内放不下的高级功能都收进详情。 +- **View API 扩展**(可选项,向后兼容):`View.rowIcon(el)` 定制行内交互图标; + View 构造函数新增第三参 `host.updateConfig(patch)`,供 View 请求改配置 + (如 LED 行内图标点击换色);configSchema 新增 `chip:'pull'` / + `pullWhenChecked` 字段声明 V/G 徽章。类型契约见 `build/core/types.d.ts`。 +- **拖动排序**:拖柄按下才允许拖拽(避免误触);松手后按 DOM 序回写配置并持久化 + (设备集合不变,不重建 worker 实例)。文件拖放导入与行排序互不干扰(按 + dataTransfer 类型区分)。 +- 新增单测:内置插件 rowIcon 存在性、`rowChips()` 引脚/V·G 徽章推导 + (含 GPIO0、`pullWhenChecked:'low'` 反例)。 + ## peris 0.2.0 - **架构重构**:框架层 TypeScript 化(`src/core` + `src/ui`,tsc 逐文件编译到 @@ -28,7 +63,9 @@ | typeId | 版本 | 变更 | |---|---|---| -| led | 1.0.0 | 初版:输出轮询、颜色、active-low、floating 提示 | -| button | 1.0.0 | 初版:瞬时按压驱动输入(含 0.2.0 前的电平语义修复) | -| touch-button | 1.0.0 | 初版:继承 button,触摸板视图(触摸外设未模拟,GPIO 语义) | -| toggle-switch | 1.0.0 | 初版:保持型开关、初始状态、onEmuReady 重驱动 | +| led | 1.1.1 | 修复:行图标颜色与配置不一致——统一 setColour 应用到灯泡+行图标 | +| button | 1.1.0 | 行内按压图标(press/release);activeLow 声明为 V/G 徽章 | +| touch-button | 1.1.0 | 行内触摸图标(继承 button 的徽章声明) | +| toggle-switch | 1.1.0 | 行内迷你拨动开关(点击 toggle);activeLow 声明为 V/G 徽章 | + +历史:led/button/touch-button/toggle-switch 1.0.0 初版(见 peris 0.1.0/0.2.0 条目)。 diff --git a/docs/DEVICE-API.md b/docs/DEVICE-API.md index 1be9a69..771cf58 100644 --- a/docs/DEVICE-API.md +++ b/docs/DEVICE-API.md @@ -18,11 +18,13 @@ export class Device { // 运行在 worker,与模拟器交互 static category = 'basic'; // Add 菜单分组 static description = '...'; // 一行说明(tooltip) static defaultConfig = {...}; // 新建实例的默认配置 - static configSchema = [ // 通用配置编辑器字段 + static configSchema = [ // 通用配置编辑器字段(展开详情内,一条一行) { key: 'pin', label: 'GPIO pin', type: 'pin' }, { key: 'color', label: 'Colour', type: 'color' }, { key: 'name', label: 'Label', type: 'text' }, // type 还支持: number(min/max/step)、select(options:[{value,label}])、checkbox + // checkbox 可加 chip:'pull' + pullWhenChecked:'high'|'low' + // → 设备行上显示 V/G 闲置电平徽章(点击翻转),详见下文"行式 UI" ]; static pins(cfg) { return [cfg.pin]; } // 声明占用的 GPIO(用于冲突检测) @@ -34,13 +36,38 @@ export class Device { // 运行在 worker,与模拟器交互 destroy() {} // 释放/恢复引脚 } -export class View { // 运行在页面,渲染设备卡片主体 - constructor(el, sendCmd) {} // el=卡片主体容器; sendCmd(cmd, params) - render(state) {} // state 来自 Device.getState() +export class View { // 运行在页面,渲染设备视图 + constructor(el, sendCmd, host) {} // el=详情区主体容器; sendCmd(cmd, params); + // host.updateConfig(patch) 请求改本设备配置 + rowIcon(el) {} // 可选:行内交互图标(el≈24px 容器),见下文 + render(state) {} // state 来自 Device.getState();行图标与详情主体一起驱动 setConfig(cfg) {} // 可选:配置编辑后回调(如换 LED 颜色) } ``` +## 行式 UI 约定(0.3.0+) + +外设面板是三栏布局中的独立一栏(控制台 | 外设 | Debug),每种外设占一行: + +``` +[交互图标] [名称] [GPIO号] [V/G] [▸展开] [×删除] [⠿拖动] +``` + +- **交互图标**(`View.rowIcon(el)`):可点击的迷你控件,操作语义由插件定义—— + LED 点击换色(取色器,经 `host.updateConfig({color})` 写回配置)、按键按下/ + 松开(连发 press/release)、拨动开关点击 toggle。`render(state)` 里同步刷新 + 图标状态(亮灭/按下/ON)。不实现 rowIcon 时显示首字母占位块。 + **调用顺序**:面板固定先 `rowIcon` 后 `setConfig`,配置式样可覆盖到图标; + 图标与详情主体是同一状态的两个视觉,插件必须共用同一 setter/render + (参考 led.js 的 `setColour()`),禁止为图标单写一份样式逻辑。 +- **名称 / GPIO 方框 / V·G 徽章**:由面板通用渲染,点击原地修改 + (名称与 GPIO 走内联编辑,V/G 徽章直接翻转)。GPIO 方框取 schema 第一个 + `type:'pin'` 字段;V/G 徽章取标记 `chip:'pull'` 的 checkbox——勾选时若 + `pullWhenChecked:'high'`(默认)显示 V(上拉到 VCC),否则 G;未勾选相反。 +- **展开详情**:设备控件主体(`el`,即构造函数第一个参数)+ 全量配置编辑器 + (一条属性一行)。行内放不下的高级功能一律放详情。 +- **拖动排序** ⠿:按下拖柄才启用拖拽,松手回写 devices 顺序(不重建 worker)。 + ## ctx(worker 侧设备上下文) | 成员 | 说明 | @@ -79,7 +106,9 @@ export class View { // 运行在页面,渲染设备卡片主体 2. **引脚输入**:`ctx.setPinInput` 会真实进入固件(读值+中断),按键类设备在 `onEmuReady` 里要重新驱动电平(模拟器重载后电平复位)。 3. **时间**:`vTaskDelay` 期间模拟器时间快进,不要试图在 CPU 睡眠窗口做引脚级波形捕获;CPU 活跃的位绑定波形可在 run_batch 批间采样,但请节制(细批约 99 MIPS,粗批 5000+ MIPS)。 4. 状态推送节流 ~30fps;`getState()` 返回的对象会 postMessage,保持小而可序列化。 -5. View 中所有用户交互统一通过 `sendCmd(cmd, params)` 发给 Device,不要在页面侧直接操作模拟器。 +5. View 中触发**设备动作**统一走 `sendCmd(cmd, params)`;**修改配置**(如行内换色) + 走 `host.updateConfig(patch)`——配置的唯一事实源在页面 PerisPanel,worker 侧 + 不得缓存/改配置。不要在页面侧直接操作模拟器。 ## 示例 diff --git a/esp-emu-peris.html b/esp-emu-peris.html index 707f7fd..8cede14 100644 --- a/esp-emu-peris.html +++ b/esp-emu-peris.html @@ -415,21 +415,17 @@
-