fmt(js): npm run fix on merge (#86801)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
hermes-seaeye[bot]
2026-08-15 07:46:57 +00:00
committed by GitHub
parent 0930133dbf
commit af585de28e
23 changed files with 91 additions and 50 deletions

View File

@@ -116,6 +116,7 @@ test('merge preserves fields the editor does not carry (org, ssh extras)', () =>
org: 'nous',
url: 'https://a.cloud'
}
const renamed = mergeConnectionInput({ id: 'c', kind: 'cloud', label: 'Renamed', url: 'https://a.cloud' }, cloud)
assert.equal(renamed.org, 'nous')

View File

@@ -44,10 +44,7 @@ test('keeps URL-dependent image actions when srcURL is available', () => {
const { actions, calls } = createActions()
const url = 'https://example.com/image.png'
const items = imageContextMenuItems(
{ mediaType: 'image', hasImageContents: true, srcURL: url, x: 5, y: 8 },
actions
)
const items = imageContextMenuItems({ mediaType: 'image', hasImageContents: true, srcURL: url, x: 5, y: 8 }, actions)
assert.deepEqual(
items.map(item => item.label),

View File

@@ -4677,6 +4677,7 @@ function downloadViaTokenToFile(url, token, ctx, options: any = {}) {
const client = parsed.protocol === 'https:' ? https : http
const timeoutMs = resolveTimeoutMs(options.timeoutMs, DEFAULT_FETCH_TIMEOUT_MS)
const req = client.request(
parsed,
{
@@ -6838,6 +6839,7 @@ function downloadViaOauthSessionToFile(url, ctx, options: any = {}) {
}
const timeoutMs = resolveTimeoutMs(options.timeoutMs, DEFAULT_FETCH_TIMEOUT_MS)
const request = electronNet.request({
method: 'GET',
url,
@@ -6845,6 +6847,7 @@ function downloadViaOauthSessionToFile(url, ctx, options: any = {}) {
useSessionCookies: true,
redirect: 'follow'
} as any)
let settled = false
const timer = setTimeout(() => {
@@ -6910,6 +6913,7 @@ async function finalizeGatewayDownload(res, statusCode, headers, ctx: any = {})
const disposition = headers['content-disposition'] || headers['Content-Disposition']
const filename = filenameFromContentDisposition(disposition) || ctx.suggested || ctx.fallbackName
const result = await dialog.showSaveDialog(mainWindow, {
defaultPath: filename,
title: 'Save File'
@@ -6967,10 +6971,12 @@ async function saveGatewayFile(payload: any = {}) {
const suggested = String(payload.suggestedName || '').trim()
const fallbackName = path.basename(filePath) || suggested || 'download'
const ctx = { suggested, fallbackName }
const requestPath = pathWithGlobalRemoteProfile(`/api/fs/download?path=${encodeURIComponent(filePath)}`, profile, {
globalRemote: globalRemoteActive(),
profileRemoteOverride: profileHasRemoteOverride(profile)
})
const url = `${connection.baseUrl}${requestPath}`
try {
@@ -7002,10 +7008,13 @@ async function saveGatewayFileViaDataUrl(connection, profile, filePath, ctx: any
profileRemoteOverride: profileHasRemoteOverride(profile)
}
)
const url = `${connection.baseUrl}${requestPath}`
const json = (
connection.authMode === 'oauth' ? await fetchJsonViaOauthSession(url) : await fetchJson(url, connection.token)
) as any
const dataUrl = json?.dataUrl
if (!dataUrl) {
@@ -7014,6 +7023,7 @@ async function saveGatewayFileViaDataUrl(connection, profile, filePath, ctx: any
const buffer = parseDataUrlToBuffer(dataUrl)
const filename = ctx.suggested || ctx.fallbackName
const result = await dialog.showSaveDialog(mainWindow, {
defaultPath: filename,
title: 'Save File'

View File

@@ -319,7 +319,8 @@ function explicitToolArtifactKey(keyPath: string, producerTool: boolean): boolea
.split('.')
.filter(segment => segment && !/^\d+$/.test(segment))
.some(
segment => STRONG_TOOL_ARTIFACT_KEY_RE.test(segment) || (producerTool && PRODUCER_TOOL_ARTIFACT_KEY_RE.test(segment))
segment =>
STRONG_TOOL_ARTIFACT_KEY_RE.test(segment) || (producerTool && PRODUCER_TOOL_ARTIFACT_KEY_RE.test(segment))
)
}

View File

@@ -154,7 +154,9 @@ describe('composer IME composition — send button visibility (#39614)', () => {
composingRef.current = true
}}
onInput={event => {
if (composingRef.current) {return}
if (composingRef.current) {
return
}
flushEditorToDraft(event.currentTarget)
}}
onKeyDown={handleKeyDown}

View File

@@ -962,7 +962,12 @@ describe('overlayLiveLanes', () => {
sessionCount: 1,
groups: [
lane({ id: '/www/app::branch::main', label: 'main', isMain: true, path: '/www/app', sessions: [session] }),
lane({ id: '/www/app/.worktrees/feature', label: 'feature', path: '/www/app/.worktrees/feature', sessions: [] })
lane({
id: '/www/app/.worktrees/feature',
label: 'feature',
path: '/www/app/.worktrees/feature',
sessions: []
})
]
}
]

View File

@@ -572,10 +572,7 @@ export function overlayRepoLanes(
(placed.isMain && placedKey
? lanes.find(
g =>
g.isMain &&
pathKey(g.path) === placedKey &&
!g.id.includes('::branch::') &&
!g.id.includes('::kanban')
g.isMain && pathKey(g.path) === placedKey && !g.id.includes('::branch::') && !g.id.includes('::kanban')
)
: undefined) ??
(!placed.isMain && placedKey ? lanes.find(g => pathKey(g.path) === placedKey) : undefined)

View File

@@ -22,7 +22,14 @@ import { Codicon } from '@/components/ui/codicon'
import { ColorSwatches } from '@/components/ui/color-swatches'
import { ConfirmDialog } from '@/components/ui/confirm-dialog'
import { CopyButton } from '@/components/ui/copy-button'
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, preventCloseButtonAutoFocus } from '@/components/ui/dialog'
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
preventCloseButtonAutoFocus
} from '@/components/ui/dialog'
import { Input } from '@/components/ui/input'
import { renameSession } from '@/hermes'
import { useI18n } from '@/i18n'

View File

@@ -65,10 +65,7 @@ describe('session row details', () => {
it('omits zero counts from metadata so the sidebar stays clean', () => {
expect(
sessionRowDetails(
session({ git_branch: null, message_count: 0, model: null, tool_call_count: 0 }),
en
)
sessionRowDetails(session({ git_branch: null, message_count: 0, model: null, tool_call_count: 0 }), en)
).toEqual({ metadata: '', preview: 'Explore Gmail-like density tiers for session rows.' })
})
@@ -99,10 +96,7 @@ describe('session row details', () => {
}
expect(
sessionRowDetails(
session({ git_branch: null, message_count: 3, model: null, tool_call_count: 5 }),
ja
)
sessionRowDetails(session({ git_branch: null, message_count: 3, model: null, tool_call_count: 5 }), ja)
).toEqual({
metadata: '3 件のメッセージ · 5 件のツール呼び出し',
preview: 'Explore Gmail-like density tiers for session rows.'

View File

@@ -177,11 +177,7 @@ describe('refreshSessions identity + loading hygiene', () => {
// both honor the tombstone, or a deleted platform thread resurrects.
removed.ids = new Set(['tg-2'])
listSidebarSessions.mockResolvedValue(
sidebar(
{ sessions: [] },
[],
[row('tg-1', { source: 'telegram' }), row('tg-2', { source: 'telegram' })]
)
sidebar({ sessions: [] }, [], [row('tg-1', { source: 'telegram' }), row('tg-2', { source: 'telegram' })])
)
const { result } = renderHook(() => useSessionListActions({ profileScope: 'default' }))
@@ -194,7 +190,11 @@ describe('refreshSessions identity + loading hygiene', () => {
// Per-platform pager: backend page still lists the doomed row.
listAllProfileSessions.mockResolvedValue({
sessions: [row('tg-1', { source: 'telegram' }), row('tg-2', { source: 'telegram' }), row('tg-3', { source: 'telegram' })],
sessions: [
row('tg-1', { source: 'telegram' }),
row('tg-2', { source: 'telegram' }),
row('tg-3', { source: 'telegram' })
],
total: 3
})

View File

@@ -28,7 +28,14 @@ import { useOnProfileSwitch } from '../hooks/use-on-profile-switch'
import { PanelEmpty } from '../overlays/panel'
import { ConfigField } from './config-field'
import { clearsEnabledToolsets, enumOptionsFor, getNested, isExternalMemoryProvider, sectionFieldEntries, setNested } from './helpers'
import {
clearsEnabledToolsets,
enumOptionsFor,
getNested,
isExternalMemoryProvider,
sectionFieldEntries,
setNested
} from './helpers'
import { MemoryConnect } from './memory/connect'
import { ProviderConfigPanel } from './memory/provider-config-panel'
import { ModelSettings, ModelSettingsSkeleton } from './model-settings'

View File

@@ -68,11 +68,7 @@ export function useElapsedSeconds(active = true, timerKey?: string, since?: numb
}
}, [active, since, timerKey])
useViewedInterval(
() => setElapsed(Math.max(0, Math.floor((Date.now() - start.current) / 1000))),
1000,
active
)
useViewedInterval(() => setElapsed(Math.max(0, Math.floor((Date.now() - start.current) / 1000))), 1000, active)
return elapsed
}

View File

@@ -191,6 +191,7 @@ export function FloatingPet() {
// re-sent over the WebSocket on every backstop refresh.
const held = $petInfo.get()
const knownRevision = held.enabled && held.spritesheetBase64 ? held.spritesheetRevision : undefined
const next = await requestGateway<PetInfo & { spritesheetUnchanged?: boolean }>('pet.info', {
knownRevision,
profile: petProfile()
@@ -253,10 +254,7 @@ export function FloatingPet() {
// Always keep a timer. Event-capable backends use the slow backstop (same
// contract as cron/sessions in use-background-sync); legacy keeps the
// historical fast-while-inactive cadence.
const timer = window.setInterval(
pullIfVisible,
petInfoPollIntervalMs(changeEventsAvailable, active)
)
const timer = window.setInterval(pullIfVisible, petInfoPollIntervalMs(changeEventsAvailable, active))
return () => {
cancelled = true

View File

@@ -13,10 +13,7 @@ export const PET_BACKSTOP_MS = 15_000
/** Cold-start retries after the first connect pull (fail-open recovery). */
export const PET_STARTUP_RETRY_MS = [1_000, 3_000, 8_000] as const
export function petInfoPollIntervalMs(
changeEventsAvailable: boolean,
active: boolean
): number {
export function petInfoPollIntervalMs(changeEventsAvailable: boolean, active: boolean): number {
if (changeEventsAvailable) {
return PET_BACKSTOP_MS
}

View File

@@ -251,12 +251,14 @@ describe('PetSprite RAF scheduling', () => {
it('draws sprite frames with bicubic smoothing for illustration art', () => {
const raf = installRaf()
const ctxMock = {
clearRect: vi.fn(),
drawImage: vi.fn(),
imageSmoothingEnabled: false,
imageSmoothingQuality: 'low'
} as unknown as CanvasRenderingContext2D
vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(ctxMock)
render(<PetSprite info={INFO} />)

View File

@@ -21,12 +21,14 @@ export function useViewedInterval(callback: () => void, intervalMs: number, enab
}
let intervalId: null | number = null
const stop = () => {
if (intervalId !== null) {
window.clearInterval(intervalId)
intervalId = null
}
}
const sync = () => {
const viewed = document.visibilityState === 'visible' && document.hasFocus()

View File

@@ -799,7 +799,8 @@ export const zh: Translations = {
autosaveFailed: '自动保存失败',
imported: '配置已导入',
invalidJson: '配置 JSON 无效',
toolsetsWipeConfirm: '确定移除所有已启用的工具集吗?这将禁用记忆、终端、网络搜索、委派以及大多数其他工具,直到你重新启用它们。',
toolsetsWipeConfirm:
'确定移除所有已启用的工具集吗?这将禁用记忆、终端、网络搜索、委派以及大多数其他工具,直到你重新启用它们。',
keepAwakeTitle: '保持电脑唤醒',
keepAwakeDesc: '阻止本机休眠,让长时间或通宵运行继续进行。屏幕仍可变暗。',
attachmentSizeTitle: '预览 / 图片加载大小上限',

View File

@@ -69,7 +69,9 @@ describe('requestModelOptions', () => {
vi.mocked(getGlobalModelOptions).mockResolvedValueOnce(restPayload)
await expect(requestModelOptions({ gateway: gateway as never, sessionId: 'session-1' })).resolves.toEqual(restPayload)
await expect(requestModelOptions({ gateway: gateway as never, sessionId: 'session-1' })).resolves.toEqual(
restPayload
)
expect(getGlobalModelOptions).toHaveBeenCalledWith({ explicitOnly: true })
})

View File

@@ -5,9 +5,7 @@ import { persistBoolean, storedBoolean } from '@/lib/storage'
const REASONING_COLLAPSED_BY_DEFAULT_STORAGE_KEY = 'hermes.desktop.reasoning.collapsedByDefault'
/** Desktop-local presentation preference; shared backend config must not be changed by a single window. */
export const $reasoningCollapsedByDefault = atom(
storedBoolean(REASONING_COLLAPSED_BY_DEFAULT_STORAGE_KEY, false)
)
export const $reasoningCollapsedByDefault = atom(storedBoolean(REASONING_COLLAPSED_BY_DEFAULT_STORAGE_KEY, false))
$reasoningCollapsedByDefault.subscribe(value => persistBoolean(REASONING_COLLAPSED_BY_DEFAULT_STORAGE_KEY, value))

View File

@@ -45,7 +45,7 @@ const cases: Array<[string, string, string]> = [
['DEL-space-DEL + char', '\x7f \x7fô', 'ô'],
['trailing text after multi DEL', '\x7f\x7f\x7fươn', 'ươn'],
['char then DEL then char fused', 'o\x7fô', 'ô'],
['multiple syllable fused', 'vuon\x7f\x7f\x7fương', 'vương'],
['multiple syllable fused', 'vuon\x7f\x7f\x7fương', 'vương']
// CR/LF are intentionally NOT split (preserve paste/return semantics), so a
// text token with an embedded CR is left whole; assert it is NOT split into
// surviving letters here — that path is covered by the composer's return /

View File

@@ -145,9 +145,27 @@ describe('Vietnamese Telex IME recomposition', () => {
// Captured byte stream for Telex "vuonwg syx hanhj": each syllable injects a
// U+202F marker, erases, and re-emits. Verified across read timings.
const reads = [
'v', 'u', 'o', NNBSP, '\x7f\x7f', '\x7f\u01B0\u01A1', 'n', 'g',
' ', 's', 'y', NNBSP, '\x7f', '\x7f\u1EF9',
' ', 'h', 'a', 'n', 'h', NNBSP, '\x7f\x7f\x7f\x7f\u1EA1nh'
'v',
'u',
'o',
NNBSP,
'\x7f\x7f',
'\x7f\u01B0\u01A1',
'n',
'g',
' ',
's',
'y',
NNBSP,
'\x7f',
'\x7f\u1EF9',
' ',
'h',
'a',
'n',
'h',
NNBSP,
'\x7f\x7f\x7f\x7f\u1EA1nh'
]
for (const gapMs of [0, 17, 25]) {
@@ -240,4 +258,4 @@ describe('Fast-echo suppression reset (60ms window)', () => {
const reads2 = [...reads, '\x7f', '\x7f']
expect(await drive(reads2)).toBe('hel')
})
})
})

View File

@@ -266,12 +266,14 @@ class TurnController {
endReasoningPhase() {
this.reasoningStreamingTimer = clear(this.reasoningStreamingTimer)
// Seal any open reasoning segment so its isLiveReasoning flag drops the
// moment the reasoning phase ends — the panel must stop tracking the
// turn's global reasoningActive, not stay "live" for the rest of the turn.
if (this.reasoningSegmentIndex !== null) {
this.syncReasoningSegment(false)
}
patchTurnState({ reasoningActive: false, reasoningStreaming: false })
}

View File

@@ -944,9 +944,11 @@ export function TextInput({
// guard. Use a short real-time window that spans a recompose burst;
// normal typing re-enables fast-echo via the append path below.
inkRepaintedRef.current = true
if (inkRepaintResetTimer.current) {
clearTimeout(inkRepaintResetTimer.current)
}
inkRepaintResetTimer.current = setTimeout(() => {
inkRepaintResetTimer.current = null
inkRepaintedRef.current = false
@@ -1446,10 +1448,12 @@ export function TextInput({
// terminal baseline is synced again — clear any pending Ink-repaint
// fast-echo suppression so normal backspace fast-echo resumes.
inkRepaintedRef.current = false
if (inkRepaintResetTimer.current) {
clearTimeout(inkRepaintResetTimer.current)
inkRepaintResetTimer.current = null
}
// ASCII-printable text advances the physical cursor by exactly
// text.length cells (canFastAppendShape rejects non-ASCII,
// wide chars, newlines). Notify Ink so the cached displayCursor