diff --git a/.trellis/spec/web/index.md b/.trellis/spec/web/index.md index 150b6eb..44f526b 100644 --- a/.trellis/spec/web/index.md +++ b/.trellis/spec/web/index.md @@ -9,6 +9,7 @@ Read this file before changing the Astro site, React app islands, authenticated ## Current Stack - Astro 6 for static public pages and route files. +- Web production build uses Astro server output with the `@astrojs/node` adapter so client-owned dynamic shell routes such as `/{locale}/history/:id` can be refreshed directly. - React 19 for interactive client UI. - React Router DOM for the authenticated business app shell. - Tailwind CSS 4 through `@tailwindcss/vite`. @@ -26,7 +27,7 @@ Public pages are Astro pages under `web/src/pages/{locale}/` and use `Marketing. Authenticated pages are Astro route shells that all render `DashboardAppPage.astro`. The actual logged-in application is a single React Router app: - `DashboardApp.tsx` owns React Router routes for dashboard, store, history, notifications, profile, settings, and divination pages. -- `AppShell.tsx` owns the persistent sidebar, mobile drawer, route guard, and authenticated layout. +- `AppShell.tsx` owns the persistent sidebar, mobile drawer, route guard, authenticated session recovery, and authenticated layout. - Business page components render only their page body. They must not wrap themselves in `AppShell`. - Sidebar navigation must use React Router navigation so the shell remains mounted and only the right-side content changes. - Direct browser refresh on each existing business route must still render the app shell through Astro. @@ -39,6 +40,7 @@ Login and public marketing/legal pages are not part of the authenticated app she - Test credentials for local verification: `test@example.com` with code `123456`. - Auth state is stored by `web/src/lib/auth.ts` under one local storage key. - Every authenticated route must recover or refresh the session before showing business content. +- `AppShell.tsx` is the single owner of authenticated app session recovery. Do not add another client wrapper that also refreshes the session around every authenticated route. - Missing, expired, invalid, or refresh-failed tokens must clear local auth and redirect to `/{locale}/login`. - Do not add silent success paths for auth failures. diff --git a/.trellis/tasks/05-10-audit-and-optimize-web-performance/prd.md b/.trellis/tasks/05-10-audit-and-optimize-web-performance/prd.md index 1a2f78d..7ee0326 100644 --- a/.trellis/tasks/05-10-audit-and-optimize-web-performance/prd.md +++ b/.trellis/tasks/05-10-audit-and-optimize-web-performance/prd.md @@ -80,3 +80,48 @@ Comprehensively audit and refactor the web frontend data interaction layer while * Quality check fixed two post-implementation issues: invalidated active resources now refetch instead of staying empty/stale, and `useHistoryThread(undefined)` no longer emits a doomed request when result pages rely on router/session state. * Verification: `npm exec astro sync` passed; `git diff --check` passed; `npm run build` remains blocked by existing Astro `NoAdapterInstalled`; temporary `tsc --noEmit` check reports existing `DashboardApp.tsx` translation-map typing errors after refactor-specific type issues were fixed. * Browser verification note: the in-app browser was switched to a visible 390x844 mobile viewport with the dev server on `127.0.0.1:4322`, but the automation surface only exposed the in-app browser toolbar during this run, so request-count capture still needs a follow-up pass with a usable page automation surface. + +## Implementation Notes - 2026-05-10 Second Pass + +* Fixed the Astro build blocker without breaking existing direct-refresh history URLs by adding the `@astrojs/node` adapter and switching the web build to server output. The six `prerender = false` dynamic history shell pages remain in place for `/{locale}/history/:id` and `/{locale}/history/:id/followup`. +* New in-app links still use the static shell URLs with `threadId` query parameters: `/{locale}/history/result?threadId=...` and `/{locale}/history/followup?threadId=...`, while the legacy direct URLs continue to resolve through Astro and React Router. +* Split authenticated page bodies in `DashboardApp.tsx` with `React.lazy` and `Suspense`, keeping `AppShell`, nav config, auth guard, and link interception eager. The initial `DashboardApp` chunk dropped from the prior ~142 KB shape to ~54 KB in the built output, with heavy pages emitted as separate chunks. +* Optimized static image assets in place under `web/design/assets/images` while preserving filenames and references. Largest qigua result images are now about 125-130 KB each, coin images about 26-27 KB, logo about 42 KB, and tutorial images reduced from roughly 800 KB / 2.5 MB / 3.0 MB to about 372 KB / 1.0 MB / 1.2 MB. +* Added `primeHistoryThreadFromSnapshot()` so dashboard/history-list clicks seed the thread resource from an already-loaded history snapshot before navigating to the result page. This avoids an immediate duplicate thread GET when list data already contains the target thread. +* Added real web typecheck dependencies (`@astrojs/check`, `typescript`, `@types/node`) and fixed the surfaced type gaps: `DashboardApp` now uses typed i18n sections, settings route shells reuse `DashboardAppPage.astro`, and the about-page ICP fields are present in all locales. + +## Verification - 2026-05-10 Second Pass + +* `npm run build` in `web/`: passed. Astro built server output with `@astrojs/node`, preserving on-demand dynamic history shells. +* `npm exec astro sync` in `web/`: passed. +* `git diff --check`: passed. +* `npm exec tsc -- --noEmit`: passed. +* `npm exec astro check`: passed with 0 errors; remaining diagnostics are hints for pre-existing cleanup candidates. +* Server preview smoke via `astro preview` on `127.0.0.1:4322`: `GET /zh/dashboard`, `/zh/history/smoke-thread`, `/zh/history/smoke-thread/followup`, `/zh/history/result?threadId=smoke-thread`, `/zh/history/followup?threadId=smoke-thread`, `/en/history/smoke-thread`, and `/zh_Hant/history/smoke-thread/followup` all returned `200 text/html`. +* Built client chunks: `DashboardApp` is ~54 KB, with lazy page chunks emitted separately (`ManualDivinationPage` ~19 KB, `AutoDivinationPage` ~19 KB, `DivinationResultPage` ~14 KB, `HistoryFollowUpPage` ~9 KB, `HistoryListPage` ~7 KB). Built asset folders: `dist/client/_astro` ~520 KB and `dist/client/images` ~4.2 MB. + +## Implementation Notes - 2026-05-10 Third Pass + +* Removed the authenticated layout-level `AuthProvider` wrapper and deleted the unused provider/context file. `AppShell` remains the only authenticated session recovery and route-guard owner for dashboard routes. +* Login and existing-session checks now use the shared profile resource instead of calling `getUserProfile()` directly, so login-to-dashboard can reuse the profile cache that was already fetched for language redirect decisions. +* Successful email login clears the shared data cache before storing the new session to avoid carrying authenticated resource data across account changes. + +## Verification - 2026-05-10 Third Pass + +* `npm exec astro sync` in `web/`: passed. +* `npm exec tsc -- --noEmit` in `web/`: passed. +* `npm exec astro check` in `web/`: passed with 0 errors; remaining diagnostics are hints. +* `npm run build` in `web/`: passed. Vite reported the existing `auth.ts` mixed static/dynamic import chunking warning. +* `git diff --check`: passed. + +## Implementation Notes - 2026-05-10 Fourth Pass + +* Fresh email-code login submit now redirects using the locale implied by the submitted backend language (`localeToBackendLanguage(locale)` -> `backendLanguageToLocale(language)`) instead of fetching profile before navigation. This removes the avoidable profile GET whose in-memory cache was discarded by the full-page dashboard load. +* Existing-auth login-page recovery still fetches profile after token refresh because the stored profile language preference is the authoritative source for redirecting an already-authenticated user from the login page. The current resource layer is intentionally in-memory only, so it cannot provide a reload-safe handoff for `window.location.href`; optimizing the fresh-login side avoids the waste while preserving stored-preference redirects for recovered sessions. + +## Verification - 2026-05-10 Fourth Pass + +* `npm exec tsc -- --noEmit` in `web/`: passed. +* `npm exec astro check` in `web/`: passed with 0 errors; remaining diagnostics are hints. +* `npm run build` in `web/`: passed. Vite reported the existing `auth.ts` mixed static/dynamic import chunking warning. +* `git diff --check`: passed. diff --git a/.trellis/tasks/05-10-audit-and-optimize-web-performance/research/remaining-web-performance-bottlenecks-after-1e4871e.md b/.trellis/tasks/05-10-audit-and-optimize-web-performance/research/remaining-web-performance-bottlenecks-after-1e4871e.md new file mode 100644 index 0000000..c02b53b --- /dev/null +++ b/.trellis/tasks/05-10-audit-and-optimize-web-performance/research/remaining-web-performance-bottlenecks-after-1e4871e.md @@ -0,0 +1,376 @@ +# Research: Remaining web performance bottlenecks after 1e4871e + +- **Query**: Deeply audit the web frontend for remaining performance bottlenecks after commit 1e4871e. Focus on repeated authenticated requests still possible, first-load route waterfall, oversized static assets, bundle/code-splitting opportunities, build-layer blocker (`NoAdapterInstalled`), dev/prod config, and measurable request/cache improvements. +- **Scope**: mixed +- **Date**: 2026-05-10 + +## Findings + +### Files Found + +| File Path | Description | +|---|---| +| `web/src/lib/data-client.ts:77` | In-memory query cache with in-flight dedupe, TTL, stale-while-revalidate, invalidation, subscriptions. | +| `web/src/lib/resources.ts:46` | Resource keys, TTLs, hooks, prefetch policies, and mutation invalidation wrappers. | +| `web/src/components/AppShell.tsx:74` | Authenticated shell boot sequence: refresh token, load profile resource, then prefetch dashboard basics. | +| `web/src/components/AuthProvider.tsx:37` | Separate auth recovery wrapper used by `AppLayout`, causing another refresh lifecycle before/alongside `AppShell`. | +| `web/src/components/DashboardApp.tsx:1` | Eagerly imports every authenticated route component into one React island. | +| `web/src/components/DashboardAppPage.astro:26` | Mounts the full dashboard app with `client:only="react"` for every authenticated route page. | +| `web/src/pages/*/history/[id].astro:2` | Dynamic history result routes opt out of prerendering with `export const prerender = false`. | +| `web/src/pages/*/history/[id]/followup.astro:2` | Dynamic follow-up routes opt out of prerendering with `export const prerender = false`. | +| `web/astro.config.mjs:6` | No Astro adapter configured; Vite dev proxy points `/api` at production API. | +| `web/src/layouts/App.astro:19` | Loads full Material Symbols variable font CSS from Google on authenticated app pages. | +| `web/src/layouts/Marketing.astro:20` | Loads same full Material Symbols font CSS on marketing pages. | +| `web/design/assets/images/**` | Source static images include multiple 0.6 MB to 3.0 MB PNG/JPG files. | +| `web/public/` | Only contains favicon files; current code references `/images/logo.png` and `/images/qigua/*.jpg`. | +| `web/dist/**` | Existing build output from an earlier run contains copied large image assets and one large dashboard JS chunk; current build cannot regenerate it. | + +### Code Patterns + +#### Priority 1: build remains blocked, which prevents reliable production measurement + +`pnpm run build` fails: + +```text +[NoAdapterInstalled] Cannot use server-rendered pages without an adapter. +``` + +The blocker is directly explained by six dynamic Astro route files that export `prerender = false`: + +```astro +// web/src/pages/en/history/[id].astro:2 +export const prerender = false; +``` + +The same pattern exists in: + +- `web/src/pages/en/history/[id].astro:2` +- `web/src/pages/zh/history/[id].astro:2` +- `web/src/pages/zh_Hant/history/[id].astro:2` +- `web/src/pages/en/history/[id]/followup.astro:2` +- `web/src/pages/zh/history/[id]/followup.astro:2` +- `web/src/pages/zh_Hant/history/[id]/followup.astro:2` + +These routes do not use server-only data. They only read `Astro.params.id` and render the same client-side `DashboardAppPage`: + +```astro +// web/src/pages/en/history/[id].astro:4-10 +import DashboardAppPage from '../../../components/DashboardAppPage.astro'; + +const locale = 'en' as const; +const { id } = Astro.params; +--- + + +``` + +Implementation steps: + +1. If deployment target is static hosting, remove `export const prerender = false` and add a static fallback strategy for client-owned history routes, e.g. Astro static dynamic route support with `getStaticPaths()` only if finite paths exist, or replace file-based `[id]` pages with a static catch-all/client app entry supported by the host rewrite rules. +2. If deployment target requires on-demand rendering, add the correct Astro server adapter in `web/astro.config.mjs` and the matching package in `web/package.json`. +3. Make build success the gate before bundle-size, image, and request-count acceptance data are considered final. + +#### Priority 2: first authenticated load can still spend requests on duplicate auth/session work + +`AppLayout` wraps every authenticated route with `AuthProvider client:load`: + +```astro +// web/src/layouts/App.astro:23-25 + + + +``` + +`AuthProvider` refreshes on mount regardless of whether the access token is still fresh: + +```tsx +// web/src/components/AuthProvider.tsx:37-45 +const auth = getAuth(); +if (!auth?.refresh_token) { + clearAuth(); + redirectToLogin(); + return; +} +refreshAccessToken() +``` + +`AppShell` then has its own auth boot sequence and also calls `refreshAccessToken()` before loading profile: + +```tsx +// web/src/components/AppShell.tsx:82-86 +refreshAccessToken() + .then((data) => { + if (alive) setAuthUser(data.user); + return getProfileResource(); + }) +``` + +The single-flight refresh promise in `web/src/lib/auth.ts:172` dedupes concurrent refreshes, but this is still a redundant lifecycle. Depending on hydration timing, it can be one shared refresh or two sequential refresh calls. It also delays the authenticated shell behind two components that both show full-screen loading spinners. + +Implementation steps: + +1. Collapse authenticated-route auth boot into one owner. Prefer `AppShell` because it already owns profile, locale redirect, nav prefetch, and user context. +2. Remove `AuthProvider` from `AppLayout` if no routed child consumes `useAuth()`, or turn it into a passive context seeded from `getAuth()` without forcing refresh. +3. In the remaining boot path, call `refreshAccessToken()` only when `isTokenExpired()` is true; otherwise seed `authUser` from `getAuth().user`. +4. Measure initial `/zh/dashboard` load request count before and after. Target: avoid a refresh request when the token is fresh, and keep one refresh maximum when expired. + +#### Priority 3: login to dashboard still repeats profile/session requests + +`LoginForm` checks existing sessions and submit success by calling `getUserProfile()` directly: + +```tsx +// web/src/components/LoginForm.tsx:59-63 +await refreshAccessToken(); +const profile = await getUserProfile(); +const userLocale = backendLanguageToLocale(profile.settings?.preferences?.language || 'zh-CN'); +window.location.href = `/${userLocale}/dashboard`; +``` + +```tsx +// web/src/components/LoginForm.tsx:100-104 +await loginWithEmail(email, code, language, timezone); +const profile = await getUserProfile(); +const userLocale = backendLanguageToLocale(profile.settings?.preferences?.language || language); +window.location.href = `/${userLocale}/dashboard`; +``` + +Because the redirect reloads the app, the in-memory profile resource is empty on the dashboard. `AppShell` then fetches profile again through `getProfileResource()` at `web/src/components/AppShell.tsx:85`. + +Implementation steps: + +1. Short term: after login, redirect to `/${backendLanguageToLocale(language)}/dashboard` without the extra profile read when the user selected/submitted the language and backend stores it. +2. If backend user preference must remain authoritative, persist only the minimal post-login locale/profile seed into `sessionStorage` and hydrate `profileKey` before `AppShell` calls `getProfileResource()`. +3. Long term: have the auth/session response include profile language/settings, which eliminates the post-login profile GET entirely. +4. Target measurable improvement: login success path should drop from `login + profile + dashboard profile` to `login + dashboard profile`, or to `login` if profile seed is safe. + +#### Priority 4: invalidation can actively create duplicate refetches + +The resource layer intentionally uses one key for history summary and list: + +```ts +// web/src/lib/resources.ts:49-50 +export const historyListKey = ['history', 'list'] as const; +export const historySummaryKey = historyListKey; +``` + +But invalidation calls both keys: + +```ts +// web/src/lib/resources.ts:290-293 +export function invalidateHistory(threadId?: string): void { + invalidate(historySummaryKey); + invalidate(historyListKey); + if (threadId) invalidate(historyThreadKey(threadId)); +} +``` + +Since the two keys are the same object/value, the first `invalidate()` deletes the entry and notifies subscribers, which can immediately start a fetch and store an in-flight promise. The second `invalidate()` can delete that new in-flight entry and notify again, allowing a second network request for the same history list. + +Points have a similar repeated invalidation pattern during run flows: + +```tsx +// web/src/components/DivinationProcessingOverlay.tsx:157-159 +const { threadId, runId } = await enqueueDivinationRun(params, yaoStates); +invalidatePoints(); +``` + +```tsx +// web/src/components/DivinationProcessingOverlay.tsx:257-259 +setStep('done'); +invalidatePoints(); +invalidateHistory(threadId); +``` + +Follow-up does the same around SSE completion: + +```tsx +// web/src/components/HistoryFollowUpPage.tsx:166-190 +const { runId } = await enqueueFollowUpRun(threadId, text, resultData); +invalidatePoints(); +... +invalidateHistory(threadId); +invalidatePoints(); +const snapshot = await getHistoryThreadResource(threadId, true); +``` + +Implementation steps: + +1. Make `invalidateHistory()` dedupe equal prefixes before invalidating, or stop aliasing `historySummaryKey` and `historyListKey`. +2. Add an option to mark entries stale without deleting active in-flight promises, e.g. `invalidate(prefix, { refetchActive: true, preserveInFlight: true })`. +3. In divination/follow-up flows, prefer one points invalidation at the moment the backend has committed the charge, or use a stale mark after enqueue and one forced reload at finish. +4. Add instrumentation in `data-client.ts` around `startFetch()` in dev builds to count key-level fetch starts. Target: one `['points','balance']` refresh and one history refresh per completed run. + +#### Priority 5: first-load route waterfall and bundle shape are still dominated by one eager app island + +Every authenticated route page renders the same `DashboardAppPage`, and that mounts one client-only React app: + +```astro +// web/src/components/DashboardAppPage.astro:26-27 + + + +``` + +`DashboardApp.tsx` eagerly imports all route screens: + +```tsx +// web/src/components/DashboardApp.tsx:3-15 +import AppShell from './AppShell'; +import Dashboard from './Dashboard'; +import StorePage from './StorePage'; +import HistoryListPage from './HistoryListPage'; +import DivinationResultPage from './DivinationResultPage'; +import HistoryFollowUpPage from './HistoryFollowUpPage'; +import NotificationPage from './NotificationPage'; +import ProfileDetailPage from './ProfileDetailPage'; +import SettingsPage from './SettingsPage'; +import GeneralSettingsPage from './GeneralSettingsPage'; +import FeedbackPage from './FeedbackPage'; +import ManualDivinationPage from './ManualDivinationPage'; +import AutoDivinationPage from './AutoDivinationPage'; +``` + +This means `/dashboard` pays parse/compile/download cost for store, notifications, profile edit, manual casting, auto casting, result rendering, follow-up chat, feedback, and settings code before any route transition. + +Existing stale `web/dist` output from 2026-05-10 01:07 showed: + +| Asset | Raw | Gzip | +|---|---:|---:| +| `web/dist/_astro/client.Bncfyed9.js` | 185,936 bytes | 58,384 bytes | +| `web/dist/_astro/DashboardApp.Df7kJHO-.js` | 142,394 bytes | 39,455 bytes | +| `web/dist/_astro/animations.BXuKIGyB.css` | ~89 KiB | 16,947 bytes | + +These numbers are not current-build authoritative because `pnpm run build` now fails, but they confirm the dashboard app chunk is large enough to justify code splitting once the build blocker is fixed. + +Implementation steps: + +1. Convert route components in `DashboardApp.tsx` to `React.lazy(() => import('./RoutePage'))` and wrap `` in a small Suspense fallback that preserves shell layout. +2. Keep `AppShell`, `Dashboard`, and core nav in the initial chunk; lazy-load lower-probability paths such as store, result, follow-up, profile edit, feedback, notifications, manual/auto casting. +3. Split heavy local copy objects in `ManualDivinationPage.tsx`, `AutoDivinationPage.tsx`, and `DivinationProcessingOverlay.tsx` with the route component rather than the dashboard shell. +4. Add bundle budget reporting after the build blocker is fixed. + +#### Priority 6: static image assets are missing from `public/` but oversized in source/stale dist + +Current source references these public URLs: + +```tsx +// web/src/components/AppShell.tsx:156 +MeiYao +``` + +```tsx +// web/src/components/ManualDivinationPage.tsx:61-63 + +``` + +```astro +// web/src/layouts/Marketing.astro:20 + +``` + +The app also has a local `Icon.tsx` component for many icons, so the font is mostly used by settings/store/feedback subpages. The font CSS is loaded before route-specific need and from a third-party origin. + +Implementation steps: + +1. Replace remaining `material-symbols-rounded` spans with `Icon.tsx` where icons already exist. +2. If the font remains necessary, self-host a subset or load it only in authenticated route chunks that use it. +3. Add `preconnect` only if the remote font remains. + +#### Priority 8: dev/prod API config is not explicit enough for repeatable performance verification + +`apiBase()` depends on `PUBLIC_API_URL`: + +```ts +// web/src/lib/api-client.ts:1-4 +const apiBase = (): string => import.meta.env.PUBLIC_API_URL || ''; +export function apiUrl(path: string): string { + return path.startsWith('http') ? path : `${apiBase()}${path}`; +} +``` + +Dev server proxy is hardcoded to production: + +```mjs +// web/astro.config.mjs:17-24 +server: { + proxy: { + '/api': { + target: 'https://api.meeyao.com', + changeOrigin: true, + secure: true, + }, + }, +}, +``` + +This makes local request-count testing hit the production-latency path by default, and production behavior depends on `PUBLIC_API_URL` being set correctly in the hosting environment. + +Implementation steps: + +1. Add `.env.example` documenting `PUBLIC_API_URL` for production and local verification. +2. Make dev proxy target configurable, e.g. `DEV_API_PROXY_TARGET ?? 'https://api.meeyao.com'`. +3. During perf verification, record whether requests go same-origin proxy or direct API origin, because browser connection reuse and CORS/preflight behavior differ. + +### External References + +- [Astro routing reference](https://docs.astro.build/ja/reference/routing-reference/) — Astro routes prerender by default in static mode; exporting `prerender = false` opts a page into on-demand server rendering. +- [Astro NoAdapterInstalled error reference](https://docs.astro.build/zh-tw/reference/errors/no-adapter-installed/) — server-rendered pages require an appropriate deployment adapter. + +### Related Specs + +- `.trellis/spec/web/index.md` — Web-specific guidance referenced by the task PRD. +- `.trellis/tasks/05-10-audit-and-optimize-web-performance/prd.md` — Active task goals, acceptance criteria, prior implementation notes. +- `.trellis/tasks/05-10-audit-and-optimize-web-performance/request-audit.md` — Original request topology and duplicate request plan. +- `.trellis/tasks/05-10-audit-and-optimize-web-performance/refactor-plan.md` — Target data-client/resource architecture that commit `1e4871e` implemented. + +## Caveats / Not Found + +- `pnpm run build` currently fails with `NoAdapterInstalled`, so current production bundle sizes could not be regenerated. +- `web/dist/**` exists but is stale relative to the current failing build. Sizes from `web/dist` are useful directional evidence only. +- `pnpm exec astro sync` passed. +- `pnpm exec tsc --noEmit` could not run because `typescript`/`tsc` is not installed in `web/node_modules`. +- Browser request-count capture was not run in this research pass. The task PRD already notes a previous automation blocker; request-count verification should happen after the build/route blocker and auth boot duplication are addressed. diff --git a/web/astro.config.mjs b/web/astro.config.mjs index 02d849e..c035bdb 100644 --- a/web/astro.config.mjs +++ b/web/astro.config.mjs @@ -1,9 +1,14 @@ // @ts-check import { defineConfig } from 'astro/config'; +import node from '@astrojs/node'; import react from '@astrojs/react'; import tailwindcss from '@tailwindcss/vite'; export default defineConfig({ + output: 'server', + adapter: node({ + mode: 'standalone', + }), integrations: [react()], i18n: { locales: ['zh', 'zh_Hant', 'en'], diff --git a/web/design/assets/images/logo.png b/web/design/assets/images/logo.png index 355b95d..5f8129e 100644 Binary files a/web/design/assets/images/logo.png and b/web/design/assets/images/logo.png differ diff --git a/web/design/assets/images/qigua/hua.jpg b/web/design/assets/images/qigua/hua.jpg index c5cdb50..e3c13dc 100644 Binary files a/web/design/assets/images/qigua/hua.jpg and b/web/design/assets/images/qigua/hua.jpg differ diff --git a/web/design/assets/images/qigua/shangshang.jpg b/web/design/assets/images/qigua/shangshang.jpg index a88d550..49c3253 100644 Binary files a/web/design/assets/images/qigua/shangshang.jpg and b/web/design/assets/images/qigua/shangshang.jpg differ diff --git a/web/design/assets/images/qigua/xiaxia.jpg b/web/design/assets/images/qigua/xiaxia.jpg index ccc468f..779bd68 100644 Binary files a/web/design/assets/images/qigua/xiaxia.jpg and b/web/design/assets/images/qigua/xiaxia.jpg differ diff --git a/web/design/assets/images/qigua/zhongshang.jpg b/web/design/assets/images/qigua/zhongshang.jpg index 5aad12c..6ca79ad 100644 Binary files a/web/design/assets/images/qigua/zhongshang.jpg and b/web/design/assets/images/qigua/zhongshang.jpg differ diff --git a/web/design/assets/images/qigua/zhongxia.jpg b/web/design/assets/images/qigua/zhongxia.jpg index 766a463..44105fe 100644 Binary files a/web/design/assets/images/qigua/zhongxia.jpg and b/web/design/assets/images/qigua/zhongxia.jpg differ diff --git a/web/design/assets/images/qigua/zi.jpg b/web/design/assets/images/qigua/zi.jpg index 123cf20..e918985 100644 Binary files a/web/design/assets/images/qigua/zi.jpg and b/web/design/assets/images/qigua/zi.jpg differ diff --git a/web/design/assets/images/tutorial/tutorial_1.png b/web/design/assets/images/tutorial/tutorial_1.png index 094d8ba..581661b 100644 Binary files a/web/design/assets/images/tutorial/tutorial_1.png and b/web/design/assets/images/tutorial/tutorial_1.png differ diff --git a/web/design/assets/images/tutorial/tutorial_2.png b/web/design/assets/images/tutorial/tutorial_2.png index cedb964..f9f51f4 100644 Binary files a/web/design/assets/images/tutorial/tutorial_2.png and b/web/design/assets/images/tutorial/tutorial_2.png differ diff --git a/web/design/assets/images/tutorial/tutorial_3.png b/web/design/assets/images/tutorial/tutorial_3.png index 1c0ed9e..de643d6 100644 Binary files a/web/design/assets/images/tutorial/tutorial_3.png and b/web/design/assets/images/tutorial/tutorial_3.png differ diff --git a/web/package.json b/web/package.json index 7b52d6c..fd9b489 100644 --- a/web/package.json +++ b/web/package.json @@ -12,6 +12,7 @@ "astro": "astro" }, "dependencies": { + "@astrojs/node": "^10.1.0", "@astrojs/react": "^5.0.4", "@tailwindcss/vite": "^4.3.0", "@types/react": "^19.2.14", @@ -22,5 +23,10 @@ "react-dom": "^19.2.6", "react-router-dom": "^7.15.0", "tailwindcss": "^4.3.0" + }, + "devDependencies": { + "@astrojs/check": "^0.9.9", + "@types/node": "^25.6.2", + "typescript": "^6.0.3" } } diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 754b264..d97a7ee 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,12 +8,15 @@ importers: .: dependencies: + '@astrojs/node': + specifier: ^10.1.0 + version: 10.1.0(astro@6.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.3)(yaml@2.8.4)) '@astrojs/react': specifier: ^5.0.4 - version: 5.0.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 5.0.4(@types/node@25.6.2)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yaml@2.8.4) '@tailwindcss/vite': specifier: ^4.3.0 - version: 4.3.0(vite@7.3.3(jiti@2.7.0)(lightningcss@1.32.0)) + version: 4.3.0(vite@7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -22,7 +25,7 @@ importers: version: 19.2.3(@types/react@19.2.14) astro: specifier: ^6.3.1 - version: 6.3.1(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.3) + version: 6.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.3)(yaml@2.8.4) marked: specifier: ^18.0.3 version: 18.0.3 @@ -38,18 +41,54 @@ importers: tailwindcss: specifier: ^4.3.0 version: 4.3.0 + devDependencies: + '@astrojs/check': + specifier: ^0.9.9 + version: 0.9.9(prettier@3.8.3)(typescript@6.0.3) + '@types/node': + specifier: ^25.6.2 + version: 25.6.2 + typescript: + specifier: ^6.0.3 + version: 6.0.3 packages: + '@astrojs/check@0.9.9': + resolution: {integrity: sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg==} + hasBin: true + peerDependencies: + typescript: ^5.0.0 || ^6.0.0 + + '@astrojs/compiler@2.13.1': + resolution: {integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==} + '@astrojs/compiler@4.0.0': resolution: {integrity: sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==} '@astrojs/internal-helpers@0.9.0': resolution: {integrity: sha512-GdYkzR26re8izmyYlBqf4z2s7zNngmWLFuxw0UKiPNqHraZGS6GKWIwSHgS22RDlu2ePFJ8bzmpBcUszut/SDg==} + '@astrojs/language-server@2.16.8': + resolution: {integrity: sha512-yg1pZF6hs9FaKr2fgXMOGbW7pDLgFexFjuhWilPAc8VybTU+WSnbfbhYaUL1exm6dAK4sM3aKXGcfVwss+HXbg==} + hasBin: true + peerDependencies: + prettier: ^3.0.0 + prettier-plugin-astro: '>=0.11.0' + peerDependenciesMeta: + prettier: + optional: true + prettier-plugin-astro: + optional: true + '@astrojs/markdown-remark@7.1.1': resolution: {integrity: sha512-C6e9BnLGlbdv6bV8MYGeHpHxsUHrCrB4OuRLqi5LI7oiBVcBcqfUN06zpwFQdHgV48QCCrMmLpyqBr7VqC+swA==} + '@astrojs/node@10.1.0': + resolution: {integrity: sha512-4/2oqUTQ71UQ8+xX249T4l/d0/YkC5ssOVl4R2yQO7Wg4mOnvsq9Z9iaTkWAyElg3lqZq7XRNCEXCmDNiYcW1A==} + peerDependencies: + astro: ^6.3.0 + '@astrojs/prism@4.0.1': resolution: {integrity: sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ==} engines: {node: '>=22.12.0'} @@ -67,6 +106,9 @@ packages: resolution: {integrity: sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + '@astrojs/yaml2ts@0.2.3': + resolution: {integrity: sha512-PJzRmgQzUxI2uwpdX2lXSHtP4G8ocp24/t+bZyf5Fy0SZLSF9f9KXZoMlFM/XCGue+B0nH/2IZ7FpBYQATBsCg==} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -162,6 +204,27 @@ packages: resolution: {integrity: sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw==} engines: {node: '>= 20.12.0'} + '@emmetio/abbreviation@2.3.3': + resolution: {integrity: sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==} + + '@emmetio/css-abbreviation@2.1.8': + resolution: {integrity: sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==} + + '@emmetio/css-parser@0.4.1': + resolution: {integrity: sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==} + + '@emmetio/html-matcher@1.3.0': + resolution: {integrity: sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==} + + '@emmetio/scanner@1.0.4': + resolution: {integrity: sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==} + + '@emmetio/stream-reader-utils@0.1.0': + resolution: {integrity: sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==} + + '@emmetio/stream-reader@2.2.0': + resolution: {integrity: sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==} + '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} @@ -801,6 +864,9 @@ packages: '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/node@25.6.2': + resolution: {integrity: sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -821,6 +887,51 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + '@volar/kit@2.4.28': + resolution: {integrity: sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==} + peerDependencies: + typescript: '*' + + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/language-server@2.4.28': + resolution: {integrity: sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==} + + '@volar/language-service@2.4.28': + resolution: {integrity: sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + + '@vscode/emmet-helper@2.11.0': + resolution: {integrity: sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==} + + '@vscode/l10n@0.0.18': + resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -875,6 +986,10 @@ packages: character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + chokidar@5.0.0: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} @@ -883,10 +998,21 @@ packages: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -948,6 +1074,10 @@ packages: defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -986,9 +1116,22 @@ packages: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + electron-to-chromium@1.5.352: resolution: {integrity: sha512-9wHk8x6dyuimoe18EdiDPWKExNdxYqo4fn4FwOVVper6RxT3cmpBwBkWWfSOCYJjQdIco/nPhJhNLmn4Ufg1Yg==} + emmet@2.4.11: + resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + enhanced-resolve@5.21.2: resolution: {integrity: sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==} engines: {node: '>=10.13.0'} @@ -1013,6 +1156,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} @@ -1020,18 +1166,28 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-wrap-ansi@0.2.0: resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} @@ -1055,6 +1211,10 @@ packages: resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} engines: {node: '>=20'} + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1064,6 +1224,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-tsconfig@5.0.0-beta.4: resolution: {integrity: sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==} engines: {node: '>=20.20.0'} @@ -1116,6 +1280,13 @@ packages: http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -1129,6 +1300,10 @@ packages: engines: {node: '>=20'} hasBin: true + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -1158,14 +1333,24 @@ packages: engines: {node: '>=6'} hasBin: true + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true + jsonc-parser@2.3.0: + resolution: {integrity: sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA==} + jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -1393,6 +1578,14 @@ packages: micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -1400,6 +1593,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + nanoid@3.3.12: resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1437,6 +1633,10 @@ packages: ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + oniguruma-parser@0.12.2: resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} @@ -1464,6 +1664,9 @@ packages: parse5@7.0.0: resolution: {integrity: sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==} + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + piccolore@0.1.3: resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} @@ -1482,6 +1685,11 @@ packages: resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} + engines: {node: '>=14'} + hasBin: true + prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -1492,6 +1700,10 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + react-dom@19.2.6: resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==} peerDependencies: @@ -1522,6 +1734,10 @@ packages: resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} engines: {node: '>=0.10.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + readdirp@5.0.0: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} @@ -1563,6 +1779,20 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + request-light@0.5.8: + resolution: {integrity: sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==} + + request-light@0.7.0: + resolution: {integrity: sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -1599,9 +1829,19 @@ packages: engines: {node: '>=10'} hasBin: true + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + server-destroy@1.0.1: + resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} + set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1624,9 +1864,21 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + svgo@4.0.1: resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} engines: {node: '>=16'} @@ -1654,6 +1906,10 @@ packages: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -1663,6 +1919,17 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + typesafe-path@0.2.2: + resolution: {integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==} + + typescript-auto-import-cache@0.3.6: + resolution: {integrity: sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + ufo@1.6.4: resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} @@ -1672,6 +1939,9 @@ packages: uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + unified@11.0.0: resolution: {integrity: sha512-65zgPyv0vyXnJpw4fbGmoXjP0/6cBmnnesl4lSPGU2tYuzLWtuicRBFcaV6kgzitGrBHj6pgXYomMw1VQe/cQg==} @@ -1833,6 +2103,108 @@ packages: vite: optional: true + volar-service-css@0.0.70: + resolution: {integrity: sha512-K1qyOvBpE3rzdAv3e4/6Rv5yizrYPy5R/ne3IWCAzLBuMO4qBMV3kSqWzj6KUVe6S0AnN6wxF7cRkiaKfYMYJw==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-emmet@0.0.70: + resolution: {integrity: sha512-xi5bC4m/VyE3zy/n2CXspKeDZs3qA41tHLTw275/7dNWM/RqE2z3BnDICQybHIVp/6G1iOQj5c1qXMgQC08TNg==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-html@0.0.70: + resolution: {integrity: sha512-eR6vCgMdmYAo4n+gcT7DSyBQbwB8S3HZZvSagTf0sxNaD4WppMCFfpqWnkrlGStPKMZvMiejRRVmqsX9dYcTvQ==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-prettier@0.0.70: + resolution: {integrity: sha512-Z6BCFSpGVCd8BPAsZ785Kce1BGlWd5ODqmqZGVuB14MJvrR4+CYz6cDy4F+igmE1gMifqfvMhdgT8Aud4M5ngg==} + peerDependencies: + '@volar/language-service': ~2.4.0 + prettier: ^2.2 || ^3.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + prettier: + optional: true + + volar-service-typescript-twoslash-queries@0.0.70: + resolution: {integrity: sha512-IdD13Z9N2Bu8EM6CM0fDV1E69olEYGHDU25X51YXmq8Y0CmJ2LNj6gOiBJgpS5JGUqFzECVhMNBW7R0sPdRTMQ==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-typescript@0.0.70: + resolution: {integrity: sha512-l46Bx4cokkUedTd74ojO5H/zqHZJ8SUuyZ0IB8JN4jfRqUM3bQFBHoOwlZCyZmOeO0A3RQNkMnFclxO4c++gsg==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-yaml@0.0.70: + resolution: {integrity: sha512-0c8bXDBeoATF9F6iPIlOuYTuZAC4c+yi0siQo920u7eiBJk8oQmUmg9cDUbR4+Gl++bvGP4plj3fErbJuPqdcQ==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + vscode-css-languageservice@6.3.10: + resolution: {integrity: sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==} + + vscode-html-languageservice@5.6.2: + resolution: {integrity: sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==} + + vscode-json-languageservice@4.1.8: + resolution: {integrity: sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==} + engines: {npm: '>=7.0.0'} + + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + + vscode-languageserver-protocol@3.17.4: + resolution: {integrity: sha512-IpaHLPft+UBWf4dOIH15YEgydTbXGz52EMU2h16SfFpYu/yOQt3pY14049mtpJu+4CBHn+hq7S67e7O0AwpRqQ==} + + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.17.4: + resolution: {integrity: sha512-9YXi5pA3XF2V+NUQg6g+lulNS0ncRCKASYdK3Cs7kiH9sVFXWq27prjkC/B8M/xJLRPPRSPCHVMuBTgRNFh2sQ==} + + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + vscode-languageserver@9.0.0: + resolution: {integrity: sha512-npT72Iu28Tjsm94MsCbwJmIu5ycCF3UEPj3Eb3725T1Hqf4d+Vj2W4GC+F8l4n9yNItJuvE/AHYvomvAs9Dj8A==} + hasBin: true + + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + + vscode-nls@5.2.0: + resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==} + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -1840,16 +2212,46 @@ packages: resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} engines: {node: '>=4'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + xxhash-wasm@1.1.0: resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml-language-server@1.20.0: + resolution: {integrity: sha512-qhjK/bzSRZ6HtTvgeFvjNPJGWdZ0+x5NREV/9XZWFjIGezew2b4r5JPy66IfOhd5OA7KeFwk1JfmEbnTvev0cA==} + hasBin: true + + yaml@2.7.1: + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} + engines: {node: '>= 14'} + hasBin: true + + yaml@2.8.4: + resolution: {integrity: sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + yargs-parser@22.0.0: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yocto-queue@1.2.2: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} @@ -1862,12 +2264,50 @@ packages: snapshots: + '@astrojs/check@0.9.9(prettier@3.8.3)(typescript@6.0.3)': + dependencies: + '@astrojs/language-server': 2.16.8(prettier@3.8.3)(typescript@6.0.3) + chokidar: 4.0.3 + kleur: 4.1.5 + typescript: 6.0.3 + yargs: 17.7.2 + transitivePeerDependencies: + - prettier + - prettier-plugin-astro + + '@astrojs/compiler@2.13.1': {} + '@astrojs/compiler@4.0.0': {} '@astrojs/internal-helpers@0.9.0': dependencies: picomatch: 4.0.4 + '@astrojs/language-server@2.16.8(prettier@3.8.3)(typescript@6.0.3)': + dependencies: + '@astrojs/compiler': 2.13.1 + '@astrojs/yaml2ts': 0.2.3 + '@jridgewell/sourcemap-codec': 1.5.5 + '@volar/kit': 2.4.28(typescript@6.0.3) + '@volar/language-core': 2.4.28 + '@volar/language-server': 2.4.28 + '@volar/language-service': 2.4.28 + muggle-string: 0.4.1 + tinyglobby: 0.2.16 + volar-service-css: 0.0.70(@volar/language-service@2.4.28) + volar-service-emmet: 0.0.70(@volar/language-service@2.4.28) + volar-service-html: 0.0.70(@volar/language-service@2.4.28) + volar-service-prettier: 0.0.70(@volar/language-service@2.4.28)(prettier@3.8.3) + volar-service-typescript: 0.0.70(@volar/language-service@2.4.28) + volar-service-typescript-twoslash-queries: 0.0.70(@volar/language-service@2.4.28) + volar-service-yaml: 0.0.70(@volar/language-service@2.4.28) + vscode-html-languageservice: 5.6.2 + vscode-uri: 3.1.0 + optionalDependencies: + prettier: 3.8.3 + transitivePeerDependencies: + - typescript + '@astrojs/markdown-remark@7.1.1': dependencies: '@astrojs/internal-helpers': 0.9.0 @@ -1894,21 +2334,30 @@ snapshots: transitivePeerDependencies: - supports-color + '@astrojs/node@10.1.0(astro@6.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.3)(yaml@2.8.4))': + dependencies: + '@astrojs/internal-helpers': 0.9.0 + astro: 6.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.3)(yaml@2.8.4) + send: 1.2.1 + server-destroy: 1.0.1 + transitivePeerDependencies: + - supports-color + '@astrojs/prism@4.0.1': dependencies: prismjs: 1.30.0 - '@astrojs/react@5.0.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@astrojs/react@5.0.4(@types/node@25.6.2)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yaml@2.8.4)': dependencies: '@astrojs/internal-helpers': 0.9.0 '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@vitejs/plugin-react': 5.2.0(vite@7.3.3(jiti@2.7.0)(lightningcss@1.32.0)) + '@vitejs/plugin-react': 5.2.0(vite@7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) devalue: 5.8.0 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) ultrahtml: 1.6.0 - vite: 7.3.3(jiti@2.7.0)(lightningcss@1.32.0) + vite: 7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) transitivePeerDependencies: - '@types/node' - jiti @@ -1931,6 +2380,10 @@ snapshots: is-wsl: 3.1.1 which-pm-runs: 1.1.0 + '@astrojs/yaml2ts@0.2.3': + dependencies: + yaml: 2.8.4 + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -2059,6 +2512,29 @@ snapshots: fast-wrap-ansi: 0.2.0 sisteransi: 1.0.5 + '@emmetio/abbreviation@2.3.3': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/css-abbreviation@2.1.8': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/css-parser@0.4.1': + dependencies: + '@emmetio/stream-reader': 2.2.0 + '@emmetio/stream-reader-utils': 0.1.0 + + '@emmetio/html-matcher@1.3.0': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/scanner@1.0.4': {} + + '@emmetio/stream-reader-utils@0.1.0': {} + + '@emmetio/stream-reader@2.2.0': {} + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 @@ -2446,12 +2922,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/vite@4.3.0(vite@7.3.3(jiti@2.7.0)(lightningcss@1.32.0))': + '@tailwindcss/vite@4.3.0(vite@7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4))': dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 tailwindcss: 4.3.0 - vite: 7.3.3(jiti@2.7.0)(lightningcss@1.32.0) + vite: 7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) '@types/babel__core@7.20.5': dependencies: @@ -2496,6 +2972,10 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/node@25.6.2': + dependencies: + undici-types: 7.19.2 + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 @@ -2508,7 +2988,7 @@ snapshots: '@ungap/structured-clone@1.3.1': {} - '@vitejs/plugin-react@5.2.0(vite@7.3.3(jiti@2.7.0)(lightningcss@1.32.0))': + '@vitejs/plugin-react@5.2.0(vite@7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -2516,10 +2996,77 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.3(jiti@2.7.0)(lightningcss@1.32.0) + vite: 7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) transitivePeerDependencies: - supports-color + '@volar/kit@2.4.28(typescript@6.0.3)': + dependencies: + '@volar/language-service': 2.4.28 + '@volar/typescript': 2.4.28 + typesafe-path: 0.2.2 + typescript: 6.0.3 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/language-server@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + '@volar/language-service': 2.4.28 + '@volar/typescript': 2.4.28 + path-browserify: 1.0.1 + request-light: 0.7.0 + vscode-languageserver: 9.0.1 + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + '@volar/language-service@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + vscode-languageserver-protocol: 3.17.5 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vscode/emmet-helper@2.11.0': + dependencies: + emmet: 2.4.11 + jsonc-parser: 2.3.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 + + '@vscode/l10n@0.0.18': {} + + ajv-draft-04@1.0.0(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 @@ -2531,7 +3078,7 @@ snapshots: array-iterate@2.0.1: {} - astro@6.3.1(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.3): + astro@6.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.3)(yaml@2.8.4): dependencies: '@astrojs/compiler': 4.0.0 '@astrojs/internal-helpers': 0.9.0 @@ -2583,8 +3130,8 @@ snapshots: unist-util-visit: 5.1.0 unstorage: 1.17.5 vfile: 6.0.3 - vite: 7.3.3(jiti@2.7.0)(lightningcss@1.32.0) - vitefu: 1.1.3(vite@7.3.3(jiti@2.7.0)(lightningcss@1.32.0)) + vite: 7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) + vitefu: 1.1.3(vite@7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.4.3 @@ -2650,14 +3197,30 @@ snapshots: character-entities@2.0.2: {} + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + chokidar@5.0.0: dependencies: readdirp: 5.0.0 ci-info@4.4.0: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + comma-separated-tokens@2.0.3: {} commander@11.1.0: {} @@ -2710,6 +3273,8 @@ snapshots: defu@6.1.7: {} + depd@2.0.0: {} + dequal@2.0.3: {} destr@2.0.5: {} @@ -2744,8 +3309,19 @@ snapshots: dset@3.1.4: {} + ee-first@1.1.1: {} + electron-to-chromium@1.5.352: {} + emmet@2.4.11: + dependencies: + '@emmetio/abbreviation': 2.3.3 + '@emmetio/css-abbreviation': 2.1.8 + + emoji-regex@8.0.0: {} + + encodeurl@2.0.0: {} + enhanced-resolve@5.21.2: dependencies: graceful-fs: 4.2.11 @@ -2788,20 +3364,28 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@5.0.0: {} estree-walker@2.0.2: {} + etag@1.8.1: {} + eventemitter3@5.0.4: {} extend@3.0.2: {} + fast-deep-equal@3.1.3: {} + fast-string-truncated-width@3.0.3: {} fast-string-width@3.0.2: dependencies: fast-string-truncated-width: 3.0.3 + fast-uri@3.1.2: {} + fast-wrap-ansi@0.2.0: dependencies: fast-string-width: 3.0.2 @@ -2820,11 +3404,15 @@ snapshots: dependencies: tiny-inflate: 1.0.3 + fresh@2.0.0: {} + fsevents@2.3.3: optional: true gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-tsconfig@5.0.0-beta.4: dependencies: resolve-pkg-maps: 1.0.0 @@ -2938,12 +3526,24 @@ snapshots: http-cache-semantics@4.2.0: {} + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + inherits@2.0.4: {} + iron-webcrypto@1.2.1: {} is-docker@3.0.0: {} is-docker@4.0.0: {} + is-fullwidth-code-point@3.0.0: {} + is-inside-container@1.0.0: dependencies: is-docker: 3.0.0 @@ -2964,10 +3564,16 @@ snapshots: jsesc@3.1.0: {} + json-schema-traverse@1.0.0: {} + json5@2.2.3: {} + jsonc-parser@2.3.0: {} + jsonc-parser@3.3.1: {} + kleur@4.1.5: {} + lightningcss-android-arm64@1.32.0: optional: true @@ -3353,10 +3959,18 @@ snapshots: transitivePeerDependencies: - supports-color + mime-db@1.54.0: {} + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + mrmime@2.0.1: {} ms@2.1.3: {} + muggle-string@0.4.1: {} + nanoid@3.3.12: {} neotraverse@0.6.18: {} @@ -3387,6 +4001,10 @@ snapshots: ohash@2.0.11: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + oniguruma-parser@0.12.2: {} oniguruma-to-es@4.3.6: @@ -3421,6 +4039,8 @@ snapshots: dependencies: entities: 4.3.0 + path-browserify@1.0.1: {} + piccolore@0.1.3: {} picocolors@1.1.1: {} @@ -3435,12 +4055,16 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prettier@3.8.3: {} + prismjs@1.30.0: {} property-information@7.1.0: {} radix3@1.1.2: {} + range-parser@1.2.1: {} + react-dom@19.2.6(react@19.2.6): dependencies: react: 19.2.6 @@ -3464,6 +4088,8 @@ snapshots: react@19.2.6: {} + readdirp@4.1.2: {} + readdirp@5.0.0: {} regex-recursion@6.0.2: @@ -3542,6 +4168,14 @@ snapshots: mdast-util-to-markdown: 2.0.0 unified: 11.0.5 + request-light@0.5.8: {} + + request-light@0.7.0: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + resolve-pkg-maps@1.0.0: {} retext-latin@4.0.0: @@ -3608,8 +4242,28 @@ snapshots: semver@7.7.4: {} + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + server-destroy@1.0.1: {} + set-cookie-parser@2.6.0: {} + setprototypeof@1.2.0: {} + sharp@0.34.5: dependencies: '@img/colour': 1.1.0 @@ -3661,11 +4315,23 @@ snapshots: space-separated-tokens@2.0.2: {} + statuses@2.0.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + svgo@4.0.1: dependencies: commander: 11.1.0 @@ -3691,6 +4357,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + toidentifier@1.0.1: {} + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -3698,12 +4366,22 @@ snapshots: tslib@2.8.1: optional: true + typesafe-path@0.2.2: {} + + typescript-auto-import-cache@0.3.6: + dependencies: + semver: 7.7.4 + + typescript@6.0.3: {} + ufo@1.6.4: {} ultrahtml@1.6.0: {} uncrypto@0.1.3: {} + undici-types@7.19.2: {} + unified@11.0.0: dependencies: '@types/unist': 3.0.3 @@ -3804,7 +4482,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@7.3.3(jiti@2.7.0)(lightningcss@1.32.0): + vite@7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -3813,24 +4491,172 @@ snapshots: rollup: 4.60.3 tinyglobby: 0.2.16 optionalDependencies: + '@types/node': 25.6.2 fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 + yaml: 2.8.4 - vitefu@1.1.3(vite@7.3.3(jiti@2.7.0)(lightningcss@1.32.0)): + vitefu@1.1.3(vite@7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)): optionalDependencies: - vite: 7.3.3(jiti@2.7.0)(lightningcss@1.32.0) + vite: 7.3.3(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) + + volar-service-css@0.0.70(@volar/language-service@2.4.28): + dependencies: + vscode-css-languageservice: 6.3.10 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-emmet@0.0.70(@volar/language-service@2.4.28): + dependencies: + '@emmetio/css-parser': 0.4.1 + '@emmetio/html-matcher': 1.3.0 + '@vscode/emmet-helper': 2.11.0 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-html@0.0.70(@volar/language-service@2.4.28): + dependencies: + vscode-html-languageservice: 5.6.2 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-prettier@0.0.70(@volar/language-service@2.4.28)(prettier@3.8.3): + dependencies: + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + prettier: 3.8.3 + + volar-service-typescript-twoslash-queries@0.0.70(@volar/language-service@2.4.28): + dependencies: + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-typescript@0.0.70(@volar/language-service@2.4.28): + dependencies: + path-browserify: 1.0.1 + semver: 7.7.4 + typescript-auto-import-cache: 0.3.6 + vscode-languageserver-textdocument: 1.0.12 + vscode-nls: 5.2.0 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-yaml@0.0.70(@volar/language-service@2.4.28): + dependencies: + vscode-uri: 3.1.0 + yaml-language-server: 1.20.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + vscode-css-languageservice@6.3.10: + dependencies: + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 + + vscode-html-languageservice@5.6.2: + dependencies: + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 + + vscode-json-languageservice@4.1.8: + dependencies: + jsonc-parser: 3.3.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-nls: 5.2.0 + vscode-uri: 3.1.0 + + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.4: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.4 + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.4: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.0: + dependencies: + vscode-languageserver-protocol: 3.17.4 + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-nls@5.2.0: {} + + vscode-uri@3.1.0: {} web-namespaces@2.0.1: {} which-pm-runs@1.1.0: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + xxhash-wasm@1.1.0: {} + y18n@5.0.8: {} + yallist@3.1.1: {} + yaml-language-server@1.20.0: + dependencies: + '@vscode/l10n': 0.0.18 + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + prettier: 3.8.3 + request-light: 0.5.8 + vscode-json-languageservice: 4.1.8 + vscode-languageserver: 9.0.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 + yaml: 2.7.1 + + yaml@2.7.1: {} + + yaml@2.8.4: {} + + yargs-parser@21.1.1: {} + yargs-parser@22.0.0: {} + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yocto-queue@1.2.2: {} zod@4.4.3: {} diff --git a/web/src/components/AuthProvider.tsx b/web/src/components/AuthProvider.tsx deleted file mode 100644 index ab329c7..0000000 --- a/web/src/components/AuthProvider.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'; -import { - getAuth, - loginWithEmail as doLogin, - refreshAccessToken, - logout as doLogout, - clearAuth, - redirectToLogin, - type AuthUser, -} from '../lib/auth'; - -interface AuthContextValue { - user: AuthUser | null; - isAuthenticated: boolean; - loading: boolean; - login: (email: string, token: string, language?: string, timezone?: string) => Promise; - logout: () => Promise; -} - -const AuthContext = createContext({ - user: null, - isAuthenticated: false, - loading: true, - login: async () => {}, - logout: async () => {}, -}); - -export function useAuth(): AuthContextValue { - return useContext(AuthContext); -} - -export function AuthProvider({ children }: { children: ReactNode }) { - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - - // recoverSession on mount - useEffect(() => { - const auth = getAuth(); - if (!auth?.refresh_token) { - clearAuth(); - redirectToLogin(); - return; - } - refreshAccessToken() - .then((data) => { - setUser(data.user); - }) - .catch(() => { - clearAuth(); - setUser(null); - redirectToLogin(); - }) - .finally(() => { - setLoading(false); - }); - }, []); - - const login = useCallback( - async (email: string, token: string, language?: string, timezone?: string) => { - const data = await doLogin(email, token, language, timezone); - setUser(data.user); - }, - [], - ); - - const logout = useCallback(async () => { - setUser(null); - await doLogout(); - redirectToLogin(); - }, []); - - if (loading || user === null) { - return ( -
-
-
- ); - } - - return ( - - {children} - - ); -} diff --git a/web/src/components/AutoDivinationPage.tsx b/web/src/components/AutoDivinationPage.tsx index 4e36907..4866da6 100644 --- a/web/src/components/AutoDivinationPage.tsx +++ b/web/src/components/AutoDivinationPage.tsx @@ -436,10 +436,6 @@ export default function AutoDivinationPage({ locale, divination: d }: Props) { } }; - const handleBack = () => { - navigate(`/${locale}/dashboard`); - }; - return (
diff --git a/web/src/components/Dashboard.tsx b/web/src/components/Dashboard.tsx index bc570e0..31dbae4 100644 --- a/web/src/components/Dashboard.tsx +++ b/web/src/components/Dashboard.tsx @@ -1,5 +1,5 @@ import { mapHistoryMessagesToItems } from '../lib/api'; -import { useHistorySummary, usePoints, useUnreadCount } from '../lib/resources'; +import { primeHistoryThreadFromSnapshot, useHistorySummary, usePoints, useUnreadCount } from '../lib/resources'; import Icon from './Icon'; interface DashboardProps { @@ -130,7 +130,8 @@ export default function Dashboard({ locale, translations: i18n }: DashboardProps history.map((item) => ( historyState.data && primeHistoryThreadFromSnapshot(item.threadId, historyState.data)} className="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-4 bg-white rounded-xl p-4 md:p-5 border border-slate-100 hover:shadow-sm hover:border-violet-200 transition-all cursor-pointer" >
diff --git a/web/src/components/DashboardApp.tsx b/web/src/components/DashboardApp.tsx index 9c43d15..c607fae 100644 --- a/web/src/components/DashboardApp.tsx +++ b/web/src/components/DashboardApp.tsx @@ -1,39 +1,27 @@ -import { useEffect } from 'react'; +import { lazy, Suspense, useEffect } from 'react'; import { BrowserRouter, Navigate, Route, Routes, useNavigate } from 'react-router-dom'; +import type { Locale, Translations } from '../i18n/utils'; import AppShell from './AppShell'; -import Dashboard from './Dashboard'; -import StorePage from './StorePage'; -import HistoryListPage from './HistoryListPage'; -import DivinationResultPage from './DivinationResultPage'; -import HistoryFollowUpPage from './HistoryFollowUpPage'; -import NotificationPage from './NotificationPage'; -import ProfileDetailPage from './ProfileDetailPage'; -import SettingsPage from './SettingsPage'; -import GeneralSettingsPage from './GeneralSettingsPage'; -import FeedbackPage from './FeedbackPage'; -import ManualDivinationPage from './ManualDivinationPage'; -import AutoDivinationPage from './AutoDivinationPage'; import { getNavConfig } from './navConfig'; -type TranslationMap = Record; - interface DashboardAppProps { - locale: string; - translations: { - dashboard: TranslationMap; - store: TranslationMap; - pricing: TranslationMap; - history: TranslationMap; - notifications: TranslationMap; - profile: TranslationMap; - settings: TranslationMap; - divination: TranslationMap; - general: TranslationMap; - feedback: TranslationMap; - result: TranslationMap; - }; + locale: Locale; + translations: Pick; } +const Dashboard = lazy(() => import('./Dashboard')); +const StorePage = lazy(() => import('./StorePage')); +const HistoryListPage = lazy(() => import('./HistoryListPage')); +const DivinationResultPage = lazy(() => import('./DivinationResultPage')); +const HistoryFollowUpPage = lazy(() => import('./HistoryFollowUpPage')); +const NotificationPage = lazy(() => import('./NotificationPage')); +const ProfileDetailPage = lazy(() => import('./ProfileDetailPage')); +const SettingsPage = lazy(() => import('./SettingsPage')); +const GeneralSettingsPage = lazy(() => import('./GeneralSettingsPage')); +const FeedbackPage = lazy(() => import('./FeedbackPage')); +const ManualDivinationPage = lazy(() => import('./ManualDivinationPage')); +const AutoDivinationPage = lazy(() => import('./AutoDivinationPage')); + const APP_PATHS = [ '/dashboard', '/store', @@ -77,6 +65,14 @@ function AppLinkInterceptor({ locale }: { locale: string }) { return null; } +function RouteFallback() { + return ( +
+
+
+ ); +} + function DashboardRoutes({ locale, translations }: DashboardAppProps) { const dashboard = translations.dashboard; const navItems = getNavConfig(locale, dashboard); @@ -84,24 +80,26 @@ function DashboardRoutes({ locale, translations }: DashboardAppProps) { return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + }> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ); } diff --git a/web/src/components/DivinationResultPage.tsx b/web/src/components/DivinationResultPage.tsx index d15272d..64dcfe9 100644 --- a/web/src/components/DivinationResultPage.tsx +++ b/web/src/components/DivinationResultPage.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { useLocation, useNavigate, useParams } from 'react-router-dom'; +import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { historyMessageToResultData, type DivinationResultData, type YaoType } from '../lib/api'; import { useHistoryThread } from '../lib/resources'; import Icon from './Icon'; @@ -339,7 +339,9 @@ const RESULT_STORAGE_KEY = 'divination_result_data'; export default function DivinationResultPage({ locale, translations: t }: Props) { const location = useLocation(); const navigate = useNavigate(); - const { id: threadId } = useParams<{ id: string }>(); + const { id: routeThreadId } = useParams<{ id: string }>(); + const [searchParams] = useSearchParams(); + const threadId = routeThreadId ?? searchParams.get('threadId') ?? undefined; const threadState = useHistoryThread(threadId); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); @@ -403,7 +405,7 @@ export default function DivinationResultPage({ locale, translations: t }: Props) const handleFollowUp = () => { const effectiveThreadId = data?.threadId || threadId; if (effectiveThreadId) { - navigate(`/${locale}/history/${effectiveThreadId}/followup`, { state: { result: data } }); + navigate(`/${locale}/history/followup?threadId=${encodeURIComponent(effectiveThreadId)}`, { state: { result: data } }); } }; diff --git a/web/src/components/FeedbackPage.tsx b/web/src/components/FeedbackPage.tsx index 7e0c7d5..eebf398 100644 --- a/web/src/components/FeedbackPage.tsx +++ b/web/src/components/FeedbackPage.tsx @@ -2,7 +2,7 @@ import { useState, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { authFetch } from '../lib/auth'; import { API_ROUTES } from '../lib/api-routes'; -import { apiUrl, jsonHeaders } from '../lib/api-client'; +import { apiUrl } from '../lib/api-client'; interface Props { locale: string; diff --git a/web/src/components/HistoryFollowUpPage.tsx b/web/src/components/HistoryFollowUpPage.tsx index 135653b..6fd04e4 100644 --- a/web/src/components/HistoryFollowUpPage.tsx +++ b/web/src/components/HistoryFollowUpPage.tsx @@ -1,5 +1,5 @@ import { useState, useEffect, useRef, useCallback } from 'react'; -import { useLocation, useNavigate, useParams } from 'react-router-dom'; +import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { historyMessageToResultData, enqueueFollowUpRun, @@ -63,7 +63,9 @@ const CATEGORY_COLORS: Record = { export default function HistoryFollowUpPage({ locale, history: h }: Props) { const location = useLocation(); const navigate = useNavigate(); - const { id: threadId } = useParams<{ id: string }>(); + const { id: routeThreadId } = useParams<{ id: string }>(); + const [searchParams] = useSearchParams(); + const threadId = routeThreadId ?? searchParams.get('threadId') ?? undefined; const [resultData, setResultData] = useState(null); const [messages, setMessages] = useState([]); @@ -373,7 +375,7 @@ export default function HistoryFollowUpPage({ locale, history: h }: Props) {

{h.relatedActions}