c4c06cfd7d236bb99cc75111b35c98b0df27d2fd
16
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b38c849726 |
Portal: Procurement surface — layout rework + stateful mock backend (#6785)
Co-authored-by: aikido-pr-checks[bot] <169896070+aikido-pr-checks[bot]@users.noreply.github.com> Co-authored-by: Connor Yoh <con.yoh13@gmail.com> |
||
|
|
0beff1a92b |
feat(shared): make @shared the single home for brand logo assets (#6714)
## What
Makes `@shared` the single home for the Stirling brand logo assets.
Moves the editor's two logo sets — `classic-logo` + `modern-logo` (22
files: marks, wordmarks, favicons, login headers, PNGs) — out of
`editor/public/` into `shared/assets/brand/`, and adds a Storybook
**Brand/Logos** gallery.
## Why this shape (not a plain move)
The editor serves logos by **URL** from `public/` and switches
`classic`/`modern` by a **user preference** (`useLogoAssets`,
`manifest.json` / `manifest-classic.json`, `index.html` favicon links).
Rewiring all that to module imports would be a large, risky change to
the variant system.
Instead the editor keeps its variant system **unchanged** and just
sources the files from shared: `vite-plugin-static-copy` copies
`shared/assets/brand/{classic,modern}-logo/*` back to the served
`/{classic,modern}-logo` paths (the editor already uses this plugin for
pdfium/pdfjs assets). Single source of truth in shared, zero editor
code/manifest/markup changes.
## Verified
- **Build:** editor builds with both sets present at
`dist/{modern,classic}-logo/`; `manifest.json` + favicon refs resolve.
- **Dev:** the vite dev server serves the bridged paths —
`/modern-logo/logo512.png`,
`/modern-logo/StirlingPDFLogoNoTextDark.svg`,
`/classic-logo/favicon.ico` all return **HTTP 200** (the plugin's dev
middleware).
- Typecheck clean on core/proprietary/saas; prettier clean; `storybook
build` succeeds with the `Brand/Logos` gallery bundled.
- The portal's existing `@shared/assets` brand imports are untouched.
## Follow-ups (not in this PR)
- **Dedup:** `shared/assets/stirling-mark-*.svg` is byte-identical to
`brand/modern-logo/StirlingPDFLogoNoTextDark.svg`, and
`stirling-pdf-logo-*` is a near-twin of the modern wordmark. Reconciling
these (and re-pointing the portal) needs a designer eye on which
wordmark is canonical, so it's left out here to avoid changing the
portal's rendered logo.
- `editor/src/logo.svg` appears unused (no references) — candidate for
deletion separately.
|
||
|
|
14245d33d1 |
feat(saas): account-link — connected self-hosted billing (Mode A) [WIP, flag-gated] (#6738)
> **Draft / WIP.** Combined-billing **Mode A** (connected self-hosted). Entirely behind `stirling.billing.account-link.enabled` (default **off** → beans absent → 404). Pairs with Stirling-PDF-SaaS PR #313 (twin migration → `v3`). ## What this does A self-hosted instance links a SaaS account in the **Portal**, gets a **device credential**, and authenticates unattended metering/entitlement with it — no long-lived user JWT on the server. The Portal then surfaces the team's **billing** (free trial → metered Processor plan) driven by the live wallet. ```mermaid sequenceDiagram participant Portal as Portal (browser) participant Supa as SaaS Supabase Auth participant Local as Self-hosted backend participant SaaS as SaaS Java (app/saas) Portal->>Supa: signIn / signUp (Supabase JS, short-lived JWT) Supa-->>Portal: JWT (SDK-refreshed, stays in browser) Portal->>Local: hand JWT (same-origin) Local->>SaaS: POST /account-link/register (Bearer JWT, leader) SaaS-->>Local: { device_id, device_secret } (secret once) Note over Local: store device_secret server-side loop unattended Local->>SaaS: /api/v1/instance/** (X-Device-Id + X-Device-Secret) SaaS-->>Local: entitlement / gate decision end ``` **Auth model:** human auth = Supabase JS (ephemeral JWT, kept for attended portal features). Durable instance auth = a team-bound **device_id + secret** (SHA-256 stored, shown once), non-user `ROLE_LINKED_INSTANCE`, path-scoped to `/api/v1/instance/**`. Instance binds to a **team**, never a user. ## Billing surface (Portal · Mode A states) `Usage & billing` is state-driven by the link/subscription dimension and built to the marketing designs, sharing one component layer across states: - **Unlinked** → link-account prompt. - **Linked · Free** — the *Processor trial*: a one-time 500-PDF free grant ("Process 500 PDFs free, then $X/PDF"), the team's free-editor fleet, and a leader-only **Switch on the Processor →** (embedded Stripe Checkout). - **Linked · Subscribed** — the *Processor plan* dashboard: PDFs-processed split (API / Agents / Automation), **spend this month** vs. a **spend limit** meter with a run-rate projection and an **in-place cap editor** (preset buckets + suggested value + guardrail), Stripe **invoices** (with billed PDFs per invoice), and the default **payment method**. Card / subscription changes deep-link to Stripe's hosted portal. Manual PDF editing is always free — only Automation / AI / API is metered; a `$0` cap blocks all metered work (≠ "no cap"). **Shared, not duplicated:** the editor-fleet card, the Enterprise upsell, and the meter (`@shared/billing` `MeterBar`) render in both the free and subscribed views; money/cap math lives once in `@shared/billing`. The page header is a sticky, full-bleed bar. **New SaaS reads** (defensive — degrade to empty/"—" when the Stripe mirror lacks a table, never 500): - `GET /api/v1/payg/payment-method` — default card (brand / last4 / expiry) from `stripe.payment_methods`. - Invoice **PDFs processed** — billed line-item quantity from `stripe.invoice_line_items`. ## Progress - [x] Schema: `V22 linked_instance` (+ Supabase twin in #313) - [x] `AccountLinkController` register / list / revoke (leader-only, team from caller) - [x] Device-credential filter (path-scoped, constant-time, revocation-aware) + `SupabaseSecurityConfig` wiring (conditional) - [x] `GET /api/v1/instance/whoami` + **`/entitlement`** (reuses `EntitlementService`/`TeamBillingService`) + tests - [x] Self-hosted backend (`app/proprietary`): orchestrator + instance gate (dark + **fail-open**) + tests - [x] Portal: in-app Supabase login modal + register hand-off + `LinkContext` (unlinked default) + "Linked instances" view — all `@shared` Storybook components - [x] **Portal billing surface** — free (Processor trial) + subscribed (Processor plan) Usage views to marketing spec; link-state derived from the **live wallet**; in-place cap editor; over-cap banner - [x] **SaaS reads** — payment-method endpoint + invoice billed-units (defensive `stripe.*` mirror DAOs) + tests - [x] Orphan guard: block leaving/accepting away from a team whose departure orphans its linked instances - [ ] Metering Step 2 (lease + reconcile loop) + bounded fail-open cutoff - [ ] Proprietary hardening (SaaS base-url config, secret-at-rest, finer billable classification) + HTTP integration test - [ ] Cross-repo Stripe lifecycle certified end-to-end (subscribe → meter → cancel → 402) - [ ] Admin ⟺ SaaS-leader enforcement (separate portal-team-mgmt workstream) ## Verification — all green | Gate | Result | |---|---| | `STIRLING_FLAVOR=saas :saas:test` | BUILD SUCCESSFUL (account-link + payg, incl. `PaygPaymentMethodControllerTest`, `PaygInvoicesControllerTest`) | | `:proprietary:test` | BUILD SUCCESSFUL (account-link + entitlement cache/interceptor) | | portal | tsc 0 · eslint 0 · **vitest 55** · storybook build (all billing stories) | | frontend post-sync | typecheck shared + portal + editor (saas + desktop): 0 | ## Screenshots — billing UI _Latest Storybook renders (Portal/Billing). Drag each capture below its caption — kept out of the repo._ **Linked · Free — Processor trial** <img width="1648" height="503" alt="01-free-processor-trial" src="https://github.com/user-attachments/assets/afe6238a-d3b4-47fd-8ea2-cbaed8b0a653" /> **Linked · Subscribed — Processor plan dashboard** <img width="1648" height="930" alt="02-subscribed-processor-plan" src="https://github.com/user-attachments/assets/329e6808-a9a9-4e65-99af-5a8a5e6bf4ab" /> **Spend limit — in-place cap editor** <img width="1648" height="411" alt="03-spend-limit-editor" src="https://github.com/user-attachments/assets/acc95096-bf8e-4ab0-a32c-3c20dc94f816" /> ## Review feedback applied Reworked the portal after first-pass feedback: linking signs in via the **shared Supabase login** (SSO + email/password) — no bespoke form; the **device secret is never shown in or sent to the FE** (the local backend registers + stores it server-side); billing copy reads **PDFs**, not "units"; the wallet surface uses **`@shared` components** matching the SaaS Plan page. Re-verified including an assertion the link response carries no `deviceSecret`/`deviceId`. **Synced onto unified auth + in-app login (2026-06-23).** Merged `main` incl. **#6725 unified auth** (`frontend/shared/auth`); the link flow uses a shared `useSupabaseLogin` hook + `SupabaseLoginForm`, a portal `LinkAccountModal`, and `useAccountLink.completeLink(session)` (+ on-mount SSO redirect-return). Config: `VITE_SAAS_SUPABASE_URL` + `VITE_SAAS_SUPABASE_ANON_KEY`. The local `/account-link/link` call carries the Spring admin bearer with the SaaS JWT in the body. **SSO** needs the SaaS Supabase project to allow-list the portal redirect URL (email/password works without it). ## Assumptions / open - **Proprietary remains a scaffold** (placeholder SaaS base-url, plaintext device secret at rest, coarse billable classification). - Payment-method + invoice-quantity render only when `stripe.payment_methods` / `stripe.invoice_line_items` are in the Sync-Engine target (confirm in the Supabase/Sync-Engine config); otherwise they degrade gracefully. - A self-contained local HTML report + manual E2E runbook live in `notes/account-link-report/` (dev artifacts, outside the repo). --------- Co-authored-by: James Brunton <jbrunton96@gmail.com> |
||
|
|
3f7e898c69 |
Add sources service and frontend (#6774)
# Description of Changes Redesign policies backend to treat sources a lot closer to how the frontend imagined them working (they're persistent now and have an API). Then connect the portal to the sources when mocks are off to allow for source creation in the UI. It's not particularly useful to do that right now because there's no policies UI, but I've tested manually that sources set up in the UI are usable by policies created via the API. I had to change the portal so that when mocks are off, it doesn't just hard crash when attempting to connect to all the backend APIs that don't exist yet. It'll still log the errors, but just continues on rendering the UI now. I also changed all the policies backend APIs to be gated behind a flag instead of behind the SaaS profile. This is because we haven't yet got the payment model sorted, but we're going to need this stuff running self-hosted to be able to test it locally. |
||
|
|
8e485801c9 |
change policies ui (#6683)
• Removed colors from policies to make them look more professional. • upgraded to enterprise link to contact us. • Hid inactive policies from users (Kept for admin and team lead). • Closing policies had wrong arrow, made a standard component for chat, tools and policies header. |
||
|
|
101502cf4f |
Upgrade to TypeScript 6 (#6772)
# Description of Changes TS6 introduced backwards-incompatible changes which affected us a little bit. Other than that, I don't think it significantly changes things for us, but we will need to deal with these breaking changes to be able to upgrade to TS7 (the version written in Go, so dramatically faster), so I'd rather do the work now before TS7 actually releases. Main things I've done: - Removed the use of `baseUrl` in the `tsconfig.json` files - Explicitly provide the `node` types where needed - Explicitly reference the un-referenced but required Google API types - Dropped the installation of `madge` which we weren't using and wasn't directly compatible with TS6 - Updated the `i18next` packages for explicit TS6 compatibility - Explicitly override `tsconfck` to force TS6 compatibility since we can't upgrade it. We're only using that for `vite-tsconfig-paths` and it all still seems to work fine, so I don't think this is an issue. I think we can theoretically drop `vite-tsconfig-paths` when we upgrade to Vite 8 ([because it supports `paths`](https://v8.vite.dev/guide/features#paths)), but that's a bigger job than I want to do in this PR |
||
|
|
1816bad1ba |
Unified auth for portal and editor (#6725)
# Description of Changes Refactor frontend auth to the shared folder and hook it up to both the portal and editor so they share the same system. Also adds various tasks to help run the portal, including `task dev:portal` to spawn the portal with the backend, and `task dev:portal:proxy` to spawn the editor, portal and backend, and a reverse proxy (at localhost:3000) to allow you to use both at once to simulate how this will actually be deployed, allowing you to check whether the seamless transition between the two actually works. |
||
|
|
dffc292888 |
I18n on portal (#6761)
## Overview Internationalizes the **developer portal**, which previously had **zero i18n** — every string was hardcoded across ~118 components. Rather than stand up a parallel system, this shares the **editor's** existing i18n setup (same TOML locale format, same Crowdin pipeline), then converts every portal surface to `react-i18next` and adds a CI guard so coverage can't regress. ## What's included ### 🔗 Shared i18n core (`@shared/i18n`) - Extracts the editor's `TomlBackend` (HTTP loader for `public/locales/{lng}/translation.toml`) and language metadata/helpers (the 42-language list, RTL set, `LanguageSource` priority, code normalizers) into `frontend/shared/i18n/`. - The **editor** now imports and re-exports these from `@shared/i18n` — its 20+ consumers are unchanged. Its local `tomlBackend.ts` is deleted. - The **portal** builds its own i18next instance from the shared core, with **en-US as the source of truth** and the same `/locales/{lng}/translation.toml` layout. ### 🌍 Full portal coverage - Every view and component converted to `t()` — all feature areas (home, pipelines, sources, infrastructure, usage, documents, agent-builder, editor-admin, policies, users, docs, catalogue, components view) plus app shell, nav, modals, and the home/domain widgets. - **1108 keys across ~30 namespaces** in `portal/public/locales/en-US/translation.toml`, grouped by feature; shared strings under `[common]`. Plurals use i18next count forms; dynamic labels (nav, settings sections, status badges) use template keys against populated tables. - Data-driven strings (values from `@portal/api/*` mocks, enum/id values, code samples) are intentionally left untranslated — they're data, not UI chrome. ### ✅ CI coverage guard - `portal/scripts/check-i18n.mjs` fails if any static `t("key")` in portal source is missing from the en-US locale. Wired into `frontend:check` and `frontend:check:all`, so missed keys break CI. This mirrors the editor's `missingTranslations` test for the portal, which has no vitest harness of its own. ## Testing - `task frontend:check:all` passes locally (typecheck all variants, lint, format, **portal i18n guard**, builds, tests, storybook). - Every static `t()` key verified to resolve in the locale (1108 keys / 186 source files); all dynamic key prefixes map to populated tables. - Runtime sweep of all 12 portal routes shows **no unresolved keys** on screen; nav labels, plurals, and array-backed copy all render real text. ## Follow-ups (not in this PR) - **Crowdin** — register `frontend/portal/public/locales/` as a source so portal strings flow through the same translation pipeline as the editor (an ops step on the Crowdin side; there's no Crowdin config in the repo). - Only `en-US` is populated; other languages will arrive via the pipeline. |
||
|
|
2b05865a84 |
Portal: unified-design surfaces (Policies, Users, Components, Agent Builder, Editor deploy) + Settings rebuild (#6696)
Builds the remaining developer-portal surfaces from the unified design and rebuilds Settings, on top of the portal scaffold merged in #6686. All tier-aware, mock-driven (MSW), componentised with Storybook coverage. Touches only `frontend/portal` + `frontend/shared` — the editor is untouched. ## New surfaces - **Policies** — org-wide governance across the five categories (Ingestion / Security / Compliance / Routing / Retention) with a designer + per-doc-type overrides - **Users** — members, roles, invite, tier-scaled SSO/SCIM access - **Components** — embeddable `@stirling/*` SDK catalogue with per-action pricing - **Getting Started** — three-step funnel (use case → analyse a document → API key + snippets) - **Agent Builder** — agent lifecycle (scenarios, tool modes, evals/golden-sets, versions), reached from Sources - **Editor deployment** — deploy/pair/operate the editor (targets, pairing, health, credential rotation, air-gapped bundle), reached from Infrastructure ## Reworks - **Documents** → review/approval queue (confidence, extractions, audit drawer, zero-standing-access elevation); the doc-type catalogue is retained as a second tab - **Pipelines** → golden-set pass column + "Promoted from the Editor" section - **Infrastructure** → new **Models** tab; deeper **Security** (managed / BYOK / HYOK + SOC 2 / ISO 27001 / HIPAA / GDPR / PCI attestations) - **Home** → "What runs on your PDFs" policy summary + tier-aware processing-status strip + pipeline-fork wizard ## Settings & shared - New shared **`SettingsShell`** (grouped left-nav + content pane), modelled on the editor's account-settings modal so both apps can converge on one layout - Portal **Settings** rebuilt on it as scoped sections — Account / Workspace / Admin (Authentication, Active sessions, Early access) ## Brand - Adopt the editor's brand mark + favicon; sidebar reads **Stirling Processor**; app-switcher labels the active app "Processor" ## Mock contract - Every surface follows the 3-layer pattern (typed `api/*` → MSW handler → fixtures); new endpoints documented in `MOCKS.md`. The read contract is backend-ready; writes are marked `// TODO(backend): <METHOD> <path>`. ## Verification - tsc (portal + shared) ✓ · eslint ✓ · dpdm (no circular) ✓ · `build:portal` ✓ · `storybook:build` ✓ · Prettier ✓ ## Deferred (noted, not in scope) - Unified shell / auth / role→surface routing / Workspace=Plan (architectural epic) - Tier rename (Editor / Processor / Bespoke) and the Usage flat-pricing + PAYG quick-amounts + Bespoke modal - Editor adopting the shared `SettingsShell`; converting marked write-stubs into live `api/` seams |
||
|
|
7e67bfc459 |
Fix SaaS issues (#6694)
# Description of Changes Fixes several SaaS issues, was integration branch for saas release --- ## 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: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: James Brunton <jbrunton96@gmail.com> Co-authored-by: Reece Browne <74901996+reecebrowne@users.noreply.github.com> Co-authored-by: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com> Co-authored-by: Reece <reece@stirlingpdf.com> Co-authored-by: EthanHealy01 <80844253+EthanHealy01@users.noreply.github.com> Co-authored-by: Ludy <Ludy87@users.noreply.github.com> |
||
|
|
5389e39cfc |
Revert "SaaS fixes (#6578)"
This reverts commit
|
||
|
|
ddf78d11ae |
SaaS fixes (#6578)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: James Brunton <jbrunton96@gmail.com> Co-authored-by: Reece Browne <74901996+reecebrowne@users.noreply.github.com> Co-authored-by: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com> Co-authored-by: Reece <reece@stirlingpdf.com> Co-authored-by: EthanHealy01 <80844253+EthanHealy01@users.noreply.github.com> Co-authored-by: Ludy <Ludy87@users.noreply.github.com> |
||
|
|
96accea984 | Portal: full mock-driven surfaces, demonolithed components, backend-ready mocks (#6686) | ||
|
|
8dde4262ec |
feat(policies): backend-driven policy enforcement (frontend) (#6598)
## Summary Adds the **Policies** feature (proprietary, behind the `POLICIES_ENABLED` flag): backend-driven enforcement that runs a fixed tool pipeline on documents, docked in the right tool sidebar alongside Tools. ## Highlights - **Policy catalog** — 5 categories; **Security** is wired (redact PII + sanitize), the others are marked "Coming soon". - **Backend as source of truth** — policies persist via the Policies engine (`/api/v1/policies`), one policy per category, with a local cache + offline fallback. - **Auto-run** — enabled policies run on every uploaded file: dispatch → poll → import outputs into the workspace. - **Security redact config** — PII preset dropdown + custom word/regex entry + advanced options; tool params map to the backend endpoint fields. - **Activity feed** with retry on failures; **file badges** showing which policies ran on a file (sidebar + files page), tinted to the policy colour. - Reuses the **Watched Folders** engine for each policy's backing folder; policy-owned folders are filtered out of the Watched Folders UI. ## Notes - Gated by `POLICIES_ENABLED` (true in proprietary, false in core) — unreachable in the open-source build. - Frontend-only diff; depends on the backend Policies engine and the merged Watched Folders feature. |
||
|
|
9b877d4f8d | Move agent section to fab (#6597) | ||
|
|
919f0ade99 |
Portal (#6391)
# Description of Changes ## What & why This PR introduces the **Stirling developer portal** — a new control-plane frontend that sits alongside the existing PDF editor — plus the shared design system and workspace structure needed to host both apps in one frontend. The portal is the parent product surface: where users connect sources, compose pipelines, wire agents, and manage usage / billing / infrastructure, with the PDF editor as one capability inside it. This PR lays the **foundation** — workspace reshape, design system, app shell, navigation, and a mock-driven home — rather than wiring real backends (those surfaces are placeholders for follow-up phases). ## What's in this PR **1. Frontend repo reshape (`frontend/src/` → `frontend/editor/`)** The existing editor app moved under `frontend/editor/`, so `editor`, `portal`, and `shared` are siblings in one workspace. All references were updated accordingly: `LICENSE`, `.dockerignore`, `.gitignore`, build/sign shell scripts, the GH language-check script, the Taskfile, and Docker config. **No editor source logic changed — path references only.** **2. New shared design system (`frontend/shared/`)** - **Design tokens** in `tokens.css` as the single runtime source of truth (light/dark, category accents, gradients). `tokens.ts` now holds only the `Tier` type — the old JS palette mirror was removed (nothing consumed it and it had drifted). - ~30 framework-light **components** (Card, Button, Input, Select, Tabs, Modal, Drawer, Toast, MetricCard, StatusBadge, Skeleton, EmptyState, …) with Storybook stories. - **Typed data catalogues**: `endpoints.ts` (10 verticals / 64 endpoints) and `ops.ts`. **3. New developer portal app (`frontend/portal/`)** - App shell: `Header`, `Sidebar`, `AssistantPanel`, search modal, notifications, tier switcher, theme toggle, MSW toggle. - **Tier-aware** home (free / pay-as-you-go / enterprise): KPI strip, 30-day usage chart, onboarding checklist, quick actions, recent activity, region health, product grid, and a curated **"Popular use cases"** teaser. - **Documents** view hosting the full, tab-filterable endpoint catalogue. - Placeholder views for Sources / Pipelines / Agents / Editor / Infrastructure / Usage & Billing / Developer Docs / Settings (follow-up phases). - **MSW-mocked** API layer: `api/*` issues real `fetch`, intercepted by mocks in dev/Storybook; pointing at a real backend is just a matter of not registering MSW. `react-router` URLs; Tier / View / UI contexts. **4. Tooling & guardrails** - ESLint extended to `portal` + `shared`, with **layering-boundary rules**: `shared/` may depend only on third-party packages and itself (no `@app` / `@portal` / `@core` / `@proprietary` / Tauri), so it stays cleanly extractable into a standalone package later. - `dpdm` circular-dependency check now walks editor + portal + shared (the old glob matched only 2 files). - New **devDependencies only** — Storybook (+ a11y/docs/themes addons), MSW. No runtime dependencies added. - New tasks: `frontend:dev:portal`, `frontend:build:portal`. ## Testing done locally - `tsc` for both `portal` and `shared` projects — clean - `eslint --max-warnings=0` across the whole frontend — clean - `dpdm` circular-dependency check — no cycles - Editor builds clean: `vite build editor --mode core` (✓ built, only the pre-existing >500 kB chunk-size advisory) - Editor runs in dev (core mode) with **zero console errors**; portal runs in dev across all three tiers ## Notes for reviewers - The change is overwhelmingly **additive**: `shared/` and `portal/` are brand-new; the existing editor is path-reference changes only. - The portal is intentionally **mock-driven** at this stage — real backends and the remaining views land in follow-up phases. --- ## 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) - [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. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |