mirror of
https://github.com/nyakang/nyaterm.git
synced 2026-11-06 16:07:55 +00:00
- 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.
319 lines
16 KiB
Markdown
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.
|