diff --git a/PRD.md b/PRD.md deleted file mode 100644 index 96ec51b..0000000 --- a/PRD.md +++ /dev/null @@ -1,462 +0,0 @@ -# MujicaUI 产品需求文档 - -版本:1.1 -日期:2026-10-05 -交付范围:500 个组件、两套主题、三种密度、原生 Gallery、文档与测试。001—100 见本文第 4 节;101—500 见 `docs/prd/ext-1.md`、`ext-2.md`、`ext-3.md`。 - -## 1. 产品定义 - -MujicaUI 是基于 MyGo 原生 UI 的 Go 桌面组件库。采用古典宫廷与哥特风格,覆盖 Windows、macOS、Linux。面向开发桌面工具、设置界面和数据应用的 Go 开发者。 - -MyGo 原生 UI 使用 Go 构建并自绘界面,不依赖 HTML、JavaScript 或 WebView。MujicaUI 复用该路线,不开发网页组件库。[^native] - -首版交付本文及扩展文件规定的全部功能。已有组件换肤、已有能力组合和新增组件合计 500 个,不是在 MyGo 之外再增加 500 个。尺寸、颜色、状态、子部件和模式不单独计数。 - -不包含浏览器端适配、新渲染引擎、新布局引擎、独立状态管理框架、业务后端和上传服务。图表与行情组件以 MyGo Painter 绘制,不建通用图表引擎;媒体组件只提供播放与采集界面,解码与设备访问由调用方完成;文档类组件只提供显示与轻量编辑,不建富文本编辑引擎。 - -## 2. MyGo 复用规则 - -| 情况 | 实现要求 | -| --- | --- | -| 只需调整颜色、字体、尺寸 | 封装现有组件,使用主题与公开样式接口。 | -| 需要改变外形或内部布局 | 使用已公开的 Base,重做外观,保留输入、焦点和无障碍行为。 | -| 需要组合交互 | 组合 MyGo 与 MujicaUI 组件;仅补充缺失的状态和行为。 | -| 系统窗口操作、文件选择 | 调用 MyGo 桌面 API;系统控制的外观保持系统样式。 | -| 公开接口不足 | 提交上游扩展;合入后固定依赖版本。不访问上游 internal,不另建渲染或输入实现。 | - -已核验的 Base 包括 Button、Checkbox、Switch、Toggle、Radio、Slider、Tabs、Segmented、Collapsible、Select、Combobox、Popover、Dialog、TextInput 和 TextArea 对应的 Base。不得假设存在未公开的 TableBase、TreeBase 等接口。[^bases] - -应用内 Menubar、DropdownMenu、ContextMenu 使用 MujicaUI 自绘样式。系统菜单、系统文件对话框和系统窗口按钮不承诺换肤;不得把系统菜单直接显示为已完成的哥特样式菜单。 - -MyGo 的主题可调整颜色、字体、间距与圆角,也可读取高对比度、文本缩放和减少动画设置。MujicaUI 扩展设计变量,不另建 CSS 或样式解释器。[^theme] - -## 3. 视觉规范 - -### 3.1 风格与装饰 - -视觉参考为用户提供的宫廷场景图片:象牙白墙面、黑红服装、金色细节与衬线标题。只提取配色、线条和字形,不使用人物、海报、Logo 或照片作为组件素材。 - -页面以象牙白或炭黑为底。酒红表示主要操作与选中状态。旧金用于分隔线、角饰和面板标题,不用于正文。成功、警告和错误使用独立语义颜色。 - -按钮、输入框和数据行不加花纹。Heading、Divider 可使用单个菱形或对称细线。Card、Dialog 的双线边框与角饰通过 `Decorated` 开启,默认关闭。装饰不占焦点、不遮挡内容、不缩小点击区域。 - -不使用背景纹理、仿皮革、仿金属、发光、玻璃效果和装饰性渐变。颜色选择器中的色谱不受此限制。 - -### 3.2 颜色 - -以下为首版默认设计值,不是从参考图精确取色的结果。 - -| 变量 | 浅色主题 | 深色主题 | 用途 | -| --- | --- | --- | --- | -| Background | `#F2EBDD` | `#191618` | 窗口背景 | -| Surface | `#FBF7EF` | `#242024` | 卡片与弹层 | -| SurfaceHover | `#E8DFD0` | `#342C32` | 悬停背景 | -| Text | `#211C1D` | `#F2EBDD` | 正文与标题 | -| TextMuted | `#6A5B55` | `#BCAEAA` | 说明与占位文字 | -| Border | `#D2C3AF` | `#51424A` | 非交互分隔线 | -| ControlBorder | `#877662` | `#968278` | 输入框等交互边界 | -| Accent | `#7D2034` | `#9C3C53` | 主按钮填充 | -| AccentHover | `#651729` | `#AA425C` | 主按钮悬停 | -| AccentPressed | `#511020` | `#813047` | 主按钮按下 | -| OnAccent | `#FFF8EE` | `#FFF8EE` | 主按钮文字与图标 | -| AccentText | `#7D2034` | `#E4A0AE` | 链接与强调文字 | -| Selection | `#EAD7D9` | `#492A36` | 选中项背景 | -| Focus | `#7D2034` | `#E4A0AE` | 焦点环 | -| Ornament | `#AD9164` | `#BDA172` | 非语义装饰 | -| Success | `#326247` | `#94C6A4` | 成功文字与图标 | -| Warning | `#805714` | `#DFC080` | 警告文字与图标 | -| Danger | `#A12635` | `#F0A1AA` | 错误文字与图标 | -| Info | `#355E80` | `#9DBDDA` | 信息文字与图标 | - -语义颜色默认用于文字、图标和边界,不直接拿浅色主题值填满深色主题控件。危险按钮的底色与前景色单独定义并检查对比度。焦点环与控件之间保留背景色间隔。 - -### 3.3 字体、尺寸与密度 - -标题使用衬线字体,中文标题使用宋体类字形。正文、表单、表格使用系统无衬线字体。代码与快捷键使用系统等宽字体。装饰字形不用于控件标签。 - -标题默认 28、22、18 DIP;正文 14 DIP;说明文字 12 DIP;正文行高为字号的 1.5 倍。字体允许由应用注册与覆盖;缺失字体使用系统回退,不显示空白或方框替代正常文字。MyGo 提供字体注册与字体回退接口。[^text] - -| 密度 | 单行控件最小高度 | 数据行最小高度 | 基础间距单位 | -| --- | --- | --- | --- | -| Compact | 28 DIP | 28 DIP | 3 DIP | -| Default | 36 DIP | 36 DIP | 4 DIP | -| Comfortable | 44 DIP | 44 DIP | 5 DIP | - -默认间距采用 4、8、12、16、24、32、48 DIP。输入框与按钮圆角 2 DIP;卡片 4 DIP;弹层 6 DIP。边框 1 DIP;焦点环 2 DIP,外留 2 DIP 间隔。图标采用 16、20、24 DIP,独立操作的命中区域不小于 28 × 28 DIP。 - -高度是下限,不是固定值。文本放大、换行或字体回退时允许增长。切换密度不缩小字号,也不改变组件语义。细线须按设备像素对齐。 - -### 3.4 组件外观与主题行为 - -Button 使用酒红填充、象牙白文字与小圆角。TextInput 使用实色背景与细边框。Checkbox 保留方框和勾选、半选含义;Radio 保留单选圆形。Slider 可使用菱形滑块,命中区域仍为矩形。Tabs 与 Sidebar 使用背景、边线和文字共同标记当前项。 - -Card 与 Dialog 的装饰只出现在边缘和标题区。DataTable 表头、行分隔和选中行不添加纹样。Button、TextInput、Checkbox、Tabs、Card、Dialog 作为视觉回归基准。 - -主题提供 `System`、`Light`、`Dark`,默认跟随系统。每个窗口可独立设置主题与密度。切换主题不得重置表单值、展开状态、选择、焦点或滚动位置。 - -样式覆盖顺序为局部参数、窗口主题、库默认值。系统高对比度、文本缩放与减少动画要求仍须生效。主色默认保留酒红,不自动替换为系统强调色;高对比度模式以可读性为先。 - -状态过渡默认 120 毫秒,弹层过渡 160 毫秒。不使用弹跳或装饰性循环动画。减少动画模式直接切换状态;加载标记保留静态说明。 - -## 4. 组件清单 - -以下每行是一个公开组件及其首版最低功能。全部适用组件同时遵守第 5 节的交互要求。 - -### 4.1 布局与桌面 · 10 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 001 | AppShell | 标题栏、侧栏、主内容、状态栏插槽;侧栏折叠;内容区独立滚动。 | -| 002 | TitleBar | 应用标题与工具插槽;窗口拖动;为系统窗口按钮留出空间。 | -| 003 | StatusBar | 左右分区;状态文字、进度与操作;宽度不足时折叠次要内容。 | -| 004 | Container | 内边距、最大宽度、居中、填充;内容裁剪可配置。 | -| 005 | Stack | 横向或纵向排列;间距、对齐、换行;隐藏项不占间距。 | -| 006 | Grid | 行列尺寸、间距、跨行跨列;固定尺寸与剩余空间分配。 | -| 007 | ScrollArea | 横纵滚动;滚动条;滚动到指定位置;保留滚动位置。 | -| 008 | SplitPane | 横纵分栏;拖动分隔条;最小尺寸;折叠;键盘调整。 | -| 009 | AspectRatio | 保持指定宽高比;在父容器约束下缩放;不拉伸子内容。 | -| 010 | Divider | 横纵分隔;文字插槽;可选菱形装饰;不获取焦点。 | - -### 4.2 基础展示 · 10 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 011 | Text | 换行、省略、选择复制;可编辑模式支持确认、取消和外部保存结果。 | -| 012 | Heading | 三级标题;衬线字形;可选副标题和分隔线;支持文本缩放。 | -| 013 | Icon | SVG 图标;尺寸与语义颜色;图标名称;加载失败回退。 | -| 014 | Image | 位图与 SVG;包含或裁切;占位、加载、错误;替代说明。 | -| 015 | Avatar | 图片、姓名首字母与默认图标回退;尺寸可配置。 | -| 016 | AvatarGroup | 头像排列;最大显示数量;剩余数量;展开完整名单。 | -| 017 | Badge | 文字或计数;上限显示;位置可配置;朗读完整数值。 | -| 018 | Tag | 标签文字、图标;可关闭;关闭动作与标签点击分离。 | -| 019 | Link | 外部地址或应用内动作;键盘激活;文本下划线;打开失败反馈。 | -| 020 | Kbd | 单键与组合键展示;按平台显示 Command 或 Ctrl;仅展示、不注册快捷键。 | - -### 4.3 基础输入 · 10 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 021 | Button | 主按钮、次按钮、描边、无底色、危险变体;图标;加载与禁用。 | -| 022 | IconButton | 仅图标按钮;必填操作名称;提示文字;加载与禁用。 | -| 023 | ButtonGroup | 横纵组合;统一边界与尺寸;组内方向键移动;逐项禁用。 | -| 024 | TextInput | 单行编辑;占位、前后插槽、清空;选择、撤销、剪贴板与输入法。 | -| 025 | TextArea | 多行编辑;最小与最大高度;滚动;字数提示;提交快捷键可配置。 | -| 026 | NumberInput | 数值输入与步进;范围、步长、精度、空值;支持仅步进按钮模式。 | -| 027 | PasswordInput | 密码遮蔽;显隐切换;粘贴;默认不朗读或记录密码内容。 | -| 028 | SearchInput | 搜索与清空;提交;查找栏模式提供匹配数量、上一项和下一项。 | -| 029 | Checkbox | 选中、未选、半选;关联标签;组内全选;键盘操作。 | -| 030 | RadioGroup | 横纵单选组;稳定选项值;方向键选择;逐项禁用。 | - -### 4.4 选择控件 · 10 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 031 | Switch | 开关值;标签;禁用;切换后回传结果;不自行调用业务接口。 | -| 032 | Toggle | 保持按下状态的按钮;文字或图标;选中状态可辨认。 | -| 033 | ToggleGroup | 单选或多选;是否允许空选;键盘导航;逐项禁用。 | -| 034 | Slider | 数值范围、步长、刻度、值提示;拖动、方向键、Home 和 End。 | -| 035 | RangeSlider | 双端范围;步长;两端不交叉;分别提供焦点和数值说明。 | -| 036 | Select | 单选列表;占位、清空、禁用项、空状态;方向键与输入定位。 | -| 037 | MultiSelect | 多选、搜索、全选、数量限制;标签折叠;无结果状态。 | -| 038 | Combobox | 输入筛选与选项选择;可允许自定义值;异步结果由调用方提供。 | -| 039 | Cascader | 多级选择;完整路径;叶节点限制;外部加载子项;加载失败重试。 | -| 040 | TreeSelect | 树形单选或多选;搜索、展开、半选;父子选择规则可配置。 | - -### 4.5 表单组合 · 10 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 041 | Form | 字段注册;校验;提交、重置;提交中防重入;错误字段定位。 | -| 042 | FormField | 标签、必填标记、说明、错误;标签关联控件;上下或左右布局。 | -| 043 | InputGroup | 输入框与前后文字、按钮组合;统一边框;各操作保留独立焦点。 | -| 044 | PinInput | 位数限制;输入与退格;整段粘贴;完成事件;允许前导零。 | -| 045 | TagsInput | 标签增删、粘贴分隔、去重、数量限制;提交前校验。 | -| 046 | Rating | 评分与只读展示;步长配置;键盘调整;数值说明。 | -| 047 | ColorPicker | HEX、RGB、透明度;颜色选择;预设色;输入校验;透明度背景格。 | -| 048 | FilePicker | 调用系统文件或目录选择器;单选、多选、扩展名筛选;取消不报错。 | -| 049 | FileDropZone | 文件拖入提示;文件列表;类型、数量和大小检查;移除与错误提示。 | -| 050 | Transfer | 两侧列表;搜索、选择、移动、批量移动;禁用项;保留稳定条目 ID。 | - -### 4.6 日期与时间 · 8 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 051 | Calendar | 月视图;日期选择;今日标记;不可选日期;周起始日可配置。 | -| 052 | DatePicker | 日期输入与日历弹层;格式、范围、清空;无效输入提示。 | -| 053 | DateRangePicker | 起止日期;范围预览;起止限制;确认与清空。 | -| 054 | TimePicker | 时分秒;12/24 小时;步长与范围;输入与选择。 | -| 055 | TimeRangePicker | 起止时间;范围校验;跨午夜须显式开启。 | -| 056 | DateTimePicker | 日期与时间组合;时区由调用方指定;范围限制;确认与取消。 | -| 057 | MonthPicker | 年份切换;月份选择;范围、禁用与清空。 | -| 058 | YearPicker | 年份区间浏览;键盘选择;范围、禁用与清空。 | - -### 4.7 导航 · 10 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 059 | Tabs | 标签与面板关联;横纵布局;方向键导航;禁用项;溢出滚动。 | -| 060 | SegmentedControl | 分段单选;文字或图标;等宽或自适应;键盘导航。 | -| 061 | Breadcrumb | 路径导航;当前项;中间项折叠;完整名称提示。 | -| 062 | Pagination | 页码、上一页、下一页、页容量、跳页;总数变化时修正有效页码。 | -| 063 | Steps | 当前、完成、等待、错误;横纵布局;是否可点击由调用方控制。 | -| 064 | Sidebar | 分组与嵌套;图标、计数、选中项;展开折叠;紧凑模式。 | -| 065 | NavigationMenu | 层级导航;当前路径;可选前进、后退按钮;接入已有导航状态。 | -| 066 | Menubar | 应用窗口内菜单栏;子菜单;勾选和单选项;快捷键提示;键盘导航。 | -| 067 | Toolbar | 操作分组;分隔;窄宽溢出菜单;方向键导航;图标名称。 | -| 068 | CommandPalette | 快捷键打开;搜索、分组、无结果;方向键选择;执行后关闭。 | - -### 4.8 内容与数据 · 12 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 069 | Card | 标题、内容、页脚插槽;描边;可选角饰;不默认增加点击行为。 | -| 070 | List | 虚拟化列表与网格模式;稳定条目键;单选、多选;键盘导航与空状态。 | -| 071 | DataTable | 虚拟化;排序、筛选、多选;列宽、列顺序、列显隐;自定义单元格。 | -| 072 | Tree | 节点展开;选择;键盘导航;外部加载子项;空状态;大量节点虚拟化。 | -| 073 | TreeTable | 树形层级与列组合;展开、选择、列宽、排序状态;可见行虚拟化。 | -| 074 | Accordion | 单项或多项展开;是否允许全部关闭;键盘操作;展开状态受控。 | -| 075 | Collapsible | 触发区与内容区;外部控制展开;隐藏内容退出焦点序列。 | -| 076 | Timeline | 时间、标题、内容、状态;纵向排列;长内容换行。 | -| 077 | DescriptionList | 名称与值;多列、跨列、复制值;窄宽时改为单列。 | -| 078 | Statistic | 数值、单位、前后缀、精度;变化值说明;不计算业务指标。 | -| 079 | ImageViewer | 放大、缩小、平移、适应窗口、切换图片;键盘操作与关闭。 | -| 080 | Carousel | 上一项、下一项、位置指示;默认不自动播放;开启后支持暂停。 | - -### 4.9 浮层与菜单 · 10 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 081 | Dialog | 标题、说明、内容、操作区;焦点限制与恢复;关闭规则可配置。 | -| 082 | AlertDialog | 危险操作确认;默认聚焦取消;默认不允许点击遮罩关闭。 | -| 083 | Drawer | 四侧展开;尺寸限制;模态或非模态;关闭与焦点恢复。 | -| 084 | Popover | 锚点定位;自动避让窗口边缘;外部点击与 Escape 关闭。 | -| 085 | Tooltip | 悬停或聚焦显示;延迟;边缘避让;不放交互内容。 | -| 086 | HoverCard | 悬停或聚焦预览;允许移入内容;键盘可进入;延迟关闭。 | -| 087 | DropdownMenu | 按钮触发菜单;分组、子菜单、勾选、禁用;操作后按规则关闭。 | -| 088 | ContextMenu | 应用内右键菜单;键盘触发;按指针或焦点定位;边缘避让。 | -| 089 | Popconfirm | 锚点旁确认与取消;说明关联;可聚焦操作;不替代高风险确认框。 | -| 090 | Overlay | 窗口内覆盖层;定位、遮罩、模态行为;与现有 MyGo 浮层协作。 | - -### 4.10 反馈与状态 · 10 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 091 | Alert | 信息、成功、警告、错误;标题、内容、操作;可关闭。 | -| 092 | Toast | 消息队列;去重 ID;操作、关闭、时长;悬停或聚焦时暂停计时。 | -| 093 | Progress | 确定与不确定进度;线形与环形;计量模式支持范围和阈值。 | -| 094 | Spinner | 加载标记;尺寸与说明;隐藏或停止加载后停止动画。 | -| 095 | Skeleton | 文字、圆形、矩形占位;固定内容尺寸;减少动画模式使用静态填充。 | -| 096 | EmptyState | 标题、说明、可选图标与操作;区分无数据和无搜索结果。 | -| 097 | Result | 成功、失败、无权限等结果页;说明与下一步操作;不执行业务跳转。 | -| 098 | StatusIndicator | 状态标记与文字;成功、警告、错误、离线、处理中;不能只靠颜色。 | -| 099 | NotificationCenter | 应用内消息列表;已读、未读、清除、操作;消息与持久化由调用方管理。 | -| 100 | LoadingOverlay | 容器范围加载遮罩;阻止该范围重复操作;说明;可选取消动作。 | - -### 4.11 上游组件归并 - -MyGo 已有能力按以下方式纳入清单,不增加计数。其余同类组件按第 2 节复用。[^native] - -| MyGo 能力 | MujicaUI 对应项 | -| --- | --- | -| Primary button、Menu button | Button 的主按钮变体、DropdownMenu | -| Stepper、Number input | NumberInput 的步进模式与输入模式 | -| Search field、Find bar | SearchInput 的搜索模式与查找栏模式 | -| Autocomplete、Combobox | Combobox 的自由输入与选项选择模式 | -| Token field、Editable text | TagsInput、Text 的可编辑模式 | -| List、Grid view | List 的列表模式与网格模式 | -| Table、Outline、Tree | DataTable、TreeTable、Tree | -| Date input、Time input | DatePicker、TimePicker | -| Back and forward buttons | NavigationMenu 的历史导航操作 | -| Meter、Progress bar | Progress 的计量模式与进度模式 | - -## 5. 交互与状态 - -### 5.1 通用状态 - -| 状态 | 要求 | -| --- | --- | -| 默认 | 内容、边界与可操作性可辨认。 | -| 悬停 | 改变背景或边界,不改变布局尺寸。 | -| 按下 | 显示按下反馈;释放在无效区域时不误触发。 | -| 聚焦 | 键盘焦点可见;选中或错误状态不能覆盖焦点环。 | -| 选中 | 同时使用标记、形状或文字,不只改变颜色。 | -| 禁用 | 不修改值,不执行操作,退出常规 Tab 序列;仍向辅助技术报告禁用。 | -| 只读 | 不修改值;文字仍可聚焦、选择和复制;不得等同于禁用。 | -| 加载 | 保留尺寸与标签;阻止重复提交;完成或失败后恢复操作。 | -| 错误 | 保留输入;显示具体字段错误;提供修正或重试入口。 | - -只为适用组件提供对应状态。展示与布局组件不添加无用途的 Loading、Disabled 等参数。 - -### 5.2 键盘与无障碍 - -所有操作必须能通过键盘完成。Tab 与 Shift+Tab 跨组移动;方向键在 Tabs、菜单、单选组和工具栏内部移动。快捷键遵循平台习惯,不抢占文本编辑和输入法按键。MyGo 已提供焦点组、平台快捷键映射和输入法处理,封装时不得移除。[^input] - -无文字的操作必须提供名称。字段关联标签、说明与错误。复合组件提供对应角色、值、选中、展开和禁用状态。装饰不进入无障碍树。沿用 MyGo 的角色、Label、Description 与 Error 机制。[^accessibility] - -项目对比度下限:正常文字与占位文字 4.5:1;用于识别控件、状态和焦点的图形 3:1。纯装饰边框与禁用状态不按正文处理。采用 WCAG 的对比度计算方法;不以此代替整套无障碍验收。[^text-contrast][^ui-contrast] - -### 5.3 输入与表单 - -文本组件必须保留中文输入法、组合输入、选择、剪贴板、撤销和重做。输入法组合期间不自动提交、不跳到下一格、不拆分尚未提交的文本。PasswordInput 不在日志、测试报告或无障碍值中泄露明文。 - -Form 支持同步校验与调用方提供的异步校验结果;支持失焦和提交时校验。首次提交前不持续显示未触碰字段错误。提交失败保留所有值并定位第一个错误字段。异步旧结果不得覆盖更新后的字段值;提交中不得重复触发。 - -Form 不自带业务模型、远程接口或持久化。跨字段规则由调用方提供。重置恢复调用方给定的初始值,并清除提交与校验状态。 - -### 5.4 数据组件 - -DataTable 以稳定行 ID 和列 ID 保存状态。排序、筛选、列调整后,选择仍指向同一条数据。提供受控模式与可选本地排序、筛选辅助函数;外部数据查询由调用方负责。 - -MyGo Table 提供排序状态和界面交互,但数据仍按调用方提供的顺序显示。MujicaUI 不能只显示排序箭头而不完成排序或向调用方发出排序请求。[^table] - -List、DataTable、Tree、TreeTable 使用虚拟化;不能为全部数据逐项创建界面元素。排序与筛选只在数据或条件变化时执行,不在每帧重复执行。异步加载分别展示首次加载、追加加载、失败和无数据。 - -### 5.5 浮层与消息 - -模态窗口限制焦点并阻止背景交互;非模态浮层不锁住整个窗口。Escape 优先交给输入法和当前编辑控件,再关闭最上层可关闭浮层。关闭后恢复到触发控件;触发控件消失时回到有效父级。复用 MyGo 的浮层和焦点机制。[^overlays] - -弹层在窗口边缘翻转或限高滚动。嵌套 Select、菜单或 Popover 时,不提前关闭父 Dialog。危险操作默认聚焦取消;不得通过 Enter 在未确认目标时执行。 - -Toast 默认显示 5 秒,同一窗口最多同时显示 3 条,其余排队。含操作的消息在悬停或聚焦时暂停计时;错误消息默认保留到用户关闭。消息不得抢走正在输入的焦点。 - -### 5.6 日期、文件与本地化 - -日期选择保存日期值,不因隐式 UTC 转换改变日期。时间范围默认不跨午夜;日期时间由调用方指定时区。无法表示或无法确定的日期时间须提示,不静默修正。 - -FilePicker 只负责选择并返回结果。FileDropZone 只负责接收、校验与展示,不包含上传。文件内容读取、图片解码和网络访问不得阻塞界面线程。取消文件选择不作为错误。 - -组件内置文案提供 `zh-CN` 与 `en-US`,应用可覆盖。日期格式、周起始日、12/24 小时制和数字格式可配置。业务文案由调用方传入,不写死在组件内部。 - -## 6. API 与实现约束 - -### 6.1 API 约定 - -对外包名为 `mujica`。函数首参使用 `*ui.Context`;值通过状态指针或专用状态结构传入;配置使用有类型的 Options。基础组件返回 `*ui.Element`;复合组件按需返回命名部件或状态结果。不用 `map[string]any` 作为常规配置接口。 - -下列为 MujicaUI 的目标接口,不是 MyGo 现有接口: - -```go -func Button(c *ui.Context, label string, opts ButtonOptions) *ui.Element -func TextInput(c *ui.Context, value *string, opts TextInputOptions) *ui.Element -func Checkbox(c *ui.Context, value *bool, label string, opts CheckboxOptions) *ui.Element -``` - -事件沿用 `.Clicked()`、`.Changed()`、`.Submitted()` 的调用习惯。一次操作只产生一次业务事件。禁用、只读、范围等会影响行为的参数须在处理输入前生效,不依赖事后换色。 - -零值 Options 提供默认样式。必填参数、有效范围和空值规则必须写入 API 文档。子内容使用回调或命名插槽,不为每种组合另建组件。原生 MyGo 组件可以与 MujicaUI 混用。 - -### 6.2 状态与生命周期 - -业务状态由应用持有;组件只保留输入、展开、焦点、动画等界面状态。临时状态使用 MyGo 已有机制,不建立第二套全局状态仓库。 - -MyGo 的视图按帧构建,Element 只属于当前帧。不得跨帧缓存 Element,也不得在 goroutine 中操作它。异步结果通过 `Window.Update` 回到界面线程;窗口关闭后取消任务或忽略结果。动态项目使用稳定键;需即时处理输入的组件通过外层有键容器保持身份。[^views] - -缓存只覆盖字体、图标、装饰路径和必要的布局结果。主题、缩放、字体或数据变化时使相关缓存失效。隐藏组件不持续请求重绘。 - -### 6.3 项目结构 - -单仓库、单 Go 模块。组件放在根包,测试与实现同目录;不按组件拆成 100 个包。 - -```text -mujicaui/ -├── go.mod -├── go.sum -├── button.go -├── button_test.go -├── ... 其余组件及对应测试 -├── theme/ 主题、设计变量与对比度测试 -├── icons/ 图标接口与必要资源 -├── internal/ 少量共享实现与上游适配 -├── examples/gallery/ 原生演示应用 -├── docs/components/ 100 个组件文档 -├── testdata/ 测试数据与视觉基准 -├── README.md -├── LICENSE -└── THIRD_PARTY_NOTICES.md -``` - -固定 MyGo 的已验证 tag 或 commit,并提交 go.mod 与 go.sum。Go 最低版本与系统依赖以该版本为准,写入兼容表。不使用浮动 main,不为 Go 库运行引入 Node.js、WebView 或浏览器依赖。 - -## 7. 平台与资源 - -Windows、macOS、Linux 均需运行同一套组件 API。允许字体栅格化、系统窗口按钮和系统对话框不同;不允许缺失组件功能。 - -TitleBar 使用 MyGo 提供的窗口控制预留区域与拖动接口,不覆盖系统按钮。原生路线在 Windows 不要求 WebView2,在 Linux 仍有 GTK 3 等系统依赖,不能把“不使用 WebView”写成“没有运行时依赖”。[^windows] - -测试 100%、125%、150%、200% 显示缩放,以及跨不同缩放显示器移动窗口。文本缩放到 200% 后,不裁掉标签、字段错误和主要操作。Gallery 默认窗口为 1200 × 800 DIP,最小为 800 × 600 DIP;窄宽时折叠导航,长表格横向滚动。 - -发布说明列出已实测的操作系统版本、CPU 架构与 Linux 会话类型。X11、原生 Wayland 和 XWayland 分别记录,不把 XWayland 结果标成原生 Wayland 支持。 - -库和 Gallery 默认离线可用,无遥测、账号和强制网络字体。应用未请求时,不读取文件内容、不连接网络、不写入业务配置。字体可由应用提供;自带资源须记录来源与许可,图标和角饰不得依赖远程请求。 - -## 8. Gallery 与文档 - -Gallery 必须使用 MujicaUI 本身构建,不用网页或截图替代原生组件。提供组件分类、中英文搜索、主题切换、密度切换和示例代码复制。 - -每个组件页面包含可交互示例、适用状态展示、参数控制、事件结果和键盘说明。事件展示不得记录密码。提供设置页、文件列表、表单提交三个组合示例,验证跨组件协作,不接业务后端。 - -文档包含安装、最小运行示例、主题配置、MyGo 兼容版本和平台依赖。每个组件单独一页,列明参数、默认值、状态、事件、键盘操作、可复制代码和限制。所有 Go 示例必须通过编译检查。 - -文档中的组件截图由原生 Gallery 或渲染测试生成。文档可作为静态站发布,但浏览器页面不宣称能运行 Go 原生组件。API 名称与代码使用英文,首版说明文档使用中文。 - -## 9. 测试与性能 - -### 9.1 功能测试 - -使用 MyGo `ui.NewTester` 执行无窗口点击、键盘、输入、滚动与状态测试;使用图像快照检查渲染。MyGo 测试接口还提供缩放、主题、偏好和输入法组合模拟。[^testing] - -每个组件均需覆盖适用的正常、边界、错误与键盘路径。复用上游不免除封装层测试;新增样式不能破坏点击区域、焦点与无障碍名称。 - -快照覆盖全部组件的浅色、深色默认状态。六个视觉基准组件额外覆盖三种密度和全部适用交互状态。各平台使用独立字体与快照基准,不要求不同系统逐像素相同。 - -真机补测系统文件选择、窗口控制、中文输入法、跨屏缩放与屏幕阅读器。无窗口测试不能替代这些项目。发布测试记录包含 VoiceOver、Windows Narrator 或 NVDA,以及 Linux Orca 的结果。 - -### 9.2 性能要求 - -以下为验收目标,不是已测量结果。三个平台各固定一台基准设备,记录 CPU、GPU、内存、系统、驱动、字体、显示缩放与 MyGo 版本;使用发布构建,关闭 Inspector。 - -| 场景 | 验收要求 | -| --- | --- | -| 大列表 | 100,000 条固定高度记录可滚动、搜索和选择;每帧创建的元素数量由可见区域与预取范围决定。 | -| 大表格 | 100,000 行、10 列纯文本数据;排序与筛选后保留有效选择;不逐帧重算排序。 | -| 大树 | 100,000 个节点;折叠子树不创建界面元素;展开、搜索与滚动不阻塞输入。 | -| 渲染开销 | 同设备、同尺寸、同内容,对有等价上游实现的控件比较;MujicaUI 的界面构建与绘制 P95 耗时增幅不超过 20%。 | -| 空闲 | 关闭加载和过渡动画后,连续观察 60 秒,无组件定时器或自身逻辑持续触发重绘。 | -| 生命周期 | 连续打开、关闭同一弹层 100 次;回收后无持续增加的组件实例、后台任务或事件订阅。 | - -持续集成执行格式检查、静态检查、单元测试、示例编译与快照检查。支持的环境运行竞态检测。平台集成与性能结果随发布保存,不用单一平台的结果代替三平台验证。 - -## 10. 发布验收 - -代码采用 MIT 许可证。仓库保留第三方声明;任何复制的上游代码须保留其声明。发布 Go 模块、源码、三平台 Gallery 构建产物、文档和兼容表。 - -| 验收项 | 通过条件 | -| --- | --- | -| 数量 | 001—500 均有实现、文档、Gallery 示例与测试;变体不重复计数。 | -| 功能 | 每项最低功能可操作;不存在占位按钮、空事件或仅静态展示的交互组件。 | -| 样式 | 全部组件覆盖浅深主题;无 MyGo 默认外观混入;系统控制的界面按已声明例外处理。 | -| 主题与密度 | 切换无需重启;业务状态、焦点与滚动位置不丢失;无文字裁切。 | -| 输入与无障碍 | 键盘、中文输入法、焦点恢复、标签关联与屏幕阅读器测试通过。 | -| 数据与生命周期 | 大数据测试通过;无状态串行、重复提交、后台任务或订阅泄漏。 | -| 三平台 | 兼容表内的平台及架构均完成构建和实机验证;限制写入对应文档。 | -| 交付 | 固定依赖、示例可编译、Gallery 可运行、默认离线可用、资源许可齐全。 | - -首次公开发布须满足全部验收项,不以占位组件或后续补齐代替首版范围。 - -## 参考资料 - -上游能力核验日期:2026-10-05。实现与发布以固定依赖版本的代码为准。 - -[^native]: MyGo — [Native UI](https://mygo.egoist.dev/docs/ui). -[^bases]: MyGo — [Custom widgets](https://mygo.egoist.dev/docs/ui/custom-widgets). -[^theme]: MyGo — [Styling and themes](https://mygo.egoist.dev/docs/ui/styling). -[^text]: MyGo — [Text](https://mygo.egoist.dev/docs/ui/text). -[^input]: MyGo — [Input](https://mygo.egoist.dev/docs/ui/input). -[^accessibility]: MyGo — [Accessibility](https://mygo.egoist.dev/docs/ui/accessibility). -[^table]: MyGo — [Table](https://mygo.egoist.dev/docs/ui/table). -[^overlays]: MyGo — [Overlays](https://mygo.egoist.dev/docs/ui/overlays). -[^views]: MyGo — [Views](https://mygo.egoist.dev/docs/ui/views). -[^windows]: MyGo — [Windows with native UI](https://mygo.egoist.dev/docs/ui/windows). -[^testing]: MyGo — [Testing](https://mygo.egoist.dev/docs/ui/testing). -[^text-contrast]: W3C — [Contrast (Minimum)](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html). -[^ui-contrast]: W3C — [Non-text Contrast](https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html). diff --git a/TASKS.md b/TASKS.md deleted file mode 100644 index 9ca350d..0000000 --- a/TASKS.md +++ /dev/null @@ -1,47 +0,0 @@ -# TASKS - -Ordered by PRD sections. Check an item only after its codex review passes. Review rounds noted as `r1`, `r2`, `r3`. - -- [x] T01 §1–2 Module, MyGo v0.2.10 pin, reuse rules, AGENTS.md — r1 FAIL (line height, enum checks), r2 PASS -- [x] T02 §3 Tokens, density, type, motion, contrast tests, `Use`, icons, i18n — r1 FAIL, r2 PASS -- [x] T03 §4.1 Layout 001–010 — r1 FAIL 8, r2 FAIL 2, r3 FAIL 1 (folded focus); fixed b34958b, verified by coordinator red/green after cap -- [x] T04 §4.2 Display 011–020 — r1 FAIL 5, r2 FAIL 5, r3 FAIL 2 (image sizing, Retry clip); fixed e922651, verified by coordinator tests and snapshot after cap -- [x] T05 §4.3 Inputs 021–030 — r1 FAIL 10, r2 FAIL 5, r3 FAIL 2 (float grid, password in test output); fixed 07a90f4, verified by coordinator red/green after cap -- [x] T06 §4.4 Choices 031–040 — r1 FAIL 16, r2 FAIL 5, r3 FAIL 1 (tiny-step direction); fixed 9e78900, verified by coordinator red/green after cap -- [x] T07 §4.5 Forms 041–050 — r1 FAIL 7, r2 FAIL 4, r3 FAIL 1 (disabled cut); fixed 49b3b78, coordinator red/green verified -- [x] T08 §4.6 Date and time 051–058 — r1 FAIL 9, r2 FAIL 5, r3 FAIL 2 (popup pick commit, selection mark); fixed e3eaef7, coordinator red/green verified -- [x] T09 §4.7 Navigation 059–068 — r1 FAIL 5, r2 FAIL 5, r3 FAIL 1 (Tab out of menubar); fixed 7e4d862, coordinator red/green verified -- [x] T10 §4.8 Content and data 069–080 — r1 FAIL 9, r2 FAIL 4, r3 PASS -- [x] T11 §4.9 Overlays and menus 081–090 — r1 FAIL 12, r2 FAIL 5, r3 PASS -- [x] T12 §4.10 Feedback 091–100 — r1 FAIL 5, r2 FAIL 4, r3 PASS -- [ ] T12.01 §4.12 Charts I 101–120 (docs/prd) -- [ ] T12.02 §4.13 Charts II 121–140 (docs/prd) -- [ ] T12.03 §4.14 Market Charts 141–160 (docs/prd) -- [ ] T12.04 §4.15 Trading 161–180 (docs/prd) -- [ ] T12.05 §4.16 Markets 181–200 (docs/prd) -- [ ] T12.06 §4.17 Chat Messages 201–220 (docs/prd) -- [ ] T12.07 §4.18 Chat Input 221–240 (docs/prd) -- [ ] T12.08 §4.19 Chat Sessions 241–260 (docs/prd) -- [ ] T12.09 §4.20 Agent 261–280 (docs/prd) -- [ ] T12.10 §4.21 Agent Ops & Motion 281–300 (docs/prd) -- [ ] T12.11 §4.22 Git 301–320 (docs/prd) -- [ ] T12.12 §4.23 Terminal & Code 321–340 (docs/prd) -- [ ] T12.13 §4.24 Media 341–360 (docs/prd) -- [ ] T12.14 §4.25 Projects 361–380 (docs/prd) -- [x] T12.15 §4.26 Files & Collaboration 381–400 — existing 17 sources only (user scope); missing 393/397/398 dropped; r1 FAIL 7, r2 FAIL 2, r3 PASS; 386 native stem selection and IME Enter remain documented MyGo API limits -- [ ] T12.16 §4.27 Messaging & Mail 401–420 (docs/prd) -- [ ] T12.17 §4.28 Calendar 421–440 (docs/prd) -- [ ] T12.18 §4.29 Dashboards & Dev Tools 441–460 (docs/prd) -- [ ] T12.19 §4.30 Settings & Account 461–480 (docs/prd) -- [ ] T12.20 §4.31 Essentials 481–500 (docs/prd) -- [ ] T13 §4.11 Upstream mapping documented -- [ ] T14 §5 States, keyboard, a11y, input, overlays: cross-cutting tests -- [ ] T15 §6 API and lifecycle audit; README, LICENSE, THIRD_PARTY_NOTICES -- [ ] T16 §7 Platform table, offline, resources -- [ ] T17 §8 Native Gallery: categories, zh/en search, theme, density, code copy, three composite demos -- [ ] T18 §8 Web mirror of the Gallery (user request) -- [ ] T19 §8 Docs: install, minimal example, theme, compat; 100 component pages; examples compile -- [ ] T20 §9 Snapshots, density baselines, large-data and lifecycle tests -- [ ] T21 §9 CI workflow -- [ ] T22 §6.3 Split into category sub-packages after 001–500 land (user decision 2026-10-05); update PRD §6.3, docs, Gallery, CI -- [ ] T23 §10 Acceptance checklist diff --git a/assignee_picker.go b/assignee_picker.go index 0d9cc06..b42543b 100644 --- a/assignee_picker.go +++ b/assignee_picker.go @@ -22,12 +22,12 @@ type AssigneePickerOptions struct { } type assigneeState struct { - first *Member + first *ProjectMember n, version int ready, open bool query, filtered string options, shown []SelectOption[string] - members map[string]Member + members map[string]ProjectMember selected map[string]bool selectedFirst *string selectedN, count int @@ -36,7 +36,7 @@ type assigneeState struct { } // AssigneePicker searches and selects members, showing their avatars. -func AssigneePicker(c *ui.Context, ids *[]string, members []Member, opts AssigneePickerOptions) ChoiceResult { +func AssigneePicker(c *ui.Context, ids *[]string, members []ProjectMember, opts AssigneePickerOptions) ChoiceResult { if opts.Max < 0 { panic("mujica: AssigneePicker Max must be non-negative") } @@ -47,7 +47,7 @@ func AssigneePicker(c *ui.Context, ids *[]string, members []Member, opts Assigne } row := ui.Row(c).Gap(d.Unit() * 2).AlignItems(ui.Center).Disabled(opts.Disabled) st := ui.Local(row, "assignees", func() assigneeState { return assigneeState{} }) - var first *Member + var first *ProjectMember var selectedFirst *string if len(members) > 0 { first = &members[0] @@ -58,7 +58,7 @@ func AssigneePicker(c *ui.Context, ids *[]string, members []Member, opts Assigne dataChanged := !st.ready || st.first != first || st.n != len(members) || st.version != opts.Version if dataChanged { st.first, st.n, st.version, st.ready = first, len(members), opts.Version, true - st.members = make(map[string]Member, len(members)) + st.members = make(map[string]ProjectMember, len(members)) st.options = make([]SelectOption[string], len(members)) for i, m := range members { if _, exists := st.members[m.ID]; exists || m.ID == "" { diff --git a/assignee_picker_test.go b/assignee_picker_test.go index 6e7b8fc..6a0d39c 100644 --- a/assignee_picker_test.go +++ b/assignee_picker_test.go @@ -83,9 +83,9 @@ func TestAssigneePickerAncestorDisableCloses(t *testing.T) { } func TestAssigneePickerLargeData(t *testing.T) { - members := make([]Member, 100000) + members := make([]ProjectMember, 100000) for i := range members { - members[i] = Member{ID: fmt.Sprint(i), Name: fmt.Sprintf("Member %d", i)} + members[i] = ProjectMember{ID: fmt.Sprint(i), Name: fmt.Sprintf("Member %d", i)} } var ids []string tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}); AssigneePicker(c, &ids, members, AssigneePickerOptions{}) }, 500, 520) @@ -140,7 +140,9 @@ func TestAssigneePickerCompactHitAreas(t *testing.T) { var ids []string tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{Density: theme.Compact}) - ui.Column(c).Padding(16).Children(func() { AssigneePicker(c, &ids, []Member{{ID: "only", Name: "Only Member"}}, AssigneePickerOptions{}) }) + ui.Column(c).Padding(16).Children(func() { + AssigneePicker(c, &ids, []ProjectMember{{ID: "only", Name: "Only Member"}}, AssigneePickerOptions{}) + }) }, 500, 520) if err := tt.Click("Assignees"); err != nil { t.Fatal(err) diff --git a/burndown_chart.go b/burndown_chart.go index 75a81e0..61caf65 100644 --- a/burndown_chart.go +++ b/burndown_chart.go @@ -73,7 +73,7 @@ func BurndownChart(c *ui.Context, remaining []float64, opts BurndownChartOptions } lo, top, step := internal.Nice(0, summary, 4) ticks := internal.Ticks(lo, top, step) - keys := []chartKey{{Name: msg(c, "burndown_chart.actual"), Color: pal[0], Shape: 0}, {Name: msg(c, "burndown_chart.ideal"), Color: pal[5], Shape: 1}} + keys := []ChartLegendItem{{Name: msg(c, "burndown_chart.actual"), Color: pal[0], Shape: 0}, {Name: msg(c, "burndown_chart.ideal"), Color: pal[5], Shape: 1}} geom := func(r ui.Rect) (ui.Rect, internal.Linear, internal.Linear) { plot := chartInset(r, 48, 26) return plot, internal.Linear{D0: 0, D1: float64(days - 1), R0: float64(plot.X), R1: float64(plot.X + plot.W)}, @@ -83,15 +83,15 @@ func BurndownChart(c *ui.Context, remaining []float64, opts BurndownChartOptions if len(remaining) == 0 { n = 0 } - spec := chartSpec{n: n, keys: keys, toggle: true} - spec.pick = func(r ui.Rect, x, y float32) int { + spec := ChartSpec{Marks: n, Legend: keys, Hidden: ch.toggles(true)} + spec.Pick = func(r ui.Rect, x, y float32) int { plot, xs, _ := geom(r) if x < plot.X-8 || x > plot.X+plot.W+8 { return -1 } return max(0, min(days-1, int(math.Round(xs.Invert(x))))) } - spec.readout = func(i int) string { + spec.Readout = func(i int) string { day := opts.Start.AddDays(i) parts := datetimeMonthName(c, day.Month) + " " + strconv.Itoa(day.Day) if v := actual(i); !math.IsNaN(v) && !ch.hidden(keys[0].Name) { @@ -102,7 +102,7 @@ func BurndownChart(c *ui.Context, remaining []float64, opts BurndownChartOptions } return parts } - spec.draw = func(p *ui.Painter, r ui.Rect, active int) { + spec.Draw = func(p *ui.Painter, r ui.Rect, active int) { plot, xs, ys := geom(r) internal.AxisY(p, plot, ys, ticks, func(v float64) string { return internal.FormatTick(v, step) }, size, k) var tx []float32 diff --git a/channel_list.go b/channel_list.go index f929c86..d56e090 100644 --- a/channel_list.go +++ b/channel_list.go @@ -16,7 +16,7 @@ type Channel struct { ID, Name string // Direct marks a private conversation, shown with the person's presence. Direct bool - Presence Presence + Presence PresenceStatus Unread int Muted bool Pinned bool diff --git a/docs/components/372-assignee-picker.md b/docs/components/372-assignee-picker.md index 4fa99db..3d5c42e 100644 --- a/docs/components/372-assignee-picker.md +++ b/docs/components/372-assignee-picker.md @@ -7,7 +7,7 @@ ## 最小示例 ```go -members := []mujica.Member{{ID: "ann", Name: "Ann"}, {ID: "ben", Name: "Ben"}} +members := []mujica.ProjectMember{{ID: "ann", Name: "Ann"}, {ID: "ben", Name: "Ben"}} ids := []string{"ann"} view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) @@ -20,7 +20,7 @@ ui.Render(view, 1000, 700, 1) ## 参数 -`ids *[]string`、`members []Member`;`Max=0` 表示自由数量;`Disabled`、`Label` 控制交互与名称;`Version` 用于外部原地修改。 +`ids *[]string`、`members []ProjectMember`;`Max=0` 表示自由数量;`Disabled`、`Label` 控制交互与名称;`Version` 用于外部原地修改。 ## 状态 diff --git a/docs/components/378-workload-view.md b/docs/components/378-workload-view.md index 0ae8335..487f1bd 100644 --- a/docs/components/378-workload-view.md +++ b/docs/components/378-workload-view.md @@ -7,7 +7,7 @@ ## 最小示例 ```go -loads := []mujica.Workload{{Member: mujica.Member{ID: "ann", Name: "Ann"}, Assigned: 46, Capacity: 40}} +loads := []mujica.Workload{{Member: mujica.ProjectMember{ID: "ann", Name: "Ann"}, Assigned: 46, Capacity: 40}} view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) mujica.WorkloadView(c, loads, mujica.WorkloadViewOptions{}) diff --git a/docs/components/407-online-status.md b/docs/components/407-online-status.md index 8e59b96..2edd031 100644 --- a/docs/components/407-online-status.md +++ b/docs/components/407-online-status.md @@ -16,7 +16,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`p Presence` 的零值为离线,支持 `PresenceOnline`、`PresenceAway`、`PresenceBusy`、`PresenceOffline`。`HideText` 默认 false;隐藏文字时保留名称和提示。`Size` 默认 10 DIP,必须 ≥ 0。 +`p PresenceStatus` 的零值为离线,支持 `PresenceOnline`、`PresenceAway`、`PresenceBusy`、`PresenceOffline`。`HideText` 默认 false;隐藏文字时保留名称和提示。`Size` 默认 10 DIP,必须 ≥ 0。 ## 状态 diff --git a/docs/components/448-trace-waterfall.md b/docs/components/448-trace-waterfall.md index b7fa2e6..a297948 100644 --- a/docs/components/448-trace-waterfall.md +++ b/docs/components/448-trace-waterfall.md @@ -7,7 +7,7 @@ ## 最小示例 ```go -spans := []mujica.TraceSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 200 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 20 * time.Millisecond, Duration: 80 * time.Millisecond}} +spans := []mujica.WaterfallSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 200 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 20 * time.Millisecond, Duration: 80 * time.Millisecond}} view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) if mujica.TraceWaterfall(c, spans, mujica.TraceWaterfallOptions{}).Changed() { @@ -19,7 +19,7 @@ ui.Render(view, 720, 480, 1) ## 参数 -Height 默认 320 DIP;TraceSpan.ID 唯一,Name 必填;Parent 关联父跨度;Start、Duration 为非负时间;Error 标记失败。 +Height 默认 320 DIP;WaterfallSpan.ID 唯一,Name 必填;Parent 关联父跨度;Start、Duration 为非负时间;Error 标记失败。 ## 状态 diff --git a/docs/components/473-user-menu.md b/docs/components/473-user-menu.md index 8737e75..3576b7e 100644 --- a/docs/components/473-user-menu.md +++ b/docs/components/473-user-menu.md @@ -9,7 +9,7 @@ ## 最小示例 ```go -status := mujica.PresenceOnline +status := mujica.UserStatusOnline view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) if mujica.UserMenu(c, &status, mujica.UserMenuOptions{Name: "Sakiko Togawa"}).SignOut() { @@ -23,7 +23,7 @@ ui.Render(view, 400, 300, 1) | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | -| `status` | `*UserStatus` | 必填 | PresenceOnline、PresenceAway、PresenceBusy、PresenceInvisible。 | +| `status` | `*UserStatus` | 必填 | UserStatusOnline、UserStatusAway、UserStatusBusy、UserStatusInvisible。 | | `Name` | `string` | 必填 | 用户名。 | | `Email` | `string` | "" | 邮箱。 | | `Avatar` | `*ui.Bitmap` | nil | 头像。 | diff --git a/docs/prd/ext-1.md b/docs/prd/ext-1.md deleted file mode 100644 index 4726b5c..0000000 --- a/docs/prd/ext-1.md +++ /dev/null @@ -1,232 +0,0 @@ -# MujicaUI PRD 扩展一:101—280 - -版本:1.1 -日期:2026-10-05 -来源:ELY GPUI 组件清单(`Ely-GPUI-Components/gpui-components.md`)中与本库路线相符的条目。 - -本扩展沿用 PRD 第 2、3、5、6、8、9、10 节的全部规则。每行一个公开组件;子部件、变体、尺寸不单独计数。图表、行情与媒体组件以 MyGo Painter 绘制,不建图表引擎、音视频解码或富文本编辑引擎:数据、帧与解码由调用方提供。 - -## 4.12 通用图表一 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 101 | LineChart | 多序列折线;数值与时间轴;缺值断开;悬停读数;序列显隐。 | -| 102 | AreaChart | 面积图;单序列与堆叠;基线可配置;悬停读数。 | -| 103 | BarChart | 纵横向柱状;分组与堆叠;负值;柱标签;键盘逐柱读数。 | -| 104 | PieChart | 饼图与环形;扇区标签;最小角合并为“其他”;中心总计。 | -| 105 | ScatterChart | 散点;点大小映射(气泡);分组颜色;框选。 | -| 106 | RadarChart | 多维雷达;多序列;刻度环;悬停读数。 | -| 107 | HeatmapChart | 二维矩阵热力;色阶图例;单元格读数;空值样式。 | -| 108 | CalendarHeatmap | 按日贡献图;年份切换;周起始可配置;色阶与读数。 | -| 109 | Histogram | 分箱计数;箱宽与箱数可配置;累计模式。 | -| 110 | BoxPlot | 四分位、须线、离群点;多组对比。 | -| 111 | WaterfallChart | 增减累计;小计与总计柱;正负语义色。 | -| 112 | FunnelChart | 漏斗阶段;转化率;阶段读数。 | -| 113 | BulletChart | 实际值、目标值、区间带;纵横向。 | -| 114 | Sparkline | 行内迷你折线、柱形与面积;末值标记;无坐标轴。 | -| 115 | Gauge | 半圆或整圆仪表;阈值分段;数值与单位;不能只靠颜色表达区间。 | -| 116 | Treemap | 层级矩形;点击下钻与返回;标签按面积省略。 | -| 117 | Sunburst | 层级环形;下钻;路径面包屑。 | -| 118 | SankeyChart | 节点与流量带;悬停高亮路径;节点排序稳定。 | -| 119 | NetworkGraph | 节点与边;调用方给定或简单力导布局;拖动节点;缩放平移。 | -| 120 | RealtimeChart | 实时追加数据;固定窗口滚动;暂停;隐藏时停止重绘。 | - -## 4.13 通用图表二与图表部件 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 121 | ChartLegend | 序列图例;点击显隐;键盘切换;横纵布局与溢出折叠。 | -| 122 | ChartTooltip | 跟随指针或焦点的读数浮层;多序列同点读数;边缘避让。 | -| 123 | ChartCrosshair | 十字光标;轴上读数标签;吸附最近数据点。 | -| 124 | ChartAxis | 数值、时间、类别轴;刻度自动与指定;格式化函数;对数轴。 | -| 125 | ChartGrid | 网格线;主次刻度;按设备像素对齐。 | -| 126 | ChartBrush | 区间选择与缩放;拖动、调整两端;键盘移动;回调区间。 | -| 127 | ChartAnnotation | 水平线、垂直线、区间带与文字标注;拖动可选。 | -| 128 | ChartExport | 以当前主题渲染图表为 PNG 字节;尺寸与缩放可配置;不写文件。 | -| 129 | ViolinPlot | 核密度轮廓;中位数与四分位;多组。 | -| 130 | ParallelCoordinates | 多维平行轴;轴上刷选;线高亮。 | -| 131 | ChordDiagram | 关系矩阵弦图;悬停高亮;组弧标签。 | -| 132 | SparkBar | 行内迷你柱;正负值;高亮末项。 | -| 133 | TrendIndicator | 趋势箭头与变化值;正负语义;不只靠颜色。 | -| 134 | MetricCard | 指标名、值、变化、迷你图插槽;加载与错误状态。 | -| 135 | ProgressChart | 多目标完成度环或条;目标线;百分比读数。 | -| 136 | StackedPercentBar | 百分比堆叠条;分段标签;悬停读数。 | -| 137 | DualAxisChart | 左右双轴组合(柱与线);各自刻度与格式。 | -| 138 | DensityPlot | 一维核密度曲线;带宽可配置;多组叠加。 | -| 139 | QuadrantChart | 四象限散点;象限标题与中线可配置。 | -| 140 | ChartEmptyState | 图表的无数据、加载、错误与重试占位;保持图表尺寸。 | - -## 4.14 行情图表与技术分析 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 141 | CandlestickChart | K 线;涨跌色可配置(红涨或绿涨);缩放平移;万根级只绘可见区。 | -| 142 | OHLCChart | 美国线;与 K 线共用数据接口。 | -| 143 | HeikinAshiChart | 平均 K 线;由调用方 OHLC 计算;与原 K 线可切换。 | -| 144 | LineQuoteChart | 分时图;昨收基线;均价线;盘前盘后分段。 | -| 145 | VolumeChart | 成交量柱;涨跌着色;与主图共享时间轴。 | -| 146 | DepthChart | 买卖深度累计面积;中间价;悬停读数。 | -| 147 | VolumeProfile | 价格分布成交量;价值区与控制点。 | -| 148 | MultiChartLayout | 1、2、4、6 宫格多图;独立品种与周期;布局切换不丢状态。 | -| 149 | ChartSync | 多图十字光标与时间范围同步;可逐图解除。 | -| 150 | TimeRangeSelector | 1D、5D、1M、3M、YTD、1Y、5Y、自定义;键盘导航。 | -| 151 | IntervalSelector | 1m 至 1M 周期;分组;收藏周期。 | -| 152 | ChartTypeSwitcher | K 线、美国线、折线、面积、平均 K 线切换。 | -| 153 | IndicatorOverlay | 主图叠加 MA、EMA、BOLL、VWAP;参数可配;计算由调用方或内置辅助函数。 | -| 154 | IndicatorPane | 副图 MACD、RSI、KDJ、OBV;多副图堆叠与高度调整。 | -| 155 | IndicatorSelector | 指标搜索、添加、移除、参数编辑。 | -| 156 | DrawingTools | 趋势线、水平线、斐波那契、矩形、文字标注;选中、移动、删除、撤销。 | -| 157 | DrawingToolbar | 画线工具栏;当前工具;锁定与清除;键盘快捷键提示。 | -| 158 | CompareSymbol | 叠加对比品种;百分比归一;序列颜色与移除。 | -| 159 | PriceAlertLine | 价格预警线;拖动调整;触发回调;不自行通知。 | -| 160 | CandleCountdown | 当前 K 线收盘倒计时;随周期变化;隐藏时停止计时重绘。 | - -## 4.15 报价、盘口与交易 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 161 | TickerTape | 滚动行情条;悬停与聚焦暂停;减少动画时静态分页。 | -| 162 | QuoteCard | 代码、名称、现价、涨跌、迷你图插槽。 | -| 163 | PriceText | 价格精度与千分位;涨跌色;变化闪烁一次(减少动画时无闪烁)。 | -| 164 | PriceChangeBadge | 涨跌额与幅度;箭头与符号;不只靠颜色。 | -| 165 | OrderBook | 买卖档位;深度条;价格聚合档位;点击价格回传。 | -| 166 | Level2Quotes | 多档买卖与委托量;逐档变化高亮。 | -| 167 | TimeAndSales | 逐笔成交;大单标记;虚拟化;暂停跟随。 | -| 168 | DepthLadder | 价格阶梯(DOM);买卖量列;当前价居中;点击下单回调。 | -| 169 | SpreadIndicator | 买卖价差与百分比;阈值提示。 | -| 170 | BidAskBar | 买卖力量对比条;数值标签。 | -| 171 | OrderEntry | 限价、市价、止损、条件单;数量、价格、有效期;校验与预估金额;提交防重入。 | -| 172 | QuickTradeButtons | 快速买卖;数量预设;危险操作确认可配置。 | -| 173 | PositionTable | 持仓;成本、市值、浮动盈亏;平仓操作回调;稳定行 ID。 | -| 174 | OrderTable | 委托;状态筛选;撤单回调;部分成交进度。 | -| 175 | TradeHistoryTable | 成交记录;时间范围筛选;导出回调。 | -| 176 | PnLDisplay | 已实现、未实现、当日盈亏;正负语义;隐私遮蔽切换。 | -| 177 | LeverageSlider | 杠杆档位;风险提示区间;确认后生效。 | -| 178 | MarginIndicator | 保证金率;警戒与强平线;不只靠颜色。 | -| 179 | RiskMeter | 风险等级刻度与说明文字。 | -| 180 | OrderConfirmDialog | 下单摘要与风险提示;默认聚焦取消;二次确认可配置。 | - -## 4.16 市场与资产 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 181 | OptionChain | 行权价居中的认购认沽表;到期日切换;价内高亮。 | -| 182 | GreeksTable | Delta、Gamma、Theta、Vega、Rho;精度与说明。 | -| 183 | PayoffDiagram | 期权组合到期损益;盈亏平衡点;当前价标记。 | -| 184 | Watchlist | 自选分组;拖动排序;实时刷新不丢选择;列可配置。 | -| 185 | SymbolSearch | 代码与名称搜索;市场筛选;最近搜索;异步结果由调用方提供。 | -| 186 | SymbolBadge | 代码、市场、类型标签;可点击。 | -| 187 | MarketHeatmap | 板块与个股热力;面积映射市值;涨跌色阶;下钻。 | -| 188 | Screener | 条件构建(字段、比较、值);结果表;保存条件回调。 | -| 189 | MarketOverview | 指数卡片组与涨跌家数;时间戳。 | -| 190 | IndexCard | 指数现价、涨跌、迷你图。 | -| 191 | MarketStatus | 开盘、收盘、盘前、盘后、休市;下一状态时间。 | -| 192 | TradingSessionClock | 多市场交易时段时钟;时区由调用方指定。 | -| 193 | EconomicCalendar | 经济事件;重要性;预期、前值、实际;国家筛选。 | -| 194 | EarningsCalendar | 财报日程;盘前盘后;预期与实际。 | -| 195 | NewsFeed | 新闻流;来源、时间、相关代码;已读状态;虚拟化。 | -| 196 | SentimentGauge | 情绪刻度(恐惧到贪婪);数值与文字。 | -| 197 | PortfolioSummary | 总资产、当日盈亏、累计收益;分账户。 | -| 198 | AssetAllocationChart | 资产配置环形与列表;目标与偏离。 | -| 199 | PerformanceChart | 收益曲线、基准对比、最大回撤区间。 | -| 200 | CurrencyConverter | 币种选择与互换;汇率由调用方提供;精度规则。 | - -## 4.17 AI 对话:消息 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 201 | ChatContainer | 消息区、输入区、侧栏插槽;新消息自动滚底,用户上翻时停止跟随。 | -| 202 | MessageList | 虚拟化消息列表;稳定消息 ID;新消息提示;按日期分隔。 | -| 203 | MessageBubble | 用户、助手、系统、错误四种;头像与名称插槽;宽度限制。 | -| 204 | MessageHeader | 模型名、时间、状态。 | -| 205 | StreamingText | 流式追加文本;平滑出字可关闭;流式光标;减少动画时直接显示。 | -| 206 | MarkdownView | Markdown 子集渲染:标题、段落、强调、列表、引用、链接、代码、表格、分隔线;流式增量渲染不重排已完成块。 | -| 207 | CodeBlock | 语言标签、行号、基础语法着色、复制、折叠、换行切换。 | -| 208 | TableBlock | 消息内表格;横向滚动;复制为 TSV。 | -| 209 | ImageGrid | 1 至 9 张图片网格;点击打开 ImageViewer;加载与失败。 | -| 210 | FileMessage | 文件名、类型图标、大小、打开与下载回调。 | -| 211 | AudioMessage | 语音条;波形;播放进度;时长;播放由调用方驱动。 | -| 212 | LinkPreviewCard | 标题、描述、站点、缩略图;元数据由调用方提供。 | -| 213 | QuoteReply | 引用原消息摘要;点击跳转原消息。 | -| 214 | MessageActions | 复制、重新生成、编辑、赞、踩、朗读、分享;按角色显示。 | -| 215 | MessageEditor | 编辑已发送消息;保存与取消;保存后产生新分支回调。 | -| 216 | BranchNavigator | 分支切换(< 2/3 >);键盘操作。 | -| 217 | ErrorMessage | 失败原因、重试;限流倒计时(RateLimit);不吞掉原错误文本。 | -| 218 | ThinkingBlock | 可折叠思考过程;思考中动画与耗时;默认折叠可配置。 | -| 219 | ScrollToBottomButton | 离开底部时出现;未读计数;点击回到底部。 | -| 220 | TypingIndicator | 对方正在输入三点动画;减少动画时显示静态文字。 | - -## 4.18 AI 对话:输入与引用 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 221 | PromptComposer | 多行输入;Enter 发送、Shift+Enter 换行可配置;输入法组合期间不发送;附件与工具插槽。 | -| 222 | AttachmentChip | 附件名、类型、大小、上传进度、移除;失败重试。 | -| 223 | PasteImagePreview | 粘贴或拖入图片的预览缩略图;移除。 | -| 224 | DragDropOverlay | 拖入文件时的全区域遮罩与提示;类型检查回调。 | -| 225 | VoiceInputButton | 录音开始与停止;录音时长;权限失败提示;录音由调用方实现。 | -| 226 | VoiceWaveform | 输入电平波形;静音提示。 | -| 227 | SlashCommandMenu | 输入“/”弹出命令;筛选;方向键选择;Tab 补全。 | -| 228 | ContextMentionMenu | 输入“@”选择文件、文档、网页等上下文;分组;异步结果。 | -| 229 | ContextChips | 已附加上下文列表;移除;溢出折叠。 | -| 230 | ModelSelector | 模型列表;能力标签与上下文长度;不可用项说明。 | -| 231 | ModeSelector | 对话、搜索、深度研究、Agent 模式切换。 | -| 232 | ToolToggleMenu | 联网、代码执行等工具开关;需授权项标记。 | -| 233 | ParameterPanel | 温度、Top-p、最大长度等参数;范围与默认值;重置。 | -| 234 | TokenCounter | 当前输入 token 数与上限;估算值由调用方提供。 | -| 235 | ContextWindowMeter | 上下文使用率条;分段(系统、历史、附件);阈值警告。 | -| 236 | SendButton | 发送与停止两态;禁用条件;快捷键提示。 | -| 237 | CitationBadge | 行内引用编号;悬停预览来源;点击定位来源。 | -| 238 | SourceCard | 来源标题、站点、摘要、时间;打开回调。 | -| 239 | SourcesPanel | 来源列表;按引用顺序;与正文引用联动高亮。 | -| 240 | SearchProgress | 搜索中关键词与已读来源数;阶段列表;完成后折叠。 | - -## 4.19 AI 对话:会话与引导 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 241 | ConversationList | 按今天、昨天、7 天内、更早分组;置顶;虚拟化。 | -| 242 | ConversationItem | 标题、时间、重命名、删除、置顶、导出回调;行内重命名。 | -| 243 | ConversationSearch | 标题与内容搜索;匹配高亮;异步结果。 | -| 244 | ProjectList | 项目与文件夹;会话归属;拖动移入。 | -| 245 | ProjectKnowledgePanel | 项目知识文件列表;添加、移除、容量提示。 | -| 246 | WelcomeScreen | 问候、能力卡片、起始提示插槽。 | -| 247 | SuggestionChips | 起始提示与追问建议;点击填入或直接发送可配置。 | -| 248 | PromptLibrary | 提示模板分类、搜索、预览、插入;变量占位符填写。 | -| 249 | CapabilityCards | 能力说明卡片;点击示例。 | -| 250 | FeedbackForm | 赞踩后的原因选项与文字;提交与取消。 | -| 251 | DateSeparator | 消息日期分隔;今天、昨天与日期格式本地化。 | -| 252 | ThinkingIndicator | 思考中状态动画与阶段文字;减少动画时静态。 | -| 253 | StopGeneratingButton | 停止生成;生成结束后隐藏;快捷键。 | -| 254 | SystemPromptEditor | 系统提示多行编辑;字数;恢复默认;未保存标记。 | -| 255 | CostEstimator | 输入、输出 token 与费用估算;单价由调用方提供。 | -| 256 | SharedConversationView | 只读会话视图;分享者与时间;复制链接回调。 | -| 257 | ConversationExport | 导出为 Markdown、JSON、纯文本;范围选择;内容由组件生成,写文件由调用方完成。 | -| 258 | RegenerateMenu | 重新生成;换模型重试;更长、更短等变体选项。 | -| 259 | ArtifactCard | 消息内产物卡片(代码、文档、网页);打开产物面板回调。 | -| 260 | ChatEmptyState | 新会话、无搜索结果、离线三种空状态。 | - -## 4.20 AI Agent 与工具调用 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 261 | ToolCallCard | 工具名、参数、结果、耗时、状态;参数与结果折叠;JSON 高亮。 | -| 262 | ToolCallGroup | 连续调用折叠为一组;总耗时与失败数。 | -| 263 | ToolApprovalDialog | 执行前审批;参数预览;允许、拒绝;默认聚焦拒绝。 | -| 264 | PermissionPrompt | 允许一次、始终允许、拒绝;作用范围说明。 | -| 265 | AgentStepList | 执行步骤时间线;当前步;步骤展开详情;失败步重试回调。 | -| 266 | AgentPlan | 计划清单;实时勾选;新增与跳过项;进度。 | -| 267 | AgentStatus | 规划中、执行中、等待、完成、失败;不只靠颜色;状态动画可关闭。 | -| 268 | AgentProgress | 总体进度与当前动作文字;不确定模式。 | -| 269 | SubAgentTree | 子 Agent 层级;各自状态;展开查看。 | -| 270 | FileChangeCard | 文件变更预览;增删统计;接受与拒绝;打开差异。 | -| 271 | MultiFileDiffReview | 多文件审阅;逐文件接受拒绝;全部接受;进度。 | -| 272 | CommandExecutionCard | 命令、工作目录、输出(ANSI)、退出码、耗时;中止回调。 | -| 273 | ScreenshotStream | Agent 画面帧序列查看;帧由调用方提供;暂停与逐帧。 | -| 274 | ArtifactPanel | 产物面板;代码、文档、网页预览插槽;复制与导出回调。 | -| 275 | ArtifactVersionSwitcher | 产物版本切换与差异入口。 | -| 276 | SandboxStatus | 沙箱运行状态、资源与剩余时间。 | -| 277 | MCPServerList | 服务器列表;连接状态;启停与重连回调;工具数。 | -| 278 | MemoryPanel | 记忆条目列表;搜索;编辑与删除。 | -| 279 | CheckpointList | 检查点;回滚确认;当前点标记。 | -| 280 | HumanInputRequest | Agent 请求用户输入;问题、选项与自由回答;超时提示。 | diff --git a/docs/prd/ext-2.md b/docs/prd/ext-2.md deleted file mode 100644 index c14aced..0000000 --- a/docs/prd/ext-2.md +++ /dev/null @@ -1,155 +0,0 @@ -# MujicaUI PRD 扩展二:281—400 - -版本:1.1 -日期:2026-10-05 -规则同扩展一。动画组件服从 PRD 3.4:状态过渡 120 毫秒、弹层 160 毫秒,不用弹跳与装饰性循环,减少动画模式直接切换或显示静态说明。 - -## 4.21 Agent 运维与动效 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 281 | InterruptButton | 中断运行中的 Agent;确认可配置;中断中状态。 | -| 282 | TokenUsageChart | 按会话或步骤的 token 用量柱线图;输入输出分色。 | -| 283 | CostBreakdown | 按模型、工具、步骤的费用分解表与合计。 | -| 284 | TraceViewer | 调用链树与时间轴;跨度耗时;选中查看属性。 | -| 285 | EvalResultTable | 评测用例、得分、通过与失败;差异查看回调。 | -| 286 | ToolRegistryPanel | 可用工具列表;参数模式预览;启停。 | -| 287 | DotsLoader | 三点加载;尺寸与说明;减少动画时静态。 | -| 288 | BarsLoader | 条形加载;同上。 | -| 289 | PulseLoader | 单点呼吸式加载;强度受限,不发光;减少动画时静态。 | -| 290 | OrbitLoader | 菱形环绕加载;与 Spinner 同一视觉语言。 | -| 291 | WaveLoader | 波形加载;同上。 | -| 292 | Shimmer | 占位扫光;仅用于加载中;减少动画时静态填充。 | -| 293 | CountUp | 数字从旧值过渡到新值;时长与精度;减少动画时直接显示。 | -| 294 | Typewriter | 逐字显示文本;速度;完成回调;跳过;减少动画时直接显示。 | -| 295 | ShimmerText | 处理中文字扫光(如“思考中”);减少动画时静态。 | -| 296 | Stagger | 子项依次进场;间隔可配;只在出现时执行一次。 | -| 297 | ReorderTransition | 列表重排时元素移动过渡;稳定键。 | -| 298 | LayoutTransition | 布局尺寸与位置变化过渡(FLIP)。 | -| 299 | Presence | 元素进出场过渡(淡入淡出、滑动、缩放);退出完成后移除。 | -| 300 | BlinkHighlight | 数值变化单次高亮;方向色;减少动画时改为静态标记。 | - -## 4.22 版本控制 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 301 | DiffViewer | 并排与统一视图;行号;增删高亮;折叠未变更区;大文件只构建可见行。 | -| 302 | InlineDiff | 行内词级差异高亮。 | -| 303 | ThreeWayMerge | 本地、基准、远端三栏与结果栏;逐块采纳。 | -| 304 | ConflictResolver | 冲突块列表;采纳当前、传入、两者;已解决计数。 | -| 305 | ChangesList | 暂存与未暂存分组;逐文件与全部暂存;状态标记。 | -| 306 | CommitInput | 提交标题与正文;标题长度提示;提交与修订;防重入。 | -| 307 | CommitGraph | 提交图谱泳道;分支与合并线;虚拟化。 | -| 308 | CommitList | 提交列表;作者、时间、哈希、消息;选择查看。 | -| 309 | BranchSelector | 本地与远端分支搜索选择;新建分支入口。 | -| 310 | BranchList | 分支列表;当前分支、上游、领先落后数;操作回调。 | -| 311 | TagList | 标签列表;注释;跳转提交。 | -| 312 | StashList | 贮藏列表;应用、弹出、删除回调。 | -| 313 | BlameView | 逐行作者与提交;同提交行合并;悬停详情。 | -| 314 | FileHistory | 单文件提交历史与差异入口。 | -| 315 | PullRequestCard | 标题、作者、状态、检查、评审人、差异统计。 | -| 316 | ReviewComment | 行级评审评论;回复;解决与重开。 | -| 317 | DiffStat | +增 −删 统计与比例条。 | -| 318 | GitStatusBadge | M、A、D、U、R、C 状态标记;不只靠颜色。 | -| 319 | CommandBlock | 命令分块(输入、输出、退出码、耗时);折叠;复制。 | -| 320 | AnsiText | ANSI 16 色、256 色、真彩与粗斜下划线渲染;未知序列原样保留并记录。 | - -## 4.23 终端、代码与调试 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 321 | Terminal | 终端网格:ANSI 光标移动与清屏、滚动缓冲、选择复制、输入回调;PTY 由调用方提供。 | -| 322 | TerminalTabs | 多终端标签;重命名;关闭确认。 | -| 323 | TerminalSearch | 滚动缓冲内搜索;匹配数与上下跳转。 | -| 324 | CommandHistory | 命令历史列表;搜索;重放回调。 | -| 325 | LogViewer | 日志级别过滤、跟随尾部、关键字高亮、虚拟化十万行。 | -| 326 | ProcessList | 进程表;CPU、内存排序;结束进程回调与确认。 | -| 327 | CodeViewer | 只读代码视图:行号、基础语法着色、折叠、当前行、选择复制;大文件虚拟化。 | -| 328 | Minimap | 代码缩略图;可见区框;拖动跳转。 | -| 329 | FindWidget | 查找与替换;大小写、整词、正则;匹配数。 | -| 330 | SearchPanel | 多文件搜索结果;按文件分组;匹配预览;包含与排除模式。 | -| 331 | ProblemsPanel | 错误、警告、信息列表;按文件分组;跳转回调。 | -| 332 | OutputPanel | 多通道输出;清空;跟随尾部。 | -| 333 | SymbolOutline | 符号大纲树;类型图标;筛选;跟随光标。 | -| 334 | CompletionMenu | 补全候选;类型图标与详情;方向键与 Tab 接受。 | -| 335 | DebugToolbar | 继续、单步、步入、步出、重启、停止;状态禁用规则。 | -| 336 | BreakpointList | 断点列表;启用开关;条件断点编辑。 | -| 337 | CallStack | 调用栈帧;当前帧;线程分组。 | -| 338 | VariablesPanel | 变量树形展开;值修改回调;变化高亮。 | -| 339 | HexViewer | 十六进制与 ASCII 对照;偏移列;选择同步;虚拟化。 | -| 340 | Flamegraph | 火焰图;点击放大子树;搜索高亮;耗时读数。 | - -## 4.24 媒体 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 341 | ImageCropper | 裁剪框;比例锁定;旋转 90 度;输出裁剪矩形。 | -| 342 | ImageAnnotator | 矩形、箭头、文字标注;选择与删除;导出标注数据。 | -| 343 | ImageThumbnail | 缩略图;比例裁切;选中与加载失败状态。 | -| 344 | Lightbox | 全窗图片浏览;左右切换;说明文字;Escape 关闭与焦点恢复。 | -| 345 | VideoPlayer | 帧由调用方提供的播放器外壳:播放、进度、倍速、字幕、全屏、画中画回调。 | -| 346 | VideoScrubber | 时间轴拖动;缓冲段;悬停时间与缩略图插槽。 | -| 347 | VideoThumbnailStrip | 缩略帧条;当前帧;点击跳转。 | -| 348 | AudioPlayer | 播放、暂停、进度、时长;播放由调用方驱动。 | -| 349 | AudioWaveform | 波形绘制;播放进度覆盖;区间选择。 | -| 350 | AudioSpectrum | 频谱柱;数据由调用方提供;隐藏时停止重绘。 | -| 351 | VolumeControl | 音量滑块与静音;方向键。 | -| 352 | PlaybackSpeedControl | 倍速选择 0.5×—2×。 | -| 353 | MediaControls | 上一首、播放、下一首、循环、随机;键盘。 | -| 354 | Playlist | 播放列表;当前项;拖动排序;移除。 | -| 355 | SubtitleEditor | 字幕条目时间与文本编辑;时间重叠校验。 | -| 356 | ScreenRecorderControls | 开始、暂停、停止、计时;录制由调用方实现。 | -| 357 | CameraPreview | 摄像头帧预览外壳;无信号状态;镜像切换。 | -| 358 | MicLevelMeter | 麦克风电平条;峰值保持。 | -| 359 | DeviceSelector | 麦克风、摄像头、扬声器选择;设备列表由调用方提供。 | -| 360 | ImageCompare | 前后对比滑块;键盘调整。 | - -## 4.25 项目与任务管理 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 361 | KanbanBoard | 列与卡片;拖动跨列移动与排序;列 WIP 上限;键盘移动卡片。 | -| 362 | TaskList | 任务列表;分组;完成过滤;虚拟化。 | -| 363 | TaskItem | 复选、标题、优先级、截止日期、负责人;行内编辑。 | -| 364 | TaskDetailPanel | 任务详情侧栏;字段编辑;活动记录插槽。 | -| 365 | SubtaskList | 子任务;进度;增删与排序。 | -| 366 | GanttChart | 任务条、依赖线、里程碑;拖动调整日期;今日线;时间缩放。 | -| 367 | Roadmap | 季度或月度路线图;泳道;条目拖动。 | -| 368 | SprintBoard | 迭代信息、剩余天数、看板。 | -| 369 | BurndownChart | 理想线与实际线;剩余工作量。 | -| 370 | PriorityIndicator | 紧急、高、中、低、无;图形与文字。 | -| 371 | StatusSelect | 工作流状态选择;状态分组。 | -| 372 | AssigneePicker | 成员搜索与多选;头像。 | -| 373 | DueDatePicker | 截止日期快捷项(今天、明天、下周)与日历;逾期标记。 | -| 374 | LabelManager | 标签创建、编辑颜色、删除、选择。 | -| 375 | IssueCard | 编号、标题、状态、优先级、负责人、标签。 | -| 376 | IssueIdBadge | ENG-123 样式编号;复制。 | -| 377 | MilestoneProgress | 里程碑完成度与日期。 | -| 378 | WorkloadView | 成员负载条;超载提示。 | -| 379 | TimeTracker | 计时开始停止;累计时长;手动修正。 | -| 380 | PomodoroTimer | 专注与休息周期;暂停;完成回调;隐藏时停止重绘。 | - -## 4.26 文件与协作 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 381 | FileExplorer | 文件夹树与文件列表组合;导航历史;选择。 | -| 382 | FileGrid | 文件网格视图;缩略图插槽;多选。 | -| 383 | FileIcon | 按扩展名与类型的图标;文件夹开合。 | -| 384 | FilePreview | 快速预览外壳:图片、文本、代码、不支持类型说明。 | -| 385 | PathBar | 路径分段;点击跳转;长路径中间折叠。 | -| 386 | RenameInline | 行内重命名;默认选中扩展名前部分;校验回调。 | -| 387 | FileOperationProgress | 复制、移动、删除进度;取消;冲突询问。 | -| 388 | TransferQueue | 上传下载队列;暂停、继续、重试、移除。 | -| 389 | StorageUsage | 存储占用分段条与明细。 | -| 390 | RecentFiles | 最近文件;时间分组;清除。 | -| 391 | ArchiveViewer | 压缩包条目树;大小与压缩率;条目由调用方提供。 | -| 392 | PresenceAvatars | 在线协作者头像;溢出计数;跟随入口。 | -| 393 | CommentThread | 评论与回复;编辑、删除、解决;提及插槽。 | -| 394 | Reactions | 表情回应与计数;添加回应选择器。 | -| 395 | ShareDialog | 分享对象、权限、链接复制回调。 | -| 396 | PermissionSelect | 可查看、可评论、可编辑。 | -| 397 | ActivityLog | 操作记录;按日分组;过滤。 | -| 398 | VersionHistory | 版本列表;预览与恢复回调;命名版本。 | -| 399 | LiveIndicator | 实时同步状态;断线提示。 | -| 400 | RemoteCursor | 远程光标与选区标记;姓名标签。 | diff --git a/docs/prd/ext-3.md b/docs/prd/ext-3.md deleted file mode 100644 index 003c5cd..0000000 --- a/docs/prd/ext-3.md +++ /dev/null @@ -1,130 +0,0 @@ -# MujicaUI PRD 扩展三:401—500 - -版本:1.1 -日期:2026-10-05 -规则同扩展一。账户类组件只负责界面与输入,不实现认证、不保存凭据;凭据只经调用方回调传出,不进日志、无障碍值或测试报告。 - -## 4.27 即时通讯与邮件 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 401 | ChannelList | 频道与私信;未读数、静音、置顶;分组折叠。 | -| 402 | ChatMessage | 连续消息合并、时间戳、编辑标记、回复与回应插槽。 | -| 403 | UnreadDivider | 未读分隔线与计数。 | -| 404 | ReadReceipt | 已发送、已送达、已读;多人已读头像。 | -| 405 | MemberList | 成员分组(在线、离线);角色。 | -| 406 | UserProfileCard | 头像、名称、状态、角色、操作插槽。 | -| 407 | OnlineStatus | 在线、离开、忙碌、离线;形状加文字。 | -| 408 | StatusSetter | 自定义状态文字与表情;清除时间。 | -| 409 | CallControls | 静音、摄像头、共享、挂断;状态与快捷键。 | -| 410 | VideoCallGrid | 参与者宫格;发言者高亮;帧由调用方提供。 | -| 411 | IncomingCallDialog | 来电接听与拒绝;默认聚焦拒绝可配置。 | -| 412 | EmojiPicker | 分类、搜索、最近使用、肤色。 | -| 413 | PinnedMessages | 置顶消息列表;跳转;取消置顶。 | -| 414 | ThreadPanel | 话题侧栏;原消息与回复。 | -| 415 | MailList | 未读粗体、星标、附件图标;多选;虚拟化。 | -| 416 | MailReader | 邮件头、正文(纯文本与安全子集)、附件。 | -| 417 | MailComposer | 收件人、抄送、密送、主题、正文、附件;草稿保存回调。 | -| 418 | RecipientInput | 收件人标签输入;地址校验;联系人建议。 | -| 419 | QuotedText | 引用文字折叠与展开。 | -| 420 | SnoozePicker | 稍后提醒快捷项与自定义时间。 | - -## 4.28 日历与时间 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 421 | CalendarMonthView | 月视图事件格;溢出“+N”;拖动改期。 | -| 422 | CalendarWeekView | 周视图时间网格;全天行;拖动创建与调整。 | -| 423 | CalendarDayView | 日视图;重叠事件并列。 | -| 424 | AgendaView | 日程列表;按日分组。 | -| 425 | YearView | 年视图;事件密度标记。 | -| 426 | EventChip | 事件块;颜色、时间、标题;截断。 | -| 427 | EventEditor | 标题、时间、地点、参与者、提醒、重复;校验。 | -| 428 | EventPopover | 事件摘要浮层;编辑、删除入口。 | -| 429 | CurrentTimeIndicator | 当前时间线;隐藏时停止更新。 | -| 430 | RecurrenceEditor | 每日、每周、每月、每年与自定义;结束条件。 | -| 431 | AvailabilityPicker | 空闲时段选择网格;多人重叠。 | -| 432 | TimezoneSelect | 时区搜索;偏移显示;常用时区。 | -| 433 | ReminderPicker | 提前提醒选项。 | -| 434 | AttendeeList | 参与者与回复状态。 | -| 435 | WeekPicker | 周选择;周起始可配置。 | -| 436 | DurationPicker | 时长输入(时分秒);范围。 | -| 437 | CronEditor | Cron 表达式编辑与可视化;校验;下次运行时间预览。 | -| 438 | RelativeTime | 相对时间文本;自动更新频率随时间远近;隐藏时停止。 | -| 439 | Countdown | 倒计时;结束回调;格式。 | -| 440 | Stopwatch | 秒表;计次;暂停与复位。 | - -## 4.29 仪表盘与开发工具 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 441 | DashboardGrid | 卡片网格;拖动与调整尺寸;布局保存回调。 | -| 442 | StatCard | 统计卡片;值、变化、迷你图;加载与错误。 | -| 443 | AlertList | 告警列表;级别、来源、时间;确认与静默回调。 | -| 444 | IncidentCard | 事件标题、状态、影响、时间线。 | -| 445 | UptimeBar | 按日可用性条;悬停详情。 | -| 446 | ServiceStatus | 服务状态列表;正常、降级、中断。 | -| 447 | LogStream | 实时日志流;暂停;级别过滤;虚拟化。 | -| 448 | TraceWaterfall | 请求瀑布图;跨度层级与耗时。 | -| 449 | ResourceGauge | CPU、内存、磁盘仪表;阈值。 | -| 450 | SystemMonitor | 多资源实时折线组合;采样数据由调用方提供。 | -| 451 | RefreshIntervalSelector | 刷新间隔选择;手动刷新。 | -| 452 | DashboardFilterBar | 时间范围、环境、标签过滤;清除。 | -| 453 | JsonViewer | JSON 树;展开折叠;路径复制;搜索。 | -| 454 | JsonEditor | JSON 文本编辑;格式化;解析错误定位。 | -| 455 | SchemaTree | 数据库、表、列、索引树。 | -| 456 | ApiRequestBuilder | 方法、URL、参数、请求头、正文;发送回调。 | -| 457 | ResponseViewer | 状态码、耗时、大小、正文(JSON 与文本)、响应头。 | -| 458 | KeyValueInput | 键值对行编辑;启用开关;增删。 | -| 459 | EnvironmentSelector | 环境切换;变量预览。 | -| 460 | RegexTester | 正则输入、测试文本、匹配高亮与分组;语法错误提示。 | - -## 4.30 设置、账户与引导 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 461 | SettingsLayout | 设置分区导航、分节、设置行(标签、说明、控件)。 | -| 462 | SettingsSearch | 设置项搜索;匹配高亮;跳转。 | -| 463 | ThemeSelector | 系统、浅色、深色预览卡选择。 | -| 464 | AccentColorPicker | 主色候选;对比度不足的候选标记。 | -| 465 | ShortcutRecorder | 录制快捷键;冲突提示;平台修饰键显示。 | -| 466 | KeyboardShortcutsList | 快捷键表;分组;搜索。 | -| 467 | LoginForm | 账号、密码、记住我;提交中与错误;凭据仅经回调传出。 | -| 468 | OAuthButtons | 第三方登录按钮组;名称由调用方提供,不使用商标图形。 | -| 469 | TwoFactorInput | 六位验证码;粘贴;重发倒计时。 | -| 470 | ProfileEditor | 头像、名称、简介;未保存标记。 | -| 471 | AccountSwitcher | 多账户切换;添加账户入口。 | -| 472 | WorkspaceSwitcher | 工作区搜索与切换。 | -| 473 | UserMenu | 用户头像菜单;状态、设置、退出。 | -| 474 | SessionList | 登录会话;设备与位置;注销其他会话回调。 | -| 475 | ApiKeyManager | 密钥列表;创建时只显示一次;撤销确认;明文不入日志。 | -| 476 | UsageQuota | 用量与配额条;重置时间。 | -| 477 | OnboardingWizard | 多步引导;进度;跳过;完成回调。 | -| 478 | Coachmark | 功能提示锚点;步骤导航;Escape 关闭。 | -| 479 | WhatsNewDialog | 更新内容列表;不再显示。 | -| 480 | FeedbackWidget | 反馈类型、文字、截图附件插槽;提交回调。 | - -## 4.31 通用补充 · 20 个 - -| 编号 | 组件 | 首版功能 | -| --- | --- | --- | -| 481 | SplitButton | 主操作与下拉菜单;两部分独立焦点。 | -| 482 | CopyButton | 复制并短暂显示已复制;屏幕阅读器播报。 | -| 483 | HoldToConfirm | 按住确认危险操作;进度;键盘按住空格;减少动画时显示计时文字。 | -| 484 | BulkActionBar | 多选后出现的批量操作条;选中数;清除选择。 | -| 485 | ScrubInput | 拖动标签调整数值;精度与范围;输入模式。 | -| 486 | CurrencyInput | 货币金额输入;币种符号;千分位;精度。 | -| 487 | MaskedInput | 格式掩码输入(电话、卡号等);输入法安全。 | -| 488 | MentionInput | @ 提及输入;候选;提及标记为整体删除。 | -| 489 | ChoiceChips | 单选或多选筛选芯片;键盘导航。 | -| 490 | Knob | 旋钮;拖动与方向键;刻度与数值。 | -| 491 | VerticalSlider | 纵向滑块;同 Slider 规则。 | -| 492 | ColorPalette | 预设色板;选择与键盘导航;自定义色入口。 | -| 493 | SignaturePad | 手写签名画板;清除;导出路径数据。 | -| 494 | Banner | 页面顶部横幅;信息、警告、错误;操作与关闭。 | -| 495 | UndoToast | 可撤销操作的消息;倒计时;撤销回调。 | -| 496 | Masonry | 瀑布流布局;列数随宽度;稳定键。 | -| 497 | ResizablePanelGroup | 多面板可调尺寸;最小值;折叠;尺寸保存回调。 | -| 498 | DockPanel | 可停靠面板区;拖动标签到边缘停靠;布局状态由调用方保存。 | -| 499 | QRCode | 文本生成二维码;纠错级别;尺寸;离线生成。 | -| 500 | ActivityFeed | 活动流;参与者、动作、对象、时间;按日分组。 | diff --git a/example_account_test.go b/example_account_test.go index cc29618..3292bbe 100644 --- a/example_account_test.go +++ b/example_account_test.go @@ -152,7 +152,7 @@ func ExampleWorkspaceSwitcher() { } func ExampleUserMenu() { - status := mujica.PresenceOnline + status := mujica.UserStatusOnline view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) if mujica.UserMenu(c, &status, mujica.UserMenuOptions{Name: "Sakiko Togawa"}).SignOut() { diff --git a/example_devtools_test.go b/example_devtools_test.go index 937de0f..4105351 100644 --- a/example_devtools_test.go +++ b/example_devtools_test.go @@ -82,7 +82,7 @@ func ExampleLogStream() { } func ExampleTraceWaterfall() { - spans := []mujica.TraceSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 200 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 20 * time.Millisecond, Duration: 80 * time.Millisecond}} + spans := []mujica.WaterfallSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 200 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 20 * time.Millisecond, Duration: 80 * time.Millisecond}} view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) if mujica.TraceWaterfall(c, spans, mujica.TraceWaterfallOptions{}).Changed() { diff --git a/example_projects_test.go b/example_projects_test.go index 93d95d8..7446e9c 100644 --- a/example_projects_test.go +++ b/example_projects_test.go @@ -140,7 +140,7 @@ func ExampleStatusSelect() { } func ExampleAssigneePicker() { - members := []mujica.Member{{ID: "ann", Name: "Ann"}, {ID: "ben", Name: "Ben"}} + members := []mujica.ProjectMember{{ID: "ann", Name: "Ann"}, {ID: "ben", Name: "Ben"}} ids := []string{"ann"} view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) @@ -207,7 +207,7 @@ func ExampleMilestoneProgress() { } func ExampleWorkloadView() { - loads := []mujica.Workload{{Member: mujica.Member{ID: "ann", Name: "Ann"}, Assigned: 46, Capacity: 40}} + loads := []mujica.Workload{{Member: mujica.ProjectMember{ID: "ann", Name: "Ann"}, Assigned: 46, Capacity: 40}} view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) mujica.WorkloadView(c, loads, mujica.WorkloadViewOptions{}) diff --git a/examples/gallery/pages_account_code.go b/examples/gallery/pages_account_code.go index 70d4a92..612f69c 100644 --- a/examples/gallery/pages_account_code.go +++ b/examples/gallery/pages_account_code.go @@ -108,7 +108,7 @@ view := func(c *ui.Context) { mujica.WorkspaceSwitcher(c, ¤t, spaces, mujica.WorkspaceSwitcherOptions{}) } ui.Render(view, 400, 300, 1)`, - "UserMenu": `status := mujica.PresenceOnline + "UserMenu": `status := mujica.UserStatusOnline view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) if mujica.UserMenu(c, &status, mujica.UserMenuOptions{Name: "Sakiko Togawa"}).SignOut() { diff --git a/examples/gallery/pages_devtools_code.go b/examples/gallery/pages_devtools_code.go index 1e33ea5..6a733dd 100644 --- a/examples/gallery/pages_devtools_code.go +++ b/examples/gallery/pages_devtools_code.go @@ -60,7 +60,7 @@ view := func(c *ui.Context) { } ui.Render(view, 720, 480, 1)` -const devTraceWaterfallCode = `spans := []mujica.TraceSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 200 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 20 * time.Millisecond, Duration: 80 * time.Millisecond}} +const devTraceWaterfallCode = `spans := []mujica.WaterfallSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 200 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 20 * time.Millisecond, Duration: 80 * time.Millisecond}} view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) if mujica.TraceWaterfall(c, spans, mujica.TraceWaterfallOptions{}).Changed() { diff --git a/examples/gallery/pages_devtools_streams.go b/examples/gallery/pages_devtools_streams.go index 99fc0b8..0824879 100644 --- a/examples/gallery/pages_devtools_streams.go +++ b/examples/gallery/pages_devtools_streams.go @@ -38,8 +38,8 @@ func devLogStreamDemo(c *ui.Context, log func(string)) { func devTraceWaterfallDemo(c *ui.Context, log func(string)) { dtSection(c, "TraceWaterfall", func() { s := devControls(c, "448", log, false) - spans := ui.Local(c.Root(), "448", func() []mujica.TraceSpan { - return []mujica.TraceSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 320 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 40 * time.Millisecond, Duration: 90 * time.Millisecond}, {ID: "pay", Parent: "root", Name: "Charge", Service: "payments", Start: 230 * time.Millisecond, Duration: 80 * time.Millisecond, Error: true}} + spans := ui.Local(c.Root(), "448", func() []mujica.WaterfallSpan { + return []mujica.WaterfallSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 320 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 40 * time.Millisecond, Duration: 90 * time.Millisecond}, {ID: "pay", Parent: "root", Name: "Charge", Service: "payments", Start: 230 * time.Millisecond, Duration: 80 * time.Millisecond, Error: true}} }) devPreview(c, s, func() { data := *spans diff --git a/examples/gallery/pages_messaging_people.go b/examples/gallery/pages_messaging_people.go index 4f68711..355f338 100644 --- a/examples/gallery/pages_messaging_people.go +++ b/examples/gallery/pages_messaging_people.go @@ -76,7 +76,7 @@ func memberListDemo(c *ui.Context, log func(string)) { func userProfileCardDemo(c *ui.Context, log func(string)) { type state struct { - presence mujica.Presence + presence mujica.PresenceStatus actions, disabled bool } s := ui.Local(c.Root(), "406", func() state { return state{actions: true, presence: mujica.PresenceOnline} }) @@ -98,7 +98,7 @@ func userProfileCardDemo(c *ui.Context, log func(string)) { func onlineStatusDemo(c *ui.Context, log func(string)) { type state struct { - presence mujica.Presence + presence mujica.PresenceStatus hidden bool } s := ui.Local(c.Root(), "407", func() state { return state{presence: mujica.PresenceOnline} }) @@ -125,8 +125,8 @@ func statusSetterDemo(c *ui.Context, log func(string)) { ui.Text(c, fmt.Sprintf("Text: %s; expires: %v", s.status.Text, s.status.Until)) } -func messagingPresence(c *ui.Context, log func(string), value *mujica.Presence) { - items := []mujica.SelectOption[mujica.Presence]{ +func messagingPresence(c *ui.Context, log func(string), value *mujica.PresenceStatus) { + items := []mujica.SelectOption[mujica.PresenceStatus]{ {Value: mujica.PresenceOnline, Label: "Online"}, {Value: mujica.PresenceAway, Label: "Away"}, {Value: mujica.PresenceBusy, Label: "Busy"}, {Value: mujica.PresenceOffline, Label: "Offline"}, } diff --git a/examples/gallery/pages_projects_demos.go b/examples/gallery/pages_projects_demos.go index 357e967..b3a5b03 100644 --- a/examples/gallery/pages_projects_demos.go +++ b/examples/gallery/pages_projects_demos.go @@ -13,7 +13,7 @@ var projectsStatuses = []mujica.WorkflowStatus{ {ID: "doing", Name: "In progress", Group: mujica.StatusStarted}, {ID: "done", Name: "Done", Group: mujica.StatusCompleted}, } -var projectsMembers = []mujica.Member{{ID: "ann", Name: "Ann"}, {ID: "ben", Name: "Ben"}, {ID: "cy", Name: "Cy"}} +var projectsMembers = []mujica.ProjectMember{{ID: "ann", Name: "Ann"}, {ID: "ben", Name: "Ben"}, {ID: "cy", Name: "Cy"}} var projectsLabels = []mujica.TaskLabel{{ID: "ui", Name: "UI", Color: mujica.EventInfo}, {ID: "release", Name: "Release", Color: mujica.EventSuccess}} func projectsCheck(c *ui.Context, log func(string), value *bool, label string) { diff --git a/examples/gallery/pages_projects_fields.go b/examples/gallery/pages_projects_fields.go index 8c2e889..7489909 100644 --- a/examples/gallery/pages_projects_fields.go +++ b/examples/gallery/pages_projects_fields.go @@ -32,7 +32,7 @@ view := func(c *ui.Context) { } } ui.Render(view, 1000, 700, 1)`}, - {ID: "372", Name: "AssigneePicker", ZhName: "负责人选择", Category: projectsCategory, Demo: projectsDemoAssigneePicker, Keys: "Enter/Space 打开;输入搜索;下键进入选项;Space/Enter 勾选;Backspace 移除最后一项;Escape 关闭。", Code: `members := []mujica.Member{{ID: "ann", Name: "Ann"}, {ID: "ben", Name: "Ben"}} + {ID: "372", Name: "AssigneePicker", ZhName: "负责人选择", Category: projectsCategory, Demo: projectsDemoAssigneePicker, Keys: "Enter/Space 打开;输入搜索;下键进入选项;Space/Enter 勾选;Backspace 移除最后一项;Escape 关闭。", Code: `members := []mujica.ProjectMember{{ID: "ann", Name: "Ann"}, {ID: "ben", Name: "Ben"}} ids := []string{"ann"} view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) diff --git a/examples/gallery/pages_projects_time.go b/examples/gallery/pages_projects_time.go index 447455b..c361623 100644 --- a/examples/gallery/pages_projects_time.go +++ b/examples/gallery/pages_projects_time.go @@ -24,7 +24,7 @@ view := func(c *ui.Context) { mujica.MilestoneProgress(c, milestone, mujica.MilestoneProgressOptions{}) } ui.Render(view, 1000, 700, 1)`}, - {ID: "378", Name: "WorkloadView", ZhName: "成员负载", Category: projectsCategory, Demo: projectsDemoWorkloadView, Keys: "静态行通过无障碍名称读取负载与超出量;滚动查看更多成员。", Code: `loads := []mujica.Workload{{Member: mujica.Member{ID: "ann", Name: "Ann"}, Assigned: 46, Capacity: 40}} + {ID: "378", Name: "WorkloadView", ZhName: "成员负载", Category: projectsCategory, Demo: projectsDemoWorkloadView, Keys: "静态行通过无障碍名称读取负载与超出量;滚动查看更多成员。", Code: `loads := []mujica.Workload{{Member: mujica.ProjectMember{ID: "ann", Name: "Ann"}, Assigned: 46, Capacity: 40}} view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) mujica.WorkloadView(c, loads, mujica.WorkloadViewOptions{}) diff --git a/issue_card.go b/issue_card.go index 26a9d1f..da121b4 100644 --- a/issue_card.go +++ b/issue_card.go @@ -12,7 +12,7 @@ import ( // IssueCardOptions configure an IssueCard. type IssueCardOptions struct { Statuses []WorkflowStatus - Members []Member + Members []ProjectMember Labels []TaskLabel Selected bool Disabled bool diff --git a/kanban_board.go b/kanban_board.go index 86ed718..74b4303 100644 --- a/kanban_board.go +++ b/kanban_board.go @@ -22,7 +22,7 @@ type KanbanColumn struct { type KanbanBoardOptions struct { Columns []KanbanColumn Statuses []WorkflowStatus - Members []Member + Members []ProjectMember Labels []TaskLabel // Version must change when tasks change in place. Version int diff --git a/member_list.go b/member_list.go index 259b02f..3a9597b 100644 --- a/member_list.go +++ b/member_list.go @@ -14,7 +14,7 @@ import ( type Member struct { ID, Name string Image *ui.Bitmap - Presence Presence + Presence PresenceStatus // Role, as "Owner"; empty shows none. Role string } diff --git a/messaging_people_test.go b/messaging_people_test.go index a7d16b8..a4a06e3 100644 --- a/messaging_people_test.go +++ b/messaging_people_test.go @@ -29,7 +29,7 @@ func channelTester(sel *string, disabled bool, n *int) *ui.Tester { func memberTestList(n int) []Member { out := make([]Member, n) for i := range out { - out[i] = Member{ID: fmt.Sprint("m", i), Name: fmt.Sprint("Member ", i), Presence: Presence(i % 4)} + out[i] = Member{ID: fmt.Sprint("m", i), Name: fmt.Sprint("Member ", i), Presence: PresenceStatus(i % 4)} } out[0].Role = "Owner" return out diff --git a/online_status.go b/online_status.go index f15f8a7..8f5017d 100644 --- a/online_status.go +++ b/online_status.go @@ -9,17 +9,17 @@ import ( "mujicaui/theme" ) -// Presence is a person's availability. The zero value is offline. -type Presence int +// PresenceStatus is a person's availability. The zero value is offline. +type PresenceStatus int const ( - PresenceOffline Presence = iota + PresenceOffline PresenceStatus = iota PresenceOnline PresenceAway PresenceBusy ) -func (p Presence) key() string { +func (p PresenceStatus) key() string { switch p { case PresenceOffline: return "online_status.offline" @@ -30,7 +30,7 @@ func (p Presence) key() string { case PresenceBusy: return "online_status.busy" } - panic(fmt.Sprintf("mujica: unknown Presence %d", int(p))) + panic(fmt.Sprintf("mujica: unknown PresenceStatus %d", int(p))) } // OnlineStatusOptions configure an OnlineStatus. @@ -42,7 +42,7 @@ type OnlineStatusOptions struct { } // OnlineStatus shows presence as a distinct shape plus its name. -func OnlineStatus(c *ui.Context, p Presence, opts OnlineStatusOptions) *ui.Element { +func OnlineStatus(c *ui.Context, p PresenceStatus, opts OnlineStatusOptions) *ui.Element { name := msg(c, p.key()) if opts.Size < 0 { panic("mujica: OnlineStatus Size must not be negative") @@ -65,7 +65,7 @@ func OnlineStatus(c *ui.Context, p Presence, opts OnlineStatusOptions) *ui.Eleme return row } -func presencePaint(p *ui.Painter, k theme.Tokens, s Presence, r ui.Rect) { +func presencePaint(p *ui.Painter, k theme.Tokens, s PresenceStatus, r ui.Rect) { cx, cy := internal.Snap(p, r.X+r.W/2), internal.Snap(p, r.Y+r.H/2) rad := r.W / 2 var disc ui.Path diff --git a/online_status_test.go b/online_status_test.go index f77426d..d968776 100644 --- a/online_status_test.go +++ b/online_status_test.go @@ -10,7 +10,7 @@ func TestOnlineStatusTextAndShape(t *testing.T) { tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) ui.Row(c).Gap(12).Children(func() { - for _, p := range []Presence{PresenceOnline, PresenceAway, PresenceBusy, PresenceOffline} { + for _, p := range []PresenceStatus{PresenceOnline, PresenceAway, PresenceBusy, PresenceOffline} { OnlineStatus(c, p, OnlineStatusOptions{}) } OnlineStatus(c, PresenceBusy, OnlineStatusOptions{HideText: true}).Key("hidden") @@ -27,13 +27,13 @@ func TestOnlineStatusTextAndShape(t *testing.T) { if _, ok := tt.Find("Busy"); !ok { t.Fatal("hidden text not kept as the name") } - mediaPanics(t, "bad presence", func(c *ui.Context) { OnlineStatus(c, Presence(9), OnlineStatusOptions{}) }) + mediaPanics(t, "bad presence", func(c *ui.Context) { OnlineStatus(c, PresenceStatus(9), OnlineStatusOptions{}) }) } func TestOnlineStatusSnapshot(t *testing.T) { snapshot(t, "online_status", 420, 60, func(c *ui.Context) { ui.Row(c).Padding(20).Gap(16).Children(func() { - for _, p := range []Presence{PresenceOnline, PresenceAway, PresenceBusy, PresenceOffline} { + for _, p := range []PresenceStatus{PresenceOnline, PresenceAway, PresenceBusy, PresenceOffline} { OnlineStatus(c, p, OnlineStatusOptions{}) } }) diff --git a/projects.go b/projects.go index 44cc3ea..e86bea7 100644 --- a/projects.go +++ b/projects.go @@ -18,8 +18,8 @@ const ( PriorityUrgent ) -// Member is a person tasks can be assigned to. -type Member struct { +// ProjectMember is a person tasks can be assigned to. +type ProjectMember struct { ID string Name string } @@ -91,8 +91,8 @@ func projectCheckPriority(p Priority) { } // projectMember resolves a member ID or panics. -func projectMember(members []Member, id string) Member { - i := slices.IndexFunc(members, func(m Member) bool { return m.ID == id }) +func projectMember(members []ProjectMember, id string) ProjectMember { + i := slices.IndexFunc(members, func(m ProjectMember) bool { return m.ID == id }) if i < 0 { panic(fmt.Sprintf("mujica: unknown member %q", id)) } diff --git a/projects_test.go b/projects_test.go index 6b6cd6a..ef26682 100644 --- a/projects_test.go +++ b/projects_test.go @@ -15,7 +15,7 @@ var projStatuses = []WorkflowStatus{ {ID: "dropped", Name: "Dropped", Group: StatusCanceled}, } -var projMembers = []Member{{ID: "saki", Name: "Sakiko Togawa"}, {ID: "mutsu", Name: "Mutsumi Wakaba"}, +var projMembers = []ProjectMember{{ID: "saki", Name: "Sakiko Togawa"}, {ID: "mutsu", Name: "Mutsumi Wakaba"}, {ID: "uika", Name: "Uika Misumi"}, {ID: "nyamu", Name: "Nyamu Yuutenji"}, {ID: "umiri", Name: "Umiri Yahata"}} var projLabels = []TaskLabel{{ID: "bug", Name: "Bug", Color: EventDanger}, {ID: "ui", Name: "UI", Color: EventInfo}, diff --git a/task_detail_panel.go b/task_detail_panel.go index 60935fb..03c3dbb 100644 --- a/task_detail_panel.go +++ b/task_detail_panel.go @@ -14,7 +14,7 @@ import ( // TaskDetailPanelOptions configure a TaskDetailPanel. type TaskDetailPanelOptions struct { Statuses []WorkflowStatus - Members []Member + Members []ProjectMember Labels []TaskLabel // Activity builds the caller's activity log below the fields. Activity func() diff --git a/task_item.go b/task_item.go index bd54a54..ad49947 100644 --- a/task_item.go +++ b/task_item.go @@ -12,7 +12,7 @@ import ( // TaskItemOptions configure a TaskItem. type TaskItemOptions struct { - Members []Member + Members []ProjectMember // Today anchors the overdue mark; zero uses the system clock. Today Date Disabled bool diff --git a/task_list.go b/task_list.go index ea533c2..137b930 100644 --- a/task_list.go +++ b/task_list.go @@ -34,7 +34,7 @@ type TaskListOptions struct { GroupBy TaskGrouping // Statuses order the status groups; required with TaskGroupStatus. Statuses []WorkflowStatus - Members []Member + Members []ProjectMember // Version must change when tasks change in place. Version int // Today anchors overdue marks; zero uses the system clock. diff --git a/trace_waterfall_test.go b/trace_waterfall_test.go index 22b021a..f154c79 100644 --- a/trace_waterfall_test.go +++ b/trace_waterfall_test.go @@ -83,7 +83,7 @@ func TestTraceWaterfallAncestorDisabled(t *testing.T) { tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) ui.Column(c).Disabled(disabled).Children(func() { - if TraceWaterfall(c, traceSample, TraceWaterfallOptions{}).Changed() { + if TraceWaterfall(c, waterfallSample, TraceWaterfallOptions{}).Changed() { changes++ } }) diff --git a/user_menu.go b/user_menu.go index 0bef3bb..f0d363c 100644 --- a/user_menu.go +++ b/user_menu.go @@ -14,10 +14,10 @@ import ( type UserStatus int const ( - PresenceOnline UserStatus = iota - PresenceAway - PresenceBusy - PresenceInvisible + UserStatusOnline UserStatus = iota + UserStatusAway + UserStatusBusy + UserStatusInvisible ) // UserMenuOptions configure a UserMenu. Name is required. @@ -47,7 +47,7 @@ func UserMenu(c *ui.Context, status *UserStatus, opts UserMenuOptions) UserMenuR if opts.Name == "" { panic("mujica: UserMenu needs a Name") } - if *status < PresenceOnline || *status > PresenceInvisible { + if *status < UserStatusOnline || *status > UserStatusInvisible { panic(fmt.Sprintf("mujica: UserMenu status %d is unknown", *status)) } k := Tokens(c) @@ -73,7 +73,7 @@ func UserMenu(c *ui.Context, status *UserStatus, opts UserMenuOptions) UserMenuR head += " · " + opts.Email } m.Group(head, func() { - for s := PresenceOnline; s <= PresenceInvisible; s++ { + for s := UserStatusOnline; s <= UserStatusInvisible; s++ { on := s == *status if m.Check(msg(c, fmt.Sprintf("user_menu.status%d", s)), &on, PopupMenuItemOptions{}) && s != *status && !disabled { *status, r.changed = s, true @@ -97,11 +97,11 @@ func userMenuDot(p *ui.Painter, k theme.Tokens, cx, cy float32, s UserStatus) { r := ui.Rect{X: cx - d/2, Y: cy - d/2, W: d, H: d} p.Fill(ui.Rect{X: r.X - 2, Y: r.Y - 2, W: d + 4, H: d + 4}, k.Surface, d) switch s { - case PresenceOnline: + case UserStatusOnline: p.Fill(r, k.Success, d) - case PresenceAway: + case UserStatusAway: p.Stroke(r, k.Warning, d, 3) - case PresenceBusy: + case UserStatusBusy: p.Fill(r, k.Danger, d) p.Fill(ui.Rect{X: r.X + 2.5, Y: cy - 1, W: d - 5, H: 2}, k.Surface, 0) default: diff --git a/user_menu_test.go b/user_menu_test.go index 71505f6..11ccd25 100644 --- a/user_menu_test.go +++ b/user_menu_test.go @@ -7,7 +7,7 @@ import ( ) func TestUserMenuStatusSettingsSignOut(t *testing.T) { - status := PresenceOnline + status := UserStatusOnline var changes, settings, leaves int tt := accountTester(500, 400, func(c *ui.Context) { r := UserMenu(c, &status, UserMenuOptions{Name: "Sakiko Togawa", Email: "sakiko@court"}) @@ -28,7 +28,7 @@ func TestUserMenuStatusSettingsSignOut(t *testing.T) { t.Fatalf("header: %q", tt.Texts()) } _ = tt.Click("Do not disturb") - if status != PresenceBusy || changes != 1 { + if status != UserStatusBusy || changes != 1 { t.Fatalf("status %v", status) } tt.Key(0, ui.KeyTab) @@ -44,7 +44,7 @@ func TestUserMenuStatusSettingsSignOut(t *testing.T) { func TestUserMenuSnapshot(t *testing.T) { snapshot(t, "user_menu", 220, 90, func(c *ui.Context) { ui.Row(c).Padding(20).Gap(16).Children(func() { - for s := PresenceOnline; s <= PresenceInvisible; s++ { + for s := UserStatusOnline; s <= UserStatusInvisible; s++ { st := s ui.Box(c).Key(int(s)).Children(func() { UserMenu(c, &st, UserMenuOptions{Name: "Sakiko Togawa"}) }) } @@ -53,7 +53,7 @@ func TestUserMenuSnapshot(t *testing.T) { } func TestUserMenuDisabledAndInvalidStatus(t *testing.T) { - status, disabled, actions := PresenceOnline, false, 0 + status, disabled, actions := UserStatusOnline, false, 0 tt := accountTester(600, 500, func(c *ui.Context) { ui.Column(c).Disabled(disabled).Children(func() { r := UserMenu(c, &status, UserMenuOptions{Name: "User"}) @@ -68,7 +68,7 @@ func TestUserMenuDisabledAndInvalidStatus(t *testing.T) { disabled = true tt.Key(0, ui.KeyDown) tt.Key(0, ui.KeyEnter) - if status != PresenceOnline || actions != 0 || tt.HasText("Sign out") { + if status != UserStatusOnline || actions != 0 || tt.HasText("Sign out") { t.Fatal("disabled user menu acts") } status = UserStatus(99) diff --git a/user_profile_card.go b/user_profile_card.go index 1457cbe..f625c9e 100644 --- a/user_profile_card.go +++ b/user_profile_card.go @@ -11,7 +11,7 @@ type UserProfile struct { // Name is required. Name string Image *ui.Bitmap - Presence Presence + Presence PresenceStatus // Status is a custom status line, as "In a rehearsal". Status string // Role, as "Admin"; empty shows none. diff --git a/workload_view.go b/workload_view.go index ba44ac6..3719bd3 100644 --- a/workload_view.go +++ b/workload_view.go @@ -13,7 +13,7 @@ import ( // Workload is one member's assigned and available hours. type Workload struct { - Member Member + Member ProjectMember Assigned float64 Capacity float64 } diff --git a/workload_view_test.go b/workload_view_test.go index 7b22374..9f21e12 100644 --- a/workload_view_test.go +++ b/workload_view_test.go @@ -49,7 +49,7 @@ func TestWorkloadViewOverload(t *testing.T) { func TestWorkloadViewLargeAndInvalid(t *testing.T) { var loads []Workload for i := range 100000 { - loads = append(loads, Workload{Member: Member{ID: fmt.Sprint(i), Name: "M"}, Assigned: float64(i % 50), Capacity: 40}) + loads = append(loads, Workload{Member: ProjectMember{ID: fmt.Sprint(i), Name: "M"}, Assigned: float64(i % 50), Capacity: 40}) } if n := len(workloadTester(loads, WorkloadViewOptions{}).Texts()); n > 200 { t.Fatalf("built %d labels", n)