mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
f7a2c626c9a62993265127488cbaa9b7e8e350cc
1180
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f7a2c626c9 |
Persist the workbench session across the editor/processor switch (#7654)
## What Switching editor -> processor (or reloading) unmounts every editor provider, which emptied the workbench. This PR mirrors the workbench into per-tab sessionStorage and refills an empty one from that record on the next mount: - **Files, selection, view and active document survive** the shell switch and reloads. Each recorded file is resolved to its *current leaf* version on restore, so a file versioned by a policy or another tab comes back at its latest state. - **The switch back lands where the user left**: the processor sidebar's "editor" button consumes a one-shot return path saved at switch time. - **The app switch respects unsaved changes**: `useOtherAppSwitch` (proprietary + saas) now routes through `requestNavigation`, so the same warning guards it as any other navigation. - Desktop shadows `WorkbenchSessionPersistence` with a stub (OS-launched files own boot there). ## How to test I've run through each of these manually: - Upload several PDFs in the editor, select a couple, and switch to the Active Files grid. Click "Open PDF Processor" in the sidebar footer, then switch back to the editor. The same files, selection and view should return, and you should land on the editor page you left. - Open a document in the viewer, then reload the tab. The workbench should refill and come back on the viewer with the same document active. - With unsaved changes in a tool, click the processor switch. The unsaved-changes warning should appear, and the switch should only proceed if you confirm. - Open a second browser tab with different files. Each tab should restore its own workbench independently (the record is per-tab sessionStorage). - While in the processor, delete one of the open files from storage, then switch back. The remaining files should restore and a warning toast should report "Restored X of Y files". --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
caeca0b88a |
Fix classification escalation: the local pass was claiming the server dispatch key (#7667)
Follow-up to #7580: the escalation it added could never fire. ## What's broken The auto-run skips a policy that has already run on a file, keyed on `(categoryId, fileId)`. `recordRunStart` claims that key — and #7580 has the **browser-side first pass** record its own run under `categoryId: "classification"` for the uploaded file. So the local heuristic ticks the very key the server escalation checks, and the AI is never asked, at any confidence. Trigger is the default seeded setup: **Classification as the only on-upload policy**, and a local verdict below `high`. Any other on-upload policy masks it, because classification then targets that policy's output — a new file id whose key was never claimed. That's why this went unnoticed. Two smaller faults in the same path: - A chained output carried no `classificationConfidence`, so `shouldDispatchToAi` waited for a verdict that could never arrive (a tool-derived file gets no local pass). - Browser-local runs were polled against the server: 3 × 404 per file, after which `MAX_NOT_FOUND` marked a local run that had actually **succeeded** as `FAILED`. ## The fix - `PolicyRunRecord.browserLocal`; `recordRunStart` skips the dispatch claim for such a run. It is the first pass, not the policy's run. - The local pass meters under `classification:local-meter` instead of the category id, so metering dedupe survives without suppressing dispatch. - The poll effect skips browser-local runs. - `CONSUME_FILES` inherits `classificationConfidence` alongside the labels, so the verdict survives a version bump. ## How to test Download [`low-confidence-classification.pdf`](https://github.com/Stirling-Tools/Stirling-PDF/raw/fix/chained-classification-confidence/frontend/editor/src/proprietary/services/heuristic/fixtures/low-confidence-classification.pdf) (checked in as a fixture, verdict pinned by a test). With **Classification as the only on-upload policy**, upload it and watch the Network tab: - **Before:** no `POST /api/v1/policies/{id}/run` for classification, ever. Console shows `local-classification-*` 404s. - **After:** exactly one, and the engine receives `POST /api/v1/documents/classify`. Judge it on that request, not on the resulting label — the model's answer varies, so a label comparison can pass or fail for the wrong reason. Headless equivalent: ``` npx vitest run --project proprietary src/proprietary/components/policies/usePolicyAutoRun.escalation.test.tsx ``` Passes here, fails on `main` on "asks the AI about an unsure verdict even though the local pass already ran". Its other two cases pass on both, so the guards still hold: a confident verdict still costs nothing, and a file with no verdict yet still waits rather than racing the free pass. New tests drive the **real** run store — mocking it is what let this through. `task frontend:check`: 255 files / 2202 tests. |
||
|
|
c93feb5dfc |
Remove a bunch of unnecessary casts from the frontend (#7662)
# Description of Changes Originally, I wanted to re-enable typed linting on our repo but using Oxlint this time to avoid the memory and speed issues that ESLint was causing. Unfortunately, it's not stable enough yet to actually use on our repo (although it is close, I suspect it'll be stable enough fairly soon). I was able to remove many of the unnecessary casts that it found though, so even though this won't be enforced, it's still worth cleaning up what I've found. |
||
|
|
f945cc7dc6 |
Regenerate expired test certificates and guard against future expiry (#7682)
The bundled signing test certificates expired at **07:41:10 UTC on 2026-08-26**. They were issued exactly one year earlier, so they went from fine to fatal mid-morning with no warning, and they take down `main` and every open branch, not just one PR. First casualty was the `docker-compose-tests` job on #6802, which started at 07:45: ``` java.security.cert.CertificateExpiredException: NotAfter: Wed Aug 26 07:41:10 UTC 2026 at CreateSignatureBase.checkValidity(CreateSignatureBase.java:159) at CertSignControllerTest.testSignPdfWithPkcs12(CertSignControllerTest.java:205) ``` ``` $ openssl x509 -in app/core/src/test/resources/certs/test-cert.pem -noout -dates notBefore=Aug 26 07:41:10 2025 GMT notAfter =Aug 26 07:41:10 2026 GMT ``` ## What was broken `CertSignControllerTest` (7 tests) and `PdfSigningServiceImplTest` (2) fail outright. `ValidateSignatureControllerMoreTest` and `CertificateValidationServiceMoreTest` read the same fixtures. Auditing the rest of the repo turned up three more time bombs that had not gone off yet: | Fixture | Was | Problem | |---|---|---| | `app/core/.../certs/test-cert.*` + `test-key.*` | expired 2026-08-26 | **already breaking every branch** | | `test-certs/valid-test.p12`, `valid-test.jks` (proprietary + frontend copies) | expire 2027-03-25 | same failure, seven months out | | `test-certs/not-yet-valid-test.p12` | valid **from** 2027-03-25 | becomes valid, so its test silently stops proving anything, on the same day | ## What this does **Regenerates every fixture** with the identical subject DN, alias, password, key size and signature algorithm as before, changing only the validity window. Nothing that any test asserts on has moved. - valid fixtures: `2025-01-01` to `2125-01-01` - `not-yet-valid-test.p12`: `2125-01-01` to `2126-01-01`, so it stays in the future - `expired-test.p12`: pinned to its permanently-past 2024 window **Adds `scripts/generate-test-certs.sh`** as the source of truth, so the next regeneration is one command instead of archaeology. It documents every DN, alias and password, pins the validity windows, and runs on Linux, macOS and Git Bash. **Adds two guard tests** that fail with an actionable message, naming the script, while there is still a year of runway: - `BundledTestCertificateExpiryTest` (app/core) checks all seven formats parse, are in their validity window, and have more than 365 days left - `BundledWorkflowCertificateExpiryTest` (proprietary) does the same for the valid pair, and additionally asserts the expired fixture is still expired and the not-yet-valid one is still in the future That last pair matters: those two fixtures exist to test a validity outcome, and each one silently stops testing anything once the clock passes its window. ## Verification Run locally against the regenerated bytes, on the exact content committed here: ``` ./gradlew :stirling-pdf:test --tests '*CertSignControllerTest*' --tests '*BundledTestCertificateExpiryTest*' \ --tests '*PdfSigningServiceImplTest*' --tests '*ValidateSignatureControllerMoreTest*' \ --tests '*CertificateValidationServiceMoreTest*' BUILD SUCCESSFUL ./gradlew :proprietary:test --tests '*BundledWorkflowCertificateExpiryTest*' --tests '*CertificateValidationIntegrationTest*' \ --tests '*SigningFinalizationServiceMoreTest*' --tests '*ServerCertificateServiceTest*' \ --tests '*CertificateSubmissionValidatorTest*' --tests '*WorkflowSessionServiceTest*' BUILD SUCCESSFUL ``` `spotlessCheck` passes on both modules. |
||
|
|
72b7892312 |
Translations + com.squareup.okhttp3:okhttp-bom from 5.3.2 to 5.4.0 (#7599)
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Signed-off-by: dependabot[bot] <support@github.com> |
||
|
|
353df7a647 |
Improve modals in Sources page in Processor (#7664)
# Description of Changes Various changes throughout to try and convert the bulk of the dev UI sources modals to production quality. Changes include: - Fixing inconsistencies between different modals - Hide things users will rarely need to change behind advanced - Removed clutter in the UI - Renaming settings in terms that the user will understand and care about <img width="2360" height="3068" alt="image" src="https://github.com/user-attachments/assets/2c637e8f-bc1b-4c7e-98cb-d836ae626ba5" /> <img width="2360" height="3008" alt="image" src="https://github.com/user-attachments/assets/d894aadc-7c83-41d1-b614-881637a6bd34" /> |
||
|
|
0d75715af2 |
Fix flaky e2e tests (#7595)
# Description of Changes e2e Playwright tests are currently failing intermittently on all platforms for different reasons, most notably WebKit, which seems to fail much more often than the others. This PR attempts to fix the issues. I've ran the e2e tests a few times now and they don't seem to be inconsistent any more, but it's difficult to tell if all the issues are genuinely fixed due to the inconsistent nature. As far as I can tell, I've not broken anything though. |
||
|
|
49c1e75ced |
Surface recorded failures in a notification bell (Review Flow PR 4) (#7478)
Review Flow PR 4. Stacked on #7477. Recorded failures appear in a notification bell, showing each reader the failures they are allowed to see and the actions they can actually take. Scope is deliberately viewing and routing only. Resolving a failure — retry, decrypt-and-retry — is #7479, which also brings the write path for it; nothing resolution-shaped ships here, not even dark. ## What's added **A notification bell** in the editor and the processor shell. Polls `GET /api/v1/notifications` every 30 seconds, shows an unread badge, and lists open failures newest first. Each row shows the failure's title, its message with **Copy error** and **Show full message** chips, an occurrence count, and its available actions. **A notification API** (`stirling.software.proprietary.notification`), derived from failures on read rather than stored in its own table: | Route | Purpose | |---|---| | `GET /api/v1/notifications` | the caller's open failures, newest first | Read-only by design: every action the bell offers is one the client runs on its own device, so there is nothing to post back. Every id is prefixed (`failure:<uuid>`), so the bell never holds a raw failure id it could hand to a failure endpoint. **Per-reader actions.** A `FailureKind` declares each action with an audience (`OWNER`, `TEAM_REVIEWER`, `ANYONE_WHO_SEES`). The server resolves that against the reader and derives `Ownership` (`MINE` / `THEIRS` / `UNOWNED`) from the row's actor, so an admin reviewing someone else's failure is not offered a document their browser does not hold. Adding a failure kind requires no frontend change. **Server-run and client-run actions are distinguished.** `FailureActionId` carries an `Execution` facet; the registry requires a bean only for server actions, and dispatching a client action on the failure surface returns 400. The notification projection goes further: it carries only client-run offers, so the bell cannot be sent a button it would refuse to draw. **Actions in the bell:** at most two. The owner of the document gets **View file** (opens it in the editor); a team reviewer gets **View in processor** (dev builds only). Dismiss stays on the failure queue in `/processor/documents` — deciding a failure's fate belongs to the review surface, not the panel that announces it. An action id the build has not wired is skipped rather than rendered dead, so the server can ship new kinds ahead of the clients that understand them. **Attended policy runs record their document.** `POST /api/v1/policies/{id}/run` accepts an optional opaque `fileId`, recorded when the run carries exactly one primary document. This is what lets a repeat fold onto one incident instead of opening a new one per upload, lets deleting the file clear its failure, and lets the owner open the document from the row. ## Behaviour changes - **The bell re-reads as soon as a failure you caused is recorded**, rather than leaving you to wait out a poll interval for news of your own upload. Applies to a failed tool run and to a policy run reaching `FAILED`. Other people's failures still arrive on the poll, which is what it is for. - **An action the reader cannot use is not rendered.** Where the server gave a reason for withholding it, that reason appears as the row's one-line note. An action that was never offered to that reader produces no note. - **Deleting a document closes every incident about it that the deleter caused**, including a failed policy run on their own upload, so a user's own errors leave the bell with the file rather than lingering with a dead button. - **The failures list in `/processor/documents` stays behind `import.meta.env.DEV`**, and View in processor is gated to match so it cannot navigate to a section that is not mounted. Both lift when failures get their own review screen. - **One poll for all bells.** The bell is mounted in three places; the list, document lookups and read marker are shared, so mounting more than one does not multiply requests. - `ACKNOWLEDGE` is no longer offered by any kind. The id, bean and status remain so existing rows stay readable. ## Known limits - The poll does not pause when the tab is hidden. - No retention or per-team cap on `file_run_events`. ## How to test Needs a proprietary or SaaS build with login enabled. `task dev:all`, then sign in. 1. **Create a failure.** Add a password-protected PDF to the editor and choose **Skip for now** when it asks to unlock. The upload starts a policy run that fails on it. 2. **Watch the bell.** The badge should appear within a second or two, not after 30 — this is the refresh-on-failure path. Open it: a row titled "Password-protected document" with the error message and the two chips. 3. **The buttons should be View file and View in processor, nothing else.** No Dismiss and no retries: dispositions live on the review surface, resolutions in #7479. 4. **View file** closes the panel and selects that document in the editor. 5. **Dismiss from the queue instead.** Open `/processor/documents` (dev build), find the row in the failures list and dismiss it there; the bell drops it on its next read. 6. **Confirm the local-document probe.** Create a second failure, then delete that file from the editor and reload. Its incident closes with it; a row whose document is still present keeps **View file**. 7. **Confirm attribution end to end.** Sign in as a plain member, run a shared policy on your own upload so it fails. The member sees their own row in the bell. Sign in as the team leader: they see it too, but with **View in processor** instead of **View file**, because the document is not in their browser. 8. **Confirm folding.** Add the same locked PDF again and skip again. The existing row's occurrence count increases rather than a second row appearing. 9. **Confirm one poll for many bells.** Open the editor and the processor in two tabs. Each tab issues its own poll, but within a tab the several mounted bells share one — the Network tab should show one `GET /api/v1/notifications` per 30s per tab, not three. ## Migration None. No new column and no new value in any CHECK-constrained enum; `CheckConstrainedEnumsTest` fails if that changes. |
||
|
|
826e487f00 |
Update Frontend 3rd Party Licenses (#7650)
Auto-generated by stirlingbot[bot] This PR updates the frontend license report based on changes to package.json dependencies. Signed-off-by: stirlingbot[bot] <stirlingbot[bot]@users.noreply.github.com> Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com> |
||
|
|
79686a3a09 |
form field editing (#6655)
# Description of Changes Building ontop of a users draft PR for form creation tools **Fill Form** becomes a full **Form Editor**: fill, create, modify and delete AcroForm fields visually. Builds on the community form-creation draft, plus a UX/UI rework pass. - **Backend**: `/api/v1/form` endpoints — `fields-with-coordinates`, `add/modify/delete-fields`, combined `edit-fields` (one round-trip), `fill`, `extract-csv/xlsx`; supports text (multiline, comb), checkbox, dropdown, list box, radio, button actions (reset/print/URL/submit) and signature placeholders - **Create**: type palette, click-or-drag placement with snap guides, inline property editor, batch "Add N fields" - **Modify**: move/resize on the page, arrow-nudge + Delete key, X/Y/W/H inputs, staged edits/deletes with chips, discard - **Fill**: live progress + required tracking, flatten toggle, Export menu (JSON/CSV/XLSX), Ctrl/Cmd+S - **Safety**: confirm dialog before discarding staged work; empty required fields warn with "Save anyway" instead of blocking - **UI**: consistent panel skeleton (fixed header / scrolling list / pinned actions), empty states that link into Create, full i18n with plural keys [walkthrough.html](https://github.com/user-attachments/files/30508976/walkthrough.html) --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. --------- Co-authored-by: Denys Vitali <denys@denv.it> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
158187ac46 |
Fix Documents tab in Processor (#7569)
# Description of Changes The Documents tab in the Processor is supposed to be available to all Processor users, but because the API is built on top of the Audit data, which is only for enterprise users, the API call always fails with 403. This means that it never fills the query cache, so every time you go back to the tab it has to reload all the data for a couple of seconds (and will fail again). This fixes the API so that it's available to any Processor user instead of just enterprise users. Also, the documents data was only being written to the log on an enterprise license, so I've changed it so that data is always tracked in the audit log because otherwise the Documents tab would still be useless to non-enterprise users. The Audit Log tab was also available to all Processor users, but would have the same issue where the table would never load because the API would 403 as well. I've just made the Audit Log tab disabled for non-enterprise users now. We might want to do something to signpost it a bit more that it's an enterprise-specific feature, but it's better than nothing for now. |
||
|
|
e50c3de0a9 |
Run classification locally first and only escalate an unsure verdict to the AI (#7580)
Split out of #7574 — this is the classification half, which is independent of the editor-source work and can land on its own. ## What this does - **Runs the local heuristic first and only escalates an unsure verdict to the AI.** A high-confidence local answer stands; anything less (or a file the heuristic hasn't reached yet) goes to the engine. A wrong label costs more than an engine call, so the bar is deliberately strict. - **Makes `classify` an authorable pipeline task**, so it can be used as a step like any other tool, and skips files that are already classified. - **Leaves the seeded Classification policy unowned** rather than naming a `system` placeholder that was never a real user; existing seeds are repaired on boot. ## Review feedback applied From @jbrunton96 on #7574: - **The generic runner no longer names classification.** Everything classification-specific moved into `proprietary/data/classificationPolicy.ts`, and `usePolicyAutoRun` now asks capability questions instead: `policyRewritesDocument`, `policyDeliversOutputFiles`, `policyRequiresAiEngine`, `shouldDispatchToAi`. There is no `id === "classification"` left in the runner. - **Ordering is no longer a name in the runner.** `pinClassificationLast` is gone; the runner sorts annotating policies after rewriting ones. The constraint is real: an annotating policy is non-blocking, so a rewriting one running after it forks from the pre-annotation version and drops the labels. To be straight about what this is and isn't - see "Still open" below - `policyRewritesDocument` is still keyed on the category id, not on a property each policy declares. The check moved out of the runner; it did not stop being a check on one id. - **Confidence is typed.** New `ClassificationConfidence` union in `core/types/fileContext.ts`, reused by `fileStorage`, `HeuristicConfidence`, and the trusted-verdict constant instead of being respelled at each site. - **Comments trimmed** to the repo's 2-line guideline, and a stale seeder javadoc that still claimed an internal-user owner was corrected. ## Still open, deliberately `classificationPolicy.ts` answers its capability questions with `categoryId === "classification"`. That is the same check relocated, not removed, and the module doc now says so outright. Deliberate, for two reasons: - **The concept it would be declared against is going away.** Policies are becoming pipelines with labels behind a separate enforcement layer, which removes the category the flag would live on. A capability system built on `categoryId` today gets migrated twice. - **Classification is genuinely privileged, not accidentally special.** It is the only policy with a browser-side implementation, so it can answer without the server. That is a product decision, and a local-only mode for set scenarios is planned - the flag for it should be designed with that feature, not guessed at now. The end state for the rest: an in-place output mode retires the ordering rule and `policyDeliversOutputFiles`, and a run result that can carry findings as well as files retires the remainder. Both touch the import path, which is the most delicate code in `usePolicyAutoRun` - not something to bolt on to a PR that has already been split once. Nothing is broken by leaving it. A user-built classify pipeline still gets its labels: the generic import path reads them off the returned PDF. It versions the file instead of labelling in place, and it misses the local-heuristic shortcut, so it always bills the engine. ## Testing - `classificationPolicy.test.ts` — 12 cases covering each capability and the escalation rule - Full frontend `proprietary` project: 39 files / 442 tests - `:proprietary:test` for `DefaultClassificationPolicySeederTest` + `ClassifyLabelControllerTest` - `tsc --noEmit` on core, proprietary, portal, saas, desktop, cloud --------- Co-authored-by: James Brunton <jbrunton96@gmail.com> |
||
|
|
dbd60d4765 |
Replace Prettier with Oxfmt (#7422)
# Description of Changes Prettier takes about 10 seconds to run over our frontend folder, but [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html) does an (almost) identical job in 0.2 seconds. This PR converts our Prettier integration to an equivalent Oxfmt integration. There's exactly 2 files in the frontend folder that Oxfmt formats differently to Prettier so it'll barely cause any disruption to the source. I've removed the `--check` option from the frontend tool models generator as part of this because we can do the same thing with Task easily enough and Oxfmt isn't directly importable like Prettier since it's a Rust binary instead of a JS library. Originally I was shelling out to Oxfmt on single-file mode to keep it all in memory but it just seemed more likely that there'd be config mismatches between that script and the task so I think it's better this way. |
||
|
|
629f501e9c |
Fix any type usages in frontend (#7617)
# Description of Changes Follow-on from #7334. Fix more `any` type usages and ban them in the linter. We're starting to get down to only difficult folders left now, so some of these fixes replace an excluded folder with a couple of individual files to reduce scope to manageable levels. There are two real behaviour changes in this PR because of bugs that were never caught due to the lack of proper typing: - In the Google Drive service, `lastModified` was always `undefined` because it should have been read via `lastModifiedUtc`, which it now is. This means that files being read from Google Drive should now accurately retain their last modified date from Drive. - In the error toasts, there was translation logic to try and make friendlier error messages, but it'd never actually fire since it relied on `i18n` being written to `globalThis`, which it never was. It now imports the singleton instead so that translation should start working. I also had to tweak the way that FitText works because it was relying on `any` typing to mix refs between different places where they weren't technically compatible but I've changed it to go via a function and the behaviour doesn't change. |
||
|
|
1df372764f |
Mobile follow-ups to #7518: tool-list search, and drop the empty overflow menu (#7660)
# Description of Changes Follow-up to #7518, picking up two mobile rough edges found while going over that branch. Two changes, one commit each. ## 1. Tool search back in the tool list (mobile) Tool search lives in the workbench bar's super search, which on mobile sits on the Workspace slide. So searching for a tool meant swiping off the tool list, typing, then swiping back. This puts a filter at the head of the tool panel on mobile. Reuses the existing `ToolSearch` component in `mode="filter"`, the same one the desktop fullscreen picker uses. Drives `setSearchQuery` on `ToolWorkflowContext`, so the query, filtering and grouped results are all existing paths. `ToolPanel` takes a new `showSearch` prop; `RightSidebar` passes `showSearch={isMobile}`. Desktop renders exactly as before. **To test:** - Open the editor at a phone-width viewport (under 1024px). - A "Search tools..." field should sit above Favourites / Recommended in the Tools pane. - Typing filters into grouped results. Clearing goes back to the compact list. - It hides once a tool is open, and comes back on the way out. - On desktop the field should not appear at all. ## 2. The mobile overflow menu opened with nothing in it `WorkbenchBarMobileActions` rendered its kebab trigger unconditionally. But every item inside is gated on `currentView === "viewer"` or `!isCustomView`. In a `custom:*` workbench both are false, so the dropdown was empty. `WorkbenchBarDesktopActions` renders nothing in that case, so this only showed on phones. Now returns `null` when neither group applies, with the two conditions named so the trigger and the items can't drift apart again. **To test:** - Phone-width viewport, load a PDF. - Open a tool with its own workbench view: Compare, Get Info report, Show JS, Validate Signature, Edit Table of Contents, or PDF Text Editor. - The kebab at the right of the workbench bar should be gone entirely, rather than opening an empty menu. - Back in the viewer or page editor it should still be there, with Print / Download / Save As / Close. |
||
|
|
d34b9f0256 |
build(deps): bump license-report from 6.8.2 to 6.8.5 in /frontend (#7645)
Bumps [license-report](https://github.com/bepo65/license-report) from 6.8.2 to 6.8.5. <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/BePo65/license-report/blob/main/CHANGELOG.md">license-report's changelog</a>.</em></p> <blockquote> <h2><a href="https://github.com/bepo65/license-report/compare/v6.8.4...v6.8.5">6.8.5</a> (2026-05-28)</h2> <h2><a href="https://github.com/kessler/license-report/compare/v6.8.3...v6.8.4">6.8.4</a> (2026-04-02)</h2> <h2><a href="https://github.com/kessler/license-report/compare/v6.8.1...v6.8.3">6.8.3</a> (2026-04-01)</h2> <h3>Bug Fixes</h3> <ul> <li>update packages to fix dependabot security warnings (<a href="https://github.com/kessler/license-report/commit/90d39679d5c458c7d59545bb06b61c32a16bf746">90d3967</a>)</li> <li>update packages to fix github dependabot security warnings (<a href="https://github.com/kessler/license-report/commit/02ab3bcd7baaa7985ed3a3164aef5b66a3c58ad0">02ab3bc</a>)</li> </ul> <h2><a href="https://github.com/kessler/license-report/compare/v6.8.0...v6.8.1">6.8.1</a> (2025-10-22)</h2> <h3>Bug Fixes</h3> <ul> <li>update packages to fix security warnings (<a href="https://github.com/kessler/license-report/commit/de28bd74a0c98b24b09470fd48ce4324fa3e193b">de28bd7</a>)</li> </ul> <h2><a href="https://github.com/kessler/license-report/compare/v6.7.2...v6.8.0">6.8.0</a> (2025-05-23)</h2> <h3>Features</h3> <ul> <li>enable usage of .npmrc file (solves issue <a href="https://redirect.github.com/kessler/license-report/issues/205">#205</a>) (<a href="https://redirect.github.com/kessler/license-report/issues/208">#208</a>) (<a href="https://github.com/kessler/license-report/commit/935608872386d531226e41bb90a4f1b71148aac5">9356088</a>)</li> </ul> <h2><a href="https://github.com/kessler/license-report/compare/v6.7.1...v6.7.2">6.7.2</a> (2025-03-01)</h2> <h3>Bug Fixes</h3> <ul> <li>replace path.join for uri with own function (<a href="https://github.com/kessler/license-report/commit/2f30a8bae9050fc2a75c228f5eea10cf91a88ab5">2f30a8b</a>)</li> </ul> <h2><a href="https://github.com/kessler/license-report/compare/v6.7.0...v6.7.1">6.7.1</a> (2024-12-10)</h2> <h2><a href="https://github.com/kessler/license-report/compare/v6.6.1...v6.7.0">6.7.0</a> (2024-09-13)</h2> <h3>Features</h3> <ul> <li>use properties of object entries as custom field (<a href="https://redirect.github.com/kessler/license-report/issues/194">#194</a>) (<a href="https://github.com/kessler/license-report/commit/d9519b23d4cdf11c8659c28644eb4fe206df1b5a">d9519b2</a>)</li> </ul> <h2><a href="https://github.com/kessler/license-report/compare/v6.6.0...v6.6.1">6.6.1</a> (2024-08-02)</h2> <h2><a href="https://github.com/kessler/license-report/compare/v6.5.1...v6.6.0">6.6.0</a> (2024-06-23)</h2> <h3>Features</h3> <ul> <li>change the project from 'default' to named exports (<a href="https://github.com/kessler/license-report/commit/1e526eb2ebd4395a45473e588a6d3e774fc9dd0d">1e526eb</a>)</li> </ul> <h2><a href="https://github.com/ironSource/license-report/compare/v6.5.0...v6.5.1">6.5.1</a> (2024-04-26)</h2> <h2><a href="https://github.com/ironSource/license-report/compare/v6.4.0...v6.5.0">6.5.0</a> (2023-10-09)</h2> <h3>Features</h3> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/BePo65/license-report/commit/a3bc6e5487f833629372a7ee9971dee94de765bd"><code>a3bc6e5</code></a> Merge pull request <a href="https://redirect.github.com/bepo65/license-report/issues/262">#262</a> from BePo65/pu/create-release</li> <li><a href="https://github.com/BePo65/license-report/commit/f68eb4be91802b095b033407b1147cf5af1bc9f1"><code>f68eb4b</code></a> chore(release): 6.8.5</li> <li><a href="https://github.com/BePo65/license-report/commit/97767e43ee578aa97696a58d78432f6cf20d1649"><code>97767e4</code></a> Merge pull request <a href="https://redirect.github.com/bepo65/license-report/issues/250">#250</a> from BePo65/dependabot/github_actions/actions/stale-1...</li> <li><a href="https://github.com/BePo65/license-report/commit/5aa73e676f80f3bb5d9db535433fc8a6cb8ee6ef"><code>5aa73e6</code></a> build(deps): bump actions/stale from 10.2.0 to 10.3.0</li> <li><a href="https://github.com/BePo65/license-report/commit/b14534d2f366c69b9617dda21e8a0ab16301c255"><code>b14534d</code></a> Merge pull request <a href="https://redirect.github.com/bepo65/license-report/issues/261">#261</a> from BePo65/pu/update-gh-workflow-stale</li> <li><a href="https://github.com/BePo65/license-report/commit/1acbb74b7ca80c12a9308da78f309e749d926bcf"><code>1acbb74</code></a> build: update actions/stale to v 10.2.0 and switch to double quotes</li> <li><a href="https://github.com/BePo65/license-report/commit/97d384bcecda69631e824b921bcc81d993e91934"><code>97d384b</code></a> Revert "chore(release): 6.8.5"</li> <li><a href="https://github.com/BePo65/license-report/commit/3ed874213f6920c69c6d3566dfe86d05142264c5"><code>3ed8742</code></a> Merge pull request <a href="https://redirect.github.com/bepo65/license-report/issues/260">#260</a> from BePo65/pu/change-ownership</li> <li><a href="https://github.com/BePo65/license-report/commit/5dd5be3b9f7eaa47869bf1ae675a5635647e0ecd"><code>5dd5be3</code></a> chore(release): 6.8.5</li> <li><a href="https://github.com/BePo65/license-report/commit/b0851a8c7df87a956e1b1cf32790ed9486976e57"><code>b0851a8</code></a> docs: changes caused by transfer of ownership</li> <li>Additional commits viewable in <a href="https://github.com/bepo65/license-report/compare/v6.8.2...v6.8.5">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
e9a9dbf644 |
fix(desktop): inset macOS and Linux app icons to platform icon grids (#7646)
# Description of Changes
The macOS Dock icon renders noticeably larger than every other app. The
cause is that
`icon.icns` was **100% full-bleed** - the red rounded square filled all
1024x1024 with zero
margin. macOS does not mask or inset legacy `.icns` icons, so the
artwork has to carry Apple's
grid itself: an **824x824 body centred on a 1024x1024 canvas**.
Full-bleed therefore rendered
**24% wider and 54% larger in area** than its neighbours.
Linux had the same defect for the same reason - the hicolor PNGs were
94.9-100% full-bleed,
and GNOME's HIG says an app icon is drawn within the canvas but must not
fill it (~10% margin,
so a body around 80%). Those small existing margins were resampling
artifacts, not padding.
Windows is deliberately **left full-bleed**. Microsoft imposes no inset:
target-size assets are
drawn without tile padding and the taskbar simply scales the bitmap into
the slot. `app.ico` is
a pure rename here, byte-identical to before.
## What changed
Icons are now split per platform, since the three platforms disagree
about how much of the
canvas the artwork may fill:
| Path | Owner | Treatment |
| --- | --- | --- |
| `icons/macos/app.icns` | `dmg`, `app` | 824/1024 Apple grid |
| `icons/macos/app-512.png` | build-time only | see note below |
| `icons/linux/app-{16..512}.png` | `deb`, `rpm`, `appimage` | ~10%
margin, KDE's small-size exception at 16/32 |
| `icons/windows/app.ico` | `msi`, NSIS | unchanged, full-bleed |
Linux is selected by a new `tauri.linux.conf.json`. Tauri merges
platform configs with
JSON Merge Patch (RFC 7396), so `bundle.icon` is **replaced wholesale**
rather than appended.
## Notes for reviewers
- **`icons/macos/app-512.png` is build ballast, not a real asset.**
`tauri-codegen` requires a
PNG in the icon list for every non-Windows target, with a hardcoded
fallback to
`icons/icon.png` - a file this PR deletes. Without it the build fails.
It is embedded as
`default_window_icon`, which tao's macOS backend discards
(`set_window_icon` there is a no-op:
"macOS doesn't have window icons"). Nothing renders it.
- **Linux icon order matters.** The bundler derives the hicolor
directory from each PNG's real
pixel dimensions, so `app-128.png` lands in `128x128/`. `app-512.png` is
listed first because
the first PNG in the list also becomes the window icon, which GTK does
honour.
- **`.imgbotconfig` had to be repointed.** Its previous entry named
`icons/icon.png`, a path this
PR deletes. That exclusion is load-bearing: ImgBot once optimised the
icon to an indexed
palette and `tauri::generate_context!()` rejects non-RGBA icons,
breaking the desktop build
(#6990). All 15 generated PNGs, including the eight inside the `.icns`,
are verified colour
type 6.
- **Not fixed here:** our corner radius is 14.3% of the body where macOS
and GNOME neighbours sit
near 22%, so the icon still reads squarer than its neighbours. That is a
brand-silhouette
decision rather than the sizing bug, so it was left alone.
- The 15 pre-existing unused assets (`Square*Logo.png`, `mstile-*`,
`android-chrome-*`,
`android/`, `ios/`) are untouched. No configured bundle target consumes
them.
## Verification
`task check` was **not** run - this PR touches no Java, TypeScript or
engine Python, so it
cannot exercise the change. What was verified directly instead:
- Simulated the RFC 7396 merge and Tauri's `find_icon` resolution per
platform: Windows resolves
to `app.ico`, macOS to `app.icns` plus the stub PNG, Linux to its own
six PNGs. Every path exists.
- Both configs validate against the bundled
`@tauri-apps/cli/config.schema.json`, base and merged.
- Every PNG's real dimensions match its filename, and every body
measures exactly its nominal
inset (410/512, 154/192, 102/128, 52/64, 28/32, 14/16).
- An overlay diff of the new macOS body against the old artwork shows
only 1px antialiasing
hairlines - the mark itself is unchanged, only inset.
- Pre-commit hooks pass.
---
## Checklist
### General
- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings
### Documentation
- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)
### Translations (if applicable)
- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)
### UI Changes (if applicable)
- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)
### Testing (if applicable)
- [ ] I have run `task check` to verify linters, typechecks, and tests
pass
- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing)
for more details.
|
||
|
|
4457260c60 |
Make the editor and settings menu mobile friendly-er (#7518)
Co-authored-by: EthanHealy01 <80844253+EthanHealy01@users.noreply.github.com> |
||
|
|
f5cf5f1077 |
Update Frontend 3rd Party Licenses (#7616)
Auto-generated by stirlingbot[bot] This PR updates the frontend license report based on changes to package.json dependencies. Signed-off-by: stirlingbot[bot] <stirlingbot[bot]@users.noreply.github.com> Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com> |
||
|
|
7fb29d002d |
deps(frontend): upgrade i18next ecosystem to v26 (#7356)
# Description of Changes - Upgraded `i18next` to 26.3.6. - Upgraded `react-i18next` to 17.0.11. - Upgraded `i18next-browser-languagedetector` to 8.2.1. - Replaced the removed `initImmediate` option with `initAsync`. - Verified compatibility with the updated `<Trans>` behavior and language detector APIs. - No translation changes were required because all `<Trans>` usages use explicit `i18nKey` values. --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
4e5b1102b6 |
refactor(search): improve TextInput clear button styling and add component unit tests (#7578)
# Description of Changes FIxes minor stylistic problem about the search bar. Mainly the X and the spacing on the result's icons vs text. ### New <img width="1594" height="600" alt="image" src="https://github.com/user-attachments/assets/f3710cad-f1a6-4aa1-9c2a-f3f474fd2dd6" /> ### Old <img width="1584" height="618" alt="image" src="https://github.com/user-attachments/assets/2d9c26a8-2d68-4d86-9d51-b12f90b17fe0" /> <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [X] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [X] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [X] I have performed a self-review of my own code - [X] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [X] I have run `task check` to verify linters, typechecks, and tests pass - [X] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
90d39aec0b |
🌐 Sync Translations + Update README Progress Table (#7583)
### Description of Changes This Pull Request was automatically generated to synchronize updates to translation files and documentation. Below are the details of the changes made: #### **1. Synchronization of Translation Files** - Updated translation files (`frontend/editor/public/locales/*/translation.toml`) to reflect changes in the reference file `en-US/translation.toml`. - Ensured consistency and synchronization across all supported language files. - Highlighted any missing or incomplete translations. - **Format**: TOML #### **2. Update README.md** - Generated the translation progress table in `README.md` using `counter_translation_v3.py`. - Added a summary of the current translation status for all supported languages. - Included up-to-date statistics on translation coverage. #### **Why these changes are necessary** - Keeps translation files aligned with the latest reference updates. - Ensures the documentation reflects the current translation progress. --- Auto-generated by [create-pull-request][1]. [1]: https://github.com/peter-evans/create-pull-request Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com> |
||
|
|
fb0bffaa31 |
fix(billing): enforce en-US locale for billing money formatting (#7487) (#7498)
# Description of Changes Fixes #7487 - **What was changed:** Explicitly specified the `'en-US'` locale in `Intl.NumberFormat` and `toLocaleString` within `formatMinor` and `formatMoneyMajor` in `frontend/editor/src/proprietary/billing/format.ts`. Also added test coverage for `formatMoneyMajor` in `frontend/editor/src/portal/billing/sharedBillingFormat.test.ts`. - **Why the change was made:** Previously, `formatMinor` used `new Intl.NumberFormat(undefined, ...)`, which inherited the host/browser environment locale. On non-US locales (e.g. German `de-DE`), this produced comma-separated decimals (`$2,24`) instead of dot-separated decimals (`$2.24`), breaking unit tests and leading to inconsistent money formatting. - **Any challenges encountered:** None. Closes #7487 --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [x] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [x] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have run `task check` to verify linters, typechecks, and tests pass - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
6d549c39dc |
Update Frontend 3rd Party Licenses (#7594)
Auto-generated by stirlingbot[bot] This PR updates the frontend license report based on changes to package.json dependencies. Signed-off-by: stirlingbot[bot] <stirlingbot[bot]@users.noreply.github.com> Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com> |
||
|
|
d9a5c9ac7e |
build(deps): bump the embedpdf group across 1 directory with 23 updates (#7469)
Bumps the embedpdf group with 21 updates in the /frontend directory: | Package | From | To | | --- | --- | --- | | [@embedpdf/core](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/core/main) | `2.14.4` | `2.15.0` | | [@embedpdf/models](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/models) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-annotation](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-annotation) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-attachment](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-attachment) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-bookmark](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-bookmark) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-document-manager](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-document-manager) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-export](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-download) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-history](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-history) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-interaction-manager](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-interaction-manager) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-pan](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-pan) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-print](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-print) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-redaction](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-redaction) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-render](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-render) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-rotate](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-rotate) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-scroll](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-scroll) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-search](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-search) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-spread](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-spread) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-thumbnail](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-thumbnail) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-tiling](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-tiling) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-viewport](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-viewport) | `2.14.4` | `2.15.0` | | [@embedpdf/plugin-zoom](https://github.com/embedpdf/embed-pdf-viewer/tree/HEAD/packages/plugin-zoom) | `2.14.4` | `2.15.0` | Updates `@embedpdf/core` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/core's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/core/main">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/engines` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/engines's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/engines/CHANGELOG.md">@embedpdf/engines's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/engines">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/models` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/models's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/models/CHANGELOG.md">@embedpdf/models's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/models">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-annotation` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-annotation's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-annotation/CHANGELOG.md">@embedpdf/plugin-annotation's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-annotation">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-attachment` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-attachment's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-attachment/CHANGELOG.md">@embedpdf/plugin-attachment's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-attachment">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-bookmark` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-bookmark's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-bookmark/CHANGELOG.md">@embedpdf/plugin-bookmark's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-bookmark">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-document-manager` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-document-manager's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-document-manager/CHANGELOG.md">@embedpdf/plugin-document-manager's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-document-manager">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-export` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-export's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-download">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-history` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-history's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-history/CHANGELOG.md">@embedpdf/plugin-history's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-history">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-interaction-manager` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-interaction-manager's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-interaction-manager/CHANGELOG.md">@embedpdf/plugin-interaction-manager's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-interaction-manager">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-pan` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-pan's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-pan/CHANGELOG.md">@embedpdf/plugin-pan's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-pan">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-print` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-print's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-print/CHANGELOG.md">@embedpdf/plugin-print's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-print">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-redaction` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-redaction's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-redaction/CHANGELOG.md">@embedpdf/plugin-redaction's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-redaction">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-render` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-render's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ start, end }</code> glyph pointers) shape emitted by <code>onSelectionChange</code>, so a saved selection can be passed straight back in to restore it; passing <code>null</code> clears the selection. Page geometry is loaded on demand, so the returned task resolves only once the highlight rects are computed. The range is normalized (start/end may be given in any order), invalid input (malformed range, non-integer/negative indices, out-of-bounds pages) is rejected, glyph indices are clamped to the available page geometry, and previously highlighted pages are repainted so switching to a disjoint selection no longer leaves stale highlights behind.</p> </li> </ul> <h2><code>@embedpdf/core</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/703">#703</a> by <a href="https://github.com/berkayozdin"><code>@berkayozdin</code></a> – Make the precompiled Svelte output work on every Svelte 5 runtime</p> <p>Svelte 5.56 changed the <code>exclude</code> argument of the private <code>rest_props</code> runtime helper from an array (<code>exclude.includes(key)</code>) to a <code>Set</code> (<code>exclude.has(key)</code>). The <code>*/svelte</code> entry points ship precompiled component code, so output built against one side of that change throws on the other: the currently published packages fail with <code>TypeError: exclude.has is not a function</code> on Svelte >= 5.56, which aborts the render of every EmbedPDF Svelte component.</p> <p>The Svelte build now routes those calls through a wrapper that hands the runtime an <code>exclude</code> value satisfying both contracts, so one published build stays valid across the whole <code>svelte: ">=5 <6"</code> peer range.</p> </li> </ul> <h2><code>@embedpdf/engines</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/models</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/pdfium</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-annotation</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-attachment</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-bookmark</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-capture</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-commands</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-document-manager</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-export</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h2><code>@embedpdf/plugin-form</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/blob/v2.15.0/packages/plugin-render/CHANGELOG.md">@embedpdf/plugin-render's changelog</a>.</em></p> <blockquote> <h2>2.15.0</h2> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/embedpdf/embed-pdf-viewer/commit/a8faf04b15291c895118b819d7395c3bca8a4959"><code>a8faf04</code></a> chore: version packages</li> <li>See full diff in <a href="https://github.com/embedpdf/embed-pdf-viewer/commits/v2.15.0/packages/plugin-render">compare view</a></li> </ul> </details> <br /> Updates `@embedpdf/plugin-rotate` from 2.14.4 to 2.15.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/embedpdf/embed-pdf-viewer/releases">@embedpdf/plugin-rotate's releases</a>.</em></p> <blockquote> <h2>Release v2.15.0</h2> <h2><code>@embedpdf/plugin-selection</code><a href="https://github.com/2"><code>@2</code></a>.15.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/embedpdf/embed-pdf-viewer/pull/685">#685</a> by <a href="https://github.com/simonmysun"><code>@simonmysun</code></a> – Add <code>setSelection(range, documentId?)</code> to the selection capability and document scope for programmatically applying or restoring a text selection.</p> <p>It accepts the same <code>SelectionRangeX</code> (<code>{ star... _Description has been truncated_ Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
383710c1d2 |
style(navigation): improve unsaved changes modal (#7508)
# Description of Changes Improves the design of the unsaved changes modal. ### New <img width="802" height="466" alt="image" src="https://github.com/user-attachments/assets/f73bc51d-fb30-4075-a516-1aa0007d2735" /> ### Old <img width="908" height="450" alt="image" src="https://github.com/user-attachments/assets/ef004923-4121-4f28-81f8-e3ee3cd68f78" /> <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [X] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [X] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [X] I have performed a self-review of my own code - [X] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [X] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [X] I have run `task check` to verify linters, typechecks, and tests pass - [X] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
96a00cebd1 |
Pdf ua converter testing (#7301)
# Description of Changes Adds a PDF/UA converter, an accessibility report, and PDF/A conformance level A. **New: `POST /api/v1/convert/pdf/ua`** (Convert tool, "PDF/UA" target). Tags an untagged PDF, marks decorative content as artifacts, embeds missing fonts and applies the document-level PDF/UA requirements (title, language, tab order, form-field descriptions), then validates with veraPDF. The `pdfuaid` declaration is written only if validation passes, so a returned file never claims more than it delivers; response headers report whether it was declared, how many checks still fail and how many images still need a description. **New: `POST /api/v1/security/accessibility-report`.** Reports what fails, what the converter can fix on its own, what needs a person, and lists the figures needing a description with the keys the conversion accepts back. Read-only; does not modify the file. Capped at 100 MB / 2000 pages and weighted `LARGE_WEIGHT`, since it runs a full veraPDF pass plus the converter's layout analysis over every page. **PDF/A level A.** `pdfa-1a`, `pdfa-2a` and `pdfa-3a` output formats on the existing `/api/v1/convert/pdf/pdfa` endpoint. Level A is level B plus tagging, so the document is tagged after Ghostscript (which discards any structure tree it is given) and the level A claim is written only if veraPDF agrees. Optional `pdfUa=true` additionally declares PDF/UA alongside PDF/A, again only if it validates. Honesty rules the implementation holds to: - **Never claim a level that was not reached.** If tagging fails, the file is returned at level B and is named `_PDFA-2b.pdf`, not `_PDFA-2a.pdf`. With `strict=true` the request fails outright rather than returning a level B file against a level A request, and a level B pass no longer satisfies a strict level A request. - **Never relabel a document's language.** The requested language (default `en-GB`) is applied only when the document declares none; a French PDF stays French unless the caller sets `overrideLanguage`, and ignoring a requested language is reported as a warning. - **Never invent alternative text.** Descriptions come from the caller. The Convert panel can list the images needing one (via the report endpoint) and send them back per figure; any image left undescribed blocks the conformance claim rather than being papered over. - **Never certify hidden content.** Marking images decorative, or suppressing text that could not be tagged reliably, withdraws the claim instead of passing the checker by hiding content. PDF/UA-1 and PDF/UA-2 are both offered; UA-2 raises the file to PDF 2.0 and namespaces the structure tree, and its test asserts conformance rather than merely reporting it. Convert steps saved in Automations/Pipelines round-trip their PDF/UA settings (profile, language, override, title, font embedding, descriptions). --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
50d34fcca5 |
Add download, rename and duplicate to the file actions menu (#7536)
# Description of Changes Adds expanded dropdown menu for download, rename and duplicate <img width="560" height="380" alt="image" src="https://github.com/user-attachments/assets/84464f0a-46e1-42cf-8098-26f77888710f" /> <img width="560" height="480" alt="image" src="https://github.com/user-attachments/assets/ed871f98-7f89-4560-869e-9ff514000b6f" /> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
91fc26f10c |
chore: Bump version to 2.14.3 (#7554)
# Description of Changes This PR bumps the Stirling PDF application version from `2.14.2` to `2.14.3` across the project. Changes include: - Updated the Gradle project version in `build.gradle` to `2.14.3`. - Updated the Tauri desktop application version in `frontend/editor/src-tauri/tauri.conf.json`. - Updated the AUR package version for `stirling-pdf-desktop`. - Updated the AUR package version for `stirling-pdf-server-bin`. - Updated the mocked `appVersion` used by the core frontend server experience simulations. - Updated the mocked `appVersion` used by the proprietary frontend server experience simulations. - Kept all application, desktop, packaging, and test/simulation version references synchronized for the `2.14.3` release. The change prepares the project metadata and packaging configuration for the `2.14.3` release and prevents different components from reporting or packaging the previous `2.14.2` version. --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
f7ed1822c7 |
Float the editor search when no file is open (#7575)
## What The super-search work pinned the editor's `WorkbenchBar` visible on every view except My Files, even with no file open. That left an empty workbench showing a fully painted bar whose only live control was the search — download / close / print / save were all disabled, because those actions only make sense with a file open. This stops forcing the bar. When nothing is open, only the global search floats (unpainted, centered), mirroring how the Processor already works. When a file **is** open, the `WorkbenchBar` renders exactly as before. Also fixes a smaller Processor issue: its floating search strip was shorter than the sidebar logo row, so the search sat higher than the brand. Its height now matches the logo row (51px) so they line up. ## Changes - **`Workbench.tsx`** — render the `WorkbenchBar` only when a file is open (or a custom view supplies content); otherwise render the new floating search. My Files and `hideTopControls` custom views are unchanged. - **`WorkbenchFloatingSearch.tsx` / `.css`** (new) — the editor's `SuperSearch` floated in an unpainted strip, mirroring `PortalSearchBar`. Its vertical band matches the bar's so opening a file swaps in the bar without a shift. - **`PortalSearchBar.css`** — strip height matched to `.portal-sidebar__logo` (51px) so the Processor search aligns with the logo. The notification bell is intentionally out of scope — it ships in a separate PR. ## Before / after (ignore the bell icon in the after that’s not live yet) <img width="2056" height="1077" alt="Screenshot 2026-08-20 at 2 28 17 AM" src="https://github.com/user-attachments/assets/144f5216-4784-42b2-8c09-afda43577ad0" /> <img width="2056" height="1071" alt="Screenshot 2026-08-20 at 2 28 31 AM" src="https://github.com/user-attachments/assets/63af9303-af8a-48dd-b113-485169fb4924" /> - **Editor, no file:** painted bar with disabled buttons → just a floating search. - **Editor, file open:** unchanged. - **Processor:** search now vertically aligned with the logo. ## Testing - `task frontend:check` — lint (incl. colour linters) + typecheck + tests (247 files / 2137 tests) all pass. - Processor alignment verified in Storybook (`Portal/Shell/AppShell`): logo row, search strip, and search pill share the same vertical center. - Editor float not verified in-browser (local backend is behind a login gate); covered by types/tests and reuses the verified Processor pattern. |
||
|
|
a744102cb6 |
Support Supporting Files in Pipelines (#7547)
# Description of Changes Currently in the Processor's Pipelines page, none of the tools which require supporting files are usable because it's never been hooked up to the new API to upload supporting files. This PR hooks it up to that so all tools using supporting files work in the processor. I had to tweak the type generation a little for this so we have a static map of which params are for supporting files so we know to handle them differently. The `Test with a file` button has to work a little differently than the main run since it's running an ad-hoc pipeline so the files haven't necessarily been saved to the server yet. In this case, it'll use whatever local changes the user has made for those pipeline steps, and for all other steps, it'll just use what's saved in the server. |
||
|
|
1690cc25cc |
Update Frontend 3rd Party Licenses (#7573)
Auto-generated by stirlingbot[bot] This PR updates the frontend license report based on changes to package.json dependencies. Signed-off-by: stirlingbot[bot] <stirlingbot[bot]@users.noreply.github.com> Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com> |
||
|
|
088e0ef4e2 |
deps(frontend): upgrade Cantoo PDF library to 2.8.2 (#7493)
# Description of Changes This pull request upgrades the frontend PDF dependency from `@cantoo/pdf-lib` 2.6.5 to 2.8.2. - Updated `frontend/package.json` to require `@cantoo/pdf-lib` `^2.8.2`. - Regenerated `frontend/package-lock.json` with `@cantoo/pdf-lib@2.8.2`, `pako@2.2.0`, and `node-html-better-parser@1.5.9`. - Added the root npm `pako` override recommended by the upstream release. - The upgrade brings upstream parser, object-stream, encryption, form, PNG, and PDF serialization fixes into the frontend dependency. - No application API migration was required because the project does not use the newly added PDF/A, XFA, Factur-X, incremental-update, fontkit, or page-content-extraction APIs. The main challenge was validating the broad upstream change set against the project's actual usage. The frontend typecheck and a direct PDF create/save/load smoke test passed. The complete `frontend:check` and `frontend:test` tasks exceeded the available execution timeout without reporting a test failure. No related issue. --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. |
||
|
|
913601ff03 |
Consolidate the editor + processor sidebar footers into one component (#7539)
## What Both sidebars ended in a different bottom section. The editor showed an account row (avatar, name, settings); the processor showed a "Link Stirling account" CTA plus a `Settings` nav item and no identity at all. They are now **one shared `<NavFooter>`** rendering the same rows in both apps, in this order: 1. the link-account CTA (self-hosted, when unlinked) 2. free credits remaining 3. **Open \<the other app\>** 4. the account row — avatar, name, settings It's a **single surface** with hairline dividers between rows, not stacked cards. Rows are assembled as a list, so a row this build doesn't show (no wallet, no processor access, nothing to link) takes its divider with it rather than leaving a stray line. This also fixes the profile-picture/initials desync between the sidebar and the account settings page. ## Screenshots Captured with the stubbed Playwright harness at 1600x900, scoped to the sidebar and auto-cropped to the region that actually changed. Base is `origin/main`; every state is driven by dummy backend stubs so all the nav-bar permutations are covered. <img width="2104" height="3044" alt="montage_cloud-dark" src="https://github.com/user-attachments/assets/667c9a71-3ab7-4582-9259-08cda521e238" /> <img width="2104" height="3044" alt="montage_cloud-light" src="https://github.com/user-attachments/assets/126bc994-efa3-436b-bf50-31d76f574eaa" /> <img width="2104" height="1492" alt="montage_editor-dark" src="https://github.com/user-attachments/assets/c725726d-ea90-4fa9-bf0a-6014f3729869" /> <img width="2104" height="1490" alt="montage_editor-light" src="https://github.com/user-attachments/assets/d0d34e0a-ac60-4f8c-af15-afb66fbd679e" /> <img width="2104" height="1066" alt="montage_processor-dark" src="https://github.com/user-attachments/assets/51ccfe63-c327-41f4-ab91-74555b6f7148" /> <img width="2104" height="1068" alt="montage_processor-light" src="https://github.com/user-attachments/assets/c90d7aba-90fa-4998-ac8f-26ce666dde71" /> The free-credits meter is a cloud-build surface, so the self-hosted capture can't reach it. Those states come from the new Storybook stories with dummy wallet data (`Shared/NavFooter`), which is also where the credit tone bands and the collapsed rail are easiest to review. ## How it's wired `NavFooter` is purely presentational. Each app resolves its own data through three `@app/*` seams, so core carries no build-specific gating and any box whose data is absent is dropped rather than rendered empty. | Seam | core | cloud / proprietary / saas | |---|---|---| | `useFreeCreditsSummary` | `null` — self-hosted editor installs aren't metered | cloud reads `freeRemaining` / `freeAllowance` off the same `useWallet()` the Plan page's free meter uses, so the sidebar and Plan can't disagree | | `useOtherAppSwitch` | `null` — core ships no processor | gated on `portalAccess` (`/api/v1/auth/me` in SaaS, the Spring session flag self-hosted) | | Link-account CTA | n/a | unchanged conditions — passed in as `accountExtras`, still only when `linkState === "unlinked"`, still a no-op in SaaS | - The processor reads the meter through its own `@portal/hooks/useFreeCreditsSummary` rather than the editor's `@app` one. Self-hosted resolves `@app/*` as proprietary → core, where the cloud wallet hook isn't in the cascade, and the implementation can't live in `proprietary/` because core/desktop builds ship no portal and must never resolve `@portal`. Keeping it in `portal/` gets the figure to the linked self-hosted processor without weakening that rule; it reads the same `GET /api/v1/payg/wallet` the Usage page's trial meter already renders, gated on link state and behind the portal's query cache. `portal-saas/` just re-exports the cloud hook, so both footers share one fetch. The processor-access gate previously lived in two near-identical `AppSwitcher` copies. It moves into `useOtherAppSwitch`, `AppSwitcher` now reads it too, and the duplicate `saas/components/shared/AppSwitcher.tsx` is deleted — the logo switcher and the footer row can no longer disagree about access. ## Profile picture sync One `useAccountIdentity` hook now backs the editor footer, the processor footer and the account settings page. Previously settings derived its initial from `email[0]` while the sidebar used `displayName[0]`, and the two drew different blue discs. Alongside that, the shared `Avatar`: - falls back to initials when a picture URL fails to load, instead of leaving an empty disc - renders one letter for single-word names (`admin` → "A", not "AD") - gains an `xl` size so the settings hero disc is the same component ## Notes - Labelled **"Free credits"** rather than "free monthly credits": `freeAllowance` is documented as a one-time lifetime grant, not a monthly reset, so "monthly" would misdescribe the data. Happy to change if the backend semantics differ from the type comments. ## Testing - `task frontend:check` and `task frontend:typecheck:all` pass (all 9 build variants). - 9 new `Shared/NavFooter` stories pass the Chromium + axe story scan; `frontend:storybook:a11y:changed` reports no regressions. - Stubbed E2E suite passes, including the `config-button` tour/settings specs that target the account row. Two failures (`console-clean › landing`, `viewer-text-selection › Ctrl+C`) also fail on `origin/main` locally — they need a backend on :8080 and clipboard permissions. |
||
|
|
cf49742d97 |
Fix the top bar styling (#7544)
Every top bar styled itself, so none of them matched the new UI. Also, colors on the premium banner (and possibly others) clashed since the theme changes. ## Before Example Issue <img width="1934" height="348" alt="Screenshot 2026-08-17 at 11 47 20 PM" src="https://github.com/user-attachments/assets/b6f13207-2f47-4084-bd3b-2392f572c1a1" /> ## After (all) <img width="2880" height="800" alt="danger__dark" src="https://github.com/user-attachments/assets/6b311dec-23e7-4059-a6bb-75527cbd2e34" /> <img width="2880" height="800" alt="danger__light" src="https://github.com/user-attachments/assets/3b04b109-5146-4874-88b3-d99770ea51f8" /> <img width="2880" height="800" alt="default-app__dark" src="https://github.com/user-attachments/assets/b0101b98-fce8-467a-99a6-dd40b8864da1" /> <img width="2880" height="800" alt="default-app__light" src="https://github.com/user-attachments/assets/8aa21f01-6549-4d78-b217-c5547d60ab5b" /> <img width="2880" height="800" alt="free-tier-limit__dark" src="https://github.com/user-attachments/assets/07fd7498-f44f-408f-8c79-9b5ea55e13df" /> <img width="2880" height="800" alt="free-tier-limit__light" src="https://github.com/user-attachments/assets/f38c527b-9f64-4db0-b84c-56ca48e464cc" /> <img width="2880" height="800" alt="server-attention__dark" src="https://github.com/user-attachments/assets/447a36fa-056d-4ca9-8b30-04aa0ccd6ed1" /> <img width="2880" height="800" alt="server-attention__light" src="https://github.com/user-attachments/assets/f0311d45-a218-4846-b0ac-47996e2637c5" /> <img width="2880" height="800" alt="team-invitation__dark" src="https://github.com/user-attachments/assets/cc368473-3b9f-4ab0-878a-67da993874c2" /> <img width="2880" height="800" alt="team-invitation__light" src="https://github.com/user-attachments/assets/19d52a3e-4028-46f0-8562-9bd9cef9397a" /> <img width="2880" height="800" alt="upgrade-prompt__dark" src="https://github.com/user-attachments/assets/e9d20daa-f41f-46d9-b827-a84f276e8af1" /> <img width="2880" height="800" alt="upgrade-prompt__light" src="https://github.com/user-attachments/assets/9b6250c1-6a61-40d6-a7ab-e37398d67322" /> ## What changed - `InfoBanner` exposed 8 colour-override props (`background`, `borderColor`, `textColor`, `iconColor`, `buttonColor`, `buttonTextColor`, `closeIconColor`, `buttonVariant`), so every caller invented its own look. Replaced with a closed tone set: `info` · `promo` · `warning` · `danger`. - Tone drives the whole bar — fill, border, icon and the button — so a CTA can't drift from the bar it sits on. Text is neutral in every tone; only the icon carries the tone colour. - All colour comes from `--c-*` tokens mixed over `--c-surface`, so the bars follow light and dark instead of ignoring them. The old bars were hardcoded: in dark mode the two licence warnings stayed cream-on-white. - `promo` keeps the gradient it was always meant to have, built from the existing `--c-hue-indigo`/`--c-hue-purple` stops (documented in `colors.css` as gradient hues, deliberately not accent-following), with the existing `premium` button accent on it. - Deleted the hardcoded colours from all four callers: the purple gradient (`#667eea`→`#764ba2`), the orange soup (`#FFF4E6` / `#9A3412` / `#EA580C`) duplicated across the urgent banner and the admin plan section, and the fixed dark bar (`--mantine-color-dark-7`) on the team invitation. - `UpgradeBanner|AdminPlanSection` sat on the theme linter's exemption list, which is how those colours survived the theme migration. Exemption removed, so `code-colors` now guards them. - The banner's class was colliding with `core/ui/Banner.css`'s `.sui-banner` (16 live rules), which restyled it in the app but not in Storybook — that's why the two disagreed on radius, border and tone. Renamed to `.app-banner`; the two surfaces now render identically. - Bar is square and full-bleed with a single hairline rule underneath; button labels are optically centred. - Added `--c-warning-subtle`, matching the existing `--c-danger-subtle` / `--c-success-subtle`. - New `Shared → Top bars` story renders all six bars at once, so a change to the shared component is visible against the whole set. - Unrelated one-liner: `frontend/.prettierignore` now ignores the gitignored `editor/screenshots/` capture artifacts, which were failing `format:check` locally. Happy to drop it if you'd rather keep this PR to the bars. ## Testing - `task frontend:check` — typecheck, lint (oxlint + 4 theme-lint passes + stylelint), format, 244 files / 2119 tests. - `frontend:storybook:a11y:changed` — clean in light and dark. - The a11y gate caught a real defect mid-change: giving each banner `role="region"` with the same label produced duplicate landmarks, which the app hits for real whenever two banners show at once. Landmark removed. - All six bars captured in the running editor, light and dark, and diffed against `origin/main`'s component rendered with each caller's original props. |
||
|
|
fb70fc13da |
Fix tools which crash in the Pipelines page (#7538)
# Description of Changes Overlay PDFs and Change Metadata both crashed in the Processor because they required `FilesModalContext` and `ViewerContext` respectively. Neither of those contexts make sense to provide in the Processor because there are no files in context and there is no Viewer, so redesign both tool settings to only optionally require these contexts. Their behaviour is unchanged in the Editor but they now work in the Processor (just without the extra info about the active files, since there are none). Also hooks up the Reorganise Pages settings so that it can be used from Automate. The component already existed but just wasn't being used, which just looks like an oversight. |
||
|
|
a14eec94ec |
Fix corner radius on Mantine checkboxes in Processor (#7537)
# Description of Changes ## Before <img width="314" height="311" alt="image" src="https://github.com/user-attachments/assets/93239e47-8dd4-4174-8a51-7a4a2494048f" /> ## After <img width="399" height="324" alt="image" src="https://github.com/user-attachments/assets/e2318559-006f-41e4-ac69-8c577ae60b89" /> |
||
|
|
89d8ffec5d |
Ci/environments cleanups, new envs and master to release naming (#7511)
# Description of Changes Ci/environments cleanups, new envs and master to release naming --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
526bb85e17 |
Translate the failures debug panel strings (#7500)
Follow-up to #7296, addressing a missing translation. |
||
|
|
99dda7a9f6 |
Convert Processor tables to be consistent with each other (#7312)
# Description of Changes Currently, the tables in the processor are inconsistently styled and function differently to each other because they don't all share the same table component. This is a big problem since the Processor is a largely table-based UI and most of most pages in it are tables. This PR creates a new common `DataTable` component and converts all existing tables in the Processor (other than those in the docs pages) to use it, tweaking the individual tables content as necessary so they all work the same. The `DataTable` component is designed to take minimal JSX as arguments. Instead, it takes typed data from the caller and it converts that into JSX, which makes it a lot easier to guarantee that the tables will all render the same and if we want to update the table styling in the future, we just need to do it in one place. I also added sorting capabilities to many of the tables since it was easy with the library and it was appropriate for many of the existing table columns. ## Table Comparisons <img width="2296" height="861" alt="AuditTab-comparison" src="https://github.com/user-attachments/assets/92473508-3f93-45c4-9485-ee21d0d1ecd0" /> <img width="2296" height="617" alt="DocumentExtractions-comparison" src="https://github.com/user-attachments/assets/cf194210-f4c9-42d1-bce1-5a70c0f8fdc5" /> <img width="2296" height="369" alt="InstanceHealthTable-comparison" src="https://github.com/user-attachments/assets/4b048c62-4237-4043-ae89-047df0cd7e60" /> <img width="2296" height="2304" alt="Integrations-comparison" src="https://github.com/user-attachments/assets/9d9edead-8da0-4d49-a249-253579cfdb0e" /> <img width="2296" height="521" alt="InvoicesList-comparison" src="https://github.com/user-attachments/assets/2b6d40b7-d63d-404b-a834-79d5fc62ba9d" /> <img width="2296" height="348" alt="LinkedInstancesTable-comparison" src="https://github.com/user-attachments/assets/fb65c660-baa4-4fcd-8893-05be1962fdab" /> <img width="2296" height="356" alt="PendingInvitations-comparison" src="https://github.com/user-attachments/assets/1f0105ed-b1f8-4e4e-998f-57024875a28f" /> <img width="2296" height="306" alt="PipelinesTable-comparison" src="https://github.com/user-attachments/assets/d84255bd-37f3-4245-b2b5-ef8c06c000ae" /> <img width="2296" height="652" alt="PolicyCatalogue-comparison" src="https://github.com/user-attachments/assets/55f25f20-46f0-47c0-aa40-516cbd5acab5" /> <img width="2296" height="1221" alt="ReviewQueueTable-comparison" src="https://github.com/user-attachments/assets/2206a50c-731f-48cc-958f-aad5e30e47b0" /> <img width="2296" height="344" alt="SourcesTable-comparison" src="https://github.com/user-attachments/assets/76b54efb-54ea-42a8-a0b1-b0cb21f73296" /> <img width="2296" height="659" alt="UsersDirectory-comparison" src="https://github.com/user-attachments/assets/524d1b63-efcf-4ac8-a07d-11aa1849c189" /> |
||
|
|
2c89f5fe52 |
fix(ci): locale path and docs (#7525)
# Description of Changes Update the TOML locale check to match the current frontend path and align comments/docs with the relocated editor and proprietary source layout. This keeps CI filtering and internal references accurate after the frontend reorganization. --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
42c84a1693 |
build(deps): bump base64 from 0.23.0 to 0.23.1 in /frontend/editor/src-tauri (#7460)
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.23.0 to 0.23.1. <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md">base64's changelog</a>.</em></p> <blockquote> <h1>0.23.1</h1> <ul> <li>Make the tests build again on non-SIMD architectures</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/marshallpierce/rust-base64/commit/069bf7067b949f5c0a92b6ceb82492920502f2c2"><code>069bf70</code></a> v0.23.1</li> <li><a href="https://github.com/marshallpierce/rust-base64/commit/6ab1fb0a5843013557a52c45c84b91f5d1bb87af"><code>6ab1fb0</code></a> Merge pull request <a href="https://redirect.github.com/marshallpierce/rust-base64/issues/310">#310</a> from musicinmybrain/test-on-non-simd-arches</li> <li><a href="https://github.com/marshallpierce/rust-base64/commit/7cffce6f971acdf48f83112cbdd63bd61125ba06"><code>7cffce6</code></a> Fix testing on architectures without unsafe SIMD support</li> <li><a href="https://github.com/marshallpierce/rust-base64/commit/e34f9a08c5c89a4641350ac22033f3fa4f5d4d97"><code>e34f9a0</code></a> Merge pull request <a href="https://redirect.github.com/marshallpierce/rust-base64/issues/308">#308</a> from atouchet/com</li> <li><a href="https://github.com/marshallpierce/rust-base64/commit/e9240c9a01e0a4934c5619e74740caa6d1f67ce9"><code>e9240c9</code></a> Remove outdated comment</li> <li>See full diff in <a href="https://github.com/marshallpierce/rust-base64/compare/v0.23.0...v0.23.1">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
6143610608 |
fix(frontend): preserve pdf link targets in desktop viewer (#7235)
Same PR as #6396, just with the conflicts resolved and some fixes on top. Original commit by @saul1310 is preserved as-is; everything else is a follow-up commit. Refs #6272 ## Conflicts #6396 was written before the frontend was restructured, so all four files it touched moved (`frontend/src/**` -> `frontend/editor/src/**`) and `LinkLayer.tsx` had drifted. Cherry-picked with rename detection and re-resolved against current `main`. ## Fixes on top - **Reuse the existing platform seam instead of adding a second one.** `main` already has `@app/platform/*` seams with per-flavour implementations; #6396 added a parallel `@app/utils/openExternalUrl` core+desktop pair that re-implemented the Tauri shell call already in `desktop/platform/openExternal.ts`. Split into a pure sanitiser (`@app/utils/externalUrl`) and a platform seam (`@app/platform/openExternalTab`), with the desktop impl delegating to the existing `openExternal`. - **Kept PDF links off the `openExternal` seam.** That seam is for leave-and-return redirects (Stripe) and its saas impl is `window.location.assign` - routing PDF links through it would navigate the whole app away from the user's document. `openExternalTab` always opens alongside the app; desktop shadows it to escape the webview. - **Fixed the same defect in two sibling call sites** that #6396 didn't cover: `BookmarkSidebar` (bookmark URI / LaunchAppOrOpenFile actions) and `useAnnotationMenuHandlers` (annotation menu "go to link"). Both called `window.open` on an unsanitised PDF-supplied URI, so on desktop they trapped the link in the webview exactly like the viewer did. - **Dropped the unguarded fallback.** The old code fell back to `window.open(uri)` when `new URL()` threw, so an unparseable URI bypassed the allowlist entirely. It is now blocked. - Empty/whitespace URIs are blocked rather than silently resolving to the app's own page via the base URL. - Tests: sanitiser cases (casing, leading whitespace, `data:`, `vbscript:`, unparseable), a core seam test asserting new-tab-not-navigate, and a desktop seam regression test asserting the URL goes to the OS rather than `window.open`. - **`openExternalTab` now re-validates its own input.** Every caller sanitises first, so nothing reached it unvalidated - but it is the sink that hands a URL to `window.open` (executes `javascript:` in our origin) or to an OS handler on desktop, and its safety shouldn't depend on callers remembering. Both impls fail closed, with tests that call them directly with `javascript:`/`data:`/`file:`/`ftp:`. ## Unrelated fix included (flagged deliberately) The last commit fixes `frontend/editor/vitest.config.ts`: `testTimeout: 10000` was set on the root `test` block, but tests all run under `projects`, which do not inherit it - so the whole suite has silently been running at vitest's 5s default. This is not cosmetic. It made `task check` fail intermittently on unrelated portal specs (`demoData`, `ConnectionModal`); the ConsignO test takes 2966ms with only the portal project running, i.e. 59% of a budget it was never meant to have, so any CPU contention tips it over. Proven with an identical 6.5s probe test: times out at 5000ms on the old config, passes at 6512ms on the fixed one. Happy to split this into its own PR if preferred - it is here because the gate could not be trusted without it. ## Validation Typecheck passes for all 7 build flavours (core, proprietary, saas, desktop, cloud, prototypes, portal); ESLint, Prettier, dpdm and the full 1662-test vitest suite pass. Driven live against the dev server + backend with a PDF carrying five URI annotations (https, `javascript:`, mailto, `file:`, relative). 14/14 behavioural checks pass on this branch; 5 of them fail on `main`: | check | main | this PR | | --- | --- | --- | | safe https link exposes real href (copy-link) | `href="#"` | `https://example.com/safe-link?a=1` | | link opens in new tab / tabnabbing-proof | no `target`/`rel` | `_blank` + `noopener noreferrer` | | mailto link exposes real href | `href="#"` | `mailto:test@example.com` | | relative URI resolved against app origin | `href="#"` | resolved | | `javascript:` / `file:` never reach href | blocked | blocked | | clicking blocked link doesn't execute or navigate | ok | ok | | clicking safe link opens new tab at source URL | - | ok, app not navigated away | --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Testing (if applicable) - [x] I have run `task check` to verify linters, typechecks, and tests pass - [x] I have tested my changes locally --------- Co-authored-by: Saul <saulifshin.cs@gmail.com> |
||
|
|
bff1ea916d |
Add n8n connection preset and workflow step operations (#7447)
# Description of Changes Add n8n, simple reuse of webhook call etc and custom API etc etc <img width="2104" height="1090" alt="image" src="https://github.com/user-attachments/assets/0d0df39a-31b3-4e1b-ac63-847aefa06ffe" /> <img width="2104" height="1592" alt="image" src="https://github.com/user-attachments/assets/4074cce2-0f4b-4300-813c-d49576b33940" /> <img width="1021" height="797" alt="image" src="https://github.com/user-attachments/assets/370f54f7-3066-4454-9618-9ddd2cb91b83" /> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
2483e9f37a |
Report editor-originated failures into the same queue (Review Flow PR 2) (#7296)
Review Flow PR 2 of 5. Editor tool failures now reach the same durable queue as failures from folders, buckets and webhooks. ## What's added **A report endpoint** — `POST /api/v1/file-run-events/reports`, open to any authenticated user. Takes four fields: `operation`, `errorCode`, `fileIds`, `detail`. No team, no actor, no filename: the first two come from the session, the third is never a field. Refused with 400 above 200 file ids, and nothing is written when refused. **Automatic reporting from every tool** — wired into `useToolOperation`, so no per-tool work is needed. Client-side refusals (an unsupported format that never reaches the server) are reported too. User cancellations are not. **Error codes parsed from Blob bodies as well as JSON** — a download-typed tool call fails with a Blob, so `errorCodeOf` handles both shapes. **Source attribution for unattended runs** — `sourceId` is threaded from `PolicyRunner` through `PolicyRun` to the recorded row and out to the wire, so a folder, bucket or webhook failure names what fed it. Previously it had none. **Deleting a file closes its failures** — `FileContext.removeFiles` notifies `POST /removed-files`, which transitions those incidents to `FILE_REMOVED`. Terminal, so they leave every reviewer's queue. The rows stay for audit. **The queue can be emptied** — reads now default to open statuses only; ask for a status explicitly to see closed rows. ## Behaviour changes - **Editor failures dedup per person.** `RecordFailure.scopeRef()` includes the actor for TOOL-origin rows, so two people hitting the same failure on the same file are two incidents rather than one. Processor rows are unaffected and their dedup key is byte-identical to before. - **`UNKNOWN` offers only Dismiss.** Acknowledge is no longer offered on it. - **Background reports no longer raise a toast.** Both calls pass `suppressErrorToast`, so a failed report is silent as intended; previously a core build showed the user a "Not Found" toast on every tool failure. ## What is stored File ids only, never names. The request type has no filename field, and a `fileNames` value handed to the client reporter is accepted and ignored. One caveat to review deliberately: the free-text `detail` is stored **verbatim**. `RecordFailure` truncates it at 2000 characters and nothing else; the redaction that used to strip name-shaped text was reverted in `024899f3f6` because it made an unclassified failure impossible to act on. A backend message that embeds a filename (LibreOffice conversion errors, IO errors) will therefore persist that text and show it to a team leader. ## How to test Needs a proprietary or SaaS build with login enabled. `task dev:all` gives you one. 1. **Report a failure from a tool.** Open a PDF, run **Remove Password** on it with a wrong password. Nothing visible changes for you: reporting is silent by design. 2. **See it recorded.** Go to `/processor/documents` and scroll to **Failures** (dev builds only). A row appears titled "Password-protected document", with `Hit by <your user>`. Press **Show raw JSON** to see exactly what was stored. 3. **Confirm no filename is stored as data.** In that JSON, `fileId` is an opaque uuid and there is no name field. Note the `detail` string may contain a filename if the backend put one in its message, per the caveat above. 4. **Confirm the request is capped.** In DevTools, POST to `/api/v1/file-run-events/reports` with 201 entries in `fileIds`. It returns 400 naming the limit, and no rows are added. 5. **Deleting a file clears its failure.** Back in the editor, delete the file you just failed on. Refresh the failures list: its row is gone from the default view. Filter by `FILE_REMOVED` to see it still exists. 6. **Two people, two incidents.** Have a colleague fail the same tool on their own copy of the same file. Two rows, not one occurrence count. ## Migration `source_id` is a new column and `FILE_REMOVED` a new status value. Both are already in the SaaS migration ([Stirling-PDF-SaaS #322](https://github.com/Stirling-Tools/Stirling-PDF-SaaS/pull/322)); self-hosted picks them up from `ddl-auto`. |
||
|
|
6f2b829f72 |
Filter Pipelines page to only show what the user thinks as pipelines (#7495)
# Description of Changes Currently, the Pipelines page shows all backend Policies, which was the desired behaviour when we first designed this, but as it's come along, it doesn't feel right anymore. This adds a filter so the Pipelines table only shows things that have been defined by the user as a New Pipeline, so not Policies etc. ## Before <img width="1510" height="789" alt="image" src="https://github.com/user-attachments/assets/5aebb065-3d42-4483-be3c-253fd8918d49" /> ## After <img width="1512" height="790" alt="image" src="https://github.com/user-attachments/assets/2966a0de-ec9f-43e6-bb1d-c4aeb30dfbf8" /> |
||
|
|
588afb6306 |
Fix a11y violations in nightlies and improve a11y config (#7499)
# Description of Changes This fixes the a11y violations that are currently failing in the nightlies in dark mode. Now that we're down to 0 baseline, we can require the a11y tests to pass in PRs before they merge, so I've changed that, and I've also made it so that the nightly will report failures in both light and dark mode instead of just light mode if that fails. <img width="2560" height="838" alt="image" src="https://github.com/user-attachments/assets/b0322182-f6ff-4dea-9a1c-5a6c9c9c5439" /> |
||
|
|
55087313b7 |
Processor UI snags: fat CTAs, real Infrastructure tabs, one surface style (#7497)
Five unrelated snags in the processor (portal) UI, plus fixes they turned up. No backend changes. `84 files changed, +892 / −3364` ## Fat CTA buttons - New `fat` prop on the SUI `Button`: 2.75rem tall, 1.25rem side padding, 0.75rem corners, semibold. Composes with all four variants/accents. - Applied to the page-header CTA on Sources, Documents, Pipelines, Users (both), Usage, Integrations, Infrastructure — 8 buttons, all in line with a page title. Nothing else. - `LandingActions` migrated onto the prop; `.landing-btn-primary` / `.landing-btn-secondary` and their four `!important`s deleted. The editor landing CTAs come down 4px with everything else. - Infrastructure's header CTA is now primary; its "Create key" dropped to secondary so they stop competing. <!--IMG:buttons--> ## Documents empty state - "Connect a source" opened the Sources *page*; it now opens the `SourceModal` connect flow in place, no route change. - No extra cache wiring: `SourceModal` already invalidates the sources query. <!--IMG:documents--> ## Infrastructure tabs - Only API Keys and Audit Logs hit real endpoints. Deployments, Security, Models and Storage read mock-only `/v1/infrastructure/*` that no backend serves. - Those four are now disabled: native `disabled`, out of the keyboard tab order, `aria-disabled`, with the view refusing non-enabled keys as a second guard. - Real tabs moved leftmost; API Keys is the default; `?tab=` deep links validated against the enabled set (the home flow's audit link still works). - Deleted: 4 tab components, their fetch fns and ~25 dead types, MSW handlers, fixtures (908 → 253 lines), dead CSS, unused formatters, 240 lines of `en-US` strings. Most of the −3364. - Page subtitle no longer advertises the disabled tabs. <!--IMG:infrastructure--> ## Surface consolidation - New `Surface` primitive (`sui-surface`): fill, hairline, radius, no shadow. Kept separate from `sui-nav-surface` so nav chrome can diverge later. - `Card` composes it and no longer draws its own shadow — this changes editor Card usages too, by design. - SUI primitives that are surfaces adopt it: `MetricCard`, `MetricStrip`, `NodeCard`, `Table`, `Collapsible`, `CodeBlock`. - The portal gets its own `.portal-surface` with the same three declarations, applied to 19 elements. A `sui-` class belongs to the component that emits it, so feature markup doesn't wear one. - `raised` variant = one subtle shadow for a surface in front of another surface (the flow diagram's tiles). Same fill as its parent, so nesting never shifts a region's colour. Dark has its own value. - Floating chrome (modals, drawers, dropdowns, assistant, sidebar) keeps its elevation; sunken wells stay sunken. <!--IMG:surfaces--> ## Sources list - Centred "No sources connected yet" empty state removed — it duplicated the header CTA and pushed the table down the page. The header's "Connect source" is the single way in. ## Drive-by fixes - The connect flow rendered unstyled outside the Sources view: `.portal-conn-picker__*` / `.portal-sources__connection-*` lived in `views/Sources.css`, which none of the five components rendering them imported. Moved to `components/sources/connections.css`. - Three inert custom properties (`--surface-input`, `--color-border-2`, `--text-default`) are defined nowhere in the codebase — `.portal-conn-picker__card` had no fill at all as a result. - Dead CSS removed from `Sources.css` (grep-verified unused): old expanded-row panel + its keyframes, type-card block. ## Testing - `task frontend:check` — typecheck, lint (oxlint + 4 theme-lint passes + stylelint), format, 238 files / 2063 tests. - `frontend:typecheck:all` across all 9 tsconfigs. - `frontend:storybook:a11y:changed` — 119 stories, light and dark, zero violations, no regressions vs baseline. - New tests: `Infrastructure.test.tsx` (tab order, default, disabled behaviour, deep-link filtering) and a Documents test that the empty-state CTA opens the modal without navigating. - Merged `origin/main` (#7438 replaced `PipelineHeader` with the new Create/Edit headers); full suite green at 240 files / 2072 tests after the merge. |
||
|
|
4b26797ad8 |
Redesign New/Edit Pipeline top bars (#7438)
# Description of Changes Replace the dev-UI top-bar in the New Pipeline and Edit Pipeline pages with a redesigned layout appropriate for users. I've got a big list of extra tweaks I'd like to do to the rest of the page including graph tweaks etc. but this is the only thing on the New/Edit Pipelines pages that is blocking for the release. ## Before ### New Pipeline <img width="1510" height="788" alt="image" src="https://github.com/user-attachments/assets/23f8a3a6-a8db-4d81-8c23-fd7a78864f2d" /> ### Edit Pipeline <img width="1512" height="791" alt="image" src="https://github.com/user-attachments/assets/4c0a0c3c-81d8-4d15-91c7-6aa4f3de69c7" /> ## After ### New Pipeline <img width="757" height="395" alt="image" src="https://github.com/user-attachments/assets/8a287cce-336c-4624-b4e9-a6a98eabb01d" /> ### Edit Pipeline <img width="1512" height="789" alt="image" src="https://github.com/user-attachments/assets/9e096dbb-0a95-4a03-8d32-d5d71d2b57c5" /> |
||
|
|
9ef20dcab8 |
Fix WebKit PDF-engine and storage failures, and catch them in cross-browser CI (#7366)
# Description of Changes Follow-up to #7314, which fixed the IndexedDB blob rejection itself. This one fixes the remaining WebKit engine gaps, fixes the ways that class of failure surfaced to the user, and adds the cross-browser signal that would have caught them on the PR instead of six weeks later. ## Why this exists Two total WebKit outages sat on `main` for weeks: 1. pdf.js reads its text stream with `for await (… of readableStream)`, and WebKit has no `ReadableStream[Symbol.asyncIterator]`. **All** pdf.js text extraction threw `TypeError: undefined is not a function` — Compare, read-aloud and the PDF text editor were dead on Safari. 2. IndexedDB in WebKit rejects Blob/File values with `UnknownError: Error preparing Blob/File data to be stored in object store`, so nothing persisted and every reload came back empty. Neither was caught, because the existing specs never did the work. The Compare specs filled both slots and asserted the button was enabled; none of them clicked it. The persistence specs asserted a *filename* reappeared after a reload, which only needs the metadata record, not the bytes. Every failure here **looked like success** — empty panes, blank thumbnails, a `src` that was set but empty. That shapes the tests more than the fixes. ## WebKit engine gaps - **`ReadableStream[Symbol.asyncIterator]`**, installed at the entry point before any PDF work starts. The lock discipline is the subtle part: releasing is idempotent, is *not* done after a successful read, and *is* done in the read's error steps — `for await` never calls `return()` when `next()` rejects, so nothing else would ever unlock an errored stream. - **`requestIdleCallback`**, installed once instead of guarded at each call site. This one wasn't broken, it was mistimed: the local fallbacks fired at 200ms and 1000ms, landing the pdfium WASM compile on top of the app's first renders. The shim honours the caller's full timeout, so `{timeout: 2000}` means 2000ms. - **`convertToBlob()` does not fail on a format it can't encode.** Per spec it silently serialises to PNG, so asking for WebP and getting PNG back looks like success. Canvas output now probes what the engine really produced (once per realm) and uses the best lossy format it honours. PNG of a rendered page is several times the size of the equivalent WebP or JPEG, held as object URLs for every page on screen, on the engine with the tightest renderer memory budget. ## WebKit storage failures These read as generic transaction hygiene. They aren't — a refused blob write **aborts its transaction**, which is the mechanism that turned a WebKit rejection into a hang. - **Blob refusal is remembered from any write**, not just the initial `add`. WebKit reports it when it can't write the blob's *backing file*, which is per-operation — an engine that accepted the add can still refuse the rewrite, and every read-modify-write rewrites the record with its body attached. - **Aborted transactions no longer hang.** Read-modify-write moves to a single `updateRecord` helper that owns its transaction, guards it once, and resolves on **commit** rather than on the put's `onsuccess`. The previous shape — two promises over one shared transaction, with an `await` between the get and the put — put the abort guard on the read, leaving the write with no handler at all. `persistVersionedOutputs` awaits that, and `.catch` can't rescue a promise that never settles, so tool outputs could silently stop persisting. - **Stored blobs are no longer re-wrapped on read.** Since #7175 the record holds the `File` itself; wrapping it in `new Blob([record.data])` can cost WebKit the backing handle, giving you an object that looks valid and reads as empty. - **The file sidebar reaches a resting state** when the library can't be read, instead of spinning forever on a rejection nobody observes. It carries on with the in-memory workbench files: an unreadable library should cost the user their history, not the file they're working on. - **Thumbnail failures are logged.** Three `catch {}` blocks returned `""`, and an empty thumbnail is indistinguishable from "this file has no preview" — which is how outage #1 hid as a cosmetic nicety. ## CI `main` now runs the whole stubbed suite once per engine (#7304), so the new `@engine-capability` specs get chromium, firefox and webkit for free. They assert the primitives actually work — a **counted** comparison, a raster thumbnail data URL with real payload, and a page rendered from a file restored by a reload — rather than that the UI rendered. Deliberately small: anything added there is paid for three times per PR, so add depth, not breadth. Run them alone with `task e2e:cross-browser -- --grep @engine-capability`. The cross-browser projects now share the stubbed project's viewport. At the device presets' default 1280x720 a layout difference would fail these specs on Firefox/WebKit only, which reads as an engine outage. `vite.config.ts` gains a `worker.plugins` entry so `@app/*` resolves inside worker bundles. Worker bundles are a separate Rollup pass and don't inherit `plugins`, so the alias worked in the app and failed in a worker — previously worked around with a relative import plus a lint exemption, which silently bypasses the layer cascade. ## Verification - `task frontend:check` green: typecheck, oxlint, theme lint, stylelint, prettier, 215 test files / 1841 tests. - The `@engine-capability` suite passes on Chromium and WebKit locally. - **Negative control:** with the `ReadableStream` shim removed, the WebKit comparison spec fails at the Deletions/Additions assertion — the exact reported Safari symptom. Restored, and it passes. Both the fix and the test that guards it are load-bearing. - The worker alias change verified both ways: the build inlines the encoding probe into the worker chunk, and removing `worker.plugins` fails with `Rollup failed to resolve import "@app/utils/canvasImageEncoding"`. - The abort regression test aborts the transaction mid-write and asserts `markFileAsProcessed` settles. Before the fix it never settles and the test times out. ## Split out of this PR Two things in earlier revisions of this branch were engine-agnostic — found via the same symptom, not the same cause — and now have their own PRs: - **#7416** — blocked IndexedDB upgrades hanging the file library (multi-tab lifecycle, the concurrent-open race, `onversionchange`). - **#7417** — the thumbnail TTL rewriting the whole library on every listing. `FileSidebar`'s try/catch appears in both this PR and #7416, identically: a WebKit rejection and a blocked-open rejection both have to stop stranding the spinner. Whichever merges second is a no-op for that file. ## Known gaps - The blob-refused **rewrite** recovery in `updateRecord` isn't unit-tested. `fake-indexeddb` never returns Blob values from a read, so the branch that converts to a copy can't be reached there. Noted in the test file. - For the same reason, `fileFromRecord`'s "hand the stored File back untouched" path is only covered on a real engine, by the reload spec. - Nothing asserts that `src/index.tsx` imports the shims. The unit suite installs the same module via `setupTests.ts` (jsdom has the same gaps WebKit does), so a future regression where the entry point drops the import would still be green under vitest. - `FileSidebar`'s resting-state fix loses its E2E coverage until #7416 lands — forcing WebKit's blob refusal from a spec isn't practical, which is why that spec blocks the database instead. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [x] My changes generate no new warnings ### Documentation - [x] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) |