diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 14b0a97..d28ac49 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,3 +9,4 @@ - [ ] La base è `dev` - [ ] `npm run lint` passa - [ ] `npm run build` passa +- [ ] Se aggiungi o cambi una lezione: il suo mazzo in `slides/decks/` è aggiornato e `npm run slides:check` e `npm run slides:verifica` passano (vedi `slides/README.md`) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d10032d..5e2b207 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -19,9 +19,16 @@ jobs: with: node-version: 22 cache: npm + cache-dependency-path: | + package-lock.json + slides/package-lock.json - name: Install dependencies run: npm ci + - name: Install slides dependencies + run: npm ci --prefix slides + - name: Build slides + run: npm run slides:build - name: Build website run: npm run build diff --git a/.github/workflows/test-deploy.yml b/.github/workflows/test-deploy.yml index 7fc2adb..2ada525 100644 --- a/.github/workflows/test-deploy.yml +++ b/.github/workflows/test-deploy.yml @@ -22,9 +22,20 @@ jobs: with: node-version: 22 cache: npm + cache-dependency-path: | + package-lock.json + slides/package-lock.json - name: Install dependencies run: npm ci + - name: Install slides dependencies + run: npm ci --prefix slides + - name: Install Chromium for the slide verification + run: npx --prefix slides playwright install --with-deps chromium + - name: Build slides + run: npm run slides:build + - name: Verify slides (nessuna slide che straborda) + run: npm run slides:verifica - name: Guardia manifest percorsi (append-only) run: npm run curriculum:check - name: Lint diff --git a/.gitignore b/.gitignore index cda19b7..a0e77c6 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,8 @@ AGENTS.md # Foto e rapporti di verifica delle figure (npm run figure:foto) .figure-foto/ + +# Presentazioni costruite (npm run slides:build): le genera la CI +static/slides/ +slides/.slidev/ +slides/.provino/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c594f51..c88d977 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,6 +33,10 @@ Una pull request verso `main` che non arriva da `dev` viene bloccata da un contr - Una descrizione che dica che cosa cambia e perché. - Per le lezioni, il contenuto in italiano e le regole di tono del progetto. Se hai dubbi, apri prima una issue. +## Lezioni e presentazioni + +Ogni lezione ha la sua presentazione in `slides/decks/` (vedi `slides/README.md`), e le due cambiano insieme: chi modifica o crea una lezione, persona o assistente IA, aggiorna o crea nello stesso commit anche il suo mazzo, poi lancia `npm run slides:check`, `npm run slides:build`, `npm run slides:verifica` e `npm run slides:provino` e corregge finché passano. `slides:check` segnala una lezione cambiata dopo il suo mazzo, e la CI si ferma. + ## Sostenitori Il nome di un sostenitore compare nell’elenco (`src/lib/v4vData.ts`) solo dopo che il maintainer ha verificato la donazione. Non modificare quel file in una pull request: scrivi a marco.farina@jcmaxwell.it. diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 8ed7586..7fd8ce7 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -165,6 +165,7 @@ export default async function createConfig(): Promise { './plugins/llms-txt/index.js', './plugins/curriculum/index.js', './plugins/archivio/index.js', + './plugins/presentazioni/index.js', [ '@docusaurus/plugin-content-docs', { diff --git a/package.json b/package.json index 62c7e14..c98ef4f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,14 @@ "curriculum:new-epoch": "node scripts/curriculum-new-epoch.js", "curriculum:check": "node scripts/check-manifest-append-only.js", "version": "node scripts/sync-pm-version.mjs", - "figure:foto": "node scripts/figure-foto.mjs" + "figure:foto": "node scripts/figure-foto.mjs", + "slides:install": "npm ci --prefix slides", + "slides:dev": "node slides/scripts/slides.mjs dev", + "slides:build": "node slides/scripts/slides.mjs build", + "slides:pdf": "node slides/scripts/slides.mjs pdf", + "slides:check": "node slides/scripts/slides.mjs check", + "slides:verifica": "node slides/scripts/slides.mjs verifica", + "slides:provino": "node slides/scripts/slides.mjs provino" }, "dependencies": { "@codemirror/commands": "^6.10.3", diff --git a/plugins/presentazioni/index.js b/plugins/presentazioni/index.js new file mode 100644 index 0000000..95b0ef0 --- /dev/null +++ b/plugins/presentazioni/index.js @@ -0,0 +1,45 @@ +// Presentazioni delle lezioni (slides/, vedi slides/README.md). +// +// Un mazzo slides/decks//.md appartiene alla lezione +// volumes//.mdx e, una volta costruito (npm run slides:build), +// è servito a /slides///. Il plugin dice al pulsante «Presenta» +// (src/components/PresentaButton) quali lezioni ne hanno uno: legge i sorgenti, non static/slides/, così in sviluppo il +// pulsante c'è anche prima della build (e porta a una 404 finché non la fai). +const path = require('path'); +const fs = require('fs'); + +const PLUGIN_NAME = 'presentazioni'; +const DECKS_DIR = path.join('slides', 'decks'); + +module.exports = function presentazioniPlugin(context) { + const decksAbs = path.join(context.siteDir, DECKS_DIR); + + return { + name: PLUGIN_NAME, + + getPathsToWatch() { + return [path.join(decksAbs, '*', '*.md')]; + }, + + async loadContent() { + const presentazioni = {}; + if (!fs.existsSync(decksAbs)) return { presentazioni }; + for (const volume of fs.readdirSync(decksAbs, { withFileTypes: true })) { + if (!volume.isDirectory() || volume.name.startsWith('_')) continue; + for (const file of fs.readdirSync(path.join(decksAbs, volume.name))) { + if (!file.endsWith('.md') || file.startsWith('_')) continue; + const slug = file.replace(/\.md$/, ''); + // Chiave: `/`, come il campo `lezione:` del mazzo. + presentazioni[`${volume.name}/${slug}`] = { + url: `/slides/${volume.name}/${slug}/`, + }; + } + } + return { presentazioni }; + }, + + async contentLoaded({ content, actions }) { + actions.setGlobalData(content); + }, + }; +}; diff --git a/slides/README.md b/slides/README.md new file mode 100644 index 0000000..7789250 --- /dev/null +++ b/slides/README.md @@ -0,0 +1,193 @@ +# Presentazioni delle lezioni + +Ogni lezione del libro può avere una presentazione: le slide che l'insegnante proietta in classe. Le presentazioni sono fatte con [Slidev](https://sli.dev) e usano un tema unico, `slides/theme/`, con la stessa carta, gli stessi font e gli stessi colori del libro. + +Nella pagina della lezione, in alto a destra, compare il pulsante **Presenta**, che apre le slide. Compare solo se la lezione ha un mazzo. + +Questo file è anche **l'istruzione per l'IA** che genera le lezioni: quando nasce una lezione nuova, nasce anche il suo mazzo, scritto con le regole qui sotto. + +## Dove stanno le cose + +| Che cosa | Dove | +|---|---| +| Mazzo di una lezione | `slides/decks//.md`, per la lezione `volumes//.mdx` | +| Mazzo da copiare per cominciare | `slides/decks/_modello.md` | +| Tema: layout, componenti, stili | `slides/theme/` | +| Build, PDF, controlli | `slides/scripts/slides.mjs` | +| Presentazioni costruite (non nel repository) | `static/slides///` | +| Pulsante nella lezione | `src/components/PresentaButton/`, dati da `plugins/presentazioni/` | + +## Comandi + +Una volta sola, dopo `npm ci`: + +```bash +npm run slides:install +``` + +Anteprima dal vivo di un mazzo, con ricarica a ogni salvataggio: + +```bash +npm run slides:dev -- archivista/le-chiavi +``` + +Controllo della struttura dei mazzi: front matter, scaletta, layout, slot, figure e dataset che esistono, lunghezza dei blocchi di codice: + +```bash +npm run slides:check +``` + +Build delle presentazioni in `static/slides/`, di tutte o di una lezione: + +```bash +npm run slides:build -- archivista/le-chiavi +``` + +Un PDF da distribuire a mano, se serve, si genera a parte con `npm run slides:pdf -- archivista/le-chiavi`. + +Verifica visiva delle presentazioni costruite: apre ogni slide in un browser senza finestra e segnala quelle il cui contenuto straborda sul piede, il cui codice è più largo del riquadro o che hanno testo che esce di lato da un callout, una colonna o dalla slide: + +```bash +npm run slides:verifica -- archivista/le-chiavi +``` + +Provino: tutte le slide di un mazzo in un'unica immagine, tre per riga e numerate, come escono nel PDF. Esce in `slides/.provino/-.png`: + +```bash +npm run slides:provino -- archivista/le-chiavi +``` + +La CI (`.github/workflows/`) esegue `slides:build` prima di `npm run build`, e nelle pull request anche `slides:verifica`: un mazzo che non passa i controlli blocca la build. In locale, i pulsanti della lezione portano a una 404 finché non hai fatto la build. + +## Istruzioni per l'IA: come si scrive un mazzo + +Queste regole valgono per chi genera i mazzi, persona o IA. Il tema fa tutto il lavoro grafico: margini, allineamenti, colori, testata, piede e numeri di pagina. **Chi scrive il mazzo sceglie solo i layout e scrive il contenuto.** Niente HTML di impaginazione, niente `style=`, niente classi di UnoCSS: se un contenuto non sta in nessun layout, il layout va aggiunto al tema, non improvvisato nel mazzo. + +### 0. Due regole che non si saltano mai + +1. **Lezione e slide cambiano insieme.** Chi modifica una lezione (`volumes//.mdx`), persona o IA, aggiorna nello stesso commit il suo mazzo (`slides/decks//.md`): un esempio cambiato, un paragrafo aggiunto o tolto, un quiz nuovo. Chi crea una lezione nuova crea anche il mazzo. Un'IA che lavora su una lezione lo fa **in automatico**, senza aspettare che glielo si chieda. `slides:check` lo fa rispettare: se la lezione è cambiata dopo il mazzo, segnala `la lezione … è cambiata dopo il mazzo: aggiorna le slide`, e la CI si ferma. +2. **Le verifiche le fa chi scrive, tutte, ogni volta.** Prima di consegnare, l'IA esegue da sé i controlli della sezione 7, nell'ordine: struttura, build, verifica (che esegue anche ogni blocco di codice e misura il risultato aperto), e **provino**, cioè l'immagine con tutte le slide, che apre e guarda slide per slide, e se serve rigenera dopo ogni correzione. Corregge finché tutto passa, e riporta l'esito nella pull request. L'insegnante deve ricevere un mazzo che funziona in classe senza doverlo ricontrollare. + +### 1. Il punto di partenza è la lezione + +Il mazzo racconta la lezione, nello stesso ordine e con gli stessi esempi (Risonanza, Hogwarts, i James Potter). Non introduce concetti che la lezione non ha e non ne salta di importanti. Il testo però è riscritto per la proiezione: frasi brevi, un'idea per slide, elenchi di tre-cinque voci. Lo leggono studenti in un'aula, mentre l'insegnante parla. + +Tono: lo stesso del libro, italiano piano, seconda persona, niente enfasi pubblicitaria e niente punti esclamativi. Le virgolette sono quelle della lezione. + +### 2. Front matter iniziale + +È la prima cosa del file, e la copertina si costruisce da qui. Non ha contenuto proprio. + +```yaml +--- +theme: ../../theme +title: 'Identificare senza ambiguità: le chiavi' # il `title` della lezione +sottotitolo: Due persone possono avere lo stesso nome… # una o due frasi dalla `description` +volume: archivista # la cartella del mazzo: programmatore, artefice, archivista, apprendista +capitolo: 2 # il numero del capitolo (curriculum/toc/.js); ometti se il volume non ha capitoli +numero: 8 # la `sidebar_position` della lezione: il numero grande della copertina +lezione: archivista/le-chiavi # /: dice al QR finale e al PDF dove sta la lezione +--- +``` + +### 3. La scaletta + +Ogni mazzo segue la stessa scaletta. È questo che rende coerenti le presentazioni di tutto il libro. + +1. **Copertina**: la prima slide, vuota (il front matter iniziale). +2. **Epigrafe** (`layout: epigrafe`) se la lezione ne ha una. +3. **Obiettivi** (`layout: obiettivi`): i `:::prereq` e i `:::learn` della lezione, accorciati. +4. Per ogni `##` della lezione: + - una slide **`layout: sezione`** con il titolo del paragrafo e una frase che dice di che cosa parla; + - le slide di contenuto, da due a sei. +5. **Sintesi** (`layout: sintesi`): il `:::nutshell`. +6. **Quiz** (`layout: quiz`): una slide per ogni domanda del ``, con `numero: 1 di 3`. +7. **Fine** (`layout: fine`): la prossima lezione, dal `:::tip[La prossima lezione]`. Il QR della lezione lo aggiunge il tema. + +Un mazzo ha in genere da 18 a 30 slide. + +### 4. I layout + +| Layout | Quando | Slot e proprietà | +|---|---|---| +| `copertina` | La prima slide, sempre. | Tutto dal front matter iniziale. | +| `epigrafe` | La citazione d'apertura. | Testo nello slot; `autore:`. | +| `obiettivi` | Prerequisiti e obiettivi. | `# Titolo`, poi `::prerequisiti::` e `::imparerai::` con un elenco ciascuno. | +| `sezione` | Apre ogni `##` della lezione. | `# Titolo` del paragrafo, poi una frase. Il numero è automatico e il titolo va nella testata delle slide che seguono. | +| `default` | Testo, elenchi, codice, callout. | `# Titolo` e contenuto. Non serve scriverlo. | +| `due-colonne` | Confronti, codice e spiegazione, i `` del libro. | `# Titolo`, poi `::sinistra::` e `::destra::`. `rapporto: 3/2`, `sinistra:` e `destra:` (etichette), `centro: true`. | +| `definizione` | Un `:::definition`, da solo. | Testo nello slot; `titolo:` (il termine). | +| `enunciato` | L'idea che la slide deve lasciare, in una frase grande. | Primo paragrafo: l'enunciato (il **grassetto** prende il colore del volume). Paragrafi successivi: una nota. | +| `figura` | ``, ``, un diagramma mermaid. | `# Titolo`, poi `::figura::` con la figura. `::lato::` (facoltativo) mette un testo breve accanto, a sinistra (`lato: destra` lo sposta). `didascalia:` facoltativa (non per ``, che ha le sue). La figura si scala da sola per stare nello spazio. **I diagrammi vanno in questo layout, mai in `due-colonne`.** | +| `domanda` | «Prova a prevedere»: gli `` del libro. | `# Titolo` e la domanda o il codice; `::risposta::` compare al clic. `invito:` cambia la frase sopra la risposta. `affiancata: true` mette codice a sinistra e risposta a destra: è obbligatoria quando c'è un blocco di codice di più di 6 righe. | +| `quiz` | Una domanda del quiz. | `# La domanda`, poi un `` per riga; una sola ha `corretta`. `numero: 1 di 3`. | +| `sintesi` | Il `:::nutshell`. | `# In sintesi`, poi ``. | +| `fine` | L'ultima slide. | `# La prossima lezione` e due righe. | + +### 5. I componenti + +I componenti del libro hanno un equivalente nelle slide. **Le figure e i diagrammi sono gli stessi del libro**, non copie: si citano per nome. + +| Nel libro | Nelle slide | +|---|---| +| `:::warning[Titolo]` … `:::` (e tutti i callout) | `` … ``. I tipi sono quelli del libro: `prereq`, `learn`, `definition`, `history`, `code`, `cleancode`, `nutshell`, `info`, `tip`, `warning`, `danger`, `note`. Senza `titolo` si usa quello predefinito. Riga vuota dopo il tag di apertura e prima di quello di chiusura, perché il contenuto sia markdown. | +| `` | ``: il nome del file in `src/figure/`. In presentazione si anima come nel libro; nel PDF si ferma sul passo finale. | +| `` | ``: si copiano le proprietà dalla lezione e si mette `:` davanti a quelle tra graffe (`:entita`, `:associazioni`, `:cella`). Le stringhe restano come sono. | +| ` ```mermaid ` | Uguale, dentro `::figura::` di un layout `figura`. | +| ` ```py live ` | ` ```python ` dentro `` … ``: in presentazione compare «Esegui», con lo stesso Brython del libro. `uscita="destra"` mette l'output accanto quando sotto non c'è posto. Ogni blocco parte da zero: deve contenere tutto quello che gli serve (import, lettura dei file, definizioni), non può usare variabili di una slide precedente. Le righe `### PRE` della lezione vanno scritte in chiaro. `errore` dichiara un blocco che fallisce apposta (un `AttributeError` da mostrare). | +| Un risultato che non entra sotto il codice | `seguito` su `` o ``: il tema crea da solo, subito dopo, la slide «… il risultato», con lo stesso blocco eseguito in automatico e a tutta pagina; «Esegui» sulla slide del codice porta lì. **Obbligatorio quando `slides:verifica` dice che il risultato aperto non sta nella slide**, e di regola in ogni layout `domanda`. Non si scrive mai a mano una slide che ripete il blocco. | +| ` ```sql live dataset=archivista/x ` | ` ```sql ` dentro ``. Con `database` mostra dopo l'esecuzione le tabelle cambiate, come il pannello «Database» del libro (righe nuove, modificate e tolte evidenziate); `database="studente,casa"` sceglie le tabelle. Con `persistente` il database resta modificato da una slide all'altra, per le sequenze INSERT → SELECT. `errore` dichiara una query che fallisce apposta (un vincolo violato). | +| Il pannello «Database» con lo stato iniziale | ``: le tabelle del dataset prima di qualsiasi query. Si vede anche nel PDF. | +| `` | `layout: domanda`, con la risposta in `::risposta::`. Per una risposta fuori da quel layout: `` … ``. | +| ``, `` | ``, ``. | +| `` con due schede | `layout: due-colonne` con `sinistra:` e `destra:` come etichette. | +| `` | `layout: quiz` e ``. Il `perche` è il ``, accorciato; accetta `codice` e **grassetto**. | +| `parola` | La parola in **grassetto**; la definizione, se serve, nella frase. | +| `` | Un blocco ` ```json ` con le righe che servono (al massimo una decina). | +| `` | `layout: epigrafe` con `autore:`. | + +Nella stampa (e in `slides:pdf`) i pulsanti «Esegui» spariscono, le figure si fermano sul passo finale, le risposte dei layout `domanda` e le spiegazioni dei quiz sono già aperte. + +### 6. Misure: che cosa sta in una slide + +- **Titolo**: al massimo due righe; meglio una. È una frase che dice di che cosa parla la slide («Senza chiave, i punti contano due volte»), non un'etichetta («Esempio»). +- **Testo**: al massimo 60 parole per slide, esclusi codice e callout. +- **Elenchi**: da tre a cinque voci, una riga o due ciascuna. +- **Codice**: al massimo 12 righe se il blocco è da solo sotto il titolo, 10 se c'è anche una frase, 16 come limite assoluto (lo controlla `slides:check`). Le righe stanno sotto i 60 caratteri, sotto i 45 in una colonna di `due-colonne`. Se il codice della lezione è più lungo, si divide su più slide oppure si mostrano solo le righe che contano. Per guidare lo sguardo su un blocco si evidenziano le righe per passi: ` ```python {1-3|5|all} `. +- **Codice eseguibile**: con `` e `` la frase di spiegazione va **prima** del blocco, perché sotto compare l'output. Se il blocco supera le 8 righe si usa `uscita="destra"`. +- **Callout**: uno per slide, due se sono brevi. +- **Nomi con il doppio trattino basso** (`__init__`, `__str__`, `_Brano__durata`): sempre tra backtick, anche nei titoli. Fuori dal codice il markdown li legge come grassetto e `# __str__` diventerebbe «str» (lo controlla `slides:check`). +- **Diagrammi mermaid**: solo nel layout `figura`, mai in una colonna; orientati in orizzontale (`flowchart LR`) perché la slide è larga e bassa. Per due gruppi da mostrare uno sopra l'altro: `flowchart TB` con `direction LR` dentro ogni `subgraph`, e il primo gruppo scritto per primo. +- **Animazioni**: `v-click` per far comparire le voci di un elenco una alla volta solo quando l'ordine conta (una dimostrazione, un ragionamento a passi). Non su ogni slide. + +### 7. Controlli e verifiche: obbligatori prima di consegnare + +Un mazzo è finito solo quando passa questi controlli, nell'ordine. Chi genera il mazzo (persona o IA) li esegue da sé e corregge fino a che passano: non li lascia a chi rivede. + +1. **Struttura**: `npm run slides:check -- /`. Controlla il front matter iniziale (`theme`, `title`, `volume` e `lezione` coerenti con il nome del file, la lezione che esiste), la scaletta (copertina in testa, `fine` in coda), che ogni layout esista nel tema e abbia i suoi slot (`::risposta::`, `::figura::`, `::sinistra::`/`::destra::`, `::prerequisiti::`/`::imparerai::`), che ogni quiz abbia una sola `` e la domanda nel titolo, che le figure (`src/figure/`) e i dataset (`static/sql-datasets/`) citati esistano, che nessun blocco di codice superi le 16 righe. Ogni errore dice slide e motivo. +2. **Build**: `npm run slides:build -- /`. Se Slidev non compila, l'errore indica il file e la riga: di solito un tag non chiuso o una riga vuota mancante attorno al markdown dentro un componente. +3. **Verifica visiva**: `npm run slides:verifica -- /`. Misura ogni slide costruita e segnala: + - `straborda di N px in basso`: il contenuto supera il corpo e copre il piede. Si toglie testo, si accorcia il codice, si divide la slide in due. **Non** si rimpicciolisce il testo e non si aggiungono stili; + - `codice più largo del riquadro`: una riga di codice non entra. Si va a capo nel codice (dentro parentesi, come farebbe un programmatore) o si accorciano i nomi negli esempi, senza cambiarne il senso; + - `figura ridotta sotto il 60%`: per entrare nello spazio la figura è stata rimpicciolita tanto da non leggersi da un proiettore. Un diagramma E/R si dispone in orizzontale (entità e rombi sulla stessa `riga`, `col` diverse), oppure si toglie lo slot `::lato::` per dargli tutta la larghezza; + - `contenuto che esce di lato`: un testo, una tabella o un codice in linea supera il bordo destro del suo callout, della sua colonna o della slide. Il codice in linea va già a capo da solo; se la segnalazione resta, di solito c'è una tabella troppo larga o un'espressione lunga che sta meglio in un blocco di codice. + - `il risultato aperto non sta nella slide`: premendo «Esegui» l'output non entrerebbe per intero sotto il codice. Si aggiunge `seguito` al blocco: il risultato avrà una slide sua, a tutta pagina; + - `il risultato non sta nemmeno nella sua slide`: anche a tutta pagina l'output è troppo lungo. Si riducono le righe (`LIMIT`), le tabelle mostrate da `database`, o le stampe del programma; + - `blocco N: va in errore`: la verifica preme «Esegui» su ogni `` e ``, e quel blocco è finito in errore. Di solito manca un import o una variabile definita in un'altra slide. Se l'errore è voluto, il blocco va dichiarato con `errore`; + - `doveva andare in errore`: il blocco è dichiarato `errore` ma è andato a buon fine. + Si ripetono build e verifica finché la lezione risulta `✓`. +4. **Revisione visiva sul provino**: `npm run slides:provino -- /`, poi si guarda l'immagine slide per slide. La verifica misura le dimensioni, ma non vede tutto: titoli storpiati dal markdown, backtick rimasti visibili, diagrammi nell'ordine sbagliato, slide troppo vuote o troppo piene rispetto alle vicine, elenchi che vanno a capo male. Chi genera il mazzo apre il provino e corregge quello che trova; un'IA che può leggere immagini lo fa da sé. +5. **Prova in classe**: `npm run slides:dev -- /`, poi si scorre tutto il mazzo. Le figure si animano, i pulsanti «Esegui» danno lo stesso risultato della lezione, le risposte dei layout `domanda` e dei quiz compaiono al clic, nessuna slide ha un'aria vuota o affollata rispetto alle altre. + +La verifica misura le slide con tutti i clic fatti, poi esegue ogni blocco come farebbe l'insegnante e misura anche il risultato aperto: deve leggersi per intero, senza barre di scorrimento. + +Quando un'IA genera una lezione nuova, la consegna comprende la lezione, il mazzo e l'esito dei controlli 1-4 riportato nella descrizione della pull request. + +## Mantenere il tema + +Il tema è pensato perché i mazzi non lo tocchino mai. Chi lo modifica tiene presente che: + +- i token di colore e i font (`theme/styles/token.css`) copiano quelli di `src/css/custom.css`: se cambiano nel libro, vanno cambiati anche qui; +- la griglia (margini, testata, piede) sta tutta in `theme/components/Cornice.vue` e `theme/styles/cornice.css`; +- i componenti React del libro entrano nelle slide attraverso `theme/components/IsolaReact.vue`, e `theme/vite.config.ts` spiega che cosa serve per compilarli; +- un layout nuovo va documentato nella tabella qui sopra e aggiunto a `slides/decks/_modello.md`. diff --git a/slides/decks/_modello.md b/slides/decks/_modello.md new file mode 100644 index 0000000..d5f58bc --- /dev/null +++ b/slides/decks/_modello.md @@ -0,0 +1,162 @@ +--- +# Modello di mazzo: copialo in slides/decks//.md e riempilo. +# Le regole sono in slides/README.md («Istruzioni per l'IA»). +theme: ../../theme +title: Titolo della lezione +sottotitolo: Una o due frasi prese dalla description della lezione. +volume: archivista +capitolo: 1 +numero: 2 +lezione: archivista/slug-della-lezione +--- + +--- +layout: epigrafe +autore: Autore, Opera (anno) +--- + +Il testo della citazione d'apertura della lezione. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Primo prerequisito, accorciato dal :::prereq +- Secondo prerequisito + +::imparerai:: + +- Primo obiettivo, dal :::learn +- Secondo obiettivo + +--- +layout: sezione +--- + +# Titolo del primo paragrafo (##) + +Una frase che dice di che cosa parla il paragrafo. + +--- + +# Una slide di contenuto: il titolo è una frase + +Testo breve, un'idea sola. Al massimo 60 parole. + +- Da tre a cinque voci +- Una o due righe ciascuna +- Il **grassetto** per i termini nuovi + + + +Il contenuto del callout, in markdown. + + + +--- + +# Codice che si esegue in classe + + + +```python +titoli = ["Blinding Lights", "Bohemian Rhapsody"] +for titolo in titoli: + print(titolo) +``` + + + +--- +layout: domanda +--- + +# Prima di eseguire, prova a prevedere + + + +```sql +SELECT nome, casa FROM studente; +``` + + + +::risposta:: + +La risposta, come nello della lezione. + +--- +layout: figura +--- + +# Il titolo della figura + +::figura:: + + + +--- +layout: due-colonne +sinistra: Prima scheda +destra: Seconda scheda +--- + +# Un confronto + +::sinistra:: + +Contenuto di sinistra. + +::destra:: + +Contenuto di destra. + +--- +layout: definizione +titolo: Termine +--- + +La definizione, dal :::definition della lezione. + +--- +layout: enunciato +--- + +L'idea che la slide deve lasciare, con il **punto chiave** in grassetto. + +Una nota più piccola che la spiega. + +--- +layout: sintesi +--- + +# In sintesi + + + +- I punti del :::nutshell della lezione + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# La domanda del quiz? + +Una risposta sbagliata. +La risposta giusta. +Un'altra risposta sbagliata. + +--- +layout: fine +--- + +# La prossima lezione + +Due righe dal :::tip[La prossima lezione]. diff --git a/slides/decks/archivista/associazioni-molti-a-molti.md b/slides/decks/archivista/associazioni-molti-a-molti.md new file mode 100644 index 0000000..af71c86 --- /dev/null +++ b/slides/decks/archivista/associazioni-molti-a-molti.md @@ -0,0 +1,283 @@ +--- +theme: ../../theme +title: Le associazioni molti a molti +sottotitolo: Un’associazione molti a molti non sta in un attributo, e i suoi attributi appartengono alla coppia. Ogni coppia però compare una volta sola, e quando lo stesso fatto si ripete serve un’entità. +volume: archivista +capitolo: 2 +numero: 9 +lezione: archivista/associazioni-molti-a-molti +--- + +--- +layout: epigrafe +autore: Alexandre Dumas, I tre moschettieri (1844) +--- + +Tous pour un, un pour tous. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Le cardinalità e i tre tipi di associazione +- L'identificatore di `corso`, materia e anno, e quello assegnato apposta +- Il registro dei punti e la tabella `assegnazione` + +::imparerai:: + +- Perché un'associazione **molti a molti** non sta in un attributo +- Decidere se un attributo è di un'entità o **dell'associazione** +- Perché una coppia compare una volta sola, e quando serve un'**entità** + +--- +layout: sezione +--- + +# I corsi in una colonna + +Ogni studente segue più corsi, e ogni corso ha più studenti. Proviamo con una colonna. + +--- + +# Tutti i corsi in una cella + +Come per la casa, una colonna nella tabella `studente`: i corsi stanno nella stessa cella, separati da virgole. + + + +--- +layout: domanda +invito: Quanti nomi elenca? +--- + +# Chi segue Pozioni? + +Il nome degli studenti per cui la colonna `corsi` è uguale a `Pozioni`: + + + +```sql +SELECT nome FROM studente +WHERE corsi = 'Pozioni'; +``` + + + +::risposta:: + +**Nessuno**, anche se tutti e nove seguono Pozioni. La cella contiene `Erbologia, Incantesimi, Pozioni, Trasfigurazione`: un testo che contiene Pozioni senza essere Pozioni. + +--- +layout: enunciato +--- + +Il DBMS non ha sbagliato: l'errore è **quattro coppie in una cella sola**. + +Ogni domanda sui corsi diventa un lavoro sul testo, che troverebbe Pozioni anche in «Pozioni avanzate». E una domanda non ha risposta in nessun modo: **che voto ha preso Hermione in Pozioni?** + +--- +layout: sezione +--- + +# Il voto è della coppia + +Né di Hermione, né del corso di Pozioni. + +--- +layout: definizione +titolo: Attributo di un'associazione +--- + +Un **attributo di un'associazione** ha un valore per ogni **coppia**, e non per le occorrenze delle due entità prese una per una. + +Il voto di Hermione in Pozioni esiste perché Hermione segue quel corso: Hermione ha un voto per ogni corso, il corso un voto per ogni studente. Il voto appartiene alla coppia (Hermione Granger, Pozioni del primo anno). + +--- +layout: figura +--- + +# L'iscrizione, con il voto + +::lato:: + +- Con attributi suoi, il nome naturale è spesso un **sostantivo**: l'**iscrizione**. +- `voto` esce dal rombo, come gli attributi di un'entità escono dal rettangolo. +- Ogni studente ha almeno un corso, `(1,N)`; un corso può non avere ancora iscritti, `(0,N)`. + +::figura:: + + + +--- + +# Una riga per ogni coppia + +Nelle tabelle, un'associazione molti a molti diventa una **tabella propria**, con una riga per ogni coppia. Come si costruisce, lo vedremo nel capitolo 3. + + + +--- +layout: domanda +invito: In quale materia Neville ha il voto più alto? +--- + +# I voti di Neville + + + +```sql +SELECT materia, voto FROM iscrizione +WHERE studente = 'Neville Longbottom'; +``` + + + +::risposta:: + +**Erbologia**, 10; in Pozioni ha 4, e chi l'ha visto sciogliere un calderone non se ne stupisce. Lo stesso Neville compare in quattro coppie, con quattro voti: il voto sta nella coppia. + +--- +layout: sezione +--- + +# La stessa coppia due volte + +Il registro dei punti sembra un'altra associazione molti a molti, con l'attributo `punti`. + +--- +layout: enunciato +--- + +Piton toglie un punto a Harry per insolenza, e poi **un altro** per non aver aiutato Neville. + +Un'associazione è un insieme di coppie: (Piton, Harry Potter) c'è oppure non c'è. Non può esserci due volte. + +--- +layout: figura +--- + +# La stessa coppia, due volte + +::figura:: + + + +--- + +# Le scorciatoie non funzionano + +- **Sommare** i due punti in −2 perde un'informazione: sono stati due volte uno, in due momenti diversi. +- **Aggiungere la data** non cambia niente: l'occorrenza resta la coppia, e la data sarebbe una sola. + + + +Quando lo stesso fatto può ripetersi per la stessa coppia, non è un'associazione: è un'**entità**, con un'occorrenza per ogni volta che succede. Le occorrenze si distinguono con un identificatore assegnato apposta, come l'`id` della tabella `assegnazione`. + + + +--- +layout: figura +didascalia: Ogni assegnazione è ricevuta da uno studente e data da un professore; le due di Piton a Harry hanno due id diversi. +--- + +# L'assegnazione come entità + +::figura:: + + + +--- +layout: enunciato +--- + +La domanda per scegliere: **la stessa coppia può comparire più di una volta?** + +Uno studente si iscrive a un corso una volta sola: l'iscrizione è un'associazione. Un professore può dare punti allo stesso studente quante volte vuole: l'assegnazione è un'entità. + +--- +layout: sintesi +--- + +# In sintesi + + + +- Una molti a molti non sta in un attributo: più valori in una cella rendono ogni domanda un lavoro sul testo. +- Un **attributo dell'associazione** ha un valore per ogni coppia: il voto è dell'iscrizione. +- Con attributi, il nome è spesso un sostantivo: l'iscrizione. +- Un'associazione è un insieme di coppie: ogni coppia compare **una volta sola**. +- Se lo stesso fatto si ripete per la stessa coppia, è un'**entità**, legata alle altre da due uno a molti. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# Quale di questi attributi appartiene all'associazione `iscrizione` fra `studente` e `corso`? + +L'aula in cui si tiene il corso. +La data in cui lo studente si è iscritto al corso. +L'anno che lo studente frequenta. +Il nome del professore che tiene il corso. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Lo stesso studente può prendere in prestito lo stesso libro più volte, in giorni diversi. Come si modella il prestito? + +Associazione molti a molti con l'attributo `data`. +Un'entità `prestito` con `data`, legata a studente e libro da due uno a molti. +Un attributo `libri_presi` con i titoli separati da virgole. +Associazione con un attributo `numero_prestiti`. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Un compagno sostituisce la colonna `corsi` con `corso_1`, `corso_2`, `corso_3` e `corso_4`. Che cosa resta da risolvere? + +Niente: ogni cella contiene un valore solo. +Solo i nomi delle colonne. +Il quinto corso non ci sta, Pozioni va cercata in quattro colonne, e il voto non ha posto. +L'identificatore dello studente. + +--- +layout: fine +--- + +# La prossima lezione + +Hai tutti gli elementi del modello E/R. Finora il dominio è cresciuto una richiesta alla volta: ora partiamo da un testo che descrive un dominio intero, la **biblioteca di Hogwarts**, e lo trasformiamo in un modello con un metodo ripetibile. diff --git a/slides/decks/archivista/che-cosa-fa-un-dbms.md b/slides/decks/archivista/che-cosa-fa-un-dbms.md new file mode 100644 index 0000000..95248ae --- /dev/null +++ b/slides/decks/archivista/che-cosa-fa-un-dbms.md @@ -0,0 +1,388 @@ +--- +theme: ../../theme +title: Che cosa fa un DBMS al posto tuo +sottotitolo: Un database non è un file più grosso. Il DBMS risponde alle domande, incrocia le tabelle, rifiuta un nome che non esiste e coordina chi scrive insieme. +volume: archivista +capitolo: 1 +numero: 4 +lezione: archivista/che-cosa-fa-un-dbms +--- + +--- +layout: epigrafe +autore: E. F. Codd, A Relational Model of Data for Large Shared Data Banks (1970) +--- + +Future users of large data banks must be protected from having to know how the data is organized in the machine (the internal representation). + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- `punti.json`, `studenti.json` e l'incrocio con un dizionario +- La ridondanza e la regola «un fatto, un posto» +- Leggere e riscrivere un file JSON con `json` + +::imparerai:: + +- Perché un database non è un file più grosso +- Una domanda che descrive il risultato contro un programma che descrive i passi +- Quando un **vincolo** rifiuta un dato e quando lo lascia passare +- Riconoscere un **aggiornamento perso** + +--- +layout: enunciato +--- + +Con i file si sceglie fra **copie che si contraddicono** e **incroci scritti a mano**. E resta una domanda: che cosa succede se due professori scrivono insieme? + +Mettiamo il registro in un database e guardiamo, un problema alla volta, che cosa fa il DBMS al posto del nostro programma. + +--- +layout: sezione +--- + +# Il registro in un database + +Il programma non apre più un file: manda una richiesta al DBMS e riceve la risposta. + +--- +layout: definizione +titolo: Database e DBMS +--- + +Un **database** è un insieme di dati organizzati secondo una struttura stabilita in anticipo, conservati in modo permanente e **condivisi** da più programmi e utenti. + +Un **DBMS** (_Database Management System_) è il software che lo gestisce: conserva i dati, risponde alle richieste e fa rispettare le regole sui dati. Il database sono i dati, il DBMS è il programma. + +--- + +# Tre tabelle, un fatto per posto + +- **`studente`**: nome, casa e anno, come `studenti.json`. +- **`professore`**: l'elenco dei professori, che nei file non c'era. +- **`assegnazione`**: chi ha ricevuto i punti, quanti e da chi. Senza la casa. + + + +--- +layout: figura +--- + +# Le tabelle si collegano attraverso i nomi + +::figura:: + + + +--- +layout: sezione +--- + +# Domande senza programma + +Al DBMS si fa una richiesta, una **query**, e lui risponde con una tabella. + +--- + +# Il totale di ogni casa + +La query si legge quasi come una frase. La sintassi la impareremo più avanti: per ora conta la forma. + + + +```sql +SELECT studente.casa, + SUM(assegnazione.punti) AS totale +FROM assegnazione +JOIN studente + ON studente.nome = assegnazione.studente +GROUP BY studente.casa +ORDER BY totale DESC; +``` + + + +La casa non sta in `assegnazione`: l'**incrocio** con `studente` lo fa il DBMS. + +--- +layout: due-colonne +sinistra: Con i file · i passi +destra: Con il DBMS · il risultato +--- + +# La domanda di Silente, in due modi + +::sinistra:: + +```python +anno_di = {} +for s in studenti: + anno_di[s["nome"]] = s["anno"] + +per_anno = {} +for a in registro: + anno = anno_di[a["studente"]] + per_anno[anno] = ( + per_anno.get(anno, 0) + a["punti"]) +``` + +::destra:: + + + +```sql +SELECT studente.anno, + SUM(assegnazione.punti) +FROM assegnazione +JOIN studente + ON studente.nome = + assegnazione.studente +GROUP BY studente.anno; +``` + + + +--- +layout: definizione +titolo: Dichiarativo e imperativo +--- + +Python descrive i **passi**: costruisci un dizionario, scorri il registro, accumula. È un linguaggio **imperativo**. + +SQL, il linguaggio delle query, descrive il **risultato**: in che ordine leggere le righe e dove accumulare lo sceglie il DBMS. È un linguaggio **dichiarativo**, come la Stanza delle Necessità: chiedi quello che ti serve, e come prepararlo è affar suo. + +--- + +# Ogni domanda è una query + +La domanda di McGonagall, che con i file era un programma nuovo. + + + +```sql +SELECT assegnazione.professore, + -SUM(assegnazione.punti) AS tolti +FROM assegnazione +JOIN studente + ON studente.nome = assegnazione.studente +WHERE studente.casa = 'Grifondoro' + AND assegnazione.punti < 0 +GROUP BY assegnazione.professore +ORDER BY tolti DESC; +``` + + + +Per il DBMS non esistono domande previste e domande nuove. E per trovare in fretta costruisce da solo un **indice**, aggiornato a ogni inserimento. + +--- +layout: sezione +--- + +# Il nome che non esiste + +Nel file JSON «Neville Logbottom» è entrato senza avvisi. E nel database? + +--- +layout: domanda +affiancata: true +invito: Che cosa fa il DBMS con il nome scritto male? +--- + +# Un'assegnazione con il nome sbagliato + +Inserisci nella tabella `assegnazione`, nelle colonne `studente`, `punti` e `professore`, questi valori: + + + +```sql +INSERT INTO assegnazione + (studente, punti, professore) +VALUES ('Neville Logbottom', 5, 'Sprout'); +``` + + + +::risposta:: + +Lo **rifiuta**: `FOREIGN KEY constraint failed`. Il nome `Neville Logbottom` non compare nella tabella `studente`, e la riga non entra. + +--- +layout: definizione +titolo: Vincolo +--- + +Una regola sui dati che il DBMS fa rispettare a **ogni scrittura** si chiama **vincolo**. Quello che lega una colonna alle righe di un'altra tabella è un vincolo di **chiave esterna**. + +Con i file l'errore arrivava a chi leggeva, magari mesi dopo. Con il DBMS arriva **a chi scrive, mentre scrive**, e vale per qualunque programma, anche per quello che qualcuno scriverà l'anno prossimo. + +--- + +# Un nome che esiste non è il nome giusto + + + +- `'Neville Logbottom'` → rifiutato: non esiste. +- `'Harry Potter'` → **accettato** senza avvisi: i punti di Neville finiscono a Harry. +- `'Neville Longbottom'` → accettato, ed è il nome giusto. + + + +Il DBMS ferma i dati **impossibili**, non quelli sbagliati ma possibili. A chi andassero davvero i punti lo sa solo chi li ha assegnati. + +--- +layout: domanda +affiancata: true +invito: Lupin non è fra i professori. Come fai accettare la sua assegnazione, senza cambiarla? +--- + +# La prima assegnazione di Lupin + + + +```sql +INSERT INTO professore (nome) +VALUES ('Lupin'); +INSERT INTO assegnazione + (studente, punti, professore) +VALUES ('Neville Longbottom', 10, 'Lupin'); +``` + + + +::risposta:: + +Prima Lupin entra in `professore`, poi la sua assegnazione. Con i file un professore esisteva solo se dava punti; qui **non può dare punti finché non esiste**. Il vincolo stabilisce l'ordine delle scritture. + +--- +layout: sezione +--- + +# Due professori nello stesso momento + +La domanda lasciata in sospeso nella prima lezione. + +--- +layout: domanda +affiancata: true +invito: Il file parte da 18 assegnazioni. Quante ne contiene alla fine? +--- + +# Due salvataggi sullo stesso file + + + +```python +import json +from archivio_hogwarts import leggi_file +disco = leggi_file("punti.json") +mcgonagall = json.loads(disco) +piton = json.loads(disco) +mcgonagall.append({"studente": "Ron Weasley", "punti": -5}) +piton.append({"studente": "Draco Malfoy", "punti": 5}) +disco = json.dumps(mcgonagall) +disco = json.dumps(piton) +registro = json.loads(disco) +print(len(registro), registro[-1]) +``` + + + +::risposta:: + +**19**, non 20: Piton ha salvato sopra una copia letta prima che McGonagall salvasse la sua. L'assegnazione di Ron è sparita. + +--- +layout: figura +--- + +# L'ultimo che salva cancella l'altro + +::figura:: + + + +--- +layout: definizione +titolo: Aggiornamento perso +--- + +Due programmi leggono lo stesso dato, lo modificano ciascuno per conto suo, e l'**ultimo che scrive cancella il lavoro dell'altro**. Nessuna istruzione dà errore. + +Nella realtà l'ordine dipende da chi salva per ultimo: a volte succede e a volte no, e un errore che succede solo a volte è il più difficile da trovare. + +--- +layout: figura +--- + +# Con il DBMS, nessuno riscrive il registro + +::figura:: + + + +--- +layout: sintesi +--- + +# In sintesi + + + +- Un database non è un file più grosso: il DBMS fa lavori che prima stavano in ogni programma. +- Al DBMS si manda una **query**; gli incroci li fa lui. +- Una query descrive il risultato (**dichiarativo**), un programma i passi (**imperativo**). +- Un **vincolo** è una regola che il DBMS fa rispettare a ogni scrittura, per qualunque programma. +- Il vincolo controlla che un nome esista, non che sia giusto, e decide l'ordine delle scritture. +- Riscrivere lo stesso file in due può perdere una scrittura in silenzio: l'**aggiornamento perso**. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# Sprout scrive per sbaglio cinque punti a `'Hermione Granger'` invece che a Neville. Che cosa fa il DBMS? + +Rifiuta con `FOREIGN KEY constraint failed`. +Accetta l'inserimento. +Accetta, ma avvisa che Hermione ha altre assegnazioni. +Chiede conferma prima di scrivere. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Perché, con un database, le assegnazioni di McGonagall e Piton non si perdono? + +Il DBMS fa una copia di sicurezza prima di ogni salvataggio. +Ogni programma manda solo la sua riga, e il DBMS la aggiunge senza riscrivere le altre. +Il DBMS è così veloce che i salvataggi non si sovrappongono. +Il DBMS accetta un solo utente alla volta. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Un compagno dice che un database è un file JSON più grande e più veloce. Quale fatto della lezione lo smentisce? + +Le tabelle hanno colonne con un nome. +Il DBMS ha rifiutato un'assegnazione a uno studente che non esiste. +Il DBMS mostra le risposte come tabelle. +I dati stanno in più tabelle invece che in un file. + +--- +layout: fine +--- + +# Il prossimo capitolo + +Le tre tabelle di questa lezione sono arrivate già pronte. Si parte dal problema che viene prima: dato un dominio, come si decide **quali tabelle servono**? Si comincia dalle **entità** e dai loro **attributi**. diff --git a/slides/decks/archivista/dati-fuori-dal-programma.md b/slides/decks/archivista/dati-fuori-dal-programma.md new file mode 100644 index 0000000..ea40ea4 --- /dev/null +++ b/slides/decks/archivista/dati-fuori-dal-programma.md @@ -0,0 +1,453 @@ +--- +theme: ../../theme +title: I dati che non stanno più nel programma +sottotitolo: Un registro salvato in JSON conserva i dati ma non risponde a nessuna domanda. Ogni domanda nuova è codice da scrivere, e ogni risposta costa la lettura dell’intero file. +volume: archivista +capitolo: 1 +numero: 2 +lezione: archivista/dati-fuori-dal-programma +--- + +--- +layout: epigrafe +autore: Albus Silente, Harry Potter e il Calice di Fuoco +--- + +I sometimes find, and I am sure you know the feeling, that I simply have too many thoughts and memories crammed into my mind. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Liste, dizionari e cicli `for` +- Funzioni con parametri +- Salvare e rileggere una lista di dizionari con il modulo `json` + +::imparerai:: + +- Distinguere dove stanno i dati da dove sta il codice che risponde +- Stimare quanto costa una domanda nuova su un archivio JSON +- Incrociare due file con un dizionario costruito apposta +- Perché, con un file, la risposta costa quanto il file + +--- +layout: enunciato +--- + +Con JSON i dati **escono dal programma** e sopravvivono al suo spegnimento. + +Ora guardiamo che cosa succede quando quel file comincia a servire: qualcuno fa domande, le domande cambiano, il file cresce. Il nostro esempio è il registro dei punti delle case di Hogwarts. + +--- +layout: sezione +--- + +# Il registro delle case + +Ogni volta che un professore assegna o toglie punti, il registro riceve una riga. + +--- +layout: due-colonne +rapporto: 1/1 +--- + +# Una riga per ogni assegnazione + +::sinistra:: + +```json +[ + {"studente": "Hermione Granger", + "casa": "Grifondoro", + "punti": -5, + "professore": "McGonagall"}, + {"studente": "Harry Potter", + "casa": "Grifondoro", + "punti": 5, + "professore": "McGonagall"}, + ... +] +``` + +::destra:: + +`punti.json` contiene le 18 assegnazioni del primo anno di Harry che ci servono. + +Per rileggerlo bastano tre righe del Volume 1: + +```python +import json + +with open("punti.json") as f: + registro = json.load(f) +``` + +--- +layout: figura +--- + +# Il file su disco, la copia in memoria + +::figura:: + + + +--- +layout: sezione +--- + +# Ogni domanda è un programma + +Il file conserva i dati. Chi risponde alle domande? + +--- +layout: domanda +affiancata: true +invito: Quanti punti ha Grifondoro? Prova a stimarlo prima di eseguire. +--- + +# La domanda di McGonagall + + + +```python +import json +from archivio_hogwarts import leggi_file + +registro = json.loads(leggi_file("punti.json")) + +totale = 0 +for assegnazione in registro: + if assegnazione["casa"] == "Grifondoro": + totale += assegnazione["punti"] +print(f"Grifondoro: {totale} punti") +``` + + + +::risposta:: + +**Tre punti**. I 170 di Silente coprono quasi da soli i 150 tolti in una notte da McGonagall. + +--- +layout: domanda +affiancata: true +invito: Fra i Grifondoro, chi è costato più punti alla casa? +--- + +# Una seconda domanda, un altro programma + + + +```python +import json +from archivio_hogwarts import leggi_file +registro = json.loads(leggi_file("punti.json")) + +persi = {} +for a in registro: + if a["casa"] == "Grifondoro" and a["punti"] < 0: + nome = a["studente"] + persi[nome] = persi.get(nome, 0) - a["punti"] + +print(persi) +print("Il peggiore:", max(persi, key=persi.get)) +``` + + + +::risposta:: + +**Hermione**, con 55 punti tolti, davanti a Harry (52) e Neville (50). La differenza la fanno i cinque punti del troll. + +--- + +# Il massimo di un dizionario + + + +- `max(persi)` → `"Neville Longbottom"`: confronta le chiavi, è l'ultimo nome in ordine alfabetico. +- `max(persi.values())` → `55`: il valore più alto, ma non dice di chi è. +- `max(persi, key=persi.get)` → il nome con più punti persi. + + + +--- +layout: enunciato +--- + +Il file JSON **non risponde a nessuna domanda**: la capacità di rispondere sta tutta nel codice. + +I due programmi hanno in comune solo la lettura del file; ciclo, condizione e struttura che accumula cambiano a ogni domanda. Se McGonagall chiede quale professore ha tolto più punti, la risposta arriverà quando qualcuno avrà scritto un terzo programma. + +--- + +# Prova a essere quel qualcuno + +Copia il programma precedente e fallo rispondere a: **quale professore ha tolto più punti a Grifondoro?** + + + +- Basta cambiare una parola: `a["professore"]` al posto di `a["studente"]`. +- E l'etichetta dell'ultimo `print`, perché dica il vero. +- Risultato: **McGonagall**, 155 punti tolti contro i 22 di Piton. +- Eppure è un programma nuovo: qualcuno ha dovuto scriverlo, eseguirlo e controllarlo. + + + +--- +layout: sezione +--- + +# Una domanda che attraversa due file + +Il registro dice di chi sono i punti, ma non in che anno è lo studente. + +--- +layout: due-colonne +rapporto: 2/3 +--- + +# Due file, legati solo dal nome + +::sinistra:: + +```json +[ + {"nome": "Harry Potter", + "casa": "Grifondoro", + "anno": 1}, + {"nome": "Hermione Granger", + "casa": "Grifondoro", + "anno": 1}, + ... +] +``` + +::destra:: + +Silente vuole i punti portati dagli studenti **di ciascun anno**. + +- i punti sono in `punti.json` +- gli anni sono in `studenti.json` +- l'unico legame è il **nome** dello studente, scritto uguale in entrambi + +--- + +# Un dizionario costruito apposta + +`anno_di` porta dal nome all'anno: non sta in nessun file, esiste solo per l'incrocio. + + + +```python +import json +from archivio_hogwarts import leggi_file +registro = json.loads(leggi_file("punti.json")) +studenti = json.loads(leggi_file("studenti.json")) +anno_di = {} +for s in studenti: + anno_di[s["nome"]] = s["anno"] +per_anno = {} +for a in registro: + anno = anno_di[a["studente"]] + per_anno[anno] = per_anno.get(anno, 0) + a["punti"] +print(per_anno) +``` + + + +--- +layout: figura +--- + +# Il ponte fra i due file + +::figura:: + + + +--- + +# Il legame esiste solo nel codice + +```python +registro.append({"studente": "Neville Logbottom", "casa": "Grifondoro", + "punti": 5, "professore": "Sprout"}) +``` + +- `KeyError`: il nome è scritto male. È il **caso fortunato**. +- Saltando i nomi sconosciuti, cinque punti sparirebbero **senza dire niente**. +- Copiare l'anno in ogni assegnazione scrive la stessa informazione **due volte**: è la prossima lezione. + +--- +layout: sezione +--- + +# Quando il registro cresce + +18 assegnazioni oggi, migliaia in un anno di tutta la scuola. + +--- +layout: domanda +affiancata: true +invito: Quante assegnazioni legge il programma la seconda volta? +--- + +# Cerchiamo Neville in due registri + + + +```python +import json +from archivio_hogwarts import leggi_file +def punti_di(nome, testo): + registro = json.loads(testo) + letti, trovati = 0, [] + for a in registro: + letti += 1 + if a["studente"] == nome: + trovati.append(a["punti"]) + print(f"{letti} lette: {trovati}") + +nome = "Neville Longbottom" +punti_di(nome, leggi_file("punti.json")) +punti_di(nome, leggi_file("punti_tutta_la_scuola.json")) +``` + + + +::risposta:: + +Tutte e **20.018**, per due numeri: non sa dove sono le righe di Neville. + +--- +layout: figura +--- + +# Il lavoro cresce con il file, non con la risposta + +::figura:: + + + +--- +layout: due-colonne +sinistra: Cercare in fretta +destra: Aggiungere una riga +--- + +# Le vie d'uscita fatte a mano costano + +::sinistra:: + +Un dizionario dal nome alle assegnazioni trova Neville subito: è un **indice**. Ma lo scrivi tu, uno per campo, e va rifatto a ogni modifica. + +::destra:: + +Un file JSON è una lista chiusa da `]`: non si aggiunge in fondo. + +Si **rilegge** e si **riscrive tutto**: 20.000 assegnazioni per aggiungerne una. E se due professori lo fanno insieme? + +--- + +# Aprire in scrittura svuota il file + + + +`open("punti.json", "w")` cancella il file **nel momento in cui lo apre**, prima che `json.dump` scriva qualcosa. Se il programma si interrompe a metà, per un valore che JSON non sa scrivere o per un computer che si spegne, il registro resta vuoto o troncato. + + + +Per questo il file si legge tutto **prima** di aprirlo in scrittura. + +--- +layout: sezione +--- + +# Che cosa vorremmo, invece + +I problemi della lezione, messi in fila, diventano una lista di richieste. + +--- + +# Le richieste a un nuovo strumento + + + +- Fare una domanda nuova senza scrivere un programma nuovo: dire **che cosa** vuoi sapere e lasciare ad altri il **come**. +- Collegare i dati di due archivi senza riscrivere ogni volta l'incrocio. +- Trovare due righe fra 20.000 senza leggere le altre, e aggiungerne una senza riscriverle tutte. + + + +Il software che fa tutto questo si chiama **DBMS** (_Database Management System_), ed è l'argomento di questo volume. + + +IMS, uno dei primi DBMS, nasce nel 1966 per il programma Apollo: teneva la distinta dei componenti del Saturn V. IBM lo sviluppa ancora oggi. + + +--- +layout: sintesi +--- + +# In sintesi + + + +- Un file JSON conserva i dati, ma non risponde: **le risposte stanno nel codice**. +- Ogni domanda nuova è un programma nuovo, spesso con una struttura diversa. +- Per incrociare due file serve un dizionario costruito apposta; il legame esiste solo nel codice. +- Per rispondere si legge l'intero file: il lavoro cresce con il file, non con la risposta. +- Un indice fatto a mano va scritto per ogni campo e rifatto a ogni modifica. +- Aggiungere un record significa rileggere e riscrivere tutto. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# McGonagall vuole sapere quale professore ha tolto più punti a Grifondoro. Che cosa serve? + +Aggiungere a `punti.json` il totale di ogni professore. +Un altro programma che accumula i punti tolti da ciascun professore. +Niente: `json.load` restituisce già i dati raggruppati. +Riordinare il file per professore. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Il registro della scuola ha 20.018 assegnazioni, Neville ne ha due. Quante ne legge `punti_di` per trovarle? + +Due, quelle di Neville. +Si ferma alla prima che trova. +Tutte e 20.018. +Circa metà, in media. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Aggiungi al registro un'assegnazione di `"Neville Logbottom"`, scritto male. Che cosa succede nel conto per anno? + +Il programma si ferma con un `KeyError`. +I punti finiscono nel primo anno, quello di Neville. +`json.loads` rifiuta il registro. +L'assegnazione viene ignorata in silenzio. + +--- +layout: fine +--- + +# La prossima lezione + +La casa di ogni studente è scritta due volte: in `punti.json`, a ogni assegnazione, e in `studenti.json`. Che cosa succede quando le due copie **smettono di dire la stessa cosa**? diff --git a/slides/decks/archivista/entita-e-attributi.md b/slides/decks/archivista/entita-e-attributi.md new file mode 100644 index 0000000..aa4b9a3 --- /dev/null +++ b/slides/decks/archivista/entita-e-attributi.md @@ -0,0 +1,339 @@ +--- +theme: ../../theme +title: Entità e attributi +sottotitolo: Che cosa diventa un’entità e che cosa resta un attributo non è una questione di gusto, lo decidono le domande a cui il database deve rispondere. +volume: archivista +capitolo: 2 +numero: 5 +lezione: archivista/entita-e-attributi +--- + +--- +layout: epigrafe +autore: G. E. P. Box, Robustness in the Strategy of Scientific Model Building (1979) +--- + +All models are wrong, but some are useful. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- La tabella `studente` con nome, casa e anno +- Ridondanza e anomalie di inserimento, aggiornamento e cancellazione +- Leggere un'istruzione al DBMS come una frase + +::imparerai:: + +- Che cos'è un **modello concettuale** e il modello **E/R** +- Decidere fra **entità** e **attributo**, con le domande del database +- I due errori opposti: l'entità nascosta e l'entità inutile +- Disegnare attributi **composti** e **opzionali**; riconoscere i **derivati** + +--- +layout: definizione +titolo: Dominio, modello concettuale, modello E/R +--- + +Il **dominio** è la parte di mondo di cui un database conserva i dati: per il registro dei punti, Hogwarts. + +Un **modello concettuale** stabilisce quali cose registrare, che cosa conservare di ciascuna e come sono legate, **prima delle tabelle**. Il più usato è il modello **Entità-Associazione** (_Entity-Relationship_, E/R), che si disegna in un diagramma. + +--- +layout: sezione +--- + +# Una casa che è solo un nome + +McGonagall chiede di registrare anche i colori e l'animale dello stemma di ogni casa. + +--- + +# Colori e animale dentro lo studente + +L'unico posto disponibile è la tabella `studente`, con due colonne nuove. È la **prima versione**. + + + +--- +layout: enunciato +--- + +`rosso e oro` è scritto **sei volte**, una per ogni Grifondoro, e il leone altrettante. + +È la ridondanza del capitolo 1: per cambiare i colori di Grifondoro bisogna correggere sei righe, e una dimenticata basta a dare alla casa due coppie di colori. + +--- +layout: domanda +affiancata: true +invito: Padma è l'unica di Corvonero. Quante case elenca la seconda istruzione? +--- + +# Cancelliamo Padma Patil + +Cancella la riga di Padma; poi elenca casa, colori e animale, ogni combinazione una volta sola. + + + +```sql +DELETE FROM studente +WHERE nome = 'Padma Patil'; +SELECT DISTINCT casa, colori_casa, + animale_casa +FROM studente; +``` + + + +::risposta:: + +**Tre**: Corvonero è sparita con Padma, insieme a blu e bronzo e all'aquila. Nella tabella `studente` una casa esiste solo finché qualcuno la nomina. + +--- +layout: sezione +--- + +# La casa come entità + +Colori e animale riguardano la casa: le diamo una tabella sua. + +--- +layout: due-colonne +--- + +# La seconda versione + +::sinistra:: + +La tabella `casa` ha nome, colori e animale. `studente` torna a nome, casa e anno: nella colonna `casa` c'è solo il nome, che rimanda a una riga di `casa`. + + + +::destra:: + + + +```sql +DELETE FROM studente +WHERE nome = 'Padma Patil'; +SELECT * FROM casa; +``` + + + +Le case restano **quattro**: la riga di Padma nominava soltanto Corvonero. + +--- +layout: definizione +titolo: Entità e attributo +--- + +Un'**entità** è un insieme di oggetti del dominio con le stesse proprietà, che **esistono per conto proprio**: gli studenti, le case. Ogni singolo oggetto, come Corvonero, è un'**occorrenza** dell'entità. + +Un **attributo** è una proprietà di un'entità, che per ogni occorrenza ha un valore: il nome di uno studente, i colori di una casa. + +--- + +# Il criterio per decidere + +Nella seconda versione la casa è un'**entità**; nella prima era un **attributo** dello studente. È diventata un'entità per due motivi: + + + +- di lei interessa **qualcosa oltre al nome**: i colori, l'animale; +- **esiste anche senza** gli studenti: Corvonero resta una casa anche senza Padma. + + + +Il criterio dipende dalle **domande** a cui il database deve rispondere, non dalla cosa in sé. Nel capitolo 1 nessuno chiedeva i colori, e la casa come testo andava bene. + +--- +layout: due-colonne +--- + +# Una classe senza metodi, e un po' di storia + +::sinistra:: + + + +Entità e occorrenza ricordano classe e istanza, con una differenza: un'entità ha **attributi e nessun metodo**. Che cosa fare con i dati lo decidono i programmi che usano il database. + + + +::destra:: + + + +Il modello E/R nasce con un articolo di Peter Chen, al MIT, nel primo numero di _ACM Transactions on Database Systems_: un rettangolo per ogni entità, un rombo per ogni associazione. I cerchi degli attributi arrivano dopo. + + + +--- +layout: sezione +--- + +# L'errore opposto + +Il criterio non dice di fare di ogni cosa un'entità. + +--- +layout: due-colonne +sinistra: In questo dominio +destra: In un altro dominio +--- + +# L'anno di corso: un attributo + +::sinistra:: + +Un'entità `anno` con la sola colonna `numero`, righe da 1 a 7, funziona: ma **non aggiunge nessuna informazione**. + +Una tabella in più da tenere aggiornata, per un dato che cambierà solo il giorno in cui Hogwarts aprirà un ottavo anno. + +::destra:: + +La classe di una scuola italiana, la **3A**, è un attributo finché interessa solo la sigla. + +Se la scuola vuole anche l'aula e il coordinatore, la classe diventa un'entità. Nessun modello registra tutto: è il senso della frase di Box. + +--- + +# Due segnali da riconoscere + + + +- `colori_casa` e `animale_casa` in `studente` → due attributi con la stessa parte di nome descrivono un'altra cosa: un'**entità nascosta**. +- un'entità `anno` con il solo `numero` → probabilmente è un **attributo** di un'altra entità. +- l'entità `casa` con nome, colori e animale; `studente` con nome e anno. + + + +Il **direttore** di una casa non è né l'uno né l'altro: è un legame fra due entità che esistono già, casa e professore. È un'**associazione**. + +--- +layout: sezione +--- + +# Disegnare entità e attributi + +Un rettangolo per l'entità, un segmento con un piccolo cerchio per ogni attributo. + +--- +layout: due-colonne +sinistra: Attributo composto +destra: Attributo opzionale +--- + +# Due decisioni prima di disegnare + +::sinistra:: + +«Quanti Weasley ci sono a Hogwarts?» Con il nome in un solo testo bisognerebbe tagliarlo dopo lo spazio. + +Il **nominativo** è formato da **nome** e **cognome**, ognuno con un significato proprio. + +::destra:: + +Il **Patronus**: Harry evoca il suo al terzo anno, e quasi nessuno del primo ne ha uno. + +Un attributo che può non avere un valore si segna **`(0,1)`**: minimo nessuno, massimo uno. Nelle tabelle, il valore che manca si chiamerà `NULL`. + +--- +layout: figura +--- + +# Lo studente e la casa, disegnati + +::figura:: + + + +--- + +# Che cosa manca, e perché + +- La colonna `casa` **non compare** fra gli attributi di `studente`, e le due entità non sono collegate: il legame è un'**associazione**, la prossima lezione. +- Tutti i cerchi sono **vuoti**: un cerchio pieno segna l'attributo che distingue un'occorrenza dalle altre. Lo vedremo nella lezione sulle **chiavi**. + + + +Si calcola dalle assegnazioni: è un attributo **derivato**. Memorizzarlo crea una seconda copia da aggiornare a ogni punto. Nei nostri diagrammi i derivati non compaiono: quando servono, si calcolano. + + + +--- +layout: sintesi +--- + +# In sintesi + + + +- Il **modello concettuale** decide che cosa registrare, prima delle tabelle; il modello **E/R** usa entità, attributi e associazioni. +- **Entità**: oggetti con le stesse proprietà e un'esistenza propria. **Attributo**: una proprietà con un valore per ogni occorrenza. +- È un'entità quando interessa altro oltre al nome ed esiste da sola. Decidono le domande. +- Attributi con la stessa parte di nome: entità nascosta. Entità con un solo attributo: probabilmente un attributo. +- Attributo **composto**: fatto di parti; **opzionale**: `(0,1)`; **derivato**: si calcola, non si memorizza. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# «Ogni studente ha un nome e frequenta un anno, dal primo al settimo.» Un compagno disegna le entità `studente`, `nome` e `anno`. Che cosa non va? + +Niente: ogni sostantivo del testo è un'entità. +Nome e anno sono attributi di `studente`: di loro non interessa altro. +`anno` va bene come entità, perché si ripete per molti studenti. +Manca l'entità `Hogwarts`, che contiene le altre. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Delle bacchette interessa solo il legno. Un compagno crea l'entità `bacchetta` con il solo attributo `legno`. Che cosa ne pensi? + +Va bene: la bacchetta è un oggetto fisico. +Va bene: lo stesso legno compare per più studenti. +Il legno è un attributo dello studente: l'entità non aggiunge informazione. +Va invertita: `legno` entità, `bacchetta` attributo. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Un compagno vuole aggiungere a `casa` l'attributo `totale_punti`, per leggere la classifica senza calcoli. Che cosa succede? + +Niente di male: è un'informazione sulla casa. +È un attributo derivato: crea una copia che ogni assegnazione deve aggiornare. +Va bene, se si ricalcola una volta al giorno. +Il totale dovrebbe essere un'entità collegata alla casa. + +--- +layout: fine +--- + +# La prossima lezione + +La colonna `casa` dello studente e il direttore di ogni casa non sono proprietà: sono **legami fra due entità**. Li disegniamo con il rombo del modello E/R, e vediamo che cosa deve esprimere un'**associazione**. diff --git a/slides/decks/archivista/generalizzazioni.md b/slides/decks/archivista/generalizzazioni.md new file mode 100644 index 0000000..8169255 --- /dev/null +++ b/slides/decks/archivista/generalizzazioni.md @@ -0,0 +1,302 @@ +--- +theme: ../../theme +title: Generalizzazioni +sottotitolo: Studenti e professori hanno nome, cognome e una bacchetta, e il modello li raccoglie sotto la persona. Ma una generalizzazione non ha una traduzione unica in tabelle, e ognuno dei tre modi perde qualcosa. +volume: archivista +capitolo: 2 +numero: 11 +lezione: archivista/generalizzazioni +--- + +--- +layout: epigrafe +autore: George Orwell, La fattoria degli animali (1945) +--- + +All animals are equal, but some animals are more equal than others. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Entità, attributi, identificatori assegnati apposta +- La `bacchetta` e l'associazione `possiede` +- L'archivio di tutti gli studenti passati da Hogwarts +- Ridondanza e tabelle unite con `JOIN` + +::imparerai:: + +- Riconoscere entità che sono casi particolari di una più generale +- Disegnare una **generalizzazione** e la sua **copertura** +- Che cosa le figlie **ereditano** dal padre +- Che cosa perde ciascuno dei tre modi di farne tabelle + +--- +layout: sezione +--- + +# Due entità con le stesse proprietà + +Anche Piton ha una bacchetta, come Harry. + +--- +layout: enunciato +--- + +Due associazioni `possiede`, nome e cognome due volte, il Patronus due volte: **ogni cosa nuova su tutte le persone va scritta due volte**. + +Studenti e professori sono casi particolari della stessa cosa: una persona che vive a Hogwarts. + +--- +layout: definizione +titolo: Generalizzazione ed ereditarietà +--- + +Una **generalizzazione** lega un'entità **padre** a una o più entità **figlie** che ne sono casi particolari: ogni occorrenza di una figlia è anche un'occorrenza del padre. Ogni studente è una persona; non ogni persona è uno studente. + +Le figlie **ereditano** attributi, identificatore e associazioni: uno studente ha un nome e possiede una bacchetta, anche se il diagramma lo scrive una volta sola, nel padre. Come l'ereditarietà fra classi Python, senza metodi. + +--- +layout: figura +didascalia: 'La freccia va dalle figlie al padre. A Hagrid hanno spezzato la bacchetta: accanto a persona, in possiede, il minimo è 0.' +--- + +# La persona generalizza lo studente e il professore + +::figura:: + + + +--- + +# Una generalizzazione non è un'associazione + + + +- un rombo `è` fra `studente` e `persona` → Harry diventa due occorrenze legate da una coppia; lo studente non eredita niente, e il nome va scritto due volte. +- una generalizzazione fra `persona` e `studente` → Harry è un'occorrenza sola, che appartiene a `persona` e a `studente` insieme. + + + +--- +layout: sezione +--- + +# La copertura + +Due lettere accanto alla freccia, che rispondono a due domande sul dominio. + +--- +layout: due-colonne +sinistra: Totale o parziale? +destra: Esclusiva o sovrapposta? +--- + +# Le due domande della copertura + +::sinistra:: + +Ogni persona registrata è per forza uno studente o un professore? + +**Hagrid** no: è il guardiacaccia. Se il database registra anche lui, la generalizzazione è **parziale**, `p`. Altrimenti **totale**, `t`. + +::destra:: + +Una persona può essere studente e professore insieme? + +In un anno scolastico no: **esclusiva**, `e`. Nell'archivio di tutti gli anni sì: **Piton** e **Lupin** hanno studiato e poi insegnato. È **sovrapposta**, `o`. + +--- +layout: definizione +titolo: Copertura +--- + +La copertura è **totale** se ogni occorrenza del padre appartiene ad almeno una figlia, **parziale** se alcune non appartengono a nessuna. È **esclusiva** se ogni occorrenza appartiene al massimo a una figlia, **sovrapposta** se può appartenere a più di una. + +Si scrive con due lettere: `(t,e)`, `(t,o)`, `(p,e)`, `(p,o)`. Come le cardinalità, non si ricava dai nomi: dipende da che cosa il database registra. + +--- +layout: figura +--- + +# Le persone di Hogwarts e le loro figlie + +::figura:: + + + +--- +layout: sezione +--- + +# Tre modi di farne tabelle + +Una tabella non ha figlie: prima di passare alle tabelle, la generalizzazione va tolta. + +--- +layout: due-colonne +rapporto: 3/2 +centro: true +--- + +# Solo il padre + +::sinistra:: + +| codice | nome | tipo | anno | materia | +|---|---|---|---|---| +| 1 | Harry | studente | 1 | | +| 2 | Severus | professore | | Pozioni | +| 3 | Rubeus | | | | + +::destra:: + +Le figlie spariscono nel padre, con una colonna `tipo`. + +- Hagrid ci sta, e `possiede` resta una. +- niente impedisce uno studente con una materia, o senza anno. +- un `tipo` solo non sa dire che Piton è stato studente. + +--- +layout: due-colonne +--- + +# Solo le figlie + +::sinistra:: + +| codice | nome | anno | +|---|---|---| +| 1 | Harry | 1 | + +| codice | nome | materia | +|---|---|---| +| 2 | Severus | Pozioni | + +::destra:: + +Il padre sparisce; ogni figlia riceve i suoi attributi. + +- le regole delle figlie restano. +- **Hagrid non ha una tabella** in cui stare. +- nell'archivio Piton compare due volte: è la ridondanza del capitolo 1. + +--- +layout: due-colonne +rapporto: 2/3 +centro: true +--- + +# Padre e figlie, collegati + +::sinistra:: + +| codice | nome | +|---|---| +| 1 | Harry | +| 2 | Severus | +| 3 | Rubeus | + +| codice | anno | +|---|---| +| 1 | 1 | + +::destra:: + +Restano tutte e tre; il codice di ogni figlia indica una riga di `persona`. + +- Hagrid sta solo in `persona`; nome e cognome scritti una volta. +- per nome e anno di uno studente serve un **incrocio** fra due tabelle. +- niente fa rispettare la copertura. + +--- + +# Che cosa conserva, che cosa perde + +| Modo | Conserva | Perde | +|---|---|---| +| solo il padre | le persone senza figlia, un'associazione sola | le regole delle figlie, la copertura sovrapposta | +| solo le figlie | le regole delle figlie | le persone senza figlia; nomi scritti due volte | +| padre e figlie | tutte le informazioni | la semplicità delle domande, il controllo della copertura | + +Nessuno va bene sempre: si sceglie guardando che cosa il dominio **non può permettersi di perdere**. + +--- +layout: sintesi +--- + +# In sintesi + + + +- Una **generalizzazione** lega un padre a figlie che ne sono casi particolari. +- Le figlie **ereditano** attributi, identificatore e associazioni; nel diagramma hanno solo i propri. +- Non è un'associazione: studente e persona sono la stessa occorrenza. +- La **copertura** è totale o parziale, esclusiva o sovrapposta, e dipende da che cosa si registra. +- Tre modi di farne tabelle: solo il padre, solo le figlie, padre e figlie. Ognuno perde qualcosa. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# Un database registra solo studenti e professori dell'anno in corso, sotto `persona`. Che copertura ha la generalizzazione? + +`(t,e)`: ognuno è studente o professore, nessuno tutti e due. +`(p,e)`: a Hogwarts c'è anche chi non è né l'uno né l'altro. +`(t,o)`: Piton è stato studente ed è professore. +Non si può dire senza vedere i dati. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Nel diagramma `studente` ha un solo attributo, `anno`. Uno studente ha un nome? + +No: il nome non compare fra i suoi attributi. +Sì: lo eredita da `persona`, con il codice e `possiede`. +Sì, ma va scritto anche in `studente`, per chiarezza. +Solo se la copertura è totale. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Il database deve registrare anche Hagrid, né studente né professore. Quale modo di fare le tabelle non può farlo? + +Solo il padre. +Solo le figlie. +Padre e figlie, collegati. +Nessuno: serve una figlia `guardiacaccia`. + +--- +layout: fine +--- + +# Il prossimo capitolo + +Il modello E/R è completo. Si riparte dalle **tabelle**: che cosa è davvero una tabella nel modello relazionale, e quali regole trasformano ogni elemento del diagramma in tabelle senza perdere informazione. diff --git a/slides/decks/archivista/le-associazioni.md b/slides/decks/archivista/le-associazioni.md new file mode 100644 index 0000000..23b9e8a --- /dev/null +++ b/slides/decks/archivista/le-associazioni.md @@ -0,0 +1,283 @@ +--- +theme: ../../theme +title: Le associazioni +sottotitolo: Un’associazione non è una linea fra due entità che hanno qualcosa a che fare. Registra un fatto preciso, ha un nome, si legge nei due versi e, se si ricava da altre, è una copia. +volume: archivista +capitolo: 2 +numero: 6 +lezione: archivista/le-associazioni +--- + +--- +layout: epigrafe +autore: Albus Silente, Harry Potter e il Calice di Fuoco (2000) +--- + +We are only as strong as we are united, as weak as we are divided. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Entità, attributo e occorrenza; `studente` e `casa` +- L'attributo derivato, che nei diagrammi non compare +- La casa ripetuta in ogni assegnazione del registro +- Leggere un `JOIN` come una frase + +::imparerai:: + +- Che cos'è un'**associazione** e una sua **occorrenza** +- Riconoscere un'associazione nascosta in un attributo +- Dare un nome all'associazione e leggerla nei due versi +- Distinguere un'associazione **derivata** da una che registra un fatto nuovo + +--- +layout: sezione +--- + +# La colonna che collega + +Nella tabella `studente`, `anno` e `casa` sembrano uguali. Non lo sono. + +--- +layout: due-colonne +sinistra: anno · descrive +destra: casa · collega +--- + +# Due colonne di natura diversa + +::sinistra:: + +Il **3** accanto a Fred Weasley è l'anno che Fred frequenta, e non rimanda a nient'altro. + +La colonna `anno` **descrive** lo studente. + +::destra:: + +La parola **Grifondoro** accanto a Fred è il nome di una riga di un'altra tabella, dove stanno colori e animale. + +La colonna `casa` **collega** lo studente a un'occorrenza di un'altra entità. Registra un fatto: _Fred Weasley appartiene a Grifondoro_. + +--- +layout: definizione +titolo: Associazione e occorrenza di associazione +--- + +Un'**associazione** è un legame fra due entità che il database deve registrare: `appartiene` lega `studente` a `casa`. + +Un'**occorrenza di associazione** è una coppia di occorrenze, una per entità, per cui il legame vale: (Harry Potter, Grifondoro). L'associazione è **l'insieme delle sue occorrenze**. + +--- +layout: figura +--- + +# Le coppie di appartiene + +::figura:: + + + +--- +layout: figura +--- + +# Un rombo con un verbo dentro + +::lato:: + +- Il nome è di solito un **verbo**: con i nomi delle entità forma una frase vera, _lo studente appartiene alla casa_. +- Nel diagramma è un **rombo**, collegato a ciascuna entità. +- La casa non è più un attributo di `studente`: il suo posto è il rombo. Le linee, per ora, non hanno numeri. + +::figura:: + + + +--- +layout: sezione +--- + +# Un fatto, due versi + +«Harry appartiene a Grifondoro» e «Grifondoro accoglie Harry» sono la stessa coppia. + +--- + +# Una frase girata non è un'altra associazione + + + +- due rombi fra `studente` e `casa`, `appartiene` e `accoglie` → le stesse coppie registrate due volte. È la ridondanza del capitolo 1. +- un solo rombo, `appartiene`, letto nei due versi. + + + +Due rombi fra le stesse entità però non sono sempre un errore. **`gioca`**, la squadra di Quidditch, ha coppie diverse da `appartiene`: anche Hermione appartiene a Grifondoro, ma il Quidditch preferisce studiarlo sui libri. **Confronta le coppie**: stesse coppie, un fatto; coppie diverse, due fatti. + +--- +layout: sezione +--- + +# Il direttore + +Un modo per riconoscere un'associazione anche senza conoscere il dominio. + +--- +layout: enunciato +--- + +Un attributo che contiene i **nomi delle occorrenze di un'altra entità** è un'associazione travestita. + +Una colonna `direttore` nella tabella `casa` conterrebbe `McGonagall`: il nome di un'occorrenza di `professore`, come `Grifondoro` nella riga di Fred è il nome di un'occorrenza di `casa`. + +--- +layout: figura +didascalia: Quattro coppie, una per casa. Silente, il preside, non dirige nessuna casa e non compare in nessuna coppia. +--- + +# L'associazione dirige + +::figura:: + + + +--- +layout: sezione +--- + +# L'associazione che si ricava + +Nel capitolo 1 il registro scriveva la casa dello studente in ogni assegnazione. + +--- +layout: definizione +titolo: Associazione derivata +--- + +Un'associazione le cui coppie si ottengono **sempre** passando da altre associazioni è **derivata**. La casa di un'assegnazione lo è: dall'assegnazione si arriva allo studente, dallo studente alla casa. + +Come l'attributo derivato, è una seconda copia di informazioni che il database contiene già, e può contraddire l'originale: i cinque punti di Neville finivano in due case. + +--- +layout: domanda +affiancata: true +invito: Se dirige si ricavasse dalle assegnazioni, ogni professore avrebbe una casa sola. Con quante case compare McGonagall? +--- + +# Un giro chiuso basta a dire «copia»? + +Il professore e la casa dello studente, senza ripetizioni, dalle assegnazioni unite agli studenti: + + + +```sql +SELECT DISTINCT assegnazione.professore, + studente.casa +FROM assegnazione +JOIN studente + ON studente.nome = assegnazione.studente +ORDER BY assegnazione.professore; +``` + + + +::risposta:: + +Con **due**: Grifondoro e Serpeverde, perché ha tolto venti punti a Draco. Anche Piton ne ha due, e Silente compare con Grifondoro, che non dirige. `dirige` **non si ricava**: il rombo serve. + +--- +layout: enunciato +--- + +Per dire che un'associazione **non** si ricava basta un'eccezione nei dati. Per dire che **si ricava**, i dati non bastano mai. + +Anche se oggi ogni professore desse punti solo alla casa che dirige, domani McGonagall potrebbe toglierne a Serpeverde. Serve una **regola del dominio**, e nei dati una regola non sta scritta: la conosce chi usa il database. + +--- +layout: sintesi +--- + +# In sintesi + + + +- Un'**associazione** è un legame fra due entità; una sua **occorrenza** è una coppia per cui il legame vale. +- Si disegna con un **rombo**; il nome è un verbo che forma una frase vera. +- Un attributo che contiene i nomi di occorrenze di un'altra entità è un'**associazione travestita**. +- Si legge nei due versi: due rombi per le stesse coppie sono ridondanza; per coppie diverse, sono due fatti. +- Un'associazione **derivata** si ricava sempre da altre: è una copia. Un'eccezione basta a escluderlo; per affermarlo serve una regola del dominio. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# Un compagno dà a `professore` l'attributo `casa_diretta`, che per McGonagall vale «Grifondoro». Che cosa non va? + +Niente: un professore dirige al massimo una casa. +Contiene il nome di un'occorrenza di `casa`: è l'associazione `dirige` travestita. +Serve un'entità `direttore`. +Va spostato in `casa`, con il nome `direttore`. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Due rombi fra `studente` e `casa`, `appartiene` e `accoglie`, hanno le stesse coppie. Che cosa ne pensi? + +Va bene: servono due rombi per i due versi. +Va bene, se uno dei due ha un attributo in più. +È la stessa associazione disegnata due volte: prima o poi si contraddicono. +È un errore solo se hanno lo stesso nome. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Oggi ogni professore ha dato punti solo alla casa che dirige. Un compagno toglie il rombo `dirige`, perché «si ricava». Che cosa ne pensi? + +Ha ragione: se i dati coincidono, è una copia. +Ha torto: serve una regola del dominio, e nessuna regola lo dice. +Ha ragione, se ogni professore ha almeno un'assegnazione. +Ha torto: un giro chiuso nel diagramma è sempre un errore. + +--- +layout: fine +--- + +# La prossima lezione + +Uno studente appartiene a **una** casa sola, una casa accoglie **molti** studenti, un professore dirige **al massimo una** casa: questi conteggi diventano le **cardinalità**, e decidono la struttura del database. diff --git a/slides/decks/archivista/le-cardinalita.md b/slides/decks/archivista/le-cardinalita.md new file mode 100644 index 0000000..e4784c3 --- /dev/null +++ b/slides/decks/archivista/le-cardinalita.md @@ -0,0 +1,295 @@ +--- +theme: ../../theme +title: Le cardinalità +sottotitolo: Le cardinalità non sono un’etichetta da aggiungere alla fine. Il minimo decide se un’occorrenza può esistere senza l’altra, il massimo se il legame sta in una colonna. +volume: archivista +capitolo: 2 +numero: 7 +lezione: archivista/le-cardinalita +--- + +--- +layout: epigrafe +autore: Highlander (1986) +--- + +There can be only one. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Associazioni e occorrenze: `appartiene` e `dirige` +- L'attributo opzionale e il segno `(0,1)` +- La bacchetta come entità, con legno, nucleo e lunghezza +- Il vincolo che fa rifiutare una scrittura + +::imparerai:: + +- Scrivere le cardinalità leggendo il dominio nei due versi +- Che cosa decidono il **minimo** e il **massimo** +- Associazioni **uno a uno**, **uno a molti**, **molti a molti** +- Quando una cardinalità fa rifiutare un fatto vero + +--- +layout: sezione +--- + +# Quante case per uno studente + +Contiamo le coppie di `appartiene`, partendo da ciascuna delle due entità. + +--- +layout: figura +--- + +# Contare le coppie nei due versi + +::figura:: + + + +--- +layout: figura +--- + +# Due numeri accanto a ogni entità + +::lato:: + +- Accanto a `studente`, **`(1,1)`**: ogni studente compare in almeno una coppia e al massimo in una. Di case, come di Highlander, ce n'è una sola. +- Accanto a `casa`, **`(0,N)`**: una casa può non avere studenti, e non ha un massimo. `N` vuol dire «quante se ne vuole». + +::figura:: + + + +--- + +# I numeri stanno accanto all'entità che descrivono + +Si leggono partendo dall'entità che hanno accanto: «ogni studente appartiene ad almeno una casa e al massimo a una», «ogni casa accoglie da zero a N studenti». + + + +In UML e nella notazione _crow's foot_ il numero sta all'altro capo, e chi le conosce tende a invertire i lati. + +- `(0,N)` accanto a `studente`, `(1,1)` accanto a `casa` → ogni casa ha esattamente uno studente. +- `(1,1)` accanto a `studente`, `(0,N)` accanto a `casa`. + + + +--- +layout: sezione +--- + +# Prima dello smistamento + +Il minimo 1 accanto a `studente` dice che uno studente senza casa non esiste. + +--- +layout: domanda +invito: La casa manca, perché ancora nessuno la conosce. Che cosa fa il DBMS? +--- + +# Ginny Weasley ad agosto + +La segreteria registra gli studenti appena parte la lettera. Ginny sarà smistata solo il 1° settembre. + + + +```sql +INSERT INTO studente (nome, anno) +VALUES ('Ginny Weasley', 1); +``` + + + +::risposta:: + +La **rifiuta**: `NOT NULL constraint failed: studente.casa`. Ginny non entra finché il Cappello Parlante non decide, il che per una Weasley è una formalità. + +--- +layout: enunciato +--- + +Il minimo risponde a una domanda: un'occorrenza può **esistere senza comparire in nessuna coppia**? + +Se il database registra gli studenti dopo lo smistamento, `(1,1)` va bene. Se li registra dalla lettera di ammissione, per qualche settimana uno studente esiste senza casa: serve **`(0,1)`**. La risposta dipende da che cosa il database registra, e da quando. + +--- +layout: sezione +--- + +# La seconda bacchetta di Ron + +Il massimo decide un'altra cosa. + +--- +layout: figura +--- + +# Tutte le bacchette avute + +::lato:: + +La prima bacchetta di Ron era di Charlie, e si è spezzata contro il Platano Picchiatore, con la Ford Anglia volante. Al terzo anno ne ha una nuova. + +Se il database conserva **tutte** le bacchette avute, accanto a `studente` il massimo diventa **N**. + +::figura:: + + + +--- +layout: due-colonne +sinistra: Massimo 1 +destra: Massimo N +--- + +# Un carattere nel diagramma, una struttura nel database + +::sinistra:: + +Legno, nucleo e lunghezza potrebbero stare **nella riga dello studente**, una volta sola. + +Se convenga, lo vedremo nel capitolo 3. + +::destra:: + +Nella riga di Ron servirebbero due legni, due nuclei, due lunghezze, e una cella contiene **un valore solo**. + +`legno_2` sposta il problema alla terza bacchetta: le bacchette vanno in una **tabella propria**, una riga per bacchetta. + +--- +layout: sezione +--- + +# Uno a uno, uno a molti, molti a molti + +I massimi dei due lati danno il nome all'associazione. + +--- +layout: definizione +titolo: Cardinalità e partecipazione +--- + +La **cardinalità** di un'entità in un'associazione è la coppia `(min,max)`: in quante occorrenze dell'associazione può comparire ogni sua occorrenza, come minimo e come massimo. + +La **partecipazione** è **obbligatoria** se il minimo è 1, **opzionale** se è 0. + +--- +layout: figura +--- + +# I tre tipi, e i minimi che non contano + +::lato:: + +- **Uno a uno** (1:1): massimo 1 dai due lati, come `dirige`. +- **Uno a molti** (1:N): come `appartiene`. +- **Molti a molti** (N:N): studenti e corsi. + +In `dirige` ogni casa ha un direttore, `(1,1)`; ogni professore ne dirige al massimo una, `(0,1)`: il minimo 0 serve a **Silente**. + +::figura:: + + + +--- +layout: sintesi +--- + +# In sintesi + + + +- `(min,max)` si scrive accanto all'entità che descrive e si legge partendo da lei. +- Il **minimo**: un'occorrenza può esistere senza coppie? 0 opzionale, 1 obbligatoria. +- Il **massimo**: può comparire in più coppie? 1 oppure N. +- Un minimo sbagliato fa rifiutare un fatto vero, come Ginny prima dello smistamento. +- Un massimo N toglie il legame dalla colonna: servono righe in una tabella propria. +- I massimi danno il tipo: 1:1, 1:N, N:N. Decide il dominio. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# Un compagno scrive `(0,N)` accanto a `studente` e `(1,1)` accanto a `casa`. Che cosa dice il suo diagramma? + +Ogni studente ha una casa, ogni casa da zero a N studenti. +Ogni casa ha esattamente uno studente, e uno studente può avere nessuna casa o molte. +Ogni studente ha almeno una casa, ogni casa al massimo uno studente. +Niente: si possono scambiare senza cambiare il significato. + +--- +layout: quiz +numero: 2 di 3 +--- + +# In `dirige` un compagno scrive `(1,1)` accanto a `professore`. Che cosa succede quando si registra Silente? + +Niente: il preside dirige tutte le case. +Silente non si può registrare: accanto a `professore` il minimo giusto è 0. +Silente viene registrato, con la casa vuota. +Bisogna cambiare il massimo accanto a `casa` in N. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Il database conserva tutte le bacchette e i loro proprietari precedenti: quella di Ron era di Charlie. Che cardinalità ha `bacchetta` in `possiede`? + +`(1,1)`: ogni bacchetta ha un solo studente. +`(0,1)`: alcune bacchette non hanno proprietario. +`(1,N)`: una bacchetta può passare da più proprietari. +Non si può dire: dipende da quante bacchette ha avuto Charlie. + +--- +layout: fine +--- + +# La prossima lezione + +Le coppie nominano le occorrenze per nome: (Harry Potter, Grifondoro). Funziona finché due studenti non si chiamano allo stesso modo. Succede, e cerchiamo un modo di distinguerli che **non si affidi alla fortuna**. diff --git a/slides/decks/archivista/le-chiavi.md b/slides/decks/archivista/le-chiavi.md new file mode 100644 index 0000000..9e45fdf --- /dev/null +++ b/slides/decks/archivista/le-chiavi.md @@ -0,0 +1,306 @@ +--- +theme: ../../theme +title: 'Identificare senza ambiguità: le chiavi' +sottotitolo: Due persone possono avere lo stesso nome. Con due James Potter nell’archivio di Hogwarts vediamo come fallisce un identificatore sbagliato e come se ne sceglie uno che regge. +volume: archivista +capitolo: 2 +numero: 8 +lezione: archivista/le-chiavi +--- + +--- +layout: epigrafe +autore: William Shakespeare, Romeo e Giulietta (1597) +--- + +What's in a name? That which we call a rose by any other name would smell as sweet. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- La tabella `studente` e il collegamento per nome con `assegnazione` +- Il **vincolo**: una regola che il DBMS fa rispettare rifiutando una scrittura +- Entità, attributi, associazioni e cardinalità + +::imparerai:: + +- I due modi in cui fallisce un identificatore sbagliato +- Che cos'è un **identificatore** e quali condizioni rispetta +- Scegliere fra un identificatore del dominio e uno assegnato apposta +- Disegnare un identificatore semplice e uno composto + +--- +layout: sezione +--- + +# Due James Potter + +Un archivio che conserva tutti gli studenti, anno dopo anno, prima o poi incontra due nomi uguali. + +--- + +# Nella famiglia di Harry è già successo + +Hogwarts vuole conservare **tutti** gli studenti passati dalla scuola, senza cancellarne nessuno. + +- **James Potter**, il padre di Harry: Grifondoro, primo anno +- **James Potter**, il figlio maggiore di Harry: primo anno, più di quarant'anni dopo + +Nella tabella `studente` la colonna `nome` è la **chiave**: la colonna con cui il DBMS distingue una riga da tutte le altre, e con cui `assegnazione` indica a chi vanno i punti. + +--- +layout: domanda +--- + +# Registriamo i due James Potter + + + +```sql +INSERT INTO studente (nome, casa, anno) +VALUES ('James Potter', 'Grifondoro', 1); +INSERT INTO studente (nome, casa, anno) +VALUES ('James Potter', 'Grifondoro', 1); +``` + + + +::risposta:: + +Il primo entra, il secondo viene rifiutato: `UNIQUE constraint failed: studente.nome`. Nella colonna `nome` lo stesso valore non può comparire due volte. + +--- +layout: enunciato +--- + +Primo guasto: il database **rifiuta uno studente vero**. + +Il DBMS fa rispettare la regola che ha ricevuto, il nome distingue ogni studente. Per questo archivio la regola è falsa, e l'errore arriva nel momento in cui qualcuno prova a scrivere. + +--- +layout: sezione +--- + +# Senza chiave + +Togliamo la regola: la tabella accetta due righe con lo stesso nome. Che cosa succede ai conti? + +--- +layout: domanda +invito: Nella lezione sul DBMS Grifondoro aveva 3 punti; arriva un'assegnazione da 10. Quanti ne ha adesso? +--- + +# Il totale dei punti per casa + + + +```sql +SELECT studente.casa, + SUM(assegnazione.punti) AS totale +FROM assegnazione +JOIN studente + ON studente.nome = assegnazione.studente +GROUP BY studente.casa +ORDER BY totale DESC; +``` + + + +::risposta:: + +Grifondoro ha **23** punti, non 13: l'assegnazione a `James Potter` si unisce a tutte e due le righe con quel nome, e i dieci punti entrano nella somma due volte. + +--- +layout: figura +--- + +# I dieci punti contati due volte + +::figura:: + + + +--- +layout: enunciato +--- + +Secondo guasto: la risposta è **sbagliata e nessuno lo segnala**. + +La query è corretta, il DBMS la esegue sui dati che ha. È il guasto più pericoloso: nessuno se ne accorge finché qualcuno non rifà il conto a mano. + +--- +layout: sezione +--- + +# Che cosa distingue uno studente + +Serve un attributo, o un gruppo di attributi, con un valore diverso per ogni occorrenza. + +--- +layout: definizione +titolo: Identificatore +--- + +Un **identificatore** di un'entità è un attributo, o un insieme di attributi, che ha un valore diverso per ogni occorrenza: per **tutte** quelle che il database conterrà, non solo per quelle di oggi. Non contiene attributi superflui. + +Formato da un solo attributo è **semplice**; da più attributi, **composto**. + +--- +layout: due-colonne +sinistra: Aggiungere attributi non basta +destra: La strada che regge +--- + +# La parola che conta è «tutte» + +::sinistra:: + +- **nome**: due James Potter +- **nome + anno**: erano tutti e due al primo anno +- **nome + data di nascita**: due omonimi nati lo stesso giorno sono rari, non impossibili + +Ogni attributo aggiunto rende il caso più raro; nessuno lo rende impossibile. + +::destra:: + +Un identificatore **nato apposta per essere unico**: il numero di **matricola** che la segreteria assegna e non riassegna mai. + +Non contiene informazioni sullo studente: non lo mettono in difficoltà né un omonimo né un cambio di cognome. A garantire che sia unico è chi lo assegna. + +--- + +# Anche il codice fiscale ha i suoi doppioni + + + +Il codice fiscale si calcola da cognome, nome, sesso, data e comune di nascita: due persone con gli stessi dati hanno lo stesso codice, e l'Agenzia delle Entrate cambia alcune cifre in lettere. È unico perché **qualcuno corregge i doppioni**. + + + + + +Il **nome di una casa** è un buon identificatore: le case sono quattro e una regola del dominio dice che restano quelle. Serve una regola che garantisca valori diversi anche domani; per i nomi delle persone non c'è. + + + +--- + +# La matricola distingue le righe, non le persone + + + +- Harry Potter registrato due volte, con due matricole → due righe valide per la stessa persona: i suoi punti si dividono e nessun vincolo se ne accorge. +- Prima di assegnare una matricola nuova, la segreteria controlla che lo studente non sia già registrato. + + + +Un identificatore assegnato apposta garantisce che due righe non si confondano. Che la stessa persona non compaia due volte lo garantisce solo **chi registra i dati**. + +--- +layout: sezione +--- + +# Identificatori composti + +A volte nessun attributo da solo distingue le occorrenze, e due attributi insieme sì. + +--- +layout: figura +--- + +# Il corso: materia e anno + +::lato:: + +- **Materia** da sola: no, Pozioni c'è in più anni +- **Anno** da solo: no, al primo anno ci sono molte materie +- **Materia + anno**: sì, e nessuno dei due è superfluo + +::figura:: + + + +--- + +# Come si disegna + +- **Identificatore semplice**: il cerchio pieno in fondo allo stelo di `matricola`. +- **Identificatore composto**: un segmento che attraversa gli steli di `materia` e `anno` e finisce in un cerchio pieno; i cerchi dei due attributi restano vuoti, perché nessuno dei due da solo identifica un corso. +- **Ogni entità ha il suo identificatore**: senza, una coppia di un'associazione non indicherebbe un'occorrenza precisa. + + +Nel modello E/R si dice identificatore; nelle tabelle, dal capitolo 3, diventerà la chiave primaria. + + +--- +layout: sintesi +--- + +# In sintesi + + + +- Un identificatore sbagliato fallisce in due modi: **rifiuta un dato vero** oppure, senza identificatore, **risponde sbagliato in silenzio**. +- Un identificatore ha un valore diverso per **ogni** occorrenza, anche futura, e nessun attributo superfluo. +- Quelli presi dal dominio vanno bene solo se una **regola del dominio** garantisce valori diversi. +- Uno assegnato apposta, come la **matricola**, distingue le righe, non le persone. +- Semplice: cerchio pieno. Composto: segmento sugli steli che finisce in un cerchio pieno. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# La tabella `professore` ha come chiave il cognome. Arriva un secondo professore con un cognome già presente: che cosa succede? + +Il DBMS lo registra, e le assegnazioni dei due si sommano. +Il DBMS rifiuta il secondo professore, anche se è una persona diversa. +Il DBMS sostituisce il primo professore con il secondo. +Il DBMS aggiunge da solo un numero al cognome. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Un compagno propone come identificatore di `corso` la terna materia, anno e professore. Che cosa ne pensi? + +Va bene: più attributi ci sono, più è sicuro. +Il professore è superfluo: materia e anno bastano già. +Basta il professore, perché insegna una materia sola. +Basta la materia, perché è il nome del corso. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Per `casa` un compagno usa un numero assegnato apposta, un altro dice che il nome va benissimo. Chi ha ragione? + +Il primo: gli identificatori del dominio prima o poi falliscono. +Il primo: un numero occupa meno spazio di un nome. +Il secondo: le case sono quattro, con nomi diversi, e restano quelle. +Nessuno dei due: serve nome e colori insieme. + +--- +layout: fine +--- + +# La prossima lezione + +Ogni studente segue più corsi, e ogni corso ha più studenti: disegniamo l'associazione **molti a molti**, e cerchiamo il posto giusto per il **voto** che uno studente prende in un corso. diff --git a/slides/decks/archivista/leggere-un-dominio.md b/slides/decks/archivista/leggere-un-dominio.md new file mode 100644 index 0000000..e86becc --- /dev/null +++ b/slides/decks/archivista/leggere-un-dominio.md @@ -0,0 +1,290 @@ +--- +theme: ../../theme +title: Leggere un dominio e ricavarne il modello +sottotitolo: Il modello E/R si ricava da un testo con un metodo ripetibile. I sostantivi danno i candidati, i verbi le associazioni, le quantità le cardinalità, e ciò che il testo non dice diventa un’ipotesi scritta. +volume: archivista +capitolo: 2 +numero: 10 +lezione: archivista/leggere-un-dominio +--- + +--- +layout: epigrafe +autore: Sherlock Holmes, Uno scandalo in Boemia (1891) +--- + +You see, but you do not observe. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Entità, attributi e il criterio per decidere +- Associazioni, derivate, e cardinalità +- Identificatori semplici, composti e assegnati apposta +- Molti a molti, e quando serve un'entità + +::imparerai:: + +- Ricavare da un testo entità, attributi e associazioni +- Tradurre le quantità in cardinalità, e scrivere le **ipotesi** +- Scrivere i **vincoli** che il diagramma non esprime +- Produrre e motivare il diagramma di un dominio nuovo + +--- +layout: enunciato +--- + +Nella realtà, e in una prova scritta, il dominio arriva **tutto insieme**, in un testo. + +Chi lo legge la prima volta disegna subito i rettangoli che gli vengono in mente. Serve invece un metodo in quattro passi, ripetibile su qualunque testo. + +--- +layout: sezione +--- + +# Il testo + +La biblioteca di Hogwarts vuole un database per i suoi prestiti. + +--- + +# La richiesta della biblioteca + +> La biblioteca di Hogwarts vuole un database per i suoi prestiti. Di ogni libro la bibliotecaria registra il titolo, l'anno di pubblicazione e gli autori: un libro può avere più autori, e di ogni autore interessano nome e cognome. Di molti libri la biblioteca possiede più copie, e ogni copia ha un numero d'inventario scritto sul dorso e uno stato di conservazione. Gli studenti prendono in prestito le copie: di ogni prestito si registrano la data in cui la copia esce e la data in cui rientra, che resta vuota finché la copia non torna. Uno studente può avere più copie in prestito nello stesso momento. Alcuni libri sono riservati: si prendono in prestito solo con il permesso firmato da un professore, e la biblioteca vuole sapere quale professore ha firmato ogni permesso. + +--- +layout: sezione +--- + +# Primo passo: i candidati + +Ogni sostantivo è un candidato. Per ognuno: interessa altro oltre al nome? Esiste da solo? + +--- + +# Il verdetto, candidato per candidato + +| Candidato | Verdetto | Perché | +|---|---|---| +| biblioteca, database, bibliotecaria | scartati | una sola, o di loro non si chiede niente | +| libro, autore, copia | entità | hanno attributi propri ed esistono da soli | +| studente, professore | entità | sono già nel modello di Hogwarts | +| titolo, anno, nome, cognome, inventario, stato | attributi | interessa solo il valore | +| riservato | attributo di `libro` | vale sì o no | +| prestito, permesso | da decidere | li decide il secondo passo | + +--- +layout: due-colonne +sinistra: L'opera +destra: L'oggetto +--- + +# Libro e copia: la decisione più delicata + +::sinistra:: + +«Ho letto quel **libro**»: titolo, anno, autori. + +Sono gli stessi per tutte le copie: stanno in **`libro`**. + +::destra:: + +«Ho perso il libro», o a Hogwarts «il libro mi ha morso»: numero d'inventario e stato. + +Riguardano l'oggetto: stanno in **`copia`**. Un'entità sola ripeterebbe titolo e autori in ogni copia. + +--- +layout: sezione +--- + +# Secondo passo: i legami + +I verbi e le frasi che mettono in relazione due candidati. + +--- + +# Quattro decisioni sui legami + + + +- **`scrive`** fra `autore` e `libro`: un autore più libri, un libro più autori. Molti a molti, senza attributi. +- **`esemplare`** fra `copia` e `libro`: uno a molti, ogni copia è di un libro solo. +- **Il prestito è un'entità**: Hermione può prendere la stessa copia a ottobre e a maggio, la coppia si ripete. Lo legano alla copia `riguarda`, allo studente `richiede`. +- **Il permesso è un'associazione**, `firma`: del permesso interessa solo chi l'ha firmato. + + + +Il libro di un prestito **non** si disegna: si ricava passando dalla copia. Sarebbe un'associazione derivata. + +--- +layout: sezione +--- + +# Terzo passo: le quantità + +«Ogni», «più», «alcuni», «può», «solo»: ogni parola così è un pezzo di cardinalità. + +--- +layout: due-colonne +sinistra: Quello che il testo dice +destra: Quello che non dice, scritto come ipotesi +--- + +# Cardinalità e ipotesi + +::sinistra:: + +- «più autori» → N accanto a `libro` in `scrive` +- «ogni copia» → `(1,1)` accanto a `copia` +- «più copie in prestito» → `(0,N)` accanto a `studente` +- «alcuni libri» → `(0,1)` accanto a `prestito` in `firma` +- «resta vuota» → `rientro` opzionale, `(0,1)` + +::destra:: + +1. Ogni libro registrato ha almeno una copia: `(1,N)`. +2. Ogni autore ha almeno un libro della biblioteca: `(1,N)`. +3. Un professore firma quanti permessi vuole, anche nessuno: `(0,N)`. + +Una scelta non scritta sembra una **svista**. + +--- + +# I vincoli che nessun rombo disegna + +Alcune regole del testo non entrano in nessuna cardinalità. Si scrivono accanto al diagramma: + + + +1. Un prestito ha una firma **se e solo se** la copia è di un libro riservato. +2. Una copia ha **al massimo un prestito** con la data di rientro vuota. + + + +Nel capitolo 5 vedremo che alcuni si possono far rispettare al DBMS; altri restano compito dei programmi. + +--- +layout: sezione +--- + +# Quarto passo: gli identificatori + +Una regola che garantisca valori diversi, oggi e domani. + +--- + +# Chi identifica chi + +- **`copia`**: il numero d'**inventario**, assegnato dalla biblioteca e mai ripetuto. +- **`libro`**: il titolo non basta, due libri possono avere lo stesso titolo: serve un **codice**. +- **`autore`**: nome e cognome non bastano: un **codice**. +- **`prestito`**: un **id** progressivo. +- **`studente`** e **`professore`**: matricola e codice, già nel modello. + +--- +layout: figura +didascalia: Ipotesi 1 e 2. Il titolo non identifica un libro, il numero d'inventario identifica una copia. +--- + +# Il modello, prima metà: le opere e le copie + +::figura:: + + + +--- +layout: figura +didascalia: Il prestito è un'entità; la firma, un'associazione facoltativa (vincoli 1 e 2 scritti a parte). +--- + +# Il modello, seconda metà: i prestiti + +::figura:: + + + +--- + +# Il diagramma da solo non è il modello + +Il modello completo è il diagramma **con le ipotesi e i vincoli scritti accanto**: chi lo legge deve capire ogni scelta senza chiederla. + + + +- un rettangolo per ogni sostantivo → `biblioteca`, `permesso`, `bibliotecaria` diventano entità che non registrano niente. +- cardinalità scelte senza scriverlo → chi legge non sa se è una decisione o una distrazione. +- ogni candidato con il suo verdetto, ogni scelta con la sua ipotesi. + + + +--- +layout: sezione +--- + +# Il tuo turno + +La Coppa di Quidditch, con i quattro passi. Su carta. + +--- + +# La Coppa di Quidditch + +> Ogni anno a Hogwarts le quattro case si contendono la Coppa di Quidditch. Ogni casa ha una squadra di sette giocatori, tutti studenti della casa, e il database registra la squadra di quest'anno. Di ogni giocatore interessa il ruolo, e uno dei sette è il capitano della squadra. Ogni partita si gioca in una data fra due case, e il database deve registrare i punti segnati da ciascuna delle due. Ogni partita è arbitrata da un professore. + +Per ogni passo: candidati con verdetto, associazioni, cardinalità, identificatori. Poi il diagramma, con ipotesi e vincoli accanto. + +--- + +# Una soluzione possibile + + + +- **Entità**: `casa`, `studente`, `partita`, `professore`. La squadra no: ogni casa ne ha una, e non ha niente di suo. +- **`gioca`** fra studente e casa, con l'attributo `ruolo`: `(0,1)` accanto a studente, **`(7,7)`** accanto a casa. +- **`capitano`** fra le stesse due entità, con coppie diverse: `(0,1)` e `(1,1)`. +- **`sfida`** fra partita e casa, con l'attributo `punti` della coppia: **`(2,2)`** accanto a partita. +- **`arbitra`**: ogni partita ha un professore, `(1,1)`. +- **Identificatori**: nome della casa, matricola, codice del professore, id della partita. + + + +--- +layout: fine +--- + +# La prossima lezione + +Studenti e professori sono due entità separate, ognuna con nome e identificatore, e tutte e due si legano alle stesse cose. Nell'ultima lezione del capitolo il modello E/R raccoglie in un'entità sola ciò che hanno in comune: le **generalizzazioni**. diff --git a/slides/decks/archivista/stessa-informazione-due-volte.md b/slides/decks/archivista/stessa-informazione-due-volte.md new file mode 100644 index 0000000..a7e3903 --- /dev/null +++ b/slides/decks/archivista/stessa-informazione-due-volte.md @@ -0,0 +1,415 @@ +--- +theme: ../../theme +title: La stessa informazione scritta due volte +sottotitolo: Quando un dato è scritto in due posti, prima o poi le due copie si contraddicono e niente dice quale sia vera. Insieme, le tre anomalie di una riga che parla di cose diverse. +volume: archivista +capitolo: 1 +numero: 3 +lezione: archivista/stessa-informazione-due-volte +--- + +--- +layout: epigrafe +autore: Legge di Segal (1961) +--- + +A man with a watch knows what time it is. A man with two watches is never sure. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Liste, dizionari e cicli `for` +- Leggere una lista di dizionari da un file JSON +- `punti.json`, `studenti.json` e l'incrocio fra i due con un dizionario + +::imparerai:: + +- Perché due copie dello stesso dato finiscono per dire cose diverse +- Scrivere un controllo che trova le copie in disaccordo, e i suoi limiti +- Distinguere un valore ripetuto da un'informazione ripetuta +- Le anomalie di **inserimento**, **aggiornamento** e **cancellazione** + +--- +layout: enunciato +--- + +Il registro ripete in ogni assegnazione la **casa** dello studente, che sta già in `studenti.json`. + +È comodo: il totale di Grifondoro si calcola senza aprire l'altro file. In questa lezione vediamo il costo nascosto di questa comodità. + +--- +layout: sezione +--- + +# Due conteggi, due classifiche + +Un'assegnazione nuova, con la casa di Neville scritta sbagliata. + +--- +layout: due-colonne +centro: true +--- + +# Cinque punti a Neville, in Tassofrasso + +::sinistra:: + +```json +{ + "studente": "Neville Longbottom", + "casa": "Tassofrasso", + "punti": 5, + "professore": "Sprout" +} +``` + +::destra:: + +La professoressa Sprout dà cinque punti a Neville. Nella riga, al posto di Grifondoro, è finito **Tassofrasso**. + +Contiamo i punti delle case in due modi: con la casa scritta nel registro, e con la casa presa da `studenti.json`. + +--- +layout: domanda +affiancata: true +invito: Quanti punti ha Grifondoro secondo ciascun conteggio? +--- + +# Due modi di contare + + + +```python +import json +from archivio_hogwarts import leggi_file +registro = json.loads(leggi_file("punti.json")) +registro.append({"studente": "Neville Longbottom", + "casa": "Tassofrasso", "punti": 5, "professore": "Sprout"}) +casa_di = {} +for s in json.loads(leggi_file("studenti.json")): + casa_di[s["nome"]] = s["casa"] +dal_registro, da_studenti = {}, {} +for a in registro: + c1, c2 = a["casa"], casa_di[a["studente"]] + dal_registro[c1] = dal_registro.get(c1, 0) + a["punti"] + da_studenti[c2] = da_studenti.get(c2, 0) + a["punti"] +print(dal_registro["Grifondoro"], da_studenti["Grifondoro"]) +``` + + + +::risposta:: + +**3** secondo il registro, **8** secondo `studenti.json`. I cinque punti di Neville finiscono in due case diverse. + +--- +layout: enunciato +--- + +Nessuno dei due conteggi è sbagliato: **sono i dati a contraddirsi**. + +Noi sappiamo che Neville è un Grifondoro perché conosciamo la storia. Il programma ha due file in disaccordo e nessun criterio per scegliere. Con 20.000 assegnazioni di studenti sconosciuti, saremmo nella sua stessa posizione. + +--- +layout: definizione +titolo: Ridondanza +--- + +Un'informazione scritta in più posti si chiama **ridondanza**. La casa nel registro è una copia: un'informazione sullo studente, che ha già il suo posto in `studenti.json`. + +Il difetto più evidente è lo spazio occupato, ma non il più grave: la prima volta che le copie non coincidono, l'archivio ha **due risposte diverse alla stessa domanda**. + +--- + +# Il disaccordo, almeno, si trova + + + +```python +import json +from archivio_hogwarts import leggi_file +registro = json.loads(leggi_file("punti.json")) +studenti = json.loads(leggi_file("studenti.json")) +registro.append({"studente": "Neville Longbottom", + "casa": "Tassofrasso", "punti": 5, "professore": "Sprout"}) +casa_di = {} +for s in studenti: + casa_di[s["nome"]] = s["casa"] +for a in registro: + nome = a["studente"] + if a["casa"] != casa_di[nome]: + print(f"{nome}: {a['casa']} contro {casa_di[nome]}") +``` + + + +Il controllo trova Neville, ma **non sa quale casa è giusta**, e va rilanciato dopo ogni modifica. + +--- + +# Virgolette dentro una f-string + + + +- `f"{a["casa"]}"` → `SyntaxError` fino a Python 3.11. +- `f"{a['casa']}"` → funziona in ogni versione. + + + +Nei blocchi del libro funzionano tutte e due, perché il motore segue Python 3.12; nell'editor della scuola potresti avere una versione più vecchia. + +--- +layout: sezione +--- + +# Copie che invecchiano + +Una copia può diventare falsa anche se nessuno sbaglia l'inserimento. + +--- +layout: due-colonne +--- + +# La scorciatoia dell'anno copiato + +::sinistra:: + +```json +{ + "studente": "Neville Longbottom", + "casa": "Grifondoro", + "anno": 1, + "punti": -50, + "professore": "McGonagall" +} +``` + +::destra:: + +Copiamo l'anno in ogni assegnazione, per contare i punti per anno senza incrocio. + +Per un anno scolastico funziona. Poi arriva il **1° settembre**: ogni studente passa all'anno successivo, e `studenti.json` va aggiornato. + +--- + +# Il 1° settembre, due cicli invece di uno + +```python +def nuovo_anno(studenti, registro): + for studente in studenti: + studente["anno"] += 1 + for assegnazione in registro: # esiste solo per la copia + assegnazione["anno"] += 1 +``` + +- Il primo ciclo aggiorna **nove** righe, una per studente. +- Il secondo ne aggiorna più di **20.000**, e ogni anno di più. +- Se si dimentica il registro, o il programma si ferma fra i due cicli, i file danno due classifiche diverse. Ogni copia **era vera** quando è stata scritta. + +--- +layout: figura +--- + +# Le copie invecchiano con l'originale + +::figura:: + + + +--- +layout: due-colonne +--- + +# Copie fatte apposta, valori solo uguali + +::sinistra:: + + + +Una copia tenuta apposta per rispondere in fretta è una **cache**. La casa nel registro è una cache improvvisata. Phil Karlton: in informatica ci sono due cose difficili, **invalidare la cache** e dare i nomi alle cose. + + + +::destra:: + + + +L'anno **in cui** Neville ha perso 50 punti riguarda l'assegnazione, e il 1° settembre non deve cambiare. Oggi vale 1 come l'anno attuale; da settembre no. Il significato lo decide chi progetta l'archivio. + + + +--- +layout: quiz +numero: 1 di 2 +--- + +# Quale campo di un'assegnazione è una copia di un'informazione che appartiene a qualcos'altro? + +`"studente"` +`"casa"` +`"punti"` +`"professore"` + +--- +layout: sezione +--- + +# Una riga, tre fatti + +Un difetto meno visibile, che riguarda i professori. + +--- + +# Che cosa registra una riga + +Flitwick dà dieci punti a Padma Patil, di Corvonero. + +| Che cosa registra la riga | Di chi è | Dove altro è scritta | +|---|---|---| +| Padma ha ricevuto 10 punti da Flitwick | dell'assegnazione | da nessuna parte | +| Padma è di Corvonero | di Padma | in `studenti.json` | +| Flitwick insegna a Hogwarts | di Flitwick | da nessuna parte | + +Nessun file elenca i professori: per l'archivio un professore esiste solo se compare in un'assegnazione. + +--- +layout: domanda +affiancata: true +invito: Annulliamo l'assegnazione di Padma, la decima. Che cosa cambia nel secondo elenco? +--- + +# L'elenco dei professori, prima e dopo + + + +```python +import json +from archivio_hogwarts import leggi_file +registro = json.loads(leggi_file("punti.json")) + +def professori(registro): + elenco = [] + for a in registro: + if a["professore"] not in elenco: + elenco.append(a["professore"]) + return elenco + +print(professori(registro)) +del registro[9] +print(professori(registro)) +``` + + + +::risposta:: + +**Manca Flitwick**. Annullare l'assegnazione doveva cancellare un fatto solo, e ne ha cancellato un altro: Flitwick era scritto soltanto lì. + +--- + +# E un professore appena arrivato? + +Lupin comincia a insegnare due anni dopo. Finché non assegna punti, l'archivio **non sa che esiste**. Per registrarlo subito servirebbe una riga finta: + +```json +{ "studente": "", "casa": "", "punti": 0, "professore": "Lupin" } +``` + +- Chi conta le assegnazioni ne trova una in più, senza avviso. +- Il calcolo dei punti per anno si ferma con un `KeyError`: in `anno_di` non c'è uno studente senza nome. + +--- +layout: definizione +titolo: Le tre anomalie +--- + +- **Aggiornamento**: un'informazione scritta in più posti va cambiata in tutti, o l'archivio si contraddice. L'anno copiato nel registro. +- **Inserimento**: un'informazione non si registra finché non c'è un'altra cosa a cui appoggiarla. Lupin. +- **Cancellazione**: eliminare un dato ne elimina altri scritti nella stessa riga. Flitwick. + +La causa comune: **una riga che parla di cose diverse**. + +--- +layout: sezione +--- + +# Un fatto, un posto + +Scrivere ogni informazione una volta sola, nel posto che le appartiene. + +--- +layout: due-colonne +sinistra: Il rimedio +destra: Il prezzo +--- + +# Tre file, ognuno con i suoi fatti + +::sinistra:: + +Casa e anno in `studenti.json`, i professori in `professori.json`, e nel registro solo l'assegnazione: + +```json +{ "studente": "Padma Patil", + "punti": 10, + "professore": "Flitwick" } +``` + +Niente copie che si contraddicono: Flitwick resta, Lupin entra il giorno in cui arriva. + +::destra:: + +Anche il totale di Grifondoro richiede un **incrocio** scritto a mano, e quasi ogni domanda ne chiede uno. + +E il legame resta affidato al codice: niente impedisce un professore che non esiste o un «Logbottom». Abbiamo scambiato dati che si contraddicono con dati che **puntano a nessuno**. + +--- +layout: enunciato +--- + +Con i file si sceglie fra **copie da tenere allineate** e **incroci da scrivere**. + +Un DBMS tiene ogni informazione al suo posto, ti lascia chiedere un incrocio invece di programmarlo e rifiuta un nome che non compare nell'elenco a cui deve riferirsi. + +--- +layout: sintesi +--- + +# In sintesi + + + +- Una copia di un'informazione che ha già un suo posto è una **ridondanza**. +- Quando due copie divergono, niente nei dati dice quale sia vera. +- Un controllo di coerenza trova i disaccordi, ma non sa correggerli e va rilanciato. +- Una copia diventa falsa anche senza errori, quando cambia l'originale. +- Un valore ripetuto non è sempre una copia: conta il significato. +- Una riga che parla di cose diverse produce le anomalie di inserimento, aggiornamento e cancellazione. +- Rimedio: ogni informazione una volta, al suo posto. Prezzo: gli incroci. + + + +--- +layout: quiz +numero: 2 di 2 +--- + +# In `prestiti.json` ogni prestito riporta studente, titolo, autore e data. Un libro prestato una volta sola perde il suo prestito: che cosa perde l'archivio? + +Solo il prestito: il libro resta sullo scaffale. +Anche il fatto che la biblioteca possieda quel libro, e chi l'ha scritto. +Niente: l'autore compare negli altri prestiti. +Il file non è più JSON valido. + +--- +layout: fine +--- + +# La prossima lezione + +Un DBMS che tiene ogni informazione al suo posto, fa gli incroci al posto tuo e **rifiuta** i dati che non rispettano le regole: vediamo che cosa fa un DBMS al posto del nostro codice. diff --git a/slides/decks/artefice/classi-e-istanze.md b/slides/decks/artefice/classi-e-istanze.md new file mode 100644 index 0000000..1ea2aa8 --- /dev/null +++ b/slides/decks/artefice/classi-e-istanze.md @@ -0,0 +1,502 @@ +--- +theme: ../../theme +title: Classi, istanze e metodi +sottotitolo: Lo stampo e l’esemplare, smontati pezzo per pezzo. Che cosa fa davvero __init__, perché self compare ovunque, come si distinguono attributi e metodi. +volume: artefice +numero: 3 +lezione: artefice/classi-e-istanze +--- + +--- +layout: epigrafe +autore: Massima delfica (Tempio di Apollo a Delfi) +--- + +Conosci te stesso. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- _Perché gli oggetti?_: un oggetto tiene insieme **dati** e **comportamento** +- Variabili, condizioni, cicli, funzioni +- Liste: `append` e ciclo `for` +- _f-string_ + +::imparerai:: + +- Che cosa fa il costruttore `__init__` +- Chi è `self`, e perché non lo passi mai tu +- **Attributi** e **metodi**, con la dot notation +- Calcolare invece di memorizzare +- Le prime notazioni **UML** e i nomi secondo PEP 8 + +--- +layout: sezione +--- + +# Si riparte da Brano + +La classe della lezione precedente è il punto di partenza: oggi apriamo il cofano. + +--- + +# Tre domande, tre meccanismi + +Che cosa succede quando scrivi `Brano("Bohemian Rhapsody", "Queen", 354)`? + + + +```python +class Brano: + def __init__(self, titolo, artista, durata): + self.titolo = titolo + self.artista = artista + self.durata = durata + self.riproduzioni = 0 + + def riproduci(self): + self.riproduzioni += 1 + return f"▶ {self.titolo} — {self.artista}" +bohemian = Brano("Bohemian Rhapsody", "Queen", 354) +print(bohemian.riproduci()) +``` + + + +--- +layout: sezione +--- + +# `__init__`: il costruttore + +Un oggetto appena nato è una scatola vuota. Chi gli dice il suo titolo? + +--- + +# Python lo chiama da solo + +`__init__` (si legge _dunder init_) **inizializza lo stato** dell'oggetto appena creato. Non lo chiami mai tu: tre oggetti, tre stampe. + + + +```python +class Brano: + def __init__(self, titolo, artista, durata): + print(f"Sto costruendo: {titolo}") + self.titolo = titolo + self.artista = artista + self.durata = durata + +bohemian = Brano("Bohemian Rhapsody", "Queen", 354) +blinding = Brano("Blinding Lights", "The Weeknd", 200) +bury = Brano("Bury a Friend", "Billie Eilish", 193) +``` + + + +--- +layout: definizione +titolo: Costruttore e istanziazione +--- + +Il **costruttore** `__init__` porta un oggetto da «scatola vuota» a «pronto all'uso», assegnando i suoi valori iniziali. + +L'atto di creare un oggetto da una classe si chiama **istanziazione**: `Brano("…", "…", 354)` istanzia un nuovo `Brano`. + +--- +layout: sezione +--- + +# self: l'oggetto che conosce sé stesso + +Il primo parametro di ogni metodo, che nessuno passa a mano. + +--- + +# Tre argomenti per quattro parametri + +```python +def __init__(self, titolo, artista, durata): # 4 parametri + ... + +bohemian = Brano("Bohemian Rhapsody", "Queen", 354) # 3 argomenti +``` + +- `self` non lo passi tu: **lo passa Python**. +- È il riferimento all'oggetto **specifico** che si sta costruendo o usando. +- `self.titolo = titolo` vuol dire: attacca questo titolo **a questo brano qui**. + +--- + +# Ogni oggetto ha il suo self + + + +```python +class Brano: + def __init__(self, titolo): + self.titolo = titolo + self.riproduzioni = 0 + + def riproduci(self): + self.riproduzioni += 1 + +bohemian = Brano("Bohemian Rhapsody") +bury = Brano("Bury a Friend") +bohemian.riproduci() +bohemian.riproduci() +bohemian.riproduci() +print(bohemian.riproduzioni, bury.riproduzioni) +``` + + + +--- +layout: domanda +affiancata: true +invito: Che cosa stampano le due chiamate? Fanno la stessa cosa? +--- + +# Che cosa succede sotto il cofano + + + +```python +class Brano: + def __init__(self, titolo): + self.riproduzioni = 0 + + def riproduci(self): + self.riproduzioni += 1 + return self.riproduzioni + +bohemian = Brano("Bohemian Rhapsody") +print(bohemian.riproduci()) # come lo scrivi tu +print(Brano.riproduci(bohemian)) # come lo legge Python +``` + + + +::risposta:: + +`1` e poi `2`: è la stessa chiamata, sullo stesso oggetto. Python prende l'oggetto **a sinistra del punto** e lo passa come `self`. + +--- + +# Esplicito è meglio che implicito + + + +In Java, C++ e JavaScript lo stesso riferimento esiste, ma è implicito e si chiama `this`. Python lo rende esplicito, come dice lo _Zen of Python_: _explicit is better than implicit_. + + + +È letteralmente l'oggetto che **conosce sé stesso**: ogni `self.qualcosa` è un dato che porta con sé e a cui può sempre accedere. + +--- +layout: sezione +--- + +# Attributi e metodi + +Ciò che un oggetto ha, e ciò che sa fare. + +--- +layout: due-colonne +sinistra: Attributi · lo stato +destra: Metodi · il comportamento +--- + +# Due tipi di cose dentro una classe + +::sinistra:: + +Le variabili attaccate a un oggetto: ciò che **ha**. + +- Si creano nel costruttore con `self.x = …` +- Ogni istanza ha la sua copia +- `titolo`, `artista`, `durata`, `riproduzioni` + +::destra:: + +Le funzioni definite nella classe: ciò che **sa fare**. + +- Il primo parametro è `self` +- Vivono nella classe, agiscono sull'oggetto +- `riproduci()` + +--- + +# La dot notation: la differenza sono le parentesi + + + +```python +class Brano: + def __init__(self, titolo, durata): + self.titolo = titolo + self.durata = durata + + def riproduci(self): + return f"▶ {self.titolo}" + +bohemian = Brano("Bohemian Rhapsody", 354) +print(bohemian.titolo) # attributo +print(bohemian.riproduci()) # metodo +bohemian.durata = 355 # scrivo dall'esterno +print(bohemian.durata) +``` + + + +Scrivere dall'esterno scavalca ogni controllo: lo affronta l'**incapsulamento**. + +--- + +# Un metodo che lavora lo stato + + + +```python +class Brano: + def __init__(self, titolo, durata): + self.titolo = titolo + self.durata = durata + + def durata_minuti(self): + minuti = self.durata // 60 + secondi = self.durata % 60 + return f"{minuti}:{secondi:02d}" + +bohemian = Brano("Bohemian Rhapsody", 354) +print(bohemian.durata_minuti()) +``` + + + +`durata_minuti` non aggiunge dati: parte da `self.durata`, l'unico dato vero, e ne **ricava** una forma leggibile. + +--- + +# I tre inciampi classici con self + + + +1. **`self` dimenticato tra i parametri**: `def riproduci():` → dentro il metodo `self` non esiste (`NameError`). +2. **`self.` dimenticato nel corpo**: `riproduzioni += 1` è una variabile locale inesistente, non l'attributo. +3. **Parentesi sbagliate**: `bohemian.titolo()` è un `TypeError`; `bohemian.riproduci` senza parentesi non esegue niente. + + + +Gli attributi sono **cose che hai**, senza parentesi. I metodi sono **cose che fai**, con le parentesi. + +--- + +# Come si chiamano le cose + + + +- **Classi** in `PascalCase`: `Brano`, `Playlist`, `LettoreMusicale` +- **Attributi e metodi** in `snake_case`: `numero_brani`, `durata_minuti`; niente `numeroBrani` +- I metodi che rispondono sì o no si leggono come una domanda: `is_preferito()`, `ha_brani()` + + + +Non è estetica: ogni sviluppatore Python dà queste convenzioni per scontate, e si confonde se le violi. + +--- +layout: sezione +--- + +# Una seconda classe: la Playlist + +Risonanza raccoglie i brani in playlist: uno stato, un elenco di brani, dei comportamenti. + +--- + +# Un oggetto che contiene altri oggetti + +`brani` è una **lista di oggetti `Brano`**: si chiama **composizione**, e avrà una lezione sua. + +```python +class Playlist: + def __init__(self, nome): + self.nome = nome + self.brani = [] # parte vuota + + def aggiungi(self, brano): + self.brani.append(brano) + + def riproduci_tutti(self): + for brano in self.brani: + print(brano.riproduci()) +``` + +`riproduci_tutti` fa lavorare i suoi `Brano`: la collaborazione fra entità della prima lezione. + +--- +layout: domanda +affiancata: true +invito: Prima di eseguire, prevedi che cosa stampa l'ultima riga. +--- + +# L'attributo che si scollega + + + +```python +class PlaylistFragile: + def __init__(self): + self.brani = [] + self.numero_brani = 0 + + def aggiungi(self, brano): + self.brani.append(brano) + # dimenticato: self.numero_brani += 1 + +p = PlaylistFragile() +p.aggiungi("Blinding Lights") +p.aggiungi("Bury a Friend") +print(len(p.brani), p.numero_brani) +``` + + + +::risposta:: + +`2 0`: la lista ha due brani, il contatore è fermo a zero. **Due fonti di verità** per lo stesso fatto: basta un aggiornamento dimenticato perché divergano. + +--- + +# Non memorizzare ciò che puoi derivare + + + +```python +class Playlist: + def __init__(self, nome): + self.nome = nome + self.brani = [] + + def aggiungi(self, brano): + self.brani.append(brano) + + def numero_brani(self): + return len(self.brani) + +p = Playlist("Workout") +p.aggiungi("Blinding Lights") +p.aggiungi("Bury a Friend") +print(p.numero_brani()) +``` + + + +--- +layout: enunciato +--- + +Se un'informazione **dipende** da un'altra, tienine una sola come dato e **calcola** l'altra. + +Il numero di brani dipende dalla lista, i minuti dipendono dai secondi. Due copie dello stesso fatto prima o poi si scollegano, sempre nel momento peggiore. Con `@property`, più avanti, il calcolo sembrerà un attributo. + +--- +layout: sezione +--- + +# Disegnare una classe + +I programmatori non si scambiano il codice: disegnano. La notazione standard è UML. + +--- +layout: figura +didascalia: Nome, attributi (lo stato), metodi (il comportamento). Il + vuol dire «visibile dall'esterno». +--- + +# La scatola a tre scomparti + +::figura:: + +```mermaid +classDiagram + class Brano { + +titolo + +artista + +durata + +riproduzioni + +riproduci() + +durata_minuti() + } + class Playlist { + +nome + +brani + +aggiungi(brano) + +riproduci_tutti() + +numero_brani() + } +``` + +--- + +# Nel codice vero: una classe per file + + + +Qui `Brano` e `Playlist` stanno insieme perché devono girare in pagina. In un progetto reale si scrive **una classe per file**: `brano.py`, `playlist.py`, e un `main.py` che le importa con `from brano import Brano`. + + + +Copi `brano.py` in un altro progetto e funziona, senza pescare le righe giuste da un file di ottocento. + +--- +layout: sintesi +--- + +# In sintesi + + + +- Il **costruttore** `__init__` inizializza lo stato; Python lo chiama **da solo**. +- `self` è l'oggetto su cui il metodo lavora: `oggetto.metodo()` è `Classe.metodo(oggetto)`. +- **Attributi**: lo stato, senza parentesi. **Metodi**: il comportamento, con le parentesi. +- Se un dato dipende da altri, **calcolalo**: due fonti di verità si scollegano. +- UML: scatola a tre scomparti. PEP 8: `PascalCase` per le classi, `snake_case` per il resto. + + + +--- +layout: quiz +numero: 1 di 2 +--- + +# Passi tre argomenti a `Brano(...)`, ma `__init__` ha quattro parametri. Perché non è un errore? + +`self` ha un valore di default. +`self` lo fornisce Python: è il nuovo oggetto in costruzione. +`durata` è numerico e conta come due parametri. +L'ultimo argomento viene ignorato. + +--- +layout: quiz +numero: 2 di 2 +--- + +# Vuoi sapere quanti brani ci sono in una `Playlist`. Qual è l'approccio più robusto? + +Un attributo `numero_brani` da incrementare a ogni modifica. +Un metodo che restituisce `len(self.brani)`. +Contarli a mano e scriverlo in un commento. +Una seconda lista parallela con i numeri dei brani. + +--- +layout: fine +--- + +# La prossima lezione + +E se un'informazione riguardasse **tutti** i brani insieme, come quanti ne sono stati creati? Arrivano gli **attributi di classe** e i **metodi di classe e statici**: roba dello stampo, non del singolo biscotto. diff --git a/slides/decks/artefice/incapsulamento.md b/slides/decks/artefice/incapsulamento.md new file mode 100644 index 0000000..71390ea --- /dev/null +++ b/slides/decks/artefice/incapsulamento.md @@ -0,0 +1,449 @@ +--- +theme: ../../theme +title: Incapsulamento +sottotitolo: Che cosa esporre e che cosa nascondere. Interfaccia pubblica e stato interno, le convenzioni _ e __, getter e setter senza cerimonia e il principio «tell, don’t ask». +volume: artefice +numero: 6 +lezione: artefice/incapsulamento +--- + +--- +layout: epigrafe +autore: Gandalf — Il Signore degli Anelli, J.R.R. Tolkien +--- + +Keep it secret. Keep it safe. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- `self`, attributi d'istanza, `__str__` e `__repr__` +- Scrivere un `__init__` e creare istanze +- La differenza fra **leggere** `brano.durata` e **scrivere** `brano.durata = …` + +::imparerai:: + +- Che cos'è l'**incapsulamento** +- **Interfaccia pubblica** e **stato interno** +- Le convenzioni `_` e `__` e il **name mangling** +- Quando servono **getter** e **setter**, e quando sono cerimonia +- Il principio **tell, don't ask** + +--- +layout: sezione +--- + +# Lo stato spalancato + +Chiunque, da qualunque punto del programma, può scrivere sugli attributi di un brano. + +--- +layout: domanda +affiancata: true +invito: Che cosa succede assegnando valori assurdi? +--- + +# Valori assurdi, nessun errore + + + +```python +class Brano: + def __init__(self, titolo, artista, durata): + self.titolo = titolo + self.artista = artista + self.durata = durata + +b = Brano("Bohemian Rhapsody", "Queen", 354) +b.durata = -50 +b.artista = 42 +print(b.titolo, b.durata, b.artista) +``` + + + +::risposta:: + +Nessun errore: Python accetta tutto. L'esplosione arriverà più tardi, quando qualcuno sommerà le durate della playlist. Lo stato è **spalancato** e nessuno fa la guardia. + +--- +layout: sezione +--- + +# Che cosa significa incapsulare + +Mettere in una capsula: decidere che cosa è visibile da fuori e che cosa resta affare interno. + +--- +layout: due-colonne +sinistra: Interfaccia pubblica +destra: Stato interno +--- + +# Ogni oggetto ben fatto ha due facce + +::sinistra:: + +I metodi e gli attributi che il mondo esterno può usare: **il contratto** della classe. + +«Puoi chiedermi di riprodurmi, puoi leggermi il titolo, puoi votarmi da 1 a 5.» + +::destra:: + +Le variabili che servono a far funzionare la classe, ma non riguardano chi sta fuori. + +È il **come funziona dentro**, non il **che cosa puoi fare con me**. + +--- +layout: definizione +titolo: Interfaccia e implementazione +--- + +L'**interfaccia** è ciò che la classe promette: i metodi e gli attributi accessibili dall'esterno. L'**implementazione** è come mantiene la promessa. + +La regola d'oro: **programma verso l'interfaccia, non verso l'implementazione**. Finché il contratto resta lo stesso, l'interno si può riscrivere da capo senza che nessuno se ne accorga. + +--- +layout: sezione +--- + +# Le convenzioni: _ e __ + +Python non ha `private` e `public`: «siamo tutti adulti consenzienti». + +--- +layout: due-colonne +sinistra: Un underscore · _durata +destra: Due underscore · __durata +--- + +# Il nome segnala l'intenzione + +::sinistra:: + +**Privato per convenzione.** + +Un cartello: è un dettaglio interno, non toccarlo se non sai che cosa fai. Python non ti ferma: ti guarda male. + +::destra:: + +**Fortemente privato.** + +Python attiva il **name mangling** e rende l'accesso da fuori davvero scomodo. Lo usiamo in questa lezione perché il meccanismo è istruttivo; con `@property` si torna spesso al singolo. + +--- +layout: domanda +affiancata: true +invito: Il blocco solleva un errore di proposito. Quale riga lo causa? +--- + +# Che cosa fa il name mangling + + + +```python +class Brano: + def __init__(self, titolo, durata): + self.__titolo = titolo + self.__durata = max(0, durata) + +b = Brano("Bohemian Rhapsody", 354) +print(b._Brano__durata) # nome «storpiato» +print(b.__durata) # nome «nudo» +``` + + + +::risposta:: + +L'ultima: `AttributeError`. Python riscrive `__durata` in **`_Brano__durata`**. Non è inaccessibile, ma è abbastanza brutto da scoraggiare l'accesso per sbaglio. + +--- + +# La trappola subdola dell'assegnazione + +```python +b.__durata = 9999 +print(b.__durata) # 9999: sembra aver funzionato +``` + + + +Python non ha toccato lo stato privato: ha creato un attributo **pubblico nuovo** che si chiama per caso `__durata`. Il vero `_Brano__durata` è ancora 354, e i metodi della classe usano quello. + + + +Con un oggetto incapsulato si passa **sempre dai metodi pubblici**. + +--- + +# Perché «name mangling» + + + +Lo scopo originale del doppio underscore era evitare **collisioni di nomi**: se una classe e una sua sottoclasse hanno entrambe `__x`, diventano `_Base__x` e `_Sotto__x`, due attributi che non si pestano i piedi. Tornerà con l'ereditarietà. + + + +--- +layout: sezione +--- + +# Getter: leggere lo stato protetto + +Un metodo pubblico che restituisce un attributo privato: per convenzione `get_qualcosa`. + +--- + +# La falla è chiusa + + + +```python +class Brano: + def __init__(self, titolo, durata): + self.__titolo = titolo + self.__durata = max(0, durata) + + def get_durata(self): + return self.__durata + +b = Brano("Bohemian Rhapsody", 354) +b.durata = -50 # spazzatura +print(b.get_durata()) # 354 +``` + + + +`b.durata = -50` crea un attributo che nessun metodo guarderà mai. Il getter restituisce il valore vero, blindato dentro `__durata`. + +--- +layout: sezione +--- + +# Setter: scrivere con validazione + +Quando un attributo deve cambiare anche da fuori, ma solo entro regole precise. + +--- + +# Una valutazione da 1 a 5 + + + +```python +class Brano: + def __init__(self, titolo): + self.__valutazione = None + + def set_valutazione(self, stelle): + if stelle not in (1, 2, 3, 4, 5): + print(f"{stelle!r} ignorato: 1-5") + return + self.__valutazione = stelle + +b = Brano("Bohemian Rhapsody") +b.set_valutazione(11) +b.set_valutazione("cinque") +b.set_valutazione(5) +``` + + + +--- + +# Non un getter e un setter per ogni attributo + + + +Attributo per attributo, chiediti: + +- **Va letto da fuori?** Se no, niente getter. +- **Va scritto da fuori?** Se no, niente setter. +- **La scrittura richiede una regola?** Se sì, il setter si guadagna il posto. Se fa solo `self.__x = valore`, è rumore. + + + +Per i casi semplici, Python ha le `@property`: sono la prossima lezione. + +--- +layout: sezione +--- + +# Tre livelli di visibilità + +Ogni attributo privato ricade in una di tre categorie. Sceglierla è una decisione di design. + +--- +layout: figura +--- + +# Due domande per ogni attributo privato + +::figura:: + +```mermaid +flowchart LR + A["self.__x"] --> B{"Letto
da fuori?"} + B -- "no" --> C["Solo interno"] + B -- "sì" --> D{"Scritto
da fuori?"} + D -- "no" --> E["Read-only"] + D -- "sì" --> F["Read/write
con validazione"] +``` + +--- + +# Solo interno, read-only, read/write + +- **Solo interno**: né getter né setter. Dati di servizio, come un contatore privato o l'ora dell'ultimo ascolto: da fuori nessuno sa che esistono. +- **Read-only**: solo il getter. Dati fissati alla nascita, come `titolo`, `artista` e `durata`: un brano non cambia durata a metà ascolto. +- **Read/write controllato**: getter e setter che valida. Stato che cambia legittimamente, come la valutazione, ma solo entro regole precise. + +--- +layout: sezione +--- + +# Tell, don't ask + +Dì all'oggetto che cosa fare, invece di chiedergli i dati e decidere al posto suo. + +--- +layout: due-colonne +sinistra: Ask · leggo, calcolo, riscrivo +destra: Tell · dico all'oggetto che cosa fare +--- + +# Contare un ascolto + +::sinistra:: + +```python +attuali = b.get_riproduzioni() +b.set_riproduzioni(attuali + 1) +``` + +La regola «riprodurre è +1» vive **fuori** dal brano. Il `Brano` è trattato come un sacco di variabili. + +::destra:: + +```python +def riproduci(self): + self.__riproduzioni += 1 + +b.riproduci() +``` + +`__riproduzioni` ha un getter e **nessun setter**: l'unico modo per farlo crescere è `riproduci()`. Se la regola cambia, si cambia **un posto solo**. + +--- +layout: due-colonne +--- + +# Due regole imparentate, e una terza + +::sinistra:: + + + +Metodi che esprimono l'**intenzione**: `riproduci()`, `vota(5)`. + +**Legge di Demeter**: parla solo con i vicini diretti. Niente catene di punti dentro gli oggetti altrui: il lettore offre un metodo suo, come `durata_corrente()`. + + + +::destra:: + + + +Quante ragioni ha questa classe per cambiare? **Una sola**. `Brano` modella un brano: non si salva su file, non disegna il player, non scarica copertine. + + + +--- +layout: figura +didascalia: Prima il codice parla con lo stato e può corromperlo; dopo ogni accesso passa da un metodo. +--- + +# Prima e dopo + +::figura:: + +```mermaid +flowchart TB + subgraph Prima["Prima: stato spalancato"] + direction LR + M1["il tuo codice"] -->|"b.durata = -50"| S1["Brano
titolo · durata"] + end + subgraph Dopo["Dopo: stato incapsulato"] + direction LR + M2["il tuo codice"] -->|"riproduci()
get_durata()"| G["metodi pubblici"] + G --> S2["Brano
__titolo · __durata"] + end + Prima ~~~ Dopo +``` + +--- +layout: sintesi +--- + +# In sintesi + + + +- L'**incapsulamento** separa **interfaccia pubblica** e **stato interno**. +- `_x`: privato per convenzione. `__x`: **name mangling** in `_Classe__x`. +- `obj.__x = …` da fuori crea un attributo-spazzatura, non tocca quello privato. +- **Getter** per leggere, **setter** solo se valida. Tre livelli: interno, read-only, read/write. +- **Tell, don't ask**: metodi che esprimono l'intenzione, come `riproduci()`. +- Parti da tutto privato; apri solo quando un caso reale lo chiede. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# Con `self.__durata` privato, da fuori scrivi `brano.__durata = 9999`. Che cosa succede? + +La durata ora vale 9999. +Crei un attributo-spazzatura; `_Brano__durata` resta intatto. +`AttributeError`, perché è privato. +Niente: l'assegnazione viene ignorata. + +--- +layout: quiz +numero: 2 di 3 +--- + +# `__riproduzioni` ha un getter ma nessun setter: cresce solo con `riproduci()`. Perché è una buona scelta? + +Il conteggio si può solo leggere, mai aumentare. +Nessuno lo falsifica da fuori, e la regola «+1» vive in un posto solo. +I getter sono più veloci dei setter. +Senza setter è read-only anche dall'interno. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Quale di questi setter si guadagna il posto? + +`set_titolo` che fa solo `self.__titolo = t` +`set_valutazione` che controlla tipo e intervallo 1-5 +`set_durata` che fa solo `self.__durata = d` +`get_riproduzioni` che restituisce il contatore + +--- +layout: fine +--- + +# La prossima lezione + +Sicuri ma più verbosi: `b.get_valutazione()` al posto di `b.valutazione`. Con **`@property`** si ha la sintassi pulita dell'attributo e, dietro le quinte, tutta la validazione di un setter. diff --git a/slides/decks/artefice/metodi-di-classe-e-statici.md b/slides/decks/artefice/metodi-di-classe-e-statici.md new file mode 100644 index 0000000..5c55101 --- /dev/null +++ b/slides/decks/artefice/metodi-di-classe-e-statici.md @@ -0,0 +1,454 @@ +--- +theme: ../../theme +title: Metodi di classe e statici +sottotitolo: Ciò che appartiene allo stampo e non al singolo biscotto. Attributi di classe, metodi di classe con @classmethod e i costruttori alternativi, metodi statici con @staticmethod. +volume: artefice +numero: 4 +lezione: artefice/metodi-di-classe-e-statici +--- + +--- +layout: epigrafe +autore: Tim Peters, lo Zen di Python +--- + +Namespaces are one honking great idea — let's do more of those! + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- _Classi, istanze e metodi_: `__init__`, `self`, attributi e metodi +- Funzioni, liste e dizionari +- `"a;b;c".split(";")` per smontare una stringa +- _f-string_ + +::imparerai:: + +- **Attributo di istanza** e **attributo di classe** +- Il trabocchetto degli attributi di classe mutabili +- **`@classmethod`**, `cls` e i costruttori alternativi +- **`@staticmethod`**: funzioni che appartengono al tema della classe +- Scegliere fra istanza, classe, statico o funzione + +--- +layout: enunciato +--- + +Quanti brani ho creato in totale? La risposta **non appartiene a nessun brano**. + +_Bohemian Rhapsody_ non sa nulla di _Blinding Lights_, e non deve saperlo. Esistono dati e comportamenti che appartengono allo **stampo**, la classe, e non al singolo biscotto. + +--- +layout: sezione +--- + +# La domanda che nessun brano sa rispondere + +Proviamo la strada ingenua: un contatore fuori dalla classe. + +--- + +# Un contatore globale: funziona, ma puzza + + + +```python +contatore_brani = 0 + +class Brano: + def __init__(self, titolo): + global contatore_brani + contatore_brani += 1 + self.titolo = titolo + +Brano("Bohemian Rhapsody") +Brano("Blinding Lights") +print(contatore_brani) +``` + + + +È un'informazione **su** `Brano` che vive lontano da `Brano`: chiunque può azzerarla. La parola `global` è la spia rossa. + +--- +layout: sezione +--- + +# Attributi di classe + +Lo stato dello stampo: una sola copia, condivisa da tutte le istanze. + +--- + +# Il contatore vive sullo stampo + + + +```python +class Brano: + creati = 0 # di CLASSE: uno solo + + def __init__(self, titolo): + Brano.creati += 1 + self.titolo = titolo + self.riproduzioni = 0 # di ISTANZA: uno per brano + +Brano("Bohemian Rhapsody") +Brano("Blinding Lights") +Brano("Bury a Friend") +print(Brano.creati) +``` + + + +--- +layout: definizione +titolo: Attributo di istanza e attributo di classe +--- + +Un **attributo di istanza** si crea con `self.x = …`, di solito in `__init__`: ogni oggetto ne ha la **propria** copia. È lo stato del biscotto. + +Un **attributo di classe** si dichiara nel corpo della classe, `x = …`: ne esiste **una sola** copia, condivisa e accessibile come `Classe.x`. È lo stato dello stampo. + +--- +layout: domanda +affiancata: true +invito: Che cosa stampano le ultime due righe? +--- + +# Quando la condivisione si ritorce contro + + + +```python +class Brano: + tag = [] # lista di CLASSE + + def __init__(self, titolo): + self.titolo = titolo + + def aggiungi_tag(self, etichetta): + self.tag.append(etichetta) +bohemian = Brano("Bohemian Rhapsody") +bury = Brano("Bury a Friend") +bohemian.aggiungi_tag("rock") +bury.aggiungi_tag("electropop") +print(bohemian.tag) +print(bury.tag) +``` + + + +::risposta:: + +Tutti e due `['rock', 'electropop']`: `tag` è **una sola lista**, appesa allo stampo. Ogni brano scrive sullo stesso oggetto. + +--- + +# La cura: la lista nasce con l'oggetto + + + +```python +class Brano: + def __init__(self, titolo): + self.titolo = titolo + self.tag = [] # una lista per brano + + def aggiungi_tag(self, etichetta): + self.tag.append(etichetta) + +bohemian = Brano("Bohemian Rhapsody") +bohemian.aggiungi_tag("rock") +print(bohemian.tag) +``` + + + +- Dato **uguale e condiviso** da tutti (contatore, costante) → attributo di classe. +- Dato **proprio** di ogni oggetto, soprattutto liste e dizionari → in `__init__`. + +--- +layout: domanda +affiancata: true +invito: Perché nel contatore abbiamo scritto Brano.creati e non self.creati? Prevedi le stampe. +--- + +# Scrivere via self non aggiorna lo stampo + + + +```python +class Brano: + creati = 0 + + def __init__(self, titolo): + self.creati += 1 # attenzione + self.titolo = titolo + +a = Brano("A") +b = Brano("B") +print(Brano.creati) +print(a.creati, b.creati) +``` + + + +::risposta:: + +`0`, poi `1 1`. `self.creati += 1` **legge** il valore di classe ma **assegna** un attributo d'istanza nuovo, che copre quello di classe. Per il dato condiviso si scrive `Brano.creati += 1`. + +--- +layout: sezione +--- + +# Metodi di classe + +`@classmethod`: un metodo che riceve la classe, `cls`, invece di un oggetto. + +--- + +# Un costruttore alternativo + +I brani spesso arrivano da un file: `"Bohemian Rhapsody;Queen;354"`. + + + +```python +class Brano: + def __init__(self, titolo, artista, durata): + self.titolo = titolo + self.artista = artista + self.durata = durata + + @classmethod + def da_riga(cls, riga): + titolo, artista, durata = riga.split(";") + return cls(titolo, artista, int(durata)) +b = Brano.da_riga("Bohemian Rhapsody;Queen;354") +print(b.titolo, b.durata) +``` + + + +--- + +# Tre cose da notare + +- `da_riga` si chiama **sulla classe**: l'oggetto non esiste ancora, è lei a crearlo. +- `cls` è la classe `Brano`: `return cls(...)` chiama il costruttore normale. Il costruttore alternativo non scavalca `__init__`, **lo usa**. +- Un eventuale contatore `Brano.creati` scatta lo stesso, perché `cls(...)` passa da `__init__`. + + + +Con l'ereditarietà, `Podcast.da_riga(...)` avrà `cls` uguale a `Podcast` e costruirà l'oggetto giusto; `Brano` scritto a mano continuerebbe a sfornare brani. Nei costruttori alternativi si usa **`cls`**. + + + +--- + +# Più strade, una sola destinazione + +```python +@classmethod +def da_dizionario(cls, dati): + return cls(dati["titolo"], dati["artista"], dati["durata"]) +``` + +```python +Brano("Bury a Friend", "Billie Eilish", 193) +Brano.da_riga("Blinding Lights;The Weeknd;200") +Brano.da_dizionario({"titolo": "Bury a Friend", "artista": "Billie Eilish", "durata": 193}) +``` + +Argomenti sciolti, testo, dizionario: ogni ingresso parla un dialetto diverso e lo traduce nella forma canonica di `__init__`. + +--- +layout: due-colonne +--- + +# Un nome per ogni strada + +::sinistra:: + + + +Un metodo che consegna un oggetto pronto, nascondendo **come** viene costruito, è il seme del pattern **Factory**. Lo incontreremo per nome più avanti. + + + +::destra:: + + + +Niente `__init__(self, titolo=None, riga=None, dati=None)` pieno di `if`. **Un `__init__` per la forma canonica**, e un `@classmethod` con un nome parlante per ogni alternativa. + + + +--- +layout: sezione +--- + +# Metodi statici + +`@staticmethod`: niente `self`, niente `cls`. Una funzione che parla del tema della classe. + +--- + +# Trasformare secondi in minuti:secondi + + + +```python +class Brano: + def __init__(self, titolo, durata): + self.titolo = titolo + self.durata = durata + + @staticmethod + def formatta_durata(secondi): + return f"{secondi // 60}:{secondi % 60:02d}" + + def durata_leggibile(self): + return Brano.formatta_durata(self.durata) + +print(Brano.formatta_durata(354)) +print(Brano("Bury a Friend", 193).durata_leggibile()) +``` + + + +--- +layout: enunciato +--- + +Una classe è anche uno **spazio dei nomi**: un cassetto dove tieni ciò che logicamente sta insieme. + +`formatta_durata` vive sotto `Brano` perché parla di durate di brani, ed è lì che chi legge il codice andrà a cercarla. È il senso della citazione di Tim Peters. + +--- + +# Non infilare tutto in una classe + + + +Se una funzione **non ha nulla a che fare** con la classe, un calcolo generico o un'utilità che useresti ovunque, è semplicemente una **funzione del modulo**, fuori dalla classe. + + + +Il test è onesto: questa logica appartiene, **per argomento**, a questa classe? Per `formatta_durata` sì. Per una `radice_quadrata` no. La OOP non è una religione. + +--- +layout: sezione +--- + +# I tre tipi, fianco a fianco + +La domanda da farsi: di che cosa ha bisogno questo metodo per fare il suo lavoro? + +--- + +# Quale metodo scrivere + +| Tipo | Riceve | Lo usi quando | Si chiama | +|---|---|---|---| +| **D'istanza** | `self` | serve lo stato del singolo oggetto | `oggetto.metodo()` | +| **Di classe** | `cls` | serve la classe, o crei istanze | `Classe.metodo()` | +| **Statico** | niente | appartiene al tema della classe | `Classe.metodo()` | + + + +1. Ti serve `self.qualcosa`? → metodo **d'istanza**, il caso normale. +2. Ti serve la classe, per costruire istanze? → **`@classmethod`**. +3. Né l'uno né l'altra, ma sta bene vicino alla classe? → **`@staticmethod`**. Altrimenti, funzione del modulo. + + + +--- +layout: figura +didascalia: I membri dello stampo (creati, da_riga, formatta_durata) sono sottolineati. +--- + +# Due piani in una scatola sola + +::figura:: + +```mermaid +classDiagram + class Brano { + +titolo + +artista + +durata + +creati$ + +riproduci() + +durata_leggibile() + +da_riga(riga)$ + +formatta_durata(secondi)$ + } +``` + +--- +layout: sintesi +--- + +# In sintesi + + + +- **Istanza**: `self.x` in `__init__`, una copia per oggetto. **Classe**: `x = …` nel corpo, una copia sola, `Classe.x`. +- Dati condivisi sulla classe; liste e dizionari propri in `__init__`. +- Si modifica la classe scrivendo `Brano.creati += 1`, non `self.creati`. +- **`@classmethod`** riceve `cls`: costruttori alternativi con `return cls(...)`. +- **`@staticmethod`** non riceve niente: funzione di supporto nello spazio dei nomi della classe. +- Domanda guida: l'oggetto, la classe, o niente? + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# Dichiari `tag = []` nel corpo della classe, non in `__init__`. Che cosa succede? + +Ogni brano ha la sua lista vuota e indipendente. +Tutti i brani condividono la stessa lista. +Python solleva un errore. +La lista torna vuota a ogni nuovo brano. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Perché scrivere `da_riga` come `@classmethod` con `return cls(...)` e non come funzione con `return Brano(...)`? + +È più veloce in memoria. +Una funzione normale non potrebbe usare `split`. +Vive sulla classe e, con `cls`, costruisce l'oggetto giusto anche per le classi figlie. +Un metodo statico non può restituire un oggetto. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Una funzione converte secondi in `"minuti:secondi"`: non usa né un brano né la classe, ma parla di durate. Come la scrivi? + +Metodo d'istanza, con `self`. +Metodo di classe, con `cls`. +Metodo statico, con `@staticmethod`. +Variabile di classe con il risultato già calcolato. + +--- +layout: fine +--- + +# La prossima lezione + +Per mostrare un brano scriviamo ancora a mano `print(f"{b.titolo} — {b.artista}")`. Con **`__str__`** e **`__repr__`** l'oggetto impara a raccontarsi da solo: `print(bohemian)` basterà. diff --git a/slides/decks/artefice/mostrare-un-oggetto.md b/slides/decks/artefice/mostrare-un-oggetto.md new file mode 100644 index 0000000..312d63c --- /dev/null +++ b/slides/decks/artefice/mostrare-un-oggetto.md @@ -0,0 +1,398 @@ +--- +theme: ../../theme +title: Mostrare un oggetto +sottotitolo: Il primo assaggio dei metodi speciali. __str__ per l’utente, __repr__ per lo sviluppatore. L’oggetto che impara a raccontarsi da solo. +volume: artefice +numero: 5 +lezione: artefice/mostrare-un-oggetto +--- + +--- +layout: epigrafe +autore: William Shakespeare, Romeo e Giulietta +--- + +What's in a name? That which we call a rose by any other name would smell as sweet. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Attributi e metodi d'istanza; `__init__` chiamato da Python +- _f-string_ +- La differenza fra **stampare** (`print`) e **restituire** (`return`) +- Che cosa succede quando stampi una lista + +::imparerai:: + +- Che cosa sono i **metodi speciali** (_dunder_) +- **`__str__`**: la descrizione per l'utente +- **`__repr__`**: la descrizione non ambigua per chi programma +- Perché `print(brano)` e `print([brano])` non sono la stessa cosa +- Definisci sempre `__repr__`, `__str__` solo se serve + +--- +layout: enunciato +--- + +L'oggetto **sa tutto di sé**, eppure tocca a noi ricostruirgli la frase ogni volta. + +`print(f"{bohemian.titolo} — {bohemian.artista}")` è ripetitivo e facile da sbagliare. Oggi il brano impara a presentarsi da solo: basterà `print(bohemian)`. + +--- +layout: sezione +--- + +# Che cosa stampa un oggetto «nudo» + +Un `Brano` senza metodi speciali, a parte `__init__`. + +--- +layout: domanda +affiancata: true +invito: Che cosa compare stampando il brano? +--- + +# Stampiamo un brano + + + +```python +class Brano: + def __init__(self, titolo, artista, durata): + self.titolo = titolo + self.artista = artista + self.durata = durata + +bohemian = Brano("Bohemian Rhapsody", "Queen", 354) +print(bohemian) +``` + + + +::risposta:: + +`<__main__.Brano object at 0x…>`: il nome della classe e l'indirizzo in memoria. Python non può indovinare quali dati vuoi vedere né come comporli: **glielo dobbiamo insegnare**. + +--- +layout: sezione +--- + +# `__str__`: la faccia per l'utente + +`print(qualcosa)` chiama dietro le quinte il metodo `__str__` dell'oggetto. + +--- + +# Il brano dice chi è + + + +```python +class Brano: + def __init__(self, titolo, artista): + self.titolo = titolo + self.artista = artista + + def __str__(self): + return f"{self.titolo} — {self.artista}" + +bohemian = Brano("Bohemian Rhapsody", "Queen") +print(bohemian) +print(f"In riproduzione: {bohemian}") +``` + + + +`__str__` **restituisce** una stringa; lo usano `print()`, `str()` e le _f-string_. La formattazione vive dentro l'oggetto: è coesione. + +--- +layout: definizione +titolo: Metodo speciale (dunder) +--- + +Un **metodo speciale** ha il nome circondato da doppio trattino basso, `__nome__`, e Python lo chiama **automaticamente** in situazioni precise: `__init__` alla nascita dell'oggetto, `__str__` quando lo stampi. + +Non lo invochi a mano: scrivi `print(bohemian)` e Python fa il resto. Sono i ganci con cui i tuoi oggetti entrano nel linguaggio. + +--- +layout: sezione +--- + +# La sorpresa della lista + +Abbiamo definito `__str__`: stampando una playlist vedremo i titoli? + +--- +layout: domanda +affiancata: true +invito: Prima di eseguire, prevedi l'output. +--- + +# Una playlist di brani + + + +```python +class Brano: + def __init__(self, titolo, artista): + self.titolo = titolo + self.artista = artista + + def __str__(self): + return f"{self.titolo} — {self.artista}" + +playlist = [Brano("Bohemian Rhapsody", "Queen"), + Brano("Blinding Lights", "The Weeknd")] +print(playlist) +``` + + + +::risposta:: + +Tornano gli indirizzi di memoria. Stampando una **lista**, Python non chiama `__str__` sugli elementi ma l'**altro** metodo speciale, `__repr__`, che non abbiamo ancora scritto. + +--- +layout: sezione +--- + +# `__repr__`: la faccia per lo sviluppatore + +Due metodi, due domande diverse. + +--- +layout: due-colonne +sinistra: __str__ · per l'utente +destra: __repr__ · per chi programma +--- + +# Leggibile o non ambiguo + +::sinistra:: + +Risponde a: **come mostro** questo oggetto a chi usa il programma? + +- leggibile, discorsivo +- anche incompleto +- `Bohemian Rhapsody — Queen` + +::destra:: + +Risponde a: **come descrivo** questo oggetto a chi programma? + +- preciso, non ambiguo +- idealmente completo +- `Brano('Bohemian Rhapsody', 'Queen', 354)` + +--- + +# Un repr che somiglia al costruttore + + + +```python +class Brano: + def __init__(self, titolo, artista, durata): + self.titolo = titolo + self.artista = artista + self.durata = durata + + def __repr__(self): + return f"Brano({self.titolo!r}, {self.artista!r}, {self.durata})" + + def __str__(self): + return f"{self.titolo} — {self.artista}" + +b = Brano("Bohemian Rhapsody", "Queen", 354) +print(b, repr(b), [b], sep="\n") +``` + + + +--- + +# Il !r dentro la f-string + + + +`{self.titolo!r}` inserisce il valore con il suo `__repr__` invece che con `__str__`. Su una stringa, il `__repr__` aggiunge gli apici: `'Bohemian Rhapsody'`. + + + +- I testi escono **con** gli apici, il numero `354` **senza**. +- Così la rappresentazione somiglia al codice che scriveresti. +- Potresti copiarla, incollarla ed eseguirla per ottenere un brano identico: è questo il senso di **non ambiguo**. + +--- +layout: figura +didascalia: La freccia tratteggiata è la regola più importante della lezione. +--- + +# Chi chiama che cosa + +::figura:: + +```mermaid +flowchart LR + A["print(brano)
str(brano)
f-string"] --> S["__str__
per l'utente"] + B["repr(brano)
console
dentro una lista"] --> R["__repr__
per lo sviluppatore"] + S -. "se __str__ manca" .-> R +``` + +--- +layout: sezione +--- + +# Quando ne basta uno + +Il ripiego va in una sola direzione. + +--- + +# Solo `__repr__`: copre tutto + + + +```python +class Brano: + def __init__(self, titolo, artista): + self.titolo = titolo + self.artista = artista + + def __repr__(self): + return f"Brano({self.titolo!r}, {self.artista!r})" + +bury = Brano("Bury a Friend", "Billie Eilish") +print(bury) # niente __str__: usa __repr__ +print([bury]) +``` + + + +Senza `__str__`, Python ripiega su `__repr__`. Il contrario no: solo `__str__` lascia liste e console con l'indirizzo di memoria. + +--- + +# La regola pratica + + + +Parti **sempre** da `__repr__`: salva ogni `print`, ogni lista, ogni sessione di debugging. Aggiungi `__str__` **solo** quando la versione per l'utente è davvero diversa. + + + + + +`print(...)` dentro `__str__` restituisce `None`; `return self.durata` restituisce un numero e dà `TypeError`. La forma giusta è sempre `return f"…"`. + + + +--- +layout: sezione +--- + +# In pratica, sul nostro player + +Una playlist, due letture: una per chi ascolta, una per chi mette le mani nel codice. Il nome con cui mostri la rosa cambia; la rosa resta la stessa. + +--- + +# La coda e lo stato interno + + + +```python +class Brano: + def __init__(self, titolo, artista): + self.titolo = titolo + self.artista = artista + def __repr__(self): + return f"Brano({self.titolo!r}, {self.artista!r})" + def __str__(self): + return f"{self.titolo} — {self.artista}" + +coda = [Brano("Bohemian Rhapsody", "Queen"), + Brano("Bury a Friend", "Billie Eilish")] +for brano in coda: + print(f"♪ {brano}") +print(coda) +``` + + + +--- + +# Il modello dati di Python + + + +`__str__` e `__repr__` sono i primi di decine di metodi speciali: `__len__` per `len(oggetto)`, `__eq__` per `==`, `__add__` per `+`, `__iter__` per i cicli `for`. Insieme formano il **modello dati** (_data model_). + + + +Ogni volta che scrivi `__qualcosa__`, stai parlando direttamente all'interprete. + +--- +layout: sintesi +--- + +# In sintesi + + + +- I **metodi speciali** (`__nome__`) li chiama Python in automatico; `__init__` è uno di loro. +- **`__str__`**: leggibile, per l'utente. Lo usano `print()`, `str()`, le _f-string_. +- **`__repr__`**: non ambiguo, per lo sviluppatore. Lo usano `repr()`, la console, liste e dizionari. +- Se manca `__str__`, si usa `__repr__`: definisci **sempre `__repr__`**. +- `{valore!r}` usa il `__repr__` del valore: utile proprio dentro `__repr__`. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# `Brano` ha `__str__` ma non `__repr__`. Che cosa stampa `print([brano])`? + +La stringa di `__str__`. +`[<__main__.Brano object at 0x…>]`. +Un errore, perché manca `__repr__`. +Una lista vuota. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Puoi scriverne uno solo fra `__str__` e `__repr__`. Quale conviene? + +`__str__`, perché lo usa `print`. +`__repr__`, perché Python ci ripiega quando manca `__str__`. +È indifferente. +`__str__`, perché `__repr__` richiede prima `__str__`. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Quale è una buona implementazione di `__repr__` per `Brano(titolo, artista, durata)`? + +`print(f"{self.titolo}")` +`return self.durata` +`return f"Brano({self.titolo!r}, {self.artista!r}, {self.durata})"` +`return "un brano"` + +--- +layout: fine +--- + +# La prossima lezione + +Chiunque può ancora scrivere `bohemian.durata = -50`: l'oggetto non si difende. Con l'**incapsulamento** impariamo come un oggetto protegge il proprio stato e decide che cosa lasciar toccare. diff --git a/slides/decks/artefice/perche-gli-oggetti.md b/slides/decks/artefice/perche-gli-oggetti.md new file mode 100644 index 0000000..a1879da --- /dev/null +++ b/slides/decks/artefice/perche-gli-oggetti.md @@ -0,0 +1,472 @@ +--- +theme: ../../theme +title: Perché gli oggetti? +sottotitolo: Le liste parallele si disallineano, i dizionari risolvono metà del problema, le classi dichiarano insieme dati e comportamento. Lo vediamo sulla libreria musicale di Risonanza. +volume: artefice +numero: 2 +lezione: artefice/perche-gli-oggetti +--- + +--- +layout: epigrafe +autore: Fred Brooks, The Mythical Man-Month (1975) +--- + +Mostrami i tuoi diagrammi di flusso e nascondimi le tue tabelle, e resterò perplesso. Mostrami le tue tabelle, e di solito i diagrammi non mi serviranno: saranno ovvi. + +--- +layout: obiettivi +--- + +# Da dove partiamo, dove arriviamo + +::prerequisiti:: + +- Variabili, tipi primitivi, condizioni, cicli +- Funzioni: parametri e valori di ritorno +- Liste e dizionari, `append`, accesso per indice e per chiave +- _f-string_ per comporre messaggi + +::imparerai:: + +- Perché la posizione è un legame fragile fra i dati +- Errore che ferma il programma e errore silenzioso +- Che cosa risolvono i dizionari e che cosa no +- **Classe**, **oggetto**, **istanza** e **coesione** +- Quando una classe serve davvero + +--- +layout: enunciato +--- + +Finora hai imparato a dare istruzioni. Adesso la domanda è: **come rappresento le cose** di cui il programma parla? + +Un brano, un giocatore, un conto corrente non sono passi da eseguire: sono entità, con informazioni addosso e operazioni che hanno senso solo su di loro. Lo vediamo su **Risonanza**, un piccolo lettore musicale da terminale. + +--- +layout: sezione +--- + +# Rappresentare un brano + +Titolo, artista, durata, riproduzioni: con gli strumenti del Volume 1 la strada più diretta è una lista per ogni informazione. + +--- + +# Le liste parallele + +A tenere insieme un brano è la **posizione**: l'elemento di indice `i` di ogni lista appartiene allo stesso brano. + + + +```python +titoli = ["Blinding Lights", "Bohemian Rhapsody"] +artisti = ["The Weeknd", "Queen"] +riproduzioni = [0, 0] + +def riproduci(indice): + riproduzioni[indice] += 1 + print(f"▶ {titoli[indice]} — {artisti[indice]}") + +riproduci(0) +print(riproduzioni) +``` + + + +--- +layout: enunciato +--- + +La corrispondenza esiste **soltanto nella nostra testa**. + +Python non sa che `titoli[0]` e `durate[0]` parlano della stessa canzone: per lui sono due liste che al momento hanno la stessa lunghezza. Tenerle allineate è un lavoro nostro, e nessuno ci avvisa quando smettiamo di farlo. + +--- +layout: sezione +--- + +# Due modi di rompere l'allineamento + +Lunghezze diverse oppure ordini diversi: il primo si fa notare, il secondo no. + +--- + +# Primo modo: una lista resta indietro + +```python +titoli.append("As It Was") +artisti.append("Harry Styles") +durate.append(167) +# riproduzioni non è stata aggiornata +``` + +`riproduzioni` ha tre elementi, le altre quattro. Alla prima chiamata di `riproduci(3)` Python cerca `riproduzioni[3]`, non lo trova e solleva un `IndexError`. + +È il **caso fortunato**: il programma si ferma e ti dice dove guardare. + +--- +layout: domanda +--- + +# Secondo modo: ordiniamo per durata + + + +```python +titoli = ["Blinding Lights", "Bohemian Rhapsody", "Bury a Friend"] +durate = [200, 354, 193] + +durate.sort(reverse=True) + +for i in range(len(titoli)): + print(f"{titoli[i]} — {durate[i]}s") +``` + + + +::risposta:: + +Le durate escono ordinate, 354 → 200 → 193, ma accanto ai titoli sbagliati: _Blinding Lights_ risulta di 354 secondi. `sort` ha riordinato `durate` e nient'altro. + +--- + +# Un errore che il programma non segnala + + + +Le due liste hanno tre elementi, gli indici da `0` a `2` sono validi, ogni accesso restituisce un valore che esiste. L'unica cosa sbagliata è l'**abbinamento**, e l'abbinamento non è scritto da nessuna parte nel codice. + + + +Un programma che si ferma con un errore è scomodo ma leale. Uno che prosegue con numeri plausibili ti lascia scoprire il guasto mesi dopo. + +Il colpevole non è `sort`: le informazioni di un brano sono sparse su quattro strutture, legate solo da un numero. + +--- +layout: sezione +--- + +# I dizionari raccolgono i dati + +Uno strumento del Volume 1 sistema buona parte della faccenda. + +--- +layout: due-colonne +rapporto: 1/1 +--- + +# Tutti i dati di un brano in una struttura sola + +::sinistra:: + + + +```python +brano = { + "titolo": "Bohemian Rhapsody", + "artista": "Queen", + "durata": 354, + "riproduzioni": 0, +} + +brano["riproduzioni"] += 1 +print(brano["riproduzioni"]) +``` + + + +::destra:: + +Una lista di dizionari si ordina, si filtra, si rimescola: ogni dizionario si porta dietro tutti i suoi campi. **Il disallineamento non può nascere.** + +È un passo avanti vero, e per molti programmi è già la soluzione definitiva. + +--- +layout: due-colonne +sinistra: Le operazioni stanno altrove +destra: Non c'è posto per le regole +--- + +# Che cosa il dizionario non fa + +::sinistra:: + +Riprodurre un brano o convertirne la durata sono **funzioni sparse nel file**. + +Niente dichiara che riguardano proprio quel tipo di dizionario: per sapere che cosa si può fare con un brano devi leggere tutto il modulo. + +::destra:: + +`brano["durata"] = -5` viene accettato. + +`brano["titlo"] = "…"` crea una chiave nuova: il refuso resta lì e te ne accorgi solo rileggendo `brano["titolo"]`. + +Il concetto di «brano valido» non esiste dentro un dizionario. + +--- +layout: sezione +--- + +# Tenere insieme dati e comportamento + +Le informazioni di un'entità e le operazioni che la riguardano si dichiarano nello stesso posto. + +--- +layout: definizione +titolo: Oggetto, classe, istanza +--- + +Un **oggetto** riunisce in un'unica unità i **dati** che descrivono un'entità (il suo **stato**) e i **comportamenti** che operano su quei dati. + +Una **classe** descrive un tipo di oggetto: quali dati avrà e quali operazioni saprà compiere. Un oggetto costruito da una classe è un'**istanza** di quella classe. + +--- + +# La classe `Brano` + +Conta **dove** stanno i dati e ciò che sai farci; `self` e `__init__` sono la prossima lezione. + + + +```python +class Brano: + def __init__(self, titolo, artista): + self.titolo = titolo + self.artista = artista + self.riproduzioni = 0 + + def riproduci(self): + self.riproduzioni += 1 + return f"▶ {self.titolo} — {self.artista}" + +bohemian = Brano("Bohemian Rhapsody", "Queen") +print(bohemian.riproduci()) +``` + + + +--- + +# Una classe è uno stampo + +- Esiste **una volta sola** nel codice, e da lì escono quanti oggetti vuoi. +- Ogni oggetto ha **i propri valori**: un secondo brano ha il suo contatore, indipendente dal primo. +- Lo stampo non contiene i biscotti: descrive la forma, che si applica quando crei l'oggetto. + + + +Quanto le parti di un'unità di codice riguardano la stessa cosa. Le liste parallele ne hanno pochissima, la classe `Brano` risponde in un punto solo a «com'è fatto un brano». Ma **coesione non vuol dire ammassare**: salvare i brani su disco o cercarli per artista stanno meglio altrove. + + + +--- + +# Il bug dell'ordinamento non si può più scrivere + +Qualunque cosa tu faccia all'ordine, sposti **oggetti interi**: ogni titolo si porta dietro la sua durata. + + + +```python +class Brano: + def __init__(self, titolo, durata): + self.titolo = titolo + self.durata = durata + +brani = [Brano("Blinding Lights", 200), + Brano("Bohemian Rhapsody", 354), + Brano("Bury a Friend", 193)] +brani.reverse() +for brano in brani: + print(f"{brano.titolo} — {brano.durata}s") +``` + + + +--- +layout: enunciato +--- + +Sparisce quel bug, **non la possibilità di sbagliare**. + +`bohemian.durata = -5` passa ancora senza proteste: la classe non controlla niente. Hai ottenuto qualcosa di più modesto e più utile: un posto preciso in cui le regole potranno stare. È il tema dell'incapsulamento. + +--- +layout: figura +didascalia: Lo scomparto superiore elenca gli attributi, quello inferiore i metodi. +--- + +# La classe disegnata in UML + +::figura:: + +```mermaid +classDiagram + class Brano { + +titolo + +artista + +durata + +riproduzioni + +riproduci() + } +``` + +--- +layout: sezione +--- + +# Procedurale e oggetti, un confronto onesto + +Il confronto con le liste parallele è troppo facile: un programma procedurale scritto bene userebbe un dizionario. + +--- +layout: due-colonne +sinistra: A oggetti +destra: Procedurale +--- + +# Due versioni che fanno la stessa cosa + +::sinistra:: + +```python +class Brano: + def __init__(self, titolo, artista): + self.titolo = titolo + self.artista = artista + self.riproduzioni = 0 + + def riproduci(self): + self.riproduzioni += 1 + +brano = Brano("Bury a Friend", + "Billie Eilish") +brano.riproduci() +``` + +::destra:: + +```python +def crea_brano(titolo, artista): + return { + "titolo": titolo, + "artista": artista, + "riproduzioni": 0, + } + +def riproduci(brano): + brano["riproduzioni"] += 1 + +brano = crea_brano("Bury a Friend", + "Billie Eilish") +riproduci(brano) +``` + +--- +layout: due-colonne +sinistra: Dove si cerca +destra: Dove si controlla +--- + +# Le differenze vere non riguardano la velocità + +::sinistra:: + +A oggetti, l'elenco di ciò che un brano sa fare **coincide con la classe**: apri `Brano` e lo leggi tutto. + +In procedurale devi scorrere il modulo e indovinare quali funzioni si aspettano un brano. + +::destra:: + +`riproduci(brano)` si fida di ricevere un dizionario fatto bene: ogni funzione deve controllare per conto proprio. + +Una classe ti dà **un solo punto d'ingresso** in cui stabilire che cosa conta come brano valido. + +--- + +# Quando conviene + + + +- Il programma gestisce **parecchie entità** con stato e comportamento propri. +- Le stesse entità compaiono in **punti lontani** del codice. +- Il programma è destinato a **durare** e a essere modificato da altri. +- Uno script di trenta righe che legge un file e stampa una media **non ci guadagna niente**: solo righe. + + + +Python non ti chiede di schierarti: funzioni, dizionari e classi convivono nello stesso file. + +--- + +# Da dove arriva + + + +Fine anni Sessanta: **Simula**, linguaggio norvegese per le simulazioni. Per simulare un porto conveniva rappresentare le cose di quel mondo, ognuna con il suo stato. + +Anni Settanta: **Alan Kay**, allo Xerox PARC, crea **Smalltalk**, dove un programma è un insieme di piccole entità che si scambiano messaggi. Kay ripeterà per decenni che la OOP dei linguaggi di oggi non è quella che intendeva lui: quella di Python è una versione più addomesticata. + + + +--- +layout: sintesi +--- + +# In sintesi + + + +- La difficoltà di un programma che cresce sta nel modo in cui **rappresenta le sue entità**. +- Le **liste parallele** si rompono in due modi: `IndexError` esplicito, oppure dati sbagliati in silenzio. +- Un **dizionario** tiene insieme i dati, ma lascia fuori le operazioni e le regole. +- Una **classe** dichiara insieme dati e comportamenti; un **oggetto** è un esemplare con i propri valori. +- **Coesione**: vicino ciò che cambia insieme, senza ammassare. +- Le classi si usano quando ripagano la complessità che aggiungono. + + + +--- +layout: quiz +numero: 1 di 3 +--- + +# Hai due liste parallele `titoli` e `durate` di tre elementi, e ordini soltanto `durate`. Che cosa succede? + +Il programma rallenta sensibilmente. +Python solleva un `IndexError`. +Nessun errore, ma ogni titolo può finire accanto alla durata di un altro brano. +Le durate vengono eliminate dalla lista. + +--- +layout: quiz +numero: 2 di 3 +--- + +# Qual è l'idea da cui nasce la programmazione a oggetti? + +Sostituire tutte le funzioni con le classi. +Dichiarare nello stesso posto i dati di un'entità e le operazioni che la riguardano. +Evitare i dizionari, che non proteggono i dati. +Rendere il programma più veloce. + +--- +layout: quiz +numero: 3 di 3 +--- + +# Con la classe `Brano` di questa lezione, quale problema **non** hai risolto? + +Ordinare i brani può scollegare titolo e durata. +Niente impedisce una durata negativa. +Aggiungere un campo richiede di modificare più punti. +I brani condividono il contatore delle riproduzioni. + +--- +layout: fine +--- + +# La prossima lezione + +Che cosa succede quando scrivi `Brano(...)`, che ruolo ha `__init__`, perché `self` è il primo parametro di ogni metodo. Ripartiamo da `Brano` e gli affianchiamo la **`Playlist`** di Risonanza. diff --git a/slides/package-lock.json b/slides/package-lock.json new file mode 100644 index 0000000..06b980f --- /dev/null +++ b/slides/package-lock.json @@ -0,0 +1,10194 @@ +{ + "name": "python-doesnt-byte-slides", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "python-doesnt-byte-slides", + "devDependencies": { + "@slidev/cli": "^53.0.0", + "@slidev/parser": "^53.0.0", + "playwright-chromium": "^1.64.1" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-2.1.0.tgz", + "integrity": "sha512-sdg9NxU3zR4Mnawfbc/x6GB5Wf17WYud5qOuEuxXjaKpYpMkISSJEjItGebXJ2bQ4DIcly4NYH23mtkGJjvKUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.8.0", + "tinyexec": "^1.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/ni": { + "version": "30.6.0", + "resolved": "https://registry.npmjs.org/@antfu/ni/-/ni-30.6.0.tgz", + "integrity": "sha512-vRZX+aPd9UzUMjsCeH0CR4ldbCwwGaKjbotF7PBRyZdbZRZJz3Em+HXUNaxGWcdajRpTBL1HQ0KECudftPs7CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fzf": "^0.5.2", + "package-manager-detector": "^1.8.0", + "tinyexec": "^1.3.0", + "tinyglobby": "^0.2.17" + }, + "bin": { + "na": "bin/na.mjs", + "nci": "bin/nci.mjs", + "nd": "bin/nd.mjs", + "ni": "bin/ni.mjs", + "nlx": "bin/nlx.mjs", + "nr": "bin/nr.mjs", + "nun": "bin/nun.mjs", + "nup": "bin/nup.mjs" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@antfu/utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.11" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.9.tgz", + "integrity": "sha512-FFwIwzU+7SCOuxxV4YtJql6T9981ZVTm+FHO5GhVsRqCTdy0WwrEZh3l42ARpXruJUDGOptdduHW6Zr7pNPLNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.9", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.11", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-13.2.0.tgz", + "integrity": "sha512-bA9dvWlhAUcHkNwU+8w9yiUxJPlj/Lm3VqS7MD+NbIR/7LmpNLuK/AWNFdCo/T3SK5ND0/UHf3IJvl/Jn/i6yg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "13.2.0", + "@chevrotain/types": "13.2.0" + } + }, + "node_modules/@chevrotain/gast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-13.2.0.tgz", + "integrity": "sha512-bCARdAsG+eEbOveSXcOAC7pVxA04I1ionzeMOwIYf5NX7SaS7zgn4Dgug2BSqoL+iyKeMNSorQjsFSHJHsCNwg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "13.2.0" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-13.2.0.tgz", + "integrity": "sha512-kxpUkLtiaK09ph+dib+Yvm+3sWSnuwlo25gqfKRgTPNmF6H7uW4gVkMjaRdtnN783S9sM6vUNWh9CsxYX4QwKw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-13.2.0.tgz", + "integrity": "sha512-7qrYHRlKfWf2AvBfJi5eVOrKAkbxvMFsy/iIHTGIiApNcEecCaVIdR+mIQhyCBsTDJxBz4JWXHMo6krbhR9hVg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-13.2.0.tgz", + "integrity": "sha512-SzKYY68rseOrb9KILVzPsl6SjkQtdWw01SRGB5aCVI5kFcHltQsOYV3JB0Ffst72DgkvHCyDQ0NLwxtmCCf97Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@comark/markdown-it": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@comark/markdown-it/-/markdown-it-0.3.4.tgz", + "integrity": "sha512-btB+klvBi08qRZGeSLbykfD2ROxhLLQJ4+uDI7FjL560ztlzTtBlGmqe6zP49mW/Yp9y39kSWeRS46iEFpGa0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "^14.0.0" + } + }, + "node_modules/@devframes/hub": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@devframes/hub/-/hub-1.2.6.tgz", + "integrity": "sha512-nZv+A+lAAwFsbMGgpVbGWlLGDWXK5Egd0LkjGITr9Ji01HHmi3A1AWjpznNDCdSX3Cmlm9EBRz7Q8VzGx/I+dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "h3": "^2.0.1", + "pathe": "^2.0.3", + "tinyexec": "^1.3.1", + "zigpty": "^0.2.1" + }, + "peerDependencies": { + "@devframes/agentic": "1.2.6", + "devframe": "1.2.6" + }, + "peerDependenciesMeta": { + "@devframes/agentic": { + "optional": true + } + } + }, + "node_modules/@devframes/json-render": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@devframes/json-render/-/json-render-1.2.6.tgz", + "integrity": "sha512-ZCDrPxU+KEKMTyqp1ggYQP1OnDlbmXJ8AUtOm9NEntobe9OVYqi8yMabwkAWtc8mhqrOiBE1NpSmczg96IO1Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@json-render/core": "^0.21.0", + "zod": "^4.6.5" + }, + "peerDependencies": { + "@devframes/hub": "1.2.6", + "devframe": "1.2.6" + }, + "peerDependenciesMeta": { + "@devframes/hub": { + "optional": true + } + } + }, + "node_modules/@drauu/core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@drauu/core/-/core-1.0.0.tgz", + "integrity": "sha512-r1fPyuKaGuNHc8vxRFUT8LxqWjJ3nx+U+zsHcEOurmJoB7uN+zpFw5kTLInfdfvQZ+qF/ebQjw1AwbGcc1XKsQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@fix-webm-duration/fix": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@fix-webm-duration/fix/-/fix-1.0.1.tgz", + "integrity": "sha512-rRN4CpWQaXRbCXYqKIxnsUq8OSWSGq/SVlnxxkx0HxMZZ1u0qnB24P766o8QS5YaMMqAOFAzmsMmfZ2OWucOLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fix-webm-duration/parser": "1.0.1", + "tslib": "^2.3.0" + } + }, + "node_modules/@fix-webm-duration/parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@fix-webm-duration/parser/-/parser-1.0.1.tgz", + "integrity": "sha512-Z6el7ohBBpym4iOmxDxumN715cHzLcAbtOtYfIbvoyToVtAuxvHt3ELXGff83iAXEU1Yj7g+obQBdiKJYVhbWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@hono/node-server": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.5.tgz", + "integrity": "sha512-MgP/I06GdvsilDspMk/qmRzpk8LOUStP6jFAbSRi40W09DhoyUn+Mj7eyKICPqeYKybOjVFLVIAQzvu1bfIa7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4 || ^5.0.0-0" + } + }, + "node_modules/@iconify-json/carbon": { + "version": "1.2.29", + "resolved": "https://registry.npmjs.org/@iconify-json/carbon/-/carbon-1.2.29.tgz", + "integrity": "sha512-whk3BOU2jXFtweigCrrrYXT7NzWXGUT1q268vEnySzhfdnwZhQhVlFjhkgZH86JRRx21/CziTmzpAJMe5V2yKQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify-json/ph": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@iconify-json/ph/-/ph-1.2.2.tgz", + "integrity": "sha512-PgkEZNtqa8hBGjHXQa4pMwZa93hmfu8FUSjs/nv4oUU6yLsgv+gh9nu28Kqi8Fz9CCVu4hj1MZs9/60J57IzFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify-json/svg-spinners": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@iconify-json/svg-spinners/-/svg-spinners-1.2.4.tgz", + "integrity": "sha512-ayn0pogFPwJA1WFZpDnoq9/hjDxN+keeCMyThaX4d3gSJ3y0mdKUxIA/b1YXWGtY9wVtZmxwcvOIeEieG4+JNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.7.tgz", + "integrity": "sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^2.0.1", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "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" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@json-render/core": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@json-render/core/-/core-0.21.3.tgz", + "integrity": "sha512-sgzPtRhRB5wkhrbLtPm9bAhna5Q0x/U0vUpb3S/VEutouWEvvZRebHhpM5XiEjRM5Shv4W70VMZ8pGcGcz7j+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "zod": "^4.3.6" + }, + "peerDependencies": { + "zod": "^4.0.0" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@lillallol/outline-pdf": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@lillallol/outline-pdf/-/outline-pdf-4.0.0.tgz", + "integrity": "sha512-tILGNyOdI3ukZfU19TNTDVoS0W1nSPlMxCKAm9FPV4OPL786Ur7e1CRLQZWKJP6uaMQsUqSDBCTzISs6lXWdAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lillallol/outline-pdf-data-structure": "^1.0.3", + "pdf-lib": "^1.16.0" + } + }, + "node_modules/@lillallol/outline-pdf-data-structure": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lillallol/outline-pdf-data-structure/-/outline-pdf-data-structure-1.0.3.tgz", + "integrity": "sha512-XlK9dERP2n9afkJ23JyJzpmesLgiOHmhqKuGgeytnT+IVGFdAsYl1wLr2o+byXNAN5fveNbc7CCI6RfBsd5FCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mdit-vue/plugin-component": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-component/-/plugin-component-3.0.2.tgz", + "integrity": "sha512-Fu53MajrZMOAjOIPGMTdTXgHLgGU9KwTqKtYc6WNYtFZNKw04euSfJ/zFg8eBY/2MlciVngkF7Gyc2IL7e8Bsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/markdown-it": "^14.1.2", + "markdown-it": "^14.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mdit-vue/plugin-frontmatter": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-frontmatter/-/plugin-frontmatter-3.0.2.tgz", + "integrity": "sha512-QKKgIva31YtqHgSAz7S7hRcL7cHXiqdog4wxTfxeQCHo+9IP4Oi5/r1Y5E93nTPccpadDWzAwr3A0F+kAEnsVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mdit-vue/types": "3.0.2", + "@types/markdown-it": "^14.1.2", + "gray-matter": "^4.0.3", + "markdown-it": "^14.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mdit-vue/types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@mdit-vue/types/-/types-3.0.2.tgz", + "integrity": "sha512-00aAZ0F0NLik6I6Yba2emGbHLxv+QYrPH00qQ5dFKXlAo1Ll2RHDXwY7nN2WAfrx2pP+WrvSRFTGFCNGdzBDHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-2.0.1.tgz", + "integrity": "sha512-j37dj/h6p6pots1L7pLPPNuABEEu9WP4OYg9pW1g+nYG331j0+3canE9OagtYdytYvdSAMG+2fKU5cxep6LgjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@chevrotain/types": "~13.2.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.4.0.tgz", + "integrity": "sha512-z8QBKqdfXLPzkLQuG4xp8hrVEMm1PZ1IgxMN2sMbJiFiAKOWXnAzogXn2W4aqIBlP1ea6U5yiqVquvUNM1Cj6Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/node": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/node/-/node-2.1.2.tgz", + "integrity": "sha512-IIcIqBpr6NCQQs5DgJTgvbSOAw+3iaR/uquqg+e0nXCdudfjPg/80x0TseUnQx14AUAtE4fsJCEpJKPyb6WPVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.1.3", + "hono": "^4.11.4" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@modelcontextprotocol/server": "^2.4.0" + } + }, + "node_modules/@modelcontextprotocol/server": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.4.0.tgz", + "integrity": "sha512-rgR9yxSaAJ+bcsi/XroT/S2AfPiIWloXAnrdfYSCms9ivVju0ishu/wW0J21Ho6+ZOR1r7RyeRbjiRuF87ix5Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@modelcontextprotocol/core": "2.4.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nuxt/kit": { + "version": "3.21.11", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-3.21.11.tgz", + "integrity": "sha512-0Xi3tgwN77w43Q8GCPIrvWmF1J7Peehkts44E0uKNIml9lB8WoUn8YxyUjxBv47XtVR86NWoWALAT+/IEMHJEA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "c12": "^3.3.4", + "consola": "^3.4.2", + "defu": "^6.1.7", + "destr": "^2.0.5", + "errx": "^0.1.2", + "exsolve": "^1.1.1", + "ignore": "^7.0.6", + "jiti": "^2.7.0", + "klona": "^2.0.6", + "knitwork": "^1.3.0", + "mlly": "^1.8.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "pkg-types": "^2.3.1", + "rc9": "^3.0.1", + "scule": "^1.3.0", + "semver": "^7.8.5", + "tinyglobby": "^0.2.17", + "ufo": "^1.6.4", + "unctx": "^2.5.0", + "untyped": "^2.0.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/@nuxt/kit/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@oxc-parser/binding-android-arm-eabi": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.153.0.tgz", + "integrity": "sha512-BcWu25dMwMjHpaDCEidmpAi7rGg1qZoihJeUUH9bD55pVZ0z4Wa5CQgqSTo0R41H81oozisQKW3WqvAN2m77IQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-android-arm64": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.153.0.tgz", + "integrity": "sha512-9l5rdp2DhRqVriBHsNpu5cEf+hizZ4B58rt93HzyQEBUgZiC6qe2oFpaAAnXGCxHGnQExI1s7S2G0QhFacUZ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-arm64": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.153.0.tgz", + "integrity": "sha512-EcpsyRXHhENDpekpG4QNaSZj2F/BAz4Gh4o89zZwNss86R8KyRSQ6sXbAj74Xyeb4z3j/yBZZ6joQZhraIWyfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-x64": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.153.0.tgz", + "integrity": "sha512-hlj80HUQy07/2lYp6rmxH+3u+H2ik3rjLR52CUdPApxmZCNxASDUf/+ykTzAHS/fehftmVMATKvPaWmuLHuasg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-freebsd-x64": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.153.0.tgz", + "integrity": "sha512-fWRcv2pMSh1qtez46MOu8vuUJ5fiaQZPHSphbWulbL7SHBMKfRgZl2PzjXZYQJbq7NEXyXbgIuIuUm+/Rmkv8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.153.0.tgz", + "integrity": "sha512-paZeUK1GXShl/bOJQET1/8AQxayFeuN4SIWAkgTRxE7ZQwv9uYSB2n0648smygP22N7jcI3zxAm0Z+ALNiIKxg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.153.0.tgz", + "integrity": "sha512-qFPp5NwMf4K1C0O+DCVGmcbOaYyYAq98Rvv9p4VSfb777iUwlJCqMUxf6QG9N5f5ls4TEX6LitOZHi+6GL6rYQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-gnu": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.153.0.tgz", + "integrity": "sha512-oufEhZCX7saeo27Logra8cvBR1jQ/yw2zUI2hHmWw3qA/XYqEW31FOvLQBv7DWN32NjVdlcaP11WBBZTJo4k9g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-musl": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.153.0.tgz", + "integrity": "sha512-zXYzuAqng85h6+LI3iRm0zeMLX1/K2z41ZAMWxVRgt0D10VvZUaV5q+TEB46KDhoKBpdexoTLC6QoB/EiP2oqQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.153.0.tgz", + "integrity": "sha512-J7+NLwLEzdkKM9cKyPHH/AjXXstWOs98KYgLrPPnO4N9MO8EZRBfPNnBcS3S67uMOBkRpUmCl2FhRnWdQ5CbBA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.153.0.tgz", + "integrity": "sha512-sD0K11GQTLWylln54nPMMTtgmBbQrR5It/vMpZpy/fogiw8O2AvIE+9LuWUXddrWk9zvFIHGRcSgFn4IHOfoYg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-musl": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.153.0.tgz", + "integrity": "sha512-7L66uql2BhvSz51LwfExjOdn6et/ydTiQxl23VApdKzJhTc3TczA1Qt6VR/UcvlQrL2ZbytIJb447vh7p5wmUw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-s390x-gnu": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.153.0.tgz", + "integrity": "sha512-hXV3T7mNyrPR19rbPYkglkQJtoM/UNo/xwbZeeqdAxEg8PXgb4LXXuxPpGgI4bzRRCWwLUxdDSS58xIyC2u1TA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-gnu": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.153.0.tgz", + "integrity": "sha512-Da8Vqs1xdtoDYtEjmozD7wGgJeLBQkMbR0WUBECOSxodyX5DXy8ySlvJWbukyO2Q0+TM9EK+4LT6tbq0jCg4hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-musl": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.153.0.tgz", + "integrity": "sha512-zGAPxNTIIw6X5WyTRscIFE/oDjpdNtrrl32ing6NOXdGjzRucadnxBilexLdeYQ9YxixF93GujpXyQ0jw7ddSg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-openharmony-arm64": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.153.0.tgz", + "integrity": "sha512-GoHzpqP5XyPIp9DHokoEdCp+K4UPl/h1neFj1uXnUB3pJYyOQ7NOhFVOttNUgCGcj2eywAvu5cRivTOSq4S10g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-arm64-msvc": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.153.0.tgz", + "integrity": "sha512-xGcy12tO9k7zFbeyQzEuilfWfQpyRq23/fLPfN3wjSWuWv58k46fQT1hsO1KFNykZuVGW9xSJE/z592TmAoSDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-ia32-msvc": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.153.0.tgz", + "integrity": "sha512-o1ykUjWncvXX6N5a4KebgLjtD8zRui+f1llMhSGoSC9oP2KOqapRpdwmZ3NcNG0axFouwVKmhGBjd+IhA8BksA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-x64-msvc": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.153.0.tgz", + "integrity": "sha512-gxQz7G96Up136fd+2zWrP+o6FYGs9NO4Dyv55IoeJFJUDwLFHcLYY11hhA5wQKs0HoM57Q8esQ3nEVhnCdsjAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz", + "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^1.0.10" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@quansync/fs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@quansync/fs/-/fs-1.1.0.tgz", + "integrity": "sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/@quansync/fs/node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz", + "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz", + "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz", + "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz", + "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz", + "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz", + "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz", + "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz", + "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz", + "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz", + "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz", + "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz", + "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz", + "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz", + "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz", + "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.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" + }, + "node_modules/@shikijs/core": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.5.0.tgz", + "integrity": "sha512-6CIgwq+bt2JZ3OSjb/nBkkwEf/CfpHcbam+Mg5Pllco7HAGRQ2/EFhZrrJtbyNTgKwknfVS+llsXGygiDGwJWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.5.0", + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.5.0.tgz", + "integrity": "sha512-Ed6UdEj84LyYkwnxM5CvSMWAD+id9ExTYri0/+x41U9zrgNifPSS9lZm0JqBP0VfYI8yr8tY5nbNq9+p9R0kFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.5.0.tgz", + "integrity": "sha512-dKVmbJecVdB9recl8a/CU4jdpNXyjJQrIcesIkpS70s5HS02ohpnCSpMUbotPnEUzbAcLQ+S4hwZYbkBBiTcJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.5.0.tgz", + "integrity": "sha512-wwBTqwduXSz0X4v9Qa2EWeFaYJEw1clCncWjoRNQRbVDu3SPRTRWucnouYapKj3b8Q+sk/41Vbc4B5c8nuYzLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/magic-move": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/magic-move/-/magic-move-4.5.0.tgz", + "integrity": "sha512-5odJkSvOtEXb/BXg7YH0e5Cneao2iefa3Kdjg2zamAbuj63d5cdD4uqxqAKnu3it1dCEKadk8KDHGsNALjT6fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-match-patch-es": "^2.0.1", + "ohash": "^2.0.12" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "react": "^18.2.0 || ^19.0.0", + "shiki": "^4.0.0", + "solid-js": "^1.9.1", + "svelte": "^5.0.0-0", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "shiki": { + "optional": true + }, + "solid-js": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@shikijs/markdown-it": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/markdown-it/-/markdown-it-4.5.0.tgz", + "integrity": "sha512-LOrNiEv0FMymu4bjql+tOl/nT927Z5pXgDNfE1G4hI6PJhi41/s5zdUH6EhNlMPcEhUF41F6RxMUqnFyRg06IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "markdown-it": "^15.0.2", + "shiki": "4.5.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "markdown-it-async": "^2.2.0" + }, + "peerDependenciesMeta": { + "markdown-it-async": { + "optional": true + } + } + }, + "node_modules/@shikijs/markdown-it/node_modules/argparse": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-3.0.4.tgz", + "integrity": "sha512-fwWK3Zq22l4XJJjx5mcBHGikIk+hJqO+32dPm0Jur7+rtG5x1scOjSfdT9ZKeWHPpsya/l0HHK4tDImBkTrtvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "PSF-2.0" + }, + "node_modules/@shikijs/markdown-it/node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@shikijs/markdown-it/node_modules/linkify-it": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-6.1.0.tgz", + "integrity": "sha512-wJ/TwpSDTLepCrQoYWYIExIKg5Zchex2Nn5yk2mFnB+6PtdkHtyLx742md9csRjjOnGkKIS/RrbY7l8D6gT9Vw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^3.0.0" + } + }, + "node_modules/@shikijs/markdown-it/node_modules/markdown-it": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-15.0.3.tgz", + "integrity": "sha512-+Az7Ojz3iRGu8KYMvJ6xVhvrxP3eE+vEtJX75Ow88XBpcQNV/0lGX5ZynIIBftREo4DC2jsveAMDaeltmm8QvQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^3.0.3", + "entities": "^8.1.0", + "linkify-it": "^6.1.0", + "mdurl": "^2.1.0", + "punycode.js": "^2.3.1", + "uc.micro": "^3.0.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/@shikijs/markdown-it/node_modules/uc.micro": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-3.0.0.tgz", + "integrity": "sha512-U3PppEkleoTnIfi8BozMx3yju3qc/L6SwqWo2Sw+54PX+PX0q9I+r1Um5HCmqD7n9VDX5/v3vQH/AjA6deDdtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@shikijs/monaco": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/monaco/-/monaco-4.5.0.tgz", + "integrity": "sha512-IT9y3yYmFHCIAhjNZ5VZ4M8uuRkvjZVQs6YMNBbvBECC01bUkiVvrK3cmVo3WHEWQIlf/zuP7BW+CSKknAATMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.5.0", + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.5.0.tgz", + "integrity": "sha512-UM7ShlA8oz0OWxLLO8BmO9Ip6fVqdkt8yF0EH7nOCqHfrx1eEAzF0ZUVK7L+I8NEgeu1PEWSRlxaiPPeCb4iDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.5.0.tgz", + "integrity": "sha512-zGb87H3UBbpuI4yoh5UguB1XbU+2BDU3kflM7UfZ+Cy2hWo9lBtqjSVK20gmLY6ukYl040hAypGXMgud9vul8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/twoslash": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/twoslash/-/twoslash-4.5.0.tgz", + "integrity": "sha512-1AW9pT5tyRwjvKVkGtjq86d3wqpoggKNL0c9LyMDwuLN7PGw4q7m5s1UCskrnYmHmsEovBWroZ12B18r8dVgQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.5.0", + "@shikijs/types": "4.5.0", + "twoslash": "^0.3.9" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "typescript": ">=5.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.5.0.tgz", + "integrity": "sha512-drtPrtD8N0DENyD2cyt6Ajy+IWsiCLgMxODwLTpvH1UsN6a9jf90tNTPjqwHvA+0Lzu9e0txk8DjfqH1p9UJRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vitepress-twoslash": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/vitepress-twoslash/-/vitepress-twoslash-4.5.0.tgz", + "integrity": "sha512-WxvyDxrPMWmobib6bswVr9GiG4ZXTy699p/9eaw8kGIiZHYWWet+ZLJNSee8kz/FlMiZCHUNiv4pSJRYdixQPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/twoslash": "4.5.0", + "floating-vue": "^5.4.0", + "lz-string-es": "^1.0.0", + "magic-string": "^1.4.2", + "markdown-it": "^15.0.2", + "mdast-util-from-markdown": "^2.0.3", + "mdast-util-gfm": "^3.1.0", + "mdast-util-to-hast": "^13.2.1", + "ohash": "^2.0.12", + "shiki": "4.5.0", + "twoslash": "^0.3.9", + "twoslash-vue": "^0.3.9", + "vue": "^3.5.43" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vitepress-twoslash/node_modules/argparse": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-3.0.4.tgz", + "integrity": "sha512-fwWK3Zq22l4XJJjx5mcBHGikIk+hJqO+32dPm0Jur7+rtG5x1scOjSfdT9ZKeWHPpsya/l0HHK4tDImBkTrtvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "PSF-2.0" + }, + "node_modules/@shikijs/vitepress-twoslash/node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@shikijs/vitepress-twoslash/node_modules/linkify-it": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-6.1.0.tgz", + "integrity": "sha512-wJ/TwpSDTLepCrQoYWYIExIKg5Zchex2Nn5yk2mFnB+6PtdkHtyLx742md9csRjjOnGkKIS/RrbY7l8D6gT9Vw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^3.0.0" + } + }, + "node_modules/@shikijs/vitepress-twoslash/node_modules/markdown-it": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-15.0.3.tgz", + "integrity": "sha512-+Az7Ojz3iRGu8KYMvJ6xVhvrxP3eE+vEtJX75Ow88XBpcQNV/0lGX5ZynIIBftREo4DC2jsveAMDaeltmm8QvQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^3.0.3", + "entities": "^8.1.0", + "linkify-it": "^6.1.0", + "mdurl": "^2.1.0", + "punycode.js": "^2.3.1", + "uc.micro": "^3.0.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/@shikijs/vitepress-twoslash/node_modules/uc.micro": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-3.0.0.tgz", + "integrity": "sha512-U3PppEkleoTnIfi8BozMx3yju3qc/L6SwqWo2Sw+54PX+PX0q9I+r1Um5HCmqD7n9VDX5/v3vQH/AjA6deDdtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.1.0.tgz", + "integrity": "sha512-cVBuhDQjBZaNMTwBquYfPydrUP+E3hE6oYGasx8h6ZrODIM7/r+gxLDXdd66oszbrggZHEntdE6j+5gYIVyMWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@slidev/cli": { + "version": "53.0.0", + "resolved": "https://registry.npmjs.org/@slidev/cli/-/cli-53.0.0.tgz", + "integrity": "sha512-/Ex5GNsu0tuR9WKaikJiHsu3wlWwvNoTeliLVHdsuE5qkdD6Fphhi6/l9uNvWjSB3JxLDZUle8Cu9LazBiQ7tA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/ni": "^30.5.0", + "@antfu/utils": "^9.3.0", + "@comark/markdown-it": "^0.3.4", + "@iconify-json/carbon": "^1.2.27", + "@iconify-json/ph": "^1.2.2", + "@iconify-json/svg-spinners": "^1.2.4", + "@lillallol/outline-pdf": "^4.0.0", + "@modelcontextprotocol/node": "^2.0.0", + "@modelcontextprotocol/server": "^2.0.0", + "@shikijs/magic-move": "^4.4.3", + "@shikijs/markdown-it": "^4.4.3", + "@shikijs/twoslash": "^4.4.3", + "@shikijs/vitepress-twoslash": "^4.4.3", + "@slidev/client": "53.0.0", + "@slidev/parser": "53.0.0", + "@slidev/types": "53.0.0", + "@unocss/extractor-mdc": "^66.10.4", + "@unocss/reset": "^66.10.4", + "@vitejs/plugin-vue": "^6.0.9", + "@vitejs/plugin-vue-jsx": "^5.1.6", + "ansis": "^4.4.0", + "chokidar": "^5.0.0", + "cli-progress": "^3.12.0", + "connect": "^3.7.0", + "fast-deep-equal": "^3.1.3", + "fast-glob": "^3.3.3", + "get-port-please": "^3.2.0", + "global-directory": "^5.0.0", + "htmlparser2": "^12.0.0", + "is-installed-globally": "^1.0.0", + "jiti": "^2.7.0", + "katex": "^0.18.7", + "local-pkg": "^1.2.1", + "lz-string": "^1.5.0", + "magic-string": "^1.4.1", + "magic-string-stack": "^2.0.0", + "markdown-exit": "^1.3.0", + "markdown-it-footnote": "^4.0.0", + "markdown-it-github-alerts": "^1.0.1", + "mlly": "^1.8.2", + "monaco-editor": "^0.56.0", + "obug": "^3.0.0", + "open": "^11.0.4", + "pathe": "^2.0.3", + "pdf-lib": "^1.17.1", + "picomatch": "^4.0.7", + "plantuml-encoder": "^1.4.0", + "postcss-nested": "^8.0.1", + "pptxgenjs": "^4.0.1", + "prompts": "^2.4.2", + "public-ip": "^8.0.0", + "resolve-from": "^5.0.0", + "resolve-global": "^2.0.0", + "shiki": "^4.4.3", + "sirv": "^3.0.2", + "source-map-js": "^1.2.1", + "typescript": "^6.0.3", + "unhead": "^3.4.1", + "unocss": "^66.10.4", + "unplugin-icons": "^24.0.0", + "unplugin-vue-components": "^32.1.0", + "unplugin-vue-markdown": "^32.1.1", + "untun": "^0.2.2", + "uqr": "^0.1.3", + "verkit": "^0.4.0", + "vite": "^8.3.0", + "vite-plugin-inspect": "^12.0.2", + "vite-plugin-remote-assets": "^2.1.0", + "vite-plugin-static-copy": "^4.1.1", + "vite-plugin-vue-server-ref": "^1.0.0", + "vitefu": "^1.1.3", + "vue": "^3.5.42", + "yaml": "^2.9.1", + "yargs": "^18.1.0", + "zod": "^4.6.5" + }, + "bin": { + "slidev": "bin/slidev.mjs" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "playwright-chromium": "^1.10.0", + "vite-plugin-pwa": "^1.0.0" + }, + "peerDependenciesMeta": { + "playwright-chromium": { + "optional": true + }, + "vite-plugin-pwa": { + "optional": true + } + } + }, + "node_modules/@slidev/client": { + "version": "53.0.0", + "resolved": "https://registry.npmjs.org/@slidev/client/-/client-53.0.0.tgz", + "integrity": "sha512-y95YzIeqEhXGAKQ0BabG7QLBYwDaqwjtAAttNmJL9E2qmgSoEuIg2KVMIyz15bdchS6XyouqlDiNdzgDag47Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/utils": "^9.3.0", + "@fix-webm-duration/fix": "^1.0.1", + "@iconify-json/carbon": "^1.2.27", + "@iconify-json/ph": "^1.2.2", + "@iconify-json/svg-spinners": "^1.2.4", + "@shikijs/engine-javascript": "^4.4.3", + "@shikijs/magic-move": "^4.4.3", + "@shikijs/monaco": "^4.4.3", + "@shikijs/vitepress-twoslash": "^4.4.3", + "@slidev/parser": "53.0.0", + "@slidev/rough-notation": "^0.1.0", + "@slidev/types": "53.0.0", + "@typescript/ata": "^0.9.8", + "@unhead/vue": "^3.4.1", + "@unocss/extractor-mdc": "^66.10.4", + "@unocss/preset-mini": "^66.10.4", + "@unocss/reset": "^66.10.4", + "@vueuse/core": "^14.4.0", + "@vueuse/math": "^14.4.0", + "@vueuse/motion": "^3.0.3", + "ansis": "^4.4.0", + "drauu": "^1.0.0", + "file-saver": "^2.0.5", + "fitty": "^2.4.2", + "floating-vue": "^5.4.0", + "fuse.js": "^7.5.0", + "katex": "^0.18.7", + "lz-string": "^1.5.0", + "markdown-it-github-alerts": "^1.0.1", + "mermaid": "^12.0.0", + "monaco-editor": "^0.56.0", + "nanotar": "^0.3.0", + "pptxgenjs": "^4.0.1", + "recordrtc": "^5.6.2", + "shiki": "^4.4.3", + "typescript": "^6.0.3", + "unocss": "^66.10.4", + "vue": "^3.5.42", + "vue-router": "^5.3.1", + "yaml": "^2.9.1" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@slidev/parser": { + "version": "53.0.0", + "resolved": "https://registry.npmjs.org/@slidev/parser/-/parser-53.0.0.tgz", + "integrity": "sha512-0wwoyPimeLUV0+BUCxJXq4GqBkufeSioPNTN6JGIByAEtm0efNNpSwQ6Pdy8UroCINuALDt+sttBFqo+MtDaFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/utils": "^9.3.0", + "@slidev/types": "53.0.0", + "pathe": "^2.0.3", + "yaml": "^2.9.1" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@slidev/rough-notation": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@slidev/rough-notation/-/rough-notation-0.1.0.tgz", + "integrity": "sha512-a/CbVmjuoO3E4JbUr2HOTsXndbcrdLWOM+ajbSQIY3gmLFzhjeXHGksGcp1NZ08pJjLZyTCxfz1C7v/ltJqycA==", + "dev": true, + "license": "MIT", + "dependencies": { + "roughjs": "^4.6.6" + } + }, + "node_modules/@slidev/types": { + "version": "53.0.0", + "resolved": "https://registry.npmjs.org/@slidev/types/-/types-53.0.0.tgz", + "integrity": "sha512-Iyr37tIJAC65lTLgFim+5c5iqNAaj7NfkcFUzu8yqxY9SAeuFyk7rB8MBc/VC4JVpPVaahVO+35EX+wPY6BAeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/utils": "^9.3.0", + "@shikijs/markdown-it": "^4.4.3", + "@vitejs/plugin-vue": "^6.0.9", + "@vitejs/plugin-vue-jsx": "^5.1.6", + "katex": "^0.18.7", + "mermaid": "^12.0.0", + "monaco-editor": "^0.56.0", + "shiki": "^4.4.3", + "unocss": "^66.10.4", + "unplugin-icons": "^24.0.0", + "unplugin-vue-markdown": "^32.1.1", + "vite-plugin-inspect": "^12.0.2", + "vite-plugin-remote-assets": "^2.1.0", + "vite-plugin-static-copy": "^4.1.1", + "vite-plugin-vue-server-ref": "^1.0.0", + "vue": "^3.5.42", + "vue-router": "^5.3.1" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.4.tgz", + "integrity": "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.12.tgz", + "integrity": "sha512-Qe/KWYhEiIIxGs7HrAAjMfShxKldx19SJtr5zu53f3afPsdZNz7HHtdTLXo/kqeiWNXVycI24kSnfzBYkTzpgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-kVd74ta9eof3eJOvbNd1vGKS/XERRyQbT26Og63hIsvDO84cjD5gEOhsXf26w3FSoNlPVz84DOFcKv/oou+fMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.9.tgz", + "integrity": "sha512-0sE1406XBYJGiqD3AusTl9ZqC//2mIXix51tbom25gDCA8ri4xnSZg28CaSE8Srl6FClqABUKfsn0qghgdepMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.5.tgz", + "integrity": "sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript/ata": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@typescript/ata/-/ata-0.9.8.tgz", + "integrity": "sha512-+M815CeDRJS5H5ciWfhFCKp25nNfF+LFWawWAaBhNlquFb2wS5IIMDI+2bKWN3GuU6mpj+FzySsOD29M4nG8Xg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=4.4.4" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.5.tgz", + "integrity": "sha512-gobLHQLmvQGJInFk31NvgMd1uYzRXsd8qgnOssyLD055R2QPnw1ubRbBQ4+T+5qC7IQVuYXdvNDE2G7tMDVHgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unhead/bundler": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@unhead/bundler/-/bundler-3.4.3.tgz", + "integrity": "sha512-noUTzazRB4fMgO/nluoirrZJXngNbNb3sQzIslA805tKkwakRv2fib4Xe4EdiTuNB7V5XmpUFvfQkKRdBQ9qAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^1.3.1", + "oxc-walker": "^1.1.1", + "unplugin": "^3.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + }, + "peerDependencies": { + "@unhead/cli": "^3.4.3", + "@vitejs/devtools-kit": "^0.7.6", + "esbuild": ">=0.17.0", + "lightningcss": ">=1.20.0", + "oxc-parser": ">=0.98.0", + "rolldown": ">=1.0.0", + "unhead": "^3.4.3", + "vite": ">=6.4.2", + "webpack": ">=5.0.0" + }, + "peerDependenciesMeta": { + "@unhead/cli": { + "optional": true + }, + "@vitejs/devtools-kit": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "oxc-parser": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/@unhead/vue": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-3.4.3.tgz", + "integrity": "sha512-Wrmk62oC/V1EcBSKRDuy77kVaBtBXXsj6b+vEEWvmfo9AQfpLlc2b6vvvSj9WnXr8VY1ksKHj49Ejo8J/wLSOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unhead/bundler": "3.4.3", + "hookable": "^6.1.1", + "unhead": "3.4.3", + "unplugin": "^3.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + }, + "peerDependencies": { + "vite": ">=6.4.2", + "vue": ">=3.5.18", + "webpack": ">=5.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/@unocss/autocomplete": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/autocomplete/-/autocomplete-66.12.0.tgz", + "integrity": "sha512-klClL63gwOr3L6FAnOiicbzwWypK/qYkn0m1UyrRqMCzy673Xl7mRt3Y1/mijP1xWd8WwYsW1KZEak+qtk5vDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fzf": "^0.5.2", + "lru-cache": "^11.5.3" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/autocomplete/node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@unocss/cli": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/cli/-/cli-66.12.0.tgz", + "integrity": "sha512-0Xb11EprIIFykseZ1NU60AS8sh8Yr7n4F/lxvXPeHF5j9q5P2aMI2WfjoeOHRwJS9+PbGi45tzEaXZBd2HyisQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "@unocss/config": "66.12.0", + "@unocss/core": "66.12.0", + "@unocss/preset-wind3": "66.12.0", + "@unocss/preset-wind4": "66.12.0", + "@unocss/transformer-directives": "66.12.0", + "cac": "^7.1.0", + "chokidar": "^5.0.0", + "colorette": "^2.0.20", + "consola": "^3.4.2", + "magic-string": "^1.4.4", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "tinyglobby": "^0.2.17", + "unplugin-utils": "^0.3.2" + }, + "bin": { + "unocss": "bin/unocss.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/config": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/config/-/config-66.12.0.tgz", + "integrity": "sha512-TP4/RDxminVfM19SMX053bYgmyZwB4TEDSpAeUchv3CLvvIsMUrxQ9LUQ2QMe+W2FU+u5U6GArKSQZpbgCSGYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "colorette": "^2.0.20", + "consola": "^3.4.2", + "unconfig": "^7.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/core": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/core/-/core-66.12.0.tgz", + "integrity": "sha512-kWPbF6ilPyrKm5+XNKsHtCZYY5BSHGeCPKb19bEQnvFknPyi57xfBpI/3GSj5zs8i0EMbQkJbT22BcgeJ4DAZw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/extractor-arbitrary-variants": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/extractor-arbitrary-variants/-/extractor-arbitrary-variants-66.12.0.tgz", + "integrity": "sha512-2T1lMbsH1MEKXOhINez98OOdT0ndHvooPnNCgAP1yiIzGhO13L0Pls+Yok0hGwg4EEoiByrxoYCPudzxSRMIDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/extractor-mdc": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/extractor-mdc/-/extractor-mdc-66.12.0.tgz", + "integrity": "sha512-wZ5utRs3N847EsiehWN2MO+EDiHFbr9Vl5N3QJMQWFO7QQQ7L6n2MmxYDLpwkucUwbHfZ2e/zPvVxWEaWQ+6bw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/inspector": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/inspector/-/inspector-66.12.0.tgz", + "integrity": "sha512-5uvjFfVBkIeHqLNK83rN/EqB3cVYDKlQGtRJdXPoTlZLrXuLnzm9XFu9AQTu8yWaIQ+fUrcoLuXMuxRNDROzPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/autocomplete": "66.12.0", + "@unocss/config": "66.12.0", + "@unocss/core": "66.12.0", + "@unocss/rule-utils": "66.12.0", + "devframe": "^1.2.6", + "tinyglobby": "^0.2.17", + "unplugin-utils": "^0.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-attributify": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-attributify/-/preset-attributify-66.12.0.tgz", + "integrity": "sha512-56dZC18pNBDObKFCQ/CcuLptBdvIQfTbo4ZPt2AjHoxgH032zmbENEiFCpKVmsRSI7MbrY3IblhlAd9qMw8ibw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-icons": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-icons/-/preset-icons-66.12.0.tgz", + "integrity": "sha512-4QHRRGAZRgh3TPWOX1uN6/ZfQnfRDyhI+MJZl+5HxW12kOUKT6Hin3v/sOA7Dyfy2pN0CKRE2EbXutb/PhJDUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@iconify/utils": "^3.1.7", + "@unocss/core": "66.12.0", + "ofetch": "^1.5.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-mini": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-mini/-/preset-mini-66.12.0.tgz", + "integrity": "sha512-aGXu7E1DQW/W1IVUBFilC+Z0h6LbGTaM2j6VpBnpAjzpnd5LGGqIk5BG2kVRVaem/9MfmuYUdkyCJn1Y1dP5XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "@unocss/extractor-arbitrary-variants": "66.12.0", + "@unocss/rule-utils": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-tagify": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-tagify/-/preset-tagify-66.12.0.tgz", + "integrity": "sha512-Ew8L83ykrYNNe65ihnk57D/XJa+i4m2yoiaNOsIkQfzNdAQGsNIsyaVibHkdoyO9yN3zUo8BkPV+kOJWPKNBaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-typography": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-typography/-/preset-typography-66.12.0.tgz", + "integrity": "sha512-9tgCevnpxq7NxCNFCPufKIex0bVJLOgtsuKREP6sMJUPYxYjXJl4loFlJzQ9POFFdKFh+7015spZwJlShzI2NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "@unocss/rule-utils": "66.12.0" + } + }, + "node_modules/@unocss/preset-uno": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-uno/-/preset-uno-66.12.0.tgz", + "integrity": "sha512-N5EIkKPuewZsVrx+qKbNo+vsx18UcKMCZbXpFqu2gMojcM3ju83IwHGsG3h+vrDlICepTBchIkzFQSpGz66RwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "@unocss/preset-wind3": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-web-fonts": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-web-fonts/-/preset-web-fonts-66.12.0.tgz", + "integrity": "sha512-sOXKZw6R1ywRn1Mta6JJ1gMBn4xRcJZixziB9pqNQfSFPBZNuVecqqI20efOqF1666CklBMwU80i0WLFyc0WsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "ofetch": "^1.5.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-wind": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-wind/-/preset-wind-66.12.0.tgz", + "integrity": "sha512-fIO8q+tGNw0bk3b9/iWPi0VF+stFWy5aIrsXWS46KuTrgs+LOs/PQsJz8CkvKnO6hmsSyKCMM56KtLVAnbtahA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "@unocss/preset-wind3": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-wind3": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-wind3/-/preset-wind3-66.12.0.tgz", + "integrity": "sha512-gp5YFh0o9TtyZ+MtjKalB6Fig1bZ1V01GBbYSBAkW+gKj2APUvNUkDI9zw0cjarrHs4ekpeb/jfOU/ghijVF0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "@unocss/preset-mini": "66.12.0", + "@unocss/rule-utils": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-wind4": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/preset-wind4/-/preset-wind4-66.12.0.tgz", + "integrity": "sha512-7P52d9ZykqBpAPfVMVVcZTnPhtWzJxQ4UAJ5mqlA49so89IQ0hyG+Dj7mijqN81GWtpjBwITK4wBxRis9Zdm8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "@unocss/extractor-arbitrary-variants": "66.12.0", + "@unocss/rule-utils": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/reset": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/reset/-/reset-66.12.0.tgz", + "integrity": "sha512-xAqBtJDIF7Al2Dh1GcnaK2TWJo9jgsrrXgP3jAIpdShXo/uag5fgsIR5Yk16rHb8cOJ4rAKMP3trn1Z5o4+x5A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/rollup": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/rollup/-/rollup-66.12.0.tgz", + "integrity": "sha512-ZKD4vYl/I/Adf0tytQzu9Poxp9bx2No1vrbFT6q6dszvjW9+wMvXM1YZ9TvBDSYsZ7P/RgA2Y518nCECvHlLuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "@unocss/config": "66.12.0", + "@unocss/core": "66.12.0", + "chokidar": "^5.0.0", + "magic-string": "^1.4.4", + "pathe": "^2.0.3", + "tinyglobby": "^0.2.17", + "unplugin-utils": "^0.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "rolldown": "^1.0.0", + "rollup": "^4.0.0" + }, + "peerDependenciesMeta": { + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/@unocss/rule-utils": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/rule-utils/-/rule-utils-66.12.0.tgz", + "integrity": "sha512-pZq/FV/TPbM6I2GKttgdR37GtVCLdAceUBz+F1EgWfLu5MvvFiC6fU+BGypgAIRhJdF3bJINwc8t621vEcph0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "magic-string": "^1.4.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/transformer-attributify-jsx": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/transformer-attributify-jsx/-/transformer-attributify-jsx-66.12.0.tgz", + "integrity": "sha512-bJ+9jhQVCaegVB+rUKf112TMDm41dNK/bt5XUT5Seeg1Wfo0jReX8VYSRsDj4rLKf9zowJwojjz8MhXO+hDEBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "oxc-parser": "^0.153.0", + "oxc-walker": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/transformer-compile-class": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/transformer-compile-class/-/transformer-compile-class-66.12.0.tgz", + "integrity": "sha512-B1mAxZP7NV3yRMVjTzMZB+/+070ZrNzmaTkfEYNOT6FnKLpTg8tb5YBnBl8pMMKg1L2J/7EqiSVccGPpeYPSkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/transformer-directives": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/transformer-directives/-/transformer-directives-66.12.0.tgz", + "integrity": "sha512-nWDgI2C1QsGjSE3nJPKhdg1KDDd2aznaSkdF3rjYvsHWnuQJBm4Ckn+cprlo0R8vVAR6TKq2bJayKaah2Pwe5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0", + "@unocss/rule-utils": "66.12.0", + "css-tree": "^3.2.1" + } + }, + "node_modules/@unocss/transformer-variant-group": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/transformer-variant-group/-/transformer-variant-group-66.12.0.tgz", + "integrity": "sha512-BsjdQLeGkKOb7GTMAlQn5t78nYZ8EKOy+wNODHrLschZ5p9Z9oJdoYkvJzd/Dg6YijHAhz/YaN1oRb72ERFtag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/core": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/vite": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/@unocss/vite/-/vite-66.12.0.tgz", + "integrity": "sha512-Wd3HYIxaGrXo9sm2dCLxvjSJlttQAVhsGdxNFeALQlu4QfVXid7m8hfW+X7ma+W3Vla+52RnddDhZkoB2jLvVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "@unocss/config": "66.12.0", + "@unocss/core": "66.12.0", + "@unocss/inspector": "66.12.0", + "chokidar": "^5.0.0", + "magic-string": "^1.4.4", + "pathe": "^2.0.3", + "tinyglobby": "^0.2.17", + "unplugin-utils": "^0.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" + } + }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, + "node_modules/@vitejs/devtools-kit": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@vitejs/devtools-kit/-/devtools-kit-0.7.6.tgz", + "integrity": "sha512-VtnTo296ypQyksFcPgrUyDDxh/SuAa/18uWEitbODcJIu5eilMFQyaSzsLKgp0HQPsxo/Kfzbfl5gowNGfjwqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@devframes/hub": "^1.0.0", + "@devframes/json-render": "^1.0.0", + "devframe": "^1.0.0", + "local-pkg": "^1.2.1", + "nostics": "^1.3.0", + "package-manager-detector": "^1.8.0" + }, + "peerDependencies": { + "vite": "*" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.10.tgz", + "integrity": "sha512-b80aUQh3n+qEzzZm5oQp3rT2QImeCx58K6eFl/RkJ3d65KW0kTuv5lNsLRVAjbW9YRUpo/wzmvzMgV34bTAItA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25 || ^3.6.0-rc.10" + } + }, + "node_modules/@vitejs/plugin-vue-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.1.6.tgz", + "integrity": "sha512-YXvi4as2clxt6DFw5+a0tTA97ntiQXm/raR8ofNj3aNwwdlVGTiG2gp7EvfZW17P50acL/9bP0ccF4XnqNmlgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-syntax-typescript": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7", + "@rolldown/pluginutils": "^1.0.1", + "@vue/babel-plugin-jsx": "^2.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.0.0" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue-macros/common": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.4.tgz", + "integrity": "sha512-/5Fv+6DgIcM9ajY05ZmKBv+LMX1M9A0X+IUwDRVdt67ciw8OV9bvG2r34p3RiEadlsQybjhKPRKNXDC8Bp23cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-sfc": "^3.5.22", + "ast-kit": "^2.1.2", + "local-pkg": "^1.1.2", + "magic-string-ast": "^1.0.2", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/vue-macros" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-2.0.1.tgz", + "integrity": "sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-2.0.1.tgz", + "integrity": "sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@vue/babel-helper-vue-transform-on": "2.0.1", + "@vue/babel-plugin-resolve-type": "2.0.1", + "@vue/shared": "^3.5.22" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-2.0.1.tgz", + "integrity": "sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.4", + "@vue/compiler-sfc": "^3.5.22" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.44.tgz", + "integrity": "sha512-k34nCytbK8PQ1gR2mGROYOw6Pnv/mumqIdrZ3x2tbyXZDWm7ViKGLTfdB+DRrWqEQsLgGJvn0WN9jBif5zCTLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.9", + "@vue/shared": "3.5.44", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.2" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.44.tgz", + "integrity": "sha512-W5AoBFWdw1lQAKiHowkfgFKFab82oSRT6sGsPfw/q1GRjsy/ZJFLHGhJyBRIFN9APLTUduDH9U2JAiPOIyKuYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.44", + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.44.tgz", + "integrity": "sha512-dzHmQ4jXTpdr5FjIQUeykVakN/uzqFbUo9/C5ikBFg3a3PeM6uRxeczx1ZbXBML0/zJstEh3ygYipzR5Z7b2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.9", + "@vue/compiler-core": "3.5.44", + "@vue/compiler-dom": "3.5.44", + "@vue/compiler-ssr": "3.5.44", + "@vue/shared": "3.5.44", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.29", + "source-map-js": "^1.2.2" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.44.tgz", + "integrity": "sha512-xr5gb1JVHlfXJwSlRz1FeP6uxoazdzHuMTVLE7up+4Vrqxk219CvxLPxD9NXBt47yvZW7+p4DXfyp4O1fkByIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.44", + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/devtools-api": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.2.1.tgz", + "integrity": "sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.2.1" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.2.1.tgz", + "integrity": "sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.2.1", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/@vue/devtools-kit/node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/devtools-shared": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz", + "integrity": "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "3.3.13", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.13.tgz", + "integrity": "sha512-axA0Xemn5LuUKuKWob0/kVq7xQ2mJJM6VqA9Exvt35nM9yRC31goWVve/G/Zb2ZCLsXC8FHYjjfXlnshknSCew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.44.tgz", + "integrity": "sha512-u+QJdKeBBEADkPdevsL+6E12QlYVfBD8Zcw5qjDQKtamI6l14KyqazJMobhCKelhWN5zpyKUiEDoTyHE93cqWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.44.tgz", + "integrity": "sha512-uEa/ndwjhrrhnOfb2p+sZy6zeoamJOISqaLSJGk/8R6nABT1ZldpzcJoPzKj+zHVomVBPcmQYyC2r4LtvDV6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.44", + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.44.tgz", + "integrity": "sha512-3fc4ARcXVhVBaJ3ujWiNZJARD6pkyRcwG+ZYhMzjKtKUp7MAY0HLwr7r3Smep3Li6ZaWT6ewdmx8XqdaD5oNZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.44", + "@vue/runtime-core": "3.5.44", + "@vue/shared": "3.5.44", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.44.tgz", + "integrity": "sha512-R4S1UoH4h5Frrauv8Z5XE5TxV9oBiUSgWIC4TyVK1FK+RocRnOLwFA5Xcmo2011RlgJp+AbZCnw6afrhlgRuUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.44", + "@vue/runtime-dom": "3.5.44", + "@vue/shared": "3.5.44" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.44.tgz", + "integrity": "sha512-H82XLH0DjmSOdbEwO15x3QTo1f5PWEtv237eLpYKc0mqLu+B8hnq16lFWmuBmNurkeiG3wr+oM1FhOll/YVFRg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.4.0.tgz", + "integrity": "sha512-X4WHz1HlCzCBoYXesUkifzzWBAcZgXG8Fi5iNPQg/epdzOB3gu8Fawj3hvuwYR1nGcXGnvxwYYcUC/71++svtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "14.4.0", + "@vueuse/shared": "14.4.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/math": { + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/@vueuse/math/-/math-14.4.0.tgz", + "integrity": "sha512-MYB3WYHFs5dQNFdCgw7h9SZsCpcOL0Hd8iyd9Ri3bLwcEfVG2smazmm3g72zRwgrKoWdOlkQFTXwxYIE4sxa/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/shared": "14.4.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.4.0.tgz", + "integrity": "sha512-swx/255R6JyHZFJhx845iz5CRWDZdCfvkZOpACWc5+c5WHcG24mv8gUT1WIdFQaHt6dq79rvILd9QnCWiyVm9g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/motion": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@vueuse/motion/-/motion-3.0.3.tgz", + "integrity": "sha512-4B+ITsxCI9cojikvrpaJcLXyq0spj3sdlzXjzesWdMRd99hhtFI6OJ/1JsqwtF73YooLe0hUn/xDR6qCtmn5GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "^13.0.0", + "@vueuse/shared": "^13.0.0", + "defu": "^6.1.4", + "framesync": "^6.1.2", + "popmotion": "^11.0.5", + "style-value-types": "^5.1.2" + }, + "optionalDependencies": { + "@nuxt/kit": "^3.13.0" + }, + "peerDependencies": { + "vue": ">=3.0.0" + } + }, + "node_modules/@vueuse/motion/node_modules/@vueuse/core": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.9.0.tgz", + "integrity": "sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "13.9.0", + "@vueuse/shared": "13.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/motion/node_modules/@vueuse/metadata": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.9.0.tgz", + "integrity": "sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/motion/node_modules/@vueuse/shared": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.9.0.tgz", + "integrity": "sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/shared": { + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.4.0.tgz", + "integrity": "sha512-JRgY90Sz8DDtPMsaDflvPMp9xYk69JZAmbuDvAquUVXKr2gEjqtzGNTTthLfckH0BzBqvnu31gb4a8TGLRe79g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.4.0.tgz", + "integrity": "sha512-9k3v7xcHwgdO/DruxGIg4HtjvlAZlcnsX/mzqUb1t3NkYnl9kK2UJ+Gq0io+vQf7iT//BD/HB/NBkUR1LWxoeA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/ast-kit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", + "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.10.0.tgz", + "integrity": "sha512-KzvoxDfXNz2Gm8KYLh47K3qcY3Zza3WKOuh2xoqa7LBu6MDKWAZLovJtU9DuAd/Er+rhQHCRn619lHS5U+qmhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^8.0.0", + "@babel/types": "^8.0.0", + "ast-kit": "^3.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope/node_modules/@babel/helper-string-parser": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.8.tgz", + "integrity": "sha512-fVM+gSuKbdv9cpmyRNh5EvsDHUZCdxQH3FdnFKSkNYHpbsn95bbrxm+ES3MVeQjcqwi2O5ur/DjLfmTQaO60ng==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/ast-walker-scope/node_modules/@babel/helper-validator-identifier": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.6.tgz", + "integrity": "sha512-HYx0YmuchRxRSpnjvcNffnPS8DCauKNmnumIuHG3jnQAVewRA7kfJGwF13ayEpSZs7s4R/oBH4eppuXcAQd5JQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/ast-walker-scope/node_modules/@babel/parser": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.8.tgz", + "integrity": "sha512-Gq65JogAr0S2p0BzvfSJboyuiRnxDQApyOqHkKmkrvm+VlLK4gC3pKsiOKEdnhFBemrFbSnOu90mZZWDpiJnmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/ast-walker-scope/node_modules/@babel/types": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.8.tgz", + "integrity": "sha512-iXSSZAKQWmaeAwGtFw10v9fg5hUiUs0L+w+Z2K11kym3pDNK+SF0VcAQPsEFvWcTSZ7LMgClMXDBvTbH0IlICg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^8.0.8", + "@babel/helper-validator-identifier": "^8.0.6" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/ast-walker-scope/node_modules/ast-kit": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-3.0.0.tgz", + "integrity": "sha512-8OG92q3R35qjC/4i6BLBMg8IB+fClWu/1PEwg2Z9Rn+BuNaiEgJzpzn+pxWOdHJWDCAwu2JP0wCDTozAM4QirQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^8.0.0", + "estree-walker": "^3.0.3", + "pathe": "^2.0.3" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.33.tgz", + "integrity": "sha512-yiVKDX+qCMRKBE084lXvq9EcwFUhKZ2nU8TK1EQQuOgPNR/lmbGxXOeDdlGUqC2Zz11Cs9rk1d/8NfgBmlkt0w==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.29.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.4.tgz", + "integrity": "sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==", + "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": { + "baseline-browser-mapping": "^2.11.29", + "caniuse-lite": "^1.0.30001816", + "electron-to-chromium": "^1.5.459", + "node-releases": "^2.0.59", + "update-browserslist-db": "^1.3.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bundle-name": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", + "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c12": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz", + "integrity": "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chokidar": "^5.0.0", + "confbox": "^0.2.4", + "defu": "^6.1.6", + "dotenv": "^17.3.1", + "exsolve": "^1.0.8", + "giget": "^3.2.0", + "jiti": "^2.6.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.0", + "rc9": "^3.0.1" + }, + "peerDependencies": { + "magicast": "*" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/cac": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cac/-/cac-7.1.0.tgz", + "integrity": "sha512-LMDU/U2yA4IPIY8KT0NsNPWEYZvAMkbgyspxJ6btJaYV+WMDOHAZCmB0lsTObiHaRS1V/ztKAl7+TSkTqXyeoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001819", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001819.tgz", + "integrity": "sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chevrotain": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-13.2.0.tgz", + "integrity": "sha512-GTouk5YzgBxlTPCaDn5q0q2EJKkrYmnPF+HmF8v0Qo4r3iyPG7HJivkN1wthCXZcm3sIcZFpGTRovLPNdJNRCA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "13.2.0", + "@chevrotain/gast": "13.2.0", + "@chevrotain/regexp-to-ast": "13.2.0", + "@chevrotain/types": "13.2.0", + "@chevrotain/utils": "13.2.0" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/clone-regexp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-3.0.0.tgz", + "integrity": "sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-regexp": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dev": true, + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/crossws": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.4.16.tgz", + "integrity": "sha512-gKc2u346c6tZazImz8/4KkRRKW6cEHot3Z3+gzKNSkfHTWkYrXeiidMKYN0JnHqeszIs5++iDaVzP/pWl/efpw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "srvx": ">=0.11.5" + }, + "peerDependenciesMeta": { + "srvx": { + "optional": true + } + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.34.4", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.4.tgz", + "integrity": "sha512-msY99tMY8iWTg+sIPGfSqDcPNOt4xcj+v/K/7qUFaCUxxlqnMkUYLBjRUzn9W5CU/R2hrmtJa5+UtovrEM2ctg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "dev": true, + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "dev": true, + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devframe": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/devframe/-/devframe-1.2.6.tgz", + "integrity": "sha512-mHzcm6ADzXgRMWdoSX+5PjwhmwAuC3SpDPi3+URznlpzxJF6tXwy5h1xr82zPKju38EVtdnYRnnP4hZwLK01vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "crossws": "^0.4.12", + "h3": "^2.0.1", + "nostics": "^1.3.0", + "pathe": "^2.0.3" + }, + "bin": { + "devframe": "bin/devframe.mjs" + }, + "peerDependencies": { + "@devframes/agentic": "1.2.6", + "cac": "^7.0.0" + }, + "peerDependenciesMeta": { + "@devframes/agentic": { + "optional": true + }, + "cac": { + "optional": true + } + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff-match-patch-es": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/diff-match-patch-es/-/diff-match-patch-es-2.0.1.tgz", + "integrity": "sha512-+Sl+/H47ArQiKPp8fA307M1RwyCwFqNhggw0mAEFherLNL/J2ZUFZsnELj65Cir91VfVPx3jlNzeKBdr+QAFTw==", + "dev": true, + "license": "Apache-2.0", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dns-socket": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/dns-socket/-/dns-socket-4.2.2.tgz", + "integrity": "sha512-BDeBd8najI4/lS00HSKpdFia+OvUMytaVjfzR9n5Lq8MlZRSvtbI+uLtx1+XmQFls5wFU9dssccTmQQ6nfpjdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.4" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-serializer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz", + "integrity": "sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0", + "entities": "^8.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz", + "integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/domhandler": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz", + "integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^3.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz", + "integrity": "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==", + "dev": true, + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/domutils": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz", + "integrity": "sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^3.0.0", + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/drauu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/drauu/-/drauu-1.0.0.tgz", + "integrity": "sha512-K3a1cbP2l4i0H/bmNM4nyGsY5/hiH5a10sEHlksqKue0+TPQCHrV9DwPad+St06CJwpkdzVJ/FyOYTIAm82rgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@drauu/core": "1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.464", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.464.tgz", + "integrity": "sha512-nRa65aFge40viPAKxMKULIY8tcXKwKboJWNn4RoKwyWXJ6SP7BnrDGKUTzc64818jIciH0PK7LSF8QzZes19GA==", + "dev": true, + "license": "ISC" + }, + "node_modules/elkjs": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", + "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==", + "dev": true, + "license": "EPL-2.0" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-2.0.1.tgz", + "integrity": "sha512-J36ntO+rMQVRuR/umlmxmfLi4TpWwtmTnHoJoXTiC2xDNazs0VDPKcX6pbhZMDd2HFtH9isMBJXMdbki+A++Pg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/errx": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/errx/-/errx-0.1.2.tgz", + "integrity": "sha512-chfpPHmCerdo/rXr/nNvPZRkV4WwDRwzwnsJ0Uzz3tVi8Z41tDctRjduYy1138ii77AFlts1qvWtX3g/Acg91Q==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/es-toolkit": { + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.53.0.tgz", + "integrity": "sha512-3+AYXp9fmJ/UhHBMYj2/U+RaUouMD90SzoItyDOB2Za7W6dB7fQApFGnZPl2MF6K8/Tv1koh1JyirSx/kju6Pw==", + "dev": true, + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types", + "tests/browser-compat" + ] + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.3.tgz", + "integrity": "sha512-kuzL9zherLQoqDeQaS55AiiIg1kb4KYXaSusTtgZTPlvKP2gGCI82LNmBNh5RjDVUXIcmgx+/Mx7vPwRzBbhJA==", + "dev": true, + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "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": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/fitty": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/fitty/-/fitty-2.4.2.tgz", + "integrity": "sha512-GNhWgImK4+wEkgEZjBkQMyu5NLSmmryg/CaRP7zYby+TWzCrUou6BHL+iqbjKzJRXMyzuJkH+LBB1+lh4oO77g==", + "dev": true, + "license": "MIT" + }, + "node_modules/floating-vue": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/floating-vue/-/floating-vue-5.4.0.tgz", + "integrity": "sha512-hb5y5N8sXs9Dy4iZfWBKLzf3FkUkqhbkQREhRulAEvIihMlSGxc/7pXGrIDetoqXm/RRHNVjvN6lql9IhMM+tQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "@nuxt/kit": "^3.2.0 || ^4.0.0 || ^5.0.0-0", + "vue": "^3.2.0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "2.4.0" + } + }, + "node_modules/framesync/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true, + "license": "0BSD" + }, + "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", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-0.1.1.tgz", + "integrity": "sha512-0NVVC0TaP7dSTvn1yMiy6d6Q8gifzbvQafO46RtLG/kHJUBNd+pVRGOBoK44wNBvtSPUJRfdVvkFdD3p0xvyZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fuse.js": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.5.0.tgz", + "integrity": "sha512-sQtrEfA+ez/3G0cCZecF70oqpCRttCexYUG4mUrtWL49ULUzUyxokt5kyqwtKzj1270RaKih+hcP3qLcumccow==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/krisk" + } + }, + "node_modules/fzf": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz", + "integrity": "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-port-please": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", + "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/giget": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/giget/-/giget-3.3.1.tgz", + "integrity": "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-directory": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", + "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "6.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/h3": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/h3/-/h3-2.0.8.tgz", + "integrity": "sha512-ENuKlF9kC6E6PrvY1PUyE0qkrnnPTIt2/v48uyEEfTVfgoiqBCE0uwwf++2XrWwT1gxjQBDLQ8PAsmZVniKvTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "rou3": "^1.0.0", + "srvx": "^1.0.6" + }, + "bin": { + "h3": "bin/h3.mjs" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "crossws": "^0.4.12", + "ocache": ">=0.3.0" + }, + "peerDependenciesMeta": { + "crossws": { + "optional": true + }, + "ocache": { + "optional": true + } + } + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "dev": true, + "license": "MIT" + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/hono": { + "version": "4.13.14", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.14.tgz", + "integrity": "sha512-yhRLQ21U6qa9Hsu2N7Qcbh8tE+WKEcoErjIo56LeSGiGXGp9sA7UpE86Ui9uexb61KakiGwuHMn2QdV7w6i6kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hookable": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.2.tgz", + "integrity": "sha512-+abwxtiEA52GCVIsQqut3S/uKTbUwYIp4Pe/vv+6py5XiXBCqMZHg6pA6Y5qhgLSEys0/cYuPbO0z1QFj5ZCmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/htmlparser2": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-12.0.0.tgz", + "integrity": "sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0", + "domutils": "^4.0.2", + "entities": "^8.0.0" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", + "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==", + "dev": true, + "license": "ISC" + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "dev": true, + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ip-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz", + "integrity": "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.1.tgz", + "integrity": "sha512-li88H4QGRoNMX27jEmY8nR515oJVspAgC/WFMlhHfFV8RJ4QyGpHhXnwAe9gH5RIgKmdYRUmrOhQobcgEjIdSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-directory": "^4.0.1", + "is-path-inside": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally/node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/is-ip": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-5.0.1.tgz", + "integrity": "sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-regex": "^5.0.0", + "super-regex": "^0.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-regexp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz", + "integrity": "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "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": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.2.tgz", + "integrity": "sha512-3l+rb15IOWtUhU0H5MFqES/T6Kh7abYwjosBey/vD6hDt8zoEffkSC5Ws5SGtgVw3gBx2NEbhTeSW1+kWkpyTQ==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/katex": { + "version": "0.18.10", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.18.10.tgz", + "integrity": "sha512-/B6p9eY9DX7aHBfpkHdpirDTZ5QH9xTwL9y837PWuzuv41O5jFdqNQwVQEQsimY0/iVNkwBY4+4hMfhQ7d4Dbw==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^15.0.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/knitwork": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/knitwork/-/knitwork-1.3.0.tgz", + "integrity": "sha512-4LqMNoONzR43B1W0ek0fhXMsDNW/zxa1NdFAVMY+k28pgZLovR4G3PB5MrpTxCy1QaZCqNoiaKPr5w5qZHfSNw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "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", + "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/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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/linkify-it": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/lz-string-es": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lz-string-es/-/lz-string-es-1.0.0.tgz", + "integrity": "sha512-2VpNj4+9zmknzR80HPgcXnuvnzNwxneAwH75KKGJLcJg5OAoZLscmEeqdAzU3d9J/CGLzSfA3K7p8DQco+K+zA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/magic-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.4.tgz", + "integrity": "sha512-jQz9sVhrZuphH4NrAIHSgkDJCluQO7Ioi4KGRLa7aTUjAODQ13hENX0YIytqYE/rIkO485gQG2LeBxJaW53kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/magic-string-ast": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.3.tgz", + "integrity": "sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.19" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/magic-string-ast/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magic-string-stack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/magic-string-stack/-/magic-string-stack-2.0.0.tgz", + "integrity": "sha512-ag1sq2zvWGKeHJLCAJkbi9A2cHEcsKx3wCIyI4mRP5egjR0XhOqfrGs/IcYXQXVQaAIXhVQhBwY6jSLM2WHVCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "magic-string": "^1.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/markdown-exit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/markdown-exit/-/markdown-exit-1.3.0.tgz", + "integrity": "sha512-bsqlcm3Hk1ukcG1Al83pp3JjVobu4w1mpZz47OzLw8bCY8XchkCX+PXC32l2TH4nZ7p5WFg2NwpnLDXkY1X7Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5.0.0", + "@types/mdurl": "^2.0.0", + "entities": "^7.0.0", + "linkify-it": "^5.0.1", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + } + }, + "node_modules/markdown-it": { + "version": "14.3.3", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.3.tgz", + "integrity": "sha512-L//tDAkjTjJCU6pBUSBTgs3Kg4InVaZuUZog/b6V12UKhcDQ7S78877ibbCanRExwPuYP1CZD7Gd2dxSy1GXRg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-footnote": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-footnote/-/markdown-it-footnote-4.0.0.tgz", + "integrity": "sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/markdown-it-github-alerts": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/markdown-it-github-alerts/-/markdown-it-github-alerts-1.0.1.tgz", + "integrity": "sha512-NNATF4QdoGI07hyCitoB2YqJ1YcNVCKT89ut2VtfFY9rkeFCXe/V2lOonKQLpJiq5DjiZZepf97BJx5xOjFIAw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "markdown-it": ">= 13.0.0" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.4.tgz", + "integrity": "sha512-z4B5c6jrnJ1ltOQemhWYPBSAo6y42Z8Gt2yEgfgNTMcLSU59B8bfj8Z8AnAlJrBGLnqkdAUZ7jdDEqQw3KuipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.1.0.tgz", + "integrity": "sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.1.tgz", + "integrity": "sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.2.0.tgz", + "integrity": "sha512-2Jn/ADrHFFyyniBMi0bncTbJqU78K3/TEYvzvwrWWzD9MYPUa/Gm4r3nOeKFQg/PDM5BsbDYdc8ackW4NTY6NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/mdurl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-12.2.0.tgz", + "integrity": "sha512-w7nr/SFrLO7BaVTs3dQTvYSV6nYSBj0Yy9DjBhsIJtAcHqRzXSq4Pg32ujHZ/AdWsAMNEvvhbm5dt9E2tOiHzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.2", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^2.0.1", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "chevrotain": "~13.2.0", + "cytoscape": "^3.34.0", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.21", + "dompurify": "^3.4.12", + "elkjs": "^0.9.3", + "es-toolkit": "^1.45.1", + "katex": "^0.18.0", + "khroma": "^2.1.0", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/micromark": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.3.tgz", + "integrity": "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-edit-map": "^1.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.4.tgz", + "integrity": "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.1.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-edit-map": "^1.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.3" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.1.0.tgz", + "integrity": "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-edit-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-edit-map/-/micromark-util-edit-map-1.0.0.tgz", + "integrity": "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.3.tgz", + "integrity": "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/monaco-editor": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.56.0.tgz", + "integrity": "sha512-sXboRm3BeBeLm938eaiyLMe0OxzfXIlZvbv4ir/jVgQy1zDhWjgmny0WoN45fuDKhCCQsYMbBJrv/A6jd8aCUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dompurify": "3.4.8", + "marked": "14.0.0" + } + }, + "node_modules/monaco-editor/node_modules/dompurify": { + "version": "3.4.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.8.tgz", + "integrity": "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==", + "dev": true, + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/monaco-editor/node_modules/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanotar": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/nanotar/-/nanotar-0.3.0.tgz", + "integrity": "sha512-Kv2JYYiCzt16Kt5QwAc9BFG89xfPNBx+oQL4GQXD9nLqPkZBiNaqaCWtwnbk/q7UVsTYevvM1b0UF8zmEI4pCg==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz", + "integrity": "sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nostics": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/nostics/-/nostics-1.3.0.tgz", + "integrity": "sha512-wzRsrJwNlFbqIRQt8aCeL2WE8USip/uuvGSOZV1D02CxX8ABTMbw8IywKjH7yWHETMXbWJI4OBpihg9rmz2Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/obug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/open": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.4.tgz", + "integrity": "sha512-++Zlftm0kVLPmzC06t6epuWmcRMDbI4z5P3NNX979WA/k23+NtSOynEGzsVfZwguKw2mi5umVgnBlJQMwRz4Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.5.1", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.2.1", + "wsl-utils": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oxc-parser": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.153.0.tgz", + "integrity": "sha512-cEnpyyV1OJRXv++SFNw0z8i+2o/cr88rVLaOYJafELDoHM5yHvCAbwGRLvxuany6SNLcbblSMUyZaGzH+quSYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "^0.153.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + }, + "optionalDependencies": { + "@oxc-parser/binding-android-arm-eabi": "0.153.0", + "@oxc-parser/binding-android-arm64": "0.153.0", + "@oxc-parser/binding-darwin-arm64": "0.153.0", + "@oxc-parser/binding-darwin-x64": "0.153.0", + "@oxc-parser/binding-freebsd-x64": "0.153.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.153.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.153.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.153.0", + "@oxc-parser/binding-linux-arm64-musl": "0.153.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.153.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.153.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.153.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.153.0", + "@oxc-parser/binding-linux-x64-gnu": "0.153.0", + "@oxc-parser/binding-linux-x64-musl": "0.153.0", + "@oxc-parser/binding-openharmony-arm64": "0.153.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.153.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.153.0", + "@oxc-parser/binding-win32-x64-msvc": "0.153.0" + } + }, + "node_modules/oxc-walker": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/oxc-walker/-/oxc-walker-1.1.1.tgz", + "integrity": "sha512-Hwd7dq28zu/Er99+bpWp16CGwFrhyalJh0W3JOB7XpPvgWo3MqMi2ksWGKuzzA9zt50mJ2pIUwR5lpAdZ9ACNQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@oxc-project/types": ">=0.98.0", + "oxc-parser": ">=0.98.0", + "rolldown": ">=1.0.0" + }, + "peerDependenciesMeta": { + "@oxc-project/types": { + "optional": true + }, + "oxc-parser": { + "optional": true + }, + "rolldown": { + "optional": true + } + } + }, + "node_modules/p-map": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.1.1.tgz", + "integrity": "sha512-6Lh3jQPLthYujSrlw6ccWXBGZTkoTCz27Hd92g79LH/kjAbgoEu0t442cCCNbegPSJsA0lwDX8z0bcKtkpA32g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz", + "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, + "node_modules/pdf-lib/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "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", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.4.0.tgz", + "integrity": "sha512-07Fys7iVx0X6TcviMOgogM1Lk33ZyqOezMnU79S+kffWqwolG/5W9V8EgQSeYCIfFTYVgssRbnH630Oa2+soBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.3.1", + "exsolve": "^1.1.3", + "pathe": "^2.0.3" + } + }, + "node_modules/pkg-types/node_modules/confbox": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", + "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/plantuml-encoder": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/plantuml-encoder/-/plantuml-encoder-1.4.0.tgz", + "integrity": "sha512-sxMwpDw/ySY1WB2CE3+IdMuEcWibJ72DDOsXLkSmEaSzwEUaYBT6DWgOfBiHGCux4q433X6+OEFWjlVqp7gL6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/playwright-chromium": { + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/playwright-chromium/-/playwright-chromium-1.64.1.tgz", + "integrity": "sha512-lvAzvvIRtbHaeLwl7XoUkhSNuWHKshbrYv2sjqYgLQS4SsTTwgSTlg9BwU5LeGxYkHxBXTnKtNDEQdZSOj/01g==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.64.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.1.tgz", + "integrity": "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "node_modules/popmotion": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.5.tgz", + "integrity": "sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA==", + "dev": true, + "license": "MIT", + "dependencies": { + "framesync": "6.1.2", + "hey-listen": "^1.0.8", + "style-value-types": "5.1.2", + "tslib": "2.4.0" + } + }, + "node_modules/popmotion/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true, + "license": "0BSD" + }, + "node_modules/postcss": { + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", + "dev": true, + "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.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-nested": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-8.0.1.tgz", + "integrity": "sha512-PvSIwDVh1NTw939iu4FBK+oeZfSm0cbErFAjXCTPiStammzobBNE2SiQNezy8Xp+Oud2uLXaLpTauv3UdxWjbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.1.4" + }, + "engines": { + "node": "^22.0.0 || ^24.0.0 || >= 26.0.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.7.tgz", + "integrity": "sha512-wWBro/7hJ40ye6TPiXkUQoXKCw23xSsjriUvqd7Uls9Sso3oLiON1flc18cCdnjgCRXw4fBVqUJJeOdDTh3P3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/powershell-utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz", + "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pptxgenjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pptxgenjs/-/pptxgenjs-4.0.1.tgz", + "integrity": "sha512-TeJISr8wouAuXw4C1F/mC33xbZs/FuEG6nH9FG1Zj+nuPcGMP5YRHl6X+j3HSUnS1f3at6k75ZZXPMZlA5Lj9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^22.8.1", + "https": "^1.0.0", + "image-size": "^1.2.1", + "jszip": "^3.10.1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/public-ip": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/public-ip/-/public-ip-8.0.0.tgz", + "integrity": "sha512-XzVyz98rNQiTRciAC+I4w45fWWxM9KKedDGNtH4unPwBcWo2Y9n7kgPXqlTiWqKN0EFlIIU1i8yrWOy9mxgZ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-socket": "^4.2.2", + "is-ip": "^5.0.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/rc9": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.1.0.tgz", + "integrity": "sha512-ufjkNVzbRHKcCOmTahZkmVsyc3W+MSk3jY03m+a7tGHkIsdVMG9l10/3HvFbWkkKzY5VFp3pkRsIo/UYgmFL7Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "defu": "^6.1.7", + "destr": "^2.0.5" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recordrtc": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/recordrtc/-/recordrtc-5.6.2.tgz", + "integrity": "sha512-1QNKKNtl7+KcwD1lyOgP3ZlbiJ1d0HtXnypUy7yq49xEERxk31PHvE9RCciDrulPCY7WJ+oz0R9hpNxgsIurGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-global": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-2.0.0.tgz", + "integrity": "sha512-gnAQ0Q/KkupGkuiMyX4L0GaBV8iFwlmoXsMtOz+DFTaKmHhOO/dSlP1RMKhpvHv/dh6K/IQkowGJBqUG0NfBUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-directory": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/resolve-global/node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/resolve-global/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/rolldown": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz", + "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.155.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.15", + "@rolldown/binding-android-arm64": "1.2.15", + "@rolldown/binding-darwin-arm64": "1.2.15", + "@rolldown/binding-darwin-x64": "1.2.15", + "@rolldown/binding-freebsd-x64": "1.2.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", + "@rolldown/binding-linux-arm64-gnu": "1.2.15", + "@rolldown/binding-linux-arm64-musl": "1.2.15", + "@rolldown/binding-linux-ppc64-gnu": "1.2.15", + "@rolldown/binding-linux-s390x-gnu": "1.2.15", + "@rolldown/binding-linux-x64-gnu": "1.2.15", + "@rolldown/binding-linux-x64-musl": "1.2.15", + "@rolldown/binding-openharmony-arm64": "1.2.15", + "@rolldown/binding-win32-arm64-msvc": "1.2.15", + "@rolldown/binding-win32-x64-msvc": "1.2.15" + } + }, + "node_modules/rolldown/node_modules/@oxc-project/types": { + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz", + "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/rou3": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rou3/-/rou3-1.0.0.tgz", + "integrity": "sha512-ySeaQkSrua+ut9g+65I7FHqOFBaY8tV0FmccXItoq0q21se32Q3u4YiW7/RqXSosWgYJaaKwRHXy3BST9PeCNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/shiki": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.5.0.tgz", + "integrity": "sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.5.0", + "@shikijs/engine-javascript": "4.5.0", + "@shikijs/engine-oniguruma": "4.5.0", + "@shikijs/langs": "4.5.0", + "@shikijs/themes": "4.5.0", + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/srvx": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/srvx/-/srvx-1.0.7.tgz", + "integrity": "sha512-8h30Qy2cpYkcXgByHNpV4+jp609bU3ic7MqnfyLQ2Fpa8BgjIaI2zfsDKAIbwm8DcYDrCLqm5izF5KeL2nxdvg==", + "dev": true, + "license": "MIT", + "bin": { + "srvx": "bin/srvx.mjs" + }, + "engines": { + "node": ">=20.16.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/style-value-types": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.1.2.tgz", + "integrity": "sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "2.4.0" + } + }, + "node_modules/style-value-types/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true, + "license": "0BSD" + }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/super-regex": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-0.2.0.tgz", + "integrity": "sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-regexp": "^3.0.0", + "function-timeout": "^0.1.0", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "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", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-dedent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz", + "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/twoslash": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/twoslash/-/twoslash-0.3.9.tgz", + "integrity": "sha512-rDclk+OtzuTX+tnea7DYLCkqGQ3eP0IyfD+kzUJ7t46X/NzlaxwrhecmEBNuSCuEn3V+n1PhcjUUQQ7gUJzX5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript/vfs": "^1.6.4", + "twoslash-protocol": "0.3.9" + }, + "peerDependencies": { + "typescript": "^5.5.0 || ^6.0.0" + } + }, + "node_modules/twoslash-protocol": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/twoslash-protocol/-/twoslash-protocol-0.3.9.tgz", + "integrity": "sha512-9/iwp+CXOnjFMPQuPL5PkuRbZnDoNpBvtJCLs9t8kDYkL3YHujbvnHfZA1i5fApDftVEdBw+T/4F+dH5kIzpYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/twoslash-vue": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/twoslash-vue/-/twoslash-vue-0.3.9.tgz", + "integrity": "sha512-2zO1u4iPhZz9k7ysuDaJL1FUn4SHzm78ZF6/0Q4yFR2VP3NW0JwRpw/4cWqEM6ye3pDf8Zr9lVPBvsX4uK315A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/language-core": "^3.2.6", + "twoslash": "0.3.9", + "twoslash-protocol": "0.3.9" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "typescript": "^5.5.0 || ^6.0.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unconfig": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/unconfig/-/unconfig-7.5.0.tgz", + "integrity": "sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "defu": "^6.1.4", + "jiti": "^2.6.1", + "quansync": "^1.0.0", + "unconfig-core": "7.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unconfig-core": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/unconfig-core/-/unconfig-core-7.5.0.tgz", + "integrity": "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unconfig-core/node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/unconfig/node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/unctx": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/unctx/-/unctx-2.5.0.tgz", + "integrity": "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "acorn": "^8.15.0", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21", + "unplugin": "^2.3.11" + } + }, + "node_modules/unctx/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/unctx/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/unctx/node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/unhead": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/unhead/-/unhead-3.4.3.tgz", + "integrity": "sha512-0FZI/qCCyIey14CKlGMFpiQumWSatlthJy7Xvro7H3nmi/V2owkcCXG03k0GXDc2/TV1K6bvrbAwhzTRe5Nhdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookable": "^6.1.1", + "unplugin": "^3.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + }, + "peerDependencies": { + "vite": ">=6.4.2" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.2.0.tgz", + "integrity": "sha512-fpsYs/VVoZxfGY9/j3FQpZmN8wdLJaV1Pt38AaJNZu9uKIHcFU3halCdBveEq1HGKO1OEfJY65w1zvoFV1b3qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.1.0.tgz", + "integrity": "sha512-vKXxz6O38sKDj1mtx/k2cWvkkFBuYGx6uGrlzBkVeWCRF/F8sdI0j3Wbkz/5rVNRRuVc8UrgRdbCxNhmYXgP3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unocss": { + "version": "66.12.0", + "resolved": "https://registry.npmjs.org/unocss/-/unocss-66.12.0.tgz", + "integrity": "sha512-BfdiiXQKc8mg6JPqVdttVNL4gbQZy5jD8MFtl5Q4QAmPMjLHy6Ybhc9nWSvwBCo4PjMxgXonX/97wF8tmJQ1Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@unocss/cli": "66.12.0", + "@unocss/core": "66.12.0", + "@unocss/preset-attributify": "66.12.0", + "@unocss/preset-icons": "66.12.0", + "@unocss/preset-mini": "66.12.0", + "@unocss/preset-tagify": "66.12.0", + "@unocss/preset-typography": "66.12.0", + "@unocss/preset-uno": "66.12.0", + "@unocss/preset-web-fonts": "66.12.0", + "@unocss/preset-wind": "66.12.0", + "@unocss/preset-wind3": "66.12.0", + "@unocss/preset-wind4": "66.12.0", + "@unocss/rollup": "66.12.0", + "@unocss/transformer-attributify-jsx": "66.12.0", + "@unocss/transformer-compile-class": "66.12.0", + "@unocss/transformer-directives": "66.12.0", + "@unocss/transformer-variant-group": "66.12.0", + "@unocss/vite": "66.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@unocss/astro": "66.12.0", + "@unocss/postcss": "66.12.0", + "@unocss/webpack": "66.12.0" + }, + "peerDependenciesMeta": { + "@unocss/astro": { + "optional": true + }, + "@unocss/postcss": { + "optional": true + }, + "@unocss/webpack": { + "optional": true + } + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unplugin": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.5.0.tgz", + "integrity": "sha512-s9qc6Y8ChhQE9RZ9Se+znEtZA0oK31LmEBO9y97ixR3ErA8UwlzA4XNybg/41od8rQ8EKScVQM857NWnI+EWkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.7", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rsbuild/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rsbuild/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/unplugin-icons": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-24.0.0.tgz", + "integrity": "sha512-wV6c7I0PtiIP8KIU7Z1Ukyc2uzgY9ind3RXNu7j63L9qTWDn7CzLnW9FD57DKbfGpWKQv3qyV2PCtBG8Y5Llcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/utils": "^3.1.7", + "local-pkg": "^1.2.1", + "obug": "^2.2.1", + "unplugin": "^3.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@svgr/core": ">=7.0.0", + "@svgx/core": "^1.0.1", + "@vue/compiler-sfc": "^3.0.2", + "@vue/compiler-vapor": "^3.6.0-rc.5", + "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "@svgr/core": { + "optional": true + }, + "@svgx/core": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "@vue/compiler-vapor": { + "optional": true + }, + "svelte": { + "optional": true + } + } + }, + "node_modules/unplugin-icons/node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unplugin-icons/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/unplugin-utils": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz", + "integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-vue-components": { + "version": "32.1.0", + "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-32.1.0.tgz", + "integrity": "sha512-YiUkSxuRjab18XFOrX5VsIxXzccrfmHVGsGeJgSgklb829DQmCy9E4vvDUE4tuvZZdxyFJZX0Oc4TPnnxiiMyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "local-pkg": "^1.2.0", + "magic-string": "^0.30.21", + "mlly": "^1.8.2", + "obug": "^2.1.1", + "picomatch": "^4.0.4", + "tinyglobby": "^0.2.16", + "unplugin": "^3.0.0", + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@nuxt/kit": "^3.2.2 || ^4.0.0", + "vue": "^3.0.0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/unplugin-vue-components/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/unplugin-vue-components/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/unplugin-vue-markdown": { + "version": "32.1.1", + "resolved": "https://registry.npmjs.org/unplugin-vue-markdown/-/unplugin-vue-markdown-32.1.1.tgz", + "integrity": "sha512-2d9MCA4cA3JQ1qLVPE1mfsIv7dqubzVhxfrNDKOwJ/Z2tfz+Zp6LJ1QMZow/vhpjmKbwJ35TyRk94uxiQ2p1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mdit-vue/plugin-component": "^3.0.2", + "@mdit-vue/plugin-frontmatter": "^3.0.2", + "@mdit-vue/types": "^3.0.2", + "markdown-exit": "^1.2.1", + "unplugin": "^3.3.0", + "unplugin-utils": "^0.3.2" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.0.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/untun": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/untun/-/untun-0.2.2.tgz", + "integrity": "sha512-+NnOJcSiEtYsVgJmXUzQbJeRAFXJC4yPJYuh6kF9B0Rm6zunXcs/3GZOTllyocSbUDIxD6Bj7e/4ATw7sph1Sw==", + "dev": true, + "license": "MIT", + "bin": { + "untun": "dist/cli.mjs" + } + }, + "node_modules/untyped": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/untyped/-/untyped-2.0.0.tgz", + "integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "citty": "^0.1.6", + "defu": "^6.1.4", + "jiti": "^2.4.2", + "knitwork": "^1.2.0", + "scule": "^1.3.0" + }, + "bin": { + "untyped": "dist/cli.mjs" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", + "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/uqr": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.3.tgz", + "integrity": "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz", + "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/verkit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.4.1.tgz", + "integrity": "sha512-mMVzj0TXExtVdlDEq+Mzp0eyOuyznNpFobNM3uAqe3HVm/Ps2c+u17BligMkZjZCA6EiXpoon8iRTer3iu40SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.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==", + "dev": true, + "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.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz", + "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.30", + "rolldown": "~1.2.14", + "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 || ^0.8.0", + "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-plugin-inspect": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-12.1.0.tgz", + "integrity": "sha512-RTUOcJ0EklTBfx5CRbt7YkFAB072qUBk/QeBCUacVVrp/mFPzs3UdWJvnLt1GzHbJ9qoG2rrOdHhtdE9KbNBFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitejs/devtools-kit": "^0.7.6", + "ansis": "^4.4.0", + "error-stack-parser-es": "^2.0.1", + "obug": "^3.0.0", + "ohash": "^2.0.12", + "open": "^11.0.4", + "perfect-debounce": "^2.1.0", + "sirv": "^3.0.2", + "unplugin-utils": "^0.3.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^8.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-remote-assets": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-remote-assets/-/vite-plugin-remote-assets-2.1.0.tgz", + "integrity": "sha512-8ajL5WG5BmYcC8zxeLOa3byCUG2AopKDAdNK7zStPHaRYYz1mxXBaeNFLu6vTEXj8UmXAsb5WlEmBBYwtlPEwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.1", + "magic-string": "^0.30.17", + "node-fetch-native": "^1.6.7", + "ohash": "^2.0.11" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": ">=5.0.0" + } + }, + "node_modules/vite-plugin-remote-assets/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/vite-plugin-static-copy": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-4.1.1.tgz", + "integrity": "sha512-GrlA8YklrAfSyxJ4M3fdQLOo9oNkp56IM9FYgX/WtEgeIFkPwhu4wzpufBCIuNKCa6Fn77FkRdYxkHqV0FwjAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.6.0", + "p-map": "^7.0.4", + "picocolors": "^1.1.1", + "tinyglobby": "^0.2.17" + }, + "engines": { + "node": "^22.0.0 || >=24.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/sapphi-red" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/vite-plugin-static-copy/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/vite-plugin-static-copy/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite-plugin-static-copy/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/vite-plugin-vue-server-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-server-ref/-/vite-plugin-vue-server-ref-1.0.0.tgz", + "integrity": "sha512-6d/JZVrnETM0xa0AVyEcI1bXFpEzQ1EPU5N/gDa7NtXo/7nfJWJhezcWq82Jih6Vf8xtGJjhi1w19AcXAtwmAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "klona": "^2.0.6", + "mlly": "^1.7.4", + "ufo": "^1.5.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": ">=2.0.0", + "vue": "^3.0.0" + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.44.tgz", + "integrity": "sha512-dvvmaOCG7lAMkJTPqPwWeGMsPS6DIsFsfZDUgBp9V6mHf9b3+/wyVwYR4Es+WV6uJC0FKRxp81Obcq0ZwUcGFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.44", + "@vue/compiler-sfc": "3.5.44", + "@vue/runtime-dom": "3.5.44", + "@vue/server-renderer": "3.5.44", + "@vue/shared": "3.5.44" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.4.0.tgz", + "integrity": "sha512-pFvcYcxinDHjZUlf7AzFBrT8r8Xqvv4uMHoJnjc+EqPawXQvHTkF8S4tfSmQLpriVHUGfkaKlI7Et0KRjTMTdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue-macros/common": "^3.1.3", + "@vue/devtools-api": "^8.1.5", + "ast-walker-scope": "^0.10.0", + "chokidar": "^5.0.0", + "confbox": "^0.3.1", + "local-pkg": "^1.2.1", + "magic-string": "^1.4.3", + "mlly": "^1.8.2", + "muggle-string": "^0.4.1", + "nostics": "^1.3.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.7", + "scule": "^1.3.0", + "tinyglobby": "^0.2.17", + "unplugin": "^3.4.0", + "unplugin-utils": "^0.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@pinia/colada": ">=0.21.2", + "@vue/compiler-sfc": "^3.5.34 || ^4.0.0", + "pinia": "^3.0.4 || ^4.0.2", + "vite": "^7.3.0 || ^8.0.0", + "vue": "^3.5.34 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@pinia/colada": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "pinia": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vue-router/node_modules/confbox": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", + "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wsl-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.1.tgz", + "integrity": "sha512-c16PKLIRxBYpR3jSWsdS7SFp0COm5SOQdiFjY818dYFHJbMGuLBszpF07LBWmOS1uzrHa9VGdynsEgoeKy0oOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/zigpty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/zigpty/-/zigpty-0.2.1.tgz", + "integrity": "sha512-MR9JqJx2wf5f4wz8zpx050AlqrmWeIW+1h0SO5iEyhG3HFRjY5luC3szS2ux2EGuPjE5OU9ZAuiCBeMWBTrqZw==", + "dev": true, + "workspaces": [ + "playground" + ] + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/slides/package.json b/slides/package.json new file mode 100644 index 0000000..ab25268 --- /dev/null +++ b/slides/package.json @@ -0,0 +1,20 @@ +{ + "name": "python-doesnt-byte-slides", + "private": true, + "type": "module", + "description": "Presentazioni Slidev delle lezioni di Python Doesn’t Byte", + "scripts": { + "dev": "node scripts/slides.mjs dev", + "build": "node scripts/slides.mjs build", + "pdf": "node scripts/slides.mjs pdf", + "check": "node scripts/slides.mjs check" + }, + "engines": { + "node": ">=20.0" + }, + "devDependencies": { + "@slidev/cli": "^53.0.0", + "@slidev/parser": "^53.0.0", + "playwright-chromium": "^1.64.1" + } +} diff --git a/slides/scripts/slides.mjs b/slides/scripts/slides.mjs new file mode 100644 index 0000000..30aaf16 --- /dev/null +++ b/slides/scripts/slides.mjs @@ -0,0 +1,521 @@ +#!/usr/bin/env node +/** + * Presentazioni delle lezioni: sviluppo, build, PDF e controlli. + * + * node scripts/slides.mjs dev archivista/le-chiavi anteprima dal vivo + * node scripts/slides.mjs build [lezione…] presentazioni in static/slides/ + * node scripts/slides.mjs pdf [lezione…] PDF da distribuire a mano + * node scripts/slides.mjs check [lezione…] controlla i mazzi + * node scripts/slides.mjs verifica [lezione…] cerca slide che strabordano + * node scripts/slides.mjs provino [lezione…] tutte le slide in un'immagine + * + * Un mazzo è slides/decks//.md e appartiene alla lezione + * volumes//.mdx. Esce in static/slides///: + * index.html, la presentazione servita dal sito a /slides/// + * (con `pdf`, anche .pdf). static/slides/ non è nel repository: + * la crea la CI prima di `npm run build` (vedi .github/workflows/). + * + * Senza lezioni indicate, i comandi lavorano su tutti i mazzi. I file che + * iniziano con `_` (bozze, prove, il modello) sono esclusi. + */ +import { spawnSync } from 'node:child_process'; +import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs'; +import { createServer } from 'node:http'; +import { basename, dirname, extname, join, normalize, relative } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { parseSync } from '@slidev/parser'; + +/** Ultima modifica di un file per git: commit, o «adesso» se ha modifiche non salvate. */ +function ultimaModifica(file) { + const rel = relative(RADICE, file); + const sporco = spawnSync('git', ['status', '--porcelain', '--', rel], { cwd: RADICE, encoding: 'utf8' }).stdout.trim(); + if (sporco) return Infinity; + const t = spawnSync('git', ['log', '-1', '--format=%ct', '--', rel], { cwd: RADICE, encoding: 'utf8' }).stdout.trim(); + return t ? Number(t) : 0; +} + +const SLIDES = fileURLToPath(new URL('..', import.meta.url)); +const RADICE = join(SLIDES, '..'); +const MAZZI = join(SLIDES, 'decks'); +const USCITA = join(RADICE, 'static', 'slides'); +const SLIDEV = join(SLIDES, 'node_modules', '.bin', 'slidev'); + +const VOLUMI = ['programmatore', 'artefice', 'archivista', 'apprendista']; +const LAYOUT = new Set( + readdirSync(join(SLIDES, 'theme', 'layouts')).map((f) => f.replace(/\.vue$/, '')), +); + +/** Tutti i mazzi, come `volume/slug`. */ +function tuttiIMazzi() { + const elenco = []; + for (const volume of readdirSync(MAZZI, { withFileTypes: true })) { + if (!volume.isDirectory() || volume.name.startsWith('_')) continue; + for (const file of readdirSync(join(MAZZI, volume.name))) { + if (file.endsWith('.md') && !file.startsWith('_')) + elenco.push(`${volume.name}/${file.replace(/\.md$/, '')}`); + } + } + return elenco.sort(); +} + +function scegli(argomenti) { + const richiesti = argomenti.filter((a) => !a.startsWith('--')); + if (!richiesti.length) return tuttiIMazzi(); + for (const r of richiesti) { + if (!existsSync(fileMazzo(r))) { + console.error(`✗ Nessun mazzo per «${r}» (cercato ${relative(RADICE, fileMazzo(r))}).`); + process.exit(1); + } + } + return richiesti; +} + +const fileMazzo = (lezione) => join(MAZZI, `${lezione}.md`); + +function esegui(argomenti, opzioni = {}) { + const esito = spawnSync(SLIDEV, argomenti, { stdio: 'inherit', cwd: SLIDES, ...opzioni }); + if (esito.status !== 0) { + console.error(`✗ slidev ${argomenti.join(' ')} è uscito con ${esito.status}.`); + process.exit(esito.status ?? 1); + } +} + +// ─── Controlli ────────────────────────────────────────────────────────────── + +/** Errori di un mazzo: [] se va bene. */ +function controlla(lezione) { + const errori = []; + const file = fileMazzo(lezione); + const [volume, slug] = lezione.split('/'); + const sorgente = readFileSync(file, 'utf8'); + let mazzo; + try { + mazzo = parseSync(sorgente, file); + } catch (e) { + return [`non si legge: ${e.message}`]; + } + const slide = mazzo.slides; + const testa = slide[0]?.frontmatter ?? {}; + + if (testa.theme !== '../../theme') errori.push('frontmatter iniziale: `theme: ../../theme`'); + if (!testa.title) errori.push('frontmatter iniziale: manca `title` (il titolo della lezione)'); + if (testa.volume !== volume) + errori.push(`frontmatter iniziale: \`volume: ${volume}\` (la cartella del mazzo)`); + if (testa.lezione !== lezione) + errori.push(`frontmatter iniziale: \`lezione: ${lezione}\` (cartella/nome del file)`); + if (!VOLUMI.includes(volume)) errori.push(`volume sconosciuto: ${volume}`); + const fileLezione = join(RADICE, 'volumes', volume, `${slug}.mdx`); + if (!existsSync(fileLezione)) errori.push(`la lezione volumes/${volume}/${slug}.mdx non esiste`); + // La lezione è cambiata dopo il suo mazzo: le slide vanno aggiornate (e il + // mazzo modificato insieme alla lezione, nello stesso commit). + else if (ultimaModifica(fileLezione) > ultimaModifica(file)) + errori.push(`la lezione volumes/${volume}/${slug}.mdx è cambiata dopo il mazzo: aggiorna le slide`); + if (testa.layout && !['copertina', 'cover'].includes(testa.layout)) + errori.push('la prima slide è la copertina: niente `layout` diverso'); + const ultima = slide[slide.length - 1]?.frontmatter?.layout; + if (ultima !== 'fine') errori.push("l'ultima slide ha `layout: fine`"); + + slide.forEach((s, i) => { + const n = i + 1; + const layout = s.frontmatter?.layout ?? (i === 0 ? 'copertina' : 'default'); + const testo = s.content ?? ''; + if (!LAYOUT.has(layout)) errori.push(`slide ${n}: layout «${layout}» non esiste nel tema`); + // Un testo con «: » senza virgolette, nello YAML, diventa un oggetto. + for (const [chiave, valore] of Object.entries(s.frontmatter ?? {})) + if (valore && typeof valore === 'object' && !Array.isArray(valore)) + errori.push(`slide ${n}: \`${chiave}:\` contiene «: » e va tra virgolette ('…')`); + const richiede = { + obiettivi: ['::prerequisiti::', '::imparerai::'], + 'due-colonne': ['::sinistra::', '::destra::'], + domanda: ['::risposta::'], + figura: ['::figura::'], + }[layout]; + for (const slot of richiede ?? []) + if (!testo.includes(slot)) errori.push(`slide ${n} (${layout}): manca lo slot ${slot}`); + if (layout === 'quiz') { + const corrette = (testo.match(/]*\bcorretta\b/g) ?? []).length; + if (corrette !== 1) + errori.push(`slide ${n} (quiz): serve una sola , trovate ${corrette}`); + if (!/^#\s/m.test(testo)) errori.push(`slide ${n} (quiz): la domanda va nel titolo (# …)`); + } + // Nel markdown `__x__` diventa grassetto: `# __str__` uscirebbe «str». + for (const riga of testo.split('\n')) { + const fuoriCodice = riga.replace(/`[^`]*`/g, ''); + if (/^\s*(#|[-*]\s|\d+\.\s)/.test(riga) && /__\w+__|\b__\w/.test(fuoriCodice)) + errori.push(`slide ${n}: «${riga.trim()}»: metti tra backtick i nomi con __ (es. \`__init__\`)`); + } + if (layout === 'figura' && s.frontmatter?.didascalia && testo.includes(' ha già le sue didascalie, togli \`didascalia:\``); + for (const [, id] of testo.matchAll(/]*\bdataset="([^"]+)"/g)) + if (!existsSync(join(RADICE, 'static', 'sql-datasets', `${ds}.sql`))) + errori.push(`slide ${n}: il dataset «${ds}» non esiste in static/sql-datasets/`); + for (const [, blocco] of testo.matchAll(/```[a-z]*[^\n]*\n([\s\S]*?)```/g)) { + const righe = blocco.split('\n').length - 1; + if (righe > 16) + errori.push(`slide ${n}: blocco di codice di ${righe} righe (massimo 16: dividilo o evidenzia per passi)`); + } + }); + return errori; +} + +function controllaTutti(lezioni) { + let falliti = 0; + for (const lezione of lezioni) { + const errori = controlla(lezione); + if (errori.length) { + falliti++; + console.error(`✗ ${lezione}`); + for (const e of errori) console.error(` ${e}`); + } else { + console.log(`✓ ${lezione}`); + } + } + if (falliti) { + console.error(`\n${falliti} mazzi da correggere.`); + process.exit(1); + } +} + +// ─── Build ────────────────────────────────────────────────────────────────── + +function costruisci(lezione) { + const out = join(USCITA, lezione); + rmSync(out, { recursive: true, force: true }); + mkdirSync(dirname(out), { recursive: true }); + console.log(`\n▸ ${lezione}: presentazione`); + esegui(['build', fileMazzo(lezione), '--base', `/slides/${lezione}/`, '--out', out]); +} + +function esporta(lezione) { + const out = join(USCITA, lezione); + mkdirSync(out, { recursive: true }); + const pdf = join(out, `${basename(lezione)}.pdf`); + console.log(`\n▸ ${lezione}: PDF`); + // Le figure si fermano da sole sul passo finale (theme/components/Figura.vue): + // l'attesa serve ai font e al primo disegno di Brython/rough.js. + esegui(['export', fileMazzo(lezione), '--output', pdf, '--wait', '2000', '--timeout', '120000']); +} + +// ─── Verifica visiva ───────────────────────────────────────────────────────── + +const TIPI = { + '.html': 'text/html', + '.js': 'text/javascript', + '.css': 'text/css', + '.json': 'application/json', + '.svg': 'image/svg+xml', + '.png': 'image/png', + '.woff2': 'font/woff2', + '.wasm': 'application/wasm', + '.sql': 'text/plain', + '.pdf': 'application/pdf', +}; + +/** static/ servita alla radice, come il sito pubblicato. */ +function serviStatic() { + const base = join(RADICE, 'static'); + const server = createServer((req, res) => { + let percorso = normalize(decodeURIComponent(new URL(req.url, 'http://x').pathname)); + if (percorso.includes('..')) return res.writeHead(400).end(); + let file = join(base, percorso); + if (existsSync(file) && statSync(file).isDirectory()) file = join(file, 'index.html'); + if (!existsSync(file)) return res.writeHead(404).end(); + res.writeHead(200, { 'Content-Type': TIPI[extname(file)] ?? 'application/octet-stream' }); + res.end(readFileSync(file)); + }); + return new Promise((ok) => server.listen(0, '127.0.0.1', () => ok(server))); +} + +/** + * Apre ogni presentazione costruita e misura ogni slide: contenuto più alto + * del corpo (straborda sul piede), codice più largo del suo riquadro, testo o + * codice in linea che esce di lato da un callout, da una colonna o dalla + * slide, figure tagliate ai bordi. Esegue anche ogni e e + * segnala quelli che finiscono in errore senza averlo dichiarato. La copertina è esclusa: il suo numero grande esce + * dal bordo apposta. + */ +async function verifica(lezioni) { + const { chromium } = await import('playwright-chromium'); + const server = await serviStatic(); + const porta = server.address().port; + const browser = await chromium.launch(); + const pagina = await browser.newPage({ viewport: { width: 1280, height: 800 } }); + let problemi = 0; + for (const lezione of lezioni) { + if (!existsSync(join(USCITA, lezione, 'index.html'))) { + console.error(`✗ ${lezione}: non costruita (npm run slides:build -- ${lezione})`); + problemi++; + continue; + } + await pagina.goto(`http://127.0.0.1:${porta}/slides/${lezione}/#/1`); + // Il numero di slide lo pubblica il tema (custom-nav-controls.vue): il + // sorgente non basta a contarle, perché le slide «risultato» le aggiunge + // il preparser. Poi si aspetta che Slidev le abbia montate tutte. + await pagina.waitForFunction(() => Number(document.documentElement.dataset.pdbTotale) > 0, null, { + timeout: 30000, + }); + const totale = Number(await pagina.evaluate(() => document.documentElement.dataset.pdbTotale)); + await pagina.waitForFunction( + (n) => document.querySelectorAll('.slidev-page').length >= n, + totale, + { timeout: 30000 }, + ); + await pagina.evaluate(() => document.fonts.ready); + await pagina.waitForTimeout(1500); + // Si scorre la presentazione come in classe, una slide alla volta: ogni + // slide ha il tempo di disegnarsi (mermaid, figure che si scalano al loro + // spazio) prima di essere misurata. Gli elementi v-click nascosti + // occupano già il loro spazio, quindi la misura vale anche a clic fatti. + const esiti = []; + for (let n = 1; n <= totale; n++) { + await pagina.evaluate((k) => { + location.hash = `#/${k}`; + }, n); + await pagina.waitForSelector(`.slidev-page-${n}`, { state: 'visible' }); + await pagina.waitForTimeout(450); + const esito = await pagina.evaluate((n) => { + const p = document.querySelector(`.slidev-page-${n}`); + const corpo = p?.querySelector('.pdb-corpo'); + if (!corpo) return null; + const alto = corpo.scrollHeight - corpo.clientHeight; + const largo = [...p.querySelectorAll('pre')].some((x) => x.scrollWidth > x.clientWidth + 2); + // Testo, codice in linea o tabelle che escono di lato da un riquadro + // (callout, colonna) o dalla slide: si confronta il bordo destro di + // ogni elemento con quello del suo contenitore. + const destra = corpo.getBoundingClientRect().right; + const laterale = [...corpo.querySelectorAll('p, li, td, th, code, h1, h2, h3, table, .pdb-callout, .pdb-risposta, .pdb-opzione')] + .filter((el) => !el.closest('pre')) + .some((el) => { + const r = el.getBoundingClientRect(); + const contenitore = el.parentElement?.closest('.pdb-callout, .pdb-risposta, .pdb-colonna, .pdb-opzione, .pdb-corpo'); + const limite = contenitore ? contenitore.getBoundingClientRect().right : destra; + return r.width > 0 && r.right > Math.min(limite, destra) + 2; + }); + // Figure tagliate: il disegno (anche in uno shadow DOM, come i + // mermaid) deve stare dentro lo spazio della figura. + const svgDentro = (r) => + r.querySelector('svg') ?? + [...r.querySelectorAll('*')].map((e) => e.shadowRoot && svgDentro(e.shadowRoot)).find(Boolean) ?? + null; + const tagliata = [...corpo.querySelectorAll('.pdb-figura')].some((f) => { + const svg = svgDentro(f); + if (!svg) return false; + const a = svg.getBoundingClientRect(); + const b = f.getBoundingClientRect(); + return a.width > 0 && (a.left < b.left - 2 || a.right > b.right + 2 || a.top < b.top - 2 || a.bottom > b.bottom + 2); + }); + // Figure rimpicciolite troppo per leggerle da un proiettore. + const minuscola = [...corpo.querySelectorAll('.pdb-figura__adatta')].some((f) => { + const m = /scale\(([\d.]+)\)/.exec(f.style.transform); + return m && Number(m[1]) < 0.6; + }); + return alto > 2 || largo || laterale || tagliata || minuscola + ? { slide: n, alto, largo, laterale, tagliata, minuscola } + : null; + }, n); + if (esito) esiti.push(esito); + + // Ogni blocco eseguibile della slide si esegue davvero, come in classe: + // deve finire senza errori (o in errore, se dichiarato `errore`), e il + // suo risultato, aperto, deve stare tutto nella slide. I blocchi + // `risultato` partono da soli: si aspetta che finiscano. + const blocchi = await pagina.locator(`.slidev-page-${n} .pdb-run`).count(); + for (let i = 0; i < blocchi; i++) { + const blocco = pagina.locator(`.slidev-page-${n} .pdb-run`).nth(i); + const soloRisultato = await blocco.evaluate((el) => el.classList.contains('pdb-run--risultato')); + // Con `seguito`, «Esegui» porta alla slide dopo: il blocco si + // controlla lì, nella sua slide «risultato». + if ((await blocco.getAttribute('data-seguito')) !== null) continue; + if (!soloRisultato) await blocco.locator('.pdb-run__esegui').click(); + const stato = await blocco + .locator('xpath=self::*[@data-stato="fatto" or @data-stato="errore"]') + .waitFor({ timeout: 20000 }) + .then(() => blocco.getAttribute('data-stato')) + .catch(() => 'bloccato'); + await pagina.waitForTimeout(250); + const atteso = (await blocco.getAttribute('data-errore-atteso')) !== null; + const testo = ( + await blocco + .locator('.pdb-run__stderr, .pdb-run__uscita') + .first() + .innerText() + .catch(() => '') + ).trim(); + const nome = `blocco ${i + 1}`; + if (stato === 'bloccato') { + esiti.push({ slide: n, esecuzione: `${nome}: non ha finito in 20 secondi` }); + } else if ((stato === 'errore') !== atteso) { + esiti.push({ + slide: n, + esecuzione: atteso + ? `${nome}: doveva andare in errore (è dichiarato \`errore\`) e invece no` + : `${nome}: va in errore — ${testo.split('\n').slice(-2).join(' ').slice(0, 160)}`, + }); + } + // Il risultato aperto deve leggersi tutto, senza scorrere. + const eccesso = await blocco + .locator('.pdb-run__esiti') + .evaluate((el) => el.scrollHeight - el.clientHeight) + .catch(() => 0); + // E il resto della slide, con il risultato aperto, resta al suo + // posto: niente spinto oltre il piede. + const spinta = await pagina + .locator(`.slidev-page-${n} .pdb-corpo`) + .evaluate((el) => el.scrollHeight - el.clientHeight) + .catch(() => 0); + if (spinta > 2 && eccesso <= 2) { + esiti.push({ + slide: n, + esecuzione: `${nome}: con il risultato aperto la slide straborda di ${spinta}px: aggiungi \`seguito\` al blocco`, + }); + } + if (eccesso > 2) { + esiti.push({ + slide: n, + esecuzione: soloRisultato + ? `${nome}: il risultato non sta nemmeno nella sua slide (mancano ${eccesso}px): riduci le righe (LIMIT) o le tabelle mostrate` + : `${nome}: il risultato aperto non sta nella slide (mancano ${eccesso}px): aggiungi \`seguito\` al blocco, e il risultato avrà una slide sua`, + }); + } + if (!soloRisultato) await blocco.locator('.pdb-run__azzera').click().catch(() => {}); + } + } + if (!totale) esiti.push({ slide: 0, esecuzione: 'nessuna slide misurata' }); + if (esiti.length) { + problemi += esiti.length; + console.error(`✗ ${lezione}`); + for (const e of esiti) { + const cosa = [ + e.alto > 2 && `straborda di ${e.alto}px in basso`, + e.largo && 'codice più largo del riquadro', + e.laterale && 'contenuto che esce di lato (da un callout, una colonna o dalla slide)', + e.tagliata && 'figura tagliata ai bordi', + e.minuscola && 'figura ridotta sotto il 60%: non si legge proiettata (disponila in orizzontale o dalle una slide intera)', + e.esecuzione, + ].filter(Boolean); + console.error(` slide ${e.slide}: ${cosa.join(', ')}`); + } + } else { + console.log(`✓ ${lezione}`); + } + } + await browser.close(); + server.close(); + if (problemi) process.exit(1); +} + +// ─── Provino ───────────────────────────────────────────────────────────────── + +const PROVINI = join(SLIDES, '.provino'); + +/** + * Tutte le slide di un mazzo in un'unica immagine, tre per riga e numerate, + * come un provino fotografico: si rivede la presentazione con un colpo + * d'occhio. Le slide si fotografano dalla presentazione costruita, come le + * vede l'insegnante in classe: con tutti i clic fatti, le figure al passo + * finale e i blocchi «risultato» già eseguiti. + * Esce in slides/.provino/-.png. + */ +async function provino(lezioni) { + const { chromium } = await import('playwright-chromium'); + mkdirSync(PROVINI, { recursive: true }); + const server = await serviStatic(); + const porta = server.address().port; + const browser = await chromium.launch(); + const pagina = await browser.newPage({ viewport: { width: 980, height: 552 } }); + for (const lezione of lezioni) { + if (!existsSync(join(USCITA, lezione, 'index.html'))) { + console.error(`✗ ${lezione}: non costruita (npm run slides:build -- ${lezione})`); + continue; + } + const cartella = join(PROVINI, lezione.replace('/', '-')); + rmSync(cartella, { recursive: true, force: true }); + mkdirSync(cartella, { recursive: true }); + console.log(`▸ ${lezione}`); + await pagina.goto(`http://127.0.0.1:${porta}/slides/${lezione}/#/1`); + await pagina.waitForFunction(() => Number(document.documentElement.dataset.pdbTotale) > 0, null, { timeout: 30000 }); + const totale = Number(await pagina.evaluate(() => document.documentElement.dataset.pdbTotale)); + await pagina.evaluate(() => document.fonts.ready); + for (let n = 1; n <= totale; n++) { + await pagina.evaluate((k) => { + location.hash = `#/${k}?clicks=99`; + }, n); + await pagina.waitForSelector(`.slidev-page-${n}`, { state: 'visible' }); + // Il tempo per i blocchi «risultato» e per le figure che si animano. + await pagina + .locator(`.slidev-page-${n} .pdb-run--risultato:not([data-stato="fatto"]):not([data-stato="errore"])`) + .waitFor({ state: 'detached', timeout: 20000 }) + .catch(() => {}); + await pagina.waitForTimeout(n === 1 ? 1200 : 700); + await pagina.evaluate((k) => { + const accesso = window.__figure; + if (!accesso) return; + for (const f of accesso.elenco()) accesso.vai(f.id, f.passi - 1, 1); + }, n); + await pagina.waitForTimeout(150); + await pagina.locator(`.slidev-page-${n}`).screenshot({ path: join(cartella, `${n}.png`) }); + } + const immagini = readdirSync(cartella) + .filter((f) => f.endsWith('.png')) + .sort((a, b) => parseInt(a) - parseInt(b)); + const html = `${immagini + .map( + (f) => + `
${parseInt(f)}
`, + ) + .join('')}`; + writeFileSync(join(cartella, 'provino.html'), html); + const foglio = await browser.newPage({ viewport: { width: 1816, height: 400 } }); + await foglio.goto(`file://${join(cartella, 'provino.html')}`); + await foglio.waitForLoadState('load'); + const uscita = join(PROVINI, `${lezione.replace('/', '-')}.png`); + await foglio.screenshot({ path: uscita, fullPage: true }); + await foglio.close(); + console.log(` ${relative(RADICE, uscita)}`); + } + await browser.close(); + server.close(); +} + +// ─── Comandi ──────────────────────────────────────────────────────────────── + +const [comando, ...resto] = process.argv.slice(2); + +switch (comando) { + case 'dev': { + const [lezione] = scegli(resto); + if (!resto.length) { + console.error('Indica la lezione: npm run slides:dev -- archivista/le-chiavi'); + process.exit(1); + } + esegui([fileMazzo(lezione), '--open']); + break; + } + case 'build': { + const lezioni = scegli(resto); + controllaTutti(lezioni); + for (const l of lezioni) costruisci(l); + console.log(`\n✓ ${lezioni.length} presentazioni in ${relative(RADICE, USCITA)}/`); + break; + } + case 'pdf': { + const lezioni = scegli(resto); + for (const l of lezioni) esporta(l); + break; + } + case 'check': + controllaTutti(scegli(resto)); + break; + case 'verifica': + await verifica(scegli(resto)); + break; + case 'provino': + await provino(scegli(resto)); + break; + default: + console.log(readFileSync(fileURLToPath(import.meta.url), 'utf8').split('\n').slice(2, 11).join('\n')); + process.exit(comando ? 1 : 0); +} diff --git a/slides/theme/components/Callout.vue b/slides/theme/components/Callout.vue new file mode 100644 index 0000000..82958f6 --- /dev/null +++ b/slides/theme/components/Callout.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/slides/theme/components/CodiceQr.vue b/slides/theme/components/CodiceQr.vue new file mode 100644 index 0000000..2621948 --- /dev/null +++ b/slides/theme/components/CodiceQr.vue @@ -0,0 +1,21 @@ + + + diff --git a/slides/theme/components/Cornice.vue b/slides/theme/components/Cornice.vue new file mode 100644 index 0000000..aef4e9a --- /dev/null +++ b/slides/theme/components/Cornice.vue @@ -0,0 +1,38 @@ + + + diff --git a/slides/theme/components/DiagrammaER.vue b/slides/theme/components/DiagrammaER.vue new file mode 100644 index 0000000..f240fb1 --- /dev/null +++ b/slides/theme/components/DiagrammaER.vue @@ -0,0 +1,17 @@ + + + diff --git a/slides/theme/components/Esito.vue b/slides/theme/components/Esito.vue new file mode 100644 index 0000000..5032d31 --- /dev/null +++ b/slides/theme/components/Esito.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/slides/theme/components/Figura.vue b/slides/theme/components/Figura.vue new file mode 100644 index 0000000..1ec237f --- /dev/null +++ b/slides/theme/components/Figura.vue @@ -0,0 +1,68 @@ + + + diff --git a/slides/theme/components/IsolaReact.vue b/slides/theme/components/IsolaReact.vue new file mode 100644 index 0000000..ce492fd --- /dev/null +++ b/slides/theme/components/IsolaReact.vue @@ -0,0 +1,42 @@ + + + diff --git a/slides/theme/components/Opzione.vue b/slides/theme/components/Opzione.vue new file mode 100644 index 0000000..286044e --- /dev/null +++ b/slides/theme/components/Opzione.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/slides/theme/components/PyRun.vue b/slides/theme/components/PyRun.vue new file mode 100644 index 0000000..58cab87 --- /dev/null +++ b/slides/theme/components/PyRun.vue @@ -0,0 +1,144 @@ + + + diff --git a/slides/theme/components/Risposta.vue b/slides/theme/components/Risposta.vue new file mode 100644 index 0000000..f6f6d5b --- /dev/null +++ b/slides/theme/components/Risposta.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/slides/theme/components/SqlRun.vue b/slides/theme/components/SqlRun.vue new file mode 100644 index 0000000..a3a3705 --- /dev/null +++ b/slides/theme/components/SqlRun.vue @@ -0,0 +1,198 @@ + + + diff --git a/slides/theme/components/StatoDb.vue b/slides/theme/components/StatoDb.vue new file mode 100644 index 0000000..4fc2ff4 --- /dev/null +++ b/slides/theme/components/StatoDb.vue @@ -0,0 +1,39 @@ + + + diff --git a/slides/theme/components/TabelleDb.vue b/slides/theme/components/TabelleDb.vue new file mode 100644 index 0000000..457b490 --- /dev/null +++ b/slides/theme/components/TabelleDb.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/slides/theme/composables/lezione.ts b/slides/theme/composables/lezione.ts new file mode 100644 index 0000000..5471887 --- /dev/null +++ b/slides/theme/composables/lezione.ts @@ -0,0 +1,120 @@ +/** + * Dati della lezione per la cornice delle slide: volume, titolo, sezione + * corrente, numero di pagina. Arrivano tutti dal frontmatter iniziale del mazzo + * (vedi slides/README.md) e dalla posizione della slide: chi scrive le slide + * non li ripete mai. + */ +import { computed } from 'vue'; +import { useSlideContext } from '@slidev/client'; + +/** Indirizzo pubblico del libro: il QR della slide finale punta lì. */ +export const SITO = 'https://python.rainbowbits.cloud'; + +/** + * Radice del sito da cui si servono Brython, sql.js e i dataset. Le + * presentazioni pubblicate stanno dentro il sito (/slides/…); in sviluppo + * static/ è la public di Vite (theme/vite.config.ts). In tutti e due i casi è + * la radice del dominio. + */ +export const RADICE_STATICI = '/'; + +export const VOLUMI: Record = + { + programmatore: { nome: 'Manuale del Programmatore', breve: 'Programmatore', n: 1 }, + artefice: { nome: 'Manuale dell’Artefice', breve: 'Artefice', n: 2 }, + archivista: { nome: 'Manuale dell’Archivista', breve: 'Archivista', n: 3 }, + apprendista: { nome: 'Biblioteca dell’Apprendista', breve: 'Apprendista', n: 4 }, + }; + +interface Configurazione { + title?: string; + volume?: string; + lezione?: string; + capitolo?: number | string; + sottotitolo?: string; +} + +interface Diapositiva { + no: number; + meta?: { + slide?: { + title?: string; + frontmatter?: Record; + }; + }; +} + +const due = (n: number) => String(n).padStart(2, '0'); + +export function useLezione() { + const { $slidev, $page, $frontmatter } = useSlideContext(); + + const config = computed(() => $slidev.configs as unknown as Configurazione); + const volume = computed(() => config.value.volume ?? 'programmatore'); + const infoVolume = computed( + () => VOLUMI[volume.value] ?? VOLUMI.programmatore, + ); + + /** `archivista/le-chiavi` → URL della lezione nel libro. */ + const urlLezione = computed(() => + config.value.lezione ? `${SITO}/${config.value.lezione}/` : SITO, + ); + + const diapositive = computed( + () => ($slidev.nav.slides as unknown as Diapositiva[]) ?? [], + ); + + /** Le slide `layout: sezione`, nell'ordine, con il loro numero. */ + const sezioni = computed(() => + diapositive.value + .filter((d) => d.meta?.slide?.frontmatter?.layout === 'sezione') + .map((d, i) => ({ + no: d.no, + numero: i + 1, + titolo: String( + d.meta?.slide?.frontmatter?.titolo ?? d.meta?.slide?.title ?? '', + ), + })), + ); + + /** La sezione in cui cade questa slide: l'ultima aperta prima di lei. */ + const sezione = computed(() => { + const qui = $page.value; + let corrente: (typeof sezioni.value)[number] | null = null; + for (const s of sezioni.value) if (s.no <= qui) corrente = s; + return corrente; + }); + + const etichettaSezione = computed(() => { + const esplicita = $frontmatter.sezione as string | undefined; + if (esplicita) return esplicita; + const s = sezione.value; + return s ? `${due(s.numero)} · ${s.titolo}` : 'Introduzione'; + }); + + const numeroSezione = computed(() => { + const qui = sezioni.value.find((s) => s.no === $page.value); + return qui ? due(qui.numero) : ''; + }); + + const kicker = computed(() => { + const parti = [infoVolume.value.nome]; + if (config.value.capitolo !== undefined) + parti.push(`Capitolo ${due(Number(config.value.capitolo))}`); + return parti.join(' · '); + }); + + return { + titolo: computed(() => config.value.title ?? ''), + sottotitolo: computed(() => config.value.sottotitolo ?? ''), + volume, + infoVolume, + kicker, + urlLezione, + etichettaSezione, + numeroSezione, + pagina: computed(() => due($page.value)), + totale: computed(() => due($slidev.nav.total)), + classeVolume: computed(() => `vol-${volume.value}`), + }; +} diff --git a/slides/theme/composables/uscita.ts b/slides/theme/composables/uscita.ts new file mode 100644 index 0000000..3596a17 --- /dev/null +++ b/slides/theme/composables/uscita.ts @@ -0,0 +1,44 @@ +/** + * L'output di un esecutore (, ) non deve mai uscire dalla + * slide: quando compare, il suo riquadro si limita allo spazio che resta fino + * al fondo del corpo, e quello che non ci sta scorre dentro il riquadro. Se lo + * spazio è troppo poco per leggere, il mazzo deve dare al risultato una slide + * sua (``): lo segnala `slides:verifica`. + */ +import { nextTick, onBeforeUnmount, watch, type Ref } from 'vue'; + +export function useSpazioUscita( + radice: Ref, + aperto: Ref, +) { + let osservatore: ResizeObserver | null = null; + + function misura() { + const el = radice.value; + const esiti = el?.querySelector('.pdb-run__esiti'); + const corpo = el?.closest('.pdb-corpo'); + if (!el || !esiti || !corpo || !corpo.clientHeight) return; + // I rettangoli sullo schermo risentono della scala con cui Slidev adatta + // la slide alla finestra: si riportano alle misure della slide. + const scala = corpo.getBoundingClientRect().height / corpo.clientHeight || 1; + const fondo = corpo.getBoundingClientRect().bottom; + const cima = esiti.getBoundingClientRect().top; + const spazio = Math.floor((fondo - cima) / scala) - 2; + esiti.style.maxHeight = `${Math.max(spazio, 60)}px`; + } + + watch(aperto, async (si) => { + osservatore?.disconnect(); + if (!si) return; + await nextTick(); + misura(); + const corpo = radice.value?.closest('.pdb-corpo'); + if (corpo) { + osservatore = new ResizeObserver(misura); + osservatore.observe(corpo); + } + }); + + onBeforeUnmount(() => osservatore?.disconnect()); + return { misura }; +} diff --git a/slides/theme/custom-nav-controls.vue b/slides/theme/custom-nav-controls.vue new file mode 100644 index 0000000..6e541f5 --- /dev/null +++ b/slides/theme/custom-nav-controls.vue @@ -0,0 +1,25 @@ + + + diff --git a/slides/theme/layouts/copertina.vue b/slides/theme/layouts/copertina.vue new file mode 100644 index 0000000..4e803f1 --- /dev/null +++ b/slides/theme/layouts/copertina.vue @@ -0,0 +1,38 @@ + + + diff --git a/slides/theme/layouts/cover.vue b/slides/theme/layouts/cover.vue new file mode 100644 index 0000000..6a3cda1 --- /dev/null +++ b/slides/theme/layouts/cover.vue @@ -0,0 +1,39 @@ + + + + diff --git a/slides/theme/layouts/default.vue b/slides/theme/layouts/default.vue new file mode 100644 index 0000000..7873d35 --- /dev/null +++ b/slides/theme/layouts/default.vue @@ -0,0 +1,6 @@ + + diff --git a/slides/theme/layouts/definizione.vue b/slides/theme/layouts/definizione.vue new file mode 100644 index 0000000..a92db8a --- /dev/null +++ b/slides/theme/layouts/definizione.vue @@ -0,0 +1,12 @@ + + + diff --git a/slides/theme/layouts/domanda.vue b/slides/theme/layouts/domanda.vue new file mode 100644 index 0000000..e2f89f6 --- /dev/null +++ b/slides/theme/layouts/domanda.vue @@ -0,0 +1,30 @@ + + + diff --git a/slides/theme/layouts/due-colonne.vue b/slides/theme/layouts/due-colonne.vue new file mode 100644 index 0000000..9f11f54 --- /dev/null +++ b/slides/theme/layouts/due-colonne.vue @@ -0,0 +1,39 @@ + + + diff --git a/slides/theme/layouts/end.vue b/slides/theme/layouts/end.vue new file mode 100644 index 0000000..1bb90a6 --- /dev/null +++ b/slides/theme/layouts/end.vue @@ -0,0 +1,28 @@ + + + diff --git a/slides/theme/layouts/enunciato.vue b/slides/theme/layouts/enunciato.vue new file mode 100644 index 0000000..ba65ab6 --- /dev/null +++ b/slides/theme/layouts/enunciato.vue @@ -0,0 +1,33 @@ + + + + diff --git a/slides/theme/layouts/epigrafe.vue b/slides/theme/layouts/epigrafe.vue new file mode 100644 index 0000000..bc25ba2 --- /dev/null +++ b/slides/theme/layouts/epigrafe.vue @@ -0,0 +1,17 @@ + + + diff --git a/slides/theme/layouts/figura.vue b/slides/theme/layouts/figura.vue new file mode 100644 index 0000000..8715400 --- /dev/null +++ b/slides/theme/layouts/figura.vue @@ -0,0 +1,97 @@ + + + diff --git a/slides/theme/layouts/fine.vue b/slides/theme/layouts/fine.vue new file mode 100644 index 0000000..1bb90a6 --- /dev/null +++ b/slides/theme/layouts/fine.vue @@ -0,0 +1,28 @@ + + + diff --git a/slides/theme/layouts/obiettivi.vue b/slides/theme/layouts/obiettivi.vue new file mode 100644 index 0000000..7d193f1 --- /dev/null +++ b/slides/theme/layouts/obiettivi.vue @@ -0,0 +1,13 @@ + + diff --git a/slides/theme/layouts/quiz.vue b/slides/theme/layouts/quiz.vue new file mode 100644 index 0000000..6db97fc --- /dev/null +++ b/slides/theme/layouts/quiz.vue @@ -0,0 +1,22 @@ + + + diff --git a/slides/theme/layouts/sezione.vue b/slides/theme/layouts/sezione.vue new file mode 100644 index 0000000..b89fe43 --- /dev/null +++ b/slides/theme/layouts/sezione.vue @@ -0,0 +1,23 @@ + + + diff --git a/slides/theme/layouts/sintesi.vue b/slides/theme/layouts/sintesi.vue new file mode 100644 index 0000000..7e73bd5 --- /dev/null +++ b/slides/theme/layouts/sintesi.vue @@ -0,0 +1,8 @@ + + diff --git a/slides/theme/package.json b/slides/theme/package.json new file mode 100644 index 0000000..40844a3 --- /dev/null +++ b/slides/theme/package.json @@ -0,0 +1,36 @@ +{ + "name": "slidev-theme-python-doesnt-byte", + "private": true, + "type": "module", + "description": "Tema Slidev delle presentazioni di Python Doesn’t Byte: stessa carta, stessi font e stessi colori del libro.", + "keywords": [ + "slidev-theme" + ], + "engines": { + "slidev": ">=53.0.0" + }, + "slidev": { + "colorSchema": "light", + "highlighter": "shiki", + "defaults": { + "aspectRatio": "16/9", + "canvasWidth": 980, + "colorSchema": "light", + "fonts": { + "provider": "none", + "sans": "Crimson Pro Variable", + "serif": "Crimson Pro Variable", + "mono": "Monaspace Neon" + }, + "lineNumbers": false, + "transition": "fade", + "mdc": true, + "drawings": { + "persist": false + }, + "exportFilename": "presentazione", + "contextMenu": false, + "routerMode": "hash" + } + } +} diff --git a/slides/theme/setup/docusaurus-shim.ts b/slides/theme/setup/docusaurus-shim.ts new file mode 100644 index 0000000..61fbef0 --- /dev/null +++ b/slides/theme/setup/docusaurus-shim.ts @@ -0,0 +1,12 @@ +/** + * Al posto di `@docusaurus/ExecutionEnvironment` (vedi vite.config.ts): le + * presentazioni girano solo nel browser, niente SSR. + */ +const ExecutionEnvironment = { + canUseDOM: true, + canUseEventListeners: true, + canUseIntersectionObserver: true, + canUseViewport: true, +}; + +export default ExecutionEnvironment; diff --git a/slides/theme/setup/mermaid.ts b/slides/theme/setup/mermaid.ts new file mode 100644 index 0000000..f5b8a3c --- /dev/null +++ b/slides/theme/setup/mermaid.ts @@ -0,0 +1,8 @@ +import { defineMermaidSetup } from '@slidev/types'; + +// Come nel libro: tema `neutral` e il mono Argon (docusaurus.config.ts). +export default defineMermaidSetup(() => ({ + theme: 'neutral', + fontFamily: "'Monaspace Argon', ui-monospace, SFMono-Regular, monospace", + fontSize: 16, +})); diff --git a/slides/theme/setup/preparser.ts b/slides/theme/setup/preparser.ts new file mode 100644 index 0000000..97412cc --- /dev/null +++ b/slides/theme/setup/preparser.ts @@ -0,0 +1,12 @@ +import { definePreparserSetup } from '@slidev/types'; +import { aggiungiSeguiti } from './seguiti'; + +// Le slide «risultato» dei blocchi con `seguito`: vedi setup/seguiti.ts. +export default definePreparserSetup(() => [ + { + name: 'pdb:seguiti', + transformRawLines(righe) { + aggiungiSeguiti(righe); + }, + }, +]); diff --git a/slides/theme/setup/seguiti.ts b/slides/theme/setup/seguiti.ts new file mode 100644 index 0000000..a31da56 --- /dev/null +++ b/slides/theme/setup/seguiti.ts @@ -0,0 +1,61 @@ +/** + * Le slide «risultato», generate da sole. + * + * Un blocco eseguibile con l'attributo `seguito` + * + * + * + * riceve subito dopo la sua slide una slide in più, con lo stesso blocco in + * modalità `risultato`: niente codice, solo l'esito, eseguito da solo (anche + * nel PDF). Il titolo è quello della slide del codice seguito da + * «: il risultato». Serve quando sotto il codice non c'è posto per leggere + * l'output: chi scrive il mazzo non copia niente, aggiunge una parola. + * + * Lavora sulle righe grezze del mazzo, prima che Slidev le divida in slide: + * la usano il preparser del tema (setup/preparser.ts) e scripts/slides.mjs, + * che deve contare le stesse slide che conta Slidev. + */ + +const APERTURA = /^\s*<(PyRun|SqlRun)\b[^>]*\bseguito\b[^>]*>\s*$/; + +/** Righe che chiudono una slide e ne aprono un'altra. */ +const separatore = (riga: string) => riga.trim() === '---'; + +export function aggiungiSeguiti(righe: string[]): void { + for (let i = 0; i < righe.length; i++) { + const apertura = APERTURA.exec(righe[i]); + if (!apertura) continue; + const tag = apertura[1]; + const chiusura = righe.findIndex((r, k) => k > i && r.trim() === ``); + if (chiusura < 0) continue; + + // Il titolo della slide: l'ultimo «# …» prima del blocco, fino al + // separatore che apre la slide. + let titolo = 'Il risultato'; + for (let k = i - 1; k >= 0 && !separatore(righe[k]); k--) { + const m = /^#\s+(.+)$/.exec(righe[k]); + if (m) { + titolo = `${m[1].replace(/[?.:!]+$/, '')}: il risultato`; + break; + } + } + + // Il blocco della slide nuova: stesso codice, modalità risultato, tutta + // la larghezza (senza `uscita`). Gli altri attributi restano, `errore` + // compreso: è lo stesso blocco. + const blocco = righe.slice(i, chiusura + 1); + blocco[0] = blocco[0] + .replace(/\s+seguito\b/, ' risultato') + .replace(/\s+uscita="[^"]*"/, ''); + // Nella slide del codice `seguito` resta: «Esegui» porta alla slide dopo. + + // La fine della slide: il primo separatore dopo il blocco (o la fine del + // file). Lì comincia la slide successiva, con o senza frontmatter. + let fine = righe.findIndex((r, k) => k > chiusura && separatore(r)); + if (fine < 0) fine = righe.length; + + const nuova = ['---', '', `# ${titolo}`, '', ...blocco, '']; + righe.splice(fine, 0, ...nuova); + i = fine + nuova.length - 1; + } +} diff --git a/slides/theme/setup/shiki.ts b/slides/theme/setup/shiki.ts new file mode 100644 index 0000000..2b6ef7b --- /dev/null +++ b/slides/theme/setup/shiki.ts @@ -0,0 +1,9 @@ +import { defineShikiSetup } from '@slidev/types'; + +// Lo stesso tema del codice nel libro (prism `github`, docusaurus.config.ts). +export default defineShikiSetup(() => ({ + themes: { + light: 'github-light', + dark: 'github-dark', + }, +})); diff --git a/slides/theme/styles/base.css b/slides/theme/styles/base.css new file mode 100644 index 0000000..1b4a178 --- /dev/null +++ b/slides/theme/styles/base.css @@ -0,0 +1,244 @@ +/* ─────────────── Tipografia di base ─────────────── + Una sola scala per tutte le slide (canvas 980 × 552). I layout non + ridefiniscono i corpi: scelgono solo dove mettere le cose. */ + +:root { + --pad-x: 56px; + --pad-alto: 26px; + --pad-basso: 22px; + --spazio: 18px; + --corpo: 22px; + --corpo-piccolo: 18px; + --corpo-etichetta: 11.5px; +} + +html, +body, +#app { + background: var(--at-bg); +} + +.slidev-layout.pdb { + position: relative; + height: 100%; + background: var(--at-bg); + color: var(--at-fg-body); + font-family: var(--font-body); + font-size: var(--corpo); + line-height: 1.42; + font-feature-settings: 'kern', 'liga', 'onum'; + -webkit-font-smoothing: antialiased; +} + +.slidev-layout.pdb h1, +.slidev-layout.pdb h2, +.slidev-layout.pdb h3 { + font-family: var(--font-display); + color: var(--at-titolo); + letter-spacing: -0.01em; + margin: 0; +} + +.slidev-layout.pdb h1 { + font-size: 38px; + line-height: 1.1; + font-weight: 600; +} + +.slidev-layout.pdb h1 + p, +.slidev-layout.pdb h1 + ul, +.slidev-layout.pdb h1 + ol, +.slidev-layout.pdb h1 + div, +.slidev-layout.pdb h1 + pre { + margin-top: var(--spazio); +} + +.slidev-layout.pdb h2 { + font-size: 28px; + line-height: 1.15; + font-weight: 600; +} + +.slidev-layout.pdb h3 { + font-size: 23px; + line-height: 1.2; + font-weight: 600; + margin-bottom: 6px; +} + +.slidev-layout.pdb p { + margin: 0 0 12px; +} + +.slidev-layout.pdb p:last-child { + margin-bottom: 0; +} + +.slidev-layout.pdb strong { + color: var(--at-fg-strong); + font-weight: 650; +} + +.slidev-layout.pdb em { + font-style: italic; +} + +.slidev-layout.pdb a { + color: var(--at-accent); + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 3px; + border: 0; +} + +/* Elenchi: puntino nel colore del volume, rientro fisso. */ +.slidev-layout.pdb ul, +.slidev-layout.pdb ol { + margin: 0 0 12px; + padding-left: 1.15em; +} + +/* Il reset di UnoCSS toglie lo stile delle liste: lo rimettiamo. */ +.slidev-layout.pdb ul { + list-style: disc; +} + +.slidev-layout.pdb ol { + list-style: decimal; + padding-left: 1.4em; +} + +.slidev-layout.pdb li { + margin: 0 0 8px; + padding-left: 0.2em; +} + +.slidev-layout.pdb li:last-child { + margin-bottom: 0; +} + +.slidev-layout.pdb ul > li::marker { + color: var(--at-accent); + content: '● '; + font-size: 0.6em; +} + +.slidev-layout.pdb ol > li::marker { + color: var(--at-accent); + font-family: var(--font-mono-ui); + font-size: 0.75em; + font-weight: 500; +} + +.slidev-layout.pdb li > ul, +.slidev-layout.pdb li > ol { + margin-top: 6px; + font-size: var(--corpo-piccolo); +} + +/* Codice in linea: come nel libro, chip grigio in Neon. */ +.slidev-layout.pdb :not(pre) > code { + font-family: var(--font-mono); + font-size: 0.78em; + padding: 0.12em 0.38em; + border-radius: var(--radius-xs); + background: var(--at-bg-chip); + color: var(--at-fg); + border: 1px solid var(--at-border); + /* Il codice in linea va a capo come il testo: un'espressione lunga dentro + una colonna o un callout non deve uscire dal riquadro. Il riquadro si + ripete su ogni riga. */ + white-space: normal; + overflow-wrap: anywhere; + -webkit-box-decoration-break: clone; + box-decoration-break: clone; +} + +.slidev-layout.pdb blockquote { + margin: 0; + padding: 4px 0 4px 18px; + border-left: 3px solid var(--at-accent-border); + color: var(--at-fg); + font-style: italic; +} + +/* Tabelle (risultati di query, confronti): righe sottili, intestazione mono. */ +.slidev-layout.pdb table { + border-collapse: collapse; + font-size: var(--corpo-piccolo); + background: var(--at-bg-panel); + border-radius: var(--radius-sm); + overflow: hidden; + box-shadow: var(--shadow-card); +} + +.slidev-layout.pdb th { + font-family: var(--font-mono-ui); + font-size: 13px; + font-weight: 500; + text-align: left; + color: var(--at-muted); + background: var(--at-bg-subtle); + padding: 8px 14px; + border-bottom: 1px solid var(--at-border-strong); +} + +.slidev-layout.pdb td { + padding: 7px 14px; + border-bottom: 1px solid var(--at-border); +} + +.slidev-layout.pdb tr:last-child td { + border-bottom: 0; +} + +/* Utilità condivise dai layout e utilizzabili nel markdown. */ +.pdb-etichetta { + font-family: var(--font-mono-ui); + font-size: var(--corpo-etichetta); + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--at-muted); +} + +.pdb-accento { + color: var(--at-accent); +} + +.pdb-tenue { + color: var(--at-muted); +} + +.pdb-piccolo { + font-size: var(--corpo-piccolo); +} + +/* Fascio di luce: la linea a tre colori delle card del libro. */ +.pdb-raggio { + height: 3px; + border-radius: 3px; + background: linear-gradient( + 90deg, + var(--raggio-1), + var(--raggio-2) 55%, + var(--raggio-3) + ); +} + +/* Gli elementi v-click nascosti non devono occupare un'ombra di spazio + diverso fra presentazione e PDF: Slidev li nasconde con opacità. */ +.slidev-vclick-hidden { + pointer-events: none; +} + +/* Tabelle una sotto l'altra: staccate; in una colonna, larghe quanto lei. */ +.slidev-layout.pdb table + table, +.slidev-layout.pdb .pdb-colonna table + p, +.slidev-layout.pdb .pdb-colonna p + table { + margin-top: 14px; +} + +.slidev-layout.pdb .pdb-colonna > table { + width: 100%; +} diff --git a/slides/theme/styles/codice.css b/slides/theme/styles/codice.css new file mode 100644 index 0000000..1dc53ef --- /dev/null +++ b/slides/theme/styles/codice.css @@ -0,0 +1,253 @@ +/* ─────────────── Codice ─────────────── + Blocchi come nel libro: carta bianca, filo sottile, Monaspace Neon. Il + corpo è fisso (14px, che proiettato resta grande) così due slide di codice + affiancate hanno la stessa grana; per blocchi lunghi si usa `{maxHeight:'…'}` di Slidev, non un + corpo più piccolo. */ + +.slidev-layout.pdb .slidev-code-wrapper { + margin: 12px 0 0; +} + +.slidev-layout.pdb .slidev-code { + --slidev-code-font-size: 14px; + --slidev-code-line-height: 1.5; + font-family: var(--font-mono) !important; + font-size: 14px !important; + line-height: 1.5 !important; + padding: 12px 18px !important; + border-radius: var(--radius-md) !important; + background: var(--at-bg-panel) !important; + box-shadow: var(--shadow-card); +} + +.slidev-layout.pdb .slidev-code code { + font-family: var(--font-mono); +} + +/* Le righe evidenziate ({2,4}) restano leggibili, le altre si attenuano. */ +.slidev-layout.pdb .slidev-code .line.dishonored { + opacity: 0.32; +} + +.slidev-layout.pdb .slidev-code .line.highlighted { + background: var(--at-accent-bg); + box-shadow: -18px 0 0 var(--at-accent-bg), 18px 0 0 var(--at-accent-bg); +} + +/* ── Esecutori in presentazione (, ) ── */ +.pdb-run { + margin-top: 12px; +} + +.pdb-run--destra.pdb-run--aperto { + display: grid; + grid-template-columns: 1.25fr 1fr; + gap: 18px; + align-items: start; +} + +.pdb-run__codice { + position: relative; + min-width: 0; +} + +.pdb-run__codice > .slidev-code-wrapper:first-child { + margin-top: 0; +} + +.pdb-run__barra { + display: flex; + justify-content: flex-end; + gap: 6px; + margin-top: 5px; +} + +.pdb-run__esegui, +.pdb-run__azzera { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 3px 10px; + border-radius: var(--radius-pill); + font-family: var(--font-mono-ui); + font-size: 11.5px; + font-weight: 500; + cursor: pointer; + border: 1px solid var(--at-accent-border); + background: var(--at-accent-chip); + color: var(--at-accent); + transition: background var(--dur-fast) var(--ease-smooth); +} + +.pdb-run__esegui:hover { + background: var(--at-accent-bg); +} + +.pdb-run__esegui:disabled { + opacity: 0.6; + cursor: progress; +} + +.pdb-run__azzera { + border-color: var(--at-border-strong); + background: var(--at-bg-panel); + color: var(--at-muted); +} + +.pdb-run__uscita { + margin-top: 10px; + padding: 10px 16px 12px; + border-radius: var(--radius-md); + background: #18181b; + color: #e4e4e7; +} + +.pdb-run--destra .pdb-run__uscita { + margin-top: 0; +} + +.pdb-run__titolo { + color: #a1a1aa; + margin-bottom: 6px; +} + +.pdb-run__testo { + margin: 0; + font-family: var(--font-mono); + font-size: 14px; + line-height: 1.5; + white-space: pre-wrap; + word-break: break-word; +} + +.pdb-run__stderr { + color: #fca5a5; +} + +.pdb-run__uscita--errore { + box-shadow: 0 0 0 1.5px #f87171 inset; +} + +.pdb-run__tabella + .pdb-run__tabella { + margin-top: 10px; +} + +.slidev-layout.pdb .pdb-run__tabella table { + background: transparent; + box-shadow: none; + font-family: var(--font-mono); + font-size: 13.5px; + color: #e4e4e7; +} + +.slidev-layout.pdb .pdb-run__tabella th { + background: rgba(255, 255, 255, 0.05); + color: #a1a1aa; + border-bottom-color: rgba(255, 255, 255, 0.14); + padding: 5px 12px; +} + +.slidev-layout.pdb .pdb-run__tabella td { + border-bottom-color: rgba(255, 255, 255, 0.07); + padding: 4px 12px; +} + +/* ── Componenti del libro montati nelle slide ── */ +.slidev-layout.pdb .pdb-figura-libro figure, +.slidev-layout.pdb .pdb-diagramma-libro figure { + margin: 0; +} + +.pdb-figura-libro, +.pdb-diagramma-libro { + width: 100%; +} + +/* La figura del libro ha i controlli in un bordo suo: nelle slide basta la + carta, senza margini verticali. */ +.slidev-layout.pdb .pdb-figura-libro > figure { + box-shadow: var(--shadow-card); + border-color: transparent; +} + +.slidev-layout.pdb .pdb-figura-libro button { + cursor: pointer; +} + +.pdb-errore { + color: var(--ko); + font-family: var(--font-mono-ui); + font-size: 13px; +} + +/* Mermaid (diagrammi UML): centrato, inchiostro del libro. */ +.slidev-layout.pdb .mermaid { + display: flex; + justify-content: center; + margin-top: 12px; +} + +/* ── Pannello «Database» di ── */ +.pdb-run__esiti { + display: grid; + gap: 10px; + align-content: start; + min-width: 0; + /* L'altezza massima la calcola composables/uscita.ts sullo spazio rimasto: + quello che non ci sta scorre qui dentro, non fuori dalla slide. */ + overflow: auto; + border-radius: var(--radius-md); +} + +.pdb-run__database { + padding: 10px 14px 12px; + border-radius: var(--radius-md); + background: var(--at-bg-panel); + box-shadow: var(--shadow-card); +} + +.pdb-run__titolo-db { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} + +.pdb-run__cambi { + color: var(--at-accent); + text-transform: none; + letter-spacing: 0; +} + +.pdb-run__esiti .pdb-run__uscita { + margin-top: 0; +} + +.pdb-run:not(.pdb-run--destra) .pdb-run__esiti { + margin-top: 10px; +} + +/* ── Slide «risultato»: niente codice, solo l'esito, eseguito da solo ── */ +.pdb-run--risultato .pdb-run__codice { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 10px; +} + +.pdb-run--risultato .pdb-run__codice > :not(.pdb-run__barra):not(.pdb-run__anteprima) { + display: none; +} + +.pdb-run--risultato .pdb-run__barra { + margin-top: 0; +} + +.pdb-run--risultato .pdb-run__esiti { + margin-top: 0; +} + +.pdb-run--risultato.pdb-run--destra.pdb-run--aperto { + display: block; +} diff --git a/slides/theme/styles/cornice.css b/slides/theme/styles/cornice.css new file mode 100644 index 0000000..adb8be0 --- /dev/null +++ b/slides/theme/styles/cornice.css @@ -0,0 +1,486 @@ +/* ─────────────── Cornice e layout ─────────────── + Griglia unica: testata 26px, corpo, piede 22px; margini laterali --pad-x. + Ogni layout di contenuto passa da , quindi titolo, testo e piede + cadono sempre sulle stesse linee. */ + +.pdb-cornice { + display: grid; + /* minmax(0, 1fr): il corpo non cresce oltre lo spazio. Un contenuto troppo + lungo straborda (e `slides:verifica` lo trova) invece di spingere il piede + fuori dalla slide. */ + grid-template-rows: auto minmax(0, 1fr) auto; + padding: var(--pad-alto) var(--pad-x) var(--pad-basso); +} + +.pdb-testata { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 14px; +} + +.pdb-testata__sezione { + display: inline-flex; + align-items: center; + gap: 9px; + color: var(--at-accent); +} + +.pdb-quadretto { + width: 8px; + height: 8px; + border-radius: 2px; + background: var(--at-accent); +} + +.pdb-corpo { + min-height: 0; + display: flex; + flex-direction: column; + justify-content: flex-start; + padding-top: 8px; +} + +.pdb-piede { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 10px; + border-top: 1px solid var(--at-border); + font-family: var(--font-mono-ui); + font-size: var(--corpo-etichetta); + color: var(--at-muted); +} + +.pdb-piede__titolo { + max-width: 75%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.pdb-piede__pagina { + font-variant-numeric: tabular-nums; +} + +/* ── Corpo centrato verticalmente (definizione, enunciato, quiz) ── */ +.pdb-corpo--centro { + justify-content: center; +} + +/* ── Due colonne ── */ +.pdb-colonne { + display: grid; + grid-template-columns: var(--colonne, 1fr 1fr); + gap: 32px; + align-items: start; + margin-top: var(--spazio); + min-height: 0; +} + +.pdb-colonne--centro { + align-items: center; +} + +.pdb-colonna { + min-width: 0; +} + +.pdb-colonna__etichetta { + display: block; + margin-bottom: 10px; +} + +/* ─────────────── Copertina ─────────────── */ +.pdb-copertina { + display: grid; + grid-template-rows: auto 1fr auto; + padding: 34px var(--pad-x) 30px; + overflow: hidden; +} + +.pdb-copertina__marchio { + display: flex; + align-items: center; + gap: 12px; + font-family: var(--font-mono-ui); + font-size: 13px; + font-weight: 500; + color: var(--at-fg); +} + +.pdb-copertina__marchio img { + width: 30px; + height: 30px; +} + +.pdb-copertina__centro { + display: flex; + flex-direction: column; + justify-content: center; + max-width: 700px; + position: relative; + z-index: 1; +} + +.pdb-copertina__kicker { + color: var(--at-accent); + margin-bottom: 18px; +} + +.slidev-layout.pdb .pdb-copertina__titolo { + font-size: 58px; + line-height: 1.02; + font-weight: 600; + letter-spacing: -0.02em; + color: var(--at-titolo); +} + +.pdb-copertina__sottotitolo { + margin-top: 20px; + font-size: 23px; + line-height: 1.38; + color: var(--at-muted); + max-width: 620px; +} + +.pdb-copertina__piede { + display: flex; + align-items: center; + gap: 18px; +} + +.pdb-copertina__piede .pdb-raggio { + flex: 1; +} + +/* Il numero della lezione, grande e a filo, sul lato destro. */ +.pdb-copertina__numero { + position: absolute; + right: -18px; + bottom: 36px; + font-family: var(--font-display); + font-size: 340px; + line-height: 0.8; + font-weight: 600; + letter-spacing: -0.04em; + color: transparent; + -webkit-text-stroke: 1.5px var(--at-accent-border); + user-select: none; + pointer-events: none; +} + +/* ─────────────── Sezione ─────────────── */ +.pdb-sezione { + display: grid; + grid-template-rows: 1fr auto; + padding: 0 var(--pad-x) 30px; +} + +.pdb-sezione__centro { + display: flex; + flex-direction: column; + justify-content: center; + max-width: 760px; +} + +.pdb-sezione__numero { + font-family: var(--font-mono-ui); + font-size: 15px; + font-weight: 500; + letter-spacing: 0.08em; + color: var(--at-accent); + margin-bottom: 14px; +} + +.slidev-layout.pdb.pdb-sezione h1 { + font-size: 52px; + line-height: 1.05; + letter-spacing: -0.02em; +} + +.slidev-layout.pdb.pdb-sezione h1 + p { + margin-top: 18px; + font-size: 23px; + color: var(--at-muted); +} + +/* ─────────────── Epigrafe ─────────────── */ +.pdb-epigrafe { + display: flex; + align-items: center; + justify-content: center; + padding: 0 calc(var(--pad-x) + 40px); +} + +.pdb-epigrafe__carta { + position: relative; + padding: 44px 52px 36px; + border-radius: var(--radius-lg); + background: var(--at-bg-panel); + box-shadow: var(--shadow-card); +} + +.pdb-epigrafe__carta::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 1.5px; + background: linear-gradient( + 120deg, + var(--raggio-1), + var(--raggio-2) 50%, + var(--raggio-3) + ); + -webkit-mask: + linear-gradient(#000 0 0) content-box, + linear-gradient(#000 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; +} + +.pdb-epigrafe__segno { + position: absolute; + top: -8px; + left: 26px; + font-family: var(--font-display); + font-size: 150px; + line-height: 1; + color: var(--at-accent-chip); + user-select: none; +} + +.pdb-epigrafe__testo { + position: relative; + font-size: 29px; + line-height: 1.35; + font-style: italic; + color: var(--at-fg); +} + +.pdb-epigrafe__autore { + display: inline-block; + margin-top: 22px; + padding: 5px 12px; + border-radius: var(--radius-pill); + background: var(--at-accent-chip); + border: 1px solid var(--at-accent-border); + font-family: var(--font-mono-ui); + font-size: 12.5px; + color: var(--at-accent); +} + +/* ─────────────── Obiettivi ─────────────── */ +.pdb-obiettivi { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + margin-top: var(--spazio); + align-items: stretch; +} + +.pdb-obiettivi .pdb-callout { + margin: 0; + height: 100%; +} + +/* ─────────────── Definizione ─────────────── */ +.pdb-definizione { + max-width: 780px; + margin: 0 auto; + width: 100%; +} + +.pdb-definizione .pdb-callout__corpo { + font-size: 24px; + line-height: 1.42; +} + +/* ─────────────── Figura ─────────────── */ +.pdb-figura__riga { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; +} + +.pdb-figura__riga--lato { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); + grid-template-rows: minmax(0, 1fr); + gap: 28px; + align-items: stretch; +} + +.pdb-figura__riga--destra .pdb-figura__lato { + order: 2; +} + +.pdb-figura__lato { + align-self: center; + margin-top: 12px; +} + +.pdb-figura { + display: flex; + justify-content: center; + align-items: center; + flex: 1; + min-height: 0; + margin-top: 12px; + overflow: hidden; +} + +.pdb-figura__adatta { + flex: none; + width: 100%; + transform-origin: center center; +} + +.pdb-figura__didascalia { + margin-top: 8px; + font-size: var(--corpo-piccolo); + color: var(--at-muted); + text-align: center; +} + +/* ─────────────── Domanda e risposta ─────────────── */ +.pdb-domanda--affiancata { + display: grid; + grid-template-columns: 1.35fr 1fr; + /* Niente spazio fra le righe: la colonna destra ne attraversa dieci + implicite, e ognuna aggiungerebbe il suo. Lo spazio sotto il titolo lo + dà il margine del titolo. */ + gap: 0 28px; + align-items: start; +} + +.slidev-layout.pdb .pdb-domanda--affiancata h1 { + margin-bottom: 12px; +} + +/* Il titolo resta a tutta larghezza, sopra le due colonne. */ +.pdb-domanda--affiancata .pdb-domanda__domanda { + display: contents; +} + +.pdb-domanda--affiancata .pdb-domanda__domanda > h1 { + grid-column: 1 / -1; +} + +.pdb-domanda--affiancata .pdb-domanda__domanda > :not(h1) { + grid-column: 1; +} + +.pdb-domanda--affiancata .pdb-domanda__lato { + grid-column: 2; + grid-row: 2 / span 10; + padding-top: 12px; +} + +.pdb-domanda--affiancata .pdb-domanda__invito { + display: flex; + align-items: flex-start; + line-height: 1.45; +} + +.pdb-domanda__invito { + display: inline-flex; + align-items: center; + gap: 8px; + margin-top: 4px; + font-family: var(--font-mono-ui); + font-size: 12.5px; + color: var(--at-accent); +} + +.pdb-domanda__risposta { + margin-top: 14px; +} + +/* ─────────────── Quiz ─────────────── */ +.pdb-quiz { + counter-reset: opzione; +} + +.slidev-layout.pdb .pdb-quiz > h1 { + font-size: 27px; + line-height: 1.28; + font-weight: 500; + margin-bottom: 20px; +} + +.pdb-quiz .pdb-opzione + .pdb-opzione { + margin-top: 10px; +} + +/* ─────────────── Sintesi ─────────────── */ +.pdb-sintesi .pdb-callout { + margin-top: var(--spazio); +} + +.pdb-sintesi li { + font-size: 19.5px; +} + +/* ─────────────── Fine ─────────────── */ +.pdb-fine { + display: grid; + grid-template-columns: 1fr auto; + grid-template-rows: 1fr auto; + gap: 0 48px; + padding: 40px var(--pad-x) 30px; +} + +.pdb-fine__testo { + display: flex; + flex-direction: column; + justify-content: center; +} + +.pdb-fine__testo h1 { + margin-bottom: 18px; +} + +.pdb-fine__qr { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12px; +} + +.pdb-fine__qr-carta { + padding: 14px; + background: #fff; + border-radius: var(--radius-md); + box-shadow: var(--shadow-card); +} + +.pdb-fine__qr-carta svg { + display: block; + width: 168px; + height: 168px; +} + +.pdb-fine__url { + font-family: var(--font-mono-ui); + font-size: 12px; + color: var(--at-muted); + text-align: center; + max-width: 220px; + word-break: break-all; +} + +.pdb-fine__piede { + grid-column: 1 / -1; + display: flex; + align-items: center; + gap: 18px; + padding-top: 24px; +} + +.pdb-fine__piede .pdb-raggio { + flex: 1; +} diff --git a/slides/theme/styles/index.ts b/slides/theme/styles/index.ts new file mode 100644 index 0000000..88f02c1 --- /dev/null +++ b/slides/theme/styles/index.ts @@ -0,0 +1,15 @@ +// Font self-hostati come nel sito (src/fonts.ts): niente Google Fonts, le +// presentazioni funzionano anche offline e nel PDF. +import '@fontsource-variable/crimson-pro'; +import '@fontsource-variable/crimson-pro/wght-italic.css'; +import '@fontsource/monaspace-neon/400.css'; +import '@fontsource/monaspace-neon/500.css'; +import '@fontsource/monaspace-argon/400.css'; +import '@fontsource/monaspace-argon/500.css'; +import '@fontsource/monaspace-radon/400.css'; + +import './token.css'; +import './base.css'; +import './cornice.css'; +import './codice.css'; +import './stampa.css'; diff --git a/slides/theme/styles/stampa.css b/slides/theme/styles/stampa.css new file mode 100644 index 0000000..6e92e71 --- /dev/null +++ b/slides/theme/styles/stampa.css @@ -0,0 +1,39 @@ +/* ─────────────── PDF ─────────────── + `slidev export` (pagina /print, html.print) e l'esportazione dal browser + (@media print): niente pulsanti né output degli esecutori, perché nel PDF + conta il codice, e niente comandi del player delle figure. */ + +html.print .pdb-run__barra, +html.print .pdb-run__esiti, +html.print .pdb-run__uscita, +html.print .pdb-figura-libro button { + display: none !important; +} + +html.print .pdb-run--destra { + display: block; +} + +@media print { + .pdb-run__barra, + .pdb-run__esiti, + .pdb-run__uscita, + .pdb-figura-libro button { + display: none !important; + } + + .pdb-run--destra { + display: block; + } +} + +/* Nella slide «risultato» l'esito è il contenuto: nel PDF resta. */ +html.print .pdb-run--risultato .pdb-run__esiti { + display: grid !important; +} + +@media print { + .pdb-run--risultato .pdb-run__esiti { + display: grid !important; + } +} diff --git a/slides/theme/styles/token.css b/slides/theme/styles/token.css new file mode 100644 index 0000000..54f0809 --- /dev/null +++ b/slides/theme/styles/token.css @@ -0,0 +1,129 @@ +/* ─────────────── Token del tema ─────────────── + Gli stessi valori di src/css/custom.css (tema Atmospheric, modalità chiara) + e delle figure: una slide e la pagina del libro devono sembrare lo stesso + oggetto. Se cambia un colore nel libro, va cambiato anche qui. + + I nomi --at-*, --radius-*, --font-* sono quelli del sito di proposito: i + componenti React del libro montati nelle slide (Figura, DiagrammaER) li + leggono così come sono. */ + +:root { + --font-display: + 'Crimson Pro Variable', 'Crimson Pro', 'Iowan Old Style', Georgia, serif; + --font-body: + 'Crimson Pro Variable', 'Crimson Pro', 'Iowan Old Style', Georgia, serif; + --font-mono: 'Monaspace Neon', ui-monospace, SFMono-Regular, Menlo, monospace; + --font-mono-comment: + 'Monaspace Radon', 'Monaspace Neon', ui-monospace, monospace; + --font-mono-ui: + 'Monaspace Argon', 'Monaspace Neon', ui-monospace, SFMono-Regular, monospace; + + --ease-smooth: cubic-bezier(0.22, 1, 0.36, 1); + --dur-fast: 150ms; + --dur-normal: 200ms; + + --radius-xs: 6px; + --radius-sm: 10px; + --radius-md: 14px; + --radius-lg: 18px; + --radius-pill: 999px; + + --at-bg: #fafaf9; + --at-bg-panel: #ffffff; + --at-bg-subtle: rgba(0, 0, 0, 0.025); + --at-bg-chip: rgba(0, 0, 0, 0.04); + --at-fg: #27272a; + --at-fg-strong: #09090b; + --at-fg-body: #3f3f46; + --at-muted: #6b6b73; + --at-faint: #a1a1aa; + --at-border: rgba(0, 0, 0, 0.08); + --at-border-strong: rgba(0, 0, 0, 0.12); + --at-titolo: #011d4a; + + --ifm-background-color: var(--at-bg); + + --shadow-card: + 0 1px 2px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.02), + 0 0 0 0.5px rgba(0, 0, 0, 0.08); + + /* Callout: palette di src/css/custom.css (modalità chiara). */ + --callout-prereq-bg: oklch(96% 0.025 240); + --callout-prereq-fg: oklch(48% 0.16 240); + --callout-learn-bg: oklch(96% 0.025 350); + --callout-learn-fg: oklch(52% 0.18 350); + --callout-exercise-bg: oklch(95% 0.03 320); + --callout-exercise-fg: oklch(52% 0.2 320); + --callout-definition-bg: oklch(95% 0.04 70); + --callout-definition-fg: oklch(48% 0.13 70); + --callout-history-bg: oklch(94% 0.045 35); + --callout-history-fg: oklch(48% 0.16 35); + --callout-code-bg: oklch(95% 0 0); + --callout-code-fg: oklch(40% 0 0); + --callout-cleancode-bg: oklch(95% 0.025 270); + --callout-cleancode-fg: oklch(48% 0.18 270); + --callout-nutshell-bg: oklch(95% 0.03 55); + --callout-nutshell-fg: oklch(46% 0.09 55); + --callout-mindmap-bg: oklch(95% 0.04 175); + --callout-mindmap-fg: oklch(46% 0.13 175); + --callout-info-bg: oklch(95% 0.04 145); + --callout-info-fg: oklch(46% 0.14 145); + --callout-tip-bg: oklch(96% 0.05 90); + --callout-tip-fg: oklch(52% 0.15 85); + --callout-warning-bg: oklch(94% 0.05 55); + --callout-warning-fg: oklch(56% 0.17 50); + --callout-danger-bg: oklch(93% 0.04 15); + --callout-danger-fg: oklch(48% 0.22 18); + --callout-note-bg: oklch(95% 0 0); + --callout-note-fg: oklch(45% 0 0); + + --ok: #127538; + --ok-bg: rgba(18, 117, 56, 0.08); + --ko: #b91c1c; + --ko-bg: rgba(185, 28, 28, 0.07); +} + +/* ─────────────── Accento del volume ─────────────── + Le quattro palette delle card dei volumi in home (src/components/VolumeCard): + blu, rosa, ambra, verde. Ogni slide prende quella del volume dichiarato nel + frontmatter iniziale del mazzo (`volume:`). */ + +.pdb { + --at-accent: #0270ad; + --at-accent-bg: rgba(2, 132, 199, 0.06); + --at-accent-chip: rgba(2, 132, 199, 0.08); + --at-accent-border: rgba(2, 132, 199, 0.22); + --raggio-1: #0891b2; + --raggio-2: #2563eb; + --raggio-3: #7c3aed; +} + +.pdb.vol-artefice { + --at-accent: #a21caf; + --at-accent-bg: rgba(236, 72, 153, 0.06); + --at-accent-chip: rgba(236, 72, 153, 0.08); + --at-accent-border: rgba(236, 72, 153, 0.24); + --raggio-1: #db2777; + --raggio-2: #7c3aed; + --raggio-3: #4f46e5; +} + +.pdb.vol-archivista { + --at-accent: #b45309; + --at-accent-bg: rgba(245, 158, 11, 0.07); + --at-accent-chip: rgba(245, 158, 11, 0.1); + --at-accent-border: rgba(245, 158, 11, 0.3); + --raggio-1: #f59e0b; + --raggio-2: #fb923c; + --raggio-3: #ef4444; +} + +.pdb.vol-apprendista { + --at-accent: #15803d; + --at-accent-bg: rgba(34, 197, 94, 0.07); + --at-accent-chip: rgba(34, 197, 94, 0.1); + --at-accent-border: rgba(34, 197, 94, 0.28); + --raggio-1: #10b981; + --raggio-2: #14b8a6; + --raggio-3: #06b6d4; +} diff --git a/slides/theme/vite.config.ts b/slides/theme/vite.config.ts new file mode 100644 index 0000000..8d18cca --- /dev/null +++ b/slides/theme/vite.config.ts @@ -0,0 +1,81 @@ +/** + * Vite del tema: Slidev la unisce alla propria (resolveViteConfigs legge il + * vite.config di ogni root, temi compresi). + * + * Le presentazioni riusano i componenti React del libro (, + * ) e i ponti degli esecutori (Brython, sql.js): i file restano in + * src/ e si importano con lo stesso alias `@site` di Docusaurus. Per farli + * girare servono tre accorgimenti: + * + * 1. i `.tsx` del libro sono JSX di React: il plugin vue-jsx di Slidev non + * deve toccarli (lo escludiamo del tutto: il tema non ha file JSX), e li + * compila `jsxDelLibro` qui sotto; + * 2. React esiste una volta sola, quello del sito (`dedupe`); + * 3. `@docusaurus/ExecutionEnvironment`, l'unico import Docusaurus della + * catena di pyBoot.ts, punta a uno shim. + * + * In sviluppo la cartella static/ del sito è la public di Vite: Brython, la + * stdlib, sql.js, i dataset e le icone dei callout si servono agli stessi + * percorsi del sito pubblicato (/brython/…, /sql-datasets/…). In build no: le + * presentazioni vivono dentro il sito (/slides/…) e quei file ci sono già. + */ +import { fileURLToPath } from 'node:url'; +import { defineConfig, transformWithOxc, type Plugin } from 'vite'; + +const radice = fileURLToPath(new URL('../../', import.meta.url)); +const shim = fileURLToPath( + new URL('./setup/docusaurus-shim.ts', import.meta.url), +); + +/** + * Compila i `.tsx` del libro con il runtime automatico di React. Non basta + * l'opzione `oxc` di Vite: vue-jsx (dentro Slidev) la restringe ai `.ts`, + * perché i `.tsx` li vorrebbe compilare lui come JSX di Vue; e la + * tsconfig.json del sito dice `jsx: preserve`, perché a Docusaurus il JSX + * serve intatto. Questo plugin arriva prima di tutti e due. + */ +function jsxDelLibro(): Plugin { + return { + name: 'pdb:jsx-del-libro', + enforce: 'pre', + async transform(codice, id) { + const file = id.split('?')[0]; + if (!file.endsWith('.tsx') || file.includes('/node_modules/')) return null; + const esito = await transformWithOxc(codice, file, { + lang: 'tsx', + tsconfig: false, + jsx: { runtime: 'automatic', importSource: 'react' }, + }); + return { code: esito.code, map: esito.map }; + }, + }; +} + +export default defineConfig(({ command }) => ({ + resolve: { + alias: [ + { find: '@docusaurus/ExecutionEnvironment', replacement: shim }, + { find: /^@site\//, replacement: radice }, + ], + dedupe: ['react', 'react-dom'], + }, + plugins: [jsxDelLibro()], + optimizeDeps: { + include: [ + 'react', + 'react-dom/client', + 'react/jsx-runtime', + 'roughjs', + 'qrcode', + '@fortawesome/react-fontawesome', + '@fortawesome/free-solid-svg-icons', + ], + }, + server: { + fs: { allow: [radice] }, + }, + ...(command === 'serve' ? { publicDir: `${radice}static` } : {}), + slidev: { + vuejsx: { exclude: [/.*/] }, + }, +})); diff --git a/src/components/PresentaButton/index.tsx b/src/components/PresentaButton/index.tsx new file mode 100644 index 0000000..dcd2d2c --- /dev/null +++ b/src/components/PresentaButton/index.tsx @@ -0,0 +1,51 @@ +import type { ReactNode } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPersonChalkboard } from '@fortawesome/free-solid-svg-icons'; +import { useDoc, useDocsVersion } from '@docusaurus/plugin-content-docs/client'; +import { usePluginData } from '@docusaurus/useGlobalData'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import styles from './styles.module.css'; + +interface Presentazione { + url: string; +} + +interface PresentazioniData { + presentazioni?: Record; +} + +// Gate come CopyPageButton: le briciole compaiono anche nelle pagine category, +// che non hanno DocProvider, e lì useDoc() lancia. +export default function PresentaButton(): ReactNode { + let id: string; + try { + // eslint-disable-next-line react-hooks/rules-of-hooks + id = useDoc().metadata.id; + } catch { + return null; + } + return ; +} + +function Pulsanti({ id }: { id: string }): ReactNode { + const { pluginId } = useDocsVersion(); + const data = usePluginData('presentazioni') as PresentazioniData | undefined; + const voce = data?.presentazioni?.[`${pluginId}/${id}`]; + const url = useBaseUrl(voce?.url ?? '/'); + if (!voce) return null; + // Un e non : le slide sono un'altra applicazione, servita da + // static/slides/, e il router di Docusaurus non le conosce. + return ( + // eslint-disable-next-line @docusaurus/no-html-links + + + Presenta + + ); +} diff --git a/src/components/PresentaButton/styles.module.css b/src/components/PresentaButton/styles.module.css new file mode 100644 index 0000000..2994d0f --- /dev/null +++ b/src/components/PresentaButton/styles.module.css @@ -0,0 +1,44 @@ +/* Pulsante «Presenta»: la stessa pillola di «Spiegamelo facile» + (src/components/CopyPageButton). */ +.presenta { + display: inline-flex; + align-items: center; + gap: 7px; + padding: 6px 12px; + font-family: var(--font-mono-ui); + font-size: 12px; + font-weight: 500; + line-height: 1; + color: var(--at-fg-body); + background: var(--at-bg-subtle); + border: 1px solid var(--at-border); + border-radius: var(--radius-pill); + text-decoration: none; + transition: + color var(--dur-fast) var(--ease-out), + border-color var(--dur-fast) var(--ease-out), + background var(--dur-fast) var(--ease-out); +} + +.presenta:hover { + color: var(--at-accent); + border-color: var(--at-accent); + background: var(--at-accent-bg); + text-decoration: none; +} + +.presenta:focus-visible { + outline: 2px solid var(--at-accent); + outline-offset: 2px; +} + +.icona { + width: 14px; + height: 14px; + color: var(--at-muted); + transition: color var(--dur-fast) var(--ease-out); +} + +.presenta:hover .icona { + color: var(--at-accent); +} diff --git a/src/theme/DocBreadcrumbs/index.tsx b/src/theme/DocBreadcrumbs/index.tsx index 749efef..4a4169d 100644 --- a/src/theme/DocBreadcrumbs/index.tsx +++ b/src/theme/DocBreadcrumbs/index.tsx @@ -1,7 +1,7 @@ /** * Swizzle di @theme/DocBreadcrumbs (eject). * - * Due differenze rispetto all'originale: + * Tre differenze rispetto all'originale: * 1. dopo la casetta (che punta alla home del sito) inseriamo una briciola col * NOME DEL VOLUME corrente, linkata all'indice del volume. Senza, in * Biblioteca dell'Apprendista la prima briciola è il volume-sorgente (es. @@ -10,6 +10,8 @@ * 2. nella sola Biblioteca dell'Apprendista la prima briciola della sidebar (il * volume-sorgente) è accorciata a «Vol. N»: affianco a «Biblioteca * dell'Apprendista» due titoli di libro interi confondono. + * 3. a destra, i pulsanti della lezione: «Presenta» (se la lezione ha le + * slide) e «Spiegamelo facile». * * Il resto è copia fedele dell'originale: ricontrollare a ogni upgrade di * Docusaurus (vedi memoria upgrade_risks). @@ -30,6 +32,7 @@ import DocBreadcrumbsStructuredData from '@theme/DocBreadcrumbs/StructuredData'; import { VOLUME_LABELS, volumeShortByLabel } from '@site/src/lib/docResolve'; import CopyPageButton from '@site/src/components/CopyPageButton'; +import PresentaButton from '@site/src/components/PresentaButton'; import styles from './styles.module.css'; @@ -137,8 +140,10 @@ export default function DocBreadcrumbs(): ReactNode { ); })} - {/* Self-gate: null fuori da una lezione (pagine category). */} + {/* Self-gate: null fuori da una lezione (pagine category) e, per + «Presenta», nelle lezioni senza slide (slides/decks/). */}
+
diff --git a/src/theme/DocBreadcrumbs/styles.module.css b/src/theme/DocBreadcrumbs/styles.module.css index 353b655..415569c 100644 --- a/src/theme/DocBreadcrumbs/styles.module.css +++ b/src/theme/DocBreadcrumbs/styles.module.css @@ -32,6 +32,12 @@ .action { flex-shrink: 0; + /* «Presenta» sopra «Spiegamelo facile»: impilati occupano la larghezza di + un pulsante solo, e le briciole restano leggibili. */ + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 6px; } /* Mobile: niente affiancamento. Tenere il chip in riga obbligherebbe le @@ -48,4 +54,10 @@ flex: 0 1 auto; width: 100%; } + + .action { + flex-direction: row; + flex-wrap: wrap; + align-items: flex-start; + } } diff --git a/tsconfig.json b/tsconfig.json index 314eab8..4990e2b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,5 +3,7 @@ "extends": "@docusaurus/tsconfig", "compilerOptions": { "baseUrl": "." - } + }, + // Le presentazioni hanno un progetto e dipendenze loro (slides/). + "exclude": ["slides", "build", "node_modules"] }