docs: update changelog and documentation for version 1.0.7

- Added new features including `Background Image` customization and `Backspace Mode` for Telnet and Serial sessions.
- Improved UI for font selection and OTP input layout.
- Fixed CSP asset protocol handling for local assets.
- Enhanced performance by loading system fonts asynchronously.
- Updated README and guides to reflect new features and improvements.
This commit is contained in:
Kang committed 2026-05-22 22:09:33 +08:00
1 parent 5616b9a838
commit 3709f202b1
16 files changed
+634 -91

No files matched your search

+40 -8
View File
@@ -4,6 +4,46 @@ All notable changes to this project will be documented in this file.
This changelog is inferred from release bump commits in git history (for example `chore: bump version to vX.Y.Z`) and grouped by Conventional Commit type.
## [1.0.7] - 2026-05-21
### Changed
- **appearance:** Improve font selection UI and show a `Loading system fonts...` state while system fonts are being discovered.
- **otp:** Improve OTP input slot layout and OTP code panel responsiveness on narrower screens.
- **profiles:** Add multiple Cargo build profiles for debugging and release workflows.
### Fixed
- **csp:** Correct CSP asset protocol handling so local assets such as background images load reliably.
### Performance
- **appearance:** Load system fonts asynchronously to keep the Appearance tab responsive.
## [1.0.6] - 2026-05-21
### Added
- **appearance:** Add `Background Image` customization for the main window, including `Image Sizing`, `Image Opacity`, and `Background Content Opacity` controls.
- **sessions:** Add `Backspace Mode` selection for Telnet and Serial sessions with `Ctrl+H (BS)` and `DEL (0x7F)` options.
### Changed
- **resource-monitor:** Refresh the resource monitor with clearer cards and improved visual hierarchy.
### Fixed
- **shortcuts:** Prevent Shift-modified terminal input from being mistaken for application shortcuts, restoring uppercase input such as `Shift+C`, `Shift+V`, and `Shift+X`.
## [1.0.5] - 2026-05-19
### Added
- **ai:** Capture AI command execution events and render inline terminal output during agent-driven workflows.
- **ai:** Add the `Terminal Output Lines` setting to control how many inline output lines are shown for AI-executed commands.
- **terminal:** Add AI Execution Profile selection to terminal session forms.
- **window:** Improve main-window modal management so child windows keep the workspace in a clearer modal state.
## [1.0.4] - 2026-05-19
### Added
@@ -75,14 +115,6 @@ This changelog is inferred from release bump commits in git history (for example
- **docs:** Update changelog with new releases and enhancements.
- **ci:** Update Docusaurus dependencies and remove optional Umami config.
## [1.0.4] - 2026-05-19
## [1.0.3] - 2026-05-18
## [1.0.2] - 2026-05-17
## [1.0.1] - 2026-05-16
## [1.0.0] - 2026-05-06
## [0.9.0] - 2026-04-30
+19 -3
View File
@@ -47,7 +47,9 @@ NyaTerm includes an AI Assistant panel for command generation, terminal output e
- **Ask mode** for one-off help such as generating commands, explaining selected output, and analyzing errors
- **Agent mode** for multi-step work using an observe-decide-run loop against the active terminal session
- **Recent-output actions** so you can ask AI to explain the latest terminal output without manually copying it
- **Structured command cards** with risk levels, execution controls, and optional save-to-quick-command support
- **Inline terminal capture** during Agent execution with configurable `Terminal Output Lines`
- **Session mentions** with `@` to bring other terminal sessions into the AI context
- **Provider management** for built-in providers and custom OpenAI-compatible endpoints
- **Risk control** for high-impact commands, including approval gates and safer alternatives
@@ -88,6 +90,7 @@ NyaTerm is built for people who move between servers, local commands, devices, a
- SSH, Local Terminal, Telnet, and Serial session support
- Multi-tab workspace with horizontal and vertical pane splits
- Saved connections with folders, icons, metadata, duplication, reconnect, and import flows
- Main-window `Background Image` customization with `cover` / `contain` / `stretch` / `tile` sizing and adjustable `Background Content Opacity`
- Left and right activity bars for file explorer, network, Security/Auth, Sync & Backup, AI Assistant, saved connections, active sessions, command history, and resource monitoring
- Child windows for settings, new-session creation, quick-command editing, and auto-upload prompts
- Tray support with optional minimize-to-tray behavior
@@ -99,10 +102,11 @@ NyaTerm is built for people who move between servers, local commands, devices, a
- Optional line-number and timestamp gutter
- Optional action links for IPv4 addresses, `host:port`, and archive filenames
- Optional keyword highlighting with built-in presets and custom rules
- AI shortcuts for explaining recent output, plus inline Agent command output with configurable `Terminal Output Lines`
- Large-output protection, configurable scrollback, SSH keep-alive, and session recording
- Online search and translation from selected terminal text
- Zmodem file transfer support directly from the terminal
- Customizable keyboard shortcuts for terminal and UI actions
- Customizable keyboard shortcuts for terminal and UI actions, including `Backspace Mode` selection for Telnet and Serial sessions
## SFTP and File Workflows
@@ -147,6 +151,18 @@ Manage SSH, local shell, Telnet, and Serial sessions inside one tabbed and split
</picture>
</p>
## Appearance and Background Image
Use a local wallpaper behind the main window, tune `Image Sizing`, `Image Opacity`, and `Background Content Opacity`, and keep child windows readable with solid surfaces.
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./docs-site/static/img/home/cover-dark.png">
<source media="(prefers-color-scheme: light)" srcset="./docs-site/static/img/home/cover-light.png">
<img alt="NyaTerm background image customization" src="./docs-site/static/img/home/cover-light.png">
</picture>
</p>
## Terminal Enhancements
Use command history, search, translation, action links, timestamps, keyword highlighting, and large-output protection in the terminal.
@@ -217,8 +233,8 @@ Download installers from [nyaterm.app](https://nyaterm.app) or the [Releases](ht
|------|-------------|-------|
| SSH | Linux / Unix remote servers | Supports SFTP, OTP, resource monitor, proxy, jump host, and tunnels |
| Local Terminal | Local shell workflows | Uses your local shell path and working directory |
| Telnet | Legacy network devices or lab systems | Lightweight terminal session without SSH-only features |
| Serial | Routers, boards, embedded devices | Configurable port, baud rate, data bits, parity, and stop bits |
| Telnet | Legacy network devices or lab systems | Lightweight terminal session without SSH-only features, with `Backspace Mode` for `Ctrl+H (BS)` or `DEL (0x7F)` |
| Serial | Routers, boards, embedded devices | Configurable port, baud rate, data bits, parity, stop bits, and `Backspace Mode` |
---
+19 -3
View File
@@ -47,7 +47,9 @@ NyaTerm 内置 AI Assistant 面板,可用于生成命令、解释终端输出
- **Ask 模式**:适合单次问答,例如生成命令、解释选中输出、分析错误
- **Agent 模式**:基于当前活跃终端会话,按“观察、决策、执行”的循环完成多步任务
- **最近输出入口**:可以直接让 AI 解释最近的终端输出,不必手动复制
- **结构化命令卡片**:展示风险等级、执行控制,并可保存为快捷命令
- **终端内联输出捕获**:Agent 执行命令时可把输出写回终端,并通过 `Terminal Output Lines` 控制显示行数
- **会话提及**:输入 `@` 可把其他终端会话纳入当前 AI 上下文
- **Provider 管理**:支持内置 provider 和自定义 OpenAI Compatible 端点
- **风险控制**:对高影响命令提供审批门槛和更安全的替代建议
@@ -89,6 +91,7 @@ NyaTerm 适合每天在服务器、本地命令、设备调试和配置文件之
- 支持 SSH、本地终端、Telnet 和串口会话
- 多标签页工作区,标签页内支持横向和纵向分屏
- 已保存连接支持分组、图标、元数据、复制、重连和导入
- 主窗口支持 `Background Image` 背景图自定义,可调 `cover` / `contain` / `stretch` / `tile` 以及 `Background Content Opacity`
- 左右活动栏覆盖文件浏览器、网络、Security/Auth、Sync & Backup、AI Assistant、已保存连接、活动会话、命令历史和资源监控
- 设置、新建连接、快捷命令编辑、自动上传提示使用独立子窗口
- 支持托盘与关闭时最小化到托盘
@@ -100,10 +103,11 @@ NyaTerm 适合每天在服务器、本地命令、设备调试和配置文件之
- 可选行号和时间戳 gutter
- 可选动作链接,识别 IPv4、`host:port` 和压缩包文件名
- 可选关键词高亮,支持内置预设和自定义规则
- 可直接让 AI 解释最近输出,并通过 `Terminal Output Lines` 控制 Agent 命令的终端内联输出
- 大输出保护、可配置回滚缓冲区、SSH Keep-Alive 和会话录制
- 可从选中文本发起在线搜索和翻译
- 支持直接在终端中使用 Zmodem 进行文件传输
- 支持自定义终端与界面操作的键盘快捷键
- 支持自定义终端与界面操作的键盘快捷键,Telnet / 串口会话还支持 `Backspace Mode`
## SFTP 与文件工作流
@@ -148,6 +152,18 @@ NyaTerm 适合每天在服务器、本地命令、设备调试和配置文件之
</picture>
</p>
## 外观与背景图
为主窗口设置本地壁纸,并通过 `Image Sizing`、`Image Opacity` 和 `Background Content Opacity` 平衡视觉效果与面板可读性。
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./docs-site/static/img/home/cover-dark.png">
<source media="(prefers-color-scheme: light)" srcset="./docs-site/static/img/home/cover-light.png">
<img alt="NyaTerm 背景图自定义" src="./docs-site/static/img/home/cover-light.png">
</picture>
</p>
## 终端增强
在终端中使用命令历史、搜索、翻译、动作链接、时间戳、关键词高亮和大输出保护。
@@ -218,8 +234,8 @@ NyaTerm 适合每天在服务器、本地命令、设备调试和配置文件之
|------|----------|------|
| SSH | Linux / Unix 远程服务器 | 支持 SFTP、OTP、资源监控、代理、跳板机和隧道 |
| 本地终端 | 本地 Shell 工作流 | 使用本机 Shell 路径和工作目录 |
| Telnet | 旧设备或实验环境 | 轻量终端会话,不包含 SSH 专属能力 |
| 串口 | 路由器、开发板、嵌入式设备 | 可配置端口、波特率、数据位、校验位和停止位 |
| Telnet | 旧设备或实验环境 | 轻量终端会话,不包含 SSH 专属能力,并支持 `Backspace Mode` 在 `Ctrl+H (BS)` 与 `DEL (0x7F)` 间切换 |
| 串口 | 路由器、开发板、嵌入式设备 | 可配置端口、波特率、数据位、校验位、停止位和 `Backspace Mode` |
---
+9
View File
@@ -28,6 +28,7 @@ Agent 模式的特点:
- 需要一个活跃的终端会话
- 每一步命令都会显示执行状态(运行中、已完成、失败、超时、需要审批、被安全策略阻止)
- 可在 **设置 → AI → Agent Settings** 中配置最大步数和单步超时
- 可通过 `Terminal Output Lines` 控制 AI 执行命令时在终端中内联展示多少行输出
- 高风险命令仍然需要人工审批后才能执行
## 会话管理
@@ -71,6 +72,14 @@ AI 返回的命令以结构化卡片展示,包含:
当 AI 检测到高风险命令时,可能会同时提供更安全的替代命令供选择。
## 最近输出与终端内联输出
AI 助手不仅能处理选中文本,也可以直接围绕“刚刚发生了什么”来工作。
- 在终端右键菜单中使用 **解释最近输出**,可以把当前会话最近的终端输出直接发送给 AI
- 在 Agent 模式下,AI 执行命令时会捕获命令执行事件,并把摘要后的输出以内联片段形式反馈到终端工作流附近
- `Terminal Output Lines` 决定这类终端内联反馈最多展示多少行;设为 `0` 则关闭
## 推理内容
如果使用的模型支持推理通道(如 DeepSeek R1、QwQ 等),AI 助手会在回复中展示推理过程。推理内容默认折叠,可以展开查看 AI 的思考链路。
+5 -2
View File
@@ -19,8 +19,8 @@ NyaTerm 不只是 SSH 客户端,而是一个把多类终端工作流放到同
|----------|----------|----------------|
| SSH | Linux / Unix 服务器远程运维 | SFTP、OTP、资源监控、代理、跳板机、隧道 |
| 本地终端 | 本地 shell、脚本调试、构建命令 | 共享同一套终端 UI、命令历史、分屏 |
| Telnet | 旧设备、实验环境、兼容性排障 | 终端工作区能力,但不包含 SSH 专属特性 |
| 串口 | 路由器、交换机、板卡、嵌入式调试口 | 串口参数配置、终端工作区能力 |
| Telnet | 旧设备、实验环境、兼容性排障 | 终端工作区能力,支持 `Backspace Mode`,但不包含 SSH 专属特性 |
| 串口 | 路由器、交换机、板卡、嵌入式调试口 | 串口参数配置、`Backspace Mode` 与终端工作区能力 |
## SSH
@@ -79,6 +79,8 @@ Telnet 会话适合:
如果你的目标只是“快速打开一个传统远程终端”,Telnet 会更直接。
对于依赖退格键兼容性的设备,Telnet 会话还提供 `Backspace Mode`,可以在 `Ctrl+H (BS)` 和 `DEL (0x7F)` 之间切换。
## 串口
串口会话适合连接:
@@ -94,6 +96,7 @@ Telnet 会话适合:
- 数据位
- 校验位
- 停止位
- `Backspace Mode`
串口会话同样可以放进 NyaTerm 的标签页与分屏工作区中,适合边看一个串口输出,边在另一个 SSH 或本地终端里执行命令。
+11 -1
View File
@@ -67,7 +67,7 @@ NyaTerm 在右侧面板中内置了 **AI Assistant**,并且可以从终端上
### 常见用法
- **生成命令**:告诉它你想完成什么,由它生成命令卡片
- **解释最近输出**:针对当前会话最近的终端输出做说明
- **解释最近输出**:针对当前会话最近的终端输出做说明,不必手动复制日志片段
- **解释选中内容**:对选中的日志、报错或片段做解释
- **分析错误**:把错误上下文交给 AI 生成修复建议
- **修复选中内容**:从选中报错推导下一步命令
@@ -81,6 +81,16 @@ AI 返回的命令可以以结构化卡片形式展示,并带有风险等级
- 是否允许把生成命令保存为快捷命令
- 是否记录 AI 历史
### 终端内联输出
当 Agent 模式执行命令时,NyaTerm 可以捕获命令执行事件,并把摘要后的输出以内联形式展示在终端工作流附近,方便你连续跟进多步操作的结果。
这个行为由 **设置 → AI → Agent Settings** 中的 `Terminal Output Lines` 控制:
- `Terminal Output Lines` 表示每次 AI 执行命令后,在终端中额外展示多少行输出
- 设为 `0` 时,关闭终端内联输出展示
- 这个设置不会影响真实命令本身的执行,只影响 AI 工作流中的附加可视化反馈
### Provider 与模型
AI 能力支持:
+13
View File
@@ -15,6 +15,17 @@ NyaTerm 支持对工作区外观做细粒度调整,包括 UI 主题、终端
如果你只是想快速切换主题,也可以使用顶部菜单中的 **View → Theme**。
## 背景图
在 **Settings → Appearance** 中,主窗口现在可以使用本地背景图。相关设置建议直接按界面里的真实名称理解:
- `Background Image`:选择主窗口背后的本地图片
- `Image Sizing`:控制图片以 `cover`、`contain`、`stretch` 或 `tile` 的方式铺开
- `Image Opacity`:控制背景图透过主题显示的强度
- `Background Content Opacity`:控制工作区面板和内容表面的透光程度
这个能力只作用于主窗口工作区。设置页和其他子窗口仍然保持实色背景,避免在表单、弹窗和独立窗口里牺牲可读性。
## 字体与字号
在 **设置 → 外观** 中可以调整:
@@ -31,6 +42,8 @@ NyaTerm 内置了这些常用字体:
同时也会读取系统已安装字体,供你加入字体回退链路。
系统字体现在是异步发现的,因此首次展开字体列表时,界面可能会短暂显示 `Loading system fonts...`。这表示应用正在后台整理系统字体,不影响你先选择内置字体或已有回退链路。
## 光标与连字
外观设置还支持这些终端细节:
+21
View File
@@ -173,6 +173,24 @@
"在侧边区域查看会话列表、文件和资源信息": {
"message": "View session lists, files, and resource information in side panels"
},
"外观": {
"message": "Appearance"
},
"用背景图定制主窗口氛围": {
"message": "Customize the workspace mood with a background image"
},
"主窗口支持本地背景图,可在保留面板可读性的同时营造更个性化的工作区视觉效果。": {
"message": "The main window supports a local background image so you can create a more personal workspace look without sacrificing panel readability."
},
"使用本地图片作为主窗口的 Background Image": {
"message": "Use a local image as the main window Background Image"
},
"通过 cover、contain、stretch 和 tile 控制图片铺法": {
"message": "Control the image layout with cover, contain, stretch, and tile"
},
"分别调整 Image Opacity 和 Background Content Opacity": {
"message": "Adjust Image Opacity and Background Content Opacity separately"
},
"终端": {
"message": "Terminal"
},
@@ -245,6 +263,9 @@
"支持 `.dgfy` 导入导出,并处理远端与本地版本冲突": {
"message": "Supports `.dgfy` import/export and handles version conflicts between remote and local data"
},
"加载预览中": {
"message": "Loading preview"
},
"支持 SSH、串口、本地 shell、SFTP 和配置备份的桌面终端客户端": {
"message": "A desktop terminal client for SSH, serial, local shell, SFTP, and configuration backups"
},
@@ -19,8 +19,8 @@ Understanding the differences helps explain why some panels or enhancements only
|--------------|------------------|------------------|
| SSH | Remote Linux / Unix administration | SFTP, OTP, resource monitoring, proxy, jump host, tunnels |
| Local Terminal | Local shell work, scripts, builds | Shared terminal UI, command history, split panes |
| Telnet | Legacy devices, lab environments, compatibility troubleshooting | Terminal workspace features, but not SSH-only features |
| Serial | Routers, switches, boards, embedded debug ports | Serial port settings plus terminal workspace features |
| Telnet | Legacy devices, lab environments, compatibility troubleshooting | Terminal workspace features with `Backspace Mode`, but not SSH-only features |
| Serial | Routers, switches, boards, embedded debug ports | Serial port settings, `Backspace Mode`, and terminal workspace features |
## SSH
@@ -77,6 +77,8 @@ You still get NyaTerm's terminal workspace model, but not SSH-specific security
If your goal is simply to open a traditional remote terminal quickly, Telnet can be the more direct choice.
For devices that expect specific erase behavior, Telnet also exposes `Backspace Mode` so you can choose `Ctrl+H (BS)` or `DEL (0x7F)`.
## Serial
Serial sessions are useful for connecting to:
@@ -92,6 +94,7 @@ When creating a serial session, you can configure:
- Data bits
- Parity
- Stop bits
- `Backspace Mode`
Serial sessions still live inside NyaTerm's tabbed and split workspace, so you can watch serial output in one pane while running commands in an SSH or local terminal pane.
@@ -47,6 +47,27 @@ NyaTerm provides two related helpers for session workflows.
While typing, NyaTerm can suggest commands based on history. This is useful for repeated operational commands, build commands, and troubleshooting scripts.
## AI Assistant from the terminal
NyaTerm can invoke AI workflows directly from terminal context instead of forcing you to copy text into a separate tool.
### Common entry points
- **Explain recent output** when you want the latest terminal activity summarized immediately
- **Explain selected text** for a highlighted log fragment or command result
- **Analyze error** when terminal output already contains a failure you want help with
- **Generate command** when you want AI to propose the next step from the current session context
### Inline terminal capture
During Agent execution, NyaTerm can capture AI command execution events and show a compact inline preview of command output close to the terminal workflow.
The amount of inline output is controlled by `Terminal Output Lines` in **Settings → AI → Agent Settings**:
- `Terminal Output Lines` sets how many output lines are shown after each AI-executed command
- Set it to `0` to disable inline terminal previews
- This setting changes the AI workflow feedback, not the underlying shell execution itself
## Optional terminal enhancements
These features are intentionally opt-in rather than enabled all at once.
@@ -15,6 +15,17 @@ In **Settings → Appearance**, you can configure these separately:
If you just want a quick theme switch, you can also use **View → Theme** from the top menu.
## Background image
In **Settings → Appearance**, the main window can now use a local wallpaper. The controls are easiest to follow by their exact UI names:
- `Background Image` — choose the local file rendered behind the main workspace
- `Image Sizing` — choose how the image is shown with `cover`, `contain`, `stretch`, or `tile`
- `Image Opacity` — control how strongly the wallpaper shows through the theme
- `Background Content Opacity` — control how translucent workspace panels and content surfaces become
This only affects the main window workspace. Settings and child windows stay solid so forms, dialogs, and secondary windows remain readable.
## Fonts and font size
In **Settings → Appearance**, you can adjust:
@@ -31,6 +42,8 @@ NyaTerm includes these built-in fonts:
System-installed fonts are also listed so you can extend the fallback chain.
System font discovery now runs asynchronously, so you may briefly see `Loading system fonts...` when opening the font picker. That simply means the app is still collecting installed fonts in the background.
## Cursor and ligatures
Appearance settings also expose terminal details such as:
+114
View File
@@ -9,6 +9,63 @@ export type ChangelogRelease = {
};
const changelogReleasesEn: ChangelogRelease[] = [
{
version: '[1.0.7] - 2026-05-21',
sections: [
{
title: 'Changed',
items: [
'**appearance:** Improve font selection UI and show a `Loading system fonts...` state while system fonts are being discovered.',
'**otp:** Improve OTP input slot layout and OTP code panel responsiveness on narrower screens.',
'**profiles:** Add multiple Cargo build profiles for debugging and release workflows.',
],
},
{
title: 'Fixed',
items: ['**csp:** Correct CSP asset protocol handling so local assets such as background images load reliably.'],
},
{
title: 'Performance',
items: ['**appearance:** Load system fonts asynchronously to keep the Appearance tab responsive.'],
},
],
},
{
version: '[1.0.6] - 2026-05-21',
sections: [
{
title: 'Added',
items: [
'**appearance:** Add `Background Image` customization for the main window, including `Image Sizing`, `Image Opacity`, and `Background Content Opacity` controls.',
'**sessions:** Add `Backspace Mode` selection for Telnet and Serial sessions with `Ctrl+H (BS)` and `DEL (0x7F)` options.',
],
},
{
title: 'Changed',
items: ['**resource-monitor:** Refresh the resource monitor with clearer cards and improved visual hierarchy.'],
},
{
title: 'Fixed',
items: [
'**shortcuts:** Prevent Shift-modified terminal input from being mistaken for application shortcuts, restoring uppercase input such as `Shift+C`, `Shift+V`, and `Shift+X`.',
],
},
],
},
{
version: '[1.0.5] - 2026-05-19',
sections: [
{
title: 'Added',
items: [
'**ai:** Capture AI command execution events and render inline terminal output during agent-driven workflows.',
'**ai:** Add the `Terminal Output Lines` setting to control how many inline output lines are shown for AI-executed commands.',
'**terminal:** Add AI Execution Profile selection to terminal session forms.',
'**window:** Improve main-window modal management so child windows keep the workspace in a clearer modal state.',
],
},
],
},
{
version: '[1.0.4] - 2026-05-19',
sections: [
@@ -948,6 +1005,63 @@ const changelogReleasesEn: ChangelogRelease[] = [
];
const changelogReleasesZhCN: ChangelogRelease[] = [
{
version: '[1.0.7] - 2026-05-21',
sections: [
{
title: '变更',
items: [
'**appearance:** 改进字体选择界面,并在系统字体发现过程中显示 `Loading system fonts...` 加载状态。',
'**otp:** 优化 OTP 输入槽位布局和 OTP 代码面板在窄屏下的响应式表现。',
'**profiles:** 为调试和发布流程新增多个 Cargo 构建 profile。',
],
},
{
title: '修复',
items: ['**csp:** 修正 CSP 中的资源协议处理,确保背景图等本地资源可以稳定加载。'],
},
{
title: '性能',
items: ['**appearance:** 改为异步加载系统字体,保持外观设置页的响应速度。'],
},
],
},
{
version: '[1.0.6] - 2026-05-21',
sections: [
{
title: '新增',
items: [
'**appearance:** 为主窗口新增 `Background Image` 自定义能力,支持 `Image Sizing`、`Image Opacity` 和 `Background Content Opacity` 控制。',
'**sessions:** 为 Telnet 和串口会话新增 `Backspace Mode` 选项,可在 `Ctrl+H (BS)` 与 `DEL (0x7F)` 之间切换。',
],
},
{
title: '变更',
items: ['**resource-monitor:** 刷新资源监控面板的卡片样式和视觉层级。'],
},
{
title: '修复',
items: [
'**shortcuts:** 修复 Shift 修饰键输入被误判为应用快捷键的问题,恢复 `Shift+C`、`Shift+V`、`Shift+X` 等大写输入。',
],
},
],
},
{
version: '[1.0.5] - 2026-05-19',
sections: [
{
title: '新增',
items: [
'**ai:** 捕获 AI 命令执行事件,并在 Agent 工作流中渲染终端内联输出。',
'**ai:** 新增 `Terminal Output Lines` 设置,用于控制 AI 执行命令时显示的内联输出行数。',
'**terminal:** 为终端会话表单新增 AI Execution Profile 选择。',
'**window:** 改进主窗口模态管理,让子窗口触发时工作区的模态状态更清晰。',
],
},
],
},
{
version: '[1.0.4] - 2026-05-19',
sections: [
+135 -10
View File
@@ -468,7 +468,7 @@
.featureTabs {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
gap: 0.55rem;
padding: 0.45rem;
border: 1px solid rgba(148, 163, 184, 0.28);
@@ -742,20 +742,29 @@
padding: 0.65rem;
}
.featurePreviewImageSlot {
.featurePreviewStage {
position: relative;
width: 100%;
overflow: hidden;
border-radius: 6px;
background:
radial-gradient(circle at top left, rgba(37, 99, 235, 0.14), transparent 52%),
linear-gradient(180deg, rgba(226, 232, 240, 0.95) 0%, rgba(241, 245, 249, 0.94) 100%);
isolation: isolate;
}
.featurePreviewDark {
display: none;
[data-theme='dark'] .featurePreviewStage {
background:
radial-gradient(circle at top left, rgba(143, 194, 255, 0.14), transparent 48%),
linear-gradient(180deg, rgba(17, 23, 32, 0.98) 0%, rgba(22, 28, 36, 0.98) 100%);
}
[data-theme='dark'] .featurePreviewLight {
display: none;
}
[data-theme='dark'] .featurePreviewDark {
display: block;
.featurePreviewPlaceholder {
width: 100%;
aspect-ratio: 16 / 10;
background:
linear-gradient(120deg, rgba(255, 255, 255, 0.3) 8%, rgba(255, 255, 255, 0.68) 18%, rgba(255, 255, 255, 0.28) 33%),
linear-gradient(180deg, rgba(241, 245, 249, 0.5), rgba(226, 232, 240, 0.72));
}
.featurePreviewImage {
@@ -765,6 +774,122 @@
border-radius: 6px;
object-fit: contain;
object-position: center;
transition:
filter 0.26s ease,
opacity 0.26s ease,
transform 0.26s ease;
animation: featurePreviewImageFadeIn 0.32s ease both;
}
.featurePreviewStageLoading .featurePreviewImage {
filter: blur(12px) saturate(0.82);
opacity: 0.38;
transform: scale(1.015);
}
.featurePreviewLoadingOverlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(248, 250, 252, 0.24);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
}
[data-theme='dark'] .featurePreviewLoadingOverlay {
background: rgba(8, 12, 18, 0.28);
}
.featurePreviewLoadingGlass {
position: relative;
z-index: 1;
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 0.7rem;
padding: 1rem 1.15rem;
border: 1px solid rgba(148, 163, 184, 0.3);
border-radius: 999px;
background: rgba(255, 255, 255, 0.56);
box-shadow: 0 22px 48px rgba(15, 23, 42, 0.14);
}
[data-theme='dark'] .featurePreviewLoadingGlass {
border-color: rgba(148, 163, 184, 0.18);
background: rgba(20, 27, 36, 0.62);
box-shadow: 0 22px 48px rgba(0, 0, 0, 0.24);
}
.featurePreviewSpinner {
width: 2.2rem;
height: 2.2rem;
border: 2px solid rgba(37, 99, 235, 0.16);
border-top-color: #2563eb;
border-radius: 999px;
animation: featurePreviewSpinner 0.9s linear infinite;
}
[data-theme='dark'] .featurePreviewSpinner {
border-color: rgba(143, 194, 255, 0.16);
border-top-color: #8fc2ff;
}
.featurePreviewLoadingText {
color: #0f172a;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.01em;
}
[data-theme='dark'] .featurePreviewLoadingText {
color: #eef3f8;
}
.featurePreviewLoadingShimmer {
position: absolute;
inset: -35%;
background: linear-gradient(115deg, transparent 28%, rgba(255, 255, 255, 0.58) 50%, transparent 72%);
opacity: 0.8;
transform: translateX(-46%);
animation: featurePreviewShimmer 1.9s linear infinite;
}
[data-theme='dark'] .featurePreviewLoadingShimmer {
background: linear-gradient(115deg, transparent 28%, rgba(143, 194, 255, 0.22) 50%, transparent 72%);
}
@keyframes featurePreviewImageFadeIn {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes featurePreviewSpinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes featurePreviewShimmer {
from {
transform: translateX(-46%);
}
to {
transform: translateX(46%);
}
}
/* Journey */
+209 -62
View File
@@ -1,4 +1,4 @@
import React, {useCallback, useEffect, useState} from 'react';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import clsx from 'clsx';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useBaseUrl from '@docusaurus/useBaseUrl';
@@ -19,9 +19,9 @@ type FeatureTab = {
darkImage: string;
};
type SummaryCard = {
title: string;
description: string;
type FeatureTabWithUrls = FeatureTab & {
lightImageUrl: string;
darkImageUrl: string;
};
type DownloadPlatformKey =
@@ -72,6 +72,90 @@ const downloadPlatforms: DownloadPlatform[] = [
const featureAutoplayStoppedKey = 'nyaterm-home-feature-tabs-autoplay-stopped';
const featureAutoplayIntervalMs = 4200;
const featurePreviewImageCache = new Map<string, 'loaded' | Promise<boolean>>();
type ThemeMode = 'light' | 'dark';
function resolveAssetUrl(baseUrl: string, assetPath: string) {
if (/^(?:[a-z]+:)?\/\//i.test(assetPath)) {
return assetPath;
}
const normalizedBase = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
const normalizedPath = assetPath.startsWith('/') ? assetPath.slice(1) : assetPath;
return `${normalizedBase}${normalizedPath}`;
}
function isFeaturePreviewImageLoaded(url: string) {
return featurePreviewImageCache.get(url) === 'loaded';
}
function preloadFeaturePreviewImage(url: string): Promise<boolean> {
if (!url) {
return Promise.resolve(false);
}
const cached = featurePreviewImageCache.get(url);
if (cached === 'loaded') {
return Promise.resolve(true);
}
if (cached) {
return cached;
}
if (typeof Image === 'undefined') {
return Promise.resolve(false);
}
const pending = new Promise<boolean>((resolve) => {
const image = new Image();
image.onload = () => {
featurePreviewImageCache.set(url, 'loaded');
resolve(true);
};
image.onerror = () => {
featurePreviewImageCache.delete(url);
resolve(false);
};
image.src = url;
});
featurePreviewImageCache.set(url, pending);
return pending;
}
function getThemeModeFromDom(): ThemeMode {
if (typeof document === 'undefined') {
return 'light';
}
return document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
}
function useThemeMode() {
const [themeMode, setThemeMode] = useState<ThemeMode>(() => getThemeModeFromDom());
useEffect(() => {
if (typeof document === 'undefined') {
return;
}
const root = document.documentElement;
const syncThemeMode = () => setThemeMode(getThemeModeFromDom());
syncThemeMode();
const observer = new MutationObserver(syncThemeMode);
observer.observe(root, {attributes: true, attributeFilter: ['data-theme']});
return () => observer.disconnect();
}, []);
return themeMode;
}
function detectDownloadPlatform(): DownloadPlatform {
if (typeof navigator === 'undefined') {
@@ -168,7 +252,7 @@ function DownloadButton() {
entropyPromise
?.then((values) => {
const detectedOs = values.platform?.toLowerCase() ?? userAgentData.platform?.toLowerCase() ?? '';
const detectedOs = values.platform?.toLowerCase() ?? userAgentData?.platform?.toLowerCase() ?? '';
const architecture = values.architecture?.toLowerCase() ?? '';
if (detectedOs) {
@@ -229,31 +313,83 @@ function DownloadButton() {
function FeaturePreview({
title,
lightImage,
darkImage,
lightImageUrl,
darkImageUrl,
}: {
title: string;
lightImage: string;
darkImage: string;
lightImageUrl: string;
darkImageUrl: string;
}) {
const lightImageUrl = useBaseUrl(lightImage);
const darkImageUrl = useBaseUrl(darkImage);
const themeMode = useThemeMode();
const targetImageUrl = themeMode === 'dark' ? darkImageUrl : lightImageUrl;
const [displayedImageUrl, setDisplayedImageUrl] = useState(() =>
isFeaturePreviewImageLoaded(targetImageUrl) ? targetImageUrl : '',
);
const [isLoading, setIsLoading] = useState(() => !isFeaturePreviewImageLoaded(targetImageUrl));
useEffect(() => {
let cancelled = false;
if (!targetImageUrl) {
setDisplayedImageUrl('');
setIsLoading(false);
return;
}
if (isFeaturePreviewImageLoaded(targetImageUrl)) {
setDisplayedImageUrl(targetImageUrl);
setIsLoading(false);
return;
}
setIsLoading(true);
void preloadFeaturePreviewImage(targetImageUrl).then((loaded) => {
if (cancelled) {
return;
}
if (loaded) {
setDisplayedImageUrl(targetImageUrl);
}
setIsLoading(false);
});
return () => {
cancelled = true;
};
}, [targetImageUrl]);
return (
<div className={styles.featurePreview} aria-label={title}>
<div className={clsx(styles.featurePreviewImageSlot, styles.featurePreviewLight)}>
<img
className={styles.featurePreviewImage}
src={lightImageUrl}
alt={translate({message: 'NyaTerm 日间主题功能截图'})}
/>
</div>
<div className={clsx(styles.featurePreviewImageSlot, styles.featurePreviewDark)}>
<img
className={styles.featurePreviewImage}
src={darkImageUrl}
alt={translate({message: 'NyaTerm 夜间主题功能截图'})}
/>
<div className={styles.featurePreview} aria-label={title} aria-busy={isLoading}>
<div className={clsx(styles.featurePreviewStage, isLoading && styles.featurePreviewStageLoading)}>
{displayedImageUrl ? (
<img
key={displayedImageUrl}
className={styles.featurePreviewImage}
src={displayedImageUrl}
alt={
themeMode === 'dark'
? translate({message: 'NyaTerm 夜间主题功能截图'})
: translate({message: 'NyaTerm 日间主题功能截图'})
}
/>
) : (
<div className={styles.featurePreviewPlaceholder} aria-hidden="true" />
)}
{isLoading ? (
<div className={styles.featurePreviewLoadingOverlay} aria-hidden="true">
<div className={styles.featurePreviewLoadingShimmer} />
<div className={styles.featurePreviewLoadingGlass}>
<span className={styles.featurePreviewSpinner} />
<span className={styles.featurePreviewLoadingText}>
<Translate>加载预览中</Translate>
</span>
</div>
</div>
) : null}
</div>
</div>
);
@@ -352,15 +488,25 @@ function HomepageHeader() {
}
function FeaturesSection({features}: {features: FeatureTab[]}) {
const baseUrl = useBaseUrl('/');
const resolvedFeatures = useMemo<FeatureTabWithUrls[]>(
() =>
features.map((feature) => ({
...feature,
lightImageUrl: resolveAssetUrl(baseUrl, feature.lightImage),
darkImageUrl: resolveAssetUrl(baseUrl, feature.darkImage),
})),
[baseUrl, features],
);
const [activeValue, setActiveValue] = useState(features[0]?.value ?? '');
const [autoplayStopped, setAutoplayStopped] = useState(false);
const activeIndex = Math.max(
0,
features.findIndex((feature) => feature.value === activeValue),
resolvedFeatures.findIndex((feature) => feature.value === activeValue),
);
const activeFeature = features[activeIndex] ?? features[0];
const isAutoplaying = !autoplayStopped && features.length > 1;
const activeFeature = resolvedFeatures[activeIndex] ?? resolvedFeatures[0];
const isAutoplaying = !autoplayStopped && resolvedFeatures.length > 1;
const stopAutoplay = useCallback(() => {
setAutoplayStopped(true);
@@ -380,14 +526,14 @@ function FeaturesSection({features}: {features: FeatureTab[]}) {
const selectFeatureByOffset = useCallback(
(offset: number) => {
const nextIndex = (activeIndex + offset + features.length) % features.length;
const nextValue = features[nextIndex]?.value;
const nextIndex = (activeIndex + offset + resolvedFeatures.length) % resolvedFeatures.length;
const nextValue = resolvedFeatures[nextIndex]?.value;
if (nextValue) {
selectFeature(nextValue);
}
},
[activeIndex, features, selectFeature],
[activeIndex, resolvedFeatures, selectFeature],
);
useEffect(() => {
@@ -399,10 +545,17 @@ function FeaturesSection({features}: {features: FeatureTab[]}) {
}, []);
useEffect(() => {
if (!features.some((feature) => feature.value === activeValue)) {
setActiveValue(features[0]?.value ?? '');
if (!resolvedFeatures.some((feature) => feature.value === activeValue)) {
setActiveValue(resolvedFeatures[0]?.value ?? '');
}
}, [activeValue, features]);
}, [activeValue, resolvedFeatures]);
useEffect(() => {
resolvedFeatures.forEach((feature) => {
void preloadFeaturePreviewImage(feature.lightImageUrl);
void preloadFeaturePreviewImage(feature.darkImageUrl);
});
}, [resolvedFeatures]);
useEffect(() => {
if (autoplayStopped) {
@@ -438,14 +591,14 @@ function FeaturesSection({features}: {features: FeatureTab[]}) {
const intervalId = window.setInterval(() => {
setActiveValue((currentValue) => {
const currentIndex = features.findIndex((feature) => feature.value === currentValue);
const nextIndex = currentIndex >= 0 ? (currentIndex + 1) % features.length : 0;
return features[nextIndex]?.value ?? currentValue;
const currentIndex = resolvedFeatures.findIndex((feature) => feature.value === currentValue);
const nextIndex = currentIndex >= 0 ? (currentIndex + 1) % resolvedFeatures.length : 0;
return resolvedFeatures[nextIndex]?.value ?? currentValue;
});
}, featureAutoplayIntervalMs);
return () => window.clearInterval(intervalId);
}, [features, isAutoplaying]);
}, [isAutoplaying, resolvedFeatures]);
if (!activeFeature) {
return null;
@@ -456,7 +609,7 @@ function FeaturesSection({features}: {features: FeatureTab[]}) {
<div className="container">
<div className={styles.featureShowcase}>
<div className={styles.featureTabs} role="tablist" aria-label={translate({message: '功能展示'})}>
{features.map((feature, index) => {
{resolvedFeatures.map((feature, index) => {
const isActive = feature.value === activeFeature.value;
return (
@@ -482,12 +635,12 @@ function FeaturesSection({features}: {features: FeatureTab[]}) {
if (event.key === 'Home') {
event.preventDefault();
selectFeature(features[0]?.value ?? feature.value);
selectFeature(resolvedFeatures[0]?.value ?? feature.value);
}
if (event.key === 'End') {
event.preventDefault();
selectFeature(features[features.length - 1]?.value ?? feature.value);
selectFeature(resolvedFeatures[resolvedFeatures.length - 1]?.value ?? feature.value);
}
}}>
<span className={styles.featureTabIndex}>{String(index + 1).padStart(2, '0')}</span>
@@ -523,8 +676,8 @@ function FeaturesSection({features}: {features: FeatureTab[]}) {
<FeaturePreview
title={activeFeature.title}
lightImage={activeFeature.lightImage}
darkImage={activeFeature.darkImage}
lightImageUrl={activeFeature.lightImageUrl}
darkImageUrl={activeFeature.darkImageUrl}
/>
</div>
</div>
@@ -583,25 +736,6 @@ function JourneySection() {
}
export default function Home(): React.ReactElement {
const overviewCards: SummaryCard[] = [
{
title: translate({message: '多种终端会话'}),
description: translate({message: '支持 SSH、本地 shell、Telnet 和串口会话,可在标签页和分屏中同时工作。'}),
},
{
title: translate({message: '终端输出阅读'}),
description: translate({message: '提供搜索、命令历史建议、时间戳、关键词高亮和大输出保护。'}),
},
{
title: translate({message: '文件、认证与网络'}),
description: translate({message: '包含 SFTP、OTP、代理、跳板机、端口转发和主机密钥校验。'}),
},
{
title: translate({message: '配置同步与备份'}),
description: translate({message: '可通过 WebDAV 或 S3 兼容存储保存加密配置快照。'}),
},
];
const featureTabs: FeatureTab[] = [
{
value: 'workspace',
@@ -616,6 +750,19 @@ export default function Home(): React.ReactElement {
lightImage: '/img/home/overview-light.png',
darkImage: '/img/home/overview-dark.png',
},
{
value: 'appearance',
label: translate({message: '外观'}),
title: translate({message: '用背景图定制主窗口氛围'}),
description: translate({message: '主窗口支持本地背景图,可在保留面板可读性的同时营造更个性化的工作区视觉效果。'}),
bullets: [
translate({message: '使用本地图片作为主窗口的 Background Image'}),
translate({message: '通过 cover、contain、stretch 和 tile 控制图片铺法'}),
translate({message: '分别调整 Image Opacity 和 Background Content Opacity'}),
],
lightImage: '/img/home/cover-light.png',
darkImage: '/img/home/cover-dark.png',
},
{
value: 'terminal',
label: translate({message: '终端'}),
Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 KiB