Commit Graph
55 Commits
Author SHA1 Message Date
Bill eccc1f52e7 chore: bump version to 1.0.14 2026-09-20 21:04:35 +08:00
Bill 806fd93638 fix: sync language at render time without notifying subscribers
setLanguage() during App's render fired onLanguageChange, which made the
same component's useSyncExternalStore schedule an update mid-render
(React: cannot update a component while rendering a different component).
syncLanguage() updates the module silently; re-renders flow through the
settings store, which is the only path a renderer language change takes.
2026-09-20 20:45:44 +08:00
Bill 1054c2a661 docs: sync STATE/ROADMAP/AGENTS/README with reality
version 1.0.13, M10 milestone, release steps matching release.cjs
(incl. the sync-changelog pre-step), removed QuickInputPanel mentions,
real test mechanism instead of vitest, full test list
2026-09-20 20:27:08 +08:00
Bill cca4ba029a test: rebuildable bundles, npm test entry point
- esbuild alias fixed in the session/sysinfo/sftp test commands (they
  could not build at all), .sftp-svc.mjs build documented, real
  connection-stability assertions
- tests/build-bundles.cjs builds every bundle fresh; npm test runs the
  seven offline suites; esbuild pinned in devDependencies
- remove the assertion-less .exact-inline.mjs; ignore/clean test temp dirs
2026-09-20 20:27:06 +08:00
Bill 24f7e7c52a fix(release): atomic update-channel publish
upload the payload first and latest.yml last, prune the previous version
only afterwards, reuse an existing release, add --channel-only; sync-
changelog uses a function replacement so $-sequences in notes survive
2026-09-20 20:27:05 +08:00
Bill c0342db1e8 fix(shared): i18n coverage and contract updates
- keyPath field no longer tells users to enter a server-side path
- settings.highlight.builtin.* notes, timeout messages in four languages
- prototype-safe dictionary lookup; language as a render-time dependency
- Partial<AppSettings> saveSettings contract, update:stateGet channel
2026-09-20 20:27:03 +08:00
Bill 23fa566fa9 fix(renderer): terminal input tracking, workspace/session safety, panel correctness
- terminal: drop the diffRewriteRef echo assumption (Tab-accept corrupted
  history and cwd tracking), handle readline control keys in the line
  buffer, clamp degenerate PTY sizes, live copyOnSelect, bound highlight
  regex input, wide-char-safe link ranges
- workspace: boot-restore try/finally (a failure used to disable snapshot
  saving for the whole run), connect re-entrancy guard + real error
  messages, broadcast registry keyed by panel id (split panes), tab
  close-others/right live-array fix, pointer-captured bottom-panel drag,
  dockview constants hoisted, hydrate-gated restore
- panels: no chmod 000 on unknown mode, chown keeps current gid, 12-bit
  special-permission round trip, overwrite confirm, redraw monitor
  charts, gate polling on visibility, no secret carry-over between
  connections, settings sent as minimal patches, update-state pull
- language applies on the first render; accent fg recomputed on theme
  switch; window.api is properly typed again (env.d.ts import path)
2026-09-20 20:27:02 +08:00
Bill e04f4f0ac1 fix(main): SFTP data integrity, session lifecycle, security hardening
- upload: per-chunk buffer (ssh2 re-reads the overflow tail after the ACK;
  a reused buffer silently corrupted every file >= ~254KB)
- close the cached SFTP channel on eviction, attach an 'error' handler,
  close the download handle, time out execQuiet, fail partial deletes
- per-session StringDecoder for the ssh data plane (CJK mojibake), real
  exit codes, safe replay truncation, zmodem abort/counter/timer fixes
- sysinfo: idempotent poll end, error routing, per-poll watchdog, proc(5)
  CPU total; expand cd ~/$HOME/%USERPROFILE% paths; log sanitizer fixes
- security: will-navigate guard, central IPC sender check, scheme
  allowlist for openExternal, single-instance else branch, layout id and
  log-name whitelists, custom theme sanitizing, atomic JSON writes with
  EPERM retry in store.writeJson
- updater: per-attempt feed choice, quitAndInstall relaunch, dev guard,
  update-state getter
2026-09-20 20:26:34 +08:00
Bill 4c6a29ef28 feat: multi-language interface (zh-CN/zh-TW/en/ja), multilingual offline changelog, settings robustness
i18n
- shared/i18n: dependency-free t() with flat per-namespace dictionaries
  (common/settings/workspace/terminal/ssh/panels/main), zh-CN fallback
- language picker in Settings -> System; antd ConfigProvider locale follows it
- main process tracks the language too: tray menu, close prompt, ssh/sftp/
  zmodem errors and the log TUI marker are translated; tray rebuilds on change

changelog
- CHANGELOG.md (zh-CN canonical) + .zh-TW/.en/.ja, bundled via ?raw and read
  per interface language with per-version fallback to zh-CN (no network)
- sync-changelog.cjs merges RELEASE_NOTES[.<lang>].md per release; release.cjs
  refuses to publish without a zh-CN entry for the version

settings robustness
- closeAction 'ask' survives the sanitizer (was silently coerced to 'tray',
  which made the 'ask every time' option dead)
- highlight rules are repaired instead of dropped: string priority, 0/1
  enabled, missing fg colour; unknown fields preserved
- load-time warnings are written to settings-warnings.log (deduped, capped)

terminal/UI
- configurable terminal toolbar: open working directory (default on), session
  log recording (off), open logs folder (off)
- global shortcut field records key combos (modifier or F-key required)
- input suggestions + command history default to off, with a one-time reset
  migration for existing installs
- settings dialog scrolling fixed (antd v6 renamed the tabs container), theme
  gallery nested scrollbar removed, joined segmented pickers with readable
  selected-state text

tests: settings-store.mjs (15 checks) added; commands-store.mjs updated for
the new off-by-default history setting
2026-09-20 14:22:29 +08:00
Bill 34094d607b 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
2026-09-20 11:25:59 +08:00
Bill c1744184a3 chore: bump version to 1.0.12 2026-09-17 09:26:20 +08:00
Bill b3744b4705 fix: luminance-aware chrome theming for light themes; serialized settings writes; buffered log flush
- Derive tab-bar/chrome palette by background luminance: light themes keep
  a near-background bar with black-tinted tab overlays instead of a muddy
  gray strip; dark themes unchanged
- Theme dockview tabs via the group-scoped --dv-*-tab-* vars its own rules
  consume (they outspecify our .dv-tab rules and leaked abyss navy onto
  light tabs); bump inactive-tab hover specificity to match
- Convert settings dialog + highlight editor hardcoded white text/border
  tints to color-mix over --chrome-fg so panes stay readable on light themes
- commands.ts: per-file log write buffer with a single drain loop per file
  (burst output coalesces into one appendFile per IO tick, chain no longer
  grows per logWrite); stop-tail rides the same buffer
- settingsStore: serialize all writers through mutateSettings() queue that
  re-reads latest state per mutation (tray close-action vs settings UI full
  saves no longer clobber each other)
- tests: burst ordering + stop-tail case for the log buffer
2026-09-17 09:11:05 +08:00
Bill 9e453e272c chore: bump version to 1.0.11 2026-09-15 15:08:13 +08:00
Bill 237265e6ba feat: clickable URLs, left-rail settings nav, proportional resizable dialog
URLs in terminal output (http/https/ftp with ports, www. hosts, bare
localhost:port dev-server forms) are detected via a link provider: hover
underlines and shows the pointer, Ctrl/Cmd+Click opens the system browser.
Wrapped URLs spanning buffer rows resolve as one link. The 网址链接 highlight
preset grows to cover ftp:// and www. forms.

The settings dialog moves to a left navigation rail with the content column
scrolling on its own, sizes itself at ~70% of the main window and follows
main-window resizes in real time (until the user drags the corner grip),
stays centred while resizing, and keeps a stable height.
2026-09-15 15:07:47 +08:00
Bill 3b2fe06f71 docs: publish domestically only
From the 2026-09-15 decision, releases go to the Gitea release + the
domestic update channel with --skip-github; the GitHub release assets are no
longer synced per version (the code/tag mirror stays).
2026-09-15 13:37:29 +08:00
Bill 97fc7171cc chore: bump version to 1.0.10 2026-09-15 13:17:28 +08:00
Bill d3d029fe4f fix: wire plain Ctrl/Cmd+V to paste and split the check by line count
Plain Ctrl+V was never the terminal's: it went to the pty as a literal ^V
(0x16) — the command history even recorded 'cd \u0016' — so nothing pasted
and the confirm dialog never saw it. It is now handled on the terminal host
in the capture phase, with preventDefault, feeding the same path as
Ctrl+Shift+V.

The paste check is now shape-based rather than length-based: two or more
lines confirm (a stray newline executes an unreviewed command), a single
line pastes straight through unless it is unusually long.
2026-09-15 11:45:17 +08:00
Bill 77fd241b43 chore(dev): give dev builds their own userData and single-instance lock
A dev instance shared the installed build's userData directory and lock, so
starting it demanded killing the real app and it wrote test settings and
session snapshots into the live profile. Dev now uses OpenTerminal-dev and
tags its window title '(dev)'; both instances run side by side.
2026-09-15 01:02:10 +08:00
Bill 4b336360f8 fix(release): scope the proxy to GitHub so the domestic channel stays direct
setGlobalDispatcher routed every request — including the Gitea release and
channel PUTs — through the local proxy, which reset mid-upload once and left
the channel half-written. The proxy agent is now passed explicitly on the
GitHub requests only.
2026-09-15 00:34:30 +08:00
Bill d7abbaad9c fix: paste through xterm so large multi-line blocks insert instead of executing
confirmPaste wrote the raw text to the pty, so a large paste bypassed
bracketed paste and PowerShell ran it line by line; it also skipped
onData, which is why history/cwd tracking needed a separate mirror. Now the
text goes through term.paste(): xterm adds the \x1b[200~ markers when the
shell enabled bracketed paste (one edit, no execution) and the normal
onData path restores tracking for free.

The risky-paste bar becomes a proper dialog (确认粘贴 / 本次会话不再提示 /
关闭后续粘贴检测), matching the behaviour users expect from other
terminals.
2026-09-15 00:27:19 +08:00
Bill cbf4c224fb fix: allocate terminal titles by lowest free number instead of a counter
Closing 终端 6/7 then opening a new one produced 终端 8 rather than
refilling the gap. nextTerminalTitle now scans live panel titles and takes
the smallest unused N, which also subsumes the restore-time counter
seeding (syncTitleSeq is gone) — duplicate retitling after a restore fills
the lowest free number too.
2026-09-14 22:24:36 +08:00
Bill 03aa79a691 chore: bump version to 1.0.8 2026-09-14 21:41:46 +08:00
Bill 458dd9c88b fix: write session logs as plain text instead of raw PTY soup
Recorded logs carried the raw stream: SGR colors, cursor moves,
synchronized-output markers, and every TUI redraw frame — unreadable in an
editor and far larger than the visible output (a short kimi session logged
21.5KB of which 4.4KB is text). A per-session sanitizer now strips ANSI
statefully across chunk boundaries, collapses carriage-return overwrites
(progress bars keep only their final text), and suppresses alternate-screen
frames with a marker line. Ink-style inline TUIs redraw in the normal
buffer and cannot be frame-collapsed without screen emulation; their
committed lines are preserved.
2026-09-14 21:17:45 +08:00
Bill 28d8af6982 fix: keep terminal titles unique across session restore
The 终端 N counter lives only in memory, so after a restore it restarted at
zero and the next new terminal duplicated a restored title. Broadcast keys
targets by session id so duplicate titles never broke the fan-out itself,
but the target list became indistinguishable. Seed the counter past the
restored maximum on session restore / template apply, and retitle
duplicates already baked into existing snapshots.
2026-09-14 20:56:02 +08:00
Bill f09bed06b5 remove: drop the quick-input panel
The toggle state was a global singleton while the bolt button renders in
every pane's tab bar, so all panes showed the same open/close state and
the single floating panel (window bottom-right, sends to the *active*
session) never corresponded to the pane whose button was clicked. A true
per-pane rework is a medium refactor for a feature that overlaps with the
inline completion and the sidebar commands panel — removing instead.
2026-09-14 20:47:21 +08:00
Bill fcb2781694 chore: bump version to 1.0.7 2026-09-14 20:12:04 +08:00
Bill 0b4a87f1e7 fix: feed pasted text into the line buffer so history and cwd memory see it
App-driven paste (context menu / Ctrl+Shift+V) writes straight to the pty,
bypassing xterm's onData, and xterm-native paste arrives wrapped in
bracketed-paste markers that the buffer guards dropped. Either way a pasted
'cd D:\path' was recorded as the bare 'cd' typed before it, so the pane's
directory memory silently stayed home. Track submitted lines from pasted
text and strip bracketed-paste markers in the line buffer.
2026-09-14 20:01:23 +08:00
Bill 01827bf70b fix: resolve drive-relative cd (cd d:) in cwd memory
path.isAbsolute('d:') is false on Windows, so a drive-relative cd resolved
against the current base, produced a nonexistent path, and the pane kept
its creation directory in the snapshot. Map a bare drive-letter argument
to the drive root (the fresh-shell answer; we cannot know the drive's
remembered directory).
2026-09-14 19:52:32 +08:00
Bill 9a330499de fix: track bare drive switches (d:) for cwd memory
PowerShell/cmd users hop drives with a bare 'd:' — no cd keyword — so the
typed-command tracker never saw it, the pane's cwd stayed at its creation
directory, and the session snapshot restored it to home. Treat a bare
drive-letter line as a cd to the drive root; the main-process existence
check drops it on platforms without drive letters.
2026-09-14 19:27:20 +08:00
Bill 3c193a48a7 chore: bump version to 1.0.6 2026-09-14 17:31:00 +08:00
Bill 9467b78560 chore: name installed NSIS shortcuts OTerminal 2026-09-14 17:22:15 +08:00
Bill be7332db17 perf: route PTY stream through a single shared IPC listener
Every pane registered its own global onPtyData/onPtyExit listener, so each
output chunk woke N listeners and N-1 discarded it after an id compare.
With many terminals under heavy output that fan-out is pure overhead.
Now one IPC listener dispatches through a Map keyed by sessionId: one
lookup per chunk, only the owning pane runs.
2026-09-14 16:59:57 +08:00
Bill bbee1d642d polish: about-page logo now uses the app icon instead of an OT text mark 2026-09-14 15:06:19 +08:00
Bill 3aa8b28f89 chore: bump version to 1.0.5 2026-09-14 14:00:55 +08:00
Bill 277d8bb117 feat(commands): whole-history dedupe + switchable, capped history
历史命令去重从「只跟最新一条比」改为「全历史去重」:重敲任意一条已有命令
时把原条目提到最前(刷新 lastUsedAt)而不是新插一条,历史里每个命令只出现一次。

新增两个设置(设置 → 终端):
- 记录命令历史:关闭后不再记录新命令;已有历史保留,不清空
- 历史条数上限:默认 100,可配 1..500;写入按上限截断,读取也按当前
  上限截断(调低立即生效)

顺修一个暴露的既有 bug:logWrite 用 async appendFile,两次快速追加会
乱序落盘(line two 先于 line one)。改为按文件串行化追加。

测试:tests/commands-store.mjs 全历史去重 / 提到最前 / 上限生效 /
开关关闭不记录 / 关闭保留已有 / 恢复记录 / 越界上限夹紧,全部通过。
2026-09-14 12:01:23 +08:00
Bill beeb32a76b fix(session): keep panel ids distinct from session ids so restore rebinds panes
面板 id 曾直接复用 pty 会话 id(`addPanel({ id: sessionId })`),而
`updateParameters` 只能换会话、不能改面板 id。于是恢复时面板 id 指向一个
已不存在的会话:分屏少一个、剩下的 pane 背后没有 pty,界面看起来是空白,
也无法输入。

- 新增 `panelId()`,面板 id 与会话 id 彻底分离,并用于所有建面板处
  (新建终端、SSH 连接、分屏复制)
- 新增 `breakIdCoincidence()`:旧快照仍带 `panelId === sessionId`,
  在 `fromJSON` 之前重写 grid/panels/views/activeView 中的 id,
  返回 old→new 映射
- `rebindSessionPanels` 用该映射回查快照,恢复每个 pane 自己的目录
  (否则新 id 查不到记录,cwd 会静默退回 home)

验证(重启恢复 E2E,legacy 快照 + 2 分屏):
  2/2 pane 挂载、2 个独立存活会话、输入可回环、
  cwd 分别回到 D:/AIGC/OpenTerminal 与 C:/Windows、回写快照无 id 冲突;
  restoreSession=false 时正确忽略快照只建 1 个终端。
2026-09-14 11:29:56 +08:00
Bill 8eacd34338 polish: frame the sidebar with top and bottom borders to match the right edge 2026-09-14 02:26:36 +08:00
Bill 6c100542c5 fix: batch of review findings — dead install button, history pollution, TUI completion interference, ssh split session kill, scrollback live apply, zmodem second transfer, sftp shell quoting, replay buffer leak, release guards 2026-09-14 02:20:40 +08:00
Bill e6fc021903 fix: drop leaked NO_COLOR/FORCE_COLOR from the pty environment (Claude Code rendered uncolored) 2026-09-14 01:39:11 +08:00
Bill c4fb6fe7e2 polish: tray balloon title no longer repeats the app name 2026-09-14 01:19:14 +08:00
Bill f63ee379a4 fix: send the first pty size immediately, debounce only subsequent resizes 2026-09-14 01:17:04 +08:00
Bill ce89dcb1e4 chore: ignore tmp-test scratch scripts 2026-09-14 01:12:41 +08:00
Bill acdf955918 fix: debounce pty resize and skip no-op resizes (kills duplicated TUI frames on maximize/restore) 2026-09-14 01:12:15 +08:00
Bill ab5f8c2f77 chore: ignore tmp-test scratch dir 2026-09-08 21:40:54 +08:00
Bill 70c33a5572 fix: register update IPC handlers (dead check/download buttons since 1.0.1); bump 1.0.4 2026-09-08 21:40:25 +08:00
Bill 7f2e4c1110 feat: terminal right-click context menu (copy/paste/find/select-all/clear) 2026-09-08 20:50:59 +08:00
Bill 7d3057ee5d fix: drop TS annotation in release.cjs 2026-09-08 17:12:52 +08:00
Bill 2b3fefafd0 feat: close-to-tray default + system settings entry; bump 1.0.3 2026-09-08 17:00:09 +08:00
Bill 8f686f56ca docs: updater proxy strategy + channel changelog in AGENTS.md 2026-09-08 09:45:29 +08:00
Bill c3baac3f05 fix: updater proxy strategy (Gitea direct, GitHub system proxy) + changelog via channel release-notes.md 2026-09-08 09:44:34 +08:00
Bill dc3d54fa07 chore: release script in repo + AGENTS.md dev/release docs 2026-09-08 01:15:02 +08:00
Bill 8cb193fa6a feat: theme-driven chrome, customizable tab accent, UI polish
- Sidebar/tab bars/dividers/settings dialog follow the terminal theme
- Tab accent color customizable in theme settings (color picker)
- Title bar shows the app icon; Material Dark is the default theme
- Slimmer rectangular tabs (28px), slate scrollbars, visible pane dividers
- Fix pure-black xterm viewport gaps after pane resize
2026-09-08 00:57:56 +08:00
Bill 0de0eee58d chore: bump version to 1.0.1 2026-09-07 17:55:41 +08:00
Bill f25a1627f3 feat: about tab with update check/download (Gitea feed first, GitHub fallback)
- updater: dual-feed state machine, manual check/download/install IPC,
  changelog from Gitea releases API with GitHub fallback
- settings: new About tab (version, channel, auto-check toggle, progress)
- system.autoCheckUpdate setting gates the startup check
2026-09-07 17:18:15 +08:00
Bill a9acdbe500 Initial commit: OpenTerminal v0.1.0
Open-source terminal app (local + SSH): split panes, themes, SFTP,
server monitoring, broadcast input, ZMODEM, system tray, single instance.
Electron + React + TypeScript. MIT License.
2026-09-07 16:15:50 +08:00