Repository navigation
Fix launch crash and empty-library bugs; add multiple libraries + manifest inference (1.1.0) - #1
Merged
Conversation
The library watcher created a recursive fs.watch over the library root but
only guarded the synchronous setup call. On Windows a recursive watch over an
OneDrive-synced Documents folder (the default root) or a network share emits
an asynchronous 'error' event ("UNKNOWN: unknown error, watch") from the
FSWatcher handle. With no 'error' listener, Node's EventEmitter rethrew it as
an uncaught exception, killing the main process with the fatal "A JavaScript
error occurred in the main process" dialog — leaving the window blank so files
could not be seen or selected.
Attach an 'error' listener that closes the watcher and degrades to manual
refresh, and add main-process uncaughtException/unhandledRejection guards so a
stray async failure no longer takes the whole app down.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rbmo7LSmxRtFf5zwfvw83
…egrade gracefully
Feature — multiple named libraries:
- New pure-Node model electron/libraries.js: a library is a named pointer
{ id, name, root }. Renaming edits the app-side label only; the folder on
disk is never touched (consistent with the title/tag override design).
Removing a library just unlinks it — files stay on disk — and the last
library can't be removed. Legacy single-root installs migrate automatically.
- main.js keeps a normalized { libraries, activeLibraryId } view; the existing
libraryRoot() now resolves the ACTIVE library, so every downstream IPC
handler keeps working unchanged. New IPC: libraries:list/add/rename/remove/
switch/reveal, plus library:chooseRoot repoints the active library.
- preload exposes window.shopdeck.libraries.*.
- Renderer: an active-library switcher in the top bar and a full Libraries
manager in Settings (use / rename / reveal / remove / add). Rename uses the
in-app modal (Electron has no window.prompt).
- Unit tests for the model (runs under the existing pure-Node CI).
Fix — sibling of the fs.watch crash (a network-share failure must not brick
the app): every IPC handler funnels through libraryRoot(), whose fs.mkdir
threw when the configured root was an offline share or a deleted folder;
library:scan then rejected and the renderer's reload() (no try/catch) hung on
"Loading…" forever. Now libraryRoot() never throws, scan degrades to a soft
error object, and the renderer shows a dismissable error banner with Retry
and Manage libraries instead of an empty window.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rbmo7LSmxRtFf5zwfvw83
Builds the NSIS + portable installers on a windows-latest runner and uploads them as a private, auto-expiring workflow artifact — a way to produce an installer without cutting a public GitHub Release (that stays with release.yml on v* tags). Triggers via workflow_dispatch or a winbuild-* tag push. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rbmo7LSmxRtFf5zwfvw83
Tag pushes aren't permitted from the current environment, so add a branch trigger for this WIP branch to kick off the installer build via CI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rbmo7LSmxRtFf5zwfvw83
…ing empty state Root cause of "the library shows the folder but no modules": OneDrive "Files On-Demand" placeholders are reparse points, and fs.readdir(withFileTypes) reports them as symlinks — dirent.isFile() is false — so the scan's walk() skipped every .html module while still listing the (real) directory. Moving the files to another folder on the same volume keeps the placeholder, so it reproduced off OneDrive too. - New entryKind() helper: trust the dirent when decisive, otherwise stat to resolve the real type (stat reads metadata only; it does not download an online-only file). walk() and walkHtmlUnder() use it, so placeholder/symlink .html files are discovered. A realpath-keyed seen-set guards against cycles now that reparse entries are followed. - scanLibrary now returns htmlCount + a `skipped` list (file + reason) for files that were found but couldn't become modules. - The library's empty state uses those to explain itself: "Found N HTML files here, but none are valid modules … First: <file> — <reason>." instead of a bare "Library is empty," so any remaining cause is visible without guesswork. - Tests: a symlinked module is discovered (reproduces the placeholder dirent), and htmlCount/skip-reasons are reported for invalid files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rbmo7LSmxRtFf5zwfvw83
…load
Files exported by the standalone timeline tool carry no embedded
module-manifest — only a <title> and a `const DATA = {…}` object — so ShopDeck
listed the folder but no modules. Support both kinds of file:
- scanLibrary now synthesises a manifest when a file has none (or an invalid
one): id from the filename, title from <title>, and — when the embedded
timeline DATA is present — type tool-swap-timeline enriched with part, event
count, date range, and operation tags pulled straight from DATA. Such modules
are flagged inferred:true; files with a real manifest stay authoritative.
- importFiles / importTree no longer reject manifest-less HTML — any
self-contained HTML can be imported and is inferred on scan.
- The card shows "· no manifest" for inferred modules; the empty-state wording
no longer claims a manifest is required.
- Tests: inference of a tool-swap timeline from its DATA (part/events/tags/
dates), generic inference from <title>/filename, and import of manifest-less
files. Verified against the real exported file (part 40-1318-01, 147 events).
- CLAUDE.md: document that the manifest is now optional (inferred when absent).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rbmo7LSmxRtFf5zwfvw83
Records each of the four bugs (launch crash, unreachable-library hang, empty OneDrive scan, manifest-less timelines) with symptom / root cause / fix / files / commit, plus the multi-library feature and the Windows installer build workflow, and a summary table. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rbmo7LSmxRtFf5zwfvw83
Minor release: adds multiple libraries + rename and manifest inference for standalone-tool timelines, on top of the launch-crash, unreachable-library, and empty-OneDrive-scan fixes. Installers produced from this branch are now named shopdeck-1.1.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rbmo7LSmxRtFf5zwfvw83
AxialForge
marked this pull request as ready for review
August 1, 2026 01:31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Started from the reported bug — "the app won't let me see or select files," a fatal dialog on launch — and fixed the full chain it uncovered, verified on a real Windows + OneDrive setup. Full write-up in
FIXES.md.Bugs fixed
1. Launch crash —
UNKNOWN: unknown error, watch(44a3ec6)The library watcher's recursive
fs.watchonly guarded its synchronous setup. On Windows, a recursive watch over an OneDrive-synced folder (the defaultDocumentsroot) or a network share fires an asynchronouserrorevent with no listener, so Node re-throws it and the main process dies before the window renders. Fix: attach an'error'listener (degrade to manual refresh) + main-processuncaughtException/unhandledRejectionguards.2. Unreachable library hung the UI on "Loading…" (
d730fcc)Every IPC handler funnels through
libraryRoot(), whosefs.mkdirthrew on an offline share/deleted folder;library:scanrejected and the renderer'sreload()had notry/catch. Fix:libraryRoot()never throws,library:scanreturns a soft error object, and the renderer shows a dismissable error banner with Retry.3. Folder shown but 0 modules — OneDrive on-demand files (
0aa5aee)OneDrive "Files On-Demand" placeholders are reparse points that
readdirreports as symlinks, soentry.isFile()was false and the scan skipped every module while still listing the directory. Fix: anentryKind()helper thatstat-resolves ambiguous entries (metadata only, no download), plus a self-diagnosing empty state (htmlCount+ skip reasons).4. Standalone-tool timelines had no manifest (
627ec6a)Files exported by the standalone tool carry a
<title>and aconst DATA = {…}but nomodule-manifest, so they were correctly found but rejected. Fix: ShopDeck now infers a manifest when one is absent — id from the filename, title from<title>, and for tool-swap timelines the part/event-count/date-range/tags fromDATA. Inferred modules are flaggedinferred: true(card shows "· no manifest"); real manifests stay authoritative. Import no longer rejects manifest-less HTML.Features
d730fcc). New pure-Nodeelectron/libraries.js: a library is a named pointer{ id, name, root }. Rename edits the app-side label only (the folder on disk is never renamed); remove just unlinks it (files stay); the last library can't be removed; legacy single-root installs migrate automatically. New IPClibraries:list/add/rename/remove/switch/reveal, a top-bar switcher, and a Libraries manager in Settings.d8ec34c,c967885).build-windows.ymlbuilds NSIS + portable installers onwindows-latestand uploads them as a private artifact — used for the test builds throughout.release.yml(public release on av*tag) is unchanged.Version
Bumped to 1.1.0 (
3a363df) — minor, since it adds features on top of the fixes.Tests
test/library.test.mjs+test/libraries.test.mjs— 24 pure-Node tests passing, including OneDrive/symlink discovery and manifest inference (timeline + generic). CI (node --test, Ubuntu) and the Windows installer build are both green. Electron can't be launched in the CI/agent environment (install scripts blocked, perCLAUDE.md); each fix was confirmed on the user's real Windows machine via a fresh installer build, ending with all 9 timelines showing correctly.🤖 Generated with Claude Code
https://claude.ai/code/session_019rbmo7LSmxRtFf5zwfvw83