diff --git a/.gitignore b/.gitignore index 2a70e71..9af9f26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ *~ +# Core +core/node_modules +viewer.js.map + # Byte-compiled / optimized / DLL files __pycache__/ *.py[codz] diff --git a/README.md b/README.md index e0ab95b..3f8aad8 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,11 @@ Open [http://localhost:8080/](http://localhost:8080/) and click **Load demo**. | **Open file** | Select a `.dclx` archive, or a standalone `.dclg` / `.xml` markup file. | | **Drag and drop** | Drop any supported file onto the page. | -Supported types: `.dclx`, `.dclg`, `.xml`. The demo URL is configured in [`demo-data.js`](demo-data.js). +Supported types: `.dclx`, `.dclg`, `.xml`. The demo/example URL is configured via the `example` attribute on the `` element in [`index.html`](index.html). ## Files -- `demo-data.js` — demo archive URL -- `index.html` — shell UI -- `viewer.js` — parsing, page alignment, bbox overlay -- `viewer.css` — layout and theme +- `index.html` — shell page; loads `viewer.js` +- `viewer.js` — standalone ESM bundle (lit inlined); tracked in git for CDN / script-tag use +- `core/dist/` — npm library build (lit external, tree-shakeable); gitignored +- `core/` — TypeScript source; run `npm run build` inside to rebuild diff --git a/assets/icon-1024.png b/assets/icon-1024.png new file mode 100644 index 0000000..a44dd08 Binary files /dev/null and b/assets/icon-1024.png differ diff --git a/assets/icon-192.png b/assets/icon-192.png new file mode 100644 index 0000000..68ae1f2 Binary files /dev/null and b/assets/icon-192.png differ diff --git a/assets/icon-512.png b/assets/icon-512.png new file mode 100644 index 0000000..2e3bee4 Binary files /dev/null and b/assets/icon-512.png differ diff --git a/core/.prettierignore b/core/.prettierignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/core/.prettierignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/core/.prettierrc b/core/.prettierrc new file mode 100644 index 0000000..b6c1a17 --- /dev/null +++ b/core/.prettierrc @@ -0,0 +1,11 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "printWidth": 88, + "quoteProps": "as-needed", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false +} diff --git a/core/eslint.config.mjs b/core/eslint.config.mjs new file mode 100644 index 0000000..0fdde16 --- /dev/null +++ b/core/eslint.config.mjs @@ -0,0 +1,20 @@ +// @ts-check + +import eslint from '@eslint/js'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['dist', 'node_modules'], + }, + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + languageOptions: { + globals: { + ...globals.browser, + }, + }, + } +); diff --git a/core/examples/README.md b/core/examples/README.md new file mode 100644 index 0000000..7a0b73b --- /dev/null +++ b/core/examples/README.md @@ -0,0 +1,28 @@ +# DocLang Viewer — component examples + +Isolated examples for the larger content-rendering components. Each page loads the demo archive via the `src` attribute — no build step required. + +## Individual panes + +| Example | Component | Description | +|---------|-----------|-------------| +| [markup-pane.html](markup-pane.html) | `` | Formatted DocLang XML source view, page-by-page | +| [reading-pane.html](reading-pane.html) | `` | Rendered reading view with layers toggle | +| [page-img-pane.html](page-img-pane.html) | `` | Original page image with bounding-box overlay (requires a `.dclx` archive with images) | + +## Composed + +| Example | Description | +|---------|-------------| +| [all-panes.html](all-panes.html) | All three panes side-by-side, with page navigation and element selection cross-linked via vanilla JS | + +## Running locally + +These pages fetch the demo archive over HTTP, so they need to be served rather than opened as `file://`: + +```bash +# from the repo root +python3 -m http.server 8080 +``` + +Then open . diff --git a/core/examples/all-panes.html b/core/examples/all-panes.html new file mode 100644 index 0000000..f71fcc7 --- /dev/null +++ b/core/examples/all-panes.html @@ -0,0 +1,102 @@ + + + + + + DocLang — three panes in sync + + + + + +
+ ← examples + All three panes in sync — click an element in any pane to cross-highlight +
+ +
+ + + Page 1 +
+ +
+ + + +
+ + + + diff --git a/core/examples/example.css b/core/examples/example.css new file mode 100644 index 0000000..45b3e7a --- /dev/null +++ b/core/examples/example.css @@ -0,0 +1,23 @@ +* { box-sizing: border-box; } + +body { + margin: 0; + display: flex; + flex-direction: column; + height: 100dvh; + font-family: system-ui, sans-serif; +} + +header { + padding: 0.5rem 1rem; + background: #f7f8fa; + border-bottom: 1px solid #e5e7eb; + font-size: 0.85rem; + color: #57606a; + display: flex; + align-items: center; + gap: 1rem; + flex-shrink: 0; +} +header a { color: #3b82d4; text-decoration: none; } +header code { background: #e5e7eb; padding: 0.1em 0.35em; border-radius: 3px; } diff --git a/core/examples/markup-pane.html b/core/examples/markup-pane.html new file mode 100644 index 0000000..9d7dec2 --- /dev/null +++ b/core/examples/markup-pane.html @@ -0,0 +1,34 @@ + + + + + + doclang-markup-pane — example + + + + + +
+ ← examples + <doclang-markup-pane> — loaded via src attribute from the demo archive +
+ + + + + diff --git a/core/examples/page-img-pane.html b/core/examples/page-img-pane.html new file mode 100644 index 0000000..4fcd872 --- /dev/null +++ b/core/examples/page-img-pane.html @@ -0,0 +1,33 @@ + + + + + + doclang-page-img-pane — example + + + + + +
+ ← examples + <doclang-page-img-pane> — original page image with bounding-box overlay +
+ + + + + diff --git a/core/examples/reading-pane.html b/core/examples/reading-pane.html new file mode 100644 index 0000000..c13ca9e --- /dev/null +++ b/core/examples/reading-pane.html @@ -0,0 +1,33 @@ + + + + + + doclang-reading-pane — example + + + + + +
+ ← examples + <doclang-reading-pane> — loaded via src attribute from the demo archive +
+ + + + + diff --git a/core/package-lock.json b/core/package-lock.json new file mode 100644 index 0000000..87ddabd --- /dev/null +++ b/core/package-lock.json @@ -0,0 +1,2420 @@ +{ + "name": "@doclang/viewer-core", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@doclang/viewer-core", + "version": "0.1.0", + "dependencies": { + "lit": "^3.3.3" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "eslint": "^10.8.1", + "globals": "^17.11.0", + "prettier": "^3.9.6", + "typescript": "^6.0.3", + "typescript-eslint": "^8.67.0", + "vite": "^8.2.1", + "vite-plugin-dts": "^5.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", + "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", + "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.144.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz", + "integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz", + "integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz", + "integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz", + "integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz", + "integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz", + "integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz", + "integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz", + "integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz", + "integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz", + "integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz", + "integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz", + "integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz", + "integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz", + "integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.67.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", + "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz", + "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/exsolve": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", + "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/globals": { + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz", + "integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lit": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.3.tgz", + "integrity": "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", + "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz", + "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.3.tgz", + "integrity": "sha512-j/lCFdcppV0JxWpCEITdbDltBxPP6cHT+yNJ6Go2OgoSA9518X847X9z0p6LtA4Nc16+eQzCZjRrWanTGvHJ5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.3.1", + "exsolve": "^1.1.1", + "pathe": "^2.0.3" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/rolldown": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.4.tgz", + "integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.144.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.4", + "@rolldown/binding-darwin-arm64": "1.2.4", + "@rolldown/binding-darwin-x64": "1.2.4", + "@rolldown/binding-freebsd-x64": "1.2.4", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", + "@rolldown/binding-linux-arm64-gnu": "1.2.4", + "@rolldown/binding-linux-arm64-musl": "1.2.4", + "@rolldown/binding-linux-ppc64-gnu": "1.2.4", + "@rolldown/binding-linux-s390x-gnu": "1.2.4", + "@rolldown/binding-linux-x64-gnu": "1.2.4", + "@rolldown/binding-linux-x64-musl": "1.2.4", + "@rolldown/binding-openharmony-arm64": "1.2.4", + "@rolldown/binding-win32-arm64-msvc": "1.2.4", + "@rolldown/binding-win32-x64-msvc": "1.2.4" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unplugin-dts": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unplugin-dts/-/unplugin-dts-1.1.0.tgz", + "integrity": "sha512-KZJ+qk+lmd9dJY/PJvDRFL9BUtVubKDiX7Ai/X6mlkCArcQNzwmupKjVqHbRA42uqO5ZfFRFc+o8/OCbQ1GonQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.4", + "@volar/typescript": "^2.4.26", + "compare-versions": "^6.1.1", + "debug": "^4.4.0", + "glob-to-regexp": "0.4.1", + "kolorist": "^1.8.0", + "local-pkg": "^1.1.1", + "magic-string": "^0.30.17", + "unplugin": "^2.3.2" + }, + "peerDependencies": { + "@microsoft/api-extractor": ">=7", + "@rspack/core": "^1", + "@vue/language-core": "^3.1.5", + "esbuild": "*", + "rolldown": "*", + "rollup": ">=3", + "typescript": ">=4", + "vite": ">=3", + "webpack": "^4 || ^5" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "@vue/language-core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-dts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-5.1.0.tgz", + "integrity": "sha512-MfLc2G+mXPUDGGxpHQeYbdunavFP9UpT9yxiAsTgUXRxfEd3HqysSAM4E72jhz5eIz3OOexCHkqiVlQq8ok9/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "unplugin-dts": "1.1.0" + }, + "peerDependencies": { + "@microsoft/api-extractor": ">=7", + "rollup": ">=3", + "vite": ">=3" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/core/package.json b/core/package.json new file mode 100644 index 0000000..a607f0d --- /dev/null +++ b/core/package.json @@ -0,0 +1,47 @@ +{ + "name": "@doclang/viewer-core", + "version": "0.1.0", + "description": "DocLang document viewer — Lit web components", + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./*": "./dist/*.js" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "vite build && vite build --config vite.standalone.config.ts", + "build:lib": "vite build", + "build:standalone": "vite build --config vite.standalone.config.ts", + "dev": "vite build --watch", + "format": "prettier --write .", + "format:check": "prettier --check .", + "lint": "eslint .", + "typecheck": "tsc --noEmit", + "check": "npm run format:check && npm run lint && npm run typecheck && npm run build" + }, + "peerDependencies": { + "lit": "^3.0.0" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "eslint": "^10.8.1", + "globals": "^17.11.0", + "lit": "^3.3.3", + "prettier": "^3.9.6", + "typescript": "^6.0.3", + "typescript-eslint": "^8.67.0", + "vite": "^8.2.1", + "vite-plugin-dts": "^4.5.4" + }, + "engines": { + "node": ">=18" + } +} diff --git a/core/src/components/base/page-controller.ts b/core/src/components/base/page-controller.ts new file mode 100644 index 0000000..c4940d8 --- /dev/null +++ b/core/src/components/base/page-controller.ts @@ -0,0 +1,173 @@ +/** + * PageController — Lit reactive controller that adds page-flip behaviour to a + * pane element via both mouse-wheel and keyboard input. + * + * Wheel handling: accumulates pixel-delta events and emits 'view-page' once the + * configured threshold is exceeded, respecting scroll-boundary and cooldown. + * + * Keyboard handling: Arrow / Page keys fire 'view-page' when the pane or any + * descendant has focus. + */ + +import type { ReactiveController } from 'lit'; +import type { DoclangPageElement } from './page-element'; + +const COOLDOWN_MS = 200; +const PIXEL_THRESHOLD = 4; +const GESTURE_MS = 100; + +export class PageController implements ReactiveController { + private readonly _host: DoclangPageElement; + private readonly _getScrollPane: () => HTMLElement | null; + + // Per-gesture pixel accumulator + private _pixelAccum = 0; + private _gestureUntil = 0; + + // Per-pane cooldown; the viewer deduplicates via page-clamping. + private _lastFlipAt = 0; + + // Track last-seen page so hostUpdated can detect a real page change. + private _lastPage: number | null = null; + + constructor( + host: DoclangPageElement, + getScrollPane: () => HTMLElement | null, + ) { + this._host = host; + this._getScrollPane = getScrollPane; + host.addController(this); + } + + hostConnected(): void { + this._host.addEventListener('wheel', this._onWheel, { passive: false }); + this._host.addEventListener('keydown', this._onKeyDown); + } + + hostDisconnected(): void { + this._host.removeEventListener('wheel', this._onWheel); + this._host.removeEventListener('keydown', this._onKeyDown); + } + + hostUpdated(): void { + const page = this._host.page; + if (this._lastPage !== null && this._lastPage !== page) { + // Page actually changed — reset scroll position to the boundary that + // makes sense for the direction of travel. + const scrollPane = this._getScrollPane(); + if (scrollPane) { + const dir = page > this._lastPage ? 1 : -1; + scrollPane.scrollTop = dir > 0 ? 0 : scrollPane.scrollHeight; + } + } + this._lastPage = page; + } + + // --------------------------------------------------------------------------- + // Keyboard navigation + // --------------------------------------------------------------------------- + + private _onKeyDown = (ev: Event): void => { + const e = ev as KeyboardEvent; + const host = this._host; + const docState = host.document; + if (!docState || docState.markupOnly || docState.pageCount <= 1) return; + + let dir = 0; + switch (e.key) { + case 'ArrowDown': + case 'PageDown': + case 'ArrowRight': + dir = 1; + break; + case 'ArrowUp': + case 'PageUp': + case 'ArrowLeft': + dir = -1; + break; + } + if (!dir) return; + + e.preventDefault(); + const targetPage = host.page + dir; + if (targetPage < 1 || targetPage > docState.pageCount) return; + + host.dispatchEvent( + new CustomEvent('view-page', { + bubbles: true, + composed: true, + detail: { page: targetPage }, + }), + ); + }; + + // --------------------------------------------------------------------------- + // Wheel navigation + // --------------------------------------------------------------------------- + + private _dir(e: WheelEvent): number { + if (e.deltaMode === 1 /* DOM_DELTA_LINE */) return e.deltaY > 0 ? 1 : e.deltaY < 0 ? -1 : 0; + if (e.deltaMode === 2 /* DOM_DELTA_PAGE */) return Math.sign(e.deltaY); + // DOM_DELTA_PIXEL — accumulate until threshold to avoid firing on every px + const now = performance.now(); + if (now > this._gestureUntil) this._pixelAccum = 0; + this._gestureUntil = now + GESTURE_MS; + this._pixelAccum += e.deltaY; + if (Math.abs(this._pixelAccum) >= PIXEL_THRESHOLD) { + const dir = this._pixelAccum > 0 ? 1 : -1; + this._pixelAccum = 0; + return dir; + } + return 0; + } + + private _onWheel = (ev: Event): void => { + const e = ev as WheelEvent; + const host = this._host; + const docState = host.document; + + // Only act when multiple pages exist + if (!docState || docState.markupOnly || docState.pageCount <= 1) return; + + const scrollPane = this._getScrollPane(); + const dir = this._dir(e); + if (!dir) return; + + if (scrollPane) { + // Scrollable container: only flip when the scroll boundary is reached + const atTop = scrollPane.scrollTop <= 0; + const atBottom = + scrollPane.scrollTop + scrollPane.clientHeight >= scrollPane.scrollHeight - 1; + const scrollable = + scrollPane.scrollHeight > scrollPane.clientHeight || + scrollPane.scrollWidth > scrollPane.clientWidth; + + if (scrollable && !(dir < 0 && atTop) && !(dir > 0 && atBottom)) return; + } + + // Cooldown check + const now = performance.now(); + if (now - this._lastFlipAt < COOLDOWN_MS) { + e.preventDefault(); + return; + } + + const targetPage = host.page + dir; + // Clamp check: don't emit if already at boundary + if (targetPage < 1 || targetPage > docState.pageCount) { + e.preventDefault(); + return; + } + + e.preventDefault(); + this._lastFlipAt = now; + + host.dispatchEvent( + new CustomEvent('view-page', { + bubbles: true, + composed: true, + detail: { page: targetPage }, + }), + ); + }; +} diff --git a/core/src/components/base/page-element.ts b/core/src/components/base/page-element.ts new file mode 100644 index 0000000..d788bba --- /dev/null +++ b/core/src/components/base/page-element.ts @@ -0,0 +1,183 @@ +/** + * Base class for doclang web components that render document content. + * + * Supports three ways to provide the DocLang document: + * + * 1. `src` attribute — a URL (http:, data:, blob:) pointing to either a + * raw XML markup string or a .dclx/.zip archive. The component fetches + * and parses it automatically. + * + * 2. Inline child ` + * + * + * + * + * The npm / bundler library build (core/dist/index.js etc.) is produced by the + * main vite.config.ts with lit kept external for deduplication. + */ +export default defineConfig({ + esbuild: { + useDefineForClassFields: false, + }, + build: { + lib: { + entry: path.resolve(import.meta.dirname, 'src/index.ts'), + formats: ['es'], + fileName: () => 'viewer.js', + }, + outDir: path.resolve(import.meta.dirname, '..'), + emptyOutDir: false, + minify: true, + sourcemap: true, + }, +}); diff --git a/demo-data.js b/demo-data.js deleted file mode 100644 index 25439d4..0000000 --- a/demo-data.js +++ /dev/null @@ -1 +0,0 @@ -const DEMO_ARCHIVE_URL = "assets/2501.17887.dclx"; diff --git a/index.html b/index.html index 576e023..9106953 100644 --- a/index.html +++ b/index.html @@ -3,334 +3,27 @@ + DocLang Viewer + - - - - - -
-
- - - -

DocLang Viewer

- -
-
- -
-
-
- - -
- - - - - - - doclang.ai -
-
+ -

Drop to open another file

- -
-
-
- -

Loading demo…

-

Preparing the sample document

-
-
-

Drop a DocLang file here

-

Supported file types:

-

or load demo

-
-
-
- -
-
-
- Files - -
-
-
- -
-
- Original page -
- - -
-
-
-
- -
-
- -
-
DocLang
-
-
- -
-
- Reading view - -
-
-
- -
-
-
+ + + + - - + diff --git a/manifest.webmanifest b/manifest.webmanifest new file mode 100644 index 0000000..3bfe5ba --- /dev/null +++ b/manifest.webmanifest @@ -0,0 +1,28 @@ +{ + "name": "DocLang Viewer", + "short_name": "DocLang", + "description": "Viewer for DocLang archives (.dclx) and markup (.dclg, .xml).", + "start_url": "./", + "scope": "./", + "display": "standalone", + "icons": [ + { "src": "assets/favicon-16x16.png", "sizes": "16x16", "type": "image/png" }, + { "src": "assets/favicon-32x32.png", "sizes": "32x32", "type": "image/png" }, + { "src": "assets/icon-192.png", "sizes": "192x192", "type": "image/png" }, + { "src": "assets/icon-512.png", "sizes": "512x512", "type": "image/png" }, + { "src": "assets/icon-1024.png", "sizes": "1024x1024", "type": "image/png" }, + { "src": "assets/doclang_v3_sail.svg", "sizes": "any", "type": "image/svg+xml" } + ], + "launch_handler": { + "client_mode": "focus-existing" + }, + "file_handlers": [ + { + "action": "./", + "accept": { + "application/x-dclx": [".dclx"], + "application/x-dclg": [".dclg"] + } + } + ] +} diff --git a/viewer.css b/viewer.css deleted file mode 100644 index ba12ead..0000000 --- a/viewer.css +++ /dev/null @@ -1,2163 +0,0 @@ -:root { - color-scheme: light dark; - --bg: #f4f4f5; - --panel: #ffffff; - --border: #d4d4d8; - --text: #18181b; - --muted: #71717a; - --accent: #2563eb; - --placeholder-bg: #fafafa; - --font-ui: system-ui, -apple-system, "Segoe UI", sans-serif; - --font-mono: ui-monospace, "Cascadia Code", "Source Code Pro", monospace; - --markup-bg: #ffffff; - --markup-fg: #24292e; - --markup-hover: rgba(37, 99, 235, 0.08); - --markup-selected: rgba(37, 99, 235, 0.14); - --kind-text: #2563eb; - --kind-heading: #7c3aed; - --kind-list: #ea580c; - --kind-ldiv: #ea580c; - --kind-table: #16a34a; - --kind-index: #0d9488; - --kind-formula: #0891b2; - --kind-code: #475569; - --kind-picture: #db2777; - --kind-group: #4f46e5; - --kind-footnote: #a16207; - --kind-page_header: #71717a; - --kind-page_footer: #71717a; - --kind-caption: #71717a; - --kind-field: #d97706; - --kind-default: #dc2626; - --overlay-reading-order: #71717a; - --overlay-fragment: #9333ea; - --xml-tag: #116329; - --xml-bracket: #24292e; - --xml-attr-name: #0550ae; - --xml-attr-value: #0a3069; - --xml-text: #24292e; - --xml-cdata: #6a737d; - --xml-cdata-delimiter: #6a737d; - --file-thumb-size: 3.75rem; - --file-thumb-close-overflow: 0.25rem; - --file-item-pad-x: 0.35rem; - --file-pane-pad-x: 0.5rem; - --file-pane-fit-width: calc( - (var(--file-pane-pad-x) * 2 + - var(--file-item-pad-x) * 2 + - var(--file-thumb-size) + - var(--file-thumb-close-overflow)) * 1.5 - ); -} - -@media (prefers-color-scheme: dark) { - :root { - --bg: #09090b; - --panel: #18181b; - --border: #3f3f46; - --text: #fafafa; - --muted: #a1a1aa; - --placeholder-bg: #27272a; - --markup-bg: #1e1e1e; - --markup-fg: #d4d4d4; - --markup-hover: rgba(96, 165, 250, 0.1); - --markup-selected: rgba(96, 165, 250, 0.18); - --kind-text: #60a5fa; - --overlay-reading-order: #a1a1aa; - --overlay-fragment: #c084fc; - --kind-heading: #a78bfa; - --kind-list: #fb923c; - --kind-ldiv: #fb923c; - --kind-table: #4ade80; - --kind-index: #2dd4bf; - --kind-formula: #22d3ee; - --kind-code: #94a3b8; - --kind-picture: #f472b6; - --kind-group: #818cf8; - --kind-footnote: #facc15; - --kind-page_header: #a1a1aa; - --kind-page_footer: #a1a1aa; - --kind-caption: #a1a1aa; - --kind-field: #fbbf24; - --kind-default: #f87171; - --xml-tag: #4ec9b0; - --xml-bracket: #808080; - --xml-attr-name: #9cdcfe; - --xml-attr-value: #ce9178; - --xml-text: #d4d4d4; - --xml-cdata: #b5cea8; - --xml-cdata-delimiter: #808080; - } -} - -* { - box-sizing: border-box; -} - -.kind-text { --kind-stroke: var(--kind-text); } -.kind-heading { --kind-stroke: var(--kind-heading); } -.kind-list { --kind-stroke: var(--kind-list); } -.kind-ldiv { --kind-stroke: var(--kind-ldiv); } -.kind-table { --kind-stroke: var(--kind-table); } -.kind-index { --kind-stroke: var(--kind-index); } -.kind-formula { --kind-stroke: var(--kind-formula); } -.kind-code { --kind-stroke: var(--kind-code); } -.kind-picture { --kind-stroke: var(--kind-picture); } -.kind-group { --kind-stroke: var(--kind-group); } -.kind-footnote { --kind-stroke: var(--kind-footnote); } -.kind-page_header { --kind-stroke: var(--kind-page_header); } -.kind-page_footer { --kind-stroke: var(--kind-page_footer); } -.kind-caption { --kind-stroke: var(--kind-caption); } -.kind-field { --kind-stroke: var(--kind-field); } -.kind-default { --kind-stroke: var(--kind-default); } - -body { - margin: 0; - font-family: var(--font-ui); - background: var(--bg); - color: var(--text); - min-height: 100vh; - display: flex; - flex-direction: column; -} - -body.drag-over { - background: color-mix(in srgb, var(--accent) 6%, var(--bg)); -} - -body.drag-over .empty-state { - border-color: var(--accent); - background: color-mix(in srgb, var(--accent) 8%, var(--placeholder-bg)); -} - -header { - display: grid; - grid-template-columns: 1fr auto 1fr; - align-items: center; - gap: 1rem; - padding: 0.75rem 1rem; - border-bottom: 1px solid var(--border); - background: var(--panel); -} - -.header-brand { - display: flex; - align-items: center; - gap: 1rem; - min-width: 0; - grid-column: 1; - justify-self: start; -} - -header h1 { - margin: 0; - font-size: 1.1rem; - font-weight: 600; -} - -.header-center { - grid-column: 2; - justify-self: center; - min-width: 0; - text-align: center; -} - -.doc-label { - color: var(--muted); - font-size: 0.8125rem; - font-weight: 400; - min-width: 0; - max-width: min(40vw, 28rem); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.pane-file { - min-width: 0; - overflow: hidden; -} - -.pane-file .pane-header, -.pane-file .pane-body { - min-width: 0; -} - -.pane-file-header { - justify-content: space-between; - padding-inline: 0.35rem; - gap: 0.35rem; -} - -.pane-file-header .pane-header-title { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.file-pane-close-all { - appearance: none; - border: 1px solid transparent; - border-radius: 0.25rem; - background: transparent; - color: var(--muted); - font: inherit; - font-size: 0.625rem; - font-weight: 600; - line-height: 1; - letter-spacing: 0.04em; - text-transform: uppercase; - padding: 0 0.15rem; - height: 1.25rem; - cursor: pointer; - white-space: nowrap; - flex-shrink: 0; - transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease; -} - -.file-pane-close-all:hover { - color: var(--text); - background: color-mix(in srgb, var(--text) 5%, transparent); -} - -.file-pane-close-all:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -#file-pane { - padding: 0.375rem var(--file-pane-pad-x); - overflow: auto; - min-width: 0; -} - -.file-view-list { - list-style: none; - margin: 0; - padding: 0; - min-width: 0; - display: flex; - flex-direction: column; - gap: 0.25rem; - width: 100%; -} - -.file-view-list > li { - width: 100%; - max-width: 100%; -} - -.file-view-item { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.35rem; - width: 100%; - max-width: 100%; - box-sizing: border-box; - border: none; - border-radius: 0; - background: transparent; - color: var(--text); - font: inherit; - font-size: 0.8125rem; - line-height: 1.35; - text-align: center; - padding: 0.5rem var(--file-item-pad-x); - cursor: pointer; -} - -.file-view-thumb-wrap { - position: relative; - flex-shrink: 0; -} - -.file-view-close { - position: absolute; - top: -0.25rem; - right: -0.25rem; - appearance: none; - display: flex; - align-items: center; - justify-content: center; - width: 1rem; - height: 1rem; - padding: 0; - border: 1px solid var(--border); - border-radius: 50%; - background: var(--bg); - color: var(--muted); - font: inherit; - font-size: 0.75rem; - line-height: 1; - cursor: pointer; - box-shadow: 0 1px 2px rgb(0 0 0 / 8%); - z-index: 1; -} - -.file-view-close:hover { - color: var(--text); - background: var(--panel); -} - -.file-view-close:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 1px; -} - -.file-view-thumb { - flex-shrink: 0; - width: var(--file-thumb-size); - height: var(--file-thumb-size); - display: flex; - align-items: center; - justify-content: center; - background: var(--placeholder-bg); - border: 1px solid var(--border); - border-radius: 0.25rem; - overflow: hidden; -} - -.file-view-thumb img { - display: block; - width: 100%; - height: 100%; - object-fit: contain; -} - -.file-view-thumb-placeholder { - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - color: var(--muted); -} - -.file-view-thumb-placeholder svg { - display: block; - width: 1.875rem; - height: 1.875rem; -} - -.file-view-label { - width: 100%; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.file-view-item:hover { - background: var(--markup-hover); -} - -.file-view-item.is-active { - background: var(--markup-selected); - color: var(--accent); - font-weight: 500; -} - -.file-view-item:focus-visible { - outline: 2px solid var(--accent); - outline-offset: -2px; -} - -.header-logo-link { - display: block; - flex-shrink: 0; - border-radius: 0.25rem; - line-height: 0; - text-decoration: none; -} - -.header-logo-link:hover { - opacity: 0.8; -} - -.header-logo { - display: block; - height: 2.25rem; - width: auto; -} - -.header-logo-link:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.toolbar { - display: flex; - align-items: center; - gap: 0.5rem; - flex-wrap: wrap; - grid-column: 3; - justify-self: end; -} - -.header-divider { - flex-shrink: 0; - align-self: center; - width: 1px; - height: 1.125rem; - background: var(--border); -} - -.toolbar-options-wrap { - position: relative; -} - -/* Menu/control triggers — visually distinct from action buttons */ -.toolbar-options-btn, -.pane-settings-toggle { - appearance: none; - border: 1px solid transparent; - background: transparent; - color: var(--muted); - font-weight: 500; - cursor: pointer; - white-space: nowrap; - flex-shrink: 0; - display: inline-flex; - align-items: center; - transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease; -} - -.toolbar-options-btn { - border-radius: 0.375rem; - padding: 0.35rem 0.45rem 0.35rem 0.55rem; - font-size: 0.8125rem; - line-height: 1.2; -} - -.toolbar-options-btn::after, -.pane-settings-toggle::after { - content: ""; - flex-shrink: 0; - width: 0.32rem; - height: 0.32rem; - margin-left: 0.3rem; - margin-top: -0.14em; - border-right: 1.5px solid currentColor; - border-bottom: 1.5px solid currentColor; - transform: rotate(45deg); - opacity: 0.65; -} - -.pane-settings-toggle::after { - width: 0.26rem; - height: 0.26rem; - margin-left: 0.22rem; - margin-top: -0.1em; -} - -.toolbar-options-btn:hover, -.pane-settings-toggle:hover { - color: var(--text); - background: color-mix(in srgb, var(--text) 5%, transparent); -} - -.toolbar-options-btn[aria-expanded="true"], -.pane-settings-toggle[aria-expanded="true"] { - color: var(--accent); - background: color-mix(in srgb, var(--accent) 10%, var(--panel)); - border-color: color-mix(in srgb, var(--accent) 22%, transparent); -} - -.toolbar-options-btn:focus-visible, -.pane-settings-toggle:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.toolbar-options-panel { - position: absolute; - top: calc(100% + 0.35rem); - right: 0; - z-index: 30; - min-width: 11rem; - padding: 0.5rem 0; - border: 1px solid var(--border); - border-radius: 0.375rem; - background: var(--panel); - box-shadow: 0 0.5rem 1.25rem color-mix(in srgb, var(--text) 12%, transparent); -} - -.toolbar-options-panel[hidden] { - display: none; -} - -.toolbar-options-heading { - padding: 0.15rem 0.75rem 0.35rem; - font-size: 0.6875rem; - font-weight: 600; - letter-spacing: 0.06em; - text-transform: uppercase; - color: var(--muted); -} - -.toolbar-options-item { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.35rem 0.75rem; - font-size: 0.875rem; - cursor: pointer; -} - -.toolbar-options-item:hover { - background: color-mix(in srgb, var(--accent) 6%, var(--panel)); -} - -.toolbar-options-item input { - margin: 0; - flex-shrink: 0; - cursor: pointer; -} - -.toolbar-options-item-disabled { - opacity: 0.45; - cursor: not-allowed; -} - -.toolbar-options-item-disabled input { - cursor: not-allowed; -} - -.toolbar-options-divider { - margin: 0.35rem 0; - border-top: 1px solid var(--border); -} - -.toolbar-options-reset { - display: block; - width: 100%; - box-sizing: border-box; - margin: 0; - padding: 0.35rem 0.75rem; - border: 0; - border-radius: 0; - background: transparent; - color: var(--text); - font: inherit; - font-size: 0.875rem; - text-align: left; - cursor: pointer; -} - -.toolbar-options-reset:hover:not(:disabled) { - background: color-mix(in srgb, var(--accent) 6%, var(--panel)); - color: var(--accent); -} - -.toolbar-options-reset:disabled { - opacity: 0.45; - cursor: not-allowed; -} - -.drop-banner { - display: none; - margin: 0; - padding: 0.35rem 1rem; - text-align: center; - font-size: 0.8125rem; - color: var(--muted); - background: color-mix(in srgb, var(--accent) 8%, var(--panel)); - border-bottom: 1px solid var(--border); -} - -body.viewer-loaded.drag-over .drop-banner { - display: block; -} - -.header-brand .page-nav { - margin-left: 0.25rem; -} - -.header-brand .page-nav::before { - content: ""; - flex-shrink: 0; - align-self: center; - width: 1px; - height: 1.125rem; - margin-right: 0.75rem; - background: var(--border); -} - -.header-site-link { - color: var(--muted); - font-size: 0.875rem; - text-decoration: none; - white-space: nowrap; -} - -.toolbar-file-group { - display: inline-flex; - align-items: center; -} - -.header-site-link:hover { - color: var(--accent); - text-decoration: underline; -} - -.header-site-link:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - border-radius: 0.125rem; -} - -button, -label.file-btn { - appearance: none; - border: 1px solid var(--border); - background: var(--panel); - color: var(--text); - border-radius: 0.375rem; - padding: 0.4rem 0.75rem; - font: inherit; - font-size: 0.875rem; - cursor: pointer; -} - -button:hover, -label.file-btn:hover { - border-color: var(--accent); -} - -button:disabled { - opacity: 0.45; - cursor: not-allowed; -} - -label.file-btn input { - display: none; -} - -.page-nav { - display: flex; - align-items: center; - gap: 0.75rem; -} - -.page-nav-btns { - display: inline-flex; - align-items: center; - gap: 0.25rem; -} - -.page-nav-btn { - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.875rem; - height: 1.875rem; - padding: 0; - flex-shrink: 0; -} - -.page-nav-chevron { - display: block; - width: 0.875rem; - height: 0.875rem; -} - -.page-nav[hidden] { - display: none; -} - -.page-indicator { - display: inline-flex; - align-items: center; - font-size: 0.875rem; - color: var(--muted); - font-variant-numeric: tabular-nums; -} - -.page-number-input { - appearance: none; - border: 1px solid var(--border); - border-radius: 0.375rem; - background: var(--panel); - color: var(--text); - font: inherit; - font-size: 0.875rem; - font-variant-numeric: tabular-nums; - text-align: center; - padding: 0 0.35rem; - margin: 0; - height: 1.875rem; - box-sizing: border-box; - width: calc(var(--page-num-digits, 1) * 1ch + 0.75rem); - line-height: 1; - cursor: text; -} - -.page-number-input:hover:not(:disabled) { - border-color: var(--accent); -} - -.page-number-input:focus { - outline: none; - border-color: var(--accent); - box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent); -} - -.page-number-input:disabled { - opacity: 0.55; - cursor: not-allowed; -} - -.pane-page-header { - justify-content: space-between; -} - -.pane-reading-header { - justify-content: space-between; -} - -.pane-page-controls { - display: flex; - align-items: center; - gap: 0.5rem; - flex-shrink: 0; -} - -.page-zoom-control { - display: flex; - align-items: center; - gap: 0.35rem; - user-select: none; -} - -.page-zoom-control-label { - font-size: 0.625rem; - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--muted); - cursor: pointer; -} - -.page-zoom-control input[type="range"] { - width: 5.5rem; - height: 1rem; - margin: 0; - cursor: pointer; -} - -.page-zoom-reset { - appearance: none; - border: 1px solid var(--border); - background: var(--bg); - color: var(--muted); - border-radius: 0.375rem; - padding: 0 0.45rem; - height: 1.25rem; - font: inherit; - font-size: 0.6875rem; - font-weight: 600; - font-variant-numeric: tabular-nums; - line-height: 1; - cursor: pointer; - min-width: 2.75rem; -} - -.page-zoom-reset:not(:disabled):hover { - border-color: var(--accent); - color: var(--text); -} - -.page-zoom-reset:disabled { - opacity: 0.55; - cursor: default; -} - -.pane-header-title { - min-width: 0; -} - -.pane-settings-toggle { - border-radius: 0.25rem; - padding: 0 0.28rem 0 0.4rem; - height: 1.25rem; - font-size: 0.625rem; - font-weight: 600; - line-height: 1; - letter-spacing: 0.04em; - text-transform: uppercase; -} - -.pane-page-layout, -.pane-reading-layout { - position: relative; - flex: 1; - min-height: 0; - min-width: 0; - display: flex; - flex-direction: column; -} - -.pane-page-layout > .pane-body, -.pane-reading-layout > .pane-body { - flex: 1 1 auto; - min-width: 0; - min-height: 0; -} - -.viewer-settings-layer { - position: absolute; - inset: 0; - z-index: 5; -} - -.viewer-settings-layer[hidden] { - display: none; -} - -.viewer-settings-scrim { - position: absolute; - inset: 0; - z-index: 0; - appearance: none; - border: none; - margin: 0; - padding: 0; - background: color-mix(in srgb, var(--bg) 28%, transparent); - cursor: default; -} - -.viewer-settings { - position: absolute; - top: 0.5rem; - right: 0.5rem; - z-index: 1; - width: 13rem; - max-height: calc(100% - 1rem); - display: flex; - flex-direction: column; - min-height: 0; - border: 1px solid var(--border); - border-radius: 0.5rem; - background: var(--panel); - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.14); - overflow: hidden; -} - -.viewer-settings-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; - padding: 0.5rem 0.75rem; - border-bottom: 1px solid var(--border); -} - -.viewer-settings-title { - margin: 0; - font-size: 0.6875rem; - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--muted); -} - -.viewer-settings-close { - appearance: none; - border: none; - background: transparent; - color: var(--muted); - width: 1.5rem; - height: 1.5rem; - border-radius: 0.25rem; - font-size: 1.1rem; - line-height: 1; - cursor: pointer; -} - -.viewer-settings-close:hover { - color: var(--text); - background: var(--bg); -} - -.viewer-settings-body { - padding: 0.75rem; - overflow: auto; -} - -.settings-option { - display: flex; - align-items: flex-start; - gap: 0.5rem; - font-size: 0.875rem; - cursor: pointer; - user-select: none; -} - -.settings-option-primary + .settings-subgroup { - margin-top: 0.65rem; -} - -.settings-option-primary { - font-weight: 600; -} - -.settings-option-heading { - cursor: default; -} - -.settings-subgroup { - display: flex; - flex-direction: column; - gap: 0.5rem; - padding-left: 0.85rem; -} - -.settings-reading-order-group { - gap: 0.35rem; - padding-left: 1.6rem; -} - -.settings-option-nested { - padding-left: 0; -} - -.settings-option-sub { - font-size: 0.8125rem; - color: var(--muted); -} - -.settings-option-disabled { - opacity: 0.45; - cursor: not-allowed; -} - -.settings-option-disabled input { - cursor: not-allowed; -} - -.settings-option input { - margin: 0.15rem 0 0; - flex-shrink: 0; - cursor: pointer; -} - -main.split { - display: none; - flex: 1 1 0; - min-height: 0; -} - -body.viewer-loaded #main { - display: grid; -} - -body.viewer-loaded #main.layout-stacked { - grid-template-columns: 1fr !important; -} - -body.viewer-loaded #main.layout-stacked .pane-splitter { - display: none !important; -} - -body.viewer-loaded #main.layout-stacked .pane { - border-right: none; - border-bottom: 1px solid var(--border); -} - -body.viewer-loaded #main.layout-stacked .pane-layout-last { - border-bottom: none; -} - -body:not(.viewer-loaded) #page-nav, -body.markup-only #page-nav { - display: none !important; -} - -body.viewer-loaded #empty-state { - display: none; -} - -body.viewer-loaded #main:not(.layout-stacked) { - grid-template-rows: minmax(0, 1fr); -} - -body.viewer-loaded #main:not(.layout-stacked) .pane { - border-right: none; -} - -.pane { - display: flex; - flex-direction: column; - border-right: 1px solid var(--border); - min-height: 0; - min-width: 0; -} - -.pane[hidden] { - display: none !important; -} - -.pane-layout-last { - border-right: none; -} - -.pane-splitter { - position: relative; - width: 1px; - margin: 0 -4px; - padding: 0 4px; - box-sizing: content-box; - cursor: col-resize; - touch-action: none; - z-index: 2; - background: transparent; -} - -.pane-splitter::after { - content: ""; - position: absolute; - top: 0; - bottom: 0; - left: 4px; - width: 1px; - background: var(--border); - transition: background 0.15s ease, width 0.15s ease; -} - -.pane-splitter:hover::after, -.pane-splitter:focus-visible::after, -.pane-splitter.is-dragging::after { - width: 3px; - background: var(--accent); -} - -.pane-splitter[hidden] { - display: none !important; -} - -body.pane-drag-active { - cursor: col-resize; - user-select: none; -} - -body.pane-drag-active .pane-splitter { - cursor: col-resize; -} - -.pane-file, -.pane-page-view { - min-width: 0; -} - -.pane-markup, -.pane-reading { - min-width: 0; -} - -.pane-header { - display: flex; - align-items: center; - gap: 0.5rem; - box-sizing: border-box; - height: 2.125rem; - padding: 0 0.75rem; - font-size: 0.75rem; - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--muted); - border-bottom: 1px solid var(--border); - background: var(--panel); -} - -.pane-body { - flex: 1; - min-height: 0; - overflow: auto; - padding: 1rem; -} - -#markup-pane { - padding: 0; - background: var(--markup-bg); - color: var(--markup-fg); -} - -#markup-pane .placeholder { - margin: 1rem; -} - -#page-pane { - overflow: hidden; - display: flex; - flex-direction: column; - padding: 0.75rem; - min-height: 0; - min-width: 0; -} - -.page-view-port { - display: flex; - overflow: auto; - flex: 1 1 auto; - width: 100%; - height: 100%; - min-width: 0; - min-height: 0; - scrollbar-gutter: stable; -} - -#page-pane.can-pan { - cursor: grab; -} - -#page-pane.is-panning { - cursor: grabbing; - user-select: none; -} - -#page-pane:focus { - outline: none; -} - -#page-pane:focus-visible { - outline: 2px solid var(--accent); - outline-offset: -2px; -} - -.placeholder { - display: flex; - align-items: center; - justify-content: center; - min-height: 12rem; - color: var(--muted); - font-style: italic; - background: var(--placeholder-bg); - border: 1px dashed var(--border); - border-radius: 0.5rem; - padding: 2rem; - text-align: center; -} - -pre.markup { - margin: 0; - font-family: var(--font-mono); - font-size: 0.8125rem; - line-height: 1.5; - white-space: pre-wrap; - word-break: break-word; -} - -.markup { - margin: 0; - padding: 0.75rem 1rem 1rem; - font-family: var(--font-mono); - font-size: 0.8125rem; - line-height: 1.55; - tab-size: 2; - --markup-indent: 2ch; - --markup-gutter-width: 1rem; -} - -.markup-ghost-tag-part { - opacity: 0.35; - cursor: help; -} - -.cursor-hint { - position: fixed; - z-index: 200; - width: max-content; - max-width: 16rem; - padding: 0.3rem 0.5rem; - border-radius: 0.25rem; - background: var(--panel); - border: 1px solid var(--border); - color: var(--text); - font-family: var(--font-ui); - font-size: 0.75rem; - line-height: 1.35; - text-align: center; - white-space: normal; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12); - pointer-events: none; -} - -.cursor-hint.cursor-hint-detail { - max-width: 24rem; - padding: 0.45rem 0.55rem; - text-align: left; -} - -.head-tooltip { - border-collapse: collapse; - width: 100%; - font-size: 0.6875rem; - line-height: 1.4; -} - -.head-tooltip th { - padding: 0.1rem 0.55rem 0.1rem 0; - color: var(--muted); - font-weight: 600; - text-align: left; - vertical-align: top; - white-space: nowrap; -} - -.head-tooltip td { - padding: 0.1rem 0; - vertical-align: top; - word-break: break-word; -} - -.head-tooltip .head-default { - color: var(--muted); - font-style: italic; - white-space: nowrap; -} - -.markup-el { - cursor: pointer; - border-radius: 0.2rem; -} - -.markup-el:hover { - background: var(--markup-hover); -} - -.markup-el.selected { - background: var(--markup-selected); - box-shadow: inset 0 0 0 1px var(--accent); -} - -.markup-line { - white-space: pre-wrap; - word-break: break-word; - cursor: pointer; - padding: 0 0.25rem; - border-radius: 0.15rem; -} - -.markup-line-content { - display: flex; - align-items: baseline; - min-width: 0; - padding-left: calc(var(--markup-depth, 0) * var(--markup-indent)); -} - -.markup-gutter { - flex: 0 0 var(--markup-gutter-width); - width: var(--markup-gutter-width); - display: flex; - align-items: center; - justify-content: center; -} - -.markup-line-body { - flex: 1 1 auto; - min-width: 0; -} - -.markup-fold-toggle { - appearance: none; - display: inline-flex; - align-items: center; - justify-content: center; - width: var(--markup-gutter-width); - height: 1rem; - margin: 0; - padding: 0; - border: none; - border-radius: 0.15rem; - background: transparent; - color: var(--muted); - cursor: pointer; - flex-shrink: 0; -} - -.markup-fold-toggle::before { - content: ""; - display: block; - width: 0; - height: 0; - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - border-left: 5px solid currentColor; - transform: rotate(90deg); - transition: transform 0.12s ease; -} - -.markup-el-foldable.markup-collapsed > .markup-line-open .markup-fold-toggle::before { - transform: rotate(0deg); -} - -.markup-fold-toggle:hover { - background: var(--markup-hover); - color: var(--markup-fg); -} - -.markup-fold-suffix { - display: none; -} - -.markup-el-foldable.markup-collapsed > .markup-line-open .markup-fold-suffix { - display: inline; -} - -.markup-el-foldable.markup-collapsed > .markup-fold-body { - display: none; -} - -.xml-tag { - color: var(--xml-tag); -} - -.xml-bracket { - color: var(--xml-bracket); -} - -.xml-attr-name { - color: var(--xml-attr-name); -} - -.xml-attr-value { - color: var(--xml-attr-value); -} - -.xml-attr-value-truncatable { - display: inline; -} - -.xml-attr-value-chip { - appearance: none; - display: inline-flex; - align-items: center; - gap: 0.3em; - margin: 0 0 0 0.35em; - padding: 0.08em 0.45em 0.08em 0.5em; - border: 1px solid var(--border); - border-radius: 0.25rem; - background: var(--placeholder-bg); - color: var(--muted); - font-family: var(--font-ui, system-ui, sans-serif); - font-size: 0.72em; - font-weight: 600; - line-height: 1.35; - cursor: pointer; - vertical-align: baseline; - white-space: nowrap; -} - -.xml-attr-value-chip::after { - content: ""; - width: 0; - height: 0; - border-top: 0.28em solid currentColor; - border-left: 0.22em solid transparent; - border-right: 0.22em solid transparent; - opacity: 0.75; - transition: transform 0.12s ease; -} - -.xml-attr-value-chip[aria-expanded="true"]::after { - transform: rotate(180deg); -} - -.xml-attr-value-chip:hover, -.xml-attr-value-chip[aria-expanded="true"] { - border-color: var(--accent); - color: var(--accent); - background: color-mix(in srgb, var(--accent) 8%, var(--placeholder-bg)); -} - -.markup-embedded-uri-panel { - cursor: text; -} - -.markup-embedded-uri-panel:hover { - background: transparent; -} - -.markup-embedded-uri-panel-body { - margin: 0.1rem 0 0.25rem; - padding: 0.45rem 0.6rem; - max-height: 8rem; - overflow: auto; - color: var(--xml-attr-value); - word-break: break-all; - background: var(--placeholder-bg); - border: 1px solid var(--border); - border-radius: 0.25rem; - user-select: text; -} - -.xml-text { - color: var(--xml-text); -} - -.xml-cdata { - color: var(--xml-cdata); -} - -.xml-cdata-delimiter { - color: var(--xml-cdata-delimiter); -} - -.page-view { - position: relative; - display: block; - width: fit-content; - flex-shrink: 0; - margin: auto; - line-height: 0; - border: 1px solid var(--border); - border-radius: 0.25rem; - overflow: hidden; -} - -.page-view img { - display: block; - width: auto; - height: auto; - max-width: none; - max-height: none; - border: none; - border-radius: 0; -} - -.page-view img:not([data-layout-ready]) { - opacity: 0; -} - -.page-view svg.overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: auto; - overflow: hidden; -} - -.overlay rect.bbox.bbox-hidden, -.overlay .overlay-badge.bbox-hidden { - display: none; -} - -.overlay rect.bbox { - stroke-width: 2; - vector-effect: non-scaling-stroke; - pointer-events: none; -} - -.overlay rect.bbox.selected { - fill: color-mix(in srgb, var(--kind-stroke, var(--accent)) 30%, transparent) !important; - stroke-width: 3.5; - filter: drop-shadow(0 0 3px color-mix(in srgb, var(--kind-stroke, var(--accent)) 60%, transparent)); -} - -.overlay rect.bbox.related:not(.selected) { - fill: color-mix(in srgb, var(--kind-stroke, var(--accent)) 30%, transparent) !important; -} - -.overlay rect.bbox.layer-furniture, -.overlay rect.bbox.layer-background { - fill: url(#layer-hatch) !important; - stroke: color-mix(in srgb, var(--kind-stroke, var(--muted)) 50%, transparent); -} - -.overlay .layer-hatch-stripe { - fill: var(--muted); - fill-opacity: 0.06; -} - -.overlay rect.bbox.layer-furniture.selected, -.overlay rect.bbox.layer-background.selected { - fill: color-mix(in srgb, var(--kind-stroke, var(--accent)) 22%, transparent) !important; -} - -.overlay rect.bbox.layer-furniture.related:not(.selected), -.overlay rect.bbox.layer-background.related:not(.selected) { - fill: color-mix(in srgb, var(--kind-stroke, var(--accent)) 18%, transparent) !important; -} - -/* text */ -.overlay .bbox-text { fill: color-mix(in srgb, var(--kind-text) 14%, transparent); stroke: var(--kind-text); } - -/* heading */ -.overlay .bbox-heading { fill: color-mix(in srgb, var(--kind-heading) 14%, transparent); stroke: var(--kind-heading); } - -/* list / ldiv */ -.overlay .bbox-list, -.overlay .bbox-ldiv { fill: color-mix(in srgb, var(--kind-list) 14%, transparent); stroke: var(--kind-list); } - -/* table */ -.overlay .bbox-table { fill: color-mix(in srgb, var(--kind-table) 14%, transparent); stroke: var(--kind-table); } - -/* index */ -.overlay .bbox-index { fill: color-mix(in srgb, var(--kind-index) 14%, transparent); stroke: var(--kind-index); } - -/* formula */ -.overlay .bbox-formula { fill: color-mix(in srgb, var(--kind-formula) 14%, transparent); stroke: var(--kind-formula); } - -/* code */ -.overlay .bbox-code { fill: color-mix(in srgb, var(--kind-code) 16%, transparent); stroke: var(--kind-code); } - -/* picture */ -.overlay .bbox-picture { fill: color-mix(in srgb, var(--kind-picture) 14%, transparent); stroke: var(--kind-picture); } - -/* group */ -.overlay .bbox-group { fill: color-mix(in srgb, var(--kind-group) 14%, transparent); stroke: var(--kind-group); } - -/* footnote */ -.overlay .bbox-footnote { fill: color-mix(in srgb, var(--kind-footnote) 14%, transparent); stroke: var(--kind-footnote); } - -/* page chrome */ -.overlay .bbox-page_header, -.overlay .bbox-page_footer { fill: color-mix(in srgb, var(--kind-page_header) 14%, transparent); stroke: var(--kind-page_header); } - -/* caption */ -.overlay .bbox-caption { fill: color-mix(in srgb, var(--kind-caption) 14%, transparent); stroke: var(--kind-caption); } - -.overlay line.caption-link { - color: var(--kind-caption); - stroke: currentColor; - stroke-width: 1.5; - vector-effect: non-scaling-stroke; - pointer-events: none; - fill: none; -} - -.overlay line.caption-link.bbox-hidden { - display: none; -} - -.overlay line.xref-link { - color: var(--kind-footnote); - stroke: currentColor; - stroke-width: 1.5; - vector-effect: non-scaling-stroke; - pointer-events: none; - fill: none; -} - -.overlay line.xref-link.bbox-hidden { - display: none; -} - -.overlay .fragment-link { - pointer-events: none; -} - -.overlay .fragment-link.bbox-hidden { - display: none; -} - -.overlay .fragment-link-path { - color: var(--overlay-fragment); - fill: none; - stroke: currentColor; - stroke-width: 1.5; - vector-effect: non-scaling-stroke; -} - -.overlay .fragment-link-path-dashed { - stroke-dasharray: 6 4; -} - -.overlay .fragment-nav { - pointer-events: none; -} - -.overlay .fragment-nav.bbox-hidden { - display: none; -} - -.overlay .fragment-nav-btn { - pointer-events: auto; - cursor: pointer; -} - -.overlay .fragment-nav-btn-disabled { - pointer-events: none; - cursor: default; - opacity: 0.35; -} - -.overlay .fragment-nav-btn-bg { - fill: var(--panel); - stroke: var(--overlay-fragment); - stroke-width: 1; - vector-effect: non-scaling-stroke; -} - -.overlay .fragment-nav-btn-label { - fill: var(--overlay-fragment); - font-family: var(--font-ui); - font-weight: 700; - text-anchor: middle; - dominant-baseline: central; - pointer-events: none; -} - -.overlay .fragment-nav-btn:not(.fragment-nav-btn-disabled):hover .fragment-nav-btn-bg { - fill: color-mix(in srgb, var(--overlay-fragment) 12%, var(--panel)); -} - -.overlay .fragment-link-label { - fill: var(--overlay-fragment); - font-family: var(--font-ui); - font-weight: 600; - font-style: italic; - text-anchor: middle; - dominant-baseline: middle; - paint-order: stroke fill; - stroke: var(--panel); - stroke-width: 3px; -} - -.overlay .reading-order-step { - color: var(--overlay-reading-order); - stroke: currentColor; - stroke-width: 1.5; - stroke-dasharray: 5 4; - vector-effect: non-scaling-stroke; - pointer-events: none; - fill: none; -} - -.overlay .reading-order-step.bbox-hidden, -.overlay .reading-order-badge.bbox-hidden { - display: none; -} - -.overlay .overlay-badge { - pointer-events: none; -} - -.overlay .overlay-badge-bg { - fill-opacity: 1; - stroke: none; -} - -.reading-order-badge .overlay-badge-bg { - fill: var(--overlay-reading-order); -} - -.overlay .element-badge { - opacity: 0.8; - pointer-events: auto; - cursor: help; -} - -.overlay .element-badge .overlay-badge-bg { - fill: var(--kind-stroke, var(--accent)); -} - -.overlay .overlay-badge-label { - fill: #ffffff; - fill-opacity: 1; - font-family: var(--font-ui); - font-weight: 700; - text-anchor: middle; - dominant-baseline: central; - pointer-events: none; -} - -/* form fields */ -.overlay .bbox-field { fill: color-mix(in srgb, var(--kind-field) 14%, transparent); stroke: var(--kind-field); } - -/* fallback */ -.overlay .bbox-default { fill: color-mix(in srgb, var(--kind-default) 12%, transparent); stroke: var(--kind-default); } - -@media (prefers-color-scheme: dark) { - .overlay .bbox-text { fill: color-mix(in srgb, var(--kind-text) 20%, transparent); } - - .overlay .bbox-heading { fill: color-mix(in srgb, var(--kind-heading) 20%, transparent); } - - .overlay .bbox-list, - .overlay .bbox-ldiv { fill: color-mix(in srgb, var(--kind-list) 20%, transparent); } - - .overlay .bbox-table { fill: color-mix(in srgb, var(--kind-table) 18%, transparent); } - - .overlay .bbox-index { fill: color-mix(in srgb, var(--kind-index) 18%, transparent); } - - .overlay .bbox-formula { fill: color-mix(in srgb, var(--kind-formula) 18%, transparent); } - - .overlay .bbox-code { fill: color-mix(in srgb, var(--kind-code) 20%, transparent); } - - .overlay .bbox-picture { fill: color-mix(in srgb, var(--kind-picture) 20%, transparent); } - - .overlay .bbox-group { fill: color-mix(in srgb, var(--kind-group) 20%, transparent); } - - .overlay .bbox-footnote { fill: color-mix(in srgb, var(--kind-footnote) 18%, transparent); } - - .overlay .bbox-page_header, - .overlay .bbox-page_footer { fill: color-mix(in srgb, var(--kind-page_header) 18%, transparent); } - - .overlay .bbox-caption { fill: color-mix(in srgb, var(--kind-caption) 18%, transparent); } - - .overlay .bbox-field { fill: color-mix(in srgb, var(--kind-field) 18%, transparent); } - - .overlay .bbox-default { fill: color-mix(in srgb, var(--kind-default) 18%, transparent); } -} - -.empty-state { - flex: 1 1 0; - display: flex; - align-items: center; - justify-content: center; - min-height: 0; - margin: 1rem; - padding: 2rem 1.5rem; - border: 2px dashed var(--border); - border-radius: 0.75rem; - background: var(--placeholder-bg); - text-align: center; - color: var(--muted); -} - -.empty-state-inner { - max-width: 28rem; -} - -.empty-state-loading { - display: none; - flex-direction: column; - align-items: center; -} - -body.demo-loading .empty-state-loading { - display: flex; -} - -body.demo-loading .empty-state-prompt { - display: none; -} - -.loading-spinner { - width: 2rem; - height: 2rem; - margin: 0 auto 1rem; - border: 2px solid var(--border); - border-top-color: var(--accent); - border-radius: 50%; - animation: loading-spin 0.7s linear infinite; -} - -@keyframes loading-spin { - to { - transform: rotate(360deg); - } -} - -.empty-state-title { - margin: 0 0 0.75rem; - font-size: 1.05rem; - color: var(--text); -} - -.empty-state p { - margin: 0.5rem 0; -} - -.empty-state-action { - margin: 0 0 0.75rem; -} - -.text-link { - color: var(--accent); - text-decoration: underline; -} - -.text-link:hover { - opacity: 0.85; -} - -.empty-state-meta { - font-size: 0.875rem; -} - -.rendered-doc { - max-width: 42rem; - margin: 0 auto; - line-height: 1.6; -} - -.rendered-el-virtual-text > p, -.rendered-el-virtual-text > div { - margin: 0; - display: inline; -} - -.rendered-doc li > .rendered-el-virtual-text { - display: inline; -} - -.rendered-doc li > .rendered-el-virtual-text > p, -.rendered-doc li > .rendered-el-virtual-text > div { - display: inline; - margin: 0; -} - -.rendered-table .rendered-el-virtual-text { - display: inline; - margin: 0; -} - -.rendered-table .rendered-el-virtual-text > p, -.rendered-table .rendered-el-virtual-text > div { - display: inline; - margin: 0; -} - -.rendered-el-virtual-text > div > .rendered-el { - display: block; - margin: 0.25rem 0; -} - -.rendered-el { - cursor: pointer; - border-radius: 0.25rem; -} - -.rendered-el:hover { - outline: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); -} - -.rendered-el.selected { - outline: 2px solid var(--accent); - outline-offset: 2px; - background: color-mix(in srgb, var(--accent) 8%, transparent); -} - -.rendered-doc p { - margin: 0 0 0.75rem; -} - -.rendered-doc h1, -.rendered-doc h2, -.rendered-doc h3, -.rendered-doc h4, -.rendered-doc h5, -.rendered-doc h6 { - margin: 1.25rem 0 0.5rem; - line-height: 1.25; -} - -.rendered-doc h1:first-child, -.rendered-doc h2:first-child, -.rendered-doc h3:first-child { - margin-top: 0; -} - -.rendered-doc ul, -.rendered-doc ol { - margin: 0 0 0.75rem; - padding-left: 1.5rem; -} - -.rendered-doc li { - margin: 0.25rem 0; -} - -.rendered-doc li > .rendered-el > p:first-child, -.rendered-doc li > .rendered-el > h1:first-child, -.rendered-doc li > .rendered-el > h2:first-child, -.rendered-doc li > .rendered-el > h3:first-child, -.rendered-doc li > .rendered-el > h4:first-child, -.rendered-doc li > .rendered-el > h5:first-child, -.rendered-doc li > .rendered-el > h6:first-child { - margin-top: 0; -} - -.rendered-marker { - margin-right: 0.35rem; -} - -.rendered-handwriting { - font-family: "Segoe Print", "Bradley Hand", cursive; -} - -.rendered-doc pre { - margin: 0 0 0.75rem; - padding: 0.75rem 1rem; - overflow-x: auto; - font-family: var(--font-mono); - font-size: 0.8125rem; - line-height: 1.5; - background: var(--placeholder-bg); - border: 1px solid var(--border); - border-radius: 0.375rem; -} - -.rendered-doc code { - font-family: var(--font-mono); - font-size: 0.875em; -} - -.rendered-doc pre code { - font-size: inherit; - background: none; - border: none; - padding: 0; -} - -.rendered-code-label { - display: block; - margin-bottom: 0.25rem; - font-size: 0.75rem; - font-weight: 600; - color: var(--muted); - text-transform: uppercase; - letter-spacing: 0.04em; -} - -.rendered-formula { - display: block; - margin: 0 0 0.75rem; - padding: 0.5rem 0.75rem; - font-family: var(--font-mono); - font-size: 0.875rem; - background: var(--placeholder-bg); - border-left: 3px solid var(--border); - overflow-x: auto; -} - -.rendered-formula-inline { - display: inline; - font-family: var(--font-mono); - font-size: 0.875em; -} - -.rendered-doc figure { - margin: 0 0 0.75rem; -} - -.rendered-doc figure img { - max-width: 100%; - height: auto; - border: 1px solid var(--border); - border-radius: 0.25rem; -} - -.rendered-doc figure img.rendered-picture-unavailable { - display: inline-block; - min-width: 3rem; - min-height: 2.5rem; - object-fit: contain; - vertical-align: middle; -} - -.rendered-doc figcaption { - margin-top: 0.35rem; - font-size: 0.875rem; - color: var(--muted); -} - -.rendered-picture-contents { - margin-top: 0.5rem; - font-size: 0.875rem; - border: 1px solid var(--border); - border-radius: 0.375rem; - background: var(--placeholder-bg); -} - -.rendered-picture-contents summary { - padding: 0.4rem 0.65rem; - cursor: pointer; - font-weight: 600; - color: var(--muted); - user-select: none; -} - -.rendered-picture-contents summary:hover { - color: var(--text); -} - -.rendered-picture-contents-body { - padding: 0.5rem 0.75rem 0.75rem; - border-top: 1px solid var(--border); -} - -.rendered-picture-contents-body .rendered-el > p { - margin-bottom: 0.35rem; - font-size: 0.8125rem; -} - -.rendered-table { - width: 100%; - margin: 0 0 0.75rem; - border-collapse: collapse; - font-size: 0.875rem; -} - -.rendered-table caption { - caption-side: top; - margin-bottom: 0.35rem; - font-size: 0.875rem; - color: var(--muted); - text-align: left; -} - -.rendered-table th, -.rendered-table td { - border: 1px solid var(--border); - padding: 0.35rem 0.5rem; - vertical-align: top; - text-align: left; -} - -.rendered-table th { - background: color-mix(in srgb, var(--border) 35%, var(--panel)); - font-weight: 600; -} - -.rendered-table .rendered-table-cell-text { - display: inline; -} - -.rendered-table .rendered-el { - margin: 0; -} - -.rendered-table .rendered-el-virtual-text > p, -.rendered-table .rendered-el-virtual-text > div { - margin: 0; -} - -.rendered-table .rendered-table { - margin: 0.25rem 0 0; -} - -.rendered-page-header, -.rendered-page-footer { - font-size: 0.875rem; - color: var(--muted); -} - -.rendered-page-header { - margin-bottom: 1rem; - padding-bottom: 0.5rem; - border-bottom: 1px solid var(--border); -} - -.rendered-page-footer { - margin-top: 1rem; - padding-top: 0.5rem; - border-top: 1px solid var(--border); -} - -.rendered-doc:not(.show-reading-furniture) .rendered-el[data-doclang-layer="furniture"] { - display: none; -} - -.rendered-doc:not(.show-reading-background) .rendered-el[data-doclang-layer="background"] { - display: none; -} - -.rendered-el[data-doclang-layer="furniture"], -.rendered-el[data-doclang-layer="background"] { - position: relative; -} - -.rendered-el[data-doclang-layer="furniture"]::before, -.rendered-el[data-doclang-layer="background"]::before { - content: ""; - position: absolute; - inset: 0; - border-radius: inherit; - pointer-events: none; - background: repeating-linear-gradient( - -45deg, - transparent 0 10px, - color-mix(in srgb, var(--muted) 6%, transparent) 10px 16px - ); -} - -.rendered-el.rendered-footnote > aside { - font-size: 0.875rem; - color: var(--muted); - border-left: 2px solid var(--border); - padding-left: 0.75rem; - margin: 0 0 0.75rem; -} - -.rendered-unsupported { - margin: 0 0 0.75rem; - padding: 0.5rem 0.75rem; - font-size: 0.8125rem; - font-style: italic; - color: var(--muted); - background: var(--placeholder-bg); - border: 1px dashed var(--border); - border-radius: 0.375rem; -} - -.rendered-field-region { - margin: 0 0 1rem; - padding: 0.75rem 1rem; - border: 1px solid var(--border); - border-radius: 0.5rem; - background: color-mix(in srgb, var(--kind-field) 6%, transparent); -} - -.rendered-field-region > .rendered-field-heading:first-child, -.rendered-field-region > .rendered-el.rendered-field_heading > .rendered-field-heading:first-child { - margin-top: 0; -} - -.rendered-field-item { - margin: 0.5rem 0; -} - -.rendered-field-item > .rendered-el.rendered-field-key, -.rendered-field-key { - font-weight: 600; -} - -.rendered-field-item > .rendered-el.rendered-field-key + .rendered-el, -.rendered-field-item > .rendered-el.rendered-field-key + .rendered-el-virtual-text { - margin-top: 0.15rem; -} - -.rendered-field-value-fillable .rendered-field-fillable-slot { - display: inline-block; - min-width: 6rem; - min-height: 1.25em; - border-bottom: 1px solid color-mix(in srgb, var(--text) 55%, transparent); - vertical-align: bottom; -} - -.rendered-field-hint { - font-size: 0.85em; - color: var(--muted); - font-style: italic; -} - -.rendered-field-heading { - color: var(--text); -} - -.rendered-checkbox { - margin: 0 0.25rem 0 0; - vertical-align: middle; - accent-color: var(--kind-field); -} - -.rendered-checkbox-wrap { - display: inline; -} - -@media (max-width: 1200px) { - body.viewer-loaded #main.layout-stacked .pane { - border-right: none; - border-bottom: 1px solid var(--border); - } - - body.viewer-loaded #main.layout-stacked .pane-layout-last { - border-bottom: none; - } -} diff --git a/viewer.js b/viewer.js index 872111b..a3c32a6 100644 --- a/viewer.js +++ b/viewer.js @@ -1,5446 +1,5461 @@ -/* DocLang Archive Viewer — archive format: github.com/doclang-project/doclang spec.md#doclang-archive-format */ - -const SUPPORTED_FILE_EXTENSIONS = [".dclx", ".dclg"]; -const OPEN_FILE_HINT = `Open a DocLang file (${SUPPORTED_FILE_EXTENSIONS.join(", ")})`; -const VIRTUAL_TEXT_TAG_HINT = "DocLang virtual ; wrapping tags not included in source"; -const FRAGMENT_LINK_LABEL_CROSS_PAGE = "cross-page content"; -const FRAGMENT_LINK_LABEL_SAME_PAGE = "fragmented content"; -const FRAGMENT_NAV_HINT_PREV = "Previous fragment"; -const FRAGMENT_NAV_HINT_NEXT = "Next fragment"; -const DOCLANG_NS = "https://www.doclang.ai/ns/v0"; -const PAGE_IMAGE_RE = /^(\d+)\.(png|jpe?g|webp)$/i; -const NO_MARKUP = "(No markup to be shown.)"; -const NO_IMAGE = "(No page image available.)"; -const FILE_THUMB_PLACEHOLDER_SVG = ``; -const PICTURE_UNAVAILABLE_ALT = "Picture asset not available"; -const INVALID_PICTURE_SRC = "data:image/png;base64,NOT_A_VALID_IMAGE"; -/** Allow small embedded raster data URIs only; remote/blob/other schemes are rejected. */ -const SAFE_DATA_IMAGE_RE = /^data:image\/(png|jpe?g|webp|gif);base64,/i; -const MAX_DATA_IMAGE_URI_LENGTH = 2 * 1024 * 1024; // 2 MiB -const ZIP_MAX_ENTRIES = 5000; -const ZIP_MAX_ENTRY_COMPRESSED_BYTES = 128 * 1024 * 1024; // 128 MiB -const ZIP_MAX_ENTRY_UNCOMPRESSED_BYTES = 128 * 1024 * 1024; // 128 MiB -const ZIP_MAX_TOTAL_UNCOMPRESSED_BYTES = 512 * 1024 * 1024; // 512 MiB -const ZIP_MAX_COMPRESSION_RATIO = 100; // uncompressed / compressed -const LONG_EMBEDDED_URI_PREVIEW_LENGTH = 30; -const HEAD_TAGS = new Set([ - "label", "thread", "xref", "href", "layer", "location", "caption", "description", "summary", "custom", -]); -const SEMANTIC_TAGS = new Set([ - "text", "heading", "footnote", "page_header", "page_footer", "field_region", "list", "table", "index", - "formula", "code", "picture", "marker", "group", "field_heading", "field_item", "key", "value", "hint", - "caption", "page_break", +//#region \0rolldown/runtime.js +var e = Object.defineProperty, t = (t, n) => { + let r = {}; + for (var i in t) e(r, i, { + get: t[i], + enumerable: !0 + }); + return n || e(r, Symbol.toStringTag, { value: "Module" }), r; +}, n = globalThis, r = n.ShadowRoot && (n.ShadyCSS === void 0 || n.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, i = Symbol(), a = /* @__PURE__ */ new WeakMap(), o = class { + constructor(e, t, n) { + if (this._$cssResult$ = !0, n !== i) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); + this.cssText = e, this.t = t; + } + get styleSheet() { + let e = this.o, t = this.t; + if (r && e === void 0) { + let n = t !== void 0 && t.length === 1; + n && (e = a.get(t)), e === void 0 && ((this.o = e = new CSSStyleSheet()).replaceSync(this.cssText), n && a.set(t, e)); + } + return e; + } + toString() { + return this.cssText; + } +}, s = (e) => new o(typeof e == "string" ? e : e + "", void 0, i), c = (e, t) => { + if (r) e.adoptedStyleSheets = t.map((e) => e instanceof CSSStyleSheet ? e : e.styleSheet); + else for (let r of t) { + let t = document.createElement("style"), i = n.litNonce; + i !== void 0 && t.setAttribute("nonce", i), t.textContent = r.cssText, e.appendChild(t); + } +}, l = r ? (e) => e : (e) => e instanceof CSSStyleSheet ? ((e) => { + let t = ""; + for (let n of e.cssRules) t += n.cssText; + return s(t); +})(e) : e, { is: u, defineProperty: d, getOwnPropertyDescriptor: f, getOwnPropertyNames: p, getOwnPropertySymbols: m, getPrototypeOf: ee } = Object, h = globalThis, g = h.trustedTypes, te = g ? g.emptyScript : "", ne = h.reactiveElementPolyfillSupport, re = (e, t) => e, ie = { + toAttribute(e, t) { + switch (t) { + case Boolean: + e = e ? te : null; + break; + case Object: + case Array: e = e == null ? e : JSON.stringify(e); + } + return e; + }, + fromAttribute(e, t) { + let n = e; + switch (t) { + case Boolean: + n = e !== null; + break; + case Number: + n = e === null ? null : Number(e); + break; + case Object: + case Array: try { + n = JSON.parse(e); + } catch { + n = null; + } + } + return n; + } +}, ae = (e, t) => !u(e, t), oe = { + attribute: !0, + type: String, + converter: ie, + reflect: !1, + useDefault: !1, + hasChanged: ae +}; +Symbol.metadata ??= Symbol("metadata"), h.litPropertyMetadata ??= /* @__PURE__ */ new WeakMap(); +var se = class extends HTMLElement { + static addInitializer(e) { + this._$Ei(), (this.l ??= []).push(e); + } + static get observedAttributes() { + return this.finalize(), this._$Eh && [...this._$Eh.keys()]; + } + static createProperty(e, t = oe) { + if (t.state && (t.attribute = !1), this._$Ei(), this.prototype.hasOwnProperty(e) && ((t = Object.create(t)).wrapped = !0), this.elementProperties.set(e, t), !t.noAccessor) { + let n = Symbol(), r = this.getPropertyDescriptor(e, n, t); + r !== void 0 && d(this.prototype, e, r); + } + } + static getPropertyDescriptor(e, t, n) { + let { get: r, set: i } = f(this.prototype, e) ?? { + get() { + return this[t]; + }, + set(e) { + this[t] = e; + } + }; + return { + get: r, + set(t) { + let a = r?.call(this); + i?.call(this, t), this.requestUpdate(e, a, n); + }, + configurable: !0, + enumerable: !0 + }; + } + static getPropertyOptions(e) { + return this.elementProperties.get(e) ?? oe; + } + static _$Ei() { + if (this.hasOwnProperty(re("elementProperties"))) return; + let e = ee(this); + e.finalize(), e.l !== void 0 && (this.l = [...e.l]), this.elementProperties = new Map(e.elementProperties); + } + static finalize() { + if (this.hasOwnProperty(re("finalized"))) return; + if (this.finalized = !0, this._$Ei(), this.hasOwnProperty(re("properties"))) { + let e = this.properties, t = [...p(e), ...m(e)]; + for (let n of t) this.createProperty(n, e[n]); + } + let e = this[Symbol.metadata]; + if (e !== null) { + let t = litPropertyMetadata.get(e); + if (t !== void 0) for (let [e, n] of t) this.elementProperties.set(e, n); + } + this._$Eh = /* @__PURE__ */ new Map(); + for (let [e, t] of this.elementProperties) { + let n = this._$Eu(e, t); + n !== void 0 && this._$Eh.set(n, e); + } + this.elementStyles = this.finalizeStyles(this.styles); + } + static finalizeStyles(e) { + let t = []; + if (Array.isArray(e)) { + let n = new Set(e.flat(1 / 0).reverse()); + for (let e of n) t.unshift(l(e)); + } else e !== void 0 && t.push(l(e)); + return t; + } + static _$Eu(e, t) { + let n = t.attribute; + return !1 === n ? void 0 : typeof n == "string" ? n : typeof e == "string" ? e.toLowerCase() : void 0; + } + constructor() { + super(), this._$Ep = void 0, this.isUpdatePending = !1, this.hasUpdated = !1, this._$Em = null, this._$Ev(); + } + _$Ev() { + this._$ES = new Promise((e) => this.enableUpdating = e), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), this.constructor.l?.forEach((e) => e(this)); + } + addController(e) { + (this._$EO ??= /* @__PURE__ */ new Set()).add(e), this.renderRoot !== void 0 && this.isConnected && e.hostConnected?.(); + } + removeController(e) { + this._$EO?.delete(e); + } + _$E_() { + let e = /* @__PURE__ */ new Map(), t = this.constructor.elementProperties; + for (let n of t.keys()) this.hasOwnProperty(n) && (e.set(n, this[n]), delete this[n]); + e.size > 0 && (this._$Ep = e); + } + createRenderRoot() { + let e = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions); + return c(e, this.constructor.elementStyles), e; + } + connectedCallback() { + this.renderRoot ??= this.createRenderRoot(), this.enableUpdating(!0), this._$EO?.forEach((e) => e.hostConnected?.()); + } + enableUpdating(e) {} + disconnectedCallback() { + this._$EO?.forEach((e) => e.hostDisconnected?.()); + } + attributeChangedCallback(e, t, n) { + this._$AK(e, n); + } + _$ET(e, t) { + let n = this.constructor.elementProperties.get(e), r = this.constructor._$Eu(e, n); + if (r !== void 0 && !0 === n.reflect) { + let i = (n.converter?.toAttribute === void 0 ? ie : n.converter).toAttribute(t, n.type); + this._$Em = e, i == null ? this.removeAttribute(r) : this.setAttribute(r, i), this._$Em = null; + } + } + _$AK(e, t) { + let n = this.constructor, r = n._$Eh.get(e); + if (r !== void 0 && this._$Em !== r) { + let e = n.getPropertyOptions(r), i = typeof e.converter == "function" ? { fromAttribute: e.converter } : e.converter?.fromAttribute === void 0 ? ie : e.converter; + this._$Em = r; + let a = i.fromAttribute(t, e.type); + this[r] = a ?? this._$Ej?.get(r) ?? a, this._$Em = null; + } + } + requestUpdate(e, t, n, r = !1, i) { + if (e !== void 0) { + let a = this.constructor; + if (!1 === r && (i = this[e]), n ??= a.getPropertyOptions(e), !((n.hasChanged ?? ae)(i, t) || n.useDefault && n.reflect && i === this._$Ej?.get(e) && !this.hasAttribute(a._$Eu(e, n)))) return; + this.C(e, t, n); + } + !1 === this.isUpdatePending && (this._$ES = this._$EP()); + } + C(e, t, { useDefault: n, reflect: r, wrapped: i }, a) { + n && !(this._$Ej ??= /* @__PURE__ */ new Map()).has(e) && (this._$Ej.set(e, a ?? t ?? this[e]), !0 !== i || a !== void 0) || (this._$AL.has(e) || (this.hasUpdated || n || (t = void 0), this._$AL.set(e, t)), !0 === r && this._$Em !== e && (this._$Eq ??= /* @__PURE__ */ new Set()).add(e)); + } + async _$EP() { + this.isUpdatePending = !0; + try { + await this._$ES; + } catch (e) { + Promise.reject(e); + } + let e = this.scheduleUpdate(); + return e != null && await e, !this.isUpdatePending; + } + scheduleUpdate() { + return this.performUpdate(); + } + performUpdate() { + if (!this.isUpdatePending) return; + if (!this.hasUpdated) { + if (this.renderRoot ??= this.createRenderRoot(), this._$Ep) { + for (let [e, t] of this._$Ep) this[e] = t; + this._$Ep = void 0; + } + let e = this.constructor.elementProperties; + if (e.size > 0) for (let [t, n] of e) { + let { wrapped: e } = n, r = this[t]; + !0 !== e || this._$AL.has(t) || r === void 0 || this.C(t, void 0, n, r); + } + } + let e = !1, t = this._$AL; + try { + e = this.shouldUpdate(t), e ? (this.willUpdate(t), this._$EO?.forEach((e) => e.hostUpdate?.()), this.update(t)) : this._$EM(); + } catch (t) { + throw e = !1, this._$EM(), t; + } + e && this._$AE(t); + } + willUpdate(e) {} + _$AE(e) { + this._$EO?.forEach((e) => e.hostUpdated?.()), this.hasUpdated || (this.hasUpdated = !0, this.firstUpdated(e)), this.updated(e); + } + _$EM() { + this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = !1; + } + get updateComplete() { + return this.getUpdateComplete(); + } + getUpdateComplete() { + return this._$ES; + } + shouldUpdate(e) { + return !0; + } + update(e) { + this._$Eq &&= this._$Eq.forEach((e) => this._$ET(e, this[e])), this._$EM(); + } + updated(e) {} + firstUpdated(e) {} +}; +se.elementStyles = [], se.shadowRootOptions = { mode: "open" }, se[re("elementProperties")] = /* @__PURE__ */ new Map(), se[re("finalized")] = /* @__PURE__ */ new Map(), ne?.({ ReactiveElement: se }), (h.reactiveElementVersions ??= []).push("2.1.2"); +//#endregion +//#region node_modules/lit-html/lit-html.js +var ce = globalThis, le = (e) => e, ue = ce.trustedTypes, de = ue ? ue.createPolicy("lit-html", { createHTML: (e) => e }) : void 0, fe = "$lit$", _ = `lit$${Math.random().toFixed(9).slice(2)}$`, pe = "?" + _, me = `<${pe}>`, v = document, he = () => v.createComment(""), ge = (e) => e === null || typeof e != "object" && typeof e != "function", _e = Array.isArray, ve = (e) => _e(e) || typeof e?.[Symbol.iterator] == "function", ye = "[ \n\f\r]", be = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, xe = /-->/g, Se = />/g, y = RegExp(`>|${ye}(?:([^\\s"'>=/]+)(${ye}*=${ye}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`, "g"), Ce = /'/g, we = /"/g, Te = /^(?:script|style|textarea|title)$/i, Ee = (e) => (t, ...n) => ({ + _$litType$: e, + strings: t, + values: n +}), b = Ee(1), x = Ee(2), S = Symbol.for("lit-noChange"), C = Symbol.for("lit-nothing"), De = /* @__PURE__ */ new WeakMap(), w = v.createTreeWalker(v, 129); +function Oe(e, t) { + if (!_e(e) || !e.hasOwnProperty("raw")) throw Error("invalid template strings array"); + return de === void 0 ? t : de.createHTML(t); +} +var ke = (e, t) => { + let n = e.length - 1, r = [], i, a = t === 2 ? "" : t === 3 ? "" : "", o = be; + for (let t = 0; t < n; t++) { + let n = e[t], s, c, l = -1, u = 0; + for (; u < n.length && (o.lastIndex = u, c = o.exec(n), c !== null);) u = o.lastIndex, o === be ? c[1] === "!--" ? o = xe : c[1] === void 0 ? c[2] === void 0 ? c[3] !== void 0 && (o = y) : (Te.test(c[2]) && (i = RegExp("" ? (o = i ?? be, l = -1) : c[1] === void 0 ? l = -2 : (l = o.lastIndex - c[2].length, s = c[1], o = c[3] === void 0 ? y : c[3] === "\"" ? we : Ce) : o === we || o === Ce ? o = y : o === xe || o === Se ? o = be : (o = y, i = void 0); + let d = o === y && e[t + 1].startsWith("/>") ? " " : ""; + a += o === be ? n + me : l >= 0 ? (r.push(s), n.slice(0, l) + fe + n.slice(l) + _ + d) : n + _ + (l === -2 ? t : d); + } + return [Oe(e, a + (e[n] || "") + (t === 2 ? "" : t === 3 ? "" : "")), r]; +}, Ae = class e { + constructor({ strings: t, _$litType$: n }, r) { + let i; + this.parts = []; + let a = 0, o = 0, s = t.length - 1, c = this.parts, [l, u] = ke(t, n); + if (this.el = e.createElement(l, r), w.currentNode = this.el.content, n === 2 || n === 3) { + let e = this.el.content.firstChild; + e.replaceWith(...e.childNodes); + } + for (; (i = w.nextNode()) !== null && c.length < s;) { + if (i.nodeType === 1) { + if (i.hasAttributes()) for (let e of i.getAttributeNames()) if (e.endsWith(fe)) { + let t = u[o++], n = i.getAttribute(e).split(_), r = /([.?@])?(.*)/.exec(t); + c.push({ + type: 1, + index: a, + name: r[2], + strings: n, + ctor: r[1] === "." ? Fe : r[1] === "?" ? Ie : r[1] === "@" ? Le : Pe + }), i.removeAttribute(e); + } else e.startsWith(_) && (c.push({ + type: 6, + index: a + }), i.removeAttribute(e)); + if (Te.test(i.tagName)) { + let e = i.textContent.split(_), t = e.length - 1; + if (t > 0) { + i.textContent = ue ? ue.emptyScript : ""; + for (let n = 0; n < t; n++) i.append(e[n], he()), w.nextNode(), c.push({ + type: 2, + index: ++a + }); + i.append(e[t], he()); + } + } + } else if (i.nodeType === 8) { + if (i.data === pe) c.push({ + type: 2, + index: a + }); + else { + let e = -1; + for (; (e = i.data.indexOf(_, e + 1)) !== -1;) c.push({ + type: 7, + index: a + }), e += _.length - 1; + } + } + a++; + } + } + static createElement(e, t) { + let n = v.createElement("template"); + return n.innerHTML = e, n; + } +}; +function je(e, t, n = e, r) { + if (t === S) return t; + let i = r === void 0 ? n._$Cl : n._$Co?.[r], a = ge(t) ? void 0 : t._$litDirective$; + return i?.constructor !== a && (i?._$AO?.(!1), a === void 0 ? i = void 0 : (i = new a(e), i._$AT(e, n, r)), r === void 0 ? n._$Cl = i : (n._$Co ??= [])[r] = i), i !== void 0 && (t = je(e, i._$AS(e, t.values), i, r)), t; +} +var Me = class { + constructor(e, t) { + this._$AV = [], this._$AN = void 0, this._$AD = e, this._$AM = t; + } + get parentNode() { + return this._$AM.parentNode; + } + get _$AU() { + return this._$AM._$AU; + } + u(e) { + let { el: { content: t }, parts: n } = this._$AD, r = (e?.creationScope ?? v).importNode(t, !0); + w.currentNode = r; + let i = w.nextNode(), a = 0, o = 0, s = n[0]; + for (; s !== void 0;) { + if (a === s.index) { + let t; + s.type === 2 ? t = new Ne(i, i.nextSibling, this, e) : s.type === 1 ? t = new s.ctor(i, s.name, s.strings, this, e) : s.type === 6 && (t = new Re(i, this, e)), this._$AV.push(t), s = n[++o]; + } + a !== s?.index && (i = w.nextNode(), a++); + } + return w.currentNode = v, r; + } + p(e) { + let t = 0; + for (let n of this._$AV) n !== void 0 && (n.strings === void 0 ? n._$AI(e[t]) : (n._$AI(e, n, t), t += n.strings.length - 2)), t++; + } +}, Ne = class e { + get _$AU() { + return this._$AM?._$AU ?? this._$Cv; + } + constructor(e, t, n, r) { + this.type = 2, this._$AH = C, this._$AN = void 0, this._$AA = e, this._$AB = t, this._$AM = n, this.options = r, this._$Cv = r?.isConnected ?? !0; + } + get parentNode() { + let e = this._$AA.parentNode, t = this._$AM; + return t !== void 0 && e?.nodeType === 11 && (e = t.parentNode), e; + } + get startNode() { + return this._$AA; + } + get endNode() { + return this._$AB; + } + _$AI(e, t = this) { + e = je(this, e, t), ge(e) ? e === C || e == null || e === "" ? (this._$AH !== C && this._$AR(), this._$AH = C) : e !== this._$AH && e !== S && this._(e) : e._$litType$ === void 0 ? e.nodeType === void 0 ? ve(e) ? this.k(e) : this._(e) : this.T(e) : this.$(e); + } + O(e) { + return this._$AA.parentNode.insertBefore(e, this._$AB); + } + T(e) { + this._$AH !== e && (this._$AR(), this._$AH = this.O(e)); + } + _(e) { + this._$AH !== C && ge(this._$AH) ? this._$AA.nextSibling.data = e : this.T(v.createTextNode(e)), this._$AH = e; + } + $(e) { + let { values: t, _$litType$: n } = e, r = typeof n == "number" ? this._$AC(e) : (n.el === void 0 && (n.el = Ae.createElement(Oe(n.h, n.h[0]), this.options)), n); + if (this._$AH?._$AD === r) this._$AH.p(t); + else { + let e = new Me(r, this), n = e.u(this.options); + e.p(t), this.T(n), this._$AH = e; + } + } + _$AC(e) { + let t = De.get(e.strings); + return t === void 0 && De.set(e.strings, t = new Ae(e)), t; + } + k(t) { + _e(this._$AH) || (this._$AH = [], this._$AR()); + let n = this._$AH, r, i = 0; + for (let a of t) i === n.length ? n.push(r = new e(this.O(he()), this.O(he()), this, this.options)) : r = n[i], r._$AI(a), i++; + i < n.length && (this._$AR(r && r._$AB.nextSibling, i), n.length = i); + } + _$AR(e = this._$AA.nextSibling, t) { + for (this._$AP?.(!1, !0, t); e !== this._$AB;) { + let t = le(e).nextSibling; + le(e).remove(), e = t; + } + } + setConnected(e) { + this._$AM === void 0 && (this._$Cv = e, this._$AP?.(e)); + } +}, Pe = class { + get tagName() { + return this.element.tagName; + } + get _$AU() { + return this._$AM._$AU; + } + constructor(e, t, n, r, i) { + this.type = 1, this._$AH = C, this._$AN = void 0, this.element = e, this.name = t, this._$AM = r, this.options = i, n.length > 2 || n[0] !== "" || n[1] !== "" ? (this._$AH = Array(n.length - 1).fill(/* @__PURE__ */ new String()), this.strings = n) : this._$AH = C; + } + _$AI(e, t = this, n, r) { + let i = this.strings, a = !1; + if (i === void 0) e = je(this, e, t, 0), a = !ge(e) || e !== this._$AH && e !== S, a && (this._$AH = e); + else { + let r = e, o, s; + for (e = i[0], o = 0; o < i.length - 1; o++) s = je(this, r[n + o], t, o), s === S && (s = this._$AH[o]), a ||= !ge(s) || s !== this._$AH[o], s === C ? e = C : e !== C && (e += (s ?? "") + i[o + 1]), this._$AH[o] = s; + } + a && !r && this.j(e); + } + j(e) { + e === C ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, e ?? ""); + } +}, Fe = class extends Pe { + constructor() { + super(...arguments), this.type = 3; + } + j(e) { + this.element[this.name] = e === C ? void 0 : e; + } +}, Ie = class extends Pe { + constructor() { + super(...arguments), this.type = 4; + } + j(e) { + this.element.toggleAttribute(this.name, !!e && e !== C); + } +}, Le = class extends Pe { + constructor(e, t, n, r, i) { + super(e, t, n, r, i), this.type = 5; + } + _$AI(e, t = this) { + if ((e = je(this, e, t, 0) ?? C) === S) return; + let n = this._$AH, r = e === C && n !== C || e.capture !== n.capture || e.once !== n.once || e.passive !== n.passive, i = e !== C && (n === C || r); + r && this.element.removeEventListener(this.name, this, n), i && this.element.addEventListener(this.name, this, e), this._$AH = e; + } + handleEvent(e) { + typeof this._$AH == "function" ? this._$AH.call(this.options?.host ?? this.element, e) : this._$AH.handleEvent(e); + } +}, Re = class { + constructor(e, t, n) { + this.element = e, this.type = 6, this._$AN = void 0, this._$AM = t, this.options = n; + } + get _$AU() { + return this._$AM._$AU; + } + _$AI(e) { + je(this, e); + } +}, ze = { + M: fe, + P: _, + A: pe, + C: 1, + L: ke, + R: Me, + D: ve, + V: je, + I: Ne, + H: Pe, + N: Ie, + U: Le, + B: Fe, + F: Re +}, Be = ce.litHtmlPolyfillSupport; +Be?.(Ae, Ne), (ce.litHtmlVersions ??= []).push("3.3.3"); +var Ve = (e, t, n) => { + let r = n?.renderBefore ?? t, i = r._$litPart$; + if (i === void 0) { + let e = n?.renderBefore ?? null; + r._$litPart$ = i = new Ne(t.insertBefore(he(), e), e, void 0, n ?? {}); + } + return i._$AI(e), i; +}, He = globalThis, T = class extends se { + constructor() { + super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0; + } + createRenderRoot() { + let e = super.createRenderRoot(); + return this.renderOptions.renderBefore ??= e.firstChild, e; + } + update(e) { + let t = this.render(); + this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(e), this._$Do = Ve(t, this.renderRoot, this.renderOptions); + } + connectedCallback() { + super.connectedCallback(), this._$Do?.setConnected(!0); + } + disconnectedCallback() { + super.disconnectedCallback(), this._$Do?.setConnected(!1); + } + render() { + return S; + } +}; +T._$litElement$ = !0, T.finalized = !0, He.litElementHydrateSupport?.({ LitElement: T }); +var Ue = He.litElementPolyfillSupport; +Ue?.({ LitElement: T }), (He.litElementVersions ??= []).push("4.2.2"); +//#endregion +//#region node_modules/@lit/reactive-element/decorators/custom-element.js +var E = (e) => (t, n) => { + n === void 0 ? customElements.define(e, t) : n.addInitializer(() => { + customElements.define(e, t); + }); +}, We = { + attribute: !0, + type: String, + converter: ie, + reflect: !1, + hasChanged: ae +}, Ge = (e = We, t, n) => { + let { kind: r, metadata: i } = n, a = globalThis.litPropertyMetadata.get(i); + if (a === void 0 && globalThis.litPropertyMetadata.set(i, a = /* @__PURE__ */ new Map()), r === "setter" && ((e = Object.create(e)).wrapped = !0), a.set(n.name, e), r === "accessor") { + let { name: r } = n; + return { + set(n) { + let i = t.get.call(this); + t.set.call(this, n), this.requestUpdate(r, i, e, !0, n); + }, + init(t) { + return t !== void 0 && this.C(r, void 0, e, t), t; + } + }; + } + if (r === "setter") { + let { name: r } = n; + return function(n) { + let i = this[r]; + t.call(this, n), this.requestUpdate(r, i, e, !0, n); + }; + } + throw Error("Unsupported decorator location: " + r); +}; +function D(e) { + return (t, n) => typeof n == "object" ? Ge(e, t, n) : ((e, t, n) => { + let r = t.hasOwnProperty(n); + return t.constructor.createProperty(n, e), r ? Object.getOwnPropertyDescriptor(t, n) : void 0; + })(e, t, n); +} +//#endregion +//#region node_modules/@lit/reactive-element/decorators/state.js +function O(e) { + return D({ + ...e, + state: !0, + attribute: !1 + }); +} +//#endregion +//#region node_modules/lit-html/directive-helpers.js +var { I: Ke } = ze, qe = (e) => e, Je = (e) => e.strings === void 0, Ye = () => document.createComment(""), Xe = (e, t, n) => { + let r = e._$AA.parentNode, i = t === void 0 ? e._$AB : t._$AA; + if (n === void 0) n = new Ke(r.insertBefore(Ye(), i), r.insertBefore(Ye(), i), e, e.options); + else { + let t = n._$AB.nextSibling, a = n._$AM, o = a !== e; + if (o) { + let t; + n._$AQ?.(e), n._$AM = e, n._$AP !== void 0 && (t = e._$AU) !== a._$AU && n._$AP(t); + } + if (t !== i || o) { + let e = n._$AA; + for (; e !== t;) { + let t = qe(e).nextSibling; + qe(r).insertBefore(e, i), e = t; + } + } + } + return n; +}, k = (e, t, n = e) => (e._$AI(t, n), e), Ze = {}, Qe = (e, t = Ze) => e._$AH = t, $e = (e) => e._$AH, et = (e) => { + e._$AR(), e._$AA.remove(); +}, tt = { + ATTRIBUTE: 1, + CHILD: 2, + PROPERTY: 3, + BOOLEAN_ATTRIBUTE: 4, + EVENT: 5, + ELEMENT: 6 +}, nt = (e) => (...t) => ({ + _$litDirective$: e, + values: t +}), rt = class { + constructor(e) {} + get _$AU() { + return this._$AM._$AU; + } + _$AT(e, t, n) { + this._$Ct = e, this._$AM = t, this._$Ci = n; + } + _$AS(e, t) { + return this.update(e, t); + } + update(e, t) { + return this.render(...t); + } +}, it = (e, t) => { + let n = e._$AN; + if (n === void 0) return !1; + for (let e of n) e._$AO?.(t, !1), it(e, t); + return !0; +}, at = (e) => { + let t, n; + do { + if ((t = e._$AM) === void 0) break; + n = t._$AN, n.delete(e), e = t; + } while (n?.size === 0); +}, ot = (e) => { + for (let t; t = e._$AM; e = t) { + let n = t._$AN; + if (n === void 0) t._$AN = n = /* @__PURE__ */ new Set(); + else if (n.has(e)) break; + n.add(e), lt(t); + } +}; +function st(e) { + this._$AN === void 0 ? this._$AM = e : (at(this), this._$AM = e, ot(this)); +} +function ct(e, t = !1, n = 0) { + let r = this._$AH, i = this._$AN; + if (i !== void 0 && i.size !== 0) { + if (t) { + if (Array.isArray(r)) for (let e = n; e < r.length; e++) it(r[e], !1), at(r[e]); + else r != null && (it(r, !1), at(r)); + } else it(this, e); + } +} +var lt = (e) => { + e.type == tt.CHILD && (e._$AP ??= ct, e._$AQ ??= st); +}, ut = class extends rt { + constructor() { + super(...arguments), this._$AN = void 0; + } + _$AT(e, t, n) { + super._$AT(e, t, n), ot(this), this.isConnected = e._$AU; + } + _$AO(e, t = !0) { + e !== this.isConnected && (this.isConnected = e, e ? this.reconnected?.() : this.disconnected?.()), t && (it(this, e), at(this)); + } + setValue(e) { + if (Je(this._$Ct)) this._$Ct._$AI(e, this); + else { + let t = [...this._$Ct._$AH]; + t[this._$Ci] = e, this._$Ct._$AI(t, this, 0); + } + } + disconnected() {} + reconnected() {} +}, A = () => new dt(), dt = class {}, ft = /* @__PURE__ */ new WeakMap(), j = nt(class extends ut { + render(e) { + return C; + } + update(e, [t]) { + let n = t !== this.G; + return n && this.rt(void 0), (n || this.lt !== this.ct) && (this.G = t, this.ht = e.options?.host, this.rt(this.ct = e.element)), C; + } + rt(e) { + if (this.G !== void 0) { + if (this.isConnected || (e = void 0), typeof this.G == "function") { + let t = this.ht ?? globalThis, n = ft.get(t); + n === void 0 && (n = /* @__PURE__ */ new WeakMap(), ft.set(t, n)), n.get(this.G) !== void 0 && this.G.call(this.ht, void 0), n.set(this.G, e), e !== void 0 && this.G.call(this.ht, e); + } else this.G.value = e; + } + } + get lt() { + return typeof this.G == "function" ? ft.get(this.ht ?? globalThis)?.get(this.G) : this.G?.value; + } + disconnected() { + this.lt === this.ct && this.rt(void 0); + } + reconnected() { + this.rt(this.ct); + } +}), pt = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{display:contents}nav{align-items:center;gap:.75rem;margin-left:.25rem;display:flex}nav:before{content:\"\";background:var(--border);flex-shrink:0;align-self:center;width:1px;height:1.125rem;margin-right:.75rem}nav[hidden]{display:none}.page-nav-btns{align-items:center;gap:.25rem;display:inline-flex}.page-nav-btn{appearance:none;border:1px solid var(--border);background:var(--panel);width:1.875rem;height:1.875rem;color:var(--text);font:inherit;cursor:pointer;border-radius:.375rem;flex-shrink:0;justify-content:center;align-items:center;padding:0;font-size:.875rem;display:inline-flex}.page-nav-btn:hover{border-color:var(--accent)}.page-nav-btn:disabled{opacity:.45;cursor:not-allowed}.page-nav-chevron{width:.875rem;height:.875rem;display:block}.page-indicator{color:var(--muted);font-variant-numeric:tabular-nums;align-items:center;font-size:.875rem;display:inline-flex}.page-number-input{appearance:none;border:1px solid var(--border);background:var(--panel);color:var(--text);font:inherit;font-variant-numeric:tabular-nums;text-align:center;box-sizing:border-box;height:1.875rem;width:calc(var(--doclang-page-num-digits,1) * 1ch + .75rem);cursor:text;border-radius:.375rem;margin:0;padding:0 .35rem;font-size:.875rem;line-height:1}.page-number-input:hover:not(:disabled){border-color:var(--accent)}.page-number-input:focus{border-color:var(--accent);box-shadow:0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent);outline:none}.page-number-input:disabled{opacity:.55;cursor:not-allowed}", M = /* @__PURE__ */ new Set([ + "label", + "thread", + "xref", + "href", + "layer", + "location", + "caption", + "description", + "summary", + "custom" +]), mt = /* @__PURE__ */ new Set([ + "text", + "heading", + "footnote", + "page_header", + "page_footer", + "field_region", + "list", + "table", + "index", + "formula", + "code", + "picture", + "marker", + "group", + "field_heading", + "field_item", + "key", + "value", + "hint", + "caption", + "page_break" +]), ht = /* @__PURE__ */ new Set([ + "fcel", + "ecel", + "ched", + "rhed", + "corn", + "srow", + "lcel", + "ucel", + "xcel", + "nl" +]), gt = /* @__PURE__ */ new Set([ + "fcel", + "ecel", + "ched", + "rhed", + "corn", + "srow" +]), _t = /* @__PURE__ */ new Set([ + "lcel", + "ucel", + "xcel" +]), N = /* @__PURE__ */ new Set([ + "table", + "index", + "tabular" ]); -const CELL_TOKENS = new Set(["fcel", "ecel", "ched", "rhed", "corn", "srow", "lcel", "ucel", "xcel", "nl"]); -const CELL_CONTENT_TAGS = new Set(["fcel", "ecel", "ched", "rhed", "corn", "srow"]); -const CELL_SPAN_TAGS = new Set(["lcel", "ucel", "xcel"]); -const OTSL_CONTAINER_TAGS = new Set(["table", "index", "tabular"]); -const RENDER_BLOCK_TAGS = new Set([ - "text", "heading", "field_heading", "footnote", "page_header", "page_footer", "list", "code", "formula", "picture", "group", - "field_region", "field_item", - "table", "index", "tabular", +function P(e) { + return e.nodeType === Node.TEXT_NODE || e.nodeType === Node.CDATA_SECTION_NODE; +} +function F(e) { + return P(e) && !e.textContent?.trim(); +} +function vt(e) { + return [...e.attributes].filter((e) => e.name !== "xmlns" || e.value !== "https://www.doclang.ai/ns/v0").map((e) => ({ + name: e.name, + value: e.value + })); +} +function I(e) { + return [...e.children]; +} +function L(e) { + return e.localName || e.tagName.replace(/^.*:/, ""); +} +function yt(e) { + return R([...e.childNodes], 0)?.locs ?? []; +} +function R(e, t) { + let n = [], r = t; + for (; r < e.length;) { + let t = e[r]; + if (!t) { + r += 1; + continue; + } + if (t.nodeType !== Node.ELEMENT_NODE) { + r += 1; + continue; + } + let i = L(t); + if (i === "location") { + if (n.push(t), r += 1, n.length === 4) return { + locs: n, + nextIndex: r + }; + continue; + } + if (n.length) break; + if (M.has(i)) { + r += 1; + continue; + } + break; + } + return n.length === 4 ? { + locs: n, + nextIndex: r + } : null; +} +function z(e, t) { + for (let n = 0; n < e.length; n += 1) { + let r = e[n]; + !r || r.nodeType !== Node.ELEMENT_NODE || (t(r), z(I(r), t)); + } +} +function B(e) { + return ht.has(e); +} +function bt(e) { + let t = L(e); + return t === "list" || N.has(t); +} +function xt(e, t) { + let n = t; + for (; n < e.length;) { + let t = e[n]; + if (!t) { + n += 1; + continue; + } + if (t.nodeType !== Node.ELEMENT_NODE) { + n += 1; + continue; + } + let r = L(t); + if (r === "ldiv" || B(r)) break; + if (M.has(r) || r === "location") { + n += 1; + continue; + } + break; + } + return n; +} +function St(e, t) { + let n = t; + for (; n < e.length;) { + let t = e[n]; + if (t && t.nodeType === Node.ELEMENT_NODE && L(t) === "ldiv") break; + n += 1; + } + return n; +} +function Ct(e, t) { + let n = t; + for (; n < e.length;) { + let t = e[n]; + if (t && t.nodeType === Node.ELEMENT_NODE && B(L(t))) break; + n += 1; + } + return n; +} +function wt(e, t) { + let n = parseInt(e.getAttribute("resolution") ?? String(t), 10); + return Number.isFinite(n) && n > 0 ? n : t; +} +function Tt(e) { + return Math.min(Math.max(parseInt(e.getAttribute("level") ?? "1", 10) || 1, 1), 6); +} +function V(e, t) { + return I(e).find((e) => L(e) === t) ?? null; +} +function Et(e, t) { + let n = e; + for (; n;) { + if (n === t) return !0; + n = n.parentNode; + } + return !1; +} +function Dt(e) { + return e.nodeType === Node.CDATA_SECTION_NODE ? `` : e.textContent?.trim() ?? ""; +} +function Ot(e) { + return Array.from(e).filter((e) => P(e) && !F(e)).map(Dt).join(""); +} +function kt(e) { + return String(e).replace(/&/g, "&").replace(//g, ">"); +} +function At(e) { + return e.replace(/\\/g, "/").replace(/^\.\//, ""); +} +function jt(e) { + let t = At(e), n = t.indexOf("assets/"); + return n === -1 ? null : t.slice(n); +} +function Mt(e) { + switch (e.split(".").pop()?.toLowerCase() ?? "") { + case "png": return "image/png"; + case "jpg": + case "jpeg": return "image/jpeg"; + case "webp": return "image/webp"; + case "gif": return "image/gif"; + case "svg": return "image/svg+xml"; + default: return "application/octet-stream"; + } +} +function Nt(e, t) { + let n = t; + for (; n < e.length;) { + let t = e[n]; + if (!t) { + n += 1; + continue; + } + if (t.nodeType !== Node.ELEMENT_NODE) { + n += 1; + continue; + } + let r = L(t); + if (B(r)) break; + if (M.has(r) || r === "h_thread" || r === "location") { + n += 1; + continue; + } + break; + } + return n; +} +function Pt(e, t) { + let n = t; + for (; n < e.length && F(e[n]);) n += 1; + for (; n < e.length;) { + let t = e[n]; + if (t && t.nodeType === Node.ELEMENT_NODE && M.has(L(t))) { + for (n += 1; n < e.length && F(e[n]);) n += 1; + continue; + } + break; + } + return n; +} +function Ft(e) { + return mt.has(L(e)); +} +function It(e) { + let t = L(e); + return t === "ldiv" || gt.has(t); +} +function Lt(e) { + return V(e, "thread")?.getAttribute("thread_id") ?? null; +} +function Rt(e) { + let t = V(e, "layer"); + if (t) { + let e = t.getAttribute("value") ?? "body"; + return zt.has(e) ? e : "body"; + } + if (yt(e).length === 4) return "body"; + let n = e.parentElement; + if (!n) return "body"; + let r = [...n.childNodes], i = r.indexOf(e); + return i < 0 ? "body" : Bt(r, i + 1); +} +var zt = /* @__PURE__ */ new Set([ + "body", + "background", + "furniture" ]); -const RENDER_FORMAT_TAGS = new Set([ - "bold", "italic", "underline", "strikethrough", "superscript", "subscript", "handwriting", "rtl", "content", +function Bt(e, t) { + let n = t; + for (; n < e.length;) { + let t = e[n]; + if (!t) { + n += 1; + continue; + } + if (t.nodeType !== Node.ELEMENT_NODE) { + n += 1; + continue; + } + let r = L(t); + if (r === "layer") { + let e = t.getAttribute("value") ?? "body"; + return zt.has(e) ? e : "body"; + } + if (r === "location") break; + if (M.has(r)) { + n += 1; + continue; + } + break; + } + return "body"; +} +function Vt(e) { + return e === "furniture" ? "layer-furniture" : e === "background" ? "layer-background" : ""; +} +function Ht(e) { + if (Ut(e)) return "text"; + let t = L(e); + if (t === "heading" || t === "field_heading") return `${t}[${Tt(e)}]`; + let n = e.getAttribute("level"); + if (n) return `${t}[${n}]`; + let r = e.getAttribute("class"); + return r ? `${t}.${r}` : t; +} +function Ut(e) { + if (yt(e).length === 4 || !Wt(e)) return !1; + let t = L(e); + return t === "ldiv" && e.parentElement && L(e.parentElement) === "list" ? !0 : B(t) && t !== "nl" && !_t.has(t) && e.parentElement ? N.has(L(e.parentElement)) : !1; +} +function Wt(e) { + let t = e.parentElement; + if (!t) return !1; + let n = [...t.childNodes], r = n.indexOf(e); + return r < 0 ? !1 : R(n, r + 1) !== null; +} +function Gt(e) { + let t = e.parentElement; + if (!t) return []; + let n = [...t.childNodes], r = n.indexOf(e); + return r < 0 ? [] : R(n, r + 1)?.locs ?? []; +} +function Kt(e) { + let t = yt(e); + return t.length === 4 ? t : Gt(e); +} +function qt(e) { + if (!e) return !1; + let t = L(e); + if (t === "picture" || t === "caption") return !1; + let n = e.parentElement; + for (; n;) { + if (L(n) === "picture") return !0; + n = n.parentElement; + } + return !1; +} +function Jt(e) { + if (!e) return !1; + let t = L(e); + if (N.has(t) || t === "caption") return !1; + let n = e.parentElement; + for (; n;) { + if (N.has(L(n))) return !0; + n = n.parentElement; + } + return !1; +} +//#endregion +//#region src/doclang/zip.ts +var Yt = 5e3, Xt = 134217728, Zt = 134217728, Qt = 536870912, $t = 100, en = new TextDecoder(); +async function tn(e, { shouldExtract: t } = {}) { + if (typeof DecompressionStream > "u") throw Error("ZIP decompression requires a browser with DecompressionStream support"); + let n = new Uint8Array(e), r = new DataView(e), i = nn(n), a = r.getUint16(i + 10, !0), o = r.getUint32(i + 16, !0); + if (a > Yt) throw Error("ZIP archive has too many entries"); + if (o >= n.length) throw Error("Invalid ZIP central directory offset"); + let s = [], c = o, l = 0; + for (let e = 0; e < a; e += 1) { + if (c + 46 > n.length || r.getUint32(c, !0) !== 33639248) throw Error("Invalid ZIP central directory"); + let e = r.getUint16(c + 10, !0), i = r.getUint32(c + 20, !0), a = r.getUint32(c + 24, !0), o = r.getUint16(c + 28, !0), u = r.getUint16(c + 30, !0), d = r.getUint16(c + 32, !0), f = r.getUint32(c + 42, !0), p = c + 46 + o; + if (p > n.length) throw Error("Invalid ZIP entry name"); + let m = on(en.decode(n.subarray(c + 46, p))); + if (c = p + u + d, c > n.length) throw Error("Invalid ZIP central directory"); + if (!m || m.endsWith("/") || cn(m) || t && !t(m)) continue; + if (i > Xt || a > Zt) throw Error(`ZIP entry exceeds size limit: ${m}`); + if (i > 0 && a > 0 && a / i > $t) throw Error(`ZIP entry compression ratio exceeds limit: ${m}`); + if (l + a > Qt) throw Error("ZIP archive exceeds total uncompressed size limit"); + if (f + 30 > n.length || r.getUint32(f, !0) !== 67324752) throw Error(`Invalid ZIP local header: ${m}`); + let ee = r.getUint16(f + 26, !0), h = r.getUint16(f + 28, !0), g = f + 30 + ee + h; + if (g + i > n.length) throw Error(`ZIP entry data out of bounds: ${m}`); + let te = await an(n.subarray(g, g + i), e, a); + if (l += te.length, l > Qt) throw Error("ZIP archive exceeds total uncompressed size limit"); + s.push({ + name: m, + data: te + }); + } + return s; +} +function nn(e) { + for (let t = e.length - 22; t >= 0; --t) if (e[t] === 80 && e[t + 1] === 75 && e[t + 2] === 5 && e[t + 3] === 6) return t; + throw Error("Invalid ZIP archive"); +} +function rn(e, t) { + let n = new Uint8Array(t), r = 0; + for (let t of e) n.set(t, r), r += t.byteLength; + return n; +} +async function an(e, t, n) { + if (t === 0) { + if (e.length > Zt) throw Error("ZIP entry exceeds size limit"); + return e; + } + if (t === 8) { + let t = new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate-raw")).getReader(), r = [], i = 0; + try { + for (;;) { + let { done: n, value: a } = await t.read(); + if (n) break; + if (i += a.byteLength, i > Zt) throw Error("ZIP entry exceeds size limit"); + if (e.length > 0 && i / e.length > $t) throw Error("ZIP entry compression ratio exceeds limit"); + r.push(a); + } + } catch (e) { + try { + await t.cancel(); + } catch {} + throw e; + } + let a = rn(r, i); + return n && a.length !== n ? a.slice(0, Math.min(a.length, n)) : a; + } + throw Error(`Unsupported ZIP compression method ${t}`); +} +function on(e) { + return e.replace(/\\/g, "/").replace(/^\.\//, ""); +} +function sn(e, t) { + return e.find((e) => e.name === t); +} +function cn(e) { + return e === ".DS_Store" || e.endsWith("/.DS_Store") ? !0 : e.split("/").some((e) => e.startsWith("._") || e === "__MACOSX"); +} +async function ln(e) { + let t = await tn(e), n = sn(t, "document.xml"); + if (!n) throw Error("Archive must contain document.xml"); + let r = new TextDecoder().decode(n.data), i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map(); + for (let e of t) { + let t = e.name.match(/^pages\/(\d+)\.(png|jpe?g|webp)$/i); + if (t) { + i.set(Number(t[1]), URL.createObjectURL(new Blob([e.data], { type: Mt(e.name) }))); + continue; + } + e.name.startsWith("assets/") && !e.name.endsWith("/") && a.set(e.name, URL.createObjectURL(new Blob([e.data], { type: Mt(e.name) }))); + } + return { + markupXml: r, + pageImages: i, + assetUrls: a + }; +} +//#endregion +//#region src/doclang/document.ts +var un = /* @__PURE__ */ t({ + NO_MARKUP: () => fn, + PAGE_IMAGE_RE: () => dn, + assignElementIds: () => _n, + buildDocumentState: () => Ln, + buildElementPageMap: () => bn, + buildThreadNavByElement: () => Sn, + buildThreadPagesById: () => xn, + collectBoundingBoxes: () => An, + collectCaptionLinks: () => jn, + collectFragmentLinks: () => Pn, + collectFragmentNavItems: () => Fn, + collectReadingOrderSteps: () => In, + collectXrefLinks: () => Mn, + computeReadingOrder: () => wn, + computeReadingOrderDisplayNumbers: () => En, + extractArchiveFromFiles: () => Rn, + extractArchiveFromZipBuffer: () => ln, + invertElementIds: () => vn, + readDefaultResolution: () => gn, + revokeDocumentState: () => zn, + segmentHasMarkup: () => hn, + splitIntoSegments: () => mn +}), dn = /^(\d+)\.(png|jpe?g|webp)$/i, fn = "(No markup to be shown.)", pn = /* @__PURE__ */ new Set([ + "bold", + "italic", + "underline", + "strikethrough", + "superscript", + "subscript", + "handwriting", + "rtl", + "content" ]); -const FORMAT_HTML_TAG = { - bold: "strong", - italic: "em", - underline: "u", - strikethrough: "s", - superscript: "sup", - subscript: "sub", +function mn(e) { + let t = I(e).filter((e) => L(e) !== "head"), n = [[]]; + for (let e of t) L(e) === "page_break" ? n.push([]) : n[n.length - 1].push(e); + return n.length ? n : [[]]; +} +function hn(e) { + return e.some((e) => e.nodeType === Node.ELEMENT_NODE); +} +function gn(e) { + let t = { + width: 512, + height: 512 + }; + if (!e) return t; + let n = I(e).find((e) => L(e) === "default_resolution"); + if (!n) return t; + let r = parseInt(n.getAttribute("width") ?? "512", 10), i = parseInt(n.getAttribute("height") ?? "512", 10); + return { + width: Number.isFinite(r) && r > 0 ? r : 512, + height: Number.isFinite(i) && i > 0 ? i : 512 + }; +} +function _n(e) { + let t = /* @__PURE__ */ new Map(), n = 0; + return z(e, (e) => { + t.set(e, `el-${n++}`); + }), t; +} +function vn(e) { + let t = /* @__PURE__ */ new Map(); + for (let [n, r] of e) t.set(r, n); + return t; +} +function yn(e) { + let t = /* @__PURE__ */ new Map(); + return z(I(e).filter((e) => L(e) !== "head"), (e) => { + for (let n of I(e)) { + if (L(n) !== "thread") continue; + let r = n.getAttribute("thread_id"); + r && (t.has(r) || t.set(r, []), t.get(r).push(e)); + } + }), t; +} +function bn(e) { + let t = /* @__PURE__ */ new Map(); + return e.forEach((e, n) => { + let r = n + 1; + z(e, (e) => t.set(e, r)); + }), t; +} +function xn(e, t) { + let n = /* @__PURE__ */ new Map(); + for (let [r, i] of yn(e)) { + let e = /* @__PURE__ */ new Set(); + for (let n of i) { + let r = t.get(n); + r && e.add(r); + } + e.size && n.set(r, e); + } + return n; +} +function Sn(e) { + let t = /* @__PURE__ */ new Map(); + for (let [, n] of yn(e)) for (let e = 0; e < n.length; e += 1) t.set(n[e], { + prev: e > 0 ? n[e - 1] : null, + next: e < n.length - 1 ? n[e + 1] : null + }); + return t; +} +function Cn(e) { + let t = L(e); + return t === "caption" || !(M.has(t) || t === "location" || t === "h_thread" || t === "page_break" || t === "nl" || _t.has(t) || pn.has(t) || t === "src" || t === "checkbox"); +} +function wn(e) { + let t = I(e).filter((e) => L(e) !== "head"), n = yn(e), r = /* @__PURE__ */ new Set(), i = []; + function a(e) { + Cn(e) && i.push(e); + } + function o(e) { + for (let t of n.get(e) ?? []) r.has(t) || (r.add(t), c(t)); + } + function s(e) { + for (let t of e.childNodes) { + if (t.nodeType !== Node.ELEMENT_NODE) continue; + let e = t, n = L(e); + if (n === "xref") { + let t = e.getAttribute("thread_id"); + t && o(t); + continue; + } + n !== "page_break" && c(e); + } + } + function c(e) { + a(e), s(e); + } + for (let e of t) L(e) !== "page_break" && (r.has(e) || c(e)); + return i; +} +function Tn(e) { + return !Cn(e) || qt(e) || Jt(e) ? !1 : yt(e).length === 4 || Ut(e); +} +function En(e) { + let t = /* @__PURE__ */ new Map(), n = 0; + for (let r of e) Tn(r) && (n += 1, t.set(r, n)); + return t; +} +function Dn(e, t, n, r, i, a, o = "body") { + let [s, c, l, u] = t, d = wt(s, n.width), f = wt(c, n.height); + e.push({ + kind: r, + tag: i, + elementId: a, + layer: o, + x0: parseInt(s.getAttribute("value") ?? "0", 10), + y0: parseInt(c.getAttribute("value") ?? "0", 10), + x1: parseInt(l.getAttribute("value") ?? "0", 10), + y1: parseInt(u.getAttribute("value") ?? "0", 10), + resW: d, + resH: f + }); +} +function On(e, t, n, r) { + let i = [...e.childNodes], a = xt(i, 0); + for (; a < i.length;) { + let e = i[a]; + if (e.nodeType !== Node.ELEMENT_NODE || L(e) !== "ldiv") { + a += 1; + continue; + } + a += 1; + let o = R(i, a); + if (o) { + let i = r.get(e); + i && Dn(n, o.locs, t, "text", "text", i, Rt(e)), a = o.nextIndex; + } + a = St(i, a); + } +} +function kn(e, t, n, r) { + let i = [...e.childNodes], a = xt(i, 0); + for (; a < i.length;) { + let e = i[a]; + if (e.nodeType !== Node.ELEMENT_NODE) { + a += 1; + continue; + } + let o = L(e); + if (!B(o)) { + a += 1; + continue; + } + if (o === "nl") { + a += 1; + continue; + } + a += 1; + let s = R(i, a); + if (s) { + let i = r.get(e); + i && Dn(n, s.locs, t, "text", "text", i, Rt(e)), a = s.nextIndex; + } + a = Ct(i, a); + } +} +function An(e, t, n) { + let r = []; + return z(e, (e) => { + let i = yt(e); + if (i.length !== 4) return; + let a = n.get(e); + a && Dn(r, i, t, L(e), Ht(e), a, Rt(e)); + }), z(e, (e) => { + let i = L(e); + i === "list" ? On(e, t, r, n) : N.has(i) && kn(e, t, r, n); + }), r; +} +function jn(e, t, n) { + let r = new Map(n.map((e) => [e.elementId, e])), i = []; + return z(e, (e) => { + if (L(e) !== "caption") return; + let n = t.get(e), a = n ? r.get(n) : null; + if (!a) return; + let o = e.parentElement; + if (!o || yt(o).length !== 4) return; + let s = t.get(o), c = s ? r.get(s) : null; + c && i.push({ + captionBox: a, + hostBox: c, + captionElementId: n, + hostElementId: s + }); + }), i; +} +function Mn(e, t, n) { + let r = new Map(n.map((e) => [e.elementId, e])), i = /* @__PURE__ */ new Map(); + z(e, (e) => { + let n = t.get(e), a = n ? r.get(n) : null; + if (a) for (let t of I(e)) { + if (L(t) !== "thread") continue; + let e = t.getAttribute("thread_id"); + e && (i.has(e) || i.set(e, []), i.get(e).push({ + elementId: n, + box: a + })); + } + }); + let a = []; + return z(e, (e) => { + let n = I(e).filter((e) => L(e) === "xref"); + if (!n.length) return; + let o = Nn(e, t, r); + if (o) for (let e of n) { + let t = e.getAttribute("thread_id"); + if (t) for (let { elementId: e, box: n } of i.get(t) ?? []) e !== o.elementId && a.push({ + fromBox: o.box, + toBox: n, + fromElementId: o.elementId, + toElementId: e + }); + } + }), a; +} +function Nn(e, t, n) { + let r = e; + for (; r;) { + let e = t.get(r), i = e ? n.get(e) : null; + if (i) return { + elementId: e, + box: i + }; + if (L(r) === "doclang") break; + r = r.parentElement; + } + return null; +} +function Pn(e, t, n, r, i) { + let a = new Map(n.map((e) => [e.elementId, e])), o = /* @__PURE__ */ new Map(); + z(e, (e) => { + let n = V(e, "thread")?.getAttribute("thread_id"); + if (!n) return; + let r = t.get(e), i = r ? a.get(r) : null; + i && (o.has(n) || o.set(n, []), o.get(n).push({ + elementId: r, + box: i + })); + }); + let s = []; + for (let [e, t] of o) { + if (t.length >= 2) { + for (let n = 0; n < t.length - 1; n += 1) s.push({ + fromBox: t[n].box, + toBox: t[n + 1].box, + fromElementId: t[n].elementId, + toElementId: t[n + 1].elementId, + threadId: e + }); + continue; + } + if (t.length !== 1) continue; + let n = i.get(e); + if (!n) continue; + let a = [...n].some((e) => e < r), o = [...n].some((e) => e > r); + if (!a && !o) continue; + let c = { + fromBox: t[0].box, + toBox: null, + fromElementId: t[0].elementId, + toElementId: null, + threadId: e + }; + a && s.push({ + ...c, + targetCorner: "tl" + }), o && s.push({ + ...c, + targetCorner: "br" + }); + } + return s; +} +function Fn(e, t, n, r) { + let i = new Map(n.map((e) => [e.elementId, e])), a = []; + return z(e, (e) => { + let n = r.get(e); + if (!n || !n.prev && !n.next) return; + let o = t.get(e), s = o ? i.get(o) : null; + s && a.push({ + elementId: o, + box: s, + hasPrev: n.prev !== null, + hasNext: n.next !== null + }); + }), a; +} +function In(e, t, n, r, i = !0, a = null) { + let o = new Map(n.map((e) => [e.elementId, e])), s = [], c = 0; + return r.forEach((e) => { + if (qt(e) || Jt(e)) return; + let n = t.get(e); + if (!n) return; + let r = o.get(n); + r && (c += 1, s.push({ + order: i ? a?.get(e) ?? c : c, + box: r, + elementId: n + })); + }), s; +} +function Ln(e, t, n, r, { markupOnly: i }) { + let a = new DOMParser().parseFromString(e, "application/xml"); + if (a.querySelector("parsererror")) return alert(`Invalid XML in ${n}`), null; + let o = a.documentElement; + if (L(o) !== "doclang") return alert(`${n}: root element must be `), null; + let s = gn(I(o).find((e) => L(e) === "head") ?? null), c = i ? [I(o).filter((e) => L(e) !== "head")] : mn(o), l = !i && t.size > 0, u = l ? Math.max(...t.keys()) : 0, d = i ? 1 : Math.max(c.length, u, 1), f = wn(o), p = bn(c); + return { + pageImages: t, + assetUrls: r, + pageCount: d, + segments: c, + defaultResolution: s, + elementIds: /* @__PURE__ */ new Map(), + idToElement: /* @__PURE__ */ new Map(), + hasPageView: l, + markupOnly: i, + docRoot: o, + threadPagesById: xn(o, p), + elementPageByEl: p, + threadNavByElement: Sn(o), + pendingSelectElement: null, + readingOrder: f, + readingOrderDisplayNumbers: En(f) + }; +} +async function Rn(e) { + let t = e.find((e) => e.name === "document.xml"); + if (!t) throw Error("Archive must contain document.xml at its root."); + let n = await t.text(), r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map(); + for (let t of e) { + let e = t.webkitRelativePath || t.name, n = e.split("/"); + if (n.length >= 2 && n[n.length - 2] === "pages") { + let e = dn.exec(t.name); + e && r.set(Number(e[1]), URL.createObjectURL(t)); + } + let a = jt(e); + a && i.set(a, URL.createObjectURL(t)); + } + return { + markupXml: n, + pageImages: r, + assetUrls: i + }; +} +function zn(e) { + if (e) { + for (let t of e.pageImages.values()) t.startsWith("blob:") && URL.revokeObjectURL(t); + for (let t of e.assetUrls.values()) t.startsWith("blob:") && URL.revokeObjectURL(t); + } +} +//#endregion +//#region \0@oxc-project+runtime@0.144.0/helpers/esm/decorate.js +function H(e, t, n, r) { + var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o; + if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r); + else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a); + return i > 3 && a && Object.defineProperty(t, n, a), a; +} +//#endregion +//#region src/components/base/page-element.ts +var U = class extends T { + constructor(...e) { + super(...e), this._docState = null, this._peerIds = /* @__PURE__ */ new Set(); + } + get document() { + return this._docState; + } + set document(e) { + let t = this._docState; + this._docState = e, this.requestUpdate("_docState", t), e || (this.page = 1, this._clearDocument()); + } + connectedCallback() { + if (super.connectedCallback(), !this._docState && !this.getAttribute("src")) { + let e = this.querySelector("script[type=\"application/doclang+xml\"]"); + e?.textContent?.trim() && this._loadXmlString(e.textContent.trim(), this.getAttribute("label") ?? ""); + } + } + disconnectedCallback() { + super.disconnectedCallback(); + } + attributeChangedCallback(e, t, n) { + super.attributeChangedCallback(e, t, n), e === "src" && n && this._loadFromUrl(n); + } + updated(e) { + if (super.updated(e), (e.has("page") || e.has("_docState")) && this._docState) { + let e = Math.min(Math.max(1, this.page), this._docState.pageCount); + if (e !== this.page) { + this.page = e; + return; + } + this._renderDocument(); + } + e.has("selected") && (this._peerIds = this.selected ? this._computePeerIds(this.selected) : /* @__PURE__ */ new Set(), this._applySelection()); + } + _applySelection() {} + _clearDocument() {} + _computePeerIds(e) { + let t = /* @__PURE__ */ new Set(); + if (!this._docState?.elementIds || !this._docState.idToElement) return t; + let n = this._docState.idToElement.get(e), r = n ? Lt(n) : null; + if (!r) return t; + for (let [e, n] of this._docState.elementIds) Lt(e) === r && t.add(n); + return t; + } + _loadXmlString(e, t) { + let n = Ln(e, /* @__PURE__ */ new Map(), t, /* @__PURE__ */ new Map(), { markupOnly: !0 }); + n && (this.document = n); + } + async _loadFromUrl(e) { + try { + let t = await fetch(e); + if (!t.ok) throw Error(`HTTP ${t.status}`); + if (e.includes(".dclx") || e.includes(".zip") || t.headers.get("content-type")?.includes("zip")) { + let { extractArchiveFromZipBuffer: n } = await Promise.resolve().then(() => un), { markupXml: r, pageImages: i, assetUrls: a } = await n(await t.arrayBuffer()), o = Ln(r, i, e.split("/").pop() ?? "", a, { markupOnly: !1 }); + o && (this.document = o); + } else { + let n = await t.text(), r = e.split("/").pop() ?? ""; + this._loadXmlString(n, r); + } + } catch (e) { + this.dispatchEvent(new CustomEvent("doclang-load-error", { + bubbles: !0, + composed: !0, + detail: { error: e } + })); + } + } }; - -function isTextLikeNode(node) { - return node.nodeType === Node.TEXT_NODE || node.nodeType === Node.CDATA_SECTION_NODE; -} - -function isWhitespaceOnlyText(node) { - return isTextLikeNode(node) && !node.textContent.trim(); -} - -function markupAttributes(el) { - return [...el.attributes] - .filter((a) => a.name !== "xmlns" || a.value !== DOCLANG_NS) - .map((a) => ({ name: a.name, value: a.value })); -} - -function xmlSpan(className, text, { ghost = false } = {}) { - const span = document.createElement("span"); - span.className = ghost ? `${className} markup-ghost-tag-part` : className; - span.textContent = text; - return span; -} - -function createMarkupLineRow(depth, { foldToggle = false } = {}) { - const line = createMarkupLine(); - line.style.setProperty("--markup-depth", String(depth)); - - const row = document.createElement("span"); - row.className = "markup-line-content"; - - const gutter = document.createElement("span"); - gutter.className = "markup-gutter"; - gutter.setAttribute("aria-hidden", "true"); - if (foldToggle) gutter.appendChild(createMarkupFoldToggle()); - row.appendChild(gutter); - - const body = document.createElement("span"); - body.className = "markup-line-body"; - row.appendChild(body); - - line.appendChild(row); - return { line, content: body }; -} - -function isTruncatableEmbeddedImageUri(value) { - if (!value || value.length <= LONG_EMBEDDED_URI_PREVIEW_LENGTH) return false; - return /^(data:image\/|blob:)/i.test(value); -} - -function formatCompactByteSize(bytes) { - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) { - const kb = bytes / 1024; - return kb < 10 ? `${kb.toFixed(1)} KB` : `${Math.round(kb)} KB`; - } - return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; -} - -function formatEmbeddedUriSizeLabel(value) { - if (/^blob:/i.test(value)) { - return value.length >= 1024 ? `${Math.round(value.length / 1024)} KB URL` : `${value.length} char URL`; - } - const comma = value.indexOf(","); - if (comma === -1) return "embedded data"; - const header = value.slice(0, comma); - const payload = value.slice(comma + 1).replace(/\s/g, ""); - const mime = /^data:([^;,]+)/i.exec(header)?.[1] ?? ""; - const shortMime = mime.startsWith("image/") ? mime.slice(6) : (mime || "data"); - const bytes = Math.floor(payload.replace(/=+$/, "").length * 3 / 4); - return `${shortMime} · ${formatCompactByteSize(bytes)}`; -} - -function createEmbeddedUriContinuationPanel(value, depth) { - const { line, content } = createMarkupLineRow(depth); - line.className = "markup-line markup-embedded-uri-panel"; - const body = document.createElement("div"); - body.className = "markup-embedded-uri-panel-body"; - body.textContent = value; - body.addEventListener("click", (e) => e.stopPropagation()); - content.appendChild(body); - return line; -} - -function createTruncatableMarkupAttrValue(value) { - const wrapper = document.createElement("span"); - wrapper.className = "xml-attr-value xml-attr-value-truncatable"; - wrapper.dataset.fullValue = value; - - const text = document.createElement("span"); - text.className = "xml-attr-value-text"; - text.textContent = value.slice(0, LONG_EMBEDDED_URI_PREVIEW_LENGTH); - - const toggle = document.createElement("button"); - toggle.type = "button"; - toggle.className = "xml-attr-value-chip"; - const sizeLabel = formatEmbeddedUriSizeLabel(value); - toggle.dataset.collapsedLabel = sizeLabel; - toggle.setAttribute("aria-expanded", "false"); - toggle.setAttribute("aria-label", `Show full value (${sizeLabel})`); - - const label = document.createElement("span"); - label.className = "xml-attr-value-chip-label"; - label.textContent = sizeLabel; - toggle.appendChild(label); - - wrapper.append(text, toggle); - return wrapper; -} - -function appendMarkupAttrValue(line, value) { - if (isTruncatableEmbeddedImageUri(value)) { - line.appendChild(createTruncatableMarkupAttrValue(value)); - } else { - line.appendChild(xmlSpan("xml-attr-value", value)); - } -} - -function toggleTruncatableMarkupAttrValue(toggle) { - const wrapper = toggle.closest(".xml-attr-value-truncatable"); - if (!wrapper) return; - - const markupLine = wrapper.closest(".markup-line"); - const fullValue = wrapper.dataset.fullValue ?? ""; - const label = toggle.querySelector(".xml-attr-value-chip-label"); - const collapsedLabel = toggle.dataset.collapsedLabel ?? label?.textContent ?? ""; - const expanded = toggle.getAttribute("aria-expanded") === "true"; - - if (expanded) { - toggle.setAttribute("aria-expanded", "false"); - toggle.setAttribute("aria-label", `Show full value (${collapsedLabel})`); - if (label) label.textContent = collapsedLabel; - markupLine?.nextElementSibling?.classList.contains("markup-embedded-uri-panel") - && markupLine.nextElementSibling.remove(); - return; - } - - toggle.setAttribute("aria-expanded", "true"); - toggle.setAttribute("aria-label", "Hide full value"); - if (label) label.textContent = "hide"; - if (!markupLine || markupLine.nextElementSibling?.classList.contains("markup-embedded-uri-panel")) { - return; - } - const depth = Number(markupLine.style.getPropertyValue("--markup-depth") || 0); - markupLine.insertAdjacentElement("afterend", createEmbeddedUriContinuationPanel(fullValue, depth)); -} - -function appendMarkupAttributes(line, attributes) { - for (const { name, value } of attributes) { - line.appendChild(document.createTextNode(" ")); - line.appendChild(xmlSpan("xml-attr-name", name)); - line.appendChild(xmlSpan("xml-bracket", '="')); - appendMarkupAttrValue(line, value); - line.appendChild(xmlSpan("xml-bracket", '"')); - } -} - -function appendMarkupTextContent(line, text) { - const cdataMatch = /^$/.exec(text); - if (cdataMatch) { - line.appendChild(xmlSpan("xml-cdata-delimiter", "")); - return; - } - line.appendChild(xmlSpan("xml-text", text)); -} - -function createMarkupLine() { - const line = document.createElement("div"); - line.className = "markup-line"; - return line; -} - -function appendOpenTagContent(line, tag, attributes, ghost = false) { - line.appendChild(xmlSpan("xml-bracket", "<", { ghost })); - line.appendChild(xmlSpan("xml-tag", tag, { ghost })); - appendMarkupAttributes(line, attributes); - line.appendChild(xmlSpan("xml-bracket", ">", { ghost })); -} - -function createMarkupFoldToggle() { - const btn = document.createElement("button"); - btn.type = "button"; - btn.className = "markup-fold-toggle"; - btn.setAttribute("aria-expanded", "true"); - btn.setAttribute("aria-label", "Collapse"); - return btn; -} - -function appendMarkupFoldableOpen(parent, depth, tag, attributes, tagHint) { - const ghost = Boolean(tagHint); - const { line, content } = createMarkupLineRow(depth, { foldToggle: true }); - line.classList.add("markup-line-open"); - appendOpenTagContent(content, tag, attributes, ghost); - - const suffix = document.createElement("span"); - suffix.className = "markup-fold-suffix"; - suffix.appendChild(xmlSpan("xml-bracket", "...", { ghost })); - suffix.appendChild(xmlSpan("xml-bracket", "", { ghost })); - content.appendChild(suffix); - parent.appendChild(line); -} - -function appendMarkupCloseTag(parent, depth, tag, tagHint) { - const ghost = Boolean(tagHint); - const { line, content } = createMarkupLineRow(depth); - content.appendChild(xmlSpan("xml-bracket", "", { ghost })); - parent.appendChild(line); -} - -function appendMarkupSelfClosingTag(parent, depth, tag, attributes) { - const { line, content } = createMarkupLineRow(depth); - content.appendChild(xmlSpan("xml-bracket", "<")); - content.appendChild(xmlSpan("xml-tag", tag)); - appendMarkupAttributes(content, attributes); - content.appendChild(xmlSpan("xml-bracket", "/>")); - parent.appendChild(line); -} - -function appendMarkupInlineElement(parent, depth, tag, attributes, text) { - const { line, content } = createMarkupLineRow(depth); - appendOpenTagContent(content, tag, attributes); - appendMarkupTextContent(content, text); - content.appendChild(xmlSpan("xml-bracket", "")); - parent.appendChild(line); -} - -function appendMarkupTextLine(parent, depth, text) { - const { line, content } = createMarkupLineRow(depth); - appendMarkupTextContent(content, text); - parent.appendChild(line); -} - -function formatMarkupTextNode(node) { - if (node.nodeType === Node.CDATA_SECTION_NODE) { - return ``; - } - return node.textContent.trim(); -} - -function serializeMarkupTextNodes(nodes) { - return [...nodes] - .filter((n) => isTextLikeNode(n) && !isWhitespaceOnlyText(n)) - .map(formatMarkupTextNode) - .join(""); -} -const PAGE_WHEEL_COOLDOWN_MS = 200; -const PAGE_WHEEL_PIXEL_THRESHOLD = 4; -const PAGE_WHEEL_GESTURE_MS = 100; -const OVERLAY_BADGE_FONT_SIZE = 11 * 1.5 * 0.8; -const OVERLAY_BADGE_PAD_X = 3; -const OVERLAY_BADGE_PAD_Y = 2; -const OVERLAY_BADGE_RADIUS_SCREEN_PX = 3; -/** Demo page size; overlay lengths are calibrated to match pre-fix sizing on these images. */ -const OVERLAY_REF_IMAGE_WIDTH = 1224; -const OVERLAY_REF_IMAGE_HEIGHT = 1584; -const PAGE_ZOOM_DEFAULT = 100; -const PAGE_PAN_DRAG_THRESHOLD = 5; -const PAGE_VIEW_BORDER_PX = 2; -const LAYOUT_STORAGE_KEY = "doclang-viewer-pane-layout"; -const PANE_MIN_RATIO = 0.12; -const PANE_KEYS = ["file", "page", "markup", "reading"]; -const DEFAULT_PANE_RATIOS = [1, 1, 1, 1]; -const DEFAULT_USER_PANE_VISIBLE = { file: false, page: true, markup: true, reading: true }; -const LAYOUT_STACK_BREAKPOINT_PX = 1200; - -/** @type {{ pageImages: Map, assetUrls: Map, currentPage: number, pageCount: number, segments: Element[][], defaultResolution: { width: number, height: number }, elementIds: Map, idToElement: Map, hasPageView: boolean, markupOnly: boolean, docRoot: Element, threadPagesById: Map>, elementPageByEl: Map, threadNavByElement: Map, pendingSelectElement: Element | null, readingOrder: Element[], readingOrderDisplayNumbers: Map, pageViewOverlay: { boxes: object[], readingOrderSteps: { order: number, box: object, elementId: string }[] } | null } | null} */ -let state = null; -let fileCatalog = []; -let activeFileIndex = -1; -let filePaneUserToggled = false; -/** @type {ResizeObserver | null} */ -let pagePaneResizeObserver = null; -/** @type {string | null} */ -let selectedElementId = null; -let showAllBboxes = true; -let showLayoutBadges = true; -let showCaptionLinks = false; -let showPictureContents = false; -let showTableContents = false; -let showFragmentLinks = false; -let showXrefLinks = false; -let showReadingOrder = false; -let showReadingOrderArrows = true; -let readingOrderGlobalNumbering = false; -let showReadingFurniture = true; -let showReadingBackground = true; -let pageSettingsOpen = false; -let readingSettingsOpen = false; -let pageZoomPercent = PAGE_ZOOM_DEFAULT; -/** @type {{ pointerId: number, startX: number, startY: number, scrollLeft: number, scrollTop: number, moved: boolean } | null} */ -let pagePanDrag = null; -let pagePanSuppressClick = false; -/** @type {{ paneW: number, paneH: number, imgW: number, imgH: number, fitScale: number } | null} */ -let pageLayoutCache = null; -/** @type {{ file: boolean, page: boolean, markup: boolean, reading: boolean }} */ -let userPaneVisible = { ...DEFAULT_USER_PANE_VISIBLE }; -/** @type {number[]} */ -let paneRatios = [...DEFAULT_PANE_RATIOS]; -/** @type {number | null} */ -let filePaneWidthPx = null; -let toolbarOptionsOpen = false; -/** @type {{ physicalSplitterIndex: number, leftKey: string, rightKey: string, startX: number, leftStart: number, rightStart: number, pointerId: number } | null} */ -let paneDrag = null; -/** @type {MediaQueryList | null} */ -let layoutStackQuery = null; -const els = { - openFileBtn: document.getElementById("open-file-btn"), - emptyStateFileTypes: document.getElementById("empty-state-file-types"), - docLabel: document.getElementById("doc-label"), - filePane: document.getElementById("file-pane"), - filePaneCloseAll: document.getElementById("btn-file-pane-close-all"), - pageNav: document.getElementById("page-nav"), - pageIndicator: document.getElementById("page-indicator"), - pageNumberInput: document.getElementById("page-number-input"), - pageCountIndicator: document.getElementById("page-count-indicator"), - btnPrev: document.getElementById("btn-prev"), - btnNext: document.getElementById("btn-next"), - showAllBboxes: document.getElementById("show-all-bboxes"), - showLayoutBadges: document.getElementById("show-layout-badges"), - showLayoutBadgesLabel: document.getElementById("show-layout-badges-label"), - settingsToggle: document.getElementById("btn-settings-toggle"), - readingSettingsToggle: document.getElementById("btn-reading-settings-toggle"), - pageSettingsLayer: document.getElementById("page-settings-layer"), - pageSettingsScrim: document.getElementById("page-settings-scrim"), - pageSettingsClose: document.getElementById("btn-page-settings-close"), - readingSettingsLayer: document.getElementById("reading-settings-layer"), - readingSettingsScrim: document.getElementById("reading-settings-scrim"), - readingSettingsClose: document.getElementById("btn-reading-settings-close"), - showReadingFurniture: document.getElementById("show-reading-furniture"), - showReadingFurnitureLabel: document.getElementById("show-reading-furniture-label"), - showReadingBackground: document.getElementById("show-reading-background"), - showReadingBackgroundLabel: document.getElementById("show-reading-background-label"), - showCaptionLinks: document.getElementById("show-caption-links"), - showCaptionLinksLabel: document.getElementById("show-caption-links-label"), - showPictureContents: document.getElementById("show-picture-contents"), - showPictureContentsLabel: document.getElementById("show-picture-contents-label"), - showTableContents: document.getElementById("show-table-contents"), - showTableContentsLabel: document.getElementById("show-table-contents-label"), - showFragmentLinks: document.getElementById("show-fragment-links"), - showFragmentLinksLabel: document.getElementById("show-fragment-links-label"), - showXrefLinks: document.getElementById("show-xref-links"), - showXrefLinksLabel: document.getElementById("show-xref-links-label"), - showReadingOrder: document.getElementById("show-reading-order"), - showReadingOrderLabel: document.getElementById("show-reading-order-label"), - readingOrderArrows: document.getElementById("reading-order-arrows"), - readingOrderArrowsLabel: document.getElementById("reading-order-arrows-label"), - readingOrderGlobal: document.getElementById("reading-order-global"), - readingOrderGlobalLabel: document.getElementById("reading-order-global-label"), - pageZoom: document.getElementById("page-zoom"), - pageZoomLabel: document.getElementById("page-zoom-label"), - pageZoomReset: document.getElementById("page-zoom-reset"), - main: document.getElementById("main"), - emptyState: document.getElementById("empty-state"), - emptyStateLoading: document.getElementById("empty-state-loading"), - emptyStatePrompt: document.getElementById("empty-state-prompt"), - markupPane: document.getElementById("markup-pane"), - renderedPane: document.getElementById("rendered-pane"), - pagePane: document.getElementById("page-pane"), - paneFile: document.querySelector(".pane-file"), - panePageView: document.querySelector(".pane-page-view"), - paneMarkup: document.querySelector(".pane-markup"), - paneReading: document.querySelector(".pane-reading"), - splitters: [ - document.getElementById("splitter-0"), - document.getElementById("splitter-1"), - document.getElementById("splitter-2"), - ], - toolbarOptionsBtn: document.getElementById("btn-toolbar-options"), - toolbarOptionsPanel: document.getElementById("toolbar-options-panel"), - toggleFilePane: document.getElementById("toggle-file-pane"), - toggleFilePaneLabel: document.getElementById("toggle-file-pane-label"), - togglePagePane: document.getElementById("toggle-page-pane"), - toggleMarkupPane: document.getElementById("toggle-markup-pane"), - toggleReadingPane: document.getElementById("toggle-reading-pane"), - togglePagePaneLabel: document.getElementById("toggle-page-pane-label"), - resetPaneLayoutBtn: document.getElementById("btn-reset-pane-layout"), +H([D({ + type: Number, + reflect: !0 +})], U.prototype, "page", void 0), H([D({ + type: String, + reflect: !0 +})], U.prototype, "selected", void 0), H([D({ type: String })], U.prototype, "src", void 0); +//#endregion +//#region src/components/page-nav/page-nav.ts +var Bn = class extends U { + constructor(...e) { + super(...e), this._inputRef = A(), this._onPrev = () => this._emitViewPage(this.page - 1), this._onNext = () => this._emitViewPage(this.page + 1), this._onInputKeydown = (e) => { + e.key === "Enter" ? (e.preventDefault(), this._commitInput(), e.target.blur()) : e.key === "Escape" && (e.preventDefault(), this._resetInput(), e.target.blur()); + }, this._onInputBlur = () => this._resetInput(); + } + static { + this.styles = s(pt); + } + get _pageCount() { + return this._docState?.pageCount ?? 1; + } + render() { + let e = Math.max(1, String(this._pageCount).length); + return b` + + `; + } + updated(e) { + super.updated(e), e.has("page") && this._resetInput(); + } + _renderDocument() {} + _clearDocument() {} + _resetInput() { + let e = this._inputRef.value; + e && (e.value = String(this.page)); + } + _commitInput() { + let e = this._inputRef.value; + if (!e) return; + let t = Number.parseInt(e.value.trim(), 10); + if (!Number.isFinite(t)) { + this._resetInput(); + return; + } + this._emitViewPage(Math.min(Math.max(1, t), this._docState?.pageCount ?? 1)); + } + _emitViewPage(e) { + this.dispatchEvent(new CustomEvent("view-page", { + bubbles: !0, + composed: !0, + detail: { page: e } + })); + } }; - -document.getElementById("btn-demo")?.addEventListener("click", loadDemo); -document.getElementById("demo-empty-link")?.addEventListener("click", (e) => { - e.preventDefault(); - loadDemo(); -}); -document.getElementById("home-link")?.addEventListener("click", (e) => { - e.preventDefault(); - resetViewer(); -}); -document.getElementById("input-archive")?.addEventListener("change", async (e) => { - const files = [...e.target.files].filter((f) => isArchiveFile(f) || isMarkupFile(f)); - if (!files.length) return; - await addFilesToCatalog(files, { replace: true }); - e.target.value = ""; -}); -els.btnPrev?.addEventListener("click", () => goToPage(state.currentPage - 1)); -els.btnNext?.addEventListener("click", () => goToPage(state.currentPage + 1)); -els.pageNumberInput?.addEventListener("keydown", (e) => { - if (e.key === "Enter") { - e.preventDefault(); - commitPageNumberInput(); - els.pageNumberInput?.blur(); - } else if (e.key === "Escape") { - e.preventDefault(); - resetPageNumberInput(); - els.pageNumberInput?.blur(); - } -}); -els.pageNumberInput?.addEventListener("blur", resetPageNumberInput); -els.pageNumberInput?.addEventListener("focus", (e) => { - e.target.select(); -}); -els.showAllBboxes?.addEventListener("change", () => { - showAllBboxes = els.showAllBboxes.checked; - syncLayoutSubtoggles(); - const img = els.pagePane?.querySelector(".page-view img"); - if (img) syncOverlayBadges(img); - applyBboxVisibility(); -}); -els.showLayoutBadges?.addEventListener("change", () => { - showLayoutBadges = els.showLayoutBadges.checked; - const img = els.pagePane?.querySelector(".page-view img"); - if (img) syncOverlayBadges(img); - applyBboxVisibility(); -}); -els.showCaptionLinks?.addEventListener("change", () => { - showCaptionLinks = els.showCaptionLinks.checked; - applyBboxVisibility(); -}); -els.showPictureContents?.addEventListener("change", () => { - showPictureContents = els.showPictureContents.checked; - applyBboxVisibility(); -}); -els.showTableContents?.addEventListener("change", () => { - showTableContents = els.showTableContents.checked; - applyBboxVisibility(); -}); -els.showFragmentLinks?.addEventListener("change", () => { - showFragmentLinks = els.showFragmentLinks.checked; - applyBboxVisibility(); -}); -els.showXrefLinks?.addEventListener("change", () => { - showXrefLinks = els.showXrefLinks.checked; - applyBboxVisibility(); -}); -els.showReadingOrder?.addEventListener("change", () => { - showReadingOrder = els.showReadingOrder.checked; - syncLayoutSubtoggles(); - const img = els.pagePane?.querySelector(".page-view img"); - if (img) syncOverlayBadges(img); - applyBboxVisibility(); -}); -els.readingOrderArrows?.addEventListener("change", () => { - showReadingOrderArrows = els.readingOrderArrows.checked; - applyBboxVisibility(); -}); -els.readingOrderGlobal?.addEventListener("change", () => { - readingOrderGlobalNumbering = els.readingOrderGlobal.checked; - if (state) renderPage(state.currentPage); -}); -els.settingsToggle?.addEventListener("click", () => setPageSettingsOpen(!pageSettingsOpen)); -els.readingSettingsToggle?.addEventListener("click", () => setReadingSettingsOpen(!readingSettingsOpen)); -els.pageSettingsClose?.addEventListener("click", () => setPageSettingsOpen(false)); -els.pageSettingsScrim?.addEventListener("click", () => setPageSettingsOpen(false)); -els.readingSettingsClose?.addEventListener("click", () => setReadingSettingsOpen(false)); -els.readingSettingsScrim?.addEventListener("click", () => setReadingSettingsOpen(false)); -document.addEventListener("keydown", (e) => { - if (e.key !== "Escape") return; - if (toolbarOptionsOpen) setToolbarOptionsOpen(false); - else if (pageSettingsOpen) setPageSettingsOpen(false); - else if (readingSettingsOpen) setReadingSettingsOpen(false); -}); -els.showReadingFurniture?.addEventListener("change", () => { - showReadingFurniture = els.showReadingFurniture.checked; - syncReadingLayerVisibility(); -}); -els.showReadingBackground?.addEventListener("change", () => { - showReadingBackground = els.showReadingBackground.checked; - syncReadingLayerVisibility(); -}); -loadLayoutPrefs(); -normalizePaneRatios(); -initToolbarOptions(); -initPaneSplitters(); -initLayoutStackListener(); -initFileTypeHints(); -initCursorHints(); -initBboxHints(); -initDragDrop(); -initFilePaneCloseAll(); -initPageWheelNav(); -initPageViewControls(); -let demoLoadInProgress = false; - -function setDemoLoading(loading) { - document.body.classList.toggle("demo-loading", loading); - const btnDemo = document.getElementById("btn-demo"); - if (btnDemo) btnDemo.disabled = loading; -} - -if (document.getElementById("btn-demo")) loadDemo(); - -async function loadDemo() { - if (demoLoadInProgress) return; - demoLoadInProgress = true; - setDemoLoading(true); - try { - const res = await fetch(DEMO_ARCHIVE_URL); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - const label = DEMO_ARCHIVE_URL.split("/").pop() || "demo.dclx"; - await addArchiveBufferToCatalog(await res.arrayBuffer(), label, { replace: true }); - } catch (err) { - alert( - `Failed to load demo: ${err.message}\n\nServe this directory over HTTP (e.g. python3 -m http.server) and open the viewer from localhost.`, - ); - } finally { - demoLoadInProgress = false; - if (!document.body.classList.contains("viewer-loaded")) setDemoLoading(false); - } -} - -async function loadFromFileList(fileList) { - if (!fileList?.length) return; - const files = [...fileList]; - if (files.some((f) => f.name === "document.xml")) { - await appendFolderArchive(files); - return; - } - const supported = files.filter((f) => isArchiveFile(f) || isMarkupFile(f)); - if (supported.length) await addFilesToCatalog(supported, { replace: false }); -} - -function createFileCatalogEntry(file) { - return { - id: crypto.randomUUID(), - label: file.name, - kind: isMarkupFile(file) ? "markup" : "archive", - source: file, - currentPage: 1, - pageZoom: PAGE_ZOOM_DEFAULT, - snapshot: null, - thumbnailUrl: null, - }; -} - -function pageImageMimeFromExt(ext) { - const normalized = ext.toLowerCase().replace("jpeg", "jpg"); - if (normalized === "png") return "image/png"; - if (normalized === "webp") return "image/webp"; - return "image/jpeg"; -} - -function createPageImageObjectUrl(data, ext) { - return URL.createObjectURL(new Blob([data], { type: pageImageMimeFromExt(ext) })); -} - -function createFirstPageImageUrlFromFiles(files) { - let bestPage = Infinity; - /** @type {File | null} */ - let bestFile = null; - for (const f of files) { - const relPath = f.webkitRelativePath || f.name; - const parts = relPath.split("/"); - if (parts.length < 2 || parts[parts.length - 2] !== "pages") continue; - const m = PAGE_IMAGE_RE.exec(f.name); - if (!m) continue; - const pageNum = Number(m[1]); - if (pageNum < bestPage) { - bestPage = pageNum; - bestFile = f; - } - } - return bestFile ? URL.createObjectURL(bestFile) : null; -} - -async function createFirstPageImageUrlFromZip(source) { - const buffer = source instanceof File ? await source.arrayBuffer() : source; - const entries = await unzip(buffer, { - shouldExtract: (name) => /^pages\/\d+\.(png|jpe?g|webp)$/i.test(name), - }); - let bestPage = Infinity; - /** @type {{ name: string, data: Uint8Array } | null} */ - let bestEntry = null; - for (const e of entries) { - const m = e.name.match(/^pages\/(\d+)\.(png|jpe?g|webp)$/i); - if (!m) continue; - const pageNum = Number(m[1]); - if (pageNum < bestPage) { - bestPage = pageNum; - bestEntry = e; - } - } - if (!bestEntry) return null; - const ext = bestEntry.name.split(".").pop() ?? "png"; - return createPageImageObjectUrl(bestEntry.data, ext); -} - -async function resolveCatalogEntryThumbnail(entry) { - if (entry.thumbnailUrl) return entry.thumbnailUrl; - if (entry.kind === "markup") return null; - try { - if (entry.kind === "folder") { - entry.thumbnailUrl = createFirstPageImageUrlFromFiles(entry.source); - } else if (entry.kind === "archive") { - entry.thumbnailUrl = await createFirstPageImageUrlFromZip(entry.source); - } - } catch { - entry.thumbnailUrl = null; - } - return entry.thumbnailUrl; -} - -function enrichCatalogEntryThumbnail(entry) { - resolveCatalogEntryThumbnail(entry).then((url) => { - if (!fileCatalog.includes(entry)) { - if (url?.startsWith("blob:")) URL.revokeObjectURL(url); - return; - } - if (url) renderFileView(); - }); -} - -function revokeCatalogEntry(entry) { - if (entry?.thumbnailUrl?.startsWith("blob:")) { - URL.revokeObjectURL(entry.thumbnailUrl); - } - if (entry) entry.thumbnailUrl = null; -} - -function createFileViewThumbnail(entry) { - const thumb = document.createElement("span"); - thumb.className = "file-view-thumb"; - thumb.setAttribute("aria-hidden", "true"); - if (entry.thumbnailUrl) { - const img = document.createElement("img"); - img.src = entry.thumbnailUrl; - img.alt = ""; - thumb.appendChild(img); - } else { - const placeholder = document.createElement("span"); - placeholder.className = "file-view-thumb-placeholder"; - placeholder.innerHTML = FILE_THUMB_PLACEHOLDER_SVG; - thumb.appendChild(placeholder); - } - return thumb; -} - -async function addFilesToCatalog(files, { replace = false } = {}) { - if (replace) { - clearFileCatalog(); - filePaneUserToggled = false; - } - const startIndex = fileCatalog.length; - for (const file of files) { - const entry = createFileCatalogEntry(file); - fileCatalog.push(entry); - enrichCatalogEntryThumbnail(entry); - } - if (!fileCatalog.length) return; - await switchToFile(replace ? 0 : startIndex); -} - -async function appendFolderArchive(files) { - if (!files.some((f) => f.name === "document.xml")) { - alert("Archive must contain document.xml at its root."); - return; - } - const rootName = (files[0].webkitRelativePath || files[0].name).split("/")[0] || "archive"; - const entry = { - id: crypto.randomUUID(), - label: rootName, - kind: "folder", - source: files, - currentPage: 1, - pageZoom: PAGE_ZOOM_DEFAULT, - snapshot: null, - thumbnailUrl: null, - }; - fileCatalog.push(entry); - enrichCatalogEntryThumbnail(entry); - await switchToFile(fileCatalog.length - 1); -} - -async function addArchiveBufferToCatalog(buffer, label, { replace = false } = {}) { - if (replace) { - clearFileCatalog(); - filePaneUserToggled = false; - } - const entry = { - id: crypto.randomUUID(), - label, - kind: "archive", - source: buffer, - currentPage: 1, - pageZoom: PAGE_ZOOM_DEFAULT, - snapshot: null, - thumbnailUrl: null, - }; - fileCatalog.push(entry); - enrichCatalogEntryThumbnail(entry); - await switchToFile(replace ? 0 : fileCatalog.length - 1); -} - -async function extractArchiveFromFiles(files) { - const markupFile = files.find((f) => f.name === "document.xml"); - if (!markupFile) throw new Error("Archive must contain document.xml at its root."); - const markupXml = await markupFile.text(); - const pageImages = new Map(); - const assetUrls = new Map(); - for (const f of files) { - const relPath = f.webkitRelativePath || f.name; - const parts = relPath.split("/"); - if (parts.length >= 2 && parts[parts.length - 2] === "pages") { - const m = PAGE_IMAGE_RE.exec(f.name); - if (m) pageImages.set(Number(m[1]), URL.createObjectURL(f)); - } - const assetPath = archiveRelativeAssetPath(relPath); - if (assetPath) assetUrls.set(assetPath, URL.createObjectURL(f)); - } - return { markupXml, pageImages, assetUrls }; -} - -async function parseCatalogEntry(entry) { - try { - if (entry.kind === "markup") { - const text = entry.source instanceof File - ? await entry.source.text() - : new TextDecoder().decode(entry.source); - return buildDocumentState(text, new Map(), entry.label, new Map(), { markupOnly: true }); - } - if (entry.kind === "archive") { - const buffer = entry.source instanceof File - ? await entry.source.arrayBuffer() - : entry.source; - const { markupXml, pageImages, assetUrls } = await extractArchiveFromZipBuffer(buffer); - return buildDocumentState(markupXml, pageImages, entry.label, assetUrls, { markupOnly: false }); - } - if (entry.kind === "folder") { - const { markupXml, pageImages, assetUrls } = await extractArchiveFromFiles(entry.source); - return buildDocumentState(markupXml, pageImages, entry.label, assetUrls, { markupOnly: false }); - } - } catch (err) { - alert(`Failed to read ${entry.label}: ${err.message}`); - } - return null; -} - -function persistActiveFileViewState() { - if (activeFileIndex < 0 || !state) return; - const entry = fileCatalog[activeFileIndex]; - entry.currentPage = state.currentPage; - entry.pageZoom = pageZoomPercent; -} - -function releaseActiveDocument() { - if (activeFileIndex >= 0) { - const entry = fileCatalog[activeFileIndex]; - if (entry?.snapshot) { - revokeDocumentState(entry.snapshot); - entry.snapshot = null; - } - } - if (state) revokeDocumentState(state); - state = null; - selectedElementId = null; - pagePanDrag = null; - pagePanSuppressClick = false; -} - -function clearFileCatalog() { - releaseActiveDocument(); - for (const entry of fileCatalog) revokeCatalogEntry(entry); - fileCatalog = []; - activeFileIndex = -1; -} - -async function switchToFile(index) { - if (index < 0 || index >= fileCatalog.length) return; - - persistActiveFileViewState(); - releaseActiveDocument(); - - activeFileIndex = index; - const entry = fileCatalog[index]; - const docState = await parseCatalogEntry(entry); - if (!docState) { - revokeCatalogEntry(entry); - fileCatalog.splice(index, 1); - activeFileIndex = -1; - if (fileCatalog.length) { - await switchToFile(Math.min(index, fileCatalog.length - 1)); - } else { - resetViewer(); - } - return; - } - - entry.snapshot = docState; - docState.currentPage = entry.currentPage ?? 1; - activateDocument(docState, entry); -} - -function defaultFilePaneVisible() { - return fileCatalog.length > 1; -} - -function syncFilePaneDefault() { - if (!filePaneUserToggled) { - const wasVisible = userPaneVisible.file; - const shouldBeVisible = defaultFilePaneVisible(); - userPaneVisible.file = shouldBeVisible; - if (!wasVisible && shouldBeVisible) { - paneRatios = [...DEFAULT_PANE_RATIOS]; - normalizePaneRatios(); - filePaneWidthPx = null; - } - } -} - -async function closeCatalogFile(index) { - if (index < 0 || index >= fileCatalog.length) return; - - const wasActive = index === activeFileIndex; - const entry = fileCatalog[index]; - - if (wasActive) { - releaseActiveDocument(); - activeFileIndex = -1; - } - - revokeCatalogEntry(entry); - fileCatalog.splice(index, 1); - - if (!fileCatalog.length) { - resetViewer(); - return; - } - - if (wasActive) { - await switchToFile(Math.min(index, fileCatalog.length - 1)); - return; - } - - if (index < activeFileIndex) { - activeFileIndex -= 1; - } - updateFileView(); -} - -function renderFileView() { - if (!els.filePane) return; - els.filePane.replaceChildren(); - if (!fileCatalog.length) return; - - const list = document.createElement("ul"); - list.className = "file-view-list"; - list.setAttribute("role", "listbox"); - list.setAttribute("aria-label", "Open files"); - - fileCatalog.forEach((entry, index) => { - const item = document.createElement("li"); - const card = document.createElement("div"); - card.className = "file-view-item"; - card.title = entry.label; - card.tabIndex = 0; - card.setAttribute("role", "option"); - if (index === activeFileIndex) { - card.classList.add("is-active"); - card.setAttribute("aria-selected", "true"); - } else { - card.setAttribute("aria-selected", "false"); - } - - const thumbWrap = document.createElement("div"); - thumbWrap.className = "file-view-thumb-wrap"; - - const closeBtn = document.createElement("button"); - closeBtn.type = "button"; - closeBtn.className = "file-view-close"; - closeBtn.setAttribute("aria-label", `Close ${entry.label}`); - closeBtn.textContent = "×"; - closeBtn.addEventListener("click", (e) => { - e.stopPropagation(); - closeCatalogFile(index); - }); - - thumbWrap.append(createFileViewThumbnail(entry), closeBtn); - - const label = document.createElement("span"); - label.className = "file-view-label"; - label.textContent = entry.label; - - card.append(thumbWrap, label); - card.addEventListener("click", (e) => { - if (e.target.closest(".file-view-close")) return; - switchToFile(index); - }); - card.addEventListener("keydown", (e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); - switchToFile(index); - } - }); - - item.appendChild(card); - list.appendChild(item); - }); - - els.filePane.appendChild(list); -} - -function updateFileView() { - syncFilePaneDefault(); - syncFilePaneCloseAllButton(); - renderFileView(); - syncToolbarPaneCheckboxes(); - applyPaneLayout(); -} - -function syncFilePaneCloseAllButton() { - if (!els.filePaneCloseAll) return; - els.filePaneCloseAll.hidden = fileCatalog.length === 0; -} - -function initFilePaneCloseAll() { - els.filePaneCloseAll?.addEventListener("click", () => { - if (!fileCatalog.length) return; - const count = fileCatalog.length; - const message = count === 1 - ? `Remove "${fileCatalog[0].label}" from the viewer?` - : `Remove all ${count} open files from the viewer?`; - if (confirm(message)) resetViewer(); - }); -} - -function initPageWheelNav() { - if (!els.pagePane) return; - - let pixelAccum = 0; - let pixelGestureUntil = 0; - let lastFlipAt = 0; - - function wheelDir(e) { - if (e.deltaMode === 1) { - return e.deltaY > 0 ? 1 : e.deltaY < 0 ? -1 : 0; - } - if (e.deltaMode === 2) { - return Math.sign(e.deltaY); - } - const now = performance.now(); - if (now > pixelGestureUntil) pixelAccum = 0; - pixelGestureUntil = now + PAGE_WHEEL_GESTURE_MS; - pixelAccum += e.deltaY; - if (Math.abs(pixelAccum) >= PAGE_WHEEL_PIXEL_THRESHOLD) { - const dir = pixelAccum > 0 ? 1 : -1; - pixelAccum = 0; - return dir; - } - return 0; - } - - function tryFlipPage(dir) { - if (!dir || !state) return false; - const now = performance.now(); - if (now - lastFlipAt < PAGE_WHEEL_COOLDOWN_MS) return false; - const before = state.currentPage; - goToPage(state.currentPage + dir); - if (state.currentPage !== before) { - lastFlipAt = now; - return true; - } - return false; - } - - function isScrollAtTop(pane) { - return pane.scrollTop <= 0; - } - - function isScrollAtBottom(pane) { - return pane.scrollTop + pane.clientHeight >= pane.scrollHeight - 1; - } - - function onScrollPaneWheel(e, pane) { - if (!state || state.markupOnly || state.pageCount <= 1) return; - const dir = wheelDir(e); - if (!dir) return; - - const atTop = isScrollAtTop(pane); - const atBottom = isScrollAtBottom(pane); - const wantPrev = dir < 0 && atTop; - const wantNext = dir > 0 && atBottom; - if (!wantPrev && !wantNext) return; - - e.preventDefault(); - if (!tryFlipPage(dir)) return; - - requestAnimationFrame(() => { - pane.scrollTop = dir > 0 ? 0 : pane.scrollHeight; - }); - } - - els.pagePane.addEventListener( - "wheel", - (e) => { - if (!state?.hasPageView) return; - const pane = pageViewScrollPane(); - if (!pane) return; - const scrollable = pane.scrollHeight > pane.clientHeight || pane.scrollWidth > pane.clientWidth; - if (scrollable) { - onScrollPaneWheel(e, pane); - return; - } - e.preventDefault(); - const dir = wheelDir(e); - if (dir) tryFlipPage(dir); - }, - { passive: false }, - ); - - for (const pane of [els.markupPane, els.renderedPane]) { - if (!pane) continue; - pane.addEventListener("wheel", (e) => onScrollPaneWheel(e, pane), { passive: false }); - } - - els.pagePane.tabIndex = 0; - els.pagePane.setAttribute("role", "region"); - els.pagePane.setAttribute("aria-label", "Original page"); - - els.pagePane.addEventListener("pointerdown", (e) => { - if (state?.hasPageView) els.pagePane.focus({ preventScroll: true }); - }); - - els.pagePane.addEventListener("keydown", (e) => { - if (!state?.hasPageView) return; - - let dir = 0; - switch (e.key) { - case "ArrowDown": - case "PageDown": - case "ArrowRight": - dir = 1; - break; - case "ArrowUp": - case "PageUp": - case "ArrowLeft": - dir = -1; - break; - default: - return; - } - - e.preventDefault(); - tryFlipPage(dir); - }); -} - -function pageViewScrollPane() { - if (!els.pagePane) return null; - return els.pagePane.querySelector(".page-view-port") ?? els.pagePane; -} - -function isPagePaneScrollable() { - const pane = pageViewScrollPane(); - if (!pane) return false; - return pane.scrollWidth > pane.clientWidth || pane.scrollHeight > pane.clientHeight; -} - -function canStartPagePan(event) { - if (!(event.target instanceof Element)) return false; - if (!event.target.closest(".page-view")) return false; - return isPagePaneScrollable(); -} - -function updatePagePanePanCursor() { - if (!els.pagePane) return; - els.pagePane.classList.toggle("can-pan", isPagePaneScrollable() && !pagePanDrag); -} - -function initPageViewControls() { - if (!els.pagePane) return; - - els.pageZoom?.addEventListener("input", () => { - pageZoomPercent = Math.max(PAGE_ZOOM_DEFAULT, Number(els.pageZoom.value)); - if (Number(els.pageZoom.value) < PAGE_ZOOM_DEFAULT) { - els.pageZoom.value = String(pageZoomPercent); - } - els.pageZoom.setAttribute("aria-valuenow", String(pageZoomPercent)); - updatePageZoomResetButton(); - refreshPageViewLayout(); - }); - - els.pageZoomReset?.addEventListener("click", () => { - if (pageZoomPercent === PAGE_ZOOM_DEFAULT) return; - resetPageZoom(); - }); - - els.pagePane.addEventListener("pointerdown", (e) => { - if (e.button !== 0 || !canStartPagePan(e)) return; - const scrollPane = pageViewScrollPane(); - if (!scrollPane) return; - pagePanDrag = { - pointerId: e.pointerId, - startX: e.clientX, - startY: e.clientY, - scrollLeft: scrollPane.scrollLeft, - scrollTop: scrollPane.scrollTop, - moved: false, - }; - }); - - els.pagePane.addEventListener("pointermove", (e) => { - if (!pagePanDrag || e.pointerId !== pagePanDrag.pointerId) return; - const dx = e.clientX - pagePanDrag.startX; - const dy = e.clientY - pagePanDrag.startY; - if (!pagePanDrag.moved && Math.hypot(dx, dy) >= PAGE_PAN_DRAG_THRESHOLD) { - pagePanDrag.moved = true; - els.pagePane.classList.add("is-panning"); - els.pagePane.classList.remove("can-pan"); - els.pagePane.setPointerCapture(e.pointerId); - } - if (!pagePanDrag.moved) return; - const scrollPane = pageViewScrollPane(); - if (!scrollPane) return; - scrollPane.scrollLeft = pagePanDrag.scrollLeft + pagePanDrag.startX - e.clientX; - scrollPane.scrollTop = pagePanDrag.scrollTop + pagePanDrag.startY - e.clientY; - e.preventDefault(); - }); - - function endPagePan(e) { - if (!pagePanDrag || e.pointerId !== pagePanDrag.pointerId) return; - if (pagePanDrag.moved) pagePanSuppressClick = true; - pagePanDrag = null; - els.pagePane.classList.remove("is-panning"); - if (els.pagePane.hasPointerCapture(e.pointerId)) { - els.pagePane.releasePointerCapture(e.pointerId); - } - updatePagePanePanCursor(); - } - - els.pagePane.addEventListener("pointerup", endPagePan); - els.pagePane.addEventListener("pointercancel", endPagePan); -} - -function escapeHtml(text) { - return String(text) - .replace(/&/g, "&") - .replace(//g, ">"); -} - -function headTextPreview(el, maxLen = 72) { - const text = el.textContent?.replace(/\s+/g, " ").trim() ?? ""; - if (!text) return "—"; - return text.length > maxLen ? `${text.slice(0, maxLen)}…` : text; -} - -function virtualTextHeadLocations(el) { - const parent = el.parentElement; - if (!parent) return []; - const nodes = [...parent.childNodes]; - const idx = nodes.indexOf(el); - if (idx < 0) return []; - return parseElementHeadAt(nodes, idx + 1)?.locs ?? []; -} - -function elementHeadLocations(el) { - const own = headLocations(el); - return own.length === 4 ? own : virtualTextHeadLocations(el); -} - -function firstHeadChild(el, tag) { - return childElements(el).find((child) => localName(child) === tag) ?? null; -} - -const ELEMENT_LAYERS = new Set(["body", "background", "furniture"]); - -function layerFromHeadNodes(nodes, startIdx) { - let i = startIdx; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE) { - i += 1; - continue; - } - const tag = localName(node); - if (tag === "layer") { - const value = node.getAttribute("value") ?? "body"; - return ELEMENT_LAYERS.has(value) ? value : "body"; - } - if (tag === "location") break; - if (HEAD_TAGS.has(tag)) { - i += 1; - continue; - } - break; - } - return "body"; -} - -function elementLayer(el) { - const layerEl = firstHeadChild(el, "layer"); - if (layerEl) { - const value = layerEl.getAttribute("value") ?? "body"; - return ELEMENT_LAYERS.has(value) ? value : "body"; - } - if (headLocations(el).length === 4) return "body"; - const parent = el.parentElement; - if (!parent) return "body"; - const nodes = [...parent.childNodes]; - const idx = nodes.indexOf(el); - if (idx < 0) return "body"; - return layerFromHeadNodes(nodes, idx + 1); -} - -function layerClassForValue(layer) { - if (layer === "furniture") return "layer-furniture"; - if (layer === "background") return "layer-background"; - return ""; -} - -function applyElementLayerAttr(sourceEl, domEl) { - domEl.setAttribute("data-doclang-layer", elementLayer(sourceEl)); -} - -function syncReadingLayerCheckboxes() { - if (els.showReadingFurniture) els.showReadingFurniture.checked = showReadingFurniture; - if (els.showReadingBackground) els.showReadingBackground.checked = showReadingBackground; -} - -/** @returns {{ key: string, value: string, isDefault: boolean }[]} */ -function collectElementHeadInfo(el, defaultResolution) { - const labelEl = firstHeadChild(el, "label"); - const threadEl = firstHeadChild(el, "thread"); - const xrefEl = firstHeadChild(el, "xref"); - const hrefEl = firstHeadChild(el, "href"); - const layerEl = firstHeadChild(el, "layer"); - const captionEl = firstHeadChild(el, "caption"); - const descriptionEl = firstHeadChild(el, "description"); - const summaryEl = firstHeadChild(el, "summary"); - const customEl = firstHeadChild(el, "custom"); - const locs = elementHeadLocations(el); - const rows = [{ key: "element", value: elementLabel(el), isDefault: false }]; - - rows.push({ - key: "label", - value: labelEl?.getAttribute("value") ?? "undefined", - isDefault: !labelEl?.hasAttribute("value"), - }); - - if (threadEl) { - rows.push({ - key: "thread_id", - value: threadEl.getAttribute("thread_id") ?? "—", - isDefault: false, - }); - } else { - rows.push({ key: "thread", value: "—", isDefault: true }); - } - - if (xrefEl) { - rows.push({ - key: "xref", - value: `thread_id ${xrefEl.getAttribute("thread_id") ?? "—"}`, - isDefault: false, - }); - } else { - rows.push({ key: "xref", value: "—", isDefault: true }); - } - - if (hrefEl) { - rows.push({ - key: "href", - value: hrefEl.getAttribute("uri") ?? "—", - isDefault: false, - }); - } else { - rows.push({ key: "href", value: "—", isDefault: true }); - } - - rows.push({ - key: "layer", - value: layerEl?.getAttribute("value") ?? "body", - isDefault: !layerEl?.hasAttribute("value"), - }); - - const cornerLabels = ["x_min", "y_min", "x_max", "y_max"]; - if (locs.length === 4) { - for (let idx = 0; idx < 4; idx += 1) { - const loc = locs[idx]; - const axisDefault = idx % 2 === 0 ? defaultResolution.width : defaultResolution.height; - const resolution = locationResolution(loc, axisDefault); - const value = loc.getAttribute("value") ?? "0"; - rows.push({ - key: cornerLabels[idx], - value: `${value} @ ${resolution}`, - isDefault: false, - }); - } - } else { - for (const key of cornerLabels) { - rows.push({ key, value: "—", isDefault: false }); - } - } - - rows.push({ - key: "caption", - value: captionEl ? headTextPreview(captionEl) : "—", - isDefault: !captionEl, - }); - rows.push({ - key: "description", - value: descriptionEl ? headTextPreview(descriptionEl) : "—", - isDefault: !descriptionEl, - }); - rows.push({ - key: "summary", - value: summaryEl ? headTextPreview(summaryEl) : "—", - isDefault: !summaryEl, - }); - rows.push({ - key: "custom", - value: customEl ? headTextPreview(customEl) : "—", - isDefault: !customEl, - }); - - return rows; -} - -function elementHeadTooltipHtml(el, defaultResolution) { - const rows = collectElementHeadInfo(el, defaultResolution); - const body = rows - .map(({ key, value, isDefault }) => { - const rendered = escapeHtml(value); - const suffix = isDefault ? ' (default)' : ""; - return `${escapeHtml(key)}${rendered}${suffix}`; - }) - .join(""); - return `${body}
`; -} - -const cursorHintEl = document.getElementById("cursor-hint"); -const CURSOR_HINT_OFFSET = 10; -const CURSOR_HINT_MARGIN = 8; - -function hideCursorHint() { - cursorHintEl.hidden = true; - cursorHintEl.classList.remove("cursor-hint-detail"); - cursorHintEl.replaceChildren(); -} - -function showCursorHint(content, clientX, clientY, { detail = false } = {}) { - cursorHintEl.replaceChildren(); - if (typeof content === "string") { - cursorHintEl.textContent = content; - } else { - cursorHintEl.appendChild(content); - } - cursorHintEl.classList.toggle("cursor-hint-detail", detail); - cursorHintEl.hidden = false; - - let left = clientX + CURSOR_HINT_OFFSET; - let top = clientY + CURSOR_HINT_OFFSET; - const rect = cursorHintEl.getBoundingClientRect(); - if (left + rect.width > window.innerWidth - CURSOR_HINT_MARGIN) { - left = clientX - rect.width - CURSOR_HINT_OFFSET; - } - if (top + rect.height > window.innerHeight - CURSOR_HINT_MARGIN) { - top = clientY - rect.height - CURSOR_HINT_OFFSET; - } - cursorHintEl.style.left = `${Math.max(CURSOR_HINT_MARGIN, left)}px`; - cursorHintEl.style.top = `${Math.max(CURSOR_HINT_MARGIN, top)}px`; -} - -function showCursorHintHtml(html, clientX, clientY) { - cursorHintEl.innerHTML = html; - cursorHintEl.classList.add("cursor-hint-detail"); - cursorHintEl.hidden = false; - - let left = clientX + CURSOR_HINT_OFFSET; - let top = clientY + CURSOR_HINT_OFFSET; - const rect = cursorHintEl.getBoundingClientRect(); - if (left + rect.width > window.innerWidth - CURSOR_HINT_MARGIN) { - left = clientX - rect.width - CURSOR_HINT_OFFSET; - } - if (top + rect.height > window.innerHeight - CURSOR_HINT_MARGIN) { - top = clientY - rect.height - CURSOR_HINT_OFFSET; - } - cursorHintEl.style.left = `${Math.max(CURSOR_HINT_MARGIN, left)}px`; - cursorHintEl.style.top = `${Math.max(CURSOR_HINT_MARGIN, top)}px`; -} - -function initFileTypeHints() { - if (!els.emptyStateFileTypes) return; - const markup = SUPPORTED_FILE_EXTENSIONS.map((ext) => `${ext}`).join(", "); - els.emptyStateFileTypes.innerHTML = markup; -} - -function initCursorHints() { - els.markupPane?.addEventListener("mousemove", (e) => { - if (!e.target.closest(".markup-ghost-tag-part")) { - hideCursorHint(); - return; - } - showCursorHint(VIRTUAL_TEXT_TAG_HINT, e.clientX, e.clientY); - }); - els.markupPane?.addEventListener("mouseleave", hideCursorHint); - - els.openFileBtn?.addEventListener("mousemove", (e) => { - showCursorHint(OPEN_FILE_HINT, e.clientX, e.clientY); - }); - els.openFileBtn?.addEventListener("mouseleave", hideCursorHint); -} - -function initBboxHints() { - if (!els.pagePane) return; - - els.pagePane.addEventListener("mousemove", (e) => { - if (pagePanDrag?.moved || els.pagePane.classList.contains("is-panning")) { - hideCursorHint(); - return; - } - const navBtn = e.target.closest(".fragment-nav-btn:not(.fragment-nav-btn-disabled)"); - if (navBtn) { - const hint = navBtn.getAttribute("data-nav") === "prev" - ? FRAGMENT_NAV_HINT_PREV - : FRAGMENT_NAV_HINT_NEXT; - showCursorHint(hint, e.clientX, e.clientY); - return; - } - const badge = e.target.closest(".element-badge[data-element-id]"); - if (!badge || !state?.idToElement) { - hideCursorHint(); - return; - } - const elementId = badge.getAttribute("data-element-id"); - const xmlEl = state.idToElement.get(elementId); - if (!xmlEl) { - hideCursorHint(); - return; - } - showCursorHintHtml(elementHeadTooltipHtml(xmlEl, state.defaultResolution), e.clientX, e.clientY); - }); - els.pagePane.addEventListener("mouseleave", hideCursorHint); -} - -function initDragDrop() { - document.body.addEventListener("dragenter", (e) => { - if (!hasArchiveTransfer(e.dataTransfer)) return; - e.preventDefault(); - document.body.classList.add("drag-over"); - }); - - document.body.addEventListener("dragover", (e) => { - if (!hasArchiveTransfer(e.dataTransfer)) return; - e.preventDefault(); - e.dataTransfer.dropEffect = "copy"; - }); - - document.body.addEventListener("dragleave", (e) => { - if (!hasArchiveTransfer(e.dataTransfer)) return; - if (e.relatedTarget && document.body.contains(e.relatedTarget)) return; - document.body.classList.remove("drag-over"); - }); - - document.body.addEventListener("drop", async (e) => { - if (!hasArchiveTransfer(e.dataTransfer)) return; - e.preventDefault(); - document.body.classList.remove("drag-over"); - await loadFromDrop(e.dataTransfer); - }); -} - -function hasArchiveTransfer(dataTransfer) { - return [...dataTransfer.types].includes("Files"); -} - -function isArchiveFile(file) { - return /\.dclx$/i.test(file.name) || /\.zip$/i.test(file.name); -} - -function isMarkupFile(file) { - return /\.(?:dclg(?:\.xml)?|xml)$/i.test(file.name); -} - -async function loadFromDrop(dataTransfer) { - const files = [...dataTransfer.files]; - if (files.some((f) => f.name === "document.xml")) { - await appendFolderArchive(files); - return; - } - const supported = files.filter((f) => isArchiveFile(f) || isMarkupFile(f)); - if (supported.length) await addFilesToCatalog(supported, { replace: false }); -} - -function buildDocumentState(markupXml, pageImages, label, assetUrls, { markupOnly }) { - const doc = new DOMParser().parseFromString(markupXml, "application/xml"); - const parseError = doc.querySelector("parsererror"); - if (parseError) { - alert(`Invalid XML in ${label}`); - return null; - } - const root = doc.documentElement; - if (localName(root) !== "doclang") { - alert(`${label}: root element must be `); - return null; - } - - const head = childElements(root).find((el) => localName(el) === "head") ?? null; - const defaultResolution = readDefaultResolution(head); - const segments = markupOnly - ? [childElements(root).filter((el) => localName(el) !== "head")] - : splitIntoSegments(root); - const hasPageView = !markupOnly && pageImages.size > 0; - const maxImagePage = hasPageView ? Math.max(...pageImages.keys()) : 0; - const pageCount = markupOnly ? 1 : Math.max(segments.length, maxImagePage, 1); - const readingOrder = computeReadingOrder(root); - const elementPageByEl = buildElementPageMap(segments); - - return { - pageImages, - assetUrls, - currentPage: 1, - pageCount, - segments, - defaultResolution, - hasPageView, - markupOnly, - docRoot: root, - threadPagesById: buildThreadPagesById(root, elementPageByEl), - elementPageByEl, - threadNavByElement: buildThreadNavByElement(root), - pendingSelectElement: null, - readingOrder, - readingOrderDisplayNumbers: computeReadingOrderDisplayNumbers(readingOrder), - pageViewOverlay: null, - }; -} - -function activateDocument(docState, entry) { - state = docState; - resetPageLayoutCache(); - closeAllSettings(); - - pageZoomPercent = entry.pageZoom ?? PAGE_ZOOM_DEFAULT; - if (els.pageZoom) { - els.pageZoom.value = String(pageZoomPercent); - els.pageZoom.setAttribute("aria-valuenow", String(pageZoomPercent)); - } - updatePageZoomResetButton(); - const port = pageViewScrollPane(); - if (port) { - port.scrollLeft = 0; - port.scrollTop = 0; - } - - setDocLabel(entry.label); - setDocumentOpen(true, { markupOnly: state.markupOnly }); - setPageViewVisible(state.hasPageView); - renderPage(state.currentPage); - updateFileView(); -} - -function setDocLabel(label) { - if (!els.docLabel) return; - if (label) { - els.docLabel.textContent = label; - els.docLabel.hidden = false; - } else { - els.docLabel.textContent = ""; - els.docLabel.hidden = true; - } -} - -function setDocumentOpen(open, { markupOnly = false } = {}) { - document.body.classList.toggle("viewer-loaded", open); - document.body.classList.toggle("markup-only", open && markupOnly); - if (els.pageNav) els.pageNav.hidden = !open || markupOnly; - syncToolbarPaneCheckboxes(); - applyPaneLayout(); -} - -function applyReadingLayerClasses(root) { - root.classList.toggle("show-reading-furniture", showReadingFurniture); - root.classList.toggle("show-reading-background", showReadingBackground); -} - -function syncReadingLayerVisibility() { - const root = els.renderedPane?.querySelector(".rendered-doc"); - if (root) applyReadingLayerClasses(root); -} - -function closeAllSettings() { - setPageSettingsOpen(false); - setReadingSettingsOpen(false); -} - -function resetPageZoom() { - pageZoomPercent = PAGE_ZOOM_DEFAULT; - if (els.pageZoom) { - els.pageZoom.value = String(PAGE_ZOOM_DEFAULT); - els.pageZoom.setAttribute("aria-valuenow", String(PAGE_ZOOM_DEFAULT)); - } - updatePageZoomResetButton(); - const port = pageViewScrollPane(); - if (port) { - port.scrollLeft = 0; - port.scrollTop = 0; - } - refreshPageViewLayout(); -} - -function updatePageZoomResetButton() { - if (!els.pageZoomReset) return; - els.pageZoomReset.textContent = `${pageZoomPercent}%`; - els.pageZoomReset.disabled = pageZoomPercent === PAGE_ZOOM_DEFAULT; -} - -function resetViewer() { - setDemoLoading(false); - clearFileCatalog(); - filePaneUserToggled = false; - selectedElementId = null; - pagePanDrag = null; - pagePanSuppressClick = false; - showReadingFurniture = true; - showReadingBackground = true; - syncReadingLayerCheckboxes(); - resetPageZoom(); - setDocLabel(null); - setDocumentOpen(false); - document.body.classList.remove("has-page-view"); - closeAllSettings(); - setToolbarOptionsOpen(false); - if (els.markupPane) els.markupPane.innerHTML = ""; - if (els.renderedPane) els.renderedPane.innerHTML = ""; - if (els.pagePane) els.pagePane.innerHTML = ""; - setPageIndicator(1, 1); - if (els.btnPrev) els.btnPrev.disabled = true; - if (els.btnNext) els.btnNext.disabled = true; - if (els.filePane) els.filePane.replaceChildren(); - updateFileView(); - applyPaneLayout(); -} - -function setPageViewVisible(visible) { - document.body.classList.toggle("has-page-view", visible); - syncPagePaneControls(); - syncToolbarPaneCheckboxes(); - if (!visible) setPageSettingsOpen(false); - applyPaneLayout(); - syncLayoutSubtoggles(); -} - -function syncPagePaneControls() { - const pageVisible = isPaneVisible("page"); - if (els.settingsToggle) els.settingsToggle.hidden = !pageVisible; - if (els.pageZoomLabel) els.pageZoomLabel.hidden = !pageVisible; - if (pageVisible) updatePageZoomResetButton(); - if (els.pagePane) els.pagePane.tabIndex = pageVisible ? 0 : -1; -} - -function setPageSettingsOpen(open) { - pageSettingsOpen = open; - if (els.pageSettingsLayer) els.pageSettingsLayer.hidden = !open; - if (els.settingsToggle) els.settingsToggle.setAttribute("aria-expanded", String(open)); -} - -function setReadingSettingsOpen(open) { - readingSettingsOpen = open; - if (els.readingSettingsLayer) els.readingSettingsLayer.hidden = !open; - if (els.readingSettingsToggle) els.readingSettingsToggle.setAttribute("aria-expanded", String(open)); -} - -function syncLayoutSubtoggles() { - const layoutEnabled = Boolean(state?.hasPageView && showAllBboxes); - for (const label of [ - els.showLayoutBadgesLabel, - els.showPictureContentsLabel, - els.showTableContentsLabel, - els.showFragmentLinksLabel, - els.showCaptionLinksLabel, - els.showXrefLinksLabel, - els.showReadingOrderLabel, - ]) { - if (!label) continue; - label.classList.toggle("settings-option-disabled", !layoutEnabled); - const input = label.querySelector("input"); - if (input) input.disabled = !layoutEnabled; - } - - const readingOrderEnabled = layoutEnabled && showReadingOrder; - for (const label of [els.readingOrderArrowsLabel, els.readingOrderGlobalLabel]) { - if (!label) continue; - label.classList.toggle("settings-option-disabled", !readingOrderEnabled); - const input = label.querySelector("input"); - if (input) input.disabled = !readingOrderEnabled; - } -} - -function paneDef(key) { - const defs = { - file: { key: "file", el: els.paneFile, canShow: () => fileCatalog.length > 0 }, - page: { key: "page", el: els.panePageView, canShow: () => Boolean(state?.hasPageView) }, - markup: { key: "markup", el: els.paneMarkup, canShow: () => Boolean(state) }, - reading: { key: "reading", el: els.paneReading, canShow: () => Boolean(state) }, - }; - return defs[key]; -} - -function isPaneAvailable(key) { - const def = paneDef(key); - return def?.canShow() ?? false; -} - -function isPaneVisible(key) { - if (!isPaneAvailable(key)) return false; - return Boolean(userPaneVisible[key]); -} - -function visiblePaneKeys() { - return PANE_KEYS.filter((key) => isPaneVisible(key)); -} - -function paneMinRatio(key) { - return PANE_MIN_RATIO; -} - -function filePaneFitWidthPx() { - const probe = document.createElement("div"); - probe.style.cssText = "position:absolute;visibility:hidden;width:var(--file-pane-fit-width);"; - document.documentElement.appendChild(probe); - const px = probe.getBoundingClientRect().width; - probe.remove(); - return Math.ceil(px) || 108; -} - -function resolvedFilePaneWidthPx() { - const fit = filePaneFitWidthPx(); - return Math.max(fit, filePaneWidthPx ?? fit); -} - -function contentPaneFrWeights(keys) { - const contentKeys = keys.filter((key) => key !== "file"); - const weights = contentKeys.map((key) => paneRatios[paneRatioIndex(key)]); - const sum = weights.reduce((a, b) => a + b, 0) || contentKeys.length; - return weights.map((weight) => weight / sum); -} - -function paneRatioIndex(key) { - return PANE_KEYS.indexOf(key); -} - -function normalizePaneRatios() { - const sum = paneRatios.reduce((a, b) => a + b, 0); - if (sum <= 0) { - paneRatios = [...DEFAULT_PANE_RATIOS]; - return; - } - paneRatios = paneRatios.map((r) => r / sum); -} - -function visiblePaneRatios(keys) { - const weights = keys.map((key) => paneRatios[paneRatioIndex(key)]); - const sum = weights.reduce((a, b) => a + b, 0) || keys.length; - return weights.map((w) => w / sum); -} - -function loadLayoutPrefs() { - try { - const raw = localStorage.getItem(LAYOUT_STORAGE_KEY); - if (!raw) return; - const data = JSON.parse(raw); - if (data?.visible && typeof data.visible === "object") { - for (const key of PANE_KEYS) { - if (typeof data.visible[key] === "boolean") userPaneVisible[key] = data.visible[key]; - } - if (typeof data.visible.file === "boolean") filePaneUserToggled = true; - } - if (Array.isArray(data?.ratios)) { - const valid = data.ratios.every((n) => typeof n === "number" && n > 0); - if (valid && data.ratios.length === 4) { - paneRatios = [...data.ratios]; - normalizePaneRatios(); - } else if (valid && data.ratios.length === 3) { - paneRatios = [1, ...data.ratios]; - normalizePaneRatios(); - } - } - if (typeof data?.filePaneWidthPx === "number" && data.filePaneWidthPx > 0) { - filePaneWidthPx = data.filePaneWidthPx; - } - } catch { - /* ignore invalid stored layout */ - } -} - -function saveLayoutPrefs() { - try { - localStorage.setItem( - LAYOUT_STORAGE_KEY, - JSON.stringify({ visible: userPaneVisible, ratios: paneRatios, filePaneWidthPx }), - ); - } catch { - /* ignore quota / private mode */ - } -} - -function resetPaneLayout() { - filePaneUserToggled = false; - userPaneVisible = { - file: defaultFilePaneVisible(), - page: true, - markup: true, - reading: true, - }; - paneRatios = [...DEFAULT_PANE_RATIOS]; - normalizePaneRatios(); - filePaneWidthPx = null; - setReadingSettingsOpen(false); - syncPagePaneControls(); - syncToolbarPaneCheckboxes(); - saveLayoutPrefs(); - applyPaneLayout(); -} - -function isLayoutStacked() { - return Boolean(layoutStackQuery?.matches); -} - -function initLayoutStackListener() { - if (!els.main) return; - layoutStackQuery = window.matchMedia(`(max-width: ${LAYOUT_STACK_BREAKPOINT_PX}px)`); - const onChange = () => applyPaneLayout(); - layoutStackQuery.addEventListener("change", onChange); - onChange(); -} - -function setToolbarOptionsOpen(open) { - toolbarOptionsOpen = open; - if (els.toolbarOptionsPanel) els.toolbarOptionsPanel.hidden = !open; - if (els.toolbarOptionsBtn) els.toolbarOptionsBtn.setAttribute("aria-expanded", String(open)); -} - -function syncToolbarPaneCheckboxes() { - if (els.toggleFilePane) { - const available = isPaneAvailable("file"); - els.toggleFilePane.checked = available && userPaneVisible.file; - els.toggleFilePane.disabled = !available; - } - if (els.toggleFilePaneLabel) { - els.toggleFilePaneLabel.classList.toggle("toolbar-options-item-disabled", !isPaneAvailable("file")); - } - if (els.togglePagePane) { - const available = isPaneAvailable("page"); - els.togglePagePane.checked = available && userPaneVisible.page; - els.togglePagePane.disabled = !available; - } - if (els.togglePagePaneLabel) { - els.togglePagePaneLabel.classList.toggle("toolbar-options-item-disabled", !isPaneAvailable("page")); - } - if (els.toggleMarkupPane) { - els.toggleMarkupPane.checked = userPaneVisible.markup; - els.toggleMarkupPane.disabled = !state; - } - if (els.toggleReadingPane) { - els.toggleReadingPane.checked = userPaneVisible.reading; - els.toggleReadingPane.disabled = !state; - } - for (const label of [ - els.toggleFilePaneLabel, - els.togglePagePaneLabel, - document.getElementById("toggle-markup-pane-label"), - document.getElementById("toggle-reading-pane-label"), - ]) { - if (!label) continue; - const input = label.querySelector("input"); - label.classList.toggle("toolbar-options-item-disabled", Boolean(input?.disabled)); - } - if (els.resetPaneLayoutBtn) els.resetPaneLayoutBtn.disabled = !state; -} - -function paneKeysAdjacent(leftKey, rightKey) { - const leftIdx = PANE_KEYS.indexOf(leftKey); - const rightIdx = PANE_KEYS.indexOf(rightKey); - return leftIdx >= 0 && rightIdx === leftIdx + 1; -} - -function onlyHiddenPanesBetween(leftKey, rightKey) { - const leftIdx = PANE_KEYS.indexOf(leftKey); - const rightIdx = PANE_KEYS.indexOf(rightKey); - if (leftIdx < 0 || rightIdx <= leftIdx) return false; - for (let i = leftIdx + 1; i < rightIdx; i++) { - if (isPaneVisible(PANE_KEYS[i])) return false; - } - return true; -} - -function shouldShowSplitterBetween(leftKey, rightKey) { - if (!isPaneVisible(leftKey) || !isPaneVisible(rightKey)) return false; - if (paneKeysAdjacent(leftKey, rightKey)) return true; - return onlyHiddenPanesBetween(leftKey, rightKey); -} - -function splitterForLayoutGap(leftKey, rightKey) { - if (!shouldShowSplitterBetween(leftKey, rightKey)) return null; - const leftIdx = PANE_KEYS.indexOf(leftKey); - if (leftIdx < 0) return null; - return els.splitters[leftIdx] ?? null; -} - -function visiblePaneNeighborAfter(key) { - const idx = PANE_KEYS.indexOf(key); - if (idx < 0) return null; - for (let i = idx + 1; i < PANE_KEYS.length; i++) { - const neighbor = PANE_KEYS[i]; - if (isPaneVisible(neighbor)) return neighbor; - } - return null; -} - -function visiblePaneNeighborBefore(key) { - const idx = PANE_KEYS.indexOf(key); - if (idx < 0) return null; - for (let i = idx - 1; i >= 0; i--) { - const neighbor = PANE_KEYS[i]; - if (isPaneVisible(neighbor)) return neighbor; - } - return null; -} - -function resolvedPhysicalSplitterKeys(physicalSplitterIndex) { - const leftPhysical = PANE_KEYS[physicalSplitterIndex]; - const rightPhysical = PANE_KEYS[physicalSplitterIndex + 1]; - if (!leftPhysical || !rightPhysical) return null; - - const leftKey = isPaneVisible(leftPhysical) ? leftPhysical : visiblePaneNeighborBefore(rightPhysical); - const rightKey = isPaneVisible(rightPhysical) ? rightPhysical : visiblePaneNeighborAfter(leftPhysical); - if (!leftKey || !rightKey || leftKey === rightKey) return null; - if (!shouldShowSplitterBetween(leftKey, rightKey)) return null; - - const canonical = splitterForLayoutGap(leftKey, rightKey); - if (!canonical || canonical !== els.splitters[physicalSplitterIndex]) return null; - return { leftKey, rightKey }; -} - -function resetPaneGridStyles() { - for (const key of PANE_KEYS) { - const el = paneDef(key)?.el; - if (el) { - el.style.gridColumn = ""; - el.style.gridRow = ""; - } - } - for (const splitter of els.splitters) { - if (!splitter) continue; - splitter.style.gridColumn = ""; - splitter.style.gridRow = ""; - splitter.hidden = true; - } -} - -function setUserPaneVisible(key, visible) { - userPaneVisible[key] = visible; - if (key === "file") filePaneUserToggled = true; - if (key === "page") syncPagePaneControls(); - if (key === "reading" && !visible) setReadingSettingsOpen(false); - syncToolbarPaneCheckboxes(); - saveLayoutPrefs(); - applyPaneLayout(); -} - -function applyPaneLayout() { - if (!els.main) return; - - const stacked = document.body.classList.contains("viewer-loaded") && isLayoutStacked(); - els.main.classList.toggle("layout-stacked", stacked); - - for (const key of PANE_KEYS) { - const def = paneDef(key); - if (!def?.el) continue; - def.el.hidden = !isPaneVisible(key); - def.el.classList.remove("pane-layout-last"); - } - - if (!document.body.classList.contains("viewer-loaded")) { - resetPaneGridStyles(); - els.main.style.gridTemplateColumns = ""; - els.main.style.gridTemplateRows = ""; - return; - } - - let keys = visiblePaneKeys(); - if (!keys.length) { - userPaneVisible.markup = true; - keys = visiblePaneKeys(); - } - - const lastKey = keys[keys.length - 1]; - paneDef(lastKey)?.el?.classList.add("pane-layout-last"); - - resetPaneGridStyles(); - - if (stacked) { - els.main.style.gridTemplateRows = ""; - els.main.style.gridTemplateColumns = "1fr"; - let row = 1; - for (const key of keys) { - const def = paneDef(key); - if (!def?.el) continue; - def.el.style.gridRow = String(row++); - } - refreshPageViewLayout(); - if (els.readingSettingsToggle) { - els.readingSettingsToggle.hidden = !state || !isPaneVisible("reading"); - } - return; - } - - const contentFr = contentPaneFrWeights(keys); - const cols = []; - let contentFrIndex = 0; - keys.forEach((key, index) => { - if (key === "file") { - cols.push(`${resolvedFilePaneWidthPx()}px`); - } else { - cols.push(`minmax(0, ${contentFr[contentFrIndex++].toFixed(6)}fr)`); - } - if (index < keys.length - 1 && shouldShowSplitterBetween(keys[index], keys[index + 1])) { - cols.push("1px"); - } - }); - els.main.style.gridTemplateColumns = cols.join(" "); - els.main.style.gridTemplateRows = "minmax(0, 1fr)"; - - let col = 1; - keys.forEach((key, index) => { - const def = paneDef(key); - if (!def?.el) return; - def.el.style.gridColumn = String(col); - col += 1; - if (index < keys.length - 1) { - const leftKey = keys[index]; - const rightKey = keys[index + 1]; - if (!shouldShowSplitterBetween(leftKey, rightKey)) return; - const splitter = splitterForLayoutGap(leftKey, rightKey); - if (splitter) { - splitter.hidden = false; - splitter.style.gridColumn = String(col); - col += 1; - } - } - }); - - refreshPageViewLayout(); - if (els.readingSettingsToggle) { - els.readingSettingsToggle.hidden = !state || !isPaneVisible("reading"); - } -} - -function initToolbarOptions() { - if (!els.toolbarOptionsBtn || !els.toolbarOptionsPanel) return; - - els.toolbarOptionsBtn.addEventListener("click", (e) => { - e.stopPropagation(); - setToolbarOptionsOpen(!toolbarOptionsOpen); - }); - - document.addEventListener("click", (e) => { - if (!toolbarOptionsOpen) return; - if (e.target instanceof Node && els.toolbarOptionsPanel.contains(e.target)) return; - if (e.target instanceof Node && els.toolbarOptionsBtn.contains(e.target)) return; - setToolbarOptionsOpen(false); - }); - - const onToggle = (key, input) => { - input?.addEventListener("change", () => { - if (!state) return; - const nextKeys = PANE_KEYS.filter((k) => (k === key ? input.checked : userPaneVisible[k] && isPaneAvailable(k))); - if (!nextKeys.length) { - input.checked = true; - return; - } - setUserPaneVisible(key, input.checked); - }); - }; - - onToggle("file", els.toggleFilePane); - onToggle("page", els.togglePagePane); - onToggle("markup", els.toggleMarkupPane); - onToggle("reading", els.toggleReadingPane); - els.resetPaneLayoutBtn?.addEventListener("click", () => { - if (!state) return; - resetPaneLayout(); - }); - syncToolbarPaneCheckboxes(); -} - -function initPaneSplitters() { - for (const [index, splitter] of els.splitters.entries()) { - if (!splitter) continue; - splitter.addEventListener("pointerdown", (e) => startPaneDrag(e, index)); - } - window.addEventListener("pointermove", onPaneDragMove); - window.addEventListener("pointerup", endPaneDrag); - window.addEventListener("pointercancel", endPaneDrag); -} - -function startPaneDrag(e, physicalSplitterIndex) { - if (e.button !== 0 || isLayoutStacked() || !document.body.classList.contains("viewer-loaded")) return; - - const resolved = resolvedPhysicalSplitterKeys(physicalSplitterIndex); - if (!resolved) return; - - const { leftKey, rightKey } = resolved; - - normalizePaneRatios(); - const leftIndex = paneRatioIndex(leftKey); - const rightIndex = paneRatioIndex(rightKey); - const dragState = { - physicalSplitterIndex, - leftKey, - rightKey, - startX: e.clientX, - leftStart: paneRatios[leftIndex], - rightStart: paneRatios[rightIndex], - pointerId: e.pointerId, - }; - if (leftKey === "file") { - dragState.leftStartPx = resolvedFilePaneWidthPx(); - } else if (rightKey === "file") { - dragState.rightStartPx = resolvedFilePaneWidthPx(); - } - paneDrag = dragState; - - e.preventDefault(); - e.currentTarget.setPointerCapture(e.pointerId); - e.currentTarget.classList.add("is-dragging"); - document.body.classList.add("pane-drag-active"); -} - -function contentPaneAvailableWidthPx() { - if (!els.main) return 1; - const rect = els.main.getBoundingClientRect(); - const keys = visiblePaneKeys(); - let reserved = 0; - if (keys.includes("file")) reserved += resolvedFilePaneWidthPx(); - for (let i = 0; i < keys.length - 1; i++) { - if (shouldShowSplitterBetween(keys[i], keys[i + 1])) reserved += 1; - } - return Math.max(rect.width - reserved, 1); -} - -function onPaneDragMove(e) { - if (!paneDrag || e.pointerId !== paneDrag.pointerId || !els.main) return; - - if (paneDrag.leftKey === "file" && typeof paneDrag.leftStartPx === "number") { - filePaneWidthPx = Math.max(filePaneFitWidthPx(), paneDrag.leftStartPx + (e.clientX - paneDrag.startX)); - applyPaneLayout(); - return; - } - if (paneDrag.rightKey === "file" && typeof paneDrag.rightStartPx === "number") { - filePaneWidthPx = Math.max(filePaneFitWidthPx(), paneDrag.rightStartPx - (e.clientX - paneDrag.startX)); - applyPaneLayout(); - return; - } - - const keys = visiblePaneKeys(); - const contentFr = contentPaneFrWeights(keys); - const contentKeys = keys.filter((key) => key !== "file"); - const leftContentIndex = contentKeys.indexOf(paneDrag.leftKey); - if (leftContentIndex < 0 || leftContentIndex + 1 >= contentFr.length) return; - - const pairFrTotal = contentFr[leftContentIndex] + contentFr[leftContentIndex + 1]; - if (!(pairFrTotal > 0)) return; - - const pairPixels = Math.max(contentPaneAvailableWidthPx() * pairFrTotal, 1); - const deltaRatio = (e.clientX - paneDrag.startX) / pairPixels; - - const leftIndex = paneRatioIndex(paneDrag.leftKey); - const rightIndex = paneRatioIndex(paneDrag.rightKey); - const pairStoredTotal = paneDrag.leftStart + paneDrag.rightStart; - if (!(pairStoredTotal > 0) || leftIndex < 0 || rightIndex < 0) return; - - let nextLeft = paneDrag.leftStart + deltaRatio * pairStoredTotal; - const leftMin = Math.min(paneMinRatio(paneDrag.leftKey), pairStoredTotal / 2); - const rightMin = Math.min(paneMinRatio(paneDrag.rightKey), pairStoredTotal / 2); - nextLeft = Math.min(Math.max(nextLeft, leftMin), pairStoredTotal - rightMin); - paneRatios[leftIndex] = nextLeft; - paneRatios[rightIndex] = pairStoredTotal - nextLeft; - applyPaneLayout(); -} - -function endPaneDrag(e) { - if (!paneDrag || e.pointerId !== paneDrag.pointerId) return; - const splitter = els.splitters[paneDrag.physicalSplitterIndex]; - splitter?.classList.remove("is-dragging"); - if (splitter?.hasPointerCapture(e.pointerId)) splitter.releasePointerCapture(e.pointerId); - paneDrag = null; - document.body.classList.remove("pane-drag-active"); - normalizePaneRatios(); - saveLayoutPrefs(); -} - -function goToPage(n) { - if (!state) return; - setPageSettingsOpen(false); - const page = Math.min(Math.max(1, n), state.pageCount); - state.currentPage = page; - renderPage(page); -} - -function resetPageNumberInput() { - if (!els.pageNumberInput) return; - els.pageNumberInput.value = state ? String(state.currentPage) : "1"; -} - -function commitPageNumberInput() { - if (!state || !els.pageNumberInput) return; - const n = Number.parseInt(els.pageNumberInput.value.trim(), 10); - if (!Number.isFinite(n)) { - resetPageNumberInput(); - return; - } - goToPage(n); -} - -function setPageIndicator(pageNum, pageCount) { - if (!els.pageIndicator) return; - if (els.pageCountIndicator) els.pageCountIndicator.textContent = `\u00A0of ${pageCount}`; - if (els.pageNumberInput) { - const digits = Math.max(1, String(pageCount).length); - els.pageNumberInput.style.setProperty("--page-num-digits", String(digits)); - els.pageNumberInput.disabled = !state; - if (document.activeElement !== els.pageNumberInput) { - els.pageNumberInput.value = String(pageNum); - } - } -} - -function renderPage(pageNum) { - if (!state) return; - const { segments, pageImages, pageCount, defaultResolution } = state; - const idx = pageNum - 1; - const segment = segments[idx] ?? []; - selectedElementId = null; - - setPageIndicator(pageNum, pageCount); - if (els.btnPrev) els.btnPrev.disabled = pageNum <= 1; - if (els.btnNext) els.btnNext.disabled = pageNum >= pageCount; - - if (!els.markupPane || !els.renderedPane || !els.pagePane) return; - - els.markupPane.innerHTML = ""; - const elementIds = assignElementIds(segment); - state.elementIds = elementIds; - state.idToElement = invertElementIds(elementIds); - - if (segmentHasMarkup(segment)) { - els.markupPane.appendChild(buildMarkupView(segment, elementIds)); - els.renderedPane.innerHTML = ""; - els.renderedPane.appendChild(buildRenderedView(segment, elementIds)); - } else { - els.markupPane.innerHTML = `
${NO_MARKUP}
`; - els.renderedPane.innerHTML = `
${NO_MARKUP}
`; - } - - els.pagePane.innerHTML = ""; - if (state.hasPageView) { - const imageUrl = pageImages.get(pageNum); - if (imageUrl) { - const port = document.createElement("div"); - port.className = "page-view-port"; - const wrap = document.createElement("div"); - wrap.className = "page-view"; - const img = document.createElement("img"); - img.alt = `Page ${pageNum}`; - - const onImageReady = () => { - if (img.dataset.layoutGeneration === String(pageNum)) return; - img.dataset.layoutGeneration = String(pageNum); - - applyPageImageSize(img, els.pagePane); - - const boxes = collectBoundingBoxes(segment, defaultResolution, elementIds); - const existing = wrap.querySelector("svg.overlay"); - if (existing) existing.remove(); - const readingOrderSteps = collectReadingOrderSteps( - segment, - elementIds, - boxes, - state.readingOrder, - readingOrderGlobalNumbering, - state.readingOrderDisplayNumbers, - ); - state.pageViewOverlay = { boxes, readingOrderSteps }; - if (boxes.length) { - wrap.appendChild( - buildOverlay( - img, - boxes, - collectCaptionLinks(segment, elementIds, boxes), - collectXrefLinks(segment, elementIds, boxes), - readingOrderSteps, - collectFragmentLinks(segment, elementIds, boxes, pageNum, state.threadPagesById), - collectFragmentNavItems(segment, elementIds, boxes), - defaultResolution, - ), - ); - } - syncPageViewChrome(img); - const pending = state.pendingSelectElement; - if (pending) { - state.pendingSelectElement = null; - const id = findElementIdOnPage(pending); - if (id) selectElement(id); - } - }; - - img.addEventListener("load", onImageReady, { once: true }); - wrap.appendChild(img); - port.appendChild(wrap); - els.pagePane.appendChild(port); - img.src = imageUrl; - if (img.complete) onImageReady(); - - if (!pagePaneResizeObserver) { - pagePaneResizeObserver = new ResizeObserver(() => { - refreshPageViewLayout(); - }); - pagePaneResizeObserver.observe(els.pagePane); - } - } else { - els.pagePane.innerHTML = `
${NO_IMAGE}
`; - } - } -} - -function pageZoomFactor() { - return pageZoomPercent / PAGE_ZOOM_DEFAULT; -} - -/** - * Convert a legacy overlay length (image pixels as tuned on the demo pages) to SVG user - * units for the current page. Matches the old demo on-screen size, stays zoom-stable, - * and scales with page resolution so other images look the same. - */ -function overlayUserLength(baseUserPx, img = els.pagePane?.querySelector(".page-view img")) { - const zoom = pageZoomFactor(); - if (!(zoom > 0)) return baseUserPx; - if (!img?.naturalWidth || !img.naturalHeight || !els.pagePane) { - return baseUserPx / zoom; - } - - const { w: paneW, h: paneH } = paneContentSize(els.pagePane); - if (!(paneW > 0 && paneH > 0)) return baseUserPx / zoom; - - const refFit = Math.min( - (paneW - PAGE_VIEW_BORDER_PX) / OVERLAY_REF_IMAGE_WIDTH, - (paneH - PAGE_VIEW_BORDER_PX) / OVERLAY_REF_IMAGE_HEIGHT, - ); - const fitScale = getCachedFitScale(img, els.pagePane); - if (!(refFit > 0) || !(fitScale > 0)) return baseUserPx / zoom; - - return (baseUserPx * refFit) / (fitScale * zoom); -} - -function computePageFitScale(img, pane) { - return getCachedFitScale(img, pane); -} - -function resetPageLayoutCache() { - pageLayoutCache = null; -} - -function paneContentSize(pane) { - const style = getComputedStyle(pane); - const padX = parseFloat(style.paddingLeft) + parseFloat(style.paddingRight); - const padY = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom); - return { - w: pane.clientWidth - padX, - h: pane.clientHeight - padY, - }; -} - -function getCachedFitScale(img, pane) { - const { w: paneW, h: paneH } = paneContentSize(pane); - const imgW = img.naturalWidth; - const imgH = img.naturalHeight; - if ( - pageLayoutCache && - pageLayoutCache.paneW === paneW && - pageLayoutCache.paneH === paneH && - pageLayoutCache.imgW === imgW && - pageLayoutCache.imgH === imgH - ) { - return pageLayoutCache.fitScale; - } - const fitScale = - paneW > 0 && paneH > 0 && imgW > 0 && imgH > 0 - ? Math.min( - (paneW - PAGE_VIEW_BORDER_PX) / imgW, - (paneH - PAGE_VIEW_BORDER_PX) / imgH, - ) - : 1; - pageLayoutCache = { paneW, paneH, imgW, imgH, fitScale }; - return fitScale; -} - -function applyPageImageSize(img, pane = els.pagePane) { - if (!img?.naturalWidth || !img.naturalHeight) return false; - pageZoomPercent = Math.max(PAGE_ZOOM_DEFAULT, pageZoomPercent); - const fitScale = getCachedFitScale(img, pane); - const scale = fitScale * (pageZoomPercent / PAGE_ZOOM_DEFAULT); - const w = Math.floor(img.naturalWidth * scale); - const h = Math.floor(img.naturalHeight * scale); - const nextW = `${w}px`; - const nextH = `${h}px`; - const unchanged = img.style.width === nextW && img.style.height === nextH; - if (!unchanged) { - img.style.width = nextW; - img.style.height = nextH; - img.style.maxWidth = "none"; - img.style.maxHeight = "none"; - } - img.dataset.layoutReady = "1"; - return !unchanged; -} - -function syncPageViewChrome(img) { - syncOverlayBadges(img); - updatePagePanePanCursor(); - applyBboxVisibility(); -} - -function applyPageLayout(img, pane = els.pagePane) { - applyPageImageSize(img, pane); - syncPageViewChrome(img); -} - -let pageLayoutFrame = 0; - -function refreshPageViewLayout() { - if (!els.pagePane) return; - cancelAnimationFrame(pageLayoutFrame); - pageLayoutFrame = requestAnimationFrame(() => { - pageLayoutFrame = 0; - const img = els.pagePane?.querySelector(".page-view img"); - if (img?.naturalWidth) applyPageLayout(img); - }); -} - -function overlayBadgeLayout(svg, text, fontSizeUser) { - const padXUser = overlayUserLength(OVERLAY_BADGE_PAD_X); - const padYUser = overlayUserLength(OVERLAY_BADGE_PAD_Y); - const probe = document.createElementNS("http://www.w3.org/2000/svg", "text"); - probe.setAttribute("class", "overlay-badge-label"); - probe.setAttribute("font-size", String(fontSizeUser)); - probe.setAttribute("font-weight", "700"); - probe.setAttribute("text-anchor", "start"); - probe.setAttribute("dominant-baseline", "text-before-edge"); - probe.setAttribute("visibility", "hidden"); - probe.textContent = String(text); - svg.appendChild(probe); - - let width; - let height; - try { - const bbox = probe.getBBox(); - width = bbox.width + padXUser * 2; - height = bbox.height + padYUser * 2; - } catch { - const label = String(text); - width = overlayUserLength(label.length * OVERLAY_BADGE_FONT_SIZE * 0.55 + OVERLAY_BADGE_PAD_X * 2); - height = overlayUserLength(OVERLAY_BADGE_FONT_SIZE * 1.1 + OVERLAY_BADGE_PAD_Y * 2); - } - probe.remove(); - return { width, height }; -} - -function appendOverlayBadge(svg, anchorX, anchorY, text, { extraClass, elementId }) { - const fontSize = overlayUserLength(OVERLAY_BADGE_FONT_SIZE); - const { width, height } = overlayBadgeLayout(svg, text, fontSize); - const radius = overlayUserLength(OVERLAY_BADGE_RADIUS_SCREEN_PX); - - const badge = document.createElementNS("http://www.w3.org/2000/svg", "g"); - badge.setAttribute("class", `overlay-badge ${extraClass}`); - badge.setAttribute("data-element-id", elementId); - - const bg = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - bg.setAttribute("class", "overlay-badge-bg"); - bg.setAttribute("x", String(anchorX - width / 2)); - bg.setAttribute("y", String(anchorY - height / 2)); - bg.setAttribute("width", String(width)); - bg.setAttribute("height", String(height)); - bg.setAttribute("rx", String(radius)); - bg.setAttribute("ry", String(radius)); - badge.appendChild(bg); - - const label = document.createElementNS("http://www.w3.org/2000/svg", "text"); - label.setAttribute("class", "overlay-badge-label"); - label.setAttribute("x", String(anchorX)); - label.setAttribute("y", String(anchorY)); - label.setAttribute("font-size", String(fontSize)); - label.textContent = String(text); - badge.appendChild(label); - - svg.appendChild(badge); - return badge; -} - -function syncOverlayBadges(img) { - const svg = img.parentElement?.querySelector("svg.overlay"); - const meta = state?.pageViewOverlay; - if (!svg || !img.naturalWidth || !meta) return; - - svg.querySelectorAll(".element-badge, .reading-order-badge").forEach((badge) => badge.remove()); - - const fontSize = overlayUserLength(OVERLAY_BADGE_FONT_SIZE); - const badgeGap = overlayUserLength(2); - const readingOrderByElementId = new Map(); - if (showAllBboxes && showReadingOrder) { - for (const step of meta.readingOrderSteps ?? []) { - readingOrderByElementId.set(step.elementId, step); - } - } - - const boxes = sortedOverlayBoxes(meta.boxes ?? []); - - for (const b of boxes) { - const { x, y } = boxPixelRect(b, img); - let tagLayout = { width: 0 }; - if (showAllBboxes && showLayoutBadges) { - tagLayout = overlayBadgeLayout(svg, b.tag, fontSize); - appendOverlayBadge(svg, x, y, b.tag, { - extraClass: `element-badge ${kindClassForTag(b.kind)}`, - elementId: b.elementId, - }); - } - - const step = readingOrderByElementId.get(b.elementId); - if (step) { - const orderText = String(step.order); - const orderLayout = overlayBadgeLayout(svg, orderText, fontSize); - const orderAnchorX = showAllBboxes && showLayoutBadges - ? x + tagLayout.width / 2 + badgeGap + orderLayout.width / 2 - : x; - appendOverlayBadge(svg, orderAnchorX, y, orderText, { - extraClass: "reading-order-badge", - elementId: b.elementId, - }); - } - } -} - -function splitIntoSegments(root) { - const body = childElements(root).filter((el) => localName(el) !== "head"); - /** @type {Element[][]} */ - const segments = [[]]; - for (const el of body) { - if (localName(el) === "page_break") { - segments.push([]); - } else { - segments[segments.length - 1].push(el); - } - } - return segments.length ? segments : [[]]; -} - -function segmentHasMarkup(segment) { - return segment.some((el) => el.nodeType === Node.ELEMENT_NODE); -} - -function readDefaultResolution(head) { - const fallback = { width: 512, height: 512 }; - if (!head) return fallback; - const dr = childElements(head).find((el) => localName(el) === "default_resolution"); - if (!dr) return fallback; - const w = parseInt(dr.getAttribute("width") ?? "512", 10); - const h = parseInt(dr.getAttribute("height") ?? "512", 10); - return { - width: Number.isFinite(w) && w > 0 ? w : 512, - height: Number.isFinite(h) && h > 0 ? h : 512, - }; -} - -/** @returns {Map} */ -function assignElementIds(segment) { - const ids = new Map(); - let counter = 0; - walkElements(segment, (el) => { - ids.set(el, `el-${counter++}`); - }); - return ids; -} - -/** @returns {Map} */ -function invertElementIds(elementIds) { - const idToElement = new Map(); - for (const [el, id] of elementIds) idToElement.set(id, el); - return idToElement; -} - -function isSemanticElement(el) { - return SEMANTIC_TAGS.has(localName(el)); -} - -function isVirtualTextHost(el) { - const tag = localName(el); - return tag === "ldiv" || CELL_CONTENT_TAGS.has(tag); -} - -function xmlContains(target, ancestor) { - let node = target; - while (node) { - if (node === ancestor) return true; - node = node.parentNode; - } - return false; -} - -function findListVirtualTextHost(list, target) { - const nodes = [...list.childNodes]; - let i = skipContainerLevelHead(nodes, 0); - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE || localName(node) !== "ldiv") { - i += 1; - continue; - } - const ldiv = node; - i += 1; - const end = skipUntilListItemBoundary(nodes, i); - if (target === ldiv || nodes.slice(i, end).some((n) => xmlContains(target, n))) { - return ldiv; - } - i = end; - } - return null; -} - -function findTableVirtualTextHost(container, target) { - const nodes = [...container.childNodes]; - let i = skipContainerLevelHead(nodes, 0); - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE) { - i += 1; - continue; - } - const tag = localName(node); - if (tag === "nl" || CELL_SPAN_TAGS.has(tag) || !isCellToken(tag)) { - i += 1; - continue; - } - const cell = node; - i += 1; - const end = skipUntilCellBoundary(nodes, i); - if (target === cell || nodes.slice(i, end).some((n) => xmlContains(target, n))) { - return cell; - } - i = end; - } - return null; -} - -function isListOrOtslContainer(el) { - const tag = localName(el); - return tag === "list" || OTSL_CONTAINER_TAGS.has(tag); -} - -function findVirtualTextHost(xmlEl) { - let node = xmlEl; - while (node) { - const parent = node.parentElement; - if (!parent) return null; - const tag = localName(parent); - if (tag === "list") { - const host = findListVirtualTextHost(parent, xmlEl); - if (host) return host; - } - if (OTSL_CONTAINER_TAGS.has(tag)) { - const host = findTableVirtualTextHost(parent, xmlEl); - if (host) return host; - } - node = parent; - } - return null; -} - -function resolveSelectionElement(xmlEl) { - if (!xmlEl) return null; - if (isSemanticElement(xmlEl) || isVirtualTextHost(xmlEl)) return xmlEl; - - let node = xmlEl.parentElement; - while (node) { - const tag = localName(node); - if (tag === "doclang") break; - if (isSemanticElement(node) && !isListOrOtslContainer(node)) return node; - node = node.parentElement; - } - - const virtualHost = findVirtualTextHost(xmlEl); - if (virtualHost) return virtualHost; - - node = xmlEl.parentElement; - while (node) { - const tag = localName(node); - if (tag === "doclang") break; - if (isSemanticElement(node) || isVirtualTextHost(node)) return node; - node = node.parentElement; - } - - return null; -} - -function resolveSelectionElementId(rawElementId) { - if (!rawElementId || !state?.idToElement || !state.elementIds) return null; - const xmlEl = state.idToElement.get(rawElementId); - if (!xmlEl) return null; - const resolved = resolveSelectionElement(xmlEl); - return resolved ? state.elementIds.get(resolved) ?? null : null; -} - -function resolveMarkupClickTarget(eventTarget) { - if (!state) return null; - - const ghostText = eventTarget.closest(".markup-el-virtual-text"); - if (ghostText?.hasAttribute("data-element-id")) { - return ghostText.getAttribute("data-element-id"); - } - - const markupEl = eventTarget.closest(".markup-el[data-element-id]"); - if (!markupEl) return null; - return resolveSelectionElementId(markupEl.getAttribute("data-element-id")); -} - -function resolveRenderedClickTarget(eventTarget) { - const ghostText = eventTarget.closest(".rendered-el-virtual-text"); - if (ghostText?.hasAttribute("data-element-id")) { - return ghostText.getAttribute("data-element-id"); - } - - const renderedEl = eventTarget.closest(".rendered-el[data-element-id]"); - if (!renderedEl) return null; - return resolveSelectionElementId(renderedEl.getAttribute("data-element-id")); -} - -/** @returns {{ kind: string, tag: string, elementId: string, layer: string, x0: number, y0: number, x1: number, y1: number, resW: number, resH: number }[]} */ -function collectBoundingBoxes(segment, defaultResolution, elementIds) { - /** @type {{ kind: string, tag: string, elementId: string, layer: string, x0: number, y0: number, x1: number, y1: number, resW: number, resH: number }[]} */ - const boxes = []; - walkElements(segment, (el) => { - const locs = headLocations(el); - if (locs.length !== 4) return; - const elementId = elementIds.get(el); - if (!elementId) return; - pushBoundingBox( - boxes, - locs, - defaultResolution, - localName(el), - elementLabel(el), - elementId, - elementLayer(el), - ); - }); - walkElements(segment, (el) => { - const tag = localName(el); - if (tag === "list") collectListVirtualTextBoxes(el, defaultResolution, boxes, elementIds); - else if (tag === "table" || tag === "index" || tag === "tabular") { - collectTableVirtualTextBoxes(el, defaultResolution, boxes, elementIds); - } - }); - return boxes; -} - -/** @returns {{ captionBox: object, hostBox: object, captionElementId: string, hostElementId: string }[]} */ -function collectCaptionLinks(segment, elementIds, boxes) { - const boxById = new Map(boxes.map((b) => [b.elementId, b])); - /** @type {{ captionBox: object, hostBox: object, captionElementId: string, hostElementId: string }[]} */ - const links = []; - walkElements(segment, (el) => { - if (localName(el) !== "caption") return; - const captionId = elementIds.get(el); - const captionBox = captionId ? boxById.get(captionId) : null; - if (!captionBox) return; - const host = el.parentElement; - if (!host || headLocations(host).length !== 4) return; - const hostId = elementIds.get(host); - const hostBox = hostId ? boxById.get(hostId) : null; - if (!hostBox) return; - links.push({ - captionBox, - hostBox, - captionElementId: captionId, - hostElementId: hostId, - }); - }); - return links; -} - -/** @returns {{ fromBox: object, toBox: object, fromElementId: string, toElementId: string }[]} */ -function collectXrefLinks(segment, elementIds, boxes) { - const boxById = new Map(boxes.map((b) => [b.elementId, b])); - /** @type {Map} */ - const threadsById = new Map(); - - walkElements(segment, (el) => { - const elementId = elementIds.get(el); - const box = elementId ? boxById.get(elementId) : null; - if (!box) return; - for (const thread of childElements(el)) { - if (localName(thread) !== "thread") continue; - const threadId = thread.getAttribute("thread_id"); - if (!threadId) continue; - if (!threadsById.has(threadId)) threadsById.set(threadId, []); - threadsById.get(threadId).push({ elementId, box }); - } - }); - - /** @type {{ fromBox: object, toBox: object, fromElementId: string, toElementId: string }[]} */ - const links = []; - walkElements(segment, (el) => { - const xrefs = childElements(el).filter((c) => localName(c) === "xref"); - if (!xrefs.length) return; - - const from = findNearestLocatedBox(el, elementIds, boxById); - if (!from) return; - - for (const xref of xrefs) { - const threadId = xref.getAttribute("thread_id"); - if (!threadId) continue; - for (const { elementId: toId, box: toBox } of threadsById.get(threadId) ?? []) { - if (toId === from.elementId) continue; - links.push({ - fromBox: from.box, - toBox, - fromElementId: from.elementId, - toElementId: toId, - }); - } - } - }); - return links; -} - -/** @returns {{ fromBox: object, toBox: object | null, fromElementId: string, toElementId: string | null, threadId: string, targetCorner?: "tl" | "br" }[]} */ -function collectFragmentLinks(segment, elementIds, boxes, pageNum, threadPagesById) { - const boxById = new Map(boxes.map((b) => [b.elementId, b])); - /** @type {Map} */ - const onPage = new Map(); - - walkElements(segment, (el) => { - const thread = firstHeadChild(el, "thread"); - const threadId = thread?.getAttribute("thread_id"); - if (!threadId) return; - const elementId = elementIds.get(el); - const box = elementId ? boxById.get(elementId) : null; - if (!box) return; - if (!onPage.has(threadId)) onPage.set(threadId, []); - onPage.get(threadId).push({ elementId, box }); - }); - - const segmentEls = new Set(); - walkElements(segment, (el) => segmentEls.add(el)); - - /** @type {{ fromBox: object, toBox: object | null, fromElementId: string, toElementId: string | null, threadId: string, targetCorner?: "tl" | "br" }[]} */ - const links = []; - - for (const [threadId, members] of onPage) { - if (members.length >= 2) { - for (let i = 0; i < members.length - 1; i += 1) { - links.push({ - fromBox: members[i].box, - toBox: members[i + 1].box, - fromElementId: members[i].elementId, - toElementId: members[i + 1].elementId, - threadId, - }); - } - continue; - } - - if (members.length !== 1) continue; - const threadPages = threadPagesById.get(threadId); - if (!threadPages) continue; - const hasPrevious = [...threadPages].some((p) => p < pageNum); - const hasFollowing = [...threadPages].some((p) => p > pageNum); - if (!hasPrevious && !hasFollowing) continue; - - const base = { - fromBox: members[0].box, - toBox: null, - fromElementId: members[0].elementId, - toElementId: null, - threadId, - }; - if (hasPrevious) links.push({ ...base, targetCorner: "tl" }); - if (hasFollowing) links.push({ ...base, targetCorner: "br" }); - } - - return links; -} - -function isReadingOrderUnit(el) { - const tag = localName(el); - if (tag === "caption") return true; - if (HEAD_TAGS.has(tag) || tag === "location" || tag === "h_thread" || tag === "page_break") return false; - if (tag === "nl" || CELL_SPAN_TAGS.has(tag)) return false; - if (RENDER_FORMAT_TAGS.has(tag) || tag === "src" || tag === "checkbox") return false; - return true; -} - -/** @returns {Map} */ -function buildThreadsById(docRoot) { - const map = new Map(); - const roots = childElements(docRoot).filter((el) => localName(el) !== "head"); - walkElements(roots, (el) => { - for (const child of childElements(el)) { - if (localName(child) !== "thread") continue; - const threadId = child.getAttribute("thread_id"); - if (!threadId) continue; - if (!map.has(threadId)) map.set(threadId, []); - map.get(threadId).push(el); - } - }); - return map; -} - -/** @returns {Map} */ -function buildElementPageMap(segments) { - const elementPage = new Map(); - segments.forEach((segment, idx) => { - const pageNum = idx + 1; - walkElements(segment, (el) => elementPage.set(el, pageNum)); - }); - return elementPage; -} - -/** @returns {Map>} */ -function buildThreadPagesById(docRoot, elementPageByEl) { - /** @type {Map>} */ - const threadPagesById = new Map(); - for (const [threadId, elements] of buildThreadsById(docRoot)) { - const pages = new Set(); - for (const el of elements) { - const page = elementPageByEl.get(el); - if (page) pages.add(page); - } - if (pages.size) threadPagesById.set(threadId, pages); - } - return threadPagesById; -} - -/** @returns {Map} */ -function buildThreadNavByElement(docRoot) { - /** @type {Map} */ - const nav = new Map(); - for (const [, elements] of buildThreadsById(docRoot)) { - for (let i = 0; i < elements.length; i += 1) { - nav.set(elements[i], { - prev: i > 0 ? elements[i - 1] : null, - next: i < elements.length - 1 ? elements[i + 1] : null, - }); - } - } - return nav; -} - -/** @returns {{ elementId: string, box: object, hasPrev: boolean, hasNext: boolean }[]} */ -function collectFragmentNavItems(segment, elementIds, boxes) { - if (!state?.threadNavByElement) return []; - const boxById = new Map(boxes.map((b) => [b.elementId, b])); - /** @type {{ elementId: string, box: object, hasPrev: boolean, hasNext: boolean }[]} */ - const items = []; - walkElements(segment, (el) => { - const nav = state.threadNavByElement.get(el); - if (!nav || (!nav.prev && !nav.next)) return; - const elementId = elementIds.get(el); - const box = elementId ? boxById.get(elementId) : null; - if (!box) return; - items.push({ - elementId, - box, - hasPrev: nav.prev !== null, - hasNext: nav.next !== null, - }); - }); - return items; -} - -function findElementIdOnPage(el) { - if (!state?.elementIds) return null; - for (const [node, id] of state.elementIds) { - if (node === el) return id; - } - return null; -} - -function navigateThreadFragment(elementId, direction) { - const el = state?.idToElement?.get(elementId); - if (!el) return; - const nav = state?.threadNavByElement?.get(el); - if (!nav) return; - const target = direction === "prev" ? nav.prev : nav.next; - if (!target) return; - const page = state.elementPageByEl.get(target); - if (!page) return; - if (page === state.currentPage) { - const id = findElementIdOnPage(target); - if (id) selectElement(id); - return; - } - state.pendingSelectElement = target; - goToPage(page); -} - -/** @returns {Element[]} */ -function computeReadingOrder(docRoot) { - const bodyChildren = childElements(docRoot).filter((el) => localName(el) !== "head"); - const threadsById = buildThreadsById(docRoot); - const consumedViaXref = new Set(); - /** @type {Element[]} */ - const order = []; - - function record(el) { - if (!isReadingOrderUnit(el)) return; - order.push(el); - } - - function consumeThread(threadId) { - for (const target of threadsById.get(threadId) ?? []) { - if (consumedViaXref.has(target)) continue; - consumedViaXref.add(target); - visitElement(target); - } - } - - function walkChildren(parent) { - for (const child of parent.childNodes) { - if (child.nodeType !== Node.ELEMENT_NODE) continue; - const tag = localName(child); - if (tag === "xref") { - const threadId = child.getAttribute("thread_id"); - if (threadId) consumeThread(threadId); - continue; - } - if (tag === "page_break") continue; - visitElement(child); - } - } - - function visitElement(el) { - record(el); - walkChildren(el); - } - - for (const el of bodyChildren) { - if (localName(el) === "page_break") continue; - if (consumedViaXref.has(el)) continue; - visitElement(el); - } - - return order; -} - -function hasVirtualTextLocations(el) { - const parent = el.parentElement; - if (!parent) return false; - const nodes = [...parent.childNodes]; - const idx = nodes.indexOf(el); - if (idx < 0) return false; - return parseElementHeadAt(nodes, idx + 1) !== null; -} - -function isVirtualTextOverlayUnit(el) { - if (headLocations(el).length === 4) return false; - if (!hasVirtualTextLocations(el)) return false; - const tag = localName(el); - if (tag === "ldiv" && localName(el.parentElement) === "list") return true; - if (isCellToken(tag) && tag !== "nl" && !CELL_SPAN_TAGS.has(tag)) { - return OTSL_CONTAINER_TAGS.has(localName(el.parentElement)); - } - return false; -} - -function isPictureContentElement(el) { - if (!el) return false; - const tag = localName(el); - if (tag === "picture" || tag === "caption") return false; - let node = el.parentElement; - while (node) { - if (localName(node) === "picture") return true; - node = node.parentElement; - } - return false; -} - -function isTableContentElement(el) { - if (!el) return false; - const tag = localName(el); - if (OTSL_CONTAINER_TAGS.has(tag) || tag === "caption") return false; - let node = el.parentElement; - while (node) { - if (OTSL_CONTAINER_TAGS.has(localName(node))) return true; - node = node.parentElement; - } - return false; -} - -function isPictureOrTableContentElement(el) { - return isPictureContentElement(el) || isTableContentElement(el); -} - -function isReadingOrderOverlayUnit(el) { - if (!isReadingOrderUnit(el)) return false; - if (isPictureOrTableContentElement(el)) return false; - if (headLocations(el).length === 4) return true; - return isVirtualTextOverlayUnit(el); -} - -/** @returns {Map} */ -function computeReadingOrderDisplayNumbers(readingOrder) { - const numbers = new Map(); - let n = 0; - for (const el of readingOrder) { - if (!isReadingOrderOverlayUnit(el)) continue; - n += 1; - numbers.set(el, n); - } - return numbers; -} - -/** @returns {{ order: number, box: object, elementId: string }[]} */ -function collectReadingOrderSteps( - segment, - elementIds, - boxes, - readingOrder, - globalNumbering = true, - displayNumbers = null, -) { - const boxById = new Map(boxes.map((b) => [b.elementId, b])); - /** @type {{ order: number, box: object, elementId: string }[]} */ - const steps = []; - let pageOrder = 0; - - readingOrder.forEach((el) => { - if (isPictureOrTableContentElement(el)) return; - const elementId = elementIds.get(el); - if (!elementId) return; - const box = boxById.get(elementId); - if (!box) return; - pageOrder += 1; - steps.push({ - order: globalNumbering ? (displayNumbers?.get(el) ?? pageOrder) : pageOrder, - box, - elementId, - }); - }); - - return steps; -} - -function pushBoundingBox(boxes, locs, defaultResolution, kind, tag, elementId, layer = "body") { - const [x0el, y0el, x1el, y1el] = locs; - const resW = locationResolution(x0el, defaultResolution.width); - const resH = locationResolution(y0el, defaultResolution.height); - boxes.push({ - kind, - tag, - elementId, - layer, - x0: parseInt(x0el.getAttribute("value") ?? "0", 10), - y0: parseInt(y0el.getAttribute("value") ?? "0", 10), - x1: parseInt(x1el.getAttribute("value") ?? "0", 10), - y1: parseInt(y1el.getAttribute("value") ?? "0", 10), - resW, - resH, - }); -} - -function collectListVirtualTextBoxes(list, defaultResolution, boxes, elementIds) { - const nodes = [...list.childNodes]; - let i = skipContainerLevelHead(nodes, 0); - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE || localName(node) !== "ldiv") { - i += 1; - continue; - } - i += 1; - const head = parseElementHeadAt(nodes, i); - if (head) { - const elementId = elementIds.get(node); - if (elementId) { - pushBoundingBox(boxes, head.locs, defaultResolution, "text", "text", elementId, elementLayer(node)); - } - i = head.nextIndex; - } - i = skipUntilListItemBoundary(nodes, i); - } -} - -function collectTableVirtualTextBoxes(container, defaultResolution, boxes, elementIds) { - const nodes = [...container.childNodes]; - let i = skipContainerLevelHead(nodes, 0); - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE) { - i += 1; - continue; - } - const tag = localName(node); - if (!isCellToken(tag)) { - i += 1; - continue; - } - if (tag === "nl") { - i += 1; - continue; - } - i += 1; - const head = parseElementHeadAt(nodes, i); - if (head) { - const elementId = elementIds.get(node); - if (elementId) { - pushBoundingBox(boxes, head.locs, defaultResolution, "text", "text", elementId, elementLayer(node)); - } - i = head.nextIndex; - } - i = skipUntilCellBoundary(nodes, i); - } -} - -function skipContainerLevelHead(nodes, startIdx) { - let i = startIdx; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE) { - i += 1; - continue; - } - const tag = localName(node); - if (tag === "ldiv" || isCellToken(tag)) break; - if (HEAD_TAGS.has(tag) || tag === "location") { - i += 1; - continue; - } - break; - } - return i; -} - -function skipUntilListItemBoundary(nodes, startIdx) { - let i = startIdx; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType === Node.ELEMENT_NODE && localName(node) === "ldiv") break; - i += 1; - } - return i; -} - -function skipUntilCellBoundary(nodes, startIdx) { - let i = startIdx; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType === Node.ELEMENT_NODE && isCellToken(localName(node))) break; - i += 1; - } - return i; -} - -function isCellToken(tag) { - return CELL_TOKENS.has(tag); -} - -/** @returns {{ locs: Element[], nextIndex: number } | null} */ -function parseElementHeadAt(nodes, startIdx) { - const locs = []; - let i = startIdx; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE) { - i += 1; - continue; - } - const tag = localName(node); - if (tag === "location") { - locs.push(node); - i += 1; - if (locs.length === 4) return { locs, nextIndex: i }; - continue; - } - if (locs.length) break; - if (HEAD_TAGS.has(tag)) { - i += 1; - continue; - } - break; - } - return locs.length === 4 ? { locs, nextIndex: i } : null; -} - -function walkElements(nodes, fn) { - for (const node of nodes) { - if (node.nodeType !== Node.ELEMENT_NODE) continue; - fn(node); - walkElements(childElements(node), fn); - } -} - -function headLocations(el) { - return parseElementHeadAt([...el.childNodes], 0)?.locs ?? []; -} - -/** @returns {{ elementId: string, box: object } | null} */ -function findNearestLocatedBox(el, elementIds, boxById) { - let node = el; - while (node) { - const elementId = elementIds.get(node); - const box = elementId ? boxById.get(elementId) : null; - if (box) return { elementId, box }; - if (localName(node) === "doclang") break; - node = node.parentElement; - } - return null; -} - -function locationResolution(el, axisDefault) { - const r = parseInt(el.getAttribute("resolution") ?? String(axisDefault), 10); - return Number.isFinite(r) && r > 0 ? r : axisDefault; -} - -function headingLevel(el) { - return Math.min(Math.max(parseInt(el.getAttribute("level") ?? "1", 10) || 1, 1), 6); -} - -function elementLabel(el) { - if (isVirtualTextOverlayUnit(el)) return "text"; - const tag = localName(el); - if (tag === "heading" || tag === "field_heading") return `${tag}[${headingLevel(el)}]`; - const level = el.getAttribute("level"); - if (level) return `${tag}[${level}]`; - const cls = el.getAttribute("class"); - if (cls) return `${tag}.${cls}`; - return tag; -} - -function elementKindKey(kind) { - if (kind.startsWith("field_") || kind === "key" || kind === "value" || kind === "hint") return "field"; - if (kind === "tabular") return "table"; - const known = new Set([ - "text", "heading", "list", "ldiv", "table", "index", "formula", "code", "picture", - "group", "footnote", "page_header", "page_footer", "caption", - ]); - return known.has(kind) ? kind : "default"; -} - -function kindClassForTag(tag) { - return `kind-${elementKindKey(tag)}`; -} - -function bboxClassForKind(kind) { - return elementKindKey(kind); -} - -function boxPixelRect(b, img) { - const x = (b.x0 / b.resW) * img.naturalWidth; - const y = (b.y0 / b.resH) * img.naturalHeight; - const w = ((b.x1 - b.x0) / b.resW) * img.naturalWidth; - const h = ((b.y1 - b.y0) / b.resH) * img.naturalHeight; - return { x, y, w, h, area: w * h }; -} - -function overlayBoxPaintPriority(box) { - if (box.kind === "text") return 0; - if (box.kind === "list" || box.kind === "table" || box.kind === "index" || box.kind === "tabular") { - return 2; - } - return 1; -} - -function overlayLayerPriority(layer) { - if (layer === "background") return 0; - if (layer === "furniture") return 1; - return 2; -} - -function compareOverlayBoxPaintOrder(a, b) { - const byLayer = overlayLayerPriority(a.layer ?? "body") - overlayLayerPriority(b.layer ?? "body"); - if (byLayer !== 0) return byLayer; - const byPriority = overlayBoxPaintPriority(a) - overlayBoxPaintPriority(b); - if (byPriority !== 0) return byPriority; - if (selectedElementId) { - const aSelected = a.elementId === selectedElementId; - const bSelected = b.elementId === selectedElementId; - if (aSelected !== bSelected) return aSelected ? 1 : -1; - } - return 0; -} - -function sortedOverlayBoxes(boxes) { - return [...boxes].sort(compareOverlayBoxPaintOrder); -} - -function boxCenter(rect) { - return { x: rect.x + rect.w / 2, y: rect.y + rect.h / 2 }; -} - -function ensureArrowMarker(defs, markerId) { - if (defs.querySelector(`#${markerId}`)) return; - const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker"); - marker.setAttribute("id", markerId); - marker.setAttribute("viewBox", "0 0 6 6"); - marker.setAttribute("refX", "6"); - marker.setAttribute("refY", "3"); - marker.setAttribute("markerWidth", "5"); - marker.setAttribute("markerHeight", "5"); - marker.setAttribute("orient", "auto"); - const arrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path"); - arrowPath.setAttribute("d", "M0,0 L6,3 L0,6 Z"); - arrowPath.setAttribute("fill", "currentColor"); - marker.appendChild(arrowPath); - defs.appendChild(marker); -} - -function alignDashedLineToEnd(line, start, end) { - const dash = 6; - const gap = 4; - const period = dash + gap; - const len = Math.hypot(end.x - start.x, end.y - start.y); - if (!len) return; - line.setAttribute("stroke-dasharray", `${dash} ${gap}`); - const offset = len % period; - if (offset > 0.01) line.setAttribute("stroke-dashoffset", String(offset)); -} - -function ensureLayerHatchPatterns(defs) { - if (defs.querySelector("#layer-hatch")) return; - - const pattern = document.createElementNS("http://www.w3.org/2000/svg", "pattern"); - pattern.setAttribute("id", "layer-hatch"); - pattern.setAttribute("width", "16"); - pattern.setAttribute("height", "16"); - pattern.setAttribute("patternUnits", "userSpaceOnUse"); - pattern.setAttribute("patternTransform", "rotate(45 8 8)"); - const stripe = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - stripe.setAttribute("class", "layer-hatch-stripe"); - stripe.setAttribute("x", "10"); - stripe.setAttribute("y", "-4"); - stripe.setAttribute("width", "6"); - stripe.setAttribute("height", "24"); - pattern.appendChild(stripe); - defs.appendChild(pattern); -} - -function ensureOverlayDefs(svg) { - let defs = svg.querySelector("defs"); - if (!defs) { - defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); - svg.insertBefore(defs, svg.firstChild); - } - return defs; -} - -function appendOverlayLinks(svg, img, links, { markerId, linkClass, fromIdAttr, toIdAttr }) { - if (!links.length) return; - - const defs = ensureOverlayDefs(svg); - ensureArrowMarker(defs, markerId); - - for (const link of links) { - const from = boxPixelRect(link.fromBox ?? link.captionBox, img); - const to = boxPixelRect(link.toBox ?? link.hostBox, img); - const start = boxCenter(from); - const end = boxCenter(to); - - const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); - line.setAttribute("class", linkClass); - line.setAttribute("x1", String(start.x)); - line.setAttribute("y1", String(start.y)); - line.setAttribute("x2", String(end.x)); - line.setAttribute("y2", String(end.y)); - line.setAttribute("marker-end", `url(#${markerId})`); - line.setAttribute(fromIdAttr, link.fromElementId ?? link.captionElementId); - line.setAttribute(toIdAttr, link.toElementId ?? link.hostElementId); - svg.appendChild(line); - } -} - -function appendCaptionLinks(svg, img, captionLinks) { - appendOverlayLinks(svg, img, captionLinks, { - markerId: "caption-arrowhead", - linkClass: "caption-link", - fromIdAttr: "data-caption-id", - toIdAttr: "data-host-id", - }); -} - -function appendXrefLinks(svg, img, xrefLinks) { - appendOverlayLinks(svg, img, xrefLinks, { - markerId: "xref-arrowhead", - linkClass: "xref-link", - fromIdAttr: "data-xref-from-id", - toIdAttr: "data-xref-to-id", - }); -} - -function docPointToPixel(xDoc, yDoc, resW, resH, img) { - return { - x: (xDoc / resW) * img.naturalWidth, - y: (yDoc / resH) * img.naturalHeight, - }; -} - -function pageCornerTarget(img, defaultResolution, corner) { - const { width: resW, height: resH } = defaultResolution; - const inset = overlayUserLength(5); - const tl = docPointToPixel(0, 0, resW, resH, img); - const br = docPointToPixel(resW, resH, resW, resH, img); - if (corner === "tl") { - return { - x: Math.min(tl.x + inset, br.x - inset), - y: Math.min(tl.y + inset, br.y - inset), - }; - } - return { - x: Math.max(br.x - inset, tl.x + inset), - y: Math.max(br.y - inset, tl.y + inset), - }; -} - -function elementThreadId(el) { - return firstHeadChild(el, "thread")?.getAttribute("thread_id") ?? null; -} - -/** @returns {Set} */ -function fragmentPeerElementIds(elementId) { - const peers = new Set(); - if (!elementId || !state?.elementIds || !state.idToElement) return peers; - const el = state.idToElement.get(elementId); - const threadId = el ? elementThreadId(el) : null; - if (!threadId) return peers; - for (const [node, id] of state.elementIds) { - if (elementThreadId(node) === threadId) peers.add(id); - } - return peers; -} - -function isFragmentLinkRelevant(linkEl, peerIds) { - const fromId = linkEl.getAttribute("data-fragment-from-id"); - const toId = linkEl.getAttribute("data-fragment-to-id"); - if (fromId && peerIds.has(fromId)) return true; - if (toId && peerIds.has(toId)) return true; - return false; -} - -function appendFragmentLinks(svg, img, links, defaultResolution) { - if (!links.length) return; - - const defs = ensureOverlayDefs(svg); - ensureArrowMarker(defs, "fragment-arrowhead"); - const fontSize = overlayUserLength(OVERLAY_BADGE_FONT_SIZE); - - for (const link of links) { - const fromRect = boxPixelRect(link.fromBox, img); - - const group = document.createElementNS("http://www.w3.org/2000/svg", "g"); - group.setAttribute("class", "fragment-link"); - group.setAttribute("data-thread-id", link.threadId); - group.setAttribute("data-fragment-from-id", link.fromElementId); - if (link.toElementId) group.setAttribute("data-fragment-to-id", link.toElementId); - - let labelAt; - if (link.toBox) { - const toRect = boxPixelRect(link.toBox, img); - const start = boxCenter(fromRect); - const end = boxCenter(toRect); - - const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); - line.setAttribute("class", "fragment-link-path fragment-link-path-dashed"); - line.setAttribute("x1", String(start.x)); - line.setAttribute("y1", String(start.y)); - line.setAttribute("x2", String(end.x)); - line.setAttribute("y2", String(end.y)); - line.setAttribute("marker-end", "url(#fragment-arrowhead)"); - alignDashedLineToEnd(line, start, end); - group.appendChild(line); - labelAt = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 }; - } else { - const corner = link.targetCorner ?? "br"; - const cornerPoint = pageCornerTarget(img, defaultResolution, corner); - const elementAnchor = boxCenter(fromRect); - const incoming = corner === "tl"; - const start = incoming ? cornerPoint : elementAnchor; - const end = incoming ? elementAnchor : cornerPoint; - - const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); - line.setAttribute("class", "fragment-link-path fragment-link-path-dashed"); - line.setAttribute("x1", String(start.x)); - line.setAttribute("y1", String(start.y)); - line.setAttribute("x2", String(end.x)); - line.setAttribute("y2", String(end.y)); - line.setAttribute("marker-end", "url(#fragment-arrowhead)"); - alignDashedLineToEnd(line, start, end); - group.appendChild(line); - labelAt = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 }; - } - - const text = document.createElementNS("http://www.w3.org/2000/svg", "text"); - text.setAttribute("class", "fragment-link-label"); - text.setAttribute("x", String(labelAt.x)); - text.setAttribute("y", String(labelAt.y)); - text.setAttribute("font-size", String(fontSize)); - text.textContent = link.toBox ? FRAGMENT_LINK_LABEL_SAME_PAGE : FRAGMENT_LINK_LABEL_CROSS_PAGE; - group.appendChild(text); - - svg.appendChild(group); - } -} - -function appendFragmentNavButtons(svg, img, items) { - if (!items.length) return; - - const btnSize = overlayUserLength(13 * 1.5); - const gap = overlayUserLength(2); - const inset = overlayUserLength(3); - const fontSize = overlayUserLength(10 * 1.5); - const radius = overlayUserLength(2 * 1.5); - - for (const item of items) { - const { x, y, w, h } = boxPixelRect(item.box, img); - const group = document.createElementNS("http://www.w3.org/2000/svg", "g"); - group.setAttribute("class", "fragment-nav"); - group.setAttribute("data-element-id", item.elementId); - - const rowY = y + h - inset - btnSize; - const nextX = x + w - inset - btnSize; - const prevX = nextX - gap - btnSize; - - appendFragmentNavButton(group, prevX, rowY, btnSize, radius, fontSize, "prev", "‹", item.hasPrev); - appendFragmentNavButton(group, nextX, rowY, btnSize, radius, fontSize, "next", "›", item.hasNext); - - svg.appendChild(group); - } -} - -function appendFragmentNavButton(group, x, y, size, radius, fontSize, direction, label, enabled) { - const btn = document.createElementNS("http://www.w3.org/2000/svg", "g"); - btn.setAttribute("class", `fragment-nav-btn fragment-nav-btn-${direction}${enabled ? "" : " fragment-nav-btn-disabled"}`); - btn.setAttribute("data-nav", direction); - if (enabled) { - btn.setAttribute("role", "button"); - btn.setAttribute( - "aria-label", - direction === "prev" ? FRAGMENT_NAV_HINT_PREV : FRAGMENT_NAV_HINT_NEXT, - ); - } - - const bg = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - bg.setAttribute("class", "fragment-nav-btn-bg"); - bg.setAttribute("x", String(x)); - bg.setAttribute("y", String(y)); - bg.setAttribute("width", String(size)); - bg.setAttribute("height", String(size)); - bg.setAttribute("rx", String(radius)); - bg.setAttribute("ry", String(radius)); - btn.appendChild(bg); - - const text = document.createElementNS("http://www.w3.org/2000/svg", "text"); - text.setAttribute("class", "fragment-nav-btn-label"); - text.setAttribute("x", String(x + size / 2)); - text.setAttribute("y", String(y + size / 2)); - text.setAttribute("font-size", String(fontSize)); - text.textContent = label; - btn.appendChild(text); - - group.appendChild(btn); -} - -function appendReadingOrderOverlay(svg, img, steps) { - if (!steps.length) return; - - if (steps.length >= 2) { - const defs = ensureOverlayDefs(svg); - ensureArrowMarker(defs, "reading-order-arrowhead"); - - for (let i = 0; i < steps.length - 1; i += 1) { - const from = boxPixelRect(steps[i].box, img); - const to = boxPixelRect(steps[i + 1].box, img); - const start = boxCenter(from); - const end = boxCenter(to); - - const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); - line.setAttribute("class", "reading-order-step"); - line.setAttribute("x1", String(start.x)); - line.setAttribute("y1", String(start.y)); - line.setAttribute("x2", String(end.x)); - line.setAttribute("y2", String(end.y)); - line.setAttribute("marker-end", "url(#reading-order-arrowhead)"); - svg.appendChild(line); - } - } -} - -function imageCoordsFromEvent(svg, evt) { - const pt = svg.createSVGPoint(); - pt.x = evt.clientX; - pt.y = evt.clientY; - const ctm = svg.getScreenCTM()?.inverse(); - if (!ctm) return null; - const p = pt.matrixTransform(ctm); - return { x: p.x, y: p.y }; -} - -function hitTestBoxes(boxes, img, x, y) { - let best = null; - let bestArea = Infinity; - for (const b of boxes) { - const { x: bx, y: by, w, h, area } = boxPixelRect(b, img); - if (x >= bx && x <= bx + w && y >= by && y <= by + h && area < bestArea) { - best = b; - bestArea = area; - } - } - return best; -} - -function buildOverlay(img, boxes, captionLinks = [], xrefLinks = [], readingOrderSteps = [], fragmentLinks = [], fragmentNavItems = [], defaultResolution = { width: 512, height: 512 }) { - const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - svg.classList.add("overlay"); - svg.setAttribute("viewBox", `0 0 ${img.naturalWidth} ${img.naturalHeight}`); - svg.setAttribute("overflow", "hidden"); - - appendCaptionLinks(svg, img, captionLinks); - appendXrefLinks(svg, img, xrefLinks); - appendFragmentLinks(svg, img, fragmentLinks, defaultResolution); - appendReadingOrderOverlay(svg, img, readingOrderSteps); - - const defs = ensureOverlayDefs(svg); - ensureLayerHatchPatterns(defs); - - for (const b of sortedOverlayBoxes(boxes)) { - const { x, y, w, h } = boxPixelRect(b, img); - const cls = bboxClassForKind(b.kind); - const kindClass = kindClassForTag(b.kind); - const layerClass = layerClassForValue(b.layer ?? "body"); - - const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - rect.setAttribute( - "class", - `bbox bbox-${cls} ${kindClass}${layerClass ? ` ${layerClass}` : ""}`, - ); - rect.setAttribute("x", String(x)); - rect.setAttribute("y", String(y)); - rect.setAttribute("width", String(Math.max(w, 1))); - rect.setAttribute("height", String(Math.max(h, 1))); - rect.setAttribute("data-element-id", b.elementId); - svg.appendChild(rect); - } - - appendFragmentNavButtons(svg, img, fragmentNavItems); - - svg.addEventListener("click", (e) => { - if (pagePanSuppressClick) { - pagePanSuppressClick = false; - return; - } - const navBtn = e.target.closest(".fragment-nav-btn:not(.fragment-nav-btn-disabled)"); - if (navBtn) { - e.stopPropagation(); - const navGroup = navBtn.closest(".fragment-nav"); - const elementId = navGroup?.getAttribute("data-element-id"); - const direction = navBtn.getAttribute("data-nav"); - if (elementId && direction) navigateThreadFragment(elementId, direction); - return; - } - const badge = e.target.closest(".overlay-badge[data-element-id]"); - if (badge) { - const elementId = resolveSelectionElementId(badge.getAttribute("data-element-id")); - if (elementId) selectElement(elementId); - return; - } - const coords = imageCoordsFromEvent(svg, e); - if (!coords) return; - const hit = hitTestBoxes(boxes, img, coords.x, coords.y); - if (hit) selectElement(hit.elementId); - else clearSelection(); - }); - - svg.addEventListener("mousemove", (e) => { - const coords = imageCoordsFromEvent(svg, e); - svg.style.cursor = coords && hitTestBoxes(boxes, img, coords.x, coords.y) ? "pointer" : ""; - }); - svg.addEventListener("mouseleave", () => { - svg.style.cursor = ""; - }); - - return svg; -} - -function selectElement(elementId) { - if (!elementId) return; - selectedElementId = elementId; - applySelection(); -} - -function clearSelection() { - selectedElementId = null; - applySelection(); -} - -function isPictureContentOverlayElement(elementId) { - return isPictureContentElement(state?.idToElement?.get(elementId) ?? null); -} - -function isTableContentOverlayElement(elementId) { - return isTableContentElement(state?.idToElement?.get(elementId) ?? null); -} - -function isContentsOptionHidden(elementId, clickVisible) { - if (clickVisible) return false; - if (!showPictureContents && isPictureContentOverlayElement(elementId)) return true; - if (!showTableContents && isTableContentOverlayElement(elementId)) return true; - return false; -} - -function applyBboxVisibility() { - if (!state?.hasPageView || !els.pagePane) return; - - const peerIds = selectedElementId ? fragmentPeerElementIds(selectedElementId) : new Set(); - - for (const el of els.pagePane.querySelectorAll(".bbox")) { - el.classList.remove("related"); - const elementId = el.getAttribute("data-element-id"); - const clickVisible = elementId === selectedElementId || peerIds.has(elementId); - if (showAllBboxes) { - if (isContentsOptionHidden(elementId, clickVisible)) { - el.classList.add("bbox-hidden"); - } else { - el.classList.remove("bbox-hidden"); - if (peerIds.has(elementId)) { - el.classList.add("related"); - } - } - continue; - } - if (elementId === selectedElementId) { - el.classList.remove("bbox-hidden"); - } else if (peerIds.has(elementId)) { - el.classList.remove("bbox-hidden"); - el.classList.add("related"); - } else { - el.classList.add("bbox-hidden"); - } - } - - for (const el of els.pagePane.querySelectorAll(".element-badge")) { - const elementId = el.getAttribute("data-element-id"); - const clickVisible = elementId === selectedElementId || peerIds.has(elementId); - if (!showAllBboxes || !showLayoutBadges) { - el.classList.add("bbox-hidden"); - continue; - } - if (isContentsOptionHidden(elementId, clickVisible)) { - el.classList.add("bbox-hidden"); - } else { - el.classList.remove("bbox-hidden"); - } - } - - for (const el of els.pagePane.querySelectorAll(".caption-link")) { - if (!showAllBboxes || !showCaptionLinks) { - el.classList.add("bbox-hidden"); - continue; - } - el.classList.remove("bbox-hidden"); - } - - for (const el of els.pagePane.querySelectorAll(".xref-link")) { - if (!showAllBboxes || !showXrefLinks) { - el.classList.add("bbox-hidden"); - continue; - } - el.classList.remove("bbox-hidden"); - } - - for (const el of els.pagePane.querySelectorAll(".fragment-link")) { - const clickVisible = Boolean(selectedElementId && isFragmentLinkRelevant(el, peerIds)); - const optionVisible = showAllBboxes && showFragmentLinks; - el.classList.toggle("bbox-hidden", !(clickVisible || optionVisible)); - } - - for (const el of els.pagePane.querySelectorAll(".fragment-nav")) { - const elementId = el.getAttribute("data-element-id"); - const clickVisible = elementId === selectedElementId || peerIds.has(elementId); - const optionVisible = showAllBboxes && showFragmentLinks; - el.classList.toggle("bbox-hidden", !(clickVisible || optionVisible)); - } - - for (const el of els.pagePane.querySelectorAll(".reading-order-badge")) { - const elementId = el.getAttribute("data-element-id"); - const clickVisible = elementId === selectedElementId || peerIds.has(elementId); - if (!showAllBboxes || !showReadingOrder) { - el.classList.add("bbox-hidden"); - continue; - } - if ( - isPictureContentOverlayElement(elementId) - || isTableContentOverlayElement(elementId) - || isContentsOptionHidden(elementId, clickVisible) - ) { - el.classList.add("bbox-hidden"); - continue; - } - el.classList.remove("bbox-hidden"); - } - - for (const el of els.pagePane.querySelectorAll(".reading-order-step")) { - if (!showAllBboxes || !showReadingOrder || !showReadingOrderArrows) { - el.classList.add("bbox-hidden"); - continue; - } - el.classList.remove("bbox-hidden"); - } -} - -function findMarkupElementForSelection(elementId) { - if (!els.markupPane) return null; - return ( - els.markupPane.querySelector(`.markup-el-virtual-text[data-element-id="${elementId}"]`) || - els.markupPane.querySelector(`[data-element-id="${elementId}"]`) - ); -} - -function findRenderedElementForSelection(elementId) { - if (!els.renderedPane) return null; - const direct = - els.renderedPane.querySelector(`.rendered-el-virtual-text[data-element-id="${elementId}"]`) || - els.renderedPane.querySelector(`.rendered-el[data-element-id="${elementId}"]`); - if (direct) return direct; - - const xmlEl = state?.idToElement?.get(elementId); - const threadId = xmlEl ? elementThreadId(xmlEl) : null; - if (!threadId) return null; - - const merged = els.renderedPane.querySelector( - `.rendered-fragment-merged[data-thread-id="${threadId}"]`, - ); - if (!merged) return null; - - const primaryId = merged.getAttribute("data-element-id"); - if (!primaryId || primaryId === elementId) return merged; - return fragmentPeerElementIds(primaryId).has(elementId) ? merged : null; -} - -function revealRenderedSelectionContext(renderedEl) { - const pictureContents = renderedEl.closest(".rendered-picture-contents"); - if (pictureContents && !pictureContents.open) { - pictureContents.open = true; - } - revealReadingLayerForSelection(renderedEl); -} - -function revealReadingLayerForSelection(renderedEl) { - const layer = renderedEl.getAttribute("data-doclang-layer"); - if (!layer || layer === "body") return; - let changed = false; - if (layer === "furniture" && !showReadingFurniture) { - showReadingFurniture = true; - changed = true; - } else if (layer === "background" && !showReadingBackground) { - showReadingBackground = true; - changed = true; - } - if (!changed) return; - syncReadingLayerCheckboxes(); - syncReadingLayerVisibility(); -} - -function applySelection() { - els.markupPane?.querySelectorAll(".markup-el.selected").forEach((el) => { - el.classList.remove("selected"); - }); - els.renderedPane?.querySelectorAll(".rendered-el.selected").forEach((el) => { - el.classList.remove("selected"); - }); - if (!els.pagePane) return; - els.pagePane.querySelectorAll(".bbox.selected, .overlay-badge.selected").forEach((el) => { - el.classList.remove("selected"); - }); - - if (!selectedElementId) { - const img = els.pagePane.querySelector(".page-view img"); - if (img) syncOverlayBadges(img); - applyBboxVisibility(); - return; - } - - const markupEl = findMarkupElementForSelection(selectedElementId); - if (markupEl) { - markupEl.classList.add("selected"); - markupEl.scrollIntoView({ block: "nearest", behavior: "smooth" }); - } - - const renderedEl = findRenderedElementForSelection(selectedElementId); - if (renderedEl) { - revealRenderedSelectionContext(renderedEl); - renderedEl.classList.add("selected"); - renderedEl.scrollIntoView({ block: "nearest", behavior: "smooth" }); - } - - if (state?.hasPageView) { - for (const el of els.pagePane.querySelectorAll(`[data-element-id="${selectedElementId}"]`)) { - el.classList.add("selected"); - } - } - - const img = els.pagePane.querySelector(".page-view img"); - if (img) syncOverlayBadges(img); - applyBboxVisibility(); -} - -function buildMarkupView(segment, elementIds) { - const root = document.createElement("div"); - root.className = "markup"; - for (const el of segment) { - if (el.nodeType === Node.ELEMENT_NODE) { - root.appendChild(buildMarkupElement(el, 0, elementIds)); - } - } - root.addEventListener("click", (e) => { - const attrToggle = e.target.closest(".xml-attr-value-chip"); - if (attrToggle) { - e.stopPropagation(); - toggleTruncatableMarkupAttrValue(attrToggle); - return; - } - const toggle = e.target.closest(".markup-fold-toggle"); - if (toggle) { - e.stopPropagation(); - const block = toggle.closest(".markup-el-foldable"); - if (block) { - const collapsed = block.classList.toggle("markup-collapsed"); - toggle.setAttribute("aria-expanded", String(!collapsed)); - toggle.setAttribute("aria-label", collapsed ? "Expand" : "Collapse"); - } - return; - } - const elementId = resolveMarkupClickTarget(e.target); - if (elementId) selectElement(elementId); - }); - return root; -} - -function sliceHasMarkupContent(nodes) { - for (const node of nodes) { - if (isTextLikeNode(node) && !isWhitespaceOnlyText(node)) return true; - if (node.nodeType === Node.ELEMENT_NODE) return true; - } - return false; -} - -function isVirtualTextSkippableNode(node) { - if (isWhitespaceOnlyText(node)) return true; - if (node.nodeType !== Node.ELEMENT_NODE) return false; - const tag = localName(node); - return tag === "location" || HEAD_TAGS.has(tag); -} - -function shouldWrapVirtualText(contentNodes) { - if (!sliceHasMarkupContent(contentNodes)) return false; - - for (const node of contentNodes) { - if (isVirtualTextSkippableNode(node)) continue; - if (isTextLikeNode(node)) return true; - if (node.nodeType === Node.ELEMENT_NODE && !isSemanticElement(node)) return true; - } - return false; -} - -function appendRenderedSliceContent(container, hostEl, contentNodes, elementIds) { - if (!sliceHasMarkupContent(contentNodes)) return; - if (shouldWrapVirtualText(contentNodes)) { - container.appendChild(renderVirtualTextBlock(hostEl, contentNodes, elementIds)); - return; - } - for (const node of contentNodes) { - if (isVirtualTextSkippableNode(node)) continue; - if (node.nodeType === Node.ELEMENT_NODE && RENDER_BLOCK_TAGS.has(localName(node))) { - const rendered = renderBlockElement(node, elementIds, { inline: false }); - if (rendered) container.appendChild(rendered); - } else { - appendRenderedNode(container, node, elementIds, { inline: true }); - } - } -} - -function appendMarkupNodesFromSlice(parent, depth, nodes, elementIds) { - for (const child of nodes) { - if (isTextLikeNode(child)) { - const text = formatMarkupTextNode(child); - if (text) appendMarkupTextLine(parent, depth, text); - } else if (child.nodeType === Node.ELEMENT_NODE) { - parent.appendChild(buildMarkupElement(child, depth, elementIds)); - } - } -} - -function appendMarkupVirtualText(parent, depth, hostEl, contentNodes, elementIds) { - if (!shouldWrapVirtualText(contentNodes)) { - appendMarkupNodesFromSlice(parent, depth, contentNodes, elementIds); - return; - } - - const block = document.createElement("div"); - block.className = "markup-el markup-el-virtual-text"; - const elementId = elementIds.get(hostEl); - if (elementId) block.setAttribute("data-element-id", elementId); - - appendMarkupFoldableOpen(block, depth, "text", [], VIRTUAL_TEXT_TAG_HINT); - block.classList.add("markup-el-foldable"); - - const foldBody = document.createElement("div"); - foldBody.className = "markup-fold-body"; - const children = document.createElement("div"); - children.className = "markup-children"; - appendMarkupNodesFromSlice(children, depth + 1, contentNodes, elementIds); - foldBody.appendChild(children); - appendMarkupCloseTag(foldBody, depth, "text", VIRTUAL_TEXT_TAG_HINT); - block.appendChild(foldBody); - parent.appendChild(block); -} - -function buildMarkupFoldableElement(el, depth, elementIds, buildBody) { - const tag = localName(el); - const block = document.createElement("div"); - block.className = "markup-el"; - const elementId = elementIds.get(el); - if (elementId) block.setAttribute("data-element-id", elementId); - - const attributes = markupAttributes(el); - appendMarkupFoldableOpen(block, depth, tag, attributes); - block.classList.add("markup-el-foldable"); - - const foldBody = document.createElement("div"); - foldBody.className = "markup-fold-body"; - const children = document.createElement("div"); - children.className = "markup-children"; - buildBody(children, depth + 1); - foldBody.appendChild(children); - appendMarkupCloseTag(foldBody, depth, tag); - block.appendChild(foldBody); - return block; -} - -function buildMarkupList(el, depth, elementIds) { - return buildMarkupFoldableElement(el, depth, elementIds, (children, childDepth) => { - const nodes = [...el.childNodes]; - let i = 0; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType === Node.ELEMENT_NODE && localName(node) === "ldiv") break; - if (isTextLikeNode(node) && isWhitespaceOnlyText(node)) { - i += 1; - continue; - } - if (isTextLikeNode(node)) break; - if (node.nodeType === Node.ELEMENT_NODE) { - const tag = localName(node); - if (HEAD_TAGS.has(tag) || tag === "location") { - children.appendChild(buildMarkupElement(node, childDepth, elementIds)); - i += 1; - continue; - } - } - break; - } - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE || localName(node) !== "ldiv") { - appendMarkupNodesFromSlice(children, childDepth, [node], elementIds); - i += 1; - continue; - } - const ldiv = node; - children.appendChild(buildMarkupElement(ldiv, childDepth, elementIds)); - i += 1; - const end = skipUntilListItemBoundary(nodes, i); - appendMarkupVirtualText(children, childDepth, ldiv, nodes.slice(i, end), elementIds); - i = end; - } - }); -} - -function buildMarkupOtslContainer(el, depth, elementIds) { - return buildMarkupFoldableElement(el, depth, elementIds, (children, childDepth) => { - const nodes = [...el.childNodes]; - let i = 0; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType === Node.ELEMENT_NODE && isCellToken(localName(node))) break; - if (isTextLikeNode(node) && isWhitespaceOnlyText(node)) { - i += 1; - continue; - } - if (isTextLikeNode(node)) break; - if (node.nodeType === Node.ELEMENT_NODE) { - const tag = localName(node); - if (HEAD_TAGS.has(tag) || tag === "location" || tag === "h_thread") { - children.appendChild(buildMarkupElement(node, childDepth, elementIds)); - i += 1; - continue; - } - } - break; - } - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE) { - appendMarkupNodesFromSlice(children, childDepth, [node], elementIds); - i += 1; - continue; - } - const tag = localName(node); - if (tag === "nl") { - appendMarkupSelfClosingTag(children, childDepth, "nl", []); - i += 1; - continue; - } - if (!isCellToken(tag)) { - appendMarkupNodesFromSlice(children, childDepth, [node], elementIds); - i += 1; - continue; - } - const cell = node; - children.appendChild(buildMarkupElement(cell, childDepth, elementIds)); - i += 1; - if (CELL_SPAN_TAGS.has(tag)) continue; - const end = skipUntilCellBoundary(nodes, i); - appendMarkupVirtualText(children, childDepth, cell, nodes.slice(i, end), elementIds); - i = end; - } - }); -} - -function buildMarkupElement(el, depth, elementIds) { - const tag = localName(el); - if (tag === "list") return buildMarkupList(el, depth, elementIds); - if (OTSL_CONTAINER_TAGS.has(tag)) return buildMarkupOtslContainer(el, depth, elementIds); - - const block = document.createElement("div"); - block.className = "markup-el"; - const elementId = elementIds.get(el); - if (elementId) block.setAttribute("data-element-id", elementId); - - const attributes = markupAttributes(el); - - if (!el.childNodes.length) { - appendMarkupSelfClosingTag(block, depth, tag, attributes); - return block; - } - - const meaningfulText = [...el.childNodes].filter((n) => isTextLikeNode(n) && !isWhitespaceOnlyText(n)); - const textOnly = meaningfulText.length > 0 && meaningfulText.every(isTextLikeNode) && !childElements(el).length; - if (textOnly) { - const text = serializeMarkupTextNodes(el.childNodes); - if (text) { - appendMarkupInlineElement(block, depth, tag, attributes, text); - return block; - } - } - - appendMarkupFoldableOpen(block, depth, tag, attributes); - block.classList.add("markup-el-foldable"); - - const foldBody = document.createElement("div"); - foldBody.className = "markup-fold-body"; - const children = document.createElement("div"); - children.className = "markup-children"; - for (const child of el.childNodes) { - if (isTextLikeNode(child)) { - const text = formatMarkupTextNode(child); - if (text) appendMarkupTextLine(children, depth + 1, text); - } else if (child.nodeType === Node.ELEMENT_NODE) { - children.appendChild(buildMarkupElement(child, depth + 1, elementIds)); - } - } - foldBody.appendChild(children); - appendMarkupCloseTag(foldBody, depth, tag); - block.appendChild(foldBody); - return block; -} - -/** @returns {Map} */ -function collectIntraPageThreads(segment) { - /** @type {Map} */ - const byThread = new Map(); - for (const el of segment) { - if (el.nodeType !== Node.ELEMENT_NODE) continue; - if (localName(el) === "page_break") continue; - const threadId = elementThreadId(el); - if (!threadId) continue; - if (!byThread.has(threadId)) byThread.set(threadId, []); - byThread.get(threadId).push(el); - } - for (const [threadId, members] of byThread) { - if (members.length < 2) byThread.delete(threadId); - } - return byThread; -} - -function findLastTextNode(node) { - if (isTextLikeNode(node)) return node; - if (node.nodeType !== Node.ELEMENT_NODE) return null; - for (let i = node.childNodes.length - 1; i >= 0; i -= 1) { - const found = findLastTextNode(node.childNodes[i]); - if (found) return found; - } - return null; -} - -function trimParentTrailingForFragmentJoin(parent) { - const lastText = findLastTextNode(parent); - if (!lastText) return; - let value = lastText.textContent ?? ""; - value = value.replace(/\s+$/u, ""); - if (value.endsWith("-")) value = value.slice(0, -1); - if (!value) { - lastText.parentNode?.removeChild(lastText); - trimParentTrailingForFragmentJoin(parent); - return; - } - lastText.textContent = value; -} - -function appendMergedTextFragments(parent, fragments, elementIds) { - for (let i = 0; i < fragments.length; i += 1) { - if (i > 0) trimParentTrailingForFragmentJoin(parent); - appendRenderedBody(parent, fragments[i], elementIds, { - inline: true, - trimLeading: i > 0, - }); - } -} - -function renderMergedIntraPageFragments(fragments, elementIds) { - const first = fragments[0]; - const tag = localName(first); - const firstId = elementIds.get(first); - const threadId = elementThreadId(first); - - let node; - if (tag === "text") { - node = document.createElement("p"); - appendMergedTextFragments(node, fragments, elementIds); - } else if (tag === "list") { - const listClass = first.getAttribute("class") ?? "unordered"; - node = document.createElement(listClass === "ordered" ? "ol" : "ul"); - for (const el of fragments) { - appendListItemsFromElement(node, el, elementIds); - } - } else { - node = document.createElement("div"); - node.className = "rendered-fragment-merged-body"; - for (const el of fragments) { - appendRenderedBodyBlocks(node, el, elementIds); - } - } - - const wrap = wrapRendered(first, node, firstId, "rendered-fragment-merged"); - if (threadId) wrap.setAttribute("data-thread-id", threadId); - return wrap; -} - -function buildRenderedView(segment, elementIds) { - const intraPageThreads = collectIntraPageThreads(segment); - const skipElements = new Set(); - /** @type {Map} */ - const mergeGroups = new Map(); - - for (const [, members] of intraPageThreads) { - mergeGroups.set(members[0], members); - for (let i = 1; i < members.length; i += 1) { - skipElements.add(members[i]); - } - } - - const root = document.createElement("article"); - root.className = "rendered-doc"; - for (const el of segment) { - if (el.nodeType !== Node.ELEMENT_NODE) continue; - if (localName(el) === "page_break") continue; - if (skipElements.has(el)) continue; - - const mergeGroup = mergeGroups.get(el); - const rendered = mergeGroup - ? renderMergedIntraPageFragments(mergeGroup, elementIds) - : renderBlockElement(el, elementIds, { inline: false }); - if (rendered) root.appendChild(rendered); - } - root.addEventListener("click", (e) => { - const elementId = resolveRenderedClickTarget(e.target); - if (elementId) selectElement(elementId); - }); - applyReadingLayerClasses(root); - return root; -} - -function wrapRendered(el, node, elementId, extraClass) { - const tag = localName(el); - const wrap = document.createElement("div"); - wrap.className = `rendered-el rendered-${tag}${extraClass ? ` ${extraClass}` : ""}`; - if (elementId) wrap.setAttribute("data-element-id", elementId); - applyElementLayerAttr(el, wrap); - wrap.appendChild(node); - return wrap; -} - -function renderBlockElement(el, elementIds, ctx) { - const tag = localName(el); - const elementId = elementIds.get(el); - - switch (tag) { - case "text": { - const p = document.createElement("p"); - appendRenderedBody(p, el, elementIds, { inline: true }); - return wrapRendered(el, p, elementId); - } - case "heading": { - const h = document.createElement(`h${headingLevel(el)}`); - appendRenderedBody(h, el, elementIds, { inline: true }); - return wrapRendered(el, h, elementId); - } - case "field_heading": { - const h = document.createElement(`h${headingLevel(el)}`); - h.className = "rendered-field-heading"; - appendRenderedBody(h, el, elementIds, { inline: true }); - return wrapRendered(el, h, elementId); - } - case "footnote": { - const aside = document.createElement("aside"); - appendRenderedBody(aside, el, elementIds, { inline: false }); - return wrapRendered(el, aside, elementId); - } - case "page_header": { - const header = document.createElement("header"); - header.className = "rendered-page-header"; - appendRenderedBody(header, el, elementIds, { inline: true }); - return wrapRendered(el, header, elementId); - } - case "page_footer": { - const footer = document.createElement("footer"); - footer.className = "rendered-page-footer"; - appendRenderedBody(footer, el, elementIds, { inline: true }); - return wrapRendered(el, footer, elementId); - } - case "list": - return renderList(el, elementIds); - case "table": - case "index": - case "tabular": - return renderOtslContainer(el, elementIds); - case "code": - return renderCode(el, elementIds, ctx); - case "formula": - return renderFormula(el, elementIds, ctx); - case "picture": - return renderPicture(el, elementIds); - case "group": { - const figure = document.createElement("figure"); - figure.className = "rendered-group"; - appendRenderedBodyBlocks(figure, el, elementIds); - const captionEl = readCaptionElement(el); - if (captionEl) { - figure.appendChild(renderEmbeddedCaption(captionEl, elementIds, "figcaption")); - } - return wrapRendered(el, figure, elementId); - } - case "field_region": { - const div = document.createElement("div"); - div.className = "rendered-field-region"; - appendRenderedBodyBlocks(div, el, elementIds); - return wrapRendered(el, div, elementId); - } - case "field_item": { - const div = document.createElement("div"); - div.className = "rendered-field-item"; - appendRenderedBodyBlocks(div, el, elementIds); - return wrapRendered(el, div, elementId); - } - default: - return renderUnsupported(el, elementIds); - } -} - -function renderUnsupported(el, elementIds) { - const stub = document.createElement("div"); - stub.className = "rendered-unsupported"; - stub.textContent = `<${localName(el)}> — not yet rendered`; - return wrapRendered(el, stub, elementIds.get(el)); -} - -function skipElementHeadNodes(nodes, startIdx) { - let i = startIdx; - while (i < nodes.length && isWhitespaceOnlyText(nodes[i])) i += 1; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType === Node.ELEMENT_NODE && HEAD_TAGS.has(localName(node))) { - i += 1; - while (i < nodes.length && isWhitespaceOnlyText(nodes[i])) i += 1; - continue; - } - break; - } - return i; -} - -function readCaptionElement(el) { - return childElements(el).find((c) => localName(c) === "caption") ?? null; -} - -function renderEmbeddedCaption(captionEl, elementIds, tagName) { - const node = document.createElement(tagName); - node.classList.add("rendered-el", "rendered-caption"); - const elementId = elementIds.get(captionEl); - if (elementId) node.setAttribute("data-element-id", elementId); - appendRenderedBody(node, captionEl, elementIds, { inline: true }); - return node; -} - -function appendRenderedBody(parent, el, elementIds, ctx) { - const nodes = [...el.childNodes]; - let i = skipElementHeadNodes(nodes, 0); - while (i < nodes.length) { - appendRenderedNode(parent, nodes[i], elementIds, ctx); - i += 1; - } -} - -function appendRenderedBodyBlocks(parent, el, elementIds) { - const nodes = [...el.childNodes]; - let i = skipElementHeadNodes(nodes, 0); - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType === Node.ELEMENT_NODE && RENDER_BLOCK_TAGS.has(localName(node))) { - const rendered = renderBlockElement(node, elementIds, { inline: false }); - if (rendered) parent.appendChild(rendered); - } else { - appendRenderedNode(parent, node, elementIds, { inline: false }); - } - i += 1; - } -} - -function renderMarkerElement(el, elementIds, ctx) { - const marker = document.createElement("span"); - marker.className = "rendered-marker rendered-el"; - const elementId = elementIds.get(el); - if (elementId) marker.setAttribute("data-element-id", elementId); - appendRenderedBody(marker, el, elementIds, ctx); - return marker; -} - -function renderCheckboxElement(el, elementIds) { - const cb = document.createElement("input"); - cb.type = "checkbox"; - cb.disabled = true; - cb.checked = (el.getAttribute("class") ?? "unselected") === "selected"; - cb.className = "rendered-checkbox"; - - const wrap = document.createElement("span"); - wrap.className = "rendered-checkbox-wrap rendered-el"; - const elementId = elementIds.get(el); - if (elementId) wrap.setAttribute("data-element-id", elementId); - applyElementLayerAttr(el, wrap); - wrap.appendChild(cb); - return wrap; -} - -function renderFieldKeyElement(el, elementIds, ctx) { - const node = document.createElement("span"); - node.className = "rendered-field-key rendered-el"; - const elementId = elementIds.get(el); - if (elementId) node.setAttribute("data-element-id", elementId); - applyElementLayerAttr(el, node); - appendRenderedBody(node, el, elementIds, { ...ctx, inline: true }); - return node; -} - -function renderFieldValueElement(el, elementIds, ctx) { - const valueClass = el.getAttribute("class") ?? "read_only"; - const node = document.createElement("span"); - node.className = `rendered-field-value rendered-field-value-${valueClass} rendered-el`; - const elementId = elementIds.get(el); - if (elementId) node.setAttribute("data-element-id", elementId); - applyElementLayerAttr(el, node); - appendRenderedBody(node, el, elementIds, { ...ctx, inline: true }); - if ( - valueClass === "fillable" - && !node.textContent.trim() - && !node.querySelector(".rendered-checkbox-wrap, img, .rendered-marker") - ) { - const slot = document.createElement("span"); - slot.className = "rendered-field-fillable-slot"; - slot.setAttribute("aria-hidden", "true"); - node.appendChild(slot); - } - return node; -} - -function renderFieldHintElement(el, elementIds, ctx) { - const node = document.createElement("span"); - node.className = "rendered-field-hint rendered-el"; - const elementId = elementIds.get(el); - if (elementId) node.setAttribute("data-element-id", elementId); - applyElementLayerAttr(el, node); - appendRenderedBody(node, el, elementIds, { ...ctx, inline: true }); - return node; -} - -function appendRenderedNode(parent, node, elementIds, ctx) { - if (isTextLikeNode(node)) { - let text = node.textContent; - if (!text || !text.trim()) return; - if (ctx.trimLeading) { - text = text.replace(/^\s+/u, ""); - ctx.trimLeading = false; - if (!text) return; - } - parent.appendChild(document.createTextNode(text)); - return; - } - if (node.nodeType !== Node.ELEMENT_NODE) return; - - const tag = localName(node); - if (HEAD_TAGS.has(tag)) return; - - if (RENDER_FORMAT_TAGS.has(tag)) { - parent.appendChild(renderFormatElement(node, elementIds, ctx)); - return; - } - - if (tag === "code" || tag === "formula") { - const rendered = renderBlockElement(node, elementIds, { inline: true }); - if (rendered) parent.appendChild(rendered); - return; - } - - if (RENDER_BLOCK_TAGS.has(tag)) { - const rendered = renderBlockElement(node, elementIds, ctx); - if (rendered) parent.appendChild(rendered); - return; - } - - if (tag === "marker") { - parent.appendChild(renderMarkerElement(node, elementIds, ctx)); - return; - } - - if (tag === "checkbox") { - parent.appendChild(renderCheckboxElement(node, elementIds)); - return; - } - - if (tag === "key") { - parent.appendChild(renderFieldKeyElement(node, elementIds, ctx)); - return; - } - - if (tag === "value") { - parent.appendChild(renderFieldValueElement(node, elementIds, ctx)); - return; - } - - if (tag === "hint") { - parent.appendChild(renderFieldHintElement(node, elementIds, ctx)); - return; - } - - if (tag === "ldiv") return; - if (isCellToken(tag) || tag === "src" || tag === "tabular") return; - - appendRenderedBody(parent, node, elementIds, ctx); -} - -function renderFormatElement(el, elementIds, ctx) { - const tag = localName(el); - if (tag === "content") { - const span = document.createElement("span"); - span.textContent = el.textContent ?? ""; - return span; - } - - let node; - if (tag === "handwriting") { - node = document.createElement("span"); - node.className = "rendered-handwriting"; - } else if (tag === "rtl") { - node = document.createElement("bdi"); - node.setAttribute("dir", "rtl"); - } else { - node = document.createElement(FORMAT_HTML_TAG[tag] ?? "span"); - } - - appendRenderedBody(node, el, elementIds, ctx); - return node; -} - -function renderCode(el, elementIds, ctx) { - const labelEl = childElements(el).find((c) => localName(c) === "label"); - const labelValue = labelEl?.getAttribute("value"); - - const code = document.createElement("code"); - appendRenderedBody(code, el, elementIds, { inline: ctx.inline }); - - if (ctx.inline) { - code.classList.add("rendered-el"); - const id = elementIds.get(el); - if (id) code.setAttribute("data-element-id", id); - return code; - } - - const pre = document.createElement("pre"); - if (labelValue && labelValue !== "undefined") { - const label = document.createElement("span"); - label.className = "rendered-code-label"; - label.textContent = labelValue; - pre.appendChild(label); - } - pre.appendChild(code); - return wrapRendered(el, pre, elementIds.get(el)); -} - -function renderFormula(el, elementIds, ctx) { - const span = document.createElement("span"); - span.className = ctx.inline ? "rendered-formula-inline" : "rendered-formula"; - appendRenderedBody(span, el, elementIds, { inline: true }); - if (ctx.inline) { - span.classList.add("rendered-el"); - const id = elementIds.get(el); - if (id) span.setAttribute("data-element-id", id); - return span; - } - return wrapRendered(el, span, elementIds.get(el)); -} - -function markPictureUnavailable(img) { - img.classList.add("rendered-picture-unavailable"); - img.alt = "\u00A0"; - img.setAttribute("aria-label", PICTURE_UNAVAILABLE_ALT); -} - -function appendPictureFigureImage(figure, uri, captionEl, elementIds) { - const img = document.createElement("img"); - figure.appendChild(img); - - const resolved = uri ? resolveArchiveUri(uri) : null; - if (resolved) { - img.alt = ""; - img.src = resolved; - img.addEventListener("error", () => markPictureUnavailable(img), { once: true }); - } else { - markPictureUnavailable(img); - img.src = INVALID_PICTURE_SRC; - } - - if (captionEl) { - figure.appendChild(renderEmbeddedCaption(captionEl, elementIds, "figcaption")); - } -} - -function renderPicture(el, elementIds) { - const figure = document.createElement("figure"); - const captionEl = readCaptionElement(el); - const srcEl = childElements(el).find((c) => localName(c) === "src") ?? null; - const uri = srcEl?.getAttribute("uri")?.trim() || null; - - appendPictureFigureImage(figure, uri, captionEl, elementIds); - - const nodes = [...el.childNodes]; - let i = skipElementHeadNodes(nodes, 0); - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE) { - i += 1; - continue; - } - const tag = localName(node); - if (tag === "src") { - i += 1; - continue; - } - if (tag === "tabular") { - const rendered = renderOtslContainer(node, elementIds); - if (rendered) figure.appendChild(rendered); - i += 1; - continue; - } - break; - } - - const bodyInner = document.createElement("div"); - bodyInner.className = "rendered-picture-contents-body"; - appendPictureBodyContent(bodyInner, nodes, i, elementIds); - if (bodyInner.textContent.trim()) { - const details = document.createElement("details"); - details.className = "rendered-picture-contents"; - const summary = document.createElement("summary"); - summary.textContent = "Picture contents"; - details.appendChild(summary); - details.appendChild(bodyInner); - figure.appendChild(details); - } - - return wrapRendered(el, figure, elementIds.get(el)); -} - -function appendPictureBodyContent(container, nodes, startIdx, elementIds) { - let i = startIdx; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType === Node.ELEMENT_NODE && RENDER_BLOCK_TAGS.has(localName(node))) { - const rendered = renderBlockElement(node, elementIds, { inline: false }); - if (rendered) container.appendChild(rendered); - } else { - appendRenderedNode(container, node, elementIds, { inline: false }); - } - i += 1; - } -} - -function renderVirtualTextBlock(hostEl, contentNodes, elementIds) { - const hasBlock = contentNodes.some( - (n) => n.nodeType === Node.ELEMENT_NODE && RENDER_BLOCK_TAGS.has(localName(n)), - ); - const inner = document.createElement(hasBlock ? "div" : "p"); - for (const node of contentNodes) { - if (node.nodeType === Node.ELEMENT_NODE && RENDER_BLOCK_TAGS.has(localName(node))) { - const rendered = renderBlockElement(node, elementIds, { inline: false }); - if (rendered) inner.appendChild(rendered); - } else { - appendRenderedNode(inner, node, elementIds, { inline: true }); - } - } - const wrap = document.createElement("div"); - wrap.className = "rendered-el rendered-text rendered-el-virtual-text"; - const elementId = elementIds.get(hostEl); - if (elementId) wrap.setAttribute("data-element-id", elementId); - applyElementLayerAttr(hostEl, wrap); - wrap.appendChild(inner); - return wrap; -} - -function appendListItemsFromElement(list, el, elementIds) { - const nodes = [...el.childNodes]; - let i = skipContainerLevelHead(nodes, 0); - - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE || localName(node) !== "ldiv") { - i += 1; - continue; - } - - const ldiv = node; - i += 1; - const li = document.createElement("li"); - - for (const child of childElements(ldiv)) { - const childTag = localName(child); - if (childTag === "marker") { - li.appendChild(renderMarkerElement(child, elementIds, { inline: true })); - } else if (childTag === "checkbox") { - li.appendChild(renderCheckboxElement(child, elementIds)); - } - } - - const contentStart = i; - const head = parseElementHeadAt(nodes, i); - if (head) i = head.nextIndex; - - while (i < nodes.length) { - const contentNode = nodes[i]; - if (contentNode.nodeType === Node.ELEMENT_NODE && localName(contentNode) === "ldiv") break; - i += 1; - } - - const contentNodes = nodes.slice(contentStart, i); - appendRenderedSliceContent(li, ldiv, contentNodes, elementIds); - - list.appendChild(li); - } -} - -function renderList(el, elementIds) { - const listClass = el.getAttribute("class") ?? "unordered"; - const list = document.createElement(listClass === "ordered" ? "ol" : "ul"); - appendListItemsFromElement(list, el, elementIds); - return wrapRendered(el, list, elementIds.get(el)); -} - -function isHeaderCellKind(kind) { - return kind === "ched" || kind === "rhed" || kind === "corn" || kind === "srow"; -} - -function skipOtslContainerHead(nodes, startIdx) { - let i = startIdx; - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE) { - i += 1; - continue; - } - const tag = localName(node); - if (isCellToken(tag)) break; - if (HEAD_TAGS.has(tag) || tag === "h_thread" || tag === "location") { - i += 1; - continue; - } - break; - } - return i; -} - -/** @returns {{ kind: string, token: Element, contentNodes: Node[] }[][]} */ -function parseOtslRows(container) { - const nodes = [...container.childNodes]; - let i = skipOtslContainerHead(nodes, 0); - /** @type {{ kind: string, token: Element, contentNodes: Node[] }[][]} */ - const rows = []; - /** @type {{ kind: string, token: Element, contentNodes: Node[] }[]} */ - let currentRow = []; - - while (i < nodes.length) { - const node = nodes[i]; - if (node.nodeType !== Node.ELEMENT_NODE) { - i += 1; - continue; - } - const tag = localName(node); - if (tag === "nl") { - rows.push(currentRow); - currentRow = []; - i += 1; - continue; - } - if (!isCellToken(tag)) { - i += 1; - continue; - } - - if (CELL_SPAN_TAGS.has(tag)) { - currentRow.push({ kind: tag, token: node, contentNodes: [] }); - i += 1; - continue; - } - - i += 1; - const head = parseElementHeadAt(nodes, i); - if (head) i = head.nextIndex; - const contentStart = i; - i = skipUntilCellBoundary(nodes, i); - currentRow.push({ kind: tag, token: node, contentNodes: nodes.slice(contentStart, i) }); - } - - if (currentRow.length) rows.push(currentRow); - return rows; -} - -function findVerticalCellOrigin(grid, row, col) { - for (let r = row - 1; r >= 0; r -= 1) { - const cell = grid[r]?.[col]; - if (!cell || cell.covered) continue; - return { cell, row: r, col }; - } - return null; -} - -function findHorizontalCellOrigin(grid, row, col) { - for (let c = col - 1; c >= 0; c -= 1) { - const cell = grid[row]?.[c]; - if (!cell || cell.covered) continue; - return { cell, row, col: c }; - } - return null; -} - -function nextFreeColumn(grid, row, col) { - let c = col; - while (grid[row]?.[c]?.covered) c += 1; - return c; -} - -/** @returns {{ kind: string, token: Element, contentNodes: Node[], colspan: number, rowspan: number, covered?: boolean }[][]} */ -function buildOtslGrid(rows) { - /** @type {{ kind: string, token: Element, contentNodes: Node[], colspan: number, rowspan: number, covered?: boolean }[][]} */ - const grid = []; - - for (let rowIdx = 0; rowIdx < rows.length; rowIdx += 1) { - if (!grid[rowIdx]) grid[rowIdx] = []; - let col = 0; - - for (const parsed of rows[rowIdx]) { - col = nextFreeColumn(grid, rowIdx, col); - - if (parsed.kind === "lcel") { - const origin = findHorizontalCellOrigin(grid, rowIdx, col); - if (origin) origin.cell.colspan += 1; - grid[rowIdx][col] = { kind: "lcel", token: parsed.token, contentNodes: [], colspan: 0, rowspan: 0, covered: true }; - col += 1; - continue; - } - - if (parsed.kind === "ucel") { - const origin = findVerticalCellOrigin(grid, rowIdx, col); - if (origin) origin.cell.rowspan += 1; - grid[rowIdx][col] = { kind: "ucel", token: parsed.token, contentNodes: [], colspan: 0, rowspan: 0, covered: true }; - col += 1; - continue; - } - - if (parsed.kind === "xcel") { - const vOrigin = findVerticalCellOrigin(grid, rowIdx, col); - const hOrigin = findHorizontalCellOrigin(grid, rowIdx, col); - if (vOrigin && hOrigin && vOrigin.cell === hOrigin.cell) { - vOrigin.cell.rowspan += 1; - vOrigin.cell.colspan += 1; - } else { - if (vOrigin) vOrigin.cell.rowspan += 1; - if (hOrigin) hOrigin.cell.colspan += 1; - } - grid[rowIdx][col] = { kind: "xcel", token: parsed.token, contentNodes: [], colspan: 0, rowspan: 0, covered: true }; - col += 1; - continue; - } - - grid[rowIdx][col] = { - kind: parsed.kind, - token: parsed.token, - contentNodes: parsed.contentNodes, - colspan: 1, - rowspan: 1, - covered: false, - }; - col += 1; - } - } - - return grid; -} - -function appendTableCellContent(container, nodes, elementIds, cellToken) { - appendRenderedSliceContent(container, cellToken, nodes, elementIds); -} - -function renderOtslContainer(el, elementIds) { - const table = document.createElement("table"); - table.className = "rendered-table"; - - const captionEl = readCaptionElement(el); - if (captionEl) { - table.appendChild(renderEmbeddedCaption(captionEl, elementIds, "caption")); - } - - const grid = buildOtslGrid(parseOtslRows(el)); - const tbody = document.createElement("tbody"); - - for (const row of grid) { - const tr = document.createElement("tr"); - for (const cell of row) { - if (!cell || cell.covered) continue; - const cellTag = isHeaderCellKind(cell.kind) ? "th" : "td"; - const td = document.createElement(cellTag); - if (cell.colspan > 1) td.colSpan = cell.colspan; - if (cell.rowspan > 1) td.rowSpan = cell.rowspan; - appendTableCellContent(td, cell.contentNodes, elementIds, cell.token); - tr.appendChild(td); - } - if (tr.childNodes.length) tbody.appendChild(tr); - } - - if (tbody.childNodes.length) table.appendChild(tbody); - return wrapRendered(el, table, elementIds.get(el)); -} - -function serializeSegment(segment) { - return segment.map((el) => serializeElement(el, 0)).join("\n"); -} - -function serializeElement(el, depth) { - const pad = " ".repeat(depth); - const tag = localName(el); - const attrs = [...el.attributes] - .filter((a) => a.name !== "xmlns" || a.value !== DOCLANG_NS) - .map((a) => `${a.name}="${a.value}"`) - .join(" "); - const attrStr = attrs ? ` ${attrs}` : ""; - - if (!el.childNodes.length) return `${pad}<${tag}${attrStr}/>`; - - const meaningfulText = [...el.childNodes].filter((n) => isTextLikeNode(n) && !isWhitespaceOnlyText(n)); - const textOnly = meaningfulText.length > 0 && meaningfulText.every(isTextLikeNode) && !childElements(el).length; - if (textOnly) { - const text = serializeMarkupTextNodes(el.childNodes); - if (text) { - return `${pad}<${tag}${attrStr}>${text}`; - } - } - - const parts = [`${pad}<${tag}${attrStr}>`]; - for (const child of el.childNodes) { - if (isTextLikeNode(child)) { - const text = formatMarkupTextNode(child); - if (text) parts.push(" ".repeat(depth + 1) + text); - } else if (child.nodeType === Node.ELEMENT_NODE) { - parts.push(serializeElement(child, depth + 1)); - } - } - parts.push(`${pad}`); - return parts.join("\n"); -} - -function childElements(el) { - return [...el.children]; -} - -function localName(el) { - return el.localName || el.tagName.replace(/^.*:/, ""); -} - -function normalizeArchivePath(path) { - return path.replace(/\\/g, "/").replace(/^\.\//, ""); -} - -function archiveRelativeAssetPath(path) { - const norm = normalizeArchivePath(path); - const idx = norm.indexOf("assets/"); - return idx === -1 ? null : norm.slice(idx); -} - -function mimeFromAssetPath(path) { - const ext = path.split(".").pop()?.toLowerCase() ?? ""; - switch (ext) { - case "png": - return "image/png"; - case "jpg": - case "jpeg": - return "image/jpeg"; - case "webp": - return "image/webp"; - case "gif": - return "image/gif"; - case "svg": - return "image/svg+xml"; - default: - return "application/octet-stream"; - } -} - -/** - * Resolve a picture @uri to a safe img src. - * Only archive asset blob URLs and small raster data: URIs are allowed. - * Remote, protocol-relative, blob:, and unresolved relative URIs are rejected. - * @returns {string | null} - */ -function resolveArchiveUri(uri) { - if (!uri) return null; - const trimmed = uri.trim(); - if (!trimmed) return null; - - if (SAFE_DATA_IMAGE_RE.test(trimmed)) { - return trimmed.length <= MAX_DATA_IMAGE_URI_LENGTH ? trimmed : null; - } - - // Reject any other scheme (http, https, blob, javascript, …) and protocol-relative URLs. - if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed) || trimmed.startsWith("//")) return null; - - return state?.assetUrls?.get(normalizeArchivePath(trimmed)) ?? null; -} - -function revokeDocumentState(docState) { - if (!docState) return; - for (const url of docState.pageImages.values()) { - if (url.startsWith("blob:")) URL.revokeObjectURL(url); - } - for (const url of docState.assetUrls.values()) { - if (url.startsWith("blob:")) URL.revokeObjectURL(url); - } -} - -function revokeArchiveUrls() { - revokeDocumentState(state); -} - -async function extractArchiveFromZipBuffer(buffer) { - const entries = await unzip(buffer); - const markupEntry = findArchiveEntry(entries, "document.xml"); - if (!markupEntry) { - throw new Error("Archive must contain document.xml"); - } - const markupXml = new TextDecoder().decode(markupEntry.data); - const pageImages = new Map(); - const assetUrls = new Map(); - for (const e of entries) { - const m = e.name.match(/^pages\/(\d+)\.(png|jpe?g|webp)$/i); - if (m) { - const ext = m[2].toLowerCase().replace("jpeg", "jpg"); - const mime = ext === "png" ? "image/png" : ext === "webp" ? "image/webp" : "image/jpeg"; - pageImages.set(Number(m[1]), URL.createObjectURL(new Blob([e.data], { type: mime }))); - continue; - } - if (e.name.startsWith("assets/") && !e.name.endsWith("/")) { - assetUrls.set( - e.name, - URL.createObjectURL(new Blob([e.data], { type: mimeFromAssetPath(e.name) })), - ); - } - } - return { markupXml, pageImages, assetUrls }; -} - -async function unzip(buffer, { shouldExtract } = {}) { - if (typeof DecompressionStream === "undefined") { - throw new Error("ZIP decompression requires a browser with DecompressionStream support"); - } - - const bytes = new Uint8Array(buffer); - const view = new DataView(buffer); - const eocdOffset = findEndOfCentralDirectory(bytes); - const entryCount = view.getUint16(eocdOffset + 10, true); - const centralDirOffset = view.getUint32(eocdOffset + 16, true); - - if (entryCount > ZIP_MAX_ENTRIES) { - throw new Error("ZIP archive has too many entries"); - } - if (centralDirOffset >= bytes.length) { - throw new Error("Invalid ZIP central directory offset"); - } - - /** @type {{ name: string, data: Uint8Array }[]} */ - const entries = []; - let offset = centralDirOffset; - let totalUncompressed = 0; - - for (let i = 0; i < entryCount; i += 1) { - if (offset + 46 > bytes.length || view.getUint32(offset, true) !== 0x02014b50) { - throw new Error("Invalid ZIP central directory"); - } - - const compression = view.getUint16(offset + 10, true); - const compressedSize = view.getUint32(offset + 20, true); - const uncompressedSize = view.getUint32(offset + 24, true); - const nameLength = view.getUint16(offset + 28, true); - const extraLength = view.getUint16(offset + 30, true); - const commentLength = view.getUint16(offset + 32, true); - const localHeaderOffset = view.getUint32(offset + 42, true); - const nameEnd = offset + 46 + nameLength; - if (nameEnd > bytes.length) { - throw new Error("Invalid ZIP entry name"); - } - const rawName = new TextDecoder().decode(bytes.subarray(offset + 46, nameEnd)); - const name = normalizeZipEntryName(rawName); - - offset = nameEnd + extraLength + commentLength; - if (offset > bytes.length) { - throw new Error("Invalid ZIP central directory"); - } - - if (!name || name.endsWith("/") || isIgnoredArchiveEntry(name)) continue; - if (shouldExtract && !shouldExtract(name)) continue; - - if (compressedSize > ZIP_MAX_ENTRY_COMPRESSED_BYTES || uncompressedSize > ZIP_MAX_ENTRY_UNCOMPRESSED_BYTES) { - throw new Error(`ZIP entry exceeds size limit: ${name}`); - } - if ( - compressedSize > 0 && - uncompressedSize > 0 && - uncompressedSize / compressedSize > ZIP_MAX_COMPRESSION_RATIO - ) { - throw new Error(`ZIP entry compression ratio exceeds limit: ${name}`); - } - if (totalUncompressed + uncompressedSize > ZIP_MAX_TOTAL_UNCOMPRESSED_BYTES) { - throw new Error("ZIP archive exceeds total uncompressed size limit"); - } - - if (localHeaderOffset + 30 > bytes.length || view.getUint32(localHeaderOffset, true) !== 0x04034b50) { - throw new Error(`Invalid ZIP local header: ${name}`); - } - const localNameLength = view.getUint16(localHeaderOffset + 26, true); - const localExtraLength = view.getUint16(localHeaderOffset + 28, true); - const dataOffset = localHeaderOffset + 30 + localNameLength + localExtraLength; - if (dataOffset + compressedSize > bytes.length) { - throw new Error(`ZIP entry data out of bounds: ${name}`); - } - const compressed = bytes.subarray(dataOffset, dataOffset + compressedSize); - const data = await decompressZipEntry(compressed, compression, uncompressedSize); - totalUncompressed += data.length; - if (totalUncompressed > ZIP_MAX_TOTAL_UNCOMPRESSED_BYTES) { - throw new Error("ZIP archive exceeds total uncompressed size limit"); - } - entries.push({ name, data }); - } - - return entries; -} - -function findEndOfCentralDirectory(bytes) { - for (let i = bytes.length - 22; i >= 0; i -= 1) { - if ( - bytes[i] === 0x50 && - bytes[i + 1] === 0x4b && - bytes[i + 2] === 0x05 && - bytes[i + 3] === 0x06 - ) { - return i; - } - } - throw new Error("Invalid ZIP archive"); -} - -function concatUint8Arrays(chunks, totalLength) { - const out = new Uint8Array(totalLength); - let offset = 0; - for (const chunk of chunks) { - out.set(chunk, offset); - offset += chunk.byteLength; - } - return out; -} - -async function decompressZipEntry(data, method, uncompressedSize) { - if (method === 0) { - if (data.length > ZIP_MAX_ENTRY_UNCOMPRESSED_BYTES) { - throw new Error("ZIP entry exceeds size limit"); - } - return data; - } - if (method === 8) { - const stream = new Blob([data]).stream().pipeThrough(new DecompressionStream("deflate-raw")); - const reader = stream.getReader(); - const chunks = []; - let total = 0; - try { - while (true) { - const { done, value } = await reader.read(); - if (done) break; - total += value.byteLength; - if (total > ZIP_MAX_ENTRY_UNCOMPRESSED_BYTES) { - throw new Error("ZIP entry exceeds size limit"); - } - if (data.length > 0 && total / data.length > ZIP_MAX_COMPRESSION_RATIO) { - throw new Error("ZIP entry compression ratio exceeds limit"); - } - chunks.push(value); - } - } catch (err) { - try { - await reader.cancel(); - } catch { - /* ignore cancel errors */ - } - throw err; - } - - const out = concatUint8Arrays(chunks, total); - if (uncompressedSize && out.length !== uncompressedSize) { - return out.slice(0, Math.min(out.length, uncompressedSize)); - } - return out; - } - throw new Error(`Unsupported ZIP compression method ${method}`); -} - -function normalizeZipEntryName(name) { - return name.replace(/\\/g, "/").replace(/^\.\//, ""); -} - -/** Match archive root files by exact name; skip macOS metadata entries. */ -function findArchiveEntry(entries, fileName) { - return entries.find((e) => e.name === fileName); -} - -function isIgnoredArchiveEntry(name) { - if (name === ".DS_Store" || name.endsWith("/.DS_Store")) return true; - return name.split("/").some((part) => part.startsWith("._") || part === "__MACOSX"); -} +Bn = H([E("doclang-page-nav")], Bn); +//#endregion +//#region node_modules/lit-html/directives/class-map.js +var W = nt(class extends rt { + constructor(e) { + if (super(e), e.type !== tt.ATTRIBUTE || e.name !== "class" || e.strings?.length > 2) throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute."); + } + render(e) { + return " " + Object.keys(e).filter((t) => e[t]).join(" ") + " "; + } + update(e, [t]) { + if (this.st === void 0) { + this.st = /* @__PURE__ */ new Set(), e.strings !== void 0 && (this.nt = new Set(e.strings.join(" ").split(/\s/).filter((e) => e !== ""))); + for (let e in t) t[e] && !this.nt?.has(e) && this.st.add(e); + return this.render(t); + } + let n = e.element.classList; + for (let e of this.st) e in t || (n.remove(e), this.st.delete(e)); + for (let e in t) { + let r = !!t[e]; + r === this.st.has(e) || this.nt?.has(e) || (r ? (n.add(e), this.st.add(e)) : (n.remove(e), this.st.delete(e))); + } + return S; + } +}), Vn = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{display:contents}.toolbar{flex-wrap:wrap;grid-column:3;justify-self:end;align-items:center;gap:.5rem;display:flex}.header-divider{background:var(--border);flex-shrink:0;align-self:center;width:1px;height:1.125rem}.toolbar-options-item{cursor:pointer;font-size:.875rem;font-family:var(--font-ui);align-items:center;gap:.5rem;padding:.35rem .75rem;display:flex}.toolbar-options-item:hover{background:color-mix(in srgb, var(--accent) 6%, var(--panel))}.toolbar-options-item input{cursor:pointer;flex-shrink:0;margin:0}.toolbar-options-item-disabled{opacity:.45;cursor:not-allowed}.toolbar-options-item-disabled input{cursor:not-allowed}.toolbar-options-divider{border-top:1px solid var(--border);margin:.35rem 0}.toolbar-options-reset{box-sizing:border-box;width:100%;color:var(--text);font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:0;margin:0;padding:.35rem .75rem;font-size:.875rem;display:block}.toolbar-options-reset:hover:not(:disabled){background:color-mix(in srgb, var(--accent) 6%, var(--panel));color:var(--accent)}.toolbar-options-reset:disabled{opacity:.45;cursor:not-allowed}.toolbar-file-group{align-items:center;display:inline-flex}button,label.file-btn{appearance:none;border:1px solid var(--border);background:var(--panel);color:var(--text);font:inherit;cursor:pointer;border-radius:.375rem;padding:.4rem .75rem;font-size:.875rem}button:hover,label.file-btn:hover{border-color:var(--accent)}button:disabled{opacity:.45;cursor:not-allowed}label.file-btn input{display:none}.header-site-link{color:var(--muted);white-space:nowrap;font-size:.875rem;text-decoration:none}.header-site-link:hover{color:var(--accent);text-decoration:underline}.header-site-link:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:.125rem}", Hn = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{display:contents}.dropdown-wrap{position:relative}.dropdown-btn{appearance:none;color:var(--muted);cursor:pointer;white-space:nowrap;font-size:.8125rem;font-weight:500;line-height:1.2;font:inherit;background:0 0;border:1px solid #0000;border-radius:.375rem;flex-shrink:0;align-items:center;padding:.35rem .45rem .35rem .55rem;transition:color .12s,background .12s,border-color .12s;display:inline-flex}.dropdown-btn:after{content:\"\";opacity:.65;border-bottom:1.5px solid;border-right:1.5px solid;flex-shrink:0;width:.32rem;height:.32rem;margin-top:-.14em;margin-left:.3rem;transform:rotate(45deg)}.dropdown-btn:hover{color:var(--text);background:color-mix(in srgb, var(--text) 5%, transparent)}.dropdown-btn[aria-expanded=true]{color:var(--accent);background:color-mix(in srgb, var(--accent) 10%, var(--panel));border-color:color-mix(in srgb, var(--accent) 22%, transparent)}.dropdown-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.dropdown-panel{z-index:30;border:1px solid var(--border);background:var(--panel);min-width:11rem;box-shadow:0 .5rem 1.25rem color-mix(in srgb, var(--text) 12%, transparent);border-radius:.375rem;padding:.5rem 0;position:absolute;top:calc(100% + .35rem);right:0}", Un = class extends T { + constructor(...e) { + super(...e), this.label = "", this._open = !1, this._onBtnClick = (e) => { + e.stopPropagation(), this.setOpen(!this._open); + }, this._onDocClick = (e) => { + this._open && (e.composedPath().includes(this) || this.setOpen(!1)); + }, this._onDocKeydown = (e) => { + this._open && e.key === "Escape" && this.setOpen(!1); + }; + } + static { + this.styles = s(Hn); + } + render() { + return b` + + `; + } + setOpen(e) { + this._open = e, this.requestUpdate(); + } + get isOpen() { + return this._open; + } + connectedCallback() { + super.connectedCallback(), document.addEventListener("click", this._onDocClick), document.addEventListener("keydown", this._onDocKeydown); + } + disconnectedCallback() { + super.disconnectedCallback(), document.removeEventListener("click", this._onDocClick), document.removeEventListener("keydown", this._onDocKeydown); + } +}; +H([D({ type: String })], Un.prototype, "label", void 0), Un = H([E("doclang-dropdown")], Un); +//#endregion +//#region src/components/viewer/toolbar.ts +var Wn = class extends T { + constructor(...e) { + super(...e), this._demoLoading = !1, this._panes = { + file: !0, + page: !0, + markup: !0, + reading: !0, + fileAvailable: !1, + pageAvailable: !1, + hasState: !1 + }, this._inputArchiveRef = A(), this._dropdownRef = A(), this._onArchiveChange = (e) => { + let t = e.target; + this.dispatchEvent(new CustomEvent("doclang-open-files", { + bubbles: !0, + composed: !0, + detail: { files: [...t.files ?? []] } + })), t.value = ""; + }; + } + static { + this.styles = s(Vn); + } + render() { + let { file: e, page: t, markup: n, reading: r, fileAvailable: i, pageAvailable: a, hasState: o } = this._panes; + return b` +
+ + + + + + + + + + + + + + + doclang.ai +
+ `; + } + syncPaneToggles(e) { + this._panes = e, this.requestUpdate(); + } + setDemoLoading(e) { + this._demoLoading = e, this.requestUpdate(); + } + _emitTogglePane(e, t) { + this.dispatchEvent(new CustomEvent("doclang-toggle-pane", { + bubbles: !0, + composed: !0, + detail: { + pane: e, + checked: t + } + })); + } +}; +Wn = H([E("doclang-toolbar")], Wn); +//#endregion +//#region node_modules/lit-html/directives/repeat.js +var Gn = (e, t, n) => { + let r = /* @__PURE__ */ new Map(); + for (let i = t; i <= n; i++) r.set(e[i], i); + return r; +}, Kn = nt(class extends rt { + constructor(e) { + if (super(e), e.type !== tt.CHILD) throw Error("repeat() can only be used in text expressions"); + } + dt(e, t, n) { + let r; + n === void 0 ? n = t : t !== void 0 && (r = t); + let i = [], a = [], o = 0; + for (let t of e) i[o] = r ? r(t, o) : o, a[o] = n(t, o), o++; + return { + values: a, + keys: i + }; + } + render(e, t, n) { + return this.dt(e, t, n).values; + } + update(e, [t, n, r]) { + let i = $e(e), { values: a, keys: o } = this.dt(t, n, r); + if (!Array.isArray(i)) return this.ut = o, a; + let s = this.ut ??= [], c = [], l, u, d = 0, f = i.length - 1, p = 0, m = a.length - 1; + for (; d <= f && p <= m;) if (i[d] === null) d++; + else if (i[f] === null) f--; + else if (s[d] === o[p]) c[p] = k(i[d], a[p]), d++, p++; + else if (s[f] === o[m]) c[m] = k(i[f], a[m]), f--, m--; + else if (s[d] === o[m]) c[m] = k(i[d], a[m]), Xe(e, c[m + 1], i[d]), d++, m--; + else if (s[f] === o[p]) c[p] = k(i[f], a[p]), Xe(e, i[d], i[f]), f--, p++; + else if (l === void 0 && (l = Gn(o, p, m), u = Gn(s, d, f)), l.has(s[d])) { + if (l.has(s[f])) { + let t = u.get(o[p]), n = t === void 0 ? null : i[t]; + if (n === null) { + let t = Xe(e, i[d]); + k(t, a[p]), c[p] = t; + } else c[p] = k(n, a[p]), Xe(e, i[d], n), i[t] = null; + p++; + } else et(i[f]), f--; + } else et(i[d]), d++; + for (; p <= m;) { + let t = Xe(e, c[m + 1]); + k(t, a[p]), c[p++] = t; + } + for (; d <= f;) { + let e = i[d++]; + e !== null && et(e); + } + return this.ut = o, Qe(e, c), S; + } +}), qn = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{border-right:1px solid var(--border);background:var(--panel);--file-thumb-size:var(--doclang-file-thumb-size,3.75rem);--file-thumb-close-overflow:var(--doclang-file-thumb-close-overflow,.25rem);--file-item-pad-x:var(--doclang-file-item-pad-x,.35rem);--file-pane-pad-x:var(--doclang-file-pane-pad-x,.5rem);--file-pane-fit-width:var(--doclang-file-pane-fit-width,calc(( var(--file-pane-pad-x) * 2 + var(--file-item-pad-x) * 2 + var(--file-thumb-size) + var(--file-thumb-close-overflow) ) * 1.5));flex-direction:column;min-width:0;min-height:0;display:flex;overflow:hidden}:host([hidden]){display:none!important}:host(.pane-layout-last){border-right:none}.pane-header{box-sizing:border-box;letter-spacing:.04em;text-transform:uppercase;height:2.125rem;color:var(--muted);border-bottom:1px solid var(--border);background:var(--panel);justify-content:space-between;align-items:center;gap:.35rem;padding:0 .35rem;font-size:.75rem;font-weight:600;display:flex}.pane-header-title{text-overflow:ellipsis;white-space:nowrap;min-width:0;padding-inline:.4rem;overflow:hidden}.file-pane-close-all{appearance:none;color:var(--muted);font:inherit;letter-spacing:.04em;text-transform:uppercase;cursor:pointer;white-space:nowrap;background:0 0;border:1px solid #0000;border-radius:.25rem;flex-shrink:0;height:1.25rem;padding:0 .15rem;font-size:.625rem;font-weight:600;line-height:1;transition:color .12s,background .12s,border-color .12s}.file-pane-close-all:hover{color:var(--text);background:color-mix(in srgb, var(--text) 5%, transparent)}.file-pane-close-all:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.pane-body{min-height:0;padding:.375rem var(--file-pane-pad-x,.5rem);flex:1;min-width:0;overflow:auto}.file-view-list{flex-direction:column;gap:.25rem;width:100%;min-width:0;margin:0;padding:0;list-style:none;display:flex}.file-view-list>li{width:100%;max-width:100%}.file-view-item{box-sizing:border-box;width:100%;max-width:100%;color:var(--text);font:inherit;text-align:center;padding:.5rem var(--file-item-pad-x,.35rem);cursor:pointer;background:0 0;border:none;border-radius:0;flex-direction:column;align-items:center;gap:.35rem;font-size:.8125rem;line-height:1.35;display:flex}.file-view-thumb-wrap{flex-shrink:0;position:relative}.file-view-close{appearance:none;border:1px solid var(--border);background:var(--bg);width:1rem;height:1rem;color:var(--muted);font:inherit;cursor:pointer;z-index:1;border-radius:50%;justify-content:center;align-items:center;padding:0;font-size:.75rem;line-height:1;display:flex;position:absolute;top:-.25rem;right:-.25rem;box-shadow:0 1px 2px #00000014}.file-view-close:hover{color:var(--text);background:var(--panel)}.file-view-close:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.file-view-thumb{width:var(--file-thumb-size,3.75rem);height:var(--file-thumb-size,3.75rem);background:var(--placeholder-bg);border:1px solid var(--border);border-radius:.25rem;flex-shrink:0;justify-content:center;align-items:center;display:flex;overflow:hidden}.file-view-thumb img{object-fit:contain;width:100%;height:100%;display:block}.file-view-thumb-placeholder{width:100%;height:100%;color:var(--muted);justify-content:center;align-items:center;display:flex}.file-view-thumb-placeholder svg{width:1.875rem;height:1.875rem;display:block}.file-view-label{text-overflow:ellipsis;white-space:nowrap;width:100%;max-width:100%;overflow:hidden}.file-view-item:hover{background:var(--markup-hover)}.file-view-item.is-active{background:var(--markup-selected);color:var(--accent);font-weight:500}.file-view-item:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}", Jn = "", Yn = class extends T { + constructor(...e) { + super(...e), this._onCloseAll = () => { + this.dispatchEvent(new CustomEvent("doclang-collection-close-all", { + bubbles: !0, + composed: !0 + })); + }, this._renderEntry = (e, t) => { + let n = (e) => { + e.target.closest(".file-view-close") || this._emitSelect(t); + }, r = (e) => { + (e.key === "Enter" || e.key === " ") && (e.preventDefault(), this._emitSelect(t)); + }, i = (e) => { + e.stopPropagation(), this._emitClose(t); + }, a = e.thumbnailUrl ? b`` : b``; + return b` +
  • +
    +
    + + +
    + ${e.label} +
    +
  • + `; + }; + } + static { + this.styles = s(qn); + } + render() { + let e = (this.entries?.length ?? 0) > 0; + return b` +
    + Files + ${e ? b`` : C} +
    +
    ${e ? b` +
      + ${Kn(this.entries ?? [], (e, t) => t, this._renderEntry)} +
    + ` : C}
    + `; + } + _emitSelect(e) { + this.dispatchEvent(new CustomEvent("doclang-collection-select", { + bubbles: !0, + composed: !0, + detail: { index: e } + })); + } + _emitClose(e) { + this.dispatchEvent(new CustomEvent("doclang-collection-close", { + bubbles: !0, + composed: !0, + detail: { index: e } + })); + } +}; +H([D({ attribute: !1 })], Yn.prototype, "entries", void 0), Yn = H([E("doclang-collection-pane")], Yn); +//#endregion +//#region src/components/base/page-controller.ts +var Xn = 200, Zn = 4, Qn = 100, $n = class { + constructor(e, t) { + this._pixelAccum = 0, this._gestureUntil = 0, this._lastFlipAt = 0, this._lastPage = null, this._onKeyDown = (e) => { + let t = e, n = this._host, r = n.document; + if (!r || r.markupOnly || r.pageCount <= 1) return; + let i = 0; + switch (t.key) { + case "ArrowDown": + case "PageDown": + case "ArrowRight": + i = 1; + break; + case "ArrowUp": + case "PageUp": + case "ArrowLeft": i = -1; + } + if (!i) return; + t.preventDefault(); + let a = n.page + i; + a < 1 || a > r.pageCount || n.dispatchEvent(new CustomEvent("view-page", { + bubbles: !0, + composed: !0, + detail: { page: a } + })); + }, this._onWheel = (e) => { + let t = e, n = this._host, r = n.document; + if (!r || r.markupOnly || r.pageCount <= 1) return; + let i = this._getScrollPane(), a = this._dir(t); + if (!a) return; + if (i) { + let e = i.scrollTop <= 0, t = i.scrollTop + i.clientHeight >= i.scrollHeight - 1; + if ((i.scrollHeight > i.clientHeight || i.scrollWidth > i.clientWidth) && !(a < 0 && e) && !(a > 0 && t)) return; + } + let o = performance.now(); + if (o - this._lastFlipAt < Xn) { + t.preventDefault(); + return; + } + let s = n.page + a; + if (s < 1 || s > r.pageCount) { + t.preventDefault(); + return; + } + t.preventDefault(), this._lastFlipAt = o, n.dispatchEvent(new CustomEvent("view-page", { + bubbles: !0, + composed: !0, + detail: { page: s } + })); + }, this._host = e, this._getScrollPane = t, e.addController(this); + } + hostConnected() { + this._host.addEventListener("wheel", this._onWheel, { passive: !1 }), this._host.addEventListener("keydown", this._onKeyDown); + } + hostDisconnected() { + this._host.removeEventListener("wheel", this._onWheel), this._host.removeEventListener("keydown", this._onKeyDown); + } + hostUpdated() { + let e = this._host.page; + if (this._lastPage !== null && this._lastPage !== e) { + let t = this._getScrollPane(); + t && (t.scrollTop = (e > this._lastPage ? 1 : -1) > 0 ? 0 : t.scrollHeight); + } + this._lastPage = e; + } + _dir(e) { + if (e.deltaMode === 1) return e.deltaY > 0 ? 1 : e.deltaY < 0 ? -1 : 0; + if (e.deltaMode === 2) return Math.sign(e.deltaY); + let t = performance.now(); + if (t > this._gestureUntil && (this._pixelAccum = 0), this._gestureUntil = t + Qn, this._pixelAccum += e.deltaY, Math.abs(this._pixelAccum) >= Zn) { + let e = this._pixelAccum > 0 ? 1 : -1; + return this._pixelAccum = 0, e; + } + return 0; + } +}, er = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{border-right:1px solid var(--border);background:var(--panel);--xml-tag:var(--doclang-xml-tag,#116329);--xml-bracket:var(--doclang-xml-bracket,#24292e);--xml-attr-name:var(--doclang-xml-attr-name,#0550ae);--xml-attr-value:var(--doclang-xml-attr-value,#0a3069);--xml-text:var(--doclang-xml-text,#24292e);--xml-cdata:var(--doclang-xml-cdata,#6a737d);--xml-cdata-delimiter:var(--doclang-xml-cdata-delimiter,#6a737d);flex-direction:column;min-width:0;min-height:0;display:flex}:host([hidden]){display:none!important}:host(.pane-layout-last){border-right:none}.pane-header{box-sizing:border-box;letter-spacing:.04em;text-transform:uppercase;height:2.125rem;color:var(--muted);border-bottom:1px solid var(--border);background:var(--panel);align-items:center;gap:.5rem;padding:0 .75rem;font-size:.75rem;font-weight:600;display:flex}.pane-body{background:var(--markup-bg);min-height:0;color:var(--markup-fg);flex:1;padding:0;overflow:auto}.pane-body .placeholder{margin:1rem}.placeholder{min-height:12rem;color:var(--muted);background:var(--placeholder-bg);border:1px dashed var(--border);text-align:center;border-radius:.5rem;justify-content:center;align-items:center;padding:2rem;font-style:italic;display:flex}pre.markup{font-family:var(--font-mono);white-space:pre-wrap;word-break:break-word;margin:0;font-size:.8125rem;line-height:1.5}.markup{font-family:var(--font-mono);tab-size:2;--markup-indent:2ch;--markup-gutter-width:1rem;margin:0;padding:.75rem 1rem 1rem;font-size:.8125rem;line-height:1.55}.markup-ghost-tag-part{opacity:.35;cursor:help}.head-tooltip{border-collapse:collapse;width:100%;font-size:.6875rem;line-height:1.4}.head-tooltip th{color:var(--muted);text-align:left;vertical-align:top;white-space:nowrap;padding:.1rem .55rem .1rem 0;font-weight:600}.head-tooltip td{vertical-align:top;word-break:break-word;padding:.1rem 0}.head-tooltip .head-default{color:var(--muted);white-space:nowrap;font-style:italic}.markup-el{cursor:pointer;border-radius:.2rem}.markup-el:hover{background:var(--markup-hover)}.markup-el.selected{background:var(--markup-selected);box-shadow:inset 0 0 0 1px var(--accent)}.markup-line{white-space:pre-wrap;word-break:break-word;cursor:pointer;border-radius:.15rem;padding:0 .25rem}.markup-line-content{min-width:0;padding-left:calc(var(--markup-depth,0) * var(--markup-indent));align-items:baseline;display:flex}.markup-gutter{flex:0 0 var(--markup-gutter-width);width:var(--markup-gutter-width);justify-content:center;align-items:center;display:flex}.markup-line-body{flex:auto;min-width:0}summary::-webkit-details-marker{display:none}summary{list-style:none;display:block}summary:focus{outline:none}.markup-fold-toggle{appearance:none;width:var(--markup-gutter-width);height:1rem;color:var(--muted);cursor:pointer;background:0 0;border:none;border-radius:.15rem;flex-shrink:0;justify-content:center;align-items:center;margin:0;padding:0;display:inline-flex}.markup-fold-toggle:before{content:\"\";border-top:4px solid #0000;border-bottom:4px solid #0000;border-left:5px solid;width:0;height:0;transition:transform .12s;display:block;transform:rotate(90deg)}details:not([open])>summary .markup-fold-toggle:before{transform:rotate(0)}.markup-fold-toggle:hover{background:var(--markup-hover);color:var(--markup-fg)}.markup-fold-suffix{display:none}details:not([open])>summary .markup-fold-suffix{display:inline}details:not([open])>.markup-fold-body{display:none}.xml-tag{color:var(--xml-tag)}.xml-bracket{color:var(--xml-bracket)}.xml-attr-name{color:var(--xml-attr-name)}.xml-attr-value{color:var(--xml-attr-value)}.xml-attr-value-truncatable{display:inline}.xml-attr-value-chip{appearance:none;border:1px solid var(--border);background:var(--placeholder-bg);color:var(--muted);font-family:var(--font-ui,system-ui, sans-serif);cursor:pointer;vertical-align:baseline;white-space:nowrap;border-radius:.25rem;align-items:center;gap:.3em;margin:0 0 0 .35em;padding:.08em .45em .08em .5em;font-size:.72em;font-weight:600;line-height:1.35;display:inline-flex}.xml-attr-value-chip:after{content:\"\";opacity:.75;border-top:.28em solid;border-left:.22em solid #0000;border-right:.22em solid #0000;width:0;height:0;transition:transform .12s}.xml-attr-value-chip[aria-expanded=true]:after{transform:rotate(180deg)}.xml-attr-value-chip:hover,.xml-attr-value-chip[aria-expanded=true]{border-color:var(--accent);color:var(--accent);background:color-mix(in srgb, var(--accent) 8%, var(--placeholder-bg))}.markup-embedded-uri-panel{cursor:text}.markup-embedded-uri-panel:hover{background:0 0}.markup-embedded-uri-panel-body{max-height:8rem;color:var(--xml-attr-value);word-break:break-all;background:var(--placeholder-bg);border:1px solid var(--border);-webkit-user-select:text;user-select:text;border-radius:.25rem;margin:.1rem 0 .25rem;padding:.45rem .6rem;overflow:auto}.xml-text{color:var(--xml-text)}.xml-cdata{color:var(--xml-cdata)}.xml-cdata-delimiter{color:var(--xml-cdata-delimiter)}@media (prefers-color-scheme:dark){:host{--xml-tag:var(--doclang-xml-tag,#4ec9b0);--xml-bracket:var(--doclang-xml-bracket,gray);--xml-attr-name:var(--doclang-xml-attr-name,#9cdcfe);--xml-attr-value:var(--doclang-xml-attr-value,#ce9178);--xml-text:var(--doclang-xml-text,#d4d4d4);--xml-cdata:var(--doclang-xml-cdata,#b5cea8);--xml-cdata-delimiter:var(--doclang-xml-cdata-delimiter,gray)}}", tr = "DocLang virtual ; wrapping tags not included in source", nr = 30; +function rr(e) { + return !e || e.length <= nr ? !1 : /^(data:image\/|blob:)/i.test(e); +} +function ir(e) { + if (e < 1024) return `${e} B`; + if (e < 1048576) { + let t = e / 1024; + return t < 10 ? `${t.toFixed(1)} KB` : `${Math.round(t)} KB`; + } + return `${(e / 1048576).toFixed(1)} MB`; +} +function ar(e) { + if (/^blob:/i.test(e)) return e.length >= 1024 ? `${Math.round(e.length / 1024)} KB URL` : `${e.length} char URL`; + let t = e.indexOf(","); + if (t === -1) return "embedded data"; + let n = e.slice(0, t), r = e.slice(t + 1).replace(/\s/g, ""), i = /^data:([^;,]+)/i.exec(n)?.[1] ?? ""; + return `${i.startsWith("image/") ? i.slice(6) : i || "data"} · ${ir(Math.floor(r.replace(/=+$/, "").length * 3 / 4))}`; +} +function or(e) { + for (let t = 0; t < e.length; t += 1) { + let n = e[t]; + if (n && (P(n) && !F(n) || n.nodeType === Node.ELEMENT_NODE)) return !0; + } + return !1; +} +function sr(e) { + if (F(e)) return !0; + if (e.nodeType !== Node.ELEMENT_NODE) return !1; + let t = L(e); + return t === "location" || M.has(t); +} +function cr(e) { + if (!or(e)) return !1; + for (let t of e) if (!sr(t) && (P(t) || t.nodeType === Node.ELEMENT_NODE && !Ft(t))) return !0; + return !1; +} +var lr = class extends U { + constructor(...e) { + super(...e), this._wheel = new $n(this, () => this.scrollPane), this._hasMarkup = null, this._markupTemplate = null, this._onBodyClick = (e) => { + let t = e.target, n = t.closest(".xml-attr-value-chip"); + if (n) { + e.stopPropagation(), this._toggleTruncatableMarkupAttrValue(n); + return; + } + let r = t.closest(".markup-el-virtual-text"), i = r?.hasAttribute("data-element-id") ? r.getAttribute("data-element-id") : t.closest(".markup-el[data-element-id]")?.getAttribute("data-element-id") ?? null; + i && this.dispatchEvent(new CustomEvent("doclang-element-select", { + bubbles: !0, + composed: !0, + detail: { id: i } + })); + }, this._onSummaryClick = (e) => { + let t = e.target; + !t.closest(".markup-fold-toggle") && !t.closest(".markup-gutter") && e.preventDefault(); + }; + } + static { + this.styles = s(er); + } + connectedCallback() { + super.connectedCallback(), this.classList.add("pane", "pane-markup"); + } + render() { + return b`
    DocLang
    ${this._hasMarkup === !1 ? b`
    ${fn}
    ` : this._hasMarkup === !0 && this._markupTemplate ? b`
    ${this._markupTemplate}
    ` : C}
    `; + } + get scrollPane() { + return this.shadowRoot?.querySelector(".pane-body") ?? null; + } + _applySelection() { + if (!this.shadowRoot) return; + for (let e of this.shadowRoot.querySelectorAll(".markup-el.selected")) e.classList.remove("selected"); + if (!this.selected) return; + let e = this.shadowRoot.querySelector(`.markup-el-virtual-text[data-element-id="${this.selected}"]`) ?? this.shadowRoot.querySelector(`[data-element-id="${this.selected}"]`); + e && (e.classList.add("selected"), e.scrollIntoView({ + block: "nearest", + behavior: "smooth" + })); + } + _renderDocument() { + let e = this._docState; + if (!e) { + this._markupTemplate = null, this._hasMarkup = null; + return; + } + let t = e.segments[this.page - 1] ?? [], n = _n(t); + e.elementIds = n, e.idToElement = vn(n), hn(t) ? (this._markupTemplate = this._buildMarkupView(t, n), this._hasMarkup = !0) : (this._markupTemplate = null, this._hasMarkup = !1), this.requestUpdate(); + } + _clearDocument() { + this._markupTemplate = null, this._hasMarkup = null, this.requestUpdate(); + } + _buildMarkupView(e, t) { + let n = []; + for (let r of e) r.nodeType === Node.ELEMENT_NODE && n.push(this._renderMarkupElement(r, 0, t)); + return n; + } + _createEmbeddedUriContinuationPanel(e, t) { + let n = document.createElement("div"); + return Ve(b`
    e.stopPropagation()}>${e}
    `, n), n.firstElementChild; + } + _renderTruncatableMarkupAttrValue(e) { + let t = ar(e); + return b`${e.slice(0, nr)}`; + } + _toggleTruncatableMarkupAttrValue(e) { + let t = e.closest(".xml-attr-value-truncatable"); + if (!t) return; + let n = t.closest(".markup-line"), r = t.dataset.fullValue ?? "", i = e.querySelector(".xml-attr-value-chip-label"), a = e.dataset.collapsedLabel ?? i?.textContent ?? ""; + if (e.getAttribute("aria-expanded") === "true") { + e.setAttribute("aria-expanded", "false"), e.setAttribute("aria-label", `Show full value (${a})`), i && (i.textContent = a), n?.nextElementSibling?.classList.contains("markup-embedded-uri-panel") && n.nextElementSibling.remove(); + return; + } + if (e.setAttribute("aria-expanded", "true"), e.setAttribute("aria-label", "Hide full value"), i && (i.textContent = "hide"), !n || n.nextElementSibling?.classList.contains("markup-embedded-uri-panel")) return; + let o = Number(n.style.getPropertyValue("--markup-depth") || 0); + n.insertAdjacentElement("afterend", this._createEmbeddedUriContinuationPanel(r, o)); + } + _renderXmlSpan(e, t, { ghost: n = !1 } = {}) { + return n ? b`${t}` : b`${t}`; + } + _renderMarkupLineRow(e, t, { foldToggle: n = !1 } = {}) { + return b`
    ${t}
    `; + } + _renderMarkupAttrValue(e) { + return rr(e) ? this._renderTruncatableMarkupAttrValue(e) : this._renderXmlSpan("xml-attr-value", e); + } + _renderMarkupAttributes(e) { + return e.map(({ name: e, value: t }) => b` ${this._renderXmlSpan("xml-attr-name", e)}${this._renderXmlSpan("xml-bracket", "=\"")}${this._renderMarkupAttrValue(t)}${this._renderXmlSpan("xml-bracket", "\"")}`); + } + _renderMarkupTextContent(e) { + let t = /^$/.exec(e); + return t ? b`${this._renderXmlSpan("xml-cdata-delimiter", "")}` : this._renderXmlSpan("xml-text", e); + } + _renderOpenTagContent(e, t, n = !1) { + return b`${this._renderXmlSpan("xml-bracket", "<", { ghost: n })}${this._renderXmlSpan("xml-tag", e, { ghost: n })}${this._renderMarkupAttributes(t)}${this._renderXmlSpan("xml-bracket", ">", { ghost: n })}`; + } + _renderMarkupFoldableOpen(e, t, n, r) { + let i = !!r, a = b`${this._renderOpenTagContent(t, n, i)}${this._renderXmlSpan("xml-bracket", "...", { ghost: i })}${this._renderXmlSpan("xml-bracket", "", { ghost: i })}`; + return b`${a}`; + } + _renderMarkupCloseTag(e, t, n) { + let r = !!n, i = b`${this._renderXmlSpan("xml-bracket", "", { ghost: r })}`; + return this._renderMarkupLineRow(e, i); + } + _renderMarkupSelfClosingTag(e, t, n) { + let r = b`${this._renderXmlSpan("xml-bracket", "<")}${this._renderXmlSpan("xml-tag", t)}${this._renderMarkupAttributes(n)}${this._renderXmlSpan("xml-bracket", "/>")}`; + return this._renderMarkupLineRow(e, r); + } + _renderMarkupInlineElement(e, t, n, r) { + let i = b`${this._renderOpenTagContent(t, n)}${this._renderMarkupTextContent(r)}${this._renderXmlSpan("xml-bracket", "")}`; + return this._renderMarkupLineRow(e, i); + } + _renderMarkupTextLine(e, t) { + return this._renderMarkupLineRow(e, this._renderMarkupTextContent(t)); + } + _renderMarkupNodesFromSlice(e, t, n) { + let r = []; + for (let i = 0; i < t.length; i += 1) { + let a = t[i]; + if (a) { + if (P(a)) { + let t = Dt(a); + t && r.push(this._renderMarkupTextLine(e, t)); + } else a.nodeType === Node.ELEMENT_NODE && r.push(this._renderMarkupElement(a, e, n)); + } + } + return r; + } + _renderMarkupVirtualText(e, t, n, r) { + return cr(n) ? b`
    ${this._renderMarkupFoldableOpen(e, "text", [], tr)}
    ${this._renderMarkupNodesFromSlice(e + 1, n, r)}
    ${this._renderMarkupCloseTag(e, "text", tr)}
    ` : this._renderMarkupNodesFromSlice(e, n, r); + } + _renderMarkupFoldableElement(e, t, n, r) { + let i = L(e); + return b`
    ${this._renderMarkupFoldableOpen(t, i, vt(e))}
    ${r(t + 1)}
    ${this._renderMarkupCloseTag(t, i)}
    `; + } + _renderMarkupList(e, t, n) { + return this._renderMarkupFoldableElement(e, t, n, (t) => { + let r = [], i = [...e.childNodes], a = 0; + for (; a < i.length;) { + let e = i[a]; + if (!e) { + a += 1; + continue; + } + if (e.nodeType === Node.ELEMENT_NODE && L(e) === "ldiv") break; + if (P(e) && F(e)) { + a += 1; + continue; + } + if (P(e)) break; + if (e.nodeType === Node.ELEMENT_NODE) { + let i = L(e); + if (M.has(i) || i === "location") { + r.push(this._renderMarkupElement(e, t, n)), a += 1; + continue; + } + } + break; + } + for (; a < i.length;) { + let e = i[a]; + if (!e) { + a += 1; + continue; + } + if (e.nodeType !== Node.ELEMENT_NODE || L(e) !== "ldiv") { + r.push(...this._renderMarkupNodesFromSlice(t, [e], n)), a += 1; + continue; + } + let o = e; + r.push(this._renderMarkupElement(o, t, n)), a += 1; + let s = St(i, a); + r.push(this._renderMarkupVirtualText(t, o, i.slice(a, s), n)), a = s; + } + return r; + }); + } + _renderMarkupOtslContainer(e, t, n) { + return this._renderMarkupFoldableElement(e, t, n, (t) => { + let r = [], i = [...e.childNodes], a = 0; + for (; a < i.length;) { + let e = i[a]; + if (!e) { + a += 1; + continue; + } + if (e.nodeType === Node.ELEMENT_NODE && B(L(e))) break; + if (P(e) && F(e)) { + a += 1; + continue; + } + if (P(e)) break; + if (e.nodeType === Node.ELEMENT_NODE) { + let i = L(e); + if (M.has(i) || i === "location" || i === "h_thread") { + r.push(this._renderMarkupElement(e, t, n)), a += 1; + continue; + } + } + break; + } + for (; a < i.length;) { + let e = i[a]; + if (!e) { + a += 1; + continue; + } + if (e.nodeType !== Node.ELEMENT_NODE) { + r.push(...this._renderMarkupNodesFromSlice(t, [e], n)), a += 1; + continue; + } + let o = L(e); + if (o === "nl") { + r.push(this._renderMarkupSelfClosingTag(t, "nl", [])), a += 1; + continue; + } + if (!B(o)) { + r.push(...this._renderMarkupNodesFromSlice(t, [e], n)), a += 1; + continue; + } + let s = e; + if (r.push(this._renderMarkupElement(s, t, n)), a += 1, _t.has(o)) continue; + let c = Ct(i, a); + r.push(this._renderMarkupVirtualText(t, s, i.slice(a, c), n)), a = c; + } + return r; + }); + } + _renderMarkupElement(e, t, n) { + let r = L(e); + if (r === "list") return this._renderMarkupList(e, t, n); + if (N.has(r)) return this._renderMarkupOtslContainer(e, t, n); + let i = n.get(e), a = vt(e); + if (!e.childNodes.length) return b`
    ${this._renderMarkupSelfClosingTag(t, r, a)}
    `; + let o = [...e.childNodes].filter((e) => P(e) && !F(e)); + if (o.length > 0 && o.every(P) && !I(e).length) { + let n = Ot(e.childNodes); + if (n) return b`
    ${this._renderMarkupInlineElement(t, r, a, n)}
    `; + } + return b`
    ${this._renderMarkupFoldableOpen(t, r, a)}
    ${[...e.childNodes].map((e) => { + if (P(e)) { + let n = Dt(e); + return n ? this._renderMarkupTextLine(t + 1, n) : C; + } + return e.nodeType === Node.ELEMENT_NODE ? this._renderMarkupElement(e, t + 1, n) : C; + })}
    ${this._renderMarkupCloseTag(t, r)}
    `; + } +}; +H([O()], lr.prototype, "_hasMarkup", void 0), H([O()], lr.prototype, "_markupTemplate", void 0), lr = H([E("doclang-markup-pane")], lr); +//#endregion +//#region src/components/page-img-pane/page-img-pane.css?inline +var ur = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{border-right:1px solid var(--border);background:var(--panel);--overlay-reading-order:var(--doclang-overlay-reading-order,#71717a);--overlay-fragment:var(--doclang-overlay-fragment,#9333ea);flex-direction:column;min-width:0;min-height:0;display:flex}:host([hidden]){display:none!important}:host(.pane-layout-last){border-right:none}.pane-header{box-sizing:border-box;letter-spacing:.04em;text-transform:uppercase;height:2.125rem;color:var(--muted);border-bottom:1px solid var(--border);background:var(--panel);justify-content:space-between;align-items:center;gap:.5rem;padding:0 .75rem;font-size:.75rem;font-weight:600;display:flex}.pane-header-title{min-width:0}.pane-page-controls{flex-shrink:0;align-items:center;gap:.5rem;display:flex}.page-zoom-control{-webkit-user-select:none;user-select:none;align-items:center;gap:.35rem;display:flex}.page-zoom-control-label{letter-spacing:.04em;text-transform:uppercase;color:var(--muted);cursor:pointer;font-size:.625rem;font-weight:600}.page-zoom-control input[type=range]{cursor:pointer;width:5.5rem;height:1rem;margin:0}.page-zoom-reset{appearance:none;border:1px solid var(--border);background:var(--bg);color:var(--muted);height:1.25rem;font:inherit;font-variant-numeric:tabular-nums;cursor:pointer;border-radius:.375rem;min-width:2.75rem;padding:0 .45rem;font-size:.6875rem;font-weight:600;line-height:1}.page-zoom-reset:not(:disabled):hover{border-color:var(--accent);color:var(--text)}.page-zoom-reset:disabled{opacity:.55;cursor:default}.pane-settings-toggle{appearance:none;color:var(--muted);cursor:pointer;white-space:nowrap;letter-spacing:.04em;text-transform:uppercase;height:1.25rem;font-size:.625rem;font-weight:600;line-height:1;font:inherit;background:0 0;border:1px solid #0000;border-radius:.25rem;flex-shrink:0;align-items:center;padding:0 .28rem 0 .4rem;transition:color .12s,background .12s,border-color .12s;display:inline-flex}.pane-settings-toggle:after{content:\"\";opacity:.65;border-bottom:1.5px solid;border-right:1.5px solid;flex-shrink:0;width:.26rem;height:.26rem;margin-top:-.1em;margin-left:.22rem;transform:rotate(45deg)}.pane-settings-toggle:hover{color:var(--text);background:color-mix(in srgb, var(--text) 5%, transparent)}.pane-settings-toggle[aria-expanded=true]{color:var(--accent);background:color-mix(in srgb, var(--accent) 10%, var(--panel));border-color:color-mix(in srgb, var(--accent) 22%, transparent)}.pane-settings-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.pane-page-layout{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex;position:relative}.pane-body{flex-direction:column;flex:auto;min-width:0;min-height:0;padding:.75rem;display:flex;overflow:hidden}.settings-option{cursor:pointer;-webkit-user-select:none;user-select:none;align-items:flex-start;gap:.5rem;font-size:.875rem;display:flex}.settings-option-primary+.settings-subgroup{margin-top:.65rem}.settings-option-primary{font-weight:600}.settings-subgroup{flex-direction:column;gap:.5rem;padding-left:.85rem;display:flex}.settings-reading-order-group{gap:.35rem;padding-left:1.6rem}.settings-option-nested{padding-left:0}.settings-option-sub{color:var(--muted);font-size:.8125rem}.settings-option-disabled{opacity:.45;cursor:not-allowed}.settings-option-disabled input{cursor:not-allowed}.settings-option input{cursor:pointer;flex-shrink:0;margin:.15rem 0 0}.settings-divider{background:var(--border);height:1px;margin:.75rem 0}.arrow-layer-row{justify-content:space-between;align-items:flex-start;gap:.375rem;display:flex}.arrow-layer-row .settings-option{flex:auto;min-width:0}.arrow-layer-row .settings-option span{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.arrow-style-btn{appearance:none;width:1.25rem;height:.9rem;color:var(--muted);cursor:pointer;background:0 0;border:none;border-radius:.25rem;flex:none;justify-content:center;align-items:center;margin:.1rem 0 0;padding:0;display:flex}.arrow-style-btn:before{content:\"\";border-top:.3rem solid #0000;border-bottom:.3rem solid #0000;border-left:.3rem solid;width:0;height:0;transition:transform .12s}.arrow-style-btn:hover{background:var(--bg);color:var(--text)}.arrow-style-btn[aria-expanded=true]{color:var(--text)}.arrow-style-btn[aria-expanded=true]:before{transform:rotate(90deg)}.arrow-layer-row.is-disabled .arrow-style-btn{opacity:.45;pointer-events:none}.arrow-style-fields{flex-direction:column;gap:.4rem;padding:.35rem 0 .55rem 1.6rem;display:flex}.settings-reading-order-group .arrow-style-fields{padding-left:.4rem}.arrow-style-fields[hidden]{display:none}.arrow-style-fields label{color:var(--muted);cursor:default;justify-content:space-between;align-items:center;gap:.5rem;font-size:.75rem;display:flex}.arrow-field-control{flex:auto;justify-content:flex-end;align-items:center;gap:.35rem;min-width:0;display:flex}.arrow-field-value{text-align:right;min-width:1.5rem;color:var(--text);font-variant-numeric:tabular-nums;flex:none}.arrow-style-fields input[type=range]{cursor:pointer;flex:auto;min-width:0;max-width:3.5rem}.arrow-style-fields input[type=color]{border:1px solid var(--border);cursor:pointer;background:0 0;border-radius:.25rem;width:2.25rem;height:1.4rem;padding:0}.arrow-style-fields select{font:inherit;color:var(--text);background:var(--panel);border:1px solid var(--border);cursor:pointer;border-radius:.25rem;padding:.1rem .25rem;font-size:.75rem}.overlays-reset{font:inherit;color:var(--text);border:1px solid var(--border);cursor:pointer;background:0 0;border-radius:.25rem;margin-top:.6rem;padding:.3rem .55rem;font-size:.75rem}.overlays-reset:hover{background:var(--bg)}.page-view-port{scrollbar-gutter:stable;flex:auto;width:100%;min-width:0;height:100%;min-height:0;display:flex;overflow:auto}.pane-body.can-pan{cursor:grab}.pane-body.is-panning{cursor:grabbing;-webkit-user-select:none;user-select:none}.pane-body:focus{outline:none}.pane-body:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.placeholder{min-height:12rem;color:var(--muted);background:var(--placeholder-bg);border:1px dashed var(--border);text-align:center;border-radius:.5rem;justify-content:center;align-items:center;padding:2rem;font-style:italic;display:flex}.page-view{border:1px solid var(--border);border-radius:.25rem;flex-shrink:0;width:fit-content;margin:auto;line-height:0;display:block;position:relative;overflow:hidden}.page-view img{border:none;border-radius:0;width:auto;max-width:none;height:auto;max-height:none;display:block}.page-view img:not([data-layout-ready]){opacity:0}.page-view svg.overlay{pointer-events:auto;width:100%;height:100%;position:absolute;top:0;left:0;overflow:hidden}.overlay rect.bbox.bbox-hidden,.overlay .overlay-badge.bbox-hidden{display:none}.overlay rect.bbox{stroke-width:2px;vector-effect:non-scaling-stroke;pointer-events:none}.kind-text{--doclang-kind-stroke:var(--kind-text)}.kind-heading{--doclang-kind-stroke:var(--kind-heading)}.kind-list{--doclang-kind-stroke:var(--kind-list)}.kind-ldiv{--doclang-kind-stroke:var(--kind-ldiv)}.kind-table{--doclang-kind-stroke:var(--kind-table)}.kind-index{--doclang-kind-stroke:var(--kind-index)}.kind-formula{--doclang-kind-stroke:var(--kind-formula)}.kind-code{--doclang-kind-stroke:var(--kind-code)}.kind-picture{--doclang-kind-stroke:var(--kind-picture)}.kind-group{--doclang-kind-stroke:var(--kind-group)}.kind-footnote{--doclang-kind-stroke:var(--kind-footnote)}.kind-page_header{--doclang-kind-stroke:var(--kind-page_header)}.kind-page_footer{--doclang-kind-stroke:var(--kind-page_footer)}.kind-caption{--doclang-kind-stroke:var(--kind-caption)}.kind-field{--doclang-kind-stroke:var(--kind-field)}.kind-default{--doclang-kind-stroke:var(--kind-default)}.overlay rect.bbox.selected{stroke-width:3.5px;filter:drop-shadow(0 0 3px color-mix(in srgb, var(--kind-stroke,var(--accent)) 60%, transparent));fill:color-mix(in srgb, var(--kind-stroke,var(--accent)) 30%, transparent)!important}.overlay rect.bbox.related:not(.selected){fill:color-mix(in srgb, var(--kind-stroke,var(--accent)) 30%, transparent)!important}.overlay rect.bbox.layer-furniture,.overlay rect.bbox.layer-background{stroke:color-mix(in srgb, var(--kind-stroke,var(--muted)) 50%, transparent);fill:url(#layer-hatch)!important}.overlay .layer-hatch-stripe{fill:var(--muted);fill-opacity:.06}.overlay rect.bbox.layer-furniture.selected,.overlay rect.bbox.layer-background.selected{fill:color-mix(in srgb, var(--kind-stroke,var(--accent)) 22%, transparent)!important}.overlay rect.bbox.layer-furniture.related:not(.selected),.overlay rect.bbox.layer-background.related:not(.selected){fill:color-mix(in srgb, var(--kind-stroke,var(--accent)) 18%, transparent)!important}.overlay .bbox-text{fill:color-mix(in srgb, var(--kind-text) 14%, transparent);stroke:var(--kind-text)}.overlay .bbox-heading{fill:color-mix(in srgb, var(--kind-heading) 14%, transparent);stroke:var(--kind-heading)}.overlay .bbox-list,.overlay .bbox-ldiv{fill:color-mix(in srgb, var(--kind-list) 14%, transparent);stroke:var(--kind-list)}.overlay .bbox-table{fill:color-mix(in srgb, var(--kind-table) 14%, transparent);stroke:var(--kind-table)}.overlay .bbox-index{fill:color-mix(in srgb, var(--kind-index) 14%, transparent);stroke:var(--kind-index)}.overlay .bbox-formula{fill:color-mix(in srgb, var(--kind-formula) 14%, transparent);stroke:var(--kind-formula)}.overlay .bbox-code{fill:color-mix(in srgb, var(--kind-code) 16%, transparent);stroke:var(--kind-code)}.overlay .bbox-picture{fill:color-mix(in srgb, var(--kind-picture) 14%, transparent);stroke:var(--kind-picture)}.overlay .bbox-group{fill:color-mix(in srgb, var(--kind-group) 14%, transparent);stroke:var(--kind-group)}.overlay .bbox-footnote{fill:color-mix(in srgb, var(--kind-footnote) 14%, transparent);stroke:var(--kind-footnote)}.overlay .bbox-page_header,.overlay .bbox-page_footer{fill:color-mix(in srgb, var(--kind-page_header) 14%, transparent);stroke:var(--kind-page_header)}.overlay .bbox-caption{fill:color-mix(in srgb, var(--kind-caption) 14%, transparent);stroke:var(--kind-caption)}.overlay line.caption-link{color:var(--arrow-caption-color,var(--kind-caption));stroke:currentColor;stroke-width:var(--arrow-caption-width,1.5);stroke-dasharray:var(--arrow-caption-dash,none);stroke-linecap:var(--arrow-caption-cap,butt);vector-effect:non-scaling-stroke;pointer-events:none;fill:none}.overlay line.caption-link.bbox-hidden{display:none}.overlay line.xref-link{color:var(--arrow-xref-color,var(--kind-footnote));stroke:currentColor;stroke-width:var(--arrow-xref-width,1.5);stroke-dasharray:var(--arrow-xref-dash,none);stroke-linecap:var(--arrow-xref-cap,butt);vector-effect:non-scaling-stroke;pointer-events:none;fill:none}.overlay line.xref-link.bbox-hidden{display:none}.overlay .fragment-link{pointer-events:none}.overlay .fragment-link.bbox-hidden{display:none}.overlay .fragment-link-path{color:var(--arrow-fragment-color,var(--overlay-fragment));fill:none;stroke:currentColor;stroke-width:var(--arrow-fragment-width,1.5);stroke-linecap:var(--arrow-fragment-cap,butt);vector-effect:non-scaling-stroke}.overlay .fragment-link-path-dashed{stroke-dasharray:var(--arrow-fragment-dash,6 4)}.overlay .fragment-nav{pointer-events:none}.overlay .fragment-nav.bbox-hidden{display:none}.overlay .fragment-nav-btn{pointer-events:auto;cursor:pointer}.overlay .fragment-nav-btn-disabled{pointer-events:none;cursor:default;opacity:.35}.overlay .fragment-nav-btn-bg{fill:var(--panel);stroke:var(--overlay-fragment);stroke-width:1px;vector-effect:non-scaling-stroke}.overlay .fragment-nav-btn-label{fill:var(--overlay-fragment);font-family:var(--font-ui);text-anchor:middle;dominant-baseline:central;pointer-events:none;font-weight:700}.overlay .fragment-nav-btn:not(.fragment-nav-btn-disabled):hover .fragment-nav-btn-bg{fill:color-mix(in srgb, var(--overlay-fragment) 12%, var(--panel))}.overlay .fragment-link-label{fill:var(--overlay-fragment);font-family:var(--font-ui);text-anchor:middle;dominant-baseline:middle;paint-order:stroke fill;stroke:var(--panel);stroke-width:3px;font-style:italic;font-weight:600}.overlay .reading-order-step{color:var(--arrow-reading-order-color,var(--overlay-reading-order));stroke:currentColor;stroke-width:var(--arrow-reading-order-width,1.5);stroke-dasharray:var(--arrow-reading-order-dash,5 4);stroke-linecap:var(--arrow-reading-order-cap,butt);vector-effect:non-scaling-stroke;pointer-events:none;fill:none}.overlay .reading-order-step.bbox-hidden,.overlay .reading-order-badge.bbox-hidden{display:none}.overlay .overlay-badge{pointer-events:none}.overlay .overlay-badge-bg{fill-opacity:1;stroke:none}.reading-order-badge .overlay-badge-bg{fill:var(--overlay-reading-order)}.overlay .element-badge{opacity:.8;pointer-events:auto;cursor:help}.overlay .element-badge .overlay-badge-bg{fill:var(--accent)}.overlay .element-badge.kind-text .overlay-badge-bg{fill:var(--kind-text)}.overlay .element-badge.kind-heading .overlay-badge-bg{fill:var(--kind-heading)}.overlay .element-badge.kind-list .overlay-badge-bg{fill:var(--kind-list)}.overlay .element-badge.kind-table .overlay-badge-bg{fill:var(--kind-table)}.overlay .element-badge.kind-index .overlay-badge-bg{fill:var(--kind-index)}.overlay .element-badge.kind-formula .overlay-badge-bg{fill:var(--kind-formula)}.overlay .element-badge.kind-code .overlay-badge-bg{fill:var(--kind-code)}.overlay .element-badge.kind-picture .overlay-badge-bg{fill:var(--kind-picture)}.overlay .element-badge.kind-group .overlay-badge-bg{fill:var(--kind-group)}.overlay .element-badge.kind-footnote .overlay-badge-bg{fill:var(--kind-footnote)}.overlay .element-badge.kind-page_header .overlay-badge-bg{fill:var(--kind-page_header)}.overlay .element-badge.kind-caption .overlay-badge-bg{fill:var(--kind-caption)}.overlay .element-badge.kind-field .overlay-badge-bg{fill:var(--kind-field)}.overlay .overlay-badge-label{fill:#fff;fill-opacity:1;font-family:var(--font-ui);text-anchor:middle;dominant-baseline:central;pointer-events:none;font-weight:700}.overlay .bbox-field{fill:color-mix(in srgb, var(--kind-field) 14%, transparent);stroke:var(--kind-field)}.overlay .bbox-default{fill:color-mix(in srgb, var(--kind-default) 12%, transparent);stroke:var(--kind-default)}@media (prefers-color-scheme:dark){:host{--overlay-reading-order:var(--doclang-overlay-reading-order,#a1a1aa);--overlay-fragment:var(--doclang-overlay-fragment,#c084fc)}.overlay .bbox-text{fill:color-mix(in srgb, var(--kind-text) 20%, transparent)}.overlay .bbox-heading{fill:color-mix(in srgb, var(--kind-heading) 20%, transparent)}.overlay .bbox-list,.overlay .bbox-ldiv{fill:color-mix(in srgb, var(--kind-list) 20%, transparent)}.overlay .bbox-table{fill:color-mix(in srgb, var(--kind-table) 18%, transparent)}.overlay .bbox-index{fill:color-mix(in srgb, var(--kind-index) 18%, transparent)}.overlay .bbox-formula{fill:color-mix(in srgb, var(--kind-formula) 18%, transparent)}.overlay .bbox-code{fill:color-mix(in srgb, var(--kind-code) 20%, transparent)}.overlay .bbox-picture{fill:color-mix(in srgb, var(--kind-picture) 20%, transparent)}.overlay .bbox-group{fill:color-mix(in srgb, var(--kind-group) 20%, transparent)}.overlay .bbox-footnote{fill:color-mix(in srgb, var(--kind-footnote) 18%, transparent)}.overlay .bbox-page_header,.overlay .bbox-page_footer{fill:color-mix(in srgb, var(--kind-page_header) 18%, transparent)}.overlay .bbox-caption{fill:color-mix(in srgb, var(--kind-caption) 18%, transparent)}.overlay .bbox-field{fill:color-mix(in srgb, var(--kind-field) 18%, transparent)}.overlay .bbox-default{fill:color-mix(in srgb, var(--kind-default) 18%, transparent)}}.page-view-hint{inset:unset;background:var(--panel);border:1px solid var(--border);width:max-content;max-width:24rem;color:var(--text);font-family:var(--font-ui);text-align:left;white-space:normal;pointer-events:none;border-radius:.25rem;margin:0;padding:.45rem .55rem;font-size:.75rem;line-height:1.35;position:fixed;top:0;left:0;box-shadow:0 2px 8px #0000001f}.page-view-hint .head-tooltip{border-collapse:collapse;width:100%;font-size:.6875rem;line-height:1.4}.page-view-hint .head-tooltip th{color:var(--muted);text-align:left;vertical-align:top;white-space:nowrap;padding:.1rem .55rem .1rem 0;font-weight:600}.page-view-hint .head-tooltip td{vertical-align:top;word-break:break-word;padding:.1rem 0}.page-view-hint .head-tooltip .head-default{color:var(--muted);white-space:nowrap;font-style:italic}", dr = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{display:contents}.settings-layer{z-index:5;position:absolute;inset:0}.settings-scrim{z-index:0;appearance:none;background:color-mix(in srgb, var(--bg) 28%, transparent);cursor:default;border:none;margin:0;padding:0;position:absolute;inset:0}.settings-panel{z-index:1;border:1px solid var(--border);background:var(--panel);border-radius:.5rem;flex-direction:column;width:13rem;min-height:0;max-height:calc(100% - 1rem);display:flex;position:absolute;top:.5rem;right:.5rem;overflow:hidden;box-shadow:0 4px 20px #00000024}.settings-header{border-bottom:1px solid var(--border);justify-content:space-between;align-items:center;gap:.5rem;padding:.5rem .75rem;display:flex}.settings-title{letter-spacing:.04em;text-transform:uppercase;color:var(--muted);margin:0;font-size:.6875rem;font-weight:600}.settings-close{appearance:none;color:var(--muted);cursor:pointer;background:0 0;border:none;border-radius:.25rem;width:1.5rem;height:1.5rem;font-size:1.1rem;line-height:1}.settings-close:hover{color:var(--text);background:var(--bg)}.settings-body{padding:.75rem;overflow:auto}", fr = class extends T { + constructor(...e) { + super(...e), this.label = "", this._open = !1, this._titleId = `settings-title-${Math.random().toString(36).slice(2)}`, this._onClose = () => { + this.setOpen(!1); + }, this._onDocKeydown = (e) => { + this._open && e.key === "Escape" && this.setOpen(!1); + }; + } + static { + this.styles = s(dr); + } + connectedCallback() { + super.connectedCallback(), document.addEventListener("keydown", this._onDocKeydown); + } + disconnectedCallback() { + super.disconnectedCallback(), document.removeEventListener("keydown", this._onDocKeydown); + } + render() { + return this._open ? b` +
    + + +
    + ` : C; + } + setOpen(e) { + this._open !== e && (this._open = e, this.requestUpdate(), e || this.dispatchEvent(new CustomEvent("doclang-settings-close", { + bubbles: !0, + composed: !0 + }))); + } + get isOpen() { + return this._open; + } +}; +H([D({ type: String })], fr.prototype, "label", void 0), fr = H([E("doclang-settings-panel")], fr); +//#endregion +//#region src/components/page-img-pane/overlay.ts +var pr = 16.5 * .8, mr = 3, hr = 2, gr = 3, _r = 1224, vr = 1584, yr = "Previous fragment", br = "Next fragment", xr = "cross-page content", Sr = "fragmented content", Cr = { + readingOrder: { + cssKey: "reading-order", + colorVar: "--overlay-reading-order", + markerId: "reading-order-arrowhead", + defaultStyle: "dashed" + }, + fragment: { + cssKey: "fragment", + colorVar: "--overlay-fragment", + markerId: "fragment-arrowhead", + defaultStyle: "dashed" + }, + xref: { + cssKey: "xref", + colorVar: "--kind-footnote", + markerId: "xref-arrowhead", + defaultStyle: "solid" + }, + caption: { + cssKey: "caption", + colorVar: "--kind-caption", + markerId: "caption-arrowhead", + defaultStyle: "solid" + } +}, wr = { + width: 1.5, + head: 5 +}, Tr = { + solid: "none", + dashed: "6 4", + dotted: "1.5 3.5" +}; +function Er(e) { + return e.startsWith("field_") || e === "key" || e === "value" || e === "hint" ? "field" : e === "tabular" ? "table" : (/* @__PURE__ */ new Set([ + "text", + "heading", + "list", + "ldiv", + "table", + "index", + "formula", + "code", + "picture", + "group", + "footnote", + "page_header", + "page_footer", + "caption" + ])).has(e) ? e : "default"; +} +function Dr(e) { + return `kind-${Er(e)}`; +} +function Or(e) { + return Er(e); +} +function kr(e) { + let t = getComputedStyle(e), n = parseFloat(t.paddingLeft) + parseFloat(t.paddingRight), r = parseFloat(t.paddingTop) + parseFloat(t.paddingBottom); + return { + w: e.clientWidth - n, + h: e.clientHeight - r + }; +} +function Ar(e, t, n) { + let { w: r, h: i } = kr(t), a = e.naturalWidth, o = e.naturalHeight, s = n.layoutCache; + if (s && s.paneW === r && s.paneH === i && s.imgW === a && s.imgH === o) return s.fitScale; + let c = r > 0 && i > 0 && a > 0 && o > 0 ? Math.min((r - 2) / a, (i - 2) / o) : 1; + return n.setLayoutCache({ + paneW: r, + paneH: i, + imgW: a, + imgH: o, + fitScale: c + }), c; +} +function G(e, t, n) { + let r = n ?? t.pane.querySelector(".page-view img"), i = t.zoomPct / 100; + if (!(i > 0)) return e; + let { pane: a } = t; + if (!r?.naturalWidth || !r.naturalHeight) return e / i; + let { w: o, h: s } = kr(a); + if (!(o > 0 && s > 0)) return e / i; + let c = Math.min((o - 2) / _r, (s - 2) / vr), l = Ar(r, a, t); + return !(c > 0) || !(l > 0) ? e / i : e * c / (l * i); +} +function jr(e, t, n) { + if (!e?.naturalWidth || !e.naturalHeight) return !1; + let r = Math.max(100, n.zoomPct), i = Ar(e, t, n) * (r / 100), a = Math.floor(e.naturalWidth * i), o = Math.floor(e.naturalHeight * i), s = `${a}px`, c = `${o}px`, l = e.style.width === s && e.style.height === c; + return l || (e.style.width = s, e.style.height = c, e.style.maxWidth = "none", e.style.maxHeight = "none"), e.dataset.layoutReady = "1", !l; +} +function K(e, t) { + let n = e.x0 / e.resW * t.naturalWidth, r = e.y0 / e.resH * t.naturalHeight, i = (e.x1 - e.x0) / e.resW * t.naturalWidth, a = (e.y1 - e.y0) / e.resH * t.naturalHeight; + return { + x: n, + y: r, + w: i, + h: a, + area: i * a + }; +} +function q(e) { + return { + x: e.x + e.w / 2, + y: e.y + e.h / 2 + }; +} +function Mr(e, t) { + let n = (e) => e === "background" ? 0 : e === "furniture" ? 1 : 2, r = (e) => e.kind === "text" ? 0 : e.kind === "list" || e.kind === "table" || e.kind === "index" || e.kind === "tabular" ? 2 : 1; + return [...e].sort((e, i) => { + let a = n(e.layer ?? "body") - n(i.layer ?? "body"); + if (a !== 0) return a; + let o = r(e) - r(i); + if (o !== 0) return o; + if (t) { + let n = e.elementId === t; + if (n !== (i.elementId === t)) return n ? 1 : -1; + } + return 0; + }); +} +function Nr(e, t) { + let n = Math.hypot(t.x - e.x, t.y - e.y); + if (!n) return { + strokeDasharray: "", + strokeDashoffset: "" + }; + let r = n % 10; + return { + strokeDasharray: "6 4", + strokeDashoffset: r > .01 ? String(r) : "" + }; +} +function Pr(e, t = {}) { + let n = t.size ?? wr.head; + return x` + + + `; +} +function Fr(e, t, n, r) { + let i = G(mr, r), a = G(hr, r), o = document.createElementNS("http://www.w3.org/2000/svg", "text"); + o.setAttribute("class", "overlay-badge-label"), o.setAttribute("font-size", String(n)), o.setAttribute("font-weight", "700"), o.setAttribute("text-anchor", "start"), o.setAttribute("dominant-baseline", "text-before-edge"), o.setAttribute("visibility", "hidden"), o.textContent = String(t), e.appendChild(o); + let s, c; + try { + let e = o.getBBox(); + s = e.width + i * 2, c = e.height + a * 2; + } catch { + s = G(String(t).length * pr * .55 + 6, r), c = G(18.520000000000003, r); + } + return o.remove(), { + width: s, + height: c + }; +} +function Ir(e, t, n, r, i, a, o) { + let s = G(pr, o), { width: c, height: l } = Fr(e, r, s, o), u = G(gr, o); + return x` + + + ${r} + `; +} +function Lr(e, t, n) { + let r = t.captionBox ?? t.fromBox, i = t.hostBox ?? t.toBox, a = q(K(r, e)), o = q(K(i, e)), s = t.captionElementId ?? t.fromElementId, c = t.hostElementId ?? t.toElementId; + return x` + `; +} +function Rr(e, t, n, r, i, a, o, s) { + let c = a === "prev" ? yr : br; + return x` + + ${s ? x`${c}` : ""} + + ${o} + `; +} +function zr(e, t) { + let { img: n, boxes: r, captionLinks: i = [], xrefLinks: a = [], readingOrderSteps: o = [], fragmentLinks: s = [], fragmentNavItems: c = [], showAllBboxes: l, showLayoutBadges: u, showReadingOrder: d, ctx: f } = t, p = (e) => f.arrowMarkerOptions ? f.arrowMarkerOptions(e) : {}, m = []; + if (n.naturalWidth) { + let t = G(pr, f), i = G(2, f), a = new Map(l && d ? o.map((e) => [e.elementId, e]) : []); + for (let o of Mr(r, f.selectedId)) { + let { x: r, y: s } = K(o, n), c = { width: 0 }; + l && u && (c = Fr(e, o.tag, t, f), m.push(Ir(e, r, s, o.tag, `element-badge ${Dr(o.kind)}`, o.elementId, f))); + let d = a.get(o.elementId); + if (d) { + let n = String(d.order), a = Fr(e, n, t, f), p = l && u ? r + c.width / 2 + i + a.width / 2 : r; + m.push(Ir(e, p, s, n, "reading-order-badge", o.elementId, f)); + } + } + } + let ee = s.map((e) => { + let t = K(e.fromBox, n), r, i; + if (e.toBox) { + let a = q(t), o = q(K(e.toBox, n)), { strokeDasharray: s, strokeDashoffset: c } = Nr(a, o); + r = x``, i = { + x: (a.x + o.x) / 2, + y: (a.y + o.y) / 2 + }; + } else { + let a = G(5, f), o = e.targetCorner ?? "br", s = n.naturalWidth, c = n.naturalHeight, l = o === "tl" ? { + x: Math.min(a, s - a), + y: Math.min(a, c - a) + } : { + x: Math.max(s - a, a), + y: Math.max(c - a, a) + }, u = q(t), d = o === "tl", p = d ? l : u, m = d ? u : l, { strokeDasharray: ee, strokeDashoffset: h } = Nr(p, m); + r = x``, i = { + x: (p.x + m.x) / 2, + y: (p.y + m.y) / 2 + }; + } + return x` + + ${r} + ${e.toBox ? Sr : xr} + `; + }), h = c.map((e) => { + let t = G(19.5, f), r = G(2, f), i = G(3, f), a = G(15, f), o = G(3, f), { x: s, y: c, w: l, h: u } = K(e.box, n), d = c + u - i - t, p = s + l - i - t, m = p - r - t; + return x` + + ${Rr(m, d, t, o, a, "prev", "‹", e.hasPrev)} + ${Rr(p, d, t, o, a, "next", "›", e.hasNext)} + `; + }), g = []; + for (let e = 0; e < o.length - 1; e += 1) { + let t = q(K(o[e].box, n)), r = q(K(o[e + 1].box, n)); + g.push(x` + `); + } + Ve(x` + + + + + ${o.length >= 2 ? Pr("reading-order-arrowhead", p("readingOrder")) : ""} + ${s.length ? Pr("fragment-arrowhead", p("fragment")) : ""} + ${i.length ? Pr("caption-arrowhead", p("caption")) : ""} + ${a.length ? Pr("xref-arrowhead", p("xref")) : ""} + + ${i.map((e) => Lr(n, e, { + markerId: "caption-arrowhead", + markerLayer: "caption", + linkClass: "caption-link", + fromIdAttr: "data-caption-id", + toIdAttr: "data-host-id" + }))} + ${a.map((e) => Lr(n, e, { + markerId: "xref-arrowhead", + markerLayer: "xref", + linkClass: "xref-link", + fromIdAttr: "data-xref-from-id", + toIdAttr: "data-xref-to-id" + }))} + ${ee} + ${g} + ${Mr(r, f.selectedId).map((e) => { + let { x: t, y: r, w: i, h: a } = K(e, n), o = Or(e.kind), s = Dr(e.kind), c = Vt(e.layer ?? "body"); + return x``; + })} + ${h} + ${m} + `, e); +} +function Br(e, t, n, r, i, a, o, s) { + zr(t, { + img: e, + boxes: n, + readingOrderSteps: r, + showAllBboxes: i, + showLayoutBadges: a, + showReadingOrder: o, + ctx: s + }); +} +function Vr(e, t, n = [], r = [], i = [], a = [], o = [], s, c, l, u, d, f) { + let p = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + return p.classList.add("overlay"), p.setAttribute("viewBox", `0 0 ${e.naturalWidth} ${e.naturalHeight}`), p.setAttribute("overflow", "hidden"), zr(p, { + img: e, + boxes: t, + captionLinks: n, + xrefLinks: r, + readingOrderSteps: i, + fragmentLinks: a, + fragmentNavItems: o, + showAllBboxes: !1, + showLayoutBadges: !1, + showReadingOrder: !1, + ctx: f + }), p.addEventListener("click", (n) => { + if (u()) { + d(!1); + return; + } + let r = n.target, i = r.closest(".fragment-nav-btn:not(.fragment-nav-btn-disabled)"); + if (i) { + n.stopPropagation(); + let e = i.closest(".fragment-nav")?.getAttribute("data-element-id"), t = i.getAttribute("data-nav"); + e && t && c(e, t); + return; + } + let a = r.closest(".overlay-badge[data-element-id]"); + if (a) { + let e = a.getAttribute("data-element-id"); + e && s(e); + return; + } + let o = p.getScreenCTM()?.inverse(); + if (o) { + let r = p.createSVGPoint(); + r.x = n.clientX, r.y = n.clientY; + let { x: i, y: a } = r.matrixTransform(o), c = Hr(t, e, i, a); + c ? s(c.elementId) : l(); + } + }), p.addEventListener("mousemove", (n) => { + let r = p.getScreenCTM()?.inverse(); + if (r) { + let i = p.createSVGPoint(); + i.x = n.clientX, i.y = n.clientY; + let { x: a, y: o } = i.matrixTransform(r); + p.style.cursor = Hr(t, e, a, o) ? "pointer" : ""; + } + }), p.addEventListener("mouseleave", () => { + p.style.cursor = ""; + }), p; +} +function Hr(e, t, n, r) { + let i = null, a = Infinity; + for (let o of e) { + let { x: e, y: s, w: c, h: l, area: u } = K(o, t); + n >= e && n <= e + c && r >= s && r <= s + l && u < a && (i = o, a = u); + } + return i; +} +//#endregion +//#region src/components/page-img-pane/page-img-pane.ts +var Ur = 5, Wr = "(No page image available.)", Gr = "doclang-viewer-overlay-prefs", Kr = "doclang-viewer-arrow-styles", qr = { + showAllBboxes: !0, + showLayoutBadges: !0, + showReadingOrder: !1, + readingOrderArrows: !0, + readingOrderGlobal: !1, + showPictureContents: !1, + showTableContents: !1, + showFragmentLinks: !1, + showXrefLinks: !1, + showCaptionLinks: !1 +}; +function Jr(e) { + let t = (e || "").trim(); + if (/^#[0-9a-fA-F]{6}$/.test(t)) return t.toLowerCase(); + if (/^#[0-9a-fA-F]{3}$/.test(t)) return `#${t.slice(1).split("").map((e) => e + e).join("").toLowerCase()}`; + let n = t.match(/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i); + return n ? `#${n.slice(1, 4).map((e) => Math.max(0, Math.min(255, Math.round(parseFloat(e)))).toString(16).padStart(2, "0")).join("")}` : "#000000"; +} +var Yr = class extends U { + constructor(...e) { + super(...e), this._bodyRef = A(), this._settingsPanelRef = A(), this._settingsOpen = !1, this._visible = !1, this._zoomPct = 100, this._opts = { ...qr }, this._arrowStyles = {}, this._expandedArrowFields = /* @__PURE__ */ new Set(), this._pageController = new $n(this, () => this.scrollPane), this._panDrag = null, this._suppressClick = !1, this._layoutCache = null, this._layoutFrame = 0, this._resizeObserver = null, this._resetAllOverlaySettings = () => { + this._arrowStyles = {}, this._persistArrowStyles(), this._applyArrowStyleVars(), this._expandedArrowFields.clear(), this._opts = { ...qr }, this._persistOverlayPrefs(), this._applyBboxVisibility(), this._emitOverlayChange(), this._docState && this._renderDocument(), this.requestUpdate(); + }, this._onZoomInput = (e) => { + this._zoomPct = Math.max(100, Number(e.target.value)), this._layoutCache = null, this.requestUpdate(), this.refreshLayout(); + }, this._onPanningChange = (e) => { + e.detail.panning && this._hideHint(); + }, this._onMousemove = (e) => { + if (this._panDrag?.moved) { + this._hideHint(); + return; + } + let t = (e.composedPath()[0] ?? e.target).closest(".element-badge[data-element-id]"); + if (!t || !this._docState?.idToElement) { + this._hideHint(); + return; + } + let n = t.getAttribute("data-element-id"), r = n ? this._docState.idToElement.get(n) : null; + if (!r) { + this._hideHint(); + return; + } + this._showHint(this._elementHeadTooltipHtml(r, this._docState.defaultResolution), e); + }, this._onMouseleave = () => this._hideHint(); + } + static { + this.styles = s(ur); + } + connectedCallback() { + super.connectedCallback(), this.classList.add("pane", "pane-page-view"), this._loadOverlayPrefs(), this._loadArrowStyles(), this._applyArrowStyleVars(), this.addEventListener("doclang-panning-change", this._onPanningChange), this.addEventListener("mousemove", this._onMousemove), this.addEventListener("mouseleave", this._onMouseleave); + } + disconnectedCallback() { + super.disconnectedCallback(), this.removeEventListener("doclang-panning-change", this._onPanningChange), this.removeEventListener("mousemove", this._onMousemove), this.removeEventListener("mouseleave", this._onMouseleave), this._resizeObserver?.disconnect(), this._resizeObserver = null; + } + render() { + let e = this._opts.showAllBboxes, t = e && this._opts.showReadingOrder; + return b` +
    + Original page +
    + ${this._visible ? b` +
    + + +
    + + ` : C} +
    +
    +
    +
    + ${this._visible ? b` + this._applySettingsOpen(!1)} + > + +
    + +
    +
    + + +
    + ${this._renderArrowStyleFields("readingOrder", t)} + +
    + + + +
    + + +
    + ${this._renderArrowStyleFields("fragment", e)} + +
    + + +
    + ${this._renderArrowStyleFields("xref", e)} + +
    + + +
    + ${this._renderArrowStyleFields("caption", e)} + + +
    + + + +
    + ` : C} +
    + + `; + } + firstUpdated() { + let e = this._bodyRef.value; + e && this._wirePanEvents(e); + } + get zoomPercent() { + return this._zoomPct; + } + get overlaySettings() { + return this._opts; + } + get scrollPane() { + return this._scrollPane(); + } + setVisible(e) { + this._visible = e, this.hidden = !e, this.requestUpdate(); + } + refreshLayout() { + let e = this._bodyRef.value; + e && (this._resizeObserver || (this._resizeObserver = new ResizeObserver(() => this.refreshLayout()), this._resizeObserver.observe(e)), cancelAnimationFrame(this._layoutFrame), this._layoutFrame = requestAnimationFrame(() => { + this._layoutFrame = 0; + let t = e.querySelector(".page-view img"); + t?.naturalWidth && (jr(t, e, this._overlayCtx()), this._updatePanCursor(), this._syncOverlayBadgesForImg(t), this.dispatchEvent(new CustomEvent("doclang-layout-refresh", { + bubbles: !0, + composed: !0 + }))); + })); + } + activateZoom(e) { + this._zoomPct = e, this._layoutCache = null, this.requestUpdate(), this._resetScroll(); + } + resetZoom() { + this._zoomPct = 100, this._layoutCache = null, this.requestUpdate(), this._resetScroll(), this.refreshLayout(); + } + toggleSettings() { + this._applySettingsOpen(!this._settingsOpen); + } + _applySettingsOpen(e) { + this._settingsOpen = e, this._settingsPanelRef.value?.setOpen(e), this.requestUpdate(); + } + _applySelection() { + let e = this._bodyRef.value; + if (!e) return; + for (let t of e.querySelectorAll(".bbox.selected, .overlay-badge.selected")) t.classList.remove("selected"); + if (this.selected && this._docState?.hasPageView) for (let t of e.querySelectorAll(`[data-element-id="${this.selected}"]`)) t.classList.add("selected"); + let t = e.querySelector(".page-view img"); + t && this._syncOverlayBadgesForImg(t), this._applyBboxVisibility(); + } + _renderDocument() { + let e = this._bodyRef.value; + if (!e) { + this.requestUpdate(), this.updateComplete.then(() => this._renderDocument()); + return; + } + let t = this._docState; + if (e.innerHTML = "", this._layoutCache = null, this.selected = null, this._peerIds = /* @__PURE__ */ new Set(), !t?.hasPageView) return; + let n = t.pageImages.get(this.page); + if (!n) { + e.innerHTML = `
    ${Wr}
    `; + return; + } + let r = document.createElement("div"); + r.className = "page-view-port"; + let i = document.createElement("div"); + i.className = "page-view"; + let a = document.createElement("img"); + a.alt = `Page ${this.page}`; + let o = this.page, s = () => { + if (a.dataset.layoutGeneration === String(o)) return; + a.dataset.layoutGeneration = String(o), jr(a, e, this._overlayCtx()); + let n = t.segments[o - 1] ?? [], r = _n(n); + t.elementIds = r, t.idToElement = vn(r); + let s = An(n, t.defaultResolution, r), c = i.querySelector("svg.overlay"); + c && c.remove(); + let l = In(n, r, s, t.readingOrder, this._opts.readingOrderGlobal, t.readingOrderDisplayNumbers); + s.length && i.appendChild(Vr(a, s, jn(n, r, s), Mn(n, r, s), l, Pn(n, r, s, o, t.threadPagesById), Fn(n, r, s, t.threadNavByElement), (e) => this.dispatchEvent(new CustomEvent("doclang-element-select", { + bubbles: !0, + composed: !0, + detail: { id: e } + })), (e, t) => this.dispatchEvent(new CustomEvent("doclang-navigate-thread", { + bubbles: !0, + composed: !0, + detail: { + elementId: e, + direction: t + } + })), () => this.dispatchEvent(new CustomEvent("doclang-clear-selection", { + bubbles: !0, + composed: !0 + })), () => this._suppressClick, (e) => { + this._suppressClick = e; + }, this._overlayCtx())), this._syncOverlayBadgesForImg(a), this._applyBboxVisibility(); + let u = t.pendingSelectElement; + if (u) { + t.pendingSelectElement = null; + let e = this._findElementIdOnPage(u, r); + e && this.dispatchEvent(new CustomEvent("doclang-element-select", { + bubbles: !0, + composed: !0, + detail: { id: e } + })); + } + }; + a.addEventListener("load", s, { once: !0 }), i.appendChild(a), r.appendChild(i), e.appendChild(r), a.src = n, a.complete && s(), this.refreshLayout(); + } + _clearDocument() { + let e = this._bodyRef.value; + e && (e.innerHTML = ""), this._layoutCache = null; + } + _overlayCtx() { + let e = this._bodyRef.value; + return { + zoomPct: this._zoomPct, + pane: e, + layoutCache: this._layoutCache, + setLayoutCache: (e) => { + this._layoutCache = e; + }, + selectedId: this.selected, + arrowMarkerOptions: (e) => this._arrowMarkerOptions(e) + }; + } + _syncOverlayBadgesForImg(e) { + let t = e.parentElement?.querySelector("svg.overlay"), n = this._docState; + if (!t || !n) return; + let r = this.page, i = n.segments[r - 1] ?? [], a = An(i, n.defaultResolution, n.elementIds), o = In(i, n.elementIds, a, n.readingOrder, this._opts.readingOrderGlobal, n.readingOrderDisplayNumbers), { showAllBboxes: s, showLayoutBadges: c, showReadingOrder: l } = this._opts; + Br(e, t, a, o, s, c, l, this._overlayCtx()); + } + _isContentsOptionHidden(e, t) { + if (t) return !1; + let n = this._docState?.idToElement?.get(e) ?? null; + return !!(!this._opts.showPictureContents && qt(n) || !this._opts.showTableContents && Jt(n)); + } + _isFragmentLinkRelevant(e) { + let t = e.getAttribute("data-fragment-from-id"), n = e.getAttribute("data-fragment-to-id"); + return !!(t && this._peerIds.has(t) || n && this._peerIds.has(n)); + } + _applyBboxVisibility() { + let e = this._bodyRef.value; + if (!e || !this._docState?.hasPageView) return; + let { showAllBboxes: t, showLayoutBadges: n, showCaptionLinks: r, showXrefLinks: i, showFragmentLinks: a, showReadingOrder: o, readingOrderArrows: s } = this._opts, c = this.selected, l = this._peerIds; + for (let n of e.querySelectorAll(".bbox")) { + n.classList.remove("related"); + let e = n.getAttribute("data-element-id") ?? "", r = e === c || l.has(e); + if (t) { + this._isContentsOptionHidden(e, r) ? n.classList.add("bbox-hidden") : (n.classList.remove("bbox-hidden"), l.has(e) && n.classList.add("related")); + continue; + } + e === c ? n.classList.remove("bbox-hidden") : l.has(e) ? (n.classList.remove("bbox-hidden"), n.classList.add("related")) : n.classList.add("bbox-hidden"); + } + for (let r of e.querySelectorAll(".element-badge")) { + let e = r.getAttribute("data-element-id") ?? "", i = e === c || l.has(e); + if (!t || !n) { + r.classList.add("bbox-hidden"); + continue; + } + this._isContentsOptionHidden(e, i) ? r.classList.add("bbox-hidden") : r.classList.remove("bbox-hidden"); + } + for (let n of e.querySelectorAll(".caption-link")) n.classList.toggle("bbox-hidden", !t || !r); + for (let n of e.querySelectorAll(".xref-link")) n.classList.toggle("bbox-hidden", !t || !i); + for (let n of e.querySelectorAll(".fragment-link")) { + let e = !!(c && this._isFragmentLinkRelevant(n)); + n.classList.toggle("bbox-hidden", !(e || t && a)); + } + for (let n of e.querySelectorAll(".fragment-nav")) { + let e = n.getAttribute("data-element-id") ?? "", r = e === c || l.has(e); + n.classList.toggle("bbox-hidden", !(r || t && a)); + } + for (let n of e.querySelectorAll(".reading-order-badge")) { + let e = n.getAttribute("data-element-id") ?? "", r = e === c || l.has(e); + if (!t || !o) { + n.classList.add("bbox-hidden"); + continue; + } + let i = this._docState?.idToElement?.get(e) ?? null; + if (qt(i) || Jt(i) || this._isContentsOptionHidden(e, r)) { + n.classList.add("bbox-hidden"); + continue; + } + n.classList.remove("bbox-hidden"); + } + for (let n of e.querySelectorAll(".reading-order-step")) n.classList.toggle("bbox-hidden", !t || !o || !s); + } + _findElementIdOnPage(e, t) { + return t.get(e) ?? null; + } + _scrollPane() { + let e = this._bodyRef.value; + return e ? e.querySelector(".page-view-port") ?? e : null; + } + _isScrollable() { + let e = this._scrollPane(); + return e ? e.scrollWidth > e.clientWidth || e.scrollHeight > e.clientHeight : !1; + } + _resetScroll() { + let e = this._scrollPane(); + e && (e.scrollLeft = 0, e.scrollTop = 0); + } + _updatePanCursor() { + let e = this._bodyRef.value; + e && e.classList.toggle("can-pan", this._isScrollable() && !this._panDrag); + } + _onOptChange(e, t) { + this._opts[e] = t, this._persistOverlayPrefs(), e === "readingOrderGlobal" && this._docState && this._renderDocument(); + let n = this._bodyRef.value?.querySelector(".page-view img"); + n && this._syncOverlayBadgesForImg(n), this._applyBboxVisibility(), this._emitOverlayChange(), this.requestUpdate(); + } + _loadOverlayPrefs() { + try { + let e = localStorage.getItem(Gr); + if (!e) return; + let t = JSON.parse(e); + if (!t || typeof t != "object") return; + for (let e of Object.keys(qr)) typeof t[e] == "boolean" && (this._opts[e] = t[e]); + } catch {} + } + _persistOverlayPrefs() { + try { + localStorage.setItem(Gr, JSON.stringify(this._opts)); + } catch {} + } + _loadArrowStyles() { + try { + let e = localStorage.getItem(Kr); + if (!e) return; + let t = JSON.parse(e); + t && typeof t == "object" && !Array.isArray(t) && (this._arrowStyles = t); + } catch { + this._arrowStyles = {}; + } + } + _persistArrowStyles() { + try { + localStorage.setItem(Kr, JSON.stringify(this._arrowStyles)); + } catch {} + } + _getArrowStyle(e) { + let t = Cr[e], n = this._arrowStyles[e] ?? {}; + return { + color: n.color, + width: n.width ?? wr.width, + head: n.head ?? wr.head, + style: n.style ?? t.defaultStyle + }; + } + _resolveArrowColor(e) { + let t = this._arrowStyles[e]?.color; + if (t) return t; + let n = Cr[e]; + return getComputedStyle(this).getPropertyValue(n.colorVar).trim() || "currentColor"; + } + _arrowMarkerOptions(e) { + return { + size: this._getArrowStyle(e).head, + color: this._resolveArrowColor(e) + }; + } + _applyArrowStyleVars() { + let e = this.style; + for (let [t, n] of Object.entries(Cr)) { + let r = this._arrowStyles[t] ?? {}, i = `--arrow-${n.cssKey}`; + r.color ? e.setProperty(`${i}-color`, r.color) : e.removeProperty(`${i}-color`), r.width == null ? e.removeProperty(`${i}-width`) : e.setProperty(`${i}-width`, String(r.width)), r.style == null ? (e.removeProperty(`${i}-dash`), e.removeProperty(`${i}-cap`)) : (e.setProperty(`${i}-dash`, Tr[r.style] ?? "none"), e.setProperty(`${i}-cap`, r.style === "dotted" ? "round" : "butt")); + } + } + _toggleArrowStyleFields(e) { + this._expandedArrowFields.has(e) ? this._expandedArrowFields.delete(e) : this._expandedArrowFields.add(e), this.requestUpdate(); + } + _onArrowStyleFieldChange(e, t, n) { + let r = this._arrowStyles[e] ?? (this._arrowStyles[e] = {}); + t === "color" ? r.color = String(n) : t === "style" ? r.style = n : (t === "width" || t === "head") && (r[t] = Number(n)), this._persistArrowStyles(), this._applyArrowStyleVars(); + let i = this._bodyRef.value?.querySelector(".page-view img"); + i && this._syncOverlayBadgesForImg(i), this.requestUpdate(); + } + _renderArrowStyleFields(e, t) { + if (!this._expandedArrowFields.has(e) || !t) return C; + let n = this._getArrowStyle(e); + return b` +
    + + + + +
    + `; + } + _emitOverlayChange() { + this.dispatchEvent(new CustomEvent("doclang-overlay-change", { + bubbles: !0, + composed: !0, + detail: { ...this._opts } + })); + } + _wirePanEvents(e) { + e.addEventListener("pointerdown", (e) => { + if (e.button !== 0 || !(e.target instanceof Element) || !e.target.closest(".page-view") || !this._isScrollable()) return; + let t = this._scrollPane(); + t && (this._panDrag = { + pointerId: e.pointerId, + startX: e.clientX, + startY: e.clientY, + scrollLeft: t.scrollLeft, + scrollTop: t.scrollTop, + moved: !1 + }); + }), e.addEventListener("pointermove", (t) => { + if (!this._panDrag || t.pointerId !== this._panDrag.pointerId) return; + let n = t.clientX - this._panDrag.startX, r = t.clientY - this._panDrag.startY; + if (!this._panDrag.moved && Math.hypot(n, r) >= Ur && (this._panDrag.moved = !0, e.classList.add("is-panning"), e.classList.remove("can-pan"), e.setPointerCapture(t.pointerId), this.dispatchEvent(new CustomEvent("doclang-panning-change", { + bubbles: !0, + composed: !0, + detail: { panning: !0 } + }))), !this._panDrag.moved) return; + let i = this._scrollPane(); + i && (i.scrollLeft = this._panDrag.scrollLeft + this._panDrag.startX - t.clientX, i.scrollTop = this._panDrag.scrollTop + this._panDrag.startY - t.clientY, t.preventDefault()); + }); + let t = (t) => { + if (!this._panDrag || t.pointerId !== this._panDrag.pointerId) return; + let n = this._panDrag.moved; + n && (this._suppressClick = !0), this._panDrag = null, e.classList.remove("is-panning"), n && this.dispatchEvent(new CustomEvent("doclang-panning-change", { + bubbles: !0, + composed: !0, + detail: { panning: !1 } + })), e.hasPointerCapture(t.pointerId) && e.releasePointerCapture(t.pointerId), this._updatePanCursor(); + }; + e.addEventListener("pointerup", (e) => t(e)), e.addEventListener("pointercancel", (e) => t(e)), e.setAttribute("role", "region"), e.setAttribute("aria-label", "Original page"), e.addEventListener("pointerdown", () => { + this._docState?.hasPageView && e.focus({ preventScroll: !0 }); + }); + } + _showHint(e, t) { + let n = this.shadowRoot?.querySelector(".page-view-hint") ?? null; + n && (n.innerHTML = e, n.style.top = `${t.clientY + 14}px`, n.style.left = `${t.clientX + 14}px`, n.matches(":popover-open") || n.showPopover()); + } + _hideHint() { + let e = this.shadowRoot?.querySelector(".page-view-hint") ?? null; + e?.matches(":popover-open") && e.hidePopover(); + } + _elementHeadTooltipHtml(e, t) { + return `${this._collectElementHeadInfo(e, t).map(({ key: e, value: t, isDefault: n }) => { + let r = kt(t), i = n ? " (default)" : ""; + return ``; + }).join("")}
    ${kt(e)}${r}${i}
    `; + } + _collectElementHeadInfo(e, t) { + let n = V(e, "label"), r = V(e, "thread"), i = V(e, "xref"), a = V(e, "href"), o = V(e, "layer"), s = V(e, "caption"), c = V(e, "description"), l = V(e, "summary"), u = V(e, "custom"), d = Kt(e), f = [{ + key: "element", + value: Ht(e), + isDefault: !1 + }]; + f.push({ + key: "label", + value: n?.getAttribute("value") ?? "undefined", + isDefault: !n?.hasAttribute("value") + }), r ? f.push({ + key: "thread_id", + value: r.getAttribute("thread_id") ?? "—", + isDefault: !1 + }) : f.push({ + key: "thread", + value: "—", + isDefault: !0 + }), i ? f.push({ + key: "xref", + value: `thread_id ${i.getAttribute("thread_id") ?? "—"}`, + isDefault: !1 + }) : f.push({ + key: "xref", + value: "—", + isDefault: !0 + }), a ? f.push({ + key: "href", + value: a.getAttribute("uri") ?? "—", + isDefault: !1 + }) : f.push({ + key: "href", + value: "—", + isDefault: !0 + }), f.push({ + key: "layer", + value: o?.getAttribute("value") ?? "body", + isDefault: !o?.hasAttribute("value") + }); + let p = [ + "x_min", + "y_min", + "x_max", + "y_max" + ]; + if (d.length === 4) for (let e = 0; e < 4; e += 1) { + let n = d[e], r = wt(n, e % 2 == 0 ? t.width : t.height), i = n.getAttribute("value") ?? "0"; + f.push({ + key: p[e], + value: `${i} @ ${r}`, + isDefault: !1 + }); + } + else for (let e of p) f.push({ + key: e, + value: "—", + isDefault: !1 + }); + let m = (e, t = 72) => { + let n = e.textContent?.replace(/\s+/g, " ").trim() ?? ""; + return n ? n.length > t ? `${n.slice(0, t)}…` : n : "—"; + }; + return f.push({ + key: "caption", + value: s ? m(s) : "—", + isDefault: !s + }), f.push({ + key: "description", + value: c ? m(c) : "—", + isDefault: !c + }), f.push({ + key: "summary", + value: l ? m(l) : "—", + isDefault: !l + }), f.push({ + key: "custom", + value: u ? m(u) : "—", + isDefault: !u + }), f; + } +}; +Yr = H([E("doclang-page-img-pane")], Yr); +//#endregion +//#region src/components/reading-pane/reading-pane.css?inline +var Xr = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{border-right:1px solid var(--border);background:var(--panel);flex-direction:column;min-width:0;min-height:0;display:flex}:host([hidden]){display:none!important}:host(.pane-layout-last){border-right:none}.pane-header{box-sizing:border-box;letter-spacing:.04em;text-transform:uppercase;height:2.125rem;color:var(--muted);border-bottom:1px solid var(--border);background:var(--panel);justify-content:space-between;align-items:center;padding:0 .75rem;font-size:.75rem;font-weight:600;display:flex}.pane-header-title{min-width:0}.pane-settings-toggle{appearance:none;color:var(--muted);cursor:pointer;white-space:nowrap;letter-spacing:.04em;text-transform:uppercase;height:1.25rem;font-size:.625rem;font-weight:600;line-height:1;font:inherit;background:0 0;border:1px solid #0000;border-radius:.25rem;flex-shrink:0;align-items:center;padding:0 .28rem 0 .4rem;transition:color .12s,background .12s,border-color .12s;display:inline-flex}.pane-settings-toggle:after{content:\"\";opacity:.65;border-bottom:1.5px solid;border-right:1.5px solid;flex-shrink:0;width:.26rem;height:.26rem;margin-top:-.1em;margin-left:.22rem;transform:rotate(45deg)}.pane-settings-toggle:hover{color:var(--text);background:color-mix(in srgb, var(--text) 5%, transparent)}.pane-settings-toggle[aria-expanded=true]{color:var(--accent);background:color-mix(in srgb, var(--accent) 10%, var(--panel));border-color:color-mix(in srgb, var(--accent) 22%, transparent)}.pane-settings-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.pane-reading-layout{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex;position:relative}.pane-body{flex:auto;min-width:0;min-height:0;padding:1rem;overflow:auto}.settings-option{cursor:pointer;-webkit-user-select:none;user-select:none;align-items:flex-start;gap:.5rem;font-size:.875rem;display:flex}.settings-subgroup{flex-direction:column;gap:.5rem;padding-left:.85rem;display:flex}.settings-option-sub{color:var(--muted);font-size:.8125rem}.settings-option input{cursor:pointer;flex-shrink:0;margin:.15rem 0 0}.rendered-doc{max-width:42rem;margin:0 auto;line-height:1.6}.rendered-el-virtual-text>p,.rendered-el-virtual-text>div{margin:0;display:inline}.rendered-doc li>.rendered-el-virtual-text{display:inline}.rendered-doc li>.rendered-el-virtual-text>p,.rendered-doc li>.rendered-el-virtual-text>div,.rendered-table .rendered-el-virtual-text,.rendered-table .rendered-el-virtual-text>p,.rendered-table .rendered-el-virtual-text>div{margin:0;display:inline}.rendered-el-virtual-text>div>.rendered-el{margin:.25rem 0;display:block}.rendered-el{cursor:pointer;border-radius:.25rem}.rendered-el:hover{outline:1px solid color-mix(in srgb, var(--accent) 35%, transparent)}.rendered-el.selected{outline:2px solid var(--accent);outline-offset:2px;background:color-mix(in srgb, var(--accent) 8%, transparent)}.rendered-doc p{margin:0 0 .75rem}.rendered-doc h1,.rendered-doc h2,.rendered-doc h3,.rendered-doc h4,.rendered-doc h5,.rendered-doc h6{margin:1.25rem 0 .5rem;line-height:1.25}.rendered-doc h1:first-child,.rendered-doc h2:first-child,.rendered-doc h3:first-child{margin-top:0}.rendered-doc ul,.rendered-doc ol{margin:0 0 .75rem;padding-left:1.5rem}.rendered-doc li{margin:.25rem 0}.rendered-doc li>.rendered-el>p:first-child,.rendered-doc li>.rendered-el>h1:first-child,.rendered-doc li>.rendered-el>h2:first-child,.rendered-doc li>.rendered-el>h3:first-child,.rendered-doc li>.rendered-el>h4:first-child,.rendered-doc li>.rendered-el>h5:first-child,.rendered-doc li>.rendered-el>h6:first-child{margin-top:0}.rendered-marker{margin-right:.35rem}.rendered-handwriting{font-family:Segoe Print,Bradley Hand,cursive}.rendered-doc pre{font-family:var(--font-mono);background:var(--placeholder-bg);border:1px solid var(--border);border-radius:.375rem;margin:0 0 .75rem;padding:.75rem 1rem;font-size:.8125rem;line-height:1.5;overflow-x:auto}.rendered-doc code{font-family:var(--font-mono);font-size:.875em}.rendered-doc pre code{font-size:inherit;background:0 0;border:none;padding:0}.rendered-code-label{color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:.25rem;font-size:.75rem;font-weight:600;display:block}.rendered-formula{font-family:var(--font-mono);background:var(--placeholder-bg);border-left:3px solid var(--border);margin:0 0 .75rem;padding:.5rem .75rem;font-size:.875rem;display:block;overflow-x:auto}.rendered-formula-inline{font-family:var(--font-mono);font-size:.875em;display:inline}.rendered-doc figure{margin:0 0 .75rem}.rendered-doc figure img{border:1px solid var(--border);border-radius:.25rem;max-width:100%;height:auto}.rendered-doc figure img.rendered-picture-unavailable{object-fit:contain;vertical-align:middle;min-width:3rem;min-height:2.5rem;display:inline-block}.rendered-doc figcaption{color:var(--muted);margin-top:.35rem;font-size:.875rem}.rendered-picture-contents{border:1px solid var(--border);background:var(--placeholder-bg);border-radius:.375rem;margin-top:.5rem;font-size:.875rem}.rendered-picture-contents summary{cursor:pointer;color:var(--muted);-webkit-user-select:none;user-select:none;padding:.4rem .65rem;font-weight:600}.rendered-picture-contents summary:hover{color:var(--text)}.rendered-picture-contents-body{border-top:1px solid var(--border);padding:.5rem .75rem .75rem}.rendered-picture-contents-body .rendered-el>p{margin-bottom:.35rem;font-size:.8125rem}.rendered-table{border-collapse:collapse;width:100%;margin:0 0 .75rem;font-size:.875rem}.rendered-table caption{caption-side:top;color:var(--muted);text-align:left;margin-bottom:.35rem;font-size:.875rem}.rendered-table th,.rendered-table td{border:1px solid var(--border);vertical-align:top;text-align:left;padding:.35rem .5rem}.rendered-table th{background:color-mix(in srgb, var(--border) 35%, var(--panel));font-weight:600}.rendered-table .rendered-table-cell-text{display:inline}.rendered-table .rendered-el,.rendered-table .rendered-el-virtual-text>p,.rendered-table .rendered-el-virtual-text>div{margin:0}.rendered-table .rendered-table{margin:.25rem 0 0}.rendered-page-header,.rendered-page-footer{color:var(--muted);font-size:.875rem}.rendered-page-header{border-bottom:1px solid var(--border);margin-bottom:1rem;padding-bottom:.5rem}.rendered-page-footer{border-top:1px solid var(--border);margin-top:1rem;padding-top:.5rem}.pane-body:not(.show-reading-furniture) .rendered-el[data-doclang-layer=furniture],.pane-body:not(.show-reading-background) .rendered-el[data-doclang-layer=background]{display:none}.rendered-el[data-doclang-layer=furniture],.rendered-el[data-doclang-layer=background]{position:relative}.rendered-el[data-doclang-layer=furniture]:before,.rendered-el[data-doclang-layer=background]:before{content:\"\";border-radius:inherit;pointer-events:none;background:repeating-linear-gradient(-45deg, transparent 0 10px, color-mix(in srgb, var(--muted) 6%, transparent) 10px 16px);position:absolute;inset:0}.rendered-el.rendered-footnote>aside{color:var(--muted);border-left:2px solid var(--border);margin:0 0 .75rem;padding-left:.75rem;font-size:.875rem}.rendered-unsupported{color:var(--muted);background:var(--placeholder-bg);border:1px dashed var(--border);border-radius:.375rem;margin:0 0 .75rem;padding:.5rem .75rem;font-size:.8125rem;font-style:italic}.rendered-field-region{border:1px solid var(--border);background:color-mix(in srgb, var(--kind-field) 6%, transparent);border-radius:.5rem;margin:0 0 1rem;padding:.75rem 1rem}.rendered-field-region>.rendered-field-heading:first-child,.rendered-field-region>.rendered-el.rendered-field_heading>.rendered-field-heading:first-child{margin-top:0}.rendered-field-item{margin:.5rem 0}.rendered-field-item>.rendered-el.rendered-field-key,.rendered-field-key{font-weight:600}.rendered-field-item>.rendered-el.rendered-field-key+.rendered-el,.rendered-field-item>.rendered-el.rendered-field-key+.rendered-el-virtual-text{margin-top:.15rem}.rendered-field-value-fillable .rendered-field-fillable-slot{border-bottom:1px solid color-mix(in srgb, var(--text) 55%, transparent);vertical-align:bottom;min-width:6rem;min-height:1.25em;display:inline-block}.rendered-field-hint{color:var(--muted);font-size:.85em;font-style:italic}.rendered-field-heading{color:var(--text)}.rendered-checkbox{vertical-align:middle;accent-color:var(--kind-field);margin:0 .25rem 0 0}.rendered-checkbox-wrap{display:inline}", Zr = /* @__PURE__ */ new Set([ + "text", + "heading", + "field_heading", + "footnote", + "page_header", + "page_footer", + "list", + "code", + "formula", + "picture", + "group", + "field_region", + "field_item", + "table", + "index", + "tabular" +]), Qr = /* @__PURE__ */ new Set([ + "bold", + "italic", + "underline", + "strikethrough", + "superscript", + "subscript", + "handwriting", + "rtl", + "content" +]), $r = { + bold: "strong", + italic: "em", + underline: "u", + strikethrough: "s", + superscript: "sup", + subscript: "sub" +}, ei = "Picture asset not available", ti = "data:image/png;base64,NOT_A_VALID_IMAGE", ni = /^data:image\/(png|jpe?g|webp|gif);base64,/i, ri = 2097152; +function ii(e, t) { + t.setAttribute("data-doclang-layer", Rt(e)); +} +function ai(e, t) { + if (!e) return null; + let n = e.trim(); + return n ? ni.test(n) ? n.length <= ri ? n : null : /^[a-z][a-z0-9+.-]*:/i.test(n) || n.startsWith("//") ? null : t?.get(At(n)) ?? null : null; +} +function oi(e) { + e.classList.add("rendered-picture-unavailable"), e.alt = "\xA0", e.setAttribute("aria-label", ei); +} +function si(e, t, n, r, i) { + let a = document.createElement("img"); + e.appendChild(a); + let o = t ? ai(t, i) : null; + o ? (a.alt = "", a.src = o, a.addEventListener("error", () => oi(a), { once: !0 })) : (oi(a), a.src = ti), n && e.appendChild(li(n, r, "figcaption", i)); +} +function ci(e) { + return I(e).find((e) => L(e) === "caption") ?? null; +} +function li(e, t, n, r) { + let i = document.createElement(n); + i.classList.add("rendered-el", "rendered-caption"); + let a = t.get(e); + return a && i.setAttribute("data-element-id", a), Y(i, e, t, { inline: !0 }, r), i; +} +function J(e, t, n, r) { + let i = L(e), a = document.createElement("div"); + return a.className = `rendered-el rendered-${i}${r ? ` ${r}` : ""}`, n && a.setAttribute("data-element-id", n), ii(e, a), a.appendChild(t), a; +} +function Y(e, t, n, r, i) { + let a = [...t.childNodes], o = Pt(a, 0); + for (; o < a.length;) Ii(e, a[o], n, r, i), o += 1; +} +function ui(e, t, n, r) { + let i = [...t.childNodes], a = Pt(i, 0); + for (; a < i.length;) { + let t = i[a]; + if (t.nodeType === Node.ELEMENT_NODE && Zr.has(L(t))) { + let i = X(t, n, { inline: !1 }, r); + i && e.appendChild(i); + } else Ii(e, t, n, { inline: !1 }, r); + a += 1; + } +} +function di(e, t, n, r) { + let i = document.createElement("span"); + i.className = "rendered-marker rendered-el"; + let a = t.get(e); + return a && i.setAttribute("data-element-id", a), Y(i, e, t, n, r), i; +} +function fi(e, t) { + let n = document.createElement("input"); + n.type = "checkbox", n.disabled = !0, n.checked = (e.getAttribute("class") ?? "unselected") === "selected", n.className = "rendered-checkbox"; + let r = document.createElement("span"); + r.className = "rendered-checkbox-wrap rendered-el"; + let i = t.get(e); + return i && r.setAttribute("data-element-id", i), ii(e, r), r.appendChild(n), r; +} +function pi(e, t, n, r) { + let i = document.createElement("span"); + i.className = "rendered-field-key rendered-el"; + let a = t.get(e); + return a && i.setAttribute("data-element-id", a), ii(e, i), Y(i, e, t, { + ...n, + inline: !0 + }, r), i; +} +function mi(e, t, n, r) { + let i = e.getAttribute("class") ?? "read_only", a = document.createElement("span"); + a.className = `rendered-field-value rendered-field-value-${i} rendered-el`; + let o = t.get(e); + if (o && a.setAttribute("data-element-id", o), ii(e, a), Y(a, e, t, { + ...n, + inline: !0 + }, r), i === "fillable" && !a.textContent?.trim() && !a.querySelector(".rendered-checkbox-wrap, img, .rendered-marker")) { + let e = document.createElement("span"); + e.className = "rendered-field-fillable-slot", e.setAttribute("aria-hidden", "true"), a.appendChild(e); + } + return a; +} +function hi(e, t, n, r) { + let i = document.createElement("span"); + i.className = "rendered-field-hint rendered-el"; + let a = t.get(e); + return a && i.setAttribute("data-element-id", a), ii(e, i), Y(i, e, t, { + ...n, + inline: !0 + }, r), i; +} +function gi(e, t, n, r) { + let i = L(e); + if (i === "content") { + let t = document.createElement("span"); + return t.textContent = e.textContent ?? "", t; + } + let a; + return i === "handwriting" ? (a = document.createElement("span"), a.className = "rendered-handwriting") : i === "rtl" ? (a = document.createElement("bdi"), a.setAttribute("dir", "rtl")) : a = document.createElement($r[i] ?? "span"), Y(a, e, t, n, r), a; +} +function _i(e, t, n, r) { + let i = I(e).find((e) => L(e) === "label")?.getAttribute("value"), a = document.createElement("code"); + if (Y(a, e, t, { inline: n.inline }, r), n.inline) { + a.classList.add("rendered-el"); + let n = t.get(e); + return n && a.setAttribute("data-element-id", n), a; + } + let o = document.createElement("pre"); + if (i && i !== "undefined") { + let e = document.createElement("span"); + e.className = "rendered-code-label", e.textContent = i, o.appendChild(e); + } + return o.appendChild(a), J(e, o, t.get(e)); +} +function vi(e, t, n, r) { + let i = document.createElement("span"); + if (i.className = n.inline ? "rendered-formula-inline" : "rendered-formula", Y(i, e, t, { inline: !0 }, r), n.inline) { + i.classList.add("rendered-el"); + let n = t.get(e); + return n && i.setAttribute("data-element-id", n), i; + } + return J(e, i, t.get(e)); +} +function yi(e, t, n) { + let r = document.createElement("figure"), i = ci(e); + si(r, (I(e).find((e) => L(e) === "src") ?? null)?.getAttribute("uri")?.trim() || null, i, t, n); + let a = [...e.childNodes], o = Pt(a, 0); + for (; o < a.length;) { + let e = a[o]; + if (e.nodeType !== Node.ELEMENT_NODE) { + o += 1; + continue; + } + let i = L(e); + if (i === "src") { + o += 1; + continue; + } + if (i === "tabular") { + let i = Pi(e, t, n); + i && r.appendChild(i), o += 1; + continue; + } + break; + } + let s = document.createElement("div"); + if (s.className = "rendered-picture-contents-body", bi(s, a, o, t, n), s.textContent?.trim()) { + let e = document.createElement("details"); + e.className = "rendered-picture-contents"; + let t = document.createElement("summary"); + t.textContent = "Picture contents", e.appendChild(t), e.appendChild(s), r.appendChild(e); + } + return J(e, r, t.get(e)); +} +function bi(e, t, n, r, i) { + let a = n; + for (; a < t.length;) { + let n = t[a]; + if (n.nodeType === Node.ELEMENT_NODE && Zr.has(L(n))) { + let t = X(n, r, { inline: !1 }, i); + t && e.appendChild(t); + } else Ii(e, n, r, { inline: !1 }, i); + a += 1; + } +} +function xi(e, t, n, r) { + let i = t.some((e) => e.nodeType === Node.ELEMENT_NODE && Zr.has(L(e))), a = document.createElement(i ? "div" : "p"); + for (let e of t) if (e.nodeType === Node.ELEMENT_NODE && Zr.has(L(e))) { + let t = X(e, n, { inline: !1 }, r); + t && a.appendChild(t); + } else Ii(a, e, n, { inline: !0 }, r); + let o = document.createElement("div"); + o.className = "rendered-el rendered-text rendered-el-virtual-text"; + let s = n.get(e); + return s && o.setAttribute("data-element-id", s), ii(e, o), o.appendChild(a), o; +} +function Si(e) { + for (let t = 0; t < e.length; t += 1) { + let n = e[t]; + if (n && (P(n) && !F(n) || n.nodeType === Node.ELEMENT_NODE)) return !0; + } + return !1; +} +function Ci(e) { + if (F(e)) return !0; + if (e.nodeType !== Node.ELEMENT_NODE) return !1; + let t = L(e); + return t === "location" || M.has(t); +} +function wi(e) { + if (!Si(e)) return !1; + for (let t of e) if (!Ci(t) && (P(t) || t.nodeType === Node.ELEMENT_NODE && !Ft(t))) return !0; + return !1; +} +function Ti(e, t, n, r, i) { + if (Si(n)) { + if (wi(n)) { + e.appendChild(xi(t, n, r, i)); + return; + } + for (let t of n) if (!Ci(t)) { + if (t.nodeType === Node.ELEMENT_NODE && Zr.has(L(t))) { + let n = X(t, r, { inline: !1 }, i); + n && e.appendChild(n); + } else Ii(e, t, r, { inline: !0 }, i); + } + } +} +function Ei(e, t, n, r) { + let i = [...t.childNodes], a = xt(i, 0); + for (; a < i.length;) { + let t = i[a]; + if (t.nodeType !== Node.ELEMENT_NODE || L(t) !== "ldiv") { + a += 1; + continue; + } + let o = t; + a += 1; + let s = document.createElement("li"); + for (let e of I(o)) { + let t = L(e); + t === "marker" ? s.appendChild(di(e, n, { inline: !0 }, r)) : t === "checkbox" && s.appendChild(fi(e, n)); + } + let c = a, l = R(i, a); + for (l && (a = l.nextIndex); a < i.length;) { + let e = i[a]; + if (e.nodeType === Node.ELEMENT_NODE && L(e) === "ldiv") break; + a += 1; + } + Ti(s, o, i.slice(c, a), n, r), e.appendChild(s); + } +} +function Di(e, t, n) { + let r = e.getAttribute("class") ?? "unordered", i = document.createElement(r === "ordered" ? "ol" : "ul"); + return Ei(i, e, t, n), J(e, i, t.get(e)); +} +function Oi(e) { + return e === "ched" || e === "rhed" || e === "corn" || e === "srow"; +} +function ki(e) { + let t = [...e.childNodes], n = Nt(t, 0), r = [], i = []; + for (; n < t.length;) { + let e = t[n]; + if (e.nodeType !== Node.ELEMENT_NODE) { + n += 1; + continue; + } + let a = L(e); + if (a === "nl") { + r.push(i), i = [], n += 1; + continue; + } + if (!B(a)) { + n += 1; + continue; + } + if (_t.has(a)) { + i.push({ + kind: a, + token: e, + contentNodes: [] + }), n += 1; + continue; + } + n += 1; + let o = R(t, n); + o && (n = o.nextIndex); + let s = n; + n = Ct(t, n), i.push({ + kind: a, + token: e, + contentNodes: t.slice(s, n) + }); + } + return i.length && r.push(i), r; +} +function Ai(e, t, n) { + for (let r = t - 1; r >= 0; --r) { + let t = e[r]?.[n]; + if (!(!t || t.covered)) return { + cell: t, + row: r, + col: n + }; + } + return null; +} +function ji(e, t, n) { + for (let r = n - 1; r >= 0; --r) { + let n = e[t]?.[r]; + if (!(!n || n.covered)) return { + cell: n, + row: t, + col: r + }; + } + return null; +} +function Mi(e, t, n) { + let r = n; + for (; e[t]?.[r]?.covered;) r += 1; + return r; +} +function Ni(e) { + let t = []; + for (let n = 0; n < e.length; n += 1) { + t[n] || (t[n] = []); + let r = 0; + for (let i of e[n]) { + if (r = Mi(t, n, r), i.kind === "lcel") { + let e = ji(t, n, r); + e && (e.cell.colspan += 1), t[n][r] = { + kind: "lcel", + token: i.token, + contentNodes: [], + colspan: 0, + rowspan: 0, + covered: !0 + }, r += 1; + continue; + } + if (i.kind === "ucel") { + let e = Ai(t, n, r); + e && (e.cell.rowspan += 1), t[n][r] = { + kind: "ucel", + token: i.token, + contentNodes: [], + colspan: 0, + rowspan: 0, + covered: !0 + }, r += 1; + continue; + } + if (i.kind === "xcel") { + let e = Ai(t, n, r), a = ji(t, n, r); + e && a && e.cell === a.cell ? (e.cell.rowspan += 1, e.cell.colspan += 1) : (e && (e.cell.rowspan += 1), a && (a.cell.colspan += 1)), t[n][r] = { + kind: "xcel", + token: i.token, + contentNodes: [], + colspan: 0, + rowspan: 0, + covered: !0 + }, r += 1; + continue; + } + t[n][r] = { + kind: i.kind, + token: i.token, + contentNodes: i.contentNodes, + colspan: 1, + rowspan: 1, + covered: !1 + }, r += 1; + } + } + return t; +} +function Pi(e, t, n) { + let r = document.createElement("table"); + r.className = "rendered-table"; + let i = ci(e); + i && r.appendChild(li(i, t, "caption", n)); + let a = Ni(ki(e)), o = document.createElement("tbody"); + for (let e of a) { + let r = document.createElement("tr"); + for (let i of e ?? []) { + if (!i || i.covered) continue; + let e = Oi(i.kind) ? "th" : "td", a = document.createElement(e); + i.colspan > 1 && (a.colSpan = i.colspan), i.rowspan > 1 && (a.rowSpan = i.rowspan), Ti(a, i.token, i.contentNodes, t, n), r.appendChild(a); + } + r.childNodes.length && o.appendChild(r); + } + return o.childNodes.length && r.appendChild(o), J(e, r, t.get(e)); +} +function Fi(e, t) { + let n = document.createElement("div"); + return n.className = "rendered-unsupported", n.textContent = `<${L(e)}> — not yet rendered`, J(e, n, t.get(e)); +} +function X(e, t, n, r) { + let i = L(e), a = t.get(e); + switch (i) { + case "text": { + let n = document.createElement("p"); + return Y(n, e, t, { inline: !0 }, r), J(e, n, a); + } + case "heading": { + let n = document.createElement(`h${Tt(e)}`); + return Y(n, e, t, { inline: !0 }, r), J(e, n, a); + } + case "field_heading": { + let n = document.createElement(`h${Tt(e)}`); + return n.className = "rendered-field-heading", Y(n, e, t, { inline: !0 }, r), J(e, n, a); + } + case "footnote": { + let n = document.createElement("aside"); + return Y(n, e, t, { inline: !1 }, r), J(e, n, a); + } + case "page_header": { + let n = document.createElement("header"); + return n.className = "rendered-page-header", Y(n, e, t, { inline: !0 }, r), J(e, n, a); + } + case "page_footer": { + let n = document.createElement("footer"); + return n.className = "rendered-page-footer", Y(n, e, t, { inline: !0 }, r), J(e, n, a); + } + case "list": return Di(e, t, r); + case "table": + case "index": + case "tabular": return Pi(e, t, r); + case "code": return _i(e, t, n, r); + case "formula": return vi(e, t, n, r); + case "picture": return yi(e, t, r); + case "group": { + let n = document.createElement("figure"); + n.className = "rendered-group", ui(n, e, t, r); + let i = ci(e); + return i && n.appendChild(li(i, t, "figcaption", r)), J(e, n, a); + } + case "field_region": { + let n = document.createElement("div"); + return n.className = "rendered-field-region", ui(n, e, t, r), J(e, n, a); + } + case "field_item": { + let n = document.createElement("div"); + return n.className = "rendered-field-item", ui(n, e, t, r), J(e, n, a); + } + default: return Fi(e, t); + } +} +function Ii(e, t, n, r, i) { + if (P(t)) { + let n = t.textContent; + if (!n || !n.trim() || r.trimLeading && (n = n.replace(/^\s+/u, ""), r.trimLeading = !1, !n)) return; + e.appendChild(document.createTextNode(n)); + return; + } + if (t.nodeType !== Node.ELEMENT_NODE) return; + let a = t, o = L(a); + if (!M.has(o)) { + if (Qr.has(o)) { + e.appendChild(gi(a, n, r, i)); + return; + } + if (o === "code" || o === "formula") { + let t = X(a, n, { inline: !0 }, i); + t && e.appendChild(t); + return; + } + if (Zr.has(o)) { + let t = X(a, n, r, i); + t && e.appendChild(t); + return; + } + if (o === "marker") { + e.appendChild(di(a, n, r, i)); + return; + } + if (o === "checkbox") { + e.appendChild(fi(a, n)); + return; + } + if (o === "key") { + e.appendChild(pi(a, n, r, i)); + return; + } + if (o === "value") { + e.appendChild(mi(a, n, r, i)); + return; + } + if (o === "hint") { + e.appendChild(hi(a, n, r, i)); + return; + } + o !== "ldiv" && (B(o) || o === "src" || o === "tabular" || Y(e, a, n, r, i)); + } +} +function Li(e) { + if (P(e)) return e; + if (e.nodeType !== Node.ELEMENT_NODE) return null; + for (let t = e.childNodes.length - 1; t >= 0; --t) { + let n = Li(e.childNodes[t]); + if (n) return n; + } + return null; +} +function Ri(e) { + let t = Li(e); + if (!t) return; + let n = t.textContent ?? ""; + if (n = n.replace(/\s+$/u, ""), n.endsWith("-") && (n = n.slice(0, -1)), !n) { + t.parentNode?.removeChild(t), Ri(e); + return; + } + t.textContent = n; +} +function zi(e, t, n, r) { + for (let i = 0; i < t.length; i += 1) i > 0 && Ri(e), Y(e, t[i], n, { + inline: !0, + trimLeading: i > 0 + }, r); +} +function Bi(e, t, n) { + let r = e[0], i = L(r), a = t.get(r), o = Lt(r), s; + if (i === "text") s = document.createElement("p"), zi(s, e, t, n); + else if (i === "list") { + let i = r.getAttribute("class") ?? "unordered"; + s = document.createElement(i === "ordered" ? "ol" : "ul"); + for (let r of e) Ei(s, r, t, n); + } else { + s = document.createElement("div"), s.className = "rendered-fragment-merged-body"; + for (let r of e) ui(s, r, t, n); + } + let c = J(r, s, a, "rendered-fragment-merged"); + return o && c.setAttribute("data-thread-id", o), c; +} +function Vi(e) { + let t = /* @__PURE__ */ new Map(); + for (let n of e) { + if (n.nodeType !== Node.ELEMENT_NODE || L(n) === "page_break") continue; + let e = Lt(n); + e && (t.has(e) || t.set(e, []), t.get(e).push(n)); + } + for (let [e, n] of t) n.length < 2 && t.delete(e); + return t; +} +var Z = class extends U { + constructor(...e) { + super(...e), this._showFurniture = !0, this._showBackground = !0, this._settingsOpen = !1, this._visible = !1, this._pageController = new $n(this, () => this.scrollPane), this._hasMarkup = null, this._pendingContent = null, this._settingsPanelRef = A(), this._onContentRef = (e) => { + e && this._pendingContent && e.replaceChildren(this._pendingContent); + }, this._onSettingsToggle = () => { + this._settingsOpen = !this._settingsOpen, this._settingsPanelRef.value?.setOpen(this._settingsOpen), this.requestUpdate(); + }, this._onSettingsClose = () => { + this._settingsOpen = !1, this.requestUpdate(); + }, this._onFurnitureChange = (e) => { + this._showFurniture = e.target.checked, this.dispatchEvent(new CustomEvent("doclang-show-reading-furniture", { + bubbles: !0, + composed: !0, + detail: { checked: this._showFurniture } + })); + }, this._onBackgroundChange = (e) => { + this._showBackground = e.target.checked, this.dispatchEvent(new CustomEvent("doclang-show-reading-background", { + bubbles: !0, + composed: !0, + detail: { checked: this._showBackground } + })); + }, this._onBodyClick = (e) => { + let t = e.target, n = t.closest(".rendered-el-virtual-text"), r = n?.hasAttribute("data-element-id") ? n.getAttribute("data-element-id") : t.closest(".rendered-el[data-element-id]")?.getAttribute("data-element-id") ?? null; + r && this.dispatchEvent(new CustomEvent("doclang-element-select", { + bubbles: !0, + composed: !0, + detail: { id: r } + })); + }; + } + static { + this.styles = s(Xr); + } + connectedCallback() { + super.connectedCallback(), this.classList.add("pane", "pane-reading"); + } + render() { + let e = { + "pane-body": !0, + "show-reading-furniture": this._showFurniture, + "show-reading-background": this._showBackground + }; + return b` +
    + Reading view + ${this._visible ? b`` : C} +
    +
    +
    + ${this._hasMarkup === !1 ? b`
    ${fn}
    ` : this._hasMarkup === !0 ? b`
    ` : C} +
    + ${this._visible ? b` + +
    + + +
    +
    + ` : C} +
    + `; + } + updated(e) { + if (super.updated(e), !this._pendingContent) return; + let t = this.shadowRoot?.querySelector(".pane-body > div"); + t && !t.contains(this._pendingContent) && t.replaceChildren(this._pendingContent); + } + get scrollPane() { + return this.shadowRoot?.querySelector(".pane-body") ?? null; + } + setVisible(e) { + this._visible = e, this.hidden = !e, this.requestUpdate(); + } + _applySelection() { + if (!this.shadowRoot) return; + for (let e of this.shadowRoot.querySelectorAll(".rendered-el.selected")) e.classList.remove("selected"); + if (!this.selected) return; + let e = this._findRenderedElement(this.selected, this._peerIds); + e && (this._revealContext(e), e.classList.add("selected"), e.scrollIntoView({ + block: "nearest", + behavior: "smooth" + })); + } + _renderDocument() { + let e = this._docState; + if (!e) { + this._pendingContent = null, this._hasMarkup = null; + return; + } + let t = e.segments[this.page - 1] ?? [], n = e.elementIds.size ? e.elementIds : _n(t); + e.elementIds = n, hn(t) ? (this._pendingContent = this._buildRenderedArticle(t, n), this._hasMarkup = !0) : (this._pendingContent = null, this._hasMarkup = !1), this.requestUpdate(); + } + _clearDocument() { + this._pendingContent = null, this._hasMarkup = null, this.requestUpdate(); + } + _buildRenderedArticle(e, t) { + let n = this._docState?.assetUrls, r = Vi(e), i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Map(); + for (let [, e] of r) { + a.set(e[0], e); + for (let t = 1; t < e.length; t += 1) i.add(e[t]); + } + let o = document.createElement("article"); + o.className = "rendered-doc"; + for (let r of e) { + if (r.nodeType !== Node.ELEMENT_NODE || L(r) === "page_break" || i.has(r)) continue; + let e = a.get(r), s = e ? Bi(e, t, n) : X(r, t, { inline: !1 }, n); + s && o.appendChild(s); + } + return o; + } + _findRenderedElement(e, t) { + if (!this.shadowRoot) return null; + let n = this.shadowRoot.querySelector(`.rendered-el-virtual-text[data-element-id="${e}"]`) ?? this.shadowRoot.querySelector(`.rendered-el[data-element-id="${e}"]`); + if (n) return n; + let r = this._docState?.idToElement?.get(e), i = r ? Lt(r) : null; + if (!i) return null; + let a = this.shadowRoot.querySelector(`.rendered-fragment-merged[data-thread-id="${i}"]`); + if (!a) return null; + let o = a.getAttribute("data-element-id"); + return !o || o === e || t.has(e) ? a : null; + } + _revealContext(e) { + let t = e.closest(".rendered-picture-contents"); + t && !t.open && (t.open = !0), this._revealLayer(e); + } + _revealLayer(e) { + let t = e.getAttribute("data-doclang-layer"); + !t || t === "body" || (t === "furniture" && !this._showFurniture ? (this._showFurniture = !0, this.dispatchEvent(new CustomEvent("doclang-show-reading-furniture", { + bubbles: !0, + composed: !0, + detail: { checked: !0 } + }))) : t === "background" && !this._showBackground && (this._showBackground = !0, this.dispatchEvent(new CustomEvent("doclang-show-reading-background", { + bubbles: !0, + composed: !0, + detail: { checked: !0 } + })))); + } +}; +H([O()], Z.prototype, "_showFurniture", void 0), H([O()], Z.prototype, "_showBackground", void 0), H([O()], Z.prototype, "_settingsOpen", void 0), H([O()], Z.prototype, "_visible", void 0), H([O()], Z.prototype, "_hasMarkup", void 0), Z = H([E("doclang-reading-pane")], Z); +//#endregion +//#region src/components/viewer/empty.css?inline +var Hi = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{display:contents}.empty-state{border:2px dashed var(--border);background:var(--placeholder-bg);text-align:center;min-height:0;color:var(--muted);border-radius:.75rem;flex:1 1 0;justify-content:center;align-items:center;margin:1rem;padding:2rem 1.5rem;display:flex}.empty-state-inner{max-width:28rem}.empty-state-loading{flex-direction:column;align-items:center;display:none}:host(.demo-loading) .empty-state-loading{display:flex}:host(.demo-loading) .empty-state-prompt{display:none}.loading-spinner{border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;width:2rem;height:2rem;margin:0 auto 1rem;animation:.7s linear infinite loading-spin}@keyframes loading-spin{to{transform:rotate(360deg)}}.empty-state-title{color:var(--text);margin:0 0 .75rem;font-size:1.05rem}.empty-state p{margin:.5rem 0}.empty-state-action{margin:0 0 .75rem}.text-link{color:var(--accent);text-decoration:underline}.text-link:hover{opacity:.85}.empty-state-meta{font-size:.875rem}code{font-family:var(--font-mono)}", Ui = class extends T { + constructor(...e) { + super(...e), this._extensions = []; + } + static { + this.styles = s(Hi); + } + render() { + return b` +
    +
    +
    + +

    Loading demo…

    +

    Preparing the sample document

    +
    +
    +

    Drop a DocLang file here

    +

    + Supported file types: + ${this._extensions.map((e, t) => b`${t > 0 ? ", " : ""}${e}`)} +

    +

    + or + { + e.preventDefault(), this.dispatchEvent(new CustomEvent("doclang-load-demo", { + bubbles: !0, + composed: !0 + })); + }} + >load demo +

    +
    +
    +
    + `; + } + setFileTypeHints(e) { + this._extensions = e, this.requestUpdate(); + } + setDemoLoading(e) { + this.classList.toggle("demo-loading", e); + } +}; +Ui = H([E("doclang-empty")], Ui); +//#endregion +//#region src/components/container/pane-stack.ts +var Wi = class extends U { + constructor(...e) { + super(...e), this._onChildViewPage = (e) => { + if (e.target === this) return; + let t = e.detail.page; + e.stopPropagation(), this.page = t; + }; + } + connectedCallback() { + super.connectedCallback(), this.addEventListener("view-page", this._onChildViewPage); + } + disconnectedCallback() { + super.disconnectedCallback(), this.removeEventListener("view-page", this._onChildViewPage); + } + render() { + return b``; + } + _renderDocument() { + this._pushToChildren(); + } + _clearDocument() { + this._pushToChildren(); + } + _applySelection() { + this._pushSelectedToChildren(); + } + _children() { + return Array.from(this.children).filter((e) => e instanceof U); + } + _pushToChildren() { + for (let e of this._children()) e.document = this._docState, e.page = this.page, e.selected = this.selected ?? null; + } + _pushSelectedToChildren() { + for (let e of this._children()) e.selected = this.selected ?? null; + } +}; +Wi = H([E("doclang-pane-stack")], Wi); +//#endregion +//#region node_modules/lit-html/directives/style-map.js +var Gi = "important", Ki = " !important", qi = nt(class extends rt { + constructor(e) { + if (super(e), e.type !== tt.ATTRIBUTE || e.name !== "style" || e.strings?.length > 2) throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute."); + } + render(e) { + return Object.keys(e).reduce((t, n) => { + let r = e[n]; + return r == null ? t : t + `${n = n.includes("-") ? n : n.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g, "-$&").toLowerCase()}:${r};`; + }, ""); + } + update(e, [t]) { + let { style: n } = e.element; + if (this.ft === void 0) return this.ft = new Set(Object.keys(t)), this.render(t); + for (let e of this.ft) t[e] ?? (this.ft.delete(e), e.includes("-") ? n.removeProperty(e) : n[e] = null); + for (let e in t) { + let r = t[e]; + if (r != null) { + this.ft.add(e); + let t = typeof r == "string" && r.endsWith(Ki); + e.includes("-") || t ? n.setProperty(e, t ? r.slice(0, -11) : r, t ? Gi : "") : n[e] = r; + } + } + return S; + } +}), Ji = ":host{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--doclang-bg,#f4f4f5);--panel:var(--doclang-panel,#fff);--border:var(--doclang-border,#d4d4d8);--text:var(--doclang-text,#18181b);--muted:var(--doclang-muted,#71717a);--accent:var(--doclang-accent,#2563eb);--placeholder-bg:var(--doclang-placeholder-bg,#fafafa);--font-ui:var(--doclang-font-ui,system-ui, -apple-system, \"Segoe UI\", sans-serif);--font-mono:var(--doclang-font-mono,ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace);--markup-bg:var(--doclang-markup-bg,#fff);--markup-fg:var(--doclang-markup-fg,#24292e);--markup-hover:var(--doclang-markup-hover,#2563eb14);--markup-selected:var(--doclang-markup-selected,#2563eb24);--kind-text:var(--doclang-kind-text,#2563eb);--kind-heading:var(--doclang-kind-heading,#7c3aed);--kind-list:var(--doclang-kind-list,#ea580c);--kind-ldiv:var(--doclang-kind-ldiv,#ea580c);--kind-table:var(--doclang-kind-table,#16a34a);--kind-index:var(--doclang-kind-index,#0d9488);--kind-formula:var(--doclang-kind-formula,#0891b2);--kind-code:var(--doclang-kind-code,#475569);--kind-picture:var(--doclang-kind-picture,#db2777);--kind-group:var(--doclang-kind-group,#4f46e5);--kind-footnote:var(--doclang-kind-footnote,#a16207);--kind-page_header:var(--doclang-kind-page_header,#71717a);--kind-page_footer:var(--doclang-kind-page_footer,#71717a);--kind-caption:var(--doclang-kind-caption,#71717a);--kind-field:var(--doclang-kind-field,#d97706);--kind-default:var(--doclang-kind-default,#dc2626)}@media (prefers-color-scheme:dark){:host{--lightningcss-light: ;--lightningcss-dark:initial;--bg:var(--doclang-bg,#09090b);--panel:var(--doclang-panel,#18181b);--border:var(--doclang-border,#3f3f46);--text:var(--doclang-text,#fafafa);--muted:var(--doclang-muted,#a1a1aa);--placeholder-bg:var(--doclang-placeholder-bg,#27272a);--markup-bg:var(--doclang-markup-bg,#1e1e1e);--markup-fg:var(--doclang-markup-fg,#d4d4d4);--markup-hover:var(--doclang-markup-hover,#60a5fa1a);--markup-selected:var(--doclang-markup-selected,#60a5fa2e);--kind-text:var(--doclang-kind-text,#60a5fa);--kind-heading:var(--doclang-kind-heading,#a78bfa);--kind-list:var(--doclang-kind-list,#fb923c);--kind-ldiv:var(--doclang-kind-ldiv,#fb923c);--kind-table:var(--doclang-kind-table,#4ade80);--kind-index:var(--doclang-kind-index,#2dd4bf);--kind-formula:var(--doclang-kind-formula,#22d3ee);--kind-code:var(--doclang-kind-code,#94a3b8);--kind-picture:var(--doclang-kind-picture,#f472b6);--kind-group:var(--doclang-kind-group,#818cf8);--kind-footnote:var(--doclang-kind-footnote,#facc15);--kind-page_header:var(--doclang-kind-page_header,#a1a1aa);--kind-page_footer:var(--doclang-kind-page_footer,#a1a1aa);--kind-caption:var(--doclang-kind-caption,#a1a1aa);--kind-field:var(--doclang-kind-field,#fbbf24);--kind-default:var(--doclang-kind-default,#f87171)}}:host{min-height:100vh;font-family:var(--font-ui);background:var(--bg);color:var(--text);flex-direction:column;display:flex}:host(.drag-over){background:color-mix(in srgb, var(--accent) 6%, var(--bg))}:host(.drag-over) doclang-empty{border-color:var(--accent);background:color-mix(in srgb, var(--accent) 8%, var(--placeholder-bg))}header{border-bottom:1px solid var(--border);background:var(--panel);grid-template-columns:1fr auto 1fr;align-items:center;gap:1rem;padding:.75rem 1rem;display:grid}.header-brand{grid-column:1;justify-self:start;align-items:center;gap:1rem;min-width:0;display:flex}header h1{margin:0;font-size:1.1rem;font-weight:600}.header-center{text-align:center;grid-column:2;justify-self:center;min-width:0}.doc-label{color:var(--muted);text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:min(40vw,28rem);font-size:.8125rem;font-weight:400;overflow:hidden}.header-logo-link{border-radius:.25rem;flex-shrink:0;line-height:0;text-decoration:none;display:block}.header-logo-link:hover{opacity:.8}.header-logo{width:auto;height:2.25rem;display:block}.header-logo-link:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.toolbar-wrap{grid-column:3;justify-self:end}.drop-banner{text-align:center;color:var(--muted);background:color-mix(in srgb, var(--accent) 8%, var(--panel));border-bottom:1px solid var(--border);margin:0;padding:.35rem 1rem;font-size:.8125rem;display:none}:host(.loaded.drag-over) .drop-banner{display:block}:host(.loaded) doclang-empty{display:none}.main{flex:1 1 0;min-height:0;display:none}:host(.loaded) .main{grid-template-rows:minmax(0,1fr);display:grid}:host(.loaded) .main .pane{border-right:none}.pane{border-right:1px solid var(--border);flex-direction:column;min-width:0;min-height:0;display:flex}.pane[hidden]{display:none!important}.pane-splitter{box-sizing:content-box;cursor:col-resize;touch-action:none;z-index:2;background:0 0;width:1px;margin:0 -4px;padding:0 4px;position:relative}.pane-splitter:after{content:\"\";background:var(--border);width:1px;transition:background .15s,width .15s;position:absolute;top:0;bottom:0;left:4px}.pane-splitter:hover:after,.pane-splitter:focus-visible:after,.pane-splitter.is-dragging:after{background:var(--accent);width:3px}.pane-splitter[hidden]{display:none!important}:host(.pane-drag-active){cursor:col-resize;-webkit-user-select:none;user-select:none}:host(.pane-drag-active) .pane-splitter{cursor:col-resize}@media (width<=1200px){:host(.loaded) .main{grid-template-rows:unset;grid-template-columns:1fr!important}:host(.loaded) .main .pane{border-right:none;border-bottom:1px solid var(--border);grid-column:1!important}:host(.loaded) .main .pane-splitter{display:none!important}}", Yi = class { + constructor(e) { + this._catalog = [], this._activeIndex = -1, this._host = e, e.addController(this); + } + hostConnected() {} + hostDisconnected() {} + get entries() { + return this._catalog.map((e, t) => ({ + label: e.label, + thumbnailUrl: e.thumbnailUrl, + isActive: t === this._activeIndex + })); + } + get size() { + return this._catalog.length; + } + get activeIndex() { + return this._activeIndex; + } + get activeEntry() { + return this._catalog[this._activeIndex] ?? null; + } + hasMultiple() { + return this._catalog.length > 1; + } + findCatalogIndexForFile(e) { + return this._catalog.findIndex((t) => { + let n = t.source; + return n instanceof File && n.name === e.name && n.size === e.size && n.lastModified === e.lastModified; + }); + } + async addFiles(e, { replace: t = !1 } = {}) { + t && this._clearAll(); + let n = this._catalog.length, r = null; + for (let n of e) { + let e = t ? -1 : this.findCatalogIndexForFile(n); + if (e !== -1) { + r === null && (r = e); + continue; + } + let i = this._createEntry(n); + this._catalog.push(i), this._enrichThumbnail(i), r === null && (r = this._catalog.length - 1); + } + return this._catalog.length ? this._switchTo(t ? 0 : r ?? n) : null; + } + async addArchiveBuffer(e, t, { replace: n = !1 } = {}) { + n && this._clearAll(); + let r = { + id: crypto.randomUUID(), + label: t, + kind: "archive", + source: e, + currentPage: 1, + pageZoom: 100, + snapshot: null, + thumbnailUrl: null + }; + return this._catalog.push(r), this._enrichThumbnail(r), this._switchTo(n ? 0 : this._catalog.length - 1); + } + async appendFolderArchive(e) { + if (!e.some((e) => e.name === "document.xml")) return alert("Archive must contain document.xml at its root."), null; + let t = (e[0].webkitRelativePath || e[0].name).split("/")[0] || "archive", n = { + id: crypto.randomUUID(), + label: t, + kind: "folder", + source: e, + currentPage: 1, + pageZoom: 100, + snapshot: null, + thumbnailUrl: null + }; + return this._catalog.push(n), this._enrichThumbnail(n), this._switchTo(this._catalog.length - 1); + } + persistActiveViewState(e, t) { + let n = this.activeEntry; + n && (n.currentPage = e, n.pageZoom = t); + } + async selectEntry(e) { + return e < 0 || e >= this._catalog.length ? null : (this._releaseActive(), this._switchTo(e)); + } + async closeEntry(e) { + if (e < 0 || e >= this._catalog.length) return { + doc: null, + newIndex: -1 + }; + let t = e === this._activeIndex, n = this._catalog[e]; + if (t && (this._releaseActive(), this._activeIndex = -1), this._revoke(n), this._catalog.splice(e, 1), !this._catalog.length) return this._requestUpdate(), { + doc: null, + newIndex: -1 + }; + if (t) { + let t = Math.min(e, this._catalog.length - 1); + return { + doc: await this._switchTo(t), + newIndex: this._activeIndex + }; + } + return e < this._activeIndex && --this._activeIndex, this._requestUpdate(), { + doc: null, + newIndex: this._activeIndex + }; + } + clearAll() { + this._clearAll(), this._requestUpdate(); + } + isArchiveFile(e) { + return /\.dclx$/i.test(e.name) || /\.zip$/i.test(e.name); + } + isMarkupFile(e) { + return /\.(?:dclg(?:\.xml)?|xml)$/i.test(e.name); + } + _requestUpdate() { + this._host.requestUpdate(); + } + async _switchTo(e) { + this._activeIndex = e; + let t = this._catalog[e], n = await this._parse(t); + return n ? (t.snapshot = n, n) : (this._revoke(t), this._catalog.splice(e, 1), this._activeIndex = -1, this._requestUpdate(), this._catalog.length ? this._switchTo(Math.min(e, this._catalog.length - 1)) : null); + } + _releaseActive() { + let e = this.activeEntry; + e?.snapshot && (zn(e.snapshot), e.snapshot = null); + } + _clearAll() { + this._releaseActive(); + for (let e of this._catalog) this._revoke(e); + this._catalog = [], this._activeIndex = -1; + } + _revoke(e) { + e.thumbnailUrl?.startsWith("blob:") && URL.revokeObjectURL(e.thumbnailUrl), e.thumbnailUrl = null; + } + _createEntry(e) { + return { + id: crypto.randomUUID(), + label: e.name, + kind: this.isMarkupFile(e) ? "markup" : "archive", + source: e, + currentPage: 1, + pageZoom: 100, + snapshot: null, + thumbnailUrl: null + }; + } + _enrichThumbnail(e) { + this._resolveThumbnail(e).then((t) => { + if (!this._catalog.includes(e)) { + t?.startsWith("blob:") && URL.revokeObjectURL(t); + return; + } + t && this._requestUpdate(); + }); + } + async _resolveThumbnail(e) { + if (e.thumbnailUrl) return e.thumbnailUrl; + if (e.kind === "markup") return null; + try { + e.kind === "folder" ? e.thumbnailUrl = this._firstPageUrlFromFiles(e.source) : e.kind === "archive" && (e.thumbnailUrl = await this._firstPageUrlFromZip(e.source)); + } catch { + e.thumbnailUrl = null; + } + return e.thumbnailUrl; + } + _firstPageUrlFromFiles(e) { + let t = Infinity, n = null; + for (let r of e) { + let e = (r.webkitRelativePath || r.name).split("/"); + if (e.length < 2 || e[e.length - 2] !== "pages") continue; + let i = dn.exec(r.name); + if (!i) continue; + let a = Number(i[1]); + a < t && (t = a, n = r); + } + return n ? URL.createObjectURL(n) : null; + } + async _firstPageUrlFromZip(e) { + let t = await tn(e instanceof File ? await e.arrayBuffer() : e, { shouldExtract: (e) => /^pages\/\d+\.(png|jpe?g|webp)$/i.test(e) }), n = Infinity, r = null; + for (let e of t) { + let t = e.name.match(/^pages\/(\d+)\.(png|jpe?g|webp)$/i); + if (!t) continue; + let i = Number(t[1]); + i < n && (n = i, r = e); + } + if (!r) return null; + let i = r.name.split(".").pop() ?? "png"; + return URL.createObjectURL(new Blob([r.data], { type: this._mimeFromExt(i) })); + } + _mimeFromExt(e) { + let t = e.toLowerCase().replace("jpeg", "jpg"); + return t === "png" ? "image/png" : t === "webp" ? "image/webp" : "image/jpeg"; + } + async _parse(e) { + try { + if (e.kind === "markup") return Ln(e.source instanceof File ? await e.source.text() : new TextDecoder().decode(e.source), /* @__PURE__ */ new Map(), e.label, /* @__PURE__ */ new Map(), { markupOnly: !0 }); + if (e.kind === "archive") { + let { markupXml: t, pageImages: n, assetUrls: r } = await ln(e.source instanceof File ? await e.source.arrayBuffer() : e.source); + return Ln(t, n, e.label, r, { markupOnly: !1 }); + } + if (e.kind === "folder") { + let { markupXml: t, pageImages: n, assetUrls: r } = await Rn(e.source); + return Ln(t, n, e.label, r, { markupOnly: !1 }); + } + } catch (t) { + alert(`Failed to read ${e.label}: ${t.message}`); + } + return null; + } +}, Xi = [".dclx", ".dclg"], Zi = "doclang-viewer-pane-layout", Q = [ + "file", + "page", + "markup", + "reading" +], Qi = .12, $i = [ + 1, + 1, + 1, + 1 +], ea = [ + "page", + "markup", + "reading" +], $ = class extends U { + constructor(...e) { + super(...e), this.example = null, this._loaded = !1, this._markupOnly = !1, this._dragOver = !1, this._paneDragActive = !1, this._demoLoading = !1, this._docLabel = null, this._mainGridStyle = {}, this._paneGridCols = /* @__PURE__ */ new Map(), this._splitterCols = [ + null, + null, + null + ], this.panes = [...ea], this._collection = new Yi(this), this._filePaneUserToggled = !1, this._paneRatios = [...$i], this._filePaneWidthPx = null, this._paneDrag = null, this._demoLoadInProgress = !1, this._suppressDemoLoad = !1, this._pageNavRef = A(), this._toolbarRef = A(), this._collectionPaneRef = A(), this._markupPaneRef = A(), this._pageViewPaneRef = A(), this._readingPaneRef = A(), this._emptyStateRef = A(), this._splitterRefs = [ + A(), + A(), + A() + ], this._mainRef = A(), this._onWindowPointerMove = (e) => { + let t = this._paneDrag; + if (!t || e.pointerId !== t.pointerId) return; + if (t.leftKey === "file" && typeof t.leftStartPx == "number") { + this._filePaneWidthPx = Math.max(this._filePaneFitWidthPx(), t.leftStartPx + (e.clientX - t.startX)), this._applyPaneLayout(); + return; + } + if (t.rightKey === "file" && typeof t.rightStartPx == "number") { + this._filePaneWidthPx = Math.max(this._filePaneFitWidthPx(), t.rightStartPx - (e.clientX - t.startX)), this._applyPaneLayout(); + return; + } + let n = this._visiblePaneKeys(), r = this._contentPaneFrWeights(n), i = n.filter((e) => e !== "file").indexOf(t.leftKey); + if (i < 0 || i + 1 >= r.length) return; + let a = r[i] + r[i + 1]; + if (!(a > 0)) return; + let o = Math.max(this._contentPaneAvailableWidthPx() * a, 1), s = (e.clientX - t.startX) / o, c = this._paneRatioIndex(t.leftKey), l = this._paneRatioIndex(t.rightKey), u = t.leftStart + t.rightStart; + if (!(u > 0) || c < 0 || l < 0) return; + let d = Math.min(Qi, u / 2), f = Math.min(Qi, u / 2), p = t.leftStart + s * u; + p = Math.min(Math.max(p, d), u - f), this._paneRatios[c] = p, this._paneRatios[l] = u - p, this._applyPaneLayout(); + }, this._onWindowPointerUp = (e) => { + let t = this._paneDrag; + if (!t || e.pointerId !== t.pointerId) return; + let n = this._splitterRefs[t.physicalSplitterIndex]?.value; + n?.classList.remove("is-dragging"), n?.hasPointerCapture(e.pointerId) && n.releasePointerCapture(e.pointerId), this._paneDrag = null, this._paneDragActive = !1, this._normalizePaneRatios(), this._saveLayoutPrefs(), this.requestUpdate(); + }, this._onHomeClick = (e) => { + e.preventDefault(), this._resetViewer(); + }, this._onLoadDemo = () => { + this._loadDemo(); + }, this._onOpenFiles = (e) => { + let t = e.detail.files.filter((e) => this._collection.isArchiveFile(e) || this._collection.isMarkupFile(e)); + t.length && this._addFilesToCatalog(t, { replace: !0 }); + }, this._onTogglePane = (e) => { + let { pane: t, checked: n } = e.detail; + if (!this._docState) { + this._syncToolbarPaneCheckboxes(); + return; + } + if (![...Q].filter((e) => e === t ? n : this.panes.includes(e) && this._isPaneAvailable(e)).length) { + this._syncToolbarPaneCheckboxes(); + return; + } + this._setUserPaneVisible(t, n); + }, this._onResetPaneLayout = () => { + this._docState && this._resetPaneLayout(); + }, this._onCollectionSelect = (e) => { + let { index: t } = e.detail; + this._collection.persistActiveViewState(this.page, this._pageViewPaneRef.value?.zoomPercent ?? 100), this._collection.selectEntry(t).then((e) => { + if (!e) { + this._resetViewer(); + return; + } + let t = this._collection.activeEntry; + this._activateDocument(e, t.currentPage ?? 1); + }); + }, this._onCollectionClose = (e) => { + let { index: t } = e.detail; + this._collection.persistActiveViewState(this.page, this._pageViewPaneRef.value?.zoomPercent ?? 100), this._collection.closeEntry(t).then(({ doc: e, newIndex: t }) => { + if (t < 0) { + this._resetViewer(); + return; + } + if (e) { + let t = this._collection.activeEntry; + this._activateDocument(e, t.currentPage ?? 1); + } else this._syncCollectionPaneDefault(), this._syncToolbarPaneCheckboxes(), this._applyPaneLayout(), this.requestUpdate(); + }); + }, this._onCollectionCloseAll = () => { + let e = this._collection.size; + if (!e) return; + let t = this._collection.entries[0]?.label ?? "", n = e === 1 ? `Remove "${t}" from the viewer?` : `Remove all ${e} open files from the viewer?`; + confirm(n) && this._resetViewer(); + }, this._onElementSelect = (e) => { + let t = e.detail.id, n = this._resolveSelectionElementId(t) ?? t; + this._selectElement(n); + }, this._onNavigateThread = (e) => { + let { elementId: t, direction: n } = e.detail; + this._navigateThreadFragment(t, n); + }, this._onClearSelection = () => { + this._clearSelection(); + }, this._onViewPage = (e) => { + this.page = e.detail.page; + }; + } + static { + this.styles = s(Ji); + } + connectedCallback() { + super.connectedCallback(), this._loadLayoutPrefs(), this._normalizePaneRatios(), this._initDragDrop(), this._initFileHandling(), this._initPaneDragListeners(), this.addEventListener("view-page", this._onViewPage), this.example && (this._demoLoading = !0, setTimeout(() => { + this._suppressDemoLoad || this._loadDemo(); + }, 0)); + } + disconnectedCallback() { + super.disconnectedCallback(), window.removeEventListener("pointermove", this._onWindowPointerMove), window.removeEventListener("pointerup", this._onWindowPointerUp), window.removeEventListener("pointercancel", this._onWindowPointerUp), this.removeEventListener("view-page", this._onViewPage); + } + updated(e) { + super.updated(e), this.classList.toggle("loaded", this._loaded), this.classList.toggle("markup-only", this._markupOnly), this.classList.toggle("drag-over", this._dragOver), this.classList.toggle("pane-drag-active", this._paneDragActive); + } + firstUpdated() { + let e = this._emptyStateRef.value; + e && e.setFileTypeHints(Xi), this._demoLoading && e && e.setDemoLoading(!0), this._syncToolbarPaneCheckboxes(); + } + render() { + return b` +
    +
    + + + +

    DocLang Viewer

    + +
    + +
    + ${this._docLabel ? b`${this._docLabel}` : C} +
    + +
    + +
    +
    + +

    Drop to open another file

    + + + +
    + this._onCollectionSelect(e)} + @doclang-collection-close=${(e) => this._onCollectionClose(e)} + @doclang-collection-close-all=${this._onCollectionCloseAll} + > + + + + + + + + + + + + + + +
    + + `; + } + _renderDocument() {} + _clearDocument() {} + _paneGridStyle(e) { + let t = this._paneGridCols.get(e); + return t === void 0 ? "" : `grid-column:${t}`; + } + _splitterGridStyle(e) { + let t = this._splitterCols[e]; + return t === null ? "" : `grid-column:${t}`; + } + _isPaneAvailable(e) { + return e === "file" ? this._collection.size > 0 : e === "page" ? !!this._docState?.hasPageView : !!this._docState; + } + _isPaneVisible(e) { + return this._isPaneAvailable(e) ? this.panes.includes(e) : !1; + } + _visiblePaneKeys() { + return [...Q].filter((e) => this._isPaneVisible(e)); + } + _filePaneFitWidthPx() { + let e = document.createElement("div"); + e.style.cssText = "position:absolute;visibility:hidden;width:var(--doclang-file-pane-fit-width);", (this.shadowRoot ?? document.documentElement).appendChild(e); + let t = e.getBoundingClientRect().width; + return e.remove(), Math.ceil(t) || 108; + } + _resolvedFilePaneWidthPx() { + let e = this._filePaneFitWidthPx(); + return Math.max(e, this._filePaneWidthPx ?? e); + } + _contentPaneFrWeights(e) { + let t = e.filter((e) => e !== "file"), n = t.map((e) => this._paneRatios[this._paneRatioIndex(e)]), r = n.reduce((e, t) => e + t, 0) || t.length; + return n.map((e) => e / r); + } + _paneRatioIndex(e) { + return Q.indexOf(e); + } + _normalizePaneRatios() { + let e = this._paneRatios.reduce((e, t) => e + t, 0); + if (e <= 0) { + this._paneRatios = [...$i]; + return; + } + this._paneRatios = this._paneRatios.map((t) => t / e); + } + _paneKeysAdjacent(e, t) { + return Q.indexOf(e) >= 0 && Q.indexOf(t) === Q.indexOf(e) + 1; + } + _onlyHiddenPanesBetween(e, t) { + let n = Q.indexOf(e), r = Q.indexOf(t); + if (n < 0 || r <= n) return !1; + for (let e = n + 1; e < r; e++) if (this._isPaneVisible(Q[e])) return !1; + return !0; + } + _shouldShowSplitter(e, t) { + return !this._isPaneVisible(e) || !this._isPaneVisible(t) ? !1 : this._paneKeysAdjacent(e, t) ? !0 : this._onlyHiddenPanesBetween(e, t); + } + _visibleNeighborAfter(e) { + let t = Q.indexOf(e); + for (let e = t + 1; e < Q.length; e++) if (this._isPaneVisible(Q[e])) return Q[e]; + return null; + } + _visibleNeighborBefore(e) { + let t = Q.indexOf(e); + for (let e = t - 1; e >= 0; e--) if (this._isPaneVisible(Q[e])) return Q[e]; + return null; + } + _applyPaneLayout() { + let e = this._visiblePaneKeys(); + e.length || (this.panes.includes("markup") || (this.panes = [...this.panes, "markup"]), e = this._visiblePaneKeys()); + let t = /* @__PURE__ */ new Map(), n = [ + null, + null, + null + ]; + if (!this._loaded) { + this._paneGridCols = t, this._splitterCols = n, this._mainGridStyle = {}, this.requestUpdate(); + return; + } + let r = this._contentPaneFrWeights(e), i = [], a = 0; + e.forEach((t, n) => { + t === "file" ? i.push(`${this._resolvedFilePaneWidthPx()}px`) : i.push(`minmax(0, ${r[a++].toFixed(6)}fr)`), n < e.length - 1 && this._shouldShowSplitter(e[n], e[n + 1]) && i.push("1px"); + }); + let o = 1; + e.forEach((r, i) => { + if (t.set(r, o++), i < e.length - 1) { + let t = e[i], r = e[i + 1]; + if (!this._shouldShowSplitter(t, r)) return; + let a = Q.indexOf(t); + a >= 0 && a < 3 && (n[a] = o++); + } + }), this._paneGridCols = t, this._splitterCols = n, this._mainGridStyle = { gridTemplateColumns: i.join(" ") }, this._pageViewPaneRef.value?.refreshLayout(), this._readingPaneRef.value?.setVisible(this._isPaneVisible("reading")), this.requestUpdate(); + } + _setUserPaneVisible(e, t) { + this.panes = t ? [...this.panes.filter((t) => t !== e), e] : this.panes.filter((t) => t !== e), e === "file" && (this._filePaneUserToggled = !0), e === "page" && this._syncPagePaneControls(), this._syncToolbarPaneCheckboxes(), this._saveLayoutPrefs(), this._applyPaneLayout(); + } + _syncPagePaneControls() { + this._pageViewPaneRef.value?.setVisible(this._isPaneVisible("page")); + } + _loadLayoutPrefs() { + try { + let e = localStorage.getItem(Zi); + if (!e) return; + let t = JSON.parse(e); + if (t?.visible && typeof t.visible == "object") { + let e = []; + for (let n of Q) t.visible[n] === !0 && e.push(n); + e.length && (this.panes = e), typeof t.visible.file == "boolean" && (this._filePaneUserToggled = !0); + } + if (Array.isArray(t?.ratios)) { + let e = t.ratios.every((e) => typeof e == "number" && e > 0); + e && t.ratios.length === 4 ? (this._paneRatios = [...t.ratios], this._normalizePaneRatios()) : e && t.ratios.length === 3 && (this._paneRatios = [1, ...t.ratios], this._normalizePaneRatios()); + } + typeof t?.filePaneWidthPx == "number" && t.filePaneWidthPx > 0 && (this._filePaneWidthPx = t.filePaneWidthPx); + } catch {} + } + _saveLayoutPrefs() { + try { + localStorage.setItem(Zi, JSON.stringify({ + visible: Object.fromEntries(Q.map((e) => [e, this.panes.includes(e)])), + ratios: this._paneRatios, + filePaneWidthPx: this._filePaneWidthPx + })); + } catch {} + } + _resetPaneLayout() { + this._filePaneUserToggled = !1, this.panes = this._collection.hasMultiple() ? [ + "file", + "page", + "markup", + "reading" + ] : [ + "page", + "markup", + "reading" + ], this._paneRatios = [...$i], this._normalizePaneRatios(), this._filePaneWidthPx = null, this._syncPagePaneControls(), this._syncToolbarPaneCheckboxes(), this._saveLayoutPrefs(), this._applyPaneLayout(); + } + _contentPaneAvailableWidthPx() { + let e = this._mainRef.value; + if (!e) return 1; + let t = e.getBoundingClientRect(), n = this._visiblePaneKeys(), r = 0; + n.includes("file") && (r += this._resolvedFilePaneWidthPx()); + for (let e = 0; e < n.length - 1; e++) this._shouldShowSplitter(n[e], n[e + 1]) && (r += 1); + return Math.max(t.width - r, 1); + } + _resolvedPhysicalSplitterKeys(e) { + let t = Q[e], n = Q[e + 1]; + if (!t || !n) return null; + let r = this._isPaneVisible(t) ? t : this._visibleNeighborBefore(n), i = this._isPaneVisible(n) ? n : this._visibleNeighborAfter(t); + return !r || !i || r === i || !this._shouldShowSplitter(r, i) || Q.indexOf(r) !== e ? null : { + leftKey: r, + rightKey: i + }; + } + _startPaneDrag(e, t) { + if (e.button !== 0 || !this._loaded) return; + let n = this._resolvedPhysicalSplitterKeys(t); + if (!n) return; + let { leftKey: r, rightKey: i } = n; + this._normalizePaneRatios(); + let a = this._paneRatioIndex(r), o = this._paneRatioIndex(i), s = { + physicalSplitterIndex: t, + leftKey: r, + rightKey: i, + startX: e.clientX, + leftStart: this._paneRatios[a], + rightStart: this._paneRatios[o], + pointerId: e.pointerId + }; + r === "file" ? s.leftStartPx = this._resolvedFilePaneWidthPx() : i === "file" && (s.rightStartPx = this._resolvedFilePaneWidthPx()), this._paneDrag = s, e.preventDefault(), e.currentTarget.setPointerCapture(e.pointerId), e.currentTarget.classList.add("is-dragging"), this._paneDragActive = !0, this.requestUpdate(); + } + _findElementIdOnPage(e) { + if (!this._docState?.elementIds) return null; + for (let [t, n] of this._docState.elementIds) if (t === e) return n; + return null; + } + _navigateThreadFragment(e, t) { + let n = this._docState, r = n?.idToElement?.get(e); + if (!r) return; + let i = n?.threadNavByElement?.get(r); + if (!i) return; + let a = t === "prev" ? i.prev : i.next; + if (!a) return; + let o = n.elementPageByEl.get(a); + if (o) { + if (o === this.page) { + let e = this._findElementIdOnPage(a); + e && this._selectElement(e); + return; + } + n.pendingSelectElement = a, this.page = o; + } + } + _findListVirtualTextHost(e, t) { + let n = [...e.childNodes], r = xt(n, 0); + for (; r < n.length;) { + let e = n[r]; + if (e.nodeType !== Node.ELEMENT_NODE || L(e) !== "ldiv") { + r++; + continue; + } + let i = e; + r++; + let a = St(n, r); + if (t === i || n.slice(r, a).some((e) => Et(t, e))) return i; + r = a; + } + return null; + } + _findTableVirtualTextHost(e, t) { + let n = [...e.childNodes], r = xt(n, 0); + for (; r < n.length;) { + let e = n[r]; + if (e.nodeType !== Node.ELEMENT_NODE) { + r++; + continue; + } + let i = L(e); + if (i === "nl" || It(e) || _t.has(i) || !B(i)) { + r++; + continue; + } + let a = e; + r++; + let o = Ct(n, r); + if (t === a || n.slice(r, o).some((e) => Et(t, e))) return a; + r = o; + } + return null; + } + _findVirtualTextHost(e) { + let t = e; + for (; t;) { + let n = t.parentElement; + if (!n) return null; + let r = L(n); + if (r === "list") { + let t = this._findListVirtualTextHost(n, e); + if (t) return t; + } + if (N.has(r)) { + let t = this._findTableVirtualTextHost(n, e); + if (t) return t; + } + t = n; + } + return null; + } + _resolveSelectionElement(e) { + if (!e) return null; + if (Ft(e) || It(e)) return e; + let t = e.parentElement; + for (; t && L(t) !== "doclang";) { + if (Ft(t) && !bt(t)) return t; + t = t.parentElement; + } + let n = this._findVirtualTextHost(e); + if (n) return n; + for (t = e.parentElement; t && L(t) !== "doclang";) { + if (Ft(t) || It(t)) return t; + t = t.parentElement; + } + return null; + } + _resolveSelectionElementId(e) { + let t = this._docState; + if (!e || !t?.idToElement || !t.elementIds) return null; + let n = t.idToElement.get(e); + if (!n) return null; + let r = this._resolveSelectionElement(n); + return r ? t.elementIds.get(r) ?? null : null; + } + _selectElement(e) { + e && (this.selected = e); + } + _clearSelection() { + this.selected = null; + } + _syncToolbarPaneCheckboxes() { + this._toolbarRef.value?.syncPaneToggles({ + file: this.panes.includes("file"), + page: this.panes.includes("page"), + markup: this.panes.includes("markup"), + reading: this.panes.includes("reading"), + fileAvailable: this._isPaneAvailable("file"), + pageAvailable: this._isPaneAvailable("page"), + hasState: !!this._docState + }); + } + _setDocumentOpen(e, { markupOnly: t = !1 } = {}) { + this._loaded = e, this._markupOnly = e && t, this._syncToolbarPaneCheckboxes(), this._applyPaneLayout(), this.requestUpdate(); + } + _setPageViewVisible() { + this._syncPagePaneControls(), this._syncToolbarPaneCheckboxes(), this._applyPaneLayout(); + } + _resetViewer() { + this._setDemoLoading(!1), this._collection.clearAll(), this._filePaneUserToggled = !1, this._pageViewPaneRef.value?.resetZoom(), this._docLabel = null, this._setDocumentOpen(!1), this.document = null, this.selected = null, this._syncCollectionPaneDefault(), this._syncToolbarPaneCheckboxes(), this._applyPaneLayout(), this.requestUpdate(); + } + _activateDocument(e, t = 1) { + let n = this._collection.activeEntry; + n && (this._pageViewPaneRef.value?.activateZoom(n.pageZoom ?? 100), this._docLabel = n.label, this.selected = null, this.document = e, this._setDocumentOpen(!0, { markupOnly: e.markupOnly }), this._setPageViewVisible(), this.page = t, this._syncCollectionPaneDefault(), this._syncToolbarPaneCheckboxes(), this._applyPaneLayout(), this.requestUpdate()); + } + _syncCollectionPaneDefault() { + if (!this._filePaneUserToggled) { + let e = this.panes.includes("file"), t = this._collection.hasMultiple(); + this.panes = t ? [...this.panes.filter((e) => e !== "file"), "file"] : this.panes.filter((e) => e !== "file"), !e && t && (this._paneRatios = [...$i], this._normalizePaneRatios(), this._filePaneWidthPx = null); + } + } + async _addFilesToCatalog(e, { replace: t = !1 } = {}) { + t && (this._filePaneUserToggled = !1); + let n = await this._collection.addFiles(e, { replace: t }); + if (!n) return; + let r = this._collection.activeEntry; + this._activateDocument(n, r.currentPage ?? 1); + } + async _appendFolderArchive(e) { + let t = await this._collection.appendFolderArchive(e); + if (!t) return; + let n = this._collection.activeEntry; + this._activateDocument(t, n.currentPage ?? 1); + } + async _addArchiveBufferToCatalog(e, t, { replace: n = !1 } = {}) { + n && (this._filePaneUserToggled = !1); + let r = await this._collection.addArchiveBuffer(e, t, { replace: n }); + if (!r) return; + let i = this._collection.activeEntry; + this._activateDocument(r, i.currentPage ?? 1); + } + _setDemoLoading(e) { + this._demoLoading = e, this._emptyStateRef.value?.setDemoLoading(e), this._toolbarRef.value?.setDemoLoading(e), this.requestUpdate(); + } + async _loadDemo() { + if (!this._demoLoadInProgress) { + this._demoLoadInProgress = !0, this._setDemoLoading(!0); + try { + let e = this.example; + if (!e) throw Error("example URL not defined"); + let t = await fetch(e); + if (!t.ok) throw Error(`HTTP ${t.status}`); + let n = e.split("/").pop() || "demo.dclx"; + await this._addArchiveBufferToCatalog(await t.arrayBuffer(), n, { replace: !0 }); + } catch (e) { + alert(`Failed to load demo: ${e.message}\n\nServe this directory over HTTP (e.g. python3 -m http.server) and open the viewer from localhost.`); + } finally { + this._demoLoadInProgress = !1, this._setDemoLoading(!1); + } + } + } + _hasArchiveTransfer(e) { + return !!(e && [...e.types].includes("Files")); + } + _initDragDrop() { + this.addEventListener("dragenter", (e) => { + this._hasArchiveTransfer(e.dataTransfer) && (e.preventDefault(), this._dragOver = !0, this.requestUpdate()); + }), this.addEventListener("dragover", (e) => { + this._hasArchiveTransfer(e.dataTransfer) && (e.preventDefault(), e.dataTransfer && (e.dataTransfer.dropEffect = "copy")); + }), this.addEventListener("dragleave", (e) => { + this._hasArchiveTransfer(e.dataTransfer) && (e.relatedTarget && this.contains(e.relatedTarget) || (this._dragOver = !1, this.requestUpdate())); + }), this.addEventListener("drop", async (e) => { + this._hasArchiveTransfer(e.dataTransfer) && (e.preventDefault(), this._dragOver = !1, this.requestUpdate(), e.dataTransfer && await this._loadFromDrop(e.dataTransfer)); + }); + } + async _loadFromFileList(e) { + if (e.some((e) => e.name === "document.xml")) { + await this._appendFolderArchive(e); + return; + } + let t = e.filter((e) => this._collection.isArchiveFile(e) || this._collection.isMarkupFile(e)); + t.length && await this._addFilesToCatalog(t, { replace: !1 }); + } + async _loadFromDrop(e) { + await this._loadFromFileList([...e.files]); + } + _initFileHandling() { + let e = window; + !("launchQueue" in window) || !e.launchQueue || e.launchQueue.setConsumer(async (e) => { + if (!e.files?.length) return; + this._suppressDemoLoad = !0; + let t = await Promise.all(e.files.map((e) => e.getFile())); + await this._loadFromFileList(t); + }); + } + _initPaneDragListeners() { + window.addEventListener("pointermove", this._onWindowPointerMove), window.addEventListener("pointerup", this._onWindowPointerUp), window.addEventListener("pointercancel", this._onWindowPointerUp); + } +}; +H([D({ type: String })], $.prototype, "example", void 0), H([O()], $.prototype, "_loaded", void 0), H([O()], $.prototype, "_markupOnly", void 0), H([O()], $.prototype, "_dragOver", void 0), H([O()], $.prototype, "_paneDragActive", void 0), H([O()], $.prototype, "_demoLoading", void 0), H([O()], $.prototype, "_docLabel", void 0), H([O()], $.prototype, "_mainGridStyle", void 0), H([O()], $.prototype, "_paneGridCols", void 0), H([O()], $.prototype, "_splitterCols", void 0), H([D({ + type: Array, + attribute: "panes", + reflect: !0 +})], $.prototype, "panes", void 0), $ = H([E("doclang-viewer")], $); +//#endregion +export { Yi as CollectionController, Yn as DoclangCollectionPane, Un as DoclangDropdown, Ui as DoclangEmpty, lr as DoclangMarkupPane, U as DoclangPageElement, Yr as DoclangPageImgPane, Bn as DoclangPageNav, Wi as DoclangPaneStack, Z as DoclangReadingPane, fr as DoclangSettingsPanel, Wn as DoclangToolbar, $ as DoclangViewer, $n as PageController }; + +//# sourceMappingURL=viewer.js.map \ No newline at end of file diff --git a/viewer.js.map b/viewer.js.map new file mode 100644 index 0000000..4bd022c --- /dev/null +++ b/viewer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"viewer.js","names":["t","e","s","o","n","r","S","c","i","e","h","r","o","n","a","c","l","p","d","u","f","b","y","t","s","t","i","s","e","h","o","n","r","l","c","u","d","f","v","m","p","M","j","s","i","t","e","r","o","t","o","e","t","r","n","r","t","t","o","i","n","e","c","f","r","s","u","h","e","i","o","c","h","n","e","t","i","e","o","n","s","i","t","s","i","t","r","s","t","r","l","p","v","o","i","n","f","e","e","r","t","s"],"sources":["core/node_modules/@lit/reactive-element/css-tag.js","core/node_modules/@lit/reactive-element/reactive-element.js","core/node_modules/lit-html/lit-html.js","core/node_modules/lit-element/lit-element.js","core/node_modules/@lit/reactive-element/decorators/custom-element.js","core/node_modules/@lit/reactive-element/decorators/property.js","core/node_modules/@lit/reactive-element/decorators/state.js","core/node_modules/lit-html/directive-helpers.js","core/node_modules/lit-html/directive.js","core/node_modules/lit-html/async-directive.js","core/node_modules/lit-html/directives/ref.js","core/src/components/page-nav/page-nav.css?inline","core/src/doclang/dom.ts","core/src/doclang/zip.ts","core/src/doclang/document.ts","core/src/components/base/page-element.ts","core/src/components/page-nav/page-nav.ts","core/node_modules/lit-html/directives/class-map.js","core/src/components/viewer/toolbar.css?inline","core/src/components/dropdown/dropdown.css?inline","core/src/components/dropdown/dropdown.ts","core/src/components/viewer/toolbar.ts","core/node_modules/lit-html/directives/repeat.js","core/src/components/collection/collection-pane.css?inline","core/src/components/collection/collection-pane.ts","core/src/components/base/page-controller.ts","core/src/components/markup-pane/markup-pane.css?inline","core/src/components/markup-pane/markup-pane.ts","core/src/components/page-img-pane/page-img-pane.css?inline","core/src/components/settings-panel/settings-panel.css?inline","core/src/components/settings-panel/settings-panel.ts","core/src/components/page-img-pane/overlay.ts","core/src/components/page-img-pane/page-img-pane.ts","core/src/components/reading-pane/reading-pane.css?inline","core/src/components/reading-pane/reading-pane.ts","core/src/components/viewer/empty.css?inline","core/src/components/viewer/empty.ts","core/src/components/container/pane-stack.ts","core/node_modules/lit-html/directives/style-map.js","core/src/components/viewer/viewer.css?inline","core/src/components/collection/collection.ts","core/src/components/viewer/viewer.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2019 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&\"adoptedStyleSheets\"in Document.prototype&&\"replace\"in CSSStyleSheet.prototype,s=Symbol(),o=new WeakMap;class n{constructor(t,e,o){if(this._$cssResult$=!0,o!==s)throw Error(\"CSSResult is not constructable. Use `unsafeCSS` or `css` instead.\");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const s=this.t;if(e&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=o.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&o.set(s,t))}return t}toString(){return this.cssText}}const r=t=>new n(\"string\"==typeof t?t:t+\"\",void 0,s),i=(t,...e)=>{const o=1===t.length?t[0]:e.reduce((e,s,o)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if(\"number\"==typeof t)return t;throw Error(\"Value passed to 'css' function must be a 'css' function result: \"+t+\". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.\")})(s)+t[o+1],t[0]);return new n(o,t,s)},S=(s,o)=>{if(e)s.adoptedStyleSheets=o.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of o){const o=document.createElement(\"style\"),n=t.litNonce;void 0!==n&&o.setAttribute(\"nonce\",n),o.textContent=e.cssText,s.appendChild(o)}},c=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e=\"\";for(const s of t.cssRules)e+=s.cssText;return r(e)})(t):t;export{n as CSSResult,S as adoptStyles,i as css,c as getCompatibleStyle,e as supportsAdoptingStyleSheets,r as unsafeCSS};\n//# sourceMappingURL=css-tag.js.map\n","import{getCompatibleStyle as t,adoptStyles as s}from\"./css-tag.js\";export{CSSResult,css,supportsAdoptingStyleSheets,unsafeCSS}from\"./css-tag.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{is:i,defineProperty:e,getOwnPropertyDescriptor:h,getOwnPropertyNames:r,getOwnPropertySymbols:o,getPrototypeOf:n}=Object,a=globalThis,c=a.trustedTypes,l=c?c.emptyScript:\"\",p=a.reactiveElementPolyfillSupport,d=(t,s)=>t,u={toAttribute(t,s){switch(s){case Boolean:t=t?l:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,s){let i=t;switch(s){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},f=(t,s)=>!i(t,s),b={attribute:!0,type:String,converter:u,reflect:!1,useDefault:!1,hasChanged:f};Symbol.metadata??=Symbol(\"metadata\"),a.litPropertyMetadata??=new WeakMap;class y extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,s=b){if(s.state&&(s.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((s=Object.create(s)).wrapped=!0),this.elementProperties.set(t,s),!s.noAccessor){const i=Symbol(),h=this.getPropertyDescriptor(t,i,s);void 0!==h&&e(this.prototype,t,h)}}static getPropertyDescriptor(t,s,i){const{get:e,set:r}=h(this.prototype,t)??{get(){return this[s]},set(t){this[s]=t}};return{get:e,set(s){const h=e?.call(this);r?.call(this,s),this.requestUpdate(t,h,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(d(\"elementProperties\")))return;const t=n(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(d(\"finalized\")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(d(\"properties\"))){const t=this.properties,s=[...r(t),...o(t)];for(const i of s)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const s=litPropertyMetadata.get(t);if(void 0!==s)for(const[t,i]of s)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,s]of this.elementProperties){const i=this._$Eu(t,s);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){const i=[];if(Array.isArray(s)){const e=new Set(s.flat(1/0).reverse());for(const s of e)i.unshift(t(s))}else void 0!==s&&i.push(t(s));return i}static _$Eu(t,s){const i=s.attribute;return!1===i?void 0:\"string\"==typeof i?i:\"string\"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,s=this.constructor.elementProperties;for(const i of s.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return s(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,s,i){this._$AK(t,i)}_$ET(t,s){const i=this.constructor.elementProperties.get(t),e=this.constructor._$Eu(t,i);if(void 0!==e&&!0===i.reflect){const h=(void 0!==i.converter?.toAttribute?i.converter:u).toAttribute(s,i.type);this._$Em=t,null==h?this.removeAttribute(e):this.setAttribute(e,h),this._$Em=null}}_$AK(t,s){const i=this.constructor,e=i._$Eh.get(t);if(void 0!==e&&this._$Em!==e){const t=i.getPropertyOptions(e),h=\"function\"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:u;this._$Em=e;const r=h.fromAttribute(s,t.type);this[e]=r??this._$Ej?.get(e)??r,this._$Em=null}}requestUpdate(t,s,i,e=!1,h){if(void 0!==t){const r=this.constructor;if(!1===e&&(h=this[t]),i??=r.getPropertyOptions(t),!((i.hasChanged??f)(h,s)||i.useDefault&&i.reflect&&h===this._$Ej?.get(t)&&!this.hasAttribute(r._$Eu(t,i))))return;this.C(t,s,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,s,{useDefault:i,reflect:e,wrapped:h},r){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,r??s??this[t]),!0!==h||void 0!==r)||(this._$AL.has(t)||(this.hasUpdated||i||(s=void 0),this._$AL.set(t,s)),!0===e&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,s]of this._$Ep)this[t]=s;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[s,i]of t){const{wrapped:t}=i,e=this[s];!0!==t||this._$AL.has(s)||void 0===e||this.C(s,void 0,i,e)}}let t=!1;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(s)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(s)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}}y.elementStyles=[],y.shadowRootOptions={mode:\"open\"},y[d(\"elementProperties\")]=new Map,y[d(\"finalized\")]=new Map,p?.({ReactiveElement:y}),(a.reactiveElementVersions??=[]).push(\"2.1.2\");export{y as ReactiveElement,s as adoptStyles,u as defaultConverter,t as getCompatibleStyle,f as notEqual};\n//# sourceMappingURL=reactive-element.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=globalThis,i=t=>t,s=t.trustedTypes,e=s?s.createPolicy(\"lit-html\",{createHTML:t=>t}):void 0,h=\"$lit$\",o=`lit$${Math.random().toFixed(9).slice(2)}$`,n=\"?\"+o,r=`<${n}>`,l=document,c=()=>l.createComment(\"\"),a=t=>null===t||\"object\"!=typeof t&&\"function\"!=typeof t,u=Array.isArray,d=t=>u(t)||\"function\"==typeof t?.[Symbol.iterator],f=\"[ \\t\\n\\f\\r]\",v=/<(?:(!--|\\/[^a-zA-Z])|(\\/?[a-zA-Z][^>\\s]*)|(\\/?$))/g,_=/-->/g,m=/>/g,p=RegExp(`>|${f}(?:([^\\\\s\"'>=/]+)(${f}*=${f}*(?:[^ \\t\\n\\f\\r\"'\\`<>=]|(\"|')|))|$)`,\"g\"),g=/'/g,$=/\"/g,y=/^(?:script|style|textarea|title)$/i,x=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),b=x(1),w=x(2),T=x(3),E=Symbol.for(\"lit-noChange\"),A=Symbol.for(\"lit-nothing\"),C=new WeakMap,P=l.createTreeWalker(l,129);function V(t,i){if(!u(t)||!t.hasOwnProperty(\"raw\"))throw Error(\"invalid template strings array\");return void 0!==e?e.createHTML(i):i}const N=(t,i)=>{const s=t.length-1,e=[];let n,l=2===i?\"\":3===i?\"\":\"\",c=v;for(let i=0;i\"===u[0]?(c=n??v,d=-1):void 0===u[1]?d=-2:(d=c.lastIndex-u[2].length,a=u[1],c=void 0===u[3]?p:'\"'===u[3]?$:g):c===$||c===g?c=p:c===_||c===m?c=v:(c=p,n=void 0);const x=c===p&&t[i+1].startsWith(\"/>\")?\" \":\"\";l+=c===v?s+r:d>=0?(e.push(a),s.slice(0,d)+h+s.slice(d)+o+x):s+o+(-2===d?i:x)}return[V(t,l+(t[s]||\"\")+(2===i?\"\":3===i?\"\":\"\")),e]};class S{constructor({strings:t,_$litType$:i},e){let r;this.parts=[];let l=0,a=0;const u=t.length-1,d=this.parts,[f,v]=N(t,i);if(this.el=S.createElement(f,e),P.currentNode=this.el.content,2===i||3===i){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(r=P.nextNode())&&d.length0){r.textContent=s?s.emptyScript:\"\";for(let s=0;s2||\"\"!==s[0]||\"\"!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=A}_$AI(t,i=this,s,e){const h=this.strings;let o=!1;if(void 0===h)t=M(this,t,i,0),o=!a(t)||t!==this._$AH&&t!==E,o&&(this._$AH=t);else{const e=t;let n,r;for(t=h[0],n=0;n{const e=s?.renderBefore??i;let h=e._$litPart$;if(void 0===h){const t=s?.renderBefore??null;e._$litPart$=h=new k(i.insertBefore(c(),t),t,void 0,s??{})}return h._$AI(t),h};export{j as _$LH,b as html,T as mathml,E as noChange,A as nothing,D as render,w as svg};\n//# sourceMappingURL=lit-html.js.map\n","import{ReactiveElement as t}from\"@lit/reactive-element\";export*from\"@lit/reactive-element\";import{render as e,noChange as r}from\"lit-html\";export*from\"lit-html\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const s=globalThis;class i extends t{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const r=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=e(r,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return r}}i._$litElement$=!0,i[\"finalized\"]=!0,s.litElementHydrateSupport?.({LitElement:i});const o=s.litElementPolyfillSupport;o?.({LitElement:i});const n={_$AK:(t,e,r)=>{t._$AK(e,r)},_$AL:t=>t._$AL};(s.litElementVersions??=[]).push(\"4.2.2\");export{i as LitElement,n as _$LE};\n//# sourceMappingURL=lit-element.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=t=>(e,o)=>{void 0!==o?o.addInitializer(()=>{customElements.define(t,e)}):customElements.define(t,e)};export{t as customElement};\n//# sourceMappingURL=custom-element.js.map\n","import{notEqual as t,defaultConverter as e}from\"../reactive-element.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const o={attribute:!0,type:String,converter:e,reflect:!1,hasChanged:t},r=(t=o,e,r)=>{const{kind:n,metadata:i}=r;let s=globalThis.litPropertyMetadata.get(i);if(void 0===s&&globalThis.litPropertyMetadata.set(i,s=new Map),\"setter\"===n&&((t=Object.create(t)).wrapped=!0),s.set(r.name,t),\"accessor\"===n){const{name:o}=r;return{set(r){const n=e.get.call(this);e.set.call(this,r),this.requestUpdate(o,n,t,!0,r)},init(e){return void 0!==e&&this.C(o,void 0,t,e),e}}}if(\"setter\"===n){const{name:o}=r;return function(r){const n=this[o];e.call(this,r),this.requestUpdate(o,n,t,!0,r)}}throw Error(\"Unsupported decorator location: \"+n)};function n(t){return(e,o)=>\"object\"==typeof o?r(t,e,o):((t,e,o)=>{const r=e.hasOwnProperty(o);return e.constructor.createProperty(o,t),r?Object.getOwnPropertyDescriptor(e,o):void 0})(t,e,o)}export{n as property,r as standardProperty};\n//# sourceMappingURL=property.js.map\n","import{property as t}from\"./property.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */function r(r){return t({...r,state:!0,attribute:!1})}export{r as state};\n//# sourceMappingURL=state.js.map\n","import{_$LH as o}from\"./lit-html.js\";\n/**\n * @license\n * Copyright 2020 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{I:t}=o,i=o=>o,n=o=>null===o||\"object\"!=typeof o&&\"function\"!=typeof o,e={HTML:1,SVG:2,MATHML:3},l=(o,t)=>void 0===t?void 0!==o?._$litType$:o?._$litType$===t,d=o=>null!=o?._$litType$?.h,c=o=>void 0!==o?._$litDirective$,f=o=>o?._$litDirective$,r=o=>void 0===o.strings,s=()=>document.createComment(\"\"),v=(o,n,e)=>{const l=o._$AA.parentNode,d=void 0===n?o._$AB:n._$AA;if(void 0===e){const i=l.insertBefore(s(),d),n=l.insertBefore(s(),d);e=new t(i,n,o,o.options)}else{const t=e._$AB.nextSibling,n=e._$AM,c=n!==o;if(c){let t;e._$AQ?.(o),e._$AM=o,void 0!==e._$AP&&(t=o._$AU)!==n._$AU&&e._$AP(t)}if(t!==d||c){let o=e._$AA;for(;o!==t;){const t=i(o).nextSibling;i(l).insertBefore(o,d),o=t}}}return e},u=(o,t,i=o)=>(o._$AI(t,i),o),m={},p=(o,t=m)=>o._$AH=t,M=o=>o._$AH,h=o=>{o._$AR(),o._$AA.remove()},j=o=>{o._$AR()};export{e as TemplateResultType,j as clearPart,M as getCommittedValue,f as getDirectiveClass,v as insertPart,d as isCompiledTemplateResult,c as isDirectiveResult,n as isPrimitive,r as isSingleExpression,l as isTemplateResult,h as removePart,u as setChildPartValue,p as setCommittedValue};\n//# sourceMappingURL=directive-helpers.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},e=t=>(...e)=>({_$litDirective$:t,values:e});class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}export{i as Directive,t as PartType,e as directive};\n//# sourceMappingURL=directive.js.map\n","import{isSingleExpression as i}from\"./directive-helpers.js\";import{Directive as t,PartType as e}from\"./directive.js\";export{directive}from\"./directive.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const s=(i,t)=>{const e=i._$AN;if(void 0===e)return!1;for(const i of e)i._$AO?.(t,!1),s(i,t);return!0},o=i=>{let t,e;do{if(void 0===(t=i._$AM))break;e=t._$AN,e.delete(i),i=t}while(0===e?.size)},r=i=>{for(let t;t=i._$AM;i=t){let e=t._$AN;if(void 0===e)t._$AN=e=new Set;else if(e.has(i))break;e.add(i),c(t)}};function h(i){void 0!==this._$AN?(o(this),this._$AM=i,r(this)):this._$AM=i}function n(i,t=!1,e=0){const r=this._$AH,h=this._$AN;if(void 0!==h&&0!==h.size)if(t)if(Array.isArray(r))for(let i=e;i{i.type==e.CHILD&&(i._$AP??=n,i._$AQ??=h)};class f extends t{constructor(){super(...arguments),this._$AN=void 0}_$AT(i,t,e){super._$AT(i,t,e),r(this),this.isConnected=i._$AU}_$AO(i,t=!0){i!==this.isConnected&&(this.isConnected=i,i?this.reconnected?.():this.disconnected?.()),t&&(s(this,i),o(this))}setValue(t){if(i(this._$Ct))this._$Ct._$AI(t,this);else{const i=[...this._$Ct._$AH];i[this._$Ci]=t,this._$Ct._$AI(i,this,0)}}disconnected(){}reconnected(){}}export{f as AsyncDirective,t as Directive,e as PartType};\n//# sourceMappingURL=async-directive.js.map\n","import{nothing as t}from\"../lit-html.js\";import{AsyncDirective as i}from\"../async-directive.js\";import{directive as s}from\"../directive.js\";\n/**\n * @license\n * Copyright 2020 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const e=()=>new h;class h{}const o=new WeakMap,n=s(class extends i{render(i){return t}update(i,[s]){const e=s!==this.G;return e&&this.rt(void 0),(e||this.lt!==this.ct)&&(this.G=s,this.ht=i.options?.host,this.rt(this.ct=i.element)),t}rt(t){if(void 0!==this.G)if(this.isConnected||(t=void 0),\"function\"==typeof this.G){const i=this.ht??globalThis;let s=o.get(i);void 0===s&&(s=new WeakMap,o.set(i,s)),void 0!==s.get(this.G)&&this.G.call(this.ht,void 0),s.set(this.G,t),void 0!==t&&this.G.call(this.ht,t)}else this.G.value=t}get lt(){return\"function\"==typeof this.G?o.get(this.ht??globalThis)?.get(this.G):this.G?.value}disconnected(){this.lt===this.ct&&this.rt(void 0)}reconnected(){this.rt(this.ct)}});export{e as createRef,n as ref};\n//# sourceMappingURL=ref.js.map\n","@import '../theme.css';\n\n:host {\n display: contents;\n}\nnav {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n margin-left: 0.25rem;\n}\nnav::before {\n content: '';\n flex-shrink: 0;\n align-self: center;\n width: 1px;\n height: 1.125rem;\n margin-right: 0.75rem;\n background: var(--border);\n}\nnav[hidden] {\n display: none;\n}\n.page-nav-btns {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n}\n.page-nav-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.875rem;\n height: 1.875rem;\n padding: 0;\n flex-shrink: 0;\n appearance: none;\n border: 1px solid var(--border);\n background: var(--panel);\n color: var(--text);\n border-radius: 0.375rem;\n font: inherit;\n font-size: 0.875rem;\n cursor: pointer;\n}\n.page-nav-btn:hover {\n border-color: var(--accent);\n}\n.page-nav-btn:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.page-nav-chevron {\n display: block;\n width: 0.875rem;\n height: 0.875rem;\n}\n.page-indicator {\n display: inline-flex;\n align-items: center;\n font-size: 0.875rem;\n color: var(--muted);\n font-variant-numeric: tabular-nums;\n}\n.page-number-input {\n appearance: none;\n border: 1px solid var(--border);\n border-radius: 0.375rem;\n background: var(--panel);\n color: var(--text);\n font: inherit;\n font-size: 0.875rem;\n font-variant-numeric: tabular-nums;\n text-align: center;\n padding: 0 0.35rem;\n margin: 0;\n height: 1.875rem;\n box-sizing: border-box;\n width: calc(var(--doclang-page-num-digits, 1) * 1ch + 0.75rem);\n line-height: 1;\n cursor: text;\n}\n.page-number-input:hover:not(:disabled) {\n border-color: var(--accent);\n}\n.page-number-input:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent);\n}\n.page-number-input:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n","\nexport const DOCLANG_NS = 'https://www.doclang.ai/ns/v0';\nexport const HEAD_TAGS = new Set([\n 'label',\n 'thread',\n 'xref',\n 'href',\n 'layer',\n 'location',\n 'caption',\n 'description',\n 'summary',\n 'custom',\n]);\nexport const SEMANTIC_TAGS = new Set([\n 'text',\n 'heading',\n 'footnote',\n 'page_header',\n 'page_footer',\n 'field_region',\n 'list',\n 'table',\n 'index',\n 'formula',\n 'code',\n 'picture',\n 'marker',\n 'group',\n 'field_heading',\n 'field_item',\n 'key',\n 'value',\n 'hint',\n 'caption',\n 'page_break',\n]);\nexport const CELL_TOKENS = new Set([\n 'fcel',\n 'ecel',\n 'ched',\n 'rhed',\n 'corn',\n 'srow',\n 'lcel',\n 'ucel',\n 'xcel',\n 'nl',\n]);\nexport const CELL_CONTENT_TAGS = new Set([\n 'fcel',\n 'ecel',\n 'ched',\n 'rhed',\n 'corn',\n 'srow',\n]);\nexport const CELL_SPAN_TAGS = new Set(['lcel', 'ucel', 'xcel']);\nexport const OTSL_CONTAINER_TAGS = new Set(['table', 'index', 'tabular']);\n\nexport function isTextLikeNode(node: Node): boolean {\n return node.nodeType === Node.TEXT_NODE || node.nodeType === Node.CDATA_SECTION_NODE;\n}\n\nexport function isWhitespaceOnlyText(node: Node): boolean {\n return isTextLikeNode(node) && !node.textContent?.trim();\n}\n\nexport function markupAttributes(el: Element): { name: string; value: string }[] {\n return [...el.attributes]\n .filter(a => a.name !== 'xmlns' || a.value !== DOCLANG_NS)\n .map(a => ({ name: a.name, value: a.value }));\n}\n\nexport function childElements(el: Element): Element[] {\n return [...el.children];\n}\n\nexport function localName(el: Element): string {\n return el.localName || el.tagName.replace(/^.*:/, '');\n}\n\nexport function headLocations(el: Element): Element[] {\n return parseElementHeadAt([...el.childNodes], 0)?.locs ?? [];\n}\n\ninterface ParsedElementHead {\n locs: Element[];\n nextIndex: number;\n}\n\nexport function parseElementHeadAt(\n nodes: ArrayLike,\n startIdx: number\n): ParsedElementHead | null {\n const locs: Element[] = [];\n let i = startIdx;\n while (i < nodes.length) {\n const node = nodes[i];\n if (!node) {\n i += 1;\n continue;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) {\n i += 1;\n continue;\n }\n const tag = localName(node as Element);\n if (tag === 'location') {\n locs.push(node as Element);\n i += 1;\n if (locs.length === 4) return { locs, nextIndex: i };\n continue;\n }\n if (locs.length) break;\n if (HEAD_TAGS.has(tag)) {\n i += 1;\n continue;\n }\n break;\n }\n return locs.length === 4 ? { locs, nextIndex: i } : null;\n}\n\nexport function walkElements(\n nodes: ArrayLike | Element[],\n fn: (el: Element) => void\n): void {\n for (let i = 0; i < nodes.length; i += 1) {\n const node = nodes[i];\n if (!node || node.nodeType !== Node.ELEMENT_NODE) continue;\n fn(node as Element);\n walkElements(childElements(node as Element), fn);\n }\n}\n\nexport function isCellToken(tag: string): boolean {\n return CELL_TOKENS.has(tag);\n}\n\nexport function isListOrOtslContainer(el: Element): boolean {\n const tag = localName(el);\n return tag === 'list' || OTSL_CONTAINER_TAGS.has(tag);\n}\n\nexport function skipContainerLevelHead(nodes: ChildNode[], startIdx: number): number {\n let i = startIdx;\n while (i < nodes.length) {\n const node = nodes[i];\n if (!node) {\n i += 1;\n continue;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) {\n i += 1;\n continue;\n }\n const tag = localName(node as Element);\n if (tag === 'ldiv' || isCellToken(tag)) break;\n if (HEAD_TAGS.has(tag) || tag === 'location') {\n i += 1;\n continue;\n }\n break;\n }\n return i;\n}\n\nexport function skipUntilListItemBoundary(\n nodes: ChildNode[],\n startIdx: number\n): number {\n let i = startIdx;\n while (i < nodes.length) {\n const node = nodes[i];\n if (\n node &&\n node.nodeType === Node.ELEMENT_NODE &&\n localName(node as Element) === 'ldiv'\n )\n break;\n i += 1;\n }\n return i;\n}\n\nexport function skipUntilCellBoundary(nodes: ChildNode[], startIdx: number): number {\n let i = startIdx;\n while (i < nodes.length) {\n const node = nodes[i];\n if (\n node &&\n node.nodeType === Node.ELEMENT_NODE &&\n isCellToken(localName(node as Element))\n )\n break;\n i += 1;\n }\n return i;\n}\n\nexport function locationResolution(el: Element, axisDefault: number): number {\n const r = parseInt(el.getAttribute('resolution') ?? String(axisDefault), 10);\n return Number.isFinite(r) && r > 0 ? r : axisDefault;\n}\n\nexport function headingLevel(el: Element): number {\n return Math.min(Math.max(parseInt(el.getAttribute('level') ?? '1', 10) || 1, 1), 6);\n}\n\nexport function firstHeadChild(el: Element, tag: string): Element | null {\n return childElements(el).find(child => localName(child) === tag) ?? null;\n}\n\nexport function xmlContains(target: Node, ancestor: Node): boolean {\n let node: Node | null = target;\n while (node) {\n if (node === ancestor) return true;\n node = node.parentNode;\n }\n return false;\n}\n\nexport function formatMarkupTextNode(node: Node): string {\n if (node.nodeType === Node.CDATA_SECTION_NODE) {\n return ``;\n }\n return node.textContent?.trim() ?? '';\n}\n\nexport function serializeMarkupTextNodes(nodes: ArrayLike): string {\n return Array.from(nodes)\n .filter(n => isTextLikeNode(n) && !isWhitespaceOnlyText(n))\n .map(formatMarkupTextNode)\n .join('');\n}\n\nexport function escapeHtml(text: string): string {\n return String(text)\n .replace(/&/g, '&')\n .replace(//g, '>');\n}\n\nexport function normalizeArchivePath(path: string): string {\n return path.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nexport function archiveRelativeAssetPath(path: string): string | null {\n const norm = normalizeArchivePath(path);\n const idx = norm.indexOf('assets/');\n return idx === -1 ? null : norm.slice(idx);\n}\n\nexport function mimeFromAssetPath(path: string): string {\n const ext = path.split('.').pop()?.toLowerCase() ?? '';\n switch (ext) {\n case 'png':\n return 'image/png';\n case 'jpg':\n case 'jpeg':\n return 'image/jpeg';\n case 'webp':\n return 'image/webp';\n case 'gif':\n return 'image/gif';\n case 'svg':\n return 'image/svg+xml';\n default:\n return 'application/octet-stream';\n }\n}\n\nexport function skipOtslContainerHead(nodes: ChildNode[], startIdx: number): number {\n let i = startIdx;\n while (i < nodes.length) {\n const node = nodes[i];\n if (!node) {\n i += 1;\n continue;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) {\n i += 1;\n continue;\n }\n const tag = localName(node as Element);\n if (isCellToken(tag)) break;\n if (HEAD_TAGS.has(tag) || tag === 'h_thread' || tag === 'location') {\n i += 1;\n continue;\n }\n break;\n }\n return i;\n}\n\nexport function skipElementHeadNodes(nodes: ChildNode[], startIdx: number): number {\n let i = startIdx;\n while (i < nodes.length && isWhitespaceOnlyText(nodes[i]!)) i += 1;\n while (i < nodes.length) {\n const node = nodes[i];\n if (\n node &&\n node.nodeType === Node.ELEMENT_NODE &&\n HEAD_TAGS.has(localName(node as Element))\n ) {\n i += 1;\n while (i < nodes.length && isWhitespaceOnlyText(nodes[i]!)) i += 1;\n continue;\n }\n break;\n }\n return i;\n}\n\nexport function isSemanticElement(el: Element): boolean {\n return SEMANTIC_TAGS.has(localName(el));\n}\n\nexport function isVirtualTextHost(el: Element): boolean {\n const tag = localName(el);\n return tag === 'ldiv' || CELL_CONTENT_TAGS.has(tag);\n}\n\nexport function elementThreadId(el: Element): string | null {\n return firstHeadChild(el, 'thread')?.getAttribute('thread_id') ?? null;\n}\n\nexport function elementLayer(el: Element): string {\n const layerEl = firstHeadChild(el, 'layer');\n if (layerEl) {\n const value = layerEl.getAttribute('value') ?? 'body';\n return ELEMENT_LAYERS.has(value) ? value : 'body';\n }\n if (headLocations(el).length === 4) return 'body';\n const parent = el.parentElement;\n if (!parent) return 'body';\n const nodes = [...parent.childNodes];\n const idx = nodes.indexOf(el);\n if (idx < 0) return 'body';\n return layerFromHeadNodes(nodes, idx + 1);\n}\n\nexport const ELEMENT_LAYERS = new Set(['body', 'background', 'furniture']);\n\nexport function layerFromHeadNodes(nodes: ChildNode[], startIdx: number): string {\n let i = startIdx;\n while (i < nodes.length) {\n const node = nodes[i];\n if (!node) {\n i += 1;\n continue;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) {\n i += 1;\n continue;\n }\n const tag = localName(node as Element);\n if (tag === 'layer') {\n const value = (node as Element).getAttribute('value') ?? 'body';\n return ELEMENT_LAYERS.has(value) ? value : 'body';\n }\n if (tag === 'location') break;\n if (HEAD_TAGS.has(tag)) {\n i += 1;\n continue;\n }\n break;\n }\n return 'body';\n}\n\nexport function layerClassForValue(layer: string): string {\n if (layer === 'furniture') return 'layer-furniture';\n if (layer === 'background') return 'layer-background';\n return '';\n}\n\nexport function headingLevelFromEl(el: Element): number {\n return headingLevel(el);\n}\n\nexport function elementLabel(el: Element): string {\n if (isVirtualTextOverlayUnit(el)) return 'text';\n const tag = localName(el);\n if (tag === 'heading' || tag === 'field_heading')\n return `${tag}[${headingLevel(el)}]`;\n const level = el.getAttribute('level');\n if (level) return `${tag}[${level}]`;\n const cls = el.getAttribute('class');\n if (cls) return `${tag}.${cls}`;\n return tag;\n}\n\nexport function isVirtualTextOverlayUnit(el: Element): boolean {\n if (headLocations(el).length === 4) return false;\n if (!hasVirtualTextLocations(el)) return false;\n const tag = localName(el);\n if (tag === 'ldiv' && el.parentElement && localName(el.parentElement) === 'list')\n return true;\n if (isCellToken(tag) && tag !== 'nl' && !CELL_SPAN_TAGS.has(tag)) {\n return el.parentElement\n ? OTSL_CONTAINER_TAGS.has(localName(el.parentElement))\n : false;\n }\n return false;\n}\n\nexport function hasVirtualTextLocations(el: Element): boolean {\n const parent = el.parentElement;\n if (!parent) return false;\n const nodes = [...parent.childNodes];\n const idx = nodes.indexOf(el);\n if (idx < 0) return false;\n return parseElementHeadAt(nodes, idx + 1) !== null;\n}\n\nexport function virtualTextHeadLocations(el: Element): Element[] {\n const parent = el.parentElement;\n if (!parent) return [];\n const nodes = [...parent.childNodes];\n const idx = nodes.indexOf(el);\n if (idx < 0) return [];\n return parseElementHeadAt(nodes, idx + 1)?.locs ?? [];\n}\n\nexport function elementHeadLocations(el: Element): Element[] {\n const own = headLocations(el);\n return own.length === 4 ? own : virtualTextHeadLocations(el);\n}\n\nexport function isPictureContentElement(el: Element | null): boolean {\n if (!el) return false;\n const tag = localName(el);\n if (tag === 'picture' || tag === 'caption') return false;\n let node: Element | null = el.parentElement;\n while (node) {\n if (localName(node) === 'picture') return true;\n node = node.parentElement;\n }\n return false;\n}\n\nexport function isTableContentElement(el: Element | null): boolean {\n if (!el) return false;\n const tag = localName(el);\n if (OTSL_CONTAINER_TAGS.has(tag) || tag === 'caption') return false;\n let node: Element | null = el.parentElement;\n while (node) {\n if (OTSL_CONTAINER_TAGS.has(localName(node))) return true;\n node = node.parentElement;\n }\n return false;\n}\n","import { mimeFromAssetPath } from './dom';\n\nconst ZIP_MAX_ENTRIES = 5000;\nconst ZIP_MAX_ENTRY_COMPRESSED_BYTES = 128 * 1024 * 1024; // 128 MiB\nconst ZIP_MAX_ENTRY_UNCOMPRESSED_BYTES = 128 * 1024 * 1024; // 128 MiB\nconst ZIP_MAX_TOTAL_UNCOMPRESSED_BYTES = 512 * 1024 * 1024; // 512 MiB\nconst ZIP_MAX_COMPRESSION_RATIO = 100; // uncompressed / compressed\n\nexport interface ZipEntry {\n name: string;\n data: Uint8Array;\n}\n\nconst TEXT_DECODER = new TextDecoder();\n\nexport async function unzip(\n buffer: ArrayBuffer,\n { shouldExtract }: { shouldExtract?: (name: string) => boolean } = {}\n): Promise {\n if (typeof DecompressionStream === 'undefined') {\n throw new Error(\n 'ZIP decompression requires a browser with DecompressionStream support'\n );\n }\n\n const bytes = new Uint8Array(buffer);\n const view = new DataView(buffer);\n const eocdOffset = findEndOfCentralDirectory(bytes);\n const entryCount = view.getUint16(eocdOffset + 10, true);\n const centralDirOffset = view.getUint32(eocdOffset + 16, true);\n\n if (entryCount > ZIP_MAX_ENTRIES) {\n throw new Error('ZIP archive has too many entries');\n }\n if (centralDirOffset >= bytes.length) {\n throw new Error('Invalid ZIP central directory offset');\n }\n\n const entries: ZipEntry[] = [];\n let offset = centralDirOffset;\n let totalUncompressed = 0;\n\n for (let i = 0; i < entryCount; i += 1) {\n if (offset + 46 > bytes.length || view.getUint32(offset, true) !== 0x02014b50) {\n throw new Error('Invalid ZIP central directory');\n }\n\n const compression = view.getUint16(offset + 10, true);\n const compressedSize = view.getUint32(offset + 20, true);\n const uncompressedSize = view.getUint32(offset + 24, true);\n const nameLength = view.getUint16(offset + 28, true);\n const extraLength = view.getUint16(offset + 30, true);\n const commentLength = view.getUint16(offset + 32, true);\n const localHeaderOffset = view.getUint32(offset + 42, true);\n const nameEnd = offset + 46 + nameLength;\n if (nameEnd > bytes.length) {\n throw new Error('Invalid ZIP entry name');\n }\n const rawName = TEXT_DECODER.decode(bytes.subarray(offset + 46, nameEnd));\n const name = normalizeZipEntryName(rawName);\n\n offset = nameEnd + extraLength + commentLength;\n if (offset > bytes.length) {\n throw new Error('Invalid ZIP central directory');\n }\n\n if (!name || name.endsWith('/') || isIgnoredArchiveEntry(name)) continue;\n if (shouldExtract && !shouldExtract(name)) continue;\n\n if (\n compressedSize > ZIP_MAX_ENTRY_COMPRESSED_BYTES ||\n uncompressedSize > ZIP_MAX_ENTRY_UNCOMPRESSED_BYTES\n ) {\n throw new Error(`ZIP entry exceeds size limit: ${name}`);\n }\n if (\n compressedSize > 0 &&\n uncompressedSize > 0 &&\n uncompressedSize / compressedSize > ZIP_MAX_COMPRESSION_RATIO\n ) {\n throw new Error(`ZIP entry compression ratio exceeds limit: ${name}`);\n }\n if (totalUncompressed + uncompressedSize > ZIP_MAX_TOTAL_UNCOMPRESSED_BYTES) {\n throw new Error('ZIP archive exceeds total uncompressed size limit');\n }\n\n if (\n localHeaderOffset + 30 > bytes.length ||\n view.getUint32(localHeaderOffset, true) !== 0x04034b50\n ) {\n throw new Error(`Invalid ZIP local header: ${name}`);\n }\n const localNameLength = view.getUint16(localHeaderOffset + 26, true);\n const localExtraLength = view.getUint16(localHeaderOffset + 28, true);\n const dataOffset = localHeaderOffset + 30 + localNameLength + localExtraLength;\n if (dataOffset + compressedSize > bytes.length) {\n throw new Error(`ZIP entry data out of bounds: ${name}`);\n }\n const compressed = bytes.subarray(dataOffset, dataOffset + compressedSize);\n const data = await decompressZipEntry(compressed, compression, uncompressedSize);\n totalUncompressed += data.length;\n if (totalUncompressed > ZIP_MAX_TOTAL_UNCOMPRESSED_BYTES) {\n throw new Error('ZIP archive exceeds total uncompressed size limit');\n }\n entries.push({ name, data });\n }\n\n return entries;\n}\n\nfunction findEndOfCentralDirectory(bytes: Uint8Array): number {\n for (let i = bytes.length - 22; i >= 0; i -= 1) {\n if (\n bytes[i] === 0x50 &&\n bytes[i + 1] === 0x4b &&\n bytes[i + 2] === 0x05 &&\n bytes[i + 3] === 0x06\n ) {\n return i;\n }\n }\n throw new Error('Invalid ZIP archive');\n}\n\nfunction concatUint8Arrays(chunks: Uint8Array[], totalLength: number): Uint8Array {\n const out = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\nasync function decompressZipEntry(\n data: Uint8Array,\n method: number,\n uncompressedSize: number\n): Promise {\n if (method === 0) {\n if (data.length > ZIP_MAX_ENTRY_UNCOMPRESSED_BYTES) {\n throw new Error('ZIP entry exceeds size limit');\n }\n return data;\n }\n if (method === 8) {\n const stream = new Blob([data as BlobPart])\n .stream()\n .pipeThrough(new DecompressionStream('deflate-raw'));\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > ZIP_MAX_ENTRY_UNCOMPRESSED_BYTES) {\n throw new Error('ZIP entry exceeds size limit');\n }\n if (data.length > 0 && total / data.length > ZIP_MAX_COMPRESSION_RATIO) {\n throw new Error('ZIP entry compression ratio exceeds limit');\n }\n chunks.push(value);\n }\n } catch (err) {\n try {\n await reader.cancel();\n } catch {\n /* ignore cancel errors */\n }\n throw err;\n }\n\n const out = concatUint8Arrays(chunks, total);\n if (uncompressedSize && out.length !== uncompressedSize) {\n return out.slice(0, Math.min(out.length, uncompressedSize));\n }\n return out;\n }\n throw new Error(`Unsupported ZIP compression method ${method}`);\n}\n\nfunction normalizeZipEntryName(name: string): string {\n return name.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nexport function findArchiveEntry(\n entries: ZipEntry[],\n fileName: string\n): ZipEntry | undefined {\n return entries.find(e => e.name === fileName);\n}\n\nexport function isIgnoredArchiveEntry(name: string): boolean {\n if (name === '.DS_Store' || name.endsWith('/.DS_Store')) return true;\n return name.split('/').some(part => part.startsWith('._') || part === '__MACOSX');\n}\n\nexport async function extractArchiveFromZipBuffer(buffer: ArrayBuffer): Promise<{\n markupXml: string;\n pageImages: Map;\n assetUrls: Map;\n}> {\n const entries = await unzip(buffer);\n const markupEntry = findArchiveEntry(entries, 'document.xml');\n if (!markupEntry) {\n throw new Error('Archive must contain document.xml');\n }\n const markupXml = new TextDecoder().decode(markupEntry.data);\n const pageImages = new Map();\n const assetUrls = new Map();\n for (const e of entries) {\n const m = e.name.match(/^pages\\/(\\d+)\\.(png|jpe?g|webp)$/i);\n if (m) {\n pageImages.set(\n Number(m[1]),\n URL.createObjectURL(new Blob([e.data as BlobPart], { type: mimeFromAssetPath(e.name) }))\n );\n continue;\n }\n if (e.name.startsWith('assets/') && !e.name.endsWith('/')) {\n assetUrls.set(\n e.name,\n URL.createObjectURL(\n new Blob([e.data as BlobPart], { type: mimeFromAssetPath(e.name) })\n )\n );\n }\n }\n return { markupXml, pageImages, assetUrls };\n}\n\n","import {\n DOCLANG_NS,\n HEAD_TAGS,\n CELL_SPAN_TAGS,\n OTSL_CONTAINER_TAGS,\n childElements,\n localName,\n headLocations,\n firstHeadChild,\n walkElements,\n locationResolution,\n parseElementHeadAt,\n skipContainerLevelHead,\n skipUntilListItemBoundary,\n skipUntilCellBoundary,\n isCellToken,\n isTextLikeNode,\n isWhitespaceOnlyText,\n serializeMarkupTextNodes,\n formatMarkupTextNode,\n archiveRelativeAssetPath,\n elementLayer,\n elementLabel,\n isVirtualTextOverlayUnit,\n} from './dom';\nimport type {\n DocumentState,\n Resolution,\n BoundingBox,\n ReadingOrderStep,\n CaptionLink,\n XrefLink,\n FragmentLink,\n FragmentNavItem,\n ThreadNav,\n} from './types';\nimport { extractArchiveFromZipBuffer } from './zip';\nimport { isPictureContentElement, isTableContentElement } from './dom';\n\nexport const PAGE_IMAGE_RE = /^(\\d+)\\.(png|jpe?g|webp)$/i;\nexport const NO_MARKUP = '(No markup to be shown.)';\nconst RENDER_FORMAT_TAGS = new Set([\n 'bold',\n 'italic',\n 'underline',\n 'strikethrough',\n 'superscript',\n 'subscript',\n 'handwriting',\n 'rtl',\n 'content',\n]);\n\n// ---------------------------------------------------------------------------\n// Document XML parsing helpers\n// ---------------------------------------------------------------------------\n\nexport function splitIntoSegments(root: Element): Element[][] {\n const body = childElements(root).filter(el => localName(el) !== 'head');\n const segments: Element[][] = [[]];\n for (const el of body) {\n if (localName(el) === 'page_break') {\n segments.push([]);\n } else {\n segments[segments.length - 1]!.push(el);\n }\n }\n return segments.length ? segments : [[]];\n}\n\nexport function segmentHasMarkup(segment: Element[]): boolean {\n return segment.some(el => el.nodeType === Node.ELEMENT_NODE);\n}\n\nexport function readDefaultResolution(head: Element | null): Resolution {\n const fallback = { width: 512, height: 512 };\n if (!head) return fallback;\n const dr = childElements(head).find(el => localName(el) === 'default_resolution');\n if (!dr) return fallback;\n const w = parseInt(dr.getAttribute('width') ?? '512', 10);\n const h = parseInt(dr.getAttribute('height') ?? '512', 10);\n return {\n width: Number.isFinite(w) && w > 0 ? w : 512,\n height: Number.isFinite(h) && h > 0 ? h : 512,\n };\n}\n\nexport function assignElementIds(segment: Element[]): Map {\n const ids = new Map();\n let counter = 0;\n walkElements(segment, el => {\n ids.set(el, `el-${counter++}`);\n });\n return ids;\n}\n\nexport function invertElementIds(\n elementIds: Map\n): Map {\n const idToElement = new Map();\n for (const [el, id] of elementIds) idToElement.set(id, el);\n return idToElement;\n}\n\n// ---------------------------------------------------------------------------\n// Thread / reading order helpers\n// ---------------------------------------------------------------------------\n\nfunction buildThreadsById(docRoot: Element): Map {\n const map = new Map();\n const roots = childElements(docRoot).filter(el => localName(el) !== 'head');\n walkElements(roots, el => {\n for (const child of childElements(el)) {\n if (localName(child) !== 'thread') continue;\n const threadId = child.getAttribute('thread_id');\n if (!threadId) continue;\n if (!map.has(threadId)) map.set(threadId, []);\n map.get(threadId)!.push(el);\n }\n });\n return map;\n}\n\nexport function buildElementPageMap(segments: Element[][]): Map {\n const elementPage = new Map();\n segments.forEach((segment, idx) => {\n const pageNum = idx + 1;\n walkElements(segment, el => elementPage.set(el, pageNum));\n });\n return elementPage;\n}\n\nexport function buildThreadPagesById(\n docRoot: Element,\n elementPageByEl: Map\n): Map> {\n const threadPagesById = new Map>();\n for (const [threadId, elements] of buildThreadsById(docRoot)) {\n const pages = new Set();\n for (const el of elements) {\n const page = elementPageByEl.get(el);\n if (page) pages.add(page);\n }\n if (pages.size) threadPagesById.set(threadId, pages);\n }\n return threadPagesById;\n}\n\nexport function buildThreadNavByElement(docRoot: Element): Map {\n const nav = new Map();\n for (const [, elements] of buildThreadsById(docRoot)) {\n for (let i = 0; i < elements.length; i += 1) {\n nav.set(elements[i]!, {\n prev: i > 0 ? elements[i - 1]! : null,\n next: i < elements.length - 1 ? elements[i + 1]! : null,\n });\n }\n }\n return nav;\n}\n\nfunction isReadingOrderUnit(el: Element): boolean {\n const tag = localName(el);\n if (tag === 'caption') return true;\n if (\n HEAD_TAGS.has(tag) ||\n tag === 'location' ||\n tag === 'h_thread' ||\n tag === 'page_break'\n )\n return false;\n if (tag === 'nl' || CELL_SPAN_TAGS.has(tag)) return false;\n if (RENDER_FORMAT_TAGS.has(tag) || tag === 'src' || tag === 'checkbox') return false;\n return true;\n}\n\nexport function computeReadingOrder(docRoot: Element): Element[] {\n const bodyChildren = childElements(docRoot).filter(el => localName(el) !== 'head');\n const threadsById = buildThreadsById(docRoot);\n const consumedViaXref = new Set();\n const order: Element[] = [];\n\n function record(el: Element): void {\n if (!isReadingOrderUnit(el)) return;\n order.push(el);\n }\n\n function consumeThread(threadId: string): void {\n for (const target of threadsById.get(threadId) ?? []) {\n if (consumedViaXref.has(target)) continue;\n consumedViaXref.add(target);\n visitElement(target);\n }\n }\n\n function walkChildren(parent: Element): void {\n for (const child of parent.childNodes) {\n if (child.nodeType !== Node.ELEMENT_NODE) continue;\n const el = child as Element;\n const tag = localName(el);\n if (tag === 'xref') {\n const threadId = el.getAttribute('thread_id');\n if (threadId) consumeThread(threadId);\n continue;\n }\n if (tag === 'page_break') continue;\n visitElement(el);\n }\n }\n\n function visitElement(el: Element): void {\n record(el);\n walkChildren(el);\n }\n\n for (const el of bodyChildren) {\n if (localName(el) === 'page_break') continue;\n if (consumedViaXref.has(el)) continue;\n visitElement(el);\n }\n\n return order;\n}\n\nfunction isReadingOrderOverlayUnit(el: Element): boolean {\n if (!isReadingOrderUnit(el)) return false;\n if (isPictureContentElement(el) || isTableContentElement(el)) return false;\n if (headLocations(el).length === 4) return true;\n return isVirtualTextOverlayUnit(el);\n}\n\nexport function computeReadingOrderDisplayNumbers(\n readingOrder: Element[]\n): Map {\n const numbers = new Map();\n let n = 0;\n for (const el of readingOrder) {\n if (!isReadingOrderOverlayUnit(el)) continue;\n n += 1;\n numbers.set(el, n);\n }\n return numbers;\n}\n\n// ---------------------------------------------------------------------------\n// Bounding box collection\n// ---------------------------------------------------------------------------\n\nfunction pushBoundingBox(\n boxes: BoundingBox[],\n locs: Element[],\n defaultResolution: Resolution,\n kind: string,\n tag: string,\n elementId: string,\n layer = 'body'\n): void {\n const [x0el, y0el, x1el, y1el] = locs as [Element, Element, Element, Element];\n const resW = locationResolution(x0el, defaultResolution.width);\n const resH = locationResolution(y0el, defaultResolution.height);\n boxes.push({\n kind,\n tag,\n elementId,\n layer,\n x0: parseInt(x0el.getAttribute('value') ?? '0', 10),\n y0: parseInt(y0el.getAttribute('value') ?? '0', 10),\n x1: parseInt(x1el.getAttribute('value') ?? '0', 10),\n y1: parseInt(y1el.getAttribute('value') ?? '0', 10),\n resW,\n resH,\n });\n}\n\nfunction collectListVirtualTextBoxes(\n list: Element,\n defaultResolution: Resolution,\n boxes: BoundingBox[],\n elementIds: Map\n): void {\n const nodes = [...list.childNodes];\n let i = skipContainerLevelHead(nodes, 0);\n while (i < nodes.length) {\n const node = nodes[i]!;\n if (node.nodeType !== Node.ELEMENT_NODE || localName(node as Element) !== 'ldiv') {\n i += 1;\n continue;\n }\n i += 1;\n const head = parseElementHeadAt(nodes, i);\n if (head) {\n const elementId = elementIds.get(node as Element);\n if (elementId) {\n pushBoundingBox(\n boxes,\n head.locs,\n defaultResolution,\n 'text',\n 'text',\n elementId,\n elementLayer(node as Element)\n );\n }\n i = head.nextIndex;\n }\n i = skipUntilListItemBoundary(nodes, i);\n }\n}\n\nfunction collectTableVirtualTextBoxes(\n container: Element,\n defaultResolution: Resolution,\n boxes: BoundingBox[],\n elementIds: Map\n): void {\n const nodes = [...container.childNodes];\n let i = skipContainerLevelHead(nodes, 0);\n while (i < nodes.length) {\n const node = nodes[i]!;\n if (node.nodeType !== Node.ELEMENT_NODE) {\n i += 1;\n continue;\n }\n const tag = localName(node as Element);\n if (!isCellToken(tag)) {\n i += 1;\n continue;\n }\n if (tag === 'nl') {\n i += 1;\n continue;\n }\n i += 1;\n const head = parseElementHeadAt(nodes, i);\n if (head) {\n const elementId = elementIds.get(node as Element);\n if (elementId) {\n pushBoundingBox(\n boxes,\n head.locs,\n defaultResolution,\n 'text',\n 'text',\n elementId,\n elementLayer(node as Element)\n );\n }\n i = head.nextIndex;\n }\n i = skipUntilCellBoundary(nodes, i);\n }\n}\n\nexport function collectBoundingBoxes(\n segment: Element[],\n defaultResolution: Resolution,\n elementIds: Map\n): BoundingBox[] {\n const boxes: BoundingBox[] = [];\n walkElements(segment, el => {\n const locs = headLocations(el);\n if (locs.length !== 4) return;\n const elementId = elementIds.get(el);\n if (!elementId) return;\n pushBoundingBox(\n boxes,\n locs,\n defaultResolution,\n localName(el),\n elementLabel(el),\n elementId,\n elementLayer(el)\n );\n });\n walkElements(segment, el => {\n const tag = localName(el);\n if (tag === 'list')\n collectListVirtualTextBoxes(el, defaultResolution, boxes, elementIds);\n else if (OTSL_CONTAINER_TAGS.has(tag)) {\n collectTableVirtualTextBoxes(el, defaultResolution, boxes, elementIds);\n }\n });\n return boxes;\n}\n\nexport function collectCaptionLinks(\n segment: Element[],\n elementIds: Map,\n boxes: BoundingBox[]\n): CaptionLink[] {\n const boxById = new Map(boxes.map(b => [b.elementId, b]));\n const links: CaptionLink[] = [];\n walkElements(segment, el => {\n if (localName(el) !== 'caption') return;\n const captionId = elementIds.get(el);\n const captionBox = captionId ? boxById.get(captionId) : null;\n if (!captionBox) return;\n const host = el.parentElement;\n if (!host || headLocations(host).length !== 4) return;\n const hostId = elementIds.get(host);\n const hostBox = hostId ? boxById.get(hostId) : null;\n if (!hostBox) return;\n links.push({\n captionBox,\n hostBox,\n captionElementId: captionId!,\n hostElementId: hostId!,\n });\n });\n return links;\n}\n\nexport function collectXrefLinks(\n segment: Element[],\n elementIds: Map,\n boxes: BoundingBox[]\n): XrefLink[] {\n const boxById = new Map(boxes.map(b => [b.elementId, b]));\n const threadsById = new Map();\n\n walkElements(segment, el => {\n const elementId = elementIds.get(el);\n const box = elementId ? boxById.get(elementId) : null;\n if (!box) return;\n for (const thread of childElements(el)) {\n if (localName(thread) !== 'thread') continue;\n const threadId = thread.getAttribute('thread_id');\n if (!threadId) continue;\n if (!threadsById.has(threadId)) threadsById.set(threadId, []);\n threadsById.get(threadId)!.push({ elementId: elementId!, box });\n }\n });\n\n const links: XrefLink[] = [];\n walkElements(segment, el => {\n const xrefs = childElements(el).filter(c => localName(c) === 'xref');\n if (!xrefs.length) return;\n const from = findNearestLocatedBox(el, elementIds, boxById);\n if (!from) return;\n for (const xref of xrefs) {\n const threadId = xref.getAttribute('thread_id');\n if (!threadId) continue;\n for (const { elementId: toId, box: toBox } of threadsById.get(threadId) ?? []) {\n if (toId === from.elementId) continue;\n links.push({\n fromBox: from.box,\n toBox,\n fromElementId: from.elementId,\n toElementId: toId,\n });\n }\n }\n });\n return links;\n}\n\nfunction findNearestLocatedBox(\n el: Element,\n elementIds: Map,\n boxById: Map\n): { elementId: string; box: BoundingBox } | null {\n let node: Element | null = el;\n while (node) {\n const elementId = elementIds.get(node);\n const box = elementId ? boxById.get(elementId) : null;\n if (box) return { elementId: elementId!, box };\n if (localName(node) === 'doclang') break;\n node = node.parentElement;\n }\n return null;\n}\n\nexport function collectFragmentLinks(\n segment: Element[],\n elementIds: Map,\n boxes: BoundingBox[],\n pageNum: number,\n threadPagesById: Map>\n): FragmentLink[] {\n const boxById = new Map(boxes.map(b => [b.elementId, b]));\n const onPage = new Map();\n\n walkElements(segment, el => {\n const thread = firstHeadChild(el, 'thread');\n const threadId = thread?.getAttribute('thread_id');\n if (!threadId) return;\n const elementId = elementIds.get(el);\n const box = elementId ? boxById.get(elementId) : null;\n if (!box) return;\n if (!onPage.has(threadId)) onPage.set(threadId, []);\n onPage.get(threadId)!.push({ elementId: elementId!, box });\n });\n\n const links: FragmentLink[] = [];\n\n for (const [threadId, members] of onPage) {\n if (members.length >= 2) {\n for (let i = 0; i < members.length - 1; i += 1) {\n links.push({\n fromBox: members[i]!.box,\n toBox: members[i + 1]!.box,\n fromElementId: members[i]!.elementId,\n toElementId: members[i + 1]!.elementId,\n threadId,\n });\n }\n continue;\n }\n\n if (members.length !== 1) continue;\n const threadPages = threadPagesById.get(threadId);\n if (!threadPages) continue;\n const hasPrevious = [...threadPages].some(p => p < pageNum);\n const hasFollowing = [...threadPages].some(p => p > pageNum);\n if (!hasPrevious && !hasFollowing) continue;\n\n const base = {\n fromBox: members[0]!.box,\n toBox: null,\n fromElementId: members[0]!.elementId,\n toElementId: null,\n threadId,\n };\n if (hasPrevious) links.push({ ...base, targetCorner: 'tl' as const });\n if (hasFollowing) links.push({ ...base, targetCorner: 'br' as const });\n }\n\n return links;\n}\n\nexport function collectFragmentNavItems(\n segment: Element[],\n elementIds: Map,\n boxes: BoundingBox[],\n threadNavByElement: Map\n): FragmentNavItem[] {\n const boxById = new Map(boxes.map(b => [b.elementId, b]));\n const items: FragmentNavItem[] = [];\n walkElements(segment, el => {\n const nav = threadNavByElement.get(el);\n if (!nav || (!nav.prev && !nav.next)) return;\n const elementId = elementIds.get(el);\n const box = elementId ? boxById.get(elementId) : null;\n if (!box) return;\n items.push({\n elementId: elementId!,\n box,\n hasPrev: nav.prev !== null,\n hasNext: nav.next !== null,\n });\n });\n return items;\n}\n\nexport function collectReadingOrderSteps(\n segment: Element[],\n elementIds: Map,\n boxes: BoundingBox[],\n readingOrder: Element[],\n globalNumbering = true,\n displayNumbers: Map | null = null\n): ReadingOrderStep[] {\n const boxById = new Map(boxes.map(b => [b.elementId, b]));\n const steps: ReadingOrderStep[] = [];\n let pageOrder = 0;\n\n readingOrder.forEach(el => {\n if (isPictureContentElement(el) || isTableContentElement(el)) return;\n const elementId = elementIds.get(el);\n if (!elementId) return;\n const box = boxById.get(elementId);\n if (!box) return;\n pageOrder += 1;\n steps.push({\n order: globalNumbering ? (displayNumbers?.get(el) ?? pageOrder) : pageOrder,\n box,\n elementId,\n });\n });\n\n return steps;\n}\n\n// ---------------------------------------------------------------------------\n// Build document state\n// ---------------------------------------------------------------------------\n\nexport function buildDocumentState(\n markupXml: string,\n pageImages: Map,\n label: string,\n assetUrls: Map,\n { markupOnly }: { markupOnly: boolean }\n): DocumentState | null {\n const doc = new DOMParser().parseFromString(markupXml, 'application/xml');\n const parseError = doc.querySelector('parsererror');\n if (parseError) {\n alert(`Invalid XML in ${label}`);\n return null;\n }\n const root = doc.documentElement;\n if (localName(root) !== 'doclang') {\n alert(`${label}: root element must be `);\n return null;\n }\n\n const head = childElements(root).find(el => localName(el) === 'head') ?? null;\n const defaultResolution = readDefaultResolution(head);\n const segments = markupOnly\n ? [childElements(root).filter(el => localName(el) !== 'head')]\n : splitIntoSegments(root);\n const hasPageView = !markupOnly && pageImages.size > 0;\n const maxImagePage = hasPageView ? Math.max(...pageImages.keys()) : 0;\n const pageCount = markupOnly ? 1 : Math.max(segments.length, maxImagePage, 1);\n const readingOrder = computeReadingOrder(root);\n const elementPageByEl = buildElementPageMap(segments);\n\n return {\n pageImages,\n assetUrls,\n pageCount,\n segments,\n defaultResolution,\n elementIds: new Map(),\n idToElement: new Map(),\n hasPageView,\n markupOnly,\n docRoot: root,\n threadPagesById: buildThreadPagesById(root, elementPageByEl),\n elementPageByEl,\n threadNavByElement: buildThreadNavByElement(root),\n pendingSelectElement: null,\n readingOrder,\n readingOrderDisplayNumbers: computeReadingOrderDisplayNumbers(readingOrder),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Archive extraction helpers\n// ---------------------------------------------------------------------------\n\nexport async function extractArchiveFromFiles(files: File[]): Promise<{\n markupXml: string;\n pageImages: Map;\n assetUrls: Map;\n}> {\n const markupFile = files.find(f => f.name === 'document.xml');\n if (!markupFile) throw new Error('Archive must contain document.xml at its root.');\n const markupXml = await markupFile.text();\n const pageImages = new Map();\n const assetUrls = new Map();\n for (const f of files) {\n const relPath = f.webkitRelativePath || f.name;\n const parts = relPath.split('/');\n if (parts.length >= 2 && parts[parts.length - 2] === 'pages') {\n const m = PAGE_IMAGE_RE.exec(f.name);\n if (m) pageImages.set(Number(m[1]), URL.createObjectURL(f));\n }\n const assetPath = archiveRelativeAssetPath(relPath);\n if (assetPath) assetUrls.set(assetPath, URL.createObjectURL(f));\n }\n return { markupXml, pageImages, assetUrls };\n}\n\nexport { extractArchiveFromZipBuffer };\n\nexport function revokeDocumentState(docState: DocumentState): void {\n if (!docState) return;\n for (const url of docState.pageImages.values()) {\n if (url.startsWith('blob:')) URL.revokeObjectURL(url);\n }\n for (const url of docState.assetUrls.values()) {\n if (url.startsWith('blob:')) URL.revokeObjectURL(url);\n }\n}\n\nexport function serializeSegment(segment: Element[]): string {\n return segment.map(el => serializeElement(el, 0)).join('\\n');\n}\n\nfunction serializeElement(el: Element, depth: number): string {\n const pad = ' '.repeat(depth);\n const tag = localName(el);\n const attrs = [...el.attributes]\n .filter(a => a.name !== 'xmlns' || a.value !== DOCLANG_NS)\n .map(a => `${a.name}=\"${a.value}\"`)\n .join(' ');\n const attrStr = attrs ? ` ${attrs}` : '';\n\n if (!el.childNodes.length) return `${pad}<${tag}${attrStr}/>`;\n\n const meaningfulText = [...el.childNodes].filter(\n n => isTextLikeNode(n) && !isWhitespaceOnlyText(n)\n );\n const textOnly =\n meaningfulText.length > 0 &&\n meaningfulText.every(isTextLikeNode) &&\n !childElements(el).length;\n if (textOnly) {\n const text = serializeMarkupTextNodes(el.childNodes);\n if (text) return `${pad}<${tag}${attrStr}>${text}`;\n }\n\n const parts = [`${pad}<${tag}${attrStr}>`];\n for (const child of el.childNodes) {\n if (isTextLikeNode(child)) {\n const text = formatMarkupTextNode(child);\n if (text) parts.push(' '.repeat(depth + 1) + text);\n } else if (child.nodeType === Node.ELEMENT_NODE) {\n parts.push(serializeElement(child as Element, depth + 1));\n }\n }\n parts.push(`${pad}`);\n return parts.join('\\n');\n}\n","/**\n * Base class for doclang web components that render document content.\n *\n * Supports three ways to provide the DocLang document:\n *\n * 1. `src` attribute — a URL (http:, data:, blob:) pointing to either a\n * raw XML markup string or a .dclx/.zip archive. The component fetches\n * and parses it automatically.\n *\n * 2. Inline child `