diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..6d590b6 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,19 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "web", + "runtimeExecutable": "bun", + "runtimeArgs": ["run", "dev"], + "cwd": "apps/web", + "port": 5173 + }, + { + "name": "web-static", + "runtimeExecutable": "python3", + "runtimeArgs": ["-m", "http.server", "5188", "--directory", "apps/web/build"], + "cwd": ".", + "port": 5188 + } + ] +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index f738c2f..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Release - -on: - push: - branches: - - main - -jobs: - release: - name: Release - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '16.x' - cache: 'yarn' - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npx semantic-release diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5945ac1..2216d48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,25 +9,33 @@ on: jobs: build: runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [14.x, 16.x] + defaults: + run: + working-directory: apps/web steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: ${{ matrix.node-version }} - cache: yarn - - - name: Build & test - run: | - yarn install --frozen-lockfile - npx playwright install - yarn build - yarn test - yarn dev & - yarn test:e2e + bun-version: latest + + - name: Install dependencies + run: bun install --frozen-lockfile + working-directory: . + + - name: Type check + run: bun run check + + - name: Unit & component tests + run: bun run test + + - name: Build + run: bun run build + + - name: Install Playwright browser + run: bunx playwright install --with-deps chromium + + - name: E2E tests + run: bun run test:e2e diff --git a/.gitignore b/.gitignore index 6bd421c..5bc1272 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,54 @@ -.DS_Store +# Dependencies node_modules -/build -/.svelte-kit -/package -.vercel -.vercel_build_output +.pnp +.pnp.js + +# Build outputs +dist +build +*.tsbuildinfo + +# Generated files +apps/web/src/routeTree.gen.ts + +# Environment variables .env -.env.* -!.env.example +.env*.local + +# IDEs and editors +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.idea +*.swp +*.swo +*~ +.DS_Store + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Turbo +.turbo +.nx + +# Better-T-Stack +.alchemy + +# Testing +coverage +.nyc_output + +# Misc +*.tgz +.cache +tmp +temp diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..c871730 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,3 @@ +{ + "ignorePatterns": [] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..31e054f --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,11 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "unicorn", "oxc"], + "categories": { + "correctness": "error" + }, + "rules": {}, + "env": { + "builtin": true + } +} diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..5d81f7a --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,56 @@ +# Learn Manoonchai + +A browser typing trainer for the **Manoonchai** Thai keyboard layout. The app +translates physical key positions into Manoonchai characters itself, so a learner +can practice the layout without reconfiguring their operating system. + +## Language + +**Manoonchai layout**: +The Thai keyboard layout this app teaches. A mapping from physical key positions +(`KeyboardEvent.code`) to a base character and a shifted character. +_Avoid_: keymap (that is the on-screen widget, not the data). + +**Lesson**: +A named unit of the curriculum, holding a pool of Thai words that introduce a +specific character or group of characters (e.g. "5. -่ (ไม้เอก)"). Lessons are +chosen freely; there is no locking or forced progression. +_Avoid_: level, chapter, exercise. + +**Drill**: +One timed run the learner performs: a sequence of words sampled from the active +lesson's pool. The learner picks the length (e.g. 10 / 25 / 50 words). A drill +has a clear start (first keystroke) and end (last word committed). +_Avoid_: sentence, test, round, session. + +**Word**: +A single Thai token within a drill, committed by pressing space. The learner's +input is compared against the target word to decide correctness. + +**Keystroke**: +One Manoonchai character produced by the learner, recorded with its timestamp and +whether it matched the expected next character. The raw material for all stats. + +**Raw WPM**: +Typing speed counting every character typed, correct or not, measured per one-second +window (chars ÷ 5 × 60). Spiky; reflects actual cadence. +_Avoid_: gross WPM. + +**Net WPM**: +Speed after penalizing errors — the headline number shown at the end of a drill and +the smoothed line on the chart. +_Avoid_: adjusted WPM, real WPM. + +**Accuracy**: +Share of keystrokes that matched the expected character, as a percentage over the +whole drill. + +**Keymap**: +The on-screen rendering of the Manoonchai layout. Highlights and glows the next key +the learner should press. +_Avoid_: keyboard (ambiguous with the physical device). + +**Next key**: +The character the learner is expected to type next, derived from the target word and +their input so far. Drives the keymap glow and the inline hint. +_Avoid_: nextchar. diff --git a/PRODUCT.md b/PRODUCT.md new file mode 100644 index 0000000..eba1dbe --- /dev/null +++ b/PRODUCT.md @@ -0,0 +1,61 @@ +# Product + +## Register + +product + +## Users + +Thai typists deliberately learning the **Manoonchai** keyboard layout (the +"Dvorak of Thai", an ergonomically re-engineered alternative to the standard +Kedmanee layout). They already type Thai fluently on another layout and are +retraining muscle memory. Context: sitting at a physical keyboard, full focus, +repeating short drills to build speed and accuracy. The job: practice a specific +character group until the new key positions feel automatic, and watch speed climb. + +## Product Purpose + +A browser typing trainer that teaches Manoonchai without the user having to +reconfigure their OS, by translating physical key positions into Manoonchai +characters in-app. Lessons introduce characters in a deliberate order; each drill +is a timed run of sampled words with live feedback (next-key keymap glow, prefix +spellcheck) and honest post-drill stats (raw WPM, net WPM, accuracy). Success: a +learner returns daily, climbs the lesson order, and sees their WPM curve rise. + +## Brand Personality + +A focused instrument. Calm, precise, keyboard-centric. Three words: **precise, +quiet, tactile**. The interface recedes so the typing line and the keymap are the +heroes; the single moment of energy is the next-key glow. Serious tool for adults, +not a game. + +## Anti-references + +- **Generic SaaS / AI-slop**: no cream-bg + single-accent template, no identical + card grids, no tiny-uppercase tracked eyebrows, nothing that reads "AI generated". +- **Childish edu-app**: no mascots, confetti, primary-color gamification, or badge + soup. +- **v1's unthemed look**: no Tailwind-default gray buttons, default green, or the + neon multi-layer box-shadow glow. Fresh identity. +- **A literal Monkeytype clone**: borrow the genre's restraint, not its exact + mono-amber-on-charcoal appearance. + +## Design Principles + +1. **The instrument is the layout.** The keymap and the typing line are the product; + chrome stays out of their way. +2. **One moment of energy.** Restraint everywhere so the next-key cue and a finished + drill land with impact. Glow is earned, not ambient. +3. **Honest readouts.** Stats say exactly what they measure (raw vs net WPM, accuracy); + no flattering or mislabeled numbers. +4. **Feedback never depends on color alone.** Correct / error / next states also carry + shape, weight, or motion, since the entire loop is red/green. +5. **Thai-first typography.** Thai script is the primary content, set to read + beautifully at size, not as an afterthought to a Latin UI. + +## Accessibility & Inclusion + +WCAG 2.1 AA contrast across light and dark themes. Full keyboard operability (it is a +typing app: every action reachable without a mouse). `prefers-reduced-motion` honored +on every animation (glow, chart draw, drill transitions degrade to instant/crossfade). +State color is always paired with a non-color cue. diff --git a/README.md b/README.md index 7239a75..d6306e3 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,55 @@ -[![logo](https://manoonchai.com/_next/image?url=%2Fmanoonchai.png&w=64&q=75)](https://www.manoonchai.com/) +# Learn Manoonchai v2 -# Learn Manoonchai +A browser typing trainer for the **Manoonchai** Thai keyboard layout. It maps +physical key positions to Manoonchai characters in-app, so you can practice the +layout without reconfiguring your OS. A full rewrite of v1 on a modern stack. -## Development +- **Domain language**: [CONTEXT.md](./CONTEXT.md) (lesson, drill, raw/net WPM, …) +- **Product brief**: [PRODUCT.md](./PRODUCT.md) +- **Key decisions**: [docs/adr/](./docs/adr/) -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: +## Stack + +SvelteKit 2 · Svelte 5 (runes) · Tailwind v4 · TypeScript · Bun workspace · +Oxlint + Oxfmt · Vite+ (Vitest) · self-hosted Sarabun · LayerChart. + +## What it does + +- **Lessons** introduce characters in a deliberate order; pick any of 30+ freely. +- A **drill** is a timed run of 10/25/50 words sampled from the active lesson. +- Live feedback: next-key keymap glow, per-character correctness, prefix check. +- Honest end-of-drill stats: raw WPM, net WPM, accuracy, and a raw/net chart with + error markers (see [ADR 0001](./docs/adr/0001-honest-stats-engine.md)). +- Light/dark themes (system default), full keyboard operability, reduced-motion. + +`Tab` restarts a drill · `Esc` opens settings. + +## Getting started ```bash -yarn -yarn dev +bun install +bun run dev # → http://localhost:5173 +``` + +## Project structure + +``` +. +├── apps/web/ +│ └── src/lib/ +│ ├── engine/ # pure domain logic (layout, drill, stats, …) + unit tests +│ ├── components/ # UI + component tests +│ ├── state/ # runes-backed, persisted settings +│ └── data/ # ported Manoonchai layout + lesson word pools +├── packages/ # shared env + tsconfig +└── v1/ # the previous app, archived ``` + +## Scripts (run inside `apps/web`) + +- `bun run dev` / `bun run build` / `bun run preview` +- `bun run check` — `svelte-check` type checking +- `bun run test` — Vitest unit + component tests +- `bun run test:e2e` — Playwright end-to-end (drives a full drill) + +Repo-wide lint/format from the `v2/` root: `bun run lint`, `bun run format`. diff --git a/apps/web/.gitignore b/apps/web/.gitignore new file mode 100644 index 0000000..f4a2e46 --- /dev/null +++ b/apps/web/.gitignore @@ -0,0 +1,24 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +.alchemy +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/.npmrc b/apps/web/.npmrc similarity index 100% rename from .npmrc rename to apps/web/.npmrc diff --git a/apps/web/e2e/drill.spec.ts b/apps/web/e2e/drill.spec.ts new file mode 100644 index 0000000..aea6169 --- /dev/null +++ b/apps/web/e2e/drill.spec.ts @@ -0,0 +1,64 @@ +import { expect, test, type Page } from "@playwright/test"; + +// Lesson 1 uses only these four base characters, so we can map each word back to +// physical key codes and drive the trainer the way a real keyboard would. +const CODE: Record = { น: "KeyF", ม: "KeyG", อ: "KeyH", า: "KeyJ" }; + +// Pin a short lesson-1 drill before the app boots. +async function bootDrill(page: Page) { + await page.addInitScript(() => { + localStorage.setItem( + "learn-manoonchai:v2", + JSON.stringify({ theme: "dark", drillLength: 10, currentLessonName: "1. น ม อ า" }), + ); + }); + await page.goto("/"); + // Wait for the first drill to be built (total reaches 10) before driving it. + await expect(page.getByTestId("progress")).toHaveText(/\/\s*10/); + await page.getByTestId("typing-input").focus(); +} + +async function typeCurrentWord(page: Page) { + const target = (await page.getByTestId("current-word").textContent())?.trim() ?? ""; + for (const ch of target) { + await page.keyboard.press(CODE[ch] ?? "KeyF"); + } + await page.keyboard.press("Space"); +} + +test("completes a drill and shows an honest summary", async ({ page }) => { + await bootDrill(page); + + for (let i = 0; i < 10 && !(await page.getByTestId("summary").isVisible()); i++) { + await typeCurrentWord(page); + } + + await expect(page.getByTestId("summary")).toBeVisible(); + // Net WPM is rendered and is a finite number. + const net = (await page.getByTestId("net-wpm").textContent())?.trim() ?? ""; + expect(Number(net)).toBeGreaterThanOrEqual(0); + expect(Number.isNaN(Number(net))).toBe(false); + await expect(page.getByRole("button", { name: /พิมพ์อีกครั้ง/ })).toBeFocused(); +}); + +test("flags a wrong keystroke without colour alone", async ({ page }) => { + await bootDrill(page); + + // ส (KeyT) is never the first character in lesson 1, so this always diverges. + await page.keyboard.press("KeyT"); + + const wrong = page.locator('[data-testid="current-word"] span.text-danger'); + await expect(wrong.first()).toBeVisible(); + // The error also carries a non-colour cue (underline). + await expect(wrong.first()).toHaveClass(/underline/); +}); + +test("Tab restarts the drill", async ({ page }) => { + await bootDrill(page); + + await typeCurrentWord(page); // commit one word -> progress 1/10 + await expect(page.getByTestId("progress")).toHaveText(/1\s*\/\s*10/); + + await page.keyboard.press("Tab"); + await expect(page.getByTestId("progress")).toHaveText(/0\s*\/\s*10/); +}); diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..77828fb --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,42 @@ +{ + "name": "web", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "vp dev", + "build": "vp build", + "preview": "vp preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "test": "vp test run -c vitest.config.ts", + "test:watch": "vp test watch -c vitest.config.ts", + "test:e2e": "playwright test" + }, + "dependencies": { + "@fontsource/sarabun": "^5.2.8", + "@v2/env": "workspace:*", + "dotenv": "catalog:", + "layerchart": "^1.0.13", + "zod": "catalog:" + }, + "devDependencies": { + "@playwright/test": "^1.60.0", + "@sveltejs/adapter-auto": "^7.0.1", + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.58.0", + "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@tailwindcss/vite": "^4.2.4", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/svelte": "^5.3.1", + "@v2/config": "workspace:*", + "jsdom": "^29.1.1", + "svelte": "^5.55.5", + "svelte-check": "^4.4.6", + "tailwindcss": "^4.2.4", + "typescript": "catalog:", + "vite": "^8.0.10", + "vitest": "^3.2.0" + } +} diff --git a/apps/web/playwright.config.ts b/apps/web/playwright.config.ts new file mode 100644 index 0000000..4a94269 --- /dev/null +++ b/apps/web/playwright.config.ts @@ -0,0 +1,17 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./e2e", + timeout: 30_000, + fullyParallel: false, + use: { + baseURL: "http://localhost:5173", + }, + webServer: { + command: "bun run dev", + url: "http://localhost:5173", + reuseExistingServer: true, + timeout: 120_000, + }, + projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }], +}); diff --git a/apps/web/src/app.css b/apps/web/src/app.css new file mode 100644 index 0000000..6535fd8 --- /dev/null +++ b/apps/web/src/app.css @@ -0,0 +1,168 @@ +@import "tailwindcss"; + +/* Manual theme switching: `.dark` on wins; otherwise the :root values + (light) apply. The initial class is set from system preference + saved choice + by the inline script in app.html before first paint. */ +@custom-variant dark (&:where(.dark, .dark *)); + +/* ---- Semantic palette (OKLCH). The mood lives in the brand colors, not the + surface: a cool graphite instrument with a teal "indicator" primary and a + terracotta "achievement" accent reserved for the climax of a drill. ---- */ +:root { + --bg: oklch(1 0 0); + --surface: oklch(0.976 0.003 240); + --panel: oklch(0.955 0.004 240); + --border: oklch(0.9 0.006 240); + --border-strong: oklch(0.82 0.008 240); + --ink: oklch(0.24 0.012 250); + --muted: oklch(0.5 0.012 250); + --faint: oklch(0.66 0.01 250); + --primary: oklch(0.52 0.12 215); + --primary-bright: oklch(0.6 0.13 215); + --primary-ink: oklch(0.99 0.005 215); + --accent: oklch(0.6 0.15 41); + --accent-ink: oklch(0.99 0.01 41); + --danger: oklch(0.54 0.2 25); + --danger-ink: oklch(0.99 0.01 25); + + color-scheme: light; +} + +.dark { + --bg: oklch(0.165 0.006 250); + --surface: oklch(0.205 0.008 250); + --panel: oklch(0.248 0.009 250); + --border: oklch(0.3 0.01 250); + --border-strong: oklch(0.38 0.012 250); + --ink: oklch(0.93 0.004 250); + --muted: oklch(0.69 0.009 250); + --faint: oklch(0.5 0.009 250); + --primary: oklch(0.74 0.13 210); + --primary-bright: oklch(0.8 0.13 205); + --primary-ink: oklch(0.16 0.02 240); + --accent: oklch(0.74 0.15 46); + --accent-ink: oklch(0.18 0.03 46); + --danger: oklch(0.71 0.18 25); + --danger-ink: oklch(0.16 0.03 25); + + color-scheme: dark; +} + +/* Map semantic vars to Tailwind utilities. `inline` keeps them live, so the + same `bg-bg` / `text-ink` utility tracks the active theme. */ +@theme inline { + --color-bg: var(--bg); + --color-surface: var(--surface); + --color-panel: var(--panel); + --color-border: var(--border); + --color-border-strong: var(--border-strong); + --color-ink: var(--ink); + --color-muted: var(--muted); + --color-faint: var(--faint); + --color-primary: var(--primary); + --color-primary-bright: var(--primary-bright); + --color-primary-ink: var(--primary-ink); + --color-accent: var(--accent); + --color-accent-ink: var(--accent-ink); + --color-danger: var(--danger); + --color-danger-ink: var(--danger-ink); + + --font-thai: "Sarabun", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; + --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", Menlo, monospace; + + --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); +} + +@layer base { + html { + background-color: var(--bg); + color: var(--ink); + font-family: var(--font-thai); + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + } + + body { + background-color: var(--bg); + color: var(--ink); + } + + ::selection { + background-color: color-mix(in oklch, var(--primary) 28%, transparent); + color: var(--ink); + } + + /* Thin, themed scrollbars — the instrument shouldn't show OS chrome. */ + * { + scrollbar-width: thin; + scrollbar-color: var(--border-strong) transparent; + } + + :focus-visible { + outline: 2px solid var(--primary); + outline-offset: 2px; + border-radius: 4px; + } +} + +/* ---- The single moment of energy: the next-key glow. A focused ring, not + v1's neon stack. Tuned per accent so it reads as "this key, now". ---- */ +@layer utilities { + .glow-primary { + box-shadow: + 0 0 0 1px var(--primary), + 0 0 14px -2px var(--primary), + 0 0 28px -8px var(--primary); + } + .glow-accent { + box-shadow: + 0 0 0 1px var(--accent), + 0 0 14px -2px var(--accent), + 0 0 26px -8px var(--accent); + } + + /* A slow breathing pulse on the live cursor so the eye finds it. */ + @keyframes cursor-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.35; + } + } + .animate-cursor { + animation: cursor-pulse 1.1s var(--ease-out-quint) infinite; + } + + @keyframes rise-in { + from { + opacity: 0; + transform: translateY(6px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + .animate-rise { + animation: rise-in 0.45s var(--ease-out-quint) both; + } +} + +/* Reduced motion is not optional: collapse every animation to a crossfade or + instant state. */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } + .animate-cursor { + animation: none !important; + opacity: 1 !important; + } +} diff --git a/apps/web/src/app.d.ts b/apps/web/src/app.d.ts new file mode 100644 index 0000000..520c421 --- /dev/null +++ b/apps/web/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/apps/web/src/app.html b/apps/web/src/app.html new file mode 100644 index 0000000..7ae6a5d --- /dev/null +++ b/apps/web/src/app.html @@ -0,0 +1,29 @@ + + + + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/apps/web/src/lib/components/Dialog.svelte b/apps/web/src/lib/components/Dialog.svelte new file mode 100644 index 0000000..5da4db9 --- /dev/null +++ b/apps/web/src/lib/components/Dialog.svelte @@ -0,0 +1,46 @@ + + + (open = false)} + {onclick} + class="m-auto w-[min(34rem,92vw)] rounded-2xl border border-border bg-surface p-0 text-ink backdrop:bg-black/45 backdrop:backdrop-blur-[2px] open:animate-rise" +> +
+

{title}

+ +
+
+ {@render children()} +
+
diff --git a/apps/web/src/lib/components/DrillSummary.svelte b/apps/web/src/lib/components/DrillSummary.svelte new file mode 100644 index 0000000..eb738ff --- /dev/null +++ b/apps/web/src/lib/components/DrillSummary.svelte @@ -0,0 +1,77 @@ + + +
+
+

จบบท {lessonName}

+
+ + {result.netWpm.toFixed(0)} + + net wpm +
+
+ +
+ {#each secondary as stat (stat.label)} +
+
{stat.value}
+
{stat.label}
+
+ {/each} +
+ + + +
+ + + +
+
diff --git a/apps/web/src/lib/components/DrillTrack.svelte b/apps/web/src/lib/components/DrillTrack.svelte new file mode 100644 index 0000000..16579a0 --- /dev/null +++ b/apps/web/src/lib/components/DrillTrack.svelte @@ -0,0 +1,28 @@ + + +
+ + + {Math.min(currentIdx, total)}/{total} + +
diff --git a/apps/web/src/lib/components/DrillTrack.test.ts b/apps/web/src/lib/components/DrillTrack.test.ts new file mode 100644 index 0000000..2c3379c --- /dev/null +++ b/apps/web/src/lib/components/DrillTrack.test.ts @@ -0,0 +1,20 @@ +import { render } from "@testing-library/svelte"; +import { describe, expect, it } from "vitest"; +import DrillTrack from "./DrillTrack.svelte"; + +describe("DrillTrack", () => { + it("shows the progress count", () => { + const { container } = render(DrillTrack, { + props: { statuses: [true, false, null], currentIdx: 1, total: 3 }, + }); + expect(container.textContent?.replace(/\s/g, "")).toContain("1/3"); + }); + + it("renders one segment per word", () => { + const { container } = render(DrillTrack, { + props: { statuses: [], currentIdx: 0, total: 5 }, + }); + const track = container.querySelector("[role=presentation]")!; + expect(track.querySelectorAll("span").length).toBe(5); + }); +}); diff --git a/apps/web/src/lib/components/FocusLens.svelte b/apps/web/src/lib/components/FocusLens.svelte new file mode 100644 index 0000000..2d05cb2 --- /dev/null +++ b/apps/web/src/lib/components/FocusLens.svelte @@ -0,0 +1,107 @@ + + +
+
+ {#if showAdjacent && prevWord} + {prevWord} + {/if} +
+ +
{#each cells as cell, i (i)}{#if i === caretAfter}{/if}{cell.seg}{/each}{#if caretAfter >= cells.length}{/if}{#if cells.length === 0}{/if}
+ +
+ {#if showAdjacent && nextWord} + {nextWord} + {/if} +
+
+ + diff --git a/apps/web/src/lib/components/FocusLens.test.ts b/apps/web/src/lib/components/FocusLens.test.ts new file mode 100644 index 0000000..a73a46a --- /dev/null +++ b/apps/web/src/lib/components/FocusLens.test.ts @@ -0,0 +1,42 @@ +import { render } from "@testing-library/svelte"; +import { describe, expect, it } from "vitest"; +import FocusLens from "./FocusLens.svelte"; + +function charCells(container: HTMLElement): HTMLElement[] { + const word = container.querySelector("[data-testid=current-word]")!; + return [...word.querySelectorAll("span:not(.caret):not(.invisible)")]; +} + +describe("FocusLens", () => { + it("keeps Thai combining marks attached by colouring per grapheme cluster", () => { + const { container } = render(FocusLens, { props: { target: "น่า", input: "" } }); + // "น่า" = [น + tone ่] + [า] -> two cells, the tone stays with its base. + expect(charCells(container).map((c) => c.textContent)).toEqual(["น่", "า"]); + }); + + it("marks typed-correct, wrong, and pending cells distinctly", () => { + const { container } = render(FocusLens, { props: { target: "นม", input: "นก" } }); + const cells = charCells(container); + expect(cells[0].className).toContain("text-ink"); // น correct + expect(cells[1].className).toContain("text-danger"); // ก != ม + expect(cells[1].className).toContain("underline"); // non-colour cue + }); + + it("renders untyped characters as muted, not faint", () => { + const { container } = render(FocusLens, { props: { target: "นม", input: "" } }); + expect(charCells(container)[0].className).toContain("text-muted"); + }); + + it("shows adjacent words only when enabled", () => { + const shown = render(FocusLens, { + props: { target: "นม", prevWord: "อา", nextWord: "มา", showAdjacent: true }, + }); + expect(shown.container.textContent).toContain("อา"); + expect(shown.container.textContent).toContain("มา"); + + const hidden = render(FocusLens, { + props: { target: "นม", prevWord: "อา", nextWord: "มา", showAdjacent: false }, + }); + expect(hidden.container.textContent).not.toContain("อา"); + }); +}); diff --git a/apps/web/src/lib/components/Key.svelte b/apps/web/src/lib/components/Key.svelte new file mode 100644 index 0000000..27eebaa --- /dev/null +++ b/apps/web/src/lib/components/Key.svelte @@ -0,0 +1,44 @@ + + +
+ {#if keyData.shift} + + {keyData.shift} + + {/if} + + {keyData.base} + + {#if keyData.home} + + {/if} +
diff --git a/apps/web/src/lib/components/Keymap.svelte b/apps/web/src/lib/components/Keymap.svelte new file mode 100644 index 0000000..30088e4 --- /dev/null +++ b/apps/web/src/lib/components/Keymap.svelte @@ -0,0 +1,67 @@ + + + + + diff --git a/apps/web/src/lib/components/Keymap.test.ts b/apps/web/src/lib/components/Keymap.test.ts new file mode 100644 index 0000000..e864b1c --- /dev/null +++ b/apps/web/src/lib/components/Keymap.test.ts @@ -0,0 +1,24 @@ +import { render } from "@testing-library/svelte"; +import { describe, expect, it } from "vitest"; +import Keymap from "./Keymap.svelte"; + +describe("Keymap", () => { + it("activates the key whose base character is next", () => { + const { container } = render(Keymap, { props: { nextChar: "น", glow: false } }); + expect(container.querySelector("[data-code=KeyF]")?.getAttribute("data-active")).toBe("true"); + expect(container.querySelector("[data-code=KeyG]")?.getAttribute("data-active")).toBeNull(); + }); + + it("activates the owning key when the next character needs Shift", () => { + // ช is the shift layer of KeyF. + const { container } = render(Keymap, { props: { nextChar: "ช", glow: false } }); + expect(container.querySelector("[data-code=KeyF]")?.getAttribute("data-active")).toBe("true"); + expect(container.textContent).toContain("SHIFT"); + }); + + it("activates no letter key when the next key is space", () => { + const { container } = render(Keymap, { props: { nextChar: " ", glow: false } }); + expect(container.querySelector("[data-active=true]")).toBeNull(); + expect(container.textContent).toContain("SPACE"); + }); +}); diff --git a/apps/web/src/lib/components/LessonPicker.svelte b/apps/web/src/lib/components/LessonPicker.svelte new file mode 100644 index 0000000..1f72fcc --- /dev/null +++ b/apps/web/src/lib/components/LessonPicker.svelte @@ -0,0 +1,58 @@ + + + +
+ + +
    + {#each filtered as lesson (lesson.name)} + {@const current = lesson.name === settings.currentLessonName} +
  • + +
  • + {:else} +
  • ไม่พบบทที่ค้นหา
  • + {/each} +
+
+
diff --git a/apps/web/src/lib/components/LiveReadout.svelte b/apps/web/src/lib/components/LiveReadout.svelte new file mode 100644 index 0000000..65328c8 --- /dev/null +++ b/apps/web/src/lib/components/LiveReadout.svelte @@ -0,0 +1,23 @@ + + +
+
+ {wpm.toFixed(0)} + wpm +
+ +
+ {accuracy.toFixed(0)}% + acc +
+
diff --git a/apps/web/src/lib/components/LiveReadout.test.ts b/apps/web/src/lib/components/LiveReadout.test.ts new file mode 100644 index 0000000..990e473 --- /dev/null +++ b/apps/web/src/lib/components/LiveReadout.test.ts @@ -0,0 +1,12 @@ +import { render } from "@testing-library/svelte"; +import { describe, expect, it } from "vitest"; +import LiveReadout from "./LiveReadout.svelte"; + +describe("LiveReadout", () => { + it("rounds WPM and accuracy to whole numbers", () => { + const { container } = render(LiveReadout, { props: { wpm: 42.6, accuracy: 88.2 } }); + const text = container.textContent ?? ""; + expect(text).toContain("43"); + expect(text).toContain("88"); + }); +}); diff --git a/apps/web/src/lib/components/Logo.svelte b/apps/web/src/lib/components/Logo.svelte new file mode 100644 index 0000000..2a518c9 --- /dev/null +++ b/apps/web/src/lib/components/Logo.svelte @@ -0,0 +1,30 @@ + + + + + + + Learn Manoonchai + + diff --git a/apps/web/src/lib/components/SettingsPanel.svelte b/apps/web/src/lib/components/SettingsPanel.svelte new file mode 100644 index 0000000..06ebea8 --- /dev/null +++ b/apps/web/src/lib/components/SettingsPanel.svelte @@ -0,0 +1,100 @@ + + + +
+ +
+ ความยาวต่อรอบ +
+ {#each DRILL_LENGTHS as len (len)} + + {/each} +
+
+ + +
+ ธีม +
+ {#each themes as t (t.value)} + + {/each} +
+
+ + +
+ {#each toggles as t (t.key)} + + {/each} +
+ +

+ Tab เริ่มรอบใหม่ · + Esc เปิดตั้งค่า +

+
+
diff --git a/apps/web/src/lib/components/ThemeToggle.svelte b/apps/web/src/lib/components/ThemeToggle.svelte new file mode 100644 index 0000000..3f2f882 --- /dev/null +++ b/apps/web/src/lib/components/ThemeToggle.svelte @@ -0,0 +1,49 @@ + + + diff --git a/apps/web/src/lib/components/WpmChart.svelte b/apps/web/src/lib/components/WpmChart.svelte new file mode 100644 index 0000000..aaaddab --- /dev/null +++ b/apps/web/src/lib/components/WpmChart.svelte @@ -0,0 +1,95 @@ + + +
+
+ + Raw WPM + + + Net WPM + + + Errors + +
+ +
+ {#if samples.length > 0} + + + + + + + + {#each errorPoints as p (p.second)} + + {/each} + + + {/if} +
+
+ + diff --git a/apps/web/src/lib/data/lessons.ts b/apps/web/src/lib/data/lessons.ts new file mode 100644 index 0000000..cebbb31 --- /dev/null +++ b/apps/web/src/lib/data/lessons.ts @@ -0,0 +1,6827 @@ +export const lessons = [ + { + name: "1. น ม อ า", + words: ["นม", "อา", "นานา", "นา", "มา", "นอ", "อม", "มน", "มอ", "นอน", "ออม", "มอม", "อาม"], + }, + { + name: "2. ร", + words: ["รอ", "รา", "ราม", "อร", "มาร", "รม", "รอน"], + }, + { + name: "3. เ- (สระเอ)", + words: ["เรา", "เอา", "เอ", "เร", "เม", "เน", "เออ", "เมา", "เรน", "เอน", "เมน"], + }, + { + name: "4. ง", + words: ["งาน", "มอง", "เอง", "นาง", "รอง", "งาม", "งง", "เงา", "งา", "ราง", "อง", "งอ", "งอน"], + }, + { + name: "5. -่ (ไม้เอก)", + words: [ + "น่า", + "อ่าน", + "ร่าง", + "อ่อน", + "เร่ง", + "เอ่อ", + "ร่า", + "เน่า", + "ร่ม", + "ม่าน", + "อ่าง", + "ม่า", + "น่าน", + "ร่อง", + ], + }, + { + name: "6. -้ (ไม้โท)", + words: [ + "ร้าน", + "น้อง", + "ร้อน", + "เน้น", + "ร้อง", + "อ้าง", + "ม้า", + "น้า", + "อ้อม", + "อ้อ", + "อ้า", + "เร้า", + "เอ้า", + "อ้น", + "ร้าง", + "เอ้อ", + ], + }, + { + name: "7. ว", + words: [ + "ว่า", + "ร่วม", + "รวม", + "วาง", + "วง", + "ราว", + "วา", + "ว่าง", + "อ้าว", + "อ้วน", + "ร่วง", + "วร", + "วน", + "วาน", + "เว้น", + "ม่วง", + "เวร", + "เอว", + "เว", + "ว่างงาน", + "ม้วน", + "ง่วง", + "วอน", + "อ่าว", + "เวน", + "วรา", + ], + }, + { + name: "8. -ื (สระอือ)", + words: [ + "เรื่อง", + "เมื่อ", + "อื่น", + "เมือง", + "มือ", + "เรือ", + "เนื้อ", + "เรื่องราว", + "เรือน", + "ร่วมมือ", + "มื้อ", + "เนื่อง", + "อืม", + "เอื้อ", + "เอื้อม", + "รื้อ", + "เอาเรื่อง", + "อือ", + "เรือง", + "เนื้อเรื่อง", + ], + }, + { + name: "9. ย", + words: [ + "อย่า", + "ย่อ", + "ยง", + "วาย", + "ร้าย", + "ยาย", + "นาย", + "ยาม", + "ยอม", + "ย้าย", + "อร่อย", + "ยา", + "ยืน", + "ราย", + "เอย", + "เนย", + "รายงาน", + "ร่องรอย", + "เยื่อ", + ], + }, + { + name: "10. ี (สระอี)", + words: [ + "มี", + "นี้", + "นี่", + "เรียน", + "อี", + "เนี่ย", + "รี", + "เมีย", + "วี", + "เยี่ยม", + "เรียง", + "รี่", + "นี", + "นี้เอง", + "เรียว", + "มี่", + "เวียน", + "เอียง", + "นี่เอง", + "ร้องเรียน", + "เยียวยา", + "มาเรียน", + "เนียน", + "เวียง", + "เยอรมนี", + "ยี", + "วี่", + "งี้", + "ยีน", + "วนเวียน", + "วานนี้", + "เรียงราย", + "นารี", + "เรีย", + ], + }, + { + name: "11. ล", + words: [ + "เลย", + "เวลา", + "ลง", + "เล่น", + "เล่า", + "ล้าน", + "เลี้ยง", + "ลอง", + "ลา", + "เล่ม", + "ลืม", + "ลม", + "ล้วน", + "ล้าง", + "ล่า", + "ล่าง", + "ลอย", + "เลื่อน", + "ลาย", + "ลี", + "ลาน", + "ล้ม", + "ลาว", + "เล", + "ล้อ", + "ลงมือ", + "ล้อม", + "เลว", + "ลอ", + "เลีย", + "เลี่ยง", + "นวล", + "ลงนาม", + "ล่อง", + "ลีลา", + "เลวร้าย", + "เลี้ยว", + "มวล", + "ล้น", + "ลี่", + "ลี้", + "ล้านนา", + "ล่อ", + "ลื้อ", + "เลือน", + "เลน", + "ลือ", + "ลวง", + "เอล", + "เลอ", + "ลาง", + "ลื่น", + "มาลา", + "เล่าเรียน", + ], + }, + { + name: "12. ั (ไม้หันอากาศ)", + words: [ + "นั้น", + "ยัง", + "มัน", + "วัน", + "อัน", + "นั่ง", + "นั่น", + "วันนี้", + "วัย", + "มั้ย", + "งั้น", + "นั่นเอง", + "ยัย", + "ยืนยัน", + "รางวัล", + "นัย", + "วัง", + "มายัง", + "เยอรมัน", + "อัล", + "วัว", + "รัง", + "มัว", + "ลั่น", + "ยั่งยืน", + "อนามัย", + "มั้ง", + "รั้ว", + "มั่น", + "มั่ง", + "รั่ว", + "ยัน", + "นัง", + "มัง", + "ลัน", + "มาลัย", + "ลังเล", + "นัน", + "เรื้อรัง", + "รั้ง", + "ยั่ว", + "ลัย", + "รายวัน", + "วันเวลา", + "นั่งเล่น", + "รัน", + "อัง", + "ยังงั้น", + ], + }, + { + name: "13. ท", + words: [ + "ที่", + "ทาง", + "ทั้ง", + "ท่าน", + "วันที่", + "ทรง", + "เท่านั้น", + "ที", + "เท่า", + "ทั้งนี้", + "รวมทั้ง", + "ทันที", + "ทั่ว", + "ทอง", + "ที่มา", + "ทีม", + "เที่ยว", + "ท่า", + "นาที", + "เท้า", + "ท้าย", + "ทา", + "ท่องเที่ยว", + "ท้อง", + "ที่นั่ง", + "ท่าทาง", + "ทั้งที่", + "ทน", + "เวที", + "ทั้งนั้น", + "ทราย", + "ทาน", + "ท่อ", + "ทัน", + "รองเท้า", + "เท", + "เทียน", + "เที่ยง", + "เท่าเทียม", + "ท่าที", + "ว่าที่", + "ทรมาน", + "ทว่า", + "ทีวี", + "ทอ", + "ท่อน", + "ทีนี้", + "มารยาท", + "ท้าทาย", + "ท้องที่", + "วาท", + "ที่นอน", + "ท้าว", + "เทา", + "ทร", + "ทวี", + "ทีมงาน", + "เนื้อที่", + "เทียม", + "ทรา", + "เทอม", + "ท่อง", + "ที่ทาง", + "เท่านี้", + "ทายาท", + "ทยอย", + "ท่วม", + "ทวน", + "เท่", + "ท่าเรือ", + "ทาย", + "เวทนา", + "นัท", + "ทอน", + ], + }, + { + name: "14. ด", + words: [ + "ด้วย", + "ดี", + "ด้าน", + "เดียว", + "ดัง", + "เดือน", + "ลด", + "ดังนั้น", + "วัด", + "ดังนี้", + "ดาว", + "เลือด", + "ยอด", + "ดวง", + "ดา", + "นัด", + "ดัน", + "ทีเดียว", + "ดื่ม", + "มารดา", + "วาด", + "ดังที่", + "ทดลอง", + "อด", + "มืด", + "ทอด", + "รอด", + "เดือดร้อน", + "เด่น", + "งดงาม", + "มีด", + "ด่า", + "ดารา", + "เดา", + "อดทน", + "ยืด", + "ด้อย", + "เดี่ยว", + "เด", + "เวียดนาม", + "นวด", + "ด่วน", + "อัด", + "เลยทีเดียว", + "ดอน", + "ลวดลาย", + "อันดี", + "ดี้", + "มัด", + "เทวดา", + "ด่าน", + "อวด", + "ดาวเทียม", + "ดีงาม", + "เดอ", + "ลอนดอน", + "ดง", + "งด", + "ดั่ง", + "รัด", + "ลาด", + "ด้า", + "เร่งรัด", + "เร่งด่วน", + "รด", + "ราด", + "งวด", + "ดม", + "ดวงดาว", + "วดี", + "ดื้อ", + "เดือด", + "ดอย", + "ด้วง", + "มด", + "ดอง", + "ลอด", + "ลัด", + "ยอดเยี่ยม", + "ยัด", + "ดัด", + "รีด", + "ง่ายดาย", + "ดด", + "มาด", + "เดน", + "ดีด", + ], + }, + { + name: "15. ห", + words: [ + "หรือ", + "หน้า", + "เหมือน", + "หลาย", + "หา", + "อาหาร", + "หลัง", + "ห้อง", + "หมด", + "หน้าที่", + "ทั้งหมด", + "เหล่านี้", + "หมอ", + "หัว", + "หัน", + "หาย", + "หล่อน", + "ทหาร", + "เหลือ", + "เหนือ", + "เหรอ", + "ห้า", + "หน่วยงาน", + "หนัง", + "หน่อย", + "หน่วย", + "หนี", + "ห้าม", + "ทั้งหลาย", + "หนี้", + "หวัง", + "หลวง", + "มหา", + "เหล่านั้น", + "ห่าง", + "หัวหน้า", + "หวาน", + "ห่วง", + "หอม", + "เนื้อหา", + "หมา", + "หลง", + "หมื่น", + "หนาว", + "หลาน", + "เหนื่อย", + "หมาย", + "เหลือง", + "หนา", + "หล่อ", + "ล่วงหน้า", + "หมวด", + "เหล้า", + "หารือ", + "เหยื่อ", + "เห", + "เหล่า", + "หอย", + "หน", + "หนอง", + "หาง", + "ห่อ", + "หนทาง", + "หมั้น", + "เหมา", + "หอ", + "ล้มเหลว", + "หลอดเลือด", + "หัด", + "หาด", + "ห้าง", + "ด้านหน้า", + "เหงา", + "หม้อ", + "ยี่ห้อ", + "หนอ", + "ห่ม", + "หม่อม", + "เหงื่อ", + "หันหน้า", + "หั่น", + "หย่า", + "หยด", + "หล่น", + "หวย", + "เหยียด", + "หันหลัง", + "เมืองหลวง", + "ทางหลวง", + "ทีหลัง", + "หวง", + "ห้องเรียน", + "หนวด", + "หย่อน", + "หมอน", + "ห้อย", + "หวน", + "หวัด", + "หด", + "หยาง", + "หมี", + "นายทหาร", + "หว่า", + "หาร", + "ห้วย", + "หลอด", + "หาเรื่อง", + "หลี", + "หมัด", + "เหลี่ยม", + "หัวเมือง", + "ห้วง", + "หวั่น", + "อ่อนหวาน", + "เหนียว", + "หมั่น", + "เหลียว", + "นัดหมาย", + "ดาวหาง", + "หมวย", + "เหี่ยว", + "หน้าร้าน", + "หือ", + "หยอด", + "เหมือง", + "เหม่อ", + "ย้อนหลัง", + "หน้าท้อง", + ], + }, + { + name: "16. ก", + words: [ + "การ", + "กัน", + "มาก", + "อีก", + "ออก", + "กว่า", + "ก่อน", + "อยาก", + "หาก", + "กล่าว", + "ดังกล่าว", + "นัก", + "รัก", + "หลัก", + "เรียก", + "การเมือง", + "มัก", + "เลือก", + "เดียวกัน", + "เรียกว่า", + "หรอก", + "อาการ", + "กลาง", + "ยก", + "ทางการ", + "นอก", + "ยาก", + "ร่างกาย", + "มากมาย", + "กลัว", + "กรรมการ", + "กรม", + "เก่า", + "รายการ", + "นักเรียน", + "หนัก", + "กว้าง", + "กล้า", + "กรรม", + "นก", + "กา", + "กี่", + "กอง", + "กาย", + "ก้าว", + "ดอก", + "หลักการ", + "เก่ง", + "ก่อ", + "เรียกร้อง", + "หก", + "หลากหลาย", + "นายก", + "กด", + "อก", + "ยกเว้น", + "กร", + "น่ารัก", + "ก้าวหน้า", + "เกม", + "วงการ", + "กล่าวหา", + "ก้ม", + "เอก", + "กังวล", + "หัก", + "เก้าอี้", + "กางเกง", + "หลีกเลี่ยง", + "ท่ามกลาง", + "กอด", + "ก้อน", + "อากร", + "กันเอง", + "กัด", + "ทางเลือก", + "นักการเมือง", + "ลาก", + "กวี", + "กล้ามเนื้อ", + "ลาออก", + "กลางวัน", + "นอกเหนือ", + "กล้อง", + "นักท่องเที่ยว", + "เกลียด", + "กล่อง", + "หลอก", + "กลม", + "ที่รัก", + "กาล", + "กันยายน", + "เก้า", + "ยกย่อง", + "เกรง", + "ทารก", + "ยกมือ", + "ก่อนหน้านี้", + "น่ากลัว", + "ลัก", + "มรดก", + "ก้อง", + "กวน", + "ก่อนหน้า", + "ทักทาย", + "ทางออก", + "กลาย", + "นักร้อง", + "ทัก", + "เกี่ยว", + "ราก", + "เกาหลี", + "ก้น", + "กั้น", + "เกล้า", + "เกลือ", + "กวาด", + "นางเอก", + "เก", + "กก", + "กลืน", + "กรอง", + "กี้", + "กี", + "อัยการ", + "วงกลม", + "นรก", + "ก่อการ", + "ก้า", + "ยัก", + "กอ", + "กาง", + "หมวก", + "กล้วย", + "ดัก", + "รก", + "กรัม", + "กรด", + "กดดัน", + "หน้าอก", + "กล", + "กลมกลืน", + "เมื่อกี้", + "ก้าน", + "มังกร", + "หมาก", + "ลอก", + "กง", + "หลอกลวง", + "กรีก", + "กลอน", + "นาก", + "กวาง", + "ลีก", + "กีดกัน", + "เกวียน", + "นันทนาการ", + "กาลเวลา", + "หลาก", + "กาม", + "ก้าวร้าว", + "กลั้น", + "กลอง", + "กรรมกร", + "หมอก", + "หนักหนา", + "กรีด", + "กรอก", + "เกื้อ", + "เกา", + "หวาดกลัว", + "กลั่นกรอง", + "เกรด", + "วาทกรรม", + "นักหนา", + "อีกหน่อย", + "เกี่ยวเนื่อง", + "กอน", + "เงือก", + "กรง", + "หน้ากาก", + "หลีก", + "นักเลง", + "เกร", + "หมัก", + "เกาหลีเหนือ", + "กัง", + "กัก", + "กลั่น", + ], + }, + { + name: "17. ไ", + words: [ + "ไม่", + "ได้", + "ไทย", + "ไว้", + "ไหน", + "อย่างไร", + "หรือไม่", + "ไหม", + "ไม่ว่า", + "ไม้", + "ยังไง", + "ไง", + "รายได้", + "ไอ้", + "ไกล", + "เมืองไทย", + "ไหล", + "ไร้", + "ที่ไหน", + "ไอ", + "ดอกไม้", + "ไม่ทัน", + "ไหว", + "ไร่", + "ไท", + "ไล่", + "ไก่", + "เท่าไหร่", + "ร้องไห้", + "กลไก", + "ไหว้", + "ได้ที่", + "เมื่อไหร่", + "ไร", + "ไหม้", + "เท่าไร", + "ไหล่", + "ไว", + "มหาดไทย", + "ห่างไกล", + "ไม", + "เมื่อไร", + "ได", + "ไล", + "ไกร", + "หาไม่", + "หวั่นไหว", + "ไน", + ], + }, + { + name: "18. ะ", + words: [ + "อะไร", + "นะ", + "ระหว่าง", + "ละ", + "ล่ะ", + "ระยะ", + "กระทรวง", + "น่ะ", + "หัวเราะ", + "ทะเล", + "ร้อยละ", + "รายละเอียด", + "เกาะ", + "อะ", + "เยอะ", + "ระ", + "กะ", + "ระวัง", + "ละเอียด", + "เหมาะ", + "วะ", + "กระ", + "ยะ", + "วาระ", + "อวัยวะ", + "มะ", + "ทีละ", + "ระมัดระวัง", + "กระทั่ง", + "ทะ", + "ทะเลาะ", + "ระยะทาง", + "อ่ะ", + "ระวาง", + "ระดม", + "เดอะ", + "กระนั้น", + "ละลาย", + "ละเลย", + "กระมัง", + "กระเทือน", + "ระยะห่าง", + "มานะ", + "ย่ะ", + "เกะ", + "กะทันหัน", + "กระเทียม", + "เยาะ", + "ละออง", + "มะนาว", + "กระดาน", + "ยะลา", + "ละเอียดอ่อน", + "ระยอง", + "ว่ะ", + "มะม่วง", + "กระทง", + ], + }, + { + name: "19. ต", + words: [ + "ต้อง", + "ตาม", + "ตัว", + "ต่อ", + "ต่าง", + "มาตรา", + "ตัวเอง", + "ตน", + "ตา", + "ตั้ง", + "ตรง", + "ตอน", + "ต้องการ", + "ตาย", + "ตลอด", + "ตนเอง", + "ตัวอย่าง", + "ตลาด", + "ต้น", + "อัตรา", + "ตอนนี้", + "ต่อมา", + "ตก", + "อดีต", + "ตัด", + "ตกลง", + "เตรียม", + "อันตราย", + "ตะวันตก", + "ต่อเนื่อง", + "ตี", + "ดนตรี", + "มาตรการ", + "ตื่น", + "ตาราง", + "ที่ตั้ง", + "หน้าตา", + "การเลือกตั้ง", + "ต้นไม้", + "ตัน", + "ตอนนั้น", + "ดวงตา", + "ตะวันออก", + "เมตร", + "เลือกตั้ง", + "ต่างหาก", + "ตรงนี้", + "เตือน", + "เต้น", + "เตียง", + "ต่อต้าน", + "ตระหนัก", + "ตื่นเต้น", + "ตรา", + "ตะ", + "หน้าต่าง", + "ตัก", + "อีตา", + "ต้า", + "ต้ม", + "เมตตา", + "ตลก", + "รวมตัว", + "ต่างด้าว", + "ต้องหา", + "ตัวเรา", + "ต่อเมื่อ", + "ไต", + "ต่อว่า", + "เตรียมตัว", + "ต่อหน้า", + "ก่อตั้ง", + "ตรงกัน", + "ต่อรอง", + "ตาก", + "เต้", + "ตะวัน", + "ตรี", + "เต", + "ตี้", + "เต่า", + "ตอนต้น", + "เตะ", + "ไต้หวัน", + "ตรงกลาง", + "ลืมตา", + "ต้าน", + "ลงตัว", + "เตา", + "ตีน", + "รัตน", + "ตายตัว", + "ต่อม", + "อัต", + "นักเตะ", + "ตื้น", + "ต่อย", + "มาตร", + "เตี้ย", + "ราตรี", + "ตอง", + "ต้านทาน", + "ตอนหลัง", + "เนตร", + "นวัตกรรม", + "กระตือรือร้น", + "ตอ", + "ไตร", + "ตั้งตัว", + "ตื่นตัว", + "เตี่ย", + "วางตัว", + "ตัวเมีย", + "ตัวเดียว", + "เกต", + "ตะกร้า", + "ตะกอน", + "ไต่", + "ไมตรี", + ], + }, + { + name: "20. ิ (สระอิ)", + words: [ + "เกิด", + "เงิน", + "เดิน", + "กิน", + "เริ่ม", + "ยิ่ง", + "ติด", + "เดิม", + "เกิน", + "เดินทาง", + "นิยม", + "ยิ้ม", + "มิ", + "มิได้", + "มหาวิทยาลัย", + "วิ่ง", + "ได้ยิน", + "ที่ดิน", + "ทิ้ง", + "เลิก", + "ติดต่อ", + "ดิน", + "นิติกรรม", + "นิ่ง", + "อย่างยิ่ง", + "มติ", + "กลิ่น", + "หิน", + "ติดตาม", + "การเงิน", + "อิน", + "เริ่มต้น", + "ยิง", + "วิ", + "นิด", + "นิ้ว", + "ดิ", + "ริม", + "ริ", + "ติ", + "อเมริกัน", + "นิ", + "อินเดีย", + "ลิ", + "อเมริกา", + "อิ", + "วิทยา", + "เงินเดือน", + "ยินดี", + "ยินยอม", + "นิทาน", + "รอยยิ้ม", + "อาทิ", + "เหลือเกิน", + "ยกเลิก", + "เกียรติ", + "เติม", + "กิ", + "ริน", + "มิติ", + "นิยาม", + "วิน", + "ดั้งเดิม", + "วินัย", + "มิตร", + "กริยา", + "วินาที", + "เหมือนเดิม", + "หิว", + "ลิ้น", + "อิ่ม", + "ละเมิด", + "ลิน", + "นิดหน่อย", + "เงินได้", + "ลิง", + "วงเงิน", + "ทางเดิน", + "อ้างอิง", + "มาริ", + "ทิ", + "นวนิยาย", + "อิตาลี", + "วิตามิน", + "กิ่ง", + "ติดตั้ง", + "กิริยา", + "วิทยาลัย", + "ติดตัว", + "เงินทอง", + "อิง", + "นิดเดียว", + "มิน", + "นิยาย", + "เงินตรา", + "วิตกกังวล", + "เดินหน้า", + "นิติ", + "ดิ่ง", + "ดิ้น", + "หากิน", + "กติกา", + "มิด", + "ริเริ่ม", + "นิก", + "ลิตร", + "ร่าเริง", + "นิว", + "นิกาย", + "หาเงิน", + "ริย", + "วิตก", + "มิก", + "ดิ้นรน", + "นิล", + "อินเดียน", + "อิม", + "นิร", + "วิหาร", + "ติก", + "หิมะ", + "วิล", + "เนิน", + "วิว", + "หิ้ว", + "ติว", + "อิหร่าน", + "กิตติ", + "ทอดทิ้ง", + "นิมิต", + "ทวิ", + "ยิน", + "ยิว", + "ลิ้ม", + "ริก", + "นินทา", + "เริง", + "ระเริง", + "นลิน", + ], + }, + { + name: "21. ุ (สระอุ)", + words: [ + "ทุก", + "กลุ่ม", + "เหตุ", + "อายุ", + "หยุด", + "รุ่น", + "หนุ่ม", + "มุม", + "ทุน", + "ลุก", + "กรุง", + "มุ่ง", + "ลุง", + "ต้นทุน", + "กองทุน", + "กระตุ้น", + "การลงทุน", + "หุ่น", + "ลงทุน", + "หุ้น", + "กุ้ง", + "อนุมัติ", + "ทุกวันนี้", + "วิทยุ", + "มีอายุ", + "หลุด", + "วัยรุ่น", + "ทุนนิยม", + "กุล", + "มุก", + "รุ่ง", + "ยุ่ง", + "หมุน", + "อนุกรรมการ", + "วุ่นวาย", + "อุดหนุน", + "นุ", + "ทุ่ม", + "ดุ", + "อนุ", + "ด้วยเหตุนี้", + "ยุ", + "ทุ่ง", + "วันหยุด", + "อุ", + "หลุม", + "ทุกที", + "ยุติ", + "หงุดหงิด", + "นุ่ม", + "นายทุน", + "เงินทุน", + "อุ่น", + "กุ", + "อุ้ม", + "ยุ่งยาก", + "ยืดหยุ่น", + "รุ่งเรือง", + "กุม", + "มุ่งมั่น", + "ทะลุ", + "อุดม", + "นักลงทุน", + "หมุนเวียน", + "หนุน", + "ตุลาการ", + "นุ่ง", + "อุด", + "รุม", + "รุก", + "ทรุด", + "ลุ่ม", + "ทุ่มเท", + "นุ่มนวล", + "หุ้ม", + "อุทาน", + "รุ", + "ลุย", + "ตุ", + "ต้นเหตุ", + "รุ้ง", + "หุง", + "ลุ้น", + "หมายเหตุ", + "ทุ่มตลาด", + "มุ่งหน้า", + "มุ่งหมาย", + "ตุง", + "อุทยาน", + "รุกราน", + "ทุ", + "กระตุก", + "ตุ่ม", + "ยุ้ย", + ], + }, + { + name: "22. ู (สระอู)", + words: [ + "อยู่", + "ดู", + "รู้", + "ลูก", + "ที่อยู่", + "หนู", + "เรียนรู้", + "หู", + "หมู่", + "ดูเหมือน", + "กู", + "ยู", + "ตู้", + "หมู", + "รู้ตัว", + "ตระกูล", + "กู้", + "รู", + "กระดูก", + "ลูกหลาน", + "เลี้ยงดู", + "ดูด", + "ทูต", + "มูล", + "งู", + "เงินกู้", + "ลูกหนี้", + "กู้ยืม", + "ทูล", + "ลู", + "หรู", + "อยู่รอด", + "มู", + "ทู", + "เมนู", + "หรูหรา", + "รู้อยู่", + "อู", + "หมู่เกาะ", + "ดูงาน", + "อู่", + "มดลูก", + "กู้เงิน", + "มูลเหตุ", + "เกื้อกูล", + "ลูกตา", + "ลู่", + ], + }, + { + name: "23. ใ (สระใอ) ", + words: [ + "ใน", + "ให้", + "ใหม่", + "ใด", + "ใกล้", + "ใต้", + "ให้การ", + "ก่อให้เกิด", + "มิให้", + "เท่าใด", + "ให้ออก", + "เมื่อใด", + "ห่วงใย", + "เกาหลีใต้", + "ใย", + "ทันใด", + "หลงใหล", + ], + }, + { + name: "24. บ", + words: [ + "กับ", + "บอก", + "กลับ", + "บาง", + "ได้รับ", + "รับ", + "บ้าน", + "ระบบ", + "บน", + "ระดับ", + "เกี่ยวกับ", + "บ้าง", + "บาท", + "ตอบ", + "รอบ", + "ใบ", + "บริการ", + "ทราบ", + "บริหาร", + "บท", + "ยอมรับ", + "ระเบียบ", + "รีบ", + "นับ", + "กระบวนการ", + "ระบุ", + "เกือบ", + "บทบาท", + "หมู่บ้าน", + "เบา", + "บ้า", + "บ่อย", + "เรียบร้อย", + "เท่ากับ", + "เงียบ", + "รับรู้", + "หลับ", + "บางที", + "บรรดา", + "กระทบ", + "รับรอง", + "บิน", + "หยิบ", + "กรอบ", + "เหมือนกับ", + "อันดับ", + "ลับ", + "บา", + "เทียบ", + "ใบหน้า", + "มอบ", + "ราวกับ", + "บิดา", + "บุตร", + "ทะเบียน", + "กลับบ้าน", + "รวบรวม", + "บรรยาย", + "บัตร", + "หลบ", + "ระบอบ", + "บ้านเมือง", + "เรียบ", + "มอบหมาย", + "ลบ", + "ดอกเบี้ย", + "บุรี", + "บ่น", + "เบื้องต้น", + "ดับ", + "ระเบิด", + "บันได", + "เบื่อ", + "บุหรี่", + "ระบาย", + "อบรม", + "บี", + "บวก", + "นับว่า", + "บรรลุ", + "รองรับ", + "บ่าย", + "อบอุ่น", + "เบิก", + "ทับ", + "อุบัติเหตุ", + "บัว", + "รบ", + "บาน", + "ต้อนรับ", + "ทบทวน", + "งบ", + "ระงับ", + "กุหลาบ", + "บ่อ", + "บัดนี้", + "บันเทิง", + "รบกวน", + "กระบอก", + "ตบ", + "รับทราบ", + "บีบ", + "บุก", + "ตอบรับ", + "ดาบ", + "ใบไม้", + "กราบ", + "ยุบ", + "บันดาล", + "เบื้องหลัง", + "บริหารงาน", + "บ่", + "บารมี", + "บก", + "บีม", + "ย้อนกลับ", + "บรรเทา", + "หลับตา", + "หอบ", + "เหยียบ", + "ดิบ", + "บ้านเรือน", + "ตับ", + "บู", + "เหลือบ", + "บ่งบอก", + "บอล", + "เบื้อง", + "บริบท", + "บุ", + "ราบ", + "อบ", + "ลอบ", + "เบ", + "อัลบั้ม", + "เทียบเท่า", + "บรรเลง", + "ระเบียง", + "กบ", + "บรรทัด", + "บิต", + "ลูบ", + "บัง", + "บริวาร", + "บ่า", + "บ่อน", + "วูบ", + "บทเรียน", + "บรรทุก", + "หยาบ", + "ทุบ", + "กระบวน", + "กลีบ", + "อนุบาล", + "บรม", + "รับมือ", + "ระนาบ", + "อาบ", + "รวบ", + "บิด", + "ไบ", + "บดี", + "เบื้องหน้า", + "บวม", + "ตาบอด", + "การบริการ", + "บังเกิด", + "บาล", + "บัน", + "อุบล", + "ระบาด", + "ราบรื่น", + "บิ", + "บอย", + "เบาหวาน", + "บาลี", + "บอกกล่าว", + "ทบ", + "ลักลอบ", + "ลงทะเบียน", + "นักรบ", + "บาย", + "ยับยั้ง", + "บ้านเกิด", + "เบื่อหน่าย", + "เบี้ย", + "บางกอก", + "ทบวง", + "บะ", + "บี้", + "บด", + "ทับทิม", + "บาตร", + "กระบี่", + "กลับกลาย", + "วับ", + "ทิเบต", + "วาบ", + "เรียบเรียง", + "เบาะ", + "กลบ", + "เบียด", + "บอ", + "เบียดเบียน", + "เบี่ยงเบน", + "บิดเบือน", + "เบน", + "อาหรับ", + "บวร", + "กระเบื้อง", + ], + }, + { + name: "25. ็ (ไม้ไต่คู้)", + words: [ + "ก็", + "เห็น", + "เด็ก", + "ก็ได้", + "เล็ก", + "เก็บ", + "เต็ม", + "เย็น", + "เร็ว", + "ก็ตาม", + "อย่างไรก็ตาม", + "รวดเร็ว", + "มองเห็น", + "เล็กน้อย", + "ก็ดี", + "เต็มที่", + "เห็นด้วย", + "เม็ด", + "เหล็ก", + "เอ็ม", + "มะเร็ง", + "เอ็น", + "เว็บ", + "อย่างไรก็ดี", + "เอ็ด", + "เอ็ง", + "เมล็ด", + "เน็ต", + "เอ็นดู", + "เหม็น", + "ละก็", + "ดีเอ็นเอ", + "เด็ด", + "เล็บ", + "เกร็ง", + "เย็บ", + "ก็อด", + "ตอนเย็น", + "ตู้เย็น", + "เล็ง", + "เต็มที", + "เบ็ด", + "กระเด็น", + "เต็มตัว", + "ล็อก", + "เก็บเกี่ยว", + ], + }, + { + name: "26. จ", + words: [ + "จะ", + "จาก", + "อาจ", + "จน", + "จริง", + "ใจ", + "จัด", + "จีน", + "เจ้า", + "เนื่องจาก", + "จังหวัด", + "รู้จัก", + "จุด", + "จับ", + "วิจัย", + "หลังจาก", + "จัดการ", + "ตรวจ", + "จบ", + "เจอ", + "นอกจาก", + "เจ้าหน้าที่", + "จิต", + "หัวใจ", + "จ่าย", + "นอกจากนี้", + "กิจกรรม", + "กิจการ", + "จิตใจ", + "เจตนา", + "ตั้งใจ", + "กระจาย", + "ตลอดจน", + "ลูกจ้าง", + "จากนั้น", + "หายใจ", + "เจ็บ", + "จัดตั้ง", + "จ้าง", + "จังหวะ", + "จนกระทั่ง", + "จี", + "จัง", + "บรรจุ", + "จิ", + "จง", + "เจรจา", + "จอด", + "ตกใจ", + "นอกจากนั้น", + "นายจ้าง", + "เจ", + "จาน", + "จริงจัง", + "มั่นใจ", + "หลังจากนั้น", + "ดีใจ", + "ยากจน", + "จดหมาย", + "เจ็ด", + "จดทะเบียน", + "บาดเจ็บ", + "จา", + "เจ้านาย", + "รายจ่าย", + "ที่จริง", + "จมูก", + "จนกว่า", + "กระจก", + "กิจ", + "จัดหา", + "จัดงาน", + "นักวิจัย", + "จิตวิทยา", + "จะเห็นได้ว่า", + "จ้อง", + "เจาะ", + "จินตนาการ", + "จัก", + "จูงใจ", + "จับกุม", + "จี้", + "ลมหายใจ", + "เนื่องมาจาก", + "จด", + "วงจร", + "จอ", + "จักร", + "จม", + "ทุจริต", + "จักรยาน", + "จาง", + "เห็นใจ", + "จิน", + "รับจ้าง", + "จอม", + "ต่างจังหวัด", + "เจ้าตัว", + "จักรวาล", + "จอง", + "ตามใจ", + "จราจร", + "จุดมุ่งหมาย", + "เจน", + "ไว้วางใจ", + "เจ้าหนี้", + "จู", + "รังเกียจ", + "ใจดี", + "จงใจ", + "จัน", + "จูบ", + "วาจา", + "เกรงใจ", + "จ้ะ", + "เต็มใจ", + "กระจ่าง", + "จู่", + "ไว้ใจ", + "จุดอ่อน", + "วิจิตร", + "ใจเย็น", + "เอาใจ", + "จุ", + "จีบ", + "จ้างงาน", + "จิตรกรรม", + "เจาะจง", + "จุดเริ่มต้น", + "จุดหมาย", + "จ้า", + "จากนี้", + "ติดใจ", + "จ้องมอง", + "จริงใจ", + "เจ้าหล่อน", + "จิร", + "เท็จ", + "จิ้ม", + "จุน", + "น้อยใจ", + "จตุ", + "จ่า", + "เจ้าอยู่หัว", + "ต่อจากนั้น", + "นิจ", + "ใจกลาง", + "จัดวาง", + "กรรมวาจก", + "ดุจ", + "จุดยืน", + "ว่าจ้าง", + "จุดเด่น", + "เจียง", + "นักจิตวิทยา", + "บรรจง", + "จรด", + "จูง", + "จับตา", + "จอย", + "อยู่จริง", + "จริงอยู่", + "จิบ", + ], + }, + { + name: "27. ค", + words: [ + "ความ", + "คน", + "คือ", + "ครั้ง", + "คิด", + "เคย", + "ใคร", + "คง", + "ควร", + "บุคคล", + "ครับ", + "ค่า", + "ก็คือ", + "ค่ะ", + "ความคิด", + "ราคา", + "คะ", + "ครู", + "คืน", + "ครอบครัว", + "คู่", + "ความรู้", + "ควบคุม", + "ความหมาย", + "ยุค", + "บังคับ", + "เครื่อง", + "ยังคง", + "คุย", + "คดี", + "ค้า", + "ไม่ค่อย", + "ค่อย", + "ความจริง", + "คอย", + "คนใน", + "หมายความ", + "คล้าย", + "วรรค", + "คราว", + "มั่นคง", + "ความต้องการ", + "นคร", + "ครบ", + "อนาคต", + "ละคร", + "อาคาร", + "ลูกค้า", + "คาด", + "คัน", + "คอ", + "เครื่องมือ", + "คุ้มครอง", + "กล่าวคือ", + "คา", + "เคลื่อนไหว", + "เค", + "เทคนิค", + "เค้า", + "คิดเห็น", + "คุรุ", + "นั่นคือ", + "มูลค่า", + "ตีความ", + "คนเรา", + "ความร่วมมือ", + "คลื่น", + "การคลัง", + "บุคลากร", + "ตัวละคร", + "มหานคร", + "คุม", + "คลอง", + "ตุลาคม", + "ใกล้เคียง", + "คว้า", + "ทั้งคู่", + "ค้าง", + "คัดเลือก", + "เครื่องหมาย", + "คม", + "คนงาน", + "บทความ", + "ครอบครอง", + "ค่าจ้าง", + "ครัว", + "เครื่องบิน", + "ค่านิยม", + "ความดี", + "คลอด", + "เคลื่อน", + "คิ้ว", + "ครอง", + "ครอบคลุม", + "คุ้นเคย", + "กลางคืน", + "ค้นหา", + "ครัวเรือน", + "ว่าความ", + "มกราคม", + "มีนาคม", + "ความหวัง", + "คบ", + "เครียด", + "มงคล", + "บริจาค", + "อายุความ", + "ครู่", + "คัดค้าน", + "ค่าย", + "ครั้น", + "คนรัก", + "คิว", + "คอน", + "คาดหวัง", + "คลาย", + "ค้นคว้า", + "ควาย", + "คัด", + "หลังคา", + "ความเร็ว", + "เคลื่อนที่", + "ค้น", + "ควัน", + "นิติบุคคล", + "ครีม", + "ความมั่นใจ", + "คลุม", + "คมนาคม", + "เคลื่อนย้าย", + "คุ้ม", + "คนร้าย", + "เคาะ", + "เครือ", + "คติ", + "คิ", + "คนจน", + "อังคาร", + "เครื่องจักร", + "คอล", + "เครดิต", + "ใคร่", + "คงที่", + "ครั้งนั้น", + "คุ้น", + "คลัง", + "ความเครียด", + "เคียง", + "รอบคอบ", + "ร้านค้า", + "ควบคู่", + "เครื่องดื่ม", + "คุ", + "คาน", + "คุก", + "ทนายความ", + "เคร่งครัด", + "คล่อง", + "คอม", + "ความกลัว", + "คุกคาม", + "อุดมคติ", + "คิง", + "รอคอย", + "หาความ", + "คี", + "ครก", + "ไมเคิล", + "ความเคลื่อนไหว", + "คืบหน้า", + "คนตาย", + "บุคลิก", + "คราบ", + "คุ้มค่า", + "คู", + "นาค", + "เค้ก", + "คิดมาก", + "เคมี", + "คู่หมั้น", + "คาบ", + "ควง", + "อคติ", + "ความเค้น", + "คู่รัก", + "คิม", + "ค้อน", + "เครื่องดนตรี", + "ครุ่นคิด", + "นักคิด", + "เลค", + "ครอบ", + "ค่อน", + "คลาน", + "เคี้ยว", + "คลี่คลาย", + "คลินิก", + "เคน", + "ค่าที่", + "ล็อค", + "เค็ม", + "นิคม", + "คาง", + "เคลือบ", + "คิดค้น", + "คับ", + "คีรี", + "ควบ", + "เทค", + "คู่มือ", + "คลอ", + "คริ", + "คร่าว", + "กลางคน", + "เคท", + "ครา", + "คนรวย", + "เคือง", + "คาดหมาย", + "รักใคร่", + ], + }, + { + name: "28. ส", + words: [ + "ส่วน", + "สอง", + "สิ่ง", + "สังคม", + "เสียง", + "สร้าง", + "ที่สุด", + "เสีย", + "สี", + "สูง", + "ส่ง", + "สาว", + "หนังสือ", + "สาม", + "เสนอ", + "สู่", + "ใส่", + "สมัย", + "สัก", + "สินค้า", + "สิ", + "สอน", + "สิบ", + "สาย", + "สวย", + "สนับสนุน", + "เสมอ", + "สั่ง", + "สนใจ", + "สุดท้าย", + "สาร", + "สี่", + "เหมาะสม", + "เส้น", + "สุด", + "ตัดสินใจ", + "อุตสาหกรรม", + "สายตา", + "เสร็จ", + "ตรวจสอบ", + "ส่งเสริม", + "สังเกต", + "เสื้อ", + "สบาย", + "สื่อสาร", + "สา", + "สวน", + "สงสัย", + "สงบ", + "สั้น", + "สื่อ", + "สาเหตุ", + "เอกสาร", + "สุ", + "สม", + "สามี", + "สูงสุด", + "ส่วนตัว", + "สมเด็จ", + "สอบ", + "สนิท", + "สมอง", + "ต่อสู้", + "สะดวก", + "สมควร", + "อิสระ", + "เสด็จ", + "สนุก", + "สงคราม", + "สะท้อน", + "ส่งออก", + "ความสนใจ", + "สะอาด", + "เสี่ยง", + "ใส", + "สด", + "สอดคล้อง", + "สิ้น", + "สมาคม", + "ลูกสาว", + "ทั้งสิ้น", + "สติ", + "สอบสวน", + "รส", + "เสริม", + "เส้นทาง", + "สมรส", + "น่าสนใจ", + "สาระ", + "สนาม", + "สวยงาม", + "ตัดสิน", + "สตรี", + "ความเสียหาย", + "สืบ", + "สู้", + "สมัยใหม่", + "นิสัย", + "สยาม", + "สวม", + "สัน", + "ทดสอบ", + "สว่าง", + "สีหน้า", + "เสรี", + "ก่อสร้าง", + "สะสม", + "สัดส่วน", + "หลักสูตร", + "ส่วนร่วม", + "สมัคร", + "นิตยสาร", + "เสียก่อน", + "สนทนา", + "เสือ", + "เอส", + "สิ้นสุด", + "ออกเสียง", + "สั่น", + "ตอบสนอง", + "สูบ", + "สากล", + "สระ", + "เสียหาย", + "สบายใจ", + "สมบัติ", + "ล่าสุด", + "เสียใจ", + "สบ", + "มีเสียง", + "เสื่อม", + "จัดสรร", + "สังกัด", + "วัสดุ", + "สูตร", + "ส่าย", + "เสา", + "สงสาร", + "ส่วนมาก", + "ทาส", + "ทันสมัย", + "ส่อง", + "ยี่สิบ", + "ส้ม", + "เสมือน", + "สวัสดิการ", + "สดใส", + "สับสน", + "สูงอายุ", + "สนอง", + "สลับ", + "สมมติ", + "สนุกสนาน", + "ในที่สุด", + "สมการ", + "สะ", + "อักเสบ", + "สีสัน", + "สน", + "มุสลิม", + "สมัยก่อน", + "ใส่ใจ", + "สารเคมี", + "สมดุล", + "สามสิบ", + "คู่สมรส", + "สิงหาคม", + "อีสาน", + "สักหน่อย", + "สนามบิน", + "สาน", + "ส่วนรวม", + "สุร", + "ตรัส", + "สวด", + "สืบสวน", + "สิริ", + "สุจริต", + "สกุล", + "สิน", + "เสียดาย", + "ออสเตรเลีย", + "สลาย", + "สมุด", + "สังคมนิยม", + "สุก", + "ส่งเสียง", + "ได้เสีย", + "สวัสดี", + "เสียที", + "รังสี", + "สันติ", + "สุดยอด", + "สัม", + "สมุทร", + "สืบทอด", + "กอส", + "อาสา", + "รหัส", + "นิสิต", + "เอาใจใส่", + "ไต่สวน", + "กิเลส", + "ไส้", + "สนิทสนม", + "สะดุ้ง", + "สร้อย", + "สงวน", + "สง่า", + "สั่งการ", + "สัมมนา", + "นางสาว", + "สามัคคี", + "เสริมสร้าง", + "สละ", + "รับสั่ง", + "สอด", + "สบู่", + "หนี้สิน", + "สมทบ", + "สั่งสอน", + "ห้องสมุด", + "สุรา", + "อาสาสมัคร", + "สุนทร", + "เทนนิส", + "ส่วนกลาง", + "ส่งมอบ", + "สู", + "เสร็จสิ้น", + "สร", + "สูด", + "ทุกสิ่งทุกอย่าง", + "สมัครใจ", + "สมมุติ", + "การเรียนการสอน", + "สห", + "คลาสสิก", + "เรื่องสั้น", + "ไตรมาส", + "สมา", + "มหาสมุทร", + "สะกด", + "เสียสละ", + "สรรหา", + "อิสลาม", + "สะดุด", + "สินะ", + "สวัส", + "สิว", + "สาหัส", + "สหาย", + "วารสาร", + "เงินสด", + "สาด", + "กระสุน", + "สัง", + "อิสราเอล", + "เสวย", + "สกัด", + "รสนิยม", + "สังหาร", + "อัตราส่วน", + "สายลม", + "เส", + "คริส", + "ค่าเสียหาย", + "สลัด", + "สังคีต", + "สับ", + "สืบเนื่อง", + "สี่เหลี่ยม", + "สะบัด", + "น่าเสียดาย", + "เสี่ย", + "นามสกุล", + "ส้อม", + "หาเสียง", + "หมดสิ้น", + "เสี้ยว", + "ตราสาร", + "เบส", + "นักสืบ", + "หุ้นส่วน", + "ดิส", + "วิสัย", + "ไวรัส", + "เจ้าสาว", + "สารคดี", + "สรร", + "มือสอง", + "เส้นตรง", + "เสียบ", + "สายการบิน", + "สวิส", + "สนั่น", + "ลูกเสือ", + "เส้นเลือด", + "สลัก", + "มเหสี", + "สิง", + "สุ่ม", + "ดุสิต", + "เสน", + "ส่งตัว", + "สอ", + "อสุจิ", + "สาบาน", + "ทะเลสาบ", + "หมั่นไส้", + "สงครามเย็น", + "สลาก", + "สง", + "เรส", + "สุสาน", + "สะกิด", + "มัส", + "สูงส่ง", + "สินสมรส", + "มาส", + "ยาสูบ", + "บทสนทนา", + "สเต", + "สรา", + "อริส", + ], + }, + { + name: "29. ป", + words: [ + "เป็น", + "ไป", + "ปี", + "ต่อไป", + "เปิด", + "รูป", + "เปลี่ยน", + "ปัจจุบัน", + "ประกอบ", + "กลายเป็น", + "ปกติ", + "ประการ", + "เปล่า", + "ปาก", + "เป็นต้น", + "ปรับ", + "ปล่อย", + "ปลา", + "ปกครอง", + "ประตู", + "ทั่วไป", + "ปิด", + "ป่า", + "ป้องกัน", + "ปัจจัย", + "ปลาย", + "ประเด็น", + "ประสบ", + "สรุป", + "อันเป็น", + "เกินไป", + "ประกอบด้วย", + "ป้า", + "เป้าหมาย", + "เปรียบเทียบ", + "ปลูก", + "ปรับปรุง", + "ต่อไปนี้", + "ประ", + "ประเมิน", + "ปืน", + "ประกัน", + "ปา", + "รับประทาน", + "ปวด", + "ประมวล", + "ความเป็นจริง", + "ประสาน", + "ประวัติ", + "ปรับตัว", + "ปู", + "รูปร่าง", + "ป่วย", + "ปู่", + "ประสาท", + "ตกเป็น", + "ประกอบการ", + "ปลัด", + "ปัก", + "อุปสรรค", + "ปรกติ", + "ปั้น", + "เป็นไร", + "ประหยัด", + "ป้าย", + "ประมง", + "ประดับ", + "ปกป้อง", + "สเปน", + "เป็นอัน", + "เจ็บปวด", + "อุป", + "ปราบปราม", + "ประหลาด", + "ประทับใจ", + "ประกาย", + "เปลือก", + "ประมาท", + "เป้า", + "ปน", + "เปน", + "ประทับ", + "ประสานงาน", + "ปลอม", + "ทั้งปวง", + "เป็นต้นมา", + "ประกวด", + "ปรา", + "ปรุง", + "รูปทรง", + "ทวีป", + "ปอด", + "ปลุก", + "เปรียบ", + "ปราสาท", + "เป็นต้นไป", + "ปีก", + "ประหาร", + "ประท้วง", + "ประมูล", + "บาป", + "เปื้อน", + "สกปรก", + "ปาน", + "เจ็บป่วย", + "ป้อม", + "ปก", + "เป่า", + "เครื่องประดับ", + "ปีใหม่", + "เป็นกลาง", + "ปะ", + "ปะการัง", + "เป้", + "ปัสสาวะ", + "ปารีส", + "เปียก", + "หลักประกัน", + "เป้น", + "ปัด", + "เทป", + "ป้อน", + "ปิ", + "ปลอด", + "ปลายทาง", + "เป็ด", + "ประคอง", + "ป่าไม้", + "ปิ้ง", + "ปลด", + "ปะทะ", + "เป็นอย่างยิ่ง", + "ได้เปรียบ", + "ปิ่น", + "ปีน", + "ปทุม", + "เปี่ยม", + "สัมปทาน", + "เปล่ง", + "ปราบ", + "ว่างเปล่า", + "ประทาน", + "ปลื้ม", + "ปั่น", + "เปรี้ยว", + "ประหลาดใจ", + "ปกปิด", + "ตรงไปตรงมา", + "ปัน", + "ปัจเจกบุคคล", + "ปานกลาง", + "สปา", + "ปัท", + "ปัจเจก", + "ปรัมปรา", + "เปรียบเสมือน", + "ปวดหัว", + "ปริ", + "เสียเปรียบ", + "ปม", + "ปักกิ่ง", + "ปอ", + "ปูน", + "ปลอบ", + "ปากกา", + "ปุ่ม", + "ป่านนี้", + "ปลดปล่อย", + "ปล่อยตัว", + "เป็นอันมาก", + "ปล้น", + "อุปทาน", + "เปรม", + "ประนอม", + "เป็นได้", + "เอาเปรียบ", + "ปี้", + "ปิดบัง", + "ปะปน", + "ประจวบ", + "เปิ้ล", + "เป", + "วิปัสสนา", + "ปลิว", + "ปกคลุม", + "ปรีดี", + "ปัง", + "ปาง", + "เปลือย", + "ปัตตานี", + "เปลี่ยนใจ", + "เป็นลม", + "อุปการะ", + "รับปาก", + "เป็นรอง", + "เครื่องปรุง", + "สาป", + ], + }, + { + name: "30. ช", + words: [ + "ใช้", + "เช่น", + "ชีวิต", + "ใช่", + "ช่วย", + "ช่วง", + "ชอบ", + "ชื่อ", + "ชาติ", + "ประชาชน", + "ชาว", + "ประชุม", + "ชั้น", + "ชนิด", + "ชาย", + "เชื่อ", + "ราชการ", + "ชุด", + "สมาชิก", + "เชิง", + "ชุมชน", + "ช่วยเหลือ", + "ชัดเจน", + "ชน", + "ราช", + "เช่นนี้", + "ชาวบ้าน", + "วิชา", + "ชา", + "ช่าง", + "ชม", + "ชวน", + "เอกชน", + "เช้า", + "ช้า", + "ชิ้น", + "ชี้", + "ชายหนุ่ม", + "ชัย", + "ชัด", + "ความเชื่อ", + "มิใช่", + "เชียง", + "ช่อง", + "วิชาการ", + "ชนบท", + "ค่าใช้จ่าย", + "ชนชั้น", + "ชั่ว", + "ชนะ", + "ประชากร", + "เช่นเดียว", + "เชื้อ", + "ลูกชาย", + "เช่นนั้น", + "ใกล้ชิด", + "เอเชีย", + "เช่า", + "รัชกาล", + "เยาวชน", + "ชาวนา", + "เห็นชอบ", + "ช้าง", + "ชิ", + "ใช้งาน", + "ชัก", + "ชื่อเสียง", + "ชิน", + "สื่อมวลชน", + "ชาติไทย", + "ใช้ได้", + "เชื่อม", + "ชิง", + "เชื่อมั่น", + "มีชื่อ", + "ชื่อว่า", + "ชู", + "ชั่วคราว", + "ชั่น", + "นักวิชาการ", + "เชียว", + "ชื่นชม", + "ใช้จ่าย", + "ชุมนุม", + "ช้อน", + "เจ้าชาย", + "ชรา", + "รายชื่อ", + "น่าเชื่อ", + "ประชา", + "ต่างชาติ", + "น้องชาย", + "รสชาติ", + "ราชา", + "มหาชน", + "เวช", + "ดังเช่น", + "ชิด", + "ค่าเช่า", + "บูชา", + "นานาชาติ", + "รับใช้", + "เดช", + "อย่างเช่น", + "เช็ค", + "ช่องทาง", + "ชล", + "เด็กชาย", + "ดัชนี", + "ชะงัก", + "เอาชนะ", + "รับราชการ", + "ชาม", + "สินเชื่อ", + "ชี", + "ชื่น", + "เชือก", + "บวช", + "ชาดก", + "ชัน", + "เชิด", + "ชะลอ", + "ชัยชนะ", + "ชมรม", + "ไชย", + "ชิม", + "ชื่อดัง", + "ชุน", + "มหาราช", + "มวลชน", + "เช็ด", + "สดชื่น", + "ตอนเช้า", + "ชดเชย", + "ช่องว่าง", + "ชนชาติ", + "ชะมัด", + "สิ่งมีชีวิต", + "ชาน", + "ชดใช้", + "เชื้อชาติ", + "กระชับ", + "เด่นชัด", + "ชาตินิยม", + "เชื้อสาย", + "สหประชาชาติ", + "เอกราช", + "ชิ้นส่วน", + "ชื่นชอบ", + "ช่อ", + "ชะตา", + "เคยชิน", + "กระชาก", + "เครื่องใช้", + "ชีวะ", + "เช", + "ประชามติ", + "ชินวัตร", + "ชาวต่างชาติ", + "ชักชวน", + "สื่อสารมวลชน", + "ชลประทาน", + "ชอบใจ", + "ประชาคม", + "ลายมือชื่อ", + "นิช", + "ชานุ", + "ชายหาด", + "วิชัย", + "คนใช้", + "ราชสีมา", + "สิ้นเชิง", + "ชั้นใน", + "ชง", + "ใช้สอย", + "ชนชั้นกลาง", + "บ่งชี้", + "ชั่ง", + "นุช", + "บุช", + "ความชอบ", + "ชะ", + "ชก", + "ชุ่ม", + "ประชาชาติ", + "กริช", + "ชีวิตชีวา", + "รัช", + "เอช", + "ชักจูง", + "ปรีชา", + "เวชกรรม", + "ชื้น", + "ประชด", + "ชายา", + "ช้อป", + "ชั่วร้าย", + "สยามบรมราชกุมารี", + ], + }, + { + name: "31. ำ (สระอำ)", + words: [ + "ทำ", + "ทำให้", + "นำ", + "คำ", + "สำหรับ", + "น้ำ", + "กำลัง", + "กำหนด", + "ทำงาน", + "จำนวน", + "อำนาจ", + "กระทำ", + "ทำไม", + "ประจำ", + "จำเป็น", + "ดำเนินการ", + "สำนักงาน", + "ต่ำ", + "จำ", + "ดำ", + "ทำการ", + "ตำรวจ", + "คำสั่ง", + "สำนัก", + "ดำเนิน", + "น้ำมัน", + "จำกัด", + "ลำดับ", + "ทำหน้าที่", + "ดำรง", + "คำตอบ", + "ทำลาย", + "ความสำเร็จ", + "ทำนอง", + "สำเร็จ", + "ชำระ", + "สำรวจ", + "จัดทำ", + "จำเลย", + "น้ำหนัก", + "จำหน่าย", + "กำเนิด", + "น้ำท่วม", + "ทำเป็น", + "น้ำตาล", + "ลำ", + "น้ำเสียง", + "ดำเนินงาน", + "ทำร้าย", + "ตำนาน", + "ลำบาก", + "น้ำตา", + "กำจัด", + "ตำบล", + "ทำท่า", + "กำลังใจ", + "กำไร", + "จำคุก", + "ตำรา", + "ย้ำ", + "ค่ำ", + "ห้องน้ำ", + "บำบัด", + "อาบน้ำ", + "กระทำการ", + "กำกับ", + "สำนวน", + "ออกกำลังกาย", + "อำนวย", + "สม่ำเสมอ", + "บำรุง", + "ทำตัว", + "ดำเนินคดี", + "สำเนา", + "รำ", + "ทองคำ", + "สำรอง", + "ทำเอา", + "น้ำใจ", + "ครอบงำ", + "น้ำเงิน", + "ล้ำ", + "ว่ายน้ำ", + "จำต้อง", + "กำ", + "ที่ทำการ", + "ทำเนียบ", + "ชั้นนำ", + "ดำรงชีวิต", + "คำร้อง", + "ตกต่ำ", + "ตำหนิ", + "ประจำตัว", + "ทำนา", + "ความจำ", + "เครื่องสำอาง", + "จำลอง", + "รับคำ", + "จดจำ", + "ทำนาย", + "ร่ำรวย", + "ลำตัว", + "ราชสำนัก", + "น้ำลาย", + "ยากลำบาก", + "ตำ", + "สำเร็จรูป", + "ทำมาหากิน", + "ลำคอ", + "ต่ำสุด", + "คล้ำ", + "คว่ำ", + "น้ำยา", + "ค้ำประกัน", + "จำนำ", + "ลำไส้", + "คำกล่าว", + "เต้นรำ", + "จำกัดความ", + "ดำน้ำ", + "มหาอำนาจ", + "ตำหนัก", + "กำหนดการ", + "กำนัน", + "ไอน้ำ", + "คำมั่น", + "ช้ำ", + "เรือนจำ", + "กองกำลัง", + "มอบอำนาจ", + "ออกกำลัง", + "บำเหน็จ", + "ทำตา", + "สำเนียง", + "ประจำเดือน", + "น้ำปลา", + "ลุ่มน้ำ", + "ค่ำคืน", + "ชำรุด", + "ระบำ", + "สายน้ำ", + "อำ", + "ตอกย้ำ", + "น้ำหอม", + ], + }, + { + name: "32. แ (สระแอ)", + words: [ + "และ", + "แล้ว", + "แต่", + "แบบ", + "แห่ง", + "แสดง", + "แม่", + "แก่", + "แรก", + "ตั้งแต่", + "แต่ละ", + "แค่", + "ตำแหน่ง", + "แม้", + "เปลี่ยนแปลง", + "แล้วก็", + "แก", + "แตกต่าง", + "แทน", + "แรง", + "แนว", + "ดูแล", + "ได้แก่", + "แก้", + "แดง", + "แบ่ง", + "รูปแบบ", + "แยก", + "แรงงาน", + "แจ้ง", + "แน่", + "แหล่ง", + "แก้ว", + "แน่นอน", + "แทบ", + "แสน", + "แปลก", + "แนวทาง", + "แม้แต่", + "แนวคิด", + "สิ่งแวดล้อม", + "แต่ง", + "แท้จริง", + "แต่งตั้ง", + "แต่งงาน", + "แปล", + "แม้ว่า", + "แนะนำ", + "แง่", + "แสง", + "กระแส", + "ออกแบบ", + "รุนแรง", + "คะแนน", + "แล", + "แตก", + "แหละ", + "แท้", + "แม่น้ำ", + "แอบ", + "นั่นแหละ", + "นับแต่", + "ตัวแทน", + "แล้วแต่", + "แสดงออก", + "แทนที่", + "แน่ใจ", + "แต่งตัว", + "แลกเปลี่ยน", + "แห้ง", + "ตอบแทน", + "ที่แล้ว", + "แน่น", + "แสวงหา", + "แย่", + "แต่ว่า", + "แปลง", + "แปด", + "ดินแดน", + "ตัวแปร", + "กันและกัน", + "แต่อย่างใด", + "ทดแทน", + "ชี้แจง", + "เว้นแต่", + "นี่แหละ", + "ความรุนแรง", + "แปลกใจ", + "แดด", + "แมน", + "แวะ", + "คำแนะนำ", + "แกล้ง", + "ร้ายแรง", + "แมว", + "แวว", + "ตกแต่ง", + "นับตั้งแต่", + "แก้ม", + "แดน", + "ตอนแรก", + "ชายแดน", + "แจก", + "แค่นี้", + "แกน", + "แหลม", + "แคบ", + "แป้ง", + "จำแนก", + "แบบจำลอง", + "แย่ง", + "แต่ก่อน", + "ดีแล้ว", + "แก่น", + "แอล", + "แกะ", + "แทรก", + "แกม", + "แม้กระทั่ง", + "เอาแต่", + "อ่อนแอ", + "ก็แล้วกัน", + "แนะแนว", + "แตะ", + "แหม", + "แอน", + "แปร", + "แล่น", + "แหง", + "แลก", + "แหวน", + "แววตา", + "ดัดแปลง", + "แคน", + "แนบ", + "แบก", + "แบ่งแยก", + "เก่าแก่", + "แมลง", + "แต่เดิม", + "แค้น", + "แกง", + "แทง", + "แล้ง", + "นักแสดง", + "แกนนำ", + "แต้ม", + "แว่น", + "แม่บ้าน", + "กระแทก", + "แสงสว่าง", + "แสงแดด", + "แต่งกาย", + "แสบ", + "แคว้น", + "หนาแน่น", + "แปลกหน้า", + "แย้ง", + "แหม่ม", + "แง่มุม", + "แก่ตัว", + "คะแนนเสียง", + "แช่", + "หนักแน่น", + "แอม", + "แวดล้อม", + "แจ่มใส", + "เลียนแบบ", + "แปรรูป", + "นั้นแหละ", + "แกรม", + "แค่นั้น", + "ต้นแบบ", + "แต่งหน้า", + "แวบ", + "แบน", + "เยอะแยะ", + "ชัดแจ้ง", + "แท่ง", + "แค", + "แตกแยก", + "แจ้งความ", + "แห่", + "แน่ชัด", + "กำกับดูแล", + "แยกแยะ", + "แบบอย่าง", + "แร่", + "แอ", + "แปรเปลี่ยน", + "แย้ม", + "แวดวง", + "แน่ะ", + "แก้ตัว", + "มันแกว", + "แม่ค้า", + "แนะ", + "แตง", + "แม่นยำ", + "แออัด", + "แล้วด้วย", + "แรม", + "เสนอแนะ", + "แบ่งปัน", + "เครื่องแบบ", + "แปะ", + "แท่น", + "แม", + "แว่ว", + "แอง", + "ทุกแห่ง", + "เริ่มแรก", + "ที่แท้", + "แม่น", + "แปรปรวน", + "ยิ้มแย้ม", + "แคนาดา", + "เจ้าแม่", + "แช", + "แสงสี", + "แต่ทว่า", + "แต่อย่างไรก็ตาม", + "ระแวง", + "แม้น", + "แบ", + ], + }, + { + name: "33. ข", + words: [ + "ของ", + "เขา", + "เข้า", + "ขอ", + "ข้อ", + "ข้อมูล", + "เข้าใจ", + "ขนาด", + "ขาย", + "เขียน", + "สุข", + "ข้าง", + "ข้าว", + "แก้ไข", + "เขต", + "เจ้าของ", + "ขาด", + "ข่าว", + "เกี่ยวข้อง", + "ขาว", + "ขั้น", + "ข้าราชการ", + "ขยาย", + "แข่งขัน", + "ขา", + "ค่อนข้าง", + "เงื่อนไข", + "สาขา", + "ข้าม", + "ข้า", + "ข้อความ", + "เข้าสู่", + "แขน", + "นำเข้า", + "ขั้นตอน", + "ขน", + "เขียว", + "ขนส่ง", + "ข้างต้น", + "ข่าวสาร", + "ขัด", + "ความขัดแย้ง", + "คนไข้", + "ข้างหน้า", + "ข้อเท็จจริง", + "แข็งแรง", + "ขนม", + "ขยายตัว", + "ขอบ", + "ตัวเลข", + "ขวา", + "ไข่", + "ข้อตกลง", + "ตรงข้าม", + "ขับ", + "แขก", + "กว้างขวาง", + "ขยับ", + "เด็ดขาด", + "ขี้", + "เครือข่าย", + "หัวข้อ", + "เข้ม", + "หมายเลข", + "ข้อบังคับ", + "แข็ง", + "ขอบเขต", + "ขยะ", + "แข่ง", + "ข่าย", + "ขอรับ", + "เข้มแข็ง", + "ขวบ", + "ตรงกันข้าม", + "ขวด", + "ขบวนการ", + "ข้อสังเกต", + "ไขมัน", + "ขุน", + "คำขอ", + "เลข", + "นักเขียน", + "ข้อเสนอ", + "ขัดแย้ง", + "สิ่งของ", + "ขี่", + "ขบวน", + "ของเสีย", + "ร้องขอ", + "ไข้", + "ขุด", + "ขำ", + "ขาดแคลน", + "เข็ม", + "ข้างใน", + "ขัง", + "ข้างนอก", + "ข้อสรุป", + "ขีด", + "ขวาง", + "กับข้าว", + "ข้อจำกัด", + "เข่า", + "เลขานุการ", + "ค้าขาย", + "ขุนนาง", + "เข้าหา", + "ขจัด", + "ขยัน", + "เขิน", + "เขมร", + "เขื่อน", + "ขอร้อง", + "เข้มข้น", + "ขู่", + "สุนัข", + "ข้าวของ", + "ขัน", + "ข้อหา", + "ขนาน", + "ข่มขู่", + "น้ำแข็ง", + "ข้อมือ", + "ข้างหลัง", + "เลขที่", + "ข้อเรียกร้อง", + "ข้อกำหนด", + "ข่มขืน", + "แขวน", + "ข้างล่าง", + "ขืน", + "แข็งแกร่ง", + "คนขับ", + "ข้อเสนอแนะ", + "ชี้ขาด", + "เข้มงวด", + "ข้อดี", + "ขนมปัง", + "ขัดขวาง", + "ข้างบน", + "คู่แข่ง", + "ขมวด", + "กดขี่", + "ขอน", + "ทางเข้า", + "ขาดทุน", + "ไข", + "ขี้เกียจ", + "ขั้ว", + "สงขลา", + "ขอบใจ", + "ขาเข้า", + "ใบขนสินค้า", + "ขาน", + "ขว้าง", + "ของเล่น", + "นักข่าว", + "เข็น", + "ขับเคลื่อน", + "ของกลาง", + "ของเก่า", + "แขนง", + "เข้าข้าง", + "ข้าหลวง", + "ขุ่น", + "ข้างเคียง", + "สังขาร", + "เลขา", + "ข้อเสีย", + "เทือกเขา", + "ข้าวสาร", + "ข้าวต้ม", + "ขะ", + "รับแขก", + "ของดี", + "ข้อยกเว้น", + "ขอตัว", + "ขาดดุล", + "เป็นอันขาด", + "คนขาย", + "ข้อสงสัย", + "ประมุข", + "มุข", + "ข้างมาก", + "ขั้นต่ำ", + "ข้อคิด", + "ข้อสอบ", + "กักขัง", + "เขย่า", + "ข่ม", + "คำร้องขอ", + "มือขวา", + "ข้าวเหนียว", + "ขาออก", + "ขับขี่", + "ขัดใจ", + ], + }, + { + name: "34. ถ", + words: [ + "ถ้า", + "ถูก", + "สามารถ", + "ถาม", + "รถ", + "ถือ", + "สถาบัน", + "ถือว่า", + "ถนน", + "คำถาม", + "สถานที่", + "ความสามารถ", + "ถูกต้อง", + "สถาน", + "ท้องถิ่น", + "เถอะ", + "แถว", + "แถม", + "วัตถุ", + "ถอน", + "สถานี", + "ถิ่น", + "ขับรถ", + "ถ่าย", + "ถวาย", + "ถุง", + "นับถือ", + "ปรารถนา", + "ถ้าหาก", + "ถ่ายทอด", + "ถัด", + "แถบ", + "ถัง", + "ถ้อยคำ", + "สถิติ", + "ถอด", + "ถ้วย", + "สถานะ", + "สอบถาม", + "มิถุนายน", + "วิถี", + "ถอย", + "ถาวร", + "ถนัด", + "วิถีชีวิต", + "ถ่ายรูป", + "เชื่อถือ", + "ความถี่", + "ครบถ้วน", + "สถาปัตยกรรม", + "วัตถุดิบ", + "แถลง", + "เถียง", + "ถ้ำ", + "ถูกใจ", + "ความถูกต้อง", + "เถิด", + "ส่วนท้องถิ่น", + "ถ่านหิน", + "รูปถ่าย", + "ถั่ว", + "นาถ", + "ดูถูก", + "ถกเถียง", + "ถล่ม", + "สถาปนา", + "ถู", + "ถ้วน", + "ถนอม", + "ประถม", + "แบบสอบถาม", + "แถลงข่าว", + "วิถีทาง", + "คนไร้ความสามารถ", + "สถาปนิก", + "ถาด", + "ถ่ายทำ", + "คาถา", + "ถีบ", + "ถัก", + "ถอยหลัง", + ], + }, + { + name: "35. โ (สระโอ)", + words: [ + "โดย", + "โลก", + "โอกาส", + "โครงการ", + "โรค", + "โรงเรียน", + "นโยบาย", + "โต", + "โอ", + "โครงสร้าง", + "โดน", + "โน้ต", + "เทคโนโลยี", + "ชั่วโมง", + "โรง", + "ประโยค", + "เติบโต", + "โดยที่", + "โรงแรม", + "โรงงาน", + "โดยตรง", + "ยุโรป", + "โร", + "โอน", + "โด", + "โค", + "โดยทั่วไป", + "โปรด", + "โม", + "โครง", + "โน", + "แนวโน้ม", + "โท", + "โทร", + "เชื่อมโยง", + "โชคดี", + "โล", + "โก", + "โย", + "ตะโกน", + "โปรแกรม", + "โมง", + "โจมตี", + "กระโดด", + "ตอบโต้", + "ขโมย", + "กิโลเมตร", + "โค้ง", + "โปร", + "สุโขทัย", + "อ่อนโยน", + "โง่", + "โน่น", + "โยน", + "โก้", + "โชค", + "โล่ง", + "โน้น", + "โบ", + "โจร", + "สงครามโลก", + "โรม", + "โกหก", + "อาวุโส", + "โจ", + "โปรดเกล้า", + "โดดเด่น", + "โมโห", + "โลหะ", + "โลหิต", + "โดยสาร", + "อนุโลม", + "โป", + "โอ้", + "โรมัน", + "โกะ", + "โหน่ง", + "กระโปรง", + "โต้", + "โปรตีน", + "สโมสร", + "อัตโนมัติ", + "กลาโหม", + "โชติ", + "โช", + "โยง", + "โว้ย", + "โวยวาย", + "ปิโตรเลียม", + "กิโลกรัม", + "โน้ม", + "โอบ", + "ลีโอ", + "โส", + "โห", + "โต้แย้ง", + "โคลน", + "มโน", + "ราชโองการ", + "โขน", + "โกง", + "โปร่งใส", + "โปร่ง", + "โทน", + "โดยสิ้นเชิง", + "โคน", + "โบกมือ", + "โต้ตอบ", + "โยกย้าย", + "โคจร", + "โสด", + "โตเกียว", + "โนะ", + "โขง", + "โน้มน้าว", + "เชื้อโรค", + "โคม", + "โรแมนติก", + "โปรล็อก", + "โดด", + "โบก", + "โดดเดี่ยว", + "กิโล", + "โด่งดัง", + "โถง", + "โหด", + "โรย", + "เปียโน", + "โค่น", + "เสื่อมโทรม", + "วิดีโอ", + "โคลง", + "โหล", + "โหดร้าย", + "โร่", + "โหย", + ], + }, + { + name: "36. ษ", + words: [ + "รักษา", + "บริษัท", + "เกษตร", + "โทษ", + "เกษตรกร", + "ลงโทษ", + "กระดาษ", + "อักษร", + "ขอโทษ", + "เมษายน", + "ทักษะ", + "ตัวอักษร", + "เกษตรกรรม", + "ราชกิจจานุเบกษา", + "เก็บรักษา", + "บุรุษ", + "รักษาการ", + "นักโทษ", + "ษา", + "เกษม", + "มานุษยวิทยา", + "เกษ", + "มนุษยชาติ", + ], + }, + { + name: "37. ภ", + words: [ + "ภาษา", + "ภายใน", + "ประเภท", + "ภาค", + "สภา", + "ภาษี", + "ภาวะ", + "ภายใต้", + "ปลอดภัย", + "ภายนอก", + "ภัย", + "ภรรยา", + "ภายหลัง", + "อำเภอ", + "บริโภค", + "ภูมิภาค", + "ภาระ", + "ภูมิ", + "ภาคใต้", + "สภาวะ", + "อภิปราย", + "ภารกิจ", + "ภูเขา", + "ภูมิใจ", + "ภาคเหนือ", + "ประกันภัย", + "ภู", + "ภา", + "ภริยา", + "ภาคกลาง", + "อภัย", + "เสมอภาค", + "ภาคี", + "ภิ", + "ภาคตะวันออก", + "ภาคภูมิใจ", + "ภาชนะ", + "ภูเก็ต", + "จงรักภักดี", + "ภาวนา", + "ภาคอีสาน", + "อุทกภัย", + "ภิกษุ", + "ภาษีเงินได้", + "ภัทร", + "ภวา", + "ลาภ", + "นายอำเภอ", + "โลภ", + "ภักดี", + "ภูมิลำเนา", + "ประภาส", + "ภู่", + "โภชนาการ", + "ภาควิชา", + "ให้อภัย", + "ส่วนภูมิภาค", + ], + }, + { + name: "38. พ", + words: [ + "เพื่อ", + "เพราะ", + "พระ", + "พูด", + "พอ", + "พบ", + "พี่", + "เพียง", + "พวก", + "เพื่อน", + "ภาพ", + "เพิ่ม", + "พ่อ", + "พร้อม", + "พื้นที่", + "พยายาม", + "พา", + "สภาพ", + "พรรค", + "เพลง", + "พัก", + "กรุงเทพ", + "เพิ่ง", + "อาชีพ", + "ข้าพเจ้า", + "พวกเขา", + "พัน", + "พลัง", + "พื้น", + "พ่อแม่", + "พร้อมกับ", + "สุขภาพ", + "พระเจ้า", + "พ้น", + "พลังงาน", + "พอใจ", + "พวกเรา", + "เพชร", + "โรงพยาบาล", + "พืช", + "เพิ่มเติม", + "วิชาชีพ", + "เพียงแต่", + "พนักงาน", + "พยาบาล", + "พลาง", + "คำพูด", + "พอดี", + "พี", + "พลอย", + "พล", + "เทพ", + "พิษ", + "เพียงพอ", + "แพง", + "แพ้", + "ทรัพยากร", + "พี่น้อง", + "พร", + "เสรีภาพ", + "คำพิพากษา", + "เพราะว่า", + "เคารพ", + "พยัก", + "พยาน", + "พม่า", + "พี่ชาย", + "พระยา", + "พูดคุย", + "กองทัพ", + "พรรคการเมือง", + "จอมพล", + "พระบรม", + "เพียงใด", + "ค้นพบ", + "พรุ่งนี้", + "พิพากษา", + "ความพยายาม", + "ยาเสพติด", + "พุ่ง", + "พระราชทาน", + "พอสมควร", + "เพียงแค่", + "กำแพง", + "พร้อมทั้ง", + "สะพาน", + "พลิก", + "อพยพ", + "พัด", + "พิ", + "พนัน", + "เชื้อเพลิง", + "มลพิษ", + "สถานภาพ", + "พิการ", + "พนักงานเจ้าหน้าที่", + "พระบาทสมเด็จ", + "พ่อค้า", + "ชมพู", + "พลาด", + "ที่พัก", + "สภาพแวดล้อม", + "ร้องเพลง", + "พระบาทสมเด็จพระเจ้าอยู่หัว", + "เพื่อนบ้าน", + "พระเอก", + "พะเยา", + "แพร่หลาย", + "เจ้าพนักงาน", + "นพ", + "พระนาง", + "พลาสติก", + "พูดจา", + "เจ้าพระยา", + "พอเพียง", + "พลเรือน", + "สมุนไพร", + "ทัพ", + "พริก", + "กัมพูชา", + "เสพ", + "พื้นบ้าน", + "เพิกถอน", + "แพร่", + "ภพ", + "ความพร้อม", + "สุภาพ", + "เสถียรภาพ", + "พลเมือง", + "พนักงานสอบสวน", + "บุคลิกภาพ", + "พระทัย", + "บกพร่อง", + "เพ", + "ชีพ", + "พื้นเมือง", + "พระราชวัง", + "พัสดุ", + "เพลิง", + "บรรพ", + "พัง", + "บรรพบุรุษ", + "ภาพรวม", + "ดุลยภาพ", + "ลำพัง", + "เพลิน", + "ภาพถ่าย", + "พับ", + "ข้อพิพาท", + "พระราชา", + "บ้านพัก", + "เพาะปลูก", + "สารพัด", + "พายุ", + "จักรพรรดิ", + "เพาะ", + "เจ้าภาพ", + "พาย", + "สมรรถภาพ", + "จำพวก", + "พระเกียรติ", + "กายภาพ", + "พอล", + "พนม", + "พิกัด", + "พก", + "สารภาพ", + "พื้นดิน", + "มะพร้าว", + "พรหม", + "พินิจ", + "หัวหน้าพรรค", + "พรม", + "พารา", + "ถ่ายภาพ", + "กรรมาชีพ", + "อิสรภาพ", + "แพ", + "พบปะ", + "พ่น", + "พ่อขุน", + "แพร", + "ไม่แพ้", + "ไพเราะ", + "พันล้าน", + "สรรพ", + "เทพเจ้า", + "พัท", + "กระเพาะ", + "เพียร", + "เพดาน", + "พลเอก", + "ยานพาหนะ", + "พุ่ม", + "สันติภาพ", + "ข้าวโพด", + "โพ", + "ทิพ", + "กรมพระยา", + "พริกไทย", + "พลัน", + "ตามลำพัง", + "พี่เลี้ยง", + "พิม", + "พาด", + "พ่ายแพ้", + "ชีวภาพ", + "ไพร่", + "เพลิดเพลิน", + "พิง", + "สหภาพ", + "เจ้าพ่อ", + "แพน", + "สรรพสิ่ง", + "น้ำพริก", + "พอง", + "เกี่ยวพัน", + "พาน", + "พ้อง", + "บทเพลง", + "หลุดพ้น", + "พวง", + "อำพราง", + "มิตรภาพ", + "พรมแดน", + "พระราชดำริ", + "หอพัก", + "พาหนะ", + "พรรคพวก", + "แพร่ง", + "เอกภาพ", + "บูรพา", + "พรรษา", + "ทอดพระเนตร", + "นายพล", + "เพคะ", + "เพชรบุรี", + "พนักงานอัยการ", + "พราน", + "เตรียมพร้อม", + "ไพ", + "พระภิกษุ", + "นิพพาน", + "พินัยกรรม", + "พันปี", + "ตรวจพบ", + "ห้างสรรพสินค้า", + "พระราชกำหนด", + "คัพ", + "พยุง", + "อวยพร", + "เพี้ยน", + "พระจุลจอมเกล้าเจ้าอยู่หัว", + "พระนางเจ้า", + "โพรโทคอล", + "สมเด็จพระเทพรัตนราชสุดา", + "ยังชีพ", + "ดับเพลิง", + "แพ็ก", + "เพ่ง", + ], + }, + { + name: "39. ผ", + words: [ + "ผู้", + "ผม", + "ผล", + "ผ่าน", + "ผิด", + "ผลิต", + "ผู้ชาย", + "เหตุผล", + "ความผิด", + "ที่ผ่านมา", + "ผู้นำ", + "ผ้า", + "ผิว", + "ส่งผล", + "แผน", + "เปิดเผย", + "ผู้คน", + "ผลงาน", + "ผลกระทบ", + "แผ่นดิน", + "สัมผัส", + "ผู้เขียน", + "เสื้อผ้า", + "แผ่น", + "ผัก", + "ผู้ป่วย", + "ผี", + "ผสม", + "รับผิดชอบ", + "ผูกพัน", + "ผู้แทน", + "แผนที่", + "ผลผลิต", + "วางแผน", + "ผู้นั้น", + "ผู้อำนวยการ", + "ผู้บริหาร", + "เผา", + "ผู้ปกครอง", + "ผู้ใช้", + "เผยแพร่", + "ผู้จัดการ", + "ผู้บริโภค", + "แผล", + "ผลไม้", + "ผูก", + "ผ่าตัด", + "ผู้ผลิต", + "ผืน", + "ผิวหนัง", + "ความรับผิดชอบ", + "ผิดพลาด", + "พักผ่อน", + "เผย", + "แผนการ", + "ผู้ทรง", + "ประเมินผล", + "ผู้สื่อข่าว", + "ผู้ช่วย", + "ผลักดัน", + "เผ่า", + "ผสมผสาน", + "ผนัง", + "ผู้ประกอบการ", + "รับผิด", + "ผู้แต่ง", + "ตัวผู้", + "เผลอ", + "โผล่", + "แผนก", + "แบบแผน", + "ผู้สมัคร", + "ผิดหวัง", + "ไผ่", + "ผู้ต้องหา", + "ผู้ตาย", + "ผู้ชม", + "ผัด", + "แผ่", + "ผู้ว่าราชการ", + "ผัว", + "ผู้โดยสาร", + "ผู้ว่า", + "เผด็จการ", + "เผื่อ", + "ผ่อนคลาย", + "ผู้เสียหาย", + "ผลัก", + "บาดแผล", + "ผ่า", + "แผ่ว", + "ผนวก", + "ผู้ส่งออก", + "ผู้กำกับ", + "แผง", + "ส่วนผสม", + "ผอม", + "ผลเสีย", + "ผลสำเร็จ", + "ผู้ร้าย", + "ผู้แสดง", + "ผุด", + "ผลตอบแทน", + "ผู้ถือหุ้น", + "พื้นผิว", + "แผนงาน", + "ผู้รับผิดชอบ", + "ผัน", + "หน้าผาก", + "ผง", + "ผู้ดี", + "ผูกขาด", + "ผ่อง", + "ผู้ขาย", + "แผ่นดินไหว", + "ผา", + "เส้นผม", + "ผู้ดูแล", + "ผ่อน", + "ผลัด", + "ผู้ค้า", + "ผ่านพ้น", + "ผีเสื้อ", + "ผับ", + "กระผม", + "เผ็ด", + "ผ่อนผัน", + "ผู้สอบ", + "ผันผวน", + "ผักตบชวา", + ], + }, + { + name: "40. ณ", + words: [ + "คุณ", + "กรณี", + "ลักษณะ", + "พิจารณา", + "คณะกรรมการ", + "ประมาณ", + "คณะ", + "บริเวณ", + "ขณะที่", + "คุณภาพ", + "งบประมาณ", + "ในขณะที่", + "ปริมาณ", + "ขณะ", + "โบราณ", + "คุณสมบัติ", + "คุณค่า", + "คุณแม่", + "ขณะนี้", + "ขอบคุณ", + "ประเพณี", + "คำนวณ", + "วรรณคดี", + "คุณหมอ", + "ราชอาณาจักร", + "ทักษิณ", + "ขณะเดียวกัน", + "คู่กรณี", + "มณี", + "อุณหภูมิ", + "วรรณกรรม", + "ในขณะเดียวกัน", + "วรรณ", + "คุณปู่", + "ในขณะนั้น", + "อาณาจักร", + "สุวรรณ", + "ปีงบประมาณ", + "กรุณา", + "ขณะนั้น", + "อาณานิคม", + "ณา", + "ในขณะนี้", + "คุณลักษณะ", + "คุณครู", + "คุณย่า", + "พรรณนา", + "พรรณ", + "คูณ", + "คุณนาย", + "สุพรรณ", + "กรรณ", + "พ่อคุณ", + "พระกรุณา", + "ณี", + "พิษณุโลก", + "บูรณาการ", + "ประณีต", + "ค้าประเวณี", + "อาณาเขต", + "ณุ", + "จรรยาบรรณ", + "ตัณหา", + "คุณตา", + "เจ้าคุณ", + "สุวรรณภูมิ", + "ดรุณี", + "โสเภณี", + "วิษณุ", + "อรุณ", + "รณ", + "รัณ", + ], + }, + { + name: "41. ์ (การันต์)", + words: [ + "มนุษย์", + "ประโยชน์", + "สัตว์", + "สัมภาษณ์", + "แพทย์", + "วิเคราะห์", + "อารมณ์", + "อาจารย์", + "เหตุการณ์", + "พระองค์", + "องค์กร", + "ทรัพย์สิน", + "ทุกข์", + "องค์", + "สถานการณ์", + "พิมพ์", + "ประสบการณ์", + "สมบูรณ์", + "ภาพยนตร์", + "อาทิตย์", + "อุปกรณ์", + "ผลประโยชน์", + "องค์การ", + "วัตถุประสงค์", + "หนังสือพิมพ์", + "พิสูจน์", + "รถยนต์", + "พาณิชย์", + "คอร์ด", + "องค์ประกอบ", + "เตอร์", + "ประสงค์", + "สัปดาห์", + "ทรัพย์", + "พระมหากษัตริย์", + "สร้างสรรค์", + "คอมพิวเตอร์", + "โจทก์", + "สหกรณ์", + "จันทร์", + "กษัตริย์", + "ลักษณ์", + "คอมมิวนิสต์", + "สวรรค์", + "อนุรักษ์", + "เบอร์", + "โชว์", + "วิจารณ์", + "ดอลลาร์", + "ครรภ์", + "ยักษ์", + "อุดมการณ์", + "นนท์", + "สิงคโปร์", + "แชมป์", + "มาร์", + "รัตน์", + "พงษ์", + "คอลัมน์", + "การ์ตูน", + "ดวงอาทิตย์", + "เอกลักษณ์", + "เสน่ห์", + "แบรนด์", + "ตีพิมพ์", + "อาร์", + "สไตล์", + "สิงห์", + "สงค์", + "บาร์", + "เกอร์", + "เสาร์", + "อิเล็กทรอนิกส์", + "คริสต์", + "ทิพย์", + "ประยุกต์", + "พิทักษ์", + "สงเคราะห์", + "ทัวร์", + "ดวงจันทร์", + "เมเจอร์", + "โบสถ์", + "ปอนด์", + "แอร์", + "ไอค์", + "การณ์", + "เดอร์", + "ผู้เยาว์", + "รณรงค์", + "วิพากษ์วิจารณ์", + "ประจักษ์", + "สตางค์", + "สันต์", + "รักษ์", + "วิทย์", + "ภาพลักษณ์", + "อสังหาริมทรัพย์", + "อุตส่าห์", + "วิพากษ์", + "นันท์", + "ไปรษณีย์", + "จอห์น", + "คัมภีร์", + "นัยน์ตา", + "เนื้อสัตว์", + "คอนเสิร์ต", + "อุปถัมภ์", + "จุดประสงค์", + "มาร์ค", + "เวอร์", + "พระองค์เจ้า", + "รถเมล์", + "สวัสดิ์", + "รัตนโกสินทร์", + "เนอร์", + "เครื่องยนต์", + "ออนไลน์", + "หลักทรัพย์", + "เบียร์", + "การ์ด", + "เจดีย์", + "ดาวเคราะห์", + "พระจันทร์", + "พราหมณ์", + "เอดส์", + "สินทรัพย์", + "เกษตรและสหกรณ์", + "แวมไพร์", + "ไวน์", + "เปอร์", + "บริบูรณ์", + "นิวยอร์ก", + "เจตนารมณ์", + "สวดมนต์", + "ไมเนอร์", + "บอร์ด", + "เพอร์", + "ร้องทุกข์", + "แลนด์", + "โจทย์", + "แอนด์", + "พยากรณ์", + "เลอร์", + "สุรินทร์", + "กีตาร์", + "พจน์", + "ออมทรัพย์", + "เกย์", + "คาดการณ์", + "ไวยากรณ์", + "นายแพทย์", + "คาร์", + "อุดมสมบูรณ์", + "เชียร์", + "พักตร์", + "พาร์", + "ภรณ์", + "เจอร์", + "นิวเคลียร์", + "นีย์", + "สภาพการณ์", + "เกมส์", + "อินเทอร์เน็ต", + "แบงก์", + "เคราะห์", + "เยาว์", + "มาร์ก", + "ลายลักษณ์", + "หงส์", + "ภาพพจน์", + "ดาร์", + "ยนต์", + "เดียร์", + "เมล์", + "สมบูรณ์แบบ", + "คอร์", + "แคร์", + "พีร์", + "เทอร์", + "เดย์", + "สังเคราะห์", + "มนต์", + "อินทร์", + "จอร์จ", + "เลย์", + "แสงอาทิตย์", + "กานต์", + "ณรงค์", + ], + }, + { + name: "42. ญ", + words: [ + "ปัญหา", + "ใหญ่", + "สำคัญ", + "หญิง", + "ผู้หญิง", + "ส่วนใหญ่", + "สัญญา", + "อนุญาต", + "ญี่ปุ่น", + "ความสำคัญ", + "ที่สำคัญ", + "พระราชบัญญัติ", + "เจริญ", + "บัญชี", + "ผู้ใหญ่", + "ญาติ", + "บุญ", + "บัญญัติ", + "บทบัญญัติ", + "เชิญ", + "ปัญญา", + "สัญญาณ", + "วิญญาณ", + "อาญา", + "สัญลักษณ์", + "สูญเสีย", + "ยิ่งใหญ่", + "หญ้า", + "เหรียญ", + "เผชิญ", + "ปรัชญา", + "เจ้าหญิง", + "บังเอิญ", + "ขวัญ", + "ทำบุญ", + "นิติบัญญัติ", + "ผู้บังคับบัญชา", + "ผู้เชี่ยวชาญ", + "ญา", + "กุญแจ", + "อาชญากรรม", + "อนุสัญญา", + "ชำนาญ", + "คุณหญิง", + "สามัญ", + "ของขวัญ", + "กุญแจเสียง", + "สติปัญญา", + "ใหญ่โต", + "รำคาญ", + "เด็กหญิง", + "คู่สัญญา", + "ปริญญา", + "เผชิญหน้า", + "ภูมิปัญญา", + "ปริญญาตรี", + "พญา", + "ชาญ", + "บังคับบัญชา", + "เชี่ยวชาญ", + "สูญหาย", + "ปัญญาชน", + "เหรียญทอง", + "สูญ", + "ผู้บัญชาการ", + "ญาณ", + "คดีอาญา", + "ผจญภัย", + "บำเพ็ญ", + "ปริญญาโท", + "กว้างใหญ่", + "วิสามัญ", + "กล้าหาญ", + "สรรเสริญ", + "มอญ", + "บุญคุณ", + "สัญชาตญาณ", + "ข้อสัญญา", + "กตัญญู", + "ผู้ใหญ่บ้าน", + "สัญ", + "สำราญ", + "ท่านหญิง", + "ญัตติ", + "สัญชาติ", + "บำนาญ", + ], + }, + { + name: "43. ึ (สระอึ)", + words: [ + "ขึ้น", + "หนึ่ง", + "ถึง", + "จึง", + "รู้สึก", + "ความรู้สึก", + "หมายถึง", + "นึก", + "จนถึง", + "บันทึก", + "กล่าวถึง", + "ลึก", + "ครึ่ง", + "ยึด", + "ดึง", + "ปรึกษา", + "รึ", + "พูดถึง", + "คิดถึง", + "ขึ้นอยู่กับ", + "คำนึง", + "นึกถึง", + "ตึก", + "พึง", + "ถึงกับ", + "จารึก", + "ถึงแม้", + "ที่ปรึกษา", + "มึง", + "เข้าถึง", + "นึง", + "สำนึก", + "พึ่ง", + "ถึงแม้ว่า", + "ถึงขนาด", + "กึ่ง", + "พึ่งพา", + "อึ้ง", + "คล้ายคลึง", + "อึดอัด", + "คำปรึกษา", + "ดึก", + "จิตสำนึก", + "ยึดถือ", + "อนึ่ง", + "ระลึก", + "หึ", + "ยึดมั่น", + "ดึงดูด", + "พึมพำ", + "นึกคิด", + "สึ", + "นิดหนึ่ง", + "ถึงที่", + "หมึก", + "บึง", + "ตึง", + "ทั่วถึง", + "ที่ระลึก", + "ลึกลับ", + "อึน", + "พึงพอใจ", + "ครู่หนึ่ง", + "ที่พึ่ง", + "คึกคัก", + "ทึ่ง", + "ตรึง", + "ทึบ", + "ตึงเครียด", + "รำลึก", + "หึง", + ], + }, + { + name: "44. ๆ", + words: ["ๆ", "สบายๆ", "เรื่อยๆ", "หวั่นๆ", "ใดๆ"], + }, + { + name: "45. ศ", + words: [ + "ประเทศ", + "การศึกษา", + "ศึกษา", + "ประกาศ", + "อาศัย", + "พิเศษ", + "ต่างประเทศ", + "ศาล", + "ศรี", + "ศาสนา", + "เพศ", + "อากาศ", + "ศพ", + "ศูนย์", + "ศิลปะ", + "ประวัติศาสตร์", + "โทรศัพท์", + "นักศึกษา", + "วิทยาศาสตร์", + "บรรยากาศ", + "วงศ์", + "ศึก", + "โทรทัศน์", + "ศักดิ์", + "ทิศ", + "ทิศทาง", + "ศิลปิน", + "เศร้า", + "ทศวรรษ", + "ศีรษะ", + "ปราศจาก", + "ศูนย์กลาง", + "เศษ", + "ศตวรรษ", + "ศัตรู", + "ศักยภาพ", + "อุดมศึกษา", + "ศุลกากร", + "ศาสตราจารย์", + "ยศ", + "ศาสตร์", + "ราศี", + "ศัพท์", + "ศิริ", + "ทัศนคติ", + "องศา", + "กุศล", + "ทัศนะ", + "เลิศ", + "ราชวงศ์", + "เทศบาล", + "มหาศาล", + "พงศ์", + "ศิลป์", + "วิเศษ", + "ศีล", + "ทัศน์", + "เทศกาล", + "นิทรรศการ", + "ศาลา", + "ประเทศชาติ", + "สารสนเทศ", + "นักวิทยาศาสตร์", + "อวกาศ", + "ศิษย์", + "ศักดิ์ศรี", + "ลูกศิษย์", + "โทรศัพท์มือถือ", + "ภูมิศาสตร์", + "ศุกร์", + "มหัศจรรย์", + "โศก", + "ชนะเลิศ", + "รัศมี", + "ศิลา", + "ปีศาจ", + "อุทิศ", + "สายอากาศ", + "คณิตศาสตร์", + "ไพศาล", + "ประถมศึกษา", + "นิติศาสตร์", + "ภาษาศาสตร์", + "ที่อยู่อาศัย", + "ศิลป", + "พงศาวดาร", + "กรมศิลปากร", + "สังคมศาสตร์", + "ศุภ", + "เทศ", + "ศูนย์การค้า", + "นเรศวร", + "ไอศกรีม", + "ศร", + "ศักดินา", + "วิศวกร", + "ศศิ", + "ทศ", + "เกียรติยศ", + "วาทศิลป์", + ], + }, + { + name: "46. ซ", + words: [ + "ซึ่ง", + "ซื้อ", + "ซี", + "ซ้ำ", + "ซะ", + "ซา", + "เซ", + "ซ้าย", + "ซิ", + "โซ", + "ซัก", + "เซลล์", + "ซอย", + "อาเซียน", + "ซ่อน", + "ซ้อน", + "ซื้อขาย", + "เปอร์เซ็นต์", + "ซับซ้อน", + "ซู", + "ลึกซึ้ง", + "ไซ", + "รัสเซีย", + "ซอง", + "ซ่อม", + "ซี่", + "มาเลเซีย", + "ซึม", + "ซาน", + "เซอร์", + "แซม", + "กระซิบ", + "เซ็น", + "ซ้อม", + "อินโดนีเซีย", + "ซาก", + "ไซต์", + "เซน", + "ซ่า", + "นีซ", + "แทรกแซง", + "ซิน", + "แท็กซี่", + "ซื่อ", + "เซนติเมตร", + "ซื่อสัตย์", + "ออกซิเจน", + "ซุ", + "ซับ", + "ผู้ซื้อ", + "ซุป", + "ดีไซน์", + "ซัน", + "ซัส", + "เซียน", + "เซ็ง", + "ซึ้ง", + "ซักถาม", + "ซ่อมแซม", + "ซอก", + "ซีก", + "ดีเซล", + "ซัด", + "ลายเซ็น", + "ซวย", + "ซีด", + "ซิ่น", + "ซุ้ม", + "มอเตอร์ไซค์", + "สลับซับซ้อน", + "โซ่", + "เซ็นทรัล", + "ซาบซึ้ง", + "ซอ", + "เซล", + "เซีย", + "โซน", + "บราซิล", + "สั่งซื้อ", + "ดีไซเนอร์", + ], + }, + { + name: "47. ธ", + words: [ + "เธอ", + "สิทธิ", + "วิธี", + "สัมพันธ์", + "วิธีการ", + "ธรรมชาติ", + "ประธาน", + "ธุรกิจ", + "อธิบาย", + "ธรรม", + "ธรรมดา", + "อิทธิพล", + "ประสิทธิภาพ", + "ธนาคาร", + "ประชาธิปไตย", + "พันธุ์", + "โกรธ", + "พุทธ", + "เลขาธิการ", + "พิธี", + "ประธานาธิบดี", + "อาวุธ", + "สมาธิ", + "พุทธศาสนา", + "ยุติธรรม", + "อธิบดี", + "สาธารณะ", + "อยุธยา", + "ศีลธรรม", + "ธิ", + "สิทธิ์", + "พระพุทธเจ้า", + "จริยธรรม", + "ธันวาคม", + "ประชาธิปัตย์", + "สาธารณสุข", + "ลัทธิ", + "บริสุทธิ์", + "ธาตุ", + "พระพุทธศาสนา", + "มูลนิธิ", + "ศรัทธา", + "กุมภาพันธ์", + "คำอธิบาย", + "คุณธรรม", + "คณะกรรมาธิการ", + "ชอบธรรม", + "อภิสิทธิ์", + "ประพันธ์", + "ธี", + "ศึกษาธิการ", + "ธานี", + "ประชาสัมพันธ์", + "ศักดิ์สิทธิ์", + "ยุทธศาสตร์", + "ธุระ", + "ทรัพยากรธรรมชาติ", + "รูปธรรม", + "อุทธรณ์", + "พันธ์", + "พิธีกรรม", + "กรรมาธิการ", + "ธรรมะ", + "ค่าธรรมเนียม", + "ความยุติธรรม", + "ธารณ", + "สุทธิ", + "ธน", + "ธง", + "อารยธรรม", + "นักธุรกิจ", + "วิธีคิด", + "ธรรมเนียม", + "ยุทธ", + "สิทธิมนุษยชน", + "มัธยม", + "โธ่", + "ประสิทธิ์", + "พันธมิตร", + "ธรรมศาสตร์", + "ผลลัพธ์", + "กลยุทธ์", + "สนธิสัญญา", + "นามธรรม", + "สิทธิบัตร", + "ธา", + "สัมพันธภาพ", + "นิพนธ์", + "ชาติพันธุ์", + "อธิการบดี", + "กรรมสิทธิ์", + "ธิดา", + "พันธุกรรม", + "มัธยมศึกษา", + "พุทธศักราช", + "กลวิธี", + "ธูป", + "ธนาคารพาณิชย์", + "โพธิสัตว์", + "ธนู", + "พุธ", + "พระพุทธรูป", + "บรรณาธิการ", + "ธร", + "สมบูรณาญาสิทธิราชย์", + "พิธีการ", + "พยาธิ", + "นราธิวาส", + "พระราชพิธี", + "โพธิ์", + "บุตรบุญธรรม", + "ราชธานี", + "ธีร", + "อธิปไตย", + "สาธิต", + "ขันธ์", + "ลิขสิทธิ์", + "อัธยาศัย", + "ธรรมราช", + "พันธบัตร", + "พระราชนิพนธ์", + "พระพุทธองค์", + ], + }, + { + name: "48. ฝ", + words: [ + "ฝ่าย", + "ฝน", + "ฝัน", + "ฝึก", + "ฝรั่ง", + "ฝรั่งเศส", + "ฝาก", + "ฝีมือ", + "ฝั่ง", + "เฝ้า", + "ฝึกอบรม", + "ฝัง", + "ฝูง", + "ริมฝีปาก", + "แฝง", + "ชายฝั่ง", + "ฝ่ายค้าน", + "ฝา", + "มูลฝอย", + "ฝุ่น", + "ฝ้าย", + "ฝ่าฝืน", + "ฝ่า", + "ฝึกฝน", + "เพื่อนฝูง", + "ฝืน", + "ปลูกฝัง", + "ฝ่ามือ", + "ฝ้า", + "ฝึกหัด", + "เงินฝาก", + "เข้าเฝ้า", + "ฝัก", + ], + }, + { + name: "49. ฮ", + words: [ + "ฮะ", + "เฮ", + "ฮา", + "เฮ้ย", + "เฮ้อ", + "ฮอร์โมน", + "ฮ่องกง", + "ฮ่า", + "ฮิ", + "โฮ", + "ไฮ", + "เฮีย", + "แฮะ", + "ฮ่อง", + "ฮัน", + "ฮือ", + "ฮวง", + "โฮสต์", + "เฮา", + "แอลกอฮอล์", + "เฮือก", + "ฮิต", + ], + }, + { + name: "50. ฏ", + words: [ + "ปฏิบัติ", + "ปรากฏ", + "ปฏิเสธ", + "ปฏิบัติงาน", + "ปฏิบัติการ", + "ปฏิรูป", + "ปรากฏการณ์", + "ปฏิวัติ", + "ปฏิกิริยา", + "กฏ", + "ปฏิสัมพันธ์", + "กบฏ", + "ปฏิบัติธรรม", + ], + }, + { + name: "51. ฐ", + words: [ + "รัฐ", + "รัฐบาล", + "ฐานะ", + "สหรัฐ", + "มาตรฐาน", + "หลักฐาน", + "นายกรัฐมนตรี", + "รัฐธรรมนูญ", + "พื้นฐาน", + "ฐาน", + "รัฐมนตรี", + "สหรัฐอเมริกา", + "คณะรัฐมนตรี", + "รัฐสภา", + "เศรษฐศาสตร์", + "รัฐมนตรีว่าการ", + "รัฐวิสาหกิจ", + "ประดิษฐ์", + "รัฐศาสตร์", + "เศรษฐี", + "รากฐาน", + "ประเสริฐ", + "ขั้นพื้นฐาน", + "สันนิษฐาน", + "รองนายกรัฐมนตรี", + "สมมติฐาน", + "สาธารณรัฐ", + "ปฐม", + "อิฐ", + "ถิ่นฐาน", + "บรรทัดฐาน", + "กรรมฐาน", + "ณัฐ", + "ฐานทัพ", + ], + }, + { + name: "52. ฤ", + words: [ + "อังกฤษ", + "พฤติกรรม", + "คึกฤทธิ์", + "ฤทธิ์", + "ฤดู", + "ประพฤติ", + "พฤษภาคม", + "พฤศจิกายน", + "วิกฤติ", + "วิกฤต", + "ฤดูกาล", + "สฤษดิ์", + "พฤติการณ์", + "สันสกฤต", + "พฤหัสบดี", + "ดาวฤกษ์", + "วิกฤตการณ์", + "ฤา", + "กฤษณา", + ], + }, + { + name: "53 ฒ", + words: [ + "พัฒนา", + "วัฒนธรรม", + "คุณวุฒิ", + "วุฒิ", + "วุฒิสภา", + "วัฒน์", + "ผู้เฒ่า", + "วิวัฒนาการ", + "วัฒนา", + "เฒ่า", + "พัฒน์", + ], + }, + { + name: "54. ฎ", + words: [ + "กฎหมาย", + "ทฤษฎี", + "กฎ", + "ราษฎร", + "กฎหมายแพ่ง", + "ศาลฎีกา", + "กรกฎาคม", + "กฎกระทรวง", + "สภาผู้แทนราษฎร", + "ประมวลกฎหมาย", + "พระราชกฤษฎีกา", + "มงกุฎ", + "ฎีกา", + "ปฎิ", + "นักกฎหมาย", + ], + }, + { + name: "55. ฆ", + words: [ + "ฆ่า", + "โฆษณา", + "โมฆะ", + "เมฆ", + "โฆษก", + "พระสงฆ์", + "โมฆียะ", + "ฆ่าตัวตาย", + "สงฆ์", + "ระฆัง", + "ฆาตกรรม", + ], + }, + { + name: "56. ฬ", + words: ["กีฬา", "นาฬิกา", "นักกีฬา", "จุฬาลงกรณ์", "จุฬา"], + }, + { + name: "57. ฑ", + words: ["หลักเกณฑ์", "ผลิตภัณฑ์", "เกณฑ์", "กฎเกณฑ์", "พิพิธภัณฑ์", "บัณฑิต", "มณฑล", "ปริมณฑล"], + }, + { + name: "58. ฯ", + words: ["นายกฯ", "ข้าฯ", "ฯพณฯ", "กรุงเทพฯ", "โปรดเกล้าฯ", "ฯลฯ"], + }, + { + name: "59. ฌ", + words: ["ฌาน", "ฌาปณกิจ", "เฌอปราง", "เฌอเอม"], + }, + { + name: "Thai short", + words: [ + "กก", + "กฎ", + "กฏ", + "กด", + "กร", + "กรณี", + "กรม", + "กรรม", + "กรอง", + "กรอบ", + "กระ", + "กระจก", + "กระทบ", + "กระทำ", + "กระแส", + "กราบ", + "กริยา", + "กรี๊ด", + "กรุง", + "กรุณา", + "กลม", + "กลับ", + "กลัว", + "กลาง", + "กลาย", + "กลิ่น", + "กลืน", + "กลุ่ม", + "กลไก", + "กล่อง", + "กล่าว", + "กล้อง", + "กล้า", + "กวน", + "กวาด", + "กวี", + "กว่า", + "กว้าง", + "กอง", + "กอด", + "กอส", + "กะ", + "กังวล", + "กัด", + "กัน", + "กับ", + "กั้น", + "กา", + "กาย", + "การ", + "การณ์", + "กาล", + "กาแฟ", + "กำ", + "กำกับ", + "กำจัด", + "กำลัง", + "กำหนด", + "กำแพง", + "กำไร", + "กิ", + "กิจ", + "กิน", + "กิเลส", + "กิ่ง", + "กี", + "กีฬา", + "กี่", + "กี้", + "กึ่ง", + "กุ", + "กุญแจ", + "กุม", + "กุล", + "กุศล", + "กุ้ง", + "กู", + "กู้", + "ก็", + "ก็คือ", + "ก็ดี", + "ก็ตาม", + "ก็ได้", + "ก่อ", + "ก่อน", + "ก้น", + "ก้ม", + "ก้อง", + "ก้อน", + "ก้าว", + "ก๊าซ", + "ขจัด", + "ขณะ", + "ขน", + "ขนม", + "ขนส่ง", + "ขนาด", + "ขนาน", + "ขบวน", + "ขยะ", + "ขยัน", + "ขยับ", + "ขยาย", + "ขวด", + "ขวบ", + "ขวัญ", + "ขวา", + "ขวาง", + "ขอ", + "ของ", + "ขอบ", + "ขอรับ", + "ขอโทษ", + "ขัง", + "ขัด", + "ขัน", + "ขับ", + "ขับรถ", + "ขั้น", + "ขา", + "ขาด", + "ขาย", + "ขาว", + "ขำ", + "ขีด", + "ขี่", + "ขี้", + "ขึ้น", + "ขุด", + "ขุน", + "ขู่", + "ขโมย", + "ข่าย", + "ข่าว", + "ข้อ", + "ข้อหา", + "ข้า", + "ข้าง", + "ข้าม", + "ข้าว", + "คง", + "คงที่", + "คณะ", + "คดี", + "คติ", + "คน", + "คนงาน", + "คนจน", + "คนรัก", + "คนเรา", + "คนใน", + "คนไข้", + "คบ", + "คม", + "ครบ", + "ครรภ์", + "ครอง", + "ครับ", + "ครัว", + "ครั้ง", + "ครั้น", + "คราว", + "ครีม", + "ครึ่ง", + "ครู", + "ครู่", + "คลอง", + "คลอด", + "คลัง", + "คลาย", + "คลื่น", + "คลุม", + "คล้าย", + "ควร", + "ควัน", + "ความ", + "ควาย", + "คว้า", + "คอ", + "คอน", + "คอย", + "คอร์ด", + "คอล", + "คะ", + "คะแนน", + "คัด", + "คัน", + "คา", + "คาด", + "คำ", + "คำขอ", + "คำตอบ", + "คำถาม", + "คำนวณ", + "คำนึง", + "คำพูด", + "คิ", + "คิด", + "คิว", + "คิ้ว", + "คืน", + "คือ", + "คุณ", + "คุม", + "คุย", + "คุรุ", + "คุ้น", + "คุ้ม", + "คู่", + "ค่อย", + "ค่ะ", + "ค่า", + "ค่าย", + "ค่ำ", + "ค้น", + "ค้นพบ", + "ค้นหา", + "ค้า", + "ค้าง", + "ฆ่า", + "งง", + "งดงาม", + "งบ", + "งั้น", + "งา", + "งาน", + "งาม", + "งู", + "ง่าย", + "จง", + "จงใจ", + "จด", + "จน", + "จนถึง", + "จบ", + "จม", + "จมูก", + "จราจร", + "จริง", + "จอ", + "จอง", + "จอด", + "จอม", + "จอมพล", + "จะ", + "จัก", + "จักร", + "จัง", + "จัด", + "จัดทำ", + "จัดหา", + "จัน", + "จับ", + "จา", + "จาก", + "จาง", + "จาน", + "จารึก", + "จำ", + "จำกัด", + "จำคุก", + "จำนวน", + "จำเลย", + "จำแนก", + "จิ", + "จิต", + "จิตใจ", + "จิน", + "จี", + "จีน", + "จี้", + "จึง", + "จุด", + "จู", + "จูงใจ", + "จูบ", + "จ่าย", + "จ้อง", + "จ้ะ", + "จ้าง", + "จ๊ะ", + "ฉบับ", + "ฉลอง", + "ฉลาด", + "ฉัน", + "ฉาก", + "ฉาย", + "ฉีด", + "ชน", + "ชนบท", + "ชนะ", + "ชนิด", + "ชม", + "ชมพู", + "ชมรม", + "ชรา", + "ชล", + "ชวน", + "ชอบ", + "ชะงัก", + "ชะลอ", + "ชัก", + "ชัด", + "ชัน", + "ชัย", + "ชั่น", + "ชั่ว", + "ชั้น", + "ชา", + "ชาดก", + "ชาติ", + "ชาม", + "ชาย", + "ชาว", + "ชาวนา", + "ชำนาญ", + "ชำระ", + "ชิ", + "ชิง", + "ชิด", + "ชิน", + "ชิม", + "ชิ้น", + "ชี", + "ชีพ", + "ชีวิต", + "ชี้", + "ชื่น", + "ชื่อ", + "ชุด", + "ชุมชน", + "ชู", + "ช่วง", + "ช่วย", + "ช่อง", + "ช่าง", + "ช้อน", + "ช้า", + "ช้าง", + "ซอง", + "ซอย", + "ซะ", + "ซัก", + "ซา", + "ซาก", + "ซาน", + "ซิ", + "ซี", + "ซี่", + "ซึม", + "ซึ่ง", + "ซื้อ", + "ซู", + "ซ่อน", + "ซ่อม", + "ซ่า", + "ซ้อน", + "ซ้อม", + "ซ้าย", + "ซ้ำ", + "ญา", + "ญาติ", + "ฐาน", + "ฐานะ", + "ณา", + "ดนตรี", + "ดวง", + "ดวงตา", + "ดอก", + "ดอน", + "ดัง", + "ดัชนี", + "ดัน", + "ดับ", + "ดา", + "ดาบ", + "ดารา", + "ดาว", + "ดำ", + "ดำรง", + "ดิ", + "ดิฉัน", + "ดิน", + "ดิบ", + "ดี", + "ดีงาม", + "ดีใจ", + "ดี้", + "ดึก", + "ดึง", + "ดื่ม", + "ดุ", + "ดู", + "ดูด", + "ดูแล", + "ด่วน", + "ด่า", + "ด่าน", + "ด้วย", + "ด้อย", + "ด้าน", + "ตก", + "ตกต่ำ", + "ตกลง", + "ตกใจ", + "ตน", + "ตนเอง", + "ตบ", + "ตรง", + "ตรวจ", + "ตรัส", + "ตรา", + "ตรี", + "ตลก", + "ตลอด", + "ตลาด", + "ตอน", + "ตอบ", + "ตะ", + "ตะวัน", + "ตะโกน", + "ตัก", + "ตัด", + "ตัน", + "ตับ", + "ตัว", + "ตั้ง", + "ตา", + "ตาก", + "ตาม", + "ตามใจ", + "ตาย", + "ตาราง", + "ตำนาน", + "ตำบล", + "ตำรวจ", + "ตำรา", + "ติ", + "ติด", + "ตี", + "ตี้", + "ตึก", + "ตื่น", + "ตู้", + "ต่อ", + "ต่อมา", + "ต่อไป", + "ต่าง", + "ต่ำ", + "ต้น", + "ต้ม", + "ต้อง", + "ต้า", + "ถนน", + "ถนัด", + "ถวาย", + "ถอด", + "ถอน", + "ถอย", + "ถัง", + "ถัด", + "ถาม", + "ถาวร", + "ถิ่น", + "ถึง", + "ถือ", + "ถุง", + "ถูก", + "ถูกใจ", + "ถ่าย", + "ถ้วย", + "ถ้า", + "ถ้ำ", + "ทดลอง", + "ทดสอบ", + "ทดแทน", + "ทน", + "ทบทวน", + "ทร", + "ทรง", + "ทรมาน", + "ทราบ", + "ทราย", + "ทฤษฎี", + "ทวี", + "ทวีป", + "ทว่า", + "ทหาร", + "ทอ", + "ทอง", + "ทองคำ", + "ทอด", + "ทะ", + "ทะเล", + "ทัก", + "ทักษะ", + "ทัน", + "ทันที", + "ทับ", + "ทัพ", + "ทัวร์", + "ทัศนะ", + "ทัศน์", + "ทั่ว", + "ทั้ง", + "ทา", + "ทาง", + "ทาน", + "ทารก", + "ทาส", + "ทำ", + "ทำการ", + "ทำงาน", + "ทำตัว", + "ทำท่า", + "ทำนอง", + "ทำบุญ", + "ทำลาย", + "ทำเอา", + "ทำให้", + "ทำไม", + "ทิ", + "ทิพย์", + "ทิศ", + "ทิ้ง", + "ที", + "ทีนี้", + "ทีม", + "ทีละ", + "ทีวี", + "ที่", + "ที่มา", + "ทุก", + "ทุกข์", + "ทุกที", + "ทุน", + "ทุ่ง", + "ทุ่ม", + "ทูต", + "ท่อ", + "ท่อน", + "ท่า", + "ท่าที", + "ท่าน", + "ท้อง", + "ท้าย", + "ท้าว", + "ธง", + "ธน", + "ธรรม", + "ธรรมะ", + "ธาตุ", + "ธานี", + "ธารณ", + "ธิ", + "ธี", + "ธุระ", + "นก", + "นคร", + "นนท์", + "นพ", + "นม", + "นวด", + "นวล", + "นอก", + "นอน", + "นะ", + "นัก", + "นัง", + "นัด", + "นับ", + "นัย", + "นั่ง", + "นั่น", + "นั้น", + "นา", + "นาง", + "นาที", + "นาน", + "นานา", + "นาม", + "นาย", + "นายก", + "นำ", + "นิ", + "นิด", + "นิทาน", + "นิยม", + "นิยาม", + "นิสัย", + "นิสิต", + "นิ่ง", + "นิ้ว", + "นี", + "นีซ", + "นี่", + "นี้", + "นึก", + "นึง", + "นุ", + "นุ่ม", + "น่ะ", + "น่า", + "น้อง", + "น้อย", + "น้า", + "น้ำ", + "น้ำตา", + "น้ำใจ", + "บก", + "บท", + "บทบาท", + "บน", + "บรรจุ", + "บรรดา", + "บรรพ", + "บรรลุ", + "บวก", + "บวช", + "บอก", + "บัญชี", + "บัตร", + "บันได", + "บัว", + "บา", + "บาง", + "บางที", + "บาท", + "บาน", + "บาป", + "บารมี", + "บาร์", + "บำบัด", + "บำรุง", + "บิดา", + "บิน", + "บี", + "บีบ", + "บีม", + "บุก", + "บุคคล", + "บุญ", + "บุตร", + "บุรี", + "บู", + "บูชา", + "บ่", + "บ่น", + "บ่อ", + "บ่อย", + "บ่าย", + "บ้า", + "บ้าง", + "บ้าน", + "ปก", + "ปกติ", + "ปน", + "ปรกติ", + "ประ", + "ประจำ", + "ประชา", + "ประตู", + "ประมง", + "ประสบ", + "ปรับ", + "ปรา", + "ปรากฏ", + "ปรุง", + "ปลอม", + "ปลัด", + "ปลา", + "ปลาย", + "ปลุก", + "ปลูก", + "ปล่อย", + "ปวด", + "ปอด", + "ปอนด์", + "ปะ", + "ปัก", + "ปัญญา", + "ปัญหา", + "ปั้น", + "ปา", + "ปาก", + "ปาน", + "ปิด", + "ปี", + "ปีก", + "ปืน", + "ปู", + "ปู่", + "ป่วย", + "ป่า", + "ป้อม", + "ป้า", + "ป้าย", + "ผนัง", + "ผม", + "ผล", + "ผลงาน", + "ผลัก", + "ผลิต", + "ผลไม้", + "ผสม", + "ผัก", + "ผัด", + "ผัว", + "ผิด", + "ผิว", + "ผี", + "ผืน", + "ผูก", + "ผู้", + "ผู้คน", + "ผู้ชม", + "ผู้นำ", + "ผ่าน", + "ผ้า", + "ฝน", + "ฝรั่ง", + "ฝัง", + "ฝัน", + "ฝั่ง", + "ฝา", + "ฝาก", + "ฝีมือ", + "ฝึก", + "ฝุ่น", + "ฝูง", + "ฝ่าย", + "ฝ้าย", + "พงศ์", + "พงษ์", + "พนัน", + "พบ", + "พม่า", + "พยัก", + "พยาน", + "พร", + "พรรค", + "พระ", + "พระยา", + "พริก", + "พร้อม", + "พล", + "พลอย", + "พลัง", + "พลาง", + "พลาด", + "พลิก", + "พวก", + "พอ", + "พอดี", + "พอใจ", + "พะเยา", + "พัก", + "พัฒนา", + "พัด", + "พัน", + "พันธ์", + "พัสดุ", + "พา", + "พิ", + "พิการ", + "พิธี", + "พิมพ์", + "พิษ", + "พิเศษ", + "พี", + "พี่", + "พึง", + "พึ่ง", + "พืช", + "พื้น", + "พุทธ", + "พุ่ง", + "พูด", + "พูดจา", + "พ่อ", + "พ้น", + "ฟรี", + "ฟอง", + "ฟัง", + "ฟัน", + "ฟ้อง", + "ฟ้า", + "ภพ", + "ภรรยา", + "ภริยา", + "ภัย", + "ภา", + "ภาค", + "ภาพ", + "ภายใน", + "ภาระ", + "ภาวะ", + "ภาษา", + "ภาษี", + "ภู", + "ภูมิ", + "ภูเขา", + "มงคล", + "มณฑล", + "มณี", + "มติ", + "มรดก", + "มลพิษ", + "มหา", + "มหาชน", + "มอง", + "มอบ", + "มะ", + "มัก", + "มัง", + "มัด", + "มัน", + "มัว", + "มั่ง", + "มั่น", + "มั้ง", + "มั้ย", + "มา", + "มาก", + "มาตรา", + "มายัง", + "มารดา", + "มาริ", + "มาร์", + "มิ", + "มิตร", + "มิติ", + "มิใช่", + "มิให้", + "มิได้", + "มี", + "มีด", + "มี่", + "มึง", + "มืด", + "มือ", + "มื้อ", + "มุก", + "มุม", + "มุ่ง", + "มูล", + "ม่วง", + "ม้า", + "ยก", + "ยกมือ", + "ยง", + "ยศ", + "ยอด", + "ยอม", + "ยะ", + "ยักษ์", + "ยัง", + "ยังคง", + "ยังไง", + "ยัน", + "ยัย", + "ยา", + "ยาก", + "ยากจน", + "ยาง", + "ยาน", + "ยาม", + "ยาย", + "ยาว", + "ยิง", + "ยินดี", + "ยิ่ง", + "ยิ้ม", + "ยึด", + "ยืด", + "ยืน", + "ยืม", + "ยื่น", + "ยุ", + "ยุค", + "ยุติ", + "ยุบ", + "ยุโรป", + "ยุ่ง", + "ยู", + "ย่อ", + "ย่อม", + "ย่อย", + "ย่า", + "ย่าง", + "ย่าน", + "ย้อน", + "ย้าย", + "ย้ำ", + "รถ", + "รถไฟ", + "รบ", + "รบกวน", + "รวม", + "รวย", + "รส", + "รหัส", + "รอ", + "รอง", + "รอด", + "รอบ", + "รอย", + "ระ", + "ระงับ", + "ระดม", + "ระดับ", + "ระบบ", + "ระบอบ", + "ระบาย", + "ระบุ", + "ระยะ", + "ระวัง", + "ระวาง", + "รัก", + "รักษา", + "รัง", + "รังสี", + "รัฐ", + "รัตน์", + "รับ", + "รั่ว", + "รั้ว", + "รา", + "ราก", + "ราคา", + "ราช", + "ราชา", + "ราม", + "ราย", + "ราว", + "ราศี", + "ราษฎร", + "รำ", + "รำคาญ", + "ริ", + "ริน", + "ริม", + "รี", + "รีบ", + "รี่", + "รึ", + "รุ่ง", + "รุ่น", + "รู", + "รูป", + "รู้", + "ร่ม", + "ร่วง", + "ร่วม", + "ร่า", + "ร่าง", + "ร้อง", + "ร้อน", + "ร้อย", + "ร้าน", + "ร้าย", + "ฤดู", + "ฤทธิ์", + "ลง", + "ลงทุน", + "ลงนาม", + "ลงมือ", + "ลงโทษ", + "ลด", + "ลบ", + "ลม", + "ลอ", + "ลอง", + "ลอย", + "ละ", + "ละคร", + "ลัก", + "ลัทธิ", + "ลับ", + "ลั่น", + "ลา", + "ลาก", + "ลาน", + "ลาย", + "ลาว", + "ลาออก", + "ลำ", + "ลำดับ", + "ลำบาก", + "ลิ", + "ลิง", + "ลิน", + "ลิ้น", + "ลี", + "ลึก", + "ลืม", + "ลุก", + "ลุง", + "ลูก", + "ล่ะ", + "ล่า", + "ล่าง", + "ล้ม", + "ล้วน", + "ล้อ", + "ล้อม", + "ล้าง", + "ล้าน", + "ล้ำ", + "วง", + "วงการ", + "วงจร", + "วงศ์", + "วน", + "วร", + "วรรค", + "วรรณ", + "วะ", + "วัง", + "วัฒน์", + "วัด", + "วัตถุ", + "วัน", + "วัย", + "วัว", + "วัสดุ", + "วา", + "วาง", + "วาจา", + "วาด", + "วาท", + "วาน", + "วาระ", + "วิ", + "วิกฤต", + "วิจัย", + "วิชา", + "วิถี", + "วิทยา", + "วิทยุ", + "วิธี", + "วิน", + "วินัย", + "วิเศษ", + "วิ่ง", + "วี", + "วุฒิ", + "ว่า", + "ว่าง", + "ศพ", + "ศรี", + "ศัตรู", + "ศัพท์", + "ศาล", + "ศาลา", + "ศาสนา", + "ศิริ", + "ศิลปะ", + "ศิลป์", + "ศีรษะ", + "ศีล", + "ศึก", + "ศึกษา", + "ศูนย์", + "สกปรก", + "สกุล", + "สงค์", + "สงบ", + "สงวน", + "สงสัย", + "สงสาร", + "สง่า", + "สด", + "สดใส", + "สตรี", + "สติ", + "สถาน", + "สถานะ", + "สถานี", + "สถิติ", + "สน", + "สนทนา", + "สนอง", + "สนาม", + "สนิท", + "สนุก", + "สนใจ", + "สบ", + "สบาย", + "สภา", + "สภาพ", + "สภาวะ", + "สม", + "สมการ", + "สมควร", + "สมดุล", + "สมมติ", + "สมรส", + "สมอง", + "สมัคร", + "สมัย", + "สมาคม", + "สมาธิ", + "สมุด", + "สมุทร", + "สยาม", + "สระ", + "สรุป", + "สร้อย", + "สร้าง", + "สลับ", + "สลาย", + "สวด", + "สวน", + "สวม", + "สวย", + "สว่าง", + "สหรัฐ", + "สอง", + "สอน", + "สอบ", + "สะ", + "สะดวก", + "สะพาน", + "สะสม", + "สะอาด", + "สัก", + "สังคม", + "สัญญา", + "สัตว์", + "สัน", + "สันติ", + "สับสน", + "สัม", + "สั่ง", + "สั่น", + "สั้น", + "สา", + "สากล", + "สาขา", + "สาน", + "สาม", + "สามัญ", + "สามี", + "สาย", + "สายตา", + "สาร", + "สาระ", + "สาว", + "สำคัญ", + "สำนวน", + "สำนัก", + "สำนึก", + "สำรวจ", + "สำรอง", + "สำเนา", + "สิ", + "สิงห์", + "สิทธิ", + "สิน", + "สิบ", + "สิริ", + "สิ่ง", + "สิ้น", + "สี", + "สีสัน", + "สี่", + "สืบ", + "สื่อ", + "สุ", + "สุก", + "สุข", + "สุด", + "สุทธิ", + "สุนัข", + "สุภาพ", + "สุร", + "สูง", + "สูตร", + "สูบ", + "สู่", + "สู้", + "สเปน", + "สไตล์", + "ส่ง", + "ส่งผล", + "ส่วน", + "ส่อง", + "ส่าย", + "ส้ม", + "หก", + "หญิง", + "หญ้า", + "หน", + "หนทาง", + "หนอ", + "หนอง", + "หนัก", + "หนัง", + "หนา", + "หนาว", + "หนี", + "หนี้", + "หนึ่ง", + "หนุ่ม", + "หนู", + "หน่วย", + "หน่อย", + "หน้า", + "หมด", + "หมวด", + "หมอ", + "หมั้น", + "หมา", + "หมาย", + "หมื่น", + "หมุน", + "หมู", + "หมู่", + "หม่อม", + "หม้อ", + "หยิบ", + "หยุด", + "หรอก", + "หรือ", + "หลง", + "หลบ", + "หลวง", + "หลอก", + "หลัก", + "หลัง", + "หลับ", + "หลาน", + "หลาย", + "หลุด", + "หลุม", + "หล่อ", + "หล่อน", + "หวัง", + "หวาน", + "หอ", + "หอบ", + "หอม", + "หอย", + "หัก", + "หัด", + "หัน", + "หัว", + "หัวใจ", + "หา", + "หาก", + "หาง", + "หาด", + "หาย", + "หายใจ", + "หารือ", + "หิน", + "หิว", + "หุ่น", + "หุ้น", + "หู", + "ห่ม", + "ห่วง", + "ห่อ", + "ห่าง", + "ห้อง", + "ห้า", + "ห้าง", + "ห้าม", + "อก", + "องค์", + "องศา", + "อด", + "อดทน", + "อดีต", + "อนาคต", + "อนุ", + "อบรม", + "อพยพ", + "อภัย", + "อยาก", + "อยู่", + "อย่า", + "อย่าง", + "อร", + "อร่อย", + "อวด", + "ออ", + "ออก", + "ออม", + "อะ", + "อะไร", + "อักษร", + "อัด", + "อัตรา", + "อัน", + "อันดี", + "อัล", + "อา", + "อากร", + "อาการ", + "อากาศ", + "อาคาร", + "อาจ", + "อาชีพ", + "อาญา", + "อาทิ", + "อาย", + "อายุ", + "อาร์", + "อาวุธ", + "อาศัย", + "อาสา", + "อาหาร", + "อำนวย", + "อำนาจ", + "อำเภอ", + "อิ", + "อิจฉา", + "อิน", + "อิสระ", + "อิ่ม", + "อี", + "อีก", + "อีตา", + "อีสาน", + "อึ้ง", + "อืม", + "อื่น", + "อุ", + "อุป", + "อุ่น", + "อุ้ม", + "อ่อน", + "อ่ะ", + "อ่าน", + "อ้วน", + "อ้อ", + "อ้อม", + "อ้า", + "อ้าง", + "อ้าย", + "อ้าว", + "ฮะ", + "ฮา", + "ฯลฯ", + "เก", + "เกณฑ์", + "เกม", + "เกรง", + "เกลือ", + "เกล้า", + "เกษตร", + "เกอร์", + "เกาะ", + "เกิด", + "เกิน", + "เกือบ", + "เก็บ", + "เก่ง", + "เก่า", + "เก้า", + "เขต", + "เขมร", + "เขา", + "เขิน", + "เขียน", + "เขียว", + "เข็ม", + "เข่า", + "เข้ม", + "เข้า", + "เค", + "เคย", + "เครือ", + "เคารพ", + "เคาะ", + "เคียง", + "เค้า", + "เงย", + "เงา", + "เงิน", + "เงียบ", + "เจ", + "เจตนา", + "เจน", + "เจรจา", + "เจริญ", + "เจอ", + "เจาะ", + "เจ็ด", + "เจ็บ", + "เจ้า", + "เจ๊", + "เฉพาะ", + "เฉย", + "เฉลิม", + "เชิง", + "เชิญ", + "เชิด", + "เชียง", + "เชียว", + "เชือก", + "เชื่อ", + "เชื้อ", + "เช็ค", + "เช่น", + "เช่า", + "เช้า", + "เซ", + "เซน", + "เซลล์", + "เซอร์", + "เซ็น", + "เด", + "เดช", + "เดอร์", + "เดอะ", + "เดา", + "เดิน", + "เดิม", + "เดียว", + "เดือน", + "เด็ก", + "เด่น", + "เต", + "เตอร์", + "เติม", + "เตียง", + "เตือน", + "เต็ม", + "เต้", + "เต้น", + "เถอะ", + "เถิด", + "เถียง", + "เท", + "เทพ", + "เทวดา", + "เทา", + "เทียน", + "เทียบ", + "เท่า", + "เท้า", + "เธอ", + "เน", + "เนี่ย", + "เนื้อ", + "เน็ต", + "เน่า", + "เน้น", + "เบอร์", + "เบา", + "เบิก", + "เบื่อ", + "เปน", + "เปล่า", + "เปิด", + "เป็น", + "เป่า", + "เป้า", + "เผชิญ", + "เผย", + "เผลอ", + "เผา", + "เผื่อ", + "เผ่า", + "เฝ้า", + "เพ", + "เพชร", + "เพราะ", + "เพลง", + "เพลิง", + "เพศ", + "เพิ่ง", + "เพิ่ม", + "เพียง", + "เพื่อ", + "เม", + "เมฆ", + "เมตตา", + "เมตร", + "เมล็ด", + "เมา", + "เมีย", + "เมือง", + "เมื่อ", + "เม็ด", + "เยอะ", + "เยือน", + "เย็น", + "เร", + "เรา", + "เริ่ม", + "เรียก", + "เรียง", + "เรียน", + "เรียบ", + "เรียว", + "เรือ", + "เรือน", + "เร็ว", + "เร่ง", + "เล", + "เลข", + "เลย", + "เลว", + "เลิก", + "เลิศ", + "เลีย", + "เลือก", + "เลือด", + "เล็ก", + "เล่น", + "เล่ม", + "เล่า", + "เว", + "เวช", + "เวที", + "เวร", + "เวลา", + "เวียน", + "เว็บ", + "เว้น", + "เศร้า", + "เศษ", + "เสด็จ", + "เสนอ", + "เสพ", + "เสมอ", + "เสริม", + "เสรี", + "เสร็จ", + "เสา", + "เสาร์", + "เสีย", + "เสียง", + "เสือ", + "เสื้อ", + "เส้น", + "เห", + "เหงา", + "เหตุ", + "เหนือ", + "เหมา", + "เหมาะ", + "เหรอ", + "เหลือ", + "เหล็ก", + "เหล่า", + "เหล้า", + "เห็น", + "เอ", + "เอก", + "เอกชน", + "เอง", + "เอฟ", + "เอว", + "เอส", + "เออ", + "เอา", + "เอียง", + "เอื้อ", + "เอ็ง", + "เอ็ด", + "เอ็น", + "เอ็ม", + "เอ่ย", + "เอ่อ", + "เอ๊ย", + "เฮ", + "เฮ้ย", + "เฮ้อ", + "แก", + "แกง", + "แกน", + "แกม", + "แกล้ง", + "แกะ", + "แก่", + "แก่น", + "แก้", + "แก้ม", + "แก้ว", + "แก้ไข", + "แขก", + "แขน", + "แข็ง", + "แข่ง", + "แคน", + "แคบ", + "แค่", + "แค้น", + "แง่", + "แจก", + "แจ้ง", + "แชมป์", + "แซม", + "แดง", + "แดด", + "แดน", + "แตก", + "แตะ", + "แต่", + "แต่ง", + "แต่ละ", + "แถบ", + "แถม", + "แถลง", + "แถว", + "แทง", + "แทน", + "แทบ", + "แทรก", + "แท้", + "แนบ", + "แนว", + "แนะนำ", + "แน่", + "แน่น", + "แน่ใจ", + "แบก", + "แบบ", + "แบ่ง", + "แปด", + "แปร", + "แปล", + "แปลก", + "แปลง", + "แป้ง", + "แผน", + "แผนก", + "แผล", + "แผ่", + "แผ่น", + "แฝง", + "แพง", + "แพทย์", + "แพร่", + "แพ้", + "แฟน", + "แมน", + "แมลง", + "แมว", + "แม่", + "แม้", + "แยก", + "แย่", + "แย่ง", + "แรก", + "แรง", + "แล", + "แลก", + "และ", + "แล่น", + "แล้ว", + "แวว", + "แววตา", + "แวะ", + "แสง", + "แสดง", + "แสน", + "แหง", + "แหม", + "แหลม", + "แหละ", + "แหล่ง", + "แหวน", + "แห่ง", + "แห้ง", + "แอน", + "แอบ", + "แอร์", + "แอล", + "โก", + "โกรธ", + "โกหก", + "โก้", + "โค", + "โครง", + "โค้ง", + "โฆษก", + "โฆษณา", + "โง่", + "โจ", + "โจทก์", + "โจมตี", + "โจร", + "โชค", + "โชคดี", + "โชว์", + "โซ", + "โด", + "โดน", + "โดย", + "โต", + "โต๊ะ", + "โท", + "โทร", + "โทษ", + "โน", + "โน่น", + "โน้ต", + "โน้น", + "โบ", + "โบราณ", + "โบสถ์", + "โปร", + "โปรด", + "โผล่", + "โม", + "โมฆะ", + "โมง", + "โมโห", + "โย", + "โยน", + "โร", + "โรค", + "โรง", + "โรม", + "โล", + "โลก", + "โลหะ", + "โล่ง", + "โอ", + "โอกาส", + "โอน", + "โอ๊ย", + "ใกล้", + "ใคร", + "ใคร่", + "ใจ", + "ใจดี", + "ใช่", + "ใช้", + "ใด", + "ใต้", + "ใน", + "ใบ", + "ใบไม้", + "ใส", + "ใส่", + "ใส่ใจ", + "ใหญ่", + "ใหม่", + "ให้", + "ไกล", + "ไก่", + "ไขมัน", + "ไข่", + "ไข้", + "ไง", + "ไชย", + "ไซ", + "ไซต์", + "ได้", + "ไต", + "ไท", + "ไทย", + "ไป", + "ไผ่", + "ไฟ", + "ไฟฟ้า", + "ไม", + "ไม่", + "ไม้", + "ไร", + "ไร่", + "ไร้", + "ไล่", + "ไว", + "ไว้", + "ไส้", + "ไหน", + "ไหม", + "ไหม้", + "ไหล", + "ไหล่", + "ไหว", + "ไหว้", + "ไอ", + "ไอค์", + "ไอ้", + ], + }, + { + name: "Thai hardcore", + words: [ + "พิษภัย", + "หญ้าฝรั่น", + "หยักศก", + "อาสน์สงฆ์", + "อินทรธนู", + "อิเล็กทรอนิกส์", + "อภิรมย์", + "ศิลปวัฒนธรรม", + "ศิลปศึกษา", + "ศึกษานิเทศก์", + "สมุห์บัญชี", + "สาธารณประโยชน์", + "สารทุกข์สุกดิบ", + "สานุศิษย์", + "สุพรรณบัฏ", + "วิกฤติการณ์", + "วิปลาส", + "วันทยหัตถ์", + "ลดราวาศอก", + "ละมุนละไม", + "ละโมบโลภมาก", + "เล่นพิเรนทร์", + "รื่นรมย์", + "ภาพยนตร์", + "ภูตผีปีศาจ", + "มัคคุเทศก์", + "ปฏิสันถาร", + "ปาฏิหาริย์", + ], + }, +]; diff --git a/apps/web/src/lib/data/manoonchai.ts b/apps/web/src/lib/data/manoonchai.ts new file mode 100644 index 0000000..1e5b238 --- /dev/null +++ b/apps/web/src/lib/data/manoonchai.ts @@ -0,0 +1,36 @@ +export default { + KeyQ: ["ใ", "ฒ"], + KeyW: ["ต", "ฏ"], + KeyE: ["ห", "ซ"], + KeyR: ["ล", "ญ"], + KeyT: ["ส", "ฟ"], + KeyY: ["ป", "ฉ"], + KeyU: ["ั", "ึ"], + KeyI: ["ก", "ธ"], + KeyO: ["ิ", "ฐ"], + KeyP: ["บ", "ฎ"], + BracketLeft: ["็", "ฆ"], + BracketRight: ["ฬ", "ฑ"], + Backslash: ["ฯ", "ฌ"], + KeyA: ["ง", "ษ"], + KeyS: ["เ", "ถ"], + KeyD: ["ร", "แ"], + KeyF: ["น", "ช"], + KeyG: ["ม", "พ"], + KeyH: ["อ", "ผ"], + KeyJ: ["า", "ำ"], + KeyK: ["่", "ข"], + KeyL: ["้", "โ"], + Semicolon: ["ว", "ภ"], + Quote: ["ื", '"'], + KeyZ: ["ุ", "ฤ"], + KeyX: ["ไ", "ฝ"], + KeyC: ["ท", "ๆ"], + KeyV: ["ย", "ณ"], + KeyB: ["จ", "๊"], + KeyN: ["ค", "๋"], + KeyM: ["ี", "์"], + Comma: ["ด", "ศ"], + Period: ["ะ", "ฮ"], + Slash: ["ู", "?"], +}; diff --git a/apps/web/src/lib/engine/drill.test.ts b/apps/web/src/lib/engine/drill.test.ts new file mode 100644 index 0000000..9562e80 --- /dev/null +++ b/apps/web/src/lib/engine/drill.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "vitest"; +import { DRILL_LENGTHS, buildDrill } from "./drill"; + +describe("buildDrill", () => { + it("offers the expected length options", () => { + expect(DRILL_LENGTHS).toEqual([10, 25, 50]); + }); + + it("produces exactly `length` words", () => { + expect(buildDrill(["a", "b", "c"], 10)).toHaveLength(10); + expect(buildDrill(["a", "b", "c"], 25)).toHaveLength(25); + }); + + it("samples from the pool using the injected rng", () => { + // rng always 0 -> always the first pool entry + expect(buildDrill(["a", "b", "c"], 4, () => 0)).toEqual(["a", "a", "a", "a"]); + // rng near 1 -> last entry + expect(buildDrill(["a", "b", "c"], 2, () => 0.99)).toEqual(["c", "c"]); + }); + + it("returns an empty drill for an empty pool", () => { + expect(buildDrill([], 10)).toEqual([]); + }); +}); diff --git a/apps/web/src/lib/engine/drill.ts b/apps/web/src/lib/engine/drill.ts new file mode 100644 index 0000000..606ee76 --- /dev/null +++ b/apps/web/src/lib/engine/drill.ts @@ -0,0 +1,17 @@ +/** Allowed drill lengths (number of sampled words). */ +export const DRILL_LENGTHS = [10, 25, 50] as const; +export type DrillLength = (typeof DRILL_LENGTHS)[number]; + +/** + * Build a drill: `length` words sampled with replacement from `pool`. `rng` is + * injectable so drills are deterministic in tests. Words can repeat, matching + * v1's behaviour and keeping short lessons usable. + */ +export function buildDrill( + pool: string[], + length: number, + rng: () => number = Math.random, +): string[] { + if (pool.length === 0) return []; + return Array.from({ length }, () => pool[Math.floor(rng() * pool.length)] ?? ""); +} diff --git a/apps/web/src/lib/engine/index.ts b/apps/web/src/lib/engine/index.ts new file mode 100644 index 0000000..53bc399 --- /dev/null +++ b/apps/web/src/lib/engine/index.ts @@ -0,0 +1,8 @@ +export * from "./types"; +export * from "./layout"; +export * from "./lessons"; +export * from "./spellcheck"; +export * from "./next-key"; +export * from "./wpm"; +export * from "./drill"; +export { StatsTracker } from "./stats"; diff --git a/apps/web/src/lib/engine/layout.test.ts b/apps/web/src/lib/engine/layout.test.ts new file mode 100644 index 0000000..68f3fa7 --- /dev/null +++ b/apps/web/src/lib/engine/layout.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from "vitest"; +import { LAYOUT_ROWS, isShifted, resolveKey } from "./layout"; + +describe("resolveKey", () => { + it("maps physical codes to base characters", () => { + expect(resolveKey("KeyF", false)).toBe("น"); + expect(resolveKey("KeyG", false)).toBe("ม"); + expect(resolveKey("KeyJ", false)).toBe("า"); + }); + + it("maps shifted codes to the shift layer", () => { + expect(resolveKey("KeyF", true)).toBe("ช"); + expect(resolveKey("KeyK", false)).toBe("่"); + }); + + it("returns empty for keys outside the layout", () => { + expect(resolveKey("Enter", false)).toBe(""); + expect(resolveKey("ArrowLeft", true)).toBe(""); + }); +}); + +describe("LAYOUT_ROWS", () => { + it("has three rows of the expected widths", () => { + expect(LAYOUT_ROWS).toHaveLength(3); + expect(LAYOUT_ROWS.map((r) => r.length)).toEqual([13, 11, 10]); + }); + + it("marks the two index-finger home keys", () => { + const home = LAYOUT_ROWS.flat().filter((k) => k.home); + expect(home.map((k) => k.code).sort()).toEqual(["KeyF", "KeyJ"]); + }); +}); + +describe("isShifted", () => { + it("is true only for characters on the shift layer", () => { + expect(isShifted("ช")).toBe(true); // shift of KeyF + expect(isShifted("น")).toBe(false); // base of KeyF + }); + + it("is false for the empty string and unknown characters", () => { + expect(isShifted("")).toBe(false); + expect(isShifted("฿")).toBe(false); + }); +}); diff --git a/apps/web/src/lib/engine/layout.ts b/apps/web/src/lib/engine/layout.ts new file mode 100644 index 0000000..93faa1c --- /dev/null +++ b/apps/web/src/lib/engine/layout.ts @@ -0,0 +1,63 @@ +import manoonchai from "../data/manoonchai"; + +/** Physical key, base (unshifted) char, shifted char, finger-home marker. */ +export interface LayoutKey { + code: string; + base: string; + shift: string; + /** True for the two index-finger "home" keys (the F/J of this layout). */ + home: boolean; +} + +/** The three physical letter rows, left-to-right, as they sit on a keyboard. */ +const ROW_CODES: string[][] = [ + [ + "KeyQ", + "KeyW", + "KeyE", + "KeyR", + "KeyT", + "KeyY", + "KeyU", + "KeyI", + "KeyO", + "KeyP", + "BracketLeft", + "BracketRight", + "Backslash", + ], + ["KeyA", "KeyS", "KeyD", "KeyF", "KeyG", "KeyH", "KeyJ", "KeyK", "KeyL", "Semicolon", "Quote"], + ["KeyZ", "KeyX", "KeyC", "KeyV", "KeyB", "KeyN", "KeyM", "Comma", "Period", "Slash"], +]; + +/** Index-finger home keys — the ones that carry the tactile bump. */ +const HOME_CODES = new Set(["KeyF", "KeyJ"]); + +export const LAYOUT_ROWS: LayoutKey[][] = ROW_CODES.map((row) => + row.map((code) => { + const pair = (manoonchai as Record)[code] ?? ["", ""]; + return { code, base: pair[0], shift: pair[1], home: HOME_CODES.has(code) }; + }), +); + +/** + * Resolve a physical key press to its Manoonchai character. Returns "" for keys + * outside the layout (so the OS keyboard layout never matters). + */ +export function resolveKey(code: string, shift: boolean): string { + const pair = (manoonchai as Record)[code]; + if (!pair) return ""; + return pair[shift ? 1 : 0] ?? ""; +} + +/** True if `char` is reachable only via Shift on this layout. */ +export function isShifted(char: string): boolean { + if (!char) return false; + for (const row of LAYOUT_ROWS) { + for (const key of row) { + if (key.base === char) return false; + if (key.shift === char) return true; + } + } + return false; +} diff --git a/apps/web/src/lib/engine/lessons.ts b/apps/web/src/lib/engine/lessons.ts new file mode 100644 index 0000000..e77e82d --- /dev/null +++ b/apps/web/src/lib/engine/lessons.ts @@ -0,0 +1,9 @@ +import { lessons as raw } from "../data/lessons"; +import type { Lesson } from "./types"; + +export const lessons: Lesson[] = raw; + +/** Look up a lesson by name, falling back to the first lesson. */ +export function lessonByName(name: string | undefined): Lesson { + return lessons.find((l) => l.name === name) ?? lessons[0]; +} diff --git a/apps/web/src/lib/engine/next-key.test.ts b/apps/web/src/lib/engine/next-key.test.ts new file mode 100644 index 0000000..2bf8a40 --- /dev/null +++ b/apps/web/src/lib/engine/next-key.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; +import { nextKey } from "./next-key"; + +describe("nextKey", () => { + it("points at the first character of an untouched word", () => { + expect(nextKey("นม", "")).toBe("น"); + }); + + it("advances with input", () => { + expect(nextKey("นม", "น")).toBe("ม"); + }); + + it("asks for space once the word is complete", () => { + expect(nextKey("นม", "นม")).toBe(" "); + expect(nextKey("นม", "นมx")).toBe(" "); + }); + + it("returns empty when there is no target", () => { + expect(nextKey("", "x")).toBe(""); + expect(nextKey(undefined, "")).toBe(""); + }); +}); diff --git a/apps/web/src/lib/engine/next-key.ts b/apps/web/src/lib/engine/next-key.ts new file mode 100644 index 0000000..b8e9d0e --- /dev/null +++ b/apps/web/src/lib/engine/next-key.ts @@ -0,0 +1,11 @@ +/** + * The character the learner should type next, given the target word and their + * input so far. Drives the keymap glow and the inline hint. Returns " " when the + * word is complete (press space), and "" when there is no target. + */ +export function nextKey(target: string | undefined, input: string): string { + if (!target) return ""; + if (!input) return target[0] ?? ""; + if (target.length <= input.length) return " "; + return target[input.length] ?? ""; +} diff --git a/apps/web/src/lib/engine/spellcheck.test.ts b/apps/web/src/lib/engine/spellcheck.test.ts new file mode 100644 index 0000000..9a382dc --- /dev/null +++ b/apps/web/src/lib/engine/spellcheck.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; +import { isPrefixCorrect } from "./spellcheck"; + +describe("isPrefixCorrect", () => { + it("accepts a valid prefix", () => { + expect(isPrefixCorrect("นม", "")).toBe(true); + expect(isPrefixCorrect("นม", "น")).toBe(true); + expect(isPrefixCorrect("นม", "นม")).toBe(true); + }); + + it("rejects a wrong character", () => { + expect(isPrefixCorrect("นม", "ม")).toBe(false); + }); + + it("rejects input longer than the target", () => { + expect(isPrefixCorrect("นม", "นมก")).toBe(false); + }); + + it("treats a missing target as correct", () => { + expect(isPrefixCorrect(undefined, "x")).toBe(true); + }); +}); diff --git a/apps/web/src/lib/engine/spellcheck.ts b/apps/web/src/lib/engine/spellcheck.ts new file mode 100644 index 0000000..438e1da --- /dev/null +++ b/apps/web/src/lib/engine/spellcheck.ts @@ -0,0 +1,7 @@ +/** + * True while `input` is still a correct prefix of `target` (or empty). The drill + * uses this to flag a wrong keystroke the moment it diverges, before space. + */ +export function isPrefixCorrect(target: string | undefined, input: string): boolean { + return !target || target.startsWith(input); +} diff --git a/apps/web/src/lib/engine/stats.test.ts b/apps/web/src/lib/engine/stats.test.ts new file mode 100644 index 0000000..a4c2e3b --- /dev/null +++ b/apps/web/src/lib/engine/stats.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from "vitest"; +import { StatsTracker } from "./stats"; + +describe("StatsTracker", () => { + it("reports a clean slate before any keystroke", () => { + const s = new StatsTracker(); + const r = s.build(0); + expect(r.totalChars).toBe(0); + expect(r.accuracy).toBe(100); + expect(r.samples).toEqual([]); + }); + + it("ignores keystrokes recorded before start()", () => { + const s = new StatsTracker(); + s.recordChar(true, 500); + expect(s.build(1000).totalChars).toBe(0); + }); + + it("derives raw/net/accuracy honestly from one second of input", () => { + const s = new StatsTracker(); + s.start(0); + s.recordChar(true, 200); + s.recordChar(true, 400); + s.recordChar(false, 600); + const r = s.build(1000); + + expect(r.totalChars).toBe(3); + expect(r.correctChars).toBe(2); + expect(r.errors).toBe(1); + expect(r.accuracy).toBeCloseTo(66.67, 1); + expect(r.seconds).toBe(1); + expect(r.rawWpm).toBeCloseTo(36, 5); // 3 chars / 5 * 60 / 1s + expect(r.netWpm).toBeCloseTo(24, 5); // 2 correct / 5 * 60 / 1s + + expect(r.samples).toEqual([{ second: 1, raw: 36, net: 24, errors: 1 }]); + }); + + it("buckets samples per second with cumulative net WPM", () => { + const s = new StatsTracker(); + s.start(0); + s.recordChar(true, 500); // second 1 + s.recordChar(true, 1500); // second 2 + const r = s.build(2000); + + expect(r.samples).toEqual([ + { second: 1, raw: 12, net: 12, errors: 0 }, + { second: 2, raw: 12, net: 12, errors: 0 }, + ]); + }); +}); diff --git a/apps/web/src/lib/engine/stats.ts b/apps/web/src/lib/engine/stats.ts new file mode 100644 index 0000000..d0bbb34 --- /dev/null +++ b/apps/web/src/lib/engine/stats.ts @@ -0,0 +1,90 @@ +import type { DrillResult, WpmSample } from "./types"; +import { CHARS_PER_WORD, wpm } from "./wpm"; + +interface CharRecord { + correct: boolean; + /** Milliseconds since the drill started. */ + t: number; +} + +/** + * Records every character keystroke (not spaces) with its timestamp and + * correctness, then derives honest stats from that single source: + * + * - raw WPM — characters typed in each one-second window (chars ÷ 5 × 60), + * - net WPM — cumulative correct characters over elapsed time, + * - accuracy — correct ÷ total characters, + * - errors — incorrect keystrokes, bucketed per second for chart markers. + * + * See docs/adr/0001-honest-stats-engine.md for why this replaces v1's math. + */ +export class StatsTracker { + private startedAt = 0; + private started = false; + private records: CharRecord[] = []; + + start(now: number): void { + if (this.started) return; + this.startedAt = now; + this.started = true; + } + + get isStarted(): boolean { + return this.started; + } + + /** Record one character keystroke. Ignored before `start()`. */ + recordChar(correct: boolean, now: number): void { + if (!this.started) return; + this.records.push({ correct, t: now - this.startedAt }); + } + + /** Per-second samples for the live and end-of-drill chart. */ + samples(now: number): WpmSample[] { + if (!this.records.length) return []; + + const elapsedMs = Math.max(now - this.startedAt, this.records[this.records.length - 1].t); + const totalSeconds = Math.max(1, Math.ceil(elapsedMs / 1000)); + const out: WpmSample[] = []; + + let idx = 0; + let cumulativeCorrect = 0; + for (let second = 1; second <= totalSeconds; second++) { + const windowEnd = second * 1000; + let inWindow = 0; + let errors = 0; + while (idx < this.records.length && this.records[idx].t < windowEnd) { + inWindow++; + if (this.records[idx].correct) cumulativeCorrect++; + else errors++; + idx++; + } + out.push({ + second, + raw: (inWindow * 60) / CHARS_PER_WORD, + net: wpm(cumulativeCorrect, second), + errors, + }); + } + return out; + } + + /** Final headline numbers for the drill. */ + build(now: number): DrillResult { + const totalChars = this.records.length; + const correctChars = this.records.filter((r) => r.correct).length; + const errors = totalChars - correctChars; + const seconds = this.started ? Math.max((now - this.startedAt) / 1000, 0) : 0; + + return { + rawWpm: wpm(totalChars, seconds), + netWpm: wpm(correctChars, seconds), + accuracy: totalChars ? (correctChars / totalChars) * 100 : 100, + correctChars, + totalChars, + errors, + seconds, + samples: this.samples(now), + }; + } +} diff --git a/apps/web/src/lib/engine/types.ts b/apps/web/src/lib/engine/types.ts new file mode 100644 index 0000000..b435548 --- /dev/null +++ b/apps/web/src/lib/engine/types.ts @@ -0,0 +1,45 @@ +/** A lesson: a named curriculum unit holding a pool of Thai words. */ +export interface Lesson { + name: string; + words: string[]; +} + +/** + * One recorded keystroke that produced a Manoonchai character. Space (word + * commit) is recorded too, with `expected`/`correct` reflecting whether the + * just-typed word matched its target. + */ +export interface Keystroke { + /** The Manoonchai character produced (or " " for a word commit). */ + char: string; + /** Whether it matched the expected next character at the time. */ + correct: boolean; + /** Milliseconds since the drill started. */ + t: number; +} + +/** Per-second sample for the performance chart. */ +export interface WpmSample { + /** Second index (1-based, for display). */ + second: number; + /** Raw WPM in this one-second window. */ + raw: number; + /** Net WPM: cumulative, error-penalised. */ + net: number; + /** Errors committed during this second (drives error markers). */ + errors: number; +} + +/** The headline numbers shown at the end of a drill. */ +export interface DrillResult { + rawWpm: number; + netWpm: number; + accuracy: number; + /** Total correct characters typed (excludes spaces). */ + correctChars: number; + /** Total characters typed (excludes spaces). */ + totalChars: number; + errors: number; + seconds: number; + samples: WpmSample[]; +} diff --git a/apps/web/src/lib/engine/wpm.test.ts b/apps/web/src/lib/engine/wpm.test.ts new file mode 100644 index 0000000..2c806b5 --- /dev/null +++ b/apps/web/src/lib/engine/wpm.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from "vitest"; +import { CHARS_PER_WORD, wpm } from "./wpm"; + +describe("wpm", () => { + it("treats 5 characters as one word", () => { + expect(CHARS_PER_WORD).toBe(5); + // 25 chars in 60s = 5 words/min + expect(wpm(25, 60)).toBe(5); + }); + + it("scales with time", () => { + // 5 chars (1 word) in 1s = 60 wpm + expect(wpm(5, 1)).toBe(60); + }); + + it("returns 0 for zero time or non-positive chars", () => { + expect(wpm(10, 0)).toBe(0); + expect(wpm(0, 10)).toBe(0); + expect(wpm(-3, 10)).toBe(0); + }); +}); diff --git a/apps/web/src/lib/engine/wpm.ts b/apps/web/src/lib/engine/wpm.ts new file mode 100644 index 0000000..9c10345 --- /dev/null +++ b/apps/web/src/lib/engine/wpm.ts @@ -0,0 +1,8 @@ +/** Standard typing-speed convention: a "word" is five characters. */ +export const CHARS_PER_WORD = 5; + +/** Words-per-minute for `chars` characters typed over `seconds`. */ +export function wpm(chars: number, seconds: number): number { + if (!seconds || chars <= 0) return 0; + return (chars / CHARS_PER_WORD / seconds) * 60; +} diff --git a/apps/web/src/lib/index.ts b/apps/web/src/lib/index.ts new file mode 100644 index 0000000..648b5d0 --- /dev/null +++ b/apps/web/src/lib/index.ts @@ -0,0 +1,2 @@ +// place files you want to import through the `$lib` alias in this folder. +export {}; diff --git a/apps/web/src/lib/state/settings.svelte.ts b/apps/web/src/lib/state/settings.svelte.ts new file mode 100644 index 0000000..2d8a976 --- /dev/null +++ b/apps/web/src/lib/state/settings.svelte.ts @@ -0,0 +1,95 @@ +import { browser } from "$app/environment"; +import type { DrillLength } from "$lib/engine"; + +export type Theme = "light" | "dark" | "system"; + +const KEY = "learn-manoonchai:v2"; + +interface Persisted { + showKeymap: boolean; + glow: boolean; + showAdjacentWords: boolean; + theme: Theme; + drillLength: DrillLength; + currentLessonName?: string; +} + +const DEFAULTS: Persisted = { + showKeymap: true, + glow: true, + showAdjacentWords: true, + theme: "system", + drillLength: 25, + currentLessonName: undefined, +}; + +function load(): Persisted { + if (!browser) return { ...DEFAULTS }; + try { + return { ...DEFAULTS, ...JSON.parse(localStorage.getItem(KEY) || "{}") }; + } catch { + return { ...DEFAULTS }; + } +} + +/** + * App settings, persisted to localStorage and reactive via runes. Only the + * controls the redesign keeps: keymap + glow, adjacent-word hints, theme, and + * drill length. (v1's logo / Esc / Tab toggles became fixed behaviours.) + */ +class Settings { + showKeymap = $state(true); + glow = $state(true); + showAdjacentWords = $state(true); + theme = $state("system"); + drillLength = $state(25); + currentLessonName = $state(undefined); + + #systemDark = $state(false); + #initialized = false; + + /** Whether the dark theme is currently active, resolving "system". */ + get resolvedDark(): boolean { + return this.theme === "dark" || (this.theme === "system" && this.#systemDark); + } + + /** Hydrate from storage and wire up persistence + theme application. */ + init(): void { + if (!browser || this.#initialized) return; + this.#initialized = true; + + const p = load(); + this.showKeymap = p.showKeymap; + this.glow = p.glow; + this.showAdjacentWords = p.showAdjacentWords; + this.theme = p.theme; + this.drillLength = p.drillLength; + this.currentLessonName = p.currentLessonName; + + const mq = window.matchMedia("(prefers-color-scheme: dark)"); + this.#systemDark = mq.matches; + mq.addEventListener("change", (e) => { + this.#systemDark = e.matches; + }); + + $effect.root(() => { + $effect(() => { + const data: Persisted = { + showKeymap: this.showKeymap, + glow: this.glow, + showAdjacentWords: this.showAdjacentWords, + theme: this.theme, + drillLength: this.drillLength, + currentLessonName: this.currentLessonName, + }; + localStorage.setItem(KEY, JSON.stringify(data)); + }); + + $effect(() => { + document.documentElement.classList.toggle("dark", this.resolvedDark); + }); + }); + } +} + +export const settings = new Settings(); diff --git a/apps/web/src/routes/+layout.svelte b/apps/web/src/routes/+layout.svelte new file mode 100644 index 0000000..0fe3b8f --- /dev/null +++ b/apps/web/src/routes/+layout.svelte @@ -0,0 +1,15 @@ + + +{@render children()} diff --git a/apps/web/src/routes/+layout.ts b/apps/web/src/routes/+layout.ts new file mode 100644 index 0000000..9221a56 --- /dev/null +++ b/apps/web/src/routes/+layout.ts @@ -0,0 +1,2 @@ +// Prerender the app to static HTML at build time (no server runtime needed). +export const prerender = true; diff --git a/apps/web/src/routes/+page.svelte b/apps/web/src/routes/+page.svelte new file mode 100644 index 0000000..202cddf --- /dev/null +++ b/apps/web/src/routes/+page.svelte @@ -0,0 +1,280 @@ + + + + Learn Manoonchai — ฝึกพิมพ์แป้นมนูญชัย + + + + + + + + + + +
+
+ +
+ + + +
+
+ +
+ {#if finished && result} + (showLesson = true)} + /> + {:else} + + +
+ + +
+ + +
+
+ + {#if settings.showKeymap} + + {/if} + +

+ Tab เริ่มใหม่ · Esc ตั้งค่า +

+ {/if} +
+ + +
+ + + diff --git a/apps/web/static/favicon.png b/apps/web/static/favicon.png new file mode 100644 index 0000000..825b9e6 Binary files /dev/null and b/apps/web/static/favicon.png differ diff --git a/apps/web/svelte.config.js b/apps/web/svelte.config.js new file mode 100644 index 0000000..925e0b3 --- /dev/null +++ b/apps/web/svelte.config.js @@ -0,0 +1,21 @@ +import adapter from "@sveltejs/adapter-static"; +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + + kit: { + // The app is fully client-side (no server routes) and a single route, so we + // fully prerender to static HTML — the emitted index.html keeps its + // meta/OG tags for SEO. No SPA fallback needed. + adapter: adapter({ + pages: "build", + assets: "build", + precompress: false, + strict: true, + }), + }, +}; + +export default config; diff --git a/apps/web/test-results/.last-run.json b/apps/web/test-results/.last-run.json new file mode 100644 index 0000000..cbcc1fb --- /dev/null +++ b/apps/web/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "passed", + "failedTests": [] +} \ No newline at end of file diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 0000000..f4d0a0e --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts new file mode 100644 index 0000000..069409d --- /dev/null +++ b/apps/web/vite.config.ts @@ -0,0 +1,7 @@ +import { sveltekit } from "@sveltejs/kit/vite"; +import tailwindcss from "@tailwindcss/vite"; +import { defineConfig } from "vite-plus"; + +export default defineConfig({ + plugins: [tailwindcss(), sveltekit()], +}); diff --git a/apps/web/vitest-setup.ts b/apps/web/vitest-setup.ts new file mode 100644 index 0000000..f149f27 --- /dev/null +++ b/apps/web/vitest-setup.ts @@ -0,0 +1 @@ +import "@testing-library/jest-dom/vitest"; diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts new file mode 100644 index 0000000..ba11052 --- /dev/null +++ b/apps/web/vitest.config.ts @@ -0,0 +1,20 @@ +import { svelte } from "@sveltejs/vite-plugin-svelte"; +import { svelteTesting } from "@testing-library/svelte/vite"; +import { fileURLToPath } from "node:url"; + +// Standalone test config: the plain Svelte plugin (not the SvelteKit plugin, +// which expects a running kit environment) plus jsdom for component tests. +export default { + plugins: [svelte(), svelteTesting()], + resolve: { + alias: { + $lib: fileURLToPath(new URL("./src/lib", import.meta.url)), + }, + }, + test: { + environment: "jsdom", + globals: true, + setupFiles: ["./vitest-setup.ts"], + include: ["src/**/*.{test,spec}.{js,ts}"], + }, +}; diff --git a/bts.jsonc b/bts.jsonc new file mode 100644 index 0000000..4ae408d --- /dev/null +++ b/bts.jsonc @@ -0,0 +1,31 @@ +// Better-T-Stack +// +// Website: https://www.better-t-stack.dev/ +// Stack Builder: https://www.better-t-stack.dev/new +// Analytics: https://www.better-t-stack.dev/analytics +// Showcase: https://www.better-t-stack.dev/showcase +// Sponsor: https://github.com/sponsors/AmanVarshney01 +// +// Add new addons with: bun create better-t-stack add +// This file is safe to delete + +{ + "$schema": "https://r2.better-t-stack.dev/schema.json", + "version": "3.32.0", + "createdAt": "2026-06-13T02:43:52.173Z", + "reproducibleCommand": "bun create better-t-stack@latest v2 --frontend svelte --backend self --runtime none --database none --orm none --api none --auth none --payments none --addons oxlint vite-plus --examples none --db-setup none --web-deploy none --server-deploy none --git --package-manager bun --install", + "database": "none", + "orm": "none", + "backend": "self", + "runtime": "none", + "frontend": ["svelte"], + "addons": ["oxlint", "vite-plus"], + "examples": [], + "auth": "none", + "payments": "none", + "packageManager": "bun", + "dbSetup": "none", + "api": "none", + "webDeploy": "none", + "serverDeploy": "none", +} diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..1cfab02 --- /dev/null +++ b/bun.lock @@ -0,0 +1,923 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "v2", + "dependencies": { + "@v2/env": "workspace:*", + "dotenv": "catalog:", + "zod": "catalog:", + }, + "devDependencies": { + "@types/node": "catalog:", + "@v2/config": "workspace:*", + "oxfmt": "^0.53.0", + "oxlint": "^1.68.0", + "rolldown": "1.1.0", + "typescript": "catalog:", + "vite-plus": "0.1.24", + }, + }, + "apps/web": { + "name": "web", + "version": "0.0.1", + "dependencies": { + "@fontsource/sarabun": "^5.2.8", + "@v2/env": "workspace:*", + "dotenv": "catalog:", + "layerchart": "^1.0.13", + "zod": "catalog:", + }, + "devDependencies": { + "@playwright/test": "^1.60.0", + "@sveltejs/adapter-auto": "^7.0.1", + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.58.0", + "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@tailwindcss/vite": "^4.2.4", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/svelte": "^5.3.1", + "@v2/config": "workspace:*", + "jsdom": "^29.1.1", + "svelte": "^5.55.5", + "svelte-check": "^4.4.6", + "tailwindcss": "^4.2.4", + "typescript": "catalog:", + "vite": "^8.0.10", + "vitest": "^3.2.0", + }, + }, + "packages/config": { + "name": "@v2/config", + "version": "0.0.0", + }, + "packages/env": { + "name": "@v2/env", + "version": "0.0.0", + "dependencies": { + "@t3-oss/env-core": "^0.13.1", + "dotenv": "catalog:", + "zod": "catalog:", + }, + "devDependencies": { + "@types/node": "catalog:", + "@v2/config": "workspace:*", + "typescript": "catalog:", + }, + }, + }, + "overrides": { + "vite": "npm:@voidzero-dev/vite-plus-core@0.1.24", + "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24", + }, + "catalog": { + "@types/node": "^22.13.14", + "dotenv": "^17.2.2", + "typescript": "^6", + "zod": "^4.1.13", + }, + "packages": { + "@adobe/css-tools": ["@adobe/css-tools@4.5.0", "", {}, "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q=="], + + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.1.11", "", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@csstools/css-calc": "^3.2.0", "@csstools/css-color-parser": "^4.1.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg=="], + + "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@7.1.1", "", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1" } }, "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ=="], + + "@asamuzakjp/generational-cache": ["@asamuzakjp/generational-cache@1.0.1", "", {}, "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg=="], + + "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], + + "@bramus/specificity": ["@bramus/specificity@2.4.2", "", { "dependencies": { "css-tree": "^3.0.0" }, "bin": { "specificity": "bin/cli.js" } }, "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw=="], + + "@csstools/color-helpers": ["@csstools/color-helpers@6.0.2", "", {}, "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q=="], + + "@csstools/css-calc": ["@csstools/css-calc@3.2.1", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg=="], + + "@csstools/css-color-parser": ["@csstools/css-color-parser@4.1.3", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.2.1" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-DOgvIPkikIOixQRlD4YF31VN6fLLUTdrzhfRbis8vm0kMTgIbEPX0Ip/YX9fOeV9iywAS4sUUbTclpan7yYP8Q=="], + + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@4.0.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w=="], + + "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.1.5", "", { "peerDependencies": { "css-tree": "^3.2.1" }, "optionalPeers": ["css-tree"] }, "sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A=="], + + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="], + + "@dagrejs/dagre": ["@dagrejs/dagre@1.1.8", "", { "dependencies": { "@dagrejs/graphlib": "2.2.4" } }, "sha512-5SEDlndt4W/LaVzPYJW+bSmSEZc9EzTf8rJ20WCKvjS5EAZAN0b+x0Yww7VMT4R3Wootkg+X9bUfUxazYw6Blw=="], + + "@dagrejs/graphlib": ["@dagrejs/graphlib@2.2.4", "", {}, "sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw=="], + + "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@exodus/bytes": ["@exodus/bytes@1.15.1", "", { "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" }, "optionalPeers": ["@noble/hashes"] }, "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + + "@fontsource/sarabun": ["@fontsource/sarabun@5.2.8", "", {}, "sha512-EDf1jYaGmdeOOi3dKqy6Bjb+qPtsrJJyleoqAPq+VgStcJExwhqzYRT9HOeGE73RtfmvgJEdxk1W72EdxL6+ow=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@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=="], + + "@layerstack/svelte-actions": ["@layerstack/svelte-actions@1.0.1", "", { "dependencies": { "@floating-ui/dom": "^1.6.13", "@layerstack/utils": "1.0.1", "d3-array": "^3.2.4", "d3-scale": "^4.0.2", "date-fns": "^4.1.0", "lodash-es": "^4.17.21" } }, "sha512-Tv8B3TeT7oaghx0R0I4avnSdfAT6GxEK+StL8k/hEaa009iNOIGFl3f76kfvNvPioQHAMFGtnWGLPHfsfD41nQ=="], + + "@layerstack/svelte-stores": ["@layerstack/svelte-stores@1.0.2", "", { "dependencies": { "@layerstack/utils": "1.0.1", "d3-array": "^3.2.4", "date-fns": "^4.1.0", "immer": "^10.1.1", "lodash-es": "^4.17.21", "zod": "^3.24.2" } }, "sha512-IxK0UKD0PVxg1VsyaR+n7NyJ+NlvyqvYYAp+J10lkjDQxm0yx58CaF2LBV08T22C3aY1iTlqJaatn/VHV4SoQg=="], + + "@layerstack/tailwind": ["@layerstack/tailwind@1.0.1", "", { "dependencies": { "@layerstack/utils": "^1.0.1", "clsx": "^2.1.1", "culori": "^4.0.1", "d3-array": "^3.2.4", "date-fns": "^4.1.0", "lodash-es": "^4.17.21", "tailwind-merge": "^2.5.4", "tailwindcss": "^3.4.15" } }, "sha512-nlshEkUCfaV0zYzrFXVVYRnS8bnBjs4M7iui6l/tu6NeBBlxDivIyRraJkdYGCSL1lZHi6FqacLQ3eerHtz90A=="], + + "@layerstack/utils": ["@layerstack/utils@1.0.1", "", { "dependencies": { "d3-array": "^3.2.4", "date-fns": "^4.1.0", "lodash-es": "^4.17.21" } }, "sha512-sWP9b+SFMkJYMZyYFI01aLxbg2ZUrix6Tv+BCDmeOrcLNxtWFsMYAomMhALzTMHbb+Vis/ua5vXhpdNXEw8a2Q=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.2" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@oxc-project/runtime": ["@oxc-project/runtime@0.133.0", "", {}, "sha512-PkvjA1Lq5++V5S1E6Patr92ZVcieE6EalDr1VJTqv4BnjZdOUC4W3p8k1wMXSd5/2aFP4b/A6N5sg2Bkzcr9vQ=="], + + "@oxc-project/types": ["@oxc-project/types@0.134.0", "", {}, "sha512-T0xuRRKrQFmocH8y+jGfpmSkGcheaJExY9lEihmR1Gm2aH+75B8CzgU2rABRQSzzDxLjZ15Sc0bRVLj5lVeNXQ=="], + + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.53.0", "", { "os": "android", "cpu": "arm" }, "sha512-XfVM8AmIovBTKXCt14Op5wbfcoM8418nttd+nhMgM3RAVaJg1MtJc73FyWfUt0oxLyBGVwfniNVUsbV/b3VmPg=="], + + "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.53.0", "", { "os": "android", "cpu": "arm64" }, "sha512-btHDfXckwdf9zgyAVznfZkf+GVyB0I1m1hlvaOMRx2xoyz3hphfPX97s89J3wfCN8QBETLtk4lQUaeOkrMuQOg=="], + + "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.53.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-k2RjMcSTkHjoOlsVGbL35JVzXL+oQco3GHPl/5kjebVF4oHNfE24In8F5isqBh9LBJucycWHKDXdGrCchdWcHQ=="], + + "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.53.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-65jIBE2H1l5SSs16fmv6/7b6sAx/WpvnsgDhVWK9qSjNFDUro7MPQ6q5UhpY7kl46yltfR046iAnxy/Bzqbiew=="], + + "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.53.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-oYe1gkz7U49PCYrS9147d2fJZj8mDI4Di6AvlsU5fu9p+Tq8S7qqOMSZjUiVTLX8bXuSA9Lk/tIxuegVjkNYRA=="], + + "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.53.0", "", { "os": "linux", "cpu": "arm" }, "sha512-ailB2vLzGi629tymdAb2VYJyEHref7oqGxP+tRBrtRBxQrb6NV55JMT7xtGZ8uTeG2+Y9zojqW4LhJYxQnz9Pg=="], + + "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.53.0", "", { "os": "linux", "cpu": "arm" }, "sha512-abh4mWBvOvD966sobqF7r103y2yYx7Rb4WGHLOS4+5igGqLbbPxS9aK5+45D6iUY7dWMsk3Muz9a8gUtufvqJA=="], + + "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.53.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-z73PvuhJ8qA+cDbaiqbtopHglA91U4+y5wn2sTJJrnpB957d5P33FEuyP3DQIFd7ofljmDmfVT4G0CVGHZaJWg=="], + + "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.53.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-I6bhOTroqc3ThrwZ89l2k3ivKuELhdPLbAcJhRNyjWvlgwb0vjRgEnVL1XLx5Jud04/ypNRZBykAWrSk6l/D+g=="], + + "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.53.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-w0p3JzB/PkkQjXALMJMqP9YfP3yq4w6zGsu5kezQmUnxRkN3b/Theg2l/nDgBsOcczxS3gL6Gam5XNAVrO6QJQ=="], + + "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.53.0", "", { "os": "linux", "cpu": "none" }, "sha512-mzBhF6k1Yq1K/dqDmVe/AAafnlJfEpx7yfUiksyeWXJk5iSzZqBSxcsa02zIytYgQFRZ7h6WPZfwHg/DoOE1Kw=="], + + "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.53.0", "", { "os": "linux", "cpu": "none" }, "sha512-AlFCpnRQhogQFzZXWbO6xB6/Udy745L+eQNmDPGg7G/OeWsYmJc4jZYfUN5pQg0reOPWSED2mOQqKZOJM1U8cA=="], + + "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.53.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-XD4ulY4f1DWbuuZXAqxhVn+gdPmrhnmojWtFN78ctVoupmS845fGhsUrk1HZXKQI+iymbaiz9vAjPsghHNQ7Ag=="], + + "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.53.0", "", { "os": "linux", "cpu": "x64" }, "sha512-xg8KWX0QnxmYWRe60CgHYWXI0ZOtBbqTsXvWiWrcl2XUHJ3fht2QerOk2iWvylzX3zNT2GpvBRxGoR4d3sxPRQ=="], + + "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.53.0", "", { "os": "linux", "cpu": "x64" }, "sha512-MWExpYBGvl+pIvVB/gj/CcWlN2al8AizT7rUbtaYaWNoQkhWARM6W3qpgoCr72CYSN9PborzPmM5MIRe2BrNdA=="], + + "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.53.0", "", { "os": "none", "cpu": "arm64" }, "sha512-u4sajgO4nxgmJIgc/y2AqPhkdbOkQH8WugXpA1+pW0ESQhvGZ1oGq61Q4xMbJHJU1hFgtO18QNrcFYDPYH0gwQ=="], + + "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.53.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yq9sOZoIOJ5xPjO0qOyHJS4CiPuTkB2en9auxZz7Ar2p5RaC7BzLyVVmAA7zz9/L9YnjjY1DwNxN+ivKXimN/A=="], + + "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.53.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-es1fVNZEkBqEcQtBpn19SYFgZF7FawlkCjkT/iImfEAus4gun8fBwB1E9hpV5LcR9B0DBNvRIXhW8BQk3JaE+Q=="], + + "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.53.0", "", { "os": "win32", "cpu": "x64" }, "sha512-QFmJs2bEu9AO4O6qsmEaZNGi6dFq8N+rT8EHAAnZIq/B9SeJDUbc4DzVxQ48MfDsL7D3sCZzo37zuTuspcURgg=="], + + "@oxlint-tsgolint/darwin-arm64": ["@oxlint-tsgolint/darwin-arm64@0.23.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gOs9PVr2wEg4ox9z0aJo+RKhhImW86YL5N6yav8BK/rgPsIrwN/igSZ+pbRr723NFvUNKde9fgMhRA6JrXAOZw=="], + + "@oxlint-tsgolint/darwin-x64": ["@oxlint-tsgolint/darwin-x64@0.23.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-kjJ8B+7n4tB9VJdxS5A9GdJt6/bYpzbu4lXp2uO1S3sRmCB5gDEABlGoiePNApRWaW+xqL4b4xgiE727jSLhuA=="], + + "@oxlint-tsgolint/linux-arm64": ["@oxlint-tsgolint/linux-arm64@0.23.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-6dCZuKNu135seMXilkRk9SpCx6i1XgmiipYGalLij5WVRX6ZYS8c4xI7preN/zv9fCXhsQclTIMDu2Y/cytTjw=="], + + "@oxlint-tsgolint/linux-x64": ["@oxlint-tsgolint/linux-x64@0.23.0", "", { "os": "linux", "cpu": "x64" }, "sha512-3bdilnyA7kmSTjK27rvjIjSxL5SIg3wt7vwNiRkouWB83ytssyKnuGvxSYJxgMEmFpSutzaBzcCUM2jDtPGcgA=="], + + "@oxlint-tsgolint/win32-arm64": ["@oxlint-tsgolint/win32-arm64@0.23.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-j+OEp44SVYiQ+ZD+uttsX7u6L9SvmbbQ77SO1pSFCcJlsVMeCk8qZsjhKfGKuT/jIA+ipOJMVs/+pqUfObBWNw=="], + + "@oxlint-tsgolint/win32-x64": ["@oxlint-tsgolint/win32-x64@0.23.0", "", { "os": "win32", "cpu": "x64" }, "sha512-5MyjFuqf+g8OUPJBSGWHJtmoWnzFJYyOg4To9WMQshZYEWig/vtu7JtJ03VWnzHv9LJkAUeApY0gVCOywFR/iQ=="], + + "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.69.0", "", { "os": "android", "cpu": "arm" }, "sha512-DKQQbD5cZ/MYfDgDI7YGyGD9FSxABlsBsYFo5p26lloob543tP9+4N3guwdXIYJN+7HSZxLe8YJuwcOWw5qnHg=="], + + "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.69.0", "", { "os": "android", "cpu": "arm64" }, "sha512-lEhb+I5pr4inux+JFwfCa1HRq3Os7NirEFQ0H1I35SVEHPm6byX0Ah47xmRha3qi6LAkxUcxViL8o/9PivjzBg=="], + + "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.69.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GY2YE8lOZW59BW1Ia1y+1gR0XyjrZRvVWHAr8LGeGhYHE0OQJ/7cRKXTkx1P+E9/6awEc3SX8a68SFTjh/E//A=="], + + "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.69.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ax1oZnOjHX3LB7myQyHEaQkDwfLb6str3/nSP6O7EVUviQGNkEGzGV0EqcBJWK+Ufwx0l4xPgyYayurvhAdl2Q=="], + + "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.69.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kHWeHv4g2h8NY+mpCxzCtY4uerMJWTN/TSnNj1CPbakFpHEJ6cTya2wWV0pDSYWOJ2+0UiEbhn3AtXxHtsnKjg=="], + + "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.69.0", "", { "os": "linux", "cpu": "arm" }, "sha512-gq84vM1a1oEehXo27YCDzGVcxPsZDI1yswZwz2Da1/cbnWtrL16XZZnz0G/+gIU8edtHpfjxq5c+vWEHqJfWoQ=="], + + "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.69.0", "", { "os": "linux", "cpu": "arm" }, "sha512-kIqEa98JQ0VRyrcncxA417m2AzasqTlD+FyVT1AksjvjkqQcvm7pBWYvoW3/mpyOP2XYvi5nSCCTIe6De1yu5g=="], + + "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.69.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j+xYiXozxGWx2cpjCrwwGR4awTxPFsRv3JZrv23RCogEPMc4R7UqjHW47p/RG0aRlbWiROCJ8coUfCwy0dvzHA=="], + + "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.69.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-xEPpNppTfN1l/nM7gYSf9iocscu/as+p/7vxkLeLEKnYU+09Dm+5V6IhDYDh+Uz6FajEupWwCLt5SOG0y1PCKg=="], + + "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.69.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Ug0+eU7HJBlek+SjklYH62IlOMirEJsdxpihH0kSqX0XdrDD4NdHpQc10fK1JC35yn6KrrcN+uYzlHD38XAf8Q=="], + + "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.69.0", "", { "os": "linux", "cpu": "none" }, "sha512-iEyI3GIg0l/s3G4qy2TlaaWKdzj4PJJStwtlocpDTC00PY9hZueotf6OKUj9+yfQh0lrpBW/pLMgTztbAHKJEg=="], + + "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.69.0", "", { "os": "linux", "cpu": "none" }, "sha512-NjHjpiI4WIKSMwuoJSZi5VToPeoYOS1FR52HLIDG6lidMdqquusgtODb4iLk0+lb1q3Z0nv2/aPRcC/olmpQGg=="], + + "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.69.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-Ai/prDewoItkDXbp38gwGZi41DycZbUTZJ3UidwoHgQC0/DaqC2TGdtBTQLJ6hSD+SAxASzh8+/eSBPmxfOacA=="], + + "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.69.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Gt3KHgp46mRKz4sJeaASmKvD8ayXookRw07RMf+NowhEztGGDZ7VrXpoW96XuKJLjFukWizOFVNjmYb/u7caNQ=="], + + "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.69.0", "", { "os": "linux", "cpu": "x64" }, "sha512-7tQhJ2+p/oHv1zcfnjYI7YVzC/7iBaVOfIvFYtxdJ5F45mWgEdrCyXZXZGfiLey5t/5JhOhsaMnnv1kAzckd7g=="], + + "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.69.0", "", { "os": "none", "cpu": "arm64" }, "sha512-vmWz6TKp/3hfA4lksR0zHBv/6xuX1jhym6eqOjdH2DXsDDHZWcp2f0KG0VCAnlVbIrjk29G4wAWMXb/Hn1YobA=="], + + "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.69.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-9RExaLgmaw6IoIkU9cTpT71mLfI0xZ86iZH8x518LVsOkjquJMYqb9P7KpC8lgd1t0Dxs41p2pxynq4XR3Ttzw=="], + + "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.69.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-1907kRPF8/PrcIw1E7LMs9JbVrpgnt/MvFdss3an8oDkYNAACXzTntV3t3869ZZhMZxb2AzRGbz1pA/jdFatXA=="], + + "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.69.0", "", { "os": "win32", "cpu": "x64" }, "sha512-w8SOXv3mT9Fi6jY8OXdXCfnvX/3KNLXGNr4HEz2TA7S4Mv/PYAOmpB8y/ge40mxvBMgGNaSaaDwZpAsQn7HtWA=="], + + "@oxlint/plugins": ["@oxlint/plugins@1.61.0", "", {}, "sha512-nkOyZEF1vH527CkdQtOp1HMrVFEM4ResURvI2JFeGoup+h+43J/k/FgdOR9b9Isxg+Yae7qVDa7y3nssE8b3TQ=="], + + "@playwright/test": ["@playwright/test@1.60.0", "", { "dependencies": { "playwright": "1.60.0" }, "bin": { "playwright": "cli.js" } }, "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag=="], + + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gCYzGOSkYY6Z034suzd20euvds7lPzMEEla62DJGE/ZAlR4OMBnNbvnBSsIGUCAr52gaWMsloGxP4tVGtN5aCA=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JQBD77MNgu+4Z6RAyg69acugdrhhVoWesr3l47zohYZ2YV2fwkWMArkN/2p4l6Ei+Sno7W5q+UsKdVWq5Ens0w=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-p/8cXUTK4Sob604e+xxPhVSbDFf29E6J0l/xESM9rdCfn3aDai3nEs6TnMHUsdD5aNlFz0+gDbiGlozLKGa2YA=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KbtOSlVv6fElujiZWMcC3aQYhEwLVVf073RcwlSmpGQvIsKZFUqc0ef4sjUuurRwfbiI6JJXji9DQn+86hawmQ=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.0", "", { "os": "linux", "cpu": "arm" }, "sha512-9fZ9i0o0/MQaw7om6Z6TsT7tfCk0jtbEFtC+aPqZL5RNsGWNcHvn6EHgL3dAprjq+AZzPTAQjg2JtpJaMt+6pg=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-+tog7T66i+yFyIuuAnjL6xmW182W/qTBOUt6BtQ6lBIM1Eikh/fSMz4HGgvuCp5uU0zuIVWng7kDYthjCMOHcg=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-4b7yruLIIj/oZ3GpcLOvxcLCLDMraohn3IhQfN2hBP4w9UekG0DTIajWguJosRGfySf/+h/NwRUiMKoCpxCrqQ=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-QRDOVZd0bhQ5jLsUsCC3dUxDWdTSVY9WMznowZgCGOrZfLLgctWpelhUASEiBwsXfat/JwYnVd1EaxMhqyT+UQ=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-ypxT+Hq76NFG7woFbNbySnGEajFuYuIXeKz/jfCU+lXUoxfi3zLE6OG/ZQNeK3RpZSYJlAe2bokpsQ046CaieQ=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.0", "", { "os": "linux", "cpu": "x64" }, "sha512-IdovCmfROFmpTLahdecTDFL74aLERVYN68F/mLZjfVh6LfoplPfI6deyHNMTcVujbokDV5k05XrFO22zfv+qjg=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.0", "", { "os": "linux", "cpu": "x64" }, "sha512-pcA8xlFp2tyk9T2R6Fi/rPe3bQ1MA+sSMDNUU5Ogu80GHOatkE4P8YCreGAvZErm5Ho2YRXnyvNrWiRncfVysQ=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.0", "", { "os": "none", "cpu": "arm64" }, "sha512-4+fexHayrLCWpriPh4c6dNvL4an34DEZCG7zOM/FD5QNF6h8DT+bDXzyB/kfC8lDJbaFb7jKShtnjDQFXVQEjg=="], + + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.1.0", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-SbL++MNmOw6QamrwIGDMSSfM4ceTzFr+RjbOExJSLLBinScU4WI5OdA413h1qwPw2yH7lVF1+H4svQ+6mSXKTQ=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-+xTE6XC7wBgk0VKRXGG+QAnyW5S9b8vfsFpiMjf0waQTmSQSU8onsH/beyZ8X4aXVveJnotiy7VDjLOaW8bTrg=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Ogji1TQNqH3ACLnYr+1Ns1nyrJ0CO2P585u9Hsh02pXvtFiFpgtgT2b3P4PnCOU86VVCvqtAeCN4OftMT8KU4w=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.10", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA=="], + + "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@7.0.1", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ=="], + + "@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.10", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew=="], + + "@sveltejs/kit": ["@sveltejs/kit@2.65.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-nUWJ4dSKNo8mIOh+HTL+XyRj8FX9Dyb1ayBxj4q9+WrTJfn4jfTt21p3WUFTnnmdnt9xAXpBKLQ+H9y41x0X7Q=="], + + "@sveltejs/load-config": ["@sveltejs/load-config@0.1.1", "", {}, "sha512-BXXm+VOH/9X4N7Dd1iZ2MqA1h7M+9i2noI8QYuLDY8QcN2WHYn7D/VK/+IJNfcAmRw7ACNJ538UT9GXIhnBTiA=="], + + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@7.1.2", "", { "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.2" }, "peerDependencies": { "svelte": "^5.46.4", "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA=="], + + "@t3-oss/env-core": ["@t3-oss/env-core@0.13.11", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-sM7GYY+KL7H/Hl0BE0inWfk3nRHZOLhmVn7sHGxaZt9FAR6KqREXAE+6TqKfiavfXmpRxO/OZ2QgKRd+oiBYRQ=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.3.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "5.21.6", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.1" } }, "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.1", "@tailwindcss/oxide-darwin-arm64": "4.3.1", "@tailwindcss/oxide-darwin-x64": "4.3.1", "@tailwindcss/oxide-freebsd-x64": "4.3.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1", "@tailwindcss/oxide-linux-arm64-musl": "4.3.1", "@tailwindcss/oxide-linux-x64-gnu": "4.3.1", "@tailwindcss/oxide-linux-x64-musl": "4.3.1", "@tailwindcss/oxide-wasm32-wasi": "4.3.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1", "@tailwindcss/oxide-win32-x64-msvc": "4.3.1" } }, "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.1", "", { "os": "android", "cpu": "arm64" }, "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1", "", { "os": "linux", "cpu": "arm" }, "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.1", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.3.1", "", { "dependencies": { "@tailwindcss/node": "4.3.1", "@tailwindcss/oxide": "4.3.1", "tailwindcss": "4.3.1" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ=="], + + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/jest-dom": ["@testing-library/jest-dom@6.9.1", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA=="], + + "@testing-library/svelte": ["@testing-library/svelte@5.3.1", "", { "dependencies": { "@testing-library/dom": "9.x.x || 10.x.x", "@testing-library/svelte-core": "1.0.0" }, "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "vite": "*", "vitest": "*" }, "optionalPeers": ["vite", "vitest"] }, "sha512-8Ez7ZOqW5geRf9PF5rkuopODe5RGy3I9XR+kc7zHh26gBiktLaxTfKmhlGaSHYUOTQE7wFsLMN9xCJVCszw47w=="], + + "@testing-library/svelte-core": ["@testing-library/svelte-core@1.0.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0" } }, "sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/node": ["@types/node@22.19.21", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA=="], + + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + + "@v2/config": ["@v2/config@workspace:packages/config"], + + "@v2/env": ["@v2/env@workspace:packages/env"], + + "@voidzero-dev/vite-plus-core": ["@voidzero-dev/vite-plus-core@0.1.24", "", { "dependencies": { "@oxc-project/runtime": "=0.133.0", "@oxc-project/types": "=0.133.0", "lightningcss": "^1.30.2", "postcss": "^8.5.6" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "@tsdown/css": "0.22.1", "@tsdown/exe": "0.22.1", "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "publint": "^0.3.8", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "typescript": "^5.0.0 || ^6.0.0", "unplugin-unused": "^0.5.0", "unrun": "*", "yaml": "^2.4.2" }, "optionalPeers": ["@arethetypeswrong/core", "@tsdown/css", "@tsdown/exe", "@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "publint", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "typescript", "unplugin-unused", "unrun", "yaml"] }, "sha512-iXPGBABnQnrDMx89H6MOCGcTZp+QW+3rY4YMVKdE6ydchSvPk2O3MI2vgaRVfOtWJ2IjnxSnf1n2yjP67ZBRFQ=="], + + "@voidzero-dev/vite-plus-darwin-arm64": ["@voidzero-dev/vite-plus-darwin-arm64@0.1.24", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Hpo9W9piSFlEsJzGkwzfDXhJGrnYByxHXF7NVQZ7g+SLOprddtlfTeM8t+gq9dxcuq0RzM8ddMAhDQP/K3fZQA=="], + + "@voidzero-dev/vite-plus-darwin-x64": ["@voidzero-dev/vite-plus-darwin-x64@0.1.24", "", { "os": "darwin", "cpu": "x64" }, "sha512-SwnnnZrEFBiU5iKlh/CZAVwn0RFt/Udrvt3kFLtdRxMtN5bKaqTFVA2H8Y/FPCWp1QX9bs4V9ZIAeXAk06zLkw=="], + + "@voidzero-dev/vite-plus-linux-arm64-gnu": ["@voidzero-dev/vite-plus-linux-arm64-gnu@0.1.24", "", { "os": "linux", "cpu": "arm64" }, "sha512-ImM3eqDki4DpRuHjW6dEh4St8zvbcfOMR7KQZJX42ArriCLQ/QdaYhDRRbcDi27XsOBqRxm2eqUUEymPrYIHpA=="], + + "@voidzero-dev/vite-plus-linux-arm64-musl": ["@voidzero-dev/vite-plus-linux-arm64-musl@0.1.24", "", { "os": "linux", "cpu": "arm64" }, "sha512-gj4mzbob/ls8Zs7iTuF9Gr0EFFF7tdpDiPxDPBkH8tJP5OkHABlzWUwJhU+9xxcUbTaXqpHDw68Mil7jm5dpMg=="], + + "@voidzero-dev/vite-plus-linux-x64-gnu": ["@voidzero-dev/vite-plus-linux-x64-gnu@0.1.24", "", { "os": "linux", "cpu": "x64" }, "sha512-x7IYK7lI+WuF1n3jSzEYU6FgJxPX/R0rDmTTsOutooGGCU7uShZvfZqIoiTXK0eFnJU5ij5BfBgenenUfsaT/A=="], + + "@voidzero-dev/vite-plus-linux-x64-musl": ["@voidzero-dev/vite-plus-linux-x64-musl@0.1.24", "", { "os": "linux", "cpu": "x64" }, "sha512-JCy2w0eSVUlWQlggK5T47MnL+j0o4EY7hLskINVI8gi+aixQF4xnYBDobz0lbxkqz3/IfiLyXUx6TcU3thcsGQ=="], + + "@voidzero-dev/vite-plus-test": ["@voidzero-dev/vite-plus-test@0.1.24", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@voidzero-dev/vite-plus-core": "0.1.24", "es-module-lexer": "^1.7.0", "obug": "^2.1.1", "pixelmatch": "^7.1.0", "pngjs": "^7.0.0", "sirv": "^3.0.2", "std-env": "^4.0.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "ws": "^8.18.3" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/coverage-istanbul": "4.1.8", "@vitest/coverage-v8": "4.1.8", "@vitest/ui": "4.1.8", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"] }, "sha512-9NiG6UadG0iOaPL1AMsO5sDKkx6MADHw4/mMOmHWZUhhUwqzfVtnnptMK37vD71e6KyR7yAscx19FrtOWWtjvA=="], + + "@voidzero-dev/vite-plus-win32-arm64-msvc": ["@voidzero-dev/vite-plus-win32-arm64-msvc@0.1.24", "", { "os": "win32", "cpu": "arm64" }, "sha512-G+/lhLKVjyn3FmgXX8jeWgq7RcE5O1kdR7QyFayQOdlMX/ZRkvUwQD7bFaqhKzgJM6Oj3a1FH3HQPYk5QOYuCQ=="], + + "@voidzero-dev/vite-plus-win32-x64-msvc": ["@voidzero-dev/vite-plus-win32-x64-msvc@0.1.24", "", { "os": "win32", "cpu": "x64" }, "sha512-b0e5XohEV1w/RdzAtv8/Hm6tvHPXouPtBNsljjW/lDJZq3NCLND5s6lqe8H4IenrgmKSoqakHWtlqJqM36cFbw=="], + + "acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + + "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], + + "aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], + + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], + + "css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="], + + "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "culori": ["culori@4.0.2", "", {}, "sha512-1+BhOB8ahCn4O0cep0Sh2l9KCOfOdY+BXJnKMHFFzDEouSr/el18QwXEMRlOj9UY5nCeA8UN3a/82rUWRBeyBw=="], + + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="], + + "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="], + + "d3-dsv": ["d3-dsv@3.0.1", "", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="], + + "d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="], + + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="], + + "d3-geo-voronoi": ["d3-geo-voronoi@2.1.0", "", { "dependencies": { "d3-array": "3", "d3-delaunay": "6", "d3-geo": "3", "d3-tricontour": "1" } }, "sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q=="], + + "d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-interpolate-path": ["d3-interpolate-path@2.3.0", "", {}, "sha512-tZYtGXxBmbgHsIc9Wms6LS5u4w6KbP8C09a4/ZYc4KLMYYqub57rRBUgpUr2CIarIrJEpdAWWxWQvofgaMpbKQ=="], + + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="], + + "d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="], + + "d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="], + + "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="], + + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-tile": ["d3-tile@1.0.0", "", {}, "sha512-79fnTKpPMPDS5xQ0xuS9ir0165NEwwkFpe/DSOmc2Gl9ldYzKKRDWogmTTE8wAJ8NA7PMapNfEcyKhI9Lxdu5Q=="], + + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "d3-tricontour": ["d3-tricontour@1.1.0", "", { "dependencies": { "d3-delaunay": "6", "d3-scale": "4" } }, "sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ=="], + + "data-urls": ["data-urls@7.0.0", "", { "dependencies": { "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0" } }, "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA=="], + + "date-fns": ["date-fns@4.4.0", "", {}, "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w=="], + + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "delaunator": ["delaunator@5.1.0", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "devalue": ["devalue@5.8.1", "", {}, "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw=="], + + "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], + + "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], + + "dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], + + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], + + "enhanced-resolve": ["enhanced-resolve@5.21.6", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ=="], + + "entities": ["entities@8.0.0", "", {}, "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], + + "esrap": ["esrap@2.2.11", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-gPdx+I+BjYEinNMQaBXFjbaJVyoPMU4ZODg5mE+M4DqVG9VusAVHHjcBX+zqyITlI0DIARwDMMzZwAWj36dRoQ=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="], + + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], + + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], + + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "jsdom": ["jsdom@29.1.1", "", { "dependencies": { "@asamuzakjp/css-color": "^5.1.11", "@asamuzakjp/dom-selector": "^7.1.1", "@bramus/specificity": "^2.4.2", "@csstools/css-syntax-patches-for-csstree": "^1.1.3", "@exodus/bytes": "^1.15.0", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.3.5", "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.1", "undici": "^7.25.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.1", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "layercake": ["layercake@8.4.3", "", { "dependencies": { "d3-array": "^3.2.4", "d3-color": "^3.1.0", "d3-scale": "^4.0.2", "d3-shape": "^3.2.0" }, "peerDependencies": { "svelte": "3 - 5 || >=5.0.0-next.120", "typescript": "^5.0.2" } }, "sha512-PZDduaPFxgHHkxlmsz5MVBECf6ZCT39DI3LgMVvuMwrmlrtlXwXUM/elJp46zHYzCE1j+cGyDuBDxnANv94tOQ=="], + + "layerchart": ["layerchart@1.0.13", "", { "dependencies": { "@dagrejs/dagre": "^1.1.4", "@layerstack/svelte-actions": "^1.0.1", "@layerstack/svelte-stores": "^1.0.2", "@layerstack/tailwind": "^1.0.1", "@layerstack/utils": "^1.0.1", "d3-array": "^3.2.4", "d3-color": "^3.1.0", "d3-delaunay": "^6.0.4", "d3-dsv": "^3.0.1", "d3-force": "^3.0.0", "d3-geo": "^3.1.1", "d3-geo-voronoi": "^2.1.0", "d3-hierarchy": "^3.1.2", "d3-interpolate": "^3.0.1", "d3-interpolate-path": "^2.3.0", "d3-path": "^3.1.0", "d3-quadtree": "^3.0.1", "d3-random": "^3.0.1", "d3-sankey": "^0.12.3", "d3-scale": "^4.0.2", "d3-scale-chromatic": "^3.1.0", "d3-shape": "^3.2.0", "d3-tile": "^1.0.0", "d3-time": "^3.1.0", "date-fns": "^4.1.0", "layercake": "8.4.3", "lodash-es": "^4.17.21" }, "peerDependencies": { "svelte": "^3.56.0 || ^4.0.0 || ^5.0.0" } }, "sha512-bjcrfyTdHtfYZn7yj26dvA1qUjM+R6+akp2VeBJ4JWKmDGhb5WvT9nMCs52Rb+gSd/omFq5SjZLz49MqlVljZw=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], + + "lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="], + + "lru-cache": ["lru-cache@11.5.1", "", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], + + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], + + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], + + "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], + + "obug": ["obug@2.1.3", "", {}, "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg=="], + + "oxfmt": ["oxfmt@0.53.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.53.0", "@oxfmt/binding-android-arm64": "0.53.0", "@oxfmt/binding-darwin-arm64": "0.53.0", "@oxfmt/binding-darwin-x64": "0.53.0", "@oxfmt/binding-freebsd-x64": "0.53.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.53.0", "@oxfmt/binding-linux-arm-musleabihf": "0.53.0", "@oxfmt/binding-linux-arm64-gnu": "0.53.0", "@oxfmt/binding-linux-arm64-musl": "0.53.0", "@oxfmt/binding-linux-ppc64-gnu": "0.53.0", "@oxfmt/binding-linux-riscv64-gnu": "0.53.0", "@oxfmt/binding-linux-riscv64-musl": "0.53.0", "@oxfmt/binding-linux-s390x-gnu": "0.53.0", "@oxfmt/binding-linux-x64-gnu": "0.53.0", "@oxfmt/binding-linux-x64-musl": "0.53.0", "@oxfmt/binding-openharmony-arm64": "0.53.0", "@oxfmt/binding-win32-arm64-msvc": "0.53.0", "@oxfmt/binding-win32-ia32-msvc": "0.53.0", "@oxfmt/binding-win32-x64-msvc": "0.53.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-9cB5glS3Ip6NMuZ+6NYTao9FCWkDhRtPYCtR3QBu/NxHoFbgzzTvi41N4jxz/GqGfuLKspui1qb/LlSu2IbMcw=="], + + "oxlint": ["oxlint@1.69.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.69.0", "@oxlint/binding-android-arm64": "1.69.0", "@oxlint/binding-darwin-arm64": "1.69.0", "@oxlint/binding-darwin-x64": "1.69.0", "@oxlint/binding-freebsd-x64": "1.69.0", "@oxlint/binding-linux-arm-gnueabihf": "1.69.0", "@oxlint/binding-linux-arm-musleabihf": "1.69.0", "@oxlint/binding-linux-arm64-gnu": "1.69.0", "@oxlint/binding-linux-arm64-musl": "1.69.0", "@oxlint/binding-linux-ppc64-gnu": "1.69.0", "@oxlint/binding-linux-riscv64-gnu": "1.69.0", "@oxlint/binding-linux-riscv64-musl": "1.69.0", "@oxlint/binding-linux-s390x-gnu": "1.69.0", "@oxlint/binding-linux-x64-gnu": "1.69.0", "@oxlint/binding-linux-x64-musl": "1.69.0", "@oxlint/binding-openharmony-arm64": "1.69.0", "@oxlint/binding-win32-arm64-msvc": "1.69.0", "@oxlint/binding-win32-ia32-msvc": "1.69.0", "@oxlint/binding-win32-x64-msvc": "1.69.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.22.1", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-ypZkK/aDc5NQV8zIR6s2H2Tl3aNW8FmJ1m9+2qsaYuRenl8vgnHNCGwTHviWJdUQzglOlHFchgopdtGhSy17Rw=="], + + "oxlint-tsgolint": ["oxlint-tsgolint@0.23.0", "", { "optionalDependencies": { "@oxlint-tsgolint/darwin-arm64": "0.23.0", "@oxlint-tsgolint/darwin-x64": "0.23.0", "@oxlint-tsgolint/linux-arm64": "0.23.0", "@oxlint-tsgolint/linux-x64": "0.23.0", "@oxlint-tsgolint/win32-arm64": "0.23.0", "@oxlint-tsgolint/win32-x64": "0.23.0" }, "bin": { "tsgolint": "bin/tsgolint.js" } }, "sha512-3mBv3CoPbh8dFbzfDGIWa2ytZjn2v+3EX4aKRXjIhsoGFzG8GCjfRirz3rwZf1wYbZzsNLTSgpw8VjQuWdp/jA=="], + + "parse5": ["parse5@8.0.1", "", { "dependencies": { "entities": "^8.0.0" } }, "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], + + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], + + "pixelmatch": ["pixelmatch@7.2.0", "", { "dependencies": { "pngjs": "^7.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-xhcb4yHu9sM/G7foGzoLtXYcC0zHEaOXXjRKhGup0fw78Nf2Tkiapv4EQyMzrbcmQPsllAI7DbFY2UT7PlI9Pg=="], + + "playwright": ["playwright@1.60.0", "", { "dependencies": { "playwright-core": "1.60.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA=="], + + "playwright-core": ["playwright-core@1.60.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA=="], + + "pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="], + + "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + + "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], + + "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="], + + "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="], + + "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], + + "postcss-selector-parser": ["postcss-selector-parser@6.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ=="], + + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + + "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="], + + "rolldown": ["rolldown@1.1.0", "", { "dependencies": { "@oxc-project/types": "=0.134.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.0", "@rolldown/binding-darwin-arm64": "1.1.0", "@rolldown/binding-darwin-x64": "1.1.0", "@rolldown/binding-freebsd-x64": "1.1.0", "@rolldown/binding-linux-arm-gnueabihf": "1.1.0", "@rolldown/binding-linux-arm64-gnu": "1.1.0", "@rolldown/binding-linux-arm64-musl": "1.1.0", "@rolldown/binding-linux-ppc64-gnu": "1.1.0", "@rolldown/binding-linux-s390x-gnu": "1.1.0", "@rolldown/binding-linux-x64-gnu": "1.1.0", "@rolldown/binding-linux-x64-musl": "1.1.0", "@rolldown/binding-openharmony-arm64": "1.1.0", "@rolldown/binding-wasm32-wasi": "1.1.0", "@rolldown/binding-win32-arm64-msvc": "1.1.0", "@rolldown/binding-win32-x64-msvc": "1.1.0" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-zpMvlJhs5PkXRTtKc0CaLBVI9AR/VDiJFpM+kx//hgToEca7FgMlGjaRIisXBcb19T76LswgmKECSQ96hjWr5A=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="], + + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + + "set-cookie-parser": ["set-cookie-parser@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="], + + "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=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="], + + "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], + + "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "svelte": ["svelte@5.56.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.10", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.8.1", "esm-env": "^1.2.1", "esrap": "^2.2.11", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-w7JvrM5IFl5cmfbY0TLik9o7mjRUJmRMhOR51tBPu708Gr/MjbGs7VnJnr/B0CaXeI4vtnOh7RKxDr0cwhMdDA=="], + + "svelte-check": ["svelte-check@4.6.0", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "@sveltejs/load-config": "0.1.1", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-KhVnDFDSid57mmZtHz8gfW8AAGylOZ0vPnOIzVmAL+urzwK8sBYXRss953gD8T0OdgAQ11mdWhE6uadmtOz8TQ=="], + + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "tailwind-merge": ["tailwind-merge@2.6.1", "", {}, "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ=="], + + "tailwindcss": ["tailwindcss@4.3.1", "", {}, "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q=="], + + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], + + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], + + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], + + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], + + "tldts": ["tldts@7.4.2", "", { "dependencies": { "tldts-core": "^7.4.2" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw=="], + + "tldts-core": ["tldts-core@7.4.2", "", {}, "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + + "tough-cookie": ["tough-cookie@6.0.1", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw=="], + + "tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="], + + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], + + "undici": ["undici@7.27.2", "", {}, "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "vite": ["@voidzero-dev/vite-plus-core@0.1.24", "", { "dependencies": { "@oxc-project/runtime": "=0.133.0", "@oxc-project/types": "=0.133.0", "lightningcss": "^1.30.2", "postcss": "^8.5.6" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "@tsdown/css": "0.22.1", "@tsdown/exe": "0.22.1", "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "publint": "^0.3.8", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "typescript": "^5.0.0 || ^6.0.0", "unplugin-unused": "^0.5.0", "unrun": "*", "yaml": "^2.4.2" }, "optionalPeers": ["@arethetypeswrong/core", "@tsdown/css", "@tsdown/exe", "@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "publint", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "typescript", "unplugin-unused", "unrun", "yaml"] }, "sha512-iXPGBABnQnrDMx89H6MOCGcTZp+QW+3rY4YMVKdE6ydchSvPk2O3MI2vgaRVfOtWJ2IjnxSnf1n2yjP67ZBRFQ=="], + + "vite-plus": ["vite-plus@0.1.24", "", { "dependencies": { "@oxc-project/types": "=0.133.0", "@oxlint/plugins": "=1.61.0", "@voidzero-dev/vite-plus-core": "0.1.24", "@voidzero-dev/vite-plus-test": "0.1.24", "oxfmt": "=0.52.0", "oxlint": "=1.67.0", "oxlint-tsgolint": "=0.23.0" }, "optionalDependencies": { "@voidzero-dev/vite-plus-darwin-arm64": "0.1.24", "@voidzero-dev/vite-plus-darwin-x64": "0.1.24", "@voidzero-dev/vite-plus-linux-arm64-gnu": "0.1.24", "@voidzero-dev/vite-plus-linux-arm64-musl": "0.1.24", "@voidzero-dev/vite-plus-linux-x64-gnu": "0.1.24", "@voidzero-dev/vite-plus-linux-x64-musl": "0.1.24", "@voidzero-dev/vite-plus-win32-arm64-msvc": "0.1.24", "@voidzero-dev/vite-plus-win32-x64-msvc": "0.1.24" }, "bin": { "oxfmt": "bin/oxfmt", "oxlint": "bin/oxlint", "vp": "bin/vp" } }, "sha512-b3fr6WtCiEhetjuzW/4KcEMOAMuZxoxZATWaXKmPzOLf1upG+pzKJOFZTb94D6wiPBlwcjxoaUtF7C3uAN+VjQ=="], + + "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], + + "vitest": ["@voidzero-dev/vite-plus-test@0.1.24", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@voidzero-dev/vite-plus-core": "0.1.24", "es-module-lexer": "^1.7.0", "obug": "^2.1.1", "pixelmatch": "^7.1.0", "pngjs": "^7.0.0", "sirv": "^3.0.2", "std-env": "^4.0.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "ws": "^8.18.3" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/coverage-istanbul": "4.1.8", "@vitest/coverage-v8": "4.1.8", "@vitest/ui": "4.1.8", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"] }, "sha512-9NiG6UadG0iOaPL1AMsO5sDKkx6MADHw4/mMOmHWZUhhUwqzfVtnnptMK37vD71e6KyR7yAscx19FrtOWWtjvA=="], + + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + + "web": ["web@workspace:apps/web"], + + "webidl-conversions": ["webidl-conversions@8.0.1", "", {}, "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@5.0.0", "", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="], + + "whatwg-url": ["whatwg-url@16.0.1", "", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="], + + "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], + + "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + + "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], + + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "@layerstack/svelte-stores/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@layerstack/tailwind/tailwindcss": ["tailwindcss@3.4.19", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.2" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + + "@voidzero-dev/vite-plus-core/@oxc-project/types": ["@oxc-project/types@0.133.0", "", {}, "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA=="], + + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="], + + "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + + "vite/@oxc-project/types": ["@oxc-project/types@0.133.0", "", {}, "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA=="], + + "vite-plus/@oxc-project/types": ["@oxc-project/types@0.133.0", "", {}, "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA=="], + + "vite-plus/oxfmt": ["oxfmt@0.52.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.52.0", "@oxfmt/binding-android-arm64": "0.52.0", "@oxfmt/binding-darwin-arm64": "0.52.0", "@oxfmt/binding-darwin-x64": "0.52.0", "@oxfmt/binding-freebsd-x64": "0.52.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.52.0", "@oxfmt/binding-linux-arm-musleabihf": "0.52.0", "@oxfmt/binding-linux-arm64-gnu": "0.52.0", "@oxfmt/binding-linux-arm64-musl": "0.52.0", "@oxfmt/binding-linux-ppc64-gnu": "0.52.0", "@oxfmt/binding-linux-riscv64-gnu": "0.52.0", "@oxfmt/binding-linux-riscv64-musl": "0.52.0", "@oxfmt/binding-linux-s390x-gnu": "0.52.0", "@oxfmt/binding-linux-x64-gnu": "0.52.0", "@oxfmt/binding-linux-x64-musl": "0.52.0", "@oxfmt/binding-openharmony-arm64": "0.52.0", "@oxfmt/binding-win32-arm64-msvc": "0.52.0", "@oxfmt/binding-win32-ia32-msvc": "0.52.0", "@oxfmt/binding-win32-x64-msvc": "0.52.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-nJlYM35F64zTDMecCNhoHNkf+D/eHv7xcjj9XDSj+bFAVtN93m7v8DQMdHd6nDG6Akf/kEYYHmDUBs2Dz27Sug=="], + + "vite-plus/oxlint": ["oxlint@1.67.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.67.0", "@oxlint/binding-android-arm64": "1.67.0", "@oxlint/binding-darwin-arm64": "1.67.0", "@oxlint/binding-darwin-x64": "1.67.0", "@oxlint/binding-freebsd-x64": "1.67.0", "@oxlint/binding-linux-arm-gnueabihf": "1.67.0", "@oxlint/binding-linux-arm-musleabihf": "1.67.0", "@oxlint/binding-linux-arm64-gnu": "1.67.0", "@oxlint/binding-linux-arm64-musl": "1.67.0", "@oxlint/binding-linux-ppc64-gnu": "1.67.0", "@oxlint/binding-linux-riscv64-gnu": "1.67.0", "@oxlint/binding-linux-riscv64-musl": "1.67.0", "@oxlint/binding-linux-s390x-gnu": "1.67.0", "@oxlint/binding-linux-x64-gnu": "1.67.0", "@oxlint/binding-linux-x64-musl": "1.67.0", "@oxlint/binding-openharmony-arm64": "1.67.0", "@oxlint/binding-win32-arm64-msvc": "1.67.0", "@oxlint/binding-win32-ia32-msvc": "1.67.0", "@oxlint/binding-win32-x64-msvc": "1.67.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.22.1", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-blwwaHPdoH8piQ5/z0KHeoHFR7FZgl12WluKJfu4qFLPkZl6mK04PkLE45Fw1NxfBRSlh40Gu7MkxHUw++ociQ=="], + + "@layerstack/tailwind/tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + + "@layerstack/tailwind/tailwindcss/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], + + "d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], + + "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + + "vite-plus/oxfmt/@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.52.0", "", { "os": "android", "cpu": "arm" }, "sha512-17EMSJnQ9g+upVHrAUYDMfH5lvRKQ9Nvg8WtEoH72oDr1VpWz+7/o3tD97U1EToen2YAQ/68JmtDYkQUi20dfQ=="], + + "vite-plus/oxfmt/@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.52.0", "", { "os": "android", "cpu": "arm64" }, "sha512-A2G1IdwGEW2lLJkIxcvuirRH1CzSl/e0NX11zTlW1gvxJThfwbI/BEoaKrTNpm7M2FchvIf6guvIQU7d5iz+OQ=="], + + "vite-plus/oxfmt/@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.52.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-f9+bLvOYxy7NttCLFTvQ7afmqDOWY4wIP9xdvfj5trQ1qj6f2UFAGwZESlfsMjvJNTyRpXfIlOanCI9FOvoeQA=="], + + "vite-plus/oxfmt/@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.52.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-YSTB9sJ5nnQd/Q0ddHkgof0ZCHPAnWZT1IW2SJ8omz7CP7KluJhO1fNHrpqdxCtpztJwSs4hY1uAee35wKxxaw=="], + + "vite-plus/oxfmt/@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.52.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-NIrRNTTPCs4UbmVs0bxLSCDlLCtIRMJIXklNKaXa5Oj2/K1UIMBvgE8+uPVo01Io3N9HF0+GAX+aAHjUgZS7vA=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.52.0", "", { "os": "linux", "cpu": "arm" }, "sha512-JXUCde8mn3GpgQouz2PXUokgy/uT1QrRJBL2s983VWcSQp62wTFYiNXgTKdeo1Jgbr0IgUnKKvzIk/YBlj/nVQ=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.52.0", "", { "os": "linux", "cpu": "arm" }, "sha512-psbUXaRZ+V8DaXz10Qf7LSHtdtdKAmC8fxXgeU608jjzrmWK4quamZMOpl6sf+dikoFHA85uE93Q0BqxrCdQrQ=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.52.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Jw7MgWUU9lcLCcy82updISP3EthTlfvAwR6gWNxPzqly7+fLvOi2gHQE9xXQjpqaVLm/8P+gOzlv9ODuoVlaaw=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.52.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-wZg6bLjDvh2KibyI3QFUYo8GTXneIFsd0JvehtvJiUmQ8WRPERgxd/VM4ctWb86U5FT1FkqgS8/wZKVB+AZScg=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.52.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-IngE8uxhNvxcMrLjZNDo9xNLY7rEK33AKnaMd2B46he1e/mz2CfcW6If/U1wUjdRZddm1QzQaciqZkuMkdh1FA=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.52.0", "", { "os": "linux", "cpu": "none" }, "sha512-H3+DdFMv/efN3Efmhsv18jDrpiWWqKG7wsfAlQBqAt6z/E2Bx+TwEj2Nowe51CPOWB8/mFBC2dAMSgVFLvvowA=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.52.0", "", { "os": "linux", "cpu": "none" }, "sha512-zji+1kb7lJKohSDjzC1IsS+K/cKRs1hdVf0ZH0VbdbiakmtLvN9twBoXo/k8VdjFax7kfo+DyPxS7vv52br1aw=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.52.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hcLBYedpCy7ToUvvBidWk7+11Yhg1oAZ4+6hKPic/mQI6NaqXJSXMps5nFlwUuX2ewhtLZZDPg63TI042qGKBg=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.52.0", "", { "os": "linux", "cpu": "x64" }, "sha512-IDO2loXK2OtTOhSPchU9MW25mWL2QCDGdJbjN8MXKZVS80qXe5gMTwQWu/gMJ3juoBHbkuUZNB2N1LHzNT7DoA=="], + + "vite-plus/oxfmt/@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.52.0", "", { "os": "linux", "cpu": "x64" }, "sha512-mAV2Hjn0SatJ+KoAzKUC3eJhdJ8wv+3m1KyuS0dTsbF0c5weq+QrCt/DRZZM+uj/XiKzCDEUKYsBF30e2qkcyw=="], + + "vite-plus/oxfmt/@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.52.0", "", { "os": "none", "cpu": "arm64" }, "sha512-vd4npaUIwChxp7XzkqmepBWTT9YMcSe/NBApVGPC30/lLyOVaV3dvma1SKo03t8O73BPRAG7EyJzGlN5cJM5hQ=="], + + "vite-plus/oxfmt/@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.52.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-k2sz6gWQdMfh5HPpIS+Bw/0UEV/kaK2xuqJRrWL233sEHx9WLlsmvlPFM4HUNThkYbSN0U0vPW7LVKZWDS8hPQ=="], + + "vite-plus/oxfmt/@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.52.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-rhke69GTcArodLHpjMTfNnvjTEBryDeZcUCKK/VjXDMtfTULl6QRh0ymX5/hbCUv2WjYm9h/QbW++q2vE15gWQ=="], + + "vite-plus/oxfmt/@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.52.0", "", { "os": "win32", "cpu": "x64" }, "sha512-q5xL7oeXkZdEtNZWBdvehJcmt+GRu9l2bK40yJs1jJXlqq+r0Hygb1rTjq+FM2o/2xyt4cufH6KRplHp3Jjsvw=="], + + "vite-plus/oxlint/@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.67.0", "", { "os": "android", "cpu": "arm" }, "sha512-VrSi571rDv1N8HaEDM+DEX8nmT0y9jJo8tzzW13vsOWTx59xQczCIJx68n2zWOXRT5YKZsOZXp4qkHN/10x4mw=="], + + "vite-plus/oxlint/@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.67.0", "", { "os": "android", "cpu": "arm64" }, "sha512-l6+NdYxMoRohix5r5bbigW16LPicceCwGcQ6LKKuE1kUdjgFfQolJjrJsQYPFetIs78Gxj/G/f5TEGoTCwj9nQ=="], + + "vite-plus/oxlint/@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.67.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jOzXxS1AxFxhImLIRbtGIMrEwaXcgMw3gR57WB1cRk8ai+vpr6726kxXqVvlNsrXtJ/FrmOm8RxlC0m8SW24Qg=="], + + "vite-plus/oxlint/@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.67.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-3DFAVY94OqjIZHXIPz37yGRSWwOFTAqChQ64/M69GYLawzP0KiwdhDNfqdKKYT0bTR/DNxmMnQsj3ns+8+X/Lg=="], + + "vite-plus/oxlint/@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.67.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-e4dDKZuLu8TR9DEBssWSDahlPgZBwojTTHZUvnjBRJfJJbpxYCjfjKfi0Z1+CSLMiJBwI2yCDtRM1XJQaARjmg=="], + + "vite-plus/oxlint/@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.67.0", "", { "os": "linux", "cpu": "arm" }, "sha512-BKytFdcQzbITV3xlnzDUDTEDtbUMCCiC4EaNTDZ4FyT8gdNvBC4gfiLucXp/sQl0XU3p7syTlorUWVVVBZab2g=="], + + "vite-plus/oxlint/@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.67.0", "", { "os": "linux", "cpu": "arm" }, "sha512-XYAv0esBDX7BpTzRDjVX2Vdj+zndd8ll2dFQiaeQ6zTZr7A8GRDTN7fH3FP3jU+O0vCDx85oH/EtG7BzPgAXuw=="], + + "vite-plus/oxlint/@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.67.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-zizRMjA0i6u/2B0evgda04iycu+MoNuf1pBy6Eh+1CjC5wMEG7qN5zdDKTCvFc0KSYSDM9QTG3gjZHirgtQuKg=="], + + "vite-plus/oxlint/@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.67.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-zB/Tf6sUjmmvvbva9Gj3JTJ8rJ9t4I8/U0o6vSRtd0DRIsIuyegBwJAzhSUFQHdMijIRJkW0exs/yBhpw2S20w=="], + + "vite-plus/oxlint/@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.67.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-kgU40Gt74CK0TCsF51KZymkIwN9U0BajKsMijB52zPqOeZU9NAHkA/NSQkZDHEaCakx42DxhXkODiAqf2b4Gug=="], + + "vite-plus/oxlint/@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.67.0", "", { "os": "linux", "cpu": "none" }, "sha512-tOYhkk/iaG9aD3FvGpBFd1Lrw0x0RaVoJBxjUkfNzS50rC5NS5BteNCwgr8A2zCdADrIIoze6D7u6U5Ic++/iQ=="], + + "vite-plus/oxlint/@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.67.0", "", { "os": "linux", "cpu": "none" }, "sha512-sEtywrPb+0b+tHYl1SDCrw903fiC4eyKoNqzP3v+f2JT3Xcv4NEYG+P8rj+eEnX7IWhqV/xj8/JmcmVj21CXaA=="], + + "vite-plus/oxlint/@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.67.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-BvR8Moa0zCLxroOx4vZaZN9nUfwAUpSTwjZdxZyKy4bv3PrzrXrxKR/ZQ0L9wNSvlPhnMJeZfa3q5w6ZCTuN6Q=="], + + "vite-plus/oxlint/@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.67.0", "", { "os": "linux", "cpu": "x64" }, "sha512-mm2cxM6fksOpq6l0uFws8BUGKAR4dNa/cZCn37Npq7PFbhD5HDJqWfnoIvTaeRKMy5XdS2tO0MA0qbHDrnXAAA=="], + + "vite-plus/oxlint/@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.67.0", "", { "os": "linux", "cpu": "x64" }, "sha512-WmbMuLapKyDlobMkXAaAL0Y+Uczh4LETfIfQsUpbId4Ip8Ai82/jqeYTOoUCkuuhBFapgqP253+d83tLKOksJg=="], + + "vite-plus/oxlint/@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.67.0", "", { "os": "none", "cpu": "arm64" }, "sha512-9g/PqxYJelzzTAOR5Y+RiRqdeydhEuXv2KxNeFcAKQ7UsvnWSY1OP4MsuPMbTO2Pf70tz7mFhl1j13H3fyh+8g=="], + + "vite-plus/oxlint/@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.67.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-2VhwE6Gatb0vJGnN0TBuQMbKCOiZlSQ/zJvVWYLK4a9d4iDiJOen/yVQkGpmsJ90MuH66fzi0kEKI0jRQMDxGA=="], + + "vite-plus/oxlint/@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.67.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-EQ3VExXfeM1InbE5+JjufhZZTWy+kHUwgt3yZR7gQ47Je/mE0WspQPan0OJznh493L5anM210YNJtH1PXjTSFg=="], + + "vite-plus/oxlint/@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.67.0", "", { "os": "win32", "cpu": "x64" }, "sha512-bw24y+/1MHS4QDkons3YyHkPT9uCMoLHHgQhb+mb8NOjTYwub1CZ+K9Ngr8aO5DMrDrkqHwTzlTwFP2vS8Y/ZQ=="], + + "@layerstack/tailwind/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "@layerstack/tailwind/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + + "@layerstack/tailwind/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + } +} diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..641e878 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[install] +linker = "isolated" diff --git a/docs/adr/0001-honest-stats-engine.md b/docs/adr/0001-honest-stats-engine.md new file mode 100644 index 0000000..701d0c4 --- /dev/null +++ b/docs/adr/0001-honest-stats-engine.md @@ -0,0 +1,15 @@ +# Honest, per-keystroke stats engine + +v1 recorded keystrokes but computed speed loosely: its chart was labelled "Raw WPM" +while actually plotting a _cumulative running average_, and per-second values were +faked as a flat `+12` per keystroke regardless of timing or correctness. + +For v2 we rebuild the stats engine around a per-keystroke record +(`{ char, expected, correct, t }`) and derive everything from it: **raw WPM** per +one-second window (chars ÷ 5 × 60), **net WPM** (error-penalized), and **accuracy**. +The chart shows raw + net with error markers, each correctly labelled per the terms +in [CONTEXT.md](../../CONTEXT.md). + +We chose this over a faithful port because the v1 numbers were misleading, and a +future reader comparing the two versions would otherwise wonder whether the changed +math was a bug. It is recorded here so the divergence reads as deliberate. diff --git a/package.json b/package.json index 43b8271..c6fe870 100644 --- a/package.json +++ b/package.json @@ -1,63 +1,47 @@ { - "name": "learn-manoonchai", - "version": "1.3.0", - "license": "MIT", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "test": "jest test --config jest.config.js", - "test:watch": "npm run test -- --watch", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --plugin-search-dir . --check . && eslint .", - "format": "prettier --plugin-search-dir . --write .", - "test:e2e": "playwright test ./e2e", - "semantic-release": "semantic-release" - }, - "devDependencies": { - "@fortawesome/free-brands-svg-icons": "^5.15.4", - "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@playwright/test": "1.25.0", - "@semantic-release/changelog": "^6.0.1", - "@semantic-release/git": "^10.0.1", - "@sveltejs/adapter-auto": "next", - "@sveltejs/kit": "^1.0.0-next.535", - "@testing-library/jest-dom": "^5.14.0", - "@testing-library/svelte": "^3.0.0", - "@testing-library/user-event": "^13.5.0", - "@types/jest": "^27.0.0", - "@types/testing-library__jest-dom": "^5.14.0", - "@typescript-eslint/eslint-plugin": "^5.27.0", - "@typescript-eslint/parser": "^5.27.0", - "autoprefixer": "^10.4.7", - "chart.js": "^3.7.0", - "cssnano": "^5.0.8", - "eslint": "^8.16.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-svelte3": "^4.0.0", - "jest": "^27.3.1", - "jest-canvas-mock": "^2.3.1", - "postcss": "^8.4.14", - "postcss-load-config": "^4.0.1", - "prettier": "^2.6.2", - "prettier-plugin-svelte": "^2.7.0", - "semantic-release": "^18.0.0", - "svelte": "^3.44.0", - "svelte-check": "^2.7.1", - "svelte-fa": "^2.3.3", - "svelte-jester": "^2.0.1", - "svelte-preprocess": "^4.10.7", - "tailwindcss": "^3.1.5", - "ts-jest": "^27.0.7", - "tslib": "^2.3.1", - "typescript": "^4.7.4", - "vite": "^3.1.0" - }, - "type": "module", - "repository": { - "type": "git", - "url": "https://github.com/Manoonchai/learn.git" - }, - "dependencies": {} + "name": "v2", + "private": true, + "workspaces": { + "packages": [ + "apps/*", + "packages/*" + ], + "catalog": { + "dotenv": "^17.2.2", + "zod": "^4.1.13", + "typescript": "^6", + "@types/node": "^22.13.14" + } + }, + "type": "module", + "scripts": { + "dev": "vp run -r dev", + "build": "vp run -r build", + "check-types": "vp run -r check-types", + "check": "oxlint && oxfmt --write", + "lint": "vp lint", + "format": "vp fmt", + "staged": "vp staged", + "hooks:setup": "vp config", + "dev:web": "vp run --filter web dev" + }, + "dependencies": { + "@v2/env": "workspace:*", + "dotenv": "catalog:", + "zod": "catalog:" + }, + "devDependencies": { + "@types/node": "catalog:", + "@v2/config": "workspace:*", + "oxfmt": "^0.53.0", + "oxlint": "^1.68.0", + "rolldown": "1.1.0", + "typescript": "catalog:", + "vite-plus": "0.1.24" + }, + "overrides": { + "vite": "npm:@voidzero-dev/vite-plus-core@0.1.24", + "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24" + }, + "packageManager": "bun@1.4.0" } diff --git a/packages/config/package.json b/packages/config/package.json new file mode 100644 index 0000000..f190e4d --- /dev/null +++ b/packages/config/package.json @@ -0,0 +1,5 @@ +{ + "name": "@v2/config", + "version": "0.0.0", + "private": true +} diff --git a/packages/config/tsconfig.base.json b/packages/config/tsconfig.base.json new file mode 100644 index 0000000..5eaa212 --- /dev/null +++ b/packages/config/tsconfig.base.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ESNext"], + "verbatimModuleSyntax": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "types": ["node"] + } +} diff --git a/packages/env/package.json b/packages/env/package.json new file mode 100644 index 0000000..aacd7a9 --- /dev/null +++ b/packages/env/package.json @@ -0,0 +1,20 @@ +{ + "name": "@v2/env", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + "./server": "./src/server.ts", + "./web": "./src/web.ts" + }, + "dependencies": { + "@t3-oss/env-core": "^0.13.1", + "dotenv": "catalog:", + "zod": "catalog:" + }, + "devDependencies": { + "@types/node": "catalog:", + "@v2/config": "workspace:*", + "typescript": "catalog:" + } +} diff --git a/packages/env/src/server.ts b/packages/env/src/server.ts new file mode 100644 index 0000000..029ae19 --- /dev/null +++ b/packages/env/src/server.ts @@ -0,0 +1,12 @@ +import "dotenv/config"; +import { createEnv } from "@t3-oss/env-core"; +import { z } from "zod"; + +export const env = createEnv({ + server: { + CORS_ORIGIN: z.url(), + NODE_ENV: z.enum(["development", "production", "test"]).default("development"), + }, + runtimeEnv: process.env, + emptyStringAsUndefined: true, +}); diff --git a/packages/env/src/web.ts b/packages/env/src/web.ts new file mode 100644 index 0000000..e6854c2 --- /dev/null +++ b/packages/env/src/web.ts @@ -0,0 +1,8 @@ +import { createEnv } from "@t3-oss/env-core"; + +export const env = createEnv({ + clientPrefix: "VITE_", + client: {}, + runtimeEnv: (import.meta as any).env, + emptyStringAsUndefined: true, +}); diff --git a/packages/env/tsconfig.json b/packages/env/tsconfig.json new file mode 100644 index 0000000..87bc029 --- /dev/null +++ b/packages/env/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@v2/config/tsconfig.base.json" +} diff --git a/tsconfig.json b/tsconfig.json index 6ae0c8c..87bc029 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,3 @@ { - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true - } - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in + "extends": "@v2/config/tsconfig.base.json" } diff --git a/.eslintignore b/v1/.eslintignore similarity index 100% rename from .eslintignore rename to v1/.eslintignore diff --git a/.eslintrc.cjs b/v1/.eslintrc.cjs similarity index 100% rename from .eslintrc.cjs rename to v1/.eslintrc.cjs diff --git a/v1/.gitignore b/v1/.gitignore new file mode 100644 index 0000000..6bd421c --- /dev/null +++ b/v1/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.vercel +.vercel_build_output +.env +.env.* +!.env.example diff --git a/v1/.npmrc b/v1/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/v1/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/v1/.prettierignore similarity index 100% rename from .prettierignore rename to v1/.prettierignore diff --git a/.prettierrc b/v1/.prettierrc similarity index 100% rename from .prettierrc rename to v1/.prettierrc diff --git a/.releaserc.json b/v1/.releaserc.json similarity index 100% rename from .releaserc.json rename to v1/.releaserc.json diff --git a/CHANGELOG.md b/v1/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to v1/CHANGELOG.md diff --git a/v1/README.md b/v1/README.md new file mode 100644 index 0000000..7239a75 --- /dev/null +++ b/v1/README.md @@ -0,0 +1,12 @@ +[![logo](https://manoonchai.com/_next/image?url=%2Fmanoonchai.png&w=64&q=75)](https://www.manoonchai.com/) + +# Learn Manoonchai + +## Development + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +yarn +yarn dev +``` diff --git a/e2e/index.spec.ts b/v1/e2e/index.spec.ts similarity index 100% rename from e2e/index.spec.ts rename to v1/e2e/index.spec.ts diff --git a/jest.config.js b/v1/jest.config.js similarity index 100% rename from jest.config.js rename to v1/jest.config.js diff --git a/jest.setup.ts b/v1/jest.setup.ts similarity index 100% rename from jest.setup.ts rename to v1/jest.setup.ts diff --git a/v1/package.json b/v1/package.json new file mode 100644 index 0000000..43b8271 --- /dev/null +++ b/v1/package.json @@ -0,0 +1,63 @@ +{ + "name": "learn-manoonchai", + "version": "1.3.0", + "license": "MIT", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "test": "jest test --config jest.config.js", + "test:watch": "npm run test -- --watch", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "prettier --plugin-search-dir . --check . && eslint .", + "format": "prettier --plugin-search-dir . --write .", + "test:e2e": "playwright test ./e2e", + "semantic-release": "semantic-release" + }, + "devDependencies": { + "@fortawesome/free-brands-svg-icons": "^5.15.4", + "@fortawesome/free-solid-svg-icons": "^5.15.4", + "@playwright/test": "1.25.0", + "@semantic-release/changelog": "^6.0.1", + "@semantic-release/git": "^10.0.1", + "@sveltejs/adapter-auto": "next", + "@sveltejs/kit": "^1.0.0-next.535", + "@testing-library/jest-dom": "^5.14.0", + "@testing-library/svelte": "^3.0.0", + "@testing-library/user-event": "^13.5.0", + "@types/jest": "^27.0.0", + "@types/testing-library__jest-dom": "^5.14.0", + "@typescript-eslint/eslint-plugin": "^5.27.0", + "@typescript-eslint/parser": "^5.27.0", + "autoprefixer": "^10.4.7", + "chart.js": "^3.7.0", + "cssnano": "^5.0.8", + "eslint": "^8.16.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-svelte3": "^4.0.0", + "jest": "^27.3.1", + "jest-canvas-mock": "^2.3.1", + "postcss": "^8.4.14", + "postcss-load-config": "^4.0.1", + "prettier": "^2.6.2", + "prettier-plugin-svelte": "^2.7.0", + "semantic-release": "^18.0.0", + "svelte": "^3.44.0", + "svelte-check": "^2.7.1", + "svelte-fa": "^2.3.3", + "svelte-jester": "^2.0.1", + "svelte-preprocess": "^4.10.7", + "tailwindcss": "^3.1.5", + "ts-jest": "^27.0.7", + "tslib": "^2.3.1", + "typescript": "^4.7.4", + "vite": "^3.1.0" + }, + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/Manoonchai/learn.git" + }, + "dependencies": {} +} diff --git a/playwright.config.ts b/v1/playwright.config.ts similarity index 100% rename from playwright.config.ts rename to v1/playwright.config.ts diff --git a/postcss.config.cjs b/v1/postcss.config.cjs similarity index 100% rename from postcss.config.cjs rename to v1/postcss.config.cjs diff --git a/src/app.d.ts b/v1/src/app.d.ts similarity index 100% rename from src/app.d.ts rename to v1/src/app.d.ts diff --git a/src/app.html b/v1/src/app.html similarity index 100% rename from src/app.html rename to v1/src/app.html diff --git a/src/app.postcss b/v1/src/app.postcss similarity index 100% rename from src/app.postcss rename to v1/src/app.postcss diff --git a/src/lib/__tests__/lessons.spec.ts b/v1/src/lib/__tests__/lessons.spec.ts similarity index 100% rename from src/lib/__tests__/lessons.spec.ts rename to v1/src/lib/__tests__/lessons.spec.ts diff --git a/src/lib/__tests__/manoonchai.spec.ts b/v1/src/lib/__tests__/manoonchai.spec.ts similarity index 100% rename from src/lib/__tests__/manoonchai.spec.ts rename to v1/src/lib/__tests__/manoonchai.spec.ts diff --git a/src/lib/__tests__/nextchar.spec.ts b/v1/src/lib/__tests__/nextchar.spec.ts similarity index 100% rename from src/lib/__tests__/nextchar.spec.ts rename to v1/src/lib/__tests__/nextchar.spec.ts diff --git a/src/lib/__tests__/spellcheck.spec.ts b/v1/src/lib/__tests__/spellcheck.spec.ts similarity index 100% rename from src/lib/__tests__/spellcheck.spec.ts rename to v1/src/lib/__tests__/spellcheck.spec.ts diff --git a/src/lib/__tests__/stats.spec.ts b/v1/src/lib/__tests__/stats.spec.ts similarity index 100% rename from src/lib/__tests__/stats.spec.ts rename to v1/src/lib/__tests__/stats.spec.ts diff --git a/src/lib/__tests__/store.spec.ts b/v1/src/lib/__tests__/store.spec.ts similarity index 100% rename from src/lib/__tests__/store.spec.ts rename to v1/src/lib/__tests__/store.spec.ts diff --git a/src/lib/__tests__/wpm.spec.ts b/v1/src/lib/__tests__/wpm.spec.ts similarity index 100% rename from src/lib/__tests__/wpm.spec.ts rename to v1/src/lib/__tests__/wpm.spec.ts diff --git a/src/lib/components/Changelog.svelte b/v1/src/lib/components/Changelog.svelte similarity index 100% rename from src/lib/components/Changelog.svelte rename to v1/src/lib/components/Changelog.svelte diff --git a/src/lib/components/Footer.svelte b/v1/src/lib/components/Footer.svelte similarity index 100% rename from src/lib/components/Footer.svelte rename to v1/src/lib/components/Footer.svelte diff --git a/src/lib/components/Keymap.svelte b/v1/src/lib/components/Keymap.svelte similarity index 100% rename from src/lib/components/Keymap.svelte rename to v1/src/lib/components/Keymap.svelte diff --git a/src/lib/components/KeymapButton.svelte b/v1/src/lib/components/KeymapButton.svelte similarity index 100% rename from src/lib/components/KeymapButton.svelte rename to v1/src/lib/components/KeymapButton.svelte diff --git a/src/lib/components/Kofi.svelte b/v1/src/lib/components/Kofi.svelte similarity index 100% rename from src/lib/components/Kofi.svelte rename to v1/src/lib/components/Kofi.svelte diff --git a/src/lib/components/LessonModal.svelte b/v1/src/lib/components/LessonModal.svelte similarity index 100% rename from src/lib/components/LessonModal.svelte rename to v1/src/lib/components/LessonModal.svelte diff --git a/src/lib/components/LibLoader.svelte b/v1/src/lib/components/LibLoader.svelte similarity index 100% rename from src/lib/components/LibLoader.svelte rename to v1/src/lib/components/LibLoader.svelte diff --git a/src/lib/components/Modal.svelte b/v1/src/lib/components/Modal.svelte similarity index 100% rename from src/lib/components/Modal.svelte rename to v1/src/lib/components/Modal.svelte diff --git a/src/lib/components/ShiftButton.svelte b/v1/src/lib/components/ShiftButton.svelte similarity index 100% rename from src/lib/components/ShiftButton.svelte rename to v1/src/lib/components/ShiftButton.svelte diff --git a/src/lib/components/__tests__/Changelog.spec.ts b/v1/src/lib/components/__tests__/Changelog.spec.ts similarity index 100% rename from src/lib/components/__tests__/Changelog.spec.ts rename to v1/src/lib/components/__tests__/Changelog.spec.ts diff --git a/src/lib/components/__tests__/Footer.spec.ts b/v1/src/lib/components/__tests__/Footer.spec.ts similarity index 100% rename from src/lib/components/__tests__/Footer.spec.ts rename to v1/src/lib/components/__tests__/Footer.spec.ts diff --git a/src/lib/components/__tests__/Keymap.spec.ts b/v1/src/lib/components/__tests__/Keymap.spec.ts similarity index 100% rename from src/lib/components/__tests__/Keymap.spec.ts rename to v1/src/lib/components/__tests__/Keymap.spec.ts diff --git a/src/lib/components/__tests__/KeymapButton.spec.ts b/v1/src/lib/components/__tests__/KeymapButton.spec.ts similarity index 100% rename from src/lib/components/__tests__/KeymapButton.spec.ts rename to v1/src/lib/components/__tests__/KeymapButton.spec.ts diff --git a/src/lib/components/__tests__/Kofi.spec.ts b/v1/src/lib/components/__tests__/Kofi.spec.ts similarity index 100% rename from src/lib/components/__tests__/Kofi.spec.ts rename to v1/src/lib/components/__tests__/Kofi.spec.ts diff --git a/src/lib/components/__tests__/LessonModal.spec.ts b/v1/src/lib/components/__tests__/LessonModal.spec.ts similarity index 100% rename from src/lib/components/__tests__/LessonModal.spec.ts rename to v1/src/lib/components/__tests__/LessonModal.spec.ts diff --git a/src/lib/components/__tests__/LibLoader.spec.ts b/v1/src/lib/components/__tests__/LibLoader.spec.ts similarity index 100% rename from src/lib/components/__tests__/LibLoader.spec.ts rename to v1/src/lib/components/__tests__/LibLoader.spec.ts diff --git a/src/lib/lessons.ts b/v1/src/lib/lessons.ts similarity index 100% rename from src/lib/lessons.ts rename to v1/src/lib/lessons.ts diff --git a/src/lib/manoonchai.ts b/v1/src/lib/manoonchai.ts similarity index 100% rename from src/lib/manoonchai.ts rename to v1/src/lib/manoonchai.ts diff --git a/src/lib/nextchar.ts b/v1/src/lib/nextchar.ts similarity index 100% rename from src/lib/nextchar.ts rename to v1/src/lib/nextchar.ts diff --git a/src/lib/spellcheck.ts b/v1/src/lib/spellcheck.ts similarity index 100% rename from src/lib/spellcheck.ts rename to v1/src/lib/spellcheck.ts diff --git a/src/lib/stats.ts b/v1/src/lib/stats.ts similarity index 100% rename from src/lib/stats.ts rename to v1/src/lib/stats.ts diff --git a/src/lib/store.ts b/v1/src/lib/store.ts similarity index 100% rename from src/lib/store.ts rename to v1/src/lib/store.ts diff --git a/src/lib/wpm.ts b/v1/src/lib/wpm.ts similarity index 100% rename from src/lib/wpm.ts rename to v1/src/lib/wpm.ts diff --git a/src/routes/+layout.svelte b/v1/src/routes/+layout.svelte similarity index 100% rename from src/routes/+layout.svelte rename to v1/src/routes/+layout.svelte diff --git a/src/routes/+page.svelte b/v1/src/routes/+page.svelte similarity index 100% rename from src/routes/+page.svelte rename to v1/src/routes/+page.svelte diff --git a/src/tailwind.postcss b/v1/src/tailwind.postcss similarity index 100% rename from src/tailwind.postcss rename to v1/src/tailwind.postcss diff --git a/static/favicon.png b/v1/static/favicon.png similarity index 100% rename from static/favicon.png rename to v1/static/favicon.png diff --git a/svelte.config.js b/v1/svelte.config.js similarity index 100% rename from svelte.config.js rename to v1/svelte.config.js diff --git a/tailwind.config.cjs b/v1/tailwind.config.cjs similarity index 100% rename from tailwind.config.cjs rename to v1/tailwind.config.cjs diff --git a/test/routes/index-dom.spec.ts b/v1/test/routes/index-dom.spec.ts similarity index 100% rename from test/routes/index-dom.spec.ts rename to v1/test/routes/index-dom.spec.ts diff --git a/v1/tsconfig.json b/v1/tsconfig.json new file mode 100644 index 0000000..6ae0c8c --- /dev/null +++ b/v1/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/tsconfig.spec.json b/v1/tsconfig.spec.json similarity index 100% rename from tsconfig.spec.json rename to v1/tsconfig.spec.json diff --git a/v1/vite.config.ts b/v1/vite.config.ts new file mode 100644 index 0000000..c999769 --- /dev/null +++ b/v1/vite.config.ts @@ -0,0 +1,11 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import type { UserConfig } from 'vite'; + +const config: UserConfig = { + plugins: [sveltekit()], + ssr: { + noExternal: ['chart.js'] + } +}; + +export default config; diff --git a/yarn.lock b/v1/yarn.lock similarity index 100% rename from yarn.lock rename to v1/yarn.lock diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..669703e --- /dev/null +++ b/vercel.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "buildCommand": "bun run build", + "installCommand": "bun install", + "outputDirectory": "apps/web/build", + "framework": null +} diff --git a/vite.config.ts b/vite.config.ts index c999769..b882db3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,11 +1,32 @@ -import { sveltekit } from '@sveltejs/kit/vite'; -import type { UserConfig } from 'vite'; +import { defineConfig } from "vite-plus"; -const config: UserConfig = { - plugins: [sveltekit()], - ssr: { - noExternal: ['chart.js'] - } -}; - -export default config; +export default defineConfig({ + lint: { + ignorePatterns: [ + "node_modules/**", + "**/node_modules/**", + "apps/web/.svelte-kit/**", + "apps/web/build/**", + "apps/web/.output/**", + ], + options: { + typeAware: false, + typeCheck: false, + }, + }, + fmt: { + ignorePatterns: [ + "node_modules/**", + "**/node_modules/**", + "apps/web/.svelte-kit/**", + "apps/web/build/**", + "apps/web/.output/**", + ], + singleQuote: false, + semi: true, + sortPackageJson: true, + }, + staged: { + "*.{js,ts,jsx,tsx,vue,svelte,json,jsonc,css,md}": "vp check --fix", + }, +});