Răsfoiți Sursa

发布 v0.4.7:修复未读数、独立工具页面与桌面图标

Samien 1 lună în urmă
părinte
comite
47d08773c9
7 a modificat fișierele cu 68 adăugiri și 18 ștergeri
  1. 2 2
      README.md
  2. 2 2
      docs/macos-app.md
  3. 23 0
      docs/releases/v0.4.7.md
  4. 2 2
      package-lock.json
  5. 1 1
      package.json
  6. 3 3
      release.json
  7. 35 8
      tests/e2e/workspace-chat.spec.ts

+ 2 - 2
README.md

@@ -1,6 +1,6 @@
 # 夭夭 AI
 
-当前 Web 与 macOS 桌面发布版本:**v0.4.6**。桌面版提供 Apple Silicon(arm64)DMG,详见 [发布说明](docs/releases/v0.4.6.md)。
+当前 Web 与 macOS 桌面发布版本:**v0.4.7**。桌面版提供 Apple Silicon(arm64)DMG,详见 [发布说明](docs/releases/v0.4.7.md)。
 
 夭夭 AI 为网页、iOS 和安卓提供统一的 Agent、群聊、文件库及语音配置服务。Hermes 通过标准 9119 HTTP/WebSocket 接口执行任务。
 
@@ -38,7 +38,7 @@ Web 数据默认保存在 `~/.yaoyao`,可用 `YAOYAO_HOME` 指定(兼容 `HE
 
 ## macOS 桌面版
 
-在 [v0.4.6 发布页](https://github.com/Lsamien/yaoyao/releases/tag/v0.4.6) 下载 `Yaoyao-0.4.6-arm64.dmg`,将“夭夭”拖到应用程序后打开。首次启动会核对并同步配套本机 Web 服务;普通退出保留后台运行,“停止后台服务并退出”可完整停止本应用管理的后台。详细说明见 [macOS App](docs/macos-app.md)。
+在 [v0.4.7 发布页](https://github.com/Lsamien/yaoyao/releases/tag/v0.4.7) 下载 `Yaoyao-0.4.7-arm64.dmg`,将“夭夭”拖到应用程序后打开。首次启动会核对并同步配套本机 Web 服务;普通退出保留后台运行,“停止后台服务并退出”可完整停止本应用管理的后台。详细说明见 [macOS App](docs/macos-app.md)。
 
 此 DMG 使用 Apple Development 开发签名,未做 Apple 公证。
 

+ 2 - 2
docs/macos-app.md

@@ -40,7 +40,7 @@ npm run desktop:build
 npm run desktop:test
 npm run desktop:pack
 codesign --verify --deep --strict desktop-release/mac-arm64/夭夭.app
-hdiutil verify desktop-release/Yaoyao-0.4.6-arm64.dmg
+hdiutil verify desktop-release/Yaoyao-0.4.7-arm64.dmg
 ```
 
 开发 App 的 6 项测试通过:真实内置服务启动、账号与 Runner 连接、关闭窗口保持运行、服务崩溃恢复、外部客户端不误停服务、退出清理、加密配置再次启动、偏好保存及手动重新打开、版本不符拒绝、钥匙串失败保护。另有更新器 14 项定向测试通过,包含 App 禁用源码更新及回退。
@@ -58,4 +58,4 @@ Electron 初始化时设置其钥匙串项目名称([v43.4.0 源码](https://g
 
 最终实际签名 App 的完整 Runner 导入、加密保存、服务崩溃恢复、窗口关闭保持连接、退出清理和再次打开自动连接均已通过;另有独立的核心服务生命周期测试通过。新加密助手在实际签名 App 中验证所属路径和签名团队。安装包经挂载检查,开发专用助手没有进入分发包。
 
-v0.4.6 同时发布 Web 与 macOS Apple Silicon DMG,见 [发布说明](releases/v0.4.6.md)。正式 App 首次启动按上文同步规则升级较旧的本机 Web;已有更新版本保留。独立预览应指定独立 `HERMES_YAOYAO_HOME` 和 `HERMES_YAOYAO_DESKTOP_PORT`。
+v0.4.7 同时发布 Web 与 macOS Apple Silicon DMG,见 [发布说明](releases/v0.4.7.md)。正式 App 首次启动按上文同步规则升级较旧的本机 Web;已有更新版本保留。独立预览应指定独立 `HERMES_YAOYAO_HOME` 和 `HERMES_YAOYAO_DESKTOP_PORT`。

+ 23 - 0
docs/releases/v0.4.7.md

@@ -0,0 +1,23 @@
+# 夭夭 AI v0.4.7
+
+本次更新 Web、macOS Apple Silicon 桌面版和 Android 1.3(163)。
+
+## 更新内容
+
+- 普通聊天的未读数只统计最终助手回复,不再把用户消息、思考过程或中间片段计入;Web 与 Android 统一采用最终回复未读数,并兼容旧服务返回值。
+- Bot 模式“已连接应用”改为独立大面板,汇总应用目录、连接账号和 MCP;自动化改为独立日程页,支持 Bot 筛选、日历、任务编辑与运行日志。
+- “关于”使用独立对话框,可从常规工作区底部菜单直接打开;上述功能不再混入设置中心。
+- macOS 应用图标使用纯白圆角底板,修复透明背景显示问题;保留原有机器人头像与个性化配置。
+
+## 下载
+
+- `Yaoyao-0.4.7-arm64.dmg`:macOS Apple Silicon,Apple Development 开发签名,未做 Apple 公证。
+- `Yaoyao-1.3-163-android.apk`:Android 12 及以上,包含未读数修复,沿用原正式发布密钥。
+- `Yaoyao-0.4.7-source.tar.gz`:Web 与桌面端源码。
+- `SHA256SUMS.txt`:发布附件的 SHA-256 校验文件。
+
+## 升级
+
+官方发布地址为 https://github.com/Lsamien/yaoyao/releases/tag/v0.4.7 。App 首次启动继续只同步较旧的本机 Web,保留已有更新的独立 Web;数据目录仍为 `~/.yaoyao`。
+
+升级前备份完整数据目录及加密密钥。源码或 Docker 部署更新版本后重新构建并重启服务,保留原数据目录或命名卷。

+ 2 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "yaoyao",
-  "version": "0.4.6",
+  "version": "0.4.7",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "yaoyao",
-      "version": "0.4.6",
+      "version": "0.4.7",
       "license": "Apache-2.0",
       "dependencies": {
         "@koa/bodyparser": "^5.0.0",

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "yaoyao",
-  "version": "0.4.6",
+  "version": "0.4.7",
   "private": true,
   "license": "Apache-2.0",
   "description": "夭夭 AI:聊天、多 Agent 团队、文件库与电脑工作空间",

+ 3 - 3
release.json

@@ -1,6 +1,6 @@
 {
   "schemaVersion": 1,
-  "releaseVersion": "0.4.6",
-  "webVersion": "0.4.6",
-  "gitTag": "v0.4.6"
+  "releaseVersion": "0.4.7",
+  "webVersion": "0.4.7",
+  "gitTag": "v0.4.7"
 }

+ 35 - 8
tests/e2e/workspace-chat.spec.ts

@@ -147,8 +147,34 @@ test('created roles and editable teams share a durable chat list without plugin
   await expect(rail.locator('.sidebar-account-switcher__main')).toContainText('fixture')
   await expect(rail.locator('.sidebar-account-switcher__main .account-initial-avatar')).toHaveText('F')
   await rail.getByRole('button', { name: '设置与模式', exact: true }).click()
-  await expect(page.getByRole('menu', { name: '设置与模式' }).getByRole('menuitem')).toHaveText(['进入设置', '进入聊天模式'])
-  await page.getByRole('menuitem', { name: '进入设置', exact: true }).click()
+  await expect(page.getByRole('menu', { name: '账号菜单' }).getByRole('menuitem')).toHaveText(['设置', '关于', '帮助', '进入聊天模式'])
+  await page.getByRole('menuitem', { name: '关于', exact: true }).click()
+  const about = page.getByRole('dialog', { name: '关于夭夭 AI', exact: true })
+  await expect(about).toBeVisible()
+  await expect(about.locator('.about-version')).toHaveText(/^版本 \d+\.\d+\.\d+$/)
+  await expect(page.getByRole('dialog', { name: '设置中心', exact: true })).toHaveCount(0)
+  await page.screenshot({ path: testInfo.outputPath('independent-about.png') })
+  await page.keyboard.press('Escape')
+  await expect(about).toHaveCount(0)
+  await rail.getByRole('button', { name: '工具', exact: true }).click()
+  await page.getByRole('menuitem', { name: '已连接应用', exact: true }).click()
+  const apps = page.getByRole('dialog', { name: '已连接应用', exact: true })
+  await expect(apps).toBeVisible()
+  await expect(apps.getByRole('tab', { name: 'MCP 服务', exact: true })).toBeVisible()
+  await expect(page.locator('.settings-sidebar')).toHaveCount(0)
+  await page.screenshot({ path: testInfo.outputPath('independent-apps.png') })
+  await apps.getByRole('button', { name: '关闭已连接应用', exact: true }).click()
+  await rail.getByRole('button', { name: '工具', exact: true }).click()
+  await page.getByRole('menuitem', { name: '自动化', exact: true }).click()
+  await expect(page).toHaveURL(/\/conversations\/automations(?:\?.*)?$/)
+  await expect(page.getByRole('main', { name: '自动化工作区' })).toBeVisible()
+  await expect(page.locator('.schedule-day')).toHaveCount(7)
+  await page.getByRole('tab', { name: '运行日志', exact: true }).click()
+  await page.screenshot({ path: testInfo.outputPath('independent-automations.png') })
+  await page.getByRole('button', { name: '返回聊天', exact: true }).click()
+  await expect(page).toHaveURL(/\/conversations$/)
+  await rail.getByRole('button', { name: '设置与模式', exact: true }).click()
+  await page.getByRole('menuitem', { name: '设置', exact: true }).click()
   const settings = page.getByRole('dialog', { name: '设置中心', exact: true })
   await expect(settings.locator('.settings-account-summary')).toContainText('fixture')
   await expect(settings.locator('.settings-account-summary .account-initial-avatar')).toHaveText('F')
@@ -165,6 +191,7 @@ test('created roles and editable teams share a durable chat list without plugin
   await expect(settings.getByRole('button', { name: '保存更改', exact: true })).toBeDisabled()
   await settings.getByRole('button', { name: '关闭设置中心', exact: true }).click()
   await rail.locator('.sidebar-account-switcher__main').click()
+  await page.getByRole('menuitem', {name:'设置',exact:true}).click()
   await settings.getByRole('button', { name: '身份与头像', exact: true }).click()
   await expect(settings.getByRole('button', { name: '造型:星星', exact: true })).toHaveAttribute('aria-pressed', 'true')
   await expect(settings.getByRole('button', { name: '使用颜色 #1488ff', exact: true })).toHaveAttribute('aria-pressed', 'true')
@@ -183,10 +210,10 @@ test('created roles and editable teams share a durable chat list without plugin
   await expect(rail).toHaveCSS('width', '264px')
   await expect(rail.getByRole('button', { name: '聊天', exact: true })).toHaveCount(0)
   await rail.getByRole('button', { name: '设置与模式', exact: true }).click()
-  const settingsMenu = page.getByRole('menu', { name: '设置与模式', exact: true })
-  await expect(settingsMenu.getByRole('menuitem')).toHaveText(['进入设置', '进入 Bot 模式'])
+  const settingsMenu = page.getByRole('menu', { name: '账号菜单', exact: true })
+  await expect(settingsMenu.getByRole('menuitem')).toHaveText(['设置', '关于', '帮助', '进入 Bot 模式'])
   await page.screenshot({ path: testInfo.outputPath('chat-settings-mode-menu.png') })
-  await settingsMenu.getByRole('menuitem', { name: '进入设置', exact: true }).click()
+  await settingsMenu.getByRole('menuitem', { name: '设置', exact: true }).click()
   await expect(page.getByRole('dialog', { name: '设置中心', exact: true })).toBeVisible()
   await page.getByRole('button', { name: '关闭设置中心', exact: true }).click()
   await expect(rail.getByRole('button', { name: '设置与模式', exact: true })).toBeFocused()
@@ -421,7 +448,7 @@ test('created roles and editable teams share a durable chat list without plugin
   await expect(page.getByRole('tab', { name: '未归档', exact: true })).toBeVisible()
   await page.getByRole('button', { name: '关闭搜索', exact: true }).click()
   await rail.locator('.sidebar-account-switcher__main').click()
-  await page.getByRole('button', { name: '进入聊天模式', exact: true }).click()
+  await page.getByRole('menuitem', { name: '进入聊天模式', exact: true }).click()
   await expect(page).toHaveURL(/\/chat$/)
   await page.goto('/')
   await expect(page).toHaveURL(/\/chat$/)
@@ -438,7 +465,7 @@ test('created roles and editable teams share a durable chat list without plugin
 test('subaccount allocation and Bot-only navigation work through the browser', async ({ page, browser }, testInfo) => {
   await login(page)
   await page.locator('.desktop-sidebar').getByRole('button', { name: '设置与模式', exact: true }).click()
-  await page.getByRole('menuitem', { name: '进入设置', exact: true }).click()
+  await page.getByRole('menuitem', { name: '设置', exact: true }).click()
   const settings = page.getByRole('dialog', { name: '设置中心', exact: true })
   await settings.getByRole('button', { name: '用户与权限', exact: true }).click()
   const username = `child-${Date.now()}`
@@ -462,7 +489,7 @@ test('subaccount allocation and Bot-only navigation work through the browser', a
   await expect(child).toHaveURL(/\/conversations$/)
   await expect(child.getByRole('heading', { name: '还没有聊天' })).toBeVisible()
   await child.locator('.desktop-sidebar').getByRole('button', { name: '设置与模式', exact: true }).click()
-  await expect(child.getByRole('menu', { name: '设置与模式' }).getByRole('menuitem')).toHaveText(['进入设置'])
+  await expect(child.getByRole('menu', { name: '账号菜单' }).getByRole('menuitem')).toHaveText(['设置', '关于', '帮助'])
   await child.keyboard.press('Escape')
   await openCreate(child, '新建 Bot')
   const dialog = child.getByRole('dialog')