From 3b61cac40545aeb06ef0e8804ba8d717136939cf Mon Sep 17 00:00:00 2001 From: Aanish Bhirud <47579874+baanish@users.noreply.github.com> Date: Tue, 12 May 2026 10:31:16 -0400 Subject: [PATCH 1/4] Optimize fragment rendering and viewer shell --- README.md | 5 +- docs/architecture.md | 11 +- docs/dependency-notes.md | 9 +- docs/deployment.md | 2 +- docs/payload-format.md | 10 +- docs/testing.md | 8 +- eslint.config.mjs | 2 +- next-env.d.ts | 1 + next.config.ts | 5 +- package-lock.json | 886 +++++++----------- package.json | 32 +- playwright.config.ts | 3 +- public/_headers | 15 + public/arx2-dictionary.json | 2 +- public/arx2-dictionary.json.br | Bin 0 -> 290 bytes public/vendor/diff-view-pure.css | 660 +++++++++++++ public/vendor/diff-view-pure.css.br | Bin 0 -> 2777 bytes scripts/bench-baseline.json | 18 +- scripts/bench-codecs.mjs | 256 ++++- scripts/check-build-budgets.mjs | 175 ++++ scripts/clean-build-output.mjs | 5 + scripts/compress-dictionary.mjs | 48 +- scripts/ensure-next-types.mjs | 67 +- scripts/serve-export.mjs | 68 +- selfhosted/server.ts | 29 +- skills/agent-render-linking/SKILL.md | 4 +- src/app/globals.css | 18 +- src/app/layout.tsx | 24 +- src/app/security/page.tsx | 8 +- src/app/url-explainer/page.tsx | 18 +- src/components/home/link-creator.tsx | 210 ++++- src/components/home/sample-link-data.ts | 57 ++ src/components/home/sample-links.tsx | 94 ++ src/components/renderers/code-renderer.tsx | 77 +- src/components/renderers/csv-renderer.tsx | 70 +- src/components/renderers/diff-renderer.tsx | 428 +++++++-- src/components/renderers/json-renderer.tsx | 60 +- .../renderers/markdown-renderer.tsx | 158 +++- src/components/renderers/mermaid-block.tsx | 22 +- src/components/theme-provider.tsx | 12 - src/components/theme-toggle.tsx | 6 +- src/components/theme/use-theme-controller.ts | 133 +++ src/components/viewer-shell.tsx | 797 ++++++---------- src/components/viewer/artifact-selector.tsx | 9 +- src/components/viewer/artifact-stage.tsx | 542 +++++++++++ .../viewer/fragment-details-disclosure.tsx | 2 - src/lib/code/language.ts | 47 +- src/lib/diff/git-patch.ts | 89 +- src/lib/payload/arx-codec.ts | 178 ++-- src/lib/payload/envelope.ts | 30 +- src/lib/payload/fragment-arx.ts | 188 ++++ src/lib/payload/fragment.ts | 212 ++--- src/lib/payload/link-creator.ts | 45 +- src/lib/payload/schema.ts | 21 +- src/lib/payload/wire-format.ts | 92 +- src/lib/site/base-path.ts | 43 + src/lib/site/canonical-base.ts | 12 +- src/lib/utils.ts | 29 +- tests/arx-codec.test.ts | 212 ++++- tests/base-path.test.ts | 37 + tests/code-language.test.ts | 7 + tests/components/code-renderer.test.tsx | 110 ++- tests/components/csv-renderer.test.tsx | 54 ++ tests/components/diff-renderer.test.tsx | 107 ++- tests/components/json-renderer.test.tsx | 72 ++ tests/components/link-creator.test.tsx | 118 +++ tests/components/markdown-renderer.test.tsx | 117 +++ tests/components/mermaid-block.test.tsx | 34 + tests/components/theme-toggle.test.tsx | 72 ++ .../viewer-shell-artifact-select.test.tsx | 160 ++++ tests/components/viewer-shell.test.tsx | 10 +- tests/diff-style-asset.test.ts | 31 + tests/e2e/viewer.spec.ts | 84 +- .../code-light-chromium.png | Bin 141873 -> 156853 bytes .../csv-compact-light-chromium.png | Bin 104970 -> 125785 bytes .../diff-light-chromium.png | Bin 161847 -> 161340 bytes .../empty-state-light-chromium.png | Bin 901708 -> 892235 bytes .../json-light-chromium.png | Bin 400389 -> 430794 bytes .../markdown-dark-chromium.png | Bin 263604 -> 292842 bytes .../markdown-light-chromium.png | Bin 258998 -> 286954 bytes tests/envelope.test.ts | 14 + tests/fragment.test.ts | 26 + tests/git-patch.test.ts | 12 + tests/headers.test.ts | 12 + tests/sample-link-data.test.ts | 23 + tests/selfhosted/api-catalog.test.ts | 294 +++++- tests/setup.tsx | 8 - tests/utils.test.ts | 12 + tsconfig.json | 2 + 89 files changed, 5851 insertions(+), 1829 deletions(-) create mode 100644 public/arx2-dictionary.json.br create mode 100644 public/vendor/diff-view-pure.css create mode 100644 public/vendor/diff-view-pure.css.br create mode 100644 scripts/check-build-budgets.mjs create mode 100644 scripts/clean-build-output.mjs create mode 100644 src/components/home/sample-link-data.ts create mode 100644 src/components/home/sample-links.tsx delete mode 100644 src/components/theme-provider.tsx create mode 100644 src/components/theme/use-theme-controller.ts create mode 100644 src/components/viewer/artifact-stage.tsx create mode 100644 src/lib/payload/fragment-arx.ts create mode 100644 src/lib/site/base-path.ts create mode 100644 tests/base-path.test.ts create mode 100644 tests/components/csv-renderer.test.tsx create mode 100644 tests/components/json-renderer.test.tsx create mode 100644 tests/components/link-creator.test.tsx create mode 100644 tests/components/markdown-renderer.test.tsx create mode 100644 tests/components/mermaid-block.test.tsx create mode 100644 tests/components/theme-toggle.test.tsx create mode 100644 tests/components/viewer-shell-artifact-select.test.tsx create mode 100644 tests/diff-style-asset.test.ts create mode 100644 tests/sample-link-data.test.ts create mode 100644 tests/utils.test.ts diff --git a/README.md b/README.md index 89415e0..84e1f31 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ The repo includes Playwright visual snapshots for the shipped viewer surfaces: - Markdown, code, diff, CSV, and JSON all render in the static shell - Fragment transport supports `plain`, `lz`, `deflate`, `arx`, and `arx2`, with automatic shortest-fragment selection across available wire formats -- The `arx` substitution dictionary is served at `/arx-dictionary.json`; the `arx2` tuple-envelope overlay is served at `/arx2-dictionary.json` +- The `arx` substitution dictionary is served at `/arx-dictionary.json` with a pre-compressed `/arx-dictionary.json.br` variant; the `arx2` tuple-envelope overlay is served at `/arx2-dictionary.json` with a pre-compressed `/arx2-dictionary.json.br` variant - The viewer toolbar copies artifact bodies to the clipboard, downloads them as files, and (for markdown) supports browser print-to-PDF - Deployment target: static hosting, including Cloudflare Pages @@ -60,7 +60,7 @@ The repo includes Playwright visual snapshots for the shipped viewer surfaces: - `code` - read-only CodeMirror view with line numbers, wrap toggle, syntax-tree-aware rainbow brackets, and maintained indentation markers - `diff` - review-style multi-file git patch viewer with unified and split modes - `csv` - parsed table view with sticky headers and horizontal overflow handling -- `json` - lightweight read-only tree view plus raw code view, with graceful malformed JSON fallback +- `json` - lightweight read-only tree view plus native raw source view, with graceful malformed JSON fallback ## Principles @@ -126,6 +126,7 @@ npm run preview ``` Set `NEXT_PUBLIC_BASE_PATH` before `npm run build` when you want to preview a subpath deployment locally. +The local preview server auto-detects the generated base path from the build manifest, so the same `npm run preview` command works for root and subpath exports. Set `NEXT_PUBLIC_SITE_URL` to your public origin before production builds so `sitemap.xml` and metadata use the correct canonical origin (see `docs/deployment.md`). diff --git a/docs/architecture.md b/docs/architecture.md index 736ac3e..c943628 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -25,7 +25,7 @@ The static export also emits `sitemap.xml` at the site root (and under `NEXT_PUB - `code` - read-only CodeMirror view with syntax-aware rendering and code affordances - `diff` - review-style diff view with unified and split modes - `csv` - table-focused data grid built from parsed rows and dynamic columns -- `json` - lightweight read-only tree view plus a raw CodeMirror view +- `json` - lightweight read-only tree view plus a native raw source view The viewer shell now routes all five artifact kinds through dynamically imported client-only renderers so the landing shell stays light and static-host friendly. @@ -60,12 +60,13 @@ That keeps the viewer static-hosting friendly while removing the brittle parts o ## Bundle tradeoffs -The largest remaining deferred cost is still the diff renderer stack, primarily `@git-diff-view/*` and its highlighting internals. It remains because it still provides the best review-style UX for multi-file git patches, split/unified modes, and syntax-aware rendering with less product code than a bespoke replacement. +The largest remaining deferred cost is still the diff renderer stack, primarily `@git-diff-view/*` and its highlighting internals. Its vendor stylesheet is served from `public/vendor/diff-view-pure.css` with a precompressed `.css.br` variant and injected only when a rich diff mounts, so the empty shell and non-diff artifacts do not pay that CSS cost. The stack remains because it still provides the best review-style UX for multi-file git patches, split/unified modes, and syntax-aware rendering with less product code than a bespoke replacement. The JSON and markdown paths are now substantially lighter because: - `vanilla-jsoneditor` was removed in favor of a lighter read-only tree view - `rehype-highlight` and its Highlight.js stack were removed +- raw markdown and CSV views use native source blocks instead of mounting CodeMirror - CodeMirror language support now loads on demand per active language ## Diff choice @@ -76,7 +77,7 @@ The JSON and markdown paths are now substantially lighter because: - split and unified views are built in - syntax highlighting and diff affordances are stronger out of the box for artifact viewing - individual file patches can be rendered as a sequence while preserving filenames and boundaries -- CodeMirror remains the better fit for raw source and raw JSON views +- CodeMirror remains the better fit for full source artifacts and markdown code fences `@codemirror/merge` stays a reasonable future option if the project ever needs a more editor-centric comparison workflow, but it is not the best default for shareable review artifacts. @@ -95,8 +96,8 @@ The fragment protocol keeps the JSON envelope stable and treats compression stri - `plain` stores base64url-encoded JSON for compatibility and debugging - `lz` stores compressed JSON via `lz-string` when it produces a smaller fragment - `deflate` stores deflate-compressed UTF-8 JSON bytes when it outperforms other codecs -- `arx` applies domain-dictionary substitution, brotli compression (quality 11), and binary-to-text encoding for best-in-class compression. Four wire shapes are candidates: base76 (ASCII, 77 fragment-safe chars), base64url (RFC 4648 `A-Za-z0-9-_` with a `B.` prefix for detection), base1k (Unicode, 1774 chars from U+00A1–U+07FF), and baseBMP (high-density Unicode, ~62k safe BMP code points from U+00A1–U+FFEF, ~15.92 bits/char). The async encoder tries all four and picks the shortest **transport** length (percent-encoded UTF-8 length for non-ASCII), so base64url can win over Unicode encodings on chat-style surfaces. baseBMP produces ~32% fewer characters than base1k and ~55% fewer than base76 for the same compressed bytes, achieving ~70% smaller fragments than deflate on typical payloads (~6.1x compression ratio for 8k markdown). Full pipeline timing is on the order of ~8–14ms for 8k payloads depending on the wire encoding. The substitution dictionary is served as a static file at `/arx-dictionary.json` so agents can fetch it for local compression; a pre-compressed `/arx-dictionary.json.br` variant is also available. The viewer loads the dictionary on startup and falls back to a built-in table if the fetch fails. -- `arx2` keeps the arx compression stack but replaces the JSON envelope with a compact tuple envelope and applies `/arx2-dictionary.json` as an overlay before the shared arx dictionary. It uses `v1.arx2..` and decodes back to the standard envelope before validation/rendering. +- `arx` applies domain-dictionary substitution, brotli compression (quality 11), and binary-to-text encoding for best-in-class compression. Four wire shapes are candidates: base76 (ASCII, 77 fragment-safe chars), base64url (RFC 4648 `A-Za-z0-9-_` with a `B.` prefix for detection), base1k (Unicode, 1774 chars from U+00A1–U+07FF), and baseBMP (high-density Unicode, ~62k safe BMP code points from U+00A1–U+FFEF, ~15.92 bits/char). The async encoder tries all four and picks the shortest **transport** length (percent-encoded UTF-8 length for non-ASCII), so base64url can win over Unicode encodings on chat-style surfaces. baseBMP produces ~32% fewer characters than base1k and ~55% fewer than base76 for the same compressed bytes, achieving ~70% smaller fragments than deflate on typical payloads (~6.1x compression ratio for 8k markdown). Full pipeline timing is on the order of ~8–14ms for 8k payloads depending on the wire encoding. The substitution dictionary is served as a static file at `/arx-dictionary.json` so agents can fetch it for local compression; a pre-compressed `/arx-dictionary.json.br` variant is also available. The viewer tries the pre-compressed dictionary first on default ARX loads, falls back to the JSON file, and only loads external dictionaries when an ARX/ARX2 encode or decode path needs them. +- `arx2` keeps the arx compression stack but replaces the JSON envelope with a compact tuple envelope and applies `/arx2-dictionary.json` as an overlay before the shared arx dictionary. The viewer tries `/arx2-dictionary.json.br` first for default overlay loads and falls back to JSON. It uses `v1.arx2..` and decodes back to the standard envelope before validation/rendering. - packed wire mode (`p: 1`) shortens transport keys before compression, then unpacks back to the standard envelope during decode - automatic async codec selection tries `arx2 -> arx -> deflate -> lz -> plain`; arx compares packed + non-packed candidates, while arx2 uses its tuple envelope - sync codec selection (used by examples and legacy paths) tries `deflate -> lz -> plain` diff --git a/docs/dependency-notes.md b/docs/dependency-notes.md index b2843c2..e181170 100644 --- a/docs/dependency-notes.md +++ b/docs/dependency-notes.md @@ -12,7 +12,6 @@ - `@replit/codemirror-indentation-markers` - MIT - `@git-diff-view/*` - MIT - `papaparse` - MIT -- `@tanstack/react-table` - MIT - `lz-string` - MIT - `fflate` - MIT - `brotli-wasm` - Apache-2.0 @@ -28,10 +27,10 @@ ## Why these libraries - `react-markdown` plus `remark-gfm` plus `rehype-sanitize` covers the markdown path without introducing unsafe raw HTML by default. -- CodeMirror handles raw source and raw JSON well because it is excellent at read-only code presentation. +- CodeMirror handles source artifacts and markdown code fences because it is excellent at read-only code presentation; JSON, markdown raw, and CSV raw views use lighter native source blocks. - `@replit/codemirror-indentation-markers` replaces custom indent-guide logic with a maintained CM6 extension. -- `@git-diff-view/*` fits review-style diffs better than a generic merge editor for the current viewer. -- `papaparse` plus `@tanstack/react-table` keeps CSV parsing and rendering readable without coupling to a heavyweight data-grid framework. +- `@git-diff-view/*` fits review-style diffs better than a generic merge editor for the current viewer. Its pure CSS file is mirrored into `public/vendor/diff-view-pure.css` with a Brotli-compressed `public/vendor/diff-view-pure.css.br` copy by `npm run assets:compress`, and loaded only by the diff renderer; `tests/diff-style-asset.test.ts` keeps those assets in sync with the package copy. +- `papaparse` handles CSV parsing; CSV rendering uses a native read-only table to avoid a data-grid dependency for the shipped static viewer. - `fflate` provides portable deflate/inflate support across iOS Safari and Android Chromium without relying on browser-specific compression streams. - `brotli-wasm` provides the arx/arx2 Brotli compression layer, including streaming decompression used to cap expanded output before allocating oversized decoded payloads. - `mermaid` renders diagram definitions (flowcharts, sequence diagrams, etc.) to SVG client-side. Dynamically imported within the markdown renderer so it does not affect initial bundle size. @@ -41,3 +40,5 @@ - `rehype-highlight` was removed after review because markdown fences now reuse the CodeMirror viewer stack directly. - `vanilla-jsoneditor` was removed because its bundle cost was too high for the default JSON tree-view use case in a viewer-first product. +- `clsx` and `tailwind-merge` were removed because the app only needed simple conditional string joining, and the merge runtime was being pulled into shared client chunks. +- `next-themes` was removed because the static shell only needs to preserve the `theme` localStorage key and synchronize the `html.dark` class. diff --git a/docs/deployment.md b/docs/deployment.md index c1a80a5..21dfd14 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -33,7 +33,7 @@ NEXT_PUBLIC_BASE_PATH=/agent-render npm run build npm run preview ``` -Then serve `out/` under `/agent-render/` and open the sample fragment links from the landing page. +The preview server reads the generated build manifest, serves `out/` under `/agent-render/`, and keeps root requests working for convenience. Open the sample fragment links from the landing page. The preview server intentionally preserves the fragment payload and does not rely on hash-based in-page navigation for diff files. diff --git a/docs/payload-format.md b/docs/payload-format.md index e4ec47f..4d7c361 100644 --- a/docs/payload-format.md +++ b/docs/payload-format.md @@ -21,7 +21,7 @@ Supported codecs: - `plain` - base64url-encoded JSON - `lz` - `lz-string` compressed JSON encoded for URL-safe transport - `deflate` - deflate-compressed UTF-8 JSON bytes encoded as base64url -- `arx` - domain-dictionary substitution + brotli (quality 11) + binary-to-text encoding. arx fragments include dictionary version metadata in the outer format (`v1.arx..`) so links stay portable across dictionary updates. Four wire shapes are tried and the shortest **transport** size wins (see `computeTransportLength` in `fragment.ts` — non-ASCII Unicode may count longer after percent-encoding): **base76** (ASCII-only, 77 fragment-safe chars), **base64url** (standard RFC 4648 alphabet `A-Za-z0-9-_`, no padding, prefixed with `B.` for detection), **base1k** (Unicode, 1774 chars from U+00A1–U+07FF), and **baseBMP** (high-density Unicode, ~62k safe BMP code points from U+00A1–U+FFEF, ~15.92 bits/char). BaseBMP produces ~32% fewer characters than base1k and ~55% fewer than base76 for the same compressed bytes. BaseBMP payloads are prefixed with a U+FFF0 marker for detection. The viewer’s `arxDecompress` auto-detects the wire shape (including the rare case where a base76 length prefix is also `B.` — it tries base64url first and falls back to base76 if Brotli fails). The substitution dictionary is served at `/arx-dictionary.json` (with a pre-compressed `/arx-dictionary.json.br` variant) so agents can fetch it for local compression. +- `arx` - domain-dictionary substitution + brotli (quality 11) + binary-to-text encoding. arx fragments include dictionary version metadata in the outer format (`v1.arx..`) so links stay portable across dictionary updates. Four wire shapes are tried and the shortest **transport** size wins (see `computeTransportLength` in `fragment.ts` — non-ASCII Unicode may count longer after percent-encoding): **base76** (ASCII-only, 77 fragment-safe chars), **base64url** (standard RFC 4648 alphabet `A-Za-z0-9-_`, no padding, prefixed with `B.` for detection), **base1k** (Unicode, 1774 chars from U+00A1–U+07FF), and **baseBMP** (high-density Unicode, ~62k safe BMP code points from U+00A1–U+FFEF, ~15.92 bits/char). BaseBMP produces ~32% fewer characters than base1k and ~55% fewer than base76 for the same compressed bytes. BaseBMP payloads are prefixed with a U+FFF0 marker for detection. The viewer’s `arxDecompress` auto-detects the wire shape (including the rare case where a base76 length prefix is also `B.` — it tries base64url first and falls back to base76 if Brotli fails). The substitution dictionary is served at `/arx-dictionary.json` with a pre-compressed `/arx-dictionary.json.br` variant; the viewer tries the `.br` file first on default loads and falls back to JSON. The arx2 overlay dictionary follows the same `.br`-then-JSON default load pattern. - `arx2` - tuple-envelope transport + arx2 overlay substitution + the shared arx dictionary + brotli (quality 11) + the same four binary-to-text wire shapes. arx2 fragments use `v1.arx2..`, where `dictVersion` is the shared arx dictionary version. Existing `arx` links remain valid; async auto-selection tries arx2 and arx and keeps the shortest transport. The encoder now also supports a packed wire representation (`p: 1`) that shortens key names before compression. Packed mode is transport-only; decoded envelopes normalize back to the standard shape. @@ -110,9 +110,9 @@ When a payload does not fit the fragment budget or the target surface is hostile Running `npm run bench:codecs` checks a fixed corpus across markdown, code, diff, CSV, JSON, and multi-artifact bundles. The current committed baseline shows: -- total `arx`: 11,889 brotli bytes -- total `arx2`: 11,767 brotli bytes -- `arx2` delta: 1.03% smaller overall +- total `arx`: 3,307 brotli bytes +- total `arx2`: 3,192 brotli bytes +- `arx2` delta: 3.48% smaller overall The gate fails if arx2 is less than 0.5% smaller overall or if any individual corpus row regresses by more than 0.5%. Use `npm run bench:codecs:update` only when intentionally refreshing the committed baseline. @@ -124,7 +124,7 @@ Internal viewer navigation, such as moving between files inside a multi-file dif ## Examples -Two sample envelopes live in `src/lib/payload/examples.ts` for local development and documentation. +Sample envelopes live in `src/lib/payload/examples.ts` for local development and documentation. The homepage uses precomputed sample link data that is checked against those generated examples in tests, keeping the large sample strings out of the initial shell chunk. ### Markdown artifact example diff --git a/docs/testing.md b/docs/testing.md index a9155dc..390594b 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -11,8 +11,10 @@ ```bash npm run test npm run test:watch +npm run assets:compress npm run bench:codecs npm run bench:codecs:update +npm run check:build-budgets npm run test:e2e npm run test:e2e:update npm run test:browsers @@ -45,7 +47,9 @@ The suite is intentionally split by responsibility: - visual tests protect empty state, artifact views, theme presentation, and compact-content spacing - component tests protect selector/disclosure UI contracts - unit tests protect transport codecs, envelope validation, diff parsing, and language inference -- `npm run bench:codecs` protects arx/arx2 compression ratios against the committed `scripts/bench-baseline.json` +- `npm run assets:compress` regenerates minified/precompressed public assets, including the ARX dictionaries and mirrored diff-view stylesheet +- `npm run bench:codecs` protects arx/arx2 compression ratios against the committed `scripts/bench-baseline.json`; its corpus is fixed in `scripts/bench-codecs.mjs` so unrelated source, docs, or package metadata edits do not create false codec regressions +- `npm run check:build-budgets` reads the generated `.next` manifests after `npm run build` and fails if the homepage shell or key deferred renderer chunks exceed their gzip budgets ## Self-hosted mode tests @@ -61,4 +65,4 @@ They run as part of the standard `npm run test` command. ## CI -The repository includes `.github/workflows/test.yml`, which installs Playwright browsers and runs `npm run test:ci` on pushes, pull requests, and manual dispatch. +The repository includes `.github/workflows/test.yml`, which installs Playwright browsers and runs `npm run test:ci` on pushes, pull requests, and manual dispatch. That CI command includes the exported-app browser suite and the generated build-budget check. diff --git a/eslint.config.mjs b/eslint.config.mjs index 72f8a34..8752a43 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,7 +12,7 @@ const compat = new FlatCompat({ const config = [...compat.extends("next/core-web-vitals")]; config.push({ - ignores: ["out/**", ".next/**"], + ignores: [".next/**", "out/**", "playwright-report/**", "test-results/**"], }); export default config; diff --git a/next-env.d.ts b/next-env.d.ts index 1b3be08..830fb59 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/next.config.ts b/next.config.ts index 07e4702..3b060e7 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,10 @@ import type { NextConfig } from "next"; const configuredBasePath = process.env.NEXT_PUBLIC_BASE_PATH?.trim() ?? ""; -const basePath = configuredBasePath === "/" ? "" : configuredBasePath.replace(/\/$/, ""); +const basePath = + configuredBasePath === "" || configuredBasePath === "/" + ? "" + : `${configuredBasePath.startsWith("/") ? "" : "/"}${configuredBasePath}`.replace(/\/$/, ""); const nextConfig: NextConfig = { output: "export", diff --git a/package-lock.json b/package-lock.json index 71f223f..72a4969 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@codemirror/commands": "^6.10.1", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.9", "@codemirror/lang-javascript": "^6.2.4", @@ -18,28 +17,22 @@ "@codemirror/lang-python": "^6.2.1", "@codemirror/lang-yaml": "^6.1.2", "@codemirror/language": "^6.12.3", - "@codemirror/search": "^6.5.10", "@codemirror/state": "^6.6.0", "@codemirror/view": "^6.38.2", - "@git-diff-view/file": "^0.1.1", "@git-diff-view/react": "^0.1.1", "@replit/codemirror-indentation-markers": "^6.5.3", - "@tanstack/react-table": "^8.21.3", "brotli-wasm": "^3.0.1", - "clsx": "^2.1.1", "fflate": "^0.8.2", "lucide-react": "^0.577.0", "lz-string": "^1.5.0", - "mermaid": "^11.14.0", - "next": "15.1.11", - "next-themes": "^0.4.6", + "mermaid": "^11.15.0", + "next": "15.5.18", "papaparse": "^5.5.3", "react": "19.1.0", "react-dom": "19.1.0", "react-markdown": "^10.1.0", "rehype-sanitize": "^6.0.0", - "remark-gfm": "^4.0.1", - "tailwind-merge": "^3.4.0" + "remark-gfm": "^4.0.1" }, "devDependencies": { "@playwright/test": "^1.58.2", @@ -53,7 +46,7 @@ "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "eslint": "^9.22.0", - "eslint-config-next": "15.1.11", + "eslint-config-next": "15.5.18", "jsdom": "^28.1.0", "tailwindcss": "^4.0.6", "tsx": "^4.21.0", @@ -151,7 +144,6 @@ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", @@ -167,7 +159,6 @@ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -201,41 +192,10 @@ "specificity": "bin/cli.js" } }, - "node_modules/@chevrotain/cst-dts-gen": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-12.0.0.tgz", - "integrity": "sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==", - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/gast": "12.0.0", - "@chevrotain/types": "12.0.0" - } - }, - "node_modules/@chevrotain/gast": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-12.0.0.tgz", - "integrity": "sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==", - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/types": "12.0.0" - } - }, - "node_modules/@chevrotain/regexp-to-ast": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-12.0.0.tgz", - "integrity": "sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==", - "license": "Apache-2.0" - }, "node_modules/@chevrotain/types": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-12.0.0.tgz", - "integrity": "sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==", - "license": "Apache-2.0" - }, - "node_modules/@chevrotain/utils": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-12.0.0.tgz", - "integrity": "sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", "license": "Apache-2.0" }, "node_modules/@codemirror/autocomplete": { @@ -250,18 +210,6 @@ "@lezer/common": "^1.0.0" } }, - "node_modules/@codemirror/commands": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.2.tgz", - "integrity": "sha512-vvX1fsih9HledO1c9zdotZYUZnE4xV0m6i3m25s5DIfXofuprk6cRcLUZvSk3CASUbwjQX21tOGbkY2BH8TpnQ==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.4.0", - "@codemirror/view": "^6.27.0", - "@lezer/common": "^1.1.0" - } - }, "node_modules/@codemirror/lang-css": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", @@ -365,6 +313,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -385,22 +334,12 @@ "crelt": "^1.0.5" } }, - "node_modules/@codemirror/search": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.6.0.tgz", - "integrity": "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.37.0", - "crelt": "^1.0.5" - } - }, "node_modules/@codemirror/state": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", "license": "MIT", + "peer": true, "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } @@ -410,6 +349,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.16.tgz", "integrity": "sha512-m6S22fFpKtOWhq8HuhzsI1WzUP/hB9THbDj0Tl5KX4gbO6Y91hwBl7Yky33NdvB6IffuRFiBxf1R8kJMyXmA4Q==", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -505,6 +445,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -545,6 +486,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } @@ -1198,19 +1140,6 @@ "lowlight": "^3.3.0" } }, - "node_modules/@git-diff-view/file": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@git-diff-view/file/-/file-0.1.1.tgz", - "integrity": "sha512-AaqRq53ks/gfFl47jVtgN3BFuOzqnLN/NAKGLcgRniytlijy5km6zJBMUHfYHBGKxhRdiRBmkd2IupOoXsfzhg==", - "license": "MIT", - "dependencies": { - "@git-diff-view/core": "^0.1.1", - "diff": "^8.0.3", - "fast-diff": "^1.3.0", - "highlight.js": "^11.11.0", - "lowlight": "^3.3.0" - } - }, "node_modules/@git-diff-view/lowlight": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@git-diff-view/lowlight/-/lowlight-0.1.1.tgz", @@ -1310,10 +1239,20 @@ "mlly": "^1.8.0" } }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "cpu": [ "arm64" ], @@ -1329,13 +1268,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "cpu": [ "x64" ], @@ -1351,13 +1290,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "cpu": [ "arm64" ], @@ -1371,9 +1310,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "cpu": [ "x64" ], @@ -1387,9 +1326,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "cpu": [ "arm" ], @@ -1403,9 +1342,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "cpu": [ "arm64" ], @@ -1418,10 +1357,42 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "cpu": [ "s390x" ], @@ -1435,9 +1406,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "cpu": [ "x64" ], @@ -1451,9 +1422,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "cpu": [ "arm64" ], @@ -1467,9 +1438,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "cpu": [ "x64" ], @@ -1483,9 +1454,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "cpu": [ "arm" ], @@ -1501,13 +1472,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" + "@img/sharp-libvips-linux-arm": "1.2.4" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "cpu": [ "arm64" ], @@ -1523,13 +1494,57 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ "s390x" ], @@ -1545,13 +1560,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" + "@img/sharp-libvips-linux-s390x": "1.2.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" ], @@ -1567,13 +1582,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" + "@img/sharp-libvips-linux-x64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" ], @@ -1589,13 +1604,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" ], @@ -1611,21 +1626,40 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", "cpu": [ "wasm32" ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.2.0" + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1634,9 +1668,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "cpu": [ "ia32" ], @@ -1653,9 +1687,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "cpu": [ "x64" ], @@ -1828,12 +1862,12 @@ "license": "MIT" }, "node_modules/@mermaid-js/parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.0.tgz", - "integrity": "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.1.tgz", + "integrity": "sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==", "license": "MIT", "dependencies": { - "langium": "^4.0.0" + "@chevrotain/types": "~11.1.1" } }, "node_modules/@napi-rs/wasm-runtime": { @@ -1850,15 +1884,15 @@ } }, "node_modules/@next/env": { - "version": "15.1.11", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.11.tgz", - "integrity": "sha512-yp++FVldfLglEG5LoS2rXhGypPyoSOyY0kxZQJ2vnlYJeP8o318t5DrDu5Tqzr03qAhDWllAID/kOCsXNLcwKw==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.18.tgz", + "integrity": "sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.1.11", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.1.11.tgz", - "integrity": "sha512-jpAu+46v5FF/TO8YUdOBHn/Wr4SCiU4IgjQ45S9Nn3vR4nZVS2SR+m9lpxcCv/xqMUoYuYFQZUP0H/ptw0W6+w==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.18.tgz", + "integrity": "sha512-w4MYq8M26a8PNrfto0JosLf5/3ssln1rsyP96g2DkC8uFVymStM5DLSz5ElxxrPRg2XnTMnFo3kREFlhYvxhWw==", "dev": true, "license": "MIT", "dependencies": { @@ -1866,9 +1900,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.1.9", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.9.tgz", - "integrity": "sha512-sQF6MfW4nk0PwMYYq8xNgqyxZJGIJV16QqNDgaZ5ze9YoVzm4/YNx17X0exZudayjL9PF0/5RGffDtzXapch0Q==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.18.tgz", + "integrity": "sha512-w0WvQf1n+txiwns/9pwIQteCJpZTbxzO2SE0FLcwuD4v0WEh1JPOjdyxWL21XwJsdpx8cFRjyzxzCS/siP7HcQ==", "cpu": [ "arm64" ], @@ -1882,9 +1916,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.1.9", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.9.tgz", - "integrity": "sha512-fp0c1rB6jZvdSDhprOur36xzQvqelAkNRXM/An92sKjjtaJxjlqJR8jiQLQImPsClIu8amQn+ZzFwl1lsEf62w==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.18.tgz", + "integrity": "sha512-znn71QmDuxm+BOaglihMZfvyySMnNljkVIY5Z2TCssBmm+WqL6c19VhtH5ktFkHa8EZ2bnTUpcNcmNSQsg67og==", "cpu": [ "x64" ], @@ -1898,9 +1932,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.1.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.9.tgz", - "integrity": "sha512-77rYykF6UtaXvxh9YyRIKoaYPI6/YX6cy8j1DL5/1XkjbfOwFDfTEhH7YGPqG/ePl+emBcbDYC2elgEqY2e+ag==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.18.tgz", + "integrity": "sha512-yPPe5MNL+igZUa+OsqQJisqSfh6oarIuA1Q0BDxljGJhRQyZeP+WRHh7rs/jZUGMh5aY0YdIjXZG0VohkKkUdw==", "cpu": [ "arm64" ], @@ -1914,9 +1948,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.1.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.9.tgz", - "integrity": "sha512-uZ1HazKcyWC7RA6j+S/8aYgvxmDqwnG+gE5S9MhY7BTMj7ahXKunpKuX8/BA2M7OvINLv7LTzoobQbw928p3WA==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.18.tgz", + "integrity": "sha512-glaCczEWIrHsokFZ3pP08U4BpKxwIdnT+txdOM32OBgpL9Yw4aqx8NejmgtZQZOdstQ5f0L3CasIZudzCuD+nw==", "cpu": [ "arm64" ], @@ -1930,9 +1964,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.1.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.9.tgz", - "integrity": "sha512-gQIX1d3ct2RBlgbbWOrp+SHExmtmFm/HSW1Do5sSGMDyzbkYhS2sdq5LRDJWWsQu+/MqpgJHqJT6ORolKp/U1g==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.18.tgz", + "integrity": "sha512-oUfg2EgJmU3R0OCOWiokGFUTvZiPfXtriXiuF3YNxRoROCdgvTedHIzYoeKH34gsZxS/V7mHbfq2hpAHwhH1/A==", "cpu": [ "x64" ], @@ -1946,9 +1980,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.1.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.9.tgz", - "integrity": "sha512-fJOwxAbCeq6Vo7pXZGDP6iA4+yIBGshp7ie2Evvge7S7lywyg7b/SGqcvWq/jYcmd0EbXdb7hBfdqSQwTtGTPg==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.18.tgz", + "integrity": "sha512-JLxSP3KTd9iu/bvUMQxH7RJo9xKSHf55/6RPE4a6FTSZygGn7uvZbCej0AHXydwkggQGSD9UddSjwv6Xz5ESfA==", "cpu": [ "x64" ], @@ -1962,9 +1996,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.1.9", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.9.tgz", - "integrity": "sha512-crfbUkAd9PVg9nGfyjSzQbz82dPvc4pb1TeP0ZaAdGzTH6OfTU9kxidpFIogw0DYIEadI7hRSvuihy2NezkaNQ==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.18.tgz", + "integrity": "sha512-ir1v7enP52K2HNz3tQQvwF+x7VNxBk1ciiZ18WBPvxf4C59IqdfmHPJYK3vH7rSxpuCVw/8C712wTXNAtEp+NA==", "cpu": [ "arm64" ], @@ -1978,9 +2012,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.1.9", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.9.tgz", - "integrity": "sha512-SBB0oA4E2a0axUrUwLqXlLkSn+bRx9OWU6LheqmRrO53QEAJP7JquKh3kF0jRzmlYOWFZtQwyIWJMEJMtvvDcQ==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.18.tgz", + "integrity": "sha512-LIu5me6QTANCd25E7I5uIEfvgQ06RK7tvHAbYo3zCb3VpxQEPvMcSpd87NwUABDT6MbGPdEGR5VRiK4PPTJhQg==", "cpu": [ "x64" ], @@ -2047,6 +2081,7 @@ "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright": "1.58.2" }, @@ -2439,12 +2474,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" - }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -2789,39 +2818,6 @@ "tailwindcss": "4.2.1" } }, - "node_modules/@tanstack/react-table": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", - "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", - "license": "MIT", - "dependencies": { - "@tanstack/table-core": "8.21.3" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/@tanstack/table-core": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", - "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", @@ -2849,7 +2845,6 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -2939,8 +2934,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/better-sqlite3": { "version": "7.6.13", @@ -3316,6 +3310,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3326,6 +3321,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -3388,6 +3384,7 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -3542,9 +3539,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { @@ -4041,6 +4038,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4091,7 +4089,6 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -4438,9 +4435,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -4495,17 +4492,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -4663,34 +4649,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/chevrotain": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-12.0.0.tgz", - "integrity": "sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==", - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/cst-dts-gen": "12.0.0", - "@chevrotain/gast": "12.0.0", - "@chevrotain/regexp-to-ast": "12.0.0", - "@chevrotain/types": "12.0.0", - "@chevrotain/utils": "12.0.0" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/chevrotain-allstar": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.4.3.tgz", - "integrity": "sha512-2X4mkroolSMKqW+H22pyPMUVDqYZzPhephTmg/NODKb1IGYPHfxfhcW0EjS7wcPJNbze2i4vBWT7zT5FKF2lrQ==", - "license": "MIT", - "dependencies": { - "lodash-es": "^4.18.1" - }, - "peerDependencies": { - "chevrotain": "^12.0.0" - } - }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -4704,34 +4662,11 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -4744,20 +4679,9 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true, + "dev": true, "license": "MIT" }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -4868,6 +4792,7 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10" } @@ -5268,6 +5193,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -5590,15 +5516,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/diff": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", - "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -5617,13 +5534,12 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dompurify": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", - "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.2.tgz", + "integrity": "sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -5872,6 +5788,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-toolkit": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", + "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/esbuild": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", @@ -5933,6 +5859,7 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5988,13 +5915,13 @@ } }, "node_modules/eslint-config-next": { - "version": "15.1.11", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.1.11.tgz", - "integrity": "sha512-RK5q3f8CKMTwNXULOqd2TAsz+7kA5+5fy5YK7T6SeczLFOuOUcuJOGlYUbyoeU6+UKQrpFsYgCz71hI1F9q5Cg==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.18.tgz", + "integrity": "sha512-HuoJU6uUPD00eyiud78IBnT4HLhztFj2V+ild2Uon5ZUrYZKe0Olu2QRD99e9IgL4/H1eg5Onka3BsfRW2U0Xw==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.1.11", + "@next/eslint-plugin-next": "15.5.18", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -6106,6 +6033,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -6542,9 +6470,9 @@ } }, "node_modules/flatted": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.0.tgz", - "integrity": "sha512-kC6Bb+ooptOIvWj5B63EQWkF0FEnNjV2ZNkLMLZRDDduIiWeFF4iKnslwhiWxjAdbg4NzTNo6h0qLuvFrcx+Sw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -7158,13 +7086,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT", - "optional": true - }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -7656,6 +7577,7 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -7781,24 +7703,6 @@ "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" }, - "node_modules/langium": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/langium/-/langium-4.2.3.tgz", - "integrity": "sha512-sOPIi4hISFnY7twwV97ca1TsxpBtXq0URu/LL1AvxwccPG/RIBBlKS7a/f/EL6w8lTNaS0EFs/F+IdSOaqYpng==", - "license": "MIT", - "dependencies": { - "@chevrotain/regexp-to-ast": "~12.0.0", - "chevrotain": "~12.0.0", - "chevrotain-allstar": "~0.4.3", - "vscode-languageserver": "~9.0.1", - "vscode-languageserver-textdocument": "~1.0.11", - "vscode-uri": "~3.1.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -8537,14 +8441,14 @@ } }, "node_modules/mermaid": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.14.0.tgz", - "integrity": "sha512-GSGloRsBs+JINmmhl0JDwjpuezCsHB4WGI4NASHxL3fHo3o/BRXTxhDLKnln8/Q0lRFRyDdEjmk1/d5Sn1Xz8g==", + "version": "11.15.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.15.0.tgz", + "integrity": "sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.2", - "@mermaid-js/parser": "^1.1.0", + "@mermaid-js/parser": "^1.1.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.1", @@ -8555,14 +8459,14 @@ "dagre-d3-es": "7.0.14", "dayjs": "^1.11.19", "dompurify": "^3.3.1", + "es-toolkit": "^1.45.1", "katex": "^0.16.25", "khroma": "^2.1.0", - "lodash-es": "^4.17.23", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", - "uuid": "^11.1.0" + "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "node_modules/micromark": { @@ -9262,15 +9166,13 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.1.11", - "resolved": "https://registry.npmjs.org/next/-/next-15.1.11.tgz", - "integrity": "sha512-UiVJaOGhKST58AadwbFUZThlNBmYhKqaCs8bVtm4plTxsgKq0mJ0zTsp7t7j/rzsbAEj9WcAMdZCztjByi4EoQ==", + "version": "15.5.18", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.18.tgz", + "integrity": "sha512-eKL8zUJkX9Y5lE+RX/2YJoItVdGlIscyVyboeD9wSpp0PaGqjoA4tTpT2qPqz9ax+5IzGESyLSeZ/RCwbSZ2uQ==", "license": "MIT", "dependencies": { - "@next/env": "15.1.11", - "@swc/counter": "0.1.3", + "@next/env": "15.5.18", "@swc/helpers": "0.5.15", - "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" @@ -9282,19 +9184,19 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.1.9", - "@next/swc-darwin-x64": "15.1.9", - "@next/swc-linux-arm64-gnu": "15.1.9", - "@next/swc-linux-arm64-musl": "15.1.9", - "@next/swc-linux-x64-gnu": "15.1.9", - "@next/swc-linux-x64-musl": "15.1.9", - "@next/swc-win32-arm64-msvc": "15.1.9", - "@next/swc-win32-x64-msvc": "15.1.9", - "sharp": "^0.33.5" + "@next/swc-darwin-arm64": "15.5.18", + "@next/swc-darwin-x64": "15.5.18", + "@next/swc-linux-arm64-gnu": "15.5.18", + "@next/swc-linux-arm64-musl": "15.5.18", + "@next/swc-linux-x64-gnu": "15.5.18", + "@next/swc-linux-x64-musl": "15.5.18", + "@next/swc-win32-arm64-msvc": "15.5.18", + "@next/swc-win32-x64-msvc": "15.5.18", + "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.41.2", + "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", @@ -9315,44 +9217,6 @@ } } }, - "node_modules/next-themes": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", - "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "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.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/node-abi": { "version": "3.89.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", @@ -9716,9 +9580,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -9775,6 +9639,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -9812,10 +9677,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", - "dev": true, + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "funding": [ { "type": "opencollective", @@ -9884,7 +9748,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -9900,7 +9763,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -9913,8 +9775,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/prop-types": { "version": "15.8.1", @@ -10011,6 +9872,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10020,6 +9882,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -10547,16 +10410,16 @@ } }, "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, "license": "Apache-2.0", "optional": true, "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.3" + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -10565,25 +10428,30 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" } }, "node_modules/shebang-command": { @@ -10739,16 +10607,6 @@ "simple-concat": "^1.0.0" } }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "license": "MIT", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -10803,14 +10661,6 @@ "node": ">= 0.4" } }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -11070,16 +10920,6 @@ "dev": true, "license": "MIT" }, - "node_modules/tailwind-merge": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", - "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, "node_modules/tailwindcss": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", @@ -11183,11 +11023,12 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11331,6 +11172,7 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -11455,6 +11297,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11489,9 +11332,9 @@ } }, "node_modules/undici": { - "version": "7.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", - "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", "dev": true, "license": "MIT", "engines": { @@ -11654,16 +11497,16 @@ "optional": true }, "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist/esm/bin/uuid" + "uuid": "dist-node/bin/uuid" } }, "node_modules/vfile": { @@ -11695,11 +11538,12 @@ } }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -11788,11 +11632,12 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11879,9 +11724,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -11891,55 +11736,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-languageserver": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", - "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", - "license": "MIT", - "dependencies": { - "vscode-languageserver-protocol": "3.17.5" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", - "license": "MIT", - "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "license": "MIT" - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "license": "MIT" - }, - "node_modules/vscode-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", - "license": "MIT" - }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", diff --git a/package.json b/package.json index abc741a..12f7b54 100644 --- a/package.json +++ b/package.json @@ -23,27 +23,28 @@ ], "scripts": { "dev": "next dev", - "build": "next build", + "build": "node scripts/clean-build-output.mjs && next build", "preview": "node scripts/serve-export.mjs", "start": "npm run preview", "lint": "eslint . && npm run check:public-export-docs", "test": "vitest run", "test:watch": "vitest", - "test:e2e": "playwright test", - "test:e2e:update": "playwright test --update-snapshots", - "test:ci": "npm run lint && npm run test && npm run typecheck && npm run test:e2e", + "test:e2e": "env -u NO_COLOR playwright test", + "test:e2e:update": "env -u NO_COLOR playwright test --update-snapshots", + "test:ci": "npm run lint && npm run test && npm run typecheck && npm run test:e2e && npm run check:build-budgets", "test:browsers": "playwright install chromium", "bench:codecs": "node scripts/bench-codecs.mjs", "bench:codecs:update": "node scripts/bench-codecs.mjs --write-baseline", + "assets:compress": "node scripts/compress-dictionary.mjs", "typecheck": "node scripts/ensure-next-types.mjs && tsc --noEmit", - "check": "npm run lint && npm run test && npm run bench:codecs && npm run typecheck && npm run build", + "check": "npm run lint && npm run test && npm run bench:codecs && npm run typecheck && npm run build && npm run check:build-budgets", + "check:build-budgets": "node scripts/check-build-budgets.mjs", "check:public-export-docs": "node scripts/check-public-export-docs.mjs", "selfhosted:dev": "node --import tsx selfhosted/server.ts", "selfhosted:build": "tsc -p selfhosted/tsconfig.json", "selfhosted:start": "node selfhosted/dist/server.js" }, "dependencies": { - "@codemirror/commands": "^6.10.1", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.9", "@codemirror/lang-javascript": "^6.2.4", @@ -52,28 +53,22 @@ "@codemirror/lang-python": "^6.2.1", "@codemirror/lang-yaml": "^6.1.2", "@codemirror/language": "^6.12.3", - "@codemirror/search": "^6.5.10", "@codemirror/state": "^6.6.0", "@codemirror/view": "^6.38.2", - "@git-diff-view/file": "^0.1.1", "@git-diff-view/react": "^0.1.1", "@replit/codemirror-indentation-markers": "^6.5.3", - "@tanstack/react-table": "^8.21.3", "brotli-wasm": "^3.0.1", - "clsx": "^2.1.1", "fflate": "^0.8.2", "lucide-react": "^0.577.0", "lz-string": "^1.5.0", - "mermaid": "^11.14.0", - "next": "15.1.11", - "next-themes": "^0.4.6", + "mermaid": "^11.15.0", + "next": "15.5.18", "papaparse": "^5.5.3", "react": "19.1.0", "react-dom": "19.1.0", "react-markdown": "^10.1.0", "rehype-sanitize": "^6.0.0", - "remark-gfm": "^4.0.1", - "tailwind-merge": "^3.4.0" + "remark-gfm": "^4.0.1" }, "devDependencies": { "@playwright/test": "^1.58.2", @@ -87,7 +82,7 @@ "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "eslint": "^9.22.0", - "eslint-config-next": "15.1.11", + "eslint-config-next": "15.5.18", "jsdom": "^28.1.0", "tailwindcss": "^4.0.6", "tsx": "^4.21.0", @@ -97,6 +92,11 @@ "optionalDependencies": { "better-sqlite3": "^12.8.0" }, + "overrides": { + "next": { + "postcss": "8.5.14" + } + }, "engines": { "node": ">=20.10.0" } diff --git a/playwright.config.ts b/playwright.config.ts index a6b61e6..3ea64fa 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,6 +1,7 @@ import { defineConfig, devices } from "@playwright/test"; const port = Number(process.env.PLAYWRIGHT_PORT || 4401); +const cleanColorEnv = "env -u NO_COLOR"; export default defineConfig({ testDir: "tests/e2e", @@ -24,7 +25,7 @@ export default defineConfig({ video: "retain-on-failure", }, webServer: { - command: `NEXT_PUBLIC_BASE_PATH=/agent-render npm run build && PORT=${port} NEXT_PUBLIC_BASE_PATH=/agent-render npm run preview`, + command: `${cleanColorEnv} NEXT_PUBLIC_BASE_PATH=/agent-render npm run build && ${cleanColorEnv} PORT=${port} NEXT_PUBLIC_BASE_PATH=/agent-render npm run preview`, port, reuseExistingServer: !process.env.CI, timeout: 120000, diff --git a/public/_headers b/public/_headers index 668acb5..05f6d97 100644 --- a/public/_headers +++ b/public/_headers @@ -4,3 +4,18 @@ X-Content-Type-Options: nosniff X-Frame-Options: DENY Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), bluetooth=(), camera=(), clipboard-read=(), clipboard-write=(self), display-capture=(), encrypted-media=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), xr-spatial-tracking=() + +/arx-dictionary.json.br + Content-Type: application/json; charset=utf-8 + Content-Encoding: br + Vary: Accept-Encoding + +/arx2-dictionary.json.br + Content-Type: application/json; charset=utf-8 + Content-Encoding: br + Vary: Accept-Encoding + +/vendor/diff-view-pure.css.br + Content-Type: text/css; charset=utf-8 + Content-Encoding: br + Vary: Accept-Encoding diff --git a/public/arx2-dictionary.json b/public/arx2-dictionary.json index f015f0f..fe769e1 100644 --- a/public/arx2-dictionary.json +++ b/public/arx2-dictionary.json @@ -1 +1 @@ -{"version":1,"description":"Overlay substitution dictionary for the arx2 tuple-envelope codec. These substitutions are applied before the shared arx v1 dictionary, then reversed after the v1 dictionary during decode.","singleByteSlots":["[\"m\",\"","[\"c\",\""],"extendedSlots":["[\"d\",\"","[\"s\",\"","[\"j\",\"","[2,","[3,","],[","\",\"","\",null",",null,\"",",null,null,\"",",\"split\"",",\"unified\"","export const ","export function ","export const value = ","export type ","export interface ","import { ","} from \""," as const","async function ","return <","className=\"","\\n## ","\\n### ","\\n- [x] ","\\n- [ ] ","](#","diff --git ","\\n+++","\\n---","\\n@@ -"]} +{"version":1,"description":"Overlay substitution dictionary for the arx2 tuple-envelope codec. These substitutions are applied before the shared arx v1 dictionary, then reversed after the v1 dictionary during decode.","singleByteSlots":["[\"m\",\"","[\"c\",\""],"extendedSlots":["[\"d\",\"","[\"s\",\"","[\"j\",\"","[2,","[3,","],[","\",\"","\",null",",null,\"",",null,null,\"",",\"split\"",",\"unified\"","export const ","export function ","export const value = ","export type ","export interface ","import { ","} from \""," as const","async function ","return <","className=\"","\\n## ","\\n### ","\\n- [x] ","\\n- [ ] ","](#","diff --git ","\\n+++","\\n---","\\n@@ -"]} \ No newline at end of file diff --git a/public/arx2-dictionary.json.br b/public/arx2-dictionary.json.br new file mode 100644 index 0000000000000000000000000000000000000000..f5a686ba7bf808ca274a321bce5928819833ef06 GIT binary patch literal 290 zcmV+-0p0!^p8^1k(D0$qx>ENo?x@&iz@=p3Eu8Iu-5l*gKuv>W)Y z^jIulh>Ptob{PX`I#hqe9n`oWu2=vsI5SHT^a>RCTmC=Pd&-O0H-r(Eh9AWSwW0d0 z-^q*{vSsv1zvc+`v)!|113#(UwJK!Mgfv0C{b}6cO6}t^<==R?;S9!TyWX4JcuZB_ zP(msM3akLmGrv_cMilbKKbb+^E%#E?LD4LovZghkq{moUL84-n!sO@*XSrr^=ZzUD zJvW{p@iDV}TI}r{Wj8>ax`eb4P%rclcbJFPu#?%;}dikls o(TFlGJqVJu<~04G=OZxwj%kt*mXi~znOCb7)`bxL^-8$Z17X09NB{r; literal 0 HcmV?d00001 diff --git a/public/vendor/diff-view-pure.css b/public/vendor/diff-view-pure.css new file mode 100644 index 0000000..81032e5 --- /dev/null +++ b/public/vendor/diff-view-pure.css @@ -0,0 +1,660 @@ +.diff-tailwindcss-wrapper .container { + width: 100%; +} +@media (min-width: 640px) { + .diff-tailwindcss-wrapper .container { + max-width: 640px; + } +} +@media (min-width: 768px) { + .diff-tailwindcss-wrapper .container { + max-width: 768px; + } +} +@media (min-width: 1024px) { + .diff-tailwindcss-wrapper .container { + max-width: 1024px; + } +} +@media (min-width: 1280px) { + .diff-tailwindcss-wrapper .container { + max-width: 1280px; + } +} +@media (min-width: 1536px) { + .diff-tailwindcss-wrapper .container { + max-width: 1536px; + } +} +.diff-tailwindcss-wrapper .invisible { + visibility: hidden; +} +.diff-tailwindcss-wrapper .absolute { + position: absolute; +} +.diff-tailwindcss-wrapper .relative { + position: relative; +} +.diff-tailwindcss-wrapper .sticky { + position: sticky; +} +.diff-tailwindcss-wrapper .left-0 { + left: 0px; +} +.diff-tailwindcss-wrapper .left-\[100\%\] { + left: 100%; +} +.diff-tailwindcss-wrapper .right-\[100\%\] { + right: 100%; +} +.diff-tailwindcss-wrapper .top-0 { + top: 0px; +} +.diff-tailwindcss-wrapper .top-\[50\%\] { + top: 50%; +} +.diff-tailwindcss-wrapper .z-\[1\] { + z-index: 1; +} +.diff-tailwindcss-wrapper .ml-\[-1\.5em\] { + margin-left: -1.5em; +} +.diff-tailwindcss-wrapper .block { + display: block; +} +.diff-tailwindcss-wrapper .inline-block { + display: inline-block; +} +.diff-tailwindcss-wrapper .flex { + display: flex; +} +.diff-tailwindcss-wrapper .table { + display: table; +} +.diff-tailwindcss-wrapper .hidden { + display: none; +} +.diff-tailwindcss-wrapper .h-\[50\%\] { + height: 50%; +} +.diff-tailwindcss-wrapper .h-full { + height: 100%; +} +.diff-tailwindcss-wrapper .min-h-\[28px\] { + min-height: 28px; +} +.diff-tailwindcss-wrapper .w-\[1\%\] { + width: 1%; +} +.diff-tailwindcss-wrapper .w-\[1\.5em\] { + width: 1.5em; +} +.diff-tailwindcss-wrapper .w-\[1\.5px\] { + width: 1.5px; +} +.diff-tailwindcss-wrapper .w-\[10px\] { + width: 10px; +} +.diff-tailwindcss-wrapper .w-\[1px\] { + width: 1px; +} +.diff-tailwindcss-wrapper .w-\[50\%\] { + width: 50%; +} +.diff-tailwindcss-wrapper .w-full { + width: 100%; +} +.diff-tailwindcss-wrapper .w-max { + width: -moz-max-content; + width: max-content; +} +.diff-tailwindcss-wrapper .min-w-\[100px\] { + min-width: 100px; +} +.diff-tailwindcss-wrapper .min-w-\[40px\] { + min-width: 40px; +} +.diff-tailwindcss-wrapper .min-w-full { + min-width: 100%; +} +.diff-tailwindcss-wrapper .flex-shrink-0 { + flex-shrink: 0; +} +.diff-tailwindcss-wrapper .shrink-0 { + flex-shrink: 0; +} +.diff-tailwindcss-wrapper .basis-\[50\%\] { + flex-basis: 50%; +} +.diff-tailwindcss-wrapper .table-fixed { + table-layout: fixed; +} +.diff-tailwindcss-wrapper .border-collapse { + border-collapse: collapse; +} +.diff-tailwindcss-wrapper .border-spacing-0 { + --tw-border-spacing-x: 0px; + --tw-border-spacing-y: 0px; + border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); +} +.diff-tailwindcss-wrapper .origin-center { + transform-origin: center; +} +.diff-tailwindcss-wrapper .translate-x-\[-50\%\] { + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.diff-tailwindcss-wrapper .translate-x-\[50\%\] { + --tw-translate-x: 50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.diff-tailwindcss-wrapper .translate-y-\[-50\%\] { + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.diff-tailwindcss-wrapper .transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.diff-tailwindcss-wrapper .cursor-pointer { + cursor: pointer; +} +.diff-tailwindcss-wrapper .select-none { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} +.diff-tailwindcss-wrapper .flex-col { + flex-direction: column; +} +.diff-tailwindcss-wrapper .items-start { + align-items: flex-start; +} +.diff-tailwindcss-wrapper .items-center { + align-items: center; +} +.diff-tailwindcss-wrapper .justify-center { + justify-content: center; +} +.diff-tailwindcss-wrapper .overflow-x-auto { + overflow-x: auto; +} +.diff-tailwindcss-wrapper .overflow-y-hidden { + overflow-y: hidden; +} +.diff-tailwindcss-wrapper .whitespace-nowrap { + white-space: nowrap; +} +.diff-tailwindcss-wrapper .break-all { + word-break: break-all; +} +.diff-tailwindcss-wrapper .rounded-\[2px\] { + border-radius: 2px; +} +.diff-tailwindcss-wrapper .rounded-md { + border-radius: 0.375rem; +} +.diff-tailwindcss-wrapper .border-l-\[1px\] { + border-left-width: 1px; +} +.diff-tailwindcss-wrapper .fill-current { + fill: currentColor; +} +.diff-tailwindcss-wrapper .p-0 { + padding: 0px; +} +.diff-tailwindcss-wrapper .p-\[1px\] { + padding: 1px; +} +.diff-tailwindcss-wrapper .px-\[10px\] { + padding-left: 10px; + padding-right: 10px; +} +.diff-tailwindcss-wrapper .py-\[2px\] { + padding-top: 2px; + padding-bottom: 2px; +} +.diff-tailwindcss-wrapper .py-\[6px\] { + padding-top: 6px; + padding-bottom: 6px; +} +.diff-tailwindcss-wrapper .pl-\[1\.5em\] { + padding-left: 1.5em; +} +.diff-tailwindcss-wrapper .pl-\[10px\] { + padding-left: 10px; +} +.diff-tailwindcss-wrapper .pl-\[2\.0em\] { + padding-left: 2.0em; +} +.diff-tailwindcss-wrapper .pr-\[10px\] { + padding-right: 10px; +} +.diff-tailwindcss-wrapper .text-right { + text-align: right; +} +.diff-tailwindcss-wrapper .indent-\[0\.2em\] { + text-indent: 0.2em; +} +.diff-tailwindcss-wrapper .align-top { + vertical-align: top; +} +.diff-tailwindcss-wrapper .align-middle { + vertical-align: middle; +} +.diff-tailwindcss-wrapper .text-\[1\.2em\] { + font-size: 1.2em; +} +.diff-tailwindcss-wrapper .leading-\[1\.6\] { + line-height: 1.6; +} +.diff-tailwindcss-wrapper .\!text-red-500 { + --tw-text-opacity: 1 !important; + color: rgb(239 68 68 / var(--tw-text-opacity, 1)) !important; +} +.diff-tailwindcss-wrapper .opacity-\[0\.5\] { + opacity: 0.5; +} +.diff-tailwindcss-wrapper .transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.diff-tailwindcss-wrapper * { + box-sizing: border-box; +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-style-root { + --diff-border--: #dedede; + --diff-add-content--: #dafbe1; + --diff-del-content--: #ffebe9; + --diff-add-lineNumber--: #aceebb; + --diff-del-lineNumber--: #ffcecb; + --diff-plain-content--: #ffffff; + --diff-expand-content--: #fafafa; + --diff-plain-lineNumber--: #fafafa; + --diff-expand-lineNumber--: #fafafa; + --diff-plain-lineNumber-color--: #555555; + --diff-expand-lineNumber-color--: #555555; + --diff-hunk-content--: #ddf4ff; + --diff-hunk-lineNumber--: #b6e3ff; + --diff-hunk-lineNumber-hover--: #0969da; + --diff-add-content-highlight--: #aceebb; + --diff-del-content-highlight--: #ffcecb; + --diff-add-widget--: #0969d2; + --diff-add-widget-color--: #ffffff; + --diff-empty-content--: #fafafa; + --diff-hunk-content-color--: #777777; + + color: black; +} +.diff-tailwindcss-wrapper .diff-style-root .diff-line-syntax-raw *, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw * { + color: var(--diff-view-light, inherit); + font-weight: var(--diff-view-light-font-weight, inherit); +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-style-root { + --diff-border--: #3d444d; + --diff-add-content--: #18271f; + --diff-del-content--: #23191c; + --diff-add-lineNumber--: #284228; + --diff-del-lineNumber--: #4f2828; + --diff-plain-content--: #0d1117; + --diff-expand-content--: #161b22; + --diff-plain-lineNumber--: #161b22; + --diff-expand-lineNumber--: #161b22; + --diff-plain-lineNumber-color--: #a0aaab; + --diff-expand-lineNumber-color--: #a0aaab; + --diff-hunk-content--: #131d2e; + --diff-hunk-lineNumber--: #0c2d6b; + --diff-hunk-lineNumber-hover--: #1f6feb; + --diff-add-content-highlight--: #2f5732; + --diff-del-content-highlight--: #713431; + --diff-add-widget--: #0969d2; + --diff-add-widget-color--: #ffffff; + --diff-empty-content--: #161b22; + --diff-hunk-content-color--: #9298a0; + + color: white; +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw * { + color: var(--diff-view-dark, inherit); + font-weight: var(--diff-view-dark-font-weight, inherit); +} +.diff-tailwindcss-wrapper table, +.diff-tailwindcss-wrapper tr, +.diff-tailwindcss-wrapper td { + border-color: transparent; + border-width: 0px; + text-align: left; +} +.diff-tailwindcss-wrapper td { + padding: 0; +} +.diff-tailwindcss-wrapper .diff-line-old-num, +.diff-tailwindcss-wrapper .diff-line-new-num, +.diff-tailwindcss-wrapper .diff-line-num { + text-align: right; +} +.diff-tailwindcss-wrapper .diff-style-root tr { + content-visibility: auto; +} +.diff-tailwindcss-wrapper .diff-add-widget-wrapper { + transform-origin: center; + transform: translateX(-50%) !important; +} +.diff-tailwindcss-wrapper .diff-line-old-content .diff-add-widget-wrapper, +.diff-tailwindcss-wrapper .diff-line-new-content .diff-add-widget-wrapper { + transform: translateX(50%) !important; +} +.diff-tailwindcss-wrapper .diff-add-widget-wrapper:hover { + transform: translateX(-50%) scale(1.1) !important; +} +.diff-tailwindcss-wrapper .diff-line-old-content .diff-add-widget-wrapper:hover, +.diff-tailwindcss-wrapper .diff-line-new-content .diff-add-widget-wrapper:hover { + transform: translateX(50%) scale(1.1) !important; +} +.diff-tailwindcss-wrapper .diff-widget-tooltip { + position: relative; +} +.diff-tailwindcss-wrapper .diff-add-widget, +.diff-tailwindcss-wrapper .diff-widget-tooltip { + font-family: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + font-size: 100%; + font-weight: inherit; + line-height: inherit; + letter-spacing: inherit; + color: inherit; + margin: 0; + text-transform: none; + border-width: 0px; + background-color: transparent; + background-image: none; +} +.diff-tailwindcss-wrapper .diff-widget-tooltip::after { + display: none; + box-sizing: border-box; + background-color: #555555; + position: absolute; + content: attr(data-title); + font-size: 11px; + padding: 1px 2px; + border-radius: 4px; + overflow: hidden; + top: 50%; + white-space: nowrap; + transform: translateY(-50%); + left: calc(100% + 8px); + color: #ffffff; +} +.diff-tailwindcss-wrapper .diff-widget-tooltip::before { + display: none; + box-sizing: border-box; + content: ""; + position: absolute; + top: 50%; + left: calc(100% - 2px); + transform: translateY(-50%); + border: 6px solid transparent; + border-right-color: #555555; +} +.diff-tailwindcss-wrapper .diff-widget-tooltip:hover { + background-color: var(--diff-hunk-lineNumber-hover--); + color: white; +} +.diff-tailwindcss-wrapper .diff-widget-tooltip:hover::before { + display: block; +} +.diff-tailwindcss-wrapper .diff-widget-tooltip:hover::after { + display: block; +} +.diff-line-extend-wrapper * { + color: initial; +} +.diff-line-widget-wrapper * { + color: initial; +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw code.hljs { + padding: 3px 5px +} +/*! + Theme: GitHub + Description: Light theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Outdated base version: https://github.com/primer/github-syntax-light + Current colors taken from GitHub's CSS +*/ +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs { + color: #24292e; + background: #ffffff +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-doctag, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-keyword, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-meta .hljs-keyword, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-template-tag, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-template-variable, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-type, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-variable.language_ { + /* prettylights-syntax-keyword */ + color: #d73a49 +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-title, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-title.class_, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-title.class_.inherited__, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-title.function_ { + /* prettylights-syntax-entity */ + color: #6f42c1 +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-attr, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-attribute, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-literal, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-meta, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-number, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-operator, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-variable, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-selector-attr, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-selector-class, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-selector-id { + /* prettylights-syntax-constant */ + color: #005cc5 +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-regexp, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-string, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-meta .hljs-string { + /* prettylights-syntax-string */ + color: #032f62 +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-built_in, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-symbol { + /* prettylights-syntax-variable */ + color: #e36209 +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-comment, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-code, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-formula { + /* prettylights-syntax-comment */ + color: #6a737d +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-name, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-quote, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-selector-tag, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-selector-pseudo { + /* prettylights-syntax-entity-tag */ + color: #22863a +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-subst { + /* prettylights-syntax-storage-modifier-import */ + color: #24292e +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-section { + /* prettylights-syntax-markup-heading */ + color: #005cc5; + font-weight: bold +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-bullet { + /* prettylights-syntax-markup-list */ + color: #735c0f +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-emphasis { + /* prettylights-syntax-markup-italic */ + color: #24292e; + font-style: italic +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-strong { + /* prettylights-syntax-markup-bold */ + color: #24292e; + font-weight: bold +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-addition { + /* prettylights-syntax-markup-inserted */ + color: #22863a; + background-color: #f0fff4 +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-deletion { + /* prettylights-syntax-markup-deleted */ + color: #b31d28; + background-color: #ffeef0 +} +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-char.escape_, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-link, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-params, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-property, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-punctuation, +.diff-tailwindcss-wrapper[data-theme="light"] .diff-line-syntax-raw .hljs-tag { + /* purposely ignored */ + +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw code.hljs { + padding: 3px 5px +} +/*! + Theme: GitHub Dark + Description: Dark theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Outdated base version: https://github.com/primer/github-syntax-dark + Current colors taken from GitHub's CSS +*/ +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs { + color: #c9d1d9; + background: #0d1117 +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-doctag, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-keyword, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-meta .hljs-keyword, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-template-tag, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-template-variable, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-type, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-variable.language_ { + /* prettylights-syntax-keyword */ + color: #ff7b72 +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-title, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-title.class_, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-title.class_.inherited__, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-title.function_ { + /* prettylights-syntax-entity */ + color: #d2a8ff +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-attr, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-attribute, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-literal, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-meta, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-number, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-operator, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-variable, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-selector-attr, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-selector-class, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-selector-id { + /* prettylights-syntax-constant */ + color: #79c0ff +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-regexp, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-string, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-meta .hljs-string { + /* prettylights-syntax-string */ + color: #a5d6ff +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-built_in, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-symbol { + /* prettylights-syntax-variable */ + color: #ffa657 +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-comment, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-code, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-formula { + /* prettylights-syntax-comment */ + color: #8b949e +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-name, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-quote, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-selector-tag, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-selector-pseudo { + /* prettylights-syntax-entity-tag */ + color: #7ee787 +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-subst { + /* prettylights-syntax-storage-modifier-import */ + color: #c9d1d9 +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-section { + /* prettylights-syntax-markup-heading */ + color: #1f6feb; + font-weight: bold +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-bullet { + /* prettylights-syntax-markup-list */ + color: #f2cc60 +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-emphasis { + /* prettylights-syntax-markup-italic */ + color: #c9d1d9; + font-style: italic +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-strong { + /* prettylights-syntax-markup-bold */ + color: #c9d1d9; + font-weight: bold +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-addition { + /* prettylights-syntax-markup-inserted */ + color: #aff5b4; + background-color: #033a16 +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-deletion { + /* prettylights-syntax-markup-deleted */ + color: #ffdcd7; + background-color: #67060c +} +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-char.escape_, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-link, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-params, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-property, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-punctuation, +.diff-tailwindcss-wrapper[data-theme="dark"] .diff-line-syntax-raw .hljs-tag { + /* purposely ignored */ + +} +.diff-tailwindcss-wrapper .hover\:scale-110:hover { + --tw-scale-x: 1.1; + --tw-scale-y: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.diff-tailwindcss-wrapper .group:hover .group-hover\:visible { + visibility: visible; +} diff --git a/public/vendor/diff-view-pure.css.br b/public/vendor/diff-view-pure.css.br new file mode 100644 index 0000000000000000000000000000000000000000..4e6954ea0496944d3e73b3bf1204b847cb1f3dad GIT binary patch literal 2777 zcmV;~3MTa%|5qTKgK^__`wvhqwXKxJq4i2WicDN)JOpjp_w|iNwrsPoe*&tTP-(;AErl*(Lg{j}`uCC7^AcrM+c?{@72KK&%7gqSATg-3j|C70adsv(2oX(}74|w}?OZ*|gpA36LDCCkT@*bW zl7bIOOe~`BbET=$o%JnGN;wDO6!ivB#OUhoFV6z}K7V<2%_+*FkXlm{wC!p`m>2Jk z|HlY$GY(~v;&gWD+5hw`DE9Y^JzjPNv3m#;AxTP{dgUh(F zHL-}H70>gSL85!ow75e~oS2|%QJkt03I}M*Ev9H-pJ~F=1~SEyh^FAvYPS;xz~}mg%FMlPEb<(mt5&dBVRj{K46&&tI#WPelU; zC&wtT#&LjAz{ngCkku3@TLJ-V@i&dAU}Qm(ZE_yESswaJ33GDdeP;(<`eD*IWru~yru z8*OcsW~I#cCU*5iMaAU3Q&G5|!@^qQX0LkaWqXjMvjR~Z!y|B^P87KzEmCZFOpmm* z7bwl;4=Fi|6iNO#rC;FE{IU7kT`&;v_xj)Zo)+d~<)fN;I9OFU-OQq}tl)+eU}E46T&MyDT-;`3Ga|{g)5@M<2d-lD zR9!Ckw1G>wU~3V(APIbFsyT?1!}+xwv0eQNX~y$_Bcu2fAZSkaYb=#a9?IuI9~Jp_%Id_m zEWw+@OnM6-D2&{*Ad$+uFg2|e_{odc)$Q?=DPmP7W*nnNQ#DW)gc0@RMAwL8$@i%? zzVsXr?{To>V8_!I4VB0S!9a#KG^T(wzg`S~2l(SmGT^ALeYz2&m|Z|bYJ07e=9e2=A$pUZ|j^|H!FWGIuwUu1iM%F{#wiy zKLv+O=rY}dK9P7f>0W7*m>T6eF_TYhauHio?B&{E$H0}w5z1k|MYU~E%cm8WBg7x@ z0|I}Lugd4(&kc6uTFPG)9pdU$(Y&CH$U4~on+){|IAQ?X9%JNqZ&^FUYqcoEg z1_AYTpq)g*3Rdqkp^vi ziLK$iD!551@w-odq%p*&*N|ZKwqv>}Vs6Z=Y>f< zkGR6ids7|US>cRJ?R5{N3ieOsQjEO6z_n4sy&B@5g87m$tz+fjESh8^%ce?-{iw1m zI3+PK>dRo|$RyJO%q-0VjkvW-A97x9ZBg7F)?p`_d8;i#C^q{Z#(QCs_7LO_@U!^} zD=<`tGM6C12ERB2p3BbpN!ecLdFaqVCgu4#ueKZYFO%$0Gt$rnZXtTB)mYN*cTHzh z!_jD)&Y{J&*0$@69wi|({>3Y@(pXcw|7rHAG!QOq0(AQEv*MT_wsCY(m;HWCnq5-`(C>j2k#q=YZ&%B z!Om4(UqE1%Un&x`9VD8)rK1*?nha&zh%BJCizk#c_O4-etdhbS19BVg^Qs2-xU}P6&&l z5!BBQHOtF?3S}H=Jdb%Gjd9f(F-}^E%gjUYvPv^C{>FjFmQv3PUg^dTM(O=$ z-tF~`2NE{kKS@W4Wy~)%qISYhJZ3&v|1~U+Fd)>Bkgy{O81vS$=GhsViAK3&1Z#E~ z56Y?8q5cRLFDuXG3i3mOB4eH`rmYiS@Nr}Bgps-n@!_?WJFs3OzezN9lt*a<}3l|{XsEi4kO0R zP0x&(03YxNV9pq@m5gDe%=1>x$HgCZ95-~GN^lDPO2F~u4%TZCg@@uooR [slot, "\x1f" + String.fromCharCode(0x20 + index)]), -]; - -function buildTrie(pairs) { +const overlayPairs = buildPairs( + overlayDictionary, + [0x1e, 0x7f], + "\x1f", + 0x20, +); + +function buildTrie(pairs, reversed = false) { const root = { children: new Map() }; for (const [from, to] of pairs) { + const match = reversed ? to : from; + const replacement = reversed ? from : to; + let node = root; - for (const char of from) { + for (const char of match) { let child = node.children.get(char); if (!child) { child = { children: new Map() }; @@ -47,7 +52,7 @@ function buildTrie(pairs) { } node = child; } - node.replacement ??= to; + node.replacement ??= replacement; } return root; } @@ -81,9 +86,9 @@ function applyTrie(text, trie) { } const v1EncodeTrie = buildTrie(v1Pairs); -const v1DecodeTrie = buildTrie(v1Pairs.map(([from, to]) => [to, from])); +const v1DecodeTrie = buildTrie(v1Pairs, true); const overlayEncodeTrie = buildTrie(overlayPairs); -const overlayDecodeTrie = buildTrie(overlayPairs.map(([from, to]) => [to, from])); +const overlayDecodeTrie = buildTrie(overlayPairs, true); function brotli(input) { return brotliCompressSync(Buffer.from(input, "utf8"), { @@ -94,7 +99,11 @@ function brotli(input) { function trimOptional(fields) { let end = fields.length; while (end > 0 && fields[end - 1] === undefined) end--; - return fields.slice(0, end).map((field) => field === undefined ? null : field); + const trimmed = new Array(end); + for (let index = 0; index < end; index++) { + trimmed[index] = fields[index] === undefined ? null : fields[index]; + } + return trimmed; } function artifactTuple(artifact) { @@ -125,13 +134,22 @@ function artifactTuple(artifact) { } function tupleEnvelope(envelope) { - const artifacts = envelope.artifacts.map(artifactTuple); + const artifacts = new Array(envelope.artifacts.length); + const activeArtifactId = envelope.activeArtifactId; + let activeIndex = -1; + + for (let index = 0; index < envelope.artifacts.length; index++) { + const artifact = envelope.artifacts[index]; + artifacts[index] = artifactTuple(artifact); + + if (artifact.id === activeArtifactId) { + activeIndex = index; + } + } + if (artifacts.length === 1) { return trimOptional([3, artifacts[0], envelope.title]); } - const activeIndex = envelope.activeArtifactId - ? envelope.artifacts.findIndex((artifact) => artifact.id === envelope.activeArtifactId) - : -1; return trimOptional([2, artifacts, envelope.title, activeIndex > 0 ? activeIndex : undefined]); } @@ -156,8 +174,8 @@ function decodeArx2(buf) { } function median(values) { - const sorted = [...values].sort((a, b) => a - b); - return sorted[Math.floor(sorted.length / 2)] ?? 0; + values.sort((a, b) => a - b); + return values[Math.floor(values.length / 2)] ?? 0; } function measure(fn, iterations = 7) { @@ -190,6 +208,170 @@ function textEnvelope(kind, title, content, extra = {}) { }; } +function repeatedFixture(block, targetLength, segmentSuffix = (index) => `\nfixture segment ${index}\n`) { + let fixture = ""; + let index = 0; + while (fixture.length < targetLength) { + fixture += `${block}${segmentSuffix(index)}`; + index++; + } + return fixture.slice(0, targetLength); +} + +const markdownAgentsFixture = repeatedFixture( + [ + "# AGENTS.md excerpt", + "", + "`agent-render` is a static artifact viewer for AI-generated outputs.", + "Keep markdown, code, diffs, CSV, and JSON readable across chat surfaces.", + "", + "## Product contract", + "", + "- Fragment payloads use `#agent-render=v1..`.", + "- Artifact contents stay out of the host request path.", + "- Supported codecs are `plain`, `lz`, `deflate`, `arx`, and `arx2`.", + "- Supported artifact kinds are `markdown`, `code`, `diff`, `csv`, and `json`.", + "", + "Preserve the static shell, the zero-retention wording, and the renderer-first layout.", + "", + ].join("\n"), + 8000, + (index) => `\nFixture note ${index}: fragment transport, renderer readiness, and artifact metadata stay aligned.\n\n`, +); + +const codeFragmentFixture = repeatedFixture( + [ + "export async function decodeFragmentAsync(hash: string, options?: DecodeOptions) {", + " const parsed = parseFragmentPrefix(hash);", + " if (!parsed.ok) return parsed;", + " if (parsed.codec === \"arx\" || parsed.codec === \"arx2\") {", + " const { decodeArxFragmentAsync } = await import(\"./fragment-arx\");", + " return decodeArxFragmentAsync(parsed, options);", + " }", + " return decodePlainFragment(parsed.payload, options);", + "}", + "", + "export async function encodeEnvelopeAsync(envelope: PayloadEnvelope, options: EncodeOptions = {}) {", + " const codec = options.codec ?? envelope.codec ?? \"deflate\";", + " if (codec === \"arx\" || codec === \"arx2\") {", + " const { encodeArxEnvelopeAsync } = await import(\"./fragment-arx\");", + " return encodeArxEnvelopeAsync(envelope, codec);", + " }", + " return encodeEnvelope(envelope, { codec });", + "}", + "", + ].join("\n"), + 8000, + (index) => `\n// fixture segment ${index}: codec branch coverage and bundle shape stay stable.\n`, +); + +const packageManifestFixture = JSON.stringify( + { + name: "agent-render", + version: "0.1.0", + private: true, + scripts: { + build: "next build", + preview: "node scripts/serve-export.mjs", + check: "npm run lint && npm run test && npm run bench:codecs && npm run typecheck && npm run build", + }, + dependencies: { + "@codemirror/view": "^6.38.2", + "@git-diff-view/react": "^0.1.1", + "brotli-wasm": "^3.0.1", + "fflate": "^0.8.2", + "lucide-react": "^0.577.0", + "next": "15.1.11", + "react": "19.1.0", + "react-dom": "19.1.0", + "react-markdown": "^10.1.0", + }, + devDependencies: { + "@playwright/test": "^1.58.2", + "typescript": "^5.8.2", + "vitest": "^4.0.18", + }, + }, + null, + 2, +); + +const readmeFixture = repeatedFixture( + [ + "# agent-render", + "", + "A static, open artifact viewer for AI outputs.", + "", + "Paste content into the browser-side link creator, choose a renderer, and share the resulting fragment URL.", + "The static host serves the shell; the browser decodes the artifact from the fragment.", + "", + "## Supported artifacts", + "", + "- Markdown with sanitized GFM and Mermaid fences.", + "- Code with a read-only CodeMirror surface.", + "- Review-style git patches with unified and split modes.", + "- CSV tables and JSON trees.", + "", + ].join("\n"), + 9000, + (index) => `\nFixture section ${index}: static export links should remain inspectable across chat clients.\n\n`, +); + +const arxCodecFixture = repeatedFixture( + [ + "const singleByteCodes = [0x01, 0x02, 0x03, 0x04, 0x05];", + "function buildPairs(dictionary, prefix = \"\\\\x00\") {", + " return dictionary.extendedSlots.map((slot, index) => [slot, prefix + String.fromCharCode(index + 1)]);", + "}", + "function applyTrie(text, trie) {", + " const out = [];", + " let index = 0;", + " while (index < text.length) {", + " let node = trie;", + " let cursor = index;", + " let replacement;", + " while (cursor < text.length) {", + " node = node.children.get(text[cursor]);", + " if (!node) break;", + " cursor++;", + " if (node.replacement !== undefined) replacement = node.replacement;", + " }", + " out.push(replacement ?? text[index]);", + " index++;", + " }", + " return out.join(\"\");", + "}", + "", + ].join("\n"), + 12000, + (index) => `\n// fixture segment ${index}: trie substitutions and tuple overlays remain comparable.\n`, +); + +const tsconfigFixture = JSON.stringify( + { + compilerOptions: { + target: "ES2022", + lib: ["dom", "dom.iterable", "esnext"], + allowJs: false, + skipLibCheck: true, + strict: true, + noEmit: true, + module: "esnext", + moduleResolution: "bundler", + resolveJsonModule: true, + isolatedModules: true, + jsx: "preserve", + paths: { + "@/*": ["./src/*"], + }, + }, + include: ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + exclude: ["node_modules"], + }, + null, + 2, +); + const patch = [ "diff --git a/src/a.ts b/src/a.ts", "--- a/src/a.ts", @@ -200,23 +382,24 @@ const patch = [ "", ].join("\n").repeat(12); -const csv = [ - "name,value,notes", - ...Array.from({ length: 180 }, (_, index) => `row-${index},${index},"export const value ${index}"`), -].join("\n"); +const csvRows = ["name,value,notes"]; +for (let index = 0; index < 180; index++) { + csvRows.push(`row-${index},${index},"export const value ${index}"`); +} +const csv = csvRows.join("\n"); const corpus = [ { name: "markdown-agents", kind: "markdown", - envelope: textEnvelope("markdown", "AGENTS.md excerpt", readFileSync("AGENTS.md", "utf8").slice(0, 8000), { + envelope: textEnvelope("markdown", "AGENTS.md excerpt", markdownAgentsFixture, { filename: "AGENTS.md", }), }, { name: "code-fragment", kind: "code", - envelope: textEnvelope("code", "fragment.ts excerpt", readFileSync("src/lib/payload/fragment.ts", "utf8").slice(0, 8000), { + envelope: textEnvelope("code", "fragment.ts excerpt", codeFragmentFixture, { filename: "fragment.ts", language: "ts", }), @@ -259,7 +442,7 @@ const corpus = [ { name: "json-package", kind: "json", - envelope: textEnvelope("json", "package.json", readFileSync("package.json", "utf8"), { filename: "package.json" }), + envelope: textEnvelope("json", "package.json", packageManifestFixture, { filename: "package.json" }), }, { name: "multi-bundle", @@ -270,17 +453,17 @@ const corpus = [ title: "Mixed bundle", activeArtifactId: "source", artifacts: [ - { id: "readme", kind: "markdown", filename: "README.md", content: readFileSync("README.md", "utf8") }, + { id: "readme", kind: "markdown", filename: "README.md", content: readmeFixture }, { id: "source", kind: "code", filename: "arx-codec.ts", language: "ts", - content: readFileSync("src/lib/payload/arx-codec.ts", "utf8").slice(0, 12000), + content: arxCodecFixture, }, { id: "patch", kind: "diff", filename: "bundle.patch", patch, view: "split" }, { id: "table", kind: "csv", filename: "table.csv", content: csv.slice(0, 1200) }, - { id: "manifest", kind: "json", filename: "tsconfig.json", content: readFileSync("tsconfig.json", "utf8") }, + { id: "manifest", kind: "json", filename: "tsconfig.json", content: tsconfigFixture }, ], }, }, @@ -314,9 +497,13 @@ for (const entry of corpus) { const baseline = { version: 1, generatedAt: new Date().toISOString(), - rows: Object.fromEntries(rows.map((row) => [row.id, { encodedBytes: row.encodedBytes }])), + rows: {}, }; +for (const row of rows) { + baseline.rows[row.id] = { encodedBytes: row.encodedBytes }; +} + if (WRITE_BASELINE) { writeFileSync(BASELINE_PATH, `${JSON.stringify(baseline, null, 2)}\n`); } @@ -324,15 +511,18 @@ if (WRITE_BASELINE) { const table = [ "| codec | kind | name | raw B | encoded B | ratio | encode ms | decode ms |", "|---|---:|---|---:|---:|---:|---:|---:|", - ...rows.map((row) => `| ${row.codec} | ${row.kind} | ${row.name} | ${row.rawBytes} | ${row.encodedBytes} | ${row.ratio.toFixed(2)}x | ${row.encodeMs.toFixed(2)} | ${row.decodeMs.toFixed(2)} |`), ]; +for (const row of rows) { + table.push(`| ${row.codec} | ${row.kind} | ${row.name} | ${row.rawBytes} | ${row.encodedBytes} | ${row.ratio.toFixed(2)}x | ${row.encodeMs.toFixed(2)} | ${row.decodeMs.toFixed(2)} |`); +} + console.log(table.join("\n")); -const totals = rows.reduce((acc, row) => { - acc[row.codec] = (acc[row.codec] ?? 0) + row.encodedBytes; - return acc; -}, {}); +const totals = {}; +for (const row of rows) { + totals[row.codec] = (totals[row.codec] ?? 0) + row.encodedBytes; +} const arx2Win = (totals.arx - totals.arx2) / totals.arx; console.log(`\nTotal arx: ${totals.arx} B`); console.log(`Total arx2: ${totals.arx2} B`); diff --git a/scripts/check-build-budgets.mjs b/scripts/check-build-budgets.mjs new file mode 100644 index 0000000..38ef070 --- /dev/null +++ b/scripts/check-build-budgets.mjs @@ -0,0 +1,175 @@ +import { existsSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { gzipSync } from "node:zlib"; + +const nextDir = join(process.cwd(), ".next"); +const appBuildManifestPath = join(nextDir, "app-build-manifest.json"); +const reactLoadableManifestPath = join(nextDir, "react-loadable-manifest.json"); +const gzipSizeCache = new Map(); + +const budgets = [ + { + maxBytes: 115 * 1024, + name: "homepage route JS", + route: "/page", + type: "route", + }, + { + importKeyParts: ["components/viewer/artifact-stage", "code-renderer"], + maxBytes: 100 * 1024, + name: "code renderer deferred JS", + type: "loadable", + }, + { + importKeyParts: ["components/viewer/artifact-stage", "markdown-renderer"], + maxBytes: 52 * 1024, + name: "markdown renderer deferred JS", + type: "loadable", + }, + { + importKeyParts: ["components/renderers/diff-renderer", "@git-diff-view/react"], + maxBytes: 340 * 1024, + name: "rich diff library deferred JS", + type: "loadable", + }, +]; + +function formatBytes(bytes) { + return `${(bytes / 1024).toFixed(1)} KiB`; +} + +function readJson(path) { + if (!existsSync(path)) { + throw new Error(`${path} is missing. Run npm run build before checking build budgets.`); + } + + return JSON.parse(readFileSync(path, "utf8")); +} + +function gzipFileSize(path) { + const cached = gzipSizeCache.get(path); + if (cached !== undefined) { + return cached; + } + + const size = gzipSync(readFileSync(path)).length; + gzipSizeCache.set(path, size); + return size; +} + +function getUniqueJsFiles(files) { + const seen = new Set(); + const jsFiles = []; + + for (const file of files) { + if (!file.endsWith(".js") || seen.has(file)) { + continue; + } + + seen.add(file); + jsFiles.push(file); + } + + return jsFiles; +} + +function getRouteFiles(appBuildManifest, route) { + const files = appBuildManifest.pages?.[route]; + + if (!files) { + throw new Error(`Route "${route}" was not found in app-build-manifest.json.`); + } + + return getUniqueJsFiles(files); +} + +function getLoadableFiles(reactLoadableManifest, keyParts) { + const files = []; + let hasMatch = false; + + for (const key in reactLoadableManifest) { + let isMatch = true; + for (const part of keyParts) { + if (!key.includes(part)) { + isMatch = false; + break; + } + } + + if (!isMatch) { + continue; + } + + hasMatch = true; + const value = reactLoadableManifest[key]; + for (const file of value.files ?? []) { + files.push(file); + } + } + + if (!hasMatch) { + throw new Error(`No react-loadable entry matched: ${keyParts.join(" + ")}`); + } + + return getUniqueJsFiles(files); +} + +function getBudgetFiles(budget, manifests) { + if (budget.type === "route") { + return getRouteFiles(manifests.appBuildManifest, budget.route); + } + + return getLoadableFiles(manifests.reactLoadableManifest, budget.importKeyParts); +} + +function measureBudget(budget, manifests) { + const files = getBudgetFiles(budget, manifests); + let gzipBytes = 0; + + for (const file of files) { + gzipBytes += gzipFileSize(join(nextDir, file)); + } + + return { + ...budget, + files, + gzipBytes, + }; +} + +const manifests = { + appBuildManifest: readJson(appBuildManifestPath), + reactLoadableManifest: readJson(reactLoadableManifestPath), +}; + +const results = []; +const failures = []; + +for (const budget of budgets) { + const result = measureBudget(budget, manifests); + results.push(result); + + if (result.gzipBytes > result.maxBytes) { + failures.push(result); + } +} + +for (const result of results) { + const status = result.gzipBytes > result.maxBytes ? "FAIL" : "ok"; + console.log( + `${status} ${result.name}: ${formatBytes(result.gzipBytes)} / ${formatBytes(result.maxBytes)}`, + ); +} + +if (failures.length > 0) { + console.error("\nBuild budget exceeded:"); + for (const failure of failures) { + console.error(`- ${failure.name}: ${formatBytes(failure.gzipBytes)} > ${formatBytes(failure.maxBytes)}`); + for (const file of failure.files) { + console.error(` ${file}`); + } + } + process.exit(1); +} + +console.log("\nBuild budget check passed."); diff --git a/scripts/clean-build-output.mjs b/scripts/clean-build-output.mjs new file mode 100644 index 0000000..429fe51 --- /dev/null +++ b/scripts/clean-build-output.mjs @@ -0,0 +1,5 @@ +import { rm } from "node:fs/promises"; + +for (const directory of [".next", "out"]) { + await rm(directory, { recursive: true, force: true }); +} diff --git a/scripts/compress-dictionary.mjs b/scripts/compress-dictionary.mjs index f7cf1f4..011aa04 100644 --- a/scripts/compress-dictionary.mjs +++ b/scripts/compress-dictionary.mjs @@ -1,31 +1,55 @@ #!/usr/bin/env node /** - * Pre-compresses public/arx-dictionary.json into minified and brotli-compressed variants. + * Prepares pre-compressed static assets that are served directly from `public/`. * * Outputs: * public/arx-dictionary.json — minified JSON (agents can fetch this directly) * public/arx-dictionary.json.br — brotli-compressed (CDN or agent can use this) + * public/arx2-dictionary.json — minified overlay JSON + * public/arx2-dictionary.json.br — brotli-compressed overlay JSON + * public/vendor/diff-view-pure.css — mirrored @git-diff-view stylesheet + * public/vendor/diff-view-pure.css.br — brotli-compressed stylesheet loaded by diffs * * Run: node scripts/compress-dictionary.mjs */ -import { readFileSync, writeFileSync } from "fs"; +import { mkdirSync, readFileSync, writeFileSync } from "fs"; import { createRequire } from "module"; +import { dirname } from "path"; const require = createRequire(import.meta.url); const brotli = require("brotli-wasm"); -const src = JSON.parse(readFileSync("public/arx-dictionary.json", "utf8")); +function writeCompressedTextAsset(path, contents) { + const compressed = brotli.compress(Buffer.from(contents, "utf8"), { quality: 11 }); -// Re-serialize minified (no whitespace) -const minified = JSON.stringify(src); -writeFileSync("public/arx-dictionary.json", minified, "utf8"); + writeFileSync(`${path}.br`, compressed); -// Brotli-compress the minified JSON -const compressed = brotli.compress(Buffer.from(minified, "utf8"), { quality: 11 }); -writeFileSync("public/arx-dictionary.json.br", compressed); + console.log(`${path}: ${contents.length} bytes`); + console.log(`${path}.br: ${compressed.length} bytes (brotli q11)`); + console.log(`Compression ratio: ${((1 - compressed.length / contents.length) * 100).toFixed(1)}%`); +} -console.log(`arx-dictionary.json: ${minified.length} bytes (minified)`); -console.log(`arx-dictionary.json.br: ${compressed.length} bytes (brotli q11)`); -console.log(`Compression ratio: ${((1 - compressed.length / minified.length) * 100).toFixed(1)}%`); +function compressDictionary(path) { + const source = JSON.parse(readFileSync(path, "utf8")); + const minified = JSON.stringify(source); + + writeFileSync(path, minified, "utf8"); + writeCompressedTextAsset(path, minified); +} + +function mirrorAndCompressTextAsset(sourcePath, targetPath) { + const source = readFileSync(sourcePath, "utf8"); + + mkdirSync(dirname(targetPath), { recursive: true }); + writeFileSync(targetPath, source, "utf8"); + writeCompressedTextAsset(targetPath, source); +} + +compressDictionary("public/arx-dictionary.json"); +compressDictionary("public/arx2-dictionary.json"); +mirrorAndCompressTextAsset( + "node_modules/@git-diff-view/react/styles/diff-view-pure.css", + "public/vendor/diff-view-pure.css", +); diff --git a/scripts/ensure-next-types.mjs b/scripts/ensure-next-types.mjs index 0de9264..eecc994 100644 --- a/scripts/ensure-next-types.mjs +++ b/scripts/ensure-next-types.mjs @@ -1,18 +1,67 @@ -import { mkdir, access, writeFile } from "node:fs/promises"; +import { access, mkdir, readdir, rm, writeFile } from "node:fs/promises"; import { constants } from "node:fs"; +import path from "node:path"; -const files = [ - [".next/types/app/layout.ts", "export {};\n"], - [".next/types/app/page.ts", "export {};\n"], - [".next/types/cache-life.d.ts", "export {};\n"], +const appDirectory = "src/app"; +const generatedTypesDirectory = ".next/types"; +const staticFiles = [ + [path.join(generatedTypesDirectory, "cache-life.d.ts"), "export {};\n"], + [path.join(generatedTypesDirectory, "routes.d.ts"), "export {};\n"], + [path.join(generatedTypesDirectory, "validator.ts"), "export {};\n"], + [path.join(generatedTypesDirectory, "package.json"), '{"type":"module"}\n'], ]; -for (const [filePath, contents] of files) { +async function fileExists(filePath) { try { await access(filePath, constants.F_OK); + return true; } catch { - const directory = filePath.slice(0, filePath.lastIndexOf("/")); - await mkdir(directory, { recursive: true }); - await writeFile(filePath, contents, "utf8"); + return false; } } + +async function* getAppEntries(directory = appDirectory) { + const children = await readdir(directory, { withFileTypes: true }); + + for (const child of children) { + const childPath = path.join(directory, child.name); + + if (child.isDirectory()) { + yield* getAppEntries(childPath); + continue; + } + + if (child.name === "layout.tsx" || child.name === "page.tsx") { + yield childPath; + } + } +} + +function getGeneratedAppTypePath(sourcePath) { + const relativePath = path.relative(appDirectory, sourcePath).replace(/\.tsx$/, ".ts"); + return path.join(generatedTypesDirectory, "app", relativePath); +} + +async function ensureFile(filePath, contents) { + if (await fileExists(filePath)) { + return false; + } + + await mkdir(path.dirname(filePath), { recursive: true }); + await writeFile(filePath, contents, "utf8"); + return true; +} + +let wroteStub = false; + +for (const [filePath, contents] of staticFiles) { + wroteStub = (await ensureFile(filePath, contents)) || wroteStub; +} + +for await (const entry of getAppEntries()) { + wroteStub = (await ensureFile(getGeneratedAppTypePath(entry), "export {};\n")) || wroteStub; +} + +if (wroteStub) { + await rm("tsconfig.tsbuildinfo", { force: true }); +} diff --git a/scripts/serve-export.mjs b/scripts/serve-export.mjs index 47d56ea..d7df799 100644 --- a/scripts/serve-export.mjs +++ b/scripts/serve-export.mjs @@ -1,13 +1,11 @@ import { createServer } from "node:http"; -import { existsSync, createReadStream } from "node:fs"; +import { existsSync, createReadStream, readFileSync } from "node:fs"; import { stat } from "node:fs/promises"; import path from "node:path"; import process from "node:process"; const outputDirectory = path.resolve("out"); const port = Number(process.env.PORT || 3000); -const configuredBasePath = (process.env.NEXT_PUBLIC_BASE_PATH || "").trim(); -const basePath = configuredBasePath === "/" ? "" : configuredBasePath.replace(/\/$/, ""); const apiCatalogContentType = 'application/linkset+json; profile="https://www.rfc-editor.org/info/rfc9727"'; const apiCatalogLinkHeader = '; rel="api-catalog"; type="application/linkset+json"'; @@ -28,18 +26,52 @@ const contentTypes = new Map([ [".jpeg", "image/jpeg"], [".woff", "font/woff"], [".woff2", "font/woff2"], + [".wasm", "application/wasm"], [".yaml", "application/yaml; charset=utf-8"], [".yml", "application/yaml; charset=utf-8"], ]); +function normalizeBasePath(value) { + const configuredBasePath = (value || "").trim(); + if (configuredBasePath === "" || configuredBasePath === "/") { + return ""; + } + + const withLeadingSlash = configuredBasePath.startsWith("/") ? configuredBasePath : `/${configuredBasePath}`; + return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash; +} + +function readManifestBasePath() { + try { + const manifest = JSON.parse(readFileSync(path.resolve(".next", "routes-manifest.json"), "utf8")); + return normalizeBasePath(typeof manifest.basePath === "string" ? manifest.basePath : ""); + } catch { + return ""; + } +} + +const basePath = normalizeBasePath(process.env.NEXT_PUBLIC_BASE_PATH) || readManifestBasePath(); + function contentTypeFor(filePath) { if (filePath.endsWith(`${path.sep}.well-known${path.sep}api-catalog`)) { return apiCatalogContentType; } + if (filePath.endsWith(".json.br")) { + return "application/json; charset=utf-8"; + } + + if (filePath.endsWith(".css.br")) { + return "text/css; charset=utf-8"; + } + return contentTypes.get(path.extname(filePath)) || "application/octet-stream"; } +function isNextStaticAsset(filePath) { + return filePath.includes(`${path.sep}_next${path.sep}static${path.sep}`); +} + function headersFor(filePath) { const headers = { "Content-Type": contentTypeFor(filePath) }; @@ -47,11 +79,30 @@ function headersFor(filePath) { headers.Link = apiCatalogLinkHeader; } + if (filePath.endsWith(".json.br") || filePath.endsWith(".css.br")) { + headers["Content-Encoding"] = "br"; + headers.Vary = "Accept-Encoding"; + } + + if (isNextStaticAsset(filePath)) { + headers["Cache-Control"] = "public, max-age=31536000, immutable"; + } + return headers; } +function isInsideOutputDirectory(filePath) { + const relativePath = path.relative(outputDirectory, filePath); + return relativePath === "" || (!relativePath.startsWith("..") && !path.isAbsolute(relativePath)); +} + function toFilePath(urlPath) { - const cleanPath = urlPath.split("?", 1)[0].split("#", 1)[0]; + const queryIndex = urlPath.indexOf("?"); + const hashIndex = urlPath.indexOf("#"); + let pathEnd = urlPath.length; + if (queryIndex !== -1) pathEnd = Math.min(pathEnd, queryIndex); + if (hashIndex !== -1) pathEnd = Math.min(pathEnd, hashIndex); + const cleanPath = urlPath.slice(0, pathEnd); let relativePath = cleanPath; if (basePath) { @@ -63,7 +114,7 @@ function toFilePath(urlPath) { relativePath = `${basePath}/`; } - if (!relativePath.startsWith(basePath)) { + if (!relativePath.startsWith(`${basePath}/`)) { return null; } @@ -71,7 +122,12 @@ function toFilePath(urlPath) { } const normalizedPath = relativePath === "/" ? "/index.html" : relativePath; - const tentativePath = path.join(outputDirectory, normalizedPath); + const tentativePath = path.resolve(outputDirectory, normalizedPath.replace(/^\/+/, "")); + + if (!isInsideOutputDirectory(tentativePath)) { + return null; + } + return normalizedPath.endsWith("/") ? path.join(tentativePath, "index.html") : tentativePath; } diff --git a/selfhosted/server.ts b/selfhosted/server.ts index 3f7e17e..9f7228a 100644 --- a/selfhosted/server.ts +++ b/selfhosted/server.ts @@ -15,6 +15,7 @@ import { validatePayload } from "./validate.js"; const port = Number(process.env.PORT || 3000); const host = process.env.HOST || "0.0.0.0"; const outputDirectory = path.resolve(process.env.OUT_DIR || "out"); +const outputDirectoryWithSeparator = `${outputDirectory}${path.sep}`; const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; const API_CATALOG_CONTENT_TYPE = 'application/linkset+json; profile="https://www.rfc-editor.org/info/rfc9727"'; @@ -32,6 +33,7 @@ const contentTypes = new Map([ [".jpeg", "image/jpeg"], [".woff", "font/woff"], [".woff2", "font/woff2"], + [".wasm", "application/wasm"], [".br", "application/octet-stream"], [".yaml", "application/yaml; charset=utf-8"], [".yml", "application/yaml; charset=utf-8"], @@ -44,9 +46,21 @@ function contentTypeFor(filePath: string): string { return API_CATALOG_CONTENT_TYPE; } + if (filePath.endsWith(".json.br")) { + return "application/json; charset=utf-8"; + } + + if (filePath.endsWith(".css.br")) { + return "text/css; charset=utf-8"; + } + return contentTypes.get(path.extname(filePath)) || "application/octet-stream"; } +function isNextStaticAsset(filePath: string): boolean { + return filePath.includes(`${path.sep}_next${path.sep}static${path.sep}`); +} + function headersFor(filePath: string): Record { const headers: Record = { "Content-Type": contentTypeFor(filePath) }; @@ -54,6 +68,15 @@ function headersFor(filePath: string): Record { headers.Link = API_CATALOG_LINK_HEADER; } + if (filePath.endsWith(".json.br") || filePath.endsWith(".css.br")) { + headers["Content-Encoding"] = "br"; + headers.Vary = "Accept-Encoding"; + } + + if (isNextStaticAsset(filePath)) { + headers["Cache-Control"] = "public, max-age=31536000, immutable"; + } + return headers; } @@ -125,14 +148,12 @@ function htmlResponse(res: ServerResponse, status: number, body: string): void { /** Serve a static file from the output directory. */ async function serveStatic(res: ServerResponse, urlPath: string, method: string): Promise { - const cleanPath = urlPath.split("?", 1)[0].split("#", 1)[0]; - const normalizedPath = cleanPath === "/" ? "/index.html" : cleanPath; + const normalizedPath = urlPath === "/" ? "/index.html" : urlPath; let filePath = path.resolve(path.join(outputDirectory, normalizedPath)); - const resolvedOutputDir = path.resolve(outputDirectory); // Prevent path traversal outside the output directory - if (!filePath.startsWith(resolvedOutputDir + path.sep) && filePath !== resolvedOutputDir) { + if (!filePath.startsWith(outputDirectoryWithSeparator) && filePath !== outputDirectory) { res.writeHead(403); res.end("Forbidden"); return; diff --git a/skills/agent-render-linking/SKILL.md b/skills/agent-render-linking/SKILL.md index 98f7264..8b54f8d 100644 --- a/skills/agent-render-linking/SKILL.md +++ b/skills/agent-render-linking/SKILL.md @@ -35,7 +35,7 @@ Supported codecs: - `lz`: `lz-string` compressed JSON encoded for URL-safe transport - `deflate`: deflate-compressed UTF-8 JSON bytes encoded as base64url - `arx`: domain-dictionary substitution + brotli (quality 11) + binary-to-text encoding (~70% smaller than deflate with baseBMP). Fetch the shared dictionary from `https://agent-render.com/arx-dictionary.json` to apply substitutions locally before brotli compression. Four wire shapes: baseBMP (~62k safe BMP code points, ~15.92 bits/char, best raw density), base1k (1774 Unicode code points U+00A1–U+07FF), base64url (ASCII `A-Za-z0-9-_`, `B.` prefix — good when Unicode would be percent-encoded), and base76 (77-char ASCII). The product encoder tries all four and picks the shortest **transport** length. -- `arx2`: tuple-envelope transport + `https://agent-render.com/arx2-dictionary.json` overlay + the shared arx dictionary + brotli (quality 11) + the same four wire shapes. Existing arx links remain valid; prefer arx2 when it is the shortest transport. +- `arx2`: tuple-envelope transport + `https://agent-render.com/arx2-dictionary.json` overlay (or pre-compressed `https://agent-render.com/arx2-dictionary.json.br`) + the shared arx dictionary + brotli (quality 11) + the same four wire shapes. Existing arx links remain valid; prefer arx2 when it is the shortest transport. - packed wire mode (`p: 1`) may be used automatically to shorten transport keys Prefer: @@ -213,7 +213,7 @@ To use the dictionary for local `arx` encoding: - Base76 uses 77 ASCII fragment-safe characters. ~6.27 bits/char 5. Prepend `v1.arx..` to form the fragment payload (use the same dictionary version used for substitution) -The dictionary includes JSON envelope boilerplate patterns (like `","kind":"Markdown","content":"`), JSON-escaped Markdown syntax, programming keywords, and common English words. The viewer loads the same dictionary on startup to reverse substitutions during decode. +The dictionary includes JSON envelope boilerplate patterns (like `","kind":"Markdown","content":"`), JSON-escaped Markdown syntax, programming keywords, and common English words. The viewer tries the pre-compressed dictionary first on default ARX/ARX2 encode or decode paths, falls back to the JSON file, and falls back again to its built-in table if external fetches fail. If the dictionary fetch fails, fall back to `deflate` codec. diff --git a/src/app/globals.css b/src/app/globals.css index 4b6e41e..b7e6335 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,5 +1,4 @@ @import "tailwindcss"; -@import "@git-diff-view/react/styles/diff-view-pure.css"; :root { /* ── Surface hierarchy — warm ivory with sunset undertone ── */ @@ -750,6 +749,7 @@ select { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; + scroll-margin-top: 4.5rem; } /* ── Compact toolbar ribbon ── */ @@ -1402,6 +1402,22 @@ select { padding: 0.75rem 0; } +.artifact-raw-source, +.json-raw-source { + max-height: min(68vh, 42rem); + overflow: auto; + margin: 0; + border: 1px solid var(--surface-code-chrome-border); + border-radius: var(--radius-lg); + background: var(--surface-code); + color: var(--surface-code-text); + padding: 1rem 1.05rem 1.15rem; + font-family: var(--font-mono), monospace; + font-size: 0.82rem; + line-height: 1.65; + white-space: pre; +} + .json-node { border-left: 1px solid color-mix(in srgb, var(--border) 82%, transparent); margin-left: 0.8rem; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index dceb4a6..03556de 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,7 +1,6 @@ import type { Metadata } from "next"; import { Fraunces, IBM_Plex_Mono, IBM_Plex_Sans } from "next/font/google"; import type { ReactNode } from "react"; -import { ThemeProvider } from "@/components/theme-provider"; import { getCanonicalSiteUrl, getMetadataBase } from "@/lib/site/canonical-base"; import "./globals.css"; @@ -32,9 +31,21 @@ export const metadata: Metadata = { description: "A static, zero-retention artifact viewer shell for fragment-based markdown, code, diff, CSV, and JSON payloads.", }; +const themeInitScript = ` +(() => { + try { + const stored = window.localStorage.getItem("theme"); + const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; + const resolved = stored === "dark" || ((stored === null || stored === "system") && prefersDark) ? "dark" : "light"; + document.documentElement.classList.toggle("dark", resolved === "dark"); + document.documentElement.style.colorScheme = resolved; + } catch {} +})(); +`; + /** - * Root layout for the static shell that installs fonts and global theme context for all viewer states. - * Accepts `children` from Next.js app routing and wraps them with the shared `ThemeProvider`. + * Root layout for the static shell that installs fonts and the pre-hydration theme class. + * Accepts `children` from Next.js app routing and keeps the exported shell provider-free. * Sets hydration-safe HTML/body structure used by lazy renderer mounts and fallback screens. */ export default function RootLayout({ @@ -44,10 +55,11 @@ export default function RootLayout({ }>) { return ( + +