diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bde5845..3df04ffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 7dea9a73..bc50d840 100644 --- a/README.md +++ b/README.md @@ -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

+## 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. + +

+ + + + NyaTerm background image customization + +

+ ## 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` | --- diff --git a/README.zh-CN.md b/README.zh-CN.md index 14f2d335..833e606b 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -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 适合每天在服务器、本地命令、设备调试和配置文件之

+## 外观与背景图 + +为主窗口设置本地壁纸,并通过 `Image Sizing`、`Image Opacity` 和 `Background Content Opacity` 平衡视觉效果与面板可读性。 + +

+ + + + NyaTerm 背景图自定义 + +

+ ## 终端增强 在终端中使用命令历史、搜索、翻译、动作链接、时间戳、关键词高亮和大输出保护。 @@ -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` | --- diff --git a/docs-site/docs/guide/ai-assistant.md b/docs-site/docs/guide/ai-assistant.md index 5817ef78..61b10a82 100644 --- a/docs-site/docs/guide/ai-assistant.md +++ b/docs-site/docs/guide/ai-assistant.md @@ -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 的思考链路。 diff --git a/docs-site/docs/guide/session-types.md b/docs-site/docs/guide/session-types.md index b381f5ef..b408fed3 100644 --- a/docs-site/docs/guide/session-types.md +++ b/docs-site/docs/guide/session-types.md @@ -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 或本地终端里执行命令。 diff --git a/docs-site/docs/guide/terminal.md b/docs-site/docs/guide/terminal.md index 38252631..7d9f6cb2 100644 --- a/docs-site/docs/guide/terminal.md +++ b/docs-site/docs/guide/terminal.md @@ -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 能力支持: diff --git a/docs-site/docs/guide/themes.md b/docs-site/docs/guide/themes.md index f2848e92..49c32c7e 100644 --- a/docs-site/docs/guide/themes.md +++ b/docs-site/docs/guide/themes.md @@ -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...`。这表示应用正在后台整理系统字体,不影响你先选择内置字体或已有回退链路。 + ## 光标与连字 外观设置还支持这些终端细节: diff --git a/docs-site/i18n/en/code.json b/docs-site/i18n/en/code.json index b1bd4b85..56b4a7ed 100644 --- a/docs-site/i18n/en/code.json +++ b/docs-site/i18n/en/code.json @@ -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" }, diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/session-types.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/session-types.md index 2cc9e4a8..1133f958 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/session-types.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/session-types.md @@ -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. diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/terminal.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/terminal.md index e73a6dd6..67f10cc4 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/terminal.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/terminal.md @@ -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. diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/themes.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/themes.md index 93432be1..27df41de 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/themes.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guide/themes.md @@ -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: diff --git a/docs-site/src/data/changelogData.ts b/docs-site/src/data/changelogData.ts index 59f447e2..f9f16c58 100644 --- a/docs-site/src/data/changelogData.ts +++ b/docs-site/src/data/changelogData.ts @@ -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: [ diff --git a/docs-site/src/pages/index.module.css b/docs-site/src/pages/index.module.css index a15f03df..73204c08 100644 --- a/docs-site/src/pages/index.module.css +++ b/docs-site/src/pages/index.module.css @@ -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 */ diff --git a/docs-site/src/pages/index.tsx b/docs-site/src/pages/index.tsx index 1e649bba..699807bb 100644 --- a/docs-site/src/pages/index.tsx +++ b/docs-site/src/pages/index.tsx @@ -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>(); + +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 { + 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((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(() => 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 ( -
-
- {translate({message: -
-
- {translate({message: +
+
+ {displayedImageUrl ? ( + { + ) : ( + ); @@ -352,15 +488,25 @@ function HomepageHeader() { } function FeaturesSection({features}: {features: FeatureTab[]}) { + const baseUrl = useBaseUrl('/'); + const resolvedFeatures = useMemo( + () => + 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[]}) {
- {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); } }}> {String(index + 1).padStart(2, '0')} @@ -523,8 +676,8 @@ function FeaturesSection({features}: {features: FeatureTab[]}) {
@@ -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: '终端'}), diff --git a/docs-site/static/img/home/cover-dark.png b/docs-site/static/img/home/cover-dark.png new file mode 100644 index 00000000..76968ce9 Binary files /dev/null and b/docs-site/static/img/home/cover-dark.png differ diff --git a/docs-site/static/img/home/cover-light.png b/docs-site/static/img/home/cover-light.png new file mode 100644 index 00000000..687551df Binary files /dev/null and b/docs-site/static/img/home/cover-light.png differ