30 webpack require() calls replaced by a single eager import.meta.glob.
The extension-to-icon mapping is preserved exactly, restructured from a
switch chain into lookup tables. Fixes alt="name" rendering the literal
string 'name'.
Migrates n-link-core's Tailwind config from 1.x to 3.x (drops future/
variants/purge, swaps custom-forms for @tailwindcss/forms) and replaces
the Vue 2 'vue/types/vue' augmentation with Vue 3
ComponentCustomProperties. Deletes the Vue 2 shims.
Plan A verification found close_device timing out despite doing no I/O:
all commands were synchronous, Tauri runs sync commands on the main
thread, so a hung list_dir blocked every subsequent command.
The 11 libnspire-touching commands now spawn_blocking. enumerate and
close_device stay sync — neither performs a transfer. Return types become
concrete because impl Serialize cannot cross the async boundary. No
change to command names or payloads.
Ten tasks: async-command backend fix, Tailwind 3 + Vite foundation, the
8 n-link-core components, the device store, the app shell, verification.
Grounded in the real scaffold rather than assumptions. Reading the actual
sources surfaced three hazards missing from the earlier follow-on notes:
vue-async-computed (Vue 2 only, drives FileBrowser's listing and spinner),
30 require() calls in FileIcon, and @tailwindcss/custom-forms.
Tailwind 3 chosen over 4: the theme is a JS config with 16 @apply/theme()
uses in scoped SCSS, and 4's CSS-first rewrite would layer a second
migration onto the Vue one.
Task 1 fixes the command-serialization defect found during Plan A
verification: all commands were sync, so a hung list_dir blocked the whole
queue. It goes first because the UI is otherwise untestable on this
hardware.
Core components inject the store via an InjectionKey rather than importing
it, so n-link-core stays decoupled from the desktop implementation and the
web consumer remains portable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Correct the vendored libnspire-sys retry limit in spec and plan: the
actual constant in vendor/libnspire-sys/libnspire/src/cx2.cpp:391 is 30,
not 100. The '10 -> 100' claim originated in nlink-cli's own Cargo.toml
comment and was propagated into both documents unverified.
- Drop @tauri-apps/plugin-opener from desktop/package.json; the Rust plugin
and its capability entry were removed in Task 3, leaving this dangling.
- Reconcile desktop/package.json version 0.1.0 -> 0.1.6 to match Cargo.toml
and tauri.conf.json.
- Rewrite desktop/README.md, which still instructed users to
'sudo apt install webkit2gtk-4.0' -- the exact unavailable package this
port exists to move off. Now documents npm workspaces, the --legacy-peer-deps
requirement, the dist/-before-cargo gotcha, snap LD_LIBRARY_PATH breakage,
AppImage FUSE fallback, and the vendored patch.
Disclosure note: commit 4626858 also committed the previously untracked
nlink-cli/ and vendor/ trees (~5900 lines). Committing vendor/ is required --
the [patch.crates-io] override cannot resolve without it -- but the commit
message did not say so. Recorded here rather than rewriting branch history.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces Tauri scaffold placeholder icons with app-icon.png. Android/iOS
variants emitted by the icon tool are gitignored — this is a Linux-only
build per the plan's scope.
Verified: deb depends only on libwebkit2gtk-4.1-0 + libgtk-3-0, with no
webkit2gtk-4.0 or libssl1.1 reference — the failure this port exists to fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a throwaway App.vue that exercises enumerate/open_device/list_dir/
close_device over Tauri 2 IPC and logs addDevice/removeDevice/progress
events. open_device, list_dir, and close_device are wrapped with a 10s
client-side timeout each so a hang or Busy response from the CX II's
known firmware issue can't block the other calls from reporting;
enumerate only reads USB descriptors and is left untimed.
Installs @tauri-apps/plugin-dialog and @tauri-apps/plugin-shell per the
task-7 brief's Step 1. Manual GUI verification against a connected
device (brief Steps 3-4) is out of scope here and left to the user;
download_file/sha256 cross-check is intentionally omitted since any
libnspire transfer against this CX II is known to hang. Replaced by
the real UI in Plan B.
Reuses nlink-cli's already-migrated cli.rs rather than repeating the
clap 3-beta -> 4 migration. Also copies NOTICE.txt alongside cli.rs
since its include_str! path is relative to the file's own directory.
Hotplug registration moves from on_page_load to setup (runs once, so the
AtomicBool guard is dropped). v1 allowlist replaced by a capability file
granting dialog:allow-open and shell:allow-open. CSP from 0472908 carried
forward to app.security.csp; bundle targets narrowed to deb + appimage.
Also derives Clone on device::AddDevice: Tauri 2's Emitter::emit requires
Serialize + Clone (v1's did not), and the addDevice event payload was
missing the derive. No wire-format or IPC contract change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Window -> WebviewWindow, emit now requires the Emitter trait, and
hashbrown's drain_filter is replaced by an explicit collect-then-remove
on std HashMap. IPC contract unchanged.
Also derive Clone on ProgressUpdate (device.rs): Tauri 2's
Emitter::emit requires Serialize + Clone, unlike Tauri 1's
Window::emit which only required Serialize. Required for
progress_sender's window.emit("progress", ProgressUpdate { .. })
call to compile. No change to serialized shape or field names.
Splits the former 478-line main.rs into focused modules. Drops hashbrown
in favour of std HashMap (the drain_filter API it was used for was
renamed with changed semantics) and drops vendored libusb1-sys in favour
of the system library, matching nlink-cli.
Also removes the Task 2 scaffold's lib.rs/tauri-plugin-opener indirection
(the brief's module layout is a plain binary with no lib target) and drops
the now-dangling "opener:default" permission from capabilities/default.json,
which otherwise fails the Tauri build script with an unresolved permission
error.
Tauri 1.0.0-beta.8 links webkit2gtk-4.0, unavailable on Ubuntu 26.04.
Verified: builds and links libwebkit2gtk-4.1.so.0, libsoup-3.0.so.0,
libjavascriptcoregtk-4.1.so.0 with no webkit-4.0 or libssl.1.1 refs.
Workspace moved from yarn to npm; yarn.lock invalidated by this port.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
n-link-cli ls / returns Busy then hangs (known CX II firmware issue,
owned by the user, out of scope here). Transfer-dependent gates are
downgraded to non-blocking; enumerate remains a full verification of the
IPC path since it reads USB descriptors only and does no libnspire
transfer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eight tasks from system-dep install through verified deb/appimage
bundles, with a webkit2gtk-4.1 go/no-go gate at Task 2 and n-link-cli
used as the verification oracle against the connected CX II.
Splits the spec's two phases into two plans; Plan B (frontend) is
written after Plan A is verified so it can be grounded in the real
scaffold output rather than assumptions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ports desktop from Tauri 1.0.0-beta.8 (webkit2gtk-4.0, unavailable on
Ubuntu 26.04) to Tauri 2.x (webkit2gtk-4.1), alongside Vue 2 -> Vue 3 and
Vue CLI -> Vite. Wires in the locally patched libnspire-sys already proven
by nlink-cli.
Scoped to Linux (deb + appimage). The web/ package shares n-link-core and
is knowingly left broken, deferred to its own project.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>