feat: shortcut recorder, configurable terminal toolbar, offline changelog

- Global shortcut setting: press-to-record input (Esc cancels, Backspace
  clears); requires a modifier or F-key so plain typing can't be hijacked
- Terminal toolbar: three settings-gated buttons — session-log record
  (default off), open logs dir (default off), open working directory
  (default on, new; local sessions only, cwd tracked via cd/OSC 7)
- Input suggestions + command history now default off, with a one-time
  migration that resets persisted true values for existing installs
- Settings dialog: fix broken scrolling — antd v6 renamed the Tabs scroll
  container to .ant-tabs-body-holder; theme gallery drops its nested
  scroll (single outer scrollbar)
- Offline changelog: CHANGELOG.md at repo root bundled via ?raw; About tab
  reads it first, network sources stay as fallback; scripts/sync-changelog.cjs
  merges RELEASE_NOTES.md per release (release.cjs fails without an entry)
- commands.ts history prefs default aligned with new off-by-default
This commit is contained in:
Bill committed 2026-09-20 11:25:59 +08:00
1 parent c1744184a3
commit 34094d607b
17 files changed
+319 -41

No files matched your search

+15 -1
View File
@@ -1,6 +1,7 @@
import { app, ipcMain } from 'electron'
import { app, ipcMain, shell } from 'electron'
import fontList from 'font-list'
import { homedir } from 'os'
import { statSync } from 'fs'
import { Ipc, type AppInfo, type LayoutMeta, type PtyCreateOptions } from '../shared/ipc'
import type { HostKeyAction, SessionOpenOptions, SshConnection, SshConnectionInput } from '../shared/connections'
import { getLayout, listLayouts, saveLayout, deleteLayout } from './layouts'
@@ -181,4 +182,17 @@ export function registerIpc(): void {
resolveCwd(current, arg)
)
ipcMain.handle(Ipc.CWD_REPORT, (_event, payload: string) => normalizeReportedCwd(payload))
// Open a local directory in the file manager. Validates the path is an
// existing directory so renderer-supplied values can't open arbitrary files.
ipcMain.handle(Ipc.CWD_OPEN, (_event, dir: unknown): boolean => {
if (typeof dir !== 'string' || dir === '') return false
try {
if (!statSync(dir).isDirectory()) return false
} catch {
return false
}
void shell.openPath(dir)
return true
})
}