feat(shared): bake xlsx + utif + jszip + docx-preview into every tool
Removes every runtime CDN load. The "ship the record player with the
record" philosophy: a downloaded .html file works offline against any
file the user can open, with no network dependency at runtime.
Newly vendored under shared/vendor/:
- xlsx.full.min.js (SheetJS, 928 KB) — XLSX/XLS preview
- utif.min.js (UTIF, 57 KB) — TIFF preview
Already there but now used by mdedit too:
- jszip.min.js, docx-preview.min.js
Call sites updated to drop the `await loadLibrary(URL)` pattern —
since the vendor JS is concatenated into the inline <script> at build
time, window.XLSX / window.JSZip / window.UTIF / window.docx are
available synchronously from page load.
Per-tool changes:
- archive/build.sh: +xlsx, +utif
- classifier/build.sh: +xlsx, +utif
- transmittal/build.sh: +xlsx, +utif
- mdedit/build.sh: +jszip, +docx-preview, +xlsx, +utif
(mdedit was the only tool not yet
bundling any of the preview deps)
- browse/build.sh: +utif
- archive/js/table.js, classifier/js/preview.js,
transmittal/js/files-preview.js, mdedit/js/file-tree.js (×2):
drop the `await loadLibrary('…cdn…')` lines.
- shared/preview-lib.js:
drop the loadLibrary(UTIF) / loadLibrary(JSZip) wrappers; assume
window.UTIF and window.JSZip are present.
Net bundle-size delta after baking:
archive: +990 KB → ~1.47 MB
browse: +57 KB → ~292 KB
classifier: +990 KB → ~1.43 MB
mdedit: +1100 KB → ~2.09 MB
transmittal: +990 KB → ~1.63 MB
Docs (AGENTS.md, ARCHITECTURE.md) updated: removed the "runtime CDN
loading exception" paragraph and the table row that flagged xlsx as
CDN-loaded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7ac2e1cc73
commit
d6206b03e7
14 changed files with 1236 additions and 26 deletions
|
|
@ -318,7 +318,7 @@ Use `git worktree` to run multiple agents on separate branches simultaneously wi
|
|||
|
||||
- Two-phase hydration: `populateStatic()` before publish, `hydrate()` on load of published file
|
||||
- Reactive state via Proxy — `app.state.mode = 'view'` auto-notifies subscribers
|
||||
- Runtime CDN loads (jszip, docx-preview, xlsx) are allowed only for the optional DOCX/XLSX preview; core features work offline
|
||||
- No runtime CDN loads. Every vendor library (jszip, docx-preview, xlsx, UTIF, Toast UI) is bundled at build time via `concat_files`. The dist HTML is fully self-contained — "ship the record player with the record."
|
||||
- Published payload stored in `<script id="transmittal-data" type="application/json">`
|
||||
|
||||
## mdedit-specific
|
||||
|
|
|
|||
|
|
@ -210,20 +210,32 @@ Some tools bundle third-party libraries. These live in `tool/vendor/` and are co
|
|||
|------|---------|------|-------|
|
||||
| mdedit | Toast UI Editor v3.2.2 | `vendor/toastui-editor-all.min.js` | Markdown editor with live preview |
|
||||
| mdedit | Toast UI Editor CSS | `vendor/toastui-editor.min.css` | Editor stylesheet |
|
||||
| transmittal | jszip, docx-preview, xlsx | CDN at runtime | Optional preview features; tool works without them |
|
||||
| shared | jszip | `shared/vendor/jszip.min.js` | ZIP read for previews + classifier hash-export |
|
||||
| shared | docx-preview | `shared/vendor/docx-preview.min.js` | DOCX preview |
|
||||
| shared | xlsx (SheetJS) | `shared/vendor/xlsx.full.min.js` | XLSX/XLS preview |
|
||||
| shared | UTIF | `shared/vendor/utif.min.js` | TIFF preview |
|
||||
|
||||
**Runtime CDN loading exception**: The transmittal tool loads jszip, docx-preview, and xlsx from CDN at runtime via `loadLibrary()` forDOCX/XLSX preview functionality. These are **optional enhancements**—core transmittal functionality (JSON payload communication) works without them. This exception is documented here because:
|
||||
**No runtime CDN loads.** Every external dependency is vendored into
|
||||
`shared/vendor/` (or, for mdedit's editor, `mdedit/vendor/`) and
|
||||
concatenated into each tool's bundle at build time. Tools that need a
|
||||
given library include the vendor path in their `build.sh`'s
|
||||
`concat_files` JS list. The "ship the record player with the record"
|
||||
philosophy: a downloaded `.html` file works offline against any file
|
||||
the user can open, with no network dependency at runtime.
|
||||
|
||||
1. The core transmittal features (creating, signing, verifying SHA-256 digests) do not depend on these libraries
|
||||
2. Preview functionality gracefully degrades if libraries fail to load
|
||||
3. Bundling would significantly increase file size for rarely-used features
|
||||
Trade-off accepted: bundle sizes are larger. archive, classifier,
|
||||
transmittal land around 1.5 MB after gzip; mdedit lands around 2 MB
|
||||
because it carries Toast UI + jszip + docx-preview + xlsx + UTIF.
|
||||
Justified by the offline-first guarantee: any tool downloaded from
|
||||
`/releases/` works without network, against air-gapped archives,
|
||||
forever. See ARCHITECTURE.md § "Why Single-File HTML Applications"
|
||||
for the longer rationale.
|
||||
|
||||
**Rule**: Runtime CDN loading is allowed only when:
|
||||
- Features are strictly optional (graceful degradation)
|
||||
- Core functionality works without the external library
|
||||
- Library is clearly documented as non-essential
|
||||
|
||||
`template.html` for tools with vendor deps loads those deps from CDN for convenient local development. The build script replaces CDN tags with the bundled vendor files in the output.
|
||||
`template.html` for tools with vendor deps loads those deps from CDN
|
||||
purely for **dev convenience** — opening a template.html directly in
|
||||
Chromium gives you a working tool without running a build. The build
|
||||
script strips/replaces those CDN tags so the dist HTML has every
|
||||
dependency inlined. No CDN URLs survive into the dist.
|
||||
|
||||
### Development vs Production
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ concat_files \
|
|||
concat_files \
|
||||
"../shared/vendor/jszip.min.js" \
|
||||
"../shared/vendor/docx-preview.min.js" \
|
||||
"../shared/vendor/xlsx.full.min.js" \
|
||||
"../shared/vendor/utif.min.js" \
|
||||
"../shared/zddc.js" \
|
||||
"../shared/hash.js" \
|
||||
"../shared/theme.js" \
|
||||
|
|
|
|||
|
|
@ -632,8 +632,8 @@
|
|||
if (!container) return;
|
||||
|
||||
try {
|
||||
await loadLibrary('https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js');
|
||||
|
||||
// XLSX is bundled into the dist HTML (shared/vendor/xlsx.full.min.js),
|
||||
// so window.XLSX is available synchronously — no runtime load.
|
||||
const arrayBuffer = await (file.handle
|
||||
? file.handle.getFile().then(f => f.arrayBuffer())
|
||||
: fetch(file.url).then(r => r.arrayBuffer()));
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ concat_files \
|
|||
# without an external HTTP dependency.
|
||||
concat_files \
|
||||
"../shared/vendor/jszip.min.js" \
|
||||
"../shared/vendor/utif.min.js" \
|
||||
"../shared/zddc.js" \
|
||||
"../shared/zddc-filter.js" \
|
||||
"../shared/theme.js" \
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ concat_files \
|
|||
concat_files \
|
||||
"../shared/vendor/jszip.min.js" \
|
||||
"../shared/vendor/docx-preview.min.js" \
|
||||
"../shared/vendor/xlsx.full.min.js" \
|
||||
"../shared/vendor/utif.min.js" \
|
||||
"../shared/zddc.js" \
|
||||
"../shared/hash.js" \
|
||||
"../shared/zddc-source.js" \
|
||||
|
|
|
|||
|
|
@ -403,8 +403,8 @@
|
|||
if (!container) return;
|
||||
|
||||
try {
|
||||
await loadLibrary('https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js');
|
||||
|
||||
// XLSX bundled into the dist HTML; window.XLSX is available
|
||||
// synchronously, no runtime load needed.
|
||||
const blob = await getFileBlob(file);
|
||||
const arrayBuffer = await blob.arrayBuffer();
|
||||
const workbook = XLSX.read(arrayBuffer, { type: 'array' });
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ concat_files \
|
|||
|
||||
# JavaScript files to concatenate in order
|
||||
concat_files \
|
||||
"../shared/vendor/jszip.min.js" \
|
||||
"../shared/vendor/docx-preview.min.js" \
|
||||
"../shared/vendor/xlsx.full.min.js" \
|
||||
"../shared/vendor/utif.min.js" \
|
||||
"../shared/zddc.js" \
|
||||
"../shared/zddc-source.js" \
|
||||
"../shared/theme.js" \
|
||||
|
|
|
|||
|
|
@ -630,9 +630,8 @@ async function displayDocxPreview(file, filePath, fileName, fileHandle, lastModi
|
|||
editorInstances.set(filePath, instanceData);
|
||||
|
||||
try {
|
||||
await loadLibrary('https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js');
|
||||
await loadLibrary('https://cdn.jsdelivr.net/npm/docx-preview@latest/dist/docx-preview.min.js');
|
||||
|
||||
// jszip + docx-preview bundled into the dist HTML; window.JSZip
|
||||
// and window.docx are available synchronously.
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
docxContainer.innerHTML = '';
|
||||
await window.docx.renderAsync(arrayBuffer, docxContainer);
|
||||
|
|
@ -692,8 +691,8 @@ async function displayXlsxPreview(file, filePath, fileName, fileHandle, lastModi
|
|||
editorInstances.set(filePath, instanceData);
|
||||
|
||||
try {
|
||||
await loadLibrary('https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js');
|
||||
|
||||
// XLSX bundled into the dist HTML; window.XLSX is available
|
||||
// synchronously, no runtime load needed.
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const workbook = XLSX.read(arrayBuffer, { type: 'array' });
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,10 @@
|
|||
opts = opts || {};
|
||||
injectStyles(doc, 'zddc-tiff-styles', TIFF_CSS);
|
||||
|
||||
return loadLibrary('https://cdn.jsdelivr.net/npm/utif@3.1.0/UTIF.js').then(function () {
|
||||
// UTIF is bundled (shared/vendor/utif.min.js) — window.UTIF is
|
||||
// available synchronously. Promise.resolve() keeps the existing
|
||||
// .then() chain shape so callers don't need to change.
|
||||
return Promise.resolve().then(function () {
|
||||
var ifds;
|
||||
try {
|
||||
ifds = window.UTIF.decode(arrayBuffer);
|
||||
|
|
@ -384,9 +387,9 @@
|
|||
opts = opts || {};
|
||||
injectStyles(doc, 'zddc-zip-styles', ZIP_CSS);
|
||||
|
||||
return loadLibrary('https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js').then(function () {
|
||||
return window.JSZip.loadAsync(arrayBuffer);
|
||||
}).then(function (zip) {
|
||||
// JSZip is bundled in every tool that uses preview-lib (each
|
||||
// tool's build.sh concatenates shared/vendor/jszip.min.js).
|
||||
return window.JSZip.loadAsync(arrayBuffer).then(function (zip) {
|
||||
var entries = [];
|
||||
zip.forEach(function (relativePath, zipEntry) {
|
||||
if (zipEntry.dir) return;
|
||||
|
|
|
|||
1160
shared/vendor/utif.min.js
vendored
Normal file
1160
shared/vendor/utif.min.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
24
shared/vendor/xlsx.full.min.js
vendored
Normal file
24
shared/vendor/xlsx.full.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -46,6 +46,8 @@ concat_files \
|
|||
concat_files \
|
||||
"../shared/vendor/jszip.min.js" \
|
||||
"../shared/vendor/docx-preview.min.js" \
|
||||
"../shared/vendor/xlsx.full.min.js" \
|
||||
"../shared/vendor/utif.min.js" \
|
||||
"../shared/zddc.js" \
|
||||
"../shared/hash.js" \
|
||||
"../shared/zddc-source.js" \
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@
|
|||
return;
|
||||
}
|
||||
try {
|
||||
await loadLibrary('https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js');
|
||||
// XLSX bundled into the dist HTML; window.XLSX is available
|
||||
// synchronously, no runtime load needed.
|
||||
var arrayBuffer = await getFileArrayBuffer(file);
|
||||
var workbook = XLSX.read(arrayBuffer, { type: 'array' });
|
||||
container.innerHTML = '';
|
||||
|
|
|
|||
Loading…
Reference in a new issue