@nxgt/material-core 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -279,6 +279,37 @@ const page = pageTextFromItems([{ str: 'Café au', hasEOL: true }, { str: 'lait'
279
279
  findTextMatches(page.text, 'cafe au lait'); // [{ start: 0, end: 12 }] — case, accents and line breaks ignored
280
280
  ```
281
281
 
282
+ To open a reader on a page, `pinToPage(node, beforeScroll, onRelease?)`
283
+ keeps that page at the top of the page area (an element inside the
284
+ `[data-slot="scroll-area-viewport"]` of a `[data-slot="scroll-area"]`) while
285
+ the pages above it change size, until a wheel, touch, pointer or key in the
286
+ page area, or the function it returns. `beforeScroll` runs before each
287
+ correction (hold your scroll spy there); `onRelease` runs once when the pin
288
+ lets go — at once when there is nothing to pin, or no `ResizeObserver` (a
289
+ server). `pinHolds(node)` tells whether the view still shows `node` where the
290
+ pin puts it: at the top, or as near as the area can scroll at its end. A
291
+ scroll that leaves it elsewhere came from find in page, a screen reader's
292
+ cursor or your own `scrollTop`, none of which the pin hears: treat it as the
293
+ user's.
294
+
295
+ ```ts
296
+ import { pinHolds, pinToPage } from '@nxgt/material-core/pdf-viewer';
297
+
298
+ // pages: Map<number, HTMLElement>; spy: from createScrollSpy
299
+ let pinned = true;
300
+ const unpin = pinToPage(
301
+ () => pages.get(3), // the page's element, looked up again on each correction
302
+ () => spy.hold(), // the corrections are not the user's scrolling
303
+ () => { pinned = false; }, // the pin let go
304
+ );
305
+
306
+ function onScroll(page: number) {
307
+ if (pinned && pinHolds(pages.get(3))) return; // still the reader's own scroll
308
+ unpin(); // the view moved elsewhere: let go, then report
309
+ reportPage(page);
310
+ }
311
+ ```
312
+
282
313
  ### A currency's decimals: `/currency-digits`
283
314
 
284
315
  ```ts
@@ -377,14 +408,16 @@ and a `data:` URL of an image, video, audio file, plain text, CSV, Markdown,
377
408
  JSON or PDF; it returns `null` for anything else (`javascript:`,
378
409
  `vbscript:`, `data:text/html`, …), after removing what the browser ignores
379
410
  (spaces and control characters at the ends, tabs and newlines anywhere), and
380
- `FilePreviewUnsafeUrlError` is what `FilePreview` reports for one it refused.
411
+ `FilePreviewUnsafeUrlError` ("Refused to preview a URL with an unsafe
412
+ scheme") is what `FilePreview` reports for one it refused.
381
413
  `readTextPreview` reads the first bytes of a `Blob`, or of what a URL answers
382
414
  (it stops downloading past the cap), decoded with the `charset` of the
383
415
  response's `Content-Type` (or of the `Blob`'s `type`) when `TextDecoder`
384
416
  supports it, else as UTF-8, and says whether there was more; an HTTP error
385
- throws `FilePreviewHttpError` with its `status`, and an aborted `signal`
386
- rejects with its reason, for a `Blob` too. Nothing runs at import, so it is
387
- safe on a server; only `readTextPreview` touches `fetch`.
417
+ throws `FilePreviewHttpError` (`Could not read <url>: HTTP <status>`) with
418
+ its `status`, and an aborted `signal` rejects with its reason, for a `Blob`
419
+ too. Nothing runs at import, so it is safe on a server; only
420
+ `readTextPreview` touches `fetch`.
388
421
 
389
422
  ```ts
390
423
  import {
@@ -496,6 +529,8 @@ of `@nxgt/material-vue` adds the files to `nitro.externals.traceInclude`.
496
529
  | `Cannot find package '@react-pdf/renderer'` (or `'react'`) when importing `/pdf` or `/pdf/render` | `bun add react @react-pdf/renderer@^4.9` — or import from `/pdf/model` where you only build documents |
497
530
  | `Cannot find module '#standard-fonts/Helvetica'` on every request of a bundled server | `pdfkitStandardFonts()` from `/vite`, or keep the renderer external |
498
531
  | `Cannot find module '…/pdfkit/js/standard-fonts/Helvetica.cjs'` from a traced server output | add `pdfkitStandardFontFiles()` (or the directories' `**/*.cjs`) to the tracer's includes — `withNxgtPrint` and the Nuxt module do |
532
+ | `Could not read <url>: HTTP 401` (`FilePreviewHttpError`) for a file behind authentication | fetch it with your credentials and pass the `Blob` to `readTextPreview` / `FilePreview` |
533
+ | `Refused to preview a URL with an unsafe scheme` (`FilePreviewUnsafeUrlError`) | pass an `https:` or relative URL, or the content as a `Blob` |
499
534
 
500
535
  Every message, with its cause, is in [troubleshooting](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/troubleshooting.md).
501
536
 
package/docs/roadmap.md CHANGED
@@ -5,13 +5,7 @@ version an item shipped in is the only number on this page.
5
5
 
6
6
  ## Now
7
7
 
8
- - **Date range presets and times** (`/date-range`) — the built-in presets'
9
- ranges, whether a field's rules rule a range out, and the time of day on
10
- each end, for both libraries' `DateRangeField`.
11
- - **File preview** (`/file-preview`) — which kind of preview a file gets from
12
- its MIME type or extension, which URLs it may link to or load, and the
13
- first bytes of a text file read with a size cap in its own charset, for
14
- both libraries' `FilePreview`.
8
+ Nothing in progress right now; see Next.
15
9
 
16
10
  ## Next
17
11
 
@@ -37,14 +31,24 @@ version an item shipped in is the only number on this page.
37
31
 
38
32
  ## Shipped
39
33
 
34
+ - **Date range presets and times** (`/date-range`) — the built-in presets'
35
+ ranges, whether a field's rules rule a range out, and the time of day on
36
+ each end, for both libraries' `DateRangeField`. 0.3.0.
37
+ - **File preview** (`/file-preview`) — which kind of preview a file gets from
38
+ its MIME type or extension, which URLs it may link to or load, and the
39
+ first bytes of a text file read with a size cap in its own charset, for
40
+ both libraries' `FilePreview`. 0.3.0.
41
+ - **Slider ticks** (`/slider-ticks`) — the values a slider's tick marks sit
42
+ at, which both libraries' `SliderField` (`ticks`) read from here instead of
43
+ keeping a copy each. 0.3.0.
44
+ - **`formatFileSize` and the pdfkit fonts resolution** — the byte-count
45
+ formatter both libraries' file list and upload field use
46
+ (`/format-file-size`), and the Node-only resolution of pdfkit's standard
47
+ fonts behind `@nxgt/material/next` and `@nxgt/material-vue`'s Nuxt module
48
+ (`/pdfkit-fonts`), each kept once instead of in both libraries. 0.2.0.
40
49
  - **A first release** — the PDF model and renderer, the print formatters,
41
50
  the PDF file helpers, the PDF reader's framework-free modules
42
51
  (`/pdf-viewer`: search, geometry, page sizes, scroll spy), the currency
43
52
  digits, the fields' blur layers and the fonts plugin, which
44
53
  `@nxgt/material` and `@nxgt/material-vue` shared file by file, are this one
45
54
  package, and both libraries depend on it. 0.1.0.
46
- - **`formatFileSize` and the pdfkit fonts resolution** — the byte-count
47
- formatter both libraries' file list and upload field use
48
- (`/format-file-size`), and the Node-only resolution of pdfkit's standard
49
- fonts behind `@nxgt/material/next` and `@nxgt/material-vue`'s Nuxt module
50
- (`/pdfkit-fonts`), each kept once instead of in both libraries. 0.2.0.
@@ -17,6 +17,11 @@ file) is an example — search for the words around it.
17
17
 
18
18
  - [`PdfReader cannot open …: pass a URL, a Blob, an ArrayBuffer, a Uint8Array or { url, httpHeaders }.`](#pdfreader-cannot-open--pass-a-url-a-blob-an-arraybuffer-a-uint8array-or--url-httpheaders-)
19
19
 
20
+ **File preview**
21
+
22
+ - [`Could not read <url>: HTTP <status>` (`FilePreviewHttpError`)](#could-not-read-url-http-status-filepreviewhttperror)
23
+ - [`Refused to preview a URL with an unsafe scheme` (`FilePreviewUnsafeUrlError`)](#refused-to-preview-a-url-with-an-unsafe-scheme-filepreviewunsafeurlerror)
24
+
20
25
  **Build**
21
26
 
22
27
  - [`… still references '#standard-fonts/' after the transform — …`](#-still-references-standard-fonts-after-the-transform--)
@@ -92,6 +97,66 @@ yet, or a `Response` instead of its bytes:
92
97
  const bytes = await (await fetch(url)).arrayBuffer(); // not the Response
93
98
  ```
94
99
 
100
+ ## File preview
101
+
102
+ ### `Could not read <url>: HTTP <status>` (`FilePreviewHttpError`)
103
+
104
+ **When:** `readTextPreview(url)` from `/file-preview` — behind both
105
+ libraries' `FilePreview`, for a text, JSON, CSV or Markdown file given as a
106
+ URL — rejects with a `FilePreviewHttpError`, its message the URL as passed
107
+ and the status, as in `Could not read /files/notes.txt: HTTP 404`, and its
108
+ `status` the number. `FilePreview` shows "This file could not be previewed."
109
+ and hands this error to `onError` (`@error` in Vue).
110
+ **Why:** the server answered, but not with a `2xx`: a missing file (`404`),
111
+ a URL that needs credentials the request did not carry (`401`, `403` —
112
+ `fetch` sends cookies to the same origin only, and no `Authorization`
113
+ header), or a server error (`5xx`). Nothing of the body is read.
114
+ **Fix:** check the URL in the message. For a file behind authentication,
115
+ fetch it yourself with your credentials and pass the `Blob`, which is read
116
+ without a request; branch on `status` to tell a missing file from the rest:
117
+
118
+ ```ts
119
+ import { FilePreviewHttpError, readTextPreview } from '@nxgt/material-core/file-preview';
120
+
121
+ const response = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
122
+ const preview = await readTextPreview(await response.blob()); // a Blob: no request
123
+
124
+ try {
125
+ await readTextPreview(url);
126
+ } catch (error) {
127
+ if (error instanceof FilePreviewHttpError && error.status === 404) showMissing();
128
+ else throw error;
129
+ }
130
+ ```
131
+
132
+ A cross-origin URL whose server sends no CORS headers is not this error: it
133
+ is `fetch`'s own `TypeError` (`Failed to fetch` in Chrome), with no status.
134
+
135
+ ### `Refused to preview a URL with an unsafe scheme` (`FilePreviewUnsafeUrlError`)
136
+
137
+ **When:** both libraries' `FilePreview` is given a URL that
138
+ `safePreviewUrl` returns `null` for: it shows "This file could not be
139
+ previewed." with no Download link, and `onError` (`@error` in Vue) receives
140
+ this error. Nothing was linked, loaded or fetched.
141
+ **Why:** only `http:`, `https:`, a relative URL, `blob:` and a `data:` URL
142
+ of an image, video, audio file, plain text, CSV, Markdown, JSON or PDF may
143
+ reach an `href`, a `src` or `fetch`. Anything else — `javascript:`,
144
+ `data:text/html`, `file:`, and also a `data:` URL of XML, TSV or YAML — could
145
+ run script in your page or reach what it should not, so it is refused
146
+ whatever the file would have been. A URL with a colon before its first `/`,
147
+ `?` or `#` (`c:/files/a.txt`) reads as a scheme and is refused too.
148
+ **Fix:** serve the file over `https:` (or from your own origin), or turn its
149
+ content into a `Blob` and pass that — a `Blob` is previewed
150
+ through a `blob:` URL. Check a URL before rendering with `safePreviewUrl`:
151
+
152
+ ```ts
153
+ import { safePreviewUrl } from '@nxgt/material-core/file-preview';
154
+
155
+ safePreviewUrl('https://example.com/a.csv'); // 'https://example.com/a.csv'
156
+ safePreviewUrl('javascript:alert(1)'); // null
157
+ const file = safePreviewUrl(url) ?? new Blob([text], { type: 'text/yaml' });
158
+ ```
159
+
95
160
  ## Build
96
161
 
97
162
  ### `… still references '#standard-fonts/' after the transform — …`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/material-core",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "The framework-free core of @nxgt/material and @nxgt/material-vue: the PDF document model and its react-pdf renderer, print formatters, PDF file helpers, the PDF viewer's pure modules, the currency digits, the file-size formatter, the field-blur layers, the date range presets, the file preview's kind detection and text reader, the pdfkit fonts Vite plugin and the Node-only pdfkit fonts resolution for Next.js and Nuxt builds",
5
5
  "license": "MIT",
6
6
  "bugs": {