-
-
-
+
diff --git a/.vitepress/components/PageActionMenu.vue b/.vitepress/components/PageActionMenu.vue
new file mode 100644
index 0000000..b8b880f
--- /dev/null
+++ b/.vitepress/components/PageActionMenu.vue
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.vitepress/components/Playground.vue b/.vitepress/components/Playground.vue
index fa64813..b45ff4a 100644
--- a/.vitepress/components/Playground.vue
+++ b/.vitepress/components/Playground.vue
@@ -1,5 +1,6 @@
\ No newline at end of file
diff --git a/.vitepress/components/SidebarText.vue b/.vitepress/components/SidebarText.vue
new file mode 100644
index 0000000..2b5b6f5
--- /dev/null
+++ b/.vitepress/components/SidebarText.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/.vitepress/components/ThemeSwitcher.vue b/.vitepress/components/ThemeSwitcher.vue
new file mode 100644
index 0000000..5dfbb65
--- /dev/null
+++ b/.vitepress/components/ThemeSwitcher.vue
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
diff --git a/.vitepress/components/VPSidebarItem.vue b/.vitepress/components/VPSidebarItem.vue
new file mode 100644
index 0000000..ef57bfa
--- /dev/null
+++ b/.vitepress/components/VPSidebarItem.vue
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.vitepress/composables/useActiveSidebar.ts b/.vitepress/composables/useActiveSidebar.ts
new file mode 100644
index 0000000..4294c04
--- /dev/null
+++ b/.vitepress/composables/useActiveSidebar.ts
@@ -0,0 +1,39 @@
+import { useData, useRoute } from 'vitepress'
+import { computed } from 'vue'
+
+export function useActiveSidebar() {
+ const { theme } = useData()
+ const route = useRoute()
+
+ return computed(() => {
+ const sidebar = theme.value.sidebar
+ const path = route.path.replace(/\.html$/, '')
+
+ let items = []
+ if (Array.isArray(sidebar)) {
+ items = sidebar
+ } else if (typeof sidebar === 'object') {
+ const key = Object.keys(sidebar)
+ .filter(k => path.startsWith(k))
+ .sort((a, b) => b.length - a.length)[0]
+
+ if (key) items = sidebar[key]
+ }
+
+ function containsActive(list: any[]): boolean {
+ if (!list) return false
+ for (const item of list) {
+ if (item.link && item.link.replace(/\.html$/, '') === path) return true
+ if (item.items && containsActive(item.items)) return true
+ }
+ return false
+ }
+
+ for (const group of items) {
+ if (group.link && group.link.replace(/\.html$/, '') === path) return group
+ if (group.items && containsActive(group.items)) return group
+ }
+
+ return null
+ })
+}
diff --git a/.vitepress/config.ts b/.vitepress/config.ts
index 7d89fcd..27c171c 100644
--- a/.vitepress/config.ts
+++ b/.vitepress/config.ts
@@ -1,4 +1,5 @@
import { defineConfig } from "vitepress";
+import { fileURLToPath, URL } from 'node:url'
import nav from "./config/nav";
import sidebar from "./config/sidebar";
import UnoCSS from 'unocss/vite'
@@ -40,10 +41,32 @@ export default defineConfig({
'blog/*',
'public/*'
],
- domain: 'https://texprjs.com'
+ domain: 'https://texpr.andka.id'
})
: undefined,
- ]
+ ],
+ resolve: {
+ alias: [
+ {
+ find: /^.*\/VPDocAsideOutline\.vue$/,
+ replacement: fileURLToPath(
+ new URL('./components/DocOutline.vue', import.meta.url)
+ )
+ },
+ {
+ find: /^.*\/VPSwitchAppearance\.vue$/,
+ replacement: fileURLToPath(
+ new URL('./components/ThemeSwitcher.vue', import.meta.url)
+ )
+ },
+ {
+ find: /^.*\/VPSidebarItem\.vue$/,
+ replacement: fileURLToPath(
+ new URL('./components/VPSidebarItem.vue', import.meta.url)
+ )
+ }
+ ]
+ }
},
themeConfig: {
logo: "/logo.svg",
diff --git a/.vitepress/config/nav.ts b/.vitepress/config/nav.ts
index 5a97a74..3d46333 100644
--- a/.vitepress/config/nav.ts
+++ b/.vitepress/config/nav.ts
@@ -94,7 +94,7 @@ export default [
activeMatch: "^/advanced/",
},
{
- text: 'v0.1.3',
+ text: 'v0.1.4',
items: [
{
text: "Project Info",
@@ -102,6 +102,7 @@ export default [
{ text: "Changelog", link: "https://github.com/xirf/texpr/blob/main/CHANGELOG.md" },
{ text: "Release Notes", link: "https://github.com/xirf/texpr/releases" },
{ text: "Contributing", link: "https://github.com/xirf/texpr/blob/main/CONTRIBUTING.md" },
+ { text: "Migration Guide", link: "/guide/migration-validate" },
],
},
{
diff --git a/.vitepress/index.ts b/.vitepress/index.ts
new file mode 100644
index 0000000..deb6669
--- /dev/null
+++ b/.vitepress/index.ts
@@ -0,0 +1,19 @@
+import DefaultTheme from 'vitepress/theme'
+import type { Theme } from 'vitepress'
+import HomeHero from './components/HomeHero.vue'
+import FeatureShowcase from './components/FeatureShowcase.vue'
+import Layout from './Layout.vue'
+
+import 'virtual:uno.css'
+import './vars.css'
+import './override.css'
+
+export default {
+ extends: DefaultTheme,
+ Layout,
+ enhanceApp({ app }) {
+ app.component('HomeHero', HomeHero)
+
+ app.component('FeatureShowcase', FeatureShowcase)
+ }
+} satisfies Theme
diff --git a/.vitepress/theme/custom.css b/.vitepress/theme/custom.css
deleted file mode 100644
index e346f5b..0000000
--- a/.vitepress/theme/custom.css
+++ /dev/null
@@ -1,513 +0,0 @@
-/* UnoCSS Migration */
-
-:root {
- /* Brand colors inspired by UnoCSS gradient */
- --vp-c-brand-1: theme('colors.violet.500');
- --vp-c-brand-2: theme('colors.violet.400');
- --vp-c-brand-3: theme('colors.violet.300');
- --vp-c-brand-soft: rgba(124, 58, 237, 0.14); /* violet-600 with opacity */
-
- --vp-c-bg-alt: theme('colors.slate.100');
- --vp-c-border: theme('colors.slate.100');
- --twoslash-border-color: theme('colors.slate.100');
- --vp-code-block-bg: #f6f8fa;
- --vp-code-copy-code-bg: #dedede;
- --vp-code-copy-code-hover-bg: #dedede;
- --vp-c-text-dark-2: #8f8f8f;
- --vp-c-text-dark-3: #8f8f8f;
- --vp-sidebar-bg-color: var(--vp-c-bg);
- --vp-custom-block-tip-border: transparent;
- --vp-custom-block-tip-text: var(--vp-c-text-1);
- --vp-nav-bg-color: color-mix(in srgb, var(--vp-c-bg) 95%, transparent);
- --vp-custom-block-tip-bg: #ecfdf5;
- --vp-custom-block-tip-code-bg: #cdf7dc;
- --vp-custom-block-tip-border: #dcfce7;
- --vp-custom-block-tip-text: #15803d;
- --vp-c-brand-nav-active: color-mix(in srgb,
- var(--vp-c-brand-light) 15%,
- transparent 100%);
- --vp-nav-logo-height: 28px;
-
- --twoslash-border-color: theme('colors.slate.200');
- --vp-c-bg-elv: theme('colors.slate.100');
-
- --vp-code-copy-code-bg: theme('colors.slate.50');
- --vp-code-copy-code-hover-bg: #fff;
- --vp-code-copy-code-border-color: theme('colors.slate.200');
-
- --vp-icon-copy: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2224%22%20height=%2224%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22rgb(128,128,128)%22%20stroke-width=%221.5%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Crect%20width=%2214%22%20height=%2214%22%20x=%228%22%20y=%228%22%20rx=%222%22%20ry=%222%22/%3E%3Cpath%20d=%22M4%2016c-1.1%200-2-.9-2-2V4c0-1.1.9-2%202-2h10c1.1%200%202%20.9%202%202%22/%3E%3C/svg%3E');
- --vp-icon-copied: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2224%22%20height=%2224%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22rgb(128,128,128)%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Cpath%20d=%22M20%206%209%2017l-5-5%22/%3E%3C/svg%3E');
-}
-
-.dark {
- --vp-c-brand-1: theme('colors.violet.300');
- --vp-c-brand-2: theme('colors.violet.200');
- --vp-c-brand-3: theme('colors.violet.100');
-
- --vp-c-bg: theme('colors.slate.900');
- --vp-c-border: theme('colors.slate.100');
- --twoslash-border-color: theme('colors.slate.100');
- --vp-c-bg-alt: theme('colors.slate.800');
- --vp-c-bg-soft: theme('colors.slate.800');
- --vp-c-divider: theme('colors.slate.700');
- --vp-code-block-bg: theme('colors.slate.800');
- --vp-c-bg-alpha-with-backdrop: #1f2937bf;
- --vp-code-copy-code-bg: theme('colors.slate.800');
- --vp-code-copy-code-hover-bg: theme('colors.slate.800');
- --vp-c-text-dark-2: #8a8a8a;
- --vp-c-text-dark-3: #8a8a8a;
- --vp-custom-block-tip-bg: #064e3b;
- --vp-custom-block-tip-border: #052e16;
- --vp-custom-block-tip-text: #f0fdf4;
- --vp-c-brand-nav-active: color-mix(in srgb,
- var(--vp-c-brand-dark) 30%,
- transparent 100%);
-
- --twoslash-border-color: theme('colors.slate.800');
- --vp-c-bg-elv: theme('colors.slate.800');
- --vp-c-neutral-inverse: theme('colors.slate.800');
-
- --vp-code-copy-code-bg: theme('colors.slate.800');
- --vp-code-copy-code-hover-bg: theme('colors.slate.700');
- --vp-code-copy-code-border-color: theme('colors.slate.600');
-
- --vp-icon-copy: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2224%22%20height=%2224%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22rgb(160,160,160)%22%20stroke-width=%221.5%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Crect%20width=%2214%22%20height=%2214%22%20x=%228%22%20y=%228%22%20rx=%222%22%20ry=%222%22/%3E%3Cpath%20d=%22M4%2016c-1.1%200-2-.9-2-2V4c0-1.1.9-2%202-2h10c1.1%200%202%20.9%202%202%22/%3E%3C/svg%3E');
- --vp-icon-copied: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2224%22%20height=%2224%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22rgb(160,160,160)%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Cpath%20d=%22M20%206%209%2017l-5-5%22/%3E%3C/svg%3E');
-}
-
-/* Hero gradient background like UnoCSS */
-.VPHero {
- position: relative;
- overflow: hidden;
-}
-
-.VPHero::before {
- content: "";
- position: absolute;
- inset: 0;
- background: radial-gradient(ellipse 80% 50% at 50% -20%,
- rgba(99, 102, 241, 0.3),
- transparent),
- radial-gradient(ellipse 60% 40% at 80% 50%,
- rgba(139, 92, 246, 0.2),
- transparent),
- radial-gradient(ellipse 50% 30% at 20% 80%,
- rgba(99, 102, 241, 0.15),
- transparent);
- z-index: -1;
- pointer-events: none;
-}
-
-.dark .VPHero::before {
- background: radial-gradient(ellipse 80% 50% at 50% -20%,
- rgba(99, 102, 241, 0.4),
- transparent),
- radial-gradient(ellipse 60% 40% at 80% 50%,
- rgba(139, 92, 246, 0.3),
- transparent),
- radial-gradient(ellipse 50% 30% at 20% 80%,
- rgba(99, 102, 241, 0.2),
- transparent);
-}
-
-/* dark/light radial transition */
-::view-transition-old(root),
-::view-transition-new(root) {
- animation: none;
- mix-blend-mode: normal;
-}
-
-::view-transition-old(root),
-.dark::view-transition-new(root) {
- z-index: 1;
-}
-
-::view-transition-new(root),
-.dark::view-transition-old(root) {
- z-index: 9999;
-}
-
-.VPSwitchAppearance {
- @apply !size-7 !bg-transparent !border-0 opacity-80 overflow-hidden mx-1;
-
- &::before {
- @apply hidden!;
- }
-
- & > .check {
- @apply m-auto !bg-transparent !shadow-none size-7!;
-
- & > .icon {
- @apply !size-7;
-
- & > span {
- @apply scale-175 top-1.75 left-1.75;
- }
- }
- }
-}
-
-.VPNav {
- @media (max-width: theme(--breakpoint-xl)) {
- z-index: 40 !important;
- }
-
- & > .VPNavBar {
- @apply bg-transparent! backdrop-blur-xs;
-
- /* Doc layout */
- & > .wrapper > .container > .content > .content-body {
- @apply bg-transparent gap-0;
- }
-
- & > .divider {
- @apply hidden;
- }
- }
-}
-
-
-#VPSidebarNav > .group {
- @apply border-t-0 min-h-11 pt-2.25;
-
- & > .VPSidebarItem {
- @apply pb-1;
-
- & > .item {
- @apply opacity-85;
-
- & > .text {
- @apply flex items-center text-base font-medium py-1.5;
-
- &::before {
- @apply mr-2.5 opacity-70;
-
- --tw-translate-y: calc(var(--spacing) * 0.175);
- translate: var(--tw-translate-x) var(--tw-translate-y);
- }
- }
-
- & > .caret {
- @apply scale-90 translate-y-0.5;
- }
- }
-
- & > .items {
- & > .VPSidebarItem {
- @apply pl-4 border-l dark:border-l-gray-600 rounded-none transition-all duration-100 ease-out;
-
- /* Separation between nested table of content */
- &:not(.collapsible),
- &.collapsible > .items > .VPSidebarItem {
- &:hover,
- &:focus,
- &.is-active.has-active {
- @apply rounded-r-lg border-transparent;
- background-color: color-mix(in srgb, var(--vp-c-brand-1) 30%, transparent 100%);
-
- & > .item > .indicator {
- background-color: var(--vp-c-brand-1);
- height: 100%;
- top:0;
- }
- }
- }
-
- &.collapsible {
- &:not(.is-active) {
- & > .item:has(.link):hover,
- & > .item:has(.link):focus {
- & > .caret {
- @apply -translate-x-2;
- }
- }
- }
-
- & > .item {
- @apply rounded-lg transition-all duration-100 ease-out;
-
- &:hover,
- &:focus {
- background-color: color-mix(in srgb, var(--vp-c-brand-1) 30%, transparent 100%);
-
- & > h3 {
- color: var(--vp-c-brand) !important;
- }
-
- & > .indicator {
- background-color: var(--vp-c-brand);
- }
- }
-
- & > .indicator {
- @apply translate-x-4;
- }
-
- & > h3 {
- @apply font-normal;
- }
-
- &:has(.link):hover,
- &:has(.link):focus {
- background-color: color-mix(in srgb, var(--vp-c-brand-1) 30%, transparent 100%);
-
- & > .indicator {
- background-color: var(--vp-c-brand);
- }
-
- & > .link > h3 {
- color: var(--vp-c-brand) !important;
- }
- }
- }
-
- &.is-active > .item {
- @apply pr-2;
- background-color: color-mix(in srgb, var(--vp-c-brand-1) 30%, transparent 100%);
- border-color: color-mix(in srgb, var(--vp-c-brand-1) 30%, transparent 100%) !important;
-
- & > .indicator {
- @apply translate-x-4;
- background-color: var(--vp-c-brand);
- }
-
- & > div > .caret-icon {
- color: var(--vp-c-brand);
- }
- }
-
- & > .items {
- @apply grid border-0 pl-0;
-
- & > .VPSidebarItem {
- @apply border-l pl-4 dark:border-l-gray-600;
- }
-
- & > .caret {
- &:hover,
- &:focus {
- background-color: transparent;
- }
- }
-
- & > .VPSidebarItem {
- @apply min-h-8;
- }
- }
- }
- }
- }
- }
-}
-
-.VPLocalNav {
- @apply !bg-transparent !border-0;
-
- & > .container {
- @apply relative flex bg-transparent mx-auto px-4 sm:px-6 md:px-0 lg:px-0 py-0 mt-2;
- max-width: 688px;
-
- & > .menu {
- @apply p-0;
- }
-
- & > .menu,
- & > .VPLocalNavOutlineDropdown > button {
- @apply !bg-white/80 dark:!bg-slate-800/80 px-2 py-0.75 rounded-xl border border-slate-700/10 dark:border-slate-300/10 backdrop-blur-md;
- }
-
- & > .VPLocalNavOutlineDropdown {
- @apply p-0 ml-auto;
-
- & > .items {
- @apply top-10 right-4 md:right-0 max-w-[calc(100%-32px)] sm:max-w-sm !bg-white/75 dark:!bg-slate-800/75 p-1 rounded-2xl border border-slate-700/10 dark:border-slate-300/10 backdrop-blur-md shadow-2xl shadow-black/5;
- left: unset;
- transition-duration: 0.5s;
- transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
-
- & > .header {
- @apply h-9 mb-2 bg-transparent;
- }
-
- & > .outline {
- @apply p-0 outline-0 bg-transparent;
-
- & > .nested {
- @apply px-0;
-
- & > li {
- & > a {
- @apply px-4;
- }
-
- & > a {
- &:hover,
- &:focus {
- @apply rounded-xl;
- background-color: var(
- --vp-c-brand-nav-active
- );
- color: var(--vp-c-brand);
- }
- }
-
- & > ul {
- @apply pl-4 pr-0;
-
- & > li {
- @apply border-l;
-
- & > a {
- @apply px-4;
- }
-
- & > a {
- &:hover,
- &:focus {
- @apply rounded-lg;
- background-color: var(
- --vp-c-brand-nav-active
- );
- color: var(--vp-c-brand);
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
-}
-
-.dark .VPSwitchAppearance .check{
- transform: translateX(0) !important;
-}
-.appearance + .social-links::before,
-.menu + .appearance::before{
- display: none !important;
-}
-
-
-button.copy::after {
- top: 0;
- width: 40px;
- height: 40px;
- color: var(--vp-icon-copy);
- background-size: contain;
-}
-
-button[title='Copy Code'].copy {
- @apply rounded-xl! transition-all!;
-
- &.copied {
- @apply rounded-l-none!;
- }
-
- &::before {
- @apply pr-1 translate-x-1 border-r-0 rounded-l-xl!;
- }
-}
-
-div[class*='language-'] {
- @apply rounded-none! sm:rounded-2xl!;
-}
-
-.VPDocAside {
- & > .VPDocAsideOutline > .content {
- @apply pl-0 border-l-0;
-
- & > .outline-marker {
- @apply z-10 w-0.75;
- transition-timing-function: var(--ease-out-expo);
- transition-duration: 0.3s;
- }
-
- & > .outline-title {
- @apply flex items-center font-medium text-gray-700 dark:text-gray-100;
-
- &::before {
- @apply inline-block size-3.5 bg-cover mr-1.5;
- content: '';
- background-image: url('data:image/svg+xml;utf8,
');
- }
-
- .dark &::before {
- @apply invert-100;
- }
- }
-
- .outline-link.active {
- @apply font-semibold relative overflow-visible;
- color: var(--vp-c-brand-1);
- transition-timing-function: var(--ease-out-expo);
- transition-duration: 0.35s;
- }
-
- .outline-link.active::before {
- @apply absolute top-0 right-0 h-full rounded-md;
- content: '';
- width: calc(100% + 20px);
- background-color: color-mix(in srgb, var(--vp-c-brand-1) 30%, transparent 100%);
- z-index: -1;
- }
-
- & > .VPDocOutlineItem {
- @apply border-l-2 border-gray-100 dark:border-gray-700 pl-4;
- }
- }
-}
-
-.aside-container {
- padding-top: calc(
- var(--vp-nav-height) + var(--vp-layout-top-height, 0px) +
- var(--vp-doc-top-height, 0px) + 24px
- ) !important;
-}
-
-::-webkit-scrollbar {
- width: 8px;
-}
-::-webkit-scrollbar-track {
- background: #0B0E14;
-}
-::-webkit-scrollbar-thumb {
- background: #334155;
- border-radius: 4px;
-}
-::-webkit-scrollbar-thumb:hover {
- background: #475569;
-}
-
-.glass-panel {
- background: rgba(21, 26, 35, 0.6);
- backdrop-filter: blur(12px);
- -webkit-backdrop-filter: blur(12px);
- border: 1px solid rgba(255, 255, 255, 0.05);
-}
-
-.glass-card {
- background: linear-gradient(180deg, rgba(30, 41, 59, 0.4) 0%, rgba(15, 23, 42, 0.4) 100%);
- backdrop-filter: blur(8px);
- border: 1px solid rgba(255, 255, 255, 0.05);
- transition: all 0.3s ease;
-}
-.glass-card:hover {
- transform: translateY(-5px);
- border-color: rgba(255, 72, 149, 0.3);
- box-shadow: 0 10px 40px -10px rgba(255, 72, 149, 0.15);
-
-}
-.token.keyword { color: #c678dd; } /* Purple */
-.token.string { color: #98c379; } /* Green */
-.token.function { color: #61afef; } /* Blue */
-.token.number { color: #d19a66; } /* Orange */
-.token.comment { color: #5c6370; font-style: italic; } /* Grey */
-.token.class-name { color: #e5c07b; } /* Yellow */
-
-
-.math-font {
- font-family: 'Times New Roman', Times, serif;
- font-style: italic;
-}
\ No newline at end of file
diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts
index e149760..d626e3e 100644
--- a/.vitepress/theme/index.ts
+++ b/.vitepress/theme/index.ts
@@ -5,7 +5,9 @@ import Layout from '../components/Layouts.vue'
import Ray from '../components/Ray.vue'
import Hero from '../components/Hero.vue'
import EYN from '../components/EYN.vue'
-import './custom.css'
+
+import './vars.css'
+import './override.css'
export default {
extends: DefaultTheme,
diff --git a/.vitepress/theme/override.css b/.vitepress/theme/override.css
new file mode 100644
index 0000000..7514f47
--- /dev/null
+++ b/.vitepress/theme/override.css
@@ -0,0 +1,76 @@
+
+
+/* Nav
+/* ================================================================ */
+
+.VPNav {
+ --at-apply: 'bg-transparent! fixed top-0 left-0 w-full backdrop-blur-sm transition-all duration-300';
+ @media (max-width: theme(--breakpoint-xl)) {
+ z-index: 40 !important;
+ }
+
+ &>.VPNavBar {
+ --at-apply: 'bg-transparent! ';
+
+ /* Doc layout */
+ &>.wrapper>.container>.content>.content-body {
+ --at-apply: 'bg-transparent gap-0';
+ }
+
+ &>.divider {
+ --at-apply: 'hidden';
+ }
+ }
+}
+
+.VPSocialLinks::before {
+ --at-apply: 'hidden';
+}
+
+/*
+/* View transition
+/* ================================================================ */
+::view-transition-old(root),
+::view-transition-new(root) {
+ animation: none;
+ mix-blend-mode: normal;
+}
+
+::view-transition-old(root),
+.dark::view-transition-new(root) {
+ z-index: 1;
+}
+
+::view-transition-new(root),
+.dark::view-transition-old(root) {
+ z-index: 9999;
+}
+
+.group + .group{
+ border: none !important;
+}
+
+.VPNavBarTitle.has-sidebar .title{
+ border: none !important;
+}
+
+/* Scrollbar
+/* ================================================================ */
+
+::-webkit-scrollbar {
+ width: 4px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+ background: theme('colors.slate.600');
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: var(--vp-c-brand);
+}
diff --git a/.vitepress/theme/vars.css b/.vitepress/theme/vars.css
new file mode 100644
index 0000000..7057659
--- /dev/null
+++ b/.vitepress/theme/vars.css
@@ -0,0 +1,109 @@
+/**
+ * Colors
+ * =============================================================== */
+
+:root {
+ --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+
+ --vp-c-brand-1: theme('colors.indigo.500');
+ --vp-c-brand-2: theme('colors.indigo.400');
+ --vp-c-brand-light: theme('colors.indigo.300');
+ --vp-c-bg-alt: theme('colors.slate.100');
+ --vp-c-brand-dark: theme('colors.indigo.500');
+ --vp-c-border: theme('colors.slate.100');
+ --twoslash-border-color: theme('colors.slate.100');
+ --vp-code-block-bg: #f6f8fa;
+ --vp-code-copy-code-bg: #dedede;
+ --vp-code-copy-code-hover-bg: #dedede;
+ --vp-c-text-dark-2: #8f8f8f;
+ --vp-c-text-dark-3: #8f8f8f;
+ --vp-sidebar-bg-color: var(--vp-c-bg);
+ --vp-custom-block-tip-border: transparent;
+ --vp-custom-block-tip-text: var(--vp-c-text-1);
+ --vp-nav-bg-color: color-mix(in srgb, var(--vp-c-bg) 95%, transparent);
+ --vp-custom-block-tip-bg: #ecfdf5;
+ --vp-custom-block-tip-code-bg: #cdf7dc;
+ --vp-custom-block-tip-border: #dcfce7;
+ --vp-custom-block-tip-text: #15803d;
+ --vp-c-brand-nav-active: color-mix(
+ in srgb,
+ var(--vp-c-brand-light) 15%,
+ transparent 100%
+ );
+ --vp-nav-logo-height: 28px;
+
+ --twoslash-border-color: theme('colors.slate.200');
+ --vp-c-bg-elv: theme('colors.slate.100');
+
+ --vp-code-copy-code-bg: theme('colors.slate.50');
+ --vp-code-copy-code-hover-bg: #fff;
+ --vp-code-copy-code-border-color: theme('colors.slate.200');
+}
+
+.dark {
+ --vp-c-brand-1: theme('colors.indigo.400');
+ --vp-c-brand-2: theme('colors.indigo.500');
+ --vp-c-brand-light: theme('colors.indigo.600');
+ --vp-c-bg-alt: theme('colors.slate.100');
+ --vp-c-brand-dark: theme('colors.indigo.700');
+ --vp-c-bg: theme('colors.slate.900');
+ --vp-c-border: theme('colors.slate.100');
+ --twoslash-border-color: theme('colors.slate.100');
+ --vp-c-bg-alt: theme('colors.slate.800');
+ --vp-c-bg-soft: theme('colors.slate.800');
+ --vp-c-divider: theme('colors.slate.700');
+ --vp-code-block-bg: theme('colors.slate.800');
+ --vp-c-bg-alpha-with-backdrop: #1f2937bf;
+ --vp-code-copy-code-bg: theme('colors.slate.800');
+ --vp-code-copy-code-hover-bg: theme('colors.slate.800');
+ --vp-c-text-dark-2: #8a8a8a;
+ --vp-c-text-dark-3: #8a8a8a;
+ --vp-custom-block-tip-bg: #064e3b;
+ --vp-custom-block-tip-border: #052e16;
+ --vp-custom-block-tip-text: #f0fdf4;
+ --vp-c-brand-nav-active: color-mix(
+ in srgb,
+ var(--vp-c-brand-dark) 30%,
+ transparent 100%
+ );
+
+ --twoslash-border-color: var(--color-slate-800);
+ --vp-c-bg-elv: theme('colors.slate.800');
+ --vp-c-neutral-inverse: theme('colors.slate.800');
+
+ --vp-code-copy-code-bg: theme('colors.slate.800');
+ --vp-code-copy-code-hover-bg: theme('colors.slate.700');
+ --vp-code-copy-code-border-color: theme('colors.slate.600');
+}
+
+/**
+ * Component: Button
+ * =============================================================== */
+
+:root {
+ --vp-button-brand-border: var(--vp-c-brand-light);
+ --vp-button-brand-text: var(--vp-c-white);
+ --vp-button-brand-bg: var(--vp-c-brand-1);
+ --vp-button-brand-hover-border: var(--vp-c-brand-light);
+ --vp-button-brand-hover-text: var(--vp-c-white);
+ --vp-button-brand-hover-bg: var(--vp-c-brand-light);
+ --vp-button-brand-active-border: var(--vp-c-brand-light);
+ --vp-button-brand-active-text: var(--vp-c-white);
+ --vp-button-brand-active-bg: var(--vp-button-brand-bg);
+}
+
+/**
+ * Component: Custom Block
+ * =============================================================== */
+
+:root {
+ --vp-custom-block-tip-border: var(--vp-c-brand-1);
+ --vp-custom-block-tip-text: var(--vp-c-brand-darker);
+ --vp-custom-block-tip-bg: var(--vp-c-brand-dimm);
+}
+
+.dark {
+ --vp-custom-block-tip-border: var(--vp-c-brand-1);
+ --vp-custom-block-tip-text: var(--vp-c-brand-lightest);
+ --vp-custom-block-tip-bg: var(--vp-c-brand-dimm);
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..f6a4c36
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "dart.flutterSdkPath": ".fvm/versions/3.35.6"
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be72552..83686e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## Unreleased
+
+## 0.1.4 - 2026-02-24
+
+### Added
+
+- Compile-time evaluability annotation: parser now annotates every AST node with `EvaluabilityInfo` during parse.
+- New AST metadata API via `Expression.compileTimeEvaluabilityInfo` for tooling and diagnostics.
+- Added TDD coverage for parser-time annotation and context-aware resolution in `test/core/compile_time_evaluability_test.dart`.
+- Added v0.1.4 improvement plan to roadmap (correctness, benchmarks, docs, migration guidance).
+- JSON AST export now supports `toJson(includeEvaluability: true)` to include optional compile-time evaluability metadata.
+- Added CI workflow with SymPy smoke verification (Dart export + Python validation with tolerance checks).
+- Added evaluability fast-path micro-benchmark and CI guardrail artifact generation.
+- Added migration guide for deprecated `isValid()` / `validate()` APIs: `doc/guide/migration-validate.md`.
+
+### Fixed
+
+- `getEvaluability()` now uses compile-time metadata when available, avoiding repeated full-tree traversals for parsed expressions.
+- Normalized structural cache keys for floating-point edge values so `-0.0` and `0.0` map to the same key and `NaN` is handled consistently.
+- Updated README and docs links/examples to match current project structure and versioning.
+
+### Removed
+
## 0.1.3 - 2026-01-12
**Boolean Logic, Bug Fixes & Doc Improvements**
diff --git a/README.md b/README.md
index b0ffb8d..2e2175a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# TeXpr 🧮
-[](https://github.com/xirf/texpr)
+[](https://github.com/xirf/texpr/actions/workflows/ci.yml)
[](https://github.com/xirf/texpr)
[](LICENSE)
[](https://pub.dev/packages/texpr)
@@ -29,7 +29,7 @@ Add the dependency to your `pubspec.yaml`:
```yaml
dependencies:
- texpr: ^0.0.1
+ texpr: ^0.1.4
```
@@ -100,19 +100,20 @@ final matrixResult = evaluator.evaluate(r'''
## Exceptions
-* [TexprException](exceptions.md): Base class for all library exceptions.
-* [ValidationResult](exceptions.md#validationresult): Detailed validation information.
+* [TexprException](doc/reference/exceptions.md): Base class for all library exceptions.
+* [ValidationResult](doc/reference/exceptions.md): Detailed validation information.
## Security
-* [Security Considerations](../security.md): Overview of security mitigations and limits.
+* [Security Considerations](doc/advanced/security.md): Overview of security mitigations and limits.
The parser provides error location offsets and suggestions for syntax errors.
```dart
-final validation = evaluator.validate(r'\frac{1{2}');
-if (!validation.isValid) {
- print('Error at ${validation.position}: ${validation.errorMessage}');
- // Suggestion: "Add a closing brace '}'"
+try {
+ evaluator.parse(r'\frac{1{2}');
+} on TexprException catch (e) {
+ print('Error at ${e.position}: ${e.message}');
+ print('Suggestion: ${e.suggestion}');
}
// Function name suggestions
@@ -197,14 +198,15 @@ Below is a selection of examples showcasing the library's capabilities.
## 📖 Documentation
-* **[Getting Started](doc/getting_started.md)**
-* **[LaTeX Commands Reference](doc/latex_commands.md)**
-* **[Symbolic Algebra](doc/symbolic_algebra.md)**
-* **[Boolean Logic](doc/guide/logic.md)**
-* **[Function Reference](doc/functions/README.md)**
-* **[Extending the Library](doc/extensions.md)**
-* **[Export Features](doc/features/export.md)**
-* **[Security Considerations](doc/security.md)**
+* **[Guide Index](doc/guide/index.md)**
+* **[Quick Start](doc/guide/quick-start.md)**
+* **[Installation](doc/guide/installation.md)**
+* **[API Reference](doc/reference/api.md)**
+* **[LaTeX Grammar](doc/reference/grammar.md)**
+* **[Functions](doc/reference/functions.md)**
+* **[Known Issues](doc/reference/known-issues.md)**
+* **[Performance](doc/how-it-works/performance.md)**
+* **[Deprecation Migration (`validate` / `isValid`)](doc/guide/migration-validate.md)**
## 🤝 Contributing
diff --git a/ROADMAP.md b/ROADMAP.md
index 2223f22..d717662 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -4,7 +4,7 @@
---
-## Current State (v0.2.0)
+## Current State (v0.1.4)
### ✅ What Works
@@ -205,7 +205,7 @@ We aim to be the **best Dart library for evaluating mathematical expressions wri
> These are acknowledged limitations in our test suite:
-- **No automated cross-CAS validation** — Results are manually verified, not programmatically compared to SymPy/Mathematica
+- **Cross-CAS coverage is smoke-level** — CI compares representative cases against SymPy, but not exhaustive property-level equivalence
- **No mutation testing** — Code coverage is measured, but mutation coverage is not
---
@@ -254,7 +254,7 @@ We aim to be the **best Dart library for evaluating mathematical expressions wri
| Task | Status | Description |
| -------------------------- | ------ | ----------------------------------------------------------------- |
| Evaluability enum | ✅ | Add `Evaluability.numeric`, `.symbolic`, `.unevaluable` to nodes |
-| Compile-time evaluability | 📋 | Parser annotates AST with evaluability at parse time |
+| Compile-time evaluability | ✅ | Parser annotates AST with evaluability at parse time |
| Semantic invariant testing | ✅ | Property-based tests for derivative correctness, round-trip, etc. |
**Why this matters:** As the parsed surface area grows (tensors, quantifiers, set notation), the gap between "parses successfully" and "has computable meaning" becomes a usability hazard. Explicit evaluability prevents false expectations.
@@ -281,5 +281,26 @@ enum Evaluability {
---
-**Last Updated:** 2026-01-08
+### Phase 7: Next Release Plan (v0.1.4)
+
+**Goal:** Improve correctness at numeric edges, strengthen validation rigor, and reduce developer confusion without introducing major API churn.
+
+| Task | Status | Description |
+| --------------------------------------------------- | ------ | --------------------------------------------------------------------------- |
+| Cache key normalization for `-0.0` and `NaN` | ✅ | Canonicalize floating-point edge values in cache keys to prevent collisions |
+| Evaluability metadata visibility in JSON export | ✅ | Optionally include compile-time evaluability info in AST JSON output |
+| Automated cross-CAS validation smoke suite | ✅ | Add CI-level numeric tolerance checks against SymPy for representative cases |
+| Benchmark guardrail for evaluability fast-path | ✅ | Add micro-benchmark comparing cached vs visitor-based evaluability lookup |
+| README and docs consistency pass | ✅ | Align version examples, test-count claims, and links with current release |
+| Deprecation migration guide (`validate`/`isValid`) | ✅ | Add explicit migration snippets before eventual 1.0 removal |
+
+**Release Gate (v0.1.4):**
+- Cache key edge-case tests pass for signed zero and NaN.
+- Benchmark artifact is reproducible in CI and checked into benchmark docs.
+- Docs no longer contain stale version or outdated compatibility claims.
+- Public API changes remain additive only (no breaking changes).
+
+---
+
+**Last Updated:** 2026-02-24
diff --git a/benchmark/README.md b/benchmark/README.md
new file mode 100644
index 0000000..45e7982
--- /dev/null
+++ b/benchmark/README.md
@@ -0,0 +1,28 @@
+# Benchmarks
+
+This directory contains micro-benchmarks and comparison benchmarks for TeXpr.
+
+## Evaluability Fast-Path Guardrail
+
+`evaluability_fast_path_benchmark.dart` compares:
+
+- **Annotated path**: `Expression.getEvaluability()` on parser-annotated AST nodes
+- **Visitor path**: `Expression.getEvaluability()` fallback visitor traversal on unannotated AST nodes
+
+### Run manually
+
+```bash
+dart run benchmark/evaluability_fast_path_benchmark.dart
+```
+
+### Run with guardrail enforcement
+
+```bash
+dart run benchmark/evaluability_fast_path_benchmark.dart --enforce --min-speedup=1.02 --json-out=benchmark/results/evaluability_fast_path.json
+```
+
+The benchmark exits non-zero when `--enforce` is passed and the measured speedup is below the configured threshold. CI runs this command and uploads the JSON result as an artifact.
+
+## Cross-Language Comparison
+
+See `benchmark/comparison/README.md` for Dart vs Python vs JavaScript comparison benchmarks.
diff --git a/benchmark/evaluability_fast_path_benchmark.dart b/benchmark/evaluability_fast_path_benchmark.dart
new file mode 100644
index 0000000..62f630a
--- /dev/null
+++ b/benchmark/evaluability_fast_path_benchmark.dart
@@ -0,0 +1,116 @@
+library;
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:texpr/texpr.dart';
+
+void main(List
args) {
+ final enforce = args.contains('--enforce');
+ final minSpeedup = _readDoubleArg(args, '--min-speedup', 1.02);
+ final jsonOut = _readStringArg(args, '--json-out');
+
+ final evaluator = Texpr(cacheConfig: CacheConfig.disabled);
+
+ final parsedAnnotated = evaluator.parse(r'x^2 + y^2 + 1');
+
+ final manualUnannotated = BinaryOp(
+ BinaryOp(
+ BinaryOp(
+ const Variable('x'), BinaryOperator.power, const NumberLiteral(2)),
+ BinaryOperator.add,
+ BinaryOp(
+ const Variable('y'), BinaryOperator.power, const NumberLiteral(2)),
+ ),
+ BinaryOperator.add,
+ const NumberLiteral(1),
+ );
+
+ const knownVars = {'x', 'y'};
+
+ const warmupIterations = 20000;
+ const benchmarkIterations = 400000;
+
+ for (var i = 0; i < warmupIterations; i++) {
+ parsedAnnotated.getEvaluability(knownVars);
+ manualUnannotated.getEvaluability(knownVars);
+ }
+
+ final annotatedMicros = _timeMicros(benchmarkIterations, () {
+ parsedAnnotated.getEvaluability(knownVars);
+ });
+
+ final visitorMicros = _timeMicros(benchmarkIterations, () {
+ manualUnannotated.getEvaluability(knownVars);
+ });
+
+ final speedup = visitorMicros / annotatedMicros;
+
+ final result = {
+ 'benchmark': 'evaluability_fast_path',
+ 'iterations': benchmarkIterations,
+ 'annotated_total_us': annotatedMicros,
+ 'visitor_total_us': visitorMicros,
+ 'annotated_avg_us': annotatedMicros / benchmarkIterations,
+ 'visitor_avg_us': visitorMicros / benchmarkIterations,
+ 'speedup': speedup,
+ 'enforced': enforce,
+ 'min_speedup': minSpeedup,
+ 'passed': !enforce || speedup >= minSpeedup,
+ 'timestamp_utc': DateTime.now().toUtc().toIso8601String(),
+ };
+
+ print('Evaluability Fast-Path Benchmark');
+ print(' iterations: $benchmarkIterations');
+ print(
+ ' annotated avg: ${(annotatedMicros / benchmarkIterations).toStringAsFixed(6)} µs');
+ print(
+ ' visitor avg: ${(visitorMicros / benchmarkIterations).toStringAsFixed(6)} µs');
+ print(
+ ' speedup: ${speedup.toStringAsFixed(3)}x (visitor / annotated)');
+
+ if (jsonOut != null && jsonOut.isNotEmpty) {
+ final outputFile = File(jsonOut);
+ outputFile.parent.createSync(recursive: true);
+ outputFile.writeAsStringSync(
+ const JsonEncoder.withIndent(' ').convert(result),
+ );
+ print(' wrote: ${outputFile.path}');
+ }
+
+ if (enforce && speedup < minSpeedup) {
+ stderr.writeln(
+ 'Guardrail failed: speedup ${speedup.toStringAsFixed(3)}x < required ${minSpeedup.toStringAsFixed(3)}x',
+ );
+ exitCode = 1;
+ }
+}
+
+int _timeMicros(int iterations, void Function() fn) {
+ final stopwatch = Stopwatch()..start();
+ for (var i = 0; i < iterations; i++) {
+ fn();
+ }
+ stopwatch.stop();
+ return stopwatch.elapsedMicroseconds;
+}
+
+double _readDoubleArg(List args, String key, double fallback) {
+ final prefix = '$key=';
+ for (final arg in args) {
+ if (arg.startsWith(prefix)) {
+ return double.tryParse(arg.substring(prefix.length)) ?? fallback;
+ }
+ }
+ return fallback;
+}
+
+String? _readStringArg(List args, String key) {
+ final prefix = '$key=';
+ for (final arg in args) {
+ if (arg.startsWith(prefix)) {
+ return arg.substring(prefix.length);
+ }
+ }
+ return null;
+}
diff --git a/bun.lock b/bun.lock
index 3fd5c9f..ac4c5e9 100644
--- a/bun.lock
+++ b/bun.lock
@@ -5,17 +5,14 @@
"": {
"name": "texpr-docs",
"dependencies": {
- "@tailwindcss/postcss": "^4.1.18",
"lucide-vue-next": "^0.562.0",
- "tailwindcss": "^4.1.18",
},
"devDependencies": {
+ "@iconify-json/solar": "^1.2.5",
"@shikijs/vitepress-twoslash": "^3.21.0",
- "@tailwindcss/vite": "^4.1.18",
"@types/node": "^25.0.6",
- "autoprefixer": "^10.4.23",
"markdown-it-mathjax3": "^4.0.0",
- "postcss": "^8.5.6",
+ "unocss": "^66.5.12",
"vitepress": "^1.6.4",
"vitepress-plugin-llms": "^1.10.0",
},
@@ -58,13 +55,21 @@
"@algolia/requester-node-http": ["@algolia/requester-node-http@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2" } }, "sha512-ciPihkletp7ttweJ8Zt+GukSVLp2ANJHU+9ttiSxsJZThXc4Y2yJ8HGVWesW5jN1zrsZsezN71KrMx/iZsOYpg=="],
- "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
+ "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
+
+ "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
+
+ "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
- "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
+ "@babel/parser": ["@babel/parser@7.27.7", "", { "dependencies": { "@babel/types": "^7.27.7" }, "bin": "./bin/babel-parser.js" }, "sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q=="],
+
+ "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
+
+ "@babel/traverse": ["@babel/traverse@7.27.7", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.7", "@babel/template": "^7.27.2", "@babel/types": "^7.27.7", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-X6ZlfR/O/s5EQ/SnUSLzr+6kGnkg8HXGMzpgsMsrJVcfDtH1vIp6ctCN4eZ1LS5c0+te5Cb6Y514fASjMRJ1nw=="],
"@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
@@ -128,8 +133,12 @@
"@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.65", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-v/O0UeqrDz6ASuRVE5g2Puo5aWyej4M/CxX6WYDBARgswwxK0mp3VQbGgPFEAAUU9QN02IjTgjMuO021gpWf2w=="],
+ "@iconify-json/solar": ["@iconify-json/solar@1.2.5", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-WMAiNwchU8zhfrySww6KQBRIBbsQ6SvgIu2yA+CHGyMima/0KQwT5MXogrZPJGoQF+1Ye3Qj6K+1CiyNn3YkoA=="],
+
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
+ "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
+
"@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="],
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
@@ -144,6 +153,10 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+ "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
+
+ "@quansync/fs": ["@quansync/fs@1.0.0", "", { "dependencies": { "quansync": "^1.0.0" } }, "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ=="],
+
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.1", "", { "os": "android", "cpu": "arm" }, "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg=="],
@@ -214,63 +227,79 @@
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
- "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="],
+ "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
- "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="],
+ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
- "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="],
+ "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
- "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="],
+ "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
- "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="],
+ "@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="],
- "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="],
+ "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
- "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="],
+ "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
- "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="],
+ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
- "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="],
+ "@types/node": ["@types/node@25.0.6", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q=="],
- "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="],
+ "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
- "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="],
+ "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="],
- "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="],
+ "@typescript/vfs": ["@typescript/vfs@1.6.2", "", { "dependencies": { "debug": "^4.1.1" }, "peerDependencies": { "typescript": "*" } }, "sha512-hoBwJwcbKHmvd2QVebiytN1aELvpk9B74B4L1mFm/XT1Q/VOYAWl2vQ9AWRFtQq8zmz6enTpfTV8WRc4ATjW/g=="],
- "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="],
+ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
- "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="],
+ "@unocss/astro": ["@unocss/astro@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "@unocss/reset": "66.6.0", "@unocss/vite": "66.6.0" }, "peerDependencies": { "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" }, "optionalPeers": ["vite"] }, "sha512-HCDgZnoXv6pZGUK9N4ko7ZeBP1YTIP8IFj0Vr7pXVdv9sGR9CofoueFbclTvPQ065UHSsG+WI+JO5z9/BGd5fw=="],
- "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="],
+ "@unocss/cli": ["@unocss/cli@66.6.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "@unocss/config": "66.6.0", "@unocss/core": "66.6.0", "@unocss/preset-wind3": "66.6.0", "@unocss/preset-wind4": "66.6.0", "@unocss/transformer-directives": "66.6.0", "cac": "^6.7.14", "chokidar": "^5.0.0", "colorette": "^2.0.20", "consola": "^3.4.2", "magic-string": "^0.30.21", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "tinyglobby": "^0.2.15", "unplugin-utils": "^0.3.1" }, "bin": { "unocss": "bin/unocss.mjs" } }, "sha512-wtA6YBIvW2f8FISdYS8rx+B3ZGTUjw3YO3Fxz1ApUCHinYSdz8SoNWShH1I61LWbcjJ4hbjI/D9t/Dmgp6vmiQ=="],
- "@tailwindcss/vite": ["@tailwindcss/vite@4.1.18", "", { "dependencies": { "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "tailwindcss": "4.1.18" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA=="],
+ "@unocss/config": ["@unocss/config@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "unconfig": "^7.4.2" } }, "sha512-YvNk/b9jGmT1TQGDbHR+0VALnTsPLzSTzw90t09ggny9YxeF0XnsP06M5+H0EJPwpmdigBC++KEIMaK8SYDsaQ=="],
- "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
+ "@unocss/core": ["@unocss/core@66.6.0", "", {}, "sha512-Sxm7HmhsPIIzxbPnWembPyobuCeA5j9KxL+jIOW2c+kZiTFjHeju7vuVWX9jmAMMC+UyDuuCQ4yE+kBo3Y7SWQ=="],
- "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+ "@unocss/extractor-arbitrary-variants": ["@unocss/extractor-arbitrary-variants@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0" } }, "sha512-AsCmpbre4hQb+cKOf3gHUeYlF7guR/aCKZvw53VBk12qY5wNF7LdfIx4zWc5LFVCoRxIZlU2C7L4/Tt7AkiFMA=="],
- "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
+ "@unocss/inspector": ["@unocss/inspector@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "@unocss/rule-utils": "66.6.0", "colorette": "^2.0.20", "gzip-size": "^6.0.0", "sirv": "^3.0.2", "vue-flow-layout": "^0.2.0" } }, "sha512-BvdY8ah+OTmzFMb+z8RZkaF15+PWRFt9S2bOARkkRBubybX9EE1rxM07l74kO5Dj16++CS4nO15XFq39pPoBvg=="],
- "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
+ "@unocss/postcss": ["@unocss/postcss@66.6.0", "", { "dependencies": { "@unocss/config": "66.6.0", "@unocss/core": "66.6.0", "@unocss/rule-utils": "66.6.0", "css-tree": "^3.1.0", "postcss": "^8.5.6", "tinyglobby": "^0.2.15" } }, "sha512-Tn8l8JMXJcTgzrPHSukpRBnVIt561bQCUle7jW8NXk61vYBy8p52nEBkNy5QMXybaCih5ghg2d/+nDIAl9YIpw=="],
- "@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="],
+ "@unocss/preset-attributify": ["@unocss/preset-attributify@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0" } }, "sha512-IfGZT9LjQkfpJaVjDtFMxOlrEoj7m1DCztRdby0FaptXChE7vdgRkYFl8HDeXMkEIlzV1YTHuIarwJ+tV+1SRQ=="],
- "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
+ "@unocss/preset-icons": ["@unocss/preset-icons@66.6.0", "", { "dependencies": { "@iconify/utils": "^3.1.0", "@unocss/core": "66.6.0", "ofetch": "^1.5.1" } }, "sha512-ObgmN9SsqU7TiClNvy+mQDyqzwOhlBXT0whXFp3iiBfSbnxUIDyf4Pr/2hwxnAWrCWCQj7xw2H0Y0sDtXq8XkA=="],
- "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
+ "@unocss/preset-mini": ["@unocss/preset-mini@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "@unocss/extractor-arbitrary-variants": "66.6.0", "@unocss/rule-utils": "66.6.0" } }, "sha512-8bQyTuMJcry/z4JTDsQokI0187/1CJIkVx9hr9eEbKf/gWti538P8ktKEmHCf8IyT0At5dfP9oLHLCUzVetdbA=="],
- "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
+ "@unocss/preset-tagify": ["@unocss/preset-tagify@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0" } }, "sha512-Vy9olM61lqTDxcHbfDkIJNp9LF2m8K9I/F2J0diD+BVZgpym1QRK6+aZaeAPJuMCyQrOk87dm7VIiAPFtLOXFA=="],
- "@types/node": ["@types/node@25.0.6", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q=="],
+ "@unocss/preset-typography": ["@unocss/preset-typography@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "@unocss/rule-utils": "66.6.0" } }, "sha512-vDogO+jaatGSAoZqqa9+GJ18WbrwYzJr5UyIFUQqXJ5TUDaWzKb4Qhty2WnOtjQaf4sOMML8JFA/cydZl+Rjjw=="],
- "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+ "@unocss/preset-uno": ["@unocss/preset-uno@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "@unocss/preset-wind3": "66.6.0" } }, "sha512-xDppgdgFk+JNrL9jhqhrn6H9IS8P10p1HSsWOYF+9owg43zqpeNpzDMvZGwq8uxq6guyB1fu6l4YzO+bDZnwvw=="],
- "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="],
+ "@unocss/preset-web-fonts": ["@unocss/preset-web-fonts@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "ofetch": "^1.5.1" } }, "sha512-Mqb8bVSAfDEnkGyAl8ZPdvaojq3YSow4lvxGCOm7nHJFIXkRKgYBgD3tmm+rvO81CUtihZd7hdw0Ay+8pYrlTw=="],
- "@typescript/vfs": ["@typescript/vfs@1.6.2", "", { "dependencies": { "debug": "^4.1.1" }, "peerDependencies": { "typescript": "*" } }, "sha512-hoBwJwcbKHmvd2QVebiytN1aELvpk9B74B4L1mFm/XT1Q/VOYAWl2vQ9AWRFtQq8zmz6enTpfTV8WRc4ATjW/g=="],
+ "@unocss/preset-wind": ["@unocss/preset-wind@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "@unocss/preset-wind3": "66.6.0" } }, "sha512-6uVq3/gV1MD9ZsycrYLP6OvAS9kI1oGAIuccKKspZHW3jqwEhJmPofDD4pYwbkx4i4zSWzoLakcfp9d67Szjqg=="],
- "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
+ "@unocss/preset-wind3": ["@unocss/preset-wind3@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "@unocss/preset-mini": "66.6.0", "@unocss/rule-utils": "66.6.0" } }, "sha512-7gzswF810BCSru7pF01BsMzGZbfrsWT5GV6JJLkhROS2pPjeNOpqy2VEfiavv5z09iGSIESeOFMlXr5ORuLZrg=="],
+
+ "@unocss/preset-wind4": ["@unocss/preset-wind4@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "@unocss/extractor-arbitrary-variants": "66.6.0", "@unocss/rule-utils": "66.6.0" } }, "sha512-1yyo9fmB+r5C92kSHU7lIaqGJdvz5UQyYZxYDxSmWLAUzWEK5HBRj6OkSF6rUnz+Yd4JvgOgACZNOShVOezPlA=="],
+
+ "@unocss/reset": ["@unocss/reset@66.6.0", "", {}, "sha512-OQK5F7Dzx0wWDSPTYEz7NRP9pekufSAkjxfKOyKokiXOUzVTg8Yx8sOvCsA3Oi0Rx5WlsO2LN+MOBekpkrttXg=="],
+
+ "@unocss/rule-utils": ["@unocss/rule-utils@66.6.0", "", { "dependencies": { "@unocss/core": "^66.6.0", "magic-string": "^0.30.21" } }, "sha512-v16l6p5VrefDx8P/gzWnp0p6/hCA0vZ4UMUN6SxHGVE6V+IBpX6I6Du3Egk9TdkhZ7o+Pe1NHxksHcjT0V/tww=="],
+
+ "@unocss/transformer-attributify-jsx": ["@unocss/transformer-attributify-jsx@66.6.0", "", { "dependencies": { "@babel/parser": "7.27.7", "@babel/traverse": "7.27.7", "@unocss/core": "66.6.0" } }, "sha512-fzjLVlhYO8JdHzIusRKAva5ZOnA4deOVYuiM6HVpbX7P19479TVHZgeSV+AG0BWLhmIJ2cer+n3/CIO5nodT6g=="],
+
+ "@unocss/transformer-compile-class": ["@unocss/transformer-compile-class@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0" } }, "sha512-OkwdbIfsbs8dtHIfBaoya/SPHZUJeogvJl2BpJb4/3nY/tWBZB/+i2vPMAML3D9aQYZAuC7uqcTRGNbuvyyy+w=="],
+
+ "@unocss/transformer-directives": ["@unocss/transformer-directives@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0", "@unocss/rule-utils": "66.6.0", "css-tree": "^3.1.0" } }, "sha512-2Z4FFjJI/bH6kKGuuuO0DpFEVSFOhFnGLTFK8y9BGz0cIOQOIuEKTemM7QLqPuyRSORBO1RKvcKvA3DV0n1X7g=="],
+
+ "@unocss/transformer-variant-group": ["@unocss/transformer-variant-group@66.6.0", "", { "dependencies": { "@unocss/core": "66.6.0" } }, "sha512-kWYYcrn8ZFKLVCU6kB8yaQY9iYgx3/XhPb9c0XZZ5QzWjoGffrl6XLUk8XrjR/yxC3qwHg/WizzsmsQ2OXF6Qg=="],
+
+ "@unocss/vite": ["@unocss/vite@66.6.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "@unocss/config": "66.6.0", "@unocss/core": "66.6.0", "@unocss/inspector": "66.6.0", "chokidar": "^5.0.0", "magic-string": "^0.30.21", "pathe": "^2.0.3", "tinyglobby": "^0.2.15", "unplugin-utils": "^0.3.1" }, "peerDependencies": { "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" } }, "sha512-SC0/rX0xSjdu8Jaj98XztHOuvXHWDVk0YaHKRAQks2Oj3yyqAOrhzhDUH0zzFaQWf5bsKVYK40H+h4rMk9vm5Q=="],
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="],
@@ -314,6 +343,8 @@
"@xmldom/xmldom": ["@xmldom/xmldom@0.9.8", "", {}, "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A=="],
+ "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
+
"algoliasearch": ["algoliasearch@5.46.2", "", { "dependencies": { "@algolia/abtesting": "1.12.2", "@algolia/client-abtesting": "5.46.2", "@algolia/client-analytics": "5.46.2", "@algolia/client-common": "5.46.2", "@algolia/client-insights": "5.46.2", "@algolia/client-personalization": "5.46.2", "@algolia/client-query-suggestions": "5.46.2", "@algolia/client-search": "5.46.2", "@algolia/ingestion": "1.46.2", "@algolia/monitoring": "1.46.2", "@algolia/recommend": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-qqAXW9QvKf2tTyhpDA4qXv1IfBwD2eduSW6tUEBFIfCeE9gn9HQ9I5+MaKoenRuHrzk5sQoNh1/iof8mY7uD6Q=="],
"alien-signals": ["alien-signals@3.1.2", "", {}, "sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw=="],
@@ -326,19 +357,13 @@
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
- "autoprefixer": ["autoprefixer@10.4.23", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001760", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA=="],
-
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
- "baseline-browser-mapping": ["baseline-browser-mapping@2.9.14", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg=="],
-
"birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="],
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
- "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
-
- "caniuse-lite": ["caniuse-lite@1.0.30001764", "", {}, "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g=="],
+ "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
@@ -352,20 +377,30 @@
"cheerio-select": ["cheerio-select@1.6.0", "", { "dependencies": { "css-select": "^4.3.0", "css-what": "^6.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.3.1", "domutils": "^2.8.0" } }, "sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g=="],
+ "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
+
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+ "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
+
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
"commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="],
+ "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
+
+ "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
+
"copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "^5.2.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="],
"css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="],
+ "css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="],
+
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
@@ -374,8 +409,12 @@
"decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
+ "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
+
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
+ "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
+
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
@@ -388,14 +427,12 @@
"domutils": ["domutils@2.8.0", "", { "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.2.0" } }, "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="],
- "electron-to-chromium": ["electron-to-chromium@1.5.267", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="],
+ "duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="],
- "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="],
-
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"esbuild": ["esbuild@0.21.5", "", { "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" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
@@ -418,22 +455,24 @@
"fault": ["fault@2.0.1", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ=="],
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
+
"floating-vue": ["floating-vue@5.2.2", "", { "dependencies": { "@floating-ui/dom": "~1.1.1", "vue-resize": "^2.0.0-alpha.1" }, "peerDependencies": { "@nuxt/kit": "^3.2.0", "vue": "^3.2.0" }, "optionalPeers": ["@nuxt/kit"] }, "sha512-afW+h2CFafo+7Y9Lvw/xsqjaQlKLdJV7h1fCHfcYQ1C4SVMlu7OAekqWgu5d4SgvkBVU0pVpLlVsrSTBURFRkg=="],
"focus-trap": ["focus-trap@7.7.1", "", { "dependencies": { "tabbable": "^6.4.0" } }, "sha512-Pkp8m55GjxBLnhBoT6OXdMvfRr4TjMAKLvFM566zlIryq5plbhaTmLAJWTGR0EkRwLjEte1lCOG9MxF1ipJrOg=="],
"format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="],
- "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
-
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
- "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+ "globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
"gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
+ "gzip-size": ["gzip-size@6.0.0", "", { "dependencies": { "duplexer": "^0.1.2" } }, "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q=="],
+
"hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
"hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
@@ -454,8 +493,12 @@
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
+ "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+
"js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
+ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+
"juice": ["juice@8.1.0", "", { "dependencies": { "cheerio": "1.0.0-rc.10", "commander": "^6.1.0", "mensch": "^0.3.4", "slick": "^1.12.2", "web-resource-inliner": "^6.0.1" }, "bin": { "juice": "bin/juice" } }, "sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA=="],
"kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
@@ -532,6 +575,8 @@
"mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
+ "mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="],
+
"mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="],
"mensch": ["mensch@0.3.4", "", {}, "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g=="],
@@ -594,6 +639,10 @@
"mj-context-menu": ["mj-context-menu@0.6.1", "", {}, "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA=="],
+ "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
+
+ "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="],
@@ -602,16 +651,20 @@
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
- "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
+ "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
+ "ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
+
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
"oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
"oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="],
+ "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
+
"parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
"parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@6.0.1", "", { "dependencies": { "parse5": "^6.0.1" } }, "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA=="],
@@ -620,15 +673,17 @@
"path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
- "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
+ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
+
+ "perfect-debounce": ["perfect-debounce@2.1.0", "", {}, "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
- "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
+ "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
- "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
+ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"preact": ["preact@10.28.1", "", {}, "sha512-u1/ixq/lVQI0CakKNvLDEcW5zfCjUQfZdK9qqWuIJtsezuyG6pk9TWj75GMuI/EzRSZB/VAE43sNWWZfiy8psw=="],
@@ -638,6 +693,10 @@
"punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="],
+ "quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="],
+
+ "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
+
"regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
"regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
@@ -664,6 +723,8 @@
"shiki": ["shiki@3.21.0", "", { "dependencies": { "@shikijs/core": "3.21.0", "@shikijs/engine-javascript": "3.21.0", "@shikijs/engine-oniguruma": "3.21.0", "@shikijs/langs": "3.21.0", "@shikijs/themes": "3.21.0", "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w=="],
+ "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="],
+
"slick": ["slick@1.12.2", "", {}, "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
@@ -688,12 +749,14 @@
"tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
- "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
+ "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
- "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
+ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"tokenx": ["tokenx@1.2.1", "", {}, "sha512-lVhFIhR2qh3uUyUA8Ype+HGzcokUJbHmRSN1TJKOe4Y26HkawQuLiGkUCkR5LD9dx+Rtp+njrwzPL8AHHYQSYA=="],
+ "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
+
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
@@ -712,6 +775,12 @@
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
+ "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
+
+ "unconfig": ["unconfig@7.5.0", "", { "dependencies": { "@quansync/fs": "^1.0.0", "defu": "^6.1.4", "jiti": "^2.6.1", "quansync": "^1.0.0", "unconfig-core": "7.5.0" } }, "sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA=="],
+
+ "unconfig-core": ["unconfig-core@7.5.0", "", { "dependencies": { "@quansync/fs": "^1.0.0", "quansync": "^1.0.0" } }, "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w=="],
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
@@ -728,7 +797,9 @@
"unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
- "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
+ "unocss": ["unocss@66.6.0", "", { "dependencies": { "@unocss/astro": "66.6.0", "@unocss/cli": "66.6.0", "@unocss/core": "66.6.0", "@unocss/postcss": "66.6.0", "@unocss/preset-attributify": "66.6.0", "@unocss/preset-icons": "66.6.0", "@unocss/preset-mini": "66.6.0", "@unocss/preset-tagify": "66.6.0", "@unocss/preset-typography": "66.6.0", "@unocss/preset-uno": "66.6.0", "@unocss/preset-web-fonts": "66.6.0", "@unocss/preset-wind": "66.6.0", "@unocss/preset-wind3": "66.6.0", "@unocss/preset-wind4": "66.6.0", "@unocss/transformer-attributify-jsx": "66.6.0", "@unocss/transformer-compile-class": "66.6.0", "@unocss/transformer-directives": "66.6.0", "@unocss/transformer-variant-group": "66.6.0", "@unocss/vite": "66.6.0" }, "peerDependencies": { "@unocss/webpack": "66.6.0", "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" }, "optionalPeers": ["@unocss/webpack", "vite"] }, "sha512-B5QsMJzFKeTHPzF5Ehr8tSMuhxzbCR9n+XP0GyhK9/2jTcBdI0/T+rCDDr9m6vUz+lku/coCVz7VAQ2BRAbZJw=="],
+
+ "unplugin-utils": ["unplugin-utils@0.3.1", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog=="],
"valid-data-url": ["valid-data-url@3.0.1", "", {}, "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA=="],
@@ -744,6 +815,8 @@
"vue": ["vue@3.5.26", "", { "dependencies": { "@vue/compiler-dom": "3.5.26", "@vue/compiler-sfc": "3.5.26", "@vue/runtime-dom": "3.5.26", "@vue/server-renderer": "3.5.26", "@vue/shared": "3.5.26" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA=="],
+ "vue-flow-layout": ["vue-flow-layout@0.2.0", "", {}, "sha512-zKgsWWkXq0xrus7H4Mc+uFs1ESrmdTXlO0YNbR6wMdPaFvosL3fMB8N7uTV308UhGy9UvTrGhIY7mVz9eN+L0Q=="],
+
"vue-resize": ["vue-resize@2.0.0-alpha.1", "", { "peerDependencies": { "vue": "^3.0.0" } }, "sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg=="],
"web-resource-inliner": ["web-resource-inliner@6.0.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "escape-goat": "^3.0.0", "htmlparser2": "^5.0.0", "mime": "^2.4.6", "node-fetch": "^2.6.0", "valid-data-url": "^3.0.0" } }, "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A=="],
@@ -764,6 +837,16 @@
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
+ "@babel/generator/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
+
+ "@babel/generator/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
+
+ "@babel/template/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
+
+ "@babel/template/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
+
+ "@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
+
"@shikijs/core/@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="],
"@shikijs/core/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="],
@@ -780,19 +863,13 @@
"@shikijs/twoslash/@shikijs/types": ["@shikijs/types@3.21.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA=="],
- "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
+ "@vue/compiler-core/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
- "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
-
- "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
-
- "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
-
- "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+ "@vue/compiler-core/entities": ["entities@7.0.0", "", {}, "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ=="],
- "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+ "@vue/compiler-sfc/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
- "@vue/compiler-core/entities": ["entities@7.0.0", "", {}, "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ=="],
+ "@vue/devtools-kit/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
"dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="],
diff --git a/doc/guide/index.md b/doc/guide/index.md
index b0c422d..1416f64 100644
--- a/doc/guide/index.md
+++ b/doc/guide/index.md
@@ -81,4 +81,5 @@ print(slope.asNumeric()); // 12.0
- [Core Concepts](/guide/concepts) – Understand key ideas
- [Boolean Logic](/guide/logic) – Logic operators and comparisons
- [Piecewise Functions](/guide/piecewise) – Conditional expressions and cases
+- [Deprecation Migration](/guide/migration-validate) – Replace `validate()` / `isValid()` with `parse()`
- [API Reference](/reference/) – Complete method documentation
diff --git a/doc/guide/migration-validate.md b/doc/guide/migration-validate.md
new file mode 100644
index 0000000..907dcbd
--- /dev/null
+++ b/doc/guide/migration-validate.md
@@ -0,0 +1,79 @@
+# Migration Guide: `isValid()` / `validate()` Deprecation
+
+`Texpr.isValid()` and `Texpr.validate()` are deprecated and will be removed in `1.0.0`.
+
+Use `parse()` with `try/catch` instead.
+
+## Why this change?
+
+- Single parsing entry point reduces API surface.
+- Parse exceptions already provide structured diagnostics (`message`, `position`, `suggestion`).
+- Encourages a single error-handling model across parsing and evaluation.
+
+## Migration Patterns
+
+### 1) Replace `isValid()`
+
+Before:
+
+```dart
+final ok = texpr.isValid(input);
+if (!ok) {
+ return;
+}
+```
+
+After:
+
+```dart
+try {
+ texpr.parse(input);
+ // valid
+} on TexprException {
+ // invalid
+}
+```
+
+### 2) Replace `validate()` with rich error handling
+
+Before:
+
+```dart
+final result = texpr.validate(input);
+if (!result.isValid) {
+ print(result.errorMessage);
+ print(result.position);
+ print(result.suggestion);
+}
+```
+
+After:
+
+```dart
+try {
+ texpr.parse(input);
+} on TexprException catch (e) {
+ print(e.message);
+ print(e.position);
+ print(e.suggestion);
+}
+```
+
+### 3) Keep parse result for reuse
+
+```dart
+Expression ast;
+try {
+ ast = texpr.parse(input);
+} on TexprException catch (e) {
+ // report validation-style diagnostics
+ rethrow;
+}
+
+final value = texpr.evaluateParsed(ast, {'x': 2.0});
+```
+
+## Timeline
+
+- **Now (0.1.x):** methods are available but deprecated.
+- **1.0.0:** methods are removed.
diff --git a/doc/how-it-works/performance.md b/doc/how-it-works/performance.md
index 6306ca1..c555f7e 100644
--- a/doc/how-it-works/performance.md
+++ b/doc/how-it-works/performance.md
@@ -197,10 +197,13 @@ final evaluator = Texpr(
}
```
-2. **Validate before evaluating**
+2. **Parse-check before evaluating**
```dart
- final validation = texpr.validate(input);
- if (!validation.isValid) return handleError(validation);
+ try {
+ texpr.parse(input);
+ } on TexprException catch (e) {
+ return handleError(e);
+ }
```
3. **Consider timeouts**
diff --git a/example/features/validation_demo.dart b/example/features/validation_demo.dart
index 6252318..4395249 100644
--- a/example/features/validation_demo.dart
+++ b/example/features/validation_demo.dart
@@ -6,9 +6,9 @@ void main() {
final evaluator = Texpr();
- // Example 1: Basic isValid() usage
- print('1. Basic Validation with isValid()');
- print(' ---------------------------------');
+ // Example 1: Basic parse() + try/catch usage
+ print('1. Basic Validation with parse()');
+ print(' --------------------------------');
_checkValid(evaluator, '2 + 3');
_checkValid(evaluator, r'\sin{0}');
_checkValid(evaluator, r'x^{2} + 1');
@@ -16,9 +16,9 @@ void main() {
_checkValid(evaluator, r'\unknown{5}'); // Invalid: unknown command
print('');
- // Example 2: Detailed validation with validate()
- print('2. Detailed Validation with validate()');
- print(' ------------------------------------');
+ // Example 2: Detailed validation with parse() errors
+ print('2. Detailed Validation with parse() errors');
+ print(' -----------------------------------------');
_detailedValidation(evaluator, r'\frac{1}{2}'); // Valid
_detailedValidation(evaluator, r'\log_{2}{8}'); // Valid
_detailedValidation(evaluator, r'\sin{'); // Invalid
@@ -57,14 +57,14 @@ void main() {
];
for (final input in userInputs) {
- final result = evaluator.validate(input);
- if (result.isValid) {
+ try {
+ evaluator.parse(input);
print(' ✓ "$input" - Valid');
- } else {
+ } on TexprException catch (e) {
print(' ✗ "$input"');
- print(' Error: ${result.errorMessage}');
- if (result.suggestion != null) {
- print(' Suggestion: ${result.suggestion}');
+ print(' Error: ${e.message}');
+ if (e.suggestion != null) {
+ print(' Suggestion: ${e.suggestion}');
}
}
}
@@ -77,53 +77,65 @@ void main() {
final evalNoImplicit = Texpr(allowImplicitMultiplication: false);
print(' With implicit multiplication enabled:');
- print(' 2x is valid: ${evalWithImplicit.isValid('2x')}');
- print(' 3xy is valid: ${evalWithImplicit.isValid('3xy')}');
+ print(' 2x is valid: ${_isParseValid(evalWithImplicit, '2x')}');
+ print(' 3xy is valid: ${_isParseValid(evalWithImplicit, '3xy')}');
print(' With implicit multiplication disabled:');
- print(' 2x is valid: ${evalNoImplicit.isValid('2x')}');
- final timesXValid = evalNoImplicit.isValid(r'2 \times x');
+ print(' 2x is valid: ${_isParseValid(evalNoImplicit, '2x')}');
+ final timesXValid = _isParseValid(evalNoImplicit, r'2 \times x');
print(' 2 \\times x is valid: $timesXValid');
print('');
- // Example 7: Using ValidationResult properties
- print('7. ValidationResult Properties');
- print(' ---------------------------');
- final invalidResult = evaluator.validate(r'\sin{');
+ // Example 7: TexprException properties
+ print('7. TexprException Properties');
+ print(' -------------------------');
print(' Expression: r\'\\sin{\'');
- print(' isValid: ${invalidResult.isValid}');
- print(' errorMessage: ${invalidResult.errorMessage}');
- print(' position: ${invalidResult.position}');
- print(' suggestion: ${invalidResult.suggestion}');
- print(' exceptionType: ${invalidResult.exceptionType}');
+ try {
+ evaluator.parse(r'\sin{');
+ print(' isValid: true');
+ } on TexprException catch (e) {
+ print(' isValid: false');
+ print(' errorMessage: ${e.message}');
+ print(' position: ${e.position}');
+ print(' suggestion: ${e.suggestion}');
+ print(' exceptionType: ${e.runtimeType}');
+ }
print('');
print('=== Demo Complete ===');
}
-/// Helper function to demonstrate isValid()
+bool _isParseValid(Texpr evaluator, String expression) {
+ try {
+ evaluator.parse(expression);
+ return true;
+ } on TexprException {
+ return false;
+ }
+}
+
+/// Helper function to demonstrate parse() validation
void _checkValid(Texpr evaluator, String expression) {
- final isValid = evaluator.isValid(expression);
+ final isValid = _isParseValid(evaluator, expression);
final status = isValid ? '✓' : '✗';
print(' $status "$expression" - ${isValid ? 'Valid' : 'Invalid'}');
}
-/// Helper function to demonstrate validate()
+/// Helper function to demonstrate parse() error reporting
void _detailedValidation(Texpr evaluator, String expression) {
- final result = evaluator.validate(expression);
-
- if (result.isValid) {
+ try {
+ evaluator.parse(expression);
print(' ✓ "$expression"');
print(' Status: Valid');
- } else {
+ } on TexprException catch (e) {
print(' ✗ "$expression"');
print(' Status: Invalid');
- print(' Error: ${result.errorMessage}');
- if (result.position != null) {
- print(' Position: ${result.position}');
+ print(' Error: ${e.message}');
+ if (e.position != null) {
+ print(' Position: ${e.position}');
}
- if (result.suggestion != null) {
- print(' Suggestion: ${result.suggestion}');
+ if (e.suggestion != null) {
+ print(' Suggestion: ${e.suggestion}');
}
}
print('');
diff --git a/example/misc/error_messages_demo.dart b/example/misc/error_messages_demo.dart
index f68798c..23cbc00 100644
--- a/example/misc/error_messages_demo.dart
+++ b/example/misc/error_messages_demo.dart
@@ -79,16 +79,18 @@ void main() {
}
print("\n${'-' * 60}\n");
- // Example 8: Validation API with error details
- print('Example 8: Using Validation API');
+ // Example 8: parse() with error details
+ print('Example 8: Using parse() errors');
print('Expression: \\sin{x\n');
- final result = evaluator.validate(r'\sin{x');
- if (!result.isValid) {
- print('Valid: ${result.isValid}');
- print('Error: ${result.errorMessage}');
- print('Position: ${result.position}');
- print('Suggestion: ${result.suggestion}');
- print('Exception Type: ${result.exceptionType}');
+ try {
+ evaluator.parse(r'\sin{x');
+ print('Valid: true');
+ } on TexprException catch (e) {
+ print('Valid: false');
+ print('Error: ${e.message}');
+ print('Position: ${e.position}');
+ print('Suggestion: ${e.suggestion}');
+ print('Exception Type: ${e.runtimeType}');
}
print("\n${'-' * 60}\n");
diff --git a/lib/src/ast/evaluability.dart b/lib/src/ast/evaluability.dart
index d94376c..915abb3 100644
--- a/lib/src/ast/evaluability.dart
+++ b/lib/src/ast/evaluability.dart
@@ -8,6 +8,22 @@ import 'matrix.dart';
import 'operations.dart';
import 'visitor.dart';
+const Set _knownConstants = {
+ 'pi',
+ 'π',
+ 'e',
+ 'i',
+ 'phi',
+ 'φ',
+ 'tau',
+ 'τ',
+ 'infty',
+ '∞',
+};
+
+final Expando _compileTimeEvaluability =
+ Expando('compileTimeEvaluability');
+
/// Describes whether an expression can be numerically evaluated.
///
/// This enum makes the distinction between "can parse" and "can evaluate"
@@ -40,6 +56,298 @@ enum Evaluability {
unevaluable,
}
+/// Compile-time evaluability metadata attached to AST nodes.
+///
+/// [evaluability] captures structural evaluability at parse time:
+/// - [Evaluability.symbolic] for symbolic-only forms
+/// - [Evaluability.numeric] when no unresolved variables remain
+/// - [Evaluability.unevaluable] when free variables must be supplied
+///
+/// [freeVariables] stores unresolved symbols required for numeric evaluation.
+class EvaluabilityInfo {
+ final Evaluability evaluability;
+ final Set freeVariables;
+
+ const EvaluabilityInfo(this.evaluability, [Set freeVariables = const {}])
+ : freeVariables = freeVariables;
+
+ Evaluability resolve([Set? knownVariables]) {
+ if (evaluability == Evaluability.symbolic) {
+ return Evaluability.symbolic;
+ }
+ if (freeVariables.isEmpty) {
+ return Evaluability.numeric;
+ }
+ if (knownVariables == null) {
+ return Evaluability.unevaluable;
+ }
+ final hasAll = freeVariables.every(knownVariables.contains);
+ return hasAll ? Evaluability.numeric : Evaluability.unevaluable;
+ }
+}
+
+/// Annotates an AST with compile-time evaluability metadata.
+///
+/// This runs once after parsing and stores node-level metadata in an [Expando],
+/// avoiding API-breaking changes to expression node classes.
+void annotateCompileTimeEvaluability(Expression expression) {
+ const visitor = CompileTimeEvaluabilityVisitor();
+ expression.accept(visitor, {});
+}
+
+/// Visitor that computes compile-time evaluability and free variables.
+///
+/// The result is attached to every visited node.
+class CompileTimeEvaluabilityVisitor
+ implements ExpressionVisitor> {
+ const CompileTimeEvaluabilityVisitor();
+
+ EvaluabilityInfo _record(Expression node, EvaluabilityInfo info) {
+ final frozen = Set.unmodifiable(info.freeVariables);
+ final value = EvaluabilityInfo(info.evaluability, frozen);
+ _compileTimeEvaluability[node] = value;
+ return value;
+ }
+
+ EvaluabilityInfo _combine(Expression node, List children) {
+ final freeVariables = {};
+ for (final child in children) {
+ freeVariables.addAll(child.freeVariables);
+ }
+
+ final evaluability = children.any((e) => e.evaluability == Evaluability.symbolic)
+ ? Evaluability.symbolic
+ : children.any((e) => e.evaluability == Evaluability.unevaluable)
+ ? Evaluability.unevaluable
+ : Evaluability.numeric;
+
+ return _record(node, EvaluabilityInfo(evaluability, freeVariables));
+ }
+
+ @override
+ EvaluabilityInfo visitNumberLiteral(NumberLiteral node, Set? context) {
+ return _record(node, const EvaluabilityInfo(Evaluability.numeric));
+ }
+
+ @override
+ EvaluabilityInfo visitVariable(Variable node, Set? context) {
+ if ((context?.contains(node.name) ?? false) ||
+ _knownConstants.contains(node.name)) {
+ return _record(node, const EvaluabilityInfo(Evaluability.numeric));
+ }
+ return _record(node, EvaluabilityInfo(Evaluability.unevaluable, {node.name}));
+ }
+
+ @override
+ EvaluabilityInfo visitBinaryOp(BinaryOp node, Set? context) {
+ final left = node.left.accept(this, context);
+ final right = node.right.accept(this, context);
+ return _combine(node, [left, right]);
+ }
+
+ @override
+ EvaluabilityInfo visitUnaryOp(UnaryOp node, Set? context) {
+ final operand = node.operand.accept(this, context);
+ return _combine(node, [operand]);
+ }
+
+ @override
+ EvaluabilityInfo visitFunctionCall(FunctionCall node, Set? context) {
+ final children = [];
+ for (final arg in node.args) {
+ children.add(arg.accept(this, context));
+ }
+ if (node.base != null) {
+ children.add(node.base!.accept(this, context));
+ }
+ if (node.optionalParam != null) {
+ children.add(node.optionalParam!.accept(this, context));
+ }
+ return _combine(node, children);
+ }
+
+ @override
+ EvaluabilityInfo visitAbsoluteValue(AbsoluteValue node, Set? context) {
+ final argument = node.argument.accept(this, context);
+ return _combine(node, [argument]);
+ }
+
+ @override
+ EvaluabilityInfo visitLimitExpr(LimitExpr node, Set? context) {
+ final target = node.target.accept(this, context);
+ final extendedContext = {...?context, node.variable};
+ final body = node.body.accept(this, extendedContext);
+ return _combine(node, [target, body]);
+ }
+
+ @override
+ EvaluabilityInfo visitSumExpr(SumExpr node, Set? context) {
+ final start = node.start.accept(this, context);
+ final end = node.end.accept(this, context);
+ final extendedContext = {...?context, node.variable};
+ final body = node.body.accept(this, extendedContext);
+ return _combine(node, [start, end, body]);
+ }
+
+ @override
+ EvaluabilityInfo visitProductExpr(ProductExpr node, Set? context) {
+ final start = node.start.accept(this, context);
+ final end = node.end.accept(this, context);
+ final extendedContext = {...?context, node.variable};
+ final body = node.body.accept(this, extendedContext);
+ return _combine(node, [start, end, body]);
+ }
+
+ @override
+ EvaluabilityInfo visitIntegralExpr(IntegralExpr node, Set? context) {
+ final extendedContext = {...?context, node.variable};
+
+ if (node.lower != null && node.upper != null) {
+ final lower = node.lower!.accept(this, context);
+ final upper = node.upper!.accept(this, context);
+ final body = node.body.accept(this, extendedContext);
+ return _combine(node, [lower, upper, body]);
+ }
+
+ final body = node.body.accept(this, extendedContext);
+ return _record(node,
+ EvaluabilityInfo(Evaluability.symbolic, Set.from(body.freeVariables)));
+ }
+
+ @override
+ EvaluabilityInfo visitMultiIntegralExpr(
+ MultiIntegralExpr node, Set? context) {
+ final extendedContext = {...?context, ...node.variables};
+ final body = node.body.accept(this, extendedContext);
+ return _record(node,
+ EvaluabilityInfo(Evaluability.symbolic, Set.from(body.freeVariables)));
+ }
+
+ @override
+ EvaluabilityInfo visitDerivativeExpr(DerivativeExpr node, Set? context) {
+ final extendedContext = {...?context, node.variable};
+ final body = node.body.accept(this, extendedContext);
+ return _combine(node, [body]);
+ }
+
+ @override
+ EvaluabilityInfo visitPartialDerivativeExpr(
+ PartialDerivativeExpr node, Set? context) {
+ final extendedContext = {...?context, node.variable};
+ final body = node.body.accept(this, extendedContext);
+ if (node.body is Variable) {
+ return _record(node,
+ EvaluabilityInfo(Evaluability.symbolic, Set.from(body.freeVariables)));
+ }
+ return _combine(node, [body]);
+ }
+
+ @override
+ EvaluabilityInfo visitBinomExpr(BinomExpr node, Set? context) {
+ final n = node.n.accept(this, context);
+ final k = node.k.accept(this, context);
+ return _combine(node, [n, k]);
+ }
+
+ @override
+ EvaluabilityInfo visitGradientExpr(GradientExpr node, Set? context) {
+ final body = node.body.accept(this, context);
+ if (node.body is Variable) {
+ return _record(node,
+ EvaluabilityInfo(Evaluability.symbolic, Set.from(body.freeVariables)));
+ }
+ return _combine(node, [body]);
+ }
+
+ @override
+ EvaluabilityInfo visitComparison(Comparison node, Set? context) {
+ final left = node.left.accept(this, context);
+ final right = node.right.accept(this, context);
+ return _combine(node, [left, right]);
+ }
+
+ @override
+ EvaluabilityInfo visitChainedComparison(
+ ChainedComparison node, Set? context) {
+ final children =
+ node.expressions.map((expression) => expression.accept(this, context)).toList();
+ return _combine(node, children);
+ }
+
+ @override
+ EvaluabilityInfo visitConditionalExpr(
+ ConditionalExpr node, Set? context) {
+ final expression = node.expression.accept(this, context);
+ final condition = node.condition.accept(this, context);
+ return _combine(node, [expression, condition]);
+ }
+
+ @override
+ EvaluabilityInfo visitPiecewise(PiecewiseExpr node, Set? context) {
+ final children = [];
+ for (final currentCase in node.cases) {
+ children.add(currentCase.expression.accept(this, context));
+ if (currentCase.condition != null) {
+ children.add(currentCase.condition!.accept(this, context));
+ }
+ }
+ return _combine(node, children);
+ }
+
+ @override
+ EvaluabilityInfo visitBooleanBinaryExpr(
+ BooleanBinaryExpr node, Set? context) {
+ final left = node.left.accept(this, context);
+ final right = node.right.accept(this, context);
+ return _combine(node, [left, right]);
+ }
+
+ @override
+ EvaluabilityInfo visitBooleanUnaryExpr(
+ BooleanUnaryExpr node, Set? context) {
+ final operand = node.operand.accept(this, context);
+ return _combine(node, [operand]);
+ }
+
+ @override
+ EvaluabilityInfo visitMatrixExpr(MatrixExpr node, Set? context) {
+ final children = [];
+ for (final row in node.rows) {
+ for (final cell in row) {
+ children.add(cell.accept(this, context));
+ }
+ }
+ return _combine(node, children);
+ }
+
+ @override
+ EvaluabilityInfo visitVectorExpr(VectorExpr node, Set? context) {
+ final children = node.components.map((component) => component.accept(this, context)).toList();
+ return _combine(node, children);
+ }
+
+ @override
+ EvaluabilityInfo visitIntervalExpr(IntervalExpr node, Set? context) {
+ final lower = node.lower.accept(this, context);
+ final upper = node.upper.accept(this, context);
+ return _combine(node, [lower, upper]);
+ }
+
+ @override
+ EvaluabilityInfo visitAssignmentExpr(AssignmentExpr node, Set? context) {
+ final value = node.value.accept(this, context);
+ return _combine(node, [value]);
+ }
+
+ @override
+ EvaluabilityInfo visitFunctionDefinitionExpr(
+ FunctionDefinitionExpr node, Set? context) {
+ final extendedContext = {...?context, ...node.parameters};
+ final body = node.body.accept(this, extendedContext);
+ return _combine(node, [body]);
+ }
+}
+
/// Visitor that determines the evaluability of an expression.
///
/// This visitor traverses the AST and computes whether each node can be
@@ -78,19 +386,7 @@ class EvaluabilityVisitor
return Evaluability.numeric;
}
// Check for known constants
- const knownConstants = {
- 'pi',
- 'π',
- 'e',
- 'i',
- 'phi',
- 'φ',
- 'tau',
- 'τ',
- 'infty',
- '∞',
- };
- if (knownConstants.contains(node.name)) {
+ if (_knownConstants.contains(node.name)) {
return Evaluability.numeric;
}
return Evaluability.unevaluable;
@@ -305,6 +601,13 @@ class EvaluabilityVisitor
/// Extension to add evaluability checking to Expression.
extension ExpressionEvaluability on Expression {
+ /// Gets compile-time evaluability metadata attached during parsing.
+ ///
+ /// Returns `null` for expression trees that were not annotated
+ /// (for example, manually constructed ASTs).
+ EvaluabilityInfo? get compileTimeEvaluabilityInfo =>
+ _compileTimeEvaluability[this];
+
/// Determines the evaluability of this expression.
///
/// [knownVariables] is an optional set of variable names that are defined
@@ -322,6 +625,11 @@ extension ExpressionEvaluability on Expression {
/// expr.getEvaluability({'x'}); // Evaluability.numeric
/// ```
Evaluability getEvaluability([Set? knownVariables]) {
+ final compileTimeInfo = _compileTimeEvaluability[this];
+ if (compileTimeInfo != null) {
+ return compileTimeInfo.resolve(knownVariables);
+ }
+
const visitor = EvaluabilityVisitor();
return accept(visitor, knownVariables);
}
diff --git a/lib/src/cache/cache_keys.dart b/lib/src/cache/cache_keys.dart
index 75cc29d..b35a048 100644
--- a/lib/src/cache/cache_keys.dart
+++ b/lib/src/cache/cache_keys.dart
@@ -12,6 +12,7 @@ class EvaluationCacheKey {
final int _exprIdentity;
final int _varsIdentity;
final bool _isIdentityBased;
+ final List<_NormalizedVariableEntry>? _structuralEntries;
/// Creates an identity-based cache key.
///
@@ -25,7 +26,8 @@ class EvaluationCacheKey {
: _exprIdentity = identityHashCode(expr),
_varsIdentity = identityHashCode(vars),
_hash = identityHashCode(expr) ^ identityHashCode(vars),
- _isIdentityBased = true;
+ _isIdentityBased = true,
+ _structuralEntries = null;
/// Creates a structural cache key using value equality.
///
@@ -40,22 +42,61 @@ class EvaluationCacheKey {
EvaluationCacheKey(Expression expression, Map variables)
: _exprIdentity = expression.hashCode,
_varsIdentity = 0, // Not used for structural comparison
- _hash = _computeStructuralHash(expression, variables),
+ _structuralEntries = _normalizedEntries(variables),
+ _hash = _computeStructuralHash(expression, _normalizedEntries(variables)),
_isIdentityBased = false;
- static int _computeStructuralHash(
- Expression expression, Map variables) {
- // Create a stable hash from the expression and sorted variable entries
- final sortedEntries = variables.entries.toList()
+ static List<_NormalizedVariableEntry> _normalizedEntries(
+ Map variables) {
+ final entries = variables.entries
+ .map((entry) =>
+ _NormalizedVariableEntry(entry.key, _normalizeDouble(entry.value)))
+ .toList()
..sort((a, b) => a.key.compareTo(b.key));
+ return List<_NormalizedVariableEntry>.unmodifiable(entries);
+ }
+ static int _computeStructuralHash(
+ Expression expression, List<_NormalizedVariableEntry> entries) {
+ // Create a stable hash from expression and sorted normalized entries.
var hash = expression.hashCode;
- for (final entry in sortedEntries) {
- hash = hash ^ entry.key.hashCode ^ entry.value.hashCode;
+ for (final entry in entries) {
+ hash = Object.hash(hash, entry.key, _normalizedDoubleHash(entry.value));
}
return hash;
}
+ static double _normalizeDouble(double value) {
+ if (value.isNaN) {
+ return double.nan;
+ }
+ // Canonicalize signed zero to avoid -0.0/+0.0 cache key divergence.
+ if (value == 0.0) {
+ return 0.0;
+ }
+ return value;
+ }
+
+ static int _normalizedDoubleHash(double value) {
+ if (value.isNaN) {
+ return 0x7ff80000;
+ }
+ if (value == 0.0) {
+ return 0;
+ }
+ return value.hashCode;
+ }
+
+ static bool _normalizedDoubleEquals(double left, double right) {
+ if (left.isNaN && right.isNaN) {
+ return true;
+ }
+ if (left == 0.0 && right == 0.0) {
+ return true;
+ }
+ return left == right;
+ }
+
@override
int get hashCode => _hash;
@@ -71,8 +112,31 @@ class EvaluationCacheKey {
_varsIdentity == other._varsIdentity;
}
- // Fallback for mixed or structural keys (deprecated path)
- return _exprIdentity == other._exprIdentity;
+ if (_isIdentityBased != other._isIdentityBased) {
+ return false;
+ }
+
+ final leftEntries = _structuralEntries;
+ final rightEntries = other._structuralEntries;
+ if (leftEntries == null || rightEntries == null) {
+ return false;
+ }
+
+ if (_exprIdentity != other._exprIdentity ||
+ leftEntries.length != rightEntries.length) {
+ return false;
+ }
+
+ for (var index = 0; index < leftEntries.length; index++) {
+ final left = leftEntries[index];
+ final right = rightEntries[index];
+ if (left.key != right.key ||
+ !_normalizedDoubleEquals(left.value, right.value)) {
+ return false;
+ }
+ }
+
+ return true;
}
@override
@@ -80,6 +144,13 @@ class EvaluationCacheKey {
'EvaluationCacheKey(hash: $_hash, identity: $_isIdentityBased)';
}
+class _NormalizedVariableEntry {
+ final String key;
+ final double value;
+
+ const _NormalizedVariableEntry(this.key, this.value);
+}
+
/// A cache key for differentiation results.
///
/// Combines expression, variable, and order to uniquely identify a derivative.
diff --git a/lib/src/parser/parser.dart b/lib/src/parser/parser.dart
index 79c2b6a..a8b5395 100644
--- a/lib/src/parser/parser.dart
+++ b/lib/src/parser/parser.dart
@@ -27,7 +27,9 @@ class Parser extends BaseParser
consume(TokenType.variable, 'Expected variable name after let').value;
consume(TokenType.equals, 'Expected = after variable name');
final value = parseExpression();
- return AssignmentExpr(variable, value);
+ final assignment = AssignmentExpr(variable, value);
+ annotateCompileTimeEvaluability(assignment);
+ return assignment;
}
// 2. Check for function definition: f(x, y) = ...
@@ -86,7 +88,9 @@ class Parser extends BaseParser
consume(TokenType.equals, 'Expected =');
final body = parseExpression();
- return FunctionDefinitionExpr(name, params, body);
+ final functionDefinition = FunctionDefinitionExpr(name, params, body);
+ annotateCompileTimeEvaluability(functionDefinition);
+ return functionDefinition;
}
}
@@ -113,6 +117,7 @@ class Parser extends BaseParser
}
}
+ annotateCompileTimeEvaluability(expr);
return expr;
}
}
diff --git a/lib/src/visitors/json_ast_visitor.dart b/lib/src/visitors/json_ast_visitor.dart
index 235f3f1..6b3d0d3 100644
--- a/lib/src/visitors/json_ast_visitor.dart
+++ b/lib/src/visitors/json_ast_visitor.dart
@@ -6,6 +6,7 @@ import '../ast/calculus.dart';
import '../ast/logic.dart';
import '../ast/matrix.dart';
import '../ast/environment.dart';
+import '../ast/evaluability.dart';
import '../ast/visitor.dart';
/// Visitor that converts an AST to a JSON-serializable Map.
@@ -40,7 +41,30 @@ import '../ast/visitor.dart';
/// }
/// ```
class JsonAstVisitor implements ExpressionVisitor