瀏覽代碼

发布 v0.3.14:恢复 Bot 账号栏并调整侧栏宽度与字号

LSamien 1 月之前
父節點
當前提交
2ac6dee52e

+ 1 - 1
README.md

@@ -1,6 +1,6 @@
 # 夭夭 Web
 
-当前发布版本:**v0.3.13**,配套 iOS **1.3(175)**。详见 [发布说明](docs/releases/v0.3.13.md)。
+当前发布版本:**v0.3.14**,配套 iOS **1.3(175)**。详见 [发布说明](docs/releases/v0.3.14.md)。
 
 夭夭 Web 为网页和 iOS 提供统一的 Agent、群聊、文件库及语音配置服务。Hermes 通过标准 9119 HTTP/WebSocket 接口执行任务;无需安装 Yaoyao 插件。
 

+ 16 - 0
docs/releases/v0.3.14.md

@@ -0,0 +1,16 @@
+# v0.3.14
+
+按浏览器批注调整 Web 的 Bot 模式侧栏。
+
+- 恢复与普通聊天同样的底部栏,Bot 模式显示当前登录用户名与账号头像。
+- 底部齿轮提供“进入设置”和“进入聊天模式”,账号按钮打开账号设置。
+- 左上角恢复夭夭 Logo,搜索与加号保持原位置关系。
+- 桌面 Bot 侧栏从 300px 加宽到 360px。
+- 列表名称、消息预览和时间各缩小 2px,分别为 15px、13px、10px。
+
+本次为 Web 更新,继续配套 iOS 1.3(175)。
+
+## 验证
+
+- 86 个测试文件、500 项测试,以及类型检查、生产构建通过。
+- 浏览器确认 Logo、底部账号栏、设置菜单、侧栏宽度与字号,并验证手机网页的账号设置和模式切换。

+ 2 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "hermes-yaoyao",
-  "version": "0.3.13",
+  "version": "0.3.14",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "hermes-yaoyao",
-      "version": "0.3.13",
+      "version": "0.3.14",
       "license": "BSL-1.1",
       "dependencies": {
         "@koa/bodyparser": "^5.0.0",

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "hermes-yaoyao",
-  "version": "0.3.13",
+  "version": "0.3.14",
   "private": true,
   "license": "BSL-1.1",
   "description": "夭夭 Web:直连 Hermes 9119 的聊天、团队、文件库与产物工作台",

+ 3 - 3
release.json

@@ -1,6 +1,6 @@
 {
   "schemaVersion": 1,
-  "releaseVersion": "0.3.13",
-  "webVersion": "0.3.13",
-  "gitTag": "v0.3.13"
+  "releaseVersion": "0.3.14",
+  "webVersion": "0.3.14",
+  "gitTag": "v0.3.14"
 }

+ 8 - 9
src/client/components/app/WorkspaceShell.vue

@@ -197,7 +197,6 @@ function closeSettingsMenu() {
   void nextTick(() => { if (settingsTrigger?.isConnected) settingsTrigger.focus() })
 }
 async function openSettingsMenu(event: MouseEvent) {
-  if (applicationWorkspace.value) { openSettings('account-security', event); return }
   if (settingsMenuOpen.value) { closeSettingsMenu(); return }
   settingsTrigger = event.currentTarget as HTMLButtonElement
   const rect = settingsTrigger.getBoundingClientRect()
@@ -213,7 +212,7 @@ function chooseSettingsAction(action: 'settings' | 'bots') {
   if (action === 'bots') { switchInterfaceMode(); return }
   settingsReturnFocus.value = settingsTrigger?.closest('.mobile-drawer')
     ? mobileNavigationTrigger.value : settingsTrigger ?? undefined
-  openSettings('agent-identity')
+  openSettings(applicationWorkspace.value ? 'account-security' : 'agent-identity')
 }
 function switchInterfaceMode() {
   settingsOpen.value = false
@@ -377,7 +376,7 @@ onBeforeUnmount(() => {
       :inert="mobileDrawerOpen"
     >
       <div v-if="applicationWorkspace" class="bot-list-header">
-        <button class="bot-account-trigger" type="button" aria-label="设置" @click="openSettings('account-security', $event)"><AgentAvatar :name="userName" :size="40" /></button>
+        <button class="bot-logo-trigger sidebar-brand" type="button" aria-label="返回 Bot 列表" title="夭夭 Web" @click="navigate('/conversations')"><BrandMark :size="32" :label="false" compact bare /></button>
         <span class="bot-list-header__spacer" />
         <button class="bot-list-toolbar-button sidebar-search-trigger" type="button" aria-label="搜索" :aria-expanded="sidebarSearchOpen" @click="openSidebarSearch(desktopSidebarContext)"><AppIcon name="search" :size="21" /></button>
         <button class="bot-list-toolbar-button sidebar-create-trigger" type="button" aria-label="新建" aria-haspopup="menu" :aria-expanded="createMenuOpen" @click="openCreateMenu"><AppIcon name="plus" :size="23" /></button>
@@ -453,7 +452,7 @@ onBeforeUnmount(() => {
         </div>
       </section>
 
-      <div v-if="!applicationWorkspace" class="sidebar-footer">
+      <div class="sidebar-footer">
         <div class="sidebar-account-switcher">
           <button class="sidebar-account-switcher__main" type="button" :title="applicationWorkspace ? '当前账号' : `切换 Agent:${profileTitle(activeProfile)}`" :aria-haspopup="applicationWorkspace ? undefined : 'listbox'" :aria-expanded="applicationWorkspace ? undefined : profileMenuOpen" @click="applicationWorkspace ? openSettings('account-security', $event) : toggleProfileMenu($event)">
             <AgentAvatar :name="applicationWorkspace ? userName : profileTitle(activeProfile)" :avatar="applicationWorkspace ? '' : activeProfile?.agentAvatar || ''" :size="30" />
@@ -463,7 +462,7 @@ onBeforeUnmount(() => {
             </span>
             <AppIcon v-if="!applicationWorkspace" class="sidebar-account-switcher__chevron" name="chevron-down" :size="14" />
           </button>
-          <button class="sidebar-settings-trigger" type="button" :title="applicationWorkspace ? '设置中心' : '设置与模式'" :aria-label="applicationWorkspace ? '打开设置中心' : '设置与模式'" :aria-haspopup="applicationWorkspace ? undefined : 'menu'" :aria-expanded="applicationWorkspace ? undefined : settingsMenuOpen" @click="openSettingsMenu">
+          <button class="sidebar-settings-trigger" type="button" title="设置与模式" aria-label="设置与模式" aria-haspopup="menu" :aria-expanded="settingsMenuOpen" @click="openSettingsMenu">
             <AppIcon name="settings" :size="17" />
           </button>
           <Transition name="menu-fade">
@@ -569,7 +568,7 @@ onBeforeUnmount(() => {
             </span>
             <AppIcon v-if="!applicationWorkspace" class="sidebar-account-switcher__chevron" name="chevron-down" :size="14" />
           </button>
-          <button class="sidebar-settings-trigger" type="button" :title="applicationWorkspace ? '设置中心' : '设置与模式'" :aria-label="applicationWorkspace ? '打开设置中心' : '设置与模式'" :aria-haspopup="applicationWorkspace ? undefined : 'menu'" :aria-expanded="applicationWorkspace ? undefined : settingsMenuOpen" @click="openSettingsMenu">
+          <button class="sidebar-settings-trigger" type="button" title="设置与模式" aria-label="设置与模式" aria-haspopup="menu" :aria-expanded="settingsMenuOpen" @click="openSettingsMenu">
             <AppIcon name="settings" :size="17" />
           </button>
           <Transition name="menu-fade">
@@ -637,7 +636,7 @@ onBeforeUnmount(() => {
       <div v-if="settingsMenuOpen" class="workspace-create-dismiss" @pointerdown.self="closeSettingsMenu" @keydown.esc.prevent.stop="closeSettingsMenu">
         <div ref="settingsMenu" class="workspace-create-menu workspace-settings-menu" :style="settingsMenuPosition" role="menu" aria-label="设置与模式" @keydown="actionMenuKeydown">
           <button type="button" role="menuitem" @click="chooseSettingsAction('settings')"><AppIcon name="settings" :size="17" />进入设置</button>
-          <button type="button" role="menuitem" @click="chooseSettingsAction('bots')"><AppIcon name="users" :size="17" />进入 Bot 模式</button>
+          <button type="button" role="menuitem" @click="chooseSettingsAction('bots')"><AppIcon :name="applicationWorkspace ? 'chat' : 'users'" :size="17" />{{ applicationWorkspace ? '进入聊天模式' : '进入 Bot 模式' }}</button>
         </div>
       </div>
       <div v-if="createMenuOpen" class="workspace-create-dismiss" @pointerdown.self="closeCreateMenu" @keydown.esc.prevent.stop="closeCreateMenu">
@@ -662,7 +661,7 @@ onBeforeUnmount(() => {
   transition: grid-template-columns 180ms var(--ease-out);
 }
 
-.workspace-shell--conversations { grid-template-columns: 300px minmax(0, 1fr) auto; }
+.workspace-shell--conversations { grid-template-columns: 360px minmax(0, 1fr) auto; }
 .workspace-shell--collapsed { grid-template-columns: 68px minmax(0, 1fr) auto; }
 .mobile-header, .mobile-drawer, .drawer-scrim { display: none; }
 
@@ -880,7 +879,7 @@ onBeforeUnmount(() => {
 
 .bot-list-header{display:flex;align-items:center;gap:10px;padding:20px 20px 12px;min-height:74px}
 .bot-list-header__spacer{flex:1}
-.bot-account-trigger{display:grid;place-items:center;padding:0;border:0;background:transparent;cursor:pointer;border-radius:50%}
+.bot-logo-trigger{display:grid;place-items:center;padding:0;border:0;background:transparent;cursor:pointer;border-radius:50%}
 .bot-list-header .bot-list-toolbar-button{position:static;display:grid;place-items:center;width:42px;height:42px;min-height:42px;flex:0 0 42px;margin:0;padding:0;border:1px solid var(--line);border-radius:50%;background:var(--surface);color:var(--text-primary)}
 .workspace-shell--conversations .sidebar-context{margin-top:0}
 @media(max-width:900px){

+ 3 - 3
src/client/components/workspace/ConversationList.vue

@@ -42,8 +42,8 @@ function action(kind: 'pin' | 'archive') {
 .conversation-list :deep(.sidebar-list){padding:0 12px 18px;scrollbar-gutter:auto}
 .conversation-list :deep(.sidebar-item){min-height:76px;padding:14px 8px;gap:16px;border-radius:12px}
 .conversation-list :deep(.sidebar-item__icon){width:44px;height:44px;flex-basis:44px}
-.conversation-list :deep(.sidebar-item__row strong){font-size:17px;font-weight:600;line-height:24px}
-.conversation-list :deep(.sidebar-item__row--secondary){font-size:15px;line-height:21px;margin-top:4px}
-.conversation-list :deep(.sidebar-item__row small){font-size:12px;color:var(--text-muted)}
+.conversation-list :deep(.sidebar-item__row strong){font-size:15px;font-weight:600;line-height:22px}
+.conversation-list :deep(.sidebar-item__row--secondary){font-size:13px;line-height:19px;margin-top:4px}
+.conversation-list :deep(.sidebar-item__row small){font-size:10px;color:var(--text-muted)}
 .conversation-menu-dismiss{position:fixed;inset:0;z-index:200}.conversation-actions{position:absolute;display:grid;min-width:155px;padding:5px;border:1px solid var(--line);border-radius:10px;background:var(--surface-raised);box-shadow:var(--shadow-float)}.conversation-actions button{padding:9px 12px;border:0;border-radius:7px;background:transparent;color:var(--text-primary);text-align:left;cursor:pointer;font-size:12px}.conversation-actions button:hover{background:var(--surface-hover)}
 </style>

+ 5 - 2
tests/client/workspaceShell.test.ts

@@ -149,10 +149,13 @@ it('uses the reference Bot list header and keeps mode changes in settings', asyn
   const rail = wrapper.get('.desktop-sidebar')
   expect(rail.find('.sidebar-feature-nav').exists()).toBe(false)
   expect(rail.find('.sidebar-primary-action').exists()).toBe(false)
-  expect(rail.find('.sidebar-footer').exists()).toBe(false)
+  expect(rail.find('.sidebar-footer').exists()).toBe(true)
+  expect(rail.get('.sidebar-account-switcher__main').text()).toContain('owner')
+  expect(rail.find('.bot-logo-trigger').exists()).toBe(true)
+  expect(rail.find('.bot-account-trigger').exists()).toBe(false)
   expect(rail.find('.sidebar-collapse').exists()).toBe(false)
   expect(rail.find('.sidebar-context__heading').exists()).toBe(false)
-  await rail.get('.bot-account-trigger').trigger('click')
+  await rail.get('.sidebar-account-switcher__main').trigger('click')
   expect(wrapper.get('[data-testid="settings-center"]').attributes('data-page')).toBe('account-security')
   await wrapper.get('[data-testid="close-settings"]').trigger('click')
   await rail.get('.sidebar-create-trigger').trigger('click')

+ 13 - 4
tests/e2e/workspace-chat.spec.ts

@@ -35,8 +35,14 @@ test('created roles and editable teams share a durable chat list without plugin
   const rail = page.locator('.desktop-sidebar')
   await expect(rail.locator('.sidebar-feature-nav')).toHaveCount(0)
   await expect(rail.locator('.sidebar-primary-action')).toHaveCount(0)
-  await expect(rail).toHaveCSS('width', '300px')
+  await expect(rail).toHaveCSS('width', '360px')
   await expect(rail.locator('.sidebar-context__heading')).toHaveCount(0)
+  await expect(rail.locator('.bot-logo-trigger .brand-mark')).toBeVisible()
+  await expect(rail.locator('.sidebar-footer')).toBeVisible()
+  await expect(rail.locator('.sidebar-account-switcher__main')).toContainText('fixture')
+  await rail.getByRole('button', { name: '设置与模式', exact: true }).click()
+  await expect(page.getByRole('menu', { name: '设置与模式' }).getByRole('menuitem')).toHaveText(['进入设置', '进入聊天模式'])
+  await page.keyboard.press('Escape')
   await expect(rail.getByRole('checkbox', { name: '显示已归档' })).toHaveCount(0)
   const plus = rail.getByRole('button', { name: '新建', exact: true })
   await expect(rail.locator('.sidebar-collapse')).toHaveCount(0)
@@ -60,12 +66,15 @@ test('created roles and editable teams share a durable chat list without plugin
   await rail.getByRole('button', { name: '设置与模式', exact: true }).click()
   await settingsMenu.getByRole('menuitem', { name: '进入 Bot 模式', exact: true }).click()
   await expect(page).toHaveURL(/\/conversations$/)
-  await expect(rail).toHaveCSS('width', '300px')
+  await expect(rail).toHaveCSS('width', '360px')
   await page.addInitScript(() => { Object.defineProperty(crypto, 'randomUUID', { value: undefined, configurable: true }) })
   await page.reload()
   await createAgent(page, '产品经理')
   await expect(rail.getByText('聊天', { exact: true })).toHaveCount(0)
   await expect(rail.getByText('聊天列表', { exact: true })).toHaveCount(0)
+  await expect(rail.locator('.sidebar-item__row strong').first()).toHaveCSS('font-size', '15px')
+  await expect(rail.locator('.sidebar-item__row--secondary').first()).toHaveCSS('font-size', '13px')
+  await expect(rail.locator('.sidebar-item__row small').first()).toHaveCSS('font-size', '10px')
   await page.getByPlaceholder('输入消息,Enter 发送,Shift + Enter 换行').fill('请说明角色职责')
   await page.getByRole('button', { name: '发送消息', exact: true }).click()
   await expect(page.locator('.message--assistant')).toContainText('我是产品经理')
@@ -115,7 +124,7 @@ test('created roles and editable teams share a durable chat list without plugin
       .toBe(await direct.locator('.agent-avatar__body > :first-child').evaluate(el => el.tagName))
   }
   await expect(page.locator('.desktop-sidebar')).not.toContainText('话题')
-  await expect(page.locator('.desktop-sidebar .bot-account-trigger')).not.toContainText(
+  await expect(page.locator('.desktop-sidebar .sidebar-account-switcher__main')).not.toContainText(
     '通用助手',
   )
   await page.getByRole('button', { name: '聊天设置', exact: true }).click()
@@ -238,7 +247,7 @@ test('created roles and editable teams share a durable chat list without plugin
   await expect(page.getByRole('dialog', { name: '搜索聊天' })).toBeVisible()
   await expect(page.getByRole('tab', { name: '未归档', exact: true })).toBeVisible()
   await page.getByRole('button', { name: '关闭搜索', exact: true }).click()
-  await rail.getByRole('button', { name: '设置', exact: true }).click()
+  await rail.locator('.sidebar-account-switcher__main').click()
   await page.getByRole('button', { name: '进入聊天模式', exact: true }).click()
   await expect(page).toHaveURL(/\/chat$/)
   await page.goto('/')