- Built-in editor (default): large modal with monospace textarea, Cmd+S save, dirty guard; reads/writes through the provider streams so it works on local and remote files identically (2MB cap with a friendly error) - External editor: remote files download to appDataDir/edits/<id>, open via macOS `open -a`, the temp DIR is watched (survives atomic rename-saves) and every save enqueues a normal upload job (progress/errors in the transfer list); watchers + temp dirs are cleaned when the session disconnects - Editor setting: Built-in | Custom with a NATIVE app picker (electrobun openFileDialog, /Applications, .app filter) and reset-to-default - Leftover temp files from a crash prompt for cleanup at boot - F4, toolbar button (mynaFileText), and File > Edit File (Cmd+E) menu entry - README: feature notes + Portability notes (open/start/xdg-open seam documented in code and README per user request) Verified headless against the box: sftp read/write roundtrip incl. UTF-8, size cap, watch->save->auto-upload->remote-updated, session cleanup, orphan listing and clearing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
66 lines
2.1 KiB
HTML
66 lines
2.1 KiB
HTML
<div class="flex h-screen flex-col bg-background text-foreground">
|
|
@if (!rpc.available) {
|
|
<div class="flex flex-1 items-center justify-center">
|
|
<div class="max-w-sm rounded-lg border border-border bg-card p-6 text-center shadow-sm">
|
|
<ng-icon name="mynaXCircle" size="1.5rem" class="text-destructive" />
|
|
<p class="mt-2 text-sm font-medium">Not connected to the app</p>
|
|
<p class="mt-1 text-xs text-muted-foreground">
|
|
This page is running in a plain browser. Launch the desktop app to use it.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
} @else {
|
|
<app-tab-bar />
|
|
|
|
<main class="flex min-h-0 flex-1 divide-x divide-border">
|
|
<app-file-panel side="left" />
|
|
<app-file-panel side="right" />
|
|
</main>
|
|
|
|
<app-transfer-bar />
|
|
<app-status-bar />
|
|
}
|
|
|
|
<!-- dialogs -->
|
|
@if (dialogs.manager()) {
|
|
<app-connection-manager />
|
|
}
|
|
@if (dialogs.prompt(); as req) {
|
|
<app-prompt-dialog [request]="req" />
|
|
}
|
|
@if (dialogs.confirm(); as req) {
|
|
<app-confirm-dialog [request]="req" />
|
|
}
|
|
@if (dialogs.chmod(); as req) {
|
|
<app-chmod-dialog [request]="req" />
|
|
}
|
|
@if (dialogs.overwrite(); as req) {
|
|
<app-overwrite-dialog [request]="req" />
|
|
}
|
|
@if (dialogs.editor(); as req) {
|
|
<app-editor-dialog [request]="req" />
|
|
}
|
|
|
|
<!-- toasts -->
|
|
<div class="pointer-events-none fixed right-3 bottom-8 z-50 flex w-80 flex-col gap-2">
|
|
@for (t of toast.toasts(); track t.id) {
|
|
<div
|
|
class="pointer-events-auto flex items-start gap-2 rounded-lg border border-border bg-card p-3 text-xs text-card-foreground shadow-lg"
|
|
>
|
|
<ng-icon
|
|
[name]="toastIcon(t.kind)"
|
|
size="1rem"
|
|
[class]="t.kind === 'error' ? 'shrink-0 text-destructive' : 'shrink-0 text-success'"
|
|
/>
|
|
<span class="min-w-0 flex-1 wrap-break-word">{{ t.text }}</span>
|
|
<button
|
|
(click)="toast.dismiss(t.id)"
|
|
class="shrink-0 text-muted-foreground hover:text-foreground"
|
|
>
|
|
<ng-icon name="mynaX" size="0.9rem" />
|
|
</button>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|