diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..6527ef8 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "dev", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "port": 3000, + "autoPort": true + } + ] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..595007d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +# two trailing spaces are a line break in Markdown +trim_trailing_whitespace = false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ddf12e8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,30 @@ +# Dependency currency is automated, not remembered. Groups take minor and +# patch updates together; a MAJOR arrives as its own pull request — a major +# is a decision with a migration story, not a batch item, and a closed one +# isn't recreated. GitHub Actions are grouped whole (CI proves them). +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + groups: + # Next.js and its ESLint config are released together; move them together. + nextjs: + patterns: ["next", "eslint-config-next"] + update-types: [minor, patch] + dependencies: + dependency-type: production + update-types: [minor, patch] + dev-dependencies: + dependency-type: development + update-types: [minor, patch] + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + groups: + actions: + patterns: ["*"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7cf7f87 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +# Every push to main and every pull request: the same checks as +# `npm run check`, then the production build — the static export Cloudflare +# deploys — with a guard that every product card made it in. +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version-file: .nvmrc + cache: npm + + - run: npm ci + - run: npm run lint + - run: npm run typecheck + - run: npm run format:check + - run: npm test + + - name: Build (static export) + shell: bash + env: + # Product cards come from the GitHub API at build time; the job + # token lifts the anonymous rate limit (60 requests an hour per IP). + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -o pipefail + npm run build 2>&1 | tee build.log + # A failed product fetch only warns (the site still builds), so + # CI is where a missing card turns into a failure. + if grep -q "\[products\]" build.log; then + echo "::error::Some product cards failed to load — see [products] lines above." + exit 1 + fi + + - name: Check the static output + run: | + for f in index.html 404.html about.html writings.html sitemap.xml robots.txt feed.xml og.png favicon.ico; do + test -f "out/$f" || { echo "::error::out/$f is missing"; exit 1; } + done diff --git a/.gitignore b/.gitignore index 5ef6a52..9fac8ff 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,12 @@ yarn-error.log* # vercel .vercel +# cloudflare +.wrangler/ + +# local agent settings (the shared .claude/launch.json is committed) +.claude/settings.local.json + # typescript *.tsbuildinfo next-env.d.ts diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f9a2070 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +.next +out +node_modules +package-lock.json +next-env.d.ts +public +# Essays are prose: the author's line breaks stay the author's. +content diff --git a/AGENTS.md b/AGENTS.md index 8bd0e39..caeace2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,5 @@ + # This is NOT the Next.js you know This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. diff --git a/README.md b/README.md index 95b3b23..6b5f524 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,9 @@ # omercelik.dev -Personal website and writing — a quiet home for open-source products and essays. - -Built with **Next.js 16** (App Router), **React 19**, **Tailwind CSS v4**, -**next-intl** and **MDX**. Design language ported from the Mockifyr design system -("quiet frame, lively content"). - -## Features - -- **i18n** — English (default, no URL prefix) plus Arabic (RTL), German, French, - Chinese and Japanese. UI strings are translated in `src/i18n/messages/*.json`; - articles stay in the language they were written in. -- **Theme** — light by default, dark toggle, no flash on load. All colours are - design tokens in one file (`src/app/theme.css`) — swap them to re-skin. -- **Products** — pulled live from the GitHub API and cached hourly. Curate the - list in `src/config/products.ts`. -- **Writing** — MDX files in `content/writings/`. Frontmatter drives the title, - date, language badge, tags and reading time. Code is highlighted with Shiki - (dual light/dark theme). -- **Contact** — dependency-free: the form composes a pre-filled message in the - visitor's mail client (no backend, no email service). Swap in an API route + - provider later if you want server-side sending. +Personal site and writing of Ömer Çelik — open-source products and essays. + +Built with **Next.js 16** (App Router, static export), **React 19**, +**Tailwind CSS v4**, **next-intl** and **MDX**. ## Develop @@ -28,10 +11,69 @@ Built with **Next.js 16** (App Router), **React 19**, **Tailwind CSS v4**, npm install cp .env.example .env.local # optional: add a GITHUB_TOKEN npm run dev # http://localhost:3000 -npm run build # production build +npm run build # static site in ./out +npm run check # lint + typecheck + format check + tests (as CI) +npm run format # apply Prettier ``` -## Add a post +## Quality gates + +- **CI** — `.github/workflows/ci.yml`, on every push to `main` and every pull + request: lint, typecheck, format check, tests, then the static build. The + build fails if any product card couldn't be fetched, and the job checks the + key files in `out/` exist. +- **Dependabot** — `.github/dependabot.yml`, weekly. Minor and patch updates + arrive grouped (Next.js and its ESLint config together); a major version + arrives as its own pull request. GitHub Actions are grouped. +- **Tests** — Vitest, `src/**/*.test.ts`: heading ids match rehype-slug, tag + slugs, the SEO helpers, and the UI messages (no empty strings or lists). +- **Tooling** — Node 24 (`.nvmrc`), Prettier defaults, `.editorconfig`. + Essays under `content/` are left out of formatting on purpose. + +## How it's put together + +- **Static export** — `output: "export"`: every page is plain HTML generated at + build time. There is no server and no proxy/middleware. +- **Language** — the interface is English, with no locale prefix in URLs. UI + copy lives in `src/i18n/messages/en.json` (next-intl, for ICU plurals and + rich text). An article can be written in Turkish: its `lang` frontmatter + sets the `lang` attribute on its text and the locale of its social card. +- **Theme** — follows the operating system until the visitor picks light or + dark with the toggle. +- **Design tokens** — colours, radius and elevation live in + `src/app/theme.css`; the type scale (`text-meta` … `text-display`) in + `src/app/globals.css`. Use the scale rather than arbitrary sizes. +- **Motion** — CSS first: `.intro` for page-load entrances, `.reveal` for + scroll-linked ones (`animation-timeline: view()`). Content is visible without + JavaScript, and `prefers-reduced-motion` switches it all off. +- **Signature diagram** — `src/components/diagram/`: an isometric stack of + architecture layers in CSS 3D (no WebGL), calm by design — nothing moves on + its own. Pointing at a layer opens the stack there (the layers above lift + and draw back, the focused one comes forward and lights up, its description + shows below); the legend does the same on hover or keyboard focus and pins + a layer on click; on touch, tapping a layer pins it. Invisible hit surfaces + at the layers' resting positions decide which layer is under the pointer, + so the focus doesn't jump while the stack opens. It opens a little as the page + scrolls and never tilts, so the layers stay where the pointer expects them. + Without JavaScript or with reduced motion it's a still diagram. +- **SEO** — `src/lib/seo.ts`: every page gets its title, description, + canonical, Open Graph and X card from `pageMetadata` / + `articleMetadata` (Next merges metadata shallowly, so pages set the whole + block). Structured data: Person + WebSite everywhere, ProfilePage on About, + BlogPosting + BreadcrumbList on articles. Social cards are real `.png` + routes (`src/app/og.png`, `src/app/og/[slug]/card.png`) so static hosts + serve them as images. Tag pages are `noindex, follow`. +- **Type** — Geist for the interface, Newsreader (serif) for essay titles and + pull quotes. +- **Products** — read from the GitHub API at build time; curate the list in + `src/config/products.ts`. Set `GITHUB_TOKEN` wherever the build runs: + unauthenticated requests are limited to 60 an hour per IP, and a failed + request drops that card from the build (with a `[products]` warning in the + build log). +- **Contact** — no backend: the form opens the visitor's own mail app with the + message filled in, and says so. + +## Writing Create `content/writings/.mdx`: @@ -39,25 +81,77 @@ Create `content/writings/.mdx`: --- title: "My post" description: "One-line summary." -date: "2026-07-09" -lang: "en" -tags: ["tag"] +date: "2026-09-11" +lang: "en" # en | tr — the language the piece is written in +tags: ["architecture"] +draft: true # optional: visible in `npm run dev` only +series: "Golden paths" # optional, with seriesOrder: 1 --- Your MDX content… ``` +What an article can use: + +- **Code** — fenced blocks are highlighted with Shiki (light + dark). Add a + file name and highlighted lines with ` ```ts title="lib/x.ts" {2-3} `, and + line numbers with `showLineNumbers`. Every block gets a copy button. +- **Callouts** — ``. +- **Figures** — `
`, + with the image under `public/writings/`. +- **Pull quotes** — `The line you want remembered.`, + set large in the serif. +- **Layer diagrams** — the hero's 3D stack, for your own systems: + + ```mdx + + + + + ``` + + String props only — the MDX pipeline doesn't evaluate `{…}` expressions. + +- **Featured** — `featured: true` in the frontmatter leads the home page with + that essay. +- **GFM** — tables, task lists and footnotes (`[^1]`). +- **Table of contents** — appears automatically from three h2/h3 headings up. +- **Comments** — Giscus (GitHub Discussions), one thread per post. + ## Configure -| What | Where | -| --- | --- | -| Name, email, social links | `src/config/site.ts` | -| Which repos show as products | `src/config/products.ts` | -| Design tokens (colours, radius, shadows) | `src/app/theme.css` | -| UI translations | `src/i18n/messages/*.json` | -| Supported locales | `src/i18n/routing.ts` | +| What | Where | +| ----------------------------------- | --------------------------- | +| Name, email, social links, comments | `src/config/site.ts` | +| Which repos show as products | `src/config/products.ts` | +| Colours, radius, shadows | `src/app/theme.css` | +| Type scale, motion, code styling | `src/app/globals.css` | +| UI copy | `src/i18n/messages/en.json` | + +## Deploy — Cloudflare Workers Builds + +Same setup as the `*.qorpe.com` sites: `wrangler.jsonc` serves `./out` as +Worker static assets, with `404.html` for unknown paths. + +1. Connect the repository in Cloudflare **Workers Builds**. Build command: + `npm run build`; deploy command: `npx wrangler deploy`. +2. Add a `GITHUB_TOKEN` build variable — a token with no permissions is enough + to read public repositories. +3. Attach the `omercelik.dev` custom domain to the Worker. + +## After the first deploy — search engines -## Deploy +The site ships everything search engines read (sitemap, canonicals, +structured data); these steps tell them it exists: -Deploys to **Vercel** out of the box. Set `GITHUB_TOKEN` in the project's -environment variables (optional but recommended). +1. **Google Search Console** — add a _Domain_ property for `omercelik.dev`, + verify with the DNS TXT record (in Cloudflare DNS), then submit + `https://omercelik.dev/sitemap.xml`. Use _URL inspection → Request + indexing_ for the home page and each new essay. +2. **Bing Webmaster Tools** — import the site from Search Console (Bing also + feeds DuckDuckGo and others). +3. **Link back to the site** from GitHub, LinkedIn and X profiles — the same + URLs listed as `sameAs` in the Person structured data. Consistent profiles + are what tie searches for the name to this site. +4. Check a page with Google's Rich Results Test and share one link on + LinkedIn/X to confirm the social card. diff --git a/content/writings/golden-paths-1.mdx b/content/writings/golden-paths-1.mdx index 62a66e8..276c39a 100644 --- a/content/writings/golden-paths-1.mdx +++ b/content/writings/golden-paths-1.mdx @@ -6,6 +6,7 @@ lang: "en" tags: ["golden-paths", "architecture", "example"] series: "Golden paths" seriesOrder: 1 +draft: true --- > This is an example post to show how a **series** looks. Replace it with the @@ -15,6 +16,8 @@ A **framework** imposes structure: you build inside its box. A **golden path** does the opposite — it paves the road you were already walking, with enterprise opinion on top of the platform you already use. +A framework says "do it my way". A golden path says "here's the fast, correct way — step off it any time". + ## The difference in one line - Framework: "do it my way." diff --git a/content/writings/golden-paths-2.mdx b/content/writings/golden-paths-2.mdx index 48a8e78..30454b8 100644 --- a/content/writings/golden-paths-2.mdx +++ b/content/writings/golden-paths-2.mdx @@ -6,6 +6,7 @@ lang: "en" tags: ["golden-paths", "spec-driven", "example"] series: "Golden paths" seriesOrder: 2 +draft: true --- > Example post, part two of the series. @@ -28,4 +29,11 @@ invariants: - every endpoint has an idempotency key ``` + + + + + + + Humans approve, guardrails verify, drift gets caught early. That's the whole idea. diff --git a/content/writings/hello-world.mdx b/content/writings/hello-world.mdx index 9698b96..5224b1f 100644 --- a/content/writings/hello-world.mdx +++ b/content/writings/hello-world.mdx @@ -14,17 +14,17 @@ is that place. A home for two things: -- **Products** — the open-source tools I ship, pulled live from GitHub. +- **Products** — the open-source tools I ship, with details pulled from GitHub. - **Writing** — notes on software, systems, and the craft of building things. -Each piece stays in the language I wrote it in. The interface speaks several -languages; the essays speak mine. +The site itself is in English; each piece stays in the language I wrote it in, +so now and then you'll find one in Turkish. ## A small taste of code Here's the shape of the thing that renders this very page: -```ts +```ts title="lib/writings.ts" export async function getWritingBySlug(slug: string) { const raw = await fs.readFile(`content/writings/${slug}.mdx`, "utf-8"); const { data, content } = matter(raw); diff --git a/content/writings/mockifyr-neden.mdx b/content/writings/mockifyr-neden.mdx index a32566f..e3cff61 100644 --- a/content/writings/mockifyr-neden.mdx +++ b/content/writings/mockifyr-neden.mdx @@ -29,3 +29,5 @@ bitmiş sayılmaz. "Çalışıyor gibi görünüyor" bir kanıt değildir. Oracl diff yeşile döner — işte o zaman bitmiştir. Bu blog, o yolculukta öğrendiklerimi Türkçe olarak buraya not ettiğim yer. +Mockifyr'ın kendisine [mockifyr.qorpe.com](https://mockifyr.qorpe.com) adresinden +ulaşabilirsin. diff --git a/eslint.config.mjs b/eslint.config.mjs index 05e726d..33bc2a6 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,6 +13,21 @@ const eslintConfig = defineConfig([ "build/**", "next-env.d.ts", ]), + { + files: ["**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts}"], + rules: { + // Leading underscore marks a binding that exists only to be discarded + // (e.g. omitting a key via rest destructuring). + "@typescript-eslint/no-unused-vars": [ + "warn", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + ignoreRestSiblings: true, + }, + ], + }, + }, ]); export default eslintConfig; diff --git a/next.config.ts b/next.config.ts index eae4da9..bace88f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -4,12 +4,9 @@ import createNextIntlPlugin from "next-intl/plugin"; const withNextIntl = createNextIntlPlugin(); const nextConfig: NextConfig = { - images: { - remotePatterns: [ - { protocol: "https", hostname: "avatars.githubusercontent.com" }, - { protocol: "https", hostname: "opengraph.githubassets.com" }, - ], - }, + // Plain HTML/CSS/JS in `out/`, served as static assets by a Cloudflare + // Worker (see wrangler.jsonc). Everything is rendered at build time. + output: "export", }; export default withNextIntl(nextConfig); diff --git a/package-lock.json b/package-lock.json index 07bbda0..af83267 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,10 @@ "name": "omercelik.dev", "version": "0.1.0", "dependencies": { + "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "lucide-react": "^1.24.0", - "next": "16.2.10", + "next": "^16.3.5", "next-intl": "^4.13.1", "next-mdx-remote": "^6.0.0", "next-themes": "^0.4.6", @@ -26,13 +27,15 @@ "devDependencies": { "@tailwindcss/postcss": "^4", "@types/mdx": "^2.0.14", - "@types/node": "^20", + "@types/node": "^24.13.4", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "16.2.10", + "eslint-config-next": "^16.3.5", + "prettier": "^3.9.6", "tailwindcss": "^4", - "typescript": "^5" + "typescript": "^5", + "vitest": "^5.0.0" } }, "node_modules/@alloc/quick-lru": { @@ -299,9 +302,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", - "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", "license": "MIT", "optional": true, "dependencies": { @@ -570,9 +573,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", "cpu": [ "arm64" ], @@ -582,19 +585,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.3" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", "cpu": [ "x64" ], @@ -604,19 +607,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", "cpu": [ "arm64" ], @@ -630,9 +652,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", "cpu": [ "x64" ], @@ -646,9 +668,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", "cpu": [ "arm" ], @@ -665,9 +687,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", "cpu": [ "arm64" ], @@ -684,9 +706,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", "cpu": [ "ppc64" ], @@ -703,9 +725,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", "cpu": [ "riscv64" ], @@ -722,9 +744,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", "cpu": [ "s390x" ], @@ -741,9 +763,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", "cpu": [ "x64" ], @@ -760,9 +782,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", "cpu": [ "arm64" ], @@ -779,9 +801,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", "cpu": [ "x64" ], @@ -798,9 +820,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", "cpu": [ "arm" ], @@ -813,19 +835,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.3" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", "cpu": [ "arm64" ], @@ -838,19 +860,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.3" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", "cpu": [ "ppc64" ], @@ -863,19 +885,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.3" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", "cpu": [ "riscv64" ], @@ -888,19 +910,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.3" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", "cpu": [ "s390x" ], @@ -913,19 +935,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.3" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", "cpu": [ "x64" ], @@ -938,19 +960,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", "cpu": [ "arm64" ], @@ -963,19 +985,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", "cpu": [ "x64" ], @@ -988,38 +1010,54 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.3" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.4" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", "cpu": [ "arm64" ], @@ -1029,16 +1067,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", "cpu": [ "ia32" ], @@ -1048,16 +1086,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", "cpu": [ "x64" ], @@ -1067,7 +1105,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -1106,9 +1144,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -1197,25 +1235,26 @@ } }, "node_modules/@next/env": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz", - "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.5.tgz", + "integrity": "sha512-NWEXVDMqoEo0ktmU6u0sE2Vg0LOcsD7NnOTJNo3/fEaTfsg+F1bMIxuDmQbda4e3yTIQwVdUREF2yIuMOusKtg==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.10.tgz", - "integrity": "sha512-Gs8D2m21VnJeFo9qvYIIqJH94frWerWYu41BprU1pLtRVF7PCQNLiFZZ3fG+iPuj3K83Cwv/rt+msLOy8Qgu3Q==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.3.5.tgz", + "integrity": "sha512-PGfSeItHJ12DH8t+6sEbuMe59NE5rAhCfgk06QKTH2ne9VUL1JlaXdYXY3B8RaiF2SO50rDYvd39TulP6A6xZQ==", "dev": true, "license": "MIT", "dependencies": { + "@eslint-community/eslint-utils": "4.9.1", "fast-glob": "3.3.1" } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz", - "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.5.tgz", + "integrity": "sha512-pMmGgETfKvElucLHtVaeiMRbp2zUbvKx7b1yGko0liBz3cw1mKSggWN/Rp/wPz8z+E1O82u3r4L1Co+ZS5hokQ==", "cpu": [ "arm64" ], @@ -1229,9 +1268,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz", - "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.5.tgz", + "integrity": "sha512-76VaGYvf6HPa5/w12yLkE3dXTn9AfdEviI79oEL3aZoAmRLc9rWitjWqyjViVysK/ht/y9YKzFkBrUdi/wGkow==", "cpu": [ "x64" ], @@ -1245,9 +1284,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz", - "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.5.tgz", + "integrity": "sha512-zKDELJ5jSQMHeO/hmXUQsAzagX4bQD4OiMi3pQ5FbUj+yK506oLVHnKA2YXMlbg1EHHqJYtyePOgByIDXD1lqw==", "cpu": [ "arm64" ], @@ -1264,9 +1303,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz", - "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.5.tgz", + "integrity": "sha512-7Vql0pgzCoHagv6+FNOZoqmJqA52c6zeVbhtS/47qFozO1MSx4ms7x7GHiciY8R5CDsSMKMQjJEryoJLcsBIbA==", "cpu": [ "arm64" ], @@ -1283,9 +1322,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz", - "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.5.tgz", + "integrity": "sha512-NH/xzehyHEFWE2nlcZon7TB/0+H4shfWCi7S1zka815XCOhJDYZhoeJtOYy0dh0WVRWACVXSyGNFFytoMxUhRg==", "cpu": [ "x64" ], @@ -1302,9 +1341,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz", - "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.5.tgz", + "integrity": "sha512-lV4+EhWMfS8jcC+EH2nn/Cm5cn6XsgbE07bU9tMH8fCo0tNAqhyzi1b5wQ/Tn6NGFTvKDY65w3ZH95EjwBRAnQ==", "cpu": [ "x64" ], @@ -1321,9 +1360,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz", - "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.5.tgz", + "integrity": "sha512-/wKzAREX2RF++MhicjDbg8tGn2AiBIM0+EFeTFKoUEUbW5D6amCJehd5Z5G1H5/gxNdgnwoXMcHz24H/c2tGkQ==", "cpu": [ "arm64" ], @@ -1337,9 +1376,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz", - "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.5.tgz", + "integrity": "sha512-LNdCHzgLFc+UeqMS84LzXPaeBRKyqDN9OMyFAr1OrB0XrNw78IRrEVtZvvA7245W/HsaoeVOQX9jPjPk8jojwA==", "cpu": [ "x64" ], @@ -1400,6 +1439,17 @@ "node": ">=12.4.0" } }, + "node_modules/@oxc-project/types": { + "version": "0.149.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.149.0.tgz", + "integrity": "sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, "node_modules/@parcel/watcher": { "version": "2.5.6", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", @@ -1725,592 +1775,693 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.8.tgz", + "integrity": "sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" - }, - "node_modules/@schummar/icu-type-parser": { - "version": "1.21.5", - "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", - "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==", - "license": "MIT" - }, - "node_modules/@shikijs/core": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.3.1.tgz", - "integrity": "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==", - "license": "MIT", - "dependencies": { - "@shikijs/primitive": "4.3.1", - "@shikijs/types": "4.3.1", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.3.1.tgz", - "integrity": "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "4.3.1", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.6" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.3.1.tgz", - "integrity": "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "4.3.1", - "@shikijs/vscode-textmate": "^10.0.2" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/langs": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.3.1.tgz", - "integrity": "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "4.3.1" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/primitive": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.3.1.tgz", - "integrity": "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A==", "license": "MIT", - "dependencies": { - "@shikijs/types": "4.3.1", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - }, + "optional": true, + "os": [ + "android" + ], + "peer": true, "engines": { - "node": ">=20" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@shikijs/themes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.3.1.tgz", - "integrity": "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.8.tgz", + "integrity": "sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@shikijs/types": "4.3.1" - }, + "optional": true, + "os": [ + "android" + ], + "peer": true, "engines": { - "node": ">=20" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@shikijs/types": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.3.1.tgz", - "integrity": "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.8.tgz", + "integrity": "sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - }, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": ">=20" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "license": "MIT" - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.43.tgz", - "integrity": "sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.8.tgz", + "integrity": "sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==", "cpu": [ - "arm64" + "x64" ], - "license": "Apache-2.0 AND MIT", + "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.43.tgz", - "integrity": "sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.8.tgz", + "integrity": "sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==", "cpu": [ "x64" ], - "license": "Apache-2.0 AND MIT", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "freebsd" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.43.tgz", - "integrity": "sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.8.tgz", + "integrity": "sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==", "cpu": [ "arm" ], - "license": "Apache-2.0", + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.43.tgz", - "integrity": "sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.8.tgz", + "integrity": "sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==", "cpu": [ "arm64" ], + "dev": true, "libc": [ "glibc" ], - "license": "Apache-2.0 AND MIT", + "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.43.tgz", - "integrity": "sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.8.tgz", + "integrity": "sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==", "cpu": [ "arm64" ], + "dev": true, "libc": [ "musl" ], - "license": "Apache-2.0 AND MIT", + "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-linux-ppc64-gnu": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.43.tgz", - "integrity": "sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.8.tgz", + "integrity": "sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==", "cpu": [ "ppc64" ], + "dev": true, "libc": [ "glibc" ], - "license": "Apache-2.0 AND MIT", + "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-linux-s390x-gnu": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.43.tgz", - "integrity": "sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.8.tgz", + "integrity": "sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==", "cpu": [ "s390x" ], + "dev": true, "libc": [ "glibc" ], - "license": "Apache-2.0 AND MIT", + "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.43.tgz", - "integrity": "sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.8.tgz", + "integrity": "sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==", "cpu": [ "x64" ], + "dev": true, "libc": [ "glibc" ], - "license": "Apache-2.0 AND MIT", + "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.43.tgz", - "integrity": "sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.8.tgz", + "integrity": "sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==", "cpu": [ "x64" ], + "dev": true, "libc": [ "musl" ], - "license": "Apache-2.0 AND MIT", + "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.43.tgz", - "integrity": "sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.8.tgz", + "integrity": "sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==", "cpu": [ "arm64" ], - "license": "Apache-2.0 AND MIT", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "win32" + "openharmony" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.43.tgz", - "integrity": "sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.8.tgz", + "integrity": "sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==", "cpu": [ - "ia32" + "arm64" ], - "license": "Apache-2.0 AND MIT", + "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.43", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.43.tgz", - "integrity": "sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.8.tgz", + "integrity": "sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==", "cpu": [ "x64" ], - "license": "Apache-2.0 AND MIT", + "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], + "peer": true, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT", + "peer": true }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@schummar/icu-type-parser": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", + "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==", + "license": "MIT" + }, + "node_modules/@shikijs/core": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.3.1.tgz", + "integrity": "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==", + "license": "MIT", "dependencies": { - "tslib": "^2.8.0" + "@shikijs/primitive": "4.3.1", + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" } }, - "node_modules/@swc/types": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.27.tgz", - "integrity": "sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==", - "license": "Apache-2.0", + "node_modules/@shikijs/engine-javascript": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.3.1.tgz", + "integrity": "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ==", + "license": "MIT", "dependencies": { - "@swc/counter": "^0.1.3" + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" } }, - "node_modules/@tailwindcss/node": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.2.tgz", - "integrity": "sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==", - "dev": true, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.3.1.tgz", + "integrity": "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg==", "license": "MIT", "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.2" + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" } }, - "node_modules/@tailwindcss/oxide": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.2.tgz", - "integrity": "sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==", - "dev": true, + "node_modules/@shikijs/langs": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.3.1.tgz", + "integrity": "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.3.1.tgz", + "integrity": "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A==", "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, "engines": { - "node": ">= 20" + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.3.1.tgz", + "integrity": "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1" }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.2", - "@tailwindcss/oxide-darwin-arm64": "4.3.2", - "@tailwindcss/oxide-darwin-x64": "4.3.2", - "@tailwindcss/oxide-freebsd-x64": "4.3.2", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", - "@tailwindcss/oxide-linux-x64-musl": "4.3.2", - "@tailwindcss/oxide-wasm32-wasi": "4.3.2", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" + "engines": { + "node": ">=20" } }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz", - "integrity": "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@shikijs/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.3.1.tgz", + "integrity": "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, "engines": { - "node": ">= 20" + "node": ">=20" } }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz", - "integrity": "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==", + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.43.tgz", + "integrity": "sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==", "cpu": [ "arm64" ], - "dev": true, - "license": "MIT", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">= 20" + "node": ">=10" } }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz", - "integrity": "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==", + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.43.tgz", + "integrity": "sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==", "cpu": [ "x64" ], - "dev": true, - "license": "MIT", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">= 20" + "node": ">=10" } }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz", - "integrity": "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==", + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.43.tgz", + "integrity": "sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==", "cpu": [ - "x64" + "arm" ], - "dev": true, - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "freebsd" + "linux" ], "engines": { - "node": ">= 20" + "node": ">=10" } }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz", - "integrity": "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==", + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.43.tgz", + "integrity": "sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==", "cpu": [ - "arm" + "arm64" ], - "dev": true, - "license": "MIT", + "libc": [ + "glibc" + ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 20" + "node": ">=10" } }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz", - "integrity": "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==", + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.43.tgz", + "integrity": "sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==", "cpu": [ "arm64" ], - "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-ppc64-gnu": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.43.tgz", + "integrity": "sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==", + "cpu": [ + "ppc64" + ], "libc": [ "glibc" ], - "license": "MIT", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 20" + "node": ">=10" } }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz", - "integrity": "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==", + "node_modules/@swc/core-linux-s390x-gnu": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.43.tgz", + "integrity": "sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==", "cpu": [ - "arm64" + "s390x" ], - "dev": true, "libc": [ - "musl" + "glibc" ], - "license": "MIT", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 20" + "node": ">=10" } }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz", - "integrity": "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==", + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.43.tgz", + "integrity": "sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==", "cpu": [ "x64" ], - "dev": true, "libc": [ "glibc" ], - "license": "MIT", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 20" + "node": ">=10" } }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz", - "integrity": "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==", + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.43.tgz", + "integrity": "sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==", "cpu": [ "x64" ], - "dev": true, "libc": [ "musl" ], - "license": "MIT", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 20" + "node": ">=10" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz", - "integrity": "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.43.tgz", + "integrity": "sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.43.tgz", + "integrity": "sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==", "cpu": [ - "wasm32" + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.43.tgz", + "integrity": "sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@swc/types": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.27.tgz", + "integrity": "sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.2.tgz", + "integrity": "sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/core": "^1.11.1", - "@emnapi/runtime": "^1.11.1", - "@emnapi/wasi-threads": "^1.2.2", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.2", - "tslib": "^2.8.1" - }, + "@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.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.2.tgz", + "integrity": "sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-x64": "4.3.2", + "@tailwindcss/oxide-freebsd-x64": "4.3.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-x64-musl": "4.3.2", + "@tailwindcss/oxide-wasm32-wasi": "4.3.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" } }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "node_modules/@tailwindcss/oxide-android-arm64": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", - "integrity": "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz", + "integrity": "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==", "cpu": [ "arm64" ], @@ -2318,55 +2469,261 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "android" ], "engines": { "node": ">= 20" } }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "node_modules/@tailwindcss/oxide-darwin-arm64": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.2.tgz", - "integrity": "sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz", + "integrity": "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ], "engines": { "node": ">= 20" } }, - "node_modules/@tailwindcss/postcss": { + "node_modules/@tailwindcss/oxide-darwin-x64": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.2.tgz", - "integrity": "sha512-rjVWYCa7Ngbi5AarT6k8TkxUG3Wl1QKzHdIZVsjZSzf36Jmo2IKZt/NHRAwly8oDkbBOH0YTu+CHuf9jPxMc+g==", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz", + "integrity": "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.3.2", - "@tailwindcss/oxide": "4.3.2", - "postcss": "^8.5.15", - "tailwindcss": "4.3.2" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz", + "integrity": "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@types/debug": { + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz", + "integrity": "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz", + "integrity": "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz", + "integrity": "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz", + "integrity": "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz", + "integrity": "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz", + "integrity": "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", + "integrity": "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.2.tgz", + "integrity": "sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.2.tgz", + "integrity": "sha512-rjVWYCa7Ngbi5AarT6k8TkxUG3Wl1QKzHdIZVsjZSzf36Jmo2IKZt/NHRAwly8oDkbBOH0YTu+CHuf9jPxMc+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.2", + "@tailwindcss/oxide": "4.3.2", + "postcss": "^8.5.15", + "tailwindcss": "4.3.2" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", @@ -2375,6 +2732,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -2435,13 +2799,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.43", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", - "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "version": "24.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.4.tgz", + "integrity": "sha512-YJ7EqCstVTzIr0fMr7qul/977en+pQHrfmuKIo6Zr9i75Be21dr3MovcfvGtyvi2HAUrRerWps5sMO9I7WaxDw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/react": { @@ -2668,16 +3032,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { @@ -3124,6 +3488,54 @@ "win32" ] }, + "node_modules/@vitest/mocker": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.0.tgz", + "integrity": "sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.0", + "estree-walker": "^3.0.3", + "magic-string": "^1.2.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/magic-string": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.3.1.tgz", + "integrity": "sha512-rm91zr2Ou+XueDTohjQQjdQEcYM6zVi8KVUCG8Ec3vHwUEKrhSdCNyfuIywkA6hcCAteIn0ZOtAHA6eGpiX+Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/@vitest/spy": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.0.tgz", + "integrity": "sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.17.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", @@ -3355,6 +3767,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -3435,9 +3857,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.42", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz", - "integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==", + "version": "2.11.22", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.22.tgz", + "integrity": "sha512-pWc4w51fBFd7mav43/zKRC+RI6f4yfzQoVlfvE8dECePyfkn1bzLp01Fj0QACcyCZyFhiEMyD2qScfKRWgWibA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -3447,9 +3869,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3471,9 +3893,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.5.tgz", - "integrity": "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==", + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", "dev": true, "funding": [ { @@ -3491,11 +3913,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.42", - "caniuse-lite": "^1.0.30001800", - "electron-to-chromium": "^1.5.387", - "node-releases": "^2.0.50", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" }, "bin": { "browserslist": "cli.js" @@ -3565,9 +3987,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001803", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz", - "integrity": "sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "funding": [ { "type": "opencollective", @@ -3594,6 +4016,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3926,9 +4358,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.389", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz", - "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==", + "version": "1.5.427", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.427.tgz", + "integrity": "sha512-n14zb3FdsChZ2BNobqNHAJMcP3ifFv4paox2LvCrfVAQcqGiSURgbJl+PfMpHVCNFkStnNc+RRVtPBTVW5PDgw==", "dev": true, "license": "ISC" }, @@ -4101,6 +4533,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", @@ -4280,13 +4719,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.10.tgz", - "integrity": "sha512-HSybLOY0QKf39i4FWUqPN0xWiNDi6A6UqJmZtgDkS3zMqjXTqULvj/sueXx3cdCG0mVG+qH6k5/qdegklH1d1w==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.3.5.tgz", + "integrity": "sha512-wPjq9MLQuWykHs8tsm2gH6OJThk6N27L8Te2JatlaGZ7jT1gtgGmJKukygL28xOlWsg5J1a1bGy/PvLyQC8OYA==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.2.10", + "@next/eslint-plugin-next": "16.3.5", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -4729,6 +5168,16 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -4799,9 +5248,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", "dev": true, "license": "ISC", "dependencies": { @@ -4888,14 +5337,30 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/function.prototype.name": { @@ -5116,9 +5581,9 @@ } }, "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", - "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -6073,9 +6538,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "funding": [ { @@ -7661,9 +8126,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", "funding": [ { "type": "github", @@ -7711,16 +8176,16 @@ } }, "node_modules/next": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.10.tgz", - "integrity": "sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/next/-/next-16.3.5.tgz", + "integrity": "sha512-MdtsTgzyfCPRLC6uJ1mN8ao7lyJ4BB0U6Inhnx3gta1UcCIdHK3yxLG0E8OWQteWD8/Q0qb8A5o7wJaL8M9y2w==", "license": "MIT", "dependencies": { - "@next/env": "16.2.10", - "@swc/helpers": "0.5.15", + "@next/env": "16.3.5", + "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", + "postcss": "8.5.23", "styled-jsx": "5.1.6" }, "bin": { @@ -7730,15 +8195,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.10", - "@next/swc-darwin-x64": "16.2.10", - "@next/swc-linux-arm64-gnu": "16.2.10", - "@next/swc-linux-arm64-musl": "16.2.10", - "@next/swc-linux-x64-gnu": "16.2.10", - "@next/swc-linux-x64-musl": "16.2.10", - "@next/swc-win32-arm64-msvc": "16.2.10", - "@next/swc-win32-x64-msvc": "16.2.10", - "sharp": "^0.34.5" + "@next/swc-darwin-arm64": "16.3.5", + "@next/swc-darwin-x64": "16.3.5", + "@next/swc-linux-arm64-gnu": "16.3.5", + "@next/swc-linux-arm64-musl": "16.3.5", + "@next/swc-linux-x64-gnu": "16.3.5", + "@next/swc-linux-x64-musl": "16.3.5", + "@next/swc-win32-arm64-msvc": "16.3.5", + "@next/swc-win32-x64-msvc": "16.3.5", + "sharp": "^0.35.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -7872,34 +8337,6 @@ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", @@ -7926,9 +8363,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.51", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", - "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "version": "2.0.55", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz", + "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==", "dev": true, "license": "MIT", "engines": { @@ -8058,6 +8495,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/oniguruma-parser": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", @@ -8262,10 +8713,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", - "dev": true, + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "funding": [ { "type": "opencollective", @@ -8282,7 +8732,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8300,6 +8750,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -8742,6 +9208,41 @@ "node": ">=0.10.0" } }, + "node_modules/rolldown": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.8.tgz", + "integrity": "sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@oxc-project/types": "=0.149.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.8", + "@rolldown/binding-android-arm64": "1.2.8", + "@rolldown/binding-darwin-arm64": "1.2.8", + "@rolldown/binding-darwin-x64": "1.2.8", + "@rolldown/binding-freebsd-x64": "1.2.8", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.8", + "@rolldown/binding-linux-arm64-gnu": "1.2.8", + "@rolldown/binding-linux-arm64-musl": "1.2.8", + "@rolldown/binding-linux-ppc64-gnu": "1.2.8", + "@rolldown/binding-linux-s390x-gnu": "1.2.8", + "@rolldown/binding-linux-x64-gnu": "1.2.8", + "@rolldown/binding-linux-x64-musl": "1.2.8", + "@rolldown/binding-openharmony-arm64": "1.2.8", + "@rolldown/binding-win32-arm64-msvc": "1.2.8", + "@rolldown/binding-win32-x64-msvc": "1.2.8" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8900,48 +9401,53 @@ } }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/sharp/node_modules/semver": { @@ -9075,6 +9581,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -9116,6 +9629,20 @@ "dev": true, "license": "MIT" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -9378,6 +9905,26 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tinybench": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.1.4.tgz", + "integrity": "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -9653,9 +10200,9 @@ } }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, @@ -9729,102 +10276,598 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", + "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-intl": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.13.1.tgz", + "integrity": "sha512-UU5C3zAC7yVg3m7rq5C8VF5J5jhAfvS19Wi9bPNCB9xB7jQYBsUcrqfdxs4Mxl9XR3x6BDB5K++iAw7/rcm3gg==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "^3.1.0", + "@schummar/icu-type-parser": "1.21.5", + "icu-minify": "^4.13.1", + "intl-messageformat": "^11.1.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-matter": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vfile-matter/-/vfile-matter-5.0.1.tgz", + "integrity": "sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw==", + "license": "MIT", + "dependencies": { + "vfile": "^6.0.0", + "yaml": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz", + "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.6", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://opencollective.com/parcel" } }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://opencollective.com/parcel" } }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://opencollective.com/parcel" } }, - "node_modules/unrs-resolver": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", - "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.4" + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" }, "funding": { - "url": "https://opencollective.com/unrs-resolver" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.12.2", - "@unrs/resolver-binding-android-arm64": "1.12.2", - "@unrs/resolver-binding-darwin-arm64": "1.12.2", - "@unrs/resolver-binding-darwin-x64": "1.12.2", - "@unrs/resolver-binding-freebsd-x64": "1.12.2", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", - "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", - "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", - "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", - "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-x64-musl": "1.12.2", - "@unrs/resolver-binding-openharmony-arm64": "1.12.2", - "@unrs/resolver-binding-wasm32-wasi": "1.12.2", - "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", - "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", - "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "node_modules/vite/node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" + "url": "https://tidelift.com/funding/github/npm/postcss" }, { "type": "github", @@ -9832,102 +10875,120 @@ } ], "license": "MIT", + "peer": true, "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "engines": { + "node": "^10 || ^12 || >=14" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/vitest": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.0.tgz", + "integrity": "sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-intl": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.13.1.tgz", - "integrity": "sha512-UU5C3zAC7yVg3m7rq5C8VF5J5jhAfvS19Wi9bPNCB9xB7jQYBsUcrqfdxs4Mxl9XR3x6BDB5K++iAw7/rcm3gg==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/amannn" - } - ], "license": "MIT", "dependencies": { - "@formatjs/fast-memoize": "^3.1.0", - "@schummar/icu-type-parser": "1.21.5", - "icu-minify": "^4.13.1", - "intl-messageformat": "^11.1.0" + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.0", + "chai": "^6.2.2", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.2.3", + "obug": "^2.1.4", + "picomatch": "^4.0.7", + "std-env": "^4.2.0", + "tinybench": "6.1.4", + "tinyexec": "1.3.0", + "tinyglobby": "^0.2.17", + "why-is-node-running": "^2.3.0" }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" + "bin": { + "vitest": "vitest.mjs" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" + "engines": { + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.0", + "@vitest/browser-preview": "5.0.0", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.0", + "@vitest/coverage-v8": "5.0.0", + "@vitest/ui": "5.0.0", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } } }, - "node_modules/vfile-matter": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/vfile-matter/-/vfile-matter-5.0.1.tgz", - "integrity": "sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw==", + "node_modules/vitest/node_modules/magic-string": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.3.1.tgz", + "integrity": "sha512-rm91zr2Ou+XueDTohjQQjdQEcYM6zVi8KVUCG8Ec3vHwUEKrhSdCNyfuIywkA6hcCAteIn0ZOtAHA6eGpiX+Pg==", + "dev": true, "license": "MIT", "dependencies": { - "vfile": "^6.0.0", - "yaml": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "@jridgewell/sourcemap-codec": "^1.6.0" } }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" + "engines": { + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/web-namespaces": { @@ -10045,6 +11106,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index fad48db..2ca6a00 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,18 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint" + "lint": "eslint", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "format": "prettier --write .", + "format:check": "prettier --check .", + "check": "npm run lint && npm run typecheck && npm run format:check && npm test" }, "dependencies": { + "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "lucide-react": "^1.24.0", - "next": "16.2.10", + "next": "^16.3.5", "next-intl": "^4.13.1", "next-mdx-remote": "^6.0.0", "next-themes": "^0.4.6", @@ -27,12 +33,17 @@ "devDependencies": { "@tailwindcss/postcss": "^4", "@types/mdx": "^2.0.14", - "@types/node": "^20", + "@types/node": "^24.13.4", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "16.2.10", + "eslint-config-next": "^16.3.5", + "prettier": "^3.9.6", "tailwindcss": "^4", - "typescript": "^5" + "typescript": "^5", + "vitest": "^5.0.0" + }, + "engines": { + "node": ">=22.12" } } diff --git a/prettier.config.mjs b/prettier.config.mjs new file mode 100644 index 0000000..0ed3c4e --- /dev/null +++ b/prettier.config.mjs @@ -0,0 +1,6 @@ +/** Prettier defaults — double quotes, semicolons, trailing commas, 80 + * columns: the style the codebase was already written in. Run + * `npm run format`; CI runs `npm run format:check`. */ +const config = {}; + +export default config; diff --git a/public/file.svg b/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/omer-160.jpg b/public/omer-160.jpg new file mode 100644 index 0000000..8f4b019 Binary files /dev/null and b/public/omer-160.jpg differ diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/window.svg b/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/[locale]/about/page.tsx b/src/app/[locale]/about/page.tsx deleted file mode 100644 index 6e4e784..0000000 --- a/src/app/[locale]/about/page.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import type { Metadata } from "next"; -import { getTranslations, setRequestLocale } from "next-intl/server"; -import { ArrowRight } from "lucide-react"; -import { Container } from "@/components/layout/container"; -import { Portrait } from "@/components/about/portrait"; -import { buttonClass } from "@/components/ui/button"; -import { Link } from "@/i18n/navigation"; -import { alternatesFor } from "@/lib/seo"; - -export async function generateMetadata({ - params, -}: { - params: Promise<{ locale: string }>; -}): Promise { - const { locale } = await params; - const t = await getTranslations({ locale, namespace: "about" }); - return { - title: t("title"), - description: t("subtitle"), - alternates: alternatesFor("/about"), - }; -} - -export default async function AboutPage({ - params, -}: { - params: Promise<{ locale: string }>; -}) { - const { locale } = await params; - setRequestLocale(locale); - const t = await getTranslations("about"); - const nav = await getTranslations("nav"); - - return ( - -
-
- -
- -
-

- {t("title")} -

-

- {t("body")} -

- -
- - {nav("contact")} - - - - {nav("products")} - -
-
-
-
- ); -} diff --git a/src/app/[locale]/contact/page.tsx b/src/app/[locale]/contact/page.tsx deleted file mode 100644 index f43d136..0000000 --- a/src/app/[locale]/contact/page.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import type { Metadata } from "next"; -import { getTranslations, setRequestLocale } from "next-intl/server"; -import { Mail } from "lucide-react"; -import { Container } from "@/components/layout/container"; -import { ContactForm } from "@/components/contact/contact-form"; -import { GithubIcon, LinkedinIcon, XIcon } from "@/components/ui/brand-icons"; -import { site } from "@/config/site"; -import { alternatesFor } from "@/lib/seo"; - -export async function generateMetadata({ - params, -}: { - params: Promise<{ locale: string }>; -}): Promise { - const { locale } = await params; - const t = await getTranslations({ locale, namespace: "contact" }); - return { - title: t("title"), - description: t("subtitle"), - alternates: alternatesFor("/contact"), - }; -} - -export default async function ContactPage({ - params, -}: { - params: Promise<{ locale: string }>; -}) { - const { locale } = await params; - setRequestLocale(locale); - const t = await getTranslations("contact"); - - const links = [ - { href: site.links.email, label: site.email, Icon: Mail }, - { href: site.links.github, label: "GitHub", Icon: GithubIcon }, - { href: site.links.x, label: "X", Icon: XIcon }, - { href: site.links.linkedin, label: "LinkedIn", Icon: LinkedinIcon }, - ]; - - return ( - -
-
-
-

- {t("title")} -

-

- {t("subtitle")} -

-
- -
- - -
-
- ); -} diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx deleted file mode 100644 index 30cb6bf..0000000 --- a/src/app/[locale]/layout.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import type { Metadata } from "next"; -import { notFound } from "next/navigation"; -import { Geist, Geist_Mono } from "next/font/google"; -import { NextIntlClientProvider, hasLocale } from "next-intl"; -import { getTranslations, setRequestLocale } from "next-intl/server"; -import { routing, rtlLocales, type Locale } from "@/i18n/routing"; -import { ThemeProvider } from "@/components/theme/theme-provider"; -import { Header } from "@/components/layout/header"; -import { Footer } from "@/components/layout/footer"; -import { site } from "@/config/site"; -import "../globals.css"; - -const geist = Geist({ - subsets: ["latin"], - variable: "--font-geist", - display: "swap", -}); -const geistMono = Geist_Mono({ - subsets: ["latin"], - variable: "--font-geist-mono", - display: "swap", -}); - -export function generateStaticParams() { - return routing.locales.map((locale) => ({ locale })); -} - -export async function generateMetadata({ - params, -}: { - params: Promise<{ locale: string }>; -}): Promise { - const { locale } = await params; - const t = await getTranslations({ locale, namespace: "meta" }); - return { - metadataBase: new URL(site.url), - title: { default: t("title"), template: `%s · ${t("title")}` }, - description: t("description"), - openGraph: { - title: t("title"), - description: t("description"), - url: site.url, - siteName: site.name, - type: "website", - }, - twitter: { - card: "summary_large_image", - title: t("title"), - description: t("description"), - }, - alternates: { - types: { "application/rss+xml": `${site.url}/feed.xml` }, - }, - }; -} - -export default async function LocaleLayout({ - children, - params, -}: { - children: React.ReactNode; - params: Promise<{ locale: string }>; -}) { - const { locale } = await params; - if (!hasLocale(routing.locales, locale)) notFound(); - setRequestLocale(locale); - - const dir = rtlLocales.includes(locale as Locale) ? "rtl" : "ltr"; - - return ( - - - - -
-
-
{children}
-
-
-
-
- - - ); -} diff --git a/src/app/[locale]/not-found.tsx b/src/app/[locale]/not-found.tsx deleted file mode 100644 index 93540d7..0000000 --- a/src/app/[locale]/not-found.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { getTranslations } from "next-intl/server"; -import { Container } from "@/components/layout/container"; -import { LinkButton } from "@/components/ui/button"; - -export default async function LocaleNotFound() { - const t = await getTranslations("notFound"); - return ( - - 404 -
-

{t("title")}

-

{t("subtitle")}

-
- - {t("home")} - -
- ); -} diff --git a/src/app/[locale]/opengraph-image.tsx b/src/app/[locale]/opengraph-image.tsx deleted file mode 100644 index e10331d..0000000 --- a/src/app/[locale]/opengraph-image.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { ImageResponse } from "next/og"; -import { site } from "@/config/site"; - -export const alt = site.name; -export const size = { width: 1200, height: 630 }; -export const contentType = "image/png"; - -// Static social card. Colours are inlined (OG images can't read CSS variables); -// they mirror the light theme tokens — update if you re-skin the brand. -export default function OpenGraphImage() { - return new ImageResponse( - ( -
-
- omercelik.dev -
- -
-
- {site.name} -
-
- AI-driven, spec-driven architecture — libraries, platforms & products. -
-
- -
-
- Products · Writing · Contact -
-
- ), - size, - ); -} diff --git a/src/app/[locale]/products/page.tsx b/src/app/[locale]/products/page.tsx deleted file mode 100644 index ea5b63f..0000000 --- a/src/app/[locale]/products/page.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import type { Metadata } from "next"; -import { getTranslations, setRequestLocale } from "next-intl/server"; -import { Container } from "@/components/layout/container"; -import { ProductCard } from "@/components/products/product-card"; -import { Reveal } from "@/components/motion/reveal"; -import { getProducts } from "@/lib/github"; -import { alternatesFor } from "@/lib/seo"; - -export async function generateMetadata({ - params, -}: { - params: Promise<{ locale: string }>; -}): Promise { - const { locale } = await params; - const t = await getTranslations({ locale, namespace: "products" }); - return { - title: t("title"), - description: t("subtitle"), - alternates: alternatesFor("/products"), - }; -} - -export default async function ProductsPage({ - params, -}: { - params: Promise<{ locale: string }>; -}) { - const { locale } = await params; - setRequestLocale(locale); - const t = await getTranslations("products"); - const products = await getProducts(); - - return ( - -
-

- {t("title")} -

-

- {t("subtitle")} -

-
- - {products.length === 0 ? ( -

- {t("empty")} -

- ) : ( -
- {products.map((product) => ( - - - - ))} -
- )} -
- ); -} diff --git a/src/app/[locale]/writings/[slug]/opengraph-image.tsx b/src/app/[locale]/writings/[slug]/opengraph-image.tsx deleted file mode 100644 index abbc966..0000000 --- a/src/app/[locale]/writings/[slug]/opengraph-image.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { ImageResponse } from "next/og"; -import { getWritingBySlug, getWritingSlugs } from "@/lib/writings"; -import { site } from "@/config/site"; - -export const alt = "Writing — omercelik.dev"; -export const size = { width: 1200, height: 630 }; -export const contentType = "image/png"; - -export async function generateStaticParams() { - const slugs = await getWritingSlugs(); - return slugs.map((slug) => ({ slug })); -} - -// Per-article social card so shared links (LinkedIn, X) show the post title. -export default async function WritingOgImage({ - params, -}: { - params: Promise<{ slug: string }>; -}) { - const { slug } = await params; - const post = await getWritingBySlug(slug); - const title = post?.title ?? "Writing"; - const date = post - ? new Intl.DateTimeFormat("en", { year: "numeric", month: "long", day: "numeric" }).format( - new Date(post.date), - ) - : ""; - - return new ImageResponse( - ( -
-
- omercelik.dev -
- -
60 ? 56 : 68, - fontWeight: 600, - letterSpacing: "-0.03em", - color: "#18181b", - lineHeight: 1.08, - maxWidth: 1000, - }} - > - {title} -
- -
-
- Writing - {date && ·} - {date && {date}} - {site.name} -
-
- ), - size, - ); -} diff --git a/src/app/[locale]/writings/[slug]/page.tsx b/src/app/[locale]/writings/[slug]/page.tsx deleted file mode 100644 index d67bd7c..0000000 --- a/src/app/[locale]/writings/[slug]/page.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import type { Metadata } from "next"; -import { notFound } from "next/navigation"; -import { compileMDX } from "next-mdx-remote/rsc"; -import { getTranslations, setRequestLocale } from "next-intl/server"; -import { ArrowLeft, Clock, Layers } from "lucide-react"; -import remarkGfm from "remark-gfm"; -import rehypeSlug from "rehype-slug"; -import rehypeAutolinkHeadings from "rehype-autolink-headings"; -import rehypePrettyCode from "rehype-pretty-code"; -import { Container } from "@/components/layout/container"; -import { Chip, TagLink } from "@/components/ui/badge"; -import { Link } from "@/i18n/navigation"; -import { mdxComponents } from "@/components/writings/mdx-components"; -import { Comments } from "@/components/writings/comments"; -import { getSeriesPosts, getWritingBySlug, getWritingSlugs } from "@/lib/writings"; -import { alternatesFor } from "@/lib/seo"; - -export async function generateStaticParams() { - const slugs = await getWritingSlugs(); - return slugs.map((slug) => ({ slug })); -} - -export async function generateMetadata({ - params, -}: { - params: Promise<{ locale: string; slug: string }>; -}): Promise { - const { slug } = await params; - const post = await getWritingBySlug(slug); - if (!post) return {}; - return { - title: post.title, - description: post.description, - alternates: alternatesFor(`/writings/${slug}`), - openGraph: { title: post.title, description: post.description, type: "article" }, - }; -} - -export default async function WritingPage({ - params, -}: { - params: Promise<{ locale: string; slug: string }>; -}) { - const { locale, slug } = await params; - setRequestLocale(locale); - const t = await getTranslations("writings"); - const post = await getWritingBySlug(slug); - if (!post) notFound(); - - const dateLabel = new Intl.DateTimeFormat(locale, { - year: "numeric", - month: "long", - day: "numeric", - }).format(new Date(post.date)); - - const seriesPosts = post.series ? await getSeriesPosts(post.series) : []; - - const { content } = await compileMDX({ - source: post.content, - components: mdxComponents, - options: { - mdxOptions: { - remarkPlugins: [remarkGfm], - rehypePlugins: [ - rehypeSlug, - [ - rehypePrettyCode, - { - theme: { light: "github-light", dark: "github-dark" }, - keepBackground: false, - }, - ], - [rehypeAutolinkHeadings, { behavior: "wrap" }], - ], - }, - }, - }); - - return ( - -
- - - {t("backToList")} - - -
-

- {post.title} -

- {post.description && ( -

- {post.description} -

- )} -
- - · - - - {t("readingTime", { minutes: post.readingMinutes })} - - - {post.lang} - -
- {post.tags && post.tags.length > 0 && ( -
- {post.tags.map((tag) => ( - - ))} -
- )} -
- - {/* Series navigator */} - {post.series && seriesPosts.length > 1 && ( - - )} - -
- {content} -
- - -
-
- ); -} diff --git a/src/app/[locale]/writings/page.tsx b/src/app/[locale]/writings/page.tsx deleted file mode 100644 index 97efc57..0000000 --- a/src/app/[locale]/writings/page.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import type { Metadata } from "next"; -import { getTranslations, setRequestLocale } from "next-intl/server"; -import { Container } from "@/components/layout/container"; -import { PostRow } from "@/components/writings/post-row"; -import { TagLink } from "@/components/ui/badge"; -import { getAllTags, getAllWritings } from "@/lib/writings"; -import { alternatesFor } from "@/lib/seo"; - -export async function generateMetadata({ - params, -}: { - params: Promise<{ locale: string }>; -}): Promise { - const { locale } = await params; - const t = await getTranslations({ locale, namespace: "writings" }); - return { - title: t("title"), - description: t("subtitle"), - alternates: alternatesFor("/writings"), - }; -} - -export default async function WritingsPage({ - params, -}: { - params: Promise<{ locale: string }>; -}) { - const { locale } = await params; - setRequestLocale(locale); - const t = await getTranslations("writings"); - const [posts, tags] = await Promise.all([getAllWritings(), getAllTags()]); - - return ( - -
-

- {t("title")} -

-

- {t("subtitle")} -

-
- - {tags.length > 0 && ( -
- {tags.map(({ tag }) => ( - - ))} -
- )} - - {posts.length === 0 ? ( -

- {t("empty")} -

- ) : ( -
- {posts.map((post) => ( - - ))} -
- )} -
- ); -} diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx new file mode 100644 index 0000000..42ecd04 --- /dev/null +++ b/src/app/about/page.tsx @@ -0,0 +1,146 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { getTranslations } from "next-intl/server"; +import { ArrowRight, ArrowUpRight, Mail, Rss } from "lucide-react"; +import { Container } from "@/components/layout/container"; +import { Portrait } from "@/components/about/portrait"; +import { PAGE_PADDING } from "@/components/ui/page-header"; +import { Tag } from "@/components/ui/badge"; +import { buttonClass } from "@/components/ui/button"; +import { GithubIcon, LinkedinIcon, XIcon } from "@/components/ui/brand-icons"; +import { JsonLd } from "@/components/seo/json-ld"; +import { getFeaturedProducts } from "@/lib/github"; +import { pageMetadata, profileJsonLd } from "@/lib/seo"; +import { site } from "@/config/site"; + +export async function generateMetadata(): Promise { + const t = await getTranslations("about"); + return pageMetadata({ + path: "/about", + title: t("metaTitle"), + description: t("metaDescription"), + }); +} + +const SUBHEAD = + "mono mb-4 border-b border-border pb-3 text-ui font-normal text-muted-foreground"; + +export default async function AboutPage() { + const t = await getTranslations("about"); + const nav = await getTranslations("nav"); + const footer = await getTranslations("footer"); + const body = t.raw("body") as string[]; + const toolbox = t.raw("toolbox") as string[]; + // "Working on" is the featured product list — the same source as the home + // page, so it can never drift from what's actually shipped. + const products = await getFeaturedProducts(); + + const elsewhere = [ + { + href: site.links.github, + label: "GitHub", + Icon: GithubIcon, + external: true, + }, + { href: site.links.x, label: "X", Icon: XIcon, external: true }, + { + href: site.links.linkedin, + label: "LinkedIn", + Icon: LinkedinIcon, + external: true, + }, + { href: site.links.email, label: site.email, Icon: Mail, external: false }, + { href: "/feed.xml", label: footer("rss"), Icon: Rss, external: false }, + ]; + + return ( + + +
+ + +
+

{t("title")}

+
+ {body.map((paragraph) => ( +

+ {paragraph} +

+ ))} +
+ + {products.length > 0 && ( +
+

{t("nowTitle")}

+ +
+ )} + +
+

{t("toolboxTitle")}

+
+ {toolbox.map((item) => ( + {item} + ))} +
+
+ +
+ + {nav("contact")} + + + + {nav("writings")} + +
+
+
+
+ ); +} diff --git a/src/app/apple-icon.png b/src/app/apple-icon.png new file mode 100644 index 0000000..1d99c73 Binary files /dev/null and b/src/app/apple-icon.png differ diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx new file mode 100644 index 0000000..5ad5b3f --- /dev/null +++ b/src/app/contact/page.tsx @@ -0,0 +1,70 @@ +import type { Metadata } from "next"; +import { getTranslations } from "next-intl/server"; +import { Mail } from "lucide-react"; +import { Container } from "@/components/layout/container"; +import { ContactForm } from "@/components/contact/contact-form"; +import { PageHeader, PAGE_PADDING } from "@/components/ui/page-header"; +import { GithubIcon, LinkedinIcon, XIcon } from "@/components/ui/brand-icons"; +import { site } from "@/config/site"; +import { pageMetadata } from "@/lib/seo"; + +export async function generateMetadata(): Promise { + const t = await getTranslations("contact"); + return pageMetadata({ + path: "/contact", + title: t("title"), + description: t("subtitle"), + }); +} + +export default async function ContactPage() { + const t = await getTranslations("contact"); + + const links = [ + { href: site.links.email, label: site.email, Icon: Mail, external: false }, + { + href: site.links.github, + label: "GitHub", + Icon: GithubIcon, + external: true, + }, + { href: site.links.x, label: "X", Icon: XIcon, external: true }, + { + href: site.links.linkedin, + label: "LinkedIn", + Icon: LinkedinIcon, + external: true, + }, + ]; + + return ( + +
+
+ + +
+ + +
+
+ ); +} diff --git a/src/app/favicon.ico b/src/app/favicon.ico new file mode 100644 index 0000000..56f25d3 Binary files /dev/null and b/src/app/favicon.ico differ diff --git a/src/app/feed.xml/route.ts b/src/app/feed.xml/route.ts index 7f8c7fe..a07e4b3 100644 --- a/src/app/feed.xml/route.ts +++ b/src/app/feed.xml/route.ts @@ -1,5 +1,5 @@ import { site } from "@/config/site"; -import { localeUrl } from "@/lib/seo"; +import { absoluteUrl } from "@/lib/seo"; import { getAllWritings } from "@/lib/writings"; // Generated at build time (reads content from disk) so it works on runtimes @@ -20,7 +20,7 @@ export async function GET() { const items = writings .map((w) => { - const url = localeUrl("en", `/writings/${w.slug}`); + const url = absoluteUrl(`/writings/${w.slug}`); return ` ${escape(w.title)} ${url} @@ -36,8 +36,8 @@ export async function GET() { ${escape(site.name)} — Writing - ${site.url}/writings - Notes on software, systems and building things. + ${absoluteUrl("/writings")} + Essays and notes on architecture, systems and the craft of building software. en ${new Date(updated).toUTCString()} diff --git a/src/app/globals.css b/src/app/globals.css index 921420c..610f0b7 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -49,6 +49,44 @@ --font-sans: var(--font-sans); --font-mono: var(--font-mono); + --font-serif: var(--font-serif); +} + +/* Type scale — the only font sizes the UI uses (`text-meta` … `text-display`). + Small steps for interface chrome, a 17px reading size for articles, fluid + headings. Pick from here instead of reaching for text-[13px]. */ +@theme { + --text-meta: 11px; + --text-meta--line-height: 1.45; + --text-caption: 12px; + --text-caption--line-height: 1.5; + --text-ui: 13px; + --text-ui--line-height: 1.55; + --text-control: 14px; + --text-control--line-height: 1.4; + --text-body: 15px; + --text-body--line-height: 1.65; + /* form fields: 16px, or iOS zooms the page on focus */ + --text-field: 16px; + --text-field--line-height: 1.5; + --text-lead: 17px; + --text-lead--line-height: 1.6; + --text-prose: 17px; + --text-prose--line-height: 1.75; + --text-h3: 1.125rem; + --text-h3--line-height: 1.4; + --text-h2: 1.375rem; + --text-h2--line-height: 1.3; + --text-h2--letter-spacing: -0.015em; + --text-h1: clamp(2rem, 4vw, 2.75rem); + --text-h1--line-height: 1.1; + --text-h1--letter-spacing: -0.03em; + --text-quote: 1.625rem; + --text-quote--line-height: 1.35; + --text-quote--letter-spacing: -0.01em; + --text-display: clamp(2.25rem, 4.6vw, 3.4rem); + --text-display--line-height: 1.06; + --text-display--letter-spacing: -0.035em; } @layer base { @@ -65,8 +103,8 @@ background: var(--surface); color: var(--foreground); font-family: var(--font-sans); - font-size: 14px; - line-height: 1.5; + font-size: var(--text-body); + line-height: var(--text-body--line-height); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; } @@ -91,42 +129,110 @@ @media (prefers-reduced-motion: no-preference) { html, html * { - transition: background-color .2s ease, border-color .2s ease, color .2s ease; + transition: + background-color 0.2s ease, + border-color 0.2s ease, + color 0.2s ease; } } -/* ---- Motion ---- */ +/* ---- Motion ---- + Content is always visible by default. Motion is layered on top only where + the browser supports it and the visitor hasn't asked for less of it — no + JavaScript involved, so nothing waits on hydration to appear. */ @keyframes reveal-up { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } } @keyframes caret-blink { - 0%, 100% { opacity: 1; } - 50% { opacity: 0; } + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0; + } } /* dashes travelling along a connector path (flowing arrow) */ @keyframes dash-flow { - to { stroke-dashoffset: -28; } + to { + stroke-dashoffset: -28; + } } /* a left→right wave across pipeline stages */ @keyframes stage-pulse { - 0%, 100% { opacity: 0.3; } - 50% { opacity: 1; } + 0%, + 100% { + opacity: 0.3; + } + 50% { + opacity: 1; + } } /* soft pulse ring on the active/running node */ @keyframes node-ring { - 0% { transform: scale(1); opacity: 0.5; } - 100% { transform: scale(2.2); opacity: 0; } -} -.reveal { - opacity: 0; + 0% { + transform: scale(1); + opacity: 0.5; + } + 100% { + transform: scale(2.2); + opacity: 0; + } } -.reveal.in { - animation: reveal-up 0.6s cubic-bezier(0.22, 1, 0.36, 1) forwards; +@media (prefers-reduced-motion: no-preference) { + /* Page-load entrance for above-the-fold content. */ + .intro { + animation: reveal-up 0.7s cubic-bezier(0.22, 1, 0.36, 1) both; + } + /* Scroll-linked entrance, driven by the element's own visibility. Browsers + without scroll-driven animations simply show the content as-is. */ + @supports (animation-timeline: view()) { + .reveal { + animation: reveal-up linear both; + animation-timeline: view(); + animation-range: entry 0% entry 40%; + } + } } @media (prefers-reduced-motion: reduce) { - .reveal, .reveal.in { opacity: 1; animation: none; transform: none; } - html { scroll-behavior: auto; } + html { + scroll-behavior: auto; + } +} + +/* Article reading progress: a hairline across the top, driven by the page's + own scroll position. Hidden where scroll-driven animations aren't there. */ +.reading-progress { + position: fixed; + inset-inline: 0; + top: 0; + z-index: 50; + display: none; + height: 2px; + background: var(--brand-accent); + transform: scaleX(0); + transform-origin: 0 50%; +} +@supports (animation-timeline: scroll()) { + @media (prefers-reduced-motion: no-preference) { + .reading-progress { + display: block; + animation: reading-progress linear both; + animation-timeline: scroll(root); + } + } +} +@keyframes reading-progress { + to { + transform: scaleX(1); + } } .caret { display: inline-block; @@ -148,23 +254,68 @@ code[data-theme] span { :root[data-theme="dark"] code[data-theme] span { color: var(--shiki-dark); } -code[data-line] { +/* Block code: figure > (figcaption title) > .code-block > pre > code > [data-line]. + Lines are full-width rows so highlighted lines paint edge to edge. */ +pre > code[data-language] { + display: grid; +} +pre [data-line] { + padding: 0 1rem; +} +[data-highlighted-line] { + background: var(--brand-accent-soft); + box-shadow: inset 2px 0 0 var(--brand-accent); +} +[data-highlighted-chars] { + border-radius: 3px; + background: var(--brand-accent-soft); + padding: 0 2px; +} +code[data-line-numbers] { + counter-reset: line; +} +code[data-line-numbers] > [data-line]::before { + counter-increment: line; + content: counter(line); display: inline-block; - width: 100%; + width: 1.25rem; + margin-inline-end: 1rem; + text-align: right; + color: var(--faint); } figcaption[data-rehype-pretty-code-title] { - margin-top: 1.25rem; + margin-top: 1.5rem; border: 1px solid var(--border); border-bottom: 0; border-radius: var(--radius-xl) var(--radius-xl) 0 0; background: var(--muted); padding: 8px 14px; font-family: var(--font-mono); - font-size: 12px; + font-size: var(--text-caption); color: var(--muted-foreground); } -figcaption[data-rehype-pretty-code-title] + pre { +figcaption[data-rehype-pretty-code-title] + .code-block { margin-top: 0; +} +figcaption[data-rehype-pretty-code-title] + .code-block pre { border-top-left-radius: 0; border-top-right-radius: 0; } + +/* GFM footnotes (remark-gfm renders section.footnotes at the end). */ +.footnotes { + margin-top: 3rem; + border-top: 1px solid var(--border); + padding-top: 1.25rem; +} +.footnotes :is(ol, li, p) { + font-size: var(--text-ui); + line-height: 1.6; + color: var(--muted-foreground); +} +[data-footnote-ref] { + margin-inline-start: 2px; + font-family: var(--font-mono); + font-size: var(--text-meta); + text-decoration: none; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..d5aeae1 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,132 @@ +import type { Metadata, Viewport } from "next"; +import { Geist, Geist_Mono, Newsreader } from "next/font/google"; +import { NextIntlClientProvider } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { ThemeProvider } from "@/components/theme/theme-provider"; +import { Header } from "@/components/layout/header"; +import { Footer } from "@/components/layout/footer"; +import { JsonLd } from "@/components/seo/json-ld"; +import { site } from "@/config/site"; +import { absoluteUrl, SITE_OG_IMAGE, siteJsonLd, X_HANDLE } from "@/lib/seo"; +import "./globals.css"; + +const geist = Geist({ + subsets: ["latin"], + variable: "--font-geist", + display: "swap", +}); +const geistMono = Geist_Mono({ + subsets: ["latin"], + variable: "--font-geist-mono", + display: "swap", +}); +// Essay titles and pull quotes: an editorial serif next to the Geist UI. +// latin-ext covers the Turkish letters in Turkish essays. +const newsreader = Newsreader({ + subsets: ["latin", "latin-ext"], + variable: "--font-newsreader", + style: ["normal", "italic"], + axes: ["opsz"], + display: "swap", +}); + +export async function generateMetadata(): Promise { + const t = await getTranslations("meta"); + // Site-wide defaults. Every page sets its own title, canonical and social + // cards on top (lib/seo), replacing these — Next merges only shallowly. + return { + metadataBase: new URL(site.url), + title: { default: t("homeTitle"), template: `%s · ${site.name}` }, + description: t("description"), + applicationName: site.name, + authors: [{ name: site.name, url: absoluteUrl("/about") }], + creator: site.name, + publisher: site.name, + robots: { + index: true, + follow: true, + googleBot: { + index: true, + follow: true, + "max-image-preview": "large", + "max-snippet": -1, + "max-video-preview": -1, + }, + }, + openGraph: { + type: "website", + siteName: site.name, + title: t("homeTitle"), + description: t("description"), + locale: "en_US", + images: [ + { url: SITE_OG_IMAGE, width: 1200, height: 630, alt: site.name }, + ], + }, + twitter: { + card: "summary_large_image", + site: X_HANDLE, + creator: X_HANDLE, + images: [SITE_OG_IMAGE], + }, + alternates: { + types: { "application/rss+xml": `${site.url}/feed.xml` }, + }, + }; +} + +export const viewport: Viewport = { + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "#ffffff" }, + { media: "(prefers-color-scheme: dark)", color: "#18181b" }, + ], +}; + +export default async function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + const nav = await getTranslations("nav"); + const home = await getTranslations("home"); + const meta = await getTranslations("meta"); + const about = await getTranslations("about"); + + return ( + + + + {nav("skip")} + + + + +
+
+
+ {children} +
+
+
+
+
+ + + ); +} diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index b15fe7f..38c497b 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -1,25 +1,31 @@ -import "./globals.css"; +import type { Metadata } from "next"; +import { getTranslations } from "next-intl/server"; +import { Container } from "@/components/layout/container"; +import { LinkButton } from "@/components/ui/button"; -/** Global fallback for routes that never resolved to a locale. Has its own - * because no root layout wraps it (the root layout lives in [locale]). */ -export default function GlobalNotFound() { +export async function generateMetadata(): Promise { + const t = await getTranslations("notFound"); + // Replaces the layout's "index, follow", which would contradict the + // noindex Next adds to the 404 by itself. + return { title: t("title"), robots: { index: false, follow: true } }; +} + +/** Unknown paths. The static export writes this as out/404.html, which the + * host serves for anything it can't find. */ +export default async function NotFound() { + const t = await getTranslations("notFound"); return ( - - -
- 404 -

Page not found

-

- The page you are looking for does not exist or has moved. -

- - Back home - -
- - + + 404 +
+

{t("title")}

+

+ {t("subtitle")} +

+
+ + {t("home")} + +
); } diff --git a/src/app/og.png/route.tsx b/src/app/og.png/route.tsx new file mode 100644 index 0000000..2f6856c --- /dev/null +++ b/src/app/og.png/route.tsx @@ -0,0 +1,18 @@ +import { ImageResponse } from "next/og"; +import { OgCard, OG_SIZE } from "@/lib/og-card"; +import { site } from "@/config/site"; + +// The site's social card, written to out/og.png at build time. +export const dynamic = "force-static"; + +export function GET() { + return new ImageResponse( + , + OG_SIZE, + ); +} diff --git a/src/app/og/[slug]/card.png/route.tsx b/src/app/og/[slug]/card.png/route.tsx new file mode 100644 index 0000000..831018f --- /dev/null +++ b/src/app/og/[slug]/card.png/route.tsx @@ -0,0 +1,41 @@ +import { ImageResponse } from "next/og"; +import { OgCard, OG_SIZE } from "@/lib/og-card"; +import { getWritingBySlug, getWritingSlugs } from "@/lib/writings"; +import { site } from "@/config/site"; + +// One social card per article (out/og//card.png), in the language the +// article is written in. Shared by both UI locales, like the article's +// canonical URL. +export const dynamic = "force-static"; +export const dynamicParams = false; + +export async function generateStaticParams() { + const slugs = await getWritingSlugs(); + return slugs.map((slug) => ({ slug })); +} + +export async function GET( + _request: Request, + { params }: { params: Promise<{ slug: string }> }, +) { + const { slug } = await params; + const post = await getWritingBySlug(slug); + const lang = post?.lang === "tr" ? "tr" : "en"; + const date = post + ? new Intl.DateTimeFormat(lang, { + year: "numeric", + month: "long", + day: "numeric", + }).format(new Date(post.date)) + : ""; + + return new ImageResponse( + , + OG_SIZE, + ); +} diff --git a/src/app/[locale]/page.tsx b/src/app/page.tsx similarity index 62% rename from src/app/[locale]/page.tsx rename to src/app/page.tsx index 7a98224..b1291a1 100644 --- a/src/app/[locale]/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,5 @@ import type { Metadata } from "next"; -import { getTranslations, setRequestLocale } from "next-intl/server"; +import { getTranslations } from "next-intl/server"; import { Container } from "@/components/layout/container"; import { SectionHead } from "@/components/ui/section"; import { Hero } from "@/components/home/hero"; @@ -7,28 +7,32 @@ import { Capabilities } from "@/components/home/capabilities"; import { CtaBand } from "@/components/home/cta-band"; import { ProductCard } from "@/components/products/product-card"; import { PostRow } from "@/components/writings/post-row"; +import { FeaturedPost } from "@/components/writings/featured-post"; import { Reveal } from "@/components/motion/reveal"; import { getFeaturedProducts } from "@/lib/github"; import { getLatestWritings } from "@/lib/writings"; -import { alternatesFor } from "@/lib/seo"; +import { pageMetadata } from "@/lib/seo"; -export function generateMetadata(): Metadata { - return { alternates: alternatesFor("/") }; +export async function generateMetadata(): Promise { + const t = await getTranslations("meta"); + return pageMetadata({ + path: "/", + title: t("homeTitle"), + description: t("description"), + absoluteTitle: true, + }); } -export default async function HomePage({ - params, -}: { - params: Promise<{ locale: string }>; -}) { - const { locale } = await params; - setRequestLocale(locale); +export default async function HomePage() { const t = await getTranslations("home"); - const [products, writings] = await Promise.all([ + const [products, latest] = await Promise.all([ getFeaturedProducts(), - getLatestWritings(4), + getLatestWritings(5), ]); + // A featured essay leads the section; the list below skips it. + const featured = latest.find((post) => post.featured) ?? null; + const writings = latest.filter((post) => post !== featured).slice(0, 4); return ( <> @@ -36,7 +40,7 @@ export default async function HomePage({ {/* 01 — what I do */} -
+
@@ -45,7 +49,7 @@ export default async function HomePage({ {/* 02 — products */} {products.length > 0 && ( -
+
0 && ( -
+ {(featured || writings.length > 0) && ( +
+ {featured && ( + + + + )}
{writings.map((post) => ( - + + + ))}
diff --git a/src/app/products/page.tsx b/src/app/products/page.tsx new file mode 100644 index 0000000..5cdae30 --- /dev/null +++ b/src/app/products/page.tsx @@ -0,0 +1,42 @@ +import type { Metadata } from "next"; +import { getTranslations } from "next-intl/server"; +import { Container } from "@/components/layout/container"; +import { PageHeader, PAGE_PADDING } from "@/components/ui/page-header"; +import { ProductCard } from "@/components/products/product-card"; +import { Reveal } from "@/components/motion/reveal"; +import { getProducts } from "@/lib/github"; +import { pageMetadata } from "@/lib/seo"; + +export async function generateMetadata(): Promise { + const t = await getTranslations("products"); + return pageMetadata({ + path: "/products", + title: t("title"), + description: t("subtitle"), + }); +} + +export default async function ProductsPage() { + const t = await getTranslations("products"); + const products = await getProducts(); + + return ( + + + + {products.length === 0 ? ( +

+ {t("empty")} +

+ ) : ( +
+ {products.map((product) => ( + + + + ))} +
+ )} +
+ ); +} diff --git a/src/app/robots.ts b/src/app/robots.ts index c5f80be..08db9ef 100644 --- a/src/app/robots.ts +++ b/src/app/robots.ts @@ -1,6 +1,9 @@ import type { MetadataRoute } from "next"; import { site } from "@/config/site"; +// Emitted as a file at build time, like feed.xml — no request-time work. +export const dynamic = "force-static"; + export default function robots(): MetadataRoute.Robots { return { rules: { userAgent: "*", allow: "/" }, diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index 404ade1..fce8822 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -1,21 +1,19 @@ import type { MetadataRoute } from "next"; -import { routing } from "@/i18n/routing"; -import { localeUrl } from "@/lib/seo"; +import { absoluteUrl } from "@/lib/seo"; import { getAllWritings } from "@/lib/writings"; -const STATIC_PATHS = ["/", "/products", "/writings", "/about"]; +// Emitted as a file at build time, like feed.xml — no request-time work. +export const dynamic = "force-static"; + +const STATIC_PATHS = ["/", "/writings", "/products", "/about", "/contact"]; export default async function sitemap(): Promise { const writings = await getAllWritings(); - const paths = [...STATIC_PATHS, ...writings.map((w) => `/writings/${w.slug}`)]; - - return paths.map((path) => ({ - url: localeUrl(routing.defaultLocale, path), - lastModified: new Date(), - alternates: { - languages: Object.fromEntries( - routing.locales.map((locale) => [locale, localeUrl(locale, path)]), - ), - }, - })); + return [ + ...STATIC_PATHS.map((path) => ({ url: absoluteUrl(path) })), + ...writings.map((w) => ({ + url: absoluteUrl(`/writings/${w.slug}`), + lastModified: new Date(w.updated ?? w.date), + })), + ]; } diff --git a/src/app/theme.css b/src/app/theme.css index 9971f35..95e0c2e 100644 --- a/src/app/theme.css +++ b/src/app/theme.css @@ -15,7 +15,10 @@ --foreground: oklch(0.141 0.005 285.823); --muted: oklch(0.967 0.001 286.375); --muted-foreground: oklch(0.552 0.016 285.938); - --faint: oklch(0.705 0.015 286.067); + /* faint still carries text (labels, indices, dates): 4.5:1 on white, the + WCAG AA minimum. Hierarchy against muted-foreground comes from size and + the mono face, not from a paler grey. */ + --faint: oklch(0.565 0.015 286.067); --border: oklch(0.92 0.004 286.32); --border-strong: oklch(0.87 0.005 286.2); --input: oklch(0.92 0.004 286.32); @@ -28,11 +31,21 @@ --brand-accent-soft: oklch(0.55 0.2 264 / 0.1); /* semantic status (kept for method/status chips; rarely used) */ - --success: oklch(0.55 0.14 152); --success-bg: oklch(0.96 0.03 152); --success-border: oklch(0.88 0.06 152); - --warning: oklch(0.62 0.13 70); --warning-bg: oklch(0.97 0.04 85); --warning-border: oklch(0.87 0.07 80); - --danger: oklch(0.55 0.2 27); --danger-bg: oklch(0.96 0.03 27); --danger-border: oklch(0.88 0.07 27); - --info: oklch(0.55 0.2 264); --info-bg: oklch(0.96 0.03 264); --info-border: oklch(0.88 0.06 264); - --violet: oklch(0.51 0.23 277); --violet-bg: oklch(0.96 0.03 277); --violet-border: oklch(0.88 0.06 277); + --success: oklch(0.55 0.14 152); + --success-bg: oklch(0.96 0.03 152); + --success-border: oklch(0.88 0.06 152); + --warning: oklch(0.62 0.13 70); + --warning-bg: oklch(0.97 0.04 85); + --warning-border: oklch(0.87 0.07 80); + --danger: oklch(0.55 0.2 27); + --danger-bg: oklch(0.96 0.03 27); + --danger-border: oklch(0.88 0.07 27); + --info: oklch(0.55 0.2 264); + --info-bg: oklch(0.96 0.03 264); + --info-border: oklch(0.88 0.06 264); + --violet: oklch(0.51 0.23 277); + --violet-bg: oklch(0.96 0.03 277); + --violet-border: oklch(0.88 0.06 277); /* geometry */ --radius-sm: 6px; @@ -40,15 +53,21 @@ --radius-lg: 10px; --radius-xl: 12px; --radius-2xl: 16px; - --hairline: 0.75px; /* elevation — none on the surface; a soft one only for floating popovers */ --shadow-surface: none; - --shadow-overlay: 0 8px 30px -8px oklch(0.141 0.005 285 / 0.14), 0 2px 8px -3px oklch(0.141 0.005 285 / 0.08); + --shadow-overlay: + 0 8px 30px -8px oklch(0.141 0.005 285 / 0.14), + 0 2px 8px -3px oklch(0.141 0.005 285 / 0.08); /* typography */ - --font-sans: var(--font-geist), ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; - --font-mono: var(--font-geist-mono), ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace; + --font-sans: + var(--font-geist), ui-sans-serif, system-ui, -apple-system, "Segoe UI", + sans-serif; + --font-mono: + var(--font-geist-mono), ui-monospace, "SF Mono", "JetBrains Mono", Menlo, + monospace; + --font-serif: var(--font-newsreader), "Iowan Old Style", Georgia, serif; } :root[data-theme="dark"] { @@ -58,7 +77,7 @@ --foreground: oklch(0.985 0 0); --muted: oklch(0.274 0.006 286.033); --muted-foreground: oklch(0.705 0.015 286.067); - --faint: oklch(0.55 0.014 286); + --faint: oklch(0.62 0.014 286); --border: oklch(1 0 0 / 0.1); --border-strong: oklch(1 0 0 / 0.18); --input: oklch(1 0 0 / 0.12); @@ -69,13 +88,24 @@ --brand-accent: oklch(0.72 0.16 262); --brand-accent-soft: oklch(0.72 0.16 262 / 0.14); - --success: oklch(0.72 0.17 152); --success-bg: oklch(0.27 0.05 152); --success-border: oklch(0.4 0.08 152); - --warning: oklch(0.8 0.14 85); --warning-bg: oklch(0.28 0.05 75); --warning-border: oklch(0.42 0.08 75); - --danger: oklch(0.7 0.18 25); --danger-bg: oklch(0.27 0.07 27); --danger-border: oklch(0.42 0.1 27); - --info: oklch(0.72 0.16 262); --info-bg: oklch(0.27 0.06 264); --info-border: oklch(0.42 0.09 264); - --violet: oklch(0.72 0.16 277); --violet-bg: oklch(0.27 0.06 277); --violet-border: oklch(0.42 0.09 277); + --success: oklch(0.72 0.17 152); + --success-bg: oklch(0.27 0.05 152); + --success-border: oklch(0.4 0.08 152); + --warning: oklch(0.8 0.14 85); + --warning-bg: oklch(0.28 0.05 75); + --warning-border: oklch(0.42 0.08 75); + --danger: oklch(0.7 0.18 25); + --danger-bg: oklch(0.27 0.07 27); + --danger-border: oklch(0.42 0.1 27); + --info: oklch(0.72 0.16 262); + --info-bg: oklch(0.27 0.06 264); + --info-border: oklch(0.42 0.09 264); + --violet: oklch(0.72 0.16 277); + --violet-bg: oklch(0.27 0.06 277); + --violet-border: oklch(0.42 0.09 277); - --shadow-overlay: 0 8px 30px -8px oklch(0 0 0 / 0.5), 0 2px 8px -3px oklch(0 0 0 / 0.4); + --shadow-overlay: + 0 8px 30px -8px oklch(0 0 0 / 0.5), 0 2px 8px -3px oklch(0 0 0 / 0.4); } @media (prefers-color-scheme: dark) { @@ -86,7 +116,7 @@ --foreground: oklch(0.985 0 0); --muted: oklch(0.274 0.006 286.033); --muted-foreground: oklch(0.705 0.015 286.067); - --faint: oklch(0.55 0.014 286); + --faint: oklch(0.62 0.014 286); --border: oklch(1 0 0 / 0.1); --border-strong: oklch(1 0 0 / 0.18); --input: oklch(1 0 0 / 0.12); diff --git a/src/app/writings/[slug]/page.tsx b/src/app/writings/[slug]/page.tsx new file mode 100644 index 0000000..54c54cb --- /dev/null +++ b/src/app/writings/[slug]/page.tsx @@ -0,0 +1,270 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { compileMDX } from "next-mdx-remote/rsc"; +import { getTranslations } from "next-intl/server"; +import { ArrowLeft, ArrowRight, Clock, Layers } from "lucide-react"; +import remarkGfm from "remark-gfm"; +import rehypeSlug from "rehype-slug"; +import rehypeAutolinkHeadings from "rehype-autolink-headings"; +import rehypePrettyCode from "rehype-pretty-code"; +import { PAGE_PADDING } from "@/components/ui/page-header"; +import { Label, TagLink } from "@/components/ui/badge"; +import { mdxComponents } from "@/components/writings/mdx-components"; +import { Comments } from "@/components/writings/comments"; +import { Toc } from "@/components/writings/toc"; +import { AuthorCard } from "@/components/writings/author-card"; +import { JsonLd } from "@/components/seo/json-ld"; +import { + extractHeadings, + getAdjacentWritings, + getSeriesPosts, + getWritingBySlug, + getWritingSlugs, + type WritingMeta, +} from "@/lib/writings"; +import { articleJsonLd, articleMetadata } from "@/lib/seo"; + +/** A table of contents earns its space from three sections up. */ +const TOC_MIN_HEADINGS = 3; + +export async function generateStaticParams() { + const slugs = await getWritingSlugs(); + return slugs.map((slug) => ({ slug })); +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + const post = await getWritingBySlug(slug); + if (!post) return {}; + return articleMetadata(post); +} + +export default async function WritingPage({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + const t = await getTranslations("writings"); + const post = await getWritingBySlug(slug); + if (!post) notFound(); + + const dateLabel = new Intl.DateTimeFormat("en", { + year: "numeric", + month: "long", + day: "numeric", + }).format(new Date(post.date)); + + const [seriesPosts, adjacent] = await Promise.all([ + post.series ? getSeriesPosts(post.series) : Promise.resolve([]), + getAdjacentWritings(slug), + ]); + const headings = extractHeadings(post.content); + const showToc = headings.length >= TOC_MIN_HEADINGS; + + const { content } = await compileMDX({ + source: post.content, + components: mdxComponents, + options: { + mdxOptions: { + remarkPlugins: [remarkGfm], + rehypePlugins: [ + rehypeSlug, + [ + rehypePrettyCode, + { + theme: { light: "github-light", dark: "github-dark" }, + keepBackground: false, + // Fences without a language still get the block treatment. + defaultLang: "plaintext", + }, + ], + [ + rehypeAutolinkHeadings, + { + behavior: "append", + properties: { + className: ["heading-anchor"], + ariaHidden: true, + tabIndex: -1, + }, + content: { type: "text", value: "#" }, + }, + ], + ], + }, + }, + }); + + return ( + // Wider than the site's 1080px column: the reading column sits centred in + // the middle track, the table of contents in the right-hand margin. +
+
+ + +
+
+ + + {t("backToList")} + + +
+ {/* The interface is English; the piece is in its own language. */} +

+ {post.title} +

+ {post.description && ( +

+ {post.description} +

+ )} +
+ + · + + + {t("readingTime", { minutes: post.readingMinutes })} + + +
+ {post.tags && post.tags.length > 0 && ( +
+ {post.tags.map((tag) => ( + + ))} +
+ )} +
+ + {post.series && seriesPosts.length > 1 && ( + + )} + + {showToc && ( +
+ + {t("toc")} + + +
+ )} + +
+ {content} +
+ + + + +
+ + {showToc && ( + + )} +
+
+ ); +} + +async function PostNav({ + newer, + older, +}: { + newer: WritingMeta | null; + older: WritingMeta | null; +}) { + if (!newer && !older) return null; + const t = await getTranslations("writings"); + const card = + "group flex flex-col gap-1.5 rounded-[var(--radius-xl)] border border-border p-4 transition-colors hover:border-foreground/35"; + + return ( + + ); +} diff --git a/src/app/writings/page.tsx b/src/app/writings/page.tsx new file mode 100644 index 0000000..f919d6d --- /dev/null +++ b/src/app/writings/page.tsx @@ -0,0 +1,54 @@ +import type { Metadata } from "next"; +import { getTranslations } from "next-intl/server"; +import { Rss } from "lucide-react"; +import { Container } from "@/components/layout/container"; +import { PageHeader, PAGE_PADDING } from "@/components/ui/page-header"; +import { PostRow } from "@/components/writings/post-row"; +import { TagLink } from "@/components/ui/badge"; +import { getAllTags, getAllWritings } from "@/lib/writings"; +import { pageMetadata } from "@/lib/seo"; + +export async function generateMetadata(): Promise { + const t = await getTranslations("writings"); + return pageMetadata({ + path: "/writings", + title: t("title"), + description: t("subtitle"), + }); +} + +export default async function WritingsPage() { + const t = await getTranslations("writings"); + const [posts, tags] = await Promise.all([getAllWritings(), getAllTags()]); + + return ( + + +
+ {tags.map(({ tag }) => ( + + ))} + + + {t("rss")} + +
+
+ + {posts.length === 0 ? ( +

+ {t("empty")} +

+ ) : ( +
+ {posts.map((post) => ( + + ))} +
+ )} +
+ ); +} diff --git a/src/app/[locale]/writings/tag/[tag]/page.tsx b/src/app/writings/tag/[tag]/page.tsx similarity index 53% rename from src/app/[locale]/writings/tag/[tag]/page.tsx rename to src/app/writings/tag/[tag]/page.tsx index dcc95b5..f633ffb 100644 --- a/src/app/[locale]/writings/tag/[tag]/page.tsx +++ b/src/app/writings/tag/[tag]/page.tsx @@ -1,12 +1,13 @@ import type { Metadata } from "next"; +import Link from "next/link"; import { notFound } from "next/navigation"; -import { getTranslations, setRequestLocale } from "next-intl/server"; +import { getTranslations } from "next-intl/server"; import { ArrowLeft } from "lucide-react"; import { Container } from "@/components/layout/container"; +import { PageHeader, PAGE_PADDING } from "@/components/ui/page-header"; import { PostRow } from "@/components/writings/post-row"; -import { Link } from "@/i18n/navigation"; import { getAllTags, getWritingsByTag } from "@/lib/writings"; -import { alternatesFor } from "@/lib/seo"; +import { pageMetadata } from "@/lib/seo"; export async function generateStaticParams() { const tags = await getAllTags(); @@ -21,48 +22,53 @@ async function tagName(slug: string): Promise { export async function generateMetadata({ params, }: { - params: Promise<{ locale: string; tag: string }>; + params: Promise<{ tag: string }>; }): Promise { - const { locale, tag } = await params; + const { tag } = await params; const name = await tagName(tag); - const t = await getTranslations({ locale, namespace: "writings" }); + const t = await getTranslations("writings"); if (!name) return {}; - return { + // Tag pages are thin lists of posts that live elsewhere: out of the index, + // but crawlable, so they still lead search engines to the articles. + return pageMetadata({ + path: `/writings/tag/${tag}`, title: `${t("tagged")}: ${name}`, - alternates: alternatesFor(`/writings/tag/${tag}`), - }; + description: t("subtitle"), + noindex: true, + }); } export default async function TagPage({ params, }: { - params: Promise<{ locale: string; tag: string }>; + params: Promise<{ tag: string }>; }) { - const { locale, tag } = await params; - setRequestLocale(locale); + const { tag } = await params; const t = await getTranslations("writings"); const name = await tagName(tag); if (!name) notFound(); const posts = await getWritingsByTag(tag); return ( - + - + {t("backToList")} -
- - {t("tagged")} · {posts.length} - -

- {name} -

-
+
+ + {t("tagged")} · {posts.length} + + } + title={name} + /> +
{posts.map((post) => ( diff --git a/src/components/about/portrait.tsx b/src/components/about/portrait.tsx index ad93ba8..724e631 100644 --- a/src/components/about/portrait.tsx +++ b/src/components/about/portrait.tsx @@ -9,7 +9,7 @@ export function Portrait({ alt }: { alt: string }) { const [failed, setFailed] = useState(false); return ( -
+
{!failed ? ( // eslint-disable-next-line @next/next/no-img-element ) : (
- ÖÇ + ÖÇ
)}
diff --git a/src/components/contact/contact-form.tsx b/src/components/contact/contact-form.tsx index a7de354..a46f3bc 100644 --- a/src/components/contact/contact-form.tsx +++ b/src/components/contact/contact-form.tsx @@ -7,79 +7,64 @@ import { buttonClass } from "@/components/ui/button"; import { site } from "@/config/site"; const inputClass = - "w-full rounded-[var(--radius-lg)] border border-input bg-background px-3.5 py-2.5 text-sm text-foreground outline-none transition-colors placeholder:text-faint focus:border-brand-accent"; + "w-full rounded-[var(--radius-lg)] border border-input bg-background px-3.5 py-2.5 text-field text-foreground outline-none transition-colors placeholder:text-faint focus:border-brand-accent"; -function mailtoHref(name: string, email: string, message: string) { - const subject = `Message from ${name}`.trim(); - const body = `${message}\n\n— ${name}${email ? ` <${email}>` : ""}`; - return `${site.links.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`; -} - -/** No backend, no dependency: composing a message opens the visitor's mail - * client pre-filled. Swap for a real API route + email service later if wanted. */ +/** No backend, no dependency, and honest about it: submitting opens the + * visitor's own mail app with the message filled in. The reply address is + * whatever account they send from, so the form doesn't ask for it. */ export function ContactForm() { const t = useTranslations("contact"); function onSubmit(e: FormEvent) { e.preventDefault(); - const data = Object.fromEntries( - new FormData(e.currentTarget), - ) as Record; - window.location.href = mailtoHref( - data.name ?? "", - data.email ?? "", - data.message ?? "", + const data = new FormData(e.currentTarget); + const name = String(data.get("name") ?? "").trim(); + const message = String(data.get("message") ?? ""); + const subject = encodeURIComponent(t("subject", { name })); + const body = encodeURIComponent(`${message}\n\n— ${name}`); + // A literal mailto: so it reads (to people and to Next's lint) as the + // external link it is, not an in-app navigation. + window.location.assign( + `mailto:${site.email}?subject=${subject}&body=${body}`, ); } return (
-
- - -
+