From 481b1c181c0de9f5b8e2ecb26cb820f0368d86be Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 24 Sep 2026 17:05:32 +0000 Subject: [PATCH] feat(ui): split setup and running views --- package.json | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- src/main.ts | 376 ++++++++++++++++++++++++-------------- src/styles.css | 98 ++++++++++ 5 files changed, 339 insertions(+), 141 deletions(-) diff --git a/package.json b/package.json index 8ff2472..8f8b59b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fieldlens/print", - "version": "0.1.15", + "version": "0.1.16", "private": true, "type": "module", "scripts": { diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 84e3242..1c3a3ab 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fieldlens-print" -version = "0.1.15" +version = "0.1.16" description = "FieldLens print-stream barcode capture companion" authors = ["Arrowstack"] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ca47c2e..f8b7ab1 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "FieldLens Print", - "version": "0.1.15", + "version": "0.1.16", "identifier": "dev.arrowstack.fieldlens.print", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/main.ts b/src/main.ts index 2f3e603..8f71331 100644 --- a/src/main.ts +++ b/src/main.ts @@ -85,7 +85,7 @@ let config: AppConfig let jobs: Job[] = [] let status: Status let loginTimer: number | undefined -let currentVersion = '0.1.15' +let currentVersion = '0.1.16' let bundleType: BundleType | null = null let availableUpdate: Update | null = null let availableManualUpdate: ManualUpdate | null = null @@ -93,6 +93,7 @@ let updateBusy = false let captureError: string | null = null let systemPrinters: SystemPrinter[] = [] let virtualPrinterStatus: VirtualPrinterStatus | null = null +let activeView: 'setup' | 'running' = 'setup' app.innerHTML = `
@@ -104,155 +105,194 @@ app.innerHTML = `

Capture and inspect print jobs before the printer driver renders them.

-
Loading
+ +
+ +
Loading
+
-
-
-
-
- PRINTER ROUTING -

Destination

-
- -
- -
- - -
-
-
P
+
+
+
+
+
- FieldLens Print - Capture + physical print + PRINTER ROUTING +

Destination

+
-
-
T
-
- FieldLens Test Capture - Full pipeline, no paper -
-
-
-
- -
- System printer - Checking FieldLens queues… -
-
+
+ -
- - - -
- -
- Advanced capture settings -
-
- - - - +
+
+
P
+
+ FieldLens Print + Capture + physical print +
+
+
+
T
+
+ FieldLens Test Capture + Full pipeline, no paper +
+
-
- - - -
-
-
-
-
- -
-
-
- ACTIVITY -

FieldLens

-
- Waiting for capture -
- -
-
-
0Jobs
-
0Codes
-
0Pending
-
- - - -
- Connection & updates -
-
- - - - -
- -
+
+
- App updates - Version … - Checking for updates shortly after startup. + System printer + Checking FieldLens queues… +
+
+ +
+ + + +
+
+
+ +
+
+
+ SETTINGS +

FieldLens Print

+
+ Local configuration +
+ +
+ + +
+ Capture settings +
+
+ + + + +
+ +
+ + + +
+
+
+ +
+ Connection & updates +
+
+ + + + +
+ +
+
+ App updates + Version … + Checking for updates shortly after startup. +
+
+ + +
+ +
+
+
- +
-
-
-
- RECENT ACTIVITY -

Print jobs

-
- Newest first -
-
-
+
@@ -263,6 +303,14 @@ function el(id: string) { return document.getElementById(id) as T } +function setView(view: 'setup' | 'running') { + activeView = view + el('setup-view').hidden = view !== 'setup' + el('running-view').hidden = view !== 'running' + el('setup-tab').classList.toggle('active', view === 'setup') + el('running-tab').classList.toggle('active', view === 'running') +} + function readForm(): AppConfig { return { ...config, @@ -304,6 +352,7 @@ function renderPrinters() { if (options.some((printer) => printer.name === current)) select.value = current const selected = options.find((printer) => printer.name === select.value) + el('running-printer').textContent = selected?.name || config?.forwardPrinter || 'No physical printer selected' const help = el('forward-printer-help') if (selected?.address) { help.textContent = `Detected RAW network endpoint: ${selected.address}` @@ -459,22 +508,38 @@ el('install-printer').addEventListener('click', async () => { } button.disabled = true - state.textContent = 'Creating FieldLens Print… administrator approval may appear.' + let stoppedForRepair = false + try { + state.textContent = 'Stopping capture before printer repair…' + await invoke('stop_capture') + stoppedForRepair = true + await refreshStatus() + + state.textContent = 'Repairing printers and migrating captured jobs… administrator approval may appear.' virtualPrinterStatus = await invoke('install_virtual_printer', { targetPrinter }) config = await invoke('save_config', { config: readForm() }) - await restartCapture() + + state.textContent = 'Restarting capture and processing migrated backlog…' + const restarted = await ensureCaptureStarted() + if (!restarted) { + throw new Error(captureError || 'capture did not restart') + } + stoppedForRepair = false + await refreshPrinters() + setView('running') } catch (error) { state.textContent = `Printer setup failed: ${String(error)}` + if (stoppedForRepair) { + await ensureCaptureStarted().catch(() => undefined) + } } finally { button.disabled = false } }) -el('self-test').addEventListener('click', async () => { - const button = el('self-test') - const state = el('virtual-printer-state') +async function submitCaptureSelfTest(button: HTMLButtonElement, state: HTMLElement) { button.disabled = true state.textContent = 'Submitting a no-paper capture self-test…' try { @@ -486,6 +551,20 @@ el('self-test').addEventListener('click', async () => { } finally { button.disabled = false } +} + +el('self-test').addEventListener('click', () => { + void submitCaptureSelfTest( + el('self-test'), + el('virtual-printer-state'), + ) +}) + +el('running-self-test').addEventListener('click', () => { + void submitCaptureSelfTest( + el('running-self-test'), + el('running-action-state'), + ) }) el('remove-printer').addEventListener('click', async () => { @@ -512,7 +591,8 @@ el('save').addEventListener('click', async () => { el('start').addEventListener('click', async () => { config = await invoke('save_config', { config: readForm() }) - await restartCapture() + const started = await restartCapture() + if (started) setView('running') }) el('stop').addEventListener('click', async () => { @@ -521,6 +601,18 @@ el('stop').addEventListener('click', async () => { await refreshStatus() }) +el('setup-tab').addEventListener('click', () => setView('setup')) +el('running-tab').addEventListener('click', () => setView('running')) + +el('running-stop').addEventListener('click', async () => { + const state = el('running-action-state') + state.textContent = 'Stopping capture…' + captureError = null + await invoke('stop_capture') + await refreshStatus() + state.textContent = 'Capture stopped. Open Setup to restart or change configuration.' +}) + el('sync').addEventListener('click', async () => { const result = await invoke<{ synced: number; pending: number }>('sync_pending') el('login-state').textContent = `Synced ${result.synced}. ${result.pending} pending.` @@ -710,6 +802,14 @@ writeForm(config) await refreshPrinters() await ensureCaptureStarted() +const readyForRunning = Boolean( + virtualPrinterStatus?.installed + && config.forwardPrinter + && status?.running + && !captureError +) +setView(readyForRunning ? 'running' : 'setup') + currentVersion = await getVersion() bundleType = await getBundleType().catch(() => null) el('version-label').textContent = `Version ${currentVersion}${bundleType ? ` · ${bundleType}` : ''}` diff --git a/src/styles.css b/src/styles.css index 42eb9fc..08df4fd 100644 --- a/src/styles.css +++ b/src/styles.css @@ -543,3 +543,101 @@ select:focus { color: #9fcef5; border-color: #294865; } + + +.topbar-actions { + display: flex; + align-items: center; + gap: 12px; +} + +.view-tabs { + display: inline-flex; + gap: 4px; + padding: 4px; + border: 1px solid #202a36; + border-radius: 10px; + background: #0d131a; +} + +.view-tab { + border: 0; + border-radius: 7px; + padding: 7px 10px; + background: transparent; + color: #718094; + font-size: 10.5px; + font-weight: 800; +} + +.view-tab:hover { + color: #dce4ed; + background: #151d27; +} + +.view-tab.active { + background: #1a2b24; + color: #b9f3d5; +} + +.app-view[hidden] { + display: none; +} + +.settings-card .settings-account { + margin-top: 0; +} + +.running-grid { + display: grid; + gap: 16px; +} + +.running-summary-card .card-body { + display: grid; + gap: 14px; +} + +.running-route { + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + border: 1px solid #222c38; + border-radius: 11px; + padding: 12px; + background: #0d131a; +} + +.running-route > div:first-child { + min-width: 0; + display: grid; + gap: 4px; +} + +.running-route strong { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; +} + +.running-action-state { + min-height: 14px; +} + +@media (max-width: 860px) { + .topbar-actions { + width: 100%; + justify-content: space-between; + } + + .topbar { + flex-wrap: wrap; + } + + .running-route { + align-items: flex-start; + flex-direction: column; + } +}