From cf809e2d52f7c72ac73edd831f9445062661c5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=85=E5=87=A4?= Date: Tue, 8 Sep 2026 01:33:25 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(icons):=20=E5=BB=BA=E7=AB=8B=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E7=95=8C=E9=9D=A2=E5=9B=BE=E6=A0=87=E5=B1=82=E4=B8=8E?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/icons/AGENTS.md | 5 +++ packages/icons/README.md | 45 +++++++++++++++++++++++++ packages/icons/package.json | 6 +++- packages/icons/src/ui/LICENSE.heroicons | 21 ++++++++++++ packages/icons/src/ui/LICENSE.lucide | 39 +++++++++++++++++++++ packages/icons/src/ui/index.ts | 30 +++++++++++++++++ 6 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 packages/icons/AGENTS.md create mode 100644 packages/icons/README.md create mode 100644 packages/icons/src/ui/LICENSE.heroicons create mode 100644 packages/icons/src/ui/LICENSE.lucide create mode 100644 packages/icons/src/ui/index.ts diff --git a/packages/icons/AGENTS.md b/packages/icons/AGENTS.md new file mode 100644 index 000000000..065152c82 --- /dev/null +++ b/packages/icons/AGENTS.md @@ -0,0 +1,5 @@ +# 图标包开发约定 + +修改本包或为 Memoh 新增、校准界面图标前,阅读 [图标层标准](README.md)。它是图标职责、命名、光学校准和迁移验收的统一契约。 + +现有品牌图标生成器会重写 `src/index.ts`,不要直接修改生成结果来建立新的手写导出。 diff --git a/packages/icons/README.md b/packages/icons/README.md new file mode 100644 index 000000000..5078c74cc --- /dev/null +++ b/packages/icons/README.md @@ -0,0 +1,45 @@ +# Memoh 图标层标准 + +本文定义图标层的目标契约。品牌图标通过 `@memohai/icon` 导出;界面图标通过 `@memohai/icon/ui` 导出,手写源码在 `src/ui/`,不受品牌生成器重写。Upload、Connector、Cloud、Computer 和 Settings 已接入该入口;其余界面图标按需迁移。 + +## 职责与依赖 + +| 层 | 负责 | 不负责 | +| --- | --- | --- | +| 图标层 | SVG 图形、默认朝向、光学中心、视觉重量、来源与许可 | 菜单行高、图标槽、文字间距、业务行为 | +| UI 组件层 | 图标槽的尺寸与居中、尺寸档位、状态颜色、交互动画 | 根据图标名称调整大小、旋转或偏移 | +| 业务层 | 选择语义图标、提供动作名称和状态 | 修正 SVG 几何或复制图标实现 | + +`@memohai/icon` 不依赖 `@felinic/ui` 或应用代码。Memoh 应用使用图标层;独立 UI 库通过图标 slot 接收业务图标,不为 Memoh 专用图标引入宿主依赖。UI 自身的通用指示图标可由其基础依赖提供,但不另行定义同一个 Memoh 语义图标。 + +## 命名与组织 + +- 界面图标按稳定语义命名,例如 `ConnectorIcon`、`UploadIcon`、`CloudIcon`,不使用 `Menu`、`Sidebar` 等调用位置前缀。 +- 品牌图标保留现有导出与品牌识别特征,不强行转成单色线框。 +- 界面图标与品牌 SVG 在包内分区维护;可以复用 Lucide,无须重新绘制每个图标。 +- 每个语义图标只有一份定义。旧名称在迁移期仅转导出同一实现,不能复制图形或保留独立校准。 +- 品牌生成器只重写 `src/index.ts`;界面图标使用独立的 `src/ui/index.ts` 与 `./ui` 子路径导出,生成品牌图标不得覆盖这个入口。 + +## 图形与光学校准 + +- 界面图标采用统一的 `0 0 24 24` 坐标系;线框基准为 2 单位笔画、圆端点和圆连接,颜色继承 `currentColor`。 +- 外部 SVG 按该坐标系适配,保留来源、版本或源文件位置和许可证。品牌图标可保留原始 viewBox 与颜色。 +- 外层尺寸由控件统一决定。视觉上偏小、偏心或朝向不合适时,在图标定义内部调整路径或 SVG 分组变换,不能把某个调用点的 16 改成 17 来补偿。 +- 不按几何包围盒机械拉满:圆形、斜线、云形的视觉面积不同,以同一尺寸下的视觉重量和光学中心验收。 +- 同一图标默认朝向在不同控件里一致。表示展开、排序或加载的状态运动由交互组件负责;它不是静态光学校准。 +- 不暴露 `offsetX`、`offsetY`、`opticalScale` 等调用层补偿参数。需要校准时修改唯一图标定义,并说明原因。 + +## 使用契约 + +- 图标输出真实 SVG,支持属性透传;图标定义不依赖 Tailwind 的旋转类才能呈现正确形状。 +- 图标外框尺寸沿用 UI 现有 12 / 14 / 16 档位,由组件尺寸 token 控制。新增档位属于组件尺寸体系,不属于单个图标特例。 +- 带文字的装饰图标对辅助技术隐藏;仅有图标的按钮由按钮提供动作名称。独立表达信息的图标必须有可访问名称。 +- 业务页面不新增针对单个图标的旋转、平移、缩放或笔画覆盖。尚未纳入图标层的现有 Lucide 使用可暂时保留;需要定制时先纳入图标层。 + +## 验收与迁移 + +新增或调整图标时,在同一对照页展示默认、12 / 14 / 16 尺寸、深浅主题以及菜单、按钮、侧栏的真实组合。检查光学中心、视觉重量、裁切、文字对齐和缩放后笔画;不能只用编译成功证明视觉一致。 + +已迁移 Upload、Connector、Cloud、Computer、Settings 的相关调用并移除未发布的 menu-icons 过渡入口;后续图标沿用包内定义和稳定导出。保持现有品牌图标导出兼容,不进行无关的全站替换。重复渲染含 SVG ID 的图标不得产生 ID 冲突。 + +完成条件是图标只有一处定义、调用处无几何补偿、生成命令不破坏导出,以及对照页的实际视觉检查。当前文档建立标准,并不代表迁移、视觉验收或自动规则检查已经完成。 diff --git a/packages/icons/package.json b/packages/icons/package.json index c9c9a4522..3bcea9f6d 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -8,7 +8,8 @@ }, "type": "module", "exports": { - ".": "./src/index.ts" + ".": "./src/index.ts", + "./ui": "./src/ui/index.ts" }, "scripts": { "generate": "tsx scripts/generate.ts" @@ -18,5 +19,8 @@ }, "devDependencies": { "tsx": "^4.21.0" + }, + "dependencies": { + "lucide-vue-next": "^0.562.0" } } diff --git a/packages/icons/src/ui/LICENSE.heroicons b/packages/icons/src/ui/LICENSE.heroicons new file mode 100644 index 000000000..d6a822907 --- /dev/null +++ b/packages/icons/src/ui/LICENSE.heroicons @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Tailwind Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/icons/src/ui/LICENSE.lucide b/packages/icons/src/ui/LICENSE.lucide new file mode 100644 index 000000000..46e696218 --- /dev/null +++ b/packages/icons/src/ui/LICENSE.lucide @@ -0,0 +1,39 @@ +ISC License + +Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2023 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2025. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--- + +The MIT License (MIT) (for portions derived from Feather) + +Copyright (c) 2013-2023 Cole Bemis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/icons/src/ui/index.ts b/packages/icons/src/ui/index.ts new file mode 100644 index 000000000..eb26df6be --- /dev/null +++ b/packages/icons/src/ui/index.ts @@ -0,0 +1,30 @@ +import { createLucideIcon, Paperclip, Monitor, Terminal, Globe, Columns2, Rows2 } from 'lucide-vue-next' + +// Heroicons cog-8-tooth; path data preserved from optimized/24/outline. +// MIT license: ./LICENSE.heroicons. Host components choose the rendered size and stroke. +export const SettingsIcon = createLucideIcon('Settings', [ + ['path', { d: 'M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 0 1 1.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.559.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.894.149c-.424.07-.764.383-.929.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 0 1-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.398.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 0 1-.12-1.45l.527-.737c.25-.35.272-.806.108-1.204-.165-.397-.506-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.108-1.204l-.526-.738a1.125 1.125 0 0 1 .12-1.45l.773-.773a1.125 1.125 0 0 1 1.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894Z' }], + ['path', { d: 'M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z' }], +]) + +// Lucide Plug 0.562.0 (ISC); canonical diagonal direction in SVG coordinates. +export const ConnectorIcon = createLucideIcon('Connector', [ + ['path', { d: 'M12 22v-5', transform: 'rotate(45 12 12)' }], + ['path', { d: 'M15 8V2', transform: 'rotate(45 12 12)' }], + ['path', { d: 'M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z', transform: 'rotate(45 12 12)' }], + ['path', { d: 'M9 8V2', transform: 'rotate(45 12 12)' }], +]) + +// Consumers choose semantics; glyph direction and geometry stay in this module. +export const UploadIcon = Paperclip +// Monitor and cloud silhouette, composed in the shared 24-unit icon frame. +export const CloudIcon = createLucideIcon('CloudComputer', [ + ['path', { d: 'M8 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v7M8 21h3M10 17v4' }], + ['path', { d: 'M16 21a4 4 0 1 1 3.77-5.33H20a2.67 2.67 0 0 1 0 5.33Z' }], +]) +export const ComputerIcon = Monitor + +export const TerminalIcon = Terminal +export const BrowserIcon = Globe +export const SplitRightIcon = Columns2 +export const SplitDownIcon = Rows2 From efb0f6821a575d883fa0de50064176f9e9615f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=85=E5=87=A4?= Date: Tue, 8 Sep 2026 01:33:27 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(web):=20=E4=BF=9D=E5=AD=98=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E3=80=81=E7=94=B5=E8=84=91=E5=85=A5=E5=8F=A3=E4=B8=8E?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=BA=93=E8=81=94=E8=B0=83=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/AGENTS.md | 2 + .../computer/bot-computer-access-dialog.vue | 36 ++++++-- .../computer/computer-access-list.vue | 16 ++-- .../web/src/components/provider-icon/icons.ts | 2 + .../searchable-select-popover/index.vue | 16 ++-- .../src/components/settings-sidebar/index.vue | 4 +- apps/web/src/components/sidebar/index.vue | 3 +- .../src/components/sidebar/session-item.vue | 10 +-- apps/web/src/i18n/locales/en.json | 11 +-- apps/web/src/i18n/locales/ja.json | 11 +-- apps/web/src/i18n/locales/zh.json | 11 +-- .../src/pages/bots/components/bot-agents.vue | 3 +- .../bots/components/model-options.test.ts | 9 +- .../pages/bots/components/model-options.vue | 26 +++--- apps/web/src/pages/bots/detail.vue | 7 +- .../dev/components/sections/SectionAtoms.vue | 2 +- .../components/sections/SectionNavigation.vue | 3 +- .../src/pages/home/components/chat-pane.vue | 11 ++- .../components/composer-connectors-menu.vue | 85 +++++++++++++++++++ .../home/components/composer-continue-on.vue | 76 +++-------------- .../dockview/header-add-actions.vue | 13 +-- .../pages/home/fixtures/connectors-preview.ts | 17 ++++ .../components/mobile-nav-sheet.vue | 3 +- apps/web/src/pages/profile/index.vue | 4 +- .../pages/providers/components/model-item.vue | 3 +- apps/web/src/pages/runtimes/index.vue | 16 +++- apps/web/src/style.css | 4 +- docs/ui-checkpoint-2026-09-08.md | 39 +++++++++ packages/ui | 2 +- patches/reka-ui@2.10.1.patch | 55 ++++++++++++ pnpm-lock.yaml | 18 ++-- pnpm-workspace.yaml | 1 + 32 files changed, 365 insertions(+), 154 deletions(-) create mode 100644 apps/web/src/pages/home/components/composer-connectors-menu.vue create mode 100644 apps/web/src/pages/home/fixtures/connectors-preview.ts create mode 100644 docs/ui-checkpoint-2026-09-08.md create mode 100644 patches/reka-ui@2.10.1.patch diff --git a/apps/web/AGENTS.md b/apps/web/AGENTS.md index 408c7caf5..0a3f56e33 100644 --- a/apps/web/AGENTS.md +++ b/apps/web/AGENTS.md @@ -402,6 +402,8 @@ const form = useForm({ ### Icon Usage +Memoh 图标层的目标契约见 [`packages/icons/README.md`](../../packages/icons/README.md)。下述 Lucide / 品牌图标划分描述现有接入方式;新增光学校准或定制图标遵循该契约,在图标层实现,不在页面或菜单调用处补偿。现有直接导入在迁移期间保留。 + - **Lucide** (primary): Direct component imports from `lucide-vue-next`. Example: `import { Plus, Search, Bot } from 'lucide-vue-next'` → ``. Used for all UI icons (actions, navigation, status indicators, etc.). - **`@memohai/icon`** (brand icons): Workspace package (`packages/icons/`) providing AI provider, search engine, and channel platform SVG icons as Vue components. Example: `import { Openai, Claude } from '@memohai/icon'`. - **Do NOT use FontAwesome** for new code. Legacy FontAwesome usage remains only in commented-out code blocks. Always use Lucide for UI icons and `@memohai/icon` for brand logos. diff --git a/apps/web/src/components/computer/bot-computer-access-dialog.vue b/apps/web/src/components/computer/bot-computer-access-dialog.vue index ed4f8ff7d..db393e240 100644 --- a/apps/web/src/components/computer/bot-computer-access-dialog.vue +++ b/apps/web/src/components/computer/bot-computer-access-dialog.vue @@ -1,6 +1,9 @@ diff --git a/apps/web/src/components/provider-icon/icons.ts b/apps/web/src/components/provider-icon/icons.ts index b7faf28b7..6a0a0da1e 100644 --- a/apps/web/src/components/provider-icon/icons.ts +++ b/apps/web/src/components/provider-icon/icons.ts @@ -1,5 +1,6 @@ import type { Component } from 'vue' import { + Slack, Anthropic, Azure, AzureColor, @@ -84,6 +85,7 @@ import { * The key is the SVG filename without extension (e.g. 'openai', 'deepseek-color'). */ export const iconMap: Record = { + 'slack': Slack, 'openai': Openai, 'anthropic': Anthropic, 'github-copilot': GithubCopilot, diff --git a/apps/web/src/components/searchable-select-popover/index.vue b/apps/web/src/components/searchable-select-popover/index.vue index 75d592d04..5be039078 100644 --- a/apps/web/src/components/searchable-select-popover/index.vue +++ b/apps/web/src/components/searchable-select-popover/index.vue @@ -44,11 +44,10 @@ > -
- + @@ -132,7 +131,7 @@ import { PopoverTrigger, PopoverContent, selectTriggerClass, - virtualListboxClass, + MenuScrollArea, } from '@felinic/ui' import { computed, nextTick, ref, useId, watch } from 'vue' import { useVirtualizer } from '@tanstack/vue-virtual' @@ -202,7 +201,8 @@ const props = withDefaults(defineProps<{ const selected = defineModel({ default: '' }) const searchTerm = ref('') const open = ref(false) -const scrollEl = ref(null) +const scrollElArea = ref | null>(null) +const scrollEl = computed(() => scrollElArea.value?.viewportElement ?? null) const selectedOption = computed(() => props.options.find((option) => option.value === selected.value), diff --git a/apps/web/src/components/settings-sidebar/index.vue b/apps/web/src/components/settings-sidebar/index.vue index 4611ed146..a314d8536 100644 --- a/apps/web/src/components/settings-sidebar/index.vue +++ b/apps/web/src/components/settings-sidebar/index.vue @@ -100,6 +100,7 @@