pict-section-fileview 0.0.0-stage → 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Steven Velozo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,196 @@
1
- # Temporary Holding Version
1
+ # pict-section-fileview
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A universal, embeddable **quality file viewer** for the Retold/Pict ecosystem. Drop one control on a page, point it
4
+ at a server-served file — a [Sluice](https://github.com/fable-retold) StoredFile record *or* a bare, range-served URL
5
+ — and it detects the type and renders it well on phones, iPads, desktops, TVs, and projected secondary displays.
6
+
7
+ It is an **orchestrator, not a renderer.** It owns type detection, a pluggable kind→renderer registry, the viewer
8
+ chrome, and child lifecycle, and delegates each media kind to a purpose-built child section that stays independently
9
+ usable on any page.
10
+
11
+ > **Status: Phase 1 complete.** Shipped: the dispatch core (registry, descriptor normalization, score-tier
12
+ > resolution, the chrome), the **probe ladder** (HEAD → Range → magic-byte sniff for bare URLs), and the full
13
+ > renderer set — the always-matches **download card**, a self-contained **text** lens, a **code** lens delegating to
14
+ > `pict-section-code`, a **markdown** lens delegating to `pict-section-content`, the **image** lenses (a
15
+ > self-contained responsive `<img>` plus a richer zoom/pan lens delegating to `pict-section-image`), the **video**
16
+ > and **audio** lenses (a self-contained native `<video>`/`<audio controls>` plus bespoke players delegating to
17
+ > `pict-section-video` / `pict-section-audio`), a **PDF** lens (PDF.js, page-lazy, Range-fed), and a **deep-zoom**
18
+ > lens for enormous images (OpenSeaDragon on a server DZI tile pyramid). See the architecture plan in the
19
+ > `pict-section-fileview` plansheet.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm install pict-section-fileview
25
+ ```
26
+
27
+ Hard dependencies are only `pict-view` and `pict-provider`. Every specialized renderer is **optional** and
28
+ host-provided: `pict-section-code` (code), `pict-section-content` (markdown), `pict-section-image` (zoom/pan image),
29
+ `pict-section-video` (video), `pict-section-audio` (audio), `PDF.js` (`window.pdfjsLib`, for PDFs), and
30
+ `OpenSeaDragon` (`window.OpenSeadragon`, for deep zoom). A renderer whose dependency is absent abstains via its
31
+ `Capability` gate, and the viewer falls through to the next best renderer — code/markdown degrade to readable text,
32
+ an image to a plain `<img>`, a video/audio to the native `<video>`/`<audio controls>`, and only a genuinely opaque
33
+ or non-web-playable file reaches the download card — rather than breaking.
34
+
35
+ ## Usage
36
+
37
+ ```javascript
38
+ const libFileview = require('pict-section-fileview');
39
+
40
+ // Register a provider + view pair with your pict application.
41
+ libFileview.createFileview(pict, { Identifier: 'MyFileView', DestinationAddress: '#SomeDiv' });
42
+
43
+ // Render the chrome, then point it at a file.
44
+ pict.views.MyFileView.render();
45
+
46
+ pict.providers.MyFileView.setFile('https://host/1.0/Depot/File/42/Open'); // a bare URL
47
+ pict.providers.MyFileView.setFile(tmpStoredFileRecord); // or a record
48
+ ```
49
+
50
+ `createFileview(pPict, pConfig)` config:
51
+
52
+ | Key | Meaning |
53
+ |---|---|
54
+ | `Identifier` | The instance hash — the registration key for the provider, the view, and every element id (so multiple fileviews coexist on one page). Default `Pict-Section-Fileview`. |
55
+ | `DestinationAddress` | CSS selector the viewer mounts into. Default `#Pict-Application-Container` (override app-wide with `setDefaultDestination()`). |
56
+ | `File` | An optional initial file (record or URL). |
57
+ | `Renderers` | Extra renderer plugins to register on top of the built-ins. |
58
+ | `DisableBuiltins` | Skip seeding the built-in renderers (you provide your own set). |
59
+ | `Fetch` | A `fetch`-compatible function the viewer uses to probe and load file bytes. Defaults to the page global; set it to route through a host REST client or to stub in tests. |
60
+ | `Probe` | `false` disables the bare-URL probe ladder (HEAD/Range/magic). Default on. |
61
+ | `ShowFullscreen` | `false` hides the toolbar's maximize button. Default on — the viewer can fill the browser viewport and back (`toggleFullscreen()` / `Esc`). |
62
+ | `CodeSectionClass` | The `pict-section-code` class for the code lens. Defaults to `window.PictSectionCode`. Absent ⇒ code files degrade to the text lens. |
63
+ | `ContentProviderClass` | The `pict-section-content` provider class for the markdown lens. Defaults to `window.PictSectionContent.PictContentProvider`. Absent ⇒ markdown degrades to the text lens. |
64
+ | `ImageSectionClass` | The `pict-section-image` class for the zoom/pan image lens. Defaults to `window.PictSectionImage`. Absent ⇒ images render in the self-contained basic `<img>` lens. |
65
+ | `VideoSectionClass` | The `pict-section-video` class for the bespoke video lens. Defaults to `window.PictSectionVideo`. Absent ⇒ video renders in the self-contained native `<video controls>` lens. |
66
+ | `AudioSectionClass` | The `pict-section-audio` class for the bespoke audio lens. Defaults to `window.PictSectionAudio`. Absent ⇒ audio renders in the self-contained native `<audio controls>` lens. |
67
+ | `PdfLibrary` | The PDF.js library for the PDF lens. Defaults to `window.pdfjsLib`. Absent ⇒ a PDF degrades to the download card. |
68
+ | `PdfWorkerSrc` | A PDF.js worker URL to set once (when PDF.js has not been given one). Alternatively set `pdfjsLib.GlobalWorkerOptions.workerSrc` yourself. |
69
+ | `OpenSeadragon` | The OpenSeaDragon factory for the deep-zoom lens. Defaults to `window.OpenSeadragon`. Absent ⇒ an enormous image degrades to the download card. |
70
+ | `OpenSeadragonOptions` | Extra options merged into the OpenSeaDragon viewer settings (e.g. `{ showNavigator: true }`). |
71
+ | `CodeMaxBytes` | Byte cap fed to the code highlighter (default 2 MB); larger files use the text lens. |
72
+ | `TextMaxBytes` | Byte cap (a Range read) for the text / markdown lenses (default 5 MB); the remainder is a download. |
73
+ | `CSSExtra` | Extra CSS appended to the shell styles. |
74
+ | `Ordinal`, `ProviderClass`, `ViewClass` | Advanced overrides. |
75
+
76
+ ## Built-in renderers
77
+
78
+ | Hash | Handles | Depends on |
79
+ |---|---|---|
80
+ | `Fileview-Renderer-Code` | source files (CodeJar, read-only, line numbers) | `pict-section-code` + a `window.CodeJar` page global |
81
+ | `Fileview-Renderer-Markdown` | `.md` / markdown | `pict-section-content`'s `parseMarkdown` |
82
+ | `Fileview-Renderer-DeepZoom` | enormous images, via a server DZI tile pyramid (zoom / pan) | OpenSeaDragon + a `Meta.TileSources` |
83
+ | `Fileview-Renderer-PDF` | PDF documents; pages rendered to canvas, page-lazy, Range-fed | PDF.js |
84
+ | `Fileview-Renderer-Image` | images, with zoom / pan / download chrome | `pict-section-image` |
85
+ | `Fileview-Renderer-Video` | web-playable video, with a bespoke control bar (play / seek / volume / rate) | `pict-section-video` |
86
+ | `Fileview-Renderer-Audio` | web-playable audio, a titled player with an optional waveform | `pict-section-audio` |
87
+ | `Fileview-Renderer-ImageBasic` | images; a responsive, fit-to-stage `<img>` | nothing (self-contained) |
88
+ | `Fileview-Renderer-VideoBasic` | web-playable video; a native `<video controls>` | nothing (self-contained) |
89
+ | `Fileview-Renderer-AudioBasic` | web-playable audio; a native `<audio controls>` | nothing (self-contained) |
90
+ | `Fileview-Renderer-Text` | any textual file; a `<pre>` | nothing (self-contained) |
91
+ | `Fileview-Renderer-DownloadCard` | everything else (the floor) | nothing |
92
+
93
+ The delegating lenses are **optional and host-provided** (see the config table): each gates on its dependency via
94
+ its `Capability`, and when that dependency is absent the viewer falls through to the next best renderer — a code or
95
+ markdown file without its lens still renders as readable text, an image without `pict-section-image` still renders
96
+ in the basic `<img>` lens, and only a genuinely opaque binary reaches the download card. The text/code/markdown
97
+ lenses fetch the file's bytes themselves (client-only, Range-capped) through the configured `Fetch`; the image
98
+ lenses load the bytes directly through an `<img>`.
99
+
100
+ An image a browser cannot paint (TIFF, HEIC, camera RAW) and an **oversize** image (raw bytes larger than ~30 MB —
101
+ never blit into an `<img>`) score 0 for the ordinary image lenses. When the descriptor carries a `Meta.TileSources`
102
+ (a DZI descriptor URL or an OpenSeaDragon tile source) **and** OpenSeaDragon is on the page, the **deep-zoom** lens
103
+ takes over and streams the image from a server tile pyramid — the client never holds the whole image. Produce that
104
+ pyramid with any DZI tiler; [`orator-file-service`](https://github.com/fable-retold/orator-file-service) generates
105
+ one with libvips (`dzsave`) and serves it (and the original bytes, with Range support) over HTTP. The deep-zoom lens
106
+ is self-contained — it news up OpenSeaDragon on its own instance-unique element, so two deep-zoom fileviews coexist
107
+ on one page. Without a tile source (or without OpenSeaDragon) an oversize/unpaintable image still degrades to the
108
+ download card.
109
+
110
+ The **PDF** lens delegates to PDF.js (a host-provided page global, `window.pdfjsLib`): it hands PDF.js the URL and
111
+ lets it fetch pages on demand with HTTP Range requests, rendering each page to a `<canvas>` lazily as it scrolls
112
+ into view (eagerly where `IntersectionObserver` is unavailable). Without PDF.js a PDF degrades to the download card.
113
+
114
+ The **video** and **audio** lenses play web-playable media: MP4/WebM/Ogg for video, MP3/M4A/Ogg/WAV/FLAC for audio.
115
+ Seeking rides the browser's native HTTP Range requests, so a range-served file scrubs without downloading the whole
116
+ clip — the same 206 contract the rest of the viewer speaks. When `pict-section-video` / `pict-section-audio` are on
117
+ the page the richer bespoke players take over (a control bar with play/seek/volume/rate, a titled audio header with
118
+ an optional `Meta.peaks` waveform); absent, the self-contained native `<video controls>` / `<audio controls>` lens
119
+ plays it anyway. A container a browser cannot decode (AVI, MKV, WMV; WMA, AIFF) scores 0 for the media lenses and
120
+ degrades to the download card — a server-side transcode into a web format is a later slice, like the deep-zoom tiler.
121
+
122
+ ## Renderer contract
123
+
124
+ A renderer is a plain object the registry dispatches to:
125
+
126
+ ```javascript
127
+ {
128
+ Hash: 'My-Renderer', // unique id
129
+ Priority: 0, // tiebreak when two renderers score equally (higher wins)
130
+ Capability: (provider) => boolean, // optional — abstain (fall through) when a page-global lib is absent
131
+ matches: (descriptor) => number, // score tier (see below)
132
+ prepareDescriptor: (descriptor, provider) => void, // optional — enrich before mount (the probe hook)
133
+ mount: (containerSelector, descriptor, provider) => handle
134
+ }
135
+ ```
136
+
137
+ `matches()` returns a **score tier**, and `resolveRenderer()` picks the highest (ties broken by `Priority`, then
138
+ registration order):
139
+
140
+ | Signal | Score |
141
+ |---|---|
142
+ | explicit config `Kind` | 10000 |
143
+ | strong extension | ~700 |
144
+ | header `Content-Type` | ~400 |
145
+ | magic bytes | ~200 |
146
+ | download-card fallback | 1 (always) |
147
+
148
+ `mount()` paints into `containerSelector` (always through `pict.ContentAssignment`) and returns a handle
149
+ `{ Hash, Descriptor, ChildViewHash?, Ready?, update?(descriptor), destroy() }`. The provider keeps one handle at a
150
+ time. On a **same-kind re-point** (the next file resolves to the same renderer) it calls the handle's optional
151
+ `update(descriptor)` to re-feed the live child in place rather than tearing it down and re-mounting — the
152
+ *reuse-one-per-kind* rule, since Pict has no `removeView` and re-minting a child per file leaks views/observers. On a
153
+ **type switch** it calls `destroy()`. `Ready` is an optional promise an async renderer resolves once its content has
154
+ painted (handy in tests). Register your own with `provider.registerRenderer(plugin)` — new types need zero changes to
155
+ the core.
156
+
157
+ ### FileDescriptor
158
+
159
+ The normalized contract every renderer reads:
160
+
161
+ ```
162
+ { URL, Name, Extension, MIMEType, Size, Kind, AcceptRanges, MagicBytes,
163
+ Meta: { title, language, poster, TileSources, duration, peaks },
164
+ Probe: { KindSource, MIMESource, ResolvedRendererHash, Strategy } }
165
+ ```
166
+
167
+ `normalizeDescriptor()` accepts either a bare URL string (`Strategy: 'auto'`) or a record (a Sluice StoredFile or a
168
+ pre-built descriptor; `Strategy: 'none'` when `Kind` is already set), and both become one FileDescriptor.
169
+
170
+ ## Examples
171
+
172
+ Two runnable example applications live under `example_applications/`:
173
+
174
+ | Example | What it shows |
175
+ |---|---|
176
+ | `fileview_gallery` | Ten independent fileview instances on one page, one per media type — text, JavaScript (code lens), markdown, a raster photo, a vector SVG, a binary that degrades to the download card, a **PDF** (PDF.js), an **enormous image** deep-zoomed with OpenSeaDragon against a live `orator-file-service` DZI pyramid, a **video** (pict-section-video), and an **audio** track (pict-section-audio). The breadth-and-multi-instance showcase. |
177
+ | `fileview_browser` | A single fileview embedded in an app shell and driven by a file list — clicking a file re-points the viewer. Demonstrates live type dispatch, the *reuse-one-per-kind* `update()` path (image→image), and `destroy()` teardown on a type switch (image→code), with the active lens shown under the viewer. |
178
+
179
+ Both drive the **real** delegated sections (pict-section-code + CodeJar, pict-section-content, pict-section-image, pict-section-video, pict-section-audio) — nothing is faked.
180
+
181
+ ```bash
182
+ npm run build # build the module
183
+ cd example_applications/fileview_gallery && npm run build # build an example's browser bundle
184
+ npm run example-serve # (from the module root) serve the built examples at http://localhost:<port>/
185
+ ```
186
+
187
+ `example-serve` runs `quack examples-serve` in static mode; open `/<example>/dist/index.html`.
188
+
189
+ ## Development
190
+
191
+ ```bash
192
+ npm test # Mocha TDD via quackage
193
+ npm run build # browser bundle via quackage
194
+ ```
195
+
196
+ MIT © Steven Velozo
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
- "name": "pict-section-fileview",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "pict-section-fileview",
3
+ "version": "1.0.1",
4
+ "description": "A universal, embeddable file viewer: one control that detects a file's type (from a Sluice record or a bare range-served URL) and dispatches it to the right renderer — code, markdown, images (ordinary + enormous), PDF, video and audio — delegating each media kind to a purpose-built child section.",
5
+ "main": "source/Pict-Section-Fileview.js",
6
+ "scripts": {
7
+ "test": "npx quack test",
8
+ "tests": "npx quack test -g",
9
+ "start": "node source/Pict-Section-Fileview.js",
10
+ "coverage": "npx quack coverage",
11
+ "build": "quack build",
12
+ "example": "npx quack examples",
13
+ "example-serve": "npx quack examples-serve",
14
+ "types": "tsc -p ."
15
+ },
16
+ "quackageExamples": {
17
+ "mode": "static"
18
+ },
19
+ "files": [
20
+ "source"
21
+ ],
22
+ "directories": {
23
+ "test": "test"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/fable-retold/pict-section-fileview.git"
28
+ },
29
+ "author": "steven velozo <steven@velozo.com>",
30
+ "license": "MIT",
31
+ "bugs": {
32
+ "url": "https://github.com/fable-retold/pict-section-fileview/issues"
33
+ },
34
+ "homepage": "https://github.com/fable-retold/pict-section-fileview#readme",
35
+ "devDependencies": {
36
+ "browser-env": "^3.3.0",
37
+ "chai": "^4.3.0",
38
+ "pict": "^1.0.372",
39
+ "pict-application": "^1.0.34",
40
+ "quackage": "^1.3.0",
41
+ "typescript": "^5.9.3"
42
+ },
43
+ "mocha": {
44
+ "diff": true,
45
+ "extension": [
46
+ "js"
47
+ ],
48
+ "package": "./package.json",
49
+ "reporter": "spec",
50
+ "slow": "75",
51
+ "timeout": "5000",
52
+ "ui": "tdd",
53
+ "watch-files": [
54
+ "source/**/*.js",
55
+ "test/**/*.js"
56
+ ]
57
+ },
58
+ "dependencies": {
59
+ "pict-provider": "^1.0.13",
60
+ "pict-view": "^1.0.68"
61
+ }
62
+ }