feat: add app management, local inference controls, and task statistics
This commit is contained in:
@@ -1,17 +1,19 @@
|
||||
# 应用导航与系统工具入口
|
||||
|
||||
本功能整理 GoodBuddy 主侧栏的入口层级:系统入口保持固定,内置应用通过最底部应用中心
|
||||
访问,并可常驻主导航;系统工具由右侧工作栏 Tab 承载。应用商店是后续功能,首期不提供
|
||||
本功能整理 GoodBuddy 主侧栏的入口层级:系统入口保持固定,知识库和智能心跳作为固定内置应用始终启用并显示;
|
||||
魔法笔记和本机推理可通过应用管理控制启用、常驻及排序。底部应用中心向上展开轻量锚定菜单,列出已启用应用;底部“管理应用”打开既有管理 Modal。系统工具由右侧工作栏 Tab 承载。应用商店是后续功能,首期不提供
|
||||
商店入口、推荐位或安装流程。
|
||||
|
||||
当前分期见 [PRD 第 8 节](./prd.md#8-分期),实施步骤见[技术设计第 11 节](./technical-design.md#11-实施顺序)。
|
||||
本轮更新实施计划与范围,旧版功能要求、验收和配套设计仍需按新计划同步,此项列为实现前的第一步。
|
||||
本轮 A–D 已接入生产源码:应用菜单、应用管理 Modal、共享应用设置、持久化偏好及本机推理。菜单不按打开历史或常驻状态筛选,无模态遮罩;点击应用行关闭菜单,知识库、智能心跳和魔法笔记打开主内容区页面。本机推理从菜单、常驻入口或管理 Modal 打开时均使用独立 Modal,保留底层工作区。侧栏依次显示知识库、智能心跳,再显示按偏好过滤的可选应用。两个固定应用在管理 Modal 中仅提供“打开”,不参与启用、常驻或排序,不增设通用空设置;原工作区配置及心跳单条计划 enabled 保持原义。验证范围及集成缺口见[实施进度](./progress.md)。
|
||||
|
||||
新增计划包括 [私有化市场与 YAML 交换](./prd.md#fr-15-私有化市场与-yaml-交换后续):
|
||||
通过市场 URL 获取应用,支持 `.yml` 导入导出,未来由 ShareServer 提供私有目录与分发。
|
||||
[本机模型管理入口](./prd.md#fr-16-本机模型管理入口新增计划项)可独立于商店交付,拟作为
|
||||
可常驻左侧的内置管理应用,区分受管进程、外部服务和进程内模型。两项均为需求记录,
|
||||
未实现,相关后续验收见 [User Stories](./user-stories.md#7-后续需求场景)。
|
||||
[本机模型管理入口](./prd.md#fr-16-本机模型管理入口新增计划项)已独立于商店接入,作为
|
||||
可常驻左侧的“本机推理”内置应用,直接查看语音、OCR、向量等本机服务状态与可用资源指标,不显示任务历史。向量服务显示独立进程的 CPU 和工作集内存;ASR、OCR 与宿主共享进程,逐行说明无法独立统计的原因。
|
||||
本轮已接入 `local-inference` 管理 Modal、类型化 IPC、真实请求登记及引擎管理;实际支持清单见
|
||||
[技术设计](./technical-design.md#111-本机推理任务与执行服务),市场仍为后续需求。
|
||||
相关验收见 [User Stories](./user-stories.md#7-后续需求场景)。
|
||||
|
||||
项目活动汇总独立于上述应用中心计划,范围见 [FR-14](./prd.md#fr-14-项目活动汇总)。
|
||||
计数规则由[活动逻辑](./logic-design.md#9-项目活动汇总)定义,入口与级联菜单见
|
||||
@@ -21,6 +23,8 @@
|
||||
|
||||
## 文档导航
|
||||
|
||||
2026-09-16 后续应用组织决策:计划将现有“智能心跳”入口整合、升级为“监督者”,内部包含工作回顾和故事线图谱。未来市场复用同一应用身份,不增加平级重复入口。该更名与整合尚未实施;当前智能心跳为固定应用,不提供应用级启用、常驻或排序。目标定义见[监督者产品设计](../conversation-supervision/supervisor-prd.md)。
|
||||
|
||||
| 文档 | 权威职责 |
|
||||
| --- | --- |
|
||||
| [PRD](./prd.md) | 用户问题、产品边界、功能要求与产品验收 |
|
||||
@@ -28,7 +32,7 @@
|
||||
| [功能逻辑设计](./logic-design.md) | 分类规则、入口状态、优先级与失败结果 |
|
||||
| [UI 设计](./ui-design.md) | 侧栏信息架构、控件、文案、键盘和响应式行为 |
|
||||
| [技术设计](./technical-design.md) | 现有模块映射、状态契约、持久化和实施顺序 |
|
||||
| [实施进度](./progress.md) | 项目活动级联的生产实现与验证记录 |
|
||||
| [实施进度](./progress.md) | 应用中心、设置及项目活动级联的实现与验证记录 |
|
||||
| [应用中心交互 Demo](./navigation-demo.html) | 对比六个候选方案;配套 CSS 和 JS 仅服务原型 |
|
||||
|
||||
Demo 可直接在支持原生 dialog、popover 的现代浏览器中打开,无需依赖或构建。
|
||||
@@ -36,7 +40,7 @@ Demo 可直接在支持原生 dialog、popover 的现代浏览器中打开,无
|
||||
A–E 仅展示内置应用,没有商店入口、推荐或安装操作;F 单独演示未来私有市场。
|
||||
模型服务、资源指标和工具内容均为演示数据,不执行真实安装、模型加载或进程操作。
|
||||
窄屏上下排列侧栏与主内容,应用页面隐藏输入区,对话页面显示输入区。
|
||||
模型管理与 F 方案均为计划预览,不代表正式功能已交付,也不纳入首期验收。
|
||||
Demo 的模型管理和 F 方案均使用模拟数据;正式本机推理 Modal 的交付范围以技术清单和实施进度为准,旧页面原型不作为 Modal 验收依据。
|
||||
底部输入区共用一个简化候选布局:附件、语音、Runtime、模式和发送位于主操作行,专家、
|
||||
知识范围及 Runtime 专属选项通过“选项”展开,非默认选择显示为可移除标签。可体验文件名
|
||||
附件、模拟语音、模板填入、原生命令选择、8 秒模拟回复、发送排队、立即插入、删除、停止,
|
||||
@@ -48,11 +52,13 @@ A–E 仅展示内置应用,没有商店入口、推荐或安装操作;F 单
|
||||
进入会话层,并提供返回按钮。页面上方可切换“有待处理、仅运行中、仅当前项目运行、全部
|
||||
空闲”四种场景。项目与会话均为演示数据;该级联交互已接入正式产品活动入口,范围与
|
||||
验证记录见[实施进度](./progress.md)。
|
||||
按最新讨论,Demo 已移除左侧系统工具快捷区:文件、终端、浏览器和资源监控只在右侧
|
||||
工作栏 Tab 中演示,不列入应用中心。上方正式文档仍保留原设计,待方案确定后同步。
|
||||
Demo 已移除左侧系统工具快捷区:文件、终端、浏览器和资源监控只在右侧
|
||||
工作栏 Tab 中演示,不列入应用中心。旧原型仍直接打开整窗 Modal,其中使用可搜索应用列表。
|
||||
|
||||
Demo 待同步更正:本节仅描述旧原型,不是生产行为或验收依据。原型直接打开 Modal,以及知识库和智能心跳的启停、常驻、排序、通用设置,均已过时;生产采用上述轻量菜单及两个固定应用规则。本轮不修改 Demo 文件。
|
||||
|
||||
六个方案共用“我的应用”区:通过 + 或“全部应用”选择常驻应用,编辑时可拖动或使用
|
||||
上下箭头排序、移除入口。魔法笔记、知识库、智能心跳及计划预览“本机模型”默认全部启用且常驻。
|
||||
上下箭头排序、移除入口。魔法笔记、知识库、智能心跳及计划预览“本机推理管理”默认全部启用且常驻。
|
||||
取消常驻仅移除导航捷径,仍可从应用中心打开;关闭应用隐藏业务入口,应用中心保留设置与
|
||||
重新开启入口,并记住常驻偏好。当前应用页关闭后显示不可用状态及设置恢复按钮。
|
||||
知识库关闭时同步隐藏侧栏入口、会话选项中的知识范围和检索方式、已选知识标签;重新开启
|
||||
@@ -62,12 +68,13 @@ A–E 仅展示内置应用,没有商店入口、推荐或安装操作;F 单
|
||||
所有内置应用提供应用中心、应用页面和全局设置入口,均打开同一应用设置面板,分别控制
|
||||
启用与常驻。魔法笔记面板另含显示未完成待办数量、AI 评论方式
|
||||
(即时/保存后自动/保存后手动)、AI 评论形式(长评 + 要点/长评/要点)。现有“显示
|
||||
魔法笔记入口”在原型中与“常驻左侧菜单”统一。启用、常驻、待办数量显示默认开启;
|
||||
魔法笔记入口”不在原型中重复提供。正式迁移保留其业务启用值,常驻使用独立配置。启用、常驻、待办数量显示默认开启;
|
||||
评论方式默认即时,评论形式默认长评 + 要点。侧栏待办数为固定的 3 项演示数据。
|
||||
修改即时生效于原型,不调用 AI 或写入真实应用设置。
|
||||
|
||||
本机模型主内容区展示紧凑服务列表,包含模型、用途、引擎、管理方式、状态、占用和使用方。
|
||||
受管进程模拟启动、停止、重启;外部服务仅提供连接配置预览;进程内模型模拟加载与释放。
|
||||
此 Demo 中的任务、进度、使用方与服务内存为旧方案模拟内容;正式 Modal 已收窄为服务状态列表,见 [FR-16](./prd.md#fr-16-本机模型管理入口新增计划项)。
|
||||
引擎和管理方式收进详情;受管进程模拟启动、停止、重启,进程内模型模拟加载与释放。
|
||||
服务停止或重启后任务标为中断,重新启动不自动恢复任务;任务级取消仍待实际引擎接入。
|
||||
影响使用方的操作先显示就地确认,取消保留原状态;隐藏或关闭管理应用不改变服务状态。
|
||||
|
||||
F 提供私有市场 URL、模拟获取目录、模拟不可达与重试,不发起网络请求或切换公共来源。
|
||||
@@ -97,7 +104,7 @@ Notion 收藏和 VS Code Profiles,当前版本已移除场景工具包与快
|
||||
| 术语 | 定义 |
|
||||
| --- | --- |
|
||||
| 系统入口 | GoodBuddy 工作流程或审计所必需的固定入口,用户不能卸载、隐藏或改变类型 |
|
||||
| 应用 | 在主内容区打开完整一级页面的功能,可显示在“当前对话”所在的主导航区 |
|
||||
| 应用 | 可显示在主导航区的功能;知识库、智能心跳和魔法笔记打开一级页面,本机推理打开保留底层工作区的 Modal |
|
||||
| 系统工具 | GoodBuddy 自带且不可卸载的辅助能力,包括文件、终端、浏览器和资源监控,由右侧助手工作栏 Tab 承载 |
|
||||
| 插件应用 | 未来由插件提供的一级页面入口 |
|
||||
| 插件工具 | 未来由插件提供的辅助面板或命令入口 |
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
|
||||
| 关联 User Stories | [User Stories](./user-stories.md) |
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
1. “当前对话”“运行记录”和“设置”始终是系统入口。
|
||||
2. 运行记录不进入工具注册表、应用显隐设置或插件市场。
|
||||
3. 文件浏览器、终端、浏览器和资源监控始终是系统工具,不能卸载或被插件替换。
|
||||
4. 左下角只提供快捷入口,工具内容仍由助手工作栏承载。
|
||||
5. 隐藏应用不等于停用功能,也不删除、迁移或清理业务数据。
|
||||
6. 快捷入口不能扩大执行空间、文件、PTY、网络或浏览器权限。
|
||||
4. 左下角提供应用中心和独立固定设置入口;文件、终端、浏览器、资源监控只通过右侧工作栏 Tab 和“+”访问,不进入应用中心。
|
||||
5. 魔法笔记、本机推理的启用、常驻及顺序是独立状态。知识库和智能心跳始终启用并固定显示,不参与这些状态;取消可选应用常驻不关闭功能,关闭不删除数据或导航偏好。
|
||||
6. 入口不能扩大执行空间、文件、PTY、网络或浏览器权限。
|
||||
7. 应用中心始终可达,点击向上展开无模态遮罩的锚定菜单,只列已启用应用,不使用打开历史或常驻过滤。菜单底部“管理应用”打开既有 Modal,关闭的可选应用在其中仍可搜索、配置和重新开启。
|
||||
8. 所有入口和新请求的功能判断读取同一份保存成功的权威设置;保存中的目标值不提前生效。
|
||||
|
||||
## 2. 入口模型
|
||||
|
||||
@@ -25,14 +27,21 @@
|
||||
type NavigationContribution =
|
||||
| {
|
||||
type: 'system-entry'
|
||||
id: 'chat' | 'activity' | 'settings'
|
||||
id: 'chat' | 'activity' | 'application-center' | 'settings'
|
||||
placement: 'primary-start' | 'primary-end' | 'footer'
|
||||
}
|
||||
| {
|
||||
type: 'application'
|
||||
id: 'magic-notes' | 'knowledge' | 'heartbeat'
|
||||
id: 'knowledge' | 'heartbeat'
|
||||
source: 'builtin'
|
||||
visible: boolean
|
||||
fixed: true
|
||||
}
|
||||
| {
|
||||
type: 'application'
|
||||
id: 'magic-notes' | 'local-inference'
|
||||
source: 'builtin'
|
||||
enabled: boolean
|
||||
pinned: boolean
|
||||
}
|
||||
| {
|
||||
type: 'system-tool'
|
||||
@@ -41,7 +50,8 @@ type NavigationContribution =
|
||||
}
|
||||
```
|
||||
|
||||
该模型只覆盖首期入口。插件贡献模型随插件 Host、权限和生命周期另行设计。
|
||||
该模型是首期的逻辑投影,启用值来自各应用的唯一业务配置,常驻及顺序来自导航偏好。
|
||||
应用清单为魔法笔记、知识库、智能心跳、本机推理,打开无需安装。市场不加入本轮清单。
|
||||
|
||||
## 3. 主导航组合规则
|
||||
|
||||
@@ -49,35 +59,62 @@ type NavigationContribution =
|
||||
|
||||
```text
|
||||
当前对话
|
||||
可见的内置应用
|
||||
知识库(固定显示)
|
||||
智能心跳(固定显示)
|
||||
可选应用(按保存顺序,仅显示启用且常驻项)
|
||||
运行记录
|
||||
```
|
||||
|
||||
当前对话和运行记录不参与排序。首期内置应用默认顺序为魔法笔记、知识库、智能心跳;显隐
|
||||
设置只从中移除对应项,不改变固定入口的位置。
|
||||
当前对话、知识库、智能心跳和运行记录不参与排序。可选应用默认顺序为魔法笔记、本机推理,默认均常驻。两个固定应用始终位于可选应用之前,过滤可选项不留下空白。
|
||||
常驻、取消常驻和排序按设备保存,重启后恢复。排序只发生在应用区,不移动固定系统入口。
|
||||
保存完整可选应用顺序,关闭或取消常驻只过滤主导航,不删除顺序记录;重新开启或常驻时按
|
||||
该顺序恢复。管理列表编辑完整可选应用顺序;上下移动和拖动共用同一顺序写入,两个固定应用不作为拖动项或目标。
|
||||
|
||||
### 3.1 应用可见性与业务状态
|
||||
|
||||
| 导航可见 | 功能启用 | 结果 |
|
||||
| --- | --- | --- |
|
||||
| 是 | 是 | 显示入口并允许进入 |
|
||||
| 否 | 是 | 隐藏入口,后台和数据保持原状 |
|
||||
| 是 | 否 | 不显示不可用入口;设置页保留启用路径 |
|
||||
| 否 | 否 | 不显示入口,保留符合该功能自身规则的数据 |
|
||||
下表仅适用于可选应用。知识库和智能心跳始终可用,管理 Modal 中仅有“打开”,保留原工作区实际配置,不增加通用空设置页。心跳单条计划 enabled 仍按原业务规则控制计划。
|
||||
|
||||
“功能启用”由各应用现有产品设置负责。例如魔法笔记启停仍使用原设置;导航偏好不能覆盖它。
|
||||
| 常驻偏好 | 功能启用 | 结果 |
|
||||
| --- | --- | --- |
|
||||
| 是 | 是 | 主导航显示,可从主导航或应用中心打开 |
|
||||
| 否 | 是 | 主导航隐藏,仍可从应用中心打开,后台和数据保持原状 |
|
||||
| 是 | 否 | 主导航、应用菜单和业务入口隐藏,管理 Modal 保留设置,记住常驻及顺序 |
|
||||
| 否 | 否 | 主导航、应用菜单和业务入口隐藏,管理 Modal 保留设置,记住未常驻状态 |
|
||||
|
||||
管理 Modal 浏览和搜索包含关闭的可选应用,搜索不修改启用或常驻。轻量菜单仅显示已启用项,点击行先关闭菜单;本机推理打开独立 Modal,其余应用请求主内容区导航。管理 Modal 的“打开”和常驻入口遵守同一规则。打开已启用应用不自动常驻;
|
||||
取消当前应用的常驻不关闭当前页面或本机推理 Modal。关闭应用后不能从旧入口继续执行新的业务操作。
|
||||
停用时仍允许管理常驻偏好,界面说明重新开启后才会显示入口。
|
||||
|
||||
### 3.2 关闭应用与恢复
|
||||
|
||||
关闭与恢复仅适用于可选应用,知识库页面、会话控件及关联,以及智能心跳入口、页面和计划 enabled 不受可选应用偏好影响。
|
||||
|
||||
| 对象 | 关闭后的结果 | 重新开启后的结果 |
|
||||
| --- | --- | --- |
|
||||
| 当前已打开应用页或本机推理 Modal | 原位置显示“应用已关闭”的不可用状态,提供应用设置恢复入口;保留内容状态及草稿,不强制跳转 | 恢复可用性;仍打开时恢复内容,已关闭或离开则不自动打开 |
|
||||
| 已在执行的请求及后台操作 | 沿用功能自身的取消、完成和清理生命周期;本次应用开关不新增取消策略 | 不自动恢复、中断后重跑或补发任务 |
|
||||
| 数据、常驻及顺序 | 保留原值,业务入口按启用状态过滤 | 按保存的常驻及顺序恢复入口,不把未常驻应用自动常驻 |
|
||||
|
||||
知识请求及单个知识库实例停用、配置修改或删除继续遵守知识库自身规则,没有应用级禁用判断。
|
||||
|
||||
### 3.3 魔法笔记设置迁移
|
||||
|
||||
魔法笔记设置迁入应用中心的共同设置页;笔记页面定位到同一页,全局设置移除该入口,
|
||||
编辑同一配置。启用、常驻、显示未完成待办数量分别控制功能、导航和徽标,不保留重复的
|
||||
“显示魔法笔记入口”开关。
|
||||
|
||||
未配置的应用启用、常驻及魔法笔记相关布尔项默认开启;已有明确保存的 `false` 必须保留。
|
||||
AI 评论方式和评论形式继续为单选,沿用现有默认值“即时”和“长评 + 要点”,不能解释为
|
||||
全部模式同时启用。迁移保留既有单选值及业务数据。关闭数量显示只隐藏徽标,不修改待办。
|
||||
|
||||
## 4. 系统工具打开规则
|
||||
|
||||
### 4.1 通用流程
|
||||
|
||||
```text
|
||||
用户激活工具按钮
|
||||
-> 校验按钮仍可用
|
||||
-> 向助手工作栏发送打开意图
|
||||
-> 工作栏按应用实例策略查找或创建实例
|
||||
-> 展开工作栏
|
||||
-> 激活目标实例
|
||||
用户展开右侧工作栏
|
||||
-> 选择已有 Tab,或从“+”目录选择工具
|
||||
-> 已有 Tab 按实例 ID 激活;目录按单实例或多实例策略创建或激活
|
||||
-> 把焦点移入面板标题或首个主要控件
|
||||
```
|
||||
|
||||
@@ -85,31 +122,27 @@ type NavigationContribution =
|
||||
|
||||
### 4.2 决策表
|
||||
|
||||
| 工具 | 匹配实例存在 | 匹配实例不存在 | 上下文不可用 |
|
||||
| 工具 | 选择已有 Tab | 从“+”目录选择 | 上下文不可用 |
|
||||
| --- | --- | --- | --- |
|
||||
| 文件 | 激活不可关闭的单实例并保留其跟随或固定状态 | 仅在迁移或布局修复时补回 `workspace` | 打开面板并显示目录修复入口 |
|
||||
| 终端 | 激活当前执行空间最近的 `starting` 或 `running` 实例 | 创建一个绑定当前执行空间的实例 | 没有项目时使用本机 Home |
|
||||
| 浏览器 | 激活绑定当前 Conversation 的最近使用实例 | 创建绑定当前 Conversation 的新实例 | 激活最近使用实例;不存在时创建待绑定实例 |
|
||||
| 资源监控 | 激活 `resources` 单实例 | 创建本机 `resources` 单实例 | 部分指标不可用时显示原因并保留其余指标 |
|
||||
| 文件 | 激活不可关闭单实例并保留跟随或固定状态 | 聚焦 `workspace`;仅迁移或布局修复时补回 | 跟随模式无有效项目目录或默认工作区时显示修复入口 |
|
||||
| 终端 | 激活选中的确切实例,保留执行空间和历史状态 | 每次创建绑定当前执行空间的额外实例 | 新建时无项目使用本机 Home;SSH 失败保留远端目标与诊断 |
|
||||
| 浏览器 | 激活选中的确切实例,保留绑定目标和页面 | 每次创建绑定当前 Conversation 的额外实例 | 新建时无 Conversation 创建待绑定空实例 |
|
||||
| 资源监控 | 激活本机 `resources` 单实例 | 已存在则聚焦,否则创建 | 部分指标不可用时显示原因并保留其余指标 |
|
||||
|
||||
文件保持单一实例原有绑定。浏览器快捷入口按当前 Conversation 匹配最近使用实例,而工作栏
|
||||
“+”始终新建浏览器实例。固定目标失效时显示该目标的修复状态,不回退到当前项目、默认
|
||||
工作区或当前会话。
|
||||
|
||||
`closing`、`exited`、`interrupted` 和 `failed` 终端不属于可复用实例。当前执行空间只有这些
|
||||
终端时,快捷按钮创建新实例并保留原 Tab 的历史状态。用户需要额外的并行终端时使用工作栏
|
||||
“+”或终端面板中的既有创建命令。
|
||||
选择已有 Tab 不按当前项目或 Conversation 查找替代实例,也不重绑目标。文件的跟随模式
|
||||
仍按工作栏自身规则更新上下文;固定目标失效时显示该目标的修复状态,不静默回退。
|
||||
终端 `closing`、`exited`、`interrupted`、`failed` Tab 保留原状态,选择它们不自动重建。
|
||||
终端和浏览器通过“+”新建时不按目标去重,不恢复旧快捷按钮的最近匹配实例复用行为。
|
||||
|
||||
### 4.3 选中与状态
|
||||
|
||||
工具按钮满足以下条件时显示选中:助手工作栏已展开,且当前活动实例的 `appId` 与工具映射
|
||||
一致。只有实例存在但未激活时不显示选中。
|
||||
工作栏按确切实例 ID 表达 Tab 选中状态,同类型其他实例不能同时显示为选中。“+”是普通
|
||||
按钮,不是 Tab。主侧栏和应用中心不显示工具选中状态或工具徽标。
|
||||
|
||||
徽标优先级为:等待用户处理 > 错误 > 运行中 > 无徽标。同一按钮只显示一个视觉徽标,
|
||||
徽标优先级为:等待用户处理 > 错误 > 运行中 > 无徽标。同一 Tab 只显示一个视觉徽标,
|
||||
可访问名称包含完整状态。
|
||||
|
||||
资源监控首期不在后台采样,因此不显示负载告警徽标。只有资源面板是工作栏当前活动实例且
|
||||
工作栏已展开时,资源按钮才显示选中状态。
|
||||
资源监控首期不在后台采样,因此不显示负载告警徽标。后台事件不自动展开工作栏或切换 Tab。
|
||||
|
||||
### 4.4 资源采样状态
|
||||
|
||||
@@ -134,39 +167,68 @@ warming/loading/live/partial/error -> inactive
|
||||
|
||||
## 5. 设置保存规则
|
||||
|
||||
应用导航偏好与应用业务启停分开保存。Switch 每次切换立即提交完整目标集合;请求完成前禁用
|
||||
三个应用显隐 Switch,下一次操作基于最新确认结果提交,防止并发更新互相覆盖。
|
||||
应用导航偏好与业务启停使用独立字段,通过同一应用设置保存链路提交。常驻、排序、启用和
|
||||
魔法笔记设置更新串行执行;前一请求确认前,所有入口中会修改这组设置的控件暂不可操作。
|
||||
每次只提交本次变更,Main 在最新已保存设置上合并并原子写入;顺序提交完整目标顺序。
|
||||
持久化成功后由 Store 发布完整权威快照,应用中心、主导航、当前应用页、会话和所有设置入口同步采用,工具配置写入也走同一通知链路。每次重开中心先刷新并锁定修改;较新的设置事件使在途旧读取及旧保存响应失效,不能覆盖新快照。
|
||||
|
||||
保存失败时:
|
||||
|
||||
- 主导航和 Switch 恢复最后一次成功保存的偏好。
|
||||
- 设置行显示可重试错误。
|
||||
- 不回滚或修改各应用的业务状态。
|
||||
- 所有界面保留最后一次确认的配置,包括启用、常驻、顺序、徽标和会话入口;不提前运行关闭或恢复副作用。
|
||||
- 发起操作的设置行或排序区域显示就地可重试错误,保留焦点及编辑上下文,不重复显示同一错误通知。
|
||||
- 若响应中断导致保存结果未知,先重读权威设置再开放修改;不能把本地旧值当成回滚成功,也不自动重发变更。
|
||||
|
||||
## 6. 与需求的对应关系
|
||||
|
||||
| 逻辑 | PRD | User Stories |
|
||||
| --- | --- | --- |
|
||||
| 固定入口不变量 | FR-1、FR-2 | US-A1、US-A2 |
|
||||
| 应用显隐和失败恢复 | FR-3、FR-4、FR-11、FR-12 | US-B1、US-B2、US-B4、US-B5 |
|
||||
| 工具打开决策表 | FR-5 至 FR-10 | US-C1 至 US-C7 |
|
||||
| 应用常驻和失败恢复 | FR-3、FR-4、FR-11、FR-12 | US-B1、US-B2、US-B4、US-B5 |
|
||||
| 应用中心、常驻顺序 | FR-3、FR-4、FR-5、FR-11、FR-12;PRD 第 8 节 | US-B6、US-B7 |
|
||||
| 可选应用启用恢复、知识库和智能心跳固定可用 | FR-3、FR-4、FR-12;PRD 第 8 节补充要求 | US-B8 |
|
||||
| 魔法笔记迁移及共同配置 | FR-11、FR-12;PRD 第 8 节补充要求 | US-B9 |
|
||||
| 工具打开决策表 | FR-6 至 FR-10 | US-C1 至 US-C7 |
|
||||
| 资源采样状态 | FR-9 | US-C6 |
|
||||
| 无障碍与窄窗口 | FR-13 | US-D1、US-D2 |
|
||||
| 活动聚合、完成通知与查看清除 | FR-14 | US-E1、US-E2、US-E3 |
|
||||
| 后续私有市场与 YAML | FR-15 | US-F1、US-F2 |
|
||||
| 服务状态、资源可用性、服务生命周期 | FR-16 | US-F3、US-F5;US-F4 入口已移除 |
|
||||
|
||||
稳定 ID 保留原编号。US-B6 为应用中心,US-B7 为顺序,US-B8 为启用联动,US-B9 为笔记迁移;
|
||||
US-F3 为服务概览,US-F4 为已移除的任务取消入口,US-F5 为服务生命周期。PRD 旧 FR-5 至 FR-10 和
|
||||
User Stories 已同步上述入口规则,工具实例行为沿用右侧工作栏契约。
|
||||
|
||||
## 7. 逻辑完整性评估
|
||||
|
||||
首期规则已覆盖固定入口、应用显隐、保存失败、系统工具匹配与显式多实例差异、本机 Home、
|
||||
托管 SSH、无活动会话、目录失效、资源部分可用、采样停止、权限边界、键盘和窄窗口。其他
|
||||
工具面板内部的取消、超时、
|
||||
连接恢复和固定目标失效继续由助手工作栏负责,本功能不复制这些状态机。
|
||||
首期覆盖应用中心访问、启用与常驻组合、顺序恢复、默认值与旧值迁移、共同设置、保存失败、
|
||||
知识库和智能心跳固定入口、可选应用已打开页面恢复,以及右侧实例选择、本机 Home、托管 SSH、无会话、
|
||||
目录失效、资源部分可用与采样停止。工具内部取消、超时、连接恢复及业务后台生命周期由
|
||||
相应功能负责,本功能不复制状态机。底部入口打开轻量菜单,“管理应用”才进入与设置相同的整窗 Modal。实现与验证的剩余范围见[实施进度](./progress.md)。
|
||||
|
||||
## 8. 后续产品决策
|
||||
|
||||
- 插件工具上线后,快捷栏允许固定的最大数量及溢出规则。
|
||||
- 插件应用是否允许用户排序,以及固定系统入口之间允许的排序范围。
|
||||
后续市场 URL、YAML 交换、ShareServer 目录与分发、插件注册及安装不进入首期。其协议、
|
||||
扩展身份与安装生命周期由市场功能另行确定;内置应用常驻及排序不依赖这些决策。
|
||||
|
||||
这些决策不影响首期四个系统工具的固定入口和内置应用显隐实现。
|
||||
### 8.1 本机推理管理
|
||||
|
||||
FR-16 独立于市场排期,作为可常驻的管理 Modal。所有打开入口只改变 Modal 打开状态,不改变底层工作区、项目、会话或草稿;关闭时卸载 Modal,服务生命周期独立。US-F3 只有服务列表,展示名称、状态、引擎、模型及错误。共享服务只展示一次;资源按独立服务进程归属,CPU 预热与指标不可用分别给出原因,不填 `0`,不将宿主进程占用分摊给共享线程。采样与单位见[技术设计](./technical-design.md)。ASR 没有 Worker 就绪观测,显示按需运行且状态未知;活动请求不覆盖服务状态。OCR 状态来自 Renderer Worker,向量状态来自 Broker。
|
||||
|
||||
US-F4 的 Modal 单任务取消入口已移除。后端取消行为保留:Worker 确认或退出前仍计入活动任务和停止影响,即使调用方已收到取消或超时错误;任务先完成则保留真实完成结果,不通过终止共享服务模拟单任务取消。
|
||||
|
||||
| 服务管理方式 | 可提供操作 | 结果要求(US-F5) |
|
||||
| --- | --- | --- |
|
||||
| GoodBuddy 受管子进程 | 引擎已支持的启动、停止、重启 | 共用同一服务实例;操作成功由真实进程和服务就绪状态确认 |
|
||||
| 外部服务 | 在模型与连接设置中管理 | 不列入本机服务,不推断本机归属 |
|
||||
| 进程内模型 | 引擎实际支持的加载、释放 | 不显示虚构 PID 或进程启动、停止状态 |
|
||||
|
||||
停止、重启或释放均先打开紧凑的嵌套影响确认,列出任务和影响;无已登记活动任务时说明执行前再次核对。取消确认不改变运行状态。
|
||||
确认存在时禁止关闭底层管理 Modal;提交期间两层均不能关闭,重复操作被拒绝。未提交时 Escape 只关闭确认,确认关闭后才可用 Escape 关闭管理 Modal。焦点规则由 [UI 设计](./ui-design.md#91-本机推理管理)定义。
|
||||
实际操作失败关闭旧确认、显示错误并刷新快照;刷新期间或失败时禁用服务操作。再次操作重新取得当前任务清单并要求确认,避免重复提交旧清单。
|
||||
再次启动不重放已中断任务;关闭管理 Modal、关闭管理应用或取消常驻不停止服务,卸载模型文件为独立操作。
|
||||
当前向量服务允许确认影响后停止,并拒绝确认清单未包含的新任务;OCR 仅空闲时允许释放。
|
||||
ASR 按请求加载与释放,支持独立取消;TTS 尚无执行服务。具体适配边界以
|
||||
[技术清单](./technical-design.md#111-本机推理任务与执行服务)为准。活动请求仅在影响确认中展示,主列表不展示历史。应用退出清理沿用各引擎原有路径。
|
||||
|
||||
## 9. 项目活动汇总
|
||||
|
||||
|
||||
@@ -122,6 +122,27 @@ dialog[data-variant="grid"] #catalog-items { display: grid; grid-template-column
|
||||
.sidebar > * { flex-shrink: 0; }.sidebar .history { flex: 1 1 80px; min-height: 0; }.sidebar .shortcuts { flex-shrink: 1; min-height: 80px; }
|
||||
.sidebar .resident-apps { margin-top: 12px; min-height: 96px; flex-shrink: 1; display: flex; flex-direction: column; max-height: 240px; }.resident-apps .section-label { margin-bottom: 4px; flex-shrink: 0; }#resident-list { overflow: auto; min-height: 0; }.resident-row { display: flex; align-items: center; }.resident-row > button { display: flex; align-items: center; gap: 10px; padding: 8px 10px; flex: 1; min-width: 0; text-align: left; }.resident-row > button span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }.resident-row [aria-current="page"] { background: var(--accent-subtle); color: var(--accent); }.resident-row[draggable="true"] { cursor: grab; }.resident-row .edit-actions { flex-shrink: 0; }.all-apps { display: flex; justify-content: space-between; padding: 6px 10px; font-size: 11px; color: var(--text-secondary); flex-shrink: 0; }.empty-apps { padding: 8px 10px; font-size: 11px; color: var(--text-secondary); }.app-placement { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }.app-placement button { font-size: 11px; padding: 6px 8px; }.app-placement button[aria-pressed="true"] { border: 1px solid var(--accent-selected); }.app-catalog-row { align-items: flex-start; }.app-catalog-row > svg { margin-top: 4px; }
|
||||
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
|
||||
.workspace { min-height: 0; }
|
||||
.workspace-header { flex: 0 0 auto; flex-wrap: wrap; gap: 8px; align-items: center; }
|
||||
#page-label { overflow-wrap: anywhere; }
|
||||
.conversation { min-height: 0; overflow: auto; }
|
||||
.conversation.app-page { justify-content: flex-start; padding: 24px; max-width: 960px; gap: 12px; }
|
||||
.app-page h1 { font-size: 24px; }
|
||||
.model-list { width: 100%; }
|
||||
.model-service { padding: 16px 0; border-bottom: 1px solid var(--border-default); }
|
||||
.model-service p, .model-service details { color: var(--text-secondary); font-size: 12px; }
|
||||
.model-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
|
||||
.model-actions button, .inline-confirm button { border: 1px solid var(--border-default); }
|
||||
.inline-confirm { padding: 12px; margin: 8px 0; background: var(--accent-subtle); border: 1px solid var(--accent-selected); border-radius: var(--radius-control); }
|
||||
#market-preview { padding: 12px 0; border-bottom: 1px solid var(--border-default); }
|
||||
#market-preview label { display: block; margin: 8px 0; }
|
||||
#market-preview textarea, #market-preview select { width: 100%; padding: 8px; font: inherit; background: var(--surface-subtle); color: var(--text-primary); border: 1px solid var(--border-default); border-radius: var(--radius-control); }
|
||||
#market-preview textarea { resize: vertical; }
|
||||
#market-preview p, #import-preview { font-size: 12px; overflow-wrap: anywhere; }
|
||||
#market-preview summary, .model-service summary { cursor: pointer; }
|
||||
.note-segments, .center-switch, .tool-tabs { flex: 0 0 auto; }
|
||||
.center-app-info { min-width: 0; overflow-wrap: anywhere; }
|
||||
dialog, dialog::backdrop { -webkit-app-region: no-drag; }
|
||||
.launcher-trigger { display: flex; align-items: center; gap: 8px; width: 100%; padding: 12px 8px; border: 1px solid var(--border-default); background: var(--surface-raised); margin-bottom: 12px; font-size: 12px; }.launcher-trigger kbd { margin-left: auto; font: 10px var(--font-family-ui); color: var(--text-secondary); white-space: nowrap; }
|
||||
[data-variant="launcher"] .shortcut-list { grid-template-columns: repeat(4, minmax(0, 1fr)); }[data-variant="launcher"] .shortcut-button { flex-direction: column; padding: 8px 0; }[data-variant="launcher"] .icon-tile { height: 24px; }[data-variant="launcher"] .store-link { border-top: 1px solid var(--border-default); border-radius: 0; }
|
||||
[data-variant="favorites"] .section-label { padding-left: 0; }[data-variant="favorites"] .shortcut-list { gap: 0; }[data-variant="favorites"] .shortcut-button { justify-content: flex-start; padding: 8px 12px; }[data-variant="favorites"] .item-name { font-size: 12px; }[data-variant="favorites"] .shortcut-item { display: flex; align-items: center; }[data-variant="favorites"] .edit-actions { margin-left: auto; }[data-variant="favorites"] .store-link { margin-top: 12px; border: 1px dashed var(--border-default); }.count { padding: 0 6px; color: var(--text-secondary); }
|
||||
@@ -130,4 +151,4 @@ dialog[data-variant="grid"] #catalog-items { display: grid; grid-template-column
|
||||
.catalog-pack { padding: 20px 0; border-bottom: 1px solid var(--border-default); }.catalog-pack h3 { margin: 8px 0; font-size: 18px; font-weight: 550; }.pack-art { display: grid; place-items: center; float: right; width: 56px; height: 56px; border: 1px solid var(--accent-selected); background: var(--accent-subtle); color: var(--accent); border-radius: var(--radius-card); }.pack-art svg { width: 28px; height: 28px; }.pack-includes { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0; }.pack-includes span { padding: 4px 8px; font-size: 11px; background: var(--surface-subtle); border-radius: var(--radius-control); }.pack-includes svg { width: 14px; height: 14px; }.catalog-pack > button { border: 1px solid var(--accent-selected); color: var(--accent); background: var(--accent-subtle); }
|
||||
dialog[data-mode="launch"] { width: min(640px, calc(100% - 32px)); }dialog[data-mode="launch"] .catalog-row:focus-within { background: var(--accent-subtle); }.catalog-row { padding-left: 8px; padding-right: 8px; }
|
||||
@media (max-width: 1080px) { body { padding: 16px; }.app { position: relative; }.workbar { position: absolute; right: 0; top: 52px; bottom: 0; width: min(320px, 100%); box-shadow: -8px 0 24px var(--border-default); }.lab-header { flex-wrap: wrap; gap: 12px; }.design-note > span:last-child { display: none; }.conversation { padding: 32px 24px; } }
|
||||
@media (max-width: 640px) { body { padding: 12px; }.lab-header > div:first-child { flex: 1; }.variants { order: 3; width: 100%; }.variants button { flex: 1; padding: 8px 4px; font-size: 12px; }.design-note { align-items: flex-start; gap: 12px; }.app { height: auto; min-height: 0; }.app .sidebar { width: 100%; flex: 0 0 auto; border: 0; padding-top: 12px; }.brand { padding-bottom: 12px; }.app .history { display: none; }.app .workspace { display: flex; min-height: 320px; border-top: 1px solid var(--border-default); }.app .workspace .composer { display: block; }.composer-area { margin: 12px 12px 4px; margin-top: auto; }.composer-toolbar { gap: 2px; }.composer-select select { max-width: 108px; font-size: 11px; }.composer-toolbar button { font-size: 11px; padding: 6px; }.composer-toolbar .composer-icon { width: 28px; }.composer-meta { flex-wrap: wrap; gap: 0; }.shortcut-list { max-height: 300px; overflow: auto; }.lab-footer { align-items: flex-start; }.lab-footer button { white-space: nowrap; }.workbar { top: 0; width: 100%; }.design-note p { font-size: 11px; } }
|
||||
@media (max-width: 640px) { body { padding: 12px; }.lab-header > div:first-child { flex: 1; }.variants { order: 3; width: 100%; }.variants button { flex: 1; padding: 8px 4px; font-size: 12px; }.design-note { align-items: flex-start; gap: 12px; }.app { height: auto; min-height: 0; }.app .sidebar { width: 100%; flex: 0 0 auto; border: 0; padding-top: 12px; }.brand { padding-bottom: 12px; }.app .history { display: none; }.app .workspace { display: flex; min-height: 320px; border-top: 1px solid var(--border-default); }.app .workspace .conversation { display: flex; overflow: visible; padding: 16px; }.app .workspace .composer { display: block; }.composer-area { margin: 12px 12px 4px; margin-top: auto; }.composer-toolbar { gap: 2px; }.composer-select select { max-width: 108px; font-size: 11px; }.composer-toolbar button { font-size: 11px; padding: 6px; }.composer-toolbar .composer-icon { width: 28px; }.composer-meta { flex-wrap: wrap; gap: 0; }.shortcut-list { max-height: 300px; overflow: auto; }.lab-footer { align-items: flex-start; }.lab-footer button { white-space: nowrap; }.workbar { top: 0; width: 100%; }.design-note p { font-size: 11px; } }
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
<button data-variant="cards" aria-pressed="false">C · 应用预览</button>
|
||||
<button data-variant="launcher" aria-pressed="false">D · 搜索启动台</button>
|
||||
<button data-variant="favorites" aria-pressed="false">E · 常驻管理</button>
|
||||
<button data-variant="scenes" aria-pressed="false">F · 商店发现</button>
|
||||
<button data-variant="scenes" aria-pressed="false">F · 私有市场预览</button>
|
||||
</div>
|
||||
<button id="theme">切换深色</button>
|
||||
</header>
|
||||
<div class="design-note"><span id="variant-label">A / 紧凑 Dock</span><p id="variant-description"></p><span>交互原型 · 商店与指标均为演示数据</span></div>
|
||||
<div class="design-note"><span id="variant-label">A / 紧凑 Dock</span><p id="variant-description"></p><span>交互原型 · 全部状态仅在内存中,刷新重置</span></div>
|
||||
<div class="project-lab"><label>项目活动演示 <select id="project-scenario"><option value="waiting">有待处理</option><option value="running">仅运行中</option><option value="current">仅当前项目运行</option><option value="idle">全部空闲</option></select></label><span>有活动时显示摘要,悬停或点击项目向右展开会话。</span></div>
|
||||
<main class="app" data-variant="dock">
|
||||
<aside class="sidebar" aria-label="主侧栏">
|
||||
@@ -63,8 +63,8 @@
|
||||
<div id="composer-options" popover aria-label="会话选项">
|
||||
<header><strong>会话选项</strong><button popovertarget="composer-options" popovertargetaction="hide" aria-label="关闭会话选项">×</button></header>
|
||||
<label class="option-row">专家<select id="composer-expert"><option value="">默认助手</option><option>产品顾问</option><option>代码审查专家</option></select></label>
|
||||
<fieldset><legend>知识范围</legend><label><input type="checkbox" data-knowledge="产品文档"> 产品文档</label><label><input type="checkbox" data-knowledge="团队知识"> 团队知识</label></fieldset>
|
||||
<label class="option-row">检索方式<select id="composer-retrieval"><option value="auto">自动检索</option><option value="always">每次检索</option></select></label>
|
||||
<fieldset data-knowledge-control><legend>知识范围</legend><label><input type="checkbox" data-knowledge="产品文档"> 产品文档</label><label><input type="checkbox" data-knowledge="团队知识"> 团队知识</label></fieldset>
|
||||
<label class="option-row" data-knowledge-control>检索方式<select id="composer-retrieval"><option value="auto">自动检索</option><option value="always">每次检索</option></select></label>
|
||||
<div id="runtime-options"><label class="option-row"><span id="agent-label">Agent</span><select id="composer-agent"><option value="">默认</option><option value="plan">Plan</option><option value="review">Review</option></select></label><label class="option-row">快捷操作<select id="composer-command"><option value="">无</option><option value="prompt">填入总结模板</option><option value="command">原生命令 /init</option></select></label></div>
|
||||
<p class="muted">非默认选项会显示在输入框上方。</p>
|
||||
</div>
|
||||
@@ -77,7 +77,16 @@
|
||||
</main>
|
||||
<footer class="lab-footer"><span id="design-tradeoff"></span><button id="reset">恢复默认应用</button></footer>
|
||||
<div class="reference-note">设计参考:<a href="https://www.raycast.com/" target="_blank" rel="noreferrer">Raycast · 搜索与扩展</a><a href="https://www.notion.com/help/intro-to-workspaces" target="_blank" rel="noreferrer">Notion · 收藏与折叠分区</a><a href="https://code.visualstudio.com/docs/configure/profiles" target="_blank" rel="noreferrer">VS Code · Profiles</a><span>借鉴交互思路,保留 GoodBuddy 的视觉与入口分类。</span></div>
|
||||
<dialog id="catalog" aria-labelledby="catalog-title"><button id="back-to-apps" hidden>‹ 返回应用中心</button><header><div><span class="eyebrow">GOODBUDDY COLLECTION</span><h2 id="catalog-title">应用中心</h2></div><button id="close-catalog" aria-label="关闭">×</button></header><p class="muted" id="catalog-description"></p><div class="center-switch" role="group" aria-label="应用中心视图"><button data-catalog="apps">我的应用</button><button data-catalog="store">商店</button></div><input id="search" type="search" aria-label="搜索应用" placeholder="搜索应用…"><div id="catalog-items"></div><section id="app-settings" aria-label="魔法笔记设置" hidden></section><footer>常驻只表示菜单入口,不表示后台运行。安装与内容均为演示。</footer></dialog>
|
||||
<dialog id="catalog" aria-labelledby="catalog-title"><button id="back-to-apps" hidden>‹ 返回应用中心</button><header><div><span class="eyebrow">GOODBUDDY COLLECTION</span><h2 id="catalog-title">应用中心</h2></div><button id="close-catalog" aria-label="关闭">×</button></header><p class="muted" id="catalog-description"></p><div class="center-switch" role="group" aria-label="应用中心视图" hidden><button data-catalog="apps">我的应用</button><button data-catalog="store">未来私有市场</button></div><section id="market-preview" hidden>
|
||||
<label>私有市场 URL<input id="market-url" type="url" value="https://market.example.invalid" aria-describedby="market-status"></label><button id="market-fetch">模拟获取目录</button><button id="market-fail">模拟不可达</button><p id="market-status" role="status">未来预览,不发送网络请求;来源仅用于市场。</p>
|
||||
<details><summary>YAML 导入 / 导出预览(mock schema)</summary><p class="muted">仅支持下列扁平示例字段,非正式 Schema。支持 .yml / .yaml 的文本预览;不包含凭据或业务数据,不替代程序包或模型权重。</p><label>应用定义<textarea id="yaml-input" rows="9" spellcheck="false">schema: mock-v1
|
||||
id: reading
|
||||
name: 阅读空间
|
||||
version: 1.0
|
||||
entry: main
|
||||
dependencies: builtin:knowledge
|
||||
resources: bundled</textarea></label><button id="yaml-preview">预览导入</button><label>导出应用<select id="yaml-app"></select></label><button id="yaml-export">预览导出 YAML</button><pre id="yaml-output" tabindex="0" hidden></pre><div id="import-preview" hidden></div></details>
|
||||
</section><input id="search" type="search" aria-label="搜索应用" placeholder="搜索应用…"><div id="catalog-items"></div><section id="app-settings" aria-label="应用设置" hidden></section><footer>取消常驻仅隐藏捷径;关闭应用隐藏业务入口,保留数据与后台状态。此处全部为模拟。</footer></dialog>
|
||||
<div id="toast" role="status" aria-live="polite"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -14,6 +14,7 @@ const items = [
|
||||
{ id: 'notes', name: '魔法笔记', icon: 'note', detail: '记录想法、整理待办', summary: '把刚才的灵感留在这里', builtin: true },
|
||||
{ id: 'knowledge', name: '知识库', icon: 'book', detail: '整理资料、检索知识', summary: '下一次提问,从已有知识开始', builtin: true },
|
||||
{ id: 'heartbeat', name: '智能心跳', icon: 'activity', detail: '管理定时任务与主动提醒', summary: '让例行任务按计划发生', builtin: true },
|
||||
{ id: 'models', name: '本机推理管理', icon: 'layers', detail: '语音、OCR、向量任务统一管理 · 计划预览', summary: '查看本机推理任务、使用方和资源占用', builtin: true },
|
||||
{ id: 'projects', name: '项目管理', icon: 'layers', detail: '跟踪项目、任务与进度', summary: '把一个想法变成可跟进的项目', builtin: false },
|
||||
{ id: 'reading', name: '阅读空间', icon: 'book', detail: '集中阅读、批注与摘录', summary: '给长文和深度阅读留一个空间', builtin: false }
|
||||
];
|
||||
@@ -23,7 +24,7 @@ const variants = {
|
||||
cards: ['C / 应用预览', '入口展示内置应用图标,展开后用卡片说明每个应用的用途。', '适合通过内容理解应用,卡片不代表后台持续运行。'],
|
||||
launcher: ['D / 搜索启动台', '最底部就是搜索入口,也可以按 Ctrl / ⌘ K 打开。', '输入名称,用方向键与 Enter 打开已安装应用。'],
|
||||
favorites: ['E / 常驻管理', '从底部管理哪些应用常驻菜单,未常驻应用也可直接打开。', '常驻应用优先排列;入口显隐不影响安装和运行状态。'],
|
||||
scenes: ['F / 商店发现', '底部入口强调应用发现,展开后查看商店应用与安装位置。', '先模拟安装,再决定是否常驻;我的应用始终可切换访问。']
|
||||
scenes: ['F / 私有市场预览', '未来实验:私有市场 URL、模拟目录获取与 YAML 交换。', '不联网、不写入文件;mock schema 不代表正式协议。']
|
||||
};
|
||||
const $ = selector => document.querySelector(selector);
|
||||
const demoProjects = [
|
||||
@@ -134,7 +135,20 @@ window.addEventListener('scroll', () => { if ($('#project-activity').matches(':p
|
||||
renderProjects();
|
||||
let variant = 'dock';
|
||||
let catalogMode = 'apps';
|
||||
let residentApps = ['notes', 'knowledge'];
|
||||
let residentApps = items.filter(item => item.builtin).map(item => item.id);
|
||||
let enabledApps = new Set(residentApps);
|
||||
let currentPage = '当前对话';
|
||||
let settingsApp = 'notes';
|
||||
let marketReady = false;
|
||||
let importDraft = null;
|
||||
const definitions = new Map();
|
||||
const modelServices = [
|
||||
{ id: 'ocr', name: 'OCR 文字识别', purpose: '文档入库 #12 · 正在识别第 8 / 24 页', engine: 'PaddleOCR worker', management: '受管进程', running: true, usage: '420 MB', users: '知识库 / 文档入库任务 #12' },
|
||||
{ id: 'speech', name: '语音识别', purpose: '语音输入 #18 · 正在转写', engine: '本机语音识别引擎', management: '受管进程', running: true, usage: '310 MB', users: '当前会话 / 语音输入 #18' },
|
||||
{ id: 'tts', name: '语音合成', purpose: '回复朗读 #19 · 正在合成', engine: '本机语音合成引擎', management: '进程内', running: true, usage: '180 MB', users: '当前会话 / 回复朗读 #19' },
|
||||
{ id: 'embedding', name: '向量生成', purpose: '文档向量化 #7 · 已处理 32 / 80 个片段', engine: 'BGE-small / ONNX Runtime', management: '进程内', running: true, usage: '128 MB', users: '知识库 / 文档向量化任务 #7' }
|
||||
];
|
||||
let modelConfirmation = null;
|
||||
let editingApps = false;
|
||||
let draggedApp = '';
|
||||
let installed = new Set(items.filter(item => item.builtin).map(item => item.id));
|
||||
@@ -143,23 +157,28 @@ let settingsOpen = false;
|
||||
const noteSettings = { showCount: true, mode: 'immediate', format: 'combined' };
|
||||
const noteModes = { immediate: '即时', 'after-save-auto': '保存后自动', 'after-save-manual': '保存后手动' };
|
||||
const noteFormats = { combined: '长评 + 要点', narrative: '长评', structured: '要点' };
|
||||
function renderNoteSettings() {
|
||||
$('#app-settings').innerHTML = `<div class="note-setting-row"><div><strong>常驻左侧菜单</strong><p>对应“显示魔法笔记入口”,移除后仍可从应用中心打开。</p></div><button class="demo-switch" role="switch" aria-label="常驻左侧菜单" aria-checked="${residentApps.includes('notes')}" data-note-toggle="resident"></button></div>
|
||||
function renderAppSettings() {
|
||||
const item = items.find(entry => entry.id === settingsApp);
|
||||
$('#app-settings').innerHTML = `<div class="note-setting-row"><div><strong>启用应用</strong><p>关闭后隐藏业务入口,保留常驻偏好、数据和后台状态。可在此重新开启。</p></div><button class="demo-switch" role="switch" aria-label="启用${item.name}" aria-checked="${enabledApps.has(item.id)}" data-app-toggle="enabled"></button></div>
|
||||
<div class="note-setting-row"><div><strong>常驻左侧菜单</strong><p>仅控制主导航捷径;应用关闭时记住此偏好。</p></div><button class="demo-switch" role="switch" aria-label="常驻左侧菜单" aria-checked="${residentApps.includes(item.id)}" data-app-toggle="pinned"></button></div>`;
|
||||
if (settingsApp === 'notes') $('#app-settings').innerHTML += `
|
||||
<div class="note-setting-row"><div><strong>显示未完成待办数量</strong><p>在左侧入口显示数量,超过 99 项显示 99+。本 Demo 使用 3 项示例。</p></div><button class="demo-switch" role="switch" aria-label="显示未完成待办数量" aria-checked="${noteSettings.showCount}" data-note-toggle="showCount"></button></div>
|
||||
<fieldset class="note-setting-options"><legend>AI 评论方式</legend><div class="note-segments" role="group" aria-label="AI 评论方式">${Object.entries(noteModes).map(([value, label]) => `<button data-note-mode="${value}" aria-pressed="${noteSettings.mode === value}">${label}</button>`).join('')}</div><p>即时:按回车并停止输入 5 秒后评论未保存草稿。保存后自动:保存后评论。保存后手动:点击 AI 分析后评论。</p></fieldset>
|
||||
<fieldset class="note-setting-options"><legend>AI 评论形式</legend><div class="note-segments" role="group" aria-label="AI 评论形式">${Object.entries(noteFormats).map(([value, label]) => `<button data-note-format="${value}" aria-pressed="${noteSettings.format === value}">${label}</button>`).join('')}</div><p>默认同时生成流式长评和结构化要点,也可只保留其中一种。</p></fieldset>
|
||||
<div class="note-settings-preview"><span class="eyebrow">当前配置预览</span><p>${noteModes[noteSettings.mode]} · ${noteFormats[noteSettings.format]}</p><small>选择后即时保留于本次 Demo 会话,不调用 AI、不修改真实设置。</small></div>`;
|
||||
if ($('#note-settings-summary')) $('#note-settings-summary').textContent = `当前设置:${noteModes[noteSettings.mode]} · ${noteFormats[noteSettings.format]}。待办数量${noteSettings.showCount ? '显示' : '隐藏'}。`;
|
||||
}
|
||||
function showNoteSettings() {
|
||||
function showAppSettings(id = 'notes') {
|
||||
settingsApp = id;
|
||||
const item = items.find(entry => entry.id === id);
|
||||
if (!$('#catalog').open) { catalogMode = 'apps'; $('#search').value = ''; }
|
||||
settingsOpen = true;
|
||||
$('#catalog').dataset.variant = variant;
|
||||
$('#catalog-title').textContent = '魔法笔记设置';
|
||||
$('#catalog-description').textContent = '调整入口与 AI 评论行为。应用中心和笔记页面共享这份演示配置。';
|
||||
$('#catalog-title').textContent = `${item.name}设置`;
|
||||
$('#catalog-description').textContent = '应用中心、应用页面和全局设置共用本次演示配置。';
|
||||
$('#back-to-apps').hidden = false; $('#app-settings').hidden = false;
|
||||
for (const selector of ['.center-switch', '#search', '#catalog-items']) $(selector).hidden = true;
|
||||
renderNoteSettings();
|
||||
for (const selector of ['.center-switch', '#search', '#catalog-items', '#market-preview']) $(selector).hidden = true;
|
||||
renderAppSettings();
|
||||
if (!$('#catalog').open) $('#catalog').showModal();
|
||||
$('#back-to-apps').focus();
|
||||
}
|
||||
@@ -169,54 +188,75 @@ function notify(text) {
|
||||
}
|
||||
function renderApps() {
|
||||
$('#edit-apps').textContent = editingApps ? '完成' : '编辑';
|
||||
$('#resident-list').innerHTML = residentApps.map((id, index) => {
|
||||
$('#resident-list').innerHTML = residentApps.filter(id => enabledApps.has(id) && (variant === 'scenes' || items.find(item => item.id === id).builtin)).map(id => {
|
||||
const index = residentApps.indexOf(id);
|
||||
const item = items.find(entry => entry.id === id);
|
||||
return `<div class="resident-row" data-resident="${id}" draggable="${editingApps}"><button data-open="${id}" ${$('#page-label').textContent === item.name ? 'aria-current="page"' : ''}>${icon(item.icon)}<span>${item.name}</span>${id === 'notes' && noteSettings.showCount ? '<small class="note-count" aria-label="3 项未完成待办(示例)">3</small>' : ''}</button>${editingApps ? `<div class="edit-actions"><button data-move="${id}" data-direction="-1" aria-label="上移${item.name}" ${index === 0 ? 'disabled' : ''}>↑</button><button data-pin="${id}" aria-label="移除${item.name}">×</button><button data-move="${id}" data-direction="1" aria-label="下移${item.name}" ${index === residentApps.length - 1 ? 'disabled' : ''}>↓</button></div>` : ''}</div>`;
|
||||
}).join('') || '<p class="empty-apps">点击 +,选择常驻应用。</p>';
|
||||
const subtitles = { dock: '', grid: `${installed.size} 个已安装应用`, cards: '笔记 · 知识 · 自动化', launcher: 'Ctrl / ⌘ K', favorites: `${residentApps.length} 个应用常驻菜单`, scenes: '发现你的下一个好应用' };
|
||||
const subtitles = { dock: '', grid: '内置应用', cards: '笔记 · 知识 · 自动化 · 模型', launcher: 'Ctrl / ⌘ K', favorites: '管理常驻偏好', scenes: '未来私有市场 · 模拟' };
|
||||
$('#center-entry').innerHTML = `<button id="open-center" aria-haspopup="dialog"><span class="brand-mark">${variant === 'launcher' ? icon('search') : 'GB'}</span><span><strong>${variant === 'launcher' ? '搜索应用…' : '应用中心'}</strong>${subtitles[variant] ? `<small>${subtitles[variant]}</small>` : ''}</span><span class="entry-arrow">${variant === 'scenes' ? '↗' : '⌃'}</span></button>`;
|
||||
}
|
||||
function renderCatalog() {
|
||||
const query = $('#search').value.trim().toLowerCase();
|
||||
const matches = items.filter(item => (catalogMode === 'store' ? !item.builtin : installed.has(item.id)) && `${item.name}${item.detail}`.toLowerCase().includes(query));
|
||||
const matches = items.filter(item => (catalogMode === 'store' ? marketReady && !item.builtin : installed.has(item.id) && (variant === 'scenes' || item.builtin)) && `${item.name}${item.detail}`.toLowerCase().includes(query));
|
||||
if (variant === 'favorites') matches.sort((a, b) => Number(residentApps.includes(b.id)) - Number(residentApps.includes(a.id)));
|
||||
$('#catalog-items').innerHTML = matches.map(item => {
|
||||
const ready = installed.has(item.id); const pinned = residentApps.includes(item.id);
|
||||
return `<article class="center-app">${icon(item.icon)}<div class="center-app-info"><strong>${item.name}</strong><p>${item.builtin ? '内置应用' : '商店应用 · 示例'}</p><p class="app-detail">${variant === 'cards' ? item.summary : item.detail}</p></div><div class="center-actions">${ready ? `<button data-open="${item.id}">打开</button>${item.id === 'notes' ? '<button data-note-settings aria-label="魔法笔记设置">设置</button>' : ''}<button data-pin="${item.id}" aria-pressed="${pinned}">${pinned ? '取消常驻' : '常驻左侧'}</button>` : `<button data-install="${item.id}">模拟安装</button><button data-install-pin="${item.id}">安装并常驻</button>`}</div></article>`;
|
||||
}).join('') || '<p class="empty-apps">没有匹配的应用。可以换个关键词,或切换到商店。</p>';
|
||||
return `<article class="center-app">${icon(item.icon)}<div class="center-app-info"><strong>${item.name}</strong><p>${item.builtin ? '内置应用' : '私有市场 · 示例'}${item.id === 'models' ? ' · 计划预览' : ''}${ready ? enabledApps.has(item.id) ? ' · 已启用' : ' · 已关闭' : ''}</p><p class="app-detail">${variant === 'cards' ? item.summary : item.detail}</p></div><div class="center-actions">${ready ? `${enabledApps.has(item.id) ? `<button data-open="${item.id}">打开</button>` : ''}<button data-app-settings="${item.id}">${enabledApps.has(item.id) ? '设置' : '设置 / 重新开启'}</button><button data-pin="${item.id}" aria-pressed="${pinned}">${pinned ? '取消常驻' : '常驻左侧'}</button>` : `<button data-install="${item.id}">模拟安装</button><button data-install-pin="${item.id}">模拟安装并常驻</button>`}</div></article>`;
|
||||
}).join('') || `<p class="empty-apps">${catalogMode === 'store' && !marketReady ? '请先模拟获取私有市场目录。' : '没有匹配的应用,请换个关键词。'}</p>`;
|
||||
$('#yaml-app').innerHTML = items.filter(item => installed.has(item.id)).map(item => `<option value="${item.id}">${item.name}</option>`).join('');
|
||||
document.querySelectorAll('.center-switch button').forEach(button => button.setAttribute('aria-pressed', String(button.dataset.catalog === catalogMode)));
|
||||
}
|
||||
function openCatalog(mode = 'apps') {
|
||||
importDraft = null; $('#import-preview').hidden = true;
|
||||
if (variant !== 'scenes') mode = 'apps';
|
||||
settingsOpen = false; $('#back-to-apps').hidden = true; $('#app-settings').hidden = true;
|
||||
for (const selector of ['.center-switch', '#search', '#catalog-items']) $(selector).hidden = false;
|
||||
$('.center-switch').hidden = variant !== 'scenes';
|
||||
$('#market-preview').hidden = mode !== 'store';
|
||||
catalogMode = mode; $('#catalog').dataset.variant = variant;
|
||||
$('#catalog-title').textContent = mode === 'store' ? '发现新应用' : '应用中心';
|
||||
$('#catalog-description').textContent = mode === 'store' ? '安装你需要的应用,自主决定是否常驻左侧菜单。' : '打开应用,或把常用应用留在左侧。系统工具请使用右侧工作栏。';
|
||||
$('#catalog-title').textContent = mode === 'store' ? '私有市场 · 未来预览' : '应用中心';
|
||||
$('#catalog-description').textContent = mode === 'store' ? '目录和安装均为模拟,未接入 ShareServer。导入不会启动模型或任务。' : '内置应用无需安装;启用与常驻分别设置。系统工具请使用右侧工作栏。';
|
||||
$('#search').value = ''; renderCatalog();
|
||||
if (!$('#catalog').open) $('#catalog').showModal();
|
||||
$('#search').focus();
|
||||
}
|
||||
function openPage(name) {
|
||||
if ($('#catalog').open) $('#catalog').close();
|
||||
function openPage(name, keepSettings = false) {
|
||||
currentPage = name;
|
||||
modelConfirmation = null;
|
||||
if ($('#catalog').open && !keepSettings) $('#catalog').close();
|
||||
$('#page-label').textContent = name;
|
||||
$('#page-content').replaceChildren();
|
||||
const title = document.createElement('h1'); title.textContent = name;
|
||||
const description = document.createElement('p'); description.className = 'muted'; description.textContent = '主内容区页面预览。右侧工作栏保持当前工具状态。';
|
||||
$('#page-content').append(title, description);
|
||||
$('#page-actions').innerHTML = name === '魔法笔记' ? `<button data-note-settings>${icon('settings')} 笔记设置</button>` : '';
|
||||
const app = items.find(item => item.name === name);
|
||||
$('.composer-area').hidden = Boolean(app) || name === '设置' || name === '运行记录';
|
||||
$('#page-content').classList.toggle('app-page', $('.composer-area').hidden);
|
||||
for (const panel of ['#composer-options', '#context-details']) if ($(panel).matches(':popover-open')) $(panel).hidePopover();
|
||||
$('#page-actions').innerHTML = app ? `<button data-app-settings="${app.id}">${icon('settings')} 应用设置</button>` : '';
|
||||
if (app && !enabledApps.has(app.id)) {
|
||||
description.textContent = '此应用已关闭,当前页面不可用。已有数据和选择保留;此操作不停止后台任务或模型服务。';
|
||||
const recovery = document.createElement('button'); recovery.dataset.appSettings = app.id; recovery.textContent = '前往设置,重新开启'; $('#page-content').append(recovery);
|
||||
renderApps(); return;
|
||||
}
|
||||
if (app?.id === 'models') renderModels();
|
||||
if (name === '魔法笔记' || name === '设置') {
|
||||
const summary = document.createElement('p'); summary.id = 'note-settings-summary'; summary.className = 'muted'; $('#page-content').append(summary);
|
||||
if (name === '设置') { const button = document.createElement('button'); button.dataset.noteSettings = ''; button.textContent = '魔法笔记设置'; $('#page-content').append(button); }
|
||||
renderNoteSettings();
|
||||
if (name === '设置') items.filter(item => item.builtin).forEach(item => { const button = document.createElement('button'); button.dataset.appSettings = item.id; button.textContent = `${item.name}设置`; $('#page-content').append(button); });
|
||||
renderAppSettings();
|
||||
}
|
||||
document.querySelectorAll('.navigation button').forEach(button => button.classList.toggle('selected', button.dataset.page === name));
|
||||
renderApps(); notify(`已打开${name}`);
|
||||
renderApps(); if (!keepSettings) notify(`已打开${name}`);
|
||||
}
|
||||
function selectVariant(name) {
|
||||
variant = name; $('.app').dataset.variant = name;
|
||||
document.querySelectorAll('button[data-variant]').forEach(button => button.setAttribute('aria-pressed', String(button.dataset.variant === name)));
|
||||
[$('#variant-label').textContent, $('#variant-description').textContent, $('#design-tradeoff').textContent] = variants[name];
|
||||
history.replaceState(null, '', `#${name}`); renderApps();
|
||||
if ($('#catalog').open) openCatalog();
|
||||
const pageApp = items.find(item => item.name === currentPage);
|
||||
if (name !== 'scenes' && pageApp && !pageApp.builtin) openPage('当前对话');
|
||||
}
|
||||
function selectTool(id) {
|
||||
const content = {
|
||||
@@ -228,6 +268,76 @@ function selectTool(id) {
|
||||
$('#tool-content').innerHTML = content[id]; $('#tool-content').setAttribute('aria-labelledby', `tab-${id}`);
|
||||
document.querySelectorAll('[data-tool]').forEach(button => { button.setAttribute('aria-selected', String(button.dataset.tool === id)); button.tabIndex = button.dataset.tool === id ? 0 : -1; });
|
||||
}
|
||||
function renderModels() {
|
||||
$('#page-content').innerHTML = `<h1>本机推理管理</h1><p class="muted">统一查看语音、OCR、向量等本机推理任务与执行服务。任务进度、引擎及占用均为模拟数据。</p><h2>推理任务与服务</h2><p class="muted">下方操作作用于执行服务;启动或加载服务不会自动恢复已中断的任务。</p><div class="model-list">${modelServices.map(service => {
|
||||
const external = service.management === '外部管理';
|
||||
const active = service.management === '进程内' ? '已加载' : '运行中';
|
||||
const actions = external ? ['config'] : service.management === '进程内' ? [service.running ? 'release' : 'load'] : service.running ? ['stop', 'restart'] : ['start'];
|
||||
return `<article class="model-service"><strong>${service.name}</strong><p>${service.interrupted ? '任务已中断,需由原应用重新发起' : service.purpose}</p><p>执行服务:${service.running ? active : '未运行'} · 内存:${service.running ? service.usage : '已释放(模拟)'}</p><p>使用方:${service.interrupted ? '暂无活动使用方' : service.users}</p><div class="model-actions">${actions.map(action => `<button data-model="${service.id}" data-model-action="${action}">${modelActionLabels[action]}</button>`).join('')}</div>${modelConfirmation?.id === service.id ? `<div class="inline-confirm" role="group" aria-label="推理服务操作确认"><p>${modelActionLabels[modelConfirmation.action]} ${service.name} 将影响 ${service.interrupted ? '该执行服务,当前没有活动任务' : service.users}。仅模拟状态变化。</p><button data-model-cancel>取消</button><button data-model-confirm>确认${modelActionLabels[modelConfirmation.action]}</button></div>` : ''}<details><summary>引擎与错误详情</summary><p>${service.engine} · ${service.management}</p><p>最近错误:无(示例)。操作仅更新本页内存状态。</p></details></article>`;
|
||||
}).join('')}</div>`;
|
||||
}
|
||||
const modelActionLabels = { start: '启动', stop: '停止', restart: '重启', load: '加载', release: '释放', config: '连接配置' };
|
||||
function applyModelAction(id, action) {
|
||||
const service = modelServices.find(entry => entry.id === id);
|
||||
service.running = !['stop', 'release'].includes(action);
|
||||
if (['stop', 'release', 'restart'].includes(action)) service.interrupted = true;
|
||||
modelConfirmation = null;
|
||||
renderModels();
|
||||
$(`[data-model="${id}"]`)?.focus();
|
||||
notify(`已模拟${modelActionLabels[action]} ${service.name}`);
|
||||
}
|
||||
function previewImport() {
|
||||
importDraft = null;
|
||||
const preview = $('#import-preview'); preview.hidden = false; preview.replaceChildren();
|
||||
const values = {};
|
||||
const fields = ['schema', 'id', 'name', 'version', 'entry', 'dependencies', 'resources'];
|
||||
for (const line of $('#yaml-input').value.split(/\r?\n/).filter(line => line.trim() && !line.trim().startsWith('#'))) {
|
||||
const match = line.match(/^([a-z]+):\s*(.+)$/);
|
||||
if (!match || !fields.includes(match[1]) || Object.hasOwn(values, match[1])) { preview.textContent = '仅支持示例中的七个扁平字段,不接受重复或未知字段;请修正后重新预览。'; return; }
|
||||
values[match[1]] = match[2].trim();
|
||||
}
|
||||
if (fields.some(field => !values[field]) || values.schema !== 'mock-v1' || !/^[a-z][a-z0-9-]*$/.test(values.id) || values.entry !== 'main') { preview.textContent = '请填写完整 mock-v1 示例,id 使用小写字母、数字或连字符,entry 为 main。'; return; }
|
||||
const existing = items.find(item => item.id === values.id);
|
||||
const dependencies = values.dependencies === 'none' ? [] : values.dependencies.split(',').map(value => value.trim());
|
||||
const missing = dependencies.filter(value => !value.startsWith('builtin:') || !installed.has(value.slice(8)) || !enabledApps.has(value.slice(8)));
|
||||
const blocked = existing?.builtin || missing.length || values.resources !== 'bundled';
|
||||
const conflict = installed.has(values.id);
|
||||
const text = document.createElement('p');
|
||||
text.textContent = `${values.name} · ${values.id} · v${values.version} · 主内容区入口。依赖:${values.dependencies}。${missing.length ? `缺失或关闭:${missing.join('、')}。` : '依赖已具备(模拟)。'}资源:${values.resources}。${values.resources !== 'bundled' ? '需补充外部资源,不能安装为可用。' : ''}${existing?.builtin ? '与内置应用冲突,不允许覆盖。' : conflict ? '同标识冲突:需显式更新定义,保留启用与常驻偏好。' : '没有同标识冲突。'}`;
|
||||
preview.append(text);
|
||||
const cancel = document.createElement('button'); cancel.textContent = '取消'; cancel.onclick = () => { importDraft = null; preview.hidden = true; $('#yaml-preview').focus(); }; preview.append(cancel);
|
||||
if (!blocked) {
|
||||
importDraft = values;
|
||||
const confirm = document.createElement('button'); confirm.id = 'import-confirm'; confirm.textContent = conflict ? '确认模拟更新定义' : '确认模拟安装';
|
||||
confirm.onclick = () => {
|
||||
if (!importDraft) return;
|
||||
const draft = importDraft;
|
||||
const item = items.find(entry => entry.id === draft.id);
|
||||
if (!item) items.push({ id: draft.id, name: draft.id, icon: 'layers', detail: 'YAML 导入 · 未来示例', summary: '导入的应用定义预览', builtin: false });
|
||||
// Imported text stays in textContent/textarea; catalog markup uses the validated identifier.
|
||||
definitions.set(draft.id, { ...draft });
|
||||
if (!installed.has(draft.id)) { installed.add(draft.id); enabledApps.add(draft.id); }
|
||||
importDraft = null; preview.hidden = true; renderCatalog(); renderApps(); $('#yaml-preview').focus(); notify('已模拟安装 / 更新定义,未启动模型或任务');
|
||||
};
|
||||
preview.append(confirm);
|
||||
}
|
||||
}
|
||||
$('#market-fetch').onclick = () => {
|
||||
const valid = /^https?:\/\/[^\s/]+(?:\/[^\s]*)?$/.test($('#market-url').value.trim());
|
||||
marketReady = valid;
|
||||
$('#market-status').textContent = valid ? `已模拟获取目录:${$('#market-url').value.trim()},无网络请求。` : '请输入 http(s) 市场 URL 后重试。';
|
||||
renderCatalog();
|
||||
};
|
||||
$('#market-fail').onclick = () => { marketReady = false; $('#market-status').textContent = '模拟来源不可达,请点击模拟获取目录重试。未切换公共市场,已安装应用仍可使用。'; renderCatalog(); };
|
||||
$('#market-url').oninput = () => { marketReady = false; $('#market-status').textContent = '来源已修改,请重新模拟获取目录。'; renderCatalog(); };
|
||||
$('#yaml-preview').onclick = previewImport;
|
||||
$('#yaml-input').oninput = () => { importDraft = null; $('#import-preview').hidden = true; };
|
||||
$('#yaml-export').onclick = () => {
|
||||
const item = items.find(entry => entry.id === $('#yaml-app').value);
|
||||
const definition = definitions.get(item.id) || { schema: 'mock-v1', id: item.id, name: item.name, version: '1.0', entry: 'main', dependencies: 'none', resources: 'bundled' };
|
||||
$('#yaml-output').hidden = false;
|
||||
$('#yaml-output').textContent = '# Mock schema only; no credentials or business data\n' + Object.entries(definition).map(([key, value]) => `${key}: ${value}`).join('\n');
|
||||
};
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('button'); if (!button) return;
|
||||
const data = button.dataset;
|
||||
@@ -246,13 +356,35 @@ document.addEventListener('click', event => {
|
||||
if (data.page) openPage(data.page);
|
||||
if (data.open) openPage(items.find(item => item.id === data.open).name);
|
||||
if (data.tool) selectTool(data.tool);
|
||||
if (button.hasAttribute('data-note-settings')) showNoteSettings();
|
||||
if (data.appSettings) showAppSettings(data.appSettings);
|
||||
if (data.appToggle) {
|
||||
const id = settingsApp;
|
||||
if (data.appToggle === 'enabled') enabledApps.has(id) ? enabledApps.delete(id) : enabledApps.add(id);
|
||||
else residentApps = residentApps.includes(id) ? residentApps.filter(entry => entry !== id) : [...residentApps, id];
|
||||
renderApps(); renderComposer();
|
||||
if (currentPage === items.find(item => item.id === id).name) {
|
||||
openPage(currentPage, true);
|
||||
}
|
||||
renderAppSettings(); $(`[data-app-toggle="${data.appToggle}"]`).focus();
|
||||
}
|
||||
if (data.modelAction) {
|
||||
if (data.modelAction === 'config') {
|
||||
const row = button.closest('.model-service');
|
||||
let config = row.querySelector('.model-config');
|
||||
if (!config) { config = document.createElement('p'); config.className = 'model-config'; config.textContent = '连接配置预览:Ollama · http://localhost:11434 · 外部管理。仅展示示例,不连接或保存配置。'; row.append(config); }
|
||||
} else {
|
||||
const service = modelServices.find(entry => entry.id === data.model);
|
||||
if (service.running && service.users) { modelConfirmation = { id: data.model, action: data.modelAction }; renderModels(); $('[data-model-cancel]').focus(); }
|
||||
else applyModelAction(data.model, data.modelAction);
|
||||
}
|
||||
}
|
||||
if (button.hasAttribute('data-model-cancel')) { const id = modelConfirmation.id; modelConfirmation = null; renderModels(); $(`[data-model="${id}"]`).focus(); }
|
||||
if (button.hasAttribute('data-model-confirm') && modelConfirmation) applyModelAction(modelConfirmation.id, modelConfirmation.action);
|
||||
if (data.noteToggle || data.noteMode || data.noteFormat) {
|
||||
if (data.noteToggle === 'resident') residentApps = residentApps.includes('notes') ? residentApps.filter(id => id !== 'notes') : [...residentApps, 'notes'];
|
||||
if (data.noteToggle === 'showCount') noteSettings.showCount = !noteSettings.showCount;
|
||||
if (data.noteMode) noteSettings.mode = data.noteMode;
|
||||
if (data.noteFormat) noteSettings.format = data.noteFormat;
|
||||
renderApps(); renderNoteSettings();
|
||||
renderApps(); renderAppSettings();
|
||||
const selector = data.noteToggle ? `[data-note-toggle="${data.noteToggle}"]` : data.noteMode ? `[data-note-mode="${data.noteMode}"]` : `[data-note-format="${data.noteFormat}"]`;
|
||||
$(selector).focus();
|
||||
}
|
||||
@@ -260,7 +392,7 @@ document.addEventListener('click', event => {
|
||||
if (button.id === 'edit-apps') { editingApps = !editingApps; renderApps(); $('#edit-apps').focus(); }
|
||||
if (data.pin || data.install || data.installPin) {
|
||||
const id = data.pin || data.install || data.installPin;
|
||||
if (data.install || data.installPin) installed.add(id);
|
||||
if (data.install || data.installPin) { importDraft = null; $('#import-preview').hidden = true; installed.add(id); enabledApps.add(id); notify('已模拟安装,未启动任务或模型'); }
|
||||
if (data.pin || data.installPin) residentApps = residentApps.includes(id) ? residentApps.filter(entry => entry !== id) : [...residentApps, id];
|
||||
renderApps();
|
||||
if ($('#catalog').open) { renderCatalog(); ($(`#catalog [data-pin="${id}"]`) || $('#search')).focus(); }
|
||||
@@ -282,7 +414,15 @@ $('#resident-list').addEventListener('drop', event => {
|
||||
$('#resident-list').addEventListener('dragend', () => { draggedApp = ''; });
|
||||
$('#search').addEventListener('input', renderCatalog);
|
||||
$('#close-catalog').onclick = () => $('#catalog').close();
|
||||
$('#back-to-apps').onclick = () => { const query = $('#search').value; openCatalog(catalogMode); $('#search').value = query; renderCatalog(); ($('#catalog [data-note-settings]') || $('#search')).focus(); };
|
||||
$('#back-to-apps').onclick = () => { const query = $('#search').value; openCatalog(catalogMode); $('#search').value = query; renderCatalog(); ($(`#catalog [data-app-settings="${settingsApp}"]`) || $('#search')).focus(); };
|
||||
$('#app-settings').addEventListener('keydown', event => {
|
||||
const group = event.target.closest('.note-segments');
|
||||
if (!group || !['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return;
|
||||
event.preventDefault();
|
||||
const buttons = [...group.querySelectorAll('button')];
|
||||
const index = buttons.indexOf(event.target);
|
||||
buttons[event.key === 'Home' ? 0 : event.key === 'End' ? buttons.length - 1 : (index + (event.key === 'ArrowRight' ? 1 : -1) + buttons.length) % buttons.length].click();
|
||||
});
|
||||
function toggleWorkbar(visible) { $('.workbar').hidden = !visible; $('#toggle-workbar').setAttribute('aria-expanded', String(visible)); }
|
||||
$('#toggle-workbar').onclick = () => toggleWorkbar($('.workbar').hidden);
|
||||
$('#close-tool').onclick = () => { toggleWorkbar(false); $('#toggle-workbar').focus(); };
|
||||
@@ -300,7 +440,7 @@ document.addEventListener('keydown', event => {
|
||||
if (event.key === 'Enter' && document.activeElement === $('#search')) { event.preventDefault(); actions[0].click(); }
|
||||
});
|
||||
$('#theme').onclick = () => { const dark = document.documentElement.dataset.theme !== 'dark'; document.documentElement.dataset.theme = dark ? 'dark' : 'light'; $('#theme').textContent = dark ? '切换浅色' : '切换深色'; };
|
||||
$('#reset').onclick = () => { installed = new Set(items.filter(item => item.builtin).map(item => item.id)); residentApps = ['notes', 'knowledge']; editingApps = false; renderApps(); notify('应用安装与常驻配置已恢复默认'); };
|
||||
$('#reset').onclick = () => { installed = new Set(items.filter(item => item.builtin).map(item => item.id)); residentApps = [...installed]; enabledApps = new Set(installed); Object.assign(noteSettings, { showCount: true, mode: 'immediate', format: 'combined' }); editingApps = false; definitions.clear(); importDraft = null; $('#import-preview').hidden = true; renderApps(); renderComposer(); openPage('当前对话'); notify('应用启用、常驻和笔记设置已恢复默认'); };
|
||||
let demoRunning = false;
|
||||
let demoRecording = false;
|
||||
let demoAttachments = [];
|
||||
@@ -317,8 +457,11 @@ function renderComposer() {
|
||||
const select = $(`#${id}`);
|
||||
if (select.value) addTag(select.selectedOptions[0].textContent, () => { select.value = ''; });
|
||||
}
|
||||
document.querySelectorAll('[data-knowledge]:checked').forEach(input => addTag(input.dataset.knowledge, () => { input.checked = false; }));
|
||||
if ($('#composer-retrieval').value === 'always') addTag('每次检索', () => { $('#composer-retrieval').value = 'auto'; });
|
||||
document.querySelectorAll('[data-knowledge-control]').forEach(element => { element.hidden = !enabledApps.has('knowledge'); });
|
||||
if (enabledApps.has('knowledge')) {
|
||||
document.querySelectorAll('[data-knowledge]:checked').forEach(input => addTag(input.dataset.knowledge, () => { input.checked = false; }));
|
||||
if ($('#composer-retrieval').value === 'always') addTag('每次检索', () => { $('#composer-retrieval').value = 'auto'; });
|
||||
}
|
||||
tags.hidden = !tags.childElementCount;
|
||||
$('#stop').hidden = !demoRunning;
|
||||
$('#send').disabled = !$('#message').value.trim() && $('#composer-command').value !== 'command';
|
||||
|
||||
@@ -4,20 +4,19 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 适用产品 | GoodBuddy 桌面端 |
|
||||
| 关联设计 | [统一界面设计系统](../../../UI-DESIGN.md)、[助手工作栏 PRD](../assistant-workbar/prd.md) |
|
||||
|
||||
## 1. 背景
|
||||
|
||||
计划修订说明:当前实施范围以[第 8 节](#8-分期)为准。本文其余章节仍包含 v0.2 的
|
||||
左下角系统工具方案;启动实现前须同步功能要求、验收及配套设计,旧快捷工具条不再列入实施。
|
||||
本版已同步应用中心方案,分期见[第 8 节](#8-分期)。左下角用于访问和管理应用,系统工具保留在右侧工作栏。
|
||||
|
||||
主侧栏当前把“当前对话”“魔法笔记”“知识库”“智能心跳”和“运行记录”放在同一导航组,
|
||||
底部长期显示“GB 本地工作区”和平台架构。底部信息不能反映用户可以执行的操作,文件、终端
|
||||
和浏览器则需要先找到右侧助手工作栏后才能打开。
|
||||
原主侧栏把“当前对话”“魔法笔记”“知识库”“智能心跳”和“运行记录”放在同一导航组,
|
||||
底部显示“GB 本地工作区”和平台架构。当前已将底部改为应用中心及独立设置入口,文件、终端
|
||||
和浏览器继续从右侧助手工作栏打开。
|
||||
|
||||
后续插件可能提供完整页面,也可能只提供辅助面板或命令。若不先区分系统入口、应用和工具,
|
||||
插件接入后会同时争用主导航、侧栏底部和助手工作栏,用户也无法判断入口会切换页面还是打开
|
||||
@@ -25,16 +24,17 @@
|
||||
|
||||
## 2. 产品定义
|
||||
|
||||
### 2.1 三类入口
|
||||
### 2.1 入口分类
|
||||
|
||||
| 类型 | 首期成员 | 默认位置 | 用户控制范围 |
|
||||
| --- | --- | --- | --- |
|
||||
| 系统入口 | 当前对话、运行记录、设置 | 主导航固定位置或侧栏底部 | 不可隐藏、卸载或改变类型 |
|
||||
| 应用 | 魔法笔记、知识库、智能心跳 | 主导航应用区 | 可控制是否显示;隐藏不删除数据 |
|
||||
| 系统工具 | 文件浏览器、终端、浏览器、资源监控 | 侧栏底部快捷工具栏 | 首期固定提供,不可卸载 |
|
||||
| 固定内置应用 | 知识库、智能心跳 | 主导航固定位置及应用中心 | 始终启用并显示,不可启停、取消常驻或排序;保留工作区实际配置及心跳单条计划 enabled |
|
||||
| 可选内置应用 | 魔法笔记、本机推理 | 应用中心及主导航常驻区 | 独立控制启用、常驻与排序;保留数据 |
|
||||
| 系统工具 | 文件浏览器、终端、浏览器、资源监控 | 右侧助手工作栏 Tab 和“+”目录 | 自带且不可卸载,按工作栏规则管理实例 |
|
||||
|
||||
运行记录是系统审计页面,不属于工具。文件浏览器、终端、浏览器和资源监控是系统工具;它们
|
||||
的快捷入口位于左下角,实际界面继续由右侧助手工作栏承载。
|
||||
的入口与实际界面均由右侧助手工作栏承载,不列入应用中心。
|
||||
|
||||
### 2.2 后续插件类型
|
||||
|
||||
@@ -43,10 +43,10 @@
|
||||
|
||||
## 3. 目标
|
||||
|
||||
- 用户从侧栏底部一次点击即可打开文件、终端、浏览器或资源监控。
|
||||
- 主导航只承载完整页面,辅助工具不占用一级页面位置。
|
||||
- 用户从侧栏底部进入应用中心,查找、打开和配置内置应用。
|
||||
- 主导航承载应用入口,本机推理打开 Modal,其余内置应用打开完整页面;辅助工具由工作栏承载。
|
||||
- 运行记录始终可发现,不受应用显示设置和插件状态影响。
|
||||
- 内置应用可以从一个设置位置控制主导航可见性,隐藏后仍可恢复。
|
||||
- 可选内置应用可独立设置启用与常驻,支持排序;知识库和智能心跳始终显示,未常驻的已启用应用仍可从应用菜单打开。
|
||||
- 为插件应用和插件工具保留明确的入口契约,不提前建设插件安装能力。
|
||||
|
||||
## 4. 非目标
|
||||
@@ -56,7 +56,7 @@
|
||||
- 不把文件树、终端或浏览器重新实现到左侧栏。
|
||||
- 不改变助手工作栏的面板实例、停靠、执行空间和安全边界。
|
||||
- 不允许隐藏运行记录、当前对话或设置。
|
||||
- 不因隐藏应用而删除魔法笔记、知识库或智能心跳数据。
|
||||
- 不因隐藏可选应用而删除业务数据。
|
||||
- 不把“显示在侧边栏”解释为停止智能心跳计划或后台任务。
|
||||
- 不提供任意本机程序启动器。
|
||||
- 首期资源监控不采集远程主机、GPU、显存或网络吞吐,不提供后台告警和历史持久化。
|
||||
@@ -75,42 +75,43 @@
|
||||
|
||||
### FR-3 内置应用区
|
||||
|
||||
魔法笔记、知识库和智能心跳显示在当前对话与运行记录之间。应用入口打开对应一级页面,保留
|
||||
现有徽标、范围和页面状态。用户可以在设置中控制其主导航可见性。
|
||||
主导航在当前对话之后依次显示固定知识库、智能心跳,再按用户顺序显示已启用且常驻的可选应用,运行记录位于应用区之后。两个固定应用不参与排序。本机推理入口打开独立 Modal,其余应用打开对应一级页面,保留
|
||||
现有徽标、范围和页面状态。管理 Modal 列出全部内置应用,知识库和智能心跳只提供“打开”。
|
||||
|
||||
### FR-4 隐藏应用
|
||||
|
||||
隐藏应用只移除主导航入口,不删除数据、不修改业务启停状态,也不终止正在进行的操作。显隐
|
||||
Switch 更新成功后主导航立即生效;更新失败时恢复原状态并允许重试。用户可从设置恢复入口。
|
||||
取消常驻只移除主导航捷径,不删除数据、不修改启用状态,也不终止正在进行的操作。
|
||||
关闭可选应用则同步移除业务入口,保留常驻偏好;知识库和智能心跳不受这些偏好影响。
|
||||
配置保存成功后各入口使用同一已确认状态,失败时保持原状态并允许重试。
|
||||
|
||||
### FR-5 系统工具快捷栏
|
||||
### FR-5 应用中心入口
|
||||
|
||||
侧栏底部提供文件浏览器、终端、浏览器和资源监控四个图标按钮,并保留独立设置按钮。每个
|
||||
按钮具有可见工具提示和可访问名称。首期系统工具顺序固定为文件、终端、浏览器、资源监控。
|
||||
侧栏底部提供应用中心和独立设置入口。点击应用中心向上展开锚定按钮的轻量菜单,无模态遮罩。菜单列出已启用应用,不按是否打开过、历史记录或常驻状态筛选;点击应用行关闭菜单,本机推理打开 Modal,其余应用在主内容区打开页面。
|
||||
菜单底部“管理应用”关闭菜单并打开既有管理 Modal,在其中浏览、搜索全部内置应用,为已关闭的可选应用提供设置与重新开启入口。无搜索结果时显示空状态并可清除搜索。
|
||||
首期不展示商店、安装、推荐或系统工具清单。入口具有可访问名称,Escape 关闭后恢复触发位置的焦点。
|
||||
|
||||
### FR-6 文件浏览器入口
|
||||
|
||||
点击文件按钮时展开助手工作栏,并聚焦默认存在且不可关闭的“工作区”单实例。该实例保持
|
||||
在右侧选择“工作区”Tab 时聚焦默认存在且不可关闭的单实例。该实例保持
|
||||
“跟随当前上下文”或“固定到指定对象”状态;迁移或布局修复发现实例缺失时补回,并默认跟随
|
||||
当前项目。没有项目时使用已配置的默认工作区。跟随上下文所需的路径不可用时,面板显示修复
|
||||
入口;已有有效固定目标不受当前项目和默认工作区状态影响。
|
||||
入口;已有有效固定目标不受当前项目和默认工作区状态影响。固定目标失效时修复该目标,不静默回退到其他目录。
|
||||
|
||||
### FR-7 终端入口
|
||||
|
||||
点击终端按钮时展开助手工作栏。当前执行空间有正在启动或运行中的终端时聚焦最近使用的实例;
|
||||
没有时创建一个绑定当前执行空间的新终端,已退出、已中断或失败的历史 Tab 保持不变。没有
|
||||
项目时当前执行空间为本机 Home。创建额外并行终端继续使用工作栏现有的“+”目录入口。
|
||||
选择已有终端 Tab 仅激活该实例,保持目标、会话和历史状态。通过工作栏“+”选择终端时
|
||||
创建绑定当前执行空间的新实例,不按目标复用已有实例;无项目时使用本机 Home。
|
||||
远程项目的新实例绑定其托管 SSH 执行空间和权威目录。创建失败保留当前主页面与其他实例。
|
||||
|
||||
### FR-8 浏览器入口
|
||||
|
||||
点击浏览器按钮时展开助手工作栏,并优先聚焦绑定当前 Conversation 的最近使用浏览器实例;
|
||||
没有匹配实例时创建一个。没有活动 Conversation 时聚焦最近使用的浏览器实例;完全不存在时
|
||||
创建待绑定空实例。该快捷入口不会因连续点击创建重复实例;用户通过工作栏“+”选择浏览器时
|
||||
则始终创建新实例。入口不打开系统默认浏览器,也不创建旁路 WebView。
|
||||
选择已有浏览器 Tab 保留其绑定目标、页面和导航状态;通过工作栏“+”选择浏览器时创建新实例,
|
||||
有活动 Conversation 时绑定该会话,否则创建待绑定空实例。同一 Conversation 的实例共享隔离
|
||||
登录上下文,页面与导航状态独立。入口不打开系统默认浏览器,也不创建旁路 WebView。
|
||||
|
||||
### FR-9 资源监控入口
|
||||
|
||||
点击资源监控按钮时展开助手工作栏,并打开或聚焦单实例的本机“资源监控”面板。面板显示
|
||||
通过右侧工作栏“+”打开或聚焦单实例的本机“资源监控”,已有面板直接选择其 Tab。面板显示
|
||||
整机 CPU 使用率、内存使用量与总量、磁盘读写速率,以及 GoodBuddy 桌面进程的 CPU、内存
|
||||
和进程数摘要。指标不可用时显示具体项目和原因,不能显示为 `0`。
|
||||
|
||||
@@ -119,25 +120,28 @@ Renderer 内存中最多保留最近 5 分钟。任一条件不满足时停止
|
||||
也不据此产生侧栏告警。首次采样及停止后恢复的首次采样只建立 CPU 和磁盘 IO 速率基线,
|
||||
不显示伪造的 `0` 值。
|
||||
|
||||
### FR-10 快捷入口状态
|
||||
### FR-10 工作栏入口状态
|
||||
|
||||
工具面板已激活时,对应按钮显示选中状态;面板存在但未激活时保持普通状态。运行中、错误或
|
||||
工具面板已激活时,对应 Tab 显示选中状态;面板存在但未激活时保持普通状态。运行中、错误或
|
||||
等待用户处理可以显示徽标,但必须有可访问文字,且不得自动抢占当前面板。
|
||||
|
||||
### FR-11 应用导航设置
|
||||
|
||||
设置页提供“应用与工具”管理入口。首期“应用”区域列出三个内置应用并提供“显示在侧边栏”
|
||||
Switch,切换后立即持久化。系统工具区域列出四项内置固定能力,不提供虚假的卸载或停用按钮。
|
||||
管理 Modal 为魔法笔记和本机推理提供应用设置、常驻管理和排序。知识库和智能心跳只提供“打开”,不添加通用空设置页;原工作区配置入口及心跳单条计划 enabled 保持不变。设置中“启用应用”与“常驻左侧菜单”分别保存,
|
||||
修改即时提交;请求期间串行处理并禁用会修改同一配置的控件,失败就地反馈。
|
||||
魔法笔记设置迁入共同设置界面,应用中心与笔记页访问同一份配置,全局设置不再提供魔法笔记入口。持久化成功后的设置变更同步覆盖工具配置写入;每次重开中心先刷新并锁定修改,较新的确认快照优先于迟到的读取结果。
|
||||
|
||||
### FR-12 偏好持久化
|
||||
|
||||
应用可见性按设备持久化。升级时保留当前行为:已有可用内置应用继续显示;魔法笔记原有功能
|
||||
启停设置保持原意,不由新的导航可见性覆盖。
|
||||
可选应用启用、常驻和顺序按设备持久化,重启后恢复。未配置的启用与常驻默认开启;
|
||||
魔法笔记待办数量默认显示,评论方式与形式沿用现有默认值。迁移保留已明确保存的关闭和排序选择,
|
||||
旧魔法笔记入口开关绑定业务启用值,迁为“启用应用”并保留原值;常驻使用独立配置,
|
||||
只有已发布的独立显隐偏好才映射到常驻,不能由常驻推导业务启停。偏好仅包含 `magic-notes` 和 `local-inference`;未发布草案中的 `knowledgeEnabled`、`heartbeatEnabled` 已删除,不增加迁移或兼容读取。
|
||||
|
||||
### FR-13 窄窗口行为
|
||||
|
||||
窄窗口下主侧栏仍按现有抽屉方式显示。系统工具按钮保持单行且不与设置按钮重叠;空间不足时
|
||||
收起文字和非必要分隔,不隐藏四个系统工具或设置。
|
||||
窄窗口下主侧栏仍按现有抽屉方式显示,应用中心和设置保持可操作且不重叠。应用列表可滚动,
|
||||
搜索、设置及键盘排序不依赖拖拽;工具沿用右侧工作栏响应式规则,不搬到左侧。
|
||||
|
||||
### FR-14 项目活动汇总
|
||||
|
||||
@@ -169,49 +173,46 @@ Switch,切换后立即持久化。系统工具区域列出四项内置固定
|
||||
- 主导航能明确区分固定系统入口和可配置内置应用。
|
||||
- 运行记录在所有应用显隐组合下保持可见并可打开。
|
||||
- 左下角不再用“本地工作区”与系统架构占据主要空间。
|
||||
- 文件、终端、浏览器和资源监控均可从左下角打开正确的工作栏面板,且不产生第二套内容界面。
|
||||
- 连续点击系统工具不会无意创建重复的浏览器或终端实例;工作区始终复用不可关闭的固定实例。
|
||||
用户通过工作栏“+”可以显式创建额外浏览器或终端实例。
|
||||
- 应用菜单只列已启用应用,点击行关闭菜单;本机推理打开保留底层工作区的 Modal,其余应用进入主内容区。“管理应用”打开可搜索的既有 Modal。知识库和智能心跳固定可用,只有两个可选应用可管理启用、常驻与顺序,首期无商店入口。
|
||||
- 系统工具仅从右侧 Tab 与“+”访问;选择 Tab 不创建或改绑实例,“+”可显式创建额外浏览器或终端。
|
||||
- 资源监控以 2 秒间隔更新,保留不超过 150 个样本;面板不活动、工作栏收起或窗口隐藏、
|
||||
最小化后停止请求新样本。
|
||||
- CPU、内存、磁盘 IO 或 GoodBuddy 指标不可用时,页面区分部分可用与整体失败。
|
||||
- 隐藏内置应用不删除数据、不停止后台计划,并能从设置恢复;保存失败时入口保持原状。
|
||||
- 取消常驻不删除数据、不停止后台计划;关闭应用按统一规则联动业务入口,保存失败时各处保持原状。
|
||||
- 魔法笔记设置迁移保留已有选择,应用中心与笔记页共用同一配置。
|
||||
- 键盘、屏幕阅读器、浅色主题、深色主题和窄窗口均可完成相同操作。
|
||||
|
||||
## 8. 分期
|
||||
|
||||
### 首期
|
||||
|
||||
- 将侧栏最底部改为内置应用中心入口,设置按钮保持独立;具体列表、宫格或卡片样式从
|
||||
Demo 中收敛确定。
|
||||
- 应用中心提供魔法笔记、知识库、智能心跳的浏览、搜索与打开,不要求安装。
|
||||
- 支持应用常驻主导航、取消常驻和排序;未常驻应用仍可从应用中心打开,隐藏不删除数据或停止后台任务。
|
||||
- 侧栏最底部应用中心打开向上锚定的轻量菜单,无模态遮罩;底部“管理应用”进入与设置相同的整窗 Modal 和可搜索应用列表,设置按钮保持独立。
|
||||
- 应用中心提供魔法笔记、知识库、智能心跳及本轮追加的本机推理入口,不要求安装。本机推理 Modal 与服务单独实现和验证。
|
||||
- 支持魔法笔记、本机推理常驻主导航、取消常驻和排序;知识库和智能心跳固定显示,已启用未常驻应用仍可从菜单打开,隐藏不删除数据或停止后台任务。
|
||||
- 应用中心和应用页面就近提供已有功能设置,共享真实设置来源;已有全局设置入口定位到同一界面。
|
||||
魔法笔记优先接入待办数量、AI 评论方式和评论形式,入口显示与常驻控制统一,避免重复开关。
|
||||
- 文件、终端、浏览器保留在右侧工作栏 Tab,不在左下角复制入口,也不纳入应用中心。
|
||||
- 原计划的本机资源监控仍作为系统工具交付项,在右侧增加单实例面板;与应用中心分别实现和验证。
|
||||
- 保持运行记录为固定系统入口。
|
||||
|
||||
首期验收需覆盖:无商店入口时可以访问全部内置应用;取消常驻后仍可重新打开和配置;
|
||||
首期验收需覆盖:无商店入口时可从菜单访问已启用应用,并从管理 Modal 恢复关闭的可选应用;取消常驻后仍可重新打开和配置;
|
||||
不同设置入口状态一致;常驻排序重启后保留;右侧工具切换不改变当前应用页面。
|
||||
本节是范围调整,Demo 交互通过不代表正式功能已经实现。
|
||||
|
||||
### 补充要求:魔法笔记迁移与入口联动
|
||||
|
||||
- 将现有魔法笔记设置迁入应用中心的应用设置界面,笔记页面内的设置入口访问同一份配置。
|
||||
魔法笔记可常驻主导航;原全局设置位置如保留入口,应定位到共同设置界面。
|
||||
魔法笔记可常驻主导航;移除原全局设置入口,保留已有配置值和业务数据。
|
||||
- 魔法笔记相关布尔开关默认全部开启,包括入口显示(常驻)和未完成待办数量显示。
|
||||
AI 评论方式、评论形式是单选项,沿用现有默认值,不把“全部开启”解释为同时选中多个模式。
|
||||
新默认值适用于未配置状态,迁移时保留用户已明确保存的选择。
|
||||
- 应用关闭后的入口联动必须覆盖主导航和会话页面,不能只更新应用中心卡片或一个菜单。
|
||||
知识库关闭后,左侧知识库菜单及会话中的知识库入口按钮同步移除;重新开启后按配置恢复。
|
||||
- “取消常驻”只移除主导航捷径;“关闭应用”移除该应用在主导航、会话等业务界面的入口。
|
||||
应用中心保留管理和重新开启入口。原型中仅有常驻控制,正式设计需补齐这两种状态,避免混用。
|
||||
- 入口变化不删除已有笔记或知识数据。知识库已打开页面、会话已关联知识以及后台任务的处理,
|
||||
在详细逻辑同步时分别定义,不能仅凭隐藏按钮推断功能或后台行为已经停用。
|
||||
- 知识库和智能心跳为固定内置应用,侧栏入口始终存在;会话知识范围、检索方式、标签及请求行为继续遵守知识库自身规则,心跳单条计划 enabled 仍由心跳页面管理。
|
||||
- 可选应用“取消常驻”只移除主导航捷径;“关闭应用”移除该应用在业务界面的入口。
|
||||
应用中心保留管理和重新开启入口,重新开启沿用常驻偏好,不自动导航。
|
||||
- 入口变化不删除已有数据。关闭后已打开的可选应用页显示不可用状态及恢复设置入口。
|
||||
已发出的请求和后台任务沿用各功能的生命周期规则,关闭入口不新增强制取消或重放行为。
|
||||
|
||||
补充验收:从任一配置入口关闭知识库,主导航和当前会话按钮立即同步移除,切换会话、
|
||||
重启后也不重新出现;重新开启后恢复相应入口。验证魔法笔记未配置时的默认开启,以及
|
||||
补充验收:关闭全部可选应用后,知识库和智能心跳侧栏入口仍可用,知识会话入口及心跳计划状态不变;管理 Modal 的两个固定应用行仅有“打开”,不能启停、取消常驻或排序。验证魔法笔记未配置时的默认开启,以及
|
||||
已有关闭选择在迁移后保留。设置保存失败时各入口保持原状态,不能出现互相矛盾的显示。
|
||||
|
||||
### 后续:应用商店与扩展
|
||||
@@ -229,12 +230,13 @@ Switch,切换后立即持久化。系统工具区域列出四项内置固定
|
||||
市场面向组织私有化部署。用户配置市场 URL 后即可获取该来源的应用目录与详情,并按需获取
|
||||
应用;需要认证的来源复用其登录流程。市场不可达时说明原因并允许重试,不影响已安装应用
|
||||
和内置应用使用,也不自动切换到公共市场。市场 URL 只影响应用市场,不改变模型、桌面更新
|
||||
或 Runtime 的下载源。
|
||||
或 Runtime 的下载源。市场 URL 按设备保存,重启后保留;认证失效时提示重新认证。
|
||||
|
||||
应用支持以 `.yml` 导入、导出,兼容 `.yaml` 扩展名,用于组织内部共享、备份和迁移。
|
||||
导出内容为应用定义及可迁移配置,包含格式版本、应用标识、应用版本、入口声明和依赖说明;
|
||||
凭据、会话、笔记及知识库业务数据不随应用定义导出。导入前展示应用摘要、缺失依赖和与已有
|
||||
应用的冲突;同标识应用由用户选择更新或取消,不静默覆盖本机配置。
|
||||
应用的冲突;同标识应用由用户选择更新或取消,不静默覆盖本机配置。成功后展示实际版本与可用状态;
|
||||
获取、校验或更新失败保留原已安装版本与配置,并显示可重试原因。
|
||||
|
||||
YAML 描述文件不能代替模型权重或插件程序包。有外部依赖时应明确资源引用;离线导入仅在
|
||||
所需资源已具备时可完成使用,否则列出需要补充的资源,不显示为可运行。导入应用不会自动
|
||||
@@ -247,19 +249,18 @@ YAML 描述文件不能代替模型权重或插件程序包。有外部依赖时
|
||||
|
||||
### FR-16 本机模型管理入口(新增计划项)
|
||||
|
||||
应用中心提供“本机模型”内置管理应用,可选择常驻左侧,打开主内容区管理页。该入口统一
|
||||
查看各功能使用的本机模型及服务状态,包括已接入的 OCR、向量、语音或推理引擎;具体支持
|
||||
清单需核对现有模型服务后确定,不因提供入口而假定全部引擎均已支持进程控制。
|
||||
应用中心提供“本机推理”内置管理应用,可选择常驻左侧。从常驻入口、应用菜单或管理 Modal 的“打开”均进入同一独立管理 Modal;来源菜单或管理 Modal 关闭,底层工作区、当前项目、会话及草稿保持不变。该入口统一
|
||||
查看本机语音识别、OCR、向量生成等服务的运行状态与可用资源指标。
|
||||
|
||||
页面展示模型名称、用途、所属引擎、管理方式、运行状态、占用情况及使用它的应用或任务。
|
||||
GoodBuddy 管理的进程提供启动、停止、重启和错误查看;外部启动的模型服务标明“外部管理”,
|
||||
提供连接状态与配置入口,未接入其管理接口时不提供停止或重启。进程内运行的模型按引擎实际
|
||||
能力提供加载与释放,不伪造独立进程。
|
||||
Modal 只有服务列表,显示名称、简短状态、引擎、模型、资源和错误,不显示任务历史、任务页签、单任务取消或外部连接列表。独立向量进程显示 CPU 与工作集内存,并标明进程范围;CPU 预热、采样失败和共享进程 worker 无法独立统计时说明原因,不能用零或整个应用进程占用代替服务指标。活动请求可能仍在排队或加载,不能据此宣称服务正在运行。ASR 当前仅显示按需运行且状态未知;OCR 和向量服务使用各自执行模块的状态。
|
||||
|
||||
停止或重启前展示正在使用该服务的任务和影响,由用户决定是否继续;取消时保留运行状态,
|
||||
失败时显示实际状态和原因。多个应用共用同一模型服务时展示同一个服务及其使用方,不重复
|
||||
启动进程。隐藏管理入口、关闭管理页面或取消应用常驻均不停止模型;卸载模型文件是独立操作。
|
||||
服务行保留实际支持的启动、停止、重启或模型加载、释放,工具栏保留刷新和模型设置入口。停止后重新启动不自动重放已中断任务。
|
||||
|
||||
资源监控仍是只读系统工具,本机模型管理负责模型生命周期,两者共享可用的状态来源。
|
||||
该项可独立于商店交付;当前只纳入计划,具体分期、任务使用期间的停止策略和各引擎管理能力
|
||||
在详细设计中确定。原首期资源监控的 GoodBuddy 桌面进程统计范围不因此扩大。
|
||||
停止、重启或释放前,在管理 Modal 上方打开紧凑影响确认,展示正在使用该服务的任务和影响;没有已登记活动任务时也说明执行前会再次核对。取消时保留运行状态,
|
||||
失败时关闭旧确认、显示原因并刷新状态,再次操作须重新确认当前影响。多个应用共用同一模型服务时只展示同一个服务,不重复
|
||||
启动进程。隐藏管理入口、关闭管理 Modal 或取消应用常驻均不停止模型;卸载模型文件是独立操作。
|
||||
确认打开时不能关闭底层管理 Modal;Escape 只关闭最上层,提交期间禁止关闭及重复执行。关闭后恢复可用入口焦点,具体规则见 [UI 设计](./ui-design.md#91-本机推理管理)。
|
||||
|
||||
资源监控仍是只读系统工具,本机推理聚焦本地服务状态。该项独立于商店接入生产源码,支持 OCR 空闲加载与释放、内置向量进程启动/停止/重启;TTS 显示不可用。后端保留请求登记及原有取消能力,供执行路径和服务影响校验使用。各引擎边界见
|
||||
[技术清单](./technical-design.md#111-本机推理任务与执行服务),验证范围见[实施进度](./progress.md)。
|
||||
原首期资源监控的 GoodBuddy 桌面进程统计范围不因此扩大。
|
||||
|
||||
@@ -1,4 +1,223 @@
|
||||
# 项目活动级联实施进度
|
||||
# 应用导航实施进度
|
||||
|
||||
## 2026-09-18:独立推理进程资源采样
|
||||
|
||||
对应 FR-16、US-F3。向量 utility process 已按 PID 接入 Electron 真实 CPU 和工作集内存,服务行显示进程范围、PID、百分比及 MiB。ASR 为主进程线程,OCR 为渲染进程 Web Worker,分别显示不能独立统计的原因。采样口径及基线规则见[技术设计](./technical-design.md)。服务 Modal 继续保持单一列表。
|
||||
|
||||
本轮验证:资源采样器、Main 服务、向量 Broker、Modal 共 39 项定向测试通过;IPC 与 OCR Bridge 另有 130 项通过,共 169 项。类型检查、修改范围 ESLint、`git diff --check` 通过。全仓 lint 仅报告未修改的 story-graph Demo 的 22 个 `document` 错误;`npm test` 超过 60 秒未完成,不记为通过。中文功能文档 deai-writing 扫描阻断项为 0,复核项中的采样限制和验证边界按事实保留。
|
||||
|
||||
真实 Windows Electron 验证直接调用生产 `createEmbeddingUtilityTransport` 和采样器,使用临时 CPU 工作负载:PID 5892 的工作集从 57,769,984 字节变为 63,090,688 字节,区间 CPU 为 98.71%;退出后返回不可用,重启得到 PID 20532 并重新预热。进程刚 spawn 时指标可能尚未登记,返回等待数据。此验证没有加载推理模型,没有调用模型 API,也没有重新验收完整 App 窗口;macOS、Linux 尚未实机验证。改动仅涉及桌面本机服务监视,不影响 GoodBuddy Agent 或远程执行。
|
||||
|
||||
## 2026-09-18:本机推理服务监视简化
|
||||
|
||||
对应 FR-16、US-F3、US-F5;US-F4 的 Modal 单任务取消入口退役。`LocalInferencePage` 直接显示服务列表,移除任务页签、历史、外部连接和长说明,保留刷新、模型设置及受支持的服务操作。现行定义见 [UI 设计](./ui-design.md#91-本机推理管理)。
|
||||
|
||||
当时没有服务级 CPU/内存采样,统一显示“暂不可用”;这一限制已由上方资源采样实现修正。ASR 模型可用时返回 `unknown`,登记活动请求不再将状态覆盖为 `running`。OCR/向量仍使用各自执行模块的状态。操作失败关闭旧确认并刷新,重试时重新确认当前影响;后端任务清单校验和 OCR 忙碌保护保留。
|
||||
|
||||
验证结果:
|
||||
|
||||
- 定向运行 LocalInferencePage、Main 服务、向量 Broker、OCR Bridge 和 IPC,5 个文件共 165 项通过;App 的本机推理入口和停用恢复场景 4 项通过。
|
||||
- `npm run typecheck` 和修改范围 ESLint 通过。`npm run lint` 仍报告无关 `docs/features/story-graph/demo.js` 的 22 个 `document` 错误。
|
||||
- `npm test` 首次超过 120 秒,再次超过 600 秒,均未完成。第二次报告的 App 失败来自旧页签断言,已改为服务列表并定向复验通过;不将全量测试记为通过。
|
||||
- 中文功能文档按 deai-writing 扫描,阻断项为 0;复核项中的状态限制和操作清单按产品事实保留。
|
||||
|
||||
本轮未启动真实 Electron 或调用模型,未重新验证原生窗口排版。改动仅影响桌面本机服务监视,不涉及 GoodBuddy Agent 或远程执行路径。未创建提交,下方历史记录保留当时的 UI 与验证边界。
|
||||
|
||||
## 2026-09-17:应用卡片与紧凑详情
|
||||
|
||||
对应 US-B6 至 US-B9、US-F3 至 US-F5。已核对 `ApplicationCenter`、`LocalInferencePage`
|
||||
及其样式:管理改为桌面双列、窄窗口单列卡片,固定应用仅有打开;可选应用提供打开和设置,
|
||||
关闭时禁用打开。启用、常驻及上下移动位于最大 `640px` 的详情,卡片拖动排序保留。
|
||||
详情使用紧凑返回标题栏,进入及返回时滚动归顶。本机推理使用内容高度 Modal,列表状态与操作
|
||||
分区并响应式排列。现行规则见 [UI 设计](./ui-design.md#4-应用中心与共同设置)。
|
||||
|
||||
本轮实现验证结果由实现方提供,本次仅更新文档,未重跑产品测试:
|
||||
|
||||
- 59 项组件测试及 8 项选定 App 测试通过;`npm run typecheck`、修改范围 ESLint 和
|
||||
`git diff --check` 通过。
|
||||
- 真实 Electron 使用当前 Renderer、已有 Main/Preload、真实 IPC 和隔离配置,四种尺寸、浅深
|
||||
主题共 32 个视图无横向溢出;导航、焦点、Escape、背景隔离及详情滚动重置已检查。
|
||||
- 实机任务列表为空态;有任务及长名称由组件测试覆盖。模型调用为 0,未重跑全量测试,
|
||||
未创建提交;此前全量测试与全仓 lint 的失败记录仍保留,不将整体验收标为完成。
|
||||
|
||||
实机报告已核对:`C:/Users/jiang/AppData/Local/Temp/opencode/application-cards-review-cUiVAc/result.json`,
|
||||
截图位于同目录。下方记录保留各轮当时的布局及验证范围,旧单列管理行和旧 Modal 尺寸不作为现行定义。
|
||||
|
||||
## 2026-09-17:本机推理改为独立 Modal
|
||||
|
||||
对应 FR-3、FR-5、FR-16、US-B1、US-B6、US-F3 至 US-F5。已核对当前 `App.tsx`、
|
||||
`LocalInferencePage.tsx`、`local-inference.css` 和 `dialog-focus.ts`:常驻入口、应用菜单及管理
|
||||
Modal 的“打开”均打开独立本机推理 Modal,保留底层工作区;其他内置应用仍打开主内容区页面。
|
||||
停止、重启和释放使用嵌套紧凑影响确认,复用背景隔离、焦点循环与恢复,按当前层处理 Escape,
|
||||
操作提交期间禁止关闭及重复执行。具体行为见 [UI 设计](./ui-design.md#91-本机推理管理)。
|
||||
|
||||
以下结果由实现代理提供,本次文档更新未重新运行代码测试:
|
||||
|
||||
- 180 项定向测试通过。
|
||||
- `npm run typecheck` 与修改范围的 ESLint 通过。
|
||||
- 完整 App 测试运行超时,未取得完整通过结果。
|
||||
- 全仓 lint 仍有 `docs/features/story-graph/demo.js` 的既有 22 个 `document` 全局声明错误。
|
||||
|
||||
随后完成真实 Electron 验证:当前 Renderer 源码配合当日已有 Main/Preload 构建,使用隔离配置,
|
||||
未模拟 IPC。三个入口均保留同一知识库工作区节点;背景隔离、Escape、焦点恢复及窄窗口侧栏
|
||||
开关回退通过。使用已有本机资产完成 OCR 模型加载与确认释放,推理请求和模型下载均为零。
|
||||
浅深主题及窄窗口未出现横向溢出,释放确认框约为 480 × 182 CSS 像素,内边距 16px、按钮高 36px。
|
||||
修正窄窗口关闭按钮换行后再次通过实机检查和 43 项相关测试。证据位于临时目录
|
||||
`inference-modal-ui-8KJFgQ/result.json` 及同目录截图;未验证重新打包后的应用。
|
||||
|
||||
`npm test` 在 240 秒边界停止,未取得全量通过结果;独立复跑心跳数据库测试为 6 项通过、
|
||||
2 项失败,均为已有的 `duplicate column name: pinned`。下方旧页面截图不作为 Modal 验收证据。
|
||||
本轮已同步源码与文档,未修改 Demo,未创建提交。
|
||||
|
||||
下方保留各轮当时的实现与验证记录,旧主内容区页面及无 props 描述不作为当前 Modal 的行为定义。
|
||||
|
||||
## 2026-09-17:生产界面密度与响应式复核
|
||||
|
||||
对应 US-B5 至 US-B9、US-F3。已审阅 `abcd-production-08rUQA` 中 A、B、D 的原生产截图。
|
||||
应用管理从全宽纵向块改为最大 `960px` 的内容高度 Modal:名称旁标明固定/可选状态,桌面操作
|
||||
同行,窄屏自然换行且排序箭头成组。共同设置统一标签、说明和开关对齐;本机推理改为弱分隔列表。
|
||||
菜单只调整共享字体和图标层级。展示规则见 [UI 设计](./ui-design.md#4-应用中心与共同设置)。
|
||||
|
||||
最终源码验证:
|
||||
|
||||
- 应用中心、应用菜单、本机推理、设置面板和共享 UI 的 5 个定向文件共 148 项通过。
|
||||
- `npm run typecheck`、改动 Renderer 文件的定向 ESLint、`git diff --check` 通过。
|
||||
- `npm run build:bundle` 成功;最后一处布局修正后,`npx electron-vite build --logLevel error` 再次成功。
|
||||
- 全仓 `npm run lint` 仍有 `docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误,未改动该文件。
|
||||
- 完整 `npm test` 用时 `648.80s`:4,454 项通过、67 项跳过、2 项失败。失败仍为心跳数据库
|
||||
迁移的 `duplicate column name: pinned`,位于未改动的 `assistant-database.ts:9988`;全量测试
|
||||
未通过。完整日志位于临时目录 `abcd-polish-tests-VxXZ34/test.log`。
|
||||
|
||||
真实 Electron 使用仓库 package 入口、当前生产 Main/Preload/App 和独立用户目录,未替换 IPC。
|
||||
最终截图与测量位于临时目录 `abcd-production-m9I4Vy`:`1200×775` 下四行约 `75px` 高,管理
|
||||
内容区 `scrollHeight` 与 `clientHeight` 均为 `420px`,操作同行;`520×740`、`375×667` 下
|
||||
管理、共同设置和推理内容无横向溢出。已审阅桌面与窄屏浅深色截图、375px 管理和设置截图。
|
||||
管理行按钮为 `34px`/`36px`,Switch 标签点击区高 `36px`,评论选项保留共享紧凑分段控件。
|
||||
生产验证覆盖菜单 End/Escape、Modal 焦点保持、页签方向键,以及 Space 切换常驻后重载 Renderer
|
||||
确认保存。运行报告为同目录 `result.json`,截图为 `A-menu*.png`、`B-manage*.png`、
|
||||
`C-settings*.png`、`D-inference*.png` 和 `D-tasks.png`。
|
||||
|
||||
此次为界面调整,未改后端生命周期;隔离目录没有安装模型,模型请求为 0,仅检查真实服务快照与任务空态。
|
||||
日志仍有既有字体 CSP 拦截,未捕获 JavaScript 异常。未验证模型启停、安装包或整个项目的功能验收。
|
||||
工作区原有 Runtime 与 story-graph 修改保留,未创建提交。
|
||||
|
||||
## 2026-09-17:轻量应用菜单与两个固定应用
|
||||
|
||||
对应 FR-3、FR-5、FR-11、FR-12、US-B5 至 US-B8、US-D1/US-D2。当前生产源码的底部应用中心向上展开轻量锚定菜单,无模态遮罩,只列已启用应用,不按打开历史或常驻状态筛选。点击应用行关闭菜单并打开主内容区;底部“管理应用”打开既有管理 Modal。
|
||||
|
||||
[ApplicationMenu](../../../src/renderer/src/ApplicationMenu.tsx) 与 [AnchoredMenu](../../../src/renderer/src/AnchoredMenu.tsx) 承担菜单及键盘、定位;[App](../../../src/renderer/src/App.tsx) 固定依次显示知识库、智能心跳,再显示已启用且常驻的可选应用。[管理 Modal](../../../src/renderer/src/ApplicationCenter.tsx) 中两个固定应用仅提供“打开”,不提供启用、常驻、排序或通用空设置;原工作区配置及心跳单条计划 enabled 不变。
|
||||
|
||||
[设置契约](../../../src/shared/application-settings-contracts.ts) 的可选偏好仅包含 `magic-notes`、`local-inference`。未发布草案中的 `knowledgeEnabled`、`heartbeatEnabled` 已删除,不增加迁移或兼容读取。[Store](../../../src/main/application-settings-store.ts) 持久化后发布权威快照的链路保留,包含配置工具写入;重开刷新期间锁定修改,较新快照使迟到的旧读取和保存响应失效。
|
||||
|
||||
本节收录本轮提供的最新验证结果;此次文档编辑未重新运行产品测试或模型请求:
|
||||
|
||||
- `npm run typecheck` 通过,`npx eslint src` 通过。
|
||||
- 完整 `npm test` 已结束:4,448 项通过、67 项跳过、2 项失败。两项仍为心跳数据库迁移的 `duplicate column name: pinned`,相关迁移未改动;全量测试未通过。
|
||||
- 全仓 `npm run lint` 仍有 `docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误,属于无关文档 Demo;全仓 lint 未通过。
|
||||
- Electron fixture 共 8 个主题/尺寸场景通过,覆盖原生键盘、焦点、菜单几何、原生视图遮挡及实际 `ApplicationSettingsStore` 持久化。此证据验证组件 fixture 和真实设置存储,不是完整生产 App 的端到端验收,也不是安装包验收。
|
||||
|
||||
完整生产 App 补充验证:`npm run build:bundle` 成功后,以隔离用户目录启动仓库 package 入口,
|
||||
使用实际 `out/main/index.js`、生产 Preload 和 App,未替换 IPC handlers。已验证上拉菜单、
|
||||
“管理应用”Modal、两个固定应用仅有“打开”、笔记常驻切换,以及刷新 Renderer 和磁盘配置的
|
||||
持久化结果;本机推理页通过真实注册 IPC 取得 ASR、OCR、向量和 TTS 状态。已审阅菜单与管理截图。
|
||||
证据位于临时目录 `abcd-production-08rUQA` 的 `result.json`、`A-menu.png`、`B-manage.png`、
|
||||
`D-inference.png` 和 `runtime.log`。生产启动日志有字体被 CSP 阻止的消息,未捕获 JavaScript 异常。
|
||||
此轮未重启整个应用验证持久化;隔离目录没有安装模型,未执行模型启停或模型请求。
|
||||
|
||||
A–D 已接入源码并通过上述生产 App 冒烟检查;不将全量测试、安装包及整个计划标为通过。
|
||||
D 的真实 ASR、OCR、向量引擎证据及最小 Main 接线限制保留在下方原记录中,本次状态读取不扩大
|
||||
其模型操作验证范围。本机资源监控仍待实现。Demo 的直接 Modal 和知识库/心跳可选设置已过时,本轮未编辑 Demo 文件。
|
||||
|
||||
下方为历史记录,保留当时实现、测试结果及限制,旧应用级开关和旧入口描述不作为现行行为定义。
|
||||
|
||||
## 2026-09-16:知识库固定入口与设置同步更正
|
||||
|
||||
本节更正下方早期记录的现行行为,历史实现和验证记录原文保留,不作为最新修订已重新通过验证的证据。
|
||||
A–D 的应用中心 Modal、可选应用共同设置、常驻排序及本机推理均已接入生产源码,整体验收边界仍见各次记录。
|
||||
|
||||
本次文档核对的源码证据:
|
||||
|
||||
- [共享设置契约](../../../src/shared/application-settings-contracts.ts) 将可编辑 ID 限为魔法笔记、智能心跳、本机推理;知识库不参与启用、常驻或排序。版本 12 为未发布分支格式,`knowledgeEnabled` 已移除,无兼容旧草案要求。
|
||||
- [应用中心](../../../src/renderer/src/ApplicationCenter.tsx) 的知识库行只有“打开”;[App](../../../src/renderer/src/App.tsx) 在首个可选槽位后插入固定知识库,再过滤可选应用。知识库始终显示,原工作区实际配置保留;旧的禁用知识库请求守卫说明已从现行设计中删除。
|
||||
- [设置 Store](../../../src/main/application-settings-store.ts) 持久化成功后发布 `onChanged`;[IPC](../../../src/main/ipc.ts) 转发完整快照,覆盖[配置工具服务](../../../src/main/goodbuddy-config-service.ts) 的写入。App 重开中心时刷新并锁定修改,较新的事件使在途旧读取和保存响应失效。
|
||||
- [向量 Broker](../../../src/main/knowledge/embedding-inference-broker.ts) 区分调用方返回与 Worker 执行结束;取消或超时后,执行记录在 Worker 确认或退出前仍占用在途名额并计入活动任务和服务停止影响。
|
||||
|
||||
本次仅更新文档并核对源码、链接和文本一致性,未运行产品测试或实机验证,也未发起模型请求。
|
||||
README 已标明 Demo 中知识库启停、常驻及通用设置描述待独立同步;本次未修改 Demo 文件。
|
||||
|
||||
## 2026-09-16:D 本机推理生产接入
|
||||
|
||||
对应 FR-16、US-F3 至 US-F5。`LocalInferencePage` 默认导出、无 props,已与中心代理的
|
||||
`local-inference` 页面入口对接,未修改 App 生产入口或应用启用契约。真实支持范围见
|
||||
[技术清单](./technical-design.md#111-本机推理任务与执行服务)。ASR、OCR、向量生产请求均登记
|
||||
生命周期,服务操作和任务取消独立。内置向量 Broker 按模型目录共享;OCR 直接控制持有模型的
|
||||
Renderer Worker。没有新增任务持久化、虚构进度、资源值或 PID。
|
||||
|
||||
验证证据:
|
||||
|
||||
- 原始 Vitest 配置运行 App、IPC、应用中心、设置存储、推理服务、ASR、OCR、向量 Broker、
|
||||
Worker 和引擎,共 12 个文件、452 项通过。完整 App 测试使用真实页面模块,没有 resolve 替身。
|
||||
- 收尾新增 OCR 加载失败恢复用例后,11 个定向文件 202 项通过;加上已通过的完整 App 251 项,
|
||||
当前覆盖合计 453 项。最终 `npm run typecheck`、D 源码及测试定向 ESLint、`git diff --check`
|
||||
均通过;diff 检查仅有工作区已有换行符提示。验证生成的临时 bundle 已清理。
|
||||
- Windows Electron 隔离用户目录,加载当前页面与生产 CSS、生产 Preload、实际引擎模块,
|
||||
通过临时最小 Main IPC 接线运行已安装的 SenseVoice、PaddleOCR、Granite 模型,未修改模型配置。
|
||||
最终一轮:3 次向量请求(含完成/取消竞态)、1 次 ASR 静音采样请求、1 次 OCR 图片请求。
|
||||
向量返回 384 维;UI 停止、启动后再次推理成功;隐藏窗口保持服务运行;OCR 识别 `HELLO`,
|
||||
UI 加载与释放成功;1080px 和 520px 窗口无横向溢出。未调用外部付费模型。
|
||||
- 实机取消竞态中执行方自然完成,管理页正确保留完成结果;取消确认和共享进程不被终止的分支
|
||||
另由 Broker 回归测试验证。停止失败、启动失败重试、新任务影响清单变化由定向测试覆盖。
|
||||
- 真实向量验证发现浏览器 ONNX `/wasm` 入口无法 fetch 本地资源;Main 改为 Node 条件入口后
|
||||
实机推理通过,具体执行错误可回传任务列表。
|
||||
|
||||
限制与阻塞:本轮不是打包安装包验收,也未驱动完整生产 App 的所有入口与原生浏览器视图。
|
||||
实机测试 Main 使用最小接线,完整生产 IPC 注册和 App 接入由上述原始测试及类型检查覆盖。
|
||||
全量 `npm test` 在 200 秒预算内未结束;已复现心跳迁移两项 `duplicate column name: pinned`,
|
||||
独立重跑仍失败,和应用设置 v12 期望值无关,未修改该数据库迁移。全仓 lint 仍被并行工作目录
|
||||
`docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误阻塞。不将全量测试或 lint
|
||||
标为通过。D 不修改 GoodBuddy Agent 或远程执行链路,无远程主机验证要求;没有创建提交。
|
||||
|
||||
本节取代下方早期记录中“缺少 LocalInferencePage”的当前阻塞描述,早期验证范围仍保留供追溯。
|
||||
|
||||
## 2026-09-16:应用中心、共同设置与知识联动
|
||||
|
||||
对应 US-A2、US-B5 至 US-B9。当前源码接入左下角应用中心和独立设置按钮;中心使用设置 Modal
|
||||
壳层,包含搜索、打开、常驻、拖动及上下移动。四个稳定 ID 为 `magic-notes`、`knowledge`、
|
||||
`heartbeat`、`local-inference`。应用启用、常驻和顺序独立持久化,固定入口及右侧工具不参与排序。
|
||||
|
||||
`ApplicationSettingsStore` 升为版本 12,保留已有 `false` 和评论单选值,为缺失项补默认值。
|
||||
笔记页与中心使用 `ApplicationSettingsView`;全局笔记入口已移除。保存期间共用锁,失败后读取
|
||||
确认值,无法确认时锁定修改并允许重新读取。关闭当前应用保留页面挂载及草稿,显示恢复入口。
|
||||
|
||||
知识关闭后移除会话知识控件与摘要,保留各会话关联;Main 交互请求入口在授予知识能力及
|
||||
检索前裁剪新请求范围,不取消已开始的请求。队列派发重新经过同一入口,本机和托管 SSH
|
||||
分支消费同一个请求对象。未修改 Skills、MCP、专家或本机推理页面与后端实现。
|
||||
|
||||
验证记录:
|
||||
|
||||
- 合并定向运行 App、IPC、配置与 Renderer 共 10 个 suite,599 项全部通过;随后新增本机推理
|
||||
主内容区入口用例,完整 App suite 251 项通过,合计覆盖 600 项;
|
||||
App 仍使用下述临时页面契约替身,其余生产代码未替换。
|
||||
- `ApplicationCenter.test.tsx`、设置、笔记、更新设置、共享界面与浏览器遮挡及配置契约的
|
||||
定向回归共 227 项通过;随后增加的保存失败、缺失笔记配置回归与配置契约共 39 项通过。
|
||||
- `ipc.test.ts` 和中心回归共 127 项通过,包含新请求禁用、在飞请求继续及重新开启的范围恢复。
|
||||
- `App.test.tsx` 共 251 项通过;因独立负责的 `LocalInferencePage.tsx` 尚未到位,使用临时
|
||||
Vitest resolve 插件提供无 props 空组件,仅用于验证 App 壳层,未替代或写入生产页面。
|
||||
- Windows Electron 使用当前中心组件、生产 CSS、真实 `ApplicationSettingsStore` 与临时
|
||||
最小 IPC 桥接验证常驻、排序、启用及重新读取持久化结果。浅深主题各验证 `1200 × 850` 和
|
||||
`680 × 560` 窗口,无水平溢出;搜索初始焦点、Escape、关闭焦点恢复及背景隔离通过。
|
||||
已审阅窄浅色和宽深色截图;证据位于本轮临时目录的 `application-center-*.png`。
|
||||
- 全量 `npm test` 的 JSON 报告为 4,153 项通过、4 项失败、67 项跳过,另有 suite 加载失败。
|
||||
其中应用配置契约旧快照已修正并定向通过;其余失败包括心跳旧库迁移两项、工作栏终端标题一项。
|
||||
终端标题用例随后单独复跑通过,未修改其实现。
|
||||
App suite 因缺少 `LocalInferencePage` 无法在原始配置下加载,不能将全量检查记为通过。
|
||||
- `npm run typecheck` 当前仅报告缺少 `./LocalInferencePage`;`npm run lint` 当前仅报告
|
||||
并行工作目录 `docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误。
|
||||
本轮修改的生产源文件定向 Lint 通过。
|
||||
|
||||
剩余集成验证:接入另一实现负责的默认导出 `LocalInferencePage` 后重跑原始 App 测试与类型
|
||||
检查;完整应用的原生浏览器遮挡和共享 Linux Host 真实请求尚未实测。本轮真实模型调用为 0。
|
||||
本节不宣称本机推理后端、管理页或整个计划已交付,也没有生成提交。
|
||||
|
||||
## 2026-09-16:设置改为大 Modal
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
|
||||
| 功能逻辑 | [功能逻辑设计](./logic-design.md) |
|
||||
| UI 设计 | [UI 设计](./ui-design.md) |
|
||||
|
||||
## 1. 现状
|
||||
|
||||
主页面没有 URL Router,由 `App.tsx` 中的 `WorkspaceView` 和 keep-alive 状态切换:
|
||||
主页面没有 URL Router,由 `App.tsx` 中的 `WorkspaceView` 和 keep-alive 状态切换;该类型也包含由独立状态打开 Modal 的入口:
|
||||
|
||||
```ts
|
||||
type WorkspaceView =
|
||||
@@ -21,6 +21,7 @@ type WorkspaceView =
|
||||
| 'magic-notes'
|
||||
| 'knowledge'
|
||||
| 'heartbeat'
|
||||
| 'local-inference'
|
||||
| 'activity'
|
||||
| 'settings'
|
||||
```
|
||||
@@ -28,90 +29,72 @@ type WorkspaceView =
|
||||
文件、浏览器和终端已经由 `RightAssistantSidebar`、`WorkbarShell` 与 `workbar-contracts.ts`
|
||||
承载。工作区是不可关闭的单实例应用,浏览器和终端是多实例应用。浏览器多实例的跨进程
|
||||
实现以[浏览器多实例技术设计](../assistant-workbar/browser-tabs-technical-design.md)为准。代码中尚无资源指标采集和资源监控
|
||||
面板。现有侧栏底部 `user-card` 整行打开设置,并显示“本地工作区”和平台架构。
|
||||
面板。侧栏底部已替换为应用中心与独立设置按钮。
|
||||
|
||||
本次改动增加入口编排、从主侧栏到工作栏的命令通道,以及本机资源监控的 Main/Preload/
|
||||
Renderer 最小实现。文件、终端和浏览器后端保持不变。
|
||||
应用中心、可选应用常驻及排序、启用联动、共同设置页和本机推理已接入生产源码;本机资源监控的
|
||||
Main/Preload/Renderer 实现仍待交付。系统工具只使用右侧工作栏已有 Tab 和“+”链路。
|
||||
行为由[功能逻辑](./logic-design.md)定义,验证范围见[实施进度](./progress.md)。
|
||||
|
||||
## 2. 模块边界
|
||||
|
||||
| 模块 | 责任 |
|
||||
| --- | --- |
|
||||
| `App.tsx` | 组合主导航、系统工具栏、设置入口和页面离开检查 |
|
||||
| `SidebarSystemTools`(新增) | 渲染四个系统工具按钮并发送打开意图 |
|
||||
| `RightAssistantSidebar.tsx` | 接收打开意图,调用工作栏实例创建或激活逻辑 |
|
||||
| `App.tsx` | 组合主导航、应用中心、固定设置,持有确认配置及页面离开检查 |
|
||||
| `ApplicationMenu.tsx` / `AnchoredMenu.tsx` | 底部向上锚定的已启用应用菜单;关闭后导航,或打开既有管理 Modal;共享几何定位与键盘行为 |
|
||||
| `ApplicationCenter.tsx` | 浏览、搜索内置清单,调用既有页面导航及共同设置入口,编辑常驻和顺序;复用设置 Modal 样式和焦点工具 |
|
||||
| `ApplicationSettingsView` | 位于 `ApplicationCenter.tsx`,应用中心、应用页面、全局设置共用的应用配置表单 |
|
||||
| `RightAssistantSidebar.tsx` | 保留右侧工具 Tab 和“+”目录调用链路 |
|
||||
| `WorkbarShell.tsx` | 继续负责实例策略、Tab、目录和焦点行为 |
|
||||
| `workbar-contracts.ts` | 继续作为工作栏应用 ID 和实例布局的权威契约 |
|
||||
| `resource-monitor-contracts.ts`(新增) | 定义本机资源快照、可用性和错误码 |
|
||||
| `resource-monitor-service.ts`(新增) | 在 Main 中采集并规范化本机与 GoodBuddy 指标 |
|
||||
| `ResourceMonitorPanel.tsx`(新增) | 按需轮询、保留有界历史并渲染资源面板 |
|
||||
| `application-settings-contracts.ts` | 增加应用导航偏好 schema 和默认值 |
|
||||
| `application-settings-store.ts` | 迁移并原子保存应用导航偏好 |
|
||||
| `PlatformFeaturesSettingsSection` 或独立设置 Section | 编辑“显示在侧边栏”偏好 |
|
||||
| `application-settings-contracts.ts` | 定义常驻、顺序及应用启用字段;保留笔记既有配置键 |
|
||||
| `application-settings-store.ts` | 迁移默认值,串行合并更新并原子保存,返回完整确认快照 |
|
||||
| `PlatformFeaturesSettingsSection` | 移除全局笔记设置页签、跳转入口及专用回调,保留通用设置和远程项目 |
|
||||
|
||||
不要在 `SidebarSystemTools` 中读取文件、创建 PTY、操作浏览器或采集系统指标;该组件只发送
|
||||
有界的工具 ID。
|
||||
应用中心只编排应用导航和配置,不读取文件、创建 PTY、操作浏览器或采集系统指标。
|
||||
不增加左侧工具组件、左侧工具状态镜像或主侧栏到工作栏的专用命令通道。
|
||||
|
||||
## 3. Renderer 契约
|
||||
|
||||
### 3.1 系统工具 ID
|
||||
### 3.1 内置应用清单
|
||||
|
||||
```ts
|
||||
export const sidebarSystemToolIds = [
|
||||
'workspace',
|
||||
'terminal',
|
||||
'browser',
|
||||
'resources'
|
||||
] as const
|
||||
type BuiltInApplicationId = 'magic-notes' | 'knowledge' | 'heartbeat' | 'local-inference'
|
||||
|
||||
export type SidebarSystemToolId = (typeof sidebarSystemToolIds)[number]
|
||||
```
|
||||
|
||||
该类型可以放在 Renderer 内共享模块。首期不暴露 IPC,因为动作只编排已有工作栏 Renderer
|
||||
状态;实际能力调用继续走现有 Preload API。
|
||||
|
||||
### 3.2 打开意图
|
||||
|
||||
`App` 向 `RightAssistantSidebar` 传入单调递增的请求或命令对象:
|
||||
|
||||
```ts
|
||||
type WorkbarOpenIntent = {
|
||||
requestId: number
|
||||
appId: 'workspace' | 'terminal' | 'browser' | 'resources'
|
||||
reason: 'sidebar-system-tool'
|
||||
context?: { conversationId?: string; projectId?: string }
|
||||
type BuiltInApplicationDefinition = {
|
||||
id: BuiltInApplicationId
|
||||
source: 'builtin'
|
||||
titleKey: string
|
||||
descriptionKey: string
|
||||
view: BuiltInApplicationId
|
||||
}
|
||||
```
|
||||
|
||||
`requestId` 使连续点击同一工具仍能被消费。不要仅设置 `assistantSidebarTab`,因为用户可能已
|
||||
关闭对应实例,且终端需要按执行空间寻找实例。
|
||||
全部应用 ID 在 `application-settings-contracts.ts` 的 `BuiltInApplicationId` 定义;`EditableApplicationId` 仅包含 `magic-notes`、`local-inference`。Renderer 的 `applicationDefinitions` 映射图标、国际化键和可选应用启用键,知识库和智能心跳始终可用。
|
||||
`App.tsx` 默认导入 `LocalInferencePage`,传入 `onClose`、`enabled` 和 `restoreFocus`。`local-inference` 保留为入口 ID,`setView` 在提交工作区切换前将其转为 `localInferenceOpen = true` 并返回,不更新 `view` 或 KeepAlive 缓存;组件按该独立状态挂载及卸载。
|
||||
静态清单复用既有应用页面和国际化键。管理 Modal 的搜索只过滤该清单,包含未常驻和关闭的应用,不请求
|
||||
远程目录。启用、常驻和排序从确认配置派生,不在清单中持久化第二份状态。
|
||||
|
||||
建议由 `RightAssistantSidebar` 在消费意图后通过现有 `WorkbarShell` 控制接口完成。控制器必须
|
||||
接收上下文和调用原因,不能只按 `appId` 查找第一个实例:
|
||||
### 3.2 应用与设置导航
|
||||
|
||||
```ts
|
||||
type WorkbarController = {
|
||||
openOrActivate(intent: WorkbarOpenIntent): Promise<WorkbarTabInstance>
|
||||
}
|
||||
```
|
||||
应用中心调用既有 `navigateFromSidebar` 或同一页面导航函数,沿用未保存离开检查和焦点
|
||||
策略。打开前检查确认的启用值;未常驻但已启用的应用不附带常驻写入。本机推理从常驻入口、菜单及管理 Modal 均经 `setView('local-inference')` 打开独立 Modal,保留底层工作区与设置草稿;其他应用继续页面导航。
|
||||
`ApplicationMenu` 按知识库、智能心跳及可选顺序派生清单,仅按启用过滤,不读取常驻或打开历史。应用行先关闭菜单再导航;底部“管理应用”关闭菜单、恢复锚点焦点并打开既有 `ApplicationCenter` Modal。
|
||||
中心和应用页的设置操作仅接受 `EditableApplicationId`,全局设置旧入口定位同一个共同页。知识库和智能心跳的管理行仅提供“打开”,原工作区实际配置保留,不添加通用空设置页。
|
||||
共同页复用设置 Modal 容器、配置读取及更新方法,不建立独立配置存储或重复表单。
|
||||
|
||||
若当前 `WorkbarShell` 没有命令式入口,先增加窄的回调或受控请求 Props,不引入全局事件总线。
|
||||
### 3.3 工作栏选择与创建
|
||||
|
||||
工作区意图始终激活注册表中的不可关闭单实例;仅在迁移或布局损坏时补回。浏览器意图按
|
||||
`conversationId` 激活最近使用的匹配实例,没有匹配实例时创建一个。工作栏“+”不走该去重
|
||||
路径,选择浏览器时始终创建新实例。
|
||||
已有 Tab 仅按实例 ID 激活,保留目标和历史状态。不得按当前项目、执行空间或 Conversation
|
||||
查找最近匹配实例替代用户选择,也不在选中时自动重建退出或失败终端。
|
||||
|
||||
### 3.3 终端匹配
|
||||
|
||||
工作栏“+”目录选择终端时每次创建新实例;侧栏系统工具按钮执行“进入当前终端”,采用:
|
||||
|
||||
1. 解析当前项目对应的 `ExecutionSpaceSelection`。
|
||||
2. 在现有终端实例中按目标 identity 查找 `starting` 或 `running` 状态。
|
||||
3. 存在多个匹配实例时激活最近使用的实例。
|
||||
4. 没有匹配实例时调用现有终端创建流程。
|
||||
|
||||
`closing`、`exited`、`interrupted` 和 `failed` 实例保留为历史 Tab,不阻止快捷按钮创建新终端。
|
||||
工作栏“+”继续按原规则创建额外终端,不受快捷按钮去重影响。
|
||||
“+”选择终端每次调用既有创建流程,解析当前 `ExecutionSpaceSelection`,绑定本机或托管
|
||||
SSH 项目的权威目录;无项目时使用本机 Home。选择浏览器每次创建额外实例,绑定当前
|
||||
Conversation,无 Conversation 时创建待绑定空实例。同一 Conversation 的浏览器页面独立,
|
||||
登录上下文共享规则继续由工作栏负责。文件按单实例聚焦,缺失时仅由布局修复补回。
|
||||
选择现有文件 Tab 保留跟随或固定模式,固定目标失效时不切换目标。
|
||||
|
||||
### 3.4 资源监控工作栏应用
|
||||
|
||||
@@ -125,8 +108,8 @@ type WorkbarController = {
|
||||
}
|
||||
```
|
||||
|
||||
该应用不加入升级后的默认打开实例。侧栏资源按钮与工作栏“+”目录都通过既有单实例规则创建
|
||||
或聚焦它。
|
||||
该应用不加入升级后的默认打开实例,仅通过右侧已有 Tab 或“+”目录访问,使用既有单实例
|
||||
规则创建或聚焦。左侧应用清单不包含 `resources`。
|
||||
|
||||
## 4. 应用导航偏好
|
||||
|
||||
@@ -135,31 +118,34 @@ type WorkbarController = {
|
||||
在 `ApplicationSettings` 增加独立字段:
|
||||
|
||||
```ts
|
||||
type BuiltInApplicationId = 'magic-notes' | 'knowledge' | 'heartbeat'
|
||||
|
||||
type ApplicationNavigationSettings = {
|
||||
hiddenBuiltInApplicationIds: BuiltInApplicationId[]
|
||||
order: EditableApplicationId[]
|
||||
pinned: Record<EditableApplicationId, boolean>
|
||||
}
|
||||
```
|
||||
|
||||
默认值为空数组,保持升级前所有可用入口可见。ID 数组可以避免为每个应用增加一个顶层布尔
|
||||
字段;Zod schema 必须去重、限制数量并拒绝未知内置
|
||||
ID。
|
||||
|
||||
首期不保存排序,因为内置应用顺序固定。插件偏好在插件系统确定稳定 identity 和卸载保留
|
||||
规则后另行扩展,不提前混入当前 schema。
|
||||
`applicationNavigation` 仅保存可选应用顺序和常驻布尔值。默认顺序为
|
||||
`magic-notes`、`local-inference`,两项常驻默认 `true`。Zod 验证顺序恰好包含
|
||||
两个可选 ID 各一次,`pinned` 仅接受这两个键。知识库和智能心跳不进入导航 schema;Renderer 将它们固定置于可选应用之前,再按启用和常驻过滤可选项。取消常驻或关闭可选应用不删顺序。
|
||||
按[排序规则](./logic-design.md#3-主导航组合规则)提交完整顺序;不持久化可推导的可见 ID 列表。
|
||||
首期不预建插件字段,也不为未发布的旧隐藏 ID 草案增加迁移读取器。
|
||||
|
||||
### 4.2 与现有开关组合
|
||||
|
||||
入口可见条件为:
|
||||
可选应用侧栏入口可见条件如下,知识库和智能心跳固定显示:
|
||||
|
||||
```ts
|
||||
const visible = featureEnabled && !hiddenBuiltInApplicationIds.includes(id)
|
||||
const visible = featureEnabled && applicationNavigation.pinned[id]
|
||||
```
|
||||
|
||||
`magicNotesEnabled` 继续控制功能启停。知识库和智能心跳若未来增加业务启停开关,也按同一组合
|
||||
规则处理。功能停用时设置页保留显隐偏好但禁用显隐 Switch,并提供原功能设置入口。导航设置
|
||||
不能写回现有开关。
|
||||
`magicNotesEnabled` 保留为笔记启用的唯一权威键,`localInferenceEnabled` 为本机推理应用启用键,默认 `true`,不在导航配置再复制 enabled map。心跳没有应用级启用字段,单条计划 enabled 的业务含义和存储不变。关闭可选应用时保留常驻控件和恢复入口。
|
||||
|
||||
笔记配置继续使用 `magicNotesShowIncompleteTodoCount`、`magicNoteCommentMode` 和
|
||||
`magicNoteCommentFormat`,应用中心与笔记页使用同一 schema 和更新链路。未配置的笔记
|
||||
布尔值(含 `magicNotesEnabled`)默认 `true`;迁移必须用字段是否存在区分缺失与显式 `false`,
|
||||
不能用 `value || true`。已有单选值保留,缺失时分别使用 `immediate` 和 `combined`。
|
||||
若旧“显示入口”实际绑定 `magicNotesEnabled`,保留其启用含义和原值;独立常驻使用新字段,
|
||||
不得直接把旧业务启用键改名为 pinned。只为确认已发布的独立显隐偏好增加对应迁移。
|
||||
|
||||
### 4.3 持久化与 IPC
|
||||
|
||||
@@ -173,63 +159,69 @@ Renderer
|
||||
-> application-settings.json
|
||||
```
|
||||
|
||||
更新继续由 Main 校验可信 sender、Zod 输入和原子写入。设置版本迁移只补充默认空数组,不
|
||||
修改已有 `magicNotesEnabled` 或其他平台设置。
|
||||
Main 继续校验可信 sender、Zod 输入,复用 `ApplicationSettingsStore` 的更新队列,读取最新
|
||||
确认值后合并本次 patch、原子写入并返回完整 `ApplicationSettings`。写入失败不发布目标值。
|
||||
存储版本 12 补充可选应用导航和启用字段,仅为缺失笔记字段补新默认值;保留已有
|
||||
`false`、单选值和无关设置,不迁移或清理笔记、知识及会话业务数据。
|
||||
版本 12 属于未发布的分支格式,原草案 `knowledgeEnabled`、`heartbeatEnabled` 已移除;不为它们或旧三项/四项导航草案增加兼容读取器或迁移。
|
||||
|
||||
## 5. 侧栏底部重构
|
||||
|
||||
现有 `user-card` 是一个覆盖整行的设置按钮。实现时替换为容器和相邻独立按钮:
|
||||
原 `user-card` 整行设置按钮已替换为容器和相邻独立按钮:
|
||||
|
||||
```tsx
|
||||
<div className="sidebar-footer">
|
||||
<SidebarSystemTools onOpenTool={openSystemTool} />
|
||||
<button onClick={toggleApplicationMenu} aria-haspopup="menu" aria-expanded={applicationMenuOpen} ...>
|
||||
{t('navigation.applicationCenter')}
|
||||
</button>
|
||||
<button aria-label={t('navigation.settings')} ...>
|
||||
<Settings aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
不能在设置按钮内部嵌套工具按钮。设置按钮继续调用 `navigateFromSidebar('settings', trigger)`,
|
||||
应用中心和设置为相邻独立按钮。设置按钮继续调用 `navigateFromSidebar('settings', trigger)`,
|
||||
保留设置预加载、焦点恢复和未保存离开确认。该调用只打开设置 Modal,不更新当前工作区或
|
||||
KeepAlive 缓存;`settingsOpen` 独立管理打开状态。关闭后卸载设置内容,下次按入口指定的分类
|
||||
和消息通道重新挂载,避免缓存上次分类覆盖本次直达目标。底层工作区继续挂载,普通关闭不跳回聊天。
|
||||
|
||||
系统工具按钮调用统一 `openSystemTool`:
|
||||
应用菜单使用 `AnchoredMenu` 的 body Portal,向上锚定,无模态遮罩或背景隔离;`menu` 语义纳入共享浏览器 viewport 相交遮挡。管理 Modal 及共同设置沿用原 Portal、焦点隔离、恢复及 Escape 机制。
|
||||
不在主侧栏复制工具入口。工作栏仍由窗口右上方固定开关展开和关闭,关闭后焦点返回该开关。
|
||||
|
||||
1. 设置工作栏打开意图。
|
||||
2. 展开工作栏。
|
||||
3. 由工作栏创建或激活实例。
|
||||
4. 按现有策略把焦点移入目标面板。
|
||||
## 6. 应用可用性与知识请求
|
||||
|
||||
工作栏仍由窗口右上方的固定开关关闭,关闭后的焦点返回该开关。
|
||||
Renderer 从同一确认快照派生可选应用主导航、中心状态和页面可用性,依赖只包含相应
|
||||
字段。keep-alive 页挂载不代表应用仍启用;禁用后在原位置渲染不可用状态和共同设置恢复入口,
|
||||
保留页面草稿及业务数据,阻止新的页面操作。重新启用不触发导航或业务重放。
|
||||
|
||||
## 6. 工作栏状态与快捷按钮
|
||||
`ApplicationAvailability` 隐藏并设置业务内容为 inert,保留挂载的页面实例;恢复入口由
|
||||
`ApplicationSettingsNavigation` Context 定位同一中心表单。笔记页面直接消费确认的评论配置,
|
||||
不因修改评论方式而重建编辑器。
|
||||
|
||||
`RightAssistantSidebar` 应向 `App` 暴露当前活动实例的 `appId` 和必要的有界状态摘要,或由
|
||||
现有 `onTabChange` 扩展得到。主侧栏不读取终端输出、文件内容、URL 或活动详情。
|
||||
知识库和智能心跳不经过 `ApplicationAvailability`;会话知识按钮、范围、检索方式和标签继续遵守原知识库规则,心跳计划 enabled 保持不变。
|
||||
请求构建没有应用级禁用知识库的裁剪或守卫;本机、托管 SSH 和排队请求沿用原知识上下文链路。
|
||||
单个知识库实例的配置、停用、删除及取消清理仍由知识库功能负责。
|
||||
|
||||
```ts
|
||||
type SidebarSystemToolState = {
|
||||
activeToolId?: SidebarSystemToolId
|
||||
statusByToolId?: Partial<Record<SidebarSystemToolId, 'attention' | 'error' | 'running'>>
|
||||
}
|
||||
```
|
||||
## 7. 串行保存与确认快照
|
||||
|
||||
首期可以只实现 `activeToolId`,没有可靠聚合来源时不显示状态徽标。不能根据局部事件猜测错误
|
||||
或运行状态。
|
||||
中心与应用页的启用、常驻、排序及笔记配置共用一个 Renderer 更新入口及 pending
|
||||
状态。任一提交未完成时,所有入口中修改这组设置的控件暂时禁用;下一次操作基于最新确认
|
||||
快照生成 patch,不提交整个过期 `ApplicationSettings` 覆盖无关字段。Main 队列仍负责串行合并。
|
||||
|
||||
## 7. 应用显隐更新
|
||||
|
||||
应用显隐 Switch 复用平台功能设置的即时更新模式。请求开始后禁用三个应用显隐 Switch,只
|
||||
允许一个完整目标集合处于提交中。Main 返回成功结果后更新 App 中的平台设置快照,主导航
|
||||
随之变化。失败时恢复控件原值,在对应设置行显示可重试错误;主导航继续读取最后一次成功
|
||||
保存的设置。
|
||||
`ApplicationSettingsStore.update` 在持久化成功后通过 `onChanged` 发布完整快照;IPC 转发
|
||||
`applicationSettingsChanged`,Preload 提供 `updates.onSettingsChanged`。工具配置服务调用同一 Store,
|
||||
因此配置工具写入也同步界面,保存失败不发事件。`App` 收到事件后推进读取修订号并替换快照,
|
||||
迟到的旧读取及旧保存响应不能覆盖新状态。每次重开中心都刷新配置,刷新期间锁定修改,失败后
|
||||
保留锁和重试入口;控件不先乐观生效。
|
||||
明确保存失败时保留最后确认值,就地显示可重试错误与编辑上下文。若 IPC 响应中断而写入
|
||||
结果未知,通过既有设置读取重新取得权威快照再解锁;读取失败继续显示错误并允许重试读取,
|
||||
不自动重发变更或写回旧配置。无须引入持久化回滚日志或独立导航快照文件。
|
||||
|
||||
## 8. 插件兼容边界
|
||||
|
||||
首期不增加插件注册表、插件 schema 或插件持久化字段,只保留系统 ID 不向插件开放的产品边界。
|
||||
插件市场设计需要单独确定 Host、权限、签名、生命周期和稳定 identity;不得直接复用 DeepSeek
|
||||
Harness 的 npm 插件市场。
|
||||
首期不增加市场 URL、YAML 导入导出、ShareServer API、插件注册表或安装服务。FR-15 的
|
||||
私有市场协议、YAML Schema、资源分发与扩展生命周期另行设计;系统 ID 不向插件开放。
|
||||
不得直接复用 DeepSeek Harness 的 npm 插件市场。后续依赖见第 11 节,首期不预建占位界面。
|
||||
|
||||
## 9. 本机资源监控
|
||||
|
||||
@@ -335,23 +327,28 @@ resourceMonitor.getLocalSnapshot(input: {
|
||||
|
||||
### 10.1 Renderer 测试
|
||||
|
||||
- 固定入口顺序不受三个应用显隐组合影响。
|
||||
- 运行记录和设置没有显隐控件。
|
||||
- 四个系统工具按钮具有名称、工具提示、键盘行为和选中状态。
|
||||
- 文件复用不可关闭单实例;浏览器和终端按当前上下文聚焦匹配实例,工作栏“+”允许显式多实例。
|
||||
- 工具按钮展开工作栏但不改变当前主页面。
|
||||
- 显隐更新成功后主导航立即变化,失败时 Switch 和主导航恢复原状态。
|
||||
- 窄窗口下工具按钮不重叠,焦点恢复正确。
|
||||
- 固定入口不受应用启用、常驻和顺序组合影响,应用中心及设置始终可达;左侧没有重复系统工具。
|
||||
- US-B6:底部菜单向上锚定且无遮罩,只列已启用应用,不受常驻或打开历史影响;点击行关闭菜单并导航,“管理应用”进入既有可搜索 Modal,包含关闭及未常驻应用。
|
||||
- US-F3/US-F5:三个入口均打开本机推理 Modal 并保留底层工作区;覆盖嵌套确认的焦点循环、逐层 Escape、提交期间禁止关闭与重复执行、关闭后的入口及后备焦点恢复,以及停用内容后的键盘焦点。
|
||||
- US-B7:常驻、取消常驻、键盘及拖动排序成功后同步,重新开启及重启保留原偏好和顺序。
|
||||
- US-B8:知识库和智能心跳始终启用且位置稳定,管理行只有打开操作,心跳计划 enabled 不变;可选应用关闭后的不可用页保留草稿,重新开启不自动导航或重放。
|
||||
- US-B9:应用中心与笔记页定位同一笔记表单,启用、常驻、待办数量、评论方式及形式共用配置;全局设置无笔记入口。
|
||||
- US-B5:所有入口共享 pending,保存失败保留相同确认值;重开刷新期间锁定修改,较新事件使旧读取失效;结果未知时重读,不自动重发。
|
||||
- 文件复用不可关闭单实例;选择任一已有终端或浏览器 Tab 保留确切目标,“+”每次创建额外实例。
|
||||
- 已退出、失败的终端 Tab 不因选中而重建;工具切换不改变主页面,后台状态不抢占 Tab。
|
||||
- 窄窗口、键盘排序、共享 Switch、Tab 语义及浮层焦点恢复正确;中心和设置不被原生浏览器遮挡。
|
||||
- 资源面板只在可见活动时轮询,跳过重叠请求并把历史限制为 150 项。
|
||||
- 窗口隐藏、最小化和恢复时停止轮询并重新建立区间基线。
|
||||
- 资源部分可用、全部失败和重新进入活动状态均显示正确结果。
|
||||
|
||||
### 10.2 Main 与契约测试
|
||||
|
||||
- 新设置字段默认值、合法 ID、重复 ID、数量上限和未知 ID 校验。
|
||||
- 旧版本设置迁移后默认显示所有既有应用。
|
||||
- 更新导航偏好不修改魔法笔记启停等无关字段。
|
||||
- 原子写入失败时保留最后一次有效设置。
|
||||
- 顺序包含两个可选 ID 各一次;拒绝知识库、智能心跳、重复、缺失、未知 ID 及无效常驻字段;不为未发布的两个旧启用键添加迁移。
|
||||
- 未配置布尔项默认开启;已保存的 `false` 和评论单选值经迁移及重启保留。
|
||||
- 更新常驻或顺序不修改启用、计划或业务数据;笔记既有配置键保持语义。
|
||||
- 串行 patch 在 Main 最新快照上合并,原子写入失败不发布目标状态;响应丢失后重读实际结果。
|
||||
- Store 持久化后才发布变更,工具配置写入使用同一通知链路;知识库和智能心跳不受可选应用偏好影响。
|
||||
- 保存启用失败不会改变业务判断;会话关联和历史知识引用不因入口隐藏被清空。
|
||||
- 资源快照拒绝 NaN、Infinity、负数字节数、越界百分比和未知错误码。
|
||||
- 采集超时、权限不足和磁盘 IO 不支持时返回有界部分结果。
|
||||
- 超时采集尚未实际结束时保持单飞,不累积新的后台采集任务。
|
||||
@@ -371,22 +368,33 @@ resourceMonitor.getLocalSnapshot(input: {
|
||||
- 浅色、深色、`220px` 与 `420px` 侧栏宽度。
|
||||
- Windows、macOS 和 Linux 的真实 CPU、内存与磁盘 IO 采集;不支持项显示准确原因。
|
||||
|
||||
FR-16 单独覆盖 US-F3 概览去重、未知指标、US-F4 单任务取消与完成竞态、US-F5 服务
|
||||
实际启动/停止/重启或加载/释放、使用方确认、外部管理限制及失败真实状态;用接入的真实引擎
|
||||
验证共享服务不被单任务取消终止,重启不重放。设计文档检查或模拟服务不算生产路径验证。
|
||||
|
||||
## 11. 实施顺序
|
||||
|
||||
实施范围以 [PRD 分期](./prd.md#8-分期)为准。本节替代旧实施顺序;前文关于四个左侧
|
||||
系统工具按钮、仅持久化隐藏 ID 的契约需在实现前同步,不能直接按旧规格开发。
|
||||
实施范围以 [PRD 分期](./prd.md#8-分期)及最新评审决定为准,逻辑映射见
|
||||
[需求对应](./logic-design.md#6-与需求的对应关系)。既有 FR/US 编号保持稳定;新增故事使用
|
||||
US-B6 应用中心、US-B7 顺序、US-B8 启用联动、US-B9 笔记迁移,以及 US-F3 概览、
|
||||
US-F4 取消、US-F5 服务生命周期。PRD、User Stories、配套设计与根 UI 规范已同步入口及持久化规则。
|
||||
|
||||
1. 同步 PRD 功能要求、User Stories、逻辑、UI 与技术契约,收敛首期应用中心样式。
|
||||
以下为实施阶段划分,应用中心及本机推理已接入源码,验证状态以进度记录为准。
|
||||
|
||||
1. 底部入口使用向上锚定的轻量菜单,通过“管理应用”进入既有整窗 Modal;核对已发布配置的迁移来源。
|
||||
2. 接入现有内置应用页面与最底部应用中心入口,保留独立设置和固定运行记录入口。
|
||||
3. 为常驻显隐和排序设计最小持久化契约,保留已发布的相关设置与用户数据,按保存结果更新导航。
|
||||
4. 接入应用设置的共同界面与既有设置更新路径,先验证魔法笔记多入口状态一致性。
|
||||
5. 验证右侧现有文件、终端、浏览器 Tab;移除计划中的左下角工具快捷条和专用于该快捷条的意图接口工作。
|
||||
3. 实现常驻、完整顺序及启用契约,迁移时保留明确旧值,通过串行保存与确认快照同步所有界面。
|
||||
4. 迁移魔法笔记共同设置,保留知识库、智能心跳固定入口及原工作区配置、单条计划 enabled,验证可选应用关闭和重新开启。
|
||||
5. 验证右侧现有文件、终端、浏览器 Tab 的确切目标与“+”额外实例行为,不实现左侧重复工具条。
|
||||
6. 单独实现资源快照契约、Main 采集服务、Preload API 与右侧 `resources` 单实例面板。
|
||||
7. 验证键盘、焦点、浅深主题、窄窗口、设置失败、重启持久化和跨平台行为。
|
||||
|
||||
首期使用内置应用清单,不依赖远程商店、插件注册或安装服务。商店目录、推荐、安装与
|
||||
更新机制在后续独立设计中确定;Demo 中的模拟商店不接入首期产品界面。
|
||||
|
||||
本轮 `local-inference` 已接入管理 Modal、类型化 IPC 与真实引擎管理,支持范围见 11.1;
|
||||
实机和自动化验证范围见[实施进度](./progress.md)。
|
||||
|
||||
后续设计输入见 [FR-15](./prd.md#fr-15-私有化市场与-yaml-交换后续) 与
|
||||
[FR-16](./prd.md#fr-16-本机模型管理入口新增计划项):
|
||||
|
||||
@@ -398,11 +406,50 @@ resourceMonitor.getLocalSnapshot(input: {
|
||||
市场来源配置不覆盖现有模型、应用更新或 Runtime 下载源设置。
|
||||
- ShareServer 的市场发布与分发实现由其独立仓库承接,本仓库先记录依赖,不将桌面需求
|
||||
记录标为服务端已实现。
|
||||
- 本机模型管理实施前盘点现有 OCR、向量、语音及推理服务的生命周期接口,优先复用其
|
||||
Main 服务。只为实际支持的操作提供 typed IPC;区分受管子进程、外部服务和进程内模型,
|
||||
不把直连模型 HTTP 请求等同于独立模型进程。
|
||||
- 模型管理的详细逻辑需确定共享服务的使用方识别、忙碌时停止与重启、退出应用时的释放
|
||||
行为,再接入管理页面并验证实际引擎。该工作单独排期,不依赖市场上线。
|
||||
|
||||
### 11.1 本机推理任务与执行服务
|
||||
|
||||
ASR、OCR 与内置向量的跨功能执行边界、任务控制缺口及有条件的优化方向,见[本机专用推理架构与任务控制评估](../../architecture/local-inference-task-control.md)。该评估区分源码现状、建议与待办,未将常驻化或后台宿主迁移列为已采用实现。
|
||||
|
||||
`LocalInferencePage` 默认导出,接收必需的 `onClose` 及可选的 `enabled`、`restoreFocus`,通过 `createPortal(..., document.body)` 渲染管理 Modal,复用 `PageHeader` 和共享
|
||||
`activateModalFocus`/`trapTabFocus`。`ImpactConfirmation` 使用独立 Portal 和紧凑确认样式;共享焦点工具隔离下层 Modal,并在关闭时恢复可用触发元素或指定后备焦点。`operation` ref 防止重复执行,`busy` 和 `control` 控制关闭与 Escape,交互细节见 [UI 设计](./ui-design.md#91-本机推理管理)。`local-inference-contracts.ts` 定义快照、操作与取消输入;Main 注册四个
|
||||
`local-inference:*` 通道,复用可信 sender 校验和 Zod 输入校验。Preload 只暴露读取、服务操作、
|
||||
任务取消及打开既有设置的方法,不接收 PID、命令或模型路径。
|
||||
|
||||
| 引擎 | 生产接入与可用操作 | 明确限制 |
|
||||
| --- | --- | --- |
|
||||
| ASR / sherpa-onnx | `SpeechTranscriptionService` 登记真实请求;执行路径保留独立 Worker 请求取消 | 每次请求加载、结束释放;未提供 Worker 就绪观测,模型可用时状态为 unknown,不从活动请求推断 running |
|
||||
| OCR / PaddleOCR | `DocumentOcrBroker` 登记文档请求;`document-ocr-bridge` 提供实际 Worker 状态、加载及空闲释放 | 有活动或排队请求时拒绝释放;不把终止共享 Worker 暴露为单任务取消 |
|
||||
| 内置向量 / Granite | 按模型目录复用引用计数 Broker;启动、停止、重启;取消消息等待 Worker 结果或取消确认 | 启动确认进程 spawn,模型在首个请求时加载;不把进程启动称为模型已加载 |
|
||||
| TTS | 显示当前没有执行服务 | 不提供虚假任务或控制 |
|
||||
| 外部向量连接 | 快照契约保留配置,Modal 不显示;通过既有模型设置管理 | 本页不主动探测,不推断本机归属,不控制外部进程 |
|
||||
|
||||
`LocalInferenceService` 在 Main 内存中登记真实 ASR、OCR、向量请求,保留活动请求和最近
|
||||
100 条终态记录。只保存能力来源、时间、模型(执行路径有提供时)、状态和错误,不保存音频、
|
||||
图片或向量输入文本。运行阶段包括排队、模型加载及执行,没有统一阶段进度;来源未提供项目或
|
||||
会话对象时不补造对象关联。Modal 仅使用活动请求生成影响确认,不展示任务历史或单任务取消。
|
||||
|
||||
向量执行端通过 `utilityProcess.fork` 创建独立进程。每个 transport 持有自己的资源采样器,按当前 `child.pid` 从 `app.getAppMetrics()` 匹配 Utility 条目。服务快照的 `resources.scope` 为 `service-process`,携带 PID、采样时间及可用指标;无进程或无法采集时为 `unavailable` 并给出原因。Broker 清除退出的 transport,重启使用新采样器,不复用旧进程指标。
|
||||
|
||||
Modal 可见且启用时每 5 秒读取一次快照,跳过重叠请求。CPU 使用累计 CPU 秒数之差除以单调时钟间隔,100% 表示一个逻辑核心,可超过 100%;首次读取、PID 或创建时间变化、采样间隔超过 15 秒、计数回退及采样失败后重新建立基线。15 秒阈值为定时器延迟和跳过重叠请求留出余量。工作集由 Electron 的 KiB 转为契约中的字节,界面显示 MiB。缺少 CPU 时间或内存读数时只标记该指标不可用,不补零。数值为整个向量服务进程的占用,包含运行时和并发使用方,不是单任务或模型权重大小。
|
||||
|
||||
ASR 使用主进程中的 `node:worker_threads`;OCR 使用渲染进程中的 Web Worker,两者均没有独立进程工作集,逐行标明无法独立统计 CPU/内存。没有将主进程或整个 Renderer 的读数归属给服务。显存未采集,本机资源监控工作栏应用仍未实现。
|
||||
|
||||
服务操作按 serviceId 串行,操作期间拒绝新增该服务请求;停止前校验所有当前活动 taskId
|
||||
都在用户确认清单内,新增使用方要求刷新后重新确认。向量停止等待实际 child exit;失败保留
|
||||
可观测状态和错误。Renderer 操作失败时关闭旧确认并重新读取快照,刷新期间或失败时禁用服务操作;再次操作须重新确认当前活动请求。重启不重放中断请求。任务取消与服务停止分开,完成竞态保留执行方结果。
|
||||
|
||||
向量 Broker 将调用方 Promise 与 Worker 执行记录分开。调用方取消或超时可先返回错误,
|
||||
执行记录在 Worker 确认、返回结果或进程退出前仍为活动项,保留在途名额并计入服务停止影响;
|
||||
停止等待超时也不能提前移除记录。请求取消与自然完成竞争时采用执行方结果。
|
||||
|
||||
OCR 模型可用性由 Main 校验,Worker 运行状态由实际持有它的 Renderer 模块提供。手动加载
|
||||
同样复用既有 Worker,加载超时恢复为可重试错误;空闲 60 秒自动释放。Modal 启用、文档可见且内容未被 hidden/inert 隔离时每 5 秒刷新,跳过未结束的请求;嵌套确认期间暂停新快照读取。
|
||||
关闭 Modal 时卸载组件并清理轮询,窗口隐藏、关闭管理应用或取消常驻不会触发服务停止。退出整个进程继续
|
||||
沿用原有应用清理路径。模型文件卸载仍在模型设置中独立进行。
|
||||
|
||||
向量 Main 引擎使用 `onnxruntime-web` 的 Node 条件入口,避免浏览器 `/wasm` 入口通过 fetch
|
||||
加载本地文件失败。执行方的具体错误随任务返回;Renderer 不把失败展示为零指标或恢复成功。
|
||||
|
||||
## 12. 项目活动汇总
|
||||
|
||||
@@ -416,7 +463,7 @@ resourceMonitor.getLocalSnapshot(input: {
|
||||
顶层 Task 转为 `completed` 时加入集合,初始历史不生成通知。`App` 的本地 `done` 事件和
|
||||
活动运行持久化快照的成功终态调用 `markConversationCompleted`,失败、取消不调用。
|
||||
新运行及 Task 恢复运行或等待处理时清除旧通知。具体优先级和通知生命周期以活动逻辑为准。
|
||||
- `App` 仅在 `view === 'chat' && !settingsOpen` 时向 Hook 传入当前会话 ID;Hook 监听
|
||||
- `App` 仅在 `view === 'chat' && !settingsOpen && !applicationCenterOpen` 时向 Hook 传入当前会话 ID;Hook 监听
|
||||
`visibilitychange`,文档不为 `hidden` 时才按当前会话清除通知。这使普通侧栏、活动菜单
|
||||
导航和可见性恢复共用查看规则,KeepAlive 挂载本身不代表查看。集合不写入 SQLite 或设置。
|
||||
- 聚合状态新增 `completed`,全局和项目计数包含 `attention`、`running`、`completed`,
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
|
||||
| 功能逻辑 | [功能逻辑设计](./logic-design.md) |
|
||||
| 全局规范 | [GoodBuddy 统一界面设计系统](../../../UI-DESIGN.md) |
|
||||
|
||||
本文定义入口、设置和轻量资源监控界面。文件、终端和浏览器的内容布局继续遵守助手工作栏
|
||||
文档。
|
||||
本文定义已接入的应用中心、共同设置和本机推理界面,以及待实现的右侧资源监控界面。
|
||||
行为以[功能逻辑](./logic-design.md)为准,需求保留 FR-1 至 FR-16 与既有 US 编号。
|
||||
根 UI 规范已同步应用中心与工具入口规则;共享控件、主题、设置 Modal 与工作栏布局遵守根规范。
|
||||
|
||||
## 1. 侧栏信息架构
|
||||
|
||||
@@ -24,110 +25,134 @@
|
||||
搜索
|
||||
|
||||
当前对话
|
||||
魔法笔记 3
|
||||
知识库
|
||||
智能心跳 1
|
||||
知识库(固定显示)
|
||||
智能心跳(固定显示,保留建议徽标)
|
||||
可选应用(魔法笔记、本机推理,按保存顺序,仅显示启用且常驻项)
|
||||
运行记录
|
||||
|
||||
最近对话
|
||||
...
|
||||
|
||||
────────────────────────
|
||||
[文件] [终端] [浏览器] [资源] [设置]
|
||||
[应用中心] [设置]
|
||||
```
|
||||
|
||||
“当前对话”和“运行记录”属于固定系统入口。三个内置应用位于二者之间。侧栏底部不再显示
|
||||
“本地工作区”和 `platform · arch`;排障信息移至关于或诊断页面。
|
||||
“当前对话”和“运行记录”属于固定系统入口,常驻应用位于二者之间。首期应用清单为魔法笔记、
|
||||
知识库、智能心跳及本轮追加的本机推理,应用中心与设置始终可见。侧栏底部不再显示“本地工作区”和 `platform · arch`;
|
||||
排障信息移至关于或诊断页面。左侧不再提供文件、终端、浏览器、资源监控的重复入口。
|
||||
|
||||
## 2. 底部系统工具栏
|
||||
## 2. 底部应用中心入口
|
||||
|
||||
底部使用连续平整表面和顶部分隔线,不做成账户卡片或嵌套卡片。按钮顺序固定:
|
||||
底部使用连续平整表面和顶部分隔线,应用中心在前,固定设置靠右,二者为独立按钮:
|
||||
|
||||
| 位置 | 图标 | 可见工具提示 | 点击结果 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | `FolderOpen` | 文件 | 聚焦不可关闭的工作区面板 |
|
||||
| 2 | `SquareTerminal` | 终端 | 打开或聚焦当前执行空间终端 |
|
||||
| 3 | `Globe` | 浏览器 | 聚焦当前 Conversation 的匹配浏览器实例 |
|
||||
| 4 | `Activity` | 资源监控 | 打开或聚焦本机资源监控面板 |
|
||||
| 1 | `Grid2X2` | 应用中心 | 向上展开已启用应用菜单,底部“管理应用”进入管理 Modal |
|
||||
| 末位 | `Settings` | 设置 | 在当前工作区上打开设置 Modal |
|
||||
|
||||
图标使用项目现有 Lucide 库。每个按钮采用统一的 `34px × 34px` 点击区域,内部图标建议
|
||||
`16px`。工具栏使用 `8px` 内边距和 `4px` 间隔;设置按钮通过自动空白靠右,不添加第二条
|
||||
视觉分隔线。
|
||||
图标使用项目现有 Lucide 库。应用中心保留文字,设置图标采用统一的 `34px × 34px` 点击区域。
|
||||
底部使用共享间距和焦点样式,设置通过弹性空白靠右;不得把整个容器做成设置按钮。
|
||||
|
||||
### 2.1 状态
|
||||
|
||||
- 默认:透明背景,图标使用次级文字色。
|
||||
- 悬停:使用弱表面色,不能强于当前选中状态。
|
||||
- 选中:使用强调选中背景,并提高图标对比度。
|
||||
- 展开:应用中心按钮显示展开反馈,不能当作当前应用的选中状态。
|
||||
- 键盘焦点:显示统一焦点环,不能被侧栏边缘裁切。
|
||||
- 处理中:创建或激活请求未完成时暂时禁用对应按钮,保留尺寸并提供进行中名称。
|
||||
- 徽标:只用于等待处理、错误或活动状态,不显示无意义的常驻绿色圆点。
|
||||
- 加载:读取应用配置时保留入口,中心内显示加载状态;失败提供重试,不用默认值冒充已保存配置。
|
||||
|
||||
选中状态表达“对应工作栏面板正在显示”,不表达功能已安装或权限已授予。
|
||||
应用中心入口不承载工具运行徽标。全部可选应用关闭时菜单仍显示知识库、智能心跳及“管理应用”,可进入 Modal 重新开启可选应用。
|
||||
|
||||
## 3. 主导航应用区
|
||||
|
||||
应用入口沿用现有 `nav-item` 行,不使用应用卡片。图标、名称和徽标保持单行:
|
||||
|
||||
```text
|
||||
[图标] 魔法笔记 3
|
||||
[图标] 知识库
|
||||
[图标] 智能心跳 1
|
||||
[图标] 魔法笔记 3
|
||||
[图标] 本机推理
|
||||
```
|
||||
|
||||
隐藏某个应用后,其余应用向上补位。运行记录始终位于最后一个可见应用之后,不因应用显隐
|
||||
改变样式或归类为工具。
|
||||
取消常驻或关闭可选应用后不留下空白。知识库、智能心跳固定在当前对话之后,始终启用并显示,不受可选顺序影响。运行记录始终位于应用区之后。
|
||||
编辑常驻顺序提供拖动及“上移”“下移”按钮,首尾无可移动目标时禁用对应按钮;两种方式
|
||||
提交相同可选应用顺序。知识库、智能心跳及固定系统入口不参与拖动,也不作为排序目标。保存成功后更新,重启保留。
|
||||
|
||||
设置页打开期间,主侧栏显示最后一次成功保存的导航状态。Switch 更新成功后导航立即变化,
|
||||
失败时保持原导航。
|
||||
取消常驻不关闭当前页面或本机推理 Modal。重新常驻按已保存顺序恢复,关闭应用保留常驻与顺序偏好。
|
||||
主侧栏始终读取最后确认配置,不展示尚未保存成功的排序或开关目标值。
|
||||
|
||||
## 4. 应用与工具设置
|
||||
## 4. 应用中心与共同设置
|
||||
|
||||
设置中心新增“应用与工具”分类,位于设置 Modal 的右侧内容区;容器与键盘规则遵守 [设置中心 UI 规范](../../../UI-DESIGN.md#138-设置中心):
|
||||
底部点击打开轻量菜单,锚定按钮向上展开,无模态遮罩、背景隔离或焦点循环。菜单依次列知识库、智能心跳和按保存顺序排列的已启用可选应用;不按打开历史或常驻状态过滤。点击应用行关闭菜单,本机推理打开独立 Modal,知识库、智能心跳和魔法笔记在主内容区打开页面。分隔线下的“管理应用”关闭菜单并打开既有管理 Modal。
|
||||
|
||||
菜单通过共享 `AnchoredMenu` 挂载到 body,宽度最多 `280px`,距窗口边缘至少 `16px`,与按钮上缘间隔 `8px`;短窗口限制高度并内部滚动,滚动、窗口和内容尺寸变化时重算位置。原生浏览器相交遮挡复用共享机制。
|
||||
|
||||
管理 Modal 沿用设置 Modal 的表面、遮罩和焦点规则,提供全部内置应用的名称及用途搜索、打开,以及可选应用的常驻和设置:
|
||||
|
||||
应用管理采用桌面双列卡片,窗口宽度不超过 `720px` 时改为单列。Modal 最大宽度 `960px`,
|
||||
高度随内容增长并受窗口限制。卡片显示图标、名称、状态和用途,底部放置操作;名称旁区分
|
||||
“固定入口”和“可选 · 已启用/已关闭”。长名称可换行,正文与控件均不可横向溢出。
|
||||
|
||||
```text
|
||||
应用与工具
|
||||
管理主导航中的应用。系统工具由 GoodBuddy 提供并固定在侧栏底部。
|
||||
|
||||
应用
|
||||
魔法笔记 笔记、待办和评论 [显示在侧边栏 开]
|
||||
知识库 管理和检索知识 [显示在侧边栏 开]
|
||||
智能心跳 查看计划、建议和运行历史 [显示在侧边栏 开]
|
||||
|
||||
系统工具
|
||||
文件 打开当前工作区 内置
|
||||
终端 进入当前执行空间 内置
|
||||
浏览器 打开 GoodBuddy 受控浏览器 内置
|
||||
资源监控 查看本机 CPU、内存和磁盘 IO 内置
|
||||
|
||||
系统入口
|
||||
当前对话、运行记录和设置位置固定,不参与上述配置。
|
||||
应用中心
|
||||
[搜索内置应用]
|
||||
知识库 固定入口 [打开]
|
||||
智能心跳 固定入口 [打开]
|
||||
魔法笔记 可选 · 已启用 [打开] [设置]
|
||||
本机推理 可选 · 已启用 [打开] [设置]
|
||||
```
|
||||
|
||||
应用显隐使用共享 Switch,标签必须是“显示在侧边栏”,不能缩写为“启用”。切换时立即保存,
|
||||
请求期间禁用三个应用显隐 Switch;失败后恢复原值,并在对应设置行显示可重试错误。系统工具
|
||||
使用紧凑只读列表,不渲染禁用的卸载、停用或固定按钮。
|
||||
已启用但未常驻的应用可以直接打开,打开不自动常驻。关闭的应用仍能被搜索和配置,
|
||||
保留禁用的“打开”按钮,可通过“设置”重新开启。启用、常驻及上下移动放在设置详情中;无搜索词且未锁定修改时,可选卡片仍支持拖动排序。无搜索结果显示清除搜索入口。
|
||||
首期不展示商店页签、推荐、安装按钮、市场 URL 或 YAML 操作,系统工具也不在此列出。
|
||||
|
||||
功能已停用时,对应应用行显示“功能已停用”和设置入口,保留显隐偏好但暂时禁用 Switch。
|
||||
### 4.1 共同应用设置页
|
||||
|
||||
若某应用自身支持业务启停,应在该应用原有设置中保留“启用功能”开关,并用说明区分:
|
||||
魔法笔记、本机推理的应用管理和应用页面入口定位到同一应用设置页,复用同一个表单及配置。知识库和智能心跳在管理 Modal 中仅提供“打开”,不提供启用、常驻、排序或通用空设置;原工作区实际配置入口保留。
|
||||
共同页遵守[设置中心 UI 规范](../../../UI-DESIGN.md#138-设置中心),不为每个入口建立一套设置。
|
||||
全局设置移除魔法笔记页签及跳转入口。旧“显示魔法笔记入口”绑定业务启用值,迁为“启用应用”并保留原值;
|
||||
“常驻左侧菜单”使用独立配置,只有已发布的独立显隐偏好才映射到常驻。
|
||||
|
||||
- “显示在侧边栏”只控制入口。
|
||||
- “启用功能”控制该功能是否运行。
|
||||
| 控件 | 表达含义 |
|
||||
| --- | --- |
|
||||
| 启用应用 | 控制业务可用性及主导航、会话等业务入口 |
|
||||
| 常驻左侧菜单 | 仅控制主导航捷径,关闭应用时保留该偏好,并说明开启后才显示 |
|
||||
| 显示未完成待办数量 | 仅控制魔法笔记导航徽标 |
|
||||
| AI 评论方式 | 即时/保存后自动/保存后手动,单选 |
|
||||
| AI 评论形式 | 长评 + 要点/长评/要点,单选 |
|
||||
|
||||
二元配置使用共享 Switch 和 `role="switch"`;评论选项使用共享单选控件。未配置布尔项
|
||||
默认开启,评论默认值沿用既有值,已有明确的关闭或单选选择保留。启用与常驻不可合并。
|
||||
设置详情最大宽度 `640px`,紧凑标题栏包含返回图标、应用名称、用途及关闭按钮。进入详情和
|
||||
返回卡片时正文滚动位置重置到顶部;进入详情聚焦关闭按钮,返回后聚焦搜索框。
|
||||
共同设置表单使用共享 Switch,相关字段分组,组间使用弱分隔线;说明沿用正文字号。
|
||||
评论分段控件按内容占宽,窄窗口中标签与选项上下排列;上下移动按钮位于详情表单下方。
|
||||
智能心跳单条计划 CRUD 及 enabled 仍由其业务页面负责,没有应用级心跳启用开关。
|
||||
|
||||
所有入口共享保存中状态。串行保存期间禁用相关启用、常驻、排序及笔记配置控件,保留
|
||||
最后确认值;持久化成功后的设置事件同步中心、侧栏、会话、应用页及其他设置入口,包括工具配置写入。重开中心刷新期间锁定修改,迟到的旧读取不能覆盖更新快照。失败在发起操作
|
||||
附近显示可重试错误并保留焦点;结果未知时显示“正在确认保存结果”,重读成功后再开放修改。
|
||||
|
||||
### 4.2 关闭后的页面与会话
|
||||
|
||||
关闭当前可选应用成功后,原页面或本机推理 Modal 显示“应用已关闭”和“前往应用设置”,保留草稿和内容状态,
|
||||
暂停该页面的新业务操作。重新开启后恢复可用性,不自动跳到已离开的应用页或重放任务。
|
||||
|
||||
知识库和智能心跳不显示应用关闭状态;会话知识控件、历史引用、工作区配置及心跳计划遵守原功能规则。可选应用的已有请求和后台任务继续按功能生命周期展示,详细边界见[关闭应用与恢复](./logic-design.md#32-关闭应用与恢复)。
|
||||
|
||||
## 5. 助手工作栏反馈
|
||||
|
||||
点击系统工具后,右侧工作栏按现有布局回流打开。主内容页面保持不变,侧栏不渲染工具内容。
|
||||
系统工具仅从右侧工作栏已有 Tab 或“+”目录进入,工作栏按现有布局回流,主内容页面保持不变。
|
||||
|
||||
- 文件:激活默认存在且不可关闭的“工作区”Tab,焦点进入目录区域或错误修复入口。
|
||||
- 终端:激活匹配终端,焦点进入终端输入区;新建期间保留按钮宽度和进度状态。
|
||||
- 浏览器:激活绑定当前 Conversation 的最近使用“浏览器”Tab;没有匹配实例时创建一个,
|
||||
没有 Conversation 时按产品规则复用最近实例或进入待绑定空状态。
|
||||
- 终端:选择已有 Tab 激活该确切终端并保留目标与历史状态;“+”每次创建额外终端,按当前
|
||||
项目选择本机或托管 SSH,无项目时使用本机 Home。新建期间显示进度,失败保留诊断。
|
||||
- 浏览器:选择已有 Tab 保留其 Conversation、页面及导航状态;“+”每次创建额外浏览器,
|
||||
无活动 Conversation 时新建待绑定空实例。不会按当前会话改选其他 Tab 或重绑已有实例。
|
||||
- 资源监控:激活“资源监控”Tab,焦点进入指标摘要;首个样本加载后开始绘制历史。
|
||||
|
||||
后台事件可以更新徽标,不能自动展开工作栏或切换当前 Tab。由用户点击快捷按钮触发的打开
|
||||
属于显式操作,可以展开工作栏并切换 Tab。
|
||||
文件和资源监控在“+”中按单实例规则聚焦或创建,终端和浏览器不按目标去重。
|
||||
后台事件可以更新 Tab 徽标,不能自动展开工作栏或切换当前 Tab。
|
||||
|
||||
### 5.1 资源监控面板
|
||||
|
||||
@@ -167,7 +192,9 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
|
||||
| 对象 | 文案 |
|
||||
| --- | --- |
|
||||
| 设置分类 | 应用与工具 |
|
||||
| 底部入口 | 应用中心 |
|
||||
| 菜单底部管理入口 | 管理应用 |
|
||||
| 应用设置开关 | 启用应用 |
|
||||
| 文件系统工具 | 文件 |
|
||||
| 终端系统工具 | 终端 |
|
||||
| 浏览器系统工具 | 浏览器 |
|
||||
@@ -175,7 +202,8 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
| 资源系统工具 | 资源监控 |
|
||||
| 资源范围 | 本机 |
|
||||
| 指标缺失 | 不可用 |
|
||||
| 应用显隐开关 | 显示在侧边栏 |
|
||||
| 应用常驻开关 | 常驻左侧菜单 |
|
||||
| 关闭的应用页 | 应用已关闭 |
|
||||
| 内置来源 | 内置 |
|
||||
|
||||
不再使用“本地工作区”作为侧栏底部身份名称。项目和默认工作区的路径继续由项目选择器、页面
|
||||
@@ -183,19 +211,22 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
|
||||
## 7. 键盘和无障碍
|
||||
|
||||
- 底部按钮均为独立 `button`,不能嵌套在整行设置按钮中。
|
||||
- Tab 顺序为文件、终端、浏览器、资源监控、设置。
|
||||
- 底部应用中心和设置均为独立 `button`,Tab 顺序与视觉顺序一致,不嵌套按钮。
|
||||
- 图标按钮通过 `aria-label` 提供名称,工具提示不能作为唯一可访问名称。
|
||||
- 工作栏打开后按现有焦点策略聚焦目标面板;用户通过固定工作栏开关关闭时,焦点返回该开关。
|
||||
- 选中按钮使用 `aria-pressed` 或等价的可访问状态,不能只依靠背景色。
|
||||
- 徽标的数量或状态合并进按钮可访问名称,例如“终端,1 个等待处理”。
|
||||
- 应用显隐 Switch 支持 Space 切换,保存失败后恢复原值并保留焦点。
|
||||
- 应用中心按钮提供 `aria-haspopup="menu"`、`aria-expanded` 和控制关系;菜单使用 `menu`/`menuitem`,打开后聚焦首个可用项,上下方向键循环选择,Home、End 到首尾,Enter、Space 激活。
|
||||
- Escape 关闭菜单并恢复入口焦点;Tab 关闭后继续正常焦点顺序,点击外部、焦点离开或再次点击入口也关闭。菜单不隔离背景,不增加遮罩。
|
||||
- “管理应用”打开的 Modal 使用共享 Portal、整窗遮罩、原生浏览器遮挡、背景隔离和焦点循环,初始焦点进入搜索。卡片操作与详情控件按正常 Tab 顺序访问,不伪装成单个菜单项;详情切换焦点见共同应用设置页。
|
||||
- 工作栏使用 `tablist`、`tab`、`tabpanel`、`aria-selected`、游标焦点和方向键、Home、End。
|
||||
“+”使用普通按钮语义,同类型 Tab 仍按实例独立标记。
|
||||
- 徽标数量或状态合并进可访问名称;Switch 支持 Space,失败保留焦点。排序按钮名称包含
|
||||
应用名,操作结果通过 polite 状态区播报;拖动不是唯一排序方式。
|
||||
|
||||
## 8. 响应式
|
||||
|
||||
- 侧栏宽度 `220px` 至 `420px` 时,底部按钮尺寸不随宽度缩放。
|
||||
- 窄窗口继续使用现有侧栏抽屉;工具栏不转为多行。
|
||||
- 空间不足时缩小设置按钮前的弹性空白,不隐藏四个系统工具和设置。
|
||||
- 侧栏宽度 `220px` 至 `420px` 时,应用中心与设置均可操作,不随宽度缩小点击区域。
|
||||
- 窄窗口继续使用现有侧栏抽屉,底部入口不重叠;应用菜单向上展开并限制窗口边界、内部滚动。管理 Modal 的长名称可换行,搜索、设置与重新开启入口始终可达。
|
||||
- 右侧 Tab 单行横向滚动,“+”持续可见;工作栏保持停靠回流,不遮罩主页面。
|
||||
- 工具提示不得超出窗口;靠近底边时显示在按钮上方。
|
||||
- 粗指针设备不依赖悬停,按钮的可访问名称和选中反馈保持有效。
|
||||
|
||||
@@ -219,8 +250,31 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
|
||||
## 9. 未来插件入口
|
||||
|
||||
插件市场交付后另行定义插件区域、固定数量、溢出和权限界面。系统入口与四个系统工具的位置
|
||||
保持保留,不向插件开放。
|
||||
FR-15、US-F1/US-F2 的市场 URL、YAML 导入导出及 ShareServer 分发在后续设计中定义。
|
||||
首期中心只浏览本地内置清单,不显示未来市场的空壳界面。
|
||||
|
||||
### 9.1 本机推理管理
|
||||
|
||||
FR-16 的 `local-inference` 入口已纳入本轮应用清单,可常驻主导航。从常驻入口、应用菜单和管理 Modal 的“打开”均进入同一本机推理 Modal,关闭来源菜单或管理 Modal,保留底层工作区、项目、会话和草稿。其他内置应用仍打开主内容区页面。
|
||||
Modal 与真实执行模块已接入;支持清单及验证范围分别见技术设计和实施进度。
|
||||
复用 `custom-task-dialog` 的整窗 body Portal、遮罩、背景隔离及浏览器遮挡机制。主 Modal 最大宽度 `960px`,高度随内容增长,最大高度为 `min(720px, 100dvh - 2 × --space-6)`;紧凑标题栏和关闭按钮固定在顶部,正文独立滚动。直接显示服务列表,没有页签或任务历史。工具栏显示“每 5 秒刷新 · CPU 100% = 一个逻辑核心”、模型与连接设置、刷新状态,窄屏可换行。服务行显示“服务进程 PID … · CPU …% · 工作集 … MiB”,预热时显示“正在建立采样基线”;共享进程的 worker 在该行显示无法独立统计的原因。
|
||||
初始焦点进入“关闭本机推理”,Tab/Shift+Tab 在当前 Modal 内循环。没有影响确认且没有操作提交时,关闭按钮或 Escape 关闭 Modal;点击遮罩不关闭。关闭后优先恢复仍可用的原触发元素,入口已移除时回到应用中心,窄侧栏已隐藏时回到侧栏开关。
|
||||
服务采用弱分隔列表;名称使用区块标题字号,状态及引擎元数据使用
|
||||
辅助字号,状态与名称同组并与操作区分开,原因与操作说明使用正文字号。桌面操作独立位于右侧;
|
||||
窗口宽度不超过 `720px` 时移到正文下方并左对齐,保留完整状态和原因。
|
||||
|
||||
- US-F3 每行显示服务名、简短状态、引擎、模型及错误。ASR 可用时显示“按需运行(状态未知)”;OCR 和向量使用执行模块的状态。TTS 显示不可用,外部连接不列入本机服务。
|
||||
- US-F4 不再提供单任务取消入口;活动任务仅在服务操作的影响确认中出现。
|
||||
- US-F5 服务行右侧保留实际支持的启动、停止、重启或模型加载、释放。
|
||||
- 停止、重启、释放前打开最大宽度 `480px` 的紧凑嵌套确认,列出正在使用的任务与影响;无已登记任务时说明执行前再次核对。初始焦点进入“保留运行状态”,最终危险按钮写明“确认停止服务/重启服务/释放模型”;取消保留运行状态。
|
||||
确认使用独立 body Portal,隔离底层管理 Modal,Tab/Shift+Tab 只在确认中循环;Escape 只关闭确认,关闭后恢复原服务操作按钮,按钮不可用时回到管理 Modal。确认存在时禁用主 Modal 关闭按钮;提交期间禁用确认和取消按钮,两层 Escape 均不能关闭,也不能重复执行。失败关闭旧确认,在服务列表显示错误并刷新;再次操作须重新确认当前影响。成功只关闭确认。
|
||||
操作失败呈现实际状态和可重试错误,无法确认则显示未知。服务恢复不自动重放任务。
|
||||
关闭 Modal、关闭管理应用或取消常驻不停止服务,模型文件卸载另行操作。
|
||||
|
||||
服务列表提供首次加载、无服务、加载失败重试及保留旧快照的刷新失败状态;刷新期间或失败时禁用服务操作。
|
||||
OCR 忙碌或模型不可用时禁用加载与释放,并在执行模块再次检查。
|
||||
页面不显示无来源的进度条或资源数值。US-B6 至 US-B9 的应用中心、顺序、启用联动、笔记共同设置映射见
|
||||
[需求对应](./logic-design.md#6-与需求的对应关系)。
|
||||
|
||||
## 10. 项目活动汇总
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
|
||||
| 关联逻辑 | [功能逻辑设计](./logic-design.md) |
|
||||
|
||||
@@ -29,47 +29,50 @@
|
||||
|
||||
验收:
|
||||
|
||||
- Given 用户隐藏任意内置应用
|
||||
- Given 用户隐藏任意可选内置应用
|
||||
- When 用户查看主导航
|
||||
- Then “运行记录”仍显示在应用区之后
|
||||
- And 设置中没有隐藏、卸载或固定运行记录的选项
|
||||
|
||||
### US-A2 从固定入口进入设置
|
||||
|
||||
作为用户,我希望设置入口始终位于侧栏底部,以便快捷工具状态变化时仍能进入配置。
|
||||
作为用户,我希望设置入口始终位于侧栏底部,以便应用状态变化时仍能进入配置。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 任意主页面和任意侧栏宽度
|
||||
- When 用户查看侧栏底部
|
||||
- Then 设置按钮保持可见且点击区域不与工具按钮重叠
|
||||
- Then 设置与应用中心为始终可见的独立按钮,点击区域不重叠
|
||||
- And 点击后继续遵守未保存内容的离开确认
|
||||
|
||||
## 3. Epic B:管理应用入口
|
||||
|
||||
本节的启用、常驻、排序和共同设置仅适用于魔法笔记、本机推理;知识库和智能心跳始终启用并固定显示。原知识库/智能心跳禁用场景已退役,保留故事 ID,并在 US-B6、US-B8 中以固定可用不变量替换验收。
|
||||
|
||||
### US-B1 识别完整应用
|
||||
|
||||
作为用户,我希望魔法笔记、知识库和智能心跳显示在主导航,以便知道它们会打开完整页面。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 对应功能已启用且应用设置为显示
|
||||
- Given 固定应用,或已启用且常驻的可选应用
|
||||
- When 用户打开主侧栏
|
||||
- Then 应用位于“当前对话”和“运行记录”之间
|
||||
- And 点击应用在主内容区打开对应一级页面
|
||||
- And 原有待办或建议徽标继续显示
|
||||
- And 点击知识库、智能心跳或魔法笔记在主内容区打开对应一级页面;本机推理打开保留底层工作区的 Modal,验收见 US-F3
|
||||
- And 原有待办或建议徽标按已保存的显示设置继续显示
|
||||
- And 当前对话之后依次固定显示知识库、智能心跳,再显示可选应用;两个固定应用不参与启用、常驻或排序
|
||||
|
||||
### US-B2 隐藏低频应用
|
||||
### US-B2 取消低频应用常驻
|
||||
|
||||
作为用户,我希望隐藏不常用的内置应用,以便缩短主导航,但保留已有数据。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 应用已有内容
|
||||
- When 用户关闭该应用的“显示在侧边栏”Switch
|
||||
- Then 主导航不再显示该应用
|
||||
- Given 应用已启用且已有内容
|
||||
- When 用户关闭该应用的“常驻左侧菜单”Switch 并保存成功
|
||||
- Then 主导航不再显示该应用,当前已打开页面保持不变
|
||||
- And 应用数据、计划和业务启停状态不变
|
||||
- And 用户可从同一设置位置恢复显示
|
||||
- And 用户可从应用中心打开该应用,或从同一设置位置恢复常驻
|
||||
|
||||
### US-B4 保留功能启停语义
|
||||
|
||||
@@ -78,35 +81,129 @@
|
||||
验收:
|
||||
|
||||
- Given 魔法笔记功能已启用
|
||||
- When 用户只隐藏其侧栏入口
|
||||
- When 用户只取消其侧栏常驻并保存成功
|
||||
- Then 笔记及待办处理能力继续按原设置工作
|
||||
- And 再次显示时恢复原页面和数据
|
||||
- And 重新常驻只恢复入口,不自动导航;再次打开时保留原页面状态和数据
|
||||
|
||||
### US-B5 恢复显隐保存失败
|
||||
### US-B5 保持设置保存一致
|
||||
|
||||
作为用户,我希望入口设置保存失败时继续使用原导航,以免界面状态与实际设置不一致。
|
||||
作为用户,我希望应用设置保存期间和失败后各入口保持一致,以免界面与实际配置不符。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 应用显隐更新尚未完成
|
||||
- When 用户切换“显示在侧边栏”Switch
|
||||
- Then 三个应用显隐 Switch 在请求期间暂时不可操作
|
||||
- And 更新成功后主导航立即变化
|
||||
- And 更新失败时 Switch 和主导航恢复原状态并显示可重试错误
|
||||
- Given 配置已加载且没有待确认的更新
|
||||
- When 用户修改启用、常驻、顺序或魔法笔记配置
|
||||
- Then 更新串行保存,在请求确认前,应用中心、应用页及全局设置中修改这组配置的所有控件均暂不可操作
|
||||
- And 各处保留最后确认值,不提前改变导航、业务可用性、徽标或会话入口
|
||||
- When 保存成功
|
||||
- Then 各入口同时采用确认配置,后续修改基于该配置提交
|
||||
- And 工具配置写入持久化成功后也同步各入口;较新的确认快照不被迟到的读取或保存响应覆盖
|
||||
- When 关闭后重新打开应用中心
|
||||
- Then 刷新最新配置期间锁定修改,读取失败保留重试入口,不允许用旧快照提交
|
||||
- When 保存明确失败
|
||||
- Then 各入口仍使用相同的最后确认配置,操作处显示可重试错误,保留焦点和编辑上下文,不重复通知
|
||||
- Given 响应中断,无法确认保存结果
|
||||
- When 用户查看设置
|
||||
- Then 显示正在确认保存结果,重读权威配置成功后才恢复修改;读取失败可重试读取,不自动重发变更或写回旧值
|
||||
|
||||
### US-B6 从应用中心访问内置应用
|
||||
|
||||
作为用户,我希望从固定应用中心找到应用,以便未常驻或已关闭的应用仍有访问和恢复入口。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 用户位于任意一级页面,包括全部可选应用均已关闭的情况
|
||||
- When 点击侧栏底部应用中心
|
||||
- Then 向上打开轻量锚定菜单,无模态遮罩;只列已启用应用,包括固定知识库、智能心跳和已启用的可选应用,不按打开历史或常驻筛选
|
||||
- When 点击菜单中的应用行
|
||||
- Then 菜单关闭;知识库、智能心跳和魔法笔记经既有离开检查在主内容区打开,本机推理打开保留底层工作区的 Modal,不自动常驻
|
||||
- When 点击菜单底部“管理应用”
|
||||
- Then 菜单关闭并打开既有管理 Modal,可按名称或用途搜索全部内置应用,包含未常驻及已关闭的可选应用
|
||||
- And 知识库和智能心跳行只提供“打开”,没有通用设置、启用、常驻或排序控件;原工作区实际配置及心跳单条计划 enabled 不变
|
||||
- And 首期没有商店、推荐、安装、市场 URL、YAML 操作或系统工具入口
|
||||
- Given 应用已启用但未常驻
|
||||
- When 用户选择打开
|
||||
- Then 本机推理打开 Modal,底层内容无需离开;其余应用经既有未保存内容离开检查进入页面,不自动常驻
|
||||
- Given 应用已关闭
|
||||
- When 用户在管理 Modal 中找到该应用
|
||||
- Then 显示关闭状态,保留禁用的“打开”按钮,可通过“设置”进入共同设置重新开启
|
||||
- Given 搜索无匹配结果
|
||||
- Then 显示无结果及清除搜索入口,搜索不修改启用和常驻
|
||||
- Given 配置正在加载或读取失败
|
||||
- Then 中心保留加载状态或重试入口,不用默认值冒充已保存配置
|
||||
|
||||
### US-B7 保存常驻及应用顺序
|
||||
|
||||
作为用户,我希望常驻和顺序按设备保存,以便重启后仍使用熟悉的导航。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 首次使用且未配置导航偏好
|
||||
- Then 知识库、智能心跳固定在前,可选应用默认均常驻,顺序为魔法笔记、本机推理;只按启用与常驻过滤可选项
|
||||
- When 用户修改常驻,或通过拖动、键盘可操作的上移和下移按钮排序并保存成功
|
||||
- Then 仅可选应用顺序变化,知识库、智能心跳、当前对话、运行记录及底部固定入口不参与排序,重启后恢复保存结果
|
||||
- And 管理 Modal 编辑两个可选应用的完整顺序,上下移动以该顺序的相邻项为目标,固定应用不作为移动目标
|
||||
- Given 应用已关闭或取消常驻
|
||||
- When 用户重新开启或常驻
|
||||
- Then 按保存顺序恢复入口;关闭或取消常驻不删除顺序,已关闭应用仍可管理常驻偏好
|
||||
- Given 保存失败
|
||||
- Then 按 US-B5 保留原常驻和顺序,在操作处提供重试并保留键盘焦点
|
||||
- Given 没有可见的可选应用或仅有一个
|
||||
- Then 固定入口和应用中心仍可用,没有移动目标的排序按钮禁用,不产生空白占位项
|
||||
|
||||
### US-B8 独立控制启用与常驻
|
||||
|
||||
作为用户,我希望关闭应用时保留数据和导航偏好,并能明确知道哪些业务操作已不可用。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 应用已启用
|
||||
- When 仅取消常驻并保存成功
|
||||
- Then 业务能力和后台操作保持原状,仍可从应用中心打开
|
||||
- Given 知识库已有会话关联,智能心跳已有启用或暂停的计划
|
||||
- When 用户关闭全部可选应用或修改其常驻与顺序
|
||||
- Then 知识库和智能心跳始终启用,侧栏位置稳定,页面可打开;知识控件及关联保持可用,请求遵守知识库自身规则,心跳单条计划 enabled 原值不变
|
||||
- And 原“禁用知识库/智能心跳后隐藏业务入口”的验收由上述不变量替换;不存在这两个应用的应用级关闭状态或恢复设置页
|
||||
- Given 关闭的应用页面或本机推理 Modal 仍打开
|
||||
- Then 原位置显示“应用已关闭”和前往应用设置的恢复入口,保留草稿及页面状态,阻止新业务操作而不强制跳转
|
||||
- When 用户重新开启并保存成功
|
||||
- Then 仍停留的页面恢复可用;已离开时不自动导航,不重放或补发旧请求和任务
|
||||
- And 保留原常驻及顺序,未常驻应用不自动常驻
|
||||
- Given 启用变更保存失败
|
||||
- Then 按 US-B5 保留原业务状态和入口,不执行关闭或恢复副作用
|
||||
|
||||
### US-B9 迁移魔法笔记共同设置
|
||||
|
||||
作为魔法笔记用户,我希望从各入口编辑同一份设置,并在升级后保留已有选择。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 用户从应用中心或笔记页面进入笔记设置
|
||||
- Then 定位到同一共同设置页,使用同一表单和配置,不保留重复控件或“显示魔法笔记入口”开关
|
||||
- And 全局设置不再显示魔法笔记页签或跳转入口,已有配置值和业务数据保持不变
|
||||
- And 启用、常驻、显示未完成待办数量分别控制业务、导航和徽标;隐藏数量不修改待办
|
||||
- Given 升级前已有明确保存的布尔值和评论选择
|
||||
- When 迁移配置并重启
|
||||
- Then 保留已有 `false`、评论单选值及业务数据,仅未配置的应用启用、常驻和笔记相关布尔项默认 `true`
|
||||
- And 旧入口开关若实际控制笔记启用,保留其启用含义和原值,不将其改作常驻
|
||||
- Given AI 评论方式或评论形式未配置
|
||||
- Then 分别默认“即时”和“长评 + 要点”,均为单选,不同时启用所有模式
|
||||
- When 任一入口修改配置并保存成功
|
||||
- Then 应用中心、主导航、笔记页、会话及其他设置入口同步采用确认值;保存中和失败均遵守 US-B5
|
||||
|
||||
## 4. Epic C:使用系统工具
|
||||
|
||||
### US-C1 一键查看文件
|
||||
|
||||
作为本地工作用户,我希望点击左下角文件按钮后直接查看当前工作区文件。
|
||||
作为本地工作用户,我希望从右侧工作栏查看文件,并保留工作区面板的目标模式。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 当前项目有有效根目录
|
||||
- When 用户点击文件按钮
|
||||
- Then 助手工作栏展开
|
||||
- And 聚焦默认存在且不可关闭的工作区单实例
|
||||
- And 文件按钮通过选中状态表明该面板正在显示
|
||||
- When 用户展开右侧工作栏并选择已有工作区 Tab,或从“+”选择文件
|
||||
- Then 聚焦默认存在且不可关闭的工作区单实例,不创建重复实例;仅迁移或布局修复时补回缺失实例
|
||||
- And 保留跟随或固定模式,跟随模式继续按工作栏规则更新上下文
|
||||
- And 仅该实例 Tab 显示选中状态,焦点进入目录区域
|
||||
|
||||
### US-C2 处理无效工作区
|
||||
|
||||
@@ -116,67 +213,62 @@
|
||||
|
||||
- Given 工作区面板处于跟随模式
|
||||
- And 当前项目和默认工作区均不可用
|
||||
- When 用户点击文件按钮
|
||||
- When 用户在右侧工作栏选择工作区 Tab 或从“+”选择文件
|
||||
- Then 工作区面板仍打开
|
||||
- And 面板说明目录不可用并提供选择或修复目录的入口
|
||||
- And 应用不静默改用其他目录
|
||||
- Given 工作区面板固定的目标已失效,即使当前项目或默认目录可用
|
||||
- When 用户选择工作区 Tab
|
||||
- Then 保留固定目标并显示该目标的修复状态,不回退到其他目录或切换为跟随模式
|
||||
|
||||
### US-C3 快速进入终端
|
||||
|
||||
作为本地工作用户,我希望点击终端按钮后进入当前执行空间的终端。
|
||||
作为本地工作用户,我希望选择确切终端继续查看,并能通过“+”创建额外终端。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 已有终端 Tab,包括绑定其他执行空间的终端
|
||||
- When 用户选择其中一个 Tab
|
||||
- Then 仅激活该确切实例,保留执行空间、目录及历史,不按当前项目查找最近匹配实例或重新绑定
|
||||
- Given 已选 Tab 正在关闭、已退出、已中断或失败
|
||||
- Then 保留其真实状态,不因选中而重建
|
||||
- Given 当前执行空间已有正在启动或运行中的终端
|
||||
- When 用户点击终端按钮
|
||||
- Then 工作栏聚焦最近使用的匹配终端,不创建重复实例
|
||||
- Given 当前执行空间没有终端
|
||||
- When 用户点击终端按钮
|
||||
- Then 创建一个绑定该执行空间的新终端
|
||||
- When 用户从右侧工作栏“+”选择终端
|
||||
- Then 每次创建绑定当前执行空间的额外实例,不复用或按目标去重
|
||||
- Given 当前没有项目
|
||||
- When 用户点击终端按钮
|
||||
- Then 创建或聚焦使用本机 Home 的终端
|
||||
- Given 当前执行空间只有已退出、已中断或失败的终端
|
||||
- When 用户点击终端按钮
|
||||
- Then 创建新终端并保留原终端的历史状态
|
||||
- When 用户从“+”选择终端
|
||||
- Then 创建使用本机 Home 的新终端,已有终端保持原目标和历史
|
||||
|
||||
### US-C4 使用远程项目终端
|
||||
|
||||
作为远程项目用户,我希望同一个终端按钮进入远程项目目录,以免误在本机执行命令。
|
||||
作为远程项目用户,我希望新建终端绑定远程项目目录,以免误在本机执行命令。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 当前项目绑定托管 SSH 执行空间
|
||||
- When 用户点击终端按钮
|
||||
- Then 创建或聚焦绑定该远程项目及其权威目录的终端
|
||||
- When 用户从右侧工作栏“+”选择终端
|
||||
- Then 创建绑定该远程项目及其权威目录的额外终端
|
||||
- And 面板持续显示远程主机和工作目录
|
||||
- And 连接失败时保留重连与诊断信息
|
||||
- And 连接失败时保留远端目标、重连与诊断信息,不回退为本机终端
|
||||
- When 切换当前项目后重新选择该终端 Tab
|
||||
- Then 仍激活原远程实例,不重绑到新项目
|
||||
|
||||
### US-C5 打开受控浏览器
|
||||
|
||||
作为用户,我希望浏览器按钮打开 GoodBuddy 受控浏览器,以便查看与 Agent 相同的会话。
|
||||
作为用户,我希望从右侧工作栏使用 GoodBuddy 受控浏览器,并按需创建独立页面。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 浏览器面板跟随当前上下文且当前会话已有浏览器会话
|
||||
- When 用户点击浏览器按钮
|
||||
- Then 工作栏聚焦绑定当前 Conversation 的最近使用浏览器实例
|
||||
- And 不打开系统默认浏览器或无意创建重复实例
|
||||
- Given 现有浏览器实例都绑定到其他 Conversation
|
||||
- When 用户点击浏览器按钮
|
||||
- Then 工作栏创建并聚焦绑定当前 Conversation 的新浏览器实例
|
||||
- Given 当前没有活动 Conversation
|
||||
- And 已存在浏览器实例
|
||||
- When 用户点击浏览器按钮
|
||||
- Then 工作栏聚焦最近使用的浏览器实例并保持其目标
|
||||
- Given 当前没有活动 Conversation
|
||||
- And 不存在浏览器实例
|
||||
- When 用户点击浏览器按钮
|
||||
- Then 创建待选择目标的空实例
|
||||
- Given 用户需要同一 Conversation 的另一个独立页面
|
||||
- Given 已有浏览器 Tab,当前 Conversation 与其目标不同或没有活动 Conversation
|
||||
- When 用户选择该 Tab
|
||||
- Then 激活该确切实例,保留绑定目标、页面及导航状态,不重绑、重建或改选最近匹配实例
|
||||
- Given 当前有活动 Conversation,无论是否已有匹配实例
|
||||
- When 用户从工作栏“+”选择浏览器
|
||||
- Then 每次创建拥有独立页面和导航状态的新实例
|
||||
- And 同一 Conversation 内共享隔离登录上下文
|
||||
- Then 每次创建绑定当前 Conversation 的额外实例,页面和导航状态独立,同一 Conversation 内共享隔离登录上下文
|
||||
- Given 当前没有活动 Conversation,无论是否已有浏览器实例
|
||||
- When 用户从“+”选择浏览器
|
||||
- Then 创建待选择目标的空实例,不复用已有实例
|
||||
- And 以上操作均使用 GoodBuddy 受控浏览器,不打开系统默认浏览器
|
||||
|
||||
### US-C6 查看本机资源
|
||||
|
||||
@@ -186,17 +278,26 @@
|
||||
验收:
|
||||
|
||||
- Given 资源监控面板尚未打开
|
||||
- When 用户点击左下角资源监控按钮
|
||||
- When 用户从右侧工作栏“+”选择资源监控
|
||||
- Then 助手工作栏创建并激活单实例资源监控面板
|
||||
- And 页面显示整机 CPU、内存、磁盘读写及 GoodBuddy 桌面进程摘要
|
||||
- And 页面每 2 秒更新一次,并用图表保留最近 5 分钟
|
||||
- And 可见活动期间每 2 秒采样一次,前次请求未结束时跳过新请求,图表最多保留 150 个真实时间样本,连续采样约 5 分钟
|
||||
- Given 资源监控实例已存在
|
||||
- When 用户选择其 Tab 或再次从“+”选择资源监控
|
||||
- Then 激活同一本机单实例,不新建重复面板
|
||||
- When 用户切换到其他工作栏 Tab、收起工作栏、隐藏窗口或最小化窗口
|
||||
- Then 资源监控停止请求新样本
|
||||
- When 用户再次显示窗口并返回资源监控
|
||||
- Then 首次采样建立区间指标基线,下一次采样开始显示 CPU 和磁盘 IO 速率
|
||||
- And 内存等瞬时指标可立即显示,基线未建立时不显示虚构零值
|
||||
- Given 某项指标在当前平台不可用
|
||||
- When 页面收到部分结果
|
||||
- Then 该指标显示“不可用”及原因,其他指标继续更新
|
||||
- Given 本次采样全部失败或超时
|
||||
- Then 显示失败原因和重试入口,保留已有历史并标明最后更新时间,不把旧值当作实时数据
|
||||
- And 失败或暂停采样期间保留真实时间缺口,不补零或伪造中间样本
|
||||
- When 可见活动期间自动重试或用户手动重试成功
|
||||
- Then 按真实采样结果恢复更新,较晚返回的失活前旧请求不覆盖当前结果
|
||||
|
||||
### US-C7 从任意应用打开工具
|
||||
|
||||
@@ -205,35 +306,51 @@
|
||||
验收:
|
||||
|
||||
- Given 用户位于任意一级页面
|
||||
- When 用户点击文件、终端、浏览器或资源监控
|
||||
- When 用户展开右侧工作栏,选择已有工具 Tab 或通过“+”选择文件、终端、浏览器或资源监控
|
||||
- Then 主页面保持不变
|
||||
- And 右侧工作栏打开对应面板
|
||||
- And 工具不会覆盖主内容或伪装成一级页面
|
||||
- And 创建失败时工作栏保持展开并显示对应错误,主页面和其他实例不被清空
|
||||
- And 后台事件不自动展开工作栏或切换当前 Tab,左侧和应用中心不复制工具入口或选中状态
|
||||
|
||||
## 5. Epic D:可访问性和布局
|
||||
|
||||
### US-D1 使用键盘打开系统工具
|
||||
### US-D1 使用键盘管理应用与访问工具
|
||||
|
||||
作为键盘用户,我希望按顺序聚焦并打开左下角工具,以便不依赖鼠标。
|
||||
作为键盘用户,我希望浏览和管理应用,并通过右侧工作栏访问工具,以便不依赖鼠标。
|
||||
|
||||
验收:
|
||||
|
||||
- 每个系统工具和设置都是独立按钮
|
||||
- Tab 顺序与视觉顺序一致
|
||||
- Enter 和 Space 均可激活按钮
|
||||
- 每个纯图标按钮都有可访问名称和工具提示
|
||||
- 焦点样式不被侧栏边界裁切
|
||||
- Given 用户使用键盘访问侧栏底部
|
||||
- Then 应用中心和设置是独立按钮,Tab 顺序与视觉顺序一致,Enter 和 Space 可激活,图标按钮有可访问名称和工具提示
|
||||
- When 打开应用中心
|
||||
- Then 入口提供菜单展开状态和控制关系,焦点进入首个可用菜单项;上下方向键循环移动,Home、End 到首尾,Enter、Space 激活
|
||||
- When 按 Escape 关闭菜单
|
||||
- Then 焦点返回应用中心入口;Tab 关闭菜单后继续正常焦点顺序,点击外部或焦点离开也关闭
|
||||
- When 选择“管理应用”
|
||||
- Then 关闭菜单并进入既有 Modal,焦点进入搜索;卡片的打开、设置及详情中的启用、常驻、排序控件按正常 Tab 顺序可达,不伪装成单个菜单项;进入详情及返回时滚动归顶,返回后聚焦搜索
|
||||
- When 通过键盘修改常驻或顺序
|
||||
- Then Switch 使用共享开关语义并支持 Space,上移和下移按钮名称包含应用名,结果通过 polite 状态区播报,失败保留焦点
|
||||
- Given 用户进入右侧工作栏
|
||||
- Then Tab 选择、游标焦点、方向键及 Home、End 由工作栏负责,使用 `tablist`、`tab`、`tabpanel` 和 `aria-selected`,按确切实例表达选中状态
|
||||
- And “+”为普通按钮,工具状态纳入 Tab 可访问名称;激活后焦点进入面板,关闭工作栏后返回固定工作栏开关
|
||||
- And 焦点环不被边界裁切,应用中心及设置沿用共享浮层和原生浏览器遮挡处理
|
||||
|
||||
### US-D2 在窄窗口使用快捷栏
|
||||
### US-D2 在窄窗口访问应用中心与工作栏
|
||||
|
||||
作为小屏用户,我希望侧栏抽屉中的工具按钮保持可操作。
|
||||
作为小屏用户,我希望应用管理和工具实例在窄窗口中仍可操作。
|
||||
|
||||
验收:
|
||||
|
||||
- 工具按钮保持单行且不重叠
|
||||
- 不因宽度不足隐藏文件、终端、浏览器、资源监控或设置
|
||||
- 触控点击区域符合统一界面规范
|
||||
- 关闭侧栏抽屉后焦点返回原触发按钮
|
||||
- Given 侧栏宽度为 `220px` 至 `420px`,或窄窗口使用侧栏抽屉
|
||||
- Then 底部应用中心和设置不重叠,不因宽度不足隐藏入口或缩小点击区域
|
||||
- When 打开应用菜单或通过“管理应用”进入 Modal
|
||||
- Then 菜单向上锚定且限制在窗口内,可内部滚动;Modal 中长名称可换行,搜索、常驻、排序、设置及重新开启入口均可达
|
||||
- And 触控不依赖悬停,工具提示保持在窗口内,点击区域符合统一规范
|
||||
- When 关闭侧栏抽屉
|
||||
- Then 焦点返回原触发按钮
|
||||
- Given 右侧工作栏有多个工具实例
|
||||
- Then Tab 单行横向滚动且“+”持续可见,工作栏停靠回流,不遮罩主页面;布局和 Tab 交互仍由工作栏负责
|
||||
|
||||
## 6. Epic E:项目活动级联
|
||||
|
||||
@@ -249,6 +366,9 @@
|
||||
- Then 进入该确切 Conversation ID,清除搜索、任务选择和会话菜单,窄窗口关闭侧栏
|
||||
- Given 设置页有未保存修改
|
||||
- Then 会话跳转仍需通过既有离开检查,取消离开保留设置草稿和原项目
|
||||
- Given 用户选择的确切 Conversation ID 未在内存中或已被删除
|
||||
- When 请求进入该会话
|
||||
- Then 刷新持久化会话列表后仍只查找同一 ID;刷新失败或会话不存在时显示通知并保留原页面和选择,不回退到项目最新会话或其他会话
|
||||
|
||||
### US-E2 键盘、窄窗口与实时结束
|
||||
|
||||
@@ -286,7 +406,7 @@
|
||||
|
||||
## 7. 后续需求场景
|
||||
|
||||
本节对应新增计划,不计入应用中心首期完成条件。
|
||||
US-F1、US-F2 为后续市场计划;US-F3 至 US-F5 的本机推理已接入源码,当前支持边界见技术设计,验证范围见实施进度。
|
||||
|
||||
### US-F1 连接组织应用市场
|
||||
|
||||
@@ -295,33 +415,85 @@
|
||||
- Given 组织已部署提供应用市场的 ShareServer
|
||||
- When 用户配置市场 URL,并在来源要求时完成登录
|
||||
- Then 可以浏览该来源的应用目录、详情及可获取版本
|
||||
- Given 市场暂时不可达
|
||||
- Then 显示失败原因与重试入口,已安装和内置应用仍可打开,不切换公共市场
|
||||
- And 市场 URL 按设备保存,重启后恢复;认证信息使用受保护凭据存储,不作为可导出配置
|
||||
- Given 市场不可达、URL 无效、登录过期或认证失败
|
||||
- When 用户访问该来源
|
||||
- Then 显示具体原因及修改地址、重新登录或重试入口,已安装和内置应用仍可打开,不静默切换公共市场或其他来源
|
||||
- Given 用户修改市场来源或开始获取应用资源
|
||||
- Then 市场配置不改变模型、应用更新及 Runtime 等独立下载源;已开始的下载保持原来源,失败不静默切源
|
||||
|
||||
### US-F2 导入导出应用定义
|
||||
|
||||
对应 FR-15。
|
||||
|
||||
- Given 用户需要在私有部署间迁移应用
|
||||
- When 导出应用为 `.yml` 并在另一台设备导入
|
||||
- When 导出应用为 YAML,并在另一台设备选择 `.yml` 或 `.yaml` 文件导入
|
||||
- Then 预览应用身份、版本、可迁移配置和依赖,凭据与业务数据不包含在导出文件中
|
||||
- When 用户确认有效定义且所需依赖已满足,导入成功
|
||||
- Then 应用目录显示导入的身份、版本和配置,可按其启用状态打开或配置,不自动启动模型或任务
|
||||
- Given 同标识应用已存在
|
||||
- Then 用户可选择更新或取消,取消后原应用与配置保持不变
|
||||
- When 用户查看导入预览
|
||||
- Then 可选择更新或取消,取消后原应用与配置保持不变
|
||||
- When 更新成功
|
||||
- Then 显示已确认的新定义和配置
|
||||
- When 更新失败
|
||||
- Then 显示具体原因及重试入口,保留原应用定义、版本和配置,不留下部分更新
|
||||
- Given 文件格式无效、版本不支持或离线环境缺失程序包或模型资源
|
||||
- Then 显示具体问题与补充要求,不将应用误报为可运行,也不自动启动模型或任务
|
||||
|
||||
### US-F3 管理本机模型服务
|
||||
### US-F3 查看本机推理服务与资源
|
||||
|
||||
对应 [FR-16](./prd.md#fr-16-本机模型管理入口新增计划项)。
|
||||
|
||||
- Given 用户使用依赖本机模型的应用
|
||||
- When 从应用中心或常驻菜单打开“本机模型”
|
||||
- Then 可查看已接入模型的状态、管理方式及使用方,两处入口访问同一页面
|
||||
- Given 多个应用共用一个 GoodBuddy 受管模型服务
|
||||
- Then 展示同一服务及使用方,启动操作不重复创建进程
|
||||
- When 用户停止或重启仍有任务使用的受管服务
|
||||
- Then 先展示任务影响供用户决定,取消后服务保持运行;操作失败显示原因与实际状态
|
||||
- Given 模型服务由外部程序启动,且未接入其管理接口
|
||||
- Then 展示“外部管理”与连接配置,不提供停止或重启
|
||||
- When 用户关闭管理页或取消常驻
|
||||
- Then 模型运行状态不变
|
||||
- When 从常驻入口、应用菜单或管理 Modal 的“打开”进入“本机推理”
|
||||
- Then 均打开同一管理 Modal,来源菜单或管理 Modal 关闭,底层工作区、当前项目、会话和未提交草稿保持不变
|
||||
- And 直接显示服务列表,不出现任务页签、任务历史或外部连接;知识库、智能心跳和魔法笔记仍打开主内容区页面
|
||||
- And 初始焦点进入关闭按钮,背景不可交互,Tab/Shift+Tab 在 Modal 内循环;标题与关闭按钮保持可见,正文独立滚动
|
||||
- Given 没有影响确认且没有操作提交
|
||||
- When 点击关闭按钮或按 Escape
|
||||
- Then 关闭并卸载管理 Modal,底层工作区继续显示,模型服务不停止;点击遮罩不关闭
|
||||
- And 焦点恢复到仍可用的原触发元素;原入口已移除时回到应用中心,窄侧栏已隐藏时回到侧栏开关
|
||||
- When 查看服务列表
|
||||
- Then 显示服务名、模型、引擎、简短状态及错误;共享服务只展示一次
|
||||
- Given 向量服务独立进程正在运行
|
||||
- Then 显示带 PID 和进程范围的 CPU、工作集内存;CPU 首次采样显示预热状态
|
||||
- Given 服务 worker 与宿主共享进程,或进程已退出、采样失败
|
||||
- Then 在服务行显示不能独立统计或当前不可用的原因,不展示虚构零值或借用整个应用进程指标
|
||||
- Given ASR 有活动请求但没有 Worker 就绪观测
|
||||
- Then 显示“按需运行(状态未知)”,不推断服务正在运行
|
||||
|
||||
### US-F4 单任务取消入口(已移除)
|
||||
|
||||
保留故事 ID。FR-16 已收窄为服务监视,Modal 不再提供任务取消按钮。执行路径的原有取消行为和活动请求登记继续用于服务影响校验,见[逻辑设计](./logic-design.md#81-本机推理管理)。
|
||||
|
||||
### US-F5 管理实际执行服务生命周期
|
||||
|
||||
对应 FR-16;当前引擎支持清单见[技术设计](./technical-design.md#111-本机推理任务与执行服务)。
|
||||
ASR 取消独立 Worker,向量停止核对所有活动请求,OCR 有活动或排队请求时拒绝释放。
|
||||
运行阶段包括排队、加载和执行;当前未接入统一进度及来源项目对象关联。资源指标按服务进程统计,见 US-F3。
|
||||
|
||||
- Given 多个应用共用一个由 GoodBuddy 受管子进程承载的模型服务
|
||||
- When 用户启动、停止或重启服务
|
||||
- Then 仅提供引擎实际支持的操作,按同一服务串行处理;重复请求不重复创建进程或并发执行同一服务的重启
|
||||
- And 以真实进程退出和服务就绪确认结果,不将命令已发送当作操作成功
|
||||
- Given 用户准备停止、重启或释放服务或模型
|
||||
- When 用户发起操作
|
||||
- Then 在管理 Modal 上方打开紧凑影响确认,列出使用方、任务及影响;无已登记活动任务时说明执行前再次核对,取消后不发送操作指令且运行状态不变
|
||||
- And 初始焦点进入“保留运行状态”,Tab/Shift+Tab 在确认内循环,底层管理 Modal 不可交互;Escape 只关闭确认并恢复服务操作按钮焦点,按钮不可用时回到管理 Modal
|
||||
- When 确认提交尚未完成
|
||||
- Then 禁止重复执行,确认和取消按钮均禁用,两层 Modal 都不能通过关闭按钮或 Escape 关闭
|
||||
- When 操作成功
|
||||
- Then 只关闭影响确认,管理 Modal 保持打开
|
||||
- Given 执行前出现确认清单未包含的新任务
|
||||
- Then 拒绝执行,关闭旧确认并显示错误,自动刷新状态;刷新期间或失败时禁用服务操作,再次发起操作须确认新的影响清单
|
||||
- Given 停止失败,或重启时停止成功但启动失败
|
||||
- Then 显示实际状态、原因和可重试错误,不假报已停止或已恢复;无法确认时显示未知
|
||||
- When 服务再次启动或重启成功
|
||||
- Then 已中断任务不自动重放,由来源应用显式重新发起
|
||||
- Given 模型连接由外部程序管理
|
||||
- Then 在既有模型与连接设置中查看,不将其列入本机服务或提供停止、重启
|
||||
- Given 模型在进程内运行
|
||||
- Then 仅提供引擎实际支持的加载、释放,不显示虚构 PID、进程数或进程启动停止状态
|
||||
- When 用户关闭管理 Modal、关闭管理应用或取消常驻
|
||||
- Then 模型服务运行状态不变,取消常驻不关闭当前 Modal,卸载模型文件为独立操作
|
||||
|
||||
Reference in New Issue
Block a user