Repository navigation
Commit 22d488f
UN-4502 [FEAT] Add the v1 PDF loader, document hook and viewer states (#2404)
* feat(frontend): v1 scaffold, strict TypeScript and import boundary (UN-4270)
Adds frontend/src/v1/ as an isolated tree for the v1 frontend (P0-T01):
- Folder taxonomy from the spec, a public entry (src/v1/index.ts, imported
by v0 as `@v1`) and a cloud overlay placeholder.
- src/v1/tsconfig.json: strict, no JS, unused locals/params are errors,
`@v1` alias only. v0's tsconfig/jsconfig exclude src/v1.
- Biome overrides for the two-way import boundary and v1 bans (no `../`,
`@/`, prop-types, antd, component CSS; noExplicitAny).
- scripts/check-v1.mjs for what Biome cannot express: TypeScript-only
files, the folder taxonomy and import contract, PropTypes, `ant-`.
- typecheck:v1, lint:v1 and test:v1 scripts; v0 scripts are unchanged.
- test-fixtures/v1-boundary/ proves each rule fails and passes as
specified, run with node --test so the v0 vitest suite never sees it.
- .gitignore re-includes src/v1/lib/ (the Python `lib/` rule matched it).
v0 build output, test counts, typecheck and Biome results are unchanged.
* fix(frontend): close v1 guardrail bypasses found in self-review (UN-4270)
check-v1.mjs:
- Scan comments with a string/template-aware tokenizer; a `/*` or `//`
inside a string or line comment no longer hides the imports after it.
- Read template-literal specifiers, import.meta.glob and `import(..., opts)`.
- Flag specifiers that leave src/v1 (`/`, `..`, `@/`, `src/`) itself, not
only via Biome, which skips template literals.
- Check `@import`/`url()` and `ant-` in styles/ CSS; report symlinks;
catch built `ant-` names; treat .test/.spec .mts/.cts as test files;
ignore .DS_Store; drop the unused --root option.
biome.json: also ban `..`, `./../**`, `/**`, `antd/**`, `prop-types/**`.
tsconfig: skipLibCheck off so v1's own .d.ts files are checked.
.gitignore: re-include lib/ at any depth under src/v1.
Fixtures: a generated matrix over every folder-contract pair, every import
form, every outside spelling, a symlink case, and cases for each new rule;
tsc cases assert error codes; output matching excludes the command text and
spawn errors fail loudly. Comment and README corrections.
* feat(frontend): run v1 checks in CI and tighten what pages may import (UN-4270)
- ci-frontend-lint: the typecheck job also runs `typecheck:v1` and
`lint:v1`, so the strict v1 tsconfig and the scripts/check-v1.mjs checks
(folder contract, TypeScript-only files, PropTypes, `ant-`) gate PRs now
rather than from P0-T10.
- Pages may no longer import app/, test/ or the v1 entry, only the shared
folders (components, shell, services, stores, hooks, lib, styles,
plugins). Test files in a page may still import test/. Contract table,
README and the fixture matrix updated together.
* refactor(frontend): address SonarCloud findings on the v1 guardrails (UN-4270)
check-v1.mjs: split the comment scanner into small helpers (cognitive
complexity 40 -> under 15), drop the nested ternary and nested template
literal, use String.raw for the import regexes, keep CLI flags in a Set.
Fixture runner:
- Cases and the expected folder contract are parsed text tables instead of
repeated object literals (the 10% duplication).
- Sorts use an explicit localeCompare comparator.
- The contract matrix is built by a helper.
- Spawns /bin/sh by absolute path and no longer needs `git init` (Biome only
needs the ignore file). The git check-ignore call keeps a PATH lookup, with
the reason and a NOSONAR marker.
Fixture cases: re-exports rewritten as small functions and read-only props,
without changing what each case exercises.
* refactor(frontend): split the contract-matrix builder to cut its complexity (UN-4270)
* fix(frontend): check every glob pattern and v0 -> v1 imports Biome cannot see (UN-4270)
Addresses Greptile review on #2380:
- import.meta.glob([...]): every pattern in the array is checked, not only
the first, so an allowed first pattern no longer hides a forbidden one.
- check-v1 --code also scans v0 (src/ outside v1, including the cloud
src/plugins overlay) for template-literal imports, globs, require(),
@/v1, src/v1, relative paths and CSS @import that reach into v1 other
than through `@v1`. Biome still covers plain imports.
Fixtures: Greptile's example as a case, a glob array with a forbidden later
pattern, and a generated test of every v0 -> v1 form (denied and allowed).
The v0 scan reports nothing on the current tree.
* UN-4299 [MISC] Turn on skipLibCheck for v1: vitest 3.2 and @types/chai 5.2 both declare containSubset
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FEAT] Add the v1 theme model: three modes, follow-system and per-browser preference
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FEAT] Add the v1 portal container and the theme preference store
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FEAT] Add the v1 token source, the generator and tokens.css in three modes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Prefix the spacing utilities in the token table so they match the v1 Tailwind root
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FEAT] Add cn() configured for the v1 utility prefix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Pin the v1 prefix in cn() with a test that fails without it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Use "ui" as the v1 utility prefix: Tailwind accepts lowercase letters only
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FEAT] Add the prefixed v1 Tailwind root with fonts, modes, theme mapping and the focus utility
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Exclude the generated tokens.css from Biome formatting so biome ci passes
src/v1/styles/tokens.css is written by scripts/build-v1-tokens.mjs and checked byte-for-byte by styles/tokens.test.ts; the formatter would rewrap it and break the check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Sort the imports in cn.ts so biome ci passes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Make the root compile test fail on unscoped selector lists and on missing variants
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FEAT] Add ThemeProvider: the v1 root, follow-system binding and portal sync
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Apply the stored theme before first paint with useLayoutEffect
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FEAT] Enforce the v1 styling policy: raw colours, pixels, inline styles, CSS imports and !important fail lint
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Scope the styling lint to class strings and cover vh/vw arbitrary values
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Keep v0's Tailwind scan out of src/v1, the lint scripts and the fixtures
v0's root auto-detected sources across the whole frontend, so the v1 tree, scripts/check-v1-styles.mjs and test-fixtures/ added 12 unprefixed utilities and 8 theme variables to v0's stylesheet. With these three @source not lines the v0 build is byte-identical to the base again (239/239 files).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Teach cn the type roles so a role and a text colour no longer cancel each other
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Reject colour literals outside class strings, named-colour arbitrary values and !important after a variant
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Keep the focus ring visible in forced-colors mode and scope variants, shadows and the root fill to v1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Create the portal container before the mode changes so the theme test exercises the mode effect
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Document the v0 rules that reach v1, the focus-ring usage, the pixel escape and the current unprefixed-class rationale
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [FIX] Name the theme storage key in the v1 README
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UN-4299 [MISC] Name the three theme modes in the v1 README
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* UN-4502 [FEAT] Add the v1 PDF loader, usePdfDocument hook and viewer states
- lib/pdf/pdfjs.ts is the only v1 import of pdf.js: the existing
pdfjs-dist 3.11.174 legacy build, its worker bundled with ?url, both
loaded on first use. getDocument always gets isEvalSupported: false
(CVE-2024-4367), and in-memory bytes are copied before the worker
transfer empties them.
- Page sizes are read in parallel, bounded at 16 in flight.
- hooks/usePdfDocument tracks empty, loading, password (missing or
wrong), unsupported, failed (with Retry) and ready. It reopens only
when the source's URL, request options or bytes change, and destroys
the previous load on change and on unmount.
- components/patterns/document-viewer/ViewerStatus renders every state
except ready.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
* UN-4299 [FEAT] Let a v1 style attribute set CSS custom properties only
Runtime geometry (a page's aspect ratio, a highlight's position) cannot be
a token or a class. Rule S1 now accepts a style attribute whose value is an
object literal with only "--name" keys, so the numbers travel through custom
properties that ui: classes read (ui:aspect-(--page-aspect)); a string, a
variable, a spread or a plain key still fails. Three fixtures pin it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* UN-4299 [FIX] Make check-v1-styles.mjs executable to match its shebang
pre-commit's check-shebang-scripts-are-executable fails on branches stacked
on this one; check-v1.mjs already carries the bit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* UN-4299 [FIX] Type the custom-property keys a v1 style attribute may set
React's CSSProperties has no index signature for "--*" keys, so the object
literal rule S1 allows fails TS2353 under strict mode. Augment it under
lib/, and typecheck the passing fixture so the augmentation is exercised.
Raised by Ali on the viewer stack.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* UN-4502 Keep signed URLs out of load errors and test the loader retry
pdf.js puts the full URL in MissingPDFException and
UnexpectedResponseException messages, so a signed URL's token reached the
screen. Build those messages from the status instead. Also cover the
reset that lets Retry load pdf.js again after a failed chunk.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
* UN-4502 Put the focus back in the password field after a wrong password
Submitting swaps the prompt for the loading state, so the field lost focus
and a keyboard user had to find it again. Raised by Greptile on #2404.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
* UN-4502 Answer passwords on the same load, keep damaged pages from failing the open
From review on #2404:
- A password goes to pdf.js's onPassword callback on the live loading task,
so a password (right or wrong) no longer downloads the file again or starts
a new worker, and Retry carries no stale password.
- A page whose size cannot be read borrows a neighbour's size and is logged;
only a document with no readable page fails to open.
- An unknown pdf.js error shows a plain message and is logged, instead of
showing pdf.js's own wording.
- destroy() handles the rejection pdf.js gives when the worker is gone.
- The hook's request type is OpenRequest, not a shadow of fetch's Request.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
* UN-4502 Load the same pdf.js build and worker as v0, not the legacy build
The legacy build put a second pdf.js engine and worker next to v0's. v1 now
imports pdfjs-dist and its default worker, the copy v0 already ships.
Also hands the rejected destroy promise in the loader test a handler up front,
so Vitest no longer reports it as an unhandled rejection while the test waits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
---------
Co-authored-by: Jaseem Jas <jaseem@zipstack.com>
Co-authored-by: pk-zipstack <praveen@zipstack.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>1 parent 3ce4b6a commit 22d488f
7 files changed
Lines changed: 1171 additions & 0 deletions
File tree
- frontend/src/v1
- components/patterns/document-viewer
- hooks
- lib/pdf
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
0 commit comments