Keep a modular monolith: domain rules and adapters communicate inside the desktop application unless a current lifecycle or isolation requirement justifies another process. Existing mux, CLI hook and acceptance-lab processes have concrete roles; they are not a reason to introduce generic microservices or a service locator.
The dependency direction is composition/UI → application capabilities → shared domain rules. Platform and I/O details adapt domain inputs/results. They must not make a domain crate depend on a view. This is a responsibility model, not a demand to rename all existing directories or create abstract interfaces everywhere.
| Area | Owns | Must not become |
|---|---|---|
nebula_settings |
Runtime settings, language registry, shared preference contracts | A UI/widget library |
nebula_split |
Split tree, geometry, navigation rules | Window management or rendering |
nebula_terminal |
Grid, VT processing, terminal/PTY behavior | Product panels or GPUI state |
nebula_config, nebula_config_derive |
Configuration abstractions and derives | Application orchestration |
pebrel-completions (source: nebula-completions/) |
Completion matching and presentation-independent results | Terminal view ownership |
nebula_app/src/completion.rs |
Completion request snapshots, shared source lifetime and per-pane source selection | A GPUI task owner or a shell interpreter |
nebula_app/src/display/suggest_engine.rs |
Shared candidate calculation from input snapshots; compatibility adapter for pane state | A second source registry or access to the terminal grid during calculation |
nebula_hook |
Small process/lifecycle hook bridge | An application dependency container |
nebula_app/src/i18n |
Static lookup, locale resolution and formatting | Runtime catalog parsing or UI ownership |
nebula_app/src/scrollbar.rs |
Shared overlay-scrollbar geometry, hit testing and drag mapping | A renderer, view owner or input dispatcher |
nebula_app/src/math |
Parse, validate, layout, compile and cache responsibilities | A duplicated per-shell math engine |
nebula_app/src/platform |
Explicit platform capabilities and native adapters | A dumping ground for unrelated logic |
nebula_app/src/ai_hook |
Normalized provider facts, bounded ordering, one shared pane lifecycle and owned installation policy; native Windows/Unix transports | Screen keyword rules or a separate state machine per UI shell |
nebula_app/src/platform/ssh_agent.rs |
Native agent endpoints, transport connection and bounded identity discovery | Host authentication policy or private key selection |
nebula_app/src/ssh_session/agent.rs |
SSH agent identity selection, signing outcomes and total discovery budget; fresh scope per host | A second authentication plan, credential store or agent forwarding service |
nebula_app/src/serial_session.rs |
Native serial port transport for non-PTY panes and the serial: destination syntax |
SSH authentication, remote files or a settings form |
nebula_app/src/ssh_session/integration.rs |
Authenticated exec/PTY orchestration for remote hook installation and shell startup | Provider policy or a second Agent state machine |
nebula_app/src/ai_agents |
Agent identity and structurally constrained screen observations | Authority to overwrite hook results or infer remote completion from silence |
nebula_app/src/gpui_shell |
GPUI views, UI state, commands and subscriptions | A second settings/domain implementation |
nebula_app/src/product_ui |
Feature-selected shared presentation facade | A route to legacy rendering dependencies |
nebula_app/src/display, renderer |
Legacy rendering and still-shared extracted models | A source of new undifferentiated functionality |
nebula_app/src/display/animations.rs |
Legacy window animation state and shared frame snapshots behind existing display operations | Public tween fields or rendering/hit-test ownership |
nebula_gpui |
Component acceptance lab | A dependency of the product |
nebula_app/build, tools/i18n-contract |
Generation and independent contract verification | Runtime configuration loading |
The map records current responsibilities; it does not assert that every legacy file has already reached the target shape. Eleven oversized legacy files remain under ratcheted budgets. Move their independent rules when working on the relevant capability; do not mix a whole-app rewrite into a fix.
OSC 7501 program records belong to each Term, with bounded validation and
prompt/process/reset lifetime shared by local PTYs and SSH. The GPUI product
projects those records into existing status badges, progress and Runtime API
states, before Hooks and screen/process inference. Pane/transport failure still
takes precedence. Reports do not create Hook ownership, session ids or additional
desktop notifications. See
the protocol boundary.
main.rs selects product_ui/mod.rs as the display module when the legacy shell
is disabled. It likewise selects product adapters for several legacy-looking paths.
The product facade currently reuses extracted models via #[path] declarations.
i18n/outcomes.rs is compiled as the separate root module localized_status.
Consequently, neither an import containing display nor a file beneath that folder
proves that the product loads the legacy renderer. Keep semantic boundaries tested
with the actual feature configuration. Avoid relocating code merely to satisfy a
string-matching architecture tool.
For a capability that grows, prefer a small entry/facade plus cohesive child modules:
capability/
mod.rs exposed operations and orchestration
model.rs owned state and transitions, when substantial
adapter.rs I/O/platform conversion, when needed
view.rs renderer-specific presentation, when needed
tests.rs behavior contracts/fixtures, when substantial
This is a possible shape, not a mandatory directory scaffold. Name children after
their real responsibility (navigation, shell_picker, parser, layout), not
part1, common2 or misc. A short cohesive module can stay a single file. Keep
orchestration readable; it should not absorb every callback, parser and widget.
Cross-feature calls should use deliberately exposed commands/results rather than mutating another feature's internal collections. Prefer concrete types and plain functions until a second real implementation, testing seam or native boundary justifies a trait. Do not make fields public simply to make extraction compile.
Background work carries an owning scope and cancellation/staleness policy. Results must be applied only to a still-valid view/session. Renderer callbacks should use prepared state, not synchronously reload preferences, scan disks or wait for a child process. Measure hot-path effects instead of extrapolating a tiny benchmark to the whole product.
Completion follows this boundary: the terminal view captures a verified input line
and caret, including an advertised native editor snapshot, and owns its pending
task; completion::Session prepares an owned request and
selects permitted sources; shared calculation takes only input/cwd/environment and
returns candidates plus any remote-directory demand. The UI applies results only
after checking the current input, environment and mode. Popup selection and viewport
state remain UI-owned. Git and project-script metadata follow a captured host,
WSL or authenticated SSH execution scope; SSH connection generations separate
cached snapshots. Queries use owned background processes or channels. Workspace
selection and command argument roles remain shared rules, with local and guest
catalogs using the same selection code. See the
metadata boundary
and editor ownership.
The existing legacy adapter still accepts pane state, then
calls the same candidate calculation; it does not gain local Git discovery through
this extraction. See the request boundary decision.
- New setting: shared registry/model → persistence contract → shell adapter → tests.
- New language: one registry row + catalog; reuse static generation and fallback.
- New panel: put presentation under the shell, reuse existing domain operations.
- New platform support: model the capability, then implement a native adapter; unsupported behavior must be explicit, not a silently successful stub.
- New dependency/crate/long-lived service: first document its responsibility, direction, cost, ownership and why an existing module is insufficient.
See CONTRIBUTING, enforced contracts, the causal note policy, and the legacy decision archive. Architectural review evaluates cohesion and knowledge shared across boundaries; CI provides narrower mechanical checks, not a guarantee against every future design mistake.
坚持模块化桌面应用:界面负责呈现与编排,领域规则有唯一权威实现,平台和 I/O 放在明确适配边界。 按能力、状态归属和生命周期分组,不为了“可扩展”先造微服务、全局容器或层层 trait。 拆文件必须减少跨模块知识,不以文件数增加作为解耦证明;旧大文件按相关功能逐步治理。