Compare commits
24 Commits
731547e6df
...
8ec4ecd310
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ec4ecd310 | |||
| cb73a9b979 | |||
| 178870427c | |||
| 824ad5a3d3 | |||
| 7615def3d1 | |||
| 18db852f30 | |||
| 225c477f8c | |||
| 82a8c511fc | |||
| 230ec60f24 | |||
| 065302544d | |||
| 93d3858a37 | |||
| 0ff24888ce | |||
| 76eedebf05 | |||
| 0f9c8fb8a3 | |||
| f0ea1a294f | |||
| b542952619 | |||
| ba9db2d75a | |||
| c1781d7831 | |||
| 1e78c7f0f9 | |||
| 30a861f910 | |||
| 6979bd1010 | |||
| a0067c73d6 | |||
| ac1f92d816 | |||
| afabdf4655 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@ public/style.css
|
||||
public/highlight.css
|
||||
public/tiptap
|
||||
public/lightbox
|
||||
public/yggdrasil-core
|
||||
public/doc
|
||||
/static
|
||||
.env
|
||||
|
||||
19
AGENTS.md
19
AGENTS.md
@ -15,7 +15,7 @@ make build # build-editor → highlight-css → tailwindcss → doc →
|
||||
make build-linux # same as build but targets x86_64-unknown-linux-musl
|
||||
make css # one-shot CSS
|
||||
make css-watch # watch mode
|
||||
make test # cargo test + vitest (tiptap-editor + lightbox libs)
|
||||
make test # cargo test + vitest (tiptap-editor + lightbox + yggdrasil-core libs)
|
||||
make doc # cargo doc (ayu 主题) → 拷贝到 public/doc/,随 build 发布
|
||||
make doc-open # 同 doc,生成后自动用浏览器打开(本地预览,不拷贝)
|
||||
make clean # cargo clean + rm public/style.css
|
||||
@ -150,6 +150,18 @@ Image lightbox (click-to-zoom) in `libs/lightbox/`, built as an IIFE library. Un
|
||||
|
||||
Do not edit `public/lightbox/` — they are build artifacts.
|
||||
|
||||
## Yggdrasil-Core Subproject
|
||||
|
||||
Core JavaScript bundle in `libs/yggdrasil-core/`, built as an IIFE library exposing `window.__initPostContent` and `window.__startThemeTransition` (and future core JS entry points). This is the designated home for all new core JS — add to it rather than creating new `public/js/` scripts.
|
||||
|
||||
- Output: `public/yggdrasil-core/` (`yggdrasil-core.js`, `yggdrasil-core.css`, `yggdrasil-core.js.map`)
|
||||
- `make build` runs `npm ci --include=dev && npm run build` inside `libs/yggdrasil-core`; the `build` script is `tsc --noEmit && vite build` (Vite 8 / Rolldown, type-check before bundle). Source is ES module, output is IIFE (`formats: ['iife']` in `vite.config.ts`) because Dioxus `[web.resource] script` injects bare `<script src>` without `type="module"` support.
|
||||
- Injected globally via `Dioxus.toml` (`script` + `style` arrays). Rust calls the entry points via `js_sys::eval("window.__xxx(...)")` with an `if (window.__xxx)` guard to survive script-load ordering races.
|
||||
- Unit tests: `npm test` (Vitest 4 + happy-dom), covering `post-content` copy-button lifecycle and `theme-transition` fallback paths (happy-dom lacks `startViewTransition`, naturally covering the degradation path).
|
||||
- Theme reveal animation uses the View Transitions API: JS synchronously toggles the `dark` class inside `startViewTransition`'s callback (so the browser snapshots the new state), CSS `@keyframes tt-reveal` expands `::view-transition-new(root)` via `clip-path: circle()` from the click point; Rust's `theme.set()` aligns state afterward (idempotent — `use_effect` sets the same class again). `prefers-reduced-motion` and browsers without VT fall back to instant switch.
|
||||
|
||||
Do not edit `public/yggdrasil-core/` — they are build artifacts.
|
||||
|
||||
## Syntax Highlighting Pipeline
|
||||
|
||||
- `themes/` contains Catppuccin Latte (light) and Mocha (dark) `.tmTheme` files
|
||||
@ -173,7 +185,7 @@ Do not edit `public/lightbox/` — they are build artifacts.
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
make test # cargo test (Rust, 402 tests) + vitest (tiptap-editor 46 tests, lightbox 23 tests)
|
||||
make test # cargo test (Rust, 402 tests) + vitest (tiptap-editor 46 tests, lightbox 23 tests, yggdrasil-core 9 tests)
|
||||
dx check # Dioxus type-check (catches component/Router issues)
|
||||
cargo clippy # lint
|
||||
```
|
||||
@ -193,8 +205,9 @@ Most tests use `#[cfg(all(test, feature = "server"))]` — they only run when th
|
||||
- `public/highlight.css` — generated by `generate_highlight_css` binary
|
||||
- `public/tiptap/` — Vite build output (editor)
|
||||
- `public/lightbox/` — Vite build output (lightbox)
|
||||
- `public/yggdrasil-core/` — Vite build output (core JS bundle)
|
||||
- `/dist`, `/.dioxus`, `/target`
|
||||
- `node_modules` (inside `libs/tiptap-editor/` and `libs/lightbox/`)
|
||||
- `node_modules` (inside `libs/tiptap-editor/`, `libs/lightbox/`, and `libs/yggdrasil-core/`)
|
||||
- `uploads/.cache/` — image processing disk cache
|
||||
|
||||
## Notes
|
||||
|
||||
@ -10,9 +10,9 @@ title = "Yggdrasil - Dioxus SSR"
|
||||
watch_path = ["src", "Cargo.toml"]
|
||||
|
||||
[web.resource]
|
||||
style = ["/style.css", "/highlight.css", "/tiptap/editor.css", "/lightbox/lightbox.css"]
|
||||
script = ["/tiptap/editor.js", "/lightbox/lightbox.js"]
|
||||
style = ["/style.css", "/highlight.css", "/tiptap/editor.css", "/lightbox/lightbox.css", "/yggdrasil-core/yggdrasil-core.css"]
|
||||
script = ["/tiptap/editor.js", "/lightbox/lightbox.js", "/yggdrasil-core/yggdrasil-core.js"]
|
||||
|
||||
[web.resource.dev]
|
||||
style = ["/style.css", "/highlight.css", "/tiptap/editor.css", "/lightbox/lightbox.css"]
|
||||
script = ["/tiptap/editor.js", "/lightbox/lightbox.js"]
|
||||
style = ["/style.css", "/highlight.css", "/tiptap/editor.css", "/lightbox/lightbox.css", "/yggdrasil-core/yggdrasil-core.css"]
|
||||
script = ["/tiptap/editor.js", "/lightbox/lightbox.js", "/yggdrasil-core/yggdrasil-core.js"]
|
||||
|
||||
51
Makefile
51
Makefile
@ -1,43 +1,59 @@
|
||||
.PHONY: dev build build-linux css css-watch clean build-editor build-editor-incremental build-lightbox build-lightbox-incremental highlight-css test doc doc-open
|
||||
.PHONY: dev build build-linux css css-watch clean build-editor build-editor-incremental build-lightbox build-lightbox-incremental build-core build-core-incremental highlight-css test doc doc-open start
|
||||
|
||||
build:
|
||||
@$(MAKE) build-editor
|
||||
@$(MAKE) build-lightbox
|
||||
@$(MAKE) build-core
|
||||
@$(MAKE) highlight-css
|
||||
@tailwindcss -i input.css -o public/style.css --minify
|
||||
@$(MAKE) doc
|
||||
@dx build --release --debug-symbols=false
|
||||
|
||||
build-linux:
|
||||
@$(MAKE) build-editor
|
||||
@$(MAKE) build-lightbox
|
||||
@$(MAKE) build-core
|
||||
@$(MAKE) highlight-css
|
||||
@tailwindcss -i input.css -o public/style.css --minify
|
||||
@dx build @client --release --debug-symbols=false --wasm-js-cfg false
|
||||
@dx build @server --release --debug-symbols=false --target x86_64-unknown-linux-musl --wasm-js-cfg false --features server
|
||||
@echo ""
|
||||
@echo "Linux build complete! The server binary is at target/dx/yggdrasil/release/web/server"
|
||||
@echo "Remember to deploy it alongside the target/dx/yggdrasil/release/web/public directory."
|
||||
@echo "When running the server, ensure DIOXUS_ASSET_DIR is set or the public directory is in CWD."
|
||||
|
||||
highlight-css:
|
||||
@cargo run --bin generate_highlight_css
|
||||
|
||||
build-editor:
|
||||
@echo "Building Tiptap editor..."
|
||||
@cd libs/tiptap-editor && npm ci --include=dev && npm run build
|
||||
@cd libs/tiptap-editor && pnpm ci --include=dev && pnpm run build
|
||||
@echo "Tiptap editor built."
|
||||
|
||||
# dev 用的增量构建:跳过 npm ci(假设 node_modules 已存在),仅 vite build。
|
||||
# dev 用的增量构建:跳过 pnpm ci(假设 node_modules 已存在),仅 vite build。
|
||||
# 与 build-editor 分开,避免每次 make dev 都重装依赖。
|
||||
build-editor-incremental:
|
||||
@cd libs/tiptap-editor && npm run build
|
||||
@cd libs/tiptap-editor && pnpm run build
|
||||
|
||||
build-lightbox:
|
||||
@echo "Building Lightbox..."
|
||||
@cd libs/lightbox && npm install && npm run build
|
||||
@cd libs/lightbox && pnpm install && pnpm run build
|
||||
@echo "Lightbox built."
|
||||
|
||||
# dev 用的增量构建:跳过 npm ci(假设 node_modules 已存在),仅 vite build。
|
||||
# dev 用的增量构建:跳过 pnpm ci(假设 node_modules 已存在),仅 vite build。
|
||||
build-lightbox-incremental:
|
||||
@cd libs/lightbox && npm run build
|
||||
@cd libs/lightbox && pnpm run build
|
||||
|
||||
dev: build-editor-incremental build-lightbox-incremental
|
||||
build-core:
|
||||
@echo "Building yggdrasil-core..."
|
||||
@cd libs/yggdrasil-core && pnpm install && pnpm run build
|
||||
@echo "yggdrasil-core built."
|
||||
|
||||
# dev 用的增量构建:跳过 pnpm install(假设 node_modules 已存在),仅 vite build。
|
||||
build-core-incremental:
|
||||
@cd libs/yggdrasil-core && pnpm run build
|
||||
|
||||
dev: build-editor-incremental build-lightbox-incremental build-core-incremental
|
||||
@echo "Cleaning static/..."
|
||||
@rm -rf static/
|
||||
@echo "Building Tiptap editor (incremental)..."
|
||||
@ -55,16 +71,30 @@ css-watch:
|
||||
|
||||
test:
|
||||
@cargo test
|
||||
@cd libs/tiptap-editor && npm test
|
||||
@cd libs/lightbox && npm test
|
||||
@cd libs/tiptap-editor && pnpm test
|
||||
@cd libs/lightbox && pnpm test
|
||||
@cd libs/yggdrasil-core && pnpm test
|
||||
|
||||
# 只编译当前 crate 的文档(--no-deps 跳过依赖,--document-private-items
|
||||
# 让纯 binary crate 的内部模块/私有项也进文档,否则页面基本是空的)。
|
||||
# RUSTDOCFLAGS 把 rustdoc 的 --default-theme=ayu 透传过去——cargo doc 本身
|
||||
# 无主题参数,但会把该环境变量转交给底层 rustdoc。注意它是默认值,浏览器
|
||||
# 若已记住上次的主题选择(localStorage)则不会被覆盖。
|
||||
#
|
||||
# 生成后拷贝到 public/doc/,让文档随 Dioxus 静态目录发布。先清空旧目录再
|
||||
# 整体拷贝,避免删除模块后残留旧文件。rustdoc 内部用相对路径引用资源
|
||||
# (如 ../../static.files/),原样挂载不会断链。
|
||||
#
|
||||
# 额外生成 public/doc/index.html 重定向页:Dioxus 在 dev 用
|
||||
# nest_service("/doc", ServeDir) 托管该目录,ServeDir 访问目录根时默认
|
||||
# 返回 index.html。用 meta refresh + JS 跳转到真正的文档入口
|
||||
# yggdrasil/index.html,这样裸路径 /doc 也能直达文档,且不与 Dioxus 的
|
||||
# /doc/* 路由冲突(手动注册 /doc 会在 merge 时 panic)。
|
||||
doc:
|
||||
@RUSTDOCFLAGS="--default-theme=ayu" cargo doc --no-deps --document-private-items
|
||||
@rm -rf public/doc
|
||||
@cp -r target/doc public/doc
|
||||
@printf '<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0;url=yggdrasil/index.html"><title>Redirecting…</title></head><body><script>location.replace("yggdrasil/index.html")</script></body></html>' > public/doc/index.html
|
||||
|
||||
# 同 doc,生成完自动用浏览器打开。
|
||||
doc-open:
|
||||
@ -73,4 +103,5 @@ doc-open:
|
||||
clean:
|
||||
@cargo clean
|
||||
@rm -f public/style.css public/highlight.css
|
||||
@rm -rf public/doc
|
||||
@rm -rf uploads/.cache
|
||||
|
||||
@ -29,13 +29,13 @@
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
background-color: var(--color-paper-theme);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--color-paper-theme);
|
||||
color: var(--color-paper-primary);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
1372
libs/lightbox/package-lock.json
generated
1372
libs/lightbox/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
849
libs/lightbox/pnpm-lock.yaml
generated
Normal file
849
libs/lightbox/pnpm-lock.yaml
generated
Normal file
@ -0,0 +1,849 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
devDependencies:
|
||||
happy-dom:
|
||||
specifier: ^20.10.6
|
||||
version: 20.10.6
|
||||
typescript:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
vite:
|
||||
specifier: ^8.1.0
|
||||
version: 8.1.0(@types/node@26.0.1)
|
||||
vitest:
|
||||
specifier: ^4.1.9
|
||||
version: 4.1.9(@types/node@26.0.1)(happy-dom@20.10.6)(vite@8.1.0(@types/node@26.0.1))
|
||||
|
||||
packages:
|
||||
|
||||
'@emnapi/core@1.11.1':
|
||||
resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
|
||||
|
||||
'@emnapi/runtime@1.11.1':
|
||||
resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
|
||||
|
||||
'@emnapi/wasi-threads@1.2.2':
|
||||
resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
'@napi-rs/wasm-runtime@1.1.6':
|
||||
resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
|
||||
peerDependencies:
|
||||
'@emnapi/core': ^1.7.1
|
||||
'@emnapi/runtime': ^1.7.1
|
||||
|
||||
'@oxc-project/types@0.137.0':
|
||||
resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==}
|
||||
|
||||
'@rolldown/binding-android-arm64@1.1.3':
|
||||
resolution: {integrity: sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.1.3':
|
||||
resolution: {integrity: sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.1.3':
|
||||
resolution: {integrity: sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.1.3':
|
||||
resolution: {integrity: sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.1.3':
|
||||
resolution: {integrity: sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.1.3':
|
||||
resolution: {integrity: sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.1.3':
|
||||
resolution: {integrity: sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.1.3':
|
||||
resolution: {integrity: sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.1.3':
|
||||
resolution: {integrity: sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.1.3':
|
||||
resolution: {integrity: sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.1.3':
|
||||
resolution: {integrity: sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.1.3':
|
||||
resolution: {integrity: sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@1.1.3':
|
||||
resolution: {integrity: sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [wasm32]
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.1.3':
|
||||
resolution: {integrity: sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.1.3':
|
||||
resolution: {integrity: sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/pluginutils@1.0.1':
|
||||
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
|
||||
|
||||
'@standard-schema/spec@1.1.0':
|
||||
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||
|
||||
'@tybys/wasm-util@0.10.3':
|
||||
resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
||||
|
||||
'@types/deep-eql@4.0.2':
|
||||
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||
|
||||
'@types/estree@1.0.9':
|
||||
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||
|
||||
'@types/node@26.0.1':
|
||||
resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==}
|
||||
|
||||
'@types/whatwg-mimetype@3.0.2':
|
||||
resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
|
||||
|
||||
'@vitest/expect@4.1.9':
|
||||
resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==}
|
||||
|
||||
'@vitest/mocker@4.1.9':
|
||||
resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
msw:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/pretty-format@4.1.9':
|
||||
resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==}
|
||||
|
||||
'@vitest/runner@4.1.9':
|
||||
resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==}
|
||||
|
||||
'@vitest/snapshot@4.1.9':
|
||||
resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==}
|
||||
|
||||
'@vitest/spy@4.1.9':
|
||||
resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==}
|
||||
|
||||
'@vitest/utils@4.1.9':
|
||||
resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==}
|
||||
|
||||
assertion-error@2.0.1:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
buffer-image-size@0.6.4:
|
||||
resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
|
||||
chai@6.2.2:
|
||||
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
convert-source-map@2.0.0:
|
||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
entities@7.0.1:
|
||||
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
es-module-lexer@2.1.0:
|
||||
resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
expect-type@1.4.0:
|
||||
resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
fdir@6.5.0:
|
||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
happy-dom@20.10.6:
|
||||
resolution: {integrity: sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
lightningcss-darwin-arm64@1.32.0:
|
||||
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-darwin-x64@1.32.0:
|
||||
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-freebsd-x64@1.32.0:
|
||||
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.32.0:
|
||||
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss-win32-x64-msvc@1.32.0:
|
||||
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss@1.32.0:
|
||||
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
nanoid@3.3.15:
|
||||
resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
obug@2.1.3:
|
||||
resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@4.0.4:
|
||||
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
postcss@8.5.15:
|
||||
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
rolldown@1.1.3:
|
||||
resolution: {integrity: sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
|
||||
siginfo@2.0.0:
|
||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
stackback@0.0.2:
|
||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||
|
||||
std-env@4.1.0:
|
||||
resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
tinyexec@1.2.4:
|
||||
resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tinyrainbow@3.1.0:
|
||||
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
typescript@6.0.3:
|
||||
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
undici-types@8.3.0:
|
||||
resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==}
|
||||
|
||||
vite@8.1.0:
|
||||
resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^20.19.0 || >=22.12.0
|
||||
'@vitejs/devtools': ^0.3.0
|
||||
esbuild: ^0.27.0 || ^0.28.0
|
||||
jiti: '>=1.21.0'
|
||||
less: ^4.0.0
|
||||
sass: ^1.70.0
|
||||
sass-embedded: ^1.70.0
|
||||
stylus: '>=0.54.8'
|
||||
sugarss: ^5.0.0
|
||||
terser: ^5.16.0
|
||||
tsx: ^4.8.1
|
||||
yaml: ^2.4.2
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitejs/devtools':
|
||||
optional: true
|
||||
esbuild:
|
||||
optional: true
|
||||
jiti:
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
sass-embedded:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
tsx:
|
||||
optional: true
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
vitest@4.1.9:
|
||||
resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==}
|
||||
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@opentelemetry/api': ^1.9.0
|
||||
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
|
||||
'@vitest/browser-playwright': 4.1.9
|
||||
'@vitest/browser-preview': 4.1.9
|
||||
'@vitest/browser-webdriverio': 4.1.9
|
||||
'@vitest/coverage-istanbul': 4.1.9
|
||||
'@vitest/coverage-v8': 4.1.9
|
||||
'@vitest/ui': 4.1.9
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
'@opentelemetry/api':
|
||||
optional: true
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitest/browser-playwright':
|
||||
optional: true
|
||||
'@vitest/browser-preview':
|
||||
optional: true
|
||||
'@vitest/browser-webdriverio':
|
||||
optional: true
|
||||
'@vitest/coverage-istanbul':
|
||||
optional: true
|
||||
'@vitest/coverage-v8':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
whatwg-mimetype@3.0.0:
|
||||
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
ws@8.21.0:
|
||||
resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@emnapi/core@1.11.1':
|
||||
dependencies:
|
||||
'@emnapi/wasi-threads': 1.2.2
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/runtime@1.11.1':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/wasi-threads@1.2.2':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.11.1
|
||||
'@emnapi/runtime': 1.11.1
|
||||
'@tybys/wasm-util': 0.10.3
|
||||
optional: true
|
||||
|
||||
'@oxc-project/types@0.137.0': {}
|
||||
|
||||
'@rolldown/binding-android-arm64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@1.1.3':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.11.1
|
||||
'@emnapi/runtime': 1.11.1
|
||||
'@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/pluginutils@1.0.1': {}
|
||||
|
||||
'@standard-schema/spec@1.1.0': {}
|
||||
|
||||
'@tybys/wasm-util@0.10.3':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
dependencies:
|
||||
'@types/deep-eql': 4.0.2
|
||||
assertion-error: 2.0.1
|
||||
|
||||
'@types/deep-eql@4.0.2': {}
|
||||
|
||||
'@types/estree@1.0.9': {}
|
||||
|
||||
'@types/node@26.0.1':
|
||||
dependencies:
|
||||
undici-types: 8.3.0
|
||||
|
||||
'@types/whatwg-mimetype@3.0.2': {}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 26.0.1
|
||||
|
||||
'@vitest/expect@4.1.9':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.1.0
|
||||
'@types/chai': 5.2.3
|
||||
'@vitest/spy': 4.1.9
|
||||
'@vitest/utils': 4.1.9
|
||||
chai: 6.2.2
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
'@vitest/mocker@4.1.9(vite@8.1.0(@types/node@26.0.1))':
|
||||
dependencies:
|
||||
'@vitest/spy': 4.1.9
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
vite: 8.1.0(@types/node@26.0.1)
|
||||
|
||||
'@vitest/pretty-format@4.1.9':
|
||||
dependencies:
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
'@vitest/runner@4.1.9':
|
||||
dependencies:
|
||||
'@vitest/utils': 4.1.9
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/snapshot@4.1.9':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 4.1.9
|
||||
'@vitest/utils': 4.1.9
|
||||
magic-string: 0.30.21
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/spy@4.1.9': {}
|
||||
|
||||
'@vitest/utils@4.1.9':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 4.1.9
|
||||
convert-source-map: 2.0.0
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
buffer-image-size@0.6.4:
|
||||
dependencies:
|
||||
'@types/node': 26.0.1
|
||||
|
||||
chai@6.2.2: {}
|
||||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
entities@7.0.1: {}
|
||||
|
||||
es-module-lexer@2.1.0: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.9
|
||||
|
||||
expect-type@1.4.0: {}
|
||||
|
||||
fdir@6.5.0(picomatch@4.0.4):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.4
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
happy-dom@20.10.6:
|
||||
dependencies:
|
||||
'@types/node': 26.0.1
|
||||
'@types/whatwg-mimetype': 3.0.2
|
||||
'@types/ws': 8.18.1
|
||||
buffer-image-size: 0.6.4
|
||||
entities: 7.0.1
|
||||
whatwg-mimetype: 3.0.0
|
||||
ws: 8.21.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-arm64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-x64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-freebsd-x64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-x64-msvc@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss@1.32.0:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
optionalDependencies:
|
||||
lightningcss-android-arm64: 1.32.0
|
||||
lightningcss-darwin-arm64: 1.32.0
|
||||
lightningcss-darwin-x64: 1.32.0
|
||||
lightningcss-freebsd-x64: 1.32.0
|
||||
lightningcss-linux-arm-gnueabihf: 1.32.0
|
||||
lightningcss-linux-arm64-gnu: 1.32.0
|
||||
lightningcss-linux-arm64-musl: 1.32.0
|
||||
lightningcss-linux-x64-gnu: 1.32.0
|
||||
lightningcss-linux-x64-musl: 1.32.0
|
||||
lightningcss-win32-arm64-msvc: 1.32.0
|
||||
lightningcss-win32-x64-msvc: 1.32.0
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
nanoid@3.3.15: {}
|
||||
|
||||
obug@2.1.3: {}
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.4: {}
|
||||
|
||||
postcss@8.5.15:
|
||||
dependencies:
|
||||
nanoid: 3.3.15
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
rolldown@1.1.3:
|
||||
dependencies:
|
||||
'@oxc-project/types': 0.137.0
|
||||
'@rolldown/pluginutils': 1.0.1
|
||||
optionalDependencies:
|
||||
'@rolldown/binding-android-arm64': 1.1.3
|
||||
'@rolldown/binding-darwin-arm64': 1.1.3
|
||||
'@rolldown/binding-darwin-x64': 1.1.3
|
||||
'@rolldown/binding-freebsd-x64': 1.1.3
|
||||
'@rolldown/binding-linux-arm-gnueabihf': 1.1.3
|
||||
'@rolldown/binding-linux-arm64-gnu': 1.1.3
|
||||
'@rolldown/binding-linux-arm64-musl': 1.1.3
|
||||
'@rolldown/binding-linux-ppc64-gnu': 1.1.3
|
||||
'@rolldown/binding-linux-s390x-gnu': 1.1.3
|
||||
'@rolldown/binding-linux-x64-gnu': 1.1.3
|
||||
'@rolldown/binding-linux-x64-musl': 1.1.3
|
||||
'@rolldown/binding-openharmony-arm64': 1.1.3
|
||||
'@rolldown/binding-wasm32-wasi': 1.1.3
|
||||
'@rolldown/binding-win32-arm64-msvc': 1.1.3
|
||||
'@rolldown/binding-win32-x64-msvc': 1.1.3
|
||||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
stackback@0.0.2: {}
|
||||
|
||||
std-env@4.1.0: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinyexec@1.2.4: {}
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
|
||||
tinyrainbow@3.1.0: {}
|
||||
|
||||
tslib@2.8.1:
|
||||
optional: true
|
||||
|
||||
typescript@6.0.3: {}
|
||||
|
||||
undici-types@8.3.0: {}
|
||||
|
||||
vite@8.1.0(@types/node@26.0.1):
|
||||
dependencies:
|
||||
lightningcss: 1.32.0
|
||||
picomatch: 4.0.4
|
||||
postcss: 8.5.15
|
||||
rolldown: 1.1.3
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
'@types/node': 26.0.1
|
||||
fsevents: 2.3.3
|
||||
|
||||
vitest@4.1.9(@types/node@26.0.1)(happy-dom@20.10.6)(vite@8.1.0(@types/node@26.0.1)):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.1.9
|
||||
'@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@26.0.1))
|
||||
'@vitest/pretty-format': 4.1.9
|
||||
'@vitest/runner': 4.1.9
|
||||
'@vitest/snapshot': 4.1.9
|
||||
'@vitest/spy': 4.1.9
|
||||
'@vitest/utils': 4.1.9
|
||||
es-module-lexer: 2.1.0
|
||||
expect-type: 1.4.0
|
||||
magic-string: 0.30.21
|
||||
obug: 2.1.3
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.4
|
||||
std-env: 4.1.0
|
||||
tinybench: 2.9.0
|
||||
tinyexec: 1.2.4
|
||||
tinyglobby: 0.2.17
|
||||
tinyrainbow: 3.1.0
|
||||
vite: 8.1.0(@types/node@26.0.1)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 26.0.1
|
||||
happy-dom: 20.10.6
|
||||
transitivePeerDependencies:
|
||||
- msw
|
||||
|
||||
whatwg-mimetype@3.0.0: {}
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
dependencies:
|
||||
siginfo: 2.0.0
|
||||
stackback: 0.0.2
|
||||
|
||||
ws@8.21.0: {}
|
||||
2093
libs/tiptap-editor/package-lock.json
generated
2093
libs/tiptap-editor/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
1374
libs/tiptap-editor/pnpm-lock.yaml
generated
Normal file
1374
libs/tiptap-editor/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
1
libs/yggdrasil-core/.gitignore
vendored
Normal file
1
libs/yggdrasil-core/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
node_modules
|
||||
19
libs/yggdrasil-core/package.json
Normal file
19
libs/yggdrasil-core/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@yggdrasil/core",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc --noEmit && vite build",
|
||||
"dev": "vite",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"happy-dom": "^20.10.6",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.1.0",
|
||||
"vitest": "^4.1.9"
|
||||
}
|
||||
}
|
||||
849
libs/yggdrasil-core/pnpm-lock.yaml
generated
Normal file
849
libs/yggdrasil-core/pnpm-lock.yaml
generated
Normal file
@ -0,0 +1,849 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
devDependencies:
|
||||
happy-dom:
|
||||
specifier: ^20.10.6
|
||||
version: 20.10.6
|
||||
typescript:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
vite:
|
||||
specifier: ^8.1.0
|
||||
version: 8.1.0(@types/node@26.0.1)
|
||||
vitest:
|
||||
specifier: ^4.1.9
|
||||
version: 4.1.9(@types/node@26.0.1)(happy-dom@20.10.6)(vite@8.1.0(@types/node@26.0.1))
|
||||
|
||||
packages:
|
||||
|
||||
'@emnapi/core@1.11.1':
|
||||
resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
|
||||
|
||||
'@emnapi/runtime@1.11.1':
|
||||
resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
|
||||
|
||||
'@emnapi/wasi-threads@1.2.2':
|
||||
resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
'@napi-rs/wasm-runtime@1.1.6':
|
||||
resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
|
||||
peerDependencies:
|
||||
'@emnapi/core': ^1.7.1
|
||||
'@emnapi/runtime': ^1.7.1
|
||||
|
||||
'@oxc-project/types@0.137.0':
|
||||
resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==}
|
||||
|
||||
'@rolldown/binding-android-arm64@1.1.3':
|
||||
resolution: {integrity: sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.1.3':
|
||||
resolution: {integrity: sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.1.3':
|
||||
resolution: {integrity: sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.1.3':
|
||||
resolution: {integrity: sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.1.3':
|
||||
resolution: {integrity: sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.1.3':
|
||||
resolution: {integrity: sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.1.3':
|
||||
resolution: {integrity: sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.1.3':
|
||||
resolution: {integrity: sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.1.3':
|
||||
resolution: {integrity: sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.1.3':
|
||||
resolution: {integrity: sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.1.3':
|
||||
resolution: {integrity: sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.1.3':
|
||||
resolution: {integrity: sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@1.1.3':
|
||||
resolution: {integrity: sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [wasm32]
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.1.3':
|
||||
resolution: {integrity: sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.1.3':
|
||||
resolution: {integrity: sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/pluginutils@1.0.1':
|
||||
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
|
||||
|
||||
'@standard-schema/spec@1.1.0':
|
||||
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||
|
||||
'@tybys/wasm-util@0.10.3':
|
||||
resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
||||
|
||||
'@types/deep-eql@4.0.2':
|
||||
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||
|
||||
'@types/estree@1.0.9':
|
||||
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||
|
||||
'@types/node@26.0.1':
|
||||
resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==}
|
||||
|
||||
'@types/whatwg-mimetype@3.0.2':
|
||||
resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
|
||||
|
||||
'@vitest/expect@4.1.9':
|
||||
resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==}
|
||||
|
||||
'@vitest/mocker@4.1.9':
|
||||
resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
msw:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/pretty-format@4.1.9':
|
||||
resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==}
|
||||
|
||||
'@vitest/runner@4.1.9':
|
||||
resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==}
|
||||
|
||||
'@vitest/snapshot@4.1.9':
|
||||
resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==}
|
||||
|
||||
'@vitest/spy@4.1.9':
|
||||
resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==}
|
||||
|
||||
'@vitest/utils@4.1.9':
|
||||
resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==}
|
||||
|
||||
assertion-error@2.0.1:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
buffer-image-size@0.6.4:
|
||||
resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
|
||||
chai@6.2.2:
|
||||
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
convert-source-map@2.0.0:
|
||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
entities@7.0.1:
|
||||
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
es-module-lexer@2.1.0:
|
||||
resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
expect-type@1.4.0:
|
||||
resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
fdir@6.5.0:
|
||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
happy-dom@20.10.6:
|
||||
resolution: {integrity: sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
lightningcss-darwin-arm64@1.32.0:
|
||||
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-darwin-x64@1.32.0:
|
||||
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-freebsd-x64@1.32.0:
|
||||
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.32.0:
|
||||
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss-win32-x64-msvc@1.32.0:
|
||||
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss@1.32.0:
|
||||
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
nanoid@3.3.15:
|
||||
resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
obug@2.1.3:
|
||||
resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@4.0.4:
|
||||
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
postcss@8.5.15:
|
||||
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
rolldown@1.1.3:
|
||||
resolution: {integrity: sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
|
||||
siginfo@2.0.0:
|
||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
stackback@0.0.2:
|
||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||
|
||||
std-env@4.1.0:
|
||||
resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
tinyexec@1.2.4:
|
||||
resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tinyrainbow@3.1.0:
|
||||
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
typescript@6.0.3:
|
||||
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
undici-types@8.3.0:
|
||||
resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==}
|
||||
|
||||
vite@8.1.0:
|
||||
resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^20.19.0 || >=22.12.0
|
||||
'@vitejs/devtools': ^0.3.0
|
||||
esbuild: ^0.27.0 || ^0.28.0
|
||||
jiti: '>=1.21.0'
|
||||
less: ^4.0.0
|
||||
sass: ^1.70.0
|
||||
sass-embedded: ^1.70.0
|
||||
stylus: '>=0.54.8'
|
||||
sugarss: ^5.0.0
|
||||
terser: ^5.16.0
|
||||
tsx: ^4.8.1
|
||||
yaml: ^2.4.2
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitejs/devtools':
|
||||
optional: true
|
||||
esbuild:
|
||||
optional: true
|
||||
jiti:
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
sass-embedded:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
tsx:
|
||||
optional: true
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
vitest@4.1.9:
|
||||
resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==}
|
||||
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@opentelemetry/api': ^1.9.0
|
||||
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
|
||||
'@vitest/browser-playwright': 4.1.9
|
||||
'@vitest/browser-preview': 4.1.9
|
||||
'@vitest/browser-webdriverio': 4.1.9
|
||||
'@vitest/coverage-istanbul': 4.1.9
|
||||
'@vitest/coverage-v8': 4.1.9
|
||||
'@vitest/ui': 4.1.9
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
'@opentelemetry/api':
|
||||
optional: true
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitest/browser-playwright':
|
||||
optional: true
|
||||
'@vitest/browser-preview':
|
||||
optional: true
|
||||
'@vitest/browser-webdriverio':
|
||||
optional: true
|
||||
'@vitest/coverage-istanbul':
|
||||
optional: true
|
||||
'@vitest/coverage-v8':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
whatwg-mimetype@3.0.0:
|
||||
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
ws@8.21.0:
|
||||
resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@emnapi/core@1.11.1':
|
||||
dependencies:
|
||||
'@emnapi/wasi-threads': 1.2.2
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/runtime@1.11.1':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/wasi-threads@1.2.2':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.11.1
|
||||
'@emnapi/runtime': 1.11.1
|
||||
'@tybys/wasm-util': 0.10.3
|
||||
optional: true
|
||||
|
||||
'@oxc-project/types@0.137.0': {}
|
||||
|
||||
'@rolldown/binding-android-arm64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@1.1.3':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.11.1
|
||||
'@emnapi/runtime': 1.11.1
|
||||
'@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.1.3':
|
||||
optional: true
|
||||
|
||||
'@rolldown/pluginutils@1.0.1': {}
|
||||
|
||||
'@standard-schema/spec@1.1.0': {}
|
||||
|
||||
'@tybys/wasm-util@0.10.3':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
dependencies:
|
||||
'@types/deep-eql': 4.0.2
|
||||
assertion-error: 2.0.1
|
||||
|
||||
'@types/deep-eql@4.0.2': {}
|
||||
|
||||
'@types/estree@1.0.9': {}
|
||||
|
||||
'@types/node@26.0.1':
|
||||
dependencies:
|
||||
undici-types: 8.3.0
|
||||
|
||||
'@types/whatwg-mimetype@3.0.2': {}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 26.0.1
|
||||
|
||||
'@vitest/expect@4.1.9':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.1.0
|
||||
'@types/chai': 5.2.3
|
||||
'@vitest/spy': 4.1.9
|
||||
'@vitest/utils': 4.1.9
|
||||
chai: 6.2.2
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
'@vitest/mocker@4.1.9(vite@8.1.0(@types/node@26.0.1))':
|
||||
dependencies:
|
||||
'@vitest/spy': 4.1.9
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
vite: 8.1.0(@types/node@26.0.1)
|
||||
|
||||
'@vitest/pretty-format@4.1.9':
|
||||
dependencies:
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
'@vitest/runner@4.1.9':
|
||||
dependencies:
|
||||
'@vitest/utils': 4.1.9
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/snapshot@4.1.9':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 4.1.9
|
||||
'@vitest/utils': 4.1.9
|
||||
magic-string: 0.30.21
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/spy@4.1.9': {}
|
||||
|
||||
'@vitest/utils@4.1.9':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 4.1.9
|
||||
convert-source-map: 2.0.0
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
buffer-image-size@0.6.4:
|
||||
dependencies:
|
||||
'@types/node': 26.0.1
|
||||
|
||||
chai@6.2.2: {}
|
||||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
entities@7.0.1: {}
|
||||
|
||||
es-module-lexer@2.1.0: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.9
|
||||
|
||||
expect-type@1.4.0: {}
|
||||
|
||||
fdir@6.5.0(picomatch@4.0.4):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.4
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
happy-dom@20.10.6:
|
||||
dependencies:
|
||||
'@types/node': 26.0.1
|
||||
'@types/whatwg-mimetype': 3.0.2
|
||||
'@types/ws': 8.18.1
|
||||
buffer-image-size: 0.6.4
|
||||
entities: 7.0.1
|
||||
whatwg-mimetype: 3.0.0
|
||||
ws: 8.21.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-arm64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-x64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-freebsd-x64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-x64-msvc@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss@1.32.0:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
optionalDependencies:
|
||||
lightningcss-android-arm64: 1.32.0
|
||||
lightningcss-darwin-arm64: 1.32.0
|
||||
lightningcss-darwin-x64: 1.32.0
|
||||
lightningcss-freebsd-x64: 1.32.0
|
||||
lightningcss-linux-arm-gnueabihf: 1.32.0
|
||||
lightningcss-linux-arm64-gnu: 1.32.0
|
||||
lightningcss-linux-arm64-musl: 1.32.0
|
||||
lightningcss-linux-x64-gnu: 1.32.0
|
||||
lightningcss-linux-x64-musl: 1.32.0
|
||||
lightningcss-win32-arm64-msvc: 1.32.0
|
||||
lightningcss-win32-x64-msvc: 1.32.0
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
nanoid@3.3.15: {}
|
||||
|
||||
obug@2.1.3: {}
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.4: {}
|
||||
|
||||
postcss@8.5.15:
|
||||
dependencies:
|
||||
nanoid: 3.3.15
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
rolldown@1.1.3:
|
||||
dependencies:
|
||||
'@oxc-project/types': 0.137.0
|
||||
'@rolldown/pluginutils': 1.0.1
|
||||
optionalDependencies:
|
||||
'@rolldown/binding-android-arm64': 1.1.3
|
||||
'@rolldown/binding-darwin-arm64': 1.1.3
|
||||
'@rolldown/binding-darwin-x64': 1.1.3
|
||||
'@rolldown/binding-freebsd-x64': 1.1.3
|
||||
'@rolldown/binding-linux-arm-gnueabihf': 1.1.3
|
||||
'@rolldown/binding-linux-arm64-gnu': 1.1.3
|
||||
'@rolldown/binding-linux-arm64-musl': 1.1.3
|
||||
'@rolldown/binding-linux-ppc64-gnu': 1.1.3
|
||||
'@rolldown/binding-linux-s390x-gnu': 1.1.3
|
||||
'@rolldown/binding-linux-x64-gnu': 1.1.3
|
||||
'@rolldown/binding-linux-x64-musl': 1.1.3
|
||||
'@rolldown/binding-openharmony-arm64': 1.1.3
|
||||
'@rolldown/binding-wasm32-wasi': 1.1.3
|
||||
'@rolldown/binding-win32-arm64-msvc': 1.1.3
|
||||
'@rolldown/binding-win32-x64-msvc': 1.1.3
|
||||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
stackback@0.0.2: {}
|
||||
|
||||
std-env@4.1.0: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinyexec@1.2.4: {}
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
|
||||
tinyrainbow@3.1.0: {}
|
||||
|
||||
tslib@2.8.1:
|
||||
optional: true
|
||||
|
||||
typescript@6.0.3: {}
|
||||
|
||||
undici-types@8.3.0: {}
|
||||
|
||||
vite@8.1.0(@types/node@26.0.1):
|
||||
dependencies:
|
||||
lightningcss: 1.32.0
|
||||
picomatch: 4.0.4
|
||||
postcss: 8.5.15
|
||||
rolldown: 1.1.3
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
'@types/node': 26.0.1
|
||||
fsevents: 2.3.3
|
||||
|
||||
vitest@4.1.9(@types/node@26.0.1)(happy-dom@20.10.6)(vite@8.1.0(@types/node@26.0.1)):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.1.9
|
||||
'@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@26.0.1))
|
||||
'@vitest/pretty-format': 4.1.9
|
||||
'@vitest/runner': 4.1.9
|
||||
'@vitest/snapshot': 4.1.9
|
||||
'@vitest/spy': 4.1.9
|
||||
'@vitest/utils': 4.1.9
|
||||
es-module-lexer: 2.1.0
|
||||
expect-type: 1.4.0
|
||||
magic-string: 0.30.21
|
||||
obug: 2.1.3
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.4
|
||||
std-env: 4.1.0
|
||||
tinybench: 2.9.0
|
||||
tinyexec: 1.2.4
|
||||
tinyglobby: 0.2.17
|
||||
tinyrainbow: 3.1.0
|
||||
vite: 8.1.0(@types/node@26.0.1)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 26.0.1
|
||||
happy-dom: 20.10.6
|
||||
transitivePeerDependencies:
|
||||
- msw
|
||||
|
||||
whatwg-mimetype@3.0.0: {}
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
dependencies:
|
||||
siginfo: 2.0.0
|
||||
stackback: 0.0.2
|
||||
|
||||
ws@8.21.0: {}
|
||||
15
libs/yggdrasil-core/src/index.ts
Normal file
15
libs/yggdrasil-core/src/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { initPostContent } from './post-content';
|
||||
import { startThemeTransition } from './theme-transition';
|
||||
import './style.css';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__initPostContent: (selector: string) => void;
|
||||
__startThemeTransition: (x: number, y: number) => void;
|
||||
}
|
||||
}
|
||||
|
||||
window.__initPostContent = initPostContent;
|
||||
window.__startThemeTransition = startThemeTransition;
|
||||
|
||||
export {};
|
||||
73
libs/yggdrasil-core/src/post-content.test.ts
Normal file
73
libs/yggdrasil-core/src/post-content.test.ts
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* post-content 测试:钉住代码块 copy 按钮的生成与复制行为。
|
||||
* 黑盒驱动:只通过 window.__initPostContent 入口。
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import './index';
|
||||
|
||||
describe('initPostContent', () => {
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
vi.restoreAllMocks();
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
it('为 pre>code 注入 .copy-code 按钮', () => {
|
||||
const root = document.createElement('div');
|
||||
root.className = 'post-content';
|
||||
root.innerHTML = '<pre><code>console.log(1)</code></pre>';
|
||||
document.body.appendChild(root);
|
||||
|
||||
window.__initPostContent('.post-content');
|
||||
|
||||
const btn = root.querySelector('pre .copy-code');
|
||||
expect(btn).not.toBeNull();
|
||||
expect(btn?.textContent).toBe('copy');
|
||||
});
|
||||
|
||||
it('已有 .copy-code 的 pre 不重复注入', () => {
|
||||
const root = document.createElement('div');
|
||||
root.className = 'post-content';
|
||||
root.innerHTML =
|
||||
'<pre><code>x</code><button class="copy-code">copy</button></pre>';
|
||||
document.body.appendChild(root);
|
||||
|
||||
window.__initPostContent('.post-content');
|
||||
|
||||
const btns = root.querySelectorAll('pre .copy-code');
|
||||
expect(btns.length).toBe(1);
|
||||
});
|
||||
|
||||
it('点击按钮调用 clipboard.writeText 并回显 copied!', () => {
|
||||
const writeText = vi.fn().mockResolvedValue(undefined);
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
value: { writeText },
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const root = document.createElement('div');
|
||||
root.className = 'post-content';
|
||||
root.innerHTML = '<pre><code>hello</code></pre>';
|
||||
document.body.appendChild(root);
|
||||
|
||||
window.__initPostContent('.post-content');
|
||||
|
||||
const btn = root.querySelector('.copy-code') as HTMLButtonElement;
|
||||
btn.click();
|
||||
|
||||
expect(writeText).toHaveBeenCalledWith('hello');
|
||||
expect(btn.textContent).toBe('copied!');
|
||||
|
||||
// 2 秒后还原回 'copy'
|
||||
vi.advanceTimersByTime(2000);
|
||||
expect(btn.textContent).toBe('copy');
|
||||
});
|
||||
|
||||
it('selector 未命中时不报错', () => {
|
||||
expect(() => window.__initPostContent('.not-exist')).not.toThrow();
|
||||
});
|
||||
});
|
||||
48
libs/yggdrasil-core/src/post-content.ts
Normal file
48
libs/yggdrasil-core/src/post-content.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 代码块 copy 按钮。
|
||||
*
|
||||
* 为 pre>code 注入一个 .copy-code 按钮,点击复制代码文本并回显 copied!。
|
||||
* 2 秒后还原文案。样式见 input.css 的 .copy-code 规则(全局 Tailwind 构建)。
|
||||
*/
|
||||
|
||||
function initCopyButtons(root: Element): void {
|
||||
const blocks = root.querySelectorAll('pre > code');
|
||||
blocks.forEach((code) => {
|
||||
const pre = code.parentElement;
|
||||
if (!pre) return;
|
||||
if (pre.querySelector('.copy-code')) return;
|
||||
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'copy-code';
|
||||
btn.textContent = 'copy';
|
||||
btn.setAttribute('aria-label', 'Copy code');
|
||||
|
||||
const codeText = code.textContent || '';
|
||||
btn.addEventListener('click', () => {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(codeText);
|
||||
} else {
|
||||
const ta = document.createElement('textarea');
|
||||
ta.value = codeText;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.opacity = '0';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
btn.textContent = 'copied!';
|
||||
setTimeout(() => {
|
||||
btn.textContent = 'copy';
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
pre.appendChild(btn);
|
||||
});
|
||||
}
|
||||
|
||||
export function initPostContent(selector: string): void {
|
||||
const root = document.querySelector(selector);
|
||||
if (!root) return;
|
||||
initCopyButtons(root);
|
||||
}
|
||||
36
libs/yggdrasil-core/src/style.css
Normal file
36
libs/yggdrasil-core/src/style.css
Normal file
@ -0,0 +1,36 @@
|
||||
/* ========== 主题切换圆形展开(View Transitions API) ========== */
|
||||
|
||||
/* 取消所有 UA 默认的动画和颜色混合 */
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation: none;
|
||||
mix-blend-mode: normal;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 无论什么方向,始终让 new (新状态) 在上方,并从小圆扩展覆盖 old */
|
||||
::view-transition-old(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
::view-transition-new(root) {
|
||||
z-index: 2;
|
||||
animation: tt-expand 0.4s ease-out forwards;
|
||||
/* 显式给伪元素加上不透明背景,防止截图透明导致看不到覆盖效果 */
|
||||
background: var(--color-paper-theme);
|
||||
}
|
||||
|
||||
/* CSS 变量由 JS 注入到 html style 中 */
|
||||
@keyframes tt-expand {
|
||||
from { clip-path: circle(0px at var(--tt-x) var(--tt-y)); }
|
||||
to { clip-path: circle(var(--tt-r) at var(--tt-x) var(--tt-y)); }
|
||||
}
|
||||
|
||||
/* 在 View Transition 期间强制禁用所有 CSS 过渡,
|
||||
确保 VT 回调中 toggle dark class 后截图捕获的是最终颜色。 */
|
||||
html.is-theme-transitioning,
|
||||
html.is-theme-transitioning *,
|
||||
html.is-theme-transitioning *::before,
|
||||
html.is-theme-transitioning *::after {
|
||||
transition: none !important;
|
||||
}
|
||||
108
libs/yggdrasil-core/src/theme-transition.test.ts
Normal file
108
libs/yggdrasil-core/src/theme-transition.test.ts
Normal file
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* theme-transition 测试。
|
||||
*
|
||||
* happy-dom 不提供 document.startViewTransition,天然覆盖降级路径(瞬切 dark class)。
|
||||
* 主路径通过 mock startViewTransition 验证:调用它、设 CSS 变量。
|
||||
*
|
||||
* 注意:startThemeTransition 只接收 (x, y),目标主题(亮/暗)从 DOM 的 dark class
|
||||
* 现状推导(取反),不依赖外部传入——避免与调用方状态不同步。
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import './index';
|
||||
|
||||
describe('startThemeTransition', () => {
|
||||
beforeEach(() => {
|
||||
document.documentElement.classList.remove('dark');
|
||||
document.documentElement.classList.remove('is-theme-transitioning');
|
||||
document.documentElement.style.cssText = '';
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
afterEach(() => {
|
||||
document.documentElement.classList.remove('dark');
|
||||
document.documentElement.classList.remove('is-theme-transitioning');
|
||||
document.documentElement.style.cssText = '';
|
||||
});
|
||||
|
||||
it('降级:无 startViewTransition 时,亮→暗(无 dark class 时 add)', () => {
|
||||
expect(document.documentElement.classList.contains('dark')).toBe(false);
|
||||
|
||||
window.__startThemeTransition(100, 200);
|
||||
|
||||
expect(document.documentElement.classList.contains('dark')).toBe(true);
|
||||
});
|
||||
|
||||
it('降级:无 startViewTransition 时,暗→亮(有 dark class 时 remove)', () => {
|
||||
document.documentElement.classList.add('dark');
|
||||
|
||||
window.__startThemeTransition(100, 200);
|
||||
|
||||
expect(document.documentElement.classList.contains('dark')).toBe(false);
|
||||
});
|
||||
|
||||
it('主路径:有 startViewTransition 时调用它,注入变量,callback 切换 dark class', async () => {
|
||||
const cbRef: { cb: (() => void) | null } = { cb: null };
|
||||
const readyP = Promise.resolve();
|
||||
const finishedP = Promise.resolve();
|
||||
const startVT = vi.fn((cb: () => void) => {
|
||||
cbRef.cb = cb;
|
||||
return { ready: readyP, finished: finishedP, skipTransition: () => {} };
|
||||
});
|
||||
Object.defineProperty(document, 'startViewTransition', {
|
||||
value: startVT,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
|
||||
window.__startThemeTransition(100, 200);
|
||||
|
||||
expect(startVT).toHaveBeenCalledTimes(1);
|
||||
|
||||
// CSS 变量应注入
|
||||
expect(document.documentElement.style.getPropertyValue('--tt-x')).toBe('100px');
|
||||
expect(document.documentElement.style.getPropertyValue('--tt-y')).toBe('200px');
|
||||
expect(document.documentElement.style.getPropertyValue('--tt-r')).toMatch(/^\d+(\.\d+)?px$/);
|
||||
|
||||
// is-theme-transitioning 应在 VT 之前添加
|
||||
expect(document.documentElement.classList.contains('is-theme-transitioning')).toBe(true);
|
||||
|
||||
// callback 里根据 DOM 现状(无 dark)切到 dark
|
||||
cbRef.cb!();
|
||||
expect(document.documentElement.classList.contains('dark')).toBe(true);
|
||||
|
||||
// finished 后移除 is-theme-transitioning 和 CSS 变量
|
||||
await finishedP;
|
||||
await Promise.resolve();
|
||||
// happy-dom microtask 可能需要额外 tick
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
expect(document.documentElement.classList.contains('is-theme-transitioning')).toBe(false);
|
||||
expect(document.documentElement.style.getPropertyValue('--tt-x')).toBe('');
|
||||
|
||||
delete (document as unknown as { startViewTransition?: unknown }).startViewTransition;
|
||||
});
|
||||
|
||||
it('reduced-motion:即使有 startViewTransition 也走降级(瞬切)', () => {
|
||||
const startVT = vi.fn(() => ({ ready: Promise.resolve(), finished: Promise.resolve(), skipTransition: () => {} }));
|
||||
Object.defineProperty(document, 'startViewTransition', {
|
||||
value: startVT,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
vi.stubGlobal('matchMedia', vi.fn((q: string) => ({
|
||||
matches: q.includes('reduce'),
|
||||
media: q,
|
||||
onchange: null,
|
||||
addEventListener: () => {},
|
||||
removeEventListener: () => {},
|
||||
addListener: () => {},
|
||||
removeListener: () => {},
|
||||
dispatchEvent: () => false,
|
||||
})));
|
||||
|
||||
window.__startThemeTransition(0, 0);
|
||||
|
||||
expect(startVT).not.toHaveBeenCalled();
|
||||
expect(document.documentElement.classList.contains('dark')).toBe(true);
|
||||
|
||||
delete (document as unknown as { startViewTransition?: unknown }).startViewTransition;
|
||||
});
|
||||
});
|
||||
84
libs/yggdrasil-core/src/theme-transition.ts
Normal file
84
libs/yggdrasil-core/src/theme-transition.ts
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 圆形展开主题切换动画(View Transitions API)。
|
||||
*
|
||||
* 采用纯 CSS 配合 CSS 变量的实现方案。
|
||||
* 核心策略:始终让"暗色层"在上方,通过 clip-path 揭示下方的"亮色层"。
|
||||
* - 亮 -> 暗: NEW 是暗色(在上方),从小圆扩大(`tt-expand`)覆盖底部的 OLD。
|
||||
* - 暗 -> 亮: OLD 是暗色(在上方),从大圆缩小(`tt-shrink`)揭开底部的 NEW。
|
||||
*
|
||||
* 相比 WAAPI 或动态注入 <style>,这种方式完全没有特异性冲突、DOM 残留或
|
||||
* API 优先级 bug,是目前最稳定的 VT 主题切换方案。
|
||||
*/
|
||||
|
||||
function prefersReducedMotion(): boolean {
|
||||
return (
|
||||
!!window.matchMedia &&
|
||||
window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
);
|
||||
}
|
||||
|
||||
function maxCornerDistance(x: number, y: number): number {
|
||||
const w = window.innerWidth;
|
||||
const h = window.innerHeight;
|
||||
const corners = [
|
||||
[0, 0],
|
||||
[w, 0],
|
||||
[0, h],
|
||||
[w, h],
|
||||
];
|
||||
let max = 0;
|
||||
for (const [cx, cy] of corners) {
|
||||
const d = Math.hypot(cx - x, cy - y);
|
||||
if (d > max) max = d;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
function applyDarkClass(isDark: boolean): void {
|
||||
const html = document.documentElement;
|
||||
if (isDark) {
|
||||
html.classList.add('dark');
|
||||
} else {
|
||||
html.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
export function startThemeTransition(x: number, y: number): void {
|
||||
const html = document.documentElement;
|
||||
const isDark = !html.classList.contains('dark');
|
||||
|
||||
const hasVT = typeof document.startViewTransition === 'function';
|
||||
const reduced = prefersReducedMotion();
|
||||
|
||||
if (!hasVT || reduced) {
|
||||
applyDarkClass(isDark);
|
||||
return;
|
||||
}
|
||||
|
||||
const maxR = maxCornerDistance(x, y);
|
||||
|
||||
// 注入动画需要的 CSS 变量
|
||||
html.style.setProperty('--tt-x', `${x}px`);
|
||||
html.style.setProperty('--tt-y', `${y}px`);
|
||||
html.style.setProperty('--tt-r', `${maxR}px`);
|
||||
|
||||
// 禁用所有 CSS transition,确保 VT 截图是最终颜色
|
||||
html.classList.add('is-theme-transitioning');
|
||||
|
||||
const vt = document.startViewTransition(() => {
|
||||
applyDarkClass(isDark);
|
||||
// 强制同步样式重算:确保 body 的 background-color 解析为目标值
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
getComputedStyle(document.body).backgroundColor;
|
||||
});
|
||||
|
||||
vt.ready.catch(() => {});
|
||||
|
||||
vt.finished.finally(() => {
|
||||
html.classList.remove('is-theme-transitioning');
|
||||
// 清理 CSS 变量
|
||||
html.style.removeProperty('--tt-x');
|
||||
html.style.removeProperty('--tt-y');
|
||||
html.style.removeProperty('--tt-r');
|
||||
});
|
||||
}
|
||||
1
libs/yggdrasil-core/src/vite-env.d.ts
vendored
Normal file
1
libs/yggdrasil-core/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
19
libs/yggdrasil-core/tsconfig.json
Normal file
19
libs/yggdrasil-core/tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
"types": []
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
23
libs/yggdrasil-core/vite.config.ts
Normal file
23
libs/yggdrasil-core/vite.config.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { resolve } from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: resolve(__dirname, '../../public/yggdrasil-core'),
|
||||
emptyOutDir: true,
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/index.ts'),
|
||||
name: 'YggdrasilCore',
|
||||
fileName: () => 'yggdrasil-core.js',
|
||||
formats: ['iife'],
|
||||
},
|
||||
rolldownOptions: {
|
||||
output: {
|
||||
assetFileNames: 'yggdrasil-core.[ext]',
|
||||
},
|
||||
},
|
||||
cssCodeSplit: false,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
},
|
||||
});
|
||||
8
libs/yggdrasil-core/vitest.config.ts
Normal file
8
libs/yggdrasil-core/vitest.config.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'happy-dom',
|
||||
include: ['src/**/*.test.ts'],
|
||||
},
|
||||
});
|
||||
@ -1,48 +0,0 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function initCopyButtons(root) {
|
||||
var blocks = root.querySelectorAll("pre > code");
|
||||
for (var i = 0; i < blocks.length; i++) {
|
||||
var code = blocks[i];
|
||||
var pre = code.parentElement;
|
||||
if (!pre) continue;
|
||||
if (pre.querySelector(".copy-code")) continue;
|
||||
|
||||
var btn = document.createElement("button");
|
||||
btn.className = "copy-code";
|
||||
btn.textContent = "copy";
|
||||
btn.setAttribute("aria-label", "Copy code");
|
||||
|
||||
(function (codeText) {
|
||||
btn.addEventListener("click", function () {
|
||||
var self = this;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(codeText);
|
||||
} else {
|
||||
var ta = document.createElement("textarea");
|
||||
ta.value = codeText;
|
||||
ta.style.position = "fixed";
|
||||
ta.style.opacity = "0";
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
self.textContent = "copied!";
|
||||
setTimeout(function () {
|
||||
self.textContent = "copy";
|
||||
}, 2000);
|
||||
});
|
||||
})(code.textContent || "");
|
||||
|
||||
pre.appendChild(btn);
|
||||
}
|
||||
}
|
||||
|
||||
window.__initPostContent = function (selector) {
|
||||
var root = document.querySelector(selector);
|
||||
if (!root) return;
|
||||
initCopyButtons(root);
|
||||
};
|
||||
})();
|
||||
@ -14,7 +14,8 @@ use dioxus::router::components::Link;
|
||||
#[derive(Props, Clone, PartialEq)]
|
||||
pub struct EmptyStateAction {
|
||||
/// 按钮文案。
|
||||
pub label: &'static str,
|
||||
#[props(into)]
|
||||
pub label: String,
|
||||
/// 跳转目标路由。
|
||||
pub to: crate::router::Route,
|
||||
}
|
||||
@ -28,11 +29,14 @@ pub struct EmptyStateAction {
|
||||
#[component]
|
||||
pub fn EmptyState(
|
||||
/// 主标题(通常为「还没有文章」之类)。
|
||||
#[props(default)] title: Option<&'static str>,
|
||||
#[props(into, default = "还没有文章".to_string())]
|
||||
title: String,
|
||||
/// 副文案,说明当前状态或引导用户。
|
||||
#[props(default)] description: Option<&'static str>,
|
||||
#[props(into, default = String::new())]
|
||||
description: String,
|
||||
/// 可选的行动按钮。
|
||||
#[props(default)] action: Option<EmptyStateAction>,
|
||||
#[props(default)]
|
||||
action: Option<EmptyStateAction>,
|
||||
) -> Element {
|
||||
rsx! {
|
||||
div { class: "flex flex-col items-center justify-center text-center py-20 px-4 page-enter",
|
||||
@ -45,12 +49,12 @@ pub fn EmptyState(
|
||||
}
|
||||
// 主标题:衬线字体,与首页 H1 风格呼应但更轻量。
|
||||
h2 { class: "mt-8 text-2xl font-bold tracking-tight text-paper-primary",
|
||||
{title.unwrap_or("还没有文章")}
|
||||
"{title}"
|
||||
}
|
||||
// 副文案:次要色,限宽保证可读性。
|
||||
if let Some(desc) = description {
|
||||
if !description.is_empty() {
|
||||
p { class: "mt-3 text-sm leading-relaxed text-paper-secondary max-w-md",
|
||||
{desc}
|
||||
"{description}"
|
||||
}
|
||||
}
|
||||
// 行动按钮:药丸形,与搜索页主按钮一致。
|
||||
@ -58,7 +62,7 @@ pub fn EmptyState(
|
||||
Link {
|
||||
class: "mt-8 inline-flex items-center px-6 py-2 bg-paper-accent text-white rounded-full font-medium text-sm hover:brightness-110 active:scale-[0.98] transition-all duration-200",
|
||||
to: act.to,
|
||||
{act.label}
|
||||
"{act.label}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,14 +10,14 @@ use dioxus::prelude::*;
|
||||
/// - `content_html`:服务端渲染的文章 HTML 字符串
|
||||
///
|
||||
/// 关键行为:
|
||||
/// - 在 `target_arch = "wasm32"` 环境下执行 `post-content.js`(代码块复制)。
|
||||
/// - 在 `target_arch = "wasm32"` 环境下调用 `window.__initPostContent` 初始化代码块
|
||||
/// 复制按钮(`yggdrasil-core.js` 已由 `Dioxus.toml` 全局注入)。
|
||||
/// 灯箱(图片灯箱 + 懒加载)改由 `Dioxus.toml` 全局注入 `lightbox.js`,
|
||||
/// 这里仅设置其初始化配置 `__lightboxSelectors` 并兜底调用。
|
||||
#[component]
|
||||
pub fn PostContent(content_html: String) -> Element {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use_effect(move || {
|
||||
let _ = js_sys::eval(include_str!("../../../public/js/post-content.js"));
|
||||
let _ = js_sys::eval("window.__initPostContent('.post-content')");
|
||||
// lightbox 改由 Dioxus.toml 全局 <script src> 加载(不再 include_str!)。
|
||||
// 双保险契约:先设配置,若 lightbox.js 已加载则立即调用;
|
||||
|
||||
17
src/main.rs
17
src/main.rs
@ -391,23 +391,6 @@ fn main() {
|
||||
"/readyz",
|
||||
axum::routing::get(crate::api::health::readyz),
|
||||
)
|
||||
// /doc 与 /doc/ 重定向到文档入口 /doc/yggdrasil/index.html。
|
||||
// public/doc/ 由 Dioxus 自动托管,深层路径无需额外处理;仅裸路径需要
|
||||
// 重定向,否则会落到 Router 的 /:..segments 兜底返回 404 页面。
|
||||
// 放在 static_routes(与 /uploads 同层),不经过 CSRF/超时/缓存中间件,
|
||||
// 行为与静态资源一致。用 301 永久重定向,让浏览器/书签记住跳转。
|
||||
.route(
|
||||
"/doc",
|
||||
axum::routing::get(|| async {
|
||||
axum::response::Redirect::permanent("/doc/yggdrasil/index.html")
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/doc/",
|
||||
axum::routing::get(|| async {
|
||||
axum::response::Redirect::permanent("/doc/yggdrasil/index.html")
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/uploads/{*path}",
|
||||
axum::routing::get(crate::api::image::serve_image),
|
||||
|
||||
@ -11,6 +11,7 @@ use dioxus::prelude::*;
|
||||
use dioxus::router::components::Link;
|
||||
|
||||
use crate::api::posts::{list_published_posts, PostListResponse};
|
||||
use crate::components::empty_state::EmptyState;
|
||||
use crate::components::skeletons::archive_skeleton::ArchiveSkeleton;
|
||||
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
|
||||
use crate::models::post::PostListItem;
|
||||
@ -119,17 +120,26 @@ fn ArchivesContent() -> Element {
|
||||
let posts_data = posts_res.read();
|
||||
match &*posts_data {
|
||||
Some(Ok(PostListResponse { posts, total })) => {
|
||||
let grouped = group_posts(posts);
|
||||
rsx! {
|
||||
div { class: "mt-2 text-base text-paper-secondary",
|
||||
"共 "
|
||||
span { class: "font-medium text-paper-primary", "{total}" }
|
||||
" 篇文章"
|
||||
if *total == 0 {
|
||||
rsx! {
|
||||
EmptyState {
|
||||
title: "还没有文章归档",
|
||||
description: "发布文章后,这里会自动按年月进行归档显示。",
|
||||
}
|
||||
}
|
||||
for year_group in grouped.iter() {
|
||||
YearSection {
|
||||
key: "{year_group.year}",
|
||||
year_group: year_group.clone(),
|
||||
} else {
|
||||
let grouped = group_posts(posts);
|
||||
rsx! {
|
||||
div { class: "mt-2 text-base text-paper-secondary",
|
||||
"共 "
|
||||
span { class: "font-medium text-paper-primary", "{total}" }
|
||||
" 篇文章"
|
||||
}
|
||||
for year_group in grouped.iter() {
|
||||
YearSection {
|
||||
key: "{year_group.year}",
|
||||
year_group: year_group.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ use crate::pages::post_detail::PostDetail;
|
||||
use crate::pages::register::Register;
|
||||
use crate::pages::search::Search;
|
||||
use crate::pages::tags::{TagDetail, Tags};
|
||||
use crate::theme::{use_theme_provider, Theme, ThemePreload};
|
||||
use crate::theme::{use_theme_provider, ThemePreload};
|
||||
|
||||
/// 全站路由枚举,每个变体对应一个页面路径
|
||||
#[derive(Clone, Routable, Debug, PartialEq)]
|
||||
@ -106,12 +106,7 @@ pub enum Route {
|
||||
/// 初始化主题提供者、全局用户上下文,并挂载样式表与 `Router`。
|
||||
#[component]
|
||||
pub fn AppRouter() -> Element {
|
||||
// 获取当前主题以设置顶层 dark 类
|
||||
let theme = use_theme_provider();
|
||||
let theme_class = match theme() {
|
||||
Theme::Dark => "dark",
|
||||
Theme::Light => "",
|
||||
};
|
||||
let _theme = use_theme_provider();
|
||||
|
||||
// 提供全局用户上下文,供登录状态与路由守卫使用
|
||||
let user = use_signal(|| None::<Arc<crate::models::user::PublicUser>>);
|
||||
@ -122,7 +117,7 @@ pub fn AppRouter() -> Element {
|
||||
document::Stylesheet { href: "/style.css" }
|
||||
document::Stylesheet { href: "/highlight.css" }
|
||||
document::Title { "Yggdrasil Blog" }
|
||||
div { class: "{theme_class}",
|
||||
div {
|
||||
ThemePreload {}
|
||||
Router::<Route> {}
|
||||
}
|
||||
|
||||
69
src/theme.rs
69
src/theme.rs
@ -6,6 +6,11 @@
|
||||
//! `prefers-color-scheme` 媒体查询;切换时同步更新 DOM class 与 localStorage。
|
||||
|
||||
use dioxus::prelude::*;
|
||||
// InteractionLocation 提供 client_coordinates(),用于读取鼠标点击的视口坐标。
|
||||
// 该 trait 不在 dioxus::prelude(后者只 re-export events::*),需单独从 dioxus::html 引入。
|
||||
// 仅 WASM 前端用到(ThemeToggle 的圆形展开动画取点击坐标),服务端构建剥离。
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use dioxus::html::InteractionLocation;
|
||||
|
||||
/// localStorage 中存储主题值的键名。
|
||||
#[cfg(any(target_arch = "wasm32", test))]
|
||||
@ -87,28 +92,19 @@ fn detect_initial_theme() -> Theme {
|
||||
/// 提供主题上下文的 Hook。
|
||||
///
|
||||
/// 初始化时按 SSR Cookie → WASM localStorage → 系统偏好的顺序检测主题;
|
||||
/// 主题变化时同步更新 HTML 根元素的 `dark` class 与 localStorage。
|
||||
/// 主题变化时将值持久化到 localStorage。
|
||||
///
|
||||
/// `<html>` 的 `dark` class 不在此处管理。WASM 端由 `ThemeToggle` 的 onclick
|
||||
/// 通过 `yggdrasil-core.js` 的圆形展开动画在 View Transition 回调里同步 toggle。
|
||||
/// 初始 class 由 `ThemePreload` 首屏脚本设置,避免闪烁。
|
||||
pub fn use_theme_provider() -> Signal<Theme> {
|
||||
let theme = use_signal(detect_initial_theme);
|
||||
|
||||
use_effect(move || {
|
||||
let current = theme();
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let current = theme();
|
||||
if let Some(window) = web_sys::window() {
|
||||
// 同步 HTML 根元素的 dark class,用于 Tailwind dark mode。
|
||||
if let Some(document) = window.document() {
|
||||
if let Some(html) = document.document_element() {
|
||||
match current {
|
||||
Theme::Dark => {
|
||||
let _ = html.class_list().add_1("dark");
|
||||
}
|
||||
Theme::Light => {
|
||||
let _ = html.class_list().remove_1("dark");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 将当前主题持久化到 localStorage。
|
||||
if let Ok(Some(storage)) = window.local_storage() {
|
||||
let theme_str = match current {
|
||||
@ -119,6 +115,8 @@ pub fn use_theme_provider() -> Signal<Theme> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 避免 unused 警告:非 wasm 构建下 current 未被读取。
|
||||
let _ = current;
|
||||
});
|
||||
|
||||
use_context_provider(|| theme);
|
||||
@ -155,16 +153,55 @@ pub fn ThemePreload() -> Element {
|
||||
}
|
||||
|
||||
/// 主题切换按钮组件。
|
||||
// evt 仅在 wasm32 用于取点击坐标,服务端构建剥离,故允许非 wasm 的 unused_variables。
|
||||
#[cfg_attr(not(target_arch = "wasm32"), allow(unused_variables))]
|
||||
#[component]
|
||||
pub fn ThemeToggle() -> Element {
|
||||
let mut theme = use_theme();
|
||||
// generation:每次点击递增。延迟回调检查自己的 gen 是否最新,过期则跳过 set。
|
||||
// 解决连续点击时多个 spawn_local 堆积导致的状态错乱。
|
||||
#[allow(unused_mut)]
|
||||
let mut click_gen = use_signal(|| 0u32);
|
||||
|
||||
rsx! {
|
||||
button {
|
||||
class: "theme-toggle p-2 rounded-full cursor-pointer hover:text-paper-accent transition-colors duration-200 text-paper-secondary",
|
||||
r#type: "button",
|
||||
aria_label: "切换深色/浅色主题",
|
||||
onclick: move |_| theme.set(theme().toggle()),
|
||||
onclick: move |evt| {
|
||||
let next = theme().toggle();
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let coords = evt.client_coordinates();
|
||||
let x = coords.x;
|
||||
let y = coords.y;
|
||||
// JS 从 DOM 现状推导目标主题(不传 isDark),避免与 Signal 状态不同步。
|
||||
let _ = js_sys::eval(&format!(
|
||||
"if (window.__startThemeTransition) \
|
||||
window.__startThemeTransition({x}, {y});",
|
||||
x = x,
|
||||
y = y,
|
||||
));
|
||||
// theme.set 推迟到动画结束:其触发的 Dioxus 微任务重渲染会打断 VT。
|
||||
// gen 确保连续点击只有最新回调 set。JS 已自治切换 dark class,
|
||||
// 动画完成后再更新 Dioxus 状态,避免组件重渲染可能带来的干扰
|
||||
let gen = click_gen() + 1;
|
||||
click_gen.set(gen);
|
||||
let mut theme_clone = theme;
|
||||
let mut gen_clone = click_gen;
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
crate::utils::time::sleep_ms(450).await;
|
||||
if gen_clone() == gen {
|
||||
theme_clone.set(next);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
theme.set(next);
|
||||
}
|
||||
},
|
||||
if theme() == Theme::Dark {
|
||||
svg {
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user