From 18e68bb17641a2b944dc52b3070da37978d1aa30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Tue, 7 Apr 2026 13:49:52 +0200 Subject: [PATCH] Add public changelog with Atom feed and What's New indicator - Changelog entries authored as markdown files in apps/journal/changelog/ with YAML frontmatter, loaded via Vite import.meta.glob at build time - /changelog list page with date, title, and preview - /changelog/:slug detail page with full markdown rendering (react-markdown) and Open Graph meta tags for social sharing - /changelog/feed.xml Atom feed with auto-discovery tags - "What's New" dot on nav bar using localStorage timestamp tracking - Notification toggle: users can permanently disable/re-enable the indicator - i18n strings for en and de - Initial changelog entry covering the Phase 1 launch Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/components/ChangelogIndicator.tsx | 78 ++ apps/journal/app/lib/changelog.server.ts | 73 ++ apps/journal/app/root.tsx | 9 +- apps/journal/app/routes.ts | 3 + apps/journal/app/routes/changelog.$slug.tsx | 50 ++ apps/journal/app/routes/changelog._index.tsx | 58 ++ .../app/routes/changelog.feed[.xml].ts | 51 ++ apps/journal/changelog/2026-03-25.md | 34 + openspec/changes/changelog/design.md | 21 +- openspec/changes/changelog/proposal.md | 6 +- .../changes/changelog/specs/changelog/spec.md | 21 + openspec/changes/changelog/tasks.md | 48 +- package.json | 1 + packages/i18n/src/locales/de.ts | 7 + packages/i18n/src/locales/en.ts | 7 + pnpm-lock.yaml | 667 ++++++++++++++++++ 16 files changed, 1109 insertions(+), 25 deletions(-) create mode 100644 apps/journal/app/components/ChangelogIndicator.tsx create mode 100644 apps/journal/app/lib/changelog.server.ts create mode 100644 apps/journal/app/routes/changelog.$slug.tsx create mode 100644 apps/journal/app/routes/changelog._index.tsx create mode 100644 apps/journal/app/routes/changelog.feed[.xml].ts create mode 100644 apps/journal/changelog/2026-03-25.md diff --git a/apps/journal/app/components/ChangelogIndicator.tsx b/apps/journal/app/components/ChangelogIndicator.tsx new file mode 100644 index 0000000..c0ac4f4 --- /dev/null +++ b/apps/journal/app/components/ChangelogIndicator.tsx @@ -0,0 +1,78 @@ +import { useEffect, useState } from "react"; +import { Link, useLocation } from "react-router"; +import { useTranslation } from "react-i18next"; + +const LAST_SEEN_KEY = "changelog:lastSeen"; +const DISABLED_KEY = "changelog:disabled"; + +export function ChangelogLink({ + newestDate, + className, +}: { + newestDate: string | null; + className: (active: boolean) => string; +}) { + const { t } = useTranslation("journal"); + const location = useLocation(); + const [showDot, setShowDot] = useState(false); + const isActive = + location.pathname === "/changelog" || + location.pathname.startsWith("/changelog/"); + + useEffect(() => { + if (!newestDate) return; + const disabled = localStorage.getItem(DISABLED_KEY); + if (disabled === "true") return; + const lastSeen = localStorage.getItem(LAST_SEEN_KEY); + if (!lastSeen || newestDate > lastSeen) { + setShowDot(true); + } + }, [newestDate]); + + useEffect(() => { + if (isActive && newestDate) { + localStorage.setItem(LAST_SEEN_KEY, newestDate); + setShowDot(false); + } + }, [isActive, newestDate]); + + return ( + + {t("nav.changelog")} + {showDot && ( + + )} + + ); +} + +export function ChangelogNotificationToggle() { + const { t } = useTranslation("journal"); + const [disabled, setDisabled] = useState(false); + + useEffect(() => { + setDisabled(localStorage.getItem(DISABLED_KEY) === "true"); + }, []); + + function toggle() { + if (disabled) { + localStorage.removeItem(DISABLED_KEY); + setDisabled(false); + } else { + localStorage.setItem(DISABLED_KEY, "true"); + setDisabled(true); + } + } + + return ( + + ); +} diff --git a/apps/journal/app/lib/changelog.server.ts b/apps/journal/app/lib/changelog.server.ts new file mode 100644 index 0000000..885dc2e --- /dev/null +++ b/apps/journal/app/lib/changelog.server.ts @@ -0,0 +1,73 @@ +const modules = import.meta.glob("/changelog/*.md", { + query: "?raw", + import: "default", + eager: true, +}) as Record; + +export interface ChangelogEntry { + slug: string; + title: string; + date: string; + preview: string; + content: string; +} + +function parseFrontmatter(raw: string): { + attrs: Record; + body: string; +} { + const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); + if (!match) return { attrs: {}, body: raw }; + const attrs: Record = {}; + for (const line of match[1]!.split("\n")) { + const idx = line.indexOf(":"); + if (idx === -1) continue; + const key = line.slice(0, idx).trim(); + const value = line.slice(idx + 1).trim().replace(/^["']|["']$/g, ""); + attrs[key] = value; + } + return { attrs, body: match[2]! }; +} + +function firstParagraph(md: string): string { + const trimmed = md.trim(); + const lines: string[] = []; + for (const line of trimmed.split("\n")) { + if (line.startsWith("#")) continue; + if (lines.length > 0 && line.trim() === "") break; + if (line.trim() !== "") lines.push(line); + } + return lines.join(" ").slice(0, 200); +} + +function parseEntry(path: string, raw: string): ChangelogEntry { + const slug = path.replace(/^\/changelog\//, "").replace(/\.md$/, ""); + const { attrs, body } = parseFrontmatter(raw); + return { + slug, + title: attrs.title ?? slug, + date: attrs.date ?? slug, + preview: firstParagraph(body), + content: body, + }; +} + +let cachedEntries: ChangelogEntry[] | null = null; + +export function getAllEntries(): ChangelogEntry[] { + if (!cachedEntries) { + cachedEntries = Object.entries(modules) + .map(([path, raw]) => parseEntry(path, raw)) + .sort((a, b) => b.date.localeCompare(a.date)); + } + return cachedEntries; +} + +export function getEntry(slug: string): ChangelogEntry | undefined { + return getAllEntries().find((e) => e.slug === slug); +} + +export function getNewestDate(): string | null { + const entries = getAllEntries(); + return entries.length > 0 ? entries[0]!.date : null; +} diff --git a/apps/journal/app/root.tsx b/apps/journal/app/root.tsx index be4a5f4..a51ea58 100644 --- a/apps/journal/app/root.tsx +++ b/apps/journal/app/root.tsx @@ -1,5 +1,6 @@ import { useEffect } from "react"; import { Links, Meta, Outlet, Scripts, ScrollRestoration, isRouteErrorResponse, useLocation, Form, Link } from "react-router"; +import { ChangelogLink } from "~/components/ChangelogIndicator"; import type { LinksFunction } from "react-router"; import type { Route } from "./+types/root"; import * as Sentry from "@sentry/react"; @@ -34,12 +35,13 @@ export function Layout({ children }: { children: React.ReactNode }) { } export async function loader({ request }: Route.LoaderArgs) { + const { getNewestDate } = await import("~/lib/changelog.server"); const user = await getSessionUser(request); const locale = detectLocale(request); - return { user: user ? { id: user.id, username: user.username } : null, locale }; + return { user: user ? { id: user.id, username: user.username } : null, locale, newestChangelogDate: getNewestDate() }; } -function NavBar({ user }: { user: { id: string; username: string } | null }) { +function NavBar({ user, newestChangelogDate }: { user: { id: string; username: string } | null; newestChangelogDate: string | null }) { const { t } = useTranslation("journal"); const location = useLocation(); @@ -60,6 +62,7 @@ function NavBar({ user }: { user: { id: string; username: string } | null }) { {t("title")} + linkClass("/changelog")} /> {user && ( <> @@ -124,7 +127,7 @@ export default function App({ loaderData }: Route.ComponentProps) { return ( - + ); diff --git a/apps/journal/app/routes.ts b/apps/journal/app/routes.ts index 60e9952..3fa8016 100644 --- a/apps/journal/app/routes.ts +++ b/apps/journal/app/routes.ts @@ -30,4 +30,7 @@ export default [ route("api/sync/disconnect/:provider", "routes/api.sync.disconnect.$provider.ts"), route("api/sync/webhook/:provider", "routes/api.sync.webhook.$provider.ts"), route("privacy", "routes/privacy.tsx"), + route("changelog", "routes/changelog._index.tsx"), + route("changelog/feed.xml", "routes/changelog.feed[.xml].ts"), + route("changelog/:slug", "routes/changelog.$slug.tsx"), ] satisfies RouteConfig; diff --git a/apps/journal/app/routes/changelog.$slug.tsx b/apps/journal/app/routes/changelog.$slug.tsx new file mode 100644 index 0000000..c13e5c8 --- /dev/null +++ b/apps/journal/app/routes/changelog.$slug.tsx @@ -0,0 +1,50 @@ +import { data } from "react-router"; +import { useTranslation } from "react-i18next"; +import Markdown from "react-markdown"; +import type { Route } from "./+types/changelog.$slug"; +import { getEntry } from "~/lib/changelog.server"; +import { ClientDate } from "~/components/ClientDate"; + +export function meta({ data: entry }: Route.MetaArgs) { + if (!entry) return [{ title: "Not Found — trails.cool" }]; + return [ + { title: `${entry.title} — Changelog — trails.cool` }, + { name: "description", content: entry.preview }, + { property: "og:title", content: entry.title }, + { property: "og:description", content: entry.preview }, + { property: "og:url", content: `https://trails.cool/changelog/${entry.slug}` }, + ]; +} + +export function links() { + return [ + { rel: "alternate", type: "application/atom+xml", title: "trails.cool Changelog", href: "/changelog/feed.xml" }, + ]; +} + +export async function loader({ params }: Route.LoaderArgs) { + const entry = getEntry(params.slug); + if (!entry) throw data(null, { status: 404 }); + return entry; +} + +export default function ChangelogEntryPage({ loaderData: entry }: Route.ComponentProps) { + const { t } = useTranslation("journal"); + + return ( +
+ + {t("changelog.backToList")} + +
+ +

{entry.title}

+
+ {entry.content} +
+
+
+ ); +} diff --git a/apps/journal/app/routes/changelog._index.tsx b/apps/journal/app/routes/changelog._index.tsx new file mode 100644 index 0000000..11076e7 --- /dev/null +++ b/apps/journal/app/routes/changelog._index.tsx @@ -0,0 +1,58 @@ +import { Link } from "react-router"; +import { useTranslation } from "react-i18next"; +import type { Route } from "./+types/changelog._index"; +import { getAllEntries, getNewestDate } from "~/lib/changelog.server"; +import { ClientDate } from "~/components/ClientDate"; +import { ChangelogNotificationToggle } from "~/components/ChangelogIndicator"; + +export function meta() { + return [ + { title: "Changelog — trails.cool" }, + { name: "description", content: "What's new on trails.cool" }, + { property: "og:title", content: "Changelog — trails.cool" }, + { property: "og:description", content: "What's new on trails.cool" }, + ]; +} + +export function links() { + return [ + { rel: "alternate", type: "application/atom+xml", title: "trails.cool Changelog", href: "/changelog/feed.xml" }, + ]; +} + +export async function loader() { + return { entries: getAllEntries(), newestDate: getNewestDate() }; +} + +export default function ChangelogListPage({ loaderData }: Route.ComponentProps) { + const { entries } = loaderData; + const { t } = useTranslation("journal"); + + return ( +
+
+
+

{t("changelog.title")}

+

{t("changelog.subtitle")}

+
+ +
+ +
+ {entries.map((entry) => ( + + +

{entry.title}

+

{entry.preview}

+ + ))} +
+
+ ); +} diff --git a/apps/journal/app/routes/changelog.feed[.xml].ts b/apps/journal/app/routes/changelog.feed[.xml].ts new file mode 100644 index 0000000..939d7dd --- /dev/null +++ b/apps/journal/app/routes/changelog.feed[.xml].ts @@ -0,0 +1,51 @@ +import { getAllEntries } from "~/lib/changelog.server"; +import Markdown from "react-markdown"; +import { renderToStaticMarkup } from "react-dom/server"; +import { createElement } from "react"; + +function renderMarkdown(md: string): string { + return renderToStaticMarkup(createElement(Markdown, null, md)); +} + +function escapeXml(str: string): string { + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); +} + +export async function loader() { + const entries = getAllEntries(); + const domain = process.env.DOMAIN ?? "trails.cool"; + const baseUrl = `https://${domain}`; + const updated = entries[0]?.date ?? new Date().toISOString().slice(0, 10); + + const atomEntries = entries.map((entry) => { + const html = renderMarkdown(entry.content); + return ` + ${escapeXml(entry.title)} + + ${baseUrl}/changelog/${entry.slug} + ${entry.date}T00:00:00Z + ${escapeXml(html)} + `; + }); + + const feed = ` + + trails.cool Changelog + + + ${baseUrl}/changelog + ${updated}T00:00:00Z +${atomEntries.join("\n")} +`; + + return new Response(feed, { + headers: { + "Content-Type": "application/atom+xml; charset=utf-8", + "Cache-Control": "public, max-age=3600", + }, + }); +} diff --git a/apps/journal/changelog/2026-03-25.md b/apps/journal/changelog/2026-03-25.md new file mode 100644 index 0000000..ff88492 --- /dev/null +++ b/apps/journal/changelog/2026-03-25.md @@ -0,0 +1,34 @@ +--- +title: "trails.cool is live" +date: 2026-03-25 +--- + +We're excited to launch trails.cool — a federated, self-hostable platform for outdoor enthusiasts. + +## Planner: Collaborative Route Planning + +Plan hiking, cycling, and driving routes together in real-time. No account needed — just share a link and start editing. + +- **Real-time collaboration** via Yjs — changes sync instantly across all participants +- **Smart routing** powered by BRouter with profiles for hiking, fast cycling, safe cycling, shortest path, and car +- **Elevation profiles** with total ascent and descent statistics +- **Route coloring** by elevation gradient or surface type +- **No-go areas** — draw polygons on the map to avoid specific regions +- **GPX import and export** — bring routes from other apps or export for your GPS device +- **Session notes** — shared text area for trip planning details +- **Crash recovery** — unsaved work is preserved if your browser crashes + +## Journal: Your Activity Hub + +Track your routes and outdoor activities with a personal journal. + +- **Route management** with GPX import/export and sequential versioning +- **Activity tracking** — log activities with GPS traces, distances, and durations +- **Wahoo integration** — automatic sync of workouts via webhook, plus manual import +- **Map previews** on route and activity list pages +- **Passkey authentication** — passwordless login, no passwords to remember +- **Magic link fallback** for devices without passkey support + +## Self-Hostable and Privacy-First + +trails.cool is designed for data ownership. Host your own instance, keep your data, export everything as GPX or JSON. We collect zero analytics, zero tracking — your routes are yours. diff --git a/openspec/changes/changelog/design.md b/openspec/changes/changelog/design.md index d88d976..09b7e15 100644 --- a/openspec/changes/changelog/design.md +++ b/openspec/changes/changelog/design.md @@ -16,7 +16,7 @@ produces shareable social links. **Non-Goals:** - CMS or admin interface for writing entries -- RSS feed (future, not now) +- RSS/Atom feed for subscribing to updates - Email notifications for new entries - Per-entry images or rich media (just markdown text) - Bilingual entries (English only for now) @@ -50,11 +50,30 @@ Store `changelog:lastSeen` timestamp in localStorage. If the newest entry's date is after this timestamp, show a dot on the nav "Changelog" link. Clicking the changelog page updates the timestamp. No server state needed. +Users can permanently disable the "What's New" indicator by setting +`changelog:disabled` in localStorage. A "Don't show again" option is available +on the changelog page. When disabled, the dot never appears regardless of new +entries. The setting can be re-enabled from the changelog page itself via a +"Show new entry notifications" toggle. All client-side, no auth required. + ### D4: Open Graph meta for social sharing Each `/changelog/:date` page sets `og:title`, `og:description` (first paragraph), and `og:url`. No og:image for now — text previews are fine. +### D5: RSS/Atom feed at `/changelog/feed.xml` + +A standard Atom feed at `/changelog/feed.xml` so users and tools can subscribe. +Generated server-side from the same changelog entries used by the HTML pages. +Each `` includes the title, date, full markdown content rendered as HTML, +and a link to the individual changelog page. + +The feed URL is advertised via a `` +tag in the `` of the changelog pages, so feed readers auto-discover it. + +Atom over RSS 2.0 because Atom has a proper spec (RFC 4287), required dates, +and better content handling. All major feed readers support both. + ## Risks / Trade-offs - **Build-time loading means deploy to publish** → Acceptable. We deploy on diff --git a/openspec/changes/changelog/proposal.md b/openspec/changes/changelog/proposal.md index 2a47bfe..eda814a 100644 --- a/openspec/changes/changelog/proposal.md +++ b/openspec/changes/changelog/proposal.md @@ -22,7 +22,7 @@ project is alive and improving), and social followers (shareable updates). ### New Capabilities -- `changelog`: Public changelog with dated entries, shareable individual pages with OG tags, "what's new" indicator +- `changelog`: Public changelog with dated entries, shareable individual pages with OG tags, "what's new" indicator, RSS/Atom feed ### Modified Capabilities @@ -30,8 +30,8 @@ project is alive and improving), and social followers (shareable updates). ## Impact -- **Files**: New routes (`/changelog`, `/changelog/:slug`), markdown entry files - in `docs/changelog/` or `apps/journal/changelog/`, nav bar update +- **Files**: New routes (`/changelog`, `/changelog/:slug`, `/changelog/feed.xml`), + markdown entry files in `apps/journal/changelog/`, nav bar update - **Dependencies**: A markdown renderer (could reuse `react-markdown` or render at build time) - **i18n**: Changelog entries written in English (primary audience), German diff --git a/openspec/changes/changelog/specs/changelog/spec.md b/openspec/changes/changelog/specs/changelog/spec.md index 6d99060..27f4bed 100644 --- a/openspec/changes/changelog/specs/changelog/spec.md +++ b/openspec/changes/changelog/specs/changelog/spec.md @@ -35,3 +35,24 @@ The system SHALL show a visual indicator when there are changelog entries the us #### Scenario: Indicator dismissed - **WHEN** a user visits the /changelog page - **THEN** the indicator is dismissed (localStorage timestamp updated) + +#### Scenario: Permanently disable indicator +- **WHEN** a user clicks "Don't show again" on the changelog page +- **THEN** `changelog:disabled` is set in localStorage +- **AND** the "What's New" dot no longer appears for any future entries + +#### Scenario: Re-enable indicator +- **WHEN** a user toggles "Show new entry notifications" on the changelog page +- **THEN** `changelog:disabled` is removed from localStorage +- **AND** the indicator resumes normal behavior + +### Requirement: RSS/Atom feed +The system SHALL provide an Atom feed at `/changelog/feed.xml` for subscribing to changelog updates. + +#### Scenario: Subscribe to feed +- **WHEN** a feed reader requests `/changelog/feed.xml` +- **THEN** it receives a valid Atom feed with all changelog entries including title, date, content, and link + +#### Scenario: Feed auto-discovery +- **WHEN** a browser or feed reader visits any `/changelog` page +- **THEN** a `` tag in the page head points to the feed URL diff --git a/openspec/changes/changelog/tasks.md b/openspec/changes/changelog/tasks.md index 276e3b4..f7a9f99 100644 --- a/openspec/changes/changelog/tasks.md +++ b/openspec/changes/changelog/tasks.md @@ -1,29 +1,41 @@ ## 1. Changelog Data Layer -- [ ] 1.1 Create `apps/journal/changelog/` directory with a sample entry (`2026-03-25.md`) covering the initial launch -- [ ] 1.2 Set up Vite `import.meta.glob` to load all `.md` files from the changelog directory with frontmatter parsing -- [ ] 1.3 Create `apps/journal/app/lib/changelog.server.ts` with functions: getAllEntries(), getEntry(date) returning parsed markdown + frontmatter +- [x] 1.1 Create `apps/journal/changelog/` directory with a sample entry (`2026-03-25.md`) covering the initial launch +- [x] 1.2 Set up Vite `import.meta.glob` to load all `.md` files from the changelog directory with frontmatter parsing +- [x] 1.3 Create `apps/journal/app/lib/changelog.server.ts` with functions: getAllEntries(), getEntry(date) returning parsed markdown + frontmatter ## 2. Routes -- [ ] 2.1 Add `/changelog` route showing all entries (date, title, preview) newest-first -- [ ] 2.2 Add `/changelog/:date` route rendering full markdown entry -- [ ] 2.3 Add Open Graph meta tags on entry pages (og:title, og:description, og:url) -- [ ] 2.4 Add both routes to `routes.ts` +- [x] 2.1 Add `/changelog` route showing all entries (date, title, preview) newest-first +- [x] 2.2 Add `/changelog/:date` route rendering full markdown entry +- [x] 2.3 Add Open Graph meta tags on entry pages (og:title, og:description, og:url) +- [x] 2.4 Add both routes to `routes.ts` -## 3. What's New Indicator +## 3. RSS/Atom Feed -- [ ] 3.1 Add "Changelog" link to Journal nav bar -- [ ] 3.2 Track `changelog:lastSeen` in localStorage, show dot when newest entry is newer -- [ ] 3.3 Clear indicator when user visits /changelog +- [x] 3.1 Add `/changelog/feed.xml` route that returns an Atom feed (application/atom+xml) with all entries +- [x] 3.2 Add `` to changelog page heads for feed auto-discovery -## 4. Content +## 4. What's New Indicator -- [ ] 4.1 Write initial changelog entry for the launch (features shipped in Phase 1) -- [ ] 4.2 Add markdown rendering (react-markdown or built-in) for entry content +- [x] 4.1 Add "Changelog" link to Journal nav bar +- [x] 4.2 Track `changelog:lastSeen` in localStorage, show dot when newest entry is newer +- [x] 4.3 Clear indicator when user visits /changelog +- [x] 4.4 Add "Don't show again" option on changelog page that sets `changelog:disabled` in localStorage +- [x] 4.5 Add "Show new entry notifications" toggle on changelog page to re-enable the indicator +- [x] 4.6 Skip indicator check when `changelog:disabled` is set -## 5. Verify +## 5. Content -- [ ] 5.1 Verify /changelog lists entries correctly -- [ ] 5.2 Verify /changelog/:date renders full entry with OG tags -- [ ] 5.3 Verify "What's New" dot appears and dismisses +- [x] 5.1 Write initial changelog entry for the launch (features shipped in Phase 1) +- [x] 5.2 Add markdown rendering (react-markdown or built-in) for entry content + +## 6. Verify + +- [x] 6.1 Verify /changelog lists entries correctly +- [x] 6.2 Verify /changelog/:date renders full entry with OG tags +- [x] 6.3 Verify "What's New" dot appears and dismisses +- [x] 6.6 Verify "Don't show again" disables indicator permanently +- [x] 6.7 Verify re-enabling notifications restores indicator behavior +- [x] 6.4 Verify /changelog/feed.xml returns valid Atom feed +- [x] 6.5 Verify feed auto-discovery link is present in page source diff --git a/package.json b/package.json index 0a2fe43..4f0780a 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "react-dom": "^19.2.4", "react-i18next": "^17.0.2", "react-leaflet": "^5.0.0", + "react-markdown": "^10.1.0", "react-router": "catalog:", "tailwindcss": "^4.2.2", "turbo": "^2.9.3", diff --git a/packages/i18n/src/locales/de.ts b/packages/i18n/src/locales/de.ts index 51f0a30..eb6fff1 100644 --- a/packages/i18n/src/locales/de.ts +++ b/packages/i18n/src/locales/de.ts @@ -192,9 +192,16 @@ export default { previous: "Zurück", next: "Weiter", }, + changelog: { + title: "Änderungsprotokoll", + subtitle: "Was gibt es Neues auf trails.cool", + backToList: "Zurück zum Änderungsprotokoll", + showNotifications: "Über neue Einträge benachrichtigen", + }, nav: { routes: "Routen", activities: "Aktivitäten", + changelog: "Änderungen", login: "Anmelden", register: "Registrieren", profile: "Profil", diff --git a/packages/i18n/src/locales/en.ts b/packages/i18n/src/locales/en.ts index 31490a8..702d3a5 100644 --- a/packages/i18n/src/locales/en.ts +++ b/packages/i18n/src/locales/en.ts @@ -192,9 +192,16 @@ export default { previous: "Previous", next: "Next", }, + changelog: { + title: "Changelog", + subtitle: "What's new on trails.cool", + backToList: "Back to Changelog", + showNotifications: "Notify me of new entries", + }, nav: { routes: "Routes", activities: "Activities", + changelog: "Changelog", login: "Sign In", register: "Register", profile: "Profile", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 994c1ab..5a9a5e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,6 +152,9 @@ importers: react-leaflet: specifier: ^5.0.0 version: 5.0.0(leaflet@1.9.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@19.2.14)(react@19.2.4) react-router: specifier: 'catalog:' version: 7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -1957,24 +1960,39 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/leaflet@1.9.21': resolution: {integrity: sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/mysql@2.15.27': resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} @@ -2001,6 +2019,12 @@ packages: '@types/tedious@4.0.14': resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -2063,6 +2087,9 @@ packages: resolution: {integrity: sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@vitejs/plugin-basic-ssl@2.3.0': resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -2160,6 +2187,9 @@ packages: babel-dead-code-elimination@1.0.12: resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -2226,10 +2256,25 @@ packages: caniuse-lite@1.0.30001781: resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -2240,6 +2285,9 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -2324,6 +2372,9 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + dedent@1.7.2: resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} peerDependencies: @@ -2351,6 +2402,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -2594,6 +2648,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -2620,6 +2677,9 @@ packages: exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-copy@4.0.2: resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} @@ -2732,6 +2792,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} @@ -2745,6 +2811,9 @@ packages: html-parse-stringify@3.0.1: resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + htmlparser2@10.1.0: resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} @@ -2800,10 +2869,22 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2812,6 +2893,13 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -2968,6 +3056,9 @@ packages: lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + lru-cache@11.2.7: resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} engines: {node: 20 || >=22} @@ -2986,6 +3077,30 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} @@ -3000,6 +3115,69 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -3122,6 +3300,9 @@ packages: resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse5@8.0.0: resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} @@ -3249,6 +3430,9 @@ packages: resolution: {integrity: sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==} engines: {node: ^16 || ^18 || >=20} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -3322,6 +3506,12 @@ packages: react: ^19.0.0 react-dom: ^19.0.0 + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -3355,6 +3545,12 @@ packages: reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -3461,6 +3657,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + splaytree-ts@1.0.2: resolution: {integrity: sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA==} @@ -3478,6 +3677,9 @@ packages: std-env@4.0.0: resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -3486,6 +3688,12 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + sweepline-intersections@1.5.0: resolution: {integrity: sha512-AoVmx72QHpKtItPu72TzFL+kcYjd67BPLDoR0LarIk+xyaRg+pDTMFXndIEvZf9xEKnJv6JdhgRMnocoG0D3AQ==} @@ -3546,6 +3754,12 @@ packages: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -3601,6 +3815,24 @@ packages: resolution: {integrity: sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==} engines: {node: '>=20.18.1'} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -3635,6 +3867,12 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -3807,6 +4045,9 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@adobe/css-tools@4.4.4': {} @@ -5342,20 +5583,38 @@ snapshots: dependencies: '@types/node': 25.5.2 + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} '@types/esrecurse@4.3.1': {} + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + '@types/estree@1.0.8': {} '@types/geojson@7946.0.16': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} '@types/leaflet@1.9.21': dependencies: '@types/geojson': 7946.0.16 + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + '@types/mysql@2.15.27': dependencies: '@types/node': 25.5.2 @@ -5390,6 +5649,10 @@ snapshots: dependencies: '@types/node': 25.5.2 + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + '@types/ws@8.18.1': dependencies: '@types/node': 25.5.2 @@ -5485,6 +5748,8 @@ snapshots: '@typescript-eslint/types': 8.58.0 eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.0': {} + '@vitejs/plugin-basic-ssl@2.3.0(vite@6.4.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0))': dependencies: vite: 6.4.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) @@ -5591,6 +5856,8 @@ snapshots: transitivePeerDependencies: - supports-color + bail@2.0.2: {} + balanced-match@4.0.4: {} base64-js@1.5.1: {} @@ -5663,8 +5930,18 @@ snapshots: caniuse-lite@1.0.30001781: {} + ccount@2.0.1: {} + chai@6.2.2: {} + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -5673,6 +5950,8 @@ snapshots: colorette@2.0.20: {} + comma-separated-tokens@2.0.3: {} + compressible@2.0.18: dependencies: mime-db: 1.54.0 @@ -5751,6 +6030,10 @@ snapshots: decimal.js@10.6.0: {} + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + dedent@1.7.2: {} deep-is@0.1.4: {} @@ -5763,6 +6046,10 @@ snapshots: detect-libc@2.1.2: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} @@ -6000,6 +6287,8 @@ snapshots: estraverse@5.3.0: {} + estree-util-is-identifier-name@3.0.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -6050,6 +6339,8 @@ snapshots: exsolve@1.0.8: {} + extend@3.0.2: {} + fast-copy@4.0.2: {} fast-deep-equal@3.1.3: {} @@ -6156,6 +6447,30 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + help-me@5.0.0: {} html-encoding-sniffer@6.0.0: @@ -6170,6 +6485,8 @@ snapshots: dependencies: void-elements: 3.1.0 + html-url-attributes@3.0.1: {} + htmlparser2@10.1.0: dependencies: domelementtype: 2.3.0 @@ -6232,14 +6549,29 @@ snapshots: inherits@2.0.4: {} + inline-style-parser@0.2.7: {} + ipaddr.js@1.9.1: {} + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + is-extglob@2.1.1: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 + is-hexadecimal@2.0.1: {} + + is-plain-obj@4.1.0: {} + is-potential-custom-element-name@1.0.1: {} isbot@5.1.37: {} @@ -6370,6 +6702,8 @@ snapshots: lodash@4.18.1: {} + longest-streak@3.1.0: {} + lru-cache@11.2.7: {} lru-cache@5.1.1: @@ -6384,6 +6718,95 @@ snapshots: math-intrinsics@1.1.0: {} + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdn-data@2.27.1: {} media-typer@0.3.0: {} @@ -6392,6 +6815,139 @@ snapshots: methods@1.1.2: {} + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + mime-db@1.52.0: {} mime-db@1.54.0: {} @@ -6487,6 +7043,16 @@ snapshots: p-map@7.0.4: {} + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse5@8.0.0: dependencies: entities: 6.0.1 @@ -6620,6 +7186,8 @@ snapshots: '@opentelemetry/api': 1.9.1 tdigest: 0.1.2 + property-information@7.1.0: {} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -6686,6 +7254,24 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.4): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.2.14 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.2.4 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-refresh@0.14.2: {} react-router@7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): @@ -6709,6 +7295,23 @@ snapshots: reflect-metadata@0.2.2: {} + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + require-from-string@2.0.2: {} require-in-the-middle@8.0.1: @@ -6853,6 +7456,8 @@ snapshots: source-map@0.6.1: {} + space-separated-tokens@2.0.2: {} + splaytree-ts@1.0.2: {} split2@4.2.0: {} @@ -6863,12 +7468,25 @@ snapshots: std-env@4.0.0: {} + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 strip-json-comments@5.0.3: {} + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + sweepline-intersections@1.5.0: dependencies: tinyqueue: 2.0.3 @@ -6918,6 +7536,10 @@ snapshots: dependencies: punycode: 2.3.1 + trim-lines@3.0.1: {} + + trough@2.2.0: {} + ts-api-utils@2.5.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -6974,6 +7596,39 @@ snapshots: undici@7.24.5: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + unpipe@1.0.0: {} update-browserslist-db@1.2.3(browserslist@4.28.1): @@ -6998,6 +7653,16 @@ snapshots: vary@1.1.2: {} + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + vite-node@3.2.4(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0): dependencies: cac: 6.7.14 @@ -7131,3 +7796,5 @@ snapshots: lib0: 0.2.117 yocto-queue@0.1.0: {} + + zwitch@2.0.4: {}