Files
nyaterm/AGENTS.md
T
Kang 010f1e4215 feat(agents): add Windows RDP support and remove ActiveX helper
- Introduced `nyaterm-windows-rdp` for Windows Native RDP integration, detailing its architecture and isolation requirements.
- Removed the `nyaterm-activex-helper` crate, including all related files and dependencies, to streamline the project and focus on modern RDP implementations.
- Updated documentation in `AGENTS.md` to reflect these changes and clarify the new architecture.
- Adjusted `Cargo.toml` and `Cargo.lock` to remove references to the deleted ActiveX helper and include the new RDP crate.
- Enhanced CI workflows to configure MSVC for Windows builds, ensuring compatibility with the new RDP implementation.
2026-11-02 19:17:20 +08:00

319 lines
16 KiB
Markdown

# Repository Guidelines
## Project Overview
NyaTerm is a native GPUI desktop application written in Rust. It provides SSH,
local shell, Telnet, Serial, RDP, VNC, SFTP, tunnels, OTP, AI assistance, and
encrypted sync and backup in one workspace.
This repository is a Rust 2024 Cargo workspace using resolver `3`. Preserve
compatibility with existing NyaTerm configuration, credentials, backups,
cloud-sync data, known hosts, and sessions. Any incompatible data-format change
must include explicit conversion logic and tests.
## Workspace Structure
* `crates/nyaterm-app`: executable entry point, bundled assets, logging setup,
and root-window creation.
* `crates/nyaterm-core`: UI-independent domain models, parsing, policies, AI
settings/risk/provider logic, schema-neutral serialization and encryption
contracts, and shared pure logic. Do not add GPUI dependencies here.
* `crates/nyaterm-desktop`: GPUI application composition, `AppShell`,
`NyaTermApp`, feature state, views, platform adapters, background-job
coordination, native HTTP adapters, and GPUI Entity stores.
* `crates/nyaterm-terminal`: terminal state machine, snapshots,
control-sequence handling, encoding, and graphics protocols. It must remain
independent of UI frameworks.
* `crates/nyaterm-terminal-gpui`: GPUI-specific terminal layout, input,
highlighting, images, and painting.
* `crates/nyaterm-transport`: local PTY, SSH, Telnet, Serial, SFTP, tunnels,
remote operations, and transfer-protocol runtime. It must remain independent
of GPUI and desktop presentation types.
* `crates/nyaterm-ui`: shared GPUI theme tokens, the `gpui-kit`
integration boundary, and reusable NyaTerm presentation and interaction
widgets.
* `crates/nyaterm-store`: persistence implementation, transactions, redb
schema, encryption adapters, and database compatibility readers. Keep pure
data models and serialization policies in `nyaterm-core`.
* `crates/nyaterm-remote-desktop`: UI-independent RDP/VNC session management,
framebuffer and input models, certificate policy, clipboard state, and the
RDP/VNC helper IPC contracts. It owns no protocol decoder; both live in the
helper crates below.
* `crates/nyaterm-rdp-helper`: isolated IronRDP helper process that communicates
with the application through the typed IPC protocol in
`nyaterm-remote-desktop`.
* `crates/nyaterm-vnc-helper`: isolated VNC helper process using the same IPC
protocol. It owns the forked `vnc-rs` decoders, the VNC reconnect ladder, and
the server-facing policy gates (`view_only`, `shared`, clipboard enablement).
Those gates must stay enforced here, not only in the application.
* `crates/nyaterm-windows-rdp`: explicitly selected Windows Native RDP system
component exception. Thread-bound ActiveX/ATL host and staged controller;
COM runs on the creating UI/STA thread outside all GPUI borrows. No GPUI
production dependency, no protocol decoder or exposed COM interfaces.
* `crates/nyaterm-otp`: bundled HOTP/TOTP implementation.
* `crates/nyaterm-app/assets`: bundled icons and images. Assets under
`icons/**` are normally tintable and rendered through `svg()` or
`mono_icon()`; full-color assets use `img()` or `color_icon()`. Preserve the
rendering distinction when adding assets.
* Third-party dependencies that NyaTerm patches are not vendored. Each is a
patch series on a fork under <https://github.com/nyakang> on branch
`nyaterm`, consumed from a revision pinned in the root `Cargo.toml`:
`alacritty` (`alacritty_terminal`), `gpui-kit`, `IronRDP`
(`ironrdp-client`, `ironrdp-connector`), `russh`, `russh-sftp`, `sspi-rs`,
`vnc-rs`, `zed` (`gpui`, `gpui_platform`), and `zmodem2`. Each branch carries
a `NYATERM.md` recording its base revision, its patches, and how they were
validated.
* `temp/vendor/`: untracked, read-only copies of those sources, kept only so
they can be read locally. Nothing under `temp/` is compiled. **Editing
anything there has no effect on the build and produces no error** — change
the fork branch instead.
## Application Architecture
`NyaTermApp` is the central GPUI composition owner. Feature-specific state
lives in focused structs for connections, commands, remote operations, remote
desktop, security, settings, AI, terminal presentation, sessions, transfers,
sync, translation, updates, tunnels, recording, and shell behavior.
Persisted collections and compatibility-sensitive catalogs belong to their
feature owners. Schema-neutral persistence formats and parsing contracts belong
in `nyaterm-core`; database execution and compatibility readers belong in
`nyaterm-store`.
The GPUI Entity stores each own state that `NyaTermApp` does not own:
* `StartupRestoreStore`: startup-restore queue.
* `OverlayStore`: authoritative quick-switch overlay state.
For new or substantially changed features:
* Prefer a focused feature-state struct or a deliberately authoritative GPUI
Entity over new top-level `NyaTermApp` fields.
* Keep exactly one authoritative mutable owner for each piece of state. Do not
mirror independently mutable state between `NyaTermApp`, feature state, and
Entity stores.
* If a method only reads and writes one feature-state struct, place that logic
on the state and keep the `NyaTermApp` method as a notifier or adapter when
needed.
* Keep GPUI element construction in views. Pure state types should not render
UI.
* Use typed events to communicate from background jobs to GPUI state.
* Never perform filesystem, database, network, SSH, SFTP, subprocess, image
decoding, or other blocking work in a render path or a long-running GPUI
update callback.
* Keep terminal parsing, snapshots, graphics protocols, and wire handling in
`nyaterm-terminal`.
* Keep terminal layout, GPUI input adapters, highlighting, image presentation,
and painting in `nyaterm-terminal-gpui`.
* Keep transport code independent of GPUI and desktop presentation types.
* Keep remote-desktop protocol and session logic in
`nyaterm-remote-desktop`; keep GPUI presentation in `nyaterm-desktop`.
* Keep protocol decoders that parse server-controlled bytes in the helper
crates, never in a crate the application links. Both helpers must translate a
decoder panic into a fatal IPC error rather than dying silently. Windows Native,
selected explicitly by the user, hosts the installed Windows system component
in process; the IronRDP/VNC decoder isolation requirement continues to apply.
* Keep persistence logic out of GPUI views.
Use normal Rust module trees with `mod.rs` files or sibling module
declarations. Do not add `#[path = "..."]` declarations or `use super::*`
imports. Name dependencies explicitly, including in tests.
Avoid broad exports from crate roots and shared feature preludes. Import
models, services, GPUI types, and helpers from their authoritative modules.
## UI and Input Rules
Ordinary forms, prompts, searches, menus, selects, switches, and dialogs should
use the wrappers exposed by `nyaterm-ui`. That crate owns the
`gpui-kit` integration, theme mapping, and stable NyaTerm component API;
desktop feature modules must not depend directly on `gpui-kit`.
Ordinary text inputs should use `nyaterm-ui::NyaInput` and `NyaInputState`,
either owned by a focused feature or through the id-keyed registry in
`features/text_inputs.rs`. Do not add hand-painted ordinary text inputs.
Terminal input, paste review, and `RemoteTextEditor` are full editing surfaces
with dedicated selection, undo, IME, and command handling. Do not replace them
with single-line registry fields.
Text-field wrappers must have a definite height and width. Parent click
handlers must not immediately steal focus from a field. Keep controls usable
with keyboard navigation and ensure overlays and child windows restore focus
predictably.
GPUI's `overflow_y_scroll()` enables scrolling but paints nothing, and `scrollbar_width` only reserves gutter space, so use `NyaScrollable` (the `ScrollableElement` trait re-exported by `nyaterm-ui`):
* In-flow containers: use `overflow_*_scrollbar()` instead of
`overflow_*_scroll()`, and do not also reserve a `scrollbar_width` gutter -
the bar overlays the content.
* Virtualized lists (`uniform_list`) and any caller-owned scroll handle: keep
`track_scroll` on the list and attach `.vertical_scrollbar(&handle)` to the
non-scrolling parent. An absolutely positioned overlay inside a scrolling
element is translated by the scroll offset and would scroll away.
* Absolutely positioned popups must not use `overflow_*_scrollbar()`. The
wrapper inherits only size and flex styles, so `position`/`top`/`left` would
move onto the inner content node and break the popup's placement. Give the
popup a non-scrolling shell and scroll an in-flow child instead.
* A `Scrollbar` inside a caller-positioned overlay needs
`viewport_from_layout()`; otherwise it paints at the scroll handle's own
bounds and ignores the overlay.
Scrollbars auto-hide (`ScrollbarMode::Hover`), installed once in
`theme_bridge.rs`. A bar fades in while the pointer is anywhere over its scroll
viewport or that axis is scrolling, and out after idle, so a horizontal bar is
discoverable without pinning it open. Two further consequences:
* A hand-positioned `Scrollbar` overlay must span the whole scroll viewport,
not just the strip the track occupies. Reveal-on-hover watches the bar's own
hitbox, so a strip-sized overlay only reacts within a track width of the edge.
`overflow_*_scrollbar()` and `vertical_scrollbar(&handle)` already do this.
* Where the two axes ride different scroll handles they are separate
`Scrollbar` elements with separate reveal state, and the upstream
component's corner-avoidance does not apply, so inset one overlay by a track
width.
Anything that writes `Theme::scrollbar_mode` or the `scrollbar*` colors directly
must call `Theme::sync_base(cx)` afterwards. `Scrollbar` reads the
`gpui_base::Theme` projection, which those assignments do not touch.
The terminal scrollback scrollbar is hand-rolled as a reserved flex column with
its own drag and overview ruler, and is deliberately exempt from all of the
above.
## Persistence and Compatibility
Changes involving redb, credentials, known hosts, OTP, cloud sync, portable
snapshots, `.nya` backups, AI or translation secrets, and application settings
are compatibility-sensitive.
Before changing these areas:
* Preserve table names, keys, serialized field names, document keys,
encryption prefixes, master-key wrapping, backup formats, and fallback
decryption behavior unless the change explicitly updates the data contract.
* Test both new-data round trips and loading representative data written by
supported NyaTerm versions.
* Do not silently discard unknown or unsupported fields.
* Validate fully before overwriting existing user data.
* Keep secret-bearing values masked when returning settings to the UI.
`nyaterm-store/src/storage/mod.rs` owns the database implementation, with
domain-specific modules under `nyaterm-store/src/storage/`. Treat existing
redb data, `.nya` backups, master-key wrapping, encrypted payload formats, and
text-document fallbacks as public compatibility contracts.
## Security Rules
Never log or commit:
* passwords or decrypted saved credentials;
* private-key contents or passphrases;
* OTP secrets or generated codes;
* AI, cloud-sync, OAuth, snippet, or translation API secrets;
* unredacted diagnostics containing terminal output, command context, or user
data.
Use custom redacted `Debug` implementations for secret-bearing structs when
debug output is required. Prefer typed secret wrappers and zeroize sensitive
buffers where practical.
Patching a third-party dependency means committing to its fork branch, pushing,
and bumping the pinned revision in the root `Cargo.toml`. Do not edit `temp/`.
Record the reason for the change and the validation performed on the patch
commit and in that branch's `NYATERM.md`, and keep the patch series split by
concern rather than squashed. Prefer rebasing the series onto a newer upstream
revision over accumulating snapshots.
## Build and Development Commands
Use package-specific checks while iterating:
* `cargo check -p nyaterm-app`
* `cargo test -p <crate-name>`
* `cargo run -p nyaterm-app --bin nyaterm`
Builtin/IronRDP and VNC run in helper processes that the application resolves
beside its own executable. Windows Native RDP is the explicitly selected
in-process system-component exception and requires MSVC with ATL when building.
`cargo run -p nyaterm-app --bin nyaterm` builds only the application, so build the
helpers into the same target directory first or the helper backends fail with
`HelperMissing`:
* `cargo build -p nyaterm-rdp-helper -p nyaterm-vnc-helper`
A bare `cargo build` or `cargo check` covers all three: they are the workspace
`default-members`. `NYATERM_RDP_HELPER` and `NYATERM_VNC_HELPER` override the
lookup with an explicit path. `scripts/release/package_native.py` is what puts the
helpers next to the application in release packages; its `HELPER_BINS` list must
name every helper.
Before review, run the relevant broader checks:
* `cargo check --workspace`
* `cargo test --workspace`
* `cargo fmt --all -- --check`
* `cargo clippy --workspace --all-targets`
Use `cargo fmt --all` only when intentionally applying formatting changes.
Platform-specific GPUI, PTY, Serial, SSH, clipboard, window, path, RDP/VNC,
and icon-rendering behavior must be verified on the affected operating system.
## Coding Style
Use standard `rustfmt` formatting and Rust 2024 idioms.
* Use `snake_case` for modules, functions, fields, and variables.
* Use `PascalCase` for structs, enums, and traits.
* Use `SCREAMING_SNAKE_CASE` for constants.
* Prefer explicit imports and narrow public interfaces.
* Prefer typed models and errors over loosely structured strings.
* Use small adapters at crate boundaries instead of importing
application-wide models into low-level crates.
* Comments should explain invariants, compatibility constraints, ownership, or
non-obvious performance decisions rather than restating code.
When splitting a large file, preserve its public facade where practical so
structural changes do not cause unrelated call-site churn. Prefer domain cuts
that move constants, records, helpers, tests, and dependencies together over
type-only splits that leave coupling behind.
## Testing Guidelines
Add tests beside the behavior being changed.
* Terminal parsing, snapshots, graphics, selection, input, and rendering tests
belong in `nyaterm-terminal` or `nyaterm-terminal-gpui`.
* SSH, SFTP, Telnet, Serial, tunnel, transfer, and terminal-session lifecycle
tests belong in `nyaterm-transport`.
* RDP/VNC protocol, framebuffer, input mapping, IPC, certificate, clipboard,
and reconnect tests belong in `nyaterm-remote-desktop`, `nyaterm-rdp-helper`,
or `nyaterm-vnc-helper`. Helper crates carry a `tests/lifecycle.rs` covering
the handshake, an ordinary disconnect, and crash/hang reaping; keep both in
step when the IPC contract changes.
* Storage changes require round-trip and supported-format compatibility tests.
* Credential and encryption changes require success, invalid-password,
corrupted-data, and compatibility-format tests.
* GPUI state changes should test state transitions separately from visual
rendering where possible.
Use descriptive, behavior-oriented test names.
## Commits and Pull Requests
Use Conventional Commit-style subjects:
`type(scope): imperative summary`
Common scopes include `terminal`, `transport`, `desktop`, `storage`, `ui`,
`remote-desktop`, `ai`, and `sync`.
Pull requests should include:
* a concise description of behavior and architectural impact;
* linked issues where applicable;
* commands and platforms tested;
* explicit notes for persistence, credentials, data compatibility, or forked
dependency changes, including the fork branch and revision a bump moves to.