fix: keep browser overlays and knowledge controls usable

Native browser pages could cover application dialogs and overlapping menus
or notifications. Suspend only the viewport while covered and restore the
same active page afterward, including nested dialogs and terminal prompts.

Keep knowledge source controls at their full height, constrain long library
cards and titles, and update automatic names when switching remote targets
without overwriting edited or saved names. Restore packaged release-note
loading now that the history exceeds its previous size limit.

Add focused regressions and real Electron UI evidence. Clean up temporary
Harness fixture directories at test completion instead of accumulating them.

Release note: 修复内嵌浏览器遮挡弹窗和浮层、知识库来源选项被压缩及长名称显示异常;切换远端知识库时自动名称正确更新,并恢复更新说明显示。
This commit is contained in:
mesalogo
2026-09-13 19:46:37 +08:00
parent a0456f687a
commit 1c22e1a06d
21 changed files with 408 additions and 22 deletions
@@ -1,9 +1,9 @@
import { EventEmitter } from 'node:events'
import { mkdir, mkdtemp, realpath, writeFile } from 'node:fs/promises'
import { mkdir, mkdtemp, realpath, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { PassThrough } from 'node:stream'
import { describe, expect, it, vi } from 'vitest'
import { describe, expect, it, onTestFinished, vi } from 'vitest'
import {
DEEPSEEK_HARNESS_CONTROL_PROTOCOL,
DEEPSEEK_HARNESS_CONTROL_VERSION,
@@ -33,6 +33,7 @@ async function fixture() {
const root = await realpath(
await mkdtemp(join(tmpdir(), 'goodbuddy-harness-launcher-'))
)
onTestFinished(() => rm(root, { recursive: true, force: true }))
const workspace = join(root, 'workspace')
const dshHome = join(root, 'home')
const hostPath = join(
+6 -2
View File
@@ -1,11 +1,11 @@
import { describe, expect, it } from 'vitest'
import { describe, expect, it, onTestFinished } from 'vitest'
import {
createBoundedNdJsonStream,
installHarnessDiagnosticGuard,
startControlledDeepSeekHarnessHost
} from './deepseek-harness-host'
import { vi } from 'vitest'
import { mkdir, mkdtemp, realpath, writeFile } from 'node:fs/promises'
import { mkdir, mkdtemp, realpath, rm, writeFile } from 'node:fs/promises'
import type {
Agent,
CreateAgentOptions
@@ -65,6 +65,7 @@ describe('controlled DeepSeek Harness host', () => {
const root = await realpath(
await mkdtemp(join(tmpdir(), 'goodbuddy-harness-host-'))
)
onTestFinished(() => rm(root, { recursive: true, force: true }))
const inbound = new TransformStream<
Record<string, unknown>,
Record<string, unknown>
@@ -125,6 +126,7 @@ describe('controlled DeepSeek Harness host', () => {
const root = await realpath(
await mkdtemp(join(tmpdir(), 'goodbuddy-harness-alias-'))
)
onTestFinished(() => rm(root, { recursive: true, force: true }))
const alias = join(root, '..', basename(root))
const inbound = new TransformStream<
Record<string, unknown>,
@@ -157,6 +159,7 @@ describe('controlled DeepSeek Harness host', () => {
const root = await realpath(
await mkdtemp(join(tmpdir(), 'goodbuddy-harness-extensions-'))
)
onTestFinished(() => rm(root, { recursive: true, force: true }))
const brokenEntrypoint = join(root, 'broken.mjs')
await writeFile(
brokenEntrypoint,
@@ -202,6 +205,7 @@ describe('controlled DeepSeek Harness host', () => {
const root = await realpath(
await mkdtemp(join(tmpdir(), 'goodbuddy-harness-skill-'))
)
onTestFinished(() => rm(root, { recursive: true, force: true }))
const skillDirectory = join(root, 'web-3d-game')
await mkdir(skillDirectory)
await writeFile(
+18 -2
View File
@@ -1,4 +1,4 @@
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
@@ -70,6 +70,22 @@ afterEach(async () => {
})
describe('ReleaseNotesService', () => {
it('reads and acknowledges the actual packaged release history', async () => {
const { version } = JSON.parse(
await readFile(join(process.cwd(), 'package.json'), 'utf8')
) as { version: string }
const { filePath, service } = await createService(version)
await writeFile(
filePath,
await readFile(join(process.cwd(), 'resources', 'release-notes.json'))
)
await expect(service.getPending()).resolves.toMatchObject({
currentVersion: version,
releases: [{ version }]
})
await service.acknowledge({ version })
await expect(service.getPending()).resolves.toMatchObject({ releases: [] })
})
it('shows only the current release on a fresh installation', async () => {
const { service } = await createService('0.8.18')
@@ -135,7 +151,7 @@ describe('ReleaseNotesService', () => {
it('rejects an oversized release-notes resource with a bounded read', async () => {
const { filePath, service } = await createService('0.8.18')
await writeFile(filePath, ' '.repeat(128 * 1024 + 1), 'utf8')
await writeFile(filePath, ' '.repeat(1024 * 1024 + 1), 'utf8')
await expect(service.getPending()).rejects.toThrow(
'Release notes exceed the size limit'
+1 -1
View File
@@ -8,7 +8,7 @@ import {
import type { ApplicationSettingsStore } from './application-settings-store'
import { compareStrictSemVer } from './version-checker'
const maximumReleaseNotesBytes = 128 * 1024
const maximumReleaseNotesBytes = 1024 * 1024
export class ReleaseNotesService {
private releases?: ReleaseNote[]