Repository navigation
Milestone/docs and fixes - #1
Merged
Merged
Conversation
The action bar rendered "↵" and a lone ⌘ glyph as plain text, which fell
back to whatever font the system picked and sat on the baseline rather
than reading as a key. Split shortcut strings into parts so each key can
be drawn as its own cap, and reuse that in the actions menu and the
shortcut recorder.
formatShortcut now spells keys out ("Command Shift V") because the
recorder button exposes it as an aria-label.
Add an overlay walkthrough and a pairing preview section, and rework the hero, types, and privacy sections around stills of the real windows. Key combinations now render with the same keycap treatment the app uses. DownloadSection gains a first-launch note, which is where the unsigned build's "Open Anyway" instructions belong.
macOS 15 gates local network access behind a user permission. Without NSLocalNetworkUsageDescription and the Bonjour service type declared in the bundle's Info.plist the prompt has nothing to show and discovery can fail with no visible error — and discovery is the entry point to pairing, since a peer that is never found can never be added. Tauri merges src-tauri/Info.plist into the plist it generates.
The fallback URLs pinned v0.1.0's filenames, so the next release would have left them pointing at an old version — and they are the only path when the GitHub releases API is rate-limited or blocked. Resolve the version at runtime instead: fall back to the version published in the raw package.json manifest, which is a different host and not throttled, and pair it with the releases/latest permalink. Only when GitHub's API hosts are both unreachable does a pinned URL get used, and that one is pinned to a release tag so it stays downloadable instead of 404ing the moment a newer release renames the assets. Platform detection read userAgentData.architecture, which only Chromium exposes: Safari on an Intel Mac was offered the Apple Silicon build, and Linux visitors were offered a macOS one. An architecture that cannot be determined now offers both Mac builds rather than guessing arm64.
The footer and both locale files say MIT, but the repository carried no LICENSE file, so it granted nothing.
The backend reports a pinned-certificate mismatch as `trust_broken` and the device is marked accordingly, but no rule matched it, so the user only ever saw the generic error where the spec asks for "trust failed, pair again". The disk rule was the opposite problem: the wording existed but no code path could produce it, so it was a message that could never be shown.
A failed list fetch went through invokeSafe, which returned null, so a history or device load that errored rendered exactly like an empty one — the user could not tell "nothing to show" from "the backend is down". List loads now report failure separately from emptiness and the existing banner and error states show it, with a loading state so an empty list is only claimed once the fetch has actually finished. Keyboard fixes: the actions menu index incremented past the end of the list, after which Enter did nothing; the type filter could only be clicked; and the listbox claimed aria-activedescendant on a container that never held focus, so screen readers could not follow the selection. The replacement puts the active-descendant on the focused search field. Delete is now a two-step confirm inside the menu rather than an immediate, unrecoverable action, and offline devices are selectable instead of disabled, so the failure is reported instead of being prevented — which is what the spec asks for. HTML items preview as text rather than raw source: the markup is parsed inertly with DOMParser and reduced to readable text. Rendering it would need a sanitiser, which this app does not ship.
The rule looked for "source gone", but the backend returns `source_file_gone`, so a paste whose source file had been cleaned up showed the generic message instead of saying the other machine no longer has it. Found by the test added alongside it, which asserts the real wire strings rather than the message wording.
The frontend had no tests at all: every check was a type check, so nothing verified behaviour. Add vitest and cover the modules that are pure logic — byte and time formatting, shortcut parsing, history grouping, the error mapping, and title labelling — including the bilingual catalogue key-parity rule, which until now was only asserted on the Rust side. Tests assert the strings the backend really sends, which is what caught the mismatched source-gone rule fixed in the previous commit.
FlatRow is a discriminated union, so the label only exists on the header variant. vitest does not type check, so this only surfaced in the build.
About pane printed "0.1.0" written into the component, and the release workflow's version gate only checked package.json, tauri.conf.json and Cargo.toml — so the string would have drifted the first time a release bumped the other three. Inject the version from package.json at build time so there is no fourth copy to forget.
The conventions that are easy to get wrong are not obvious from the code: UI work is bound by design-system/lanpaste, every user-visible string needs a key in both catalogues, and backend errors are codes that must be mapped on the frontend. Write them down, along with how to build, test and release. The bug template asks the things that actually decide most reports — both machines' operating systems, whether they are on the same network, and whether discovery or the transfer is the part that fails.
The root `site` script invoked pnpm while site/ carries an npm lockfile and CI installs with `npm ci`, so the convenience script only worked for someone who happened to have pnpm installed. Also add a `test:rust` script so the Rust suite runs the same way as the frontend one.
Nothing ran the test suites: the frontend had no runner at all, and the Rust tests existed but only ever ran on a developer's machine. Add a workflow covering type checking, tests and locale parity for the app, a build of the marketing site, and `cargo test` on macOS, which is the platform the clipboard layer and the golden path are built for. Deliberately no `cargo fmt --check` and no clippy gate: the crate is not rustfmt clean today (13 files differ), so either would fail on its first run. Both are worth adding once formatting is settled in its own change.
Three claims no longer matched the code. The overlay is no longer macOS only, so the north star should not read as a macOS-only target. The sticky time-section label was dropped by design and was never implemented, so listing it as a requirement invites someone to "fix" it back. And the checklist named only the macOS chord. Also add the constraint the app already follows but the design system never stated: user-visible copy comes from the locale catalogues, both of which must carry the same keys, and a design doc should cite the key rather than a literal string in one language.
The spec still described a macOS-only v1 with a Cmd+Shift+V default and Simplified-Chinese-only copy; Windows shipped, the default is Option+Shift+V on macOS and Ctrl+Shift+V elsewhere, and the UI is bilingual and follows the system language. Fix those in place, and add a short section recording what landed that the spec never anticipated, so none of it gets mistaken later for missing work. The implementation contract had drifted in smaller ways: the pairing input window is 400x220 rather than 400x200, a tray click toggles the overlay instead of only showing it, and the event list was missing locale-changed. Note at the top that the code wins where the two disagree.
The shortcut sections were already right, but a reader had no link to the published site, and "Building from source" stopped at the dev server. Add the download page, the production build command, and a line that the site under site/ is built separately. Both languages get the same changes.
Four problems, all around what the network layer promises. Transfers could not resume. The server has always answered Range requests, but the client asked for the whole file, buffered it in memory, and threw every received byte away on failure — so a large file on a flaky link could never finish. The client now streams to a partial file beside the blob store, retries with `Range` plus `If-Range` so a changed file is never spliced onto stale bytes, and only moves the bytes into place once the whole file has arrived. A partial that already holds the full length (the process died between the last byte and the commit) is finalized without touching the network. Progress is reported cumulatively so the bar does not jump back on resume, and per-item locking keeps two downloads of the same file from interleaving into one partial. A blob could be deleted mid-transfer. The download handler marked a blob in-flight and then cleared the mark from a task that slept two seconds, regardless of whether the stream had finished — so any transfer longer than two seconds, or two concurrent downloads of one blob, could have its bytes garbage-collected underneath it. The mark is now held by the response stream itself and released when the stream ends, the client disconnects, or the response drops. The retry queue both leaked and stalled. A send that failed while the peer was reachable — rejected, over the size cap, any HTTP error — was dropped silently, and the caller ignored the error. Failures are now classified: transient ones are queued, permanent ones are returned so the UI can show them. The queue also records attempts and the last error in new columns that migrate existing databases in place, and it evicts rows that can never succeed instead of retrying them every three seconds forever. Three endpoints trusted more than they should. `POST /pair/revoke` had no authentication at all, so any host on the network could unpair someone else's machines; it now requires a signed request and a device may only revoke itself. `GET /files/:id` skipped its token check whenever an item had no token, and image items never got one, leaving those blobs readable by any paired device; every downloadable item now carries a token and the token is always required. And `pair_confirm` accepted whatever key, certificate and fingerprint the caller supplied, never comparing them to what the pairing request had declared — it now cross-checks every field and rejects mismatched or malformed claims.
Rejecting a confirm whose identity claims do not match the request introduced a new code, and it is one a user can hit while pairing, so it needs wording rather than the generic failure message.
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.
No description provided.