diff --git a/app/src-tauri/tauri.conf.json b/app/src-tauri/tauri.conf.json index 0fc5a14..1fc5dc1 100644 --- a/app/src-tauri/tauri.conf.json +++ b/app/src-tauri/tauri.conf.json @@ -34,7 +34,7 @@ "category": "Productivity", "shortDescription": "Nextcloud Deck on your desktop", "longDescription": "A focused cross-platform desktop client for viewing and editing Nextcloud Deck boards.", - "copyright": "Copyright © 2026 Elijah Kuntz", + "copyright": "Copyright (c) 2026 Elijah Kuntz", "windows": { "webviewInstallMode": { "type": "downloadBootstrapper" diff --git a/app/src/App.svelte b/app/src/App.svelte index 7fde8a8..4b03d42 100644 --- a/app/src/App.svelte +++ b/app/src/App.svelte @@ -14,7 +14,6 @@ ChevronRight, Columns3, LayoutList, - LoaderCircle, Minus, Pencil, Plus, @@ -113,7 +112,7 @@ { id: 106, stackId: 13, title: 'Patho Concept Map', type: 'plain', owner: 'admin', order: 2, color: 'E69A88', done: null } ]} ]; - status = 'Homework · 6 cards · Preview data'; + status = 'Preview data'; return; } if (!serverUrl || !username) return; @@ -149,7 +148,7 @@ return; } updateState = 'checking'; - updateMessage = 'Checking Forgejo for a newer signed release…'; + updateMessage = 'Checking Forgejo for a newer signed release...'; updateProgress = 0; pendingUpdate = null; try { @@ -170,7 +169,7 @@ async function installUpdate() { if (!pendingUpdate) return; updateState = 'downloading'; - updateMessage = `Downloading Decky ${pendingUpdate.version}…`; + updateMessage = `Downloading Decky ${pendingUpdate.version}...`; let downloaded = 0; let total = 0; try { @@ -184,7 +183,7 @@ } else if (event.event === 'Finished') { updateState = 'installing'; updateProgress = 100; - updateMessage = 'Download verified. Installing and restarting Decky…'; + updateMessage = 'Download verified. Installing and restarting Decky...'; } }); await relaunch(); @@ -196,7 +195,7 @@ async function connect(silent = false) { busy = true; - status = 'Connecting to Nextcloud…'; + status = 'Connecting...'; try { client = await DeckApiClient.connect({ serverUrl, username, appPassword }); boards = (await client.getBoards()).filter((board) => !board.archived && !board.deletedAt); @@ -228,13 +227,12 @@ async function refreshBoard() { if (!client || !selectedBoard) return; busy = true; - status = `Loading ${selectedBoard.title}…`; + status = 'Refreshing...'; try { stacks = (await client.getStacks(selectedBoard.id)) .filter((stack) => !stack.deletedAt) .sort((a, b) => a.order - b.order); - const count = stacks.reduce((total, stack) => total + (stack.cards?.filter((card) => !card.archived).length ?? 0), 0); - status = `${selectedBoard.title} · ${count} card${count === 1 ? '' : 's'} · Updated ${new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}`; + status = `Updated ${new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}`; } catch (error) { status = friendlyError(error); } finally { @@ -249,7 +247,7 @@ const interactionAtStart = lastInteractionAt; if (!background) { busy = true; - status = selectedBoard ? `Loading ${selectedBoard.title}…` : 'Refreshing boards…'; + status = 'Refreshing...'; } try { const refreshedBoards = (await activeClient.getBoards()).filter((board) => !board.archived && !board.deletedAt); @@ -273,8 +271,7 @@ .sort((a, b) => a.order - b.order); if (client !== activeClient || selectedBoard?.id !== board.id || (background && (lastInteractionAt !== interactionAtStart || document.visibilityState !== 'visible'))) return; stacks = refreshedStacks; - const count = stacks.reduce((total, stack) => total + (stack.cards?.filter((card) => !card.archived).length ?? 0), 0); - status = `${board.title} · ${count} card${count === 1 ? '' : 's'} · Updated ${new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}`; + status = `Updated ${new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}`; } catch (error) { status = `${background ? 'Background refresh failed' : 'Refresh failed'}: ${friendlyError(error)}`; } finally { @@ -503,7 +500,7 @@ } async function deleteCard(card: Card) { - if (!client || !selectedBoard || !confirm(`Delete “${card.title}”?`)) return; + if (!client || !selectedBoard || !confirm(`Delete "${card.title}"?`)) return; busy = true; try { await client.deleteCard(selectedBoard.id, card.stackId, card.id); @@ -675,8 +672,7 @@ } else if (stackId !== null) { const targetIndex = stacks.findIndex((stack) => stack.id === stackId); stacks = await client.reorderStack(board.id, stackId, targetIndex); - const count = stacks.reduce((total, stack) => total + (stack.cards?.filter((item) => !item.archived).length ?? 0), 0); - status = `${board.title} · ${count} card${count === 1 ? '' : 's'} · Stack order saved`; + status = `Updated ${new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}`; } } catch (error) { stacks = origin; @@ -711,7 +707,7 @@ } -{selectedBoard ? `${selectedBoard.title} · Decky` : 'Decky'} +{selectedBoard ? `${selectedBoard.title} - Decky` : 'Decky'}
- +
+ + {status} +
@@ -841,10 +840,6 @@ {/if} - {#if dragGhost} @@ -867,7 +862,7 @@ diff --git a/app/src/app.css b/app/src/app.css index 01db576..10b9a57 100644 --- a/app/src/app.css +++ b/app/src/app.css @@ -29,7 +29,7 @@ button { color: inherit; } .app-shell { height: 100%; display: grid; - grid-template: 34px minmax(0, 1fr) 28px / 250px minmax(0, 1fr); + grid-template: 34px minmax(0, 1fr) / 250px minmax(0, 1fr); background: var(--bg); } @@ -115,6 +115,8 @@ textarea { resize: vertical; min-height: 90px; } .sidebar-layout button { justify-content: center; gap: 6px; padding: 0 6px; background: #292929; } .sidebar-layout button.active { color: #fff; background: #3a3a3a; box-shadow: inset 0 -2px var(--accent); } .sidebar-tool span, .sidebar-layout span { font-size: 12px; } +.refresh-row { min-width: 0; display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 4px; } +.sync-status { overflow: hidden; padding-right: 7px; color: var(--muted); font-size: 10px; text-align: right; text-overflow: ellipsis; white-space: nowrap; } .workspace { grid-column: 2; grid-row: 2; min-width: 0; min-height: 0; display: flex; flex-direction: column; background: #232323; } .workspace-tools { min-height: 46px; flex: 0 0 auto; display: grid; grid-template-columns: minmax(120px, 1fr) minmax(180px, 280px) minmax(80px, 1fr); align-items: center; gap: 16px; padding: 6px 18px; color: var(--muted); font-size: 12px; border-bottom: 1px solid var(--line); } @@ -173,8 +175,6 @@ textarea { resize: vertical; min-height: 90px; } .empty-illustration span { position: absolute; width: 66px; height: 44px; z-index: -1; border: 1px solid var(--line); border-radius: 8px; background: var(--panel-2); transform: translate(-28px, 13px) rotate(-12deg); } .empty-illustration span:last-child { transform: translate(28px, 13px) rotate(12deg); } -.statusbar { grid-column: 1 / -1; grid-row: 3; display: flex; align-items: center; gap: 8px; min-width: 0; padding: 0 12px; border-top: 1px solid var(--line); color: var(--muted); background: #161616; font-size: 11px; } -.statusbar span:last-child { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .spinning { animation: spin .9s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } }