Skip to content

Commit d1f7093

Browse files
v1.4.4 — auto-update toggle, Settings refonte, sidebar friends, storage UX
- Auto-update toggle in Settings → Updates (electron-updater wired through main/preload, preference persisted to <userData>/auto-update.json) + manual "Check for updates now" button. - Settings panel rebuilt as a flat layout — Updates / Data / Tools / About, with inline collapsibles. Export → Settings (gear icon). API URL with one-click Copy button. - macOS app name fix via `app.setName('Tiger Studio Manager')` before whenReady — About menu and dock now show the proper product name. - 4 KPI stats moved from sidebar to the top header (Spools / Stock / TigerTag / TigerTag+) — replaces the old title block. - TD1S / Scale / Cloud icons always pinned to the right edge of the header via `margin-left: auto` (.top-status-icons), regardless of stats wrap visibility. - New EMPTY stat in Storage view counting depleted spools (0g). Disambig: slot "Empty" → "Free", spool "Depleted" → "Empty" across all 9 locales. - Storage location row in spool detail panel — clickable to navigate to the slot in Storage with the spool UID auto-typed in search. New Auto-assign button for unranked spools. - Auto Storage + Auto Unstorage toggles in the Spools-not-stored sidecard (snapshot-driven with _inFlight flag). - Depleted spools (0g) get a 6px coloured strip + "0g" tag inside their slot so they stay visible. Search hits get a positive orange ring + animated glow on the matching slot. - Sidebar friends quick-access list under the Friends button — flat rows (avatar + name) when expanded, avatar circles when collapsed. Click switches inventory view; click again to go back. - Body-appended floating tooltip (#sbFriendTip) for collapsed friend chips — escapes the sidebar's overflow:hidden. - Sidebar user header redesigned to a horizontal row: avatar on the left, Welcome back + display name stacked on its right. - Friend avatars now 40×40 in both expanded and collapsed sidebar (match the user avatar in collapsed mode). - readableTextOn(bg) helper computes WCAG luminance via 1×1 canvas and switches initials to #1a1a1a on light avatar backgrounds — fixes white-on-white when picking near-white custom colours. Applied across all 10 avatar render sites. - Display name prompt no longer fires on first login when Firestore already has one — uses {source:"server"} + 1s grace period. - New SVGs: icon_settings, icon_wrench.
1 parent 707b838 commit d1f7093

19 files changed

Lines changed: 1799 additions & 177 deletions

‎README.md‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@
4141

4242
## Changelog
4343

44+
### v1.4.4 — 2026-05-02
45+
46+
- **Auto-update toggle.** New "Updates" section in **Settings** lets the user enable or disable automatic update downloads, and exposes a **Check for updates now** button. The preference persists to `<userData>/auto-update.json`. When enabled, the existing `electron-updater` flow runs on launch and prompts for restart when an update is ready; when disabled, the app stays on the installed version until the user manually checks. (`main.js`, `preload.js`, `renderer/inventory.js`)
47+
- **Settings panel rebuilt.** The old card-in-card layout was replaced by a single flat panel with hairline-separated sections — **Updates / Data / Tools / About** — and inline collapsibles for Data and Tools. The "Export" button became "Settings" (gear icon) and the API URL is now exposed as a read-only field with a one-click **Copy** button.
48+
- **macOS app name fix.** `app.setName('Tiger Studio Manager')` is set before `whenReady` so the About menu, Cmd+Q, and the dock all read the proper product name instead of `tigertag-inventory`. (`main.js`)
49+
- **Top header KPI stats.** The 4 stat tiles (Spools / Stock / TigerTag / TigerTag+) moved from the sidebar to the top of the main pane, replacing the old title block. They now greet the user with at-a-glance numbers immediately on load.
50+
- **Status icons always pinned to the right.** TD1S / Scale / Cloud (and the optional RFID pill) now always sit at the far-right of the top header via `margin-left: auto`, regardless of whether the stats wrap is visible. They no longer drift to the left when stats are hidden during initial load.
51+
- **Storage — `EMPTY` stat for depleted spools.** A new tile counts spools whose remaining weight is 0 g. The two existing labels were renamed to remove a confusion users were running into: slot **"Empty"** → **"Free"** (a free spot in a rack), and spool **"Depleted"** → **"Empty"** (a spool with no filament left). German/French/Spanish/Italian/Polish/Portuguese/Chinese updated in the same edit.
52+
- **Spool detail — Storage location row.** A new section in the spool side panel shows `Rack name · A3` for spools that are placed, and a single **Auto-assign** button for spools that are not. Clicking the location row closes the side panel, switches to the Storage view, and pre-fills the search field with the spool's UID so the slot is found instantly. The auto-assign button places the spool into the next available slot in one click.
53+
- **Auto Storage + Auto Unstorage** toggles in the "Spools not stored" sidecard. When auto storage is on, every newly-detected spool that has no rack assignment is placed automatically; when auto unstorage is on, any spool whose weight drops to 0 g is removed from its slot automatically. Both run snapshot-driven with an `_inFlight` flag so they can't loop.
54+
- **Depleted spools now visible in racks.** Spools at 0 g previously showed `height: 0%` and looked invisible. They now display a 6 px coloured strip plus a "0 g" tag inside their slot, so the user can still see the spool occupies that position. Search hits also get a positive orange ring + animated glow on the matching slot, instead of relying solely on dimming the non-matches.
55+
- **Sidebar — friends quick-access list.** Friends now appear under the **Friends** button in the sidebar as flat rows (avatar + display name), and as avatar-only circles when the sidebar is collapsed. Click switches the inventory view to that friend's read-only inventory; click again to go back to your own view. The list shows a body-appended floating tooltip on hover in collapsed mode (escapes the sidebar's `overflow: hidden`).
56+
- **Sidebar — user header redesign.** The user avatar moved from a centered column to a horizontal row: avatar on the left, "Welcome back" + display name stacked to its right. Reads cleaner and matches the friend chip layout below.
57+
- **Readable initials on light avatar colours.** Initials previously rendered white-on-white when a user picked a near-white custom colour. A new `readableTextOn(bg)` helper computes WCAG relative luminance via a 1×1 canvas and switches the text to `#1a1a1a` on light backgrounds. Applied across all 10 avatar render sites (sidebar, dropdowns, friend chips, profile modal, friends panel, blocklist, friend-request preview, friend-view banner, edit-account modal). Cached per colour for negligible cost.
58+
- **Display name prompt — race condition fix.** On first login, the prompt sometimes fired even when Firestore already had a display name, because `syncUserDoc` was reading from cache. Now uses `{ source: "server" }` plus a 1-second grace period that re-fetches from Firestore before prompting, so users with an existing pseudo are no longer asked again.
59+
4460
### v1.4.3 — 2026-05-02
4561

4662
- **Storage view — major UX overhaul.** The rack management screen has been restructured around a dense, scannable toolbar and tighter spatial logic so users with a dozen+ racks can navigate at a glance.
@@ -171,8 +187,8 @@ Built installers are placed in the `dist/` folder (ignored by git).
171187
Pushing a version tag automatically triggers a parallel build on **macOS, Windows, and Linux** and publishes a GitHub Release with the installers attached.
172188

173189
```bash
174-
git tag v1.4.2
175-
git push origin v1.4.2
190+
git tag v1.4.4
191+
git push origin v1.4.4
176192
```
177193

178194
| Platform | Output | Signed? |

‎assets/svg/icons/icon_settings.svg‎

Lines changed: 1 addition & 0 deletions
Loading

‎assets/svg/icons/icon_wrench.svg‎

Lines changed: 1 addition & 0 deletions
Loading

‎main.js‎

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ const http = require('http');
66
const crypto = require('crypto');
77
const db = require('./services/tigertagDbService');
88

9+
// ── App display name (macOS menu bar, About dialog, Dock, etc.)
10+
// package.json `name` is "tigertag-inventory" (npm-friendly slug). Force the
11+
// human-readable product name so macOS shows "Tiger Studio Manager" in:
12+
// - app menu (Apple menu → "About Tiger Studio Manager", "Quit Tiger Studio Manager")
13+
// - Dock tooltip
14+
// - Window menu items
15+
// Must be called BEFORE app.whenReady() / before any window is created.
16+
app.setName('Tiger Studio Manager');
17+
918
// ── Single-instance lock ────────────────────────────────────────────────────
1019
// Prevent multiple Electron processes from sharing the same userData directory
1120
// (which would deadlock IndexedDB / LevelDB — Firebase Auth, image cache, etc.).
@@ -429,24 +438,86 @@ function initTD1S() {
429438
td1sConnect();
430439
}
431440

432-
// ── Auto-updater
433-
function initUpdater() {
434-
autoUpdater.checkForUpdatesAndNotify();
441+
// ── Auto-updater preference ─────────────────────────────────────────────
442+
// Persisted in <userData>/auto-update.json so it survives across launches
443+
// and is read at startup BEFORE the renderer has had a chance to send its
444+
// localStorage value. Renderer can override at runtime via 'update:set-auto'.
445+
const _autoUpdatePrefsPath = () => path.join(app.getPath('userData'), 'auto-update.json');
446+
function readAutoUpdatePref() {
447+
try {
448+
const raw = fs.readFileSync(_autoUpdatePrefsPath(), 'utf8');
449+
const obj = JSON.parse(raw);
450+
return obj.enabled !== false; // default ON if file missing or malformed
451+
} catch (_) {
452+
return true;
453+
}
454+
}
455+
function writeAutoUpdatePref(enabled) {
456+
try {
457+
fs.writeFileSync(_autoUpdatePrefsPath(),
458+
JSON.stringify({ enabled: !!enabled }, null, 2));
459+
} catch (e) {
460+
console.warn('[updater] failed to write pref:', e.message);
461+
}
462+
}
435463

436-
autoUpdater.on('update-available', () => {
437-
mainWindow?.webContents.send('update-status', { status: 'available' });
464+
// ── Auto-updater
465+
// Lifecycle events are wired ONCE here; the actual check is gated by the
466+
// stored preference and can be re-triggered manually via 'update:check-now'.
467+
let _updaterEventsWired = false;
468+
function wireUpdaterEvents() {
469+
if (_updaterEventsWired) return;
470+
_updaterEventsWired = true;
471+
autoUpdater.on('checking-for-update', () => {
472+
mainWindow?.webContents.send('update-status', { status: 'checking' });
438473
});
439-
440-
autoUpdater.on('update-downloaded', () => {
441-
mainWindow?.webContents.send('update-status', { status: 'ready' });
474+
autoUpdater.on('update-available', (info) => {
475+
mainWindow?.webContents.send('update-status', { status: 'available', version: info?.version });
476+
});
477+
autoUpdater.on('update-not-available', () => {
478+
mainWindow?.webContents.send('update-status', { status: 'up-to-date' });
479+
});
480+
autoUpdater.on('update-downloaded', (info) => {
481+
mainWindow?.webContents.send('update-status', { status: 'ready', version: info?.version });
442482
});
483+
autoUpdater.on('error', (err) => {
484+
mainWindow?.webContents.send('update-status', { status: 'error', error: err?.message || String(err) });
485+
});
486+
}
487+
488+
function initUpdater() {
489+
wireUpdaterEvents();
490+
if (!readAutoUpdatePref()) {
491+
console.log('[updater] auto-update disabled by user preference — skipping startup check');
492+
return;
493+
}
494+
autoUpdater.checkForUpdatesAndNotify();
443495
}
444496

445497
// IPC: renderer asks to install downloaded update
446498
ipcMain.on('install-update', () => {
447499
autoUpdater.quitAndInstall();
448500
});
449501

502+
// IPC: renderer flips the auto-update preference (persisted to disk)
503+
ipcMain.on('update:set-auto', (_evt, enabled) => {
504+
writeAutoUpdatePref(enabled);
505+
console.log(`[updater] auto-update preference set to ${!!enabled}`);
506+
});
507+
508+
// IPC: renderer triggers a manual update check (regardless of the
509+
// auto-update preference — explicit user action). Resolves with the
510+
// outcome so the UI can show "Checking…" / "Up to date" / etc.
511+
ipcMain.handle('update:check-now', async () => {
512+
wireUpdaterEvents();
513+
try {
514+
await autoUpdater.checkForUpdates();
515+
return { ok: true };
516+
} catch (e) {
517+
return { ok: false, error: e?.message || String(e) };
518+
}
519+
});
520+
450521
// ── Image cache IPC handler ───────────────────────────────────────────────────
451522
ipcMain.handle('img:get', async (_, url) => {
452523
if (!url || url === '--') return null;

‎package-lock.json‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tigertag-inventory",
3-
"version": "1.4.3",
3+
"version": "1.4.4",
44
"description": "Tiger Studio Manager — desktop app",
55
"main": "main.js",
66
"engines": {

‎preload.js‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ contextBridge.exposeInMainWorld('electronAPI', {
3030
// Ask main process to install the downloaded update and restart
3131
installUpdate: () => ipcRenderer.send('install-update'),
3232

33+
// ── Auto-update preference (ON by default) ─────────────────────────────
34+
// Renderer toggles auto-update at runtime. Main process honours it on
35+
// startup and on subsequent manual checks.
36+
setAutoUpdate: (enabled) => ipcRenderer.send('update:set-auto', !!enabled),
37+
38+
// Manually trigger an update check (used by "Check for updates" button).
39+
// Resolves with { ok: true } or { ok: false, error: "…" }.
40+
checkForUpdates: () => ipcRenderer.invoke('update:check-now'),
41+
3342
// ── App info (version / platform — used by the diagnostic report) ──────
3443
getAppInfo: () => ipcRenderer.invoke('app:info'),
3544

0 commit comments

Comments
 (0)