diff --git a/.gitignore b/.gitignore index dce7625..d8cde36 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,14 @@ build/ **/dev-dist/ **/coverage/ +# vue-router auto-generated typed routes +**/typed-router.d.ts + +# unplugin-auto-import / unplugin-vue-components generated declarations +**/auto-imports.d.ts +**/components.d.ts +**/.eslintrc-auto-import.json + # Tauri / Rust apps/desktop/src-tauri/target/ apps/desktop/src-tauri/gen/ diff --git a/apps/client/README.md b/apps/client/README.md new file mode 100644 index 0000000..ce0b4d6 --- /dev/null +++ b/apps/client/README.md @@ -0,0 +1,160 @@ +# @tarkov-checker/client + +Vue PWA + Leaflet map. Runs both inside the Tauri overlay (`apps/desktop`) and as a plain browser PWA on phones over LAN. Same code path, different transport (Tauri events vs WebSocket). + +Specifics about Tauri internals, Windows build quirks, and dev workflow live in [the repo CLAUDE.md](../../CLAUDE.md). This README is for contributors working **inside the client**. + +## Stack + +- **Vue 3.5** (Composition API, ` @@ -229,85 +51,6 @@ const statusIconClass = computed(() => { :class="isTauri ? '' : 'bg-surface-950'" @contextmenu="onMapContextMenu" > - - - - - - {{ mapDisplayName }} - - - - - - - - - - - - Map load error: {{ mapError }} - - - Extracts: {{ extractsError }} - - - - - - - - {{ part }} - - + - - - - - - - - + diff --git a/apps/client/src/api/server-config.ts b/apps/client/src/api/server-config.ts deleted file mode 100644 index f847485..0000000 --- a/apps/client/src/api/server-config.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - serverConfigResponseSchema, - type ServerConfigResponse, - type ServerConfigUpdate, -} from "@shared/config-api"; - -const isTauri = "__TAURI_INTERNALS__" in window; - -function apiBase(): string { - return `http://${window.location.hostname}:3000`; -} - -async function tauriInvoke(name: string, args?: Record): Promise { - // Lazy-import so the chunk only loads when actually running inside Tauri. - const { invoke } = await import("@tauri-apps/api/core"); - return invoke(name, args); -} - -export async function fetchServerConfig(): Promise { - if (isTauri) { - const data = await tauriInvoke("get_config"); - return serverConfigResponseSchema.parse(data); - } - const r = await fetch(`${apiBase()}/api/config`); - if (!r.ok) throw new Error(`GET /api/config failed: HTTP ${r.status}`); - return serverConfigResponseSchema.parse(await r.json()); -} - -export async function putServerConfig( - patch: ServerConfigUpdate, -): Promise { - if (isTauri) { - const data = await tauriInvoke("update_config", { patch }); - return serverConfigResponseSchema.parse(data); - } - const r = await fetch(`${apiBase()}/api/config`, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(patch), - }); - if (!r.ok) { - const body: unknown = await r.json().catch(() => ({})); - const error = - typeof body === "object" && body !== null && "error" in body ? body.error : body; - throw new Error(`PUT /api/config failed: HTTP ${r.status} — ${JSON.stringify(error)}`); - } - return serverConfigResponseSchema.parse(await r.json()); -} diff --git a/apps/client/src/app/router.ts b/apps/client/src/app/router.ts new file mode 100644 index 0000000..b8f0e0b --- /dev/null +++ b/apps/client/src/app/router.ts @@ -0,0 +1,21 @@ +import { createRouter, createMemoryHistory, createWebHistory } from "vue-router"; +import { routes } from "vue-router/auto-routes"; + +const isTauri = "__TAURI_INTERNALS__" in window; + +/** + * Route table is generated from src/pages/*.vue at build time by the + * `vue-router/vite` plugin (see vite.config.ts) — adding a page is just + * creating a file, no edits here. The typed-router.d.ts file alongside this + * one (gitignored, regenerated on dev/build) is what gives `router.push` + * compile-time autocompletion for route names. + * + * In Tauri the URL bar is invisible and route changes don't need to round-trip + * through the browser history — memory history avoids stale entries when the + * window is re-opened. The browser/PWA build keeps web history so deep links + * (e.g. /raid on a phone) survive bookmarking. + */ +export const router = createRouter({ + history: isTauri ? createMemoryHistory() : createWebHistory(), + routes, +}); diff --git a/apps/client/src/components/SettingsPanel.vue b/apps/client/src/components/SettingsPanel.vue deleted file mode 100644 index 12cbf25..0000000 --- a/apps/client/src/components/SettingsPanel.vue +++ /dev/null @@ -1,602 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - {{ t.factions[opt.value] }} - - - - - {{ t.labels }} - - {{ t.labelHint }} - - - - {{ t.labelSize }} - - - - - - {{ t.playerFollow }} - - {{ t.playerFollowHint }} - - - - - - {{ t.overlay.alwaysOnTop }} - - - - - - {{ t.overlay.opacity }} - {{ opacityPercent }}% - - - - - - - - {{ t.overlay.mapOpacity }} - - {{ mapOpacityPercent }}% - - - - {{ t.overlay.mapOpacityHint }} - - - - - {{ t.overlay.zoom }} - - - - - - - - - - - - - {{ t.hotkeys.lockHint }} - - - - - - - {{ t.cache.lastUpdated }}: {{ cacheRelativeAge }} - - - - - {{ cacheError }} - - {{ t.cache.hint }} - - - - - {{ t.systemSection }} - - - - - - - - - - {{ pathsError }} - - … - - - - - {{ t.paths.gameDir }} - - - - - - - {{ t.paths.logsDir }}: {{ serverConfig.logsDir.value ?? "—" }} - - - - - - {{ t.paths.screenshotsDir }} - - - - - - - - - - {{ t.paths.saved }} - - - - - - {{ t.paths.mobileHint }} - - - - - - - - diff --git a/apps/client/src/composables/useLeafletMap.ts b/apps/client/src/composables/useLeafletMap.ts deleted file mode 100644 index 5466ba8..0000000 --- a/apps/client/src/composables/useLeafletMap.ts +++ /dev/null @@ -1,519 +0,0 @@ -import { onMounted, onBeforeUnmount, ref, shallowRef, type Ref, type ShallowRef } from "vue"; -import L, { type Map as LeafletMap, type LatLngExpression, type CRS, type Marker } from "leaflet"; -import { mapInfo, mapSvgPath, type TarkovMapCode } from "@shared/maps"; -import type { Extract, Position3D } from "@shared/tarkov-api"; - -interface LoadedMap { - width: number; - height: number; - floors: Map; -} - -interface MarkerEntry { - marker: Marker; - extract: Extract; - /** Tooltip offset in screen pixels relative to the marker centre. */ - tooltipOffset: [number, number]; -} - -export type LabelMode = "hover" | "always"; -export type LabelSize = "sm" | "md" | "lg"; -export type PlayerFollow = "off" | "sm" | "md" | "lg"; - -interface UseLeafletMapResult { - map: ShallowRef; - loaded: ShallowRef; - mapError: Ref; - currentFloor: Ref; - addExtractMarkers: (extracts: readonly Extract[]) => void; - setExtractFilter: (visibleFactions: ReadonlyArray) => void; - setLabelMode: (mode: LabelMode) => void; - setLabelSize: (size: LabelSize) => void; - setPlayerFollow: (mode: PlayerFollow) => void; - setActiveFloor: (id: string) => void; - setPlayerPosition: (pos: Position3D, yaw?: number | null) => void; - clearPlayerPosition: () => void; - zoomIn: () => void; - zoomOut: () => void; - nextFloor: () => void; - prevFloor: () => void; -} - -const LABEL_SIZE_PX: Readonly> = { - sm: "9px", - md: "11px", - lg: "14px", -}; - -/** Zoom levels (delta from initialZoom) for each follow mode; clamped to maxZoom. */ -const FOLLOW_ZOOM_DELTA: Readonly, number>> = { - sm: 1, - md: 2, - lg: 3, -}; - -const EXTRACT_ICON_SIZE = 26; -const EXTRACT_ICONS: Readonly> = { - pmc: L.icon({ - iconUrl: "/icons/extracts/extract_pmc.png", - iconSize: [EXTRACT_ICON_SIZE, EXTRACT_ICON_SIZE], - iconAnchor: [EXTRACT_ICON_SIZE / 2, EXTRACT_ICON_SIZE / 2], - tooltipAnchor: [0, 0], - }), - scav: L.icon({ - iconUrl: "/icons/extracts/extract_scav.png", - iconSize: [EXTRACT_ICON_SIZE, EXTRACT_ICON_SIZE], - iconAnchor: [EXTRACT_ICON_SIZE / 2, EXTRACT_ICON_SIZE / 2], - tooltipAnchor: [0, 0], - }), - shared: L.icon({ - iconUrl: "/icons/extracts/extract_shared.png", - iconSize: [EXTRACT_ICON_SIZE, EXTRACT_ICON_SIZE], - iconAnchor: [EXTRACT_ICON_SIZE / 2, EXTRACT_ICON_SIZE / 2], - tooltipAnchor: [0, 0], - }), -}; - -function extractIcon(faction: Extract["faction"]): L.Icon { - const key = (faction ?? "shared") as keyof typeof EXTRACT_ICONS; - return EXTRACT_ICONS[key] ?? EXTRACT_ICONS.shared; -} - -/** Extracts within this many in-game units of each other share a tooltip ring. */ -const COLOCATION_TOLERANCE = 2; -/** Radial distance from marker centre to the centre of its tooltip, in screen pixels. */ -const TOOLTIP_RING_RADIUS = 28; -/** How much of the map bounds the user is allowed to pan past (0.15 = 15%). */ -const PAN_PAD = 0.15; - -function factionForFilter(faction: Extract["faction"]): string { - return faction ?? "shared"; -} - -function applyRotation(latLng: L.LatLng, rotationDeg: number): L.LatLng { - if (rotationDeg === 0) return latLng; - if (latLng.lat === 0 && latLng.lng === 0) return L.latLng(0, 0); - const rad = (rotationDeg * Math.PI) / 180; - const cos = Math.cos(rad); - const sin = Math.sin(rad); - const { lng: x, lat: y } = latLng; - const rotatedX = x * cos - y * sin; - const rotatedY = x * sin + y * cos; - return L.latLng(rotatedY, rotatedX); -} - -function buildCRS(transform: readonly [number, number, number, number], rotation: number): CRS { - const [scaleX, marginX, scaleYRaw, marginY] = transform; - const scaleY = scaleYRaw * -1; - return L.extend({}, L.CRS.Simple, { - transformation: new L.Transformation(scaleX, marginX, scaleY, marginY), - projection: L.extend({}, L.Projection.LonLat, { - project(latLng: L.LatLng): L.Point { - return L.Projection.LonLat.project(applyRotation(latLng, rotation)); - }, - unproject(point: L.Point): L.LatLng { - return applyRotation(L.Projection.LonLat.unproject(point), -rotation); - }, - }), - }) as CRS; -} - -function inGameLatLng(x: number, z: number): LatLngExpression { - return [z, x]; -} - -function mapLatLngBounds( - bounds: readonly [readonly [number, number], readonly [number, number]], -): L.LatLngBounds { - const [[x1, z1], [x2, z2]] = bounds; - return L.latLngBounds(L.latLng(z1, x1), L.latLng(z2, x2)); -} - -async function fetchSvg(url: string): Promise<{ - svg: SVGSVGElement; - width: number; - height: number; - floors: Map; -}> { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`Failed to load ${url}: HTTP ${response.status}`); - } - const text = await response.text(); - const doc = new DOMParser().parseFromString(text, "image/svg+xml"); - const parseError = doc.querySelector("parsererror"); - if (parseError) { - throw new Error(`SVG parse error in ${url}: ${parseError.textContent ?? "unknown"}`); - } - const svg = doc.documentElement as unknown as SVGSVGElement; - const viewBoxAttr = svg.getAttribute("viewBox"); - if (!viewBoxAttr) { - throw new Error(`SVG at ${url} has no viewBox attribute`); - } - const parts = viewBoxAttr - .trim() - .split(/[\s,]+/) - .map(Number); - if (parts.length !== 4 || parts.some((n) => !Number.isFinite(n))) { - throw new Error(`SVG at ${url} has malformed viewBox: ${viewBoxAttr}`); - } - const width = parts[2] as number; - const height = parts[3] as number; - - const floors = new Map(); - for (const child of Array.from(svg.children)) { - if (child.tagName.toLowerCase() === "g" && child.id) { - floors.set(child.id, child as SVGGElement); - } - } - return { svg, width, height, floors }; -} - -export function useLeafletMap( - containerRef: Ref, - mapCode: TarkovMapCode, -): UseLeafletMapResult { - const map = shallowRef(null); - const loaded = shallowRef(null); - const mapError = ref(null); - const currentFloor = ref(null); - - const info = mapInfo(mapCode); - const crs = buildCRS(info.transform, info.rotation); - const bounds = mapLatLngBounds(info.bounds); - - let extractsLayer: L.LayerGroup | null = null; - const entries: MarkerEntry[] = []; - let initialZoom = 0; - - let playerLayer: L.LayerGroup | null = null; - let playerCore: L.Marker | null = null; - - // Internal state, mutated by setters; addExtractMarkers re-applies when (re)creating markers. - const state = { - visibleFactions: new Set(["pmc", "scav", "shared"]), - labelMode: "hover" as LabelMode, - playerFollow: "off" as PlayerFollow, - }; - let lastFollowedX = Number.NaN; - let lastFollowedZ = Number.NaN; - let lastFollowedYaw: number | null = Number.NaN; - - function isEntryVisible(entry: MarkerEntry): boolean { - return state.visibleFactions.has(factionForFilter(entry.extract.faction)); - } - - function buildTooltipOpts(): Omit { - return { - direction: "center", - opacity: 0.95, - permanent: state.labelMode === "always", - sticky: state.labelMode === "hover", - }; - } - - function applyTooltipBindings(): void { - const base = buildTooltipOpts(); - for (const entry of entries) { - entry.marker.unbindTooltip(); - const factionClass = factionForFilter(entry.extract.faction); - entry.marker.bindTooltip(entry.extract.name, { - ...base, - offset: entry.tooltipOffset, - className: `extract-tooltip extract-tooltip--${factionClass}`, - }); - entry.marker.off("click", reopenAllPermanentTooltips); - entry.marker.on("click", reopenAllPermanentTooltips); - } - } - - /** - * In `always` mode a stray click — either on a marker or on the empty map — - * closes the permanent tooltip Leaflet just rendered. Re-open every visible - * marker's tooltip after each click so they stay parked. - */ - function reopenAllPermanentTooltips(): void { - if (state.labelMode !== "always") return; - setTimeout(() => { - for (const entry of entries) { - if (isEntryVisible(entry)) entry.marker.openTooltip(); - } - }, 0); - } - - function applyVisibility(): void { - if (!map.value || !extractsLayer) return; - for (const entry of entries) { - const visible = isEntryVisible(entry); - const onLayer = extractsLayer.hasLayer(entry.marker); - if (visible && !onLayer) { - extractsLayer.addLayer(entry.marker); - } else if (!visible && onLayer) { - extractsLayer.removeLayer(entry.marker); - } - } - } - - function addExtractMarkers(extracts: readonly Extract[]): void { - if (!map.value) return; - if (extractsLayer) { - extractsLayer.clearLayers(); - } else { - extractsLayer = L.layerGroup().addTo(map.value); - } - entries.length = 0; - for (const ex of extracts) { - const marker = L.marker(inGameLatLng(ex.position.x, ex.position.z), { - icon: extractIcon(ex.faction), - pane: "extracts", - }); - entries.push({ marker, extract: ex, tooltipOffset: [0, -TOOLTIP_RING_RADIUS] }); - } - computeTooltipOffsets(); - applyTooltipBindings(); - applyVisibility(); - } - - function computeTooltipOffsets(): void { - // Markers stay at their true (x, z); only their tooltips get a radial offset - // so labels do not stack on top of each other when extracts share a spot - // (e.g. RUAF Roadblock has both a PMC and a Scav variant a metre apart). - const bucketSize = Math.max(COLOCATION_TOLERANCE, 1); - const groups = new Map(); - for (const entry of entries) { - const bx = Math.round(entry.extract.position.x / bucketSize); - const bz = Math.round(entry.extract.position.z / bucketSize); - const key = `${bx},${bz}`; - const group = groups.get(key); - if (group) { - group.push(entry); - } else { - groups.set(key, [entry]); - } - } - for (const group of groups.values()) { - const total = group.length; - const step = (2 * Math.PI) / total; - for (let i = 0; i < total; i++) { - const angle = i * step - Math.PI / 2; - group[i]!.tooltipOffset = [ - Math.cos(angle) * TOOLTIP_RING_RADIUS, - Math.sin(angle) * TOOLTIP_RING_RADIUS, - ]; - } - } - } - - function setExtractFilter(visibleFactions: ReadonlyArray): void { - state.visibleFactions = new Set(visibleFactions); - applyVisibility(); - } - - function setLabelSize(size: LabelSize): void { - document.documentElement.style.setProperty("--extract-label-size", LABEL_SIZE_PX[size]); - } - - function setPlayerFollow(mode: PlayerFollow): void { - state.playerFollow = mode; - } - - function setLabelMode(mode: LabelMode): void { - if (state.labelMode === mode) return; - state.labelMode = mode; - applyTooltipBindings(); - if (mode === "hover") { - for (const entry of entries) { - entry.marker.closeTooltip(); - } - } - } - - function setPlayerPosition(pos: Position3D, yaw: number | null = null): void { - if (!map.value) return; - const latLng = inGameLatLng(pos.x, pos.z); - if (!playerLayer) { - playerLayer = L.layerGroup().addTo(map.value); - } - // The in-game yaw must be rotated by the map's own coordinateRotation - // so the arrow points where the player is looking in the rendered view. - const displayYaw = yaw === null ? null : yaw + info.rotation; - const iconHtml = buildPlayerIconHtml(displayYaw); - const icon = L.divIcon({ - html: iconHtml, - className: "player-icon-wrapper", - iconSize: [36, 36], - iconAnchor: [18, 18], - }); - if (!playerCore) { - playerCore = L.marker(latLng, { - icon, - interactive: false, - keyboard: false, - zIndexOffset: 1000, - pane: "extracts", - }).addTo(playerLayer); - } else { - playerCore.setLatLng(latLng); - playerCore.setIcon(icon); - } - - const changed = pos.x !== lastFollowedX || pos.z !== lastFollowedZ || yaw !== lastFollowedYaw; - if (changed && state.playerFollow !== "off") { - const targetZoom = Math.min( - initialZoom + FOLLOW_ZOOM_DELTA[state.playerFollow], - map.value.getMaxZoom(), - ); - map.value.setView(latLng, targetZoom, { animate: true, duration: 0.4 }); - } - if (changed) { - lastFollowedX = pos.x; - lastFollowedZ = pos.z; - lastFollowedYaw = yaw; - } - } - - function buildPlayerIconHtml(displayYaw: number | null): string { - if (displayYaw === null) { - return ``; - } - return ``; - } - - function clearPlayerPosition(): void { - if (playerLayer && map.value) { - map.value.removeLayer(playerLayer); - } - playerLayer = null; - playerCore = null; - } - - onMounted(async () => { - if (!containerRef.value) return; - - const instance = L.map(containerRef.value, { - crs, - attributionControl: false, - zoomControl: false, - minZoom: -5, - maxZoom: 4, - zoomSnap: 0.25, - maxBoundsViscosity: 1.0, - }); - map.value = instance; - // Custom pane for overlay markers — sits above the default overlayPane - // (z 400) where L.svgOverlay lands, so markers stay visible after a map - // switch even though the SVG is fetched asynchronously and lands later. - const markersPane = instance.createPane("extracts"); - markersPane.style.zIndex = "500"; - instance.fitBounds(bounds); - initialZoom = instance.getZoom(); - instance.setMinZoom(initialZoom); - instance.setMaxBounds(bounds.pad(PAN_PAD)); - instance.on("click", reopenAllPermanentTooltips); - - try { - const svgUrl = mapSvgPath(mapCode); - const { svg, width, height, floors } = await fetchSvg(svgUrl); - L.svgOverlay(svg, bounds, { interactive: false }).addTo(instance); - loaded.value = { width, height, floors }; - if (info.defaultFloor && info.floors.length > 0) { - setActiveFloor(info.defaultFloor); - } - } catch (err) { - mapError.value = err instanceof Error ? err.message : String(err); - } - }); - - function zoomIn(): void { - map.value?.zoomIn(); - } - - function zoomOut(): void { - map.value?.zoomOut(); - } - - function shiftFloor(delta: 1 | -1): void { - const floorIds = info.floors.map((f) => f.id); - if (floorIds.length <= 1) return; - const active = currentFloor.value ?? info.defaultFloor; - // No usable anchor — neither a current nor default floor — bail. - if (active === null) return; - const idx = floorIds.indexOf(active); - // Wrap around so repeated presses cycle the list. - const nextIdx = (idx + delta + floorIds.length) % floorIds.length; - const next = floorIds[nextIdx]; - if (next) setActiveFloor(next); - } - function nextFloor(): void { - shiftFloor(1); - } - function prevFloor(): void { - shiftFloor(-1); - } - - function setActiveFloor(id: string): void { - const floorIds = info.floors.map((f) => f.id); - if (floorIds.length === 0) return; - if (!floorIds.includes(id)) return; - const map = loaded.value?.floors; - if (!map) { - // SVG hasn't loaded yet; remember choice and apply on load. - currentFloor.value = id; - return; - } - const ground = info.defaultFloor; - for (const fid of floorIds) { - const group = map.get(fid); - if (!group) continue; - if (fid === id) { - group.style.display = ""; - group.style.opacity = ""; - } else if (fid === ground) { - // Ground is the persistent context — always visible, dimmed when - // another floor sits on top. - group.style.display = ""; - group.style.opacity = "0.15"; - } else { - group.style.display = "none"; - group.style.opacity = ""; - } - } - // SVG draws later siblings on top of earlier ones. Move the active - // group to the end so it always renders above ground (and above other - // floors that may sit between them in the original DOM order — e.g. - // Labs Technical_Level is the first child, ground is the second). - const activeGroup = map.get(id); - if (activeGroup && activeGroup.parentNode) { - activeGroup.parentNode.appendChild(activeGroup); - } - currentFloor.value = id; - } - - onBeforeUnmount(() => { - extractsLayer = null; - entries.length = 0; - playerLayer = null; - playerCore = null; - map.value?.remove(); - map.value = null; - }); - - return { - map, - loaded, - mapError, - currentFloor, - addExtractMarkers, - setExtractFilter, - setLabelMode, - setLabelSize, - setPlayerFollow, - setActiveFloor, - setPlayerPosition, - clearPlayerPosition, - zoomIn, - zoomOut, - nextFloor, - prevFloor, - }; -} diff --git a/apps/client/src/env.d.ts b/apps/client/src/env.d.ts index 9fd2cff..fa34357 100644 --- a/apps/client/src/env.d.ts +++ b/apps/client/src/env.d.ts @@ -1,5 +1,16 @@ /// /// +/// +/// + +interface ImportMetaEnv { + /** LAN Node backend port (Fastify). Defaults to 3000 when unset. */ + readonly VITE_SERVER_PORT?: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} declare module "*.vue" { import type { DefineComponent } from "vue"; diff --git a/apps/client/src/components/HotkeyRecorder.vue b/apps/client/src/features/hotkeys/components/HotkeyRecorder.vue similarity index 88% rename from apps/client/src/components/HotkeyRecorder.vue rename to apps/client/src/features/hotkeys/components/HotkeyRecorder.vue index edcc3dd..00f664b 100644 --- a/apps/client/src/components/HotkeyRecorder.vue +++ b/apps/client/src/features/hotkeys/components/HotkeyRecorder.vue @@ -1,8 +1,5 @@ + + + + + Map load error: {{ mapError }} + + + Extracts: {{ extractsError }} + + + diff --git a/apps/client/src/features/overlay/components/OverlayHeader.vue b/apps/client/src/features/overlay/components/OverlayHeader.vue new file mode 100644 index 0000000..4da0eba --- /dev/null +++ b/apps/client/src/features/overlay/components/OverlayHeader.vue @@ -0,0 +1,66 @@ + + + + + + + {{ mapDisplayName }} + + + + + + + + + diff --git a/apps/client/src/features/overlay/components/OverlayLockIndicator.vue b/apps/client/src/features/overlay/components/OverlayLockIndicator.vue new file mode 100644 index 0000000..114c97d --- /dev/null +++ b/apps/client/src/features/overlay/components/OverlayLockIndicator.vue @@ -0,0 +1,43 @@ + + + + + + + + {{ part }} + + + + + + + + + + + + diff --git a/apps/client/src/features/overlay/composables/useCloseConfirm.ts b/apps/client/src/features/overlay/composables/useCloseConfirm.ts new file mode 100644 index 0000000..bc79529 --- /dev/null +++ b/apps/client/src/features/overlay/composables/useCloseConfirm.ts @@ -0,0 +1,31 @@ +import { useConfirm } from "primevue/useconfirm"; +import { useTauriOverlay } from "./useTauriOverlay"; + +/** + * Returns a `confirmClose()` function that pops a PrimeVue confirm dialog + * and, on accept, closes the Tauri window. No-op in browser context (the + * close button is hidden there anyway). + */ +export function useCloseConfirm(): () => void { + const confirm = useConfirm(); + const { t } = useI18n(); + const { isTauri } = useTauriOverlay(); + + return function confirmClose(): void { + if (!isTauri) return; + confirm.require({ + message: t("closeConfirm.message"), + header: t("closeConfirm.title"), + icon: "pi pi-times-circle", + acceptLabel: t("closeConfirm.accept"), + rejectLabel: t("closeConfirm.reject"), + acceptClass: "p-button-danger", + accept: () => { + void (async () => { + const { getCurrentWindow } = await import("@tauri-apps/api/window"); + await getCurrentWindow().close(); + })(); + }, + }); + }; +} diff --git a/apps/client/src/features/overlay/composables/useOverlayBootstrap.ts b/apps/client/src/features/overlay/composables/useOverlayBootstrap.ts new file mode 100644 index 0000000..2eda0cd --- /dev/null +++ b/apps/client/src/features/overlay/composables/useOverlayBootstrap.ts @@ -0,0 +1,23 @@ +import { useTauriOverlay } from "./useTauriOverlay"; + +/** + * One-shot Tauri overlay init: + * - Force-resets click-through to false on startup. The locked state is + * intentionally non-persistent — booting into a locked window with a + * broken hotkey would be unrecoverable. + * - Mirrors `overlayClickThrough` into the native window via + * setIgnoreCursorEvents. + * - Adds the `overlay-window` class to for the rounded-corner clip + * (CSS rule lives in styles.css). + * + * No-op in browser context. + */ +export function useOverlayBootstrap(overlayClickThrough: Ref): void { + const overlay = useTauriOverlay(); + if (!overlay.isTauri) return; + + overlayClickThrough.value = false; + void overlay.setClickThrough(false); + watch(overlayClickThrough, (locked) => void overlay.setClickThrough(locked)); + document.documentElement.classList.add("overlay-window"); +} diff --git a/apps/client/src/features/overlay/composables/useOverlaySync.ts b/apps/client/src/features/overlay/composables/useOverlaySync.ts new file mode 100644 index 0000000..070a080 --- /dev/null +++ b/apps/client/src/features/overlay/composables/useOverlaySync.ts @@ -0,0 +1,59 @@ +import { useOverlayStore } from "../store"; +import { useTauriOverlay } from "./useTauriOverlay"; +import { opacityPercentBinding } from "../lib/opacity"; + +export interface UseOverlaySync { + opacityPercent: WritableComputedRef; + mapOpacityPercent: WritableComputedRef; + mapOpacityDisabled: ComputedRef; +} + +/** + * Mirrors overlay-related settings into the Tauri window in real time + * (always-on-top, native opacity, webview zoom) and drives the + * `--map-bg-alpha` CSS variable that controls the Leaflet container's + * background transparency. + * + * Returns slider-friendly integer percent bindings for the two opacity + * sliders — the store keeps them as 0–1 floats internally. + */ +export function useOverlaySync(): UseOverlaySync { + const overlay = useTauriOverlay(); + const { + alwaysOnTop: overlayAlwaysOnTop, + opacity: overlayOpacity, + mapOpacity: overlayMapOpacity, + zoom: overlayZoom, + } = storeToRefs(useOverlayStore()); + + // Overlay opacity is clamped to 30% min — a fully invisible window is + // unrecoverable. Map opacity goes all the way to 0 so the user can hide + // the surface entirely and see only the SVG + markers. + const opacityPercent = opacityPercentBinding(overlayOpacity, 30, 100); + const mapOpacityPercent = opacityPercentBinding(overlayMapOpacity, 0, 100); + + // Disabled at full overlay opacity — a transparent map background behind a + // fully opaque overlay would just look like a hole in the UI. + const mapOpacityDisabled = computed(() => overlayOpacity.value >= 1); + + // When overall opacity is 100% we force the map back to fully opaque + // regardless of the stored value, keeping the visual model consistent with + // the disabled-slider hint. + function applyMapBgAlpha(): void { + const effective = overlayOpacity.value < 1 ? overlayMapOpacity.value : 1; + document.documentElement.style.setProperty("--map-bg-alpha", String(effective)); + } + applyMapBgAlpha(); + watch([overlayOpacity, overlayMapOpacity], applyMapBgAlpha); + + if (overlay.isTauri) { + void overlay.setAlwaysOnTop(overlayAlwaysOnTop.value); + void overlay.setOpacity(overlayOpacity.value); + void overlay.setZoom(Number(overlayZoom.value) / 100); + watch(overlayAlwaysOnTop, (v) => void overlay.setAlwaysOnTop(v)); + watch(overlayOpacity, (v) => void overlay.setOpacity(v)); + watch(overlayZoom, (v) => void overlay.setZoom(Number(v) / 100)); + } + + return { opacityPercent, mapOpacityPercent, mapOpacityDisabled }; +} diff --git a/apps/client/src/composables/useTauriOverlay.ts b/apps/client/src/features/overlay/composables/useTauriOverlay.ts similarity index 100% rename from apps/client/src/composables/useTauriOverlay.ts rename to apps/client/src/features/overlay/composables/useTauriOverlay.ts diff --git a/apps/client/src/features/overlay/composables/useTrayIcon.ts b/apps/client/src/features/overlay/composables/useTrayIcon.ts new file mode 100644 index 0000000..f367430 --- /dev/null +++ b/apps/client/src/features/overlay/composables/useTrayIcon.ts @@ -0,0 +1,93 @@ +import type { TrayIcon } from "@tauri-apps/api/tray"; +import type { Menu } from "@tauri-apps/api/menu"; +import { useI18nStore } from "@/features/i18n/store"; + +type TrayHandle = Awaited>; +type TrayMenu = Awaited>; + +const TRAY_ID = "tarkov-checker-tray"; + +/** + * Owns the Tauri system-tray icon lifecycle: creates the icon on mount, + * rebuilds the menu when the UI language changes, and removes the icon on + * unmount. No-op in browser context. + */ +export function useTrayIcon(isTauri: boolean, overlayClickThrough: Ref): void { + if (!isTauri) return; + + const { t } = useI18n(); + const { apiLang } = storeToRefs(useI18nStore()); + let trayRef: TrayHandle | null = null; + + async function buildTrayMenu(): Promise { + const [{ Menu }, { getCurrentWindow }] = await Promise.all([ + import("@tauri-apps/api/menu"), + import("@tauri-apps/api/window"), + ]); + return Menu.new({ + items: [ + { + id: "toggle-lock", + text: t("tray.toggleLock"), + action: () => { + overlayClickThrough.value = !overlayClickThrough.value; + }, + }, + { + id: "show", + text: t("tray.showWindow"), + action: async () => { + const win = getCurrentWindow(); + await win.show(); + await win.setFocus(); + }, + }, + { item: "Separator" }, + { + id: "quit", + text: t("tray.quit"), + action: async () => { + await getCurrentWindow().close(); + }, + }, + ], + }); + } + + onMounted(async () => { + try { + const [{ TrayIcon }, { defaultWindowIcon }] = await Promise.all([ + import("@tauri-apps/api/tray"), + import("@tauri-apps/api/app"), + ]); + const icon = await defaultWindowIcon(); + trayRef = await TrayIcon.new({ + id: TRAY_ID, + icon: icon ?? undefined, + menu: await buildTrayMenu(), + tooltip: t("tray.tooltip"), + showMenuOnLeftClick: true, + }); + } catch (err) { + // eslint-disable-next-line no-console + console.error("[tray] creation failed:", err); + } + }); + + watch(apiLang, async () => { + if (!trayRef) return; + try { + await trayRef.setMenu(await buildTrayMenu()); + await trayRef.setTooltip(t("tray.tooltip")); + } catch (err) { + // eslint-disable-next-line no-console + console.error("[tray] i18n refresh failed:", err); + } + }); + + onBeforeUnmount(async () => { + const { TrayIcon } = await import("@tauri-apps/api/tray"); + await TrayIcon.removeById(TRAY_ID); + trayRef = null; + }); +} diff --git a/apps/client/src/features/overlay/lib/opacity.ts b/apps/client/src/features/overlay/lib/opacity.ts new file mode 100644 index 0000000..60c994e --- /dev/null +++ b/apps/client/src/features/overlay/lib/opacity.ts @@ -0,0 +1,18 @@ +/** + * Slider-friendly binding for a 0–1 opacity store ref. Reads as an integer + * percent (rounded), writes back clamped to the given range and divided by + * 100. Used by both the full settings drawer and the right-click quick + * panel so the two stay in lockstep. + */ +export function opacityPercentBinding( + source: Ref, + min = 0, + max = 100, +): WritableComputedRef { + return computed({ + get: () => Math.round(source.value * 100), + set: (pct) => { + source.value = Math.max(min, Math.min(max, pct)) / 100; + }, + }); +} diff --git a/apps/client/src/features/overlay/store.ts b/apps/client/src/features/overlay/store.ts new file mode 100644 index 0000000..43c8b78 --- /dev/null +++ b/apps/client/src/features/overlay/store.ts @@ -0,0 +1,18 @@ +import { z } from "zod"; +import { persistedRef } from "@/shared/persisted-store"; + +const overlayZoomSchema = z.enum(["75", "100", "125", "150"]); +export type OverlayZoom = z.infer; + +export const useOverlayStore = defineStore("overlay", () => { + const alwaysOnTop = persistedRef("tc.overlay.alwaysOnTop", z.boolean(), false); + const opacity = persistedRef("tc.overlay.opacity", z.number().min(0.3).max(1), 1); + const mapOpacity = persistedRef("tc.overlay.mapOpacity", z.number().min(0).max(1), 1); + const zoom = persistedRef("tc.overlay.zoom", overlayZoomSchema, "100" as OverlayZoom); + + // Session-only: booting into a locked overlay with a broken hotkey would be + // unrecoverable, so the locked state is intentionally NOT persisted. + const clickThrough = ref(false); + + return { alwaysOnTop, opacity, mapOpacity, zoom, clickThrough }; +}); diff --git a/apps/client/src/features/server/api/ipc-contract.ts b/apps/client/src/features/server/api/ipc-contract.ts new file mode 100644 index 0000000..c8439a7 --- /dev/null +++ b/apps/client/src/features/server/api/ipc-contract.ts @@ -0,0 +1,29 @@ +import type { ServerConfigResponse, ServerConfigUpdate } from "@shared/config-api"; +import type { ExtractsCacheResponse } from "@shared/tarkov-api"; + +/** + * The full set of Tauri IPC commands the Rust side exposes (see + * `apps/desktop/src-tauri/src/commands.rs`). Adding a new command means + * adding one entry here — `callBackend` then enforces correct args and + * return type at every call site. + * + * Args of `undefined` mean the command takes no payload. The runtime call + * passes nothing for `args`, but TS still requires the key to be declared + * for the inferred-key narrowing to work. + */ +export interface IpcContract { + get_config: { + args: undefined; + result: ServerConfigResponse; + }; + update_config: { + args: { patch: ServerConfigUpdate }; + result: ServerConfigResponse; + }; + get_extracts: { + args: { lang: string; refresh?: boolean }; + result: ExtractsCacheResponse; + }; +} + +export type IpcCommand = keyof IpcContract; diff --git a/apps/client/src/features/server/api/server-config.ts b/apps/client/src/features/server/api/server-config.ts new file mode 100644 index 0000000..5aad2ef --- /dev/null +++ b/apps/client/src/features/server/api/server-config.ts @@ -0,0 +1,24 @@ +import { + serverConfigResponseSchema, + type ServerConfigResponse, + type ServerConfigUpdate, +} from "@shared/config-api"; +import { callBackend } from "./transport"; + +const parseConfig = (d: unknown): ServerConfigResponse => serverConfigResponseSchema.parse(d); + +export function fetchServerConfig(): Promise { + return callBackend({ + tauri: { cmd: "get_config" }, + http: { method: "GET", path: "/api/config" }, + parse: parseConfig, + }); +} + +export function putServerConfig(patch: ServerConfigUpdate): Promise { + return callBackend({ + tauri: { cmd: "update_config", args: { patch } }, + http: { method: "PUT", path: "/api/config", body: patch }, + parse: parseConfig, + }); +} diff --git a/apps/client/src/features/server/api/transport.ts b/apps/client/src/features/server/api/transport.ts new file mode 100644 index 0000000..4cdb1e6 --- /dev/null +++ b/apps/client/src/features/server/api/transport.ts @@ -0,0 +1,72 @@ +/** + * Single dispatch point for client→backend reads/writes. The same client code + * runs both inside the Tauri overlay and as a plain browser PWA: + * + * - In Tauri: invokes a named IPC command via `@tauri-apps/api/core`. + * - In a plain browser: calls the LAN Node server (apps/server) over HTTP at + * the port from VITE_SERVER_PORT (default 3000). + * + * The raw response is fed through `parse` (typically a zod schema's `.parse`) + * so callers get a validated, typed result either way. + */ + +import { apiBase } from "@/shared/config"; +import type { IpcContract } from "./ipc-contract"; + +const isTauri = "__TAURI_INTERNALS__" in window; + +export interface HttpCall { + method?: "GET" | "PUT" | "POST" | "DELETE"; + path: string; + body?: unknown; + query?: Record; +} + +/** + * A call to one IPC command. `cmd` is keyof IpcContract — opt-in narrowing + * gives a typo on the command name a compile-time error, and `args`/`parse` + * line up with the contract's declared shapes per command. + */ +export type BackendCall = { + tauri: { cmd: K } & (IpcContract[K]["args"] extends undefined + ? { args?: undefined } + : { args: IpcContract[K]["args"] }); + http: HttpCall; + parse: (data: unknown) => IpcContract[K]["result"]; +}; + +export async function callBackend( + call: BackendCall, +): Promise { + if (isTauri) { + // Lazy-import so the @tauri-apps/api chunk only loads when actually + // running inside Tauri. + const { invoke } = await import("@tauri-apps/api/core"); + const data = await invoke( + call.tauri.cmd, + call.tauri.args as Record | undefined, + ); + return call.parse(data); + } + return httpRequest(call.http, call.parse); +} + +async function httpRequest(http: HttpCall, parse: (data: unknown) => T): Promise { + const method = http.method ?? "GET"; + const qs = http.query ? "?" + new URLSearchParams(http.query).toString() : ""; + const init: RequestInit = { method }; + if (http.body !== undefined) { + init.headers = { "Content-Type": "application/json" }; + init.body = JSON.stringify(http.body); + } + const r = await fetch(`${apiBase()}${http.path}${qs}`, init); + if (!r.ok) { + const body: unknown = await r.json().catch(() => ({})); + const detail = + typeof body === "object" && body !== null && "error" in body ? body.error : body; + throw new Error( + `${method} ${http.path} failed: HTTP ${r.status} — ${JSON.stringify(detail)}`, + ); + } + return parse(await r.json()); +} diff --git a/apps/client/src/features/server/composables/useExtractsCacheControl.ts b/apps/client/src/features/server/composables/useExtractsCacheControl.ts new file mode 100644 index 0000000..1949187 --- /dev/null +++ b/apps/client/src/features/server/composables/useExtractsCacheControl.ts @@ -0,0 +1,95 @@ +import { fetchAllExtracts, getCacheTimestamp, refreshExtracts } from "@/features/map/api/tarkov-dev"; +import { useI18nStore } from "@/features/i18n/store"; +import { TARKOV_MAPS, type TarkovMapCode } from "@shared/maps"; + +export interface UseExtractsCacheControl { + mapLabelFor: (code: TarkovMapCode) => string; + cacheRefreshing: Ref; + cacheError: Ref; + cacheRelativeAge: ComputedRef; + refreshCache: () => Promise; +} + +/** + * Owns two related concerns that share the same /api/extracts fetch: + * + * - Localized display names for the map dropdown. Falls back to + * TARKOV_MAPS.displayName when the server is unreachable. + * - Cache freshness indicator + manual refresh button. + * + * Bundled together because the underlying API layer dedups concurrent calls + * — splitting these into two composables would double-fetch on mount. + */ +export function useExtractsCacheControl(): UseExtractsCacheControl { + const { apiLang } = storeToRefs(useI18nStore()); + const { t } = useI18n(); + + const localizedMapNames = ref>>({}); + const cacheTimestamp = ref(null); + const cacheRefreshing = ref(false); + const cacheError = ref(null); + + function refreshCacheTimestamp(): void { + cacheTimestamp.value = getCacheTimestamp(apiLang.value); + } + + async function loadMapNames(): Promise { + try { + const all = await fetchAllExtracts(apiLang.value); + const byNameId: Record = {}; + for (const entry of all) { + byNameId[entry.nameId.toLowerCase()] = entry.name; + } + localizedMapNames.value = byNameId; + } catch { + // Fall back silently to TARKOV_MAPS.displayName. + } finally { + refreshCacheTimestamp(); + } + } + + function mapLabelFor(code: TarkovMapCode): string { + return localizedMapNames.value[code.toLowerCase()] ?? TARKOV_MAPS[code].displayName; + } + + async function refreshCache(): Promise { + cacheRefreshing.value = true; + cacheError.value = null; + try { + await refreshExtracts(apiLang.value); + refreshCacheTimestamp(); + } catch (err) { + cacheError.value = err instanceof Error ? err.message : String(err); + } finally { + cacheRefreshing.value = false; + } + } + + const cacheRelativeAge = computed(() => { + const ts = cacheTimestamp.value; + if (!ts) return t("cache.never"); + const ms = Date.now() - ts; + const minutes = Math.round(ms / 60_000); + if (minutes < 1) return "<1m"; + if (minutes < 60) return `${minutes}m`; + const hours = Math.round(minutes / 60); + if (hours < 24) return `${hours}h`; + const days = Math.round(hours / 24); + return `${days}d`; + }); + + void loadMapNames(); + refreshCacheTimestamp(); + watch(apiLang, () => { + void loadMapNames(); + refreshCacheTimestamp(); + }); + + return { + mapLabelFor, + cacheRefreshing, + cacheError, + cacheRelativeAge, + refreshCache, + }; +} diff --git a/apps/client/src/features/server/composables/useServerEvents.ts b/apps/client/src/features/server/composables/useServerEvents.ts new file mode 100644 index 0000000..be704d9 --- /dev/null +++ b/apps/client/src/features/server/composables/useServerEvents.ts @@ -0,0 +1,58 @@ +import type { ServerMessage, ServerMessageType } from "@shared/ws-messages"; + +type EventMap = { + [K in ServerMessageType]: Extract; +}; + +type Handler = (msg: EventMap[K]) => void; + +/** + * Module-level subscriber registry. Lives outside Vue's lifecycle so it + * survives component remounts (e.g. when a route swaps the map view in and + * out — subscribers re-attach via onScopeDispose, but the bus itself stays). + */ +const handlers = new Map>>(); + +/** + * Fan-out: invoked by the transport layer (useServerTransport / useWebSocket) + * each time a parsed server message arrives. Synchronous — handlers run in + * registration order on the dispatching tick. + */ +export function dispatchServerEvent(msg: EventMap[K]): void { + const set = handlers.get(msg.type); + if (!set) return; + for (const h of set) (h as Handler)(msg); +} + +/** + * Manual subscription. Returns an `off()` function. Prefer `useServerEvent` + * inside components — this is for non-component contexts (e.g. stores). + */ +export function onServerEvent( + type: K, + handler: Handler, +): () => void { + let set = handlers.get(type); + if (!set) { + set = new Set(); + handlers.set(type, set); + } + const erased = handler as Handler; + set.add(erased); + return () => { + set!.delete(erased); + }; +} + +/** + * Lifecycle-aware subscription. Auto-unsubscribes when the surrounding + * effect scope disposes (component unmount, EffectScope.stop, etc.). + * Subscription is active immediately — no onMounted delay. + */ +export function useServerEvent( + type: K, + handler: Handler, +): void { + const off = onServerEvent(type, handler); + onScopeDispose(off); +} diff --git a/apps/client/src/features/server/composables/useServerPaths.ts b/apps/client/src/features/server/composables/useServerPaths.ts new file mode 100644 index 0000000..97cf77e --- /dev/null +++ b/apps/client/src/features/server/composables/useServerPaths.ts @@ -0,0 +1,127 @@ +import type { ServerConfigResponse } from "@shared/config-api"; +import { fetchServerConfig, putServerConfig } from "@/features/server/api/server-config"; + +export type PathSlot = "gameDir" | "screenshotsDir" | "logsDir"; + +export interface UseServerPaths { + serverConfig: Ref; + gameDirInput: Ref; + screenshotsDirInput: Ref; + pathsLoading: Ref; + pathsSaving: Ref; + pathsError: Ref; + pathsJustSaved: Ref; + gameDirLocked: ComputedRef; + screenshotsDirLocked: ComputedRef; + canSavePaths: ComputedRef; + loadPaths: () => Promise; + savePaths: () => Promise; + statusIconClass: (slot: PathSlot) => string; +} + +/** + * Owns the Tarkov-paths CRUD UX: load + save the server config, track + * dirty/locked/can-save state, and translate folder-existence info into + * status-icon classes for the IconField indicator. + * + * `canEditPaths` is passed in because the rule (mobile-disabled, Tauri-always-on) + * is owned by the panel — the composable just respects it. + */ +export function useServerPaths(canEditPaths: Ref): UseServerPaths { + const serverConfig = ref(null); + const gameDirInput = ref(""); + const screenshotsDirInput = ref(""); + const pathsLoading = ref(false); + const pathsSaving = ref(false); + const pathsError = ref(null); + const pathsJustSaved = ref(false); + + const gameDirLocked = computed(() => serverConfig.value?.gameDir.source === "env"); + const screenshotsDirLocked = computed( + () => serverConfig.value?.screenshotsDir.source === "env", + ); + const gameDirDirty = computed( + () => (serverConfig.value?.gameDir.value ?? "") !== gameDirInput.value, + ); + const screenshotsDirDirty = computed( + () => (serverConfig.value?.screenshotsDir.value ?? "") !== screenshotsDirInput.value, + ); + const canSavePaths = computed( + () => + canEditPaths.value && + !pathsSaving.value && + (gameDirDirty.value || screenshotsDirDirty.value), + ); + + function syncInputsFromConfig(cfg: ServerConfigResponse): void { + gameDirInput.value = cfg.gameDir.value ?? ""; + screenshotsDirInput.value = cfg.screenshotsDir.value ?? ""; + } + + async function loadPaths(): Promise { + pathsLoading.value = true; + pathsError.value = null; + try { + const cfg = await fetchServerConfig(); + serverConfig.value = cfg; + syncInputsFromConfig(cfg); + } catch (err) { + pathsError.value = err instanceof Error ? err.message : String(err); + } finally { + pathsLoading.value = false; + } + } + + async function savePaths(): Promise { + if (!canSavePaths.value) return; + pathsSaving.value = true; + pathsError.value = null; + try { + const patch = { + ...(gameDirLocked.value ? {} : { gameDir: gameDirInput.value.trim() || null }), + ...(screenshotsDirLocked.value + ? {} + : { screenshotsDir: screenshotsDirInput.value.trim() || null }), + }; + const cfg = await putServerConfig(patch); + serverConfig.value = cfg; + syncInputsFromConfig(cfg); + pathsJustSaved.value = true; + setTimeout(() => { + pathsJustSaved.value = false; + }, 2_000); + } catch (err) { + pathsError.value = err instanceof Error ? err.message : String(err); + } finally { + pathsSaving.value = false; + } + } + + function statusIconClass(slot: PathSlot): string { + const item = serverConfig.value?.[slot]; + if (!item) return "pi pi-circle text-surface-500"; + if (item.exists) return "pi pi-check-circle text-green-500"; + if (item.value) return "pi pi-exclamation-circle text-amber-400"; + return "pi pi-times-circle text-red-500"; + } + + onMounted(() => { + void loadPaths(); + }); + + return { + serverConfig, + gameDirInput, + screenshotsDirInput, + pathsLoading, + pathsSaving, + pathsError, + pathsJustSaved, + gameDirLocked, + screenshotsDirLocked, + canSavePaths, + loadPaths, + savePaths, + statusIconClass, + }; +} diff --git a/apps/client/src/composables/useServerTransport.ts b/apps/client/src/features/server/composables/useServerTransport.ts similarity index 51% rename from apps/client/src/composables/useServerTransport.ts rename to apps/client/src/features/server/composables/useServerTransport.ts index 7b28128..73bbb34 100644 --- a/apps/client/src/composables/useServerTransport.ts +++ b/apps/client/src/features/server/composables/useServerTransport.ts @@ -1,31 +1,30 @@ -import { ref, onMounted, onBeforeUnmount, type Ref } from "vue"; -import type { ServerMessage } from "@shared/ws-messages"; +import type { PositionMessage } from "@shared/ws-messages"; import { useWebSocket } from "./useWebSocket"; +import { dispatchServerEvent } from "./useServerEvents"; export type TransportStatus = "connecting" | "open" | "closed"; export interface UseServerTransport { status: Ref; - lastMessage: Ref; } const isTauri = "__TAURI_INTERNALS__" in window; -interface PositionPayload { - t: number; - x: number; - y: number; - z: number; - yaw: number | null; -} +// The Rust side emits the position payload without the discriminator — the +// event channel name ("position") already carries that information, so the +// payload shape is PositionMessage minus the literal `type` field. +type PositionPayload = Omit; /** - * Single entry point for "server-pushed" messages. + * Single entry point for "server-pushed" messages — mount once at the app + * root for side effects. Parsed messages are fanned out via the + * `useServerEvents` bus; subscribers attach with `useServerEvent(type, ...)` + * without prop-drilling through the component tree. * * - In Tauri: subscribes to the `position` event emitted by the Rust - * screenshot watcher. Transport status is hardcoded to `"open"` once - * listeners are attached — the same process owns both sides, so there - * isn't any meaningful "down" state to surface. + * screenshot watcher. Status is hardcoded to `"open"` once listeners are + * attached — the same process owns both sides, so there isn't any + * meaningful "down" state to surface. * - In a plain browser (PWA on phone): falls back to the LAN WebSocket * server (Node `apps/server`) on port 3000. */ @@ -33,21 +32,12 @@ export function useServerTransport(wsUrl: string): UseServerTransport { if (!isTauri) return useWebSocket(wsUrl); const status = ref("connecting"); - const lastMessage = ref(null); let unlisten: (() => void) | null = null; onMounted(async () => { const { listen } = await import("@tauri-apps/api/event"); const handle = await listen("position", (event) => { - const p = event.payload; - lastMessage.value = { - type: "position", - t: p.t, - x: p.x, - y: p.y, - z: p.z, - yaw: p.yaw, - }; + dispatchServerEvent({ type: "position", ...event.payload }); }); unlisten = handle; status.value = "open"; @@ -59,5 +49,5 @@ export function useServerTransport(wsUrl: string): UseServerTransport { status.value = "closed"; }); - return { status, lastMessage }; + return { status }; } diff --git a/apps/client/src/features/server/composables/useTransportStatus.ts b/apps/client/src/features/server/composables/useTransportStatus.ts new file mode 100644 index 0000000..6f067cf --- /dev/null +++ b/apps/client/src/features/server/composables/useTransportStatus.ts @@ -0,0 +1,16 @@ +import type { TransportStatus } from "./useServerTransport"; + +const TransportStatusKey: InjectionKey> = Symbol("TransportStatus"); + +/** + * App-root provides the live transport status; route views read it via + * `useTransportStatus()`. Keeps the transport singleton mounted in App.vue + * while leaving the status pill free to live in any route's chrome. + */ +export function provideTransportStatus(status: Ref): void { + provide(TransportStatusKey, status); +} + +export function useTransportStatus(): Ref { + return inject(TransportStatusKey, ref("connecting")); +} diff --git a/apps/client/src/composables/useWebSocket.ts b/apps/client/src/features/server/composables/useWebSocket.ts similarity index 77% rename from apps/client/src/composables/useWebSocket.ts rename to apps/client/src/features/server/composables/useWebSocket.ts index 7096eda..4e49ae2 100644 --- a/apps/client/src/composables/useWebSocket.ts +++ b/apps/client/src/features/server/composables/useWebSocket.ts @@ -1,16 +1,14 @@ -import { ref, onMounted, onBeforeUnmount, type Ref } from "vue"; -import { serverMessage, type ServerMessage } from "@shared/ws-messages"; +import { serverMessage } from "@shared/ws-messages"; +import { dispatchServerEvent } from "./useServerEvents"; export type WsStatus = "connecting" | "open" | "closed"; interface UseWebSocketResult { status: Ref; - lastMessage: Ref; } export function useWebSocket(url: string): UseWebSocketResult { const status = ref("connecting"); - const lastMessage = ref(null); let socket: WebSocket | null = null; function connect(): void { @@ -27,7 +25,7 @@ export function useWebSocket(url: string): UseWebSocketResult { const parsed: unknown = JSON.parse(event.data); const result = serverMessage.safeParse(parsed); if (result.success) { - lastMessage.value = result.data; + dispatchServerEvent(result.data); } } catch { // Ignore malformed payloads; the server is the only sender. @@ -50,5 +48,5 @@ export function useWebSocket(url: string): UseWebSocketResult { socket = null; }); - return { status, lastMessage }; + return { status }; } diff --git a/apps/client/src/features/settings/SettingsPanel.vue b/apps/client/src/features/settings/SettingsPanel.vue new file mode 100644 index 0000000..e8fc954 --- /dev/null +++ b/apps/client/src/features/settings/SettingsPanel.vue @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + {{ t("systemSection") }} + + + + + + + + + + diff --git a/apps/client/src/features/settings/sections/CacheSection.vue b/apps/client/src/features/settings/sections/CacheSection.vue new file mode 100644 index 0000000..83422d1 --- /dev/null +++ b/apps/client/src/features/settings/sections/CacheSection.vue @@ -0,0 +1,30 @@ + + + + + + + {{ t("cache.lastUpdated") }}: {{ cacheRelativeAge }} + + + + + {{ cacheError }} + + {{ t("cache.hint") }} + + diff --git a/apps/client/src/features/settings/sections/ExtractsSection.vue b/apps/client/src/features/settings/sections/ExtractsSection.vue new file mode 100644 index 0000000..0708592 --- /dev/null +++ b/apps/client/src/features/settings/sections/ExtractsSection.vue @@ -0,0 +1,80 @@ + + + + + + + + + {{ t(`factions.${opt.value}`) }} + + + + + {{ t("labels") }} + + {{ t("labelHint") }} + + + + {{ t("labelSize") }} + + + + diff --git a/apps/client/src/features/settings/sections/HotkeysSection.vue b/apps/client/src/features/settings/sections/HotkeysSection.vue new file mode 100644 index 0000000..d9cb220 --- /dev/null +++ b/apps/client/src/features/settings/sections/HotkeysSection.vue @@ -0,0 +1,21 @@ + + + + + + + + + + + {{ t("hotkeys.lockHint") }} + + + diff --git a/apps/client/src/features/settings/sections/LanguageSection.vue b/apps/client/src/features/settings/sections/LanguageSection.vue new file mode 100644 index 0000000..9f8c0a5 --- /dev/null +++ b/apps/client/src/features/settings/sections/LanguageSection.vue @@ -0,0 +1,25 @@ + + + + + + + diff --git a/apps/client/src/features/settings/sections/MapSection.vue b/apps/client/src/features/settings/sections/MapSection.vue new file mode 100644 index 0000000..08df0b3 --- /dev/null +++ b/apps/client/src/features/settings/sections/MapSection.vue @@ -0,0 +1,25 @@ + + + + + + + diff --git a/apps/client/src/features/settings/sections/OverlaySection.vue b/apps/client/src/features/settings/sections/OverlaySection.vue new file mode 100644 index 0000000..3906042 --- /dev/null +++ b/apps/client/src/features/settings/sections/OverlaySection.vue @@ -0,0 +1,78 @@ + + + + + + + {{ t("overlay.alwaysOnTop") }} + + + + + + {{ t("overlay.opacity") }} + {{ opacityPercent }}% + + + + + + + + {{ t("overlay.mapOpacity") }} + + {{ mapOpacityPercent }}% + + + + {{ t("overlay.mapOpacityHint") }} + + + + + {{ t("overlay.zoom") }} + + + + + diff --git a/apps/client/src/features/settings/sections/PathsSection.vue b/apps/client/src/features/settings/sections/PathsSection.vue new file mode 100644 index 0000000..128c605 --- /dev/null +++ b/apps/client/src/features/settings/sections/PathsSection.vue @@ -0,0 +1,111 @@ + + + + + + {{ pathsError }} + + … + + + + + {{ t("paths.gameDir") }} + + + + + + + {{ t("paths.logsDir") }}: {{ serverConfig.logsDir.value ?? "—" }} + + + + + + {{ t("paths.screenshotsDir") }} + + + + + + + + + + {{ t("paths.saved") }} + + + + + + {{ t("paths.mobileHint") }} + + + + diff --git a/apps/client/src/features/settings/sections/PlayerSection.vue b/apps/client/src/features/settings/sections/PlayerSection.vue new file mode 100644 index 0000000..906134c --- /dev/null +++ b/apps/client/src/features/settings/sections/PlayerSection.vue @@ -0,0 +1,29 @@ + + + + + {{ t("playerFollow") }} + + {{ t("playerFollowHint") }} + + diff --git a/apps/client/src/i18n.ts b/apps/client/src/i18n.ts deleted file mode 100644 index 50a6246..0000000 --- a/apps/client/src/i18n.ts +++ /dev/null @@ -1,267 +0,0 @@ -import { computed, type ComputedRef } from "vue"; -import { storeToRefs } from "pinia"; -import { useSettingsStore, type ApiLang, type ExtractFactionFilter } from "./stores/settings"; - -interface UiBundle { - readonly language: string; - readonly map: string; - readonly extracts: string; - readonly on: string; - readonly off: string; - readonly labels: string; - readonly labelHover: string; - readonly labelAlways: string; - readonly labelHint: string; - readonly labelSize: string; - readonly labelSizes: Readonly>; - readonly player: string; - readonly playerFollow: string; - readonly playerFollowHint: string; - readonly playerFollowOptions: Readonly>; - readonly settings: string; - readonly systemSection: string; - readonly floor: string; - readonly factions: Readonly>; - readonly paths: { - readonly heading: string; - readonly gameDir: string; - readonly screenshotsDir: string; - readonly logsDir: string; - readonly save: string; - readonly saved: string; - readonly mobileHint: string; - readonly source: Readonly>; - readonly missingTooltip: string; - readonly placeholderGameDir: string; - readonly placeholderScreenshotsDir: string; - }; - readonly cache: { - readonly heading: string; - readonly lastUpdated: string; - readonly never: string; - readonly refresh: string; - readonly refreshing: string; - readonly hint: string; - }; - readonly overlay: { - readonly heading: string; - readonly alwaysOnTop: string; - readonly clickThrough: string; - readonly clickThroughWarning: string; - readonly opacity: string; - readonly mapOpacity: string; - readonly mapOpacityHint: string; - readonly zoom: string; - readonly quickMenuTitle: string; - }; - readonly hotkeys: { - readonly heading: string; - readonly lock: string; - readonly lockHint: string; - readonly zoomIn: string; - readonly zoomOut: string; - readonly floorUp: string; - readonly floorDown: string; - readonly record: string; - readonly recording: string; - readonly recordingPrompt: string; - readonly invalid: string; - readonly conflict: string; - }; - readonly closeConfirm: { - readonly title: string; - readonly message: string; - readonly accept: string; - readonly reject: string; - }; - readonly tray: { - readonly toggleLock: string; - readonly showWindow: string; - readonly quit: string; - readonly tooltip: string; - }; -} - -const BUNDLES: Readonly> = { - en: { - language: "Language", - map: "Map", - extracts: "Extracts", - on: "On", - off: "Off", - labels: "Labels", - labelHover: "On hover", - labelAlways: "Always", - labelHint: "Always shows extract names; switch to On hover for a minimal view.", - labelSize: "Label size", - labelSizes: { sm: "S", md: "M", lg: "L" }, - player: "Player", - playerFollow: "Auto-follow", - playerFollowHint: "Recenter and zoom the map on every new position update.", - playerFollowOptions: { off: "Off", sm: "S", md: "M", lg: "L" }, - settings: "Settings", - systemSection: "System", - floor: "Floor", - factions: { - pmc: "PMC", - scav: "Scav", - shared: "Shared", - }, - paths: { - heading: "Tarkov paths", - gameDir: "Game folder", - screenshotsDir: "Screenshots folder", - logsDir: "Logs", - save: "Save", - saved: "Saved", - mobileHint: "Open this page on the machine running Tarkov to configure paths.", - source: { - env: "from .env", - manual: "manual", - detected: "auto-detected", - missing: "not found", - }, - missingTooltip: "Folder doesn't exist on disk yet.", - placeholderGameDir: "D:\\EFT", - placeholderScreenshotsDir: "Documents\\Escape from Tarkov\\Screenshots", - }, - cache: { - heading: "Data cache", - lastUpdated: "Last updated", - never: "Never", - refresh: "Refresh", - refreshing: "Refreshing…", - hint: "Extract coordinates are cached locally. tarkov.dev may be blocked on some networks — fetch once, use offline.", - }, - overlay: { - heading: "Overlay", - alwaysOnTop: "Always on top", - clickThrough: "Click-through", - clickThroughWarning: "Once enabled, you can't click the window. Restart the app to re-enable interaction.", - opacity: "Opacity", - mapOpacity: "Map opacity", - mapOpacityHint: "Available when overall opacity is below 100%.", - zoom: "Zoom", - quickMenuTitle: "Transparency", - }, - hotkeys: { - heading: "Hotkeys", - lock: "Toggle lock", - lockHint: "Pressed globally to toggle click-through. F1–F24 work without a modifier; everything else needs Ctrl/Alt/Shift.", - zoomIn: "Zoom in", - zoomOut: "Zoom out", - floorUp: "Next floor", - floorDown: "Previous floor", - record: "Change", - recording: "Recording…", - recordingPrompt: "Press the new combination (Esc to cancel)", - invalid: "Need a modifier (Ctrl/Alt/Shift) — or a bare F-key.", - conflict: "This combination is taken by another app. Try another.", - }, - closeConfirm: { - title: "Close the app?", - message: "The window will be closed.", - accept: "Close", - reject: "Cancel", - }, - tray: { - toggleLock: "Toggle lock", - showWindow: "Show window", - quit: "Quit", - tooltip: "tarkov-checker", - }, - }, - ru: { - language: "Язык", - map: "Карта", - extracts: "Выходы", - on: "Вкл", - off: "Выкл", - labels: "Подписи", - labelHover: "При наведении", - labelAlways: "Всегда", - labelHint: "Подписи видны всегда; переключи на «При наведении», если хочется меньше шума.", - labelSize: "Размер подписей", - labelSizes: { sm: "S", md: "M", lg: "L" }, - player: "Игрок", - playerFollow: "Авто-следование", - playerFollowHint: "Центрируем и приближаем карту при каждом новом обновлении позиции.", - playerFollowOptions: { off: "Выкл", sm: "S", md: "M", lg: "L" }, - settings: "Настройки", - systemSection: "Системные", - floor: "Уровень", - factions: { - pmc: "ЧВК", - scav: "Дикие", - shared: "Общие", - }, - paths: { - heading: "Пути Tarkov", - gameDir: "Папка с игрой", - screenshotsDir: "Папка скриншотов", - logsDir: "Логи", - save: "Сохранить", - saved: "Сохранено", - mobileHint: "Открой эту страницу на машине с Tarkov, чтобы настроить пути.", - source: { - env: "из .env", - manual: "вручную", - detected: "найдено автоматически", - missing: "не найдено", - }, - missingTooltip: "Папка пока не существует.", - placeholderGameDir: "D:\\EFT", - placeholderScreenshotsDir: "Documents\\Escape from Tarkov\\Screenshots", - }, - cache: { - heading: "Кеш данных", - lastUpdated: "Обновлено", - never: "Никогда", - refresh: "Обновить", - refreshing: "Обновляю…", - hint: "Координаты выходов кешируются локально. tarkov.dev иногда недоступен с RU IP — обновили один раз и пользуемся.", - }, - overlay: { - heading: "Оверлей", - alwaysOnTop: "Поверх всех окон", - clickThrough: "Прозрачное для кликов", - clickThroughWarning: "После включения окно перестанет реагировать на мышь. Чтобы вернуть взаимодействие — перезапусти приложение.", - opacity: "Прозрачность", - mapOpacity: "Прозрачность карты", - mapOpacityHint: "Доступно когда общая прозрачность ниже 100%.", - zoom: "Масштаб", - quickMenuTitle: "Прозрачность", - }, - hotkeys: { - heading: "Хоткеи", - lock: "Переключить блокировку", - lockHint: "Глобально переключает click-through. F1–F24 работают без модификатора; всем остальным нужен Ctrl/Alt/Shift.", - zoomIn: "Приблизить", - zoomOut: "Отдалить", - floorUp: "Следующий этаж", - floorDown: "Предыдущий этаж", - record: "Изменить", - recording: "Запись…", - recordingPrompt: "Нажми новое сочетание (Esc — отмена)", - invalid: "Нужен модификатор (Ctrl/Alt/Shift) — или F-клавиша без него.", - conflict: "Это сочетание уже занято другим приложением. Попробуй другое.", - }, - closeConfirm: { - title: "Закрыть приложение?", - message: "Окно будет закрыто.", - accept: "Закрыть", - reject: "Отмена", - }, - tray: { - toggleLock: "Переключить блокировку", - showWindow: "Показать окно", - quit: "Выход", - tooltip: "tarkov-checker", - }, - }, -}; - -export function useUiText(): ComputedRef { - const { apiLang } = storeToRefs(useSettingsStore()); - return computed(() => BUNDLES[apiLang.value]); -} diff --git a/apps/client/src/main.ts b/apps/client/src/main.ts index efe01fe..6f31465 100644 --- a/apps/client/src/main.ts +++ b/apps/client/src/main.ts @@ -1,8 +1,8 @@ -import { createApp } from "vue"; -import { createPinia } from "pinia"; import PrimeVue from "primevue/config"; import ConfirmationService from "primevue/confirmationservice"; import App from "./App.vue"; +import { router } from "@/app/router"; +import { i18n } from "@/features/i18n"; import { TarkovPreset } from "./theme"; import "./styles.css"; @@ -23,6 +23,8 @@ if (import.meta.env.DEV && typeof window !== "undefined") { const app = createApp(App); app.use(createPinia()); +app.use(router); +app.use(i18n); app.use(ConfirmationService); app.use(PrimeVue, { theme: { diff --git a/apps/client/src/pages/index.vue b/apps/client/src/pages/index.vue new file mode 100644 index 0000000..91a9f4d --- /dev/null +++ b/apps/client/src/pages/index.vue @@ -0,0 +1,63 @@ + + + + + + + + + + + diff --git a/apps/client/src/shared/config.ts b/apps/client/src/shared/config.ts new file mode 100644 index 0000000..3e4aafd --- /dev/null +++ b/apps/client/src/shared/config.ts @@ -0,0 +1,18 @@ +/** + * Where the browser/PWA client expects the LAN Node backend to live. + * + * Both HTTP and WebSocket URLs are derived from the same port — they always + * point at the same Fastify process, so a single env override is enough. + * Tauri builds don't actually hit either URL (the in-process Rust port owns + * IPC + position events), but the helpers stay safe to call regardless. + */ + +const SERVER_PORT = import.meta.env.VITE_SERVER_PORT || "3000"; + +export function apiBase(): string { + return `http://${window.location.hostname}:${SERVER_PORT}`; +} + +export function wsUrl(): string { + return `ws://${window.location.hostname}:${SERVER_PORT}/ws`; +} diff --git a/apps/client/src/shared/persisted-store.ts b/apps/client/src/shared/persisted-store.ts new file mode 100644 index 0000000..d19b868 --- /dev/null +++ b/apps/client/src/shared/persisted-store.ts @@ -0,0 +1,39 @@ +import type { z } from "zod"; + +/** + * Reactive ref backed by localStorage, validated against a zod schema on read. + * + * Each persisted field owns its own localStorage key (convention: "tc.."), + * so adding/removing a field never breaks unrelated state — there is no monolithic + * blob to version. Corrupt or schema-incompatible data falls back to `defaultValue` + * silently; the bad entry is left in place so a future schema can re-validate it + * (e.g. after a rollback). If you need to force a reset, change the key. + */ +export function persistedRef( + key: string, + schema: T, + defaultValue: z.infer, +): Ref> { + let initial = defaultValue; + if (typeof localStorage !== "undefined") { + const raw = localStorage.getItem(key); + if (raw !== null) { + try { + const parsed = schema.safeParse(JSON.parse(raw)); + if (parsed.success) initial = parsed.data as z.infer; + } catch { + // Malformed JSON — fall through to default. + } + } + } + const r = ref>(initial) as Ref>; + watch( + r, + (value) => { + if (typeof localStorage === "undefined") return; + localStorage.setItem(key, JSON.stringify(value)); + }, + { deep: true }, + ); + return r; +} diff --git a/apps/client/src/stores/raid.ts b/apps/client/src/stores/raid.ts deleted file mode 100644 index b928478..0000000 --- a/apps/client/src/stores/raid.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { defineStore } from "pinia"; -import { ref } from "vue"; -import { mapDisplayName, type TarkovMapCode } from "@shared/maps"; - -export const useRaidStore = defineStore("raid", () => { - const currentMapCode = ref(null); - const inRaid = ref(false); - - function setMap(code: TarkovMapCode): void { - currentMapCode.value = code; - } - - function clearMap(): void { - currentMapCode.value = null; - inRaid.value = false; - } - - function displayName(): string { - return currentMapCode.value ? mapDisplayName(currentMapCode.value) : "No raid"; - } - - return { currentMapCode, inRaid, setMap, clearMap, displayName }; -}); diff --git a/apps/client/src/stores/settings.ts b/apps/client/src/stores/settings.ts deleted file mode 100644 index 0e4d153..0000000 --- a/apps/client/src/stores/settings.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { defineStore } from "pinia"; -import { ref, watch } from "vue"; -import { z } from "zod"; -import { TARKOV_MAPS, type TarkovMapCode } from "@shared/maps"; - -const STORAGE_KEY = "tarkov-checker:settings:v3"; - -const apiLangSchema = z.enum(["en", "ru"]); -const extractFactionSchema = z.enum(["pmc", "scav", "shared"]); -const labelModeSchema = z.enum(["hover", "always"]); -const labelSizeSchema = z.enum(["sm", "md", "lg"]); -const playerFollowSchema = z.enum(["off", "sm", "md", "lg"]); -const overlayZoomSchema = z.enum(["75", "100", "125", "150"]); -const mapCodeSchema = z.string().refine((s): s is TarkovMapCode => s in TARKOV_MAPS); -// Validates a Tauri global-shortcut accelerator string. Either: -// * one+ modifier(s) followed by any non-empty main-key token, OR -// * a bare F1..F24 key (allowed without modifiers — rarely used for typing). -// Other reserved system shortcuts aren't policed here — if register() rejects -// the combo the watcher surfaces the error and the previous shortcut stays. -const lockHotkeySchema = z - .string() - .regex( - /^((CommandOrControl|Control|Ctrl|Alt|Shift|Meta|Super)\+)+[^+\s]+$|^F([1-9]|1[0-9]|2[0-4])$/, - ); - -const persistedSchema = z.object({ - apiLang: apiLangSchema, - extractFactions: z.array(extractFactionSchema), - extractLabelMode: labelModeSchema, - extractLabelSize: labelSizeSchema.default("md"), - playerFollow: playerFollowSchema.default("off"), - mapCode: mapCodeSchema.default("bigmap"), - overlayAlwaysOnTop: z.boolean().default(false), - overlayClickThrough: z.boolean().default(false), - overlayOpacity: z.number().min(0.3).max(1).default(1), - overlayMapOpacity: z.number().min(0).max(1).default(1), - overlayZoom: overlayZoomSchema.default("100"), - lockHotkey: lockHotkeySchema.default("CommandOrControl+Alt+L"), - zoomInHotkey: lockHotkeySchema.default("CommandOrControl+="), - zoomOutHotkey: lockHotkeySchema.default("CommandOrControl+-"), - floorUpHotkey: lockHotkeySchema.default("CommandOrControl+Shift+="), - floorDownHotkey: lockHotkeySchema.default("CommandOrControl+Shift+-"), -}); - -export type ApiLang = z.infer; -export type ExtractFactionFilter = z.infer; -export type ExtractLabelMode = z.infer; -export type ExtractLabelSize = z.infer; -export type PlayerFollow = z.infer; -export type OverlayZoom = z.infer; - -const DEFAULTS = { - apiLang: "en" as const, - extractFactions: ["pmc", "scav", "shared"] as const satisfies readonly ExtractFactionFilter[], - extractLabelMode: "always" as const, - extractLabelSize: "md" as const, - playerFollow: "off" as const, - mapCode: "bigmap" as const satisfies TarkovMapCode, - overlayAlwaysOnTop: false, - overlayClickThrough: false, - overlayOpacity: 1, - overlayMapOpacity: 1, - overlayZoom: "100" as const, - lockHotkey: "CommandOrControl+Alt+L", - zoomInHotkey: "CommandOrControl+=", - zoomOutHotkey: "CommandOrControl+-", - floorUpHotkey: "CommandOrControl+Shift+=", - floorDownHotkey: "CommandOrControl+Shift+-", -}; - -function defaultState(): z.infer { - return { - apiLang: DEFAULTS.apiLang, - extractFactions: [...DEFAULTS.extractFactions], - extractLabelMode: DEFAULTS.extractLabelMode, - extractLabelSize: DEFAULTS.extractLabelSize, - playerFollow: DEFAULTS.playerFollow, - mapCode: DEFAULTS.mapCode, - overlayAlwaysOnTop: DEFAULTS.overlayAlwaysOnTop, - overlayClickThrough: DEFAULTS.overlayClickThrough, - overlayOpacity: DEFAULTS.overlayOpacity, - overlayMapOpacity: DEFAULTS.overlayMapOpacity, - overlayZoom: DEFAULTS.overlayZoom, - lockHotkey: DEFAULTS.lockHotkey, - zoomInHotkey: DEFAULTS.zoomInHotkey, - zoomOutHotkey: DEFAULTS.zoomOutHotkey, - floorUpHotkey: DEFAULTS.floorUpHotkey, - floorDownHotkey: DEFAULTS.floorDownHotkey, - }; -} - -function loadFromStorage(): z.infer { - if (typeof localStorage === "undefined") return defaultState(); - const raw = localStorage.getItem(STORAGE_KEY); - if (!raw) return defaultState(); - try { - const parsed = persistedSchema.safeParse(JSON.parse(raw)); - if (parsed.success) return parsed.data; - } catch { - // fall through to defaults - } - return defaultState(); -} - -export const useSettingsStore = defineStore("settings", () => { - const initial = loadFromStorage(); - - const apiLang = ref(initial.apiLang); - const extractFactions = ref([...initial.extractFactions]); - const extractLabelMode = ref(initial.extractLabelMode); - const extractLabelSize = ref(initial.extractLabelSize); - const playerFollow = ref(initial.playerFollow); - const mapCode = ref(initial.mapCode); - const overlayAlwaysOnTop = ref(initial.overlayAlwaysOnTop); - const overlayClickThrough = ref(initial.overlayClickThrough); - const overlayOpacity = ref(initial.overlayOpacity); - const overlayMapOpacity = ref(initial.overlayMapOpacity); - const overlayZoom = ref(initial.overlayZoom); - const lockHotkey = ref(initial.lockHotkey); - const zoomInHotkey = ref(initial.zoomInHotkey); - const zoomOutHotkey = ref(initial.zoomOutHotkey); - const floorUpHotkey = ref(initial.floorUpHotkey); - const floorDownHotkey = ref(initial.floorDownHotkey); - - watch( - [ - apiLang, - extractFactions, - extractLabelMode, - extractLabelSize, - playerFollow, - mapCode, - overlayAlwaysOnTop, - overlayClickThrough, - overlayOpacity, - overlayMapOpacity, - overlayZoom, - lockHotkey, - zoomInHotkey, - zoomOutHotkey, - floorUpHotkey, - floorDownHotkey, - ], - () => { - if (typeof localStorage === "undefined") return; - localStorage.setItem( - STORAGE_KEY, - JSON.stringify({ - apiLang: apiLang.value, - extractFactions: extractFactions.value, - extractLabelMode: extractLabelMode.value, - extractLabelSize: extractLabelSize.value, - playerFollow: playerFollow.value, - mapCode: mapCode.value, - overlayAlwaysOnTop: overlayAlwaysOnTop.value, - overlayClickThrough: overlayClickThrough.value, - overlayOpacity: overlayOpacity.value, - overlayMapOpacity: overlayMapOpacity.value, - overlayZoom: overlayZoom.value, - lockHotkey: lockHotkey.value, - zoomInHotkey: zoomInHotkey.value, - zoomOutHotkey: zoomOutHotkey.value, - floorUpHotkey: floorUpHotkey.value, - floorDownHotkey: floorDownHotkey.value, - }), - ); - }, - { deep: true }, - ); - - function toggleFaction(faction: ExtractFactionFilter): void { - const idx = extractFactions.value.indexOf(faction); - if (idx === -1) { - extractFactions.value = [...extractFactions.value, faction]; - } else { - extractFactions.value = extractFactions.value.filter((f) => f !== faction); - } - } - - function isFactionVisible(faction: ExtractFactionFilter | null): boolean { - const f = faction ?? "shared"; - return extractFactions.value.includes(f); - } - - return { - apiLang, - extractFactions, - extractLabelMode, - extractLabelSize, - playerFollow, - mapCode, - overlayAlwaysOnTop, - overlayClickThrough, - overlayOpacity, - overlayMapOpacity, - overlayZoom, - lockHotkey, - zoomInHotkey, - zoomOutHotkey, - floorUpHotkey, - floorDownHotkey, - toggleFaction, - isFactionVisible, - }; -}); diff --git a/apps/client/vite.config.ts b/apps/client/vite.config.ts index 82b35e6..0e527e5 100644 --- a/apps/client/vite.config.ts +++ b/apps/client/vite.config.ts @@ -1,5 +1,10 @@ import { defineConfig, type PluginOption } from "vite"; +import VueRouter from "vue-router/vite"; import vue from "@vitejs/plugin-vue"; +import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"; +import AutoImport from "unplugin-auto-import/vite"; +import Components from "unplugin-vue-components/vite"; +import { PrimeVueResolver } from "@primevue/auto-import-resolver"; import tailwindcss from "@tailwindcss/vite"; import { VitePWA } from "vite-plugin-pwa"; import { visualizer } from "rollup-plugin-visualizer"; @@ -22,7 +27,47 @@ export default defineConfig(({ mode }) => ({ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false", }, plugins: [ + // Scans src/pages for *.vue files and generates the routes table at build + // time. MUST come before vue() so its SFC transform can see blocks + // and inject typed-route metadata. + VueRouter({ + routesFolder: "src/pages", + dts: "src/typed-router.d.ts", + }), vue(), + // Auto-import the Vue/Pinia/Router/VueUse composition APIs so SFCs don't + // need 5–10 lines of boilerplate imports each. Feature-owned composables + // and stores are deliberately NOT auto-imported — explicit imports keep + // cross-feature dependencies visible (and policeable by lint rules). + AutoImport({ + imports: ["vue", "vue-router", "pinia", "@vueuse/core", "vue-i18n"], + // Scope is intentionally narrow: framework idioms (ref/computed/watch, + // defineStore, useRouter, VueUse) cover dozens of call sites each, so + // dropping them as auto-imports pays back many lines per addition. We + // deliberately do NOT auto-import PrimeVue composables (useConfirm, + // useToast, ...) — @primevue/auto-import-resolver only handles + // components, and listing each composable by hand here trades a single + // explicit import for a runtime-crash risk if the entry is missed. + dts: "src/auto-imports.d.ts", + eslintrc: { enabled: true, filepath: "./.eslintrc-auto-import.json" }, + vueTemplate: true, + }), + // Auto-register PrimeVue components on demand. `dirs: []` disables the + // default scan of src/components — we want auto-registration only for + // the UI library, not our own components (which stay explicit). + Components({ + resolvers: [PrimeVueResolver()], + dts: "src/components.d.ts", + dirs: [], + }), + // Compiles JSON locale files to a lean runtime format (parses faster on + // app boot than re-parsing JSON) and exposes the global $t typing the + // Volar plugin needs. Locale files live next to the i18n entry. + VueI18nPlugin({ + include: [fileURLToPath(new URL("./src/features/i18n/locales/**", import.meta.url))], + strictMessage: false, + runtimeOnly: false, + }), tailwindcss(), VitePWA({ registerType: "autoUpdate", @@ -133,10 +178,7 @@ export default defineConfig(({ mode }) => ({ // — known upstream issue, harmless side-effect annotation. Drop only // this specific warning so real ones still surface. onwarn(warning, defaultHandler) { - if ( - warning.code === "INVALID_ANNOTATION" && - warning.id?.includes("@vueuse/core") - ) { + if (warning.code === "INVALID_ANNOTATION" && warning.id?.includes("@vueuse/core")) { return; } defaultHandler(warning); diff --git a/eslint.config.js b/eslint.config.js index 198766d..57fb138 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,3 +1,5 @@ +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; import js from "@eslint/js"; import tseslint from "typescript-eslint"; import vue from "eslint-plugin-vue"; @@ -5,6 +7,22 @@ import vueParser from "vue-eslint-parser"; import prettier from "eslint-config-prettier"; import globals from "globals"; +// Generated by unplugin-auto-import on dev/build. Contains the list of names +// (ref, computed, defineStore, ...) that the plugin injects as globals so +// they no longer need explicit imports. Missing on a fresh clone before the +// first vite run — empty globals object is a safe fallback. +let autoImportGlobals = {}; +try { + const filepath = fileURLToPath( + new URL("./apps/client/.eslintrc-auto-import.json", import.meta.url), + ); + const parsed = JSON.parse(readFileSync(filepath, "utf8")); + autoImportGlobals = parsed.globals ?? {}; +} catch { + // First run before vite has generated the file; auto-import names will be + // flagged as no-undef until then. Run `pnpm dev` once. +} + export default tseslint.config( { ignores: [ @@ -25,7 +43,7 @@ export default tseslint.config( languageOptions: { ecmaVersion: 2022, sourceType: "module", - globals: { ...globals.node, ...globals.browser }, + globals: { ...globals.node, ...globals.browser, ...autoImportGlobals }, }, rules: { "no-console": "warn", @@ -55,5 +73,26 @@ export default tseslint.config( "vue/multi-word-component-names": "off", }, }, + { + // Hybrid import style in the client: same-feature stays relative (./X, + // ../store), cross-feature MUST go through the @/ alias. The two-or-more + // levels-up pattern is the load-bearing signal — it's how you'd reach + // out of the current feature, and that should be explicit. + files: ["apps/client/src/**/*.{ts,vue}"], + rules: { + "no-restricted-imports": [ + "error", + { + patterns: [ + { + group: ["../../*", "../../**/*", "../../../*", "../../../**/*"], + message: + "Cross-feature/cross-layer import — use @/features//... or @/shared/... instead.", + }, + ], + }, + ], + }, + }, prettier, ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63c4ee2..f5d4e54 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,8 +63,8 @@ importers: specifier: ^1.9.4 version: 1.9.4 pinia: - specifier: ^2.3.0 - version: 2.3.1(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)) + specifier: ^3.0.4 + version: 3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)) primeicons: specifier: ^7.0.0 version: 7.0.0 @@ -77,13 +77,25 @@ importers: vue: specifier: ^3.5.13 version: 3.5.34(typescript@5.9.3) + vue-i18n: + specifier: ^11.4.4 + version: 11.4.4(vue@3.5.34(typescript@5.9.3)) + vue-router: + specifier: ^5.1.0 + version: 5.1.0(@vue/compiler-sfc@3.5.34)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)))(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))(vue@3.5.34(typescript@5.9.3)) zod: specifier: ^3.24.1 version: 3.25.76 devDependencies: + '@intlify/unplugin-vue-i18n': + specifier: ^11.2.3 + version: 11.2.3(@vue/compiler-dom@3.5.34)(eslint@9.39.4(jiti@2.7.0))(rollup@4.60.4)(typescript@5.9.3)(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))(vue-i18n@11.4.4(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3)) + '@primevue/auto-import-resolver': + specifier: ^4.5.5 + version: 4.5.5 '@tailwindcss/vite': - specifier: ^4.0.0 - version: 4.3.0(vite@5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0)) + specifier: ^4.3.0 + version: 4.3.0(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)) '@types/leaflet': specifier: ^1.9.15 version: 1.9.21 @@ -91,8 +103,8 @@ importers: specifier: ^22.10.2 version: 22.19.19 '@vitejs/plugin-vue': - specifier: ^5.2.1 - version: 5.2.4(vite@5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0))(vue@3.5.34(typescript@5.9.3)) + specifier: ^6.0.7 + version: 6.0.7(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))(vue@3.5.34(typescript@5.9.3)) '@vue/tsconfig': specifier: ^0.7.0 version: 0.7.0(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)) @@ -101,22 +113,28 @@ importers: version: 25.0.1 rollup-plugin-visualizer: specifier: ^7.0.1 - version: 7.0.1(rollup@4.60.4) + version: 7.0.1(rolldown@1.0.2)(rollup@4.60.4) tailwindcss: - specifier: ^4.0.0 + specifier: ^4.3.0 version: 4.3.0 typescript: specifier: ^5.7.2 version: 5.9.3 + unplugin-auto-import: + specifier: ^21.0.0 + version: 21.0.0(@vueuse/core@14.3.0(vue@3.5.34(typescript@5.9.3))) + unplugin-vue-components: + specifier: ^32.1.0 + version: 32.1.0(vue@3.5.34(typescript@5.9.3)) vite: - specifier: ^5.4.11 - version: 5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0) + specifier: ^8.0.14 + version: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0) vite-plugin-pwa: - specifier: ^0.21.1 - version: 0.21.2(vite@5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0))(workbox-build@7.4.1)(workbox-window@7.4.1) + specifier: ^1.3.0 + version: 1.3.0(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1) vitest: - specifier: ^2.1.8 - version: 2.1.9(@types/node@22.19.19)(jsdom@25.0.1)(lightningcss@1.32.0)(terser@5.48.0) + specifier: ^4.1.7 + version: 4.1.7(@types/node@22.19.19)(jsdom@25.0.1)(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)) vue-tsc: specifier: ^2.1.10 version: 2.2.12(typescript@5.9.3) @@ -204,6 +222,10 @@ packages: resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} + '@babel/generator@8.0.0-rc.6': + resolution: {integrity: sha512-6mIzgVK8DgEzvIapoQwhXTMnnkuE4STQmVv9H03i/tZ2ml8oev3TRvZJgTenK2Bsq0YWNtzOrFdTyNzCMFtjJQ==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -275,10 +297,18 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.0-rc.6': + resolution: {integrity: sha512-BCkFy+zN6kXQed3YOT7aJl93NfDSzQc3pBfsvTVPs9gU9X3V0aefEF5kwBT0E+mDWH9QgKaZstYUQN9VdQZT4g==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.0-rc.6': + resolution: {integrity: sha512-nVJ+1JcCgntv8d78rRo++o2wuODT0Irknx2BF8Np4Ft2CRgjLqIs4qzSZ8b66yGbBdMWGmZBO9WEZv1hhNiSpg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -296,6 +326,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.0-rc.6': + resolution: {integrity: sha512-rOS8IpdO7mQELkTPlCsTgPejO0bFuZdEDCGQJouYbYf9e1FLTym7Fei2pEjq8q7MWbX0ravcd7QQYKs1TxOuog==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -689,6 +724,10 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@8.0.0-rc.6': + resolution: {integrity: sha512-p7/ABylAYlexb31wtRdIfH9L9A0Z2T/9H6zAqzqndkY2PLkvNNc580wGhp/gGKN4Sp9sQvSkhc6Oga8/O+wTyw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} @@ -717,9 +756,18 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -729,9 +777,9 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -741,9 +789,9 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} cpu: [arm] os: [android] @@ -753,9 +801,9 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} cpu: [x64] os: [android] @@ -765,9 +813,9 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -777,9 +825,9 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -789,9 +837,9 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -801,9 +849,9 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -813,9 +861,9 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -825,9 +873,9 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -837,9 +885,9 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -849,9 +897,9 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -861,9 +909,9 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -873,9 +921,9 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -885,9 +933,9 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -897,9 +945,9 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -909,9 +957,9 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -921,15 +969,21 @@ packages: cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.28.0': resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -939,15 +993,21 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.28.0': resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -957,15 +1017,21 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/openharmony-arm64@0.28.0': resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -975,9 +1041,9 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -987,9 +1053,9 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -999,9 +1065,9 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1102,6 +1168,68 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@intlify/bundle-utils@11.2.3': + resolution: {integrity: sha512-9mrJyUJGPFJCIFGthvIFT58CknG701z9D0VRtLBtat3teo0fisP3Q6bo/t9YHnljBTEZ42hYm1ukn16LfLkRRg==} + engines: {node: '>= 22.13'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/core-base@11.4.4': + resolution: {integrity: sha512-w/vItlylrAmhebkIbVl5YY8XMCtj8Mb2g70ttxktMYuf5AuRahgEHL2iLgLIsZBIbTSgs4hkUo7ucCL0uTJvOg==} + engines: {node: '>= 22'} + + '@intlify/devtools-types@11.4.4': + resolution: {integrity: sha512-PcBLmGmDQsTSVV911P8upzpcLJO1CNVYi/IH6bGnLR2nA+0L963+kXN1ZrisTEnbtw2ewN6HMMSldqzjronA0Q==} + engines: {node: '>= 22'} + + '@intlify/message-compiler@11.4.4': + resolution: {integrity: sha512-vn0OAV9pYkJlPPmgnsSm5eAG3mL0+9C/oaded2JY9jmxBbhmUXT3TcAUY8WRgLY9Hte7lkUJKpXrVlYjMXBD2w==} + engines: {node: '>= 22'} + + '@intlify/shared@11.4.4': + resolution: {integrity: sha512-QRUCHqda1U6aR14FR0vvXD4+4gj6+fm0AhAozvSuRCw0fCvrmCugWpgiR4xH2NI6s8am6N9p5OhirplsX8ZS3g==} + engines: {node: '>= 22'} + + '@intlify/unplugin-vue-i18n@11.2.3': + resolution: {integrity: sha512-fbPHjOVAkxrPnbhAs6PTNJlfLOJj35ZqYh8CZ9OpeKZiZoulA9lkvrWYP3kfsZ5K/CG9jIHXxpb1/mf5n/mBYA==} + engines: {node: '>= 22.13'} + peerDependencies: + petite-vue-i18n: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vite: + optional: true + vue-i18n: + optional: true + + '@intlify/vue-i18n-extensions@8.0.0': + resolution: {integrity: sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==} + engines: {node: '>= 18'} + peerDependencies: + '@intlify/shared': ^9.0.0 || ^10.0.0 || ^11.0.0 + '@vue/compiler-dom': ^3.0.0 + vue: ^3.0.0 + vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0 + peerDependenciesMeta: + '@intlify/shared': + optional: true + '@vue/compiler-dom': + optional: true + vue: + optional: true + vue-i18n: + optional: true + '@isaacs/cliui@9.0.0': resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} engines: {node: '>=18'} @@ -1129,6 +1257,27 @@ packages: resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} engines: {node: '>=8'} + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oxc-project/types@0.132.0': + resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} + '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -1146,6 +1295,10 @@ packages: resolution: {integrity: sha512-pZ5f+vj7wSzRhC7KoEQRU5fvYAe+RP9+m39CTscZ3UywCD1Y2o6Fe1rRgklMPSkzUcty2jzkA0zMYkiJBD1hgg==} engines: {node: '>=12.11.0'} + '@primevue/auto-import-resolver@4.5.5': + resolution: {integrity: sha512-1LWftK+1c/pLgMyW81xqsCLESm2a8GGlLz0eg13oFBVmJdXBs9nmzF+SIhvtkRDROB8TCMwlOpyqVCi1Yw89UQ==} + engines: {node: '>=12.11.0'} + '@primevue/core@4.5.5': resolution: {integrity: sha512-JpkXhq1ddc70JdsC3CC4dM+UbeeWuCW/8DpS9dNBfrOk824TLSlRlMEGFyVKqRMn5WPQvYLiy3xXfLQeNdSqhQ==} engines: {node: '>=12.11.0'} @@ -1156,6 +1309,102 @@ packages: resolution: {integrity: sha512-eteOhTdAOXEYE9qW1AOrBBgDxQ2szHJxSkEK1XVdV2TKxGM5FQf03Ovms0VDyZTc16XBIgvwYjXJQS0BPbhPaA==} engines: {node: '>=12.11.0'} + '@primevue/metadata@4.5.5': + resolution: {integrity: sha512-ZYLu9m3Nm5BmL0woqCJX84EZfLBepJn+T19v5oj3PlsMpUVNnDAsm2jgYdT6/b3RkdOuccxk4Pg/0EvlATOAhA==} + engines: {node: '>=12.11.0'} + + '@rolldown/binding-android-arm64@1.0.2': + resolution: {integrity: sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.2': + resolution: {integrity: sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.2': + resolution: {integrity: sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.2': + resolution: {integrity: sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': + resolution: {integrity: sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.2': + resolution: {integrity: sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.2': + resolution: {integrity: sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.0.2': + resolution: {integrity: sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.0.2': + resolution: {integrity: sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.2': + resolution: {integrity: sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.2': + resolution: {integrity: sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.0.2': + resolution: {integrity: sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.2': + resolution: {integrity: sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.2': + resolution: {integrity: sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.2': + resolution: {integrity: sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/plugin-babel@6.1.0': resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==} engines: {node: '>=14.0.0'} @@ -1330,6 +1579,9 @@ packages: cpu: [x64] os: [win32] + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tailwindcss/node@4.3.0': resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==} @@ -1534,6 +1786,15 @@ packages: cpu: [arm64] os: [win32] + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@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.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -1543,6 +1804,9 @@ packages: '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1620,41 +1884,41 @@ packages: resolution: {integrity: sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-vue@5.2.4': - resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} - engines: {node: ^18.0.0 || >=20.0.0} + '@vitejs/plugin-vue@6.0.7': + resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 vue: ^3.2.25 - '@vitest/expect@2.1.9': - resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} + '@vitest/expect@4.1.7': + resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} - '@vitest/mocker@2.1.9': - resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} + '@vitest/mocker@4.1.7': + resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@2.1.9': - resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + '@vitest/pretty-format@4.1.7': + resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} - '@vitest/runner@2.1.9': - resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} + '@vitest/runner@4.1.7': + resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} - '@vitest/snapshot@2.1.9': - resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} + '@vitest/snapshot@4.1.7': + resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} - '@vitest/spy@2.1.9': - resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} + '@vitest/spy@4.1.7': + resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} - '@vitest/utils@2.1.9': - resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + '@vitest/utils@4.1.7': + resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} '@volar/language-core@2.4.15': resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} @@ -1665,6 +1929,15 @@ packages: '@volar/typescript@2.4.15': resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} + '@vue-macros/common@3.1.2': + resolution: {integrity: sha512-h9t4ArDdniO9ekYHAD95t9AZcAbb19lEGK+26iAjUODOIJKmObDNBSe4+6ELQAA3vtYiFPPBtHh7+cQCKi3Dng==} + engines: {node: '>=20.19.0'} + peerDependencies: + vue: ^2.7.0 || ^3.2.25 + peerDependenciesMeta: + vue: + optional: true + '@vue/compiler-core@3.5.34': resolution: {integrity: sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==} @@ -1683,6 +1956,24 @@ packages: '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + '@vue/devtools-api@7.7.9': + resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} + + '@vue/devtools-api@8.1.2': + resolution: {integrity: sha512-vA0O112YqyDuNA1s7Yb2gCgToQ/OxOWiFDO5ThLCcDy0ldHnSd1dUTaSYhOldbqoNgumE4dxtGAoAaSUKUD1Zg==} + + '@vue/devtools-kit@7.7.9': + resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} + + '@vue/devtools-kit@8.1.2': + resolution: {integrity: sha512-f75/upc+GCyjXErpgPGz4582ujS0L/adAltGy+tqXMGUJpgAcfGr6CxnnhpZY8BHuMYt6KpbF8uaFrrQG66rGQ==} + + '@vue/devtools-shared@7.7.9': + resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} + + '@vue/devtools-shared@8.1.2': + resolution: {integrity: sha512-X9RyVFYAdkBe4IUf5v48TxBF/6QPmF8CmWrDAjXzfUHrgQ/HGfTC1A6TqgXqZ03ye66l3AD51BAGD69IvKM9sw==} + '@vue/language-core@2.2.12': resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} peerDependencies: @@ -1797,6 +2088,14 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-kit@2.2.0: + resolution: {integrity: sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==} + engines: {node: '>=20.19.0'} + + ast-walker-scope@0.9.0: + resolution: {integrity: sha512-IJdzo2vLiElBxKzwS36VsCue/62d6IdWjnPB2v3nuPKeWGynp6FF/CYoLa5i/3jXH/z97ZDdsXz6abpgM6w07A==} + engines: {node: '>=20.19.0'} + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -1852,6 +2151,9 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + birpc@2.9.0: + resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -1865,6 +2167,10 @@ packages: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + browserslist@4.28.2: resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1880,10 +2186,6 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1903,22 +2205,22 @@ packages: caniuse-lite@1.0.30001793: resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - check-error@2.1.3: - resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} - engines: {node: '>= 16'} - 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'} + cliui@9.0.1: resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} engines: {node: '>=20'} @@ -1947,6 +2249,12 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -1958,6 +2266,10 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + copy-anything@4.0.5: + resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} + engines: {node: '>=18'} + core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} @@ -2015,10 +2327,6 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2107,8 +2415,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} @@ -2122,9 +2430,9 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} hasBin: true esbuild@0.28.0: @@ -2143,6 +2451,15 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + eslint-config-prettier@9.1.2: resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} hasBin: true @@ -2193,6 +2510,11 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -2231,6 +2553,9 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + fast-copy@3.0.2: resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} @@ -2240,6 +2565,10 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -2283,6 +2612,10 @@ packages: filelist@1.0.6: resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + find-my-way@9.6.0: resolution: {integrity: sha512-Zf4Xve4RymLl7NgaavNebZ01joJ8MfVerOG43wy7SHLO+r+K0C6d/SE0BiR7AV5V1VOCFlOP7ecdo+I4qmiHrQ==} engines: {node: '>=20'} @@ -2360,6 +2693,10 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -2427,6 +2764,9 @@ packages: help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -2557,6 +2897,10 @@ packages: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + is-obj@1.0.1: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} @@ -2608,6 +2952,10 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + is-what@5.5.0: + resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} + engines: {node: '>=18'} + is-wsl@3.1.1: resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} @@ -2638,6 +2986,9 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@4.1.1: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true @@ -2676,6 +3027,10 @@ packages: engines: {node: '>=6'} hasBin: true + jsonc-eslint-parser@2.4.2: + resolution: {integrity: sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} @@ -2770,6 +3125,10 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + local-pkg@1.2.1: + resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} + engines: {node: '>=14'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -2786,9 +3145,6 @@ packages: lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2799,6 +3155,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string-ast@1.0.3: + resolution: {integrity: sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==} + engines: {node: '>=20.19.0'} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -2806,6 +3166,14 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2841,6 +3209,12 @@ packages: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2877,6 +3251,9 @@ packages: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} @@ -2932,25 +3309,31 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} - pinia@2.3.1: - resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==} + pinia@3.0.4: + resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==} peerDependencies: - typescript: '>=4.4.4' - vue: ^2.7.0 || ^3.5.11 + typescript: '>=4.5.0' + vue: ^3.5.11 peerDependenciesMeta: typescript: optional: true @@ -2969,6 +3352,12 @@ packages: resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} hasBin: true + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.1: + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -3026,6 +3415,12 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -3041,6 +3436,10 @@ packages: 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'} + real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -3095,6 +3494,11 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rolldown@1.0.2: + resolution: {integrity: sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup-plugin-visualizer@7.0.1: resolution: {integrity: sha512-UJUT4+1Ho4OcWmPYU3sYXgUqI8B8Ayfe06MX7y0qCJ1K8aGoKtR/NDd/2nZqM7ADkrzny+I99Ul7GgyoiVNAgg==} engines: {node: '>=22'} @@ -3123,6 +3527,9 @@ packages: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} @@ -3153,6 +3560,9 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} @@ -3248,6 +3658,10 @@ packages: engines: {node: '>= 8'} deprecated: The work that was done in this beta branch won't be included in future versions + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -3259,8 +3673,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -3308,6 +3722,13 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + superjson@2.2.6: + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} + engines: {node: '>=16'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -3350,23 +3771,16 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.2.2: + resolution: {integrity: sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==} + engines: {node: '>=18'} tinyglobby@0.2.16: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} - engines: {node: '>=14.0.0'} - - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} tldts-core@6.1.86: @@ -3376,6 +3790,10 @@ packages: resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + toad-cache@3.7.1: resolution: {integrity: sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ==} engines: {node: '>=20'} @@ -3401,6 +3819,9 @@ packages: peerDependencies: typescript: '>=4.8.4' + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.22.3: resolution: {integrity: sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==} engines: {node: '>=18.0.0'} @@ -3450,6 +3871,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -3473,6 +3897,10 @@ packages: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} + unimport@5.7.0: + resolution: {integrity: sha512-njnL6sp8lEA8QQbZrt+52p/g4X0rw3bnGGmUcJnt1jeG8+iiqO779aGz0PirCtydAIVcuTBRlJ52F0u46z309Q==} + engines: {node: '>=18.12.0'} + unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} @@ -3481,6 +3909,40 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + unplugin-auto-import@21.0.0: + resolution: {integrity: sha512-vWuC8SwqJmxZFYwPojhOhOXDb5xFhNNcEVb9K/RFkyk/3VnfaOjzitWN7v+8DEKpMjSsY2AEGXNgt6I0yQrhRQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@nuxt/kit': ^4.0.0 + '@vueuse/core': '*' + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@vueuse/core': + optional: true + + unplugin-utils@0.3.1: + resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==} + engines: {node: '>=20.19.0'} + + unplugin-vue-components@32.1.0: + resolution: {integrity: sha512-YiUkSxuRjab18XFOrX5VsIxXzccrfmHVGsGeJgSgklb829DQmCy9E4vvDUE4tuvZZdxyFJZX0Oc4TPnnxiiMyg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@nuxt/kit': ^3.2.2 || ^4.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + + unplugin@3.0.0: + resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==} + engines: {node: ^20.19.0 || >=22.12.0} + upath@1.2.0: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} engines: {node: '>=4'} @@ -3497,44 +3959,47 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite-node@2.1.9: - resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - - vite-plugin-pwa@0.21.2: - resolution: {integrity: sha512-vFhH6Waw8itNu37hWUJxL50q+CBbNcMVzsKaYHQVrfxTt3ihk3PeLO22SbiP1UNWzcEPaTQv+YVxe4G0KOjAkg==} + vite-plugin-pwa@1.3.0: + resolution: {integrity: sha512-c5kMgN+ITrOtHXp8PAtk2uOIEea6XjP/unCGxOWWBzQ6qa65qj/awHg0wf+QF9E/2u9vh86LqxPwzEPNbM2r5A==} engines: {node: '>=16.0.0'} peerDependencies: - '@vite-pwa/assets-generator': ^0.2.6 - vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 - workbox-build: ^7.3.0 - workbox-window: ^7.3.0 + '@vite-pwa/assets-generator': ^1.0.0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + workbox-build: ^7.4.1 + workbox-window: ^7.4.1 peerDependenciesMeta: '@vite-pwa/assets-generator': optional: true - vite@5.4.21: - resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@8.0.14: + resolution: {integrity: sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + 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 - less: + '@vitejs/devtools': optional: true - lightningcss: + esbuild: optional: true - sass: + jiti: + optional: true + less: + optional: true + sass: optional: true sass-embedded: optional: true @@ -3544,24 +4009,44 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true - vitest@2.1.9: - resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} - engines: {node: ^18.0.0 || >=20.0.0} + vitest@4.1.7: + resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.9 - '@vitest/ui': 2.1.9 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.7 + '@vitest/browser-preview': 4.1.7 + '@vitest/browser-webdriverio': 4.1.7 + '@vitest/coverage-istanbul': 4.1.7 + '@vitest/coverage-v8': 4.1.7 + '@vitest/ui': 4.1.7 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': + '@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 @@ -3573,23 +4058,36 @@ packages: vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - vue-demi@0.14.10: - resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} - engines: {node: '>=12'} - hasBin: true - peerDependencies: - '@vue/composition-api': ^1.0.0-rc.1 - vue: ^3.0.0-0 || ^2.6.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - vue-eslint-parser@9.4.3: resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' + vue-i18n@11.4.4: + resolution: {integrity: sha512-gIbXVSFQV4jcSJxfwdZ5zSZmZ+12CnX0K3vBkRSd6Zn+HSzCp+QwUgPwpD/uN0oKNKI9RzlUXPKVedEuMgNG0A==} + engines: {node: '>= 22'} + peerDependencies: + vue: ^3.0.0 + + vue-router@5.1.0: + resolution: {integrity: sha512-HAbiLzLEHQwxPgvsbOJDAwtavszEgLwri6XfyrsPECIFez8+59xc9LofWVdc/HEaSRT822lJ8H9Ns38VVond5g==} + peerDependencies: + '@pinia/colada': '>=0.21.2' + '@vue/compiler-sfc': ^3.5.34 + pinia: ^3.0.4 + vite: ^7.0.0 || ^8.0.0 + vue: ^3.5.34 + peerDependenciesMeta: + '@pinia/colada': + optional: true + '@vue/compiler-sfc': + optional: true + pinia: + optional: true + vite: + optional: true + vue-tsc@2.2.12: resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} hasBin: true @@ -3615,6 +4113,9 @@ packages: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -3751,6 +4252,15 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml-eslint-parser@1.3.2: + resolution: {integrity: sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==} + engines: {node: ^14.17.0 || >=16.0.0} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@22.0.0: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} @@ -3818,6 +4328,15 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@8.0.0-rc.6': + dependencies: + '@babel/parser': 8.0.0-rc.6 + '@babel/types': 8.0.0-rc.6 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.29.0 @@ -3919,8 +4438,12 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@8.0.0-rc.6': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@8.0.0-rc.6': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helper-wrap-function@7.28.6': @@ -3940,6 +4463,10 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/parser@8.0.0-rc.6': + dependencies: + '@babel/types': 8.0.0-rc.6 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -4445,6 +4972,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@8.0.0-rc.6': + dependencies: + '@babel/helper-string-parser': 8.0.0-rc.6 + '@babel/helper-validator-identifier': 8.0.0-rc.6 + '@csstools/color-helpers@5.1.0': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': @@ -4465,148 +4997,173 @@ snapshots: '@csstools/css-tokenizer@3.0.4': {} - '@esbuild/aix-ppc64@0.21.5': + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.12': optional: true '@esbuild/aix-ppc64@0.28.0': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-arm64@0.25.12': optional: true '@esbuild/android-arm64@0.28.0': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/android-arm@0.25.12': optional: true '@esbuild/android-arm@0.28.0': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/android-x64@0.25.12': optional: true '@esbuild/android-x64@0.28.0': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/darwin-arm64@0.25.12': optional: true '@esbuild/darwin-arm64@0.28.0': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/darwin-x64@0.25.12': optional: true '@esbuild/darwin-x64@0.28.0': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/freebsd-arm64@0.25.12': optional: true '@esbuild/freebsd-arm64@0.28.0': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/freebsd-x64@0.25.12': optional: true '@esbuild/freebsd-x64@0.28.0': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-arm64@0.25.12': optional: true '@esbuild/linux-arm64@0.28.0': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-arm@0.25.12': optional: true '@esbuild/linux-arm@0.28.0': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ia32@0.25.12': optional: true '@esbuild/linux-ia32@0.28.0': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-loong64@0.25.12': optional: true '@esbuild/linux-loong64@0.28.0': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-mips64el@0.25.12': optional: true '@esbuild/linux-mips64el@0.28.0': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ppc64@0.25.12': optional: true '@esbuild/linux-ppc64@0.28.0': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-riscv64@0.25.12': optional: true '@esbuild/linux-riscv64@0.28.0': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-s390x@0.25.12': optional: true '@esbuild/linux-s390x@0.28.0': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-x64@0.25.12': optional: true '@esbuild/linux-x64@0.28.0': optional: true + '@esbuild/netbsd-arm64@0.25.12': + optional: true + '@esbuild/netbsd-arm64@0.28.0': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/netbsd-x64@0.25.12': optional: true '@esbuild/netbsd-x64@0.28.0': optional: true + '@esbuild/openbsd-arm64@0.25.12': + optional: true + '@esbuild/openbsd-arm64@0.28.0': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/openbsd-x64@0.25.12': optional: true '@esbuild/openbsd-x64@0.28.0': optional: true + '@esbuild/openharmony-arm64@0.25.12': + optional: true + '@esbuild/openharmony-arm64@0.28.0': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/sunos-x64@0.25.12': optional: true '@esbuild/sunos-x64@0.28.0': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/win32-arm64@0.25.12': optional: true '@esbuild/win32-arm64@0.28.0': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/win32-ia32@0.25.12': optional: true '@esbuild/win32-ia32@0.28.0': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/win32-x64@0.25.12': optional: true '@esbuild/win32-x64@0.28.0': @@ -4730,6 +5287,72 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@intlify/bundle-utils@11.2.3(vue-i18n@11.4.4(vue@3.5.34(typescript@5.9.3)))': + dependencies: + '@intlify/message-compiler': 11.4.4 + '@intlify/shared': 11.4.4 + acorn: 8.16.0 + esbuild: 0.25.12 + escodegen: 2.1.0 + estree-walker: 2.0.2 + jsonc-eslint-parser: 2.4.2 + source-map-js: 1.2.1 + yaml-eslint-parser: 1.3.2 + optionalDependencies: + vue-i18n: 11.4.4(vue@3.5.34(typescript@5.9.3)) + + '@intlify/core-base@11.4.4': + dependencies: + '@intlify/devtools-types': 11.4.4 + '@intlify/message-compiler': 11.4.4 + '@intlify/shared': 11.4.4 + + '@intlify/devtools-types@11.4.4': + dependencies: + '@intlify/core-base': 11.4.4 + '@intlify/shared': 11.4.4 + + '@intlify/message-compiler@11.4.4': + dependencies: + '@intlify/shared': 11.4.4 + source-map-js: 1.2.1 + + '@intlify/shared@11.4.4': {} + + '@intlify/unplugin-vue-i18n@11.2.3(@vue/compiler-dom@3.5.34)(eslint@9.39.4(jiti@2.7.0))(rollup@4.60.4)(typescript@5.9.3)(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))(vue-i18n@11.4.4(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3))': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@intlify/bundle-utils': 11.2.3(vue-i18n@11.4.4(vue@3.5.34(typescript@5.9.3))) + '@intlify/shared': 11.4.4 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.4)(@vue/compiler-dom@3.5.34)(vue-i18n@11.4.4(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3)) + '@rollup/pluginutils': 5.3.0(rollup@4.60.4) + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@5.9.3) + debug: 4.4.3 + fast-glob: 3.3.3 + pathe: 2.0.3 + picocolors: 1.1.1 + unplugin: 2.3.11 + vue: 3.5.34(typescript@5.9.3) + optionalDependencies: + vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0) + vue-i18n: 11.4.4(vue@3.5.34(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/compiler-dom' + - eslint + - rollup + - supports-color + - typescript + + '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.4.4)(@vue/compiler-dom@3.5.34)(vue-i18n@11.4.4(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3))': + dependencies: + '@babel/parser': 7.29.3 + optionalDependencies: + '@intlify/shared': 11.4.4 + '@vue/compiler-dom': 3.5.34 + vue: 3.5.34(typescript@5.9.3) + vue-i18n: 11.4.4(vue@3.5.34(typescript@5.9.3)) + '@isaacs/cliui@9.0.0': {} '@jridgewell/gen-mapping@0.3.13': @@ -4758,6 +5381,27 @@ snapshots: '@lukeed/ms@2.0.2': {} + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@oxc-project/types@0.132.0': {} + '@pinojs/redact@0.4.0': {} '@primeuix/styled@0.7.4': @@ -4774,6 +5418,10 @@ snapshots: '@primeuix/utils@0.6.4': {} + '@primevue/auto-import-resolver@4.5.5': + dependencies: + '@primevue/metadata': 4.5.5 + '@primevue/core@4.5.5(vue@3.5.34(typescript@5.9.3))': dependencies: '@primeuix/styled': 0.7.4 @@ -4787,6 +5435,59 @@ snapshots: transitivePeerDependencies: - vue + '@primevue/metadata@4.5.5': {} + + '@rolldown/binding-android-arm64@1.0.2': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.2': + optional: true + + '@rolldown/binding-darwin-x64@1.0.2': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.2': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.2': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.2': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.2': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.2': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.2': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.2': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.2': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.2': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.2': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.2': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.0)(rollup@4.60.4)': dependencies: '@babel/core': 7.29.0 @@ -4905,6 +5606,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.4': optional: true + '@standard-schema/spec@1.1.0': {} + '@tailwindcss/node@4.3.0': dependencies: '@jridgewell/remapping': 2.3.5 @@ -4966,12 +5669,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/vite@4.3.0(vite@5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0))': + '@tailwindcss/vite@4.3.0(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 tailwindcss: 4.3.0 - vite: 5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0) + vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0) '@tauri-apps/api@2.11.0': {} @@ -5055,12 +5758,26 @@ snapshots: '@turbo/windows-arm64@2.9.14': optional: true + '@tybys/wasm-util@0.10.2': + 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.8': {} '@types/estree@1.0.9': {} '@types/geojson@7946.0.16': {} + '@types/jsesc@2.5.1': {} + '@types/json-schema@7.0.15': {} '@types/leaflet@1.9.21': @@ -5168,50 +5885,52 @@ snapshots: '@typescript-eslint/types': 8.59.4 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0))(vue@3.5.34(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.7(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))(vue@3.5.34(typescript@5.9.3))': dependencies: - vite: 5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0) + '@rolldown/pluginutils': 1.0.1 + vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0) vue: 3.5.34(typescript@5.9.3) - '@vitest/expect@2.1.9': + '@vitest/expect@4.1.7': dependencies: - '@vitest/spy': 2.1.9 - '@vitest/utils': 2.1.9 - chai: 5.3.3 - tinyrainbow: 1.2.0 + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 + chai: 6.2.2 + tinyrainbow: 3.1.0 - '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0))': + '@vitest/mocker@4.1.7(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: - '@vitest/spy': 2.1.9 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0) + vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0) - '@vitest/pretty-format@2.1.9': + '@vitest/pretty-format@4.1.7': dependencies: - tinyrainbow: 1.2.0 + tinyrainbow: 3.1.0 - '@vitest/runner@2.1.9': + '@vitest/runner@4.1.7': dependencies: - '@vitest/utils': 2.1.9 - pathe: 1.1.2 + '@vitest/utils': 4.1.7 + pathe: 2.0.3 - '@vitest/snapshot@2.1.9': + '@vitest/snapshot@4.1.7': dependencies: - '@vitest/pretty-format': 2.1.9 + '@vitest/pretty-format': 4.1.7 + '@vitest/utils': 4.1.7 magic-string: 0.30.21 - pathe: 1.1.2 + pathe: 2.0.3 - '@vitest/spy@2.1.9': - dependencies: - tinyspy: 3.0.2 + '@vitest/spy@4.1.7': {} - '@vitest/utils@2.1.9': + '@vitest/utils@4.1.7': dependencies: - '@vitest/pretty-format': 2.1.9 - loupe: 3.2.1 - tinyrainbow: 1.2.0 + '@vitest/pretty-format': 4.1.7 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 '@volar/language-core@2.4.15': dependencies: @@ -5225,6 +5944,16 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.1.0 + '@vue-macros/common@3.1.2(vue@3.5.34(typescript@5.9.3))': + dependencies: + '@vue/compiler-sfc': 3.5.34 + ast-kit: 2.2.0 + local-pkg: 1.2.1 + magic-string-ast: 1.0.3 + unplugin-utils: 0.3.1 + optionalDependencies: + vue: 3.5.34(typescript@5.9.3) + '@vue/compiler-core@3.5.34': dependencies: '@babel/parser': 7.29.3 @@ -5262,6 +5991,37 @@ snapshots: '@vue/devtools-api@6.6.4': {} + '@vue/devtools-api@7.7.9': + dependencies: + '@vue/devtools-kit': 7.7.9 + + '@vue/devtools-api@8.1.2': + dependencies: + '@vue/devtools-kit': 8.1.2 + + '@vue/devtools-kit@7.7.9': + dependencies: + '@vue/devtools-shared': 7.7.9 + birpc: 2.9.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.6 + + '@vue/devtools-kit@8.1.2': + dependencies: + '@vue/devtools-shared': 8.1.2 + birpc: 2.9.0 + hookable: 5.5.3 + perfect-debounce: 2.1.0 + + '@vue/devtools-shared@7.7.9': + dependencies: + rfdc: 1.4.1 + + '@vue/devtools-shared@8.1.2': {} + '@vue/language-core@2.2.12(typescript@5.9.3)': dependencies: '@volar/language-core': 2.4.15 @@ -5378,6 +6138,17 @@ snapshots: assertion-error@2.0.1: {} + ast-kit@2.2.0: + dependencies: + '@babel/parser': 7.29.3 + pathe: 2.0.3 + + ast-walker-scope@0.9.0: + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + ast-kit: 2.2.0 + async-function@1.0.0: {} async@3.2.6: {} @@ -5429,6 +6200,8 @@ snapshots: baseline-browser-mapping@2.10.32: {} + birpc@2.9.0: {} + boolbase@1.0.0: {} brace-expansion@1.1.14: @@ -5444,6 +6217,10 @@ snapshots: dependencies: balanced-match: 4.0.4 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.32 @@ -5463,8 +6240,6 @@ snapshots: dependencies: run-applescript: 7.1.0 - cac@6.7.14: {} - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -5486,25 +6261,21 @@ snapshots: caniuse-lite@1.0.30001793: {} - chai@5.3.3: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.3 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 + chai@6.2.2: {} chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - check-error@2.1.3: {} - chokidar@4.0.3: dependencies: readdirp: 4.1.2 + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + cliui@9.0.1: dependencies: string-width: 7.2.0 @@ -5529,6 +6300,10 @@ snapshots: concat-map@0.0.1: {} + confbox@0.1.8: {} + + confbox@0.2.4: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -5537,6 +6312,10 @@ snapshots: cookie@1.1.1: {} + copy-anything@4.0.5: + dependencies: + is-what: 5.5.0 + core-js-compat@3.49.0: dependencies: browserslist: 4.28.2 @@ -5591,8 +6370,6 @@ snapshots: decimal.js@10.6.0: {} - deep-eql@5.0.2: {} - deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -5721,7 +6498,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@1.7.0: {} + es-module-lexer@2.1.0: {} es-object-atoms@1.1.2: dependencies: @@ -5740,31 +6517,34 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.21.5: + esbuild@0.25.12: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 esbuild@0.28.0: optionalDependencies: @@ -5801,6 +6581,16 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.7.0)): dependencies: eslint: 9.39.4(jiti@2.7.0) @@ -5888,6 +6678,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 3.4.3 + esprima@4.0.1: {} + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -5914,12 +6706,22 @@ snapshots: expect-type@1.3.0: {} + exsolve@1.0.8: {} + fast-copy@3.0.2: {} fast-decode-uri-component@1.0.1: {} fast-deep-equal@3.1.3: {} + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-json-stringify@6.4.0: @@ -5977,6 +6779,10 @@ snapshots: dependencies: minimatch: 5.1.9 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + find-my-way@9.6.0: dependencies: fast-deep-equal: 3.1.3 @@ -6069,6 +6875,10 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -6125,6 +6935,8 @@ snapshots: help-me@5.0.0: {} + hookable@5.5.3: {} + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -6257,6 +7069,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-number@7.0.0: {} + is-obj@1.0.1: {} is-potential-custom-element-name@1.0.1: {} @@ -6304,6 +7118,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-what@5.5.0: {} + is-wsl@3.1.1: dependencies: is-inside-container: 1.0.0 @@ -6328,6 +7144,8 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + js-yaml@4.1.1: dependencies: argparse: 2.0.1 @@ -6376,6 +7194,13 @@ snapshots: json5@2.2.3: {} + jsonc-eslint-parser@2.4.2: + dependencies: + acorn: 8.16.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.8.1 + jsonfile@6.2.1: dependencies: universalify: 2.0.1 @@ -6452,6 +7277,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + local-pkg@1.2.1: + dependencies: + mlly: 1.8.2 + pkg-types: 2.3.1 + quansync: 0.2.11 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -6464,8 +7295,6 @@ snapshots: lodash@4.18.1: {} - loupe@3.2.1: {} - lru-cache@10.4.3: {} lru-cache@11.5.0: {} @@ -6474,12 +7303,23 @@ snapshots: dependencies: yallist: 3.1.1 + magic-string-ast@1.0.3: + dependencies: + magic-string: 0.30.21 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 math-intrinsics@1.1.0: {} + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + mime-db@1.52.0: {} mime-types@2.1.35: @@ -6508,6 +7348,15 @@ snapshots: minipass@7.1.3: {} + mitt@3.0.1: {} + + mlly@1.8.2: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + ms@2.1.3: {} muggle-string@0.4.1: {} @@ -6537,6 +7386,8 @@ snapshots: has-symbols: 1.1.0 object-keys: 1.1.1 + obug@2.1.1: {} + on-exit-leak-free@2.1.2: {} once@1.4.0: @@ -6598,23 +7449,24 @@ snapshots: lru-cache: 11.5.0 minipass: 7.1.3 - pathe@1.1.2: {} + pathe@2.0.3: {} - pathval@2.0.1: {} + perfect-debounce@1.0.0: {} + + perfect-debounce@2.1.0: {} picocolors@1.1.1: {} + picomatch@2.3.2: {} + picomatch@4.0.4: {} - pinia@2.3.1(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)): + pinia@3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)): dependencies: - '@vue/devtools-api': 6.6.4 + '@vue/devtools-api': 7.7.9 vue: 3.5.34(typescript@5.9.3) - vue-demi: 0.14.10(vue@3.5.34(typescript@5.9.3)) optionalDependencies: typescript: 5.9.3 - transitivePeerDependencies: - - '@vue/composition-api' pino-abstract-transport@2.0.0: dependencies: @@ -6653,6 +7505,18 @@ snapshots: sonic-boom: 4.2.1 thread-stream: 3.1.0 + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + pkg-types@2.3.1: + dependencies: + confbox: 0.2.4 + exsolve: 1.0.8 + pathe: 2.0.3 + possible-typed-array-names@1.1.0: {} postcss-selector-parser@6.1.2: @@ -6701,6 +7565,10 @@ snapshots: punycode@2.3.1: {} + quansync@0.2.11: {} + + queue-microtask@1.2.3: {} + quick-format-unescaped@4.0.4: {} readable-stream@3.6.2: @@ -6719,6 +7587,8 @@ snapshots: readdirp@4.1.2: {} + readdirp@5.0.0: {} + real-require@0.2.0: {} reflect.getprototypeof@1.0.10: @@ -6779,13 +7649,35 @@ snapshots: rfdc@1.4.1: {} - rollup-plugin-visualizer@7.0.1(rollup@4.60.4): + rolldown@1.0.2: + dependencies: + '@oxc-project/types': 0.132.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.2 + '@rolldown/binding-darwin-arm64': 1.0.2 + '@rolldown/binding-darwin-x64': 1.0.2 + '@rolldown/binding-freebsd-x64': 1.0.2 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.2 + '@rolldown/binding-linux-arm64-gnu': 1.0.2 + '@rolldown/binding-linux-arm64-musl': 1.0.2 + '@rolldown/binding-linux-ppc64-gnu': 1.0.2 + '@rolldown/binding-linux-s390x-gnu': 1.0.2 + '@rolldown/binding-linux-x64-gnu': 1.0.2 + '@rolldown/binding-linux-x64-musl': 1.0.2 + '@rolldown/binding-openharmony-arm64': 1.0.2 + '@rolldown/binding-wasm32-wasi': 1.0.2 + '@rolldown/binding-win32-arm64-msvc': 1.0.2 + '@rolldown/binding-win32-x64-msvc': 1.0.2 + + rollup-plugin-visualizer@7.0.1(rolldown@1.0.2)(rollup@4.60.4): dependencies: open: 11.0.0 picomatch: 4.0.4 source-map: 0.7.6 yargs: 18.0.0 optionalDependencies: + rolldown: 1.0.2 rollup: 4.60.4 rollup@4.60.4: @@ -6825,6 +7717,10 @@ snapshots: run-applescript@7.1.0: {} + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 @@ -6858,6 +7754,8 @@ snapshots: dependencies: xmlchars: 2.2.0 + scule@1.3.0: {} + secure-json-parse@2.7.0: {} secure-json-parse@4.1.0: {} @@ -6953,13 +7851,15 @@ snapshots: dependencies: whatwg-url: 7.1.0 + speakingurl@14.0.1: {} + split2@4.2.0: {} stackback@0.0.2: {} statuses@2.0.2: {} - std-env@3.10.0: {} + std-env@4.1.0: {} stop-iteration-iterator@1.1.0: dependencies: @@ -7031,6 +7931,14 @@ snapshots: strip-json-comments@3.1.1: {} + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + superjson@2.2.6: + dependencies: + copy-anything: 4.0.5 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -7069,18 +7977,14 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.2: {} + tinyexec@1.2.2: {} tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinypool@1.1.1: {} - - tinyrainbow@1.2.0: {} - - tinyspy@3.0.2: {} + tinyrainbow@3.1.0: {} tldts-core@6.1.86: {} @@ -7088,6 +7992,10 @@ snapshots: dependencies: tldts-core: 6.1.86 + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + toad-cache@3.7.1: {} toidentifier@1.0.1: {} @@ -7108,6 +8016,9 @@ snapshots: dependencies: typescript: 5.9.3 + tslib@2.8.1: + optional: true + tsx@4.22.3: dependencies: esbuild: 0.28.0 @@ -7177,6 +8088,8 @@ snapshots: typescript@5.9.3: {} + ufo@1.6.4: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -7197,12 +8110,71 @@ snapshots: unicode-property-aliases-ecmascript@2.2.0: {} + unimport@5.7.0: + dependencies: + acorn: 8.16.0 + escape-string-regexp: 5.0.0 + estree-walker: 3.0.3 + local-pkg: 1.2.1 + magic-string: 0.30.21 + mlly: 1.8.2 + pathe: 2.0.3 + picomatch: 4.0.4 + pkg-types: 2.3.1 + scule: 1.3.0 + strip-literal: 3.1.0 + tinyglobby: 0.2.16 + unplugin: 2.3.11 + unplugin-utils: 0.3.1 + unique-string@2.0.0: dependencies: crypto-random-string: 2.0.0 universalify@2.0.1: {} + unplugin-auto-import@21.0.0(@vueuse/core@14.3.0(vue@3.5.34(typescript@5.9.3))): + dependencies: + local-pkg: 1.2.1 + magic-string: 0.30.21 + picomatch: 4.0.4 + unimport: 5.7.0 + unplugin: 2.3.11 + unplugin-utils: 0.3.1 + optionalDependencies: + '@vueuse/core': 14.3.0(vue@3.5.34(typescript@5.9.3)) + + unplugin-utils@0.3.1: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.4 + + unplugin-vue-components@32.1.0(vue@3.5.34(typescript@5.9.3)): + dependencies: + chokidar: 5.0.0 + local-pkg: 1.2.1 + magic-string: 0.30.21 + mlly: 1.8.2 + obug: 2.1.1 + picomatch: 4.0.4 + tinyglobby: 0.2.16 + unplugin: 3.0.0 + unplugin-utils: 0.3.1 + vue: 3.5.34(typescript@5.9.3) + + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.16.0 + picomatch: 4.0.4 + webpack-virtual-modules: 0.6.2 + + unplugin@3.0.0: + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.4 + webpack-virtual-modules: 0.6.2 + upath@1.2.0: {} update-browserslist-db@1.2.3(browserslist@4.28.2): @@ -7217,88 +8189,63 @@ snapshots: util-deprecate@1.0.2: {} - vite-node@2.1.9(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 1.1.2 - vite: 5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vite-plugin-pwa@0.21.2(vite@5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0))(workbox-build@7.4.1)(workbox-window@7.4.1): + vite-plugin-pwa@1.3.0(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1): dependencies: debug: 4.4.3 pretty-bytes: 6.1.1 tinyglobby: 0.2.16 - vite: 5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0) + vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0) workbox-build: 7.4.1 workbox-window: 7.4.1 transitivePeerDependencies: - supports-color - vite@5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0): + vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0): dependencies: - esbuild: 0.21.5 + lightningcss: 1.32.0 + picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.60.4 + rolldown: 1.0.2 + tinyglobby: 0.2.16 optionalDependencies: '@types/node': 22.19.19 + esbuild: 0.28.0 fsevents: 2.3.3 - lightningcss: 1.32.0 + jiti: 2.7.0 terser: 5.48.0 - - vitest@2.1.9(@types/node@22.19.19)(jsdom@25.0.1)(lightningcss@1.32.0)(terser@5.48.0): - dependencies: - '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0)) - '@vitest/pretty-format': 2.1.9 - '@vitest/runner': 2.1.9 - '@vitest/snapshot': 2.1.9 - '@vitest/spy': 2.1.9 - '@vitest/utils': 2.1.9 - chai: 5.3.3 - debug: 4.4.3 + tsx: 4.22.3 + yaml: 2.9.0 + + vitest@4.1.7(@types/node@22.19.19)(jsdom@25.0.1)(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 + es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 - pathe: 1.1.2 - std-env: 3.10.0 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 0.3.2 - tinypool: 1.1.1 - tinyrainbow: 1.2.0 - vite: 5.4.21(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0) - vite-node: 2.1.9(@types/node@22.19.19)(lightningcss@1.32.0)(terser@5.48.0) + tinyexec: 1.2.2 + tinyglobby: 0.2.16 + tinyrainbow: 3.1.0 + vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.19 jsdom: 25.0.1 transitivePeerDependencies: - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser vscode-uri@3.1.0: {} - vue-demi@0.14.10(vue@3.5.34(typescript@5.9.3)): - dependencies: - vue: 3.5.34(typescript@5.9.3) - vue-eslint-parser@9.4.3(eslint@9.39.4(jiti@2.7.0)): dependencies: debug: 4.4.3 @@ -7312,6 +8259,39 @@ snapshots: transitivePeerDependencies: - supports-color + vue-i18n@11.4.4(vue@3.5.34(typescript@5.9.3)): + dependencies: + '@intlify/core-base': 11.4.4 + '@intlify/devtools-types': 11.4.4 + '@intlify/shared': 11.4.4 + '@vue/devtools-api': 6.6.4 + vue: 3.5.34(typescript@5.9.3) + + vue-router@5.1.0(@vue/compiler-sfc@3.5.34)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)))(vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))(vue@3.5.34(typescript@5.9.3)): + dependencies: + '@babel/generator': 8.0.0-rc.6 + '@vue-macros/common': 3.1.2(vue@3.5.34(typescript@5.9.3)) + '@vue/devtools-api': 8.1.2 + ast-walker-scope: 0.9.0 + chokidar: 5.0.0 + json5: 2.2.3 + local-pkg: 1.2.1 + magic-string: 0.30.21 + mlly: 1.8.2 + muggle-string: 0.4.1 + pathe: 2.0.3 + picomatch: 4.0.4 + scule: 1.3.0 + tinyglobby: 0.2.16 + unplugin: 3.0.0 + unplugin-utils: 0.3.1 + vue: 3.5.34(typescript@5.9.3) + yaml: 2.9.0 + optionalDependencies: + '@vue/compiler-sfc': 3.5.34 + pinia: 3.0.4(typescript@5.9.3)(vue@3.5.34(typescript@5.9.3)) + vite: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0) + vue-tsc@2.2.12(typescript@5.9.3): dependencies: '@volar/typescript': 2.4.15 @@ -7336,6 +8316,8 @@ snapshots: webidl-conversions@7.0.0: {} + webpack-virtual-modules@0.6.2: {} + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -7543,6 +8525,13 @@ snapshots: yallist@3.1.1: {} + yaml-eslint-parser@1.3.2: + dependencies: + eslint-visitor-keys: 3.4.3 + yaml: 2.9.0 + + yaml@2.9.0: {} + yargs-parser@22.0.0: {} yargs@18.0.0:
{{ t.labels }}
{{ t.labelHint }}
{{ t.labelSize }}
{{ t.playerFollow }}
{{ t.playerFollowHint }}
- {{ t.overlay.mapOpacityHint }} -
{{ t.overlay.zoom }}
{{ t.hotkeys.lockHint }}
{{ t.cache.hint }}
- {{ t.systemSection }} -
…
- {{ t.paths.logsDir }}: {{ serverConfig.logsDir.value ?? "—" }} -
- {{ t.paths.mobileHint }} -
+ {{ t("systemSection") }} +
{{ t("cache.hint") }}
{{ t("labels") }}
{{ t("labelHint") }}
{{ t("labelSize") }}
{{ t("hotkeys.lockHint") }}
+ {{ t("overlay.mapOpacityHint") }} +
{{ t("overlay.zoom") }}
+ {{ t("paths.logsDir") }}: {{ serverConfig.logsDir.value ?? "—" }} +
+ {{ t("paths.mobileHint") }} +
{{ t("playerFollow") }}
{{ t("playerFollowHint") }}