Full interactive terminal via SwiftTerm, About sheet, English docs
This commit is contained in:
+54
-26
@@ -1,33 +1,61 @@
|
||||
# Architektur
|
||||
# Architecture
|
||||
|
||||
```
|
||||
Sources/RemminaMac/
|
||||
RemminaMacApp.swift # @main, WindowGroup, lädt ProfileStore
|
||||
Models/ConnectionProfile.swift # ProtocolType + ConnectionProfile + StoredSSHKey (Codable)
|
||||
Stores/ProfileStore.swift # ObservableObject, JSON-Persistenz, Filter/Suche/Gruppen
|
||||
Security/KeychainHelper.swift# SecItemAdd/Copy/Delete Wrapper
|
||||
Services/ConnectionServices.swift # SSHService (ssh-Befehl) + RDPService (freerdp/rdc/vnc)
|
||||
Views/ContentView.swift # NavigationSplitView: Sidebar + Tabs + Statusbar
|
||||
Views/SessionView.swift # Detail pro Sitzung
|
||||
Views/ProfileEditorView.swift# Formular Neu/Bearbeiten + Keychain-Anbindung
|
||||
Views/SSHKeysView.swift # Key-Liste, Import, ssh-keygen
|
||||
RemminaMacApp.swift # @main, WindowGroup, loads ProfileStore
|
||||
Models/ConnectionProfile.swift # ProtocolType + ConnectionProfile + StoredSSHKey (Codable)
|
||||
Models/ProfileValidation.swift # input checks + trimming (pure, tested)
|
||||
Stores/ProfileStore.swift # ObservableObject, JSON persistence, filter/search/groups
|
||||
Security/KeychainHelper.swift # SecItemAdd/Copy/Delete wrapper
|
||||
Services/ConnectionServices.swift # SSHService (ssh args, secrets, sshpass, agent) + RDPService
|
||||
Services/PTYSession.swift # real PTY session (posix_openpt), stderr pipe, TerminalManager
|
||||
Services/AnsiParser.swift # ANSI/SGR parser for diagnostics buffer (pure, tested)
|
||||
Services/TCPCheck.swift # TCP pre-check via Network.framework
|
||||
Services/HostKeyService.swift # keyscan/fingerprint/accept/revoke via system ssh tools
|
||||
Services/TerminalTheme.swift # Dracula palette (SwiftUI + NSColor + SwiftTerm)
|
||||
Views/ContentView.swift # NavigationSplitView: sidebar + tabs + status bar
|
||||
Views/SessionView.swift # per-session detail
|
||||
Views/SwiftTermView.swift # full terminal emulator view (SwiftTerm, Dracula)
|
||||
Views/ProfileEditorView.swift # new/edit form + Keychain wiring + validation
|
||||
Views/SSHKeysView.swift # key list, import, ssh-keygen
|
||||
Views/HostKeySheet.swift # fingerprint popup (accept/decline, MITM warning)
|
||||
Views/AboutView.swift # about sheet (contributor, repo link)
|
||||
Sources/ctty-run/
|
||||
ctty-run.c # gives the ssh child a controlling terminal
|
||||
# (double-fork: setsid + slave open + exec)
|
||||
```
|
||||
|
||||
## Datenfluss
|
||||
1. `RemminaMacApp` erstellt `ProfileStore` als `@StateObject`, injiziert als `environmentObject`.
|
||||
2. `ProfileStore.load()` liest `profiles.json` + `sshkeys.json`, sonst Sample-Daten.
|
||||
3. `ContentView` filtert via `searchText`/`selectedGroup`, `selection` steuert Detail.
|
||||
4. `connect(_:)` setzt `lastConnected`, öffnet Tab, delegiert an `SSHService`/`RDPService`.
|
||||
5. Secrets nur via `KeychainHelper`, JSON enthält nur Account-Referenzen.
|
||||
## Data flow
|
||||
1. `RemminaMacApp` creates `ProfileStore` as `@StateObject`, injects as `environmentObject`.
|
||||
2. `ProfileStore.load()` reads `profiles.json` + `sshkeys.json`, else sample data.
|
||||
3. `ContentView` filters via `searchText`/`selectedGroup`; `selection` drives detail.
|
||||
4. `connect(_:)` stamps `lastConnected`, opens a tab. The terminal view runs:
|
||||
TCP pre-check → host-key status (popup if unknown/changed) → `PTYSession.start()`.
|
||||
5. `PTYSession` spawns via `ctty-run` (controlling terminal): stdin/stdout on the
|
||||
pty slave, stderr on a loss-free pipe. Output feeds SwiftTerm (`onData`) and
|
||||
the diagnostics buffer (`lines`, used by the error banner + report).
|
||||
6. Keyboard flows from SwiftTerm's delegate straight into the pty master
|
||||
(`sendBytes`); window resizes propagate via `TIOCSWINSZ` (`setWinsize`).
|
||||
7. Secrets only via `KeychainHelper`; JSON holds account references.
|
||||
`TerminalManager` caches one session per profile and reconfigures on any
|
||||
config change (fingerprint incl. secret + sshpass presence).
|
||||
|
||||
## Wichtige Dateien
|
||||
- `Stores/ProfileStore.swift:13` — Pfade + `load()`/`save()`
|
||||
- `Models/ConnectionProfile.swift:3` — Protokoll-Defaults (Ports, Icons)
|
||||
- `Services/ConnectionServices.swift:6` — `sshArguments(for:key:)`
|
||||
- `Views/ContentView.swift` — Remmina-Layout
|
||||
## Key files
|
||||
- `Stores/ProfileStore.swift` — paths + `load()`/`save()`
|
||||
- `Models/ConnectionProfile.swift` — protocol defaults (ports, icons)
|
||||
- `Services/PTYSession.swift` — spawn, lifecycle, `TerminalManager`
|
||||
- `Services/HostKeyService.swift` — fingerprint flow
|
||||
- `Views/SwiftTermView.swift` — emulator wiring + Dracula theme
|
||||
- `Views/ContentView.swift` — Remmina layout
|
||||
|
||||
## Nächste Ausbaustufen
|
||||
1. SwiftTerm-View statt externem Terminal + `swift-nio-ssh` für echte SSH-Sessions.
|
||||
2. FreeRDP als XPC-Service einbetten statt `Process`.
|
||||
3. SFTP-Browser (NIO SFTP oder libssh2-Bridge).
|
||||
4. Export/Import-Buttons + Drag&Drop für Keys.
|
||||
## Verification strategy
|
||||
Logic is verified with throwaway `swiftc` harnesses in `/tmp` (never committed):
|
||||
ANSI parser, PTY roundtrips, real `ssh` runs, Keychain roundtrips, TCP checks,
|
||||
host-key flow against a local paramiko test server, SwiftTerm core headless
|
||||
(cursor addressing, alt-screen, clear), view-layer stress. See `docs/ERRORS.md`.
|
||||
|
||||
## Next steps
|
||||
1. Embedded RDP (FreeRDP bridge) instead of `Process`.
|
||||
2. Integrated SFTP browser.
|
||||
3. Export/Import buttons + drag & drop for keys.
|
||||
4. Tray / favorites / folder badges like Remmina.
|
||||
|
||||
Reference in New Issue
Block a user