123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Backer-Upper</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header>
<h1>Backer-Upper</h1>
<span id="status-bar"></span>
<button id="btn-close-app" title="Close">✕</button>
</header>
<main>
<!-- DriveSelect -->
<div id="screen-drive-select" class="screen">
<div class="screen-header">
<h2>Select Drive</h2>
<button id="btn-refresh">↻ Refresh</button>
</div>
<div id="drive-list" class="drive-list">
<div class="empty-state">Loading drives…</div>
</div>
<div class="btn-row">
<button id="btn-format-drive" class="danger-outline" disabled>Format for Backup</button>
<button id="btn-open-drive" class="primary" disabled>Open Drive</button>
</div>
<div id="drive-select-error" class="error-text" style="display: none"></div>
</div>
<!-- PasswordPrompt -->
<div id="screen-password" class="screen">
<div class="dialog">
<h2 id="password-heading">Unlock Drive</h2>
<div class="card">
<p>This drive is LUKS-encrypted. Enter the passphrase to unlock it.</p>
<div class="form-grid">
<label>Passphrase</label>
<input type="password" id="password-input" placeholder="Passphrase" />
</div>
<div id="password-error" class="error-text" style="display: none"></div>
<div class="btn-row">
<button id="btn-password-cancel">Cancel</button>
<button id="btn-password-unlock" class="primary">Unlock</button>
</div>
</div>
</div>
</div>
<!-- ConfigEditor -->
<div id="screen-config" class="screen">
<div class="screen-header">
<h2>Backup Configuration</h2>
<button id="btn-eject">⏏ Eject</button>
</div>
<div class="meta-row">
<span id="config-mount-point" class="muted"></span>
<span id="config-last-backup" class="muted"></span>
</div>
<div class="card card-flush">
<table class="jobs-table">
<thead>
<tr>
<th>Name</th>
<th>Source → Drive</th>
<th>Mode</th>
<th>Enabled</th>
<th></th>
</tr>
</thead>
<tbody id="jobs-tbody"></tbody>
</table>
</div>
<div class="btn-row">
<button id="btn-add-job">+ Add Job</button>
<button id="btn-save-config" style="display: none" class="primary">Save Config</button>
<span id="unsaved-indicator" style="display: none">Unsaved changes</span>
<button id="btn-wipe-free-space" class="danger-outline">Wipe Free Space…</button>
<button id="btn-restore" class="danger-outline">Restore from Backup…</button>
<button id="btn-next" class="primary">Begin Backup…</button>
</div>
<div id="config-error" class="error-text" style="display: none"></div>
</div>
<!-- JobEdit -->
<div id="screen-job-edit" class="screen">
<h2 id="job-edit-heading">Edit Job</h2>
<div class="card">
<div class="form-grid">
<label>Name</label>
<input type="text" id="job-name" placeholder="e.g. Home" />
<label>Source folder</label>
<input type="text" id="job-source" placeholder="/home/user" />
<label>Destination (on drive)</label>
<input type="text" id="job-dest" placeholder="home" />
<label>Mode</label>
<div class="radio-group">
<label class="radio-option">
<input type="radio" name="job-mode" value="Backup" />
<span>
<strong>📸 Snapshot</strong>
<span class="radio-desc"
>Exact copy of current state; deletions are mirrored</span
>
</span>
</label>
<label class="radio-option">
<input type="radio" name="job-mode" value="Media" />
<span>
<strong>📚 Hoard</strong>
<span class="radio-desc"
>Only adds missing files; nothing is ever deleted from backup</span
>
</span>
</label>
</div>
<label>Enabled</label>
<input type="checkbox" id="job-enabled" />
<label for="job-exclude-caches">Skip caches</label>
<span class="checkbox-with-desc">
<input type="checkbox" id="job-exclude-caches" />
<span class="radio-desc"
>Skip directories marked with a <code>CACHEDIR.TAG</code> file (Cargo, pip, ccache,
browsers…)</span
>
</span>
</div>
<div class="excludes-section">
<div class="excludes-section-header">
<label class="excludes-label">Files to back up</label>
<span class="excludes-hint">Uncheck to exclude</span>
</div>
<div id="excludes-tree" class="excludes-tree">
<div class="tree-msg"><span class="spinner"></span> Loading…</div>
</div>
<div class="presets-row">
<span class="presets-row-label">Suggested:</span>
<div id="presets-chips" class="presets-chips"></div>
</div>
<label class="excludes-label excludes-patterns-label">Exclude patterns</label>
<textarea id="job-excludes-manual" rows="5" placeholder="*.iso *.log"></textarea>
</div>
<div class="btn-row">
<button id="btn-job-cancel">Cancel</button>
<button id="btn-job-save" class="primary">Save Job</button>
<button id="btn-job-delete" class="danger">Delete Job</button>
</div>
</div>
</div>
<!-- Preview -->
<div id="screen-preview" class="screen">
<h2>Review Backup Commands</h2>
<p class="muted">These rsync commands will run in order:</p>
<div id="preview-commands"></div>
<div id="preview-empty" class="empty-state" style="display: none">No enabled jobs.</div>
<div class="btn-row">
<button id="btn-preview-cancel">← Back</button>
<button id="btn-run-backup" class="primary">▶ Run Backup</button>
</div>
</div>
<!-- Wipe Free Space -->
<div id="screen-wipe" class="screen">
<h2>Wiping Free Space…</h2>
<div id="wipe-status-banner" class="banner" style="display: none"></div>
<p class="muted">
Writing zeros to all unallocated space on the drive. This cannot be cancelled cleanly
mid-way — let it finish for best results.
</p>
<div class="progress-wrap">
<div id="wipe-progress-bar" class="progress-bar" style="width: 0%"></div>
<span class="progress-label" id="wipe-progress-label">0%</span>
</div>
<div class="muted" id="wipe-size-info"></div>
<div class="btn-row" id="wipe-btn-row">
<button id="btn-wipe-cancel">Cancel</button>
</div>
</div>
<!-- Backup -->
<div id="screen-backup" class="screen">
<h2>Backup</h2>
<div id="backup-status-banner" class="banner" style="display: none"></div>
<div id="backup-job-info"></div>
<div class="progress-wrap">
<div id="backup-progress-bar" class="progress-bar" style="width: 0%"></div>
<div id="backup-progress-label" class="progress-label">0%</div>
</div>
<div class="muted" id="backup-elapsed"></div>
<div class="log-section">
<div class="log-label">Output Log</div>
<div id="backup-log" class="log-box"></div>
</div>
<div class="btn-row" id="backup-btn-row">
<button id="btn-backup-pause">Pause</button>
<button id="btn-backup-cancel">Cancel</button>
</div>
</div>
<!-- Restore -->
<div id="screen-restore" class="screen">
<div class="screen-header">
<h2>Restore from Backup</h2>
</div>
<div class="banner danger">
<strong>⚠ This will overwrite files on your computer.</strong>
Files from the selected backup will be copied to their original locations, replacing any
local versions. This cannot be undone — make sure you have any important unsaved work
elsewhere before proceeding.
</div>
<div class="card">
<div class="section-label">Restore from</div>
<div id="restore-snapshot-list"></div>
</div>
<div class="card">
<div class="section-label">Jobs to restore</div>
<div id="restore-jobs-list"></div>
</div>
<div class="card">
<label class="radio-option">
<input type="checkbox" id="restore-delete-extra" />
<span>
<strong>Also delete local files not in the backup</strong>
<span class="radio-desc"
>Permanently removes local files that don't exist in the selected backup. Use with
caution.</span
>
</span>
</label>
</div>
<div class="card">
<div class="form-grid">
<label>Confirm</label>
<input
type="text"
id="restore-confirm-input"
placeholder='Type "RESTORE" to confirm'
autocomplete="off"
/>
</div>
<div id="restore-validation-msg" class="error-text" style="display: none"></div>
</div>
<div id="restore-error" class="error-text" style="display: none"></div>
<div class="btn-row">
<button id="btn-restore-cancel">Cancel</button>
<button id="btn-do-restore" class="primary" disabled>Preview Restore →</button>
</div>
</div>
<!-- RestorePreview -->
<div id="screen-restore-preview" class="screen">
<div class="screen-header">
<h2>Preview Restore</h2>
</div>
<p class="muted">These files would be changed. No files have been written yet.</p>
<div class="log-section">
<div class="log-label">Dry-run output</div>
<div id="restore-preview-log" class="log-box"></div>
</div>
<div id="restore-preview-empty" class="empty-state" style="display: none">
No files would be changed by this restore.
</div>
<div id="restore-preview-error" class="error-text" style="display: none"></div>
<div class="btn-row">
<button id="btn-restore-preview-back">← Change Settings</button>
<button id="btn-restore-preview-proceed" class="danger">Restore from Backup</button>
</div>
</div>
<!-- FormatSetup -->
<div id="screen-format-setup" class="screen">
<h2>Format Drive for Backup</h2>
<div id="format-main-content">
<div class="banner danger">
<strong>DANGER — ALL DATA ON THIS DRIVE WILL BE PERMANENTLY DESTROYED</strong>
<span id="format-drive-info-line"></span>
</div>
<div class="format-columns">
<div class="format-col-probe">
<div class="card terminal">
<div id="format-probe-content">
<em>Reading drive contents…</em>
</div>
</div>
<details>
<summary>Commands that will run</summary>
<div id="format-cmd-preview" class="log-box"></div>
</details>
</div>
<div class="format-col-form">
<div class="card">
<div class="form-grid">
<label>Drive label</label>
<input type="text" id="format-label" value="Backup" />
<label>Filesystem</label>
<select id="format-fstype">
<option value="btrfs">btrfs (recommended)</option>
<option value="ext4">ext4</option>
</select>
<label>Encryption</label>
<label class="checkbox-inline">
<input type="checkbox" id="format-luks" checked />
LUKS encryption
</label>
<div id="format-luks-section" style="display: contents">
<label>Passphrase</label>
<input type="password" id="format-pass1" placeholder="Choose a passphrase" />
<label>Confirm</label>
<input type="password" id="format-pass2" placeholder="Repeat passphrase" />
</div>
<label id="format-confirm-label">Type device path</label>
<input type="text" id="format-confirm-input" placeholder="/dev/sdX" />
</div>
<div id="format-validation-msg" style="display: none" class="error-text"></div>
<div id="format-error" class="error-text" style="display: none"></div>
<div class="btn-row">
<button id="btn-format-cancel">Cancel</button>
<button id="btn-do-format" class="danger" disabled>
Erase & Format Drive
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- FormatProgress -->
<div id="screen-format-progress" class="screen">
<h2>Formatting Drive…</h2>
<div id="format-progress-banner" class="banner" style="display: none"></div>
<div id="format-step-label"></div>
<div id="format-step-dots" class="step-dots"></div>
<div id="format-log" class="log-box"></div>
<div class="btn-row" id="format-done-btn-row" style="display: none">
<button id="btn-format-done" class="primary">Back to Drive Select</button>
</div>
</div>
</main>
<script type="module" src="app.js"></script>
</body>
</html>
|