start of ai-based development

This commit is contained in:
2026-07-06 10:44:32 +02:00
parent 6573ec6ac1
commit e50099a0c7
10 changed files with 1203 additions and 26 deletions
+168
View File
@@ -0,0 +1,168 @@
---
name: elixforms-common-library
description: >
Documentazione della libreria condivisa common/ del progetto elixForms.
Descrive i componenti ElixForms, il pattern di form field factory,
le interfacce TypeScript, i tipi, e l'helper per i query parameter.
Attiva questa skill quando lavori sui componenti condivisi, crei nuovi tipi di campo,
modifichi la gestione dello stato del form, o integri con la piattaforma elixForms.
---
# Libreria Common elixForms
## Panoramica
La cartella `react/common/` contiene la libreria condivisa di componenti React/TypeScript usata da tutte le pagine custom. È basata su **Fluent UI React v8** e implementa un sistema di form con campi dinamici.
## Architettura dei Componenti
```
ElixFormsComponentAbstract (abstract class, extends React.Component)
└── ElixFormsComponent (classe concreta, exported as ElixFormsReact)
└── Usata nelle pagine come `new ElixForms.ElixFormsReact(props)`
```
### Gerarchia delle classi
1. **`ElixFormsComponentAbstract`** (`ElixFormsComponentAbstract.tsx`)
- Classe astratta che estende `React.Component<IElixFormsComponentProperties, IElixFormsComponentFormState>`
- Contiene tutta la logica di creazione dei campi form (factory methods)
- Gestisce lo state del form (`formData: { [key: string]: any }`)
- Implementa `render()` che genera `<form>` con action POST verso elixForms
2. **`ElixFormsComponent`** (`ElixFormsComponent.tsx`)
- Classe concreta che estende `ElixFormsComponentAbstract`
- Esportata come `ElixFormsReact` per uso nelle pagine
- Attualmente vuota (la logica è nell'abstract)
## Interfacce TypeScript
### `IElixFormsComponentProperties` (Props)
```typescript
interface IElixFormsComponentProperties {
description: string;
isDarkTheme: boolean;
environmentMessage: string;
hasTeamsContext: boolean;
userDisplayName: string;
additionalFieldsJson?: string; // JSON schema per campi dinamici
customFormFieldsConfiguration?: ( // Callback con factory per campi custom
formFieldFactory: IElixFormsComponentCustomFormFieldFactory
) => JSX.Element;
}
```
### `IElixFormsComponentFormState` (State)
```typescript
interface IElixFormsComponentFormState {
formData: { [key: string]: any };
}
```
### `IElixFormsComponentCustomFormFieldFactory` (Form Field Factory)
```typescript
type IElixFormsComponentCustomFormFieldFactory = {
createBooleanInput(name: string, label: string, required?: boolean): JSX.Element;
createCheckboxInput(name: string, label: string, options: ElixFormsCheckboxOption[]): JSX.Element;
createDropdownInput(name: string, label: string, options: ElixFormsDropdownOption[], required?: boolean): JSX.Element;
createHiddenInput(name: string): JSX.Element;
createNumberInput(name: string, label: string, required?: boolean): JSX.Element;
createRadioInput(name: string, label: string, options: ElixFormsRadioOption[], required?: boolean): JSX.Element;
createTextInput(name: string, label: string, required?: boolean): JSX.Element;
createTextAreaInput(name: string, label: string, required?: boolean): JSX.Element;
};
```
### Tipi per le Opzioni (`ElixFormsTypes.tsx`)
```typescript
type ElixFormsCheckboxOption = { value: number; label: string; required?: boolean };
type ElixFormsDropdownOption = { value: number; label: string };
type ElixFormsRadioOption = { value: number; label: string };
```
## Form Field Factory Pattern
Le pagine usano il pattern **Custom Form Field Factory** per dichiarare i campi del form:
```jsx
// In App.jsx della pagina
var myElixFormsReact = new ElixForms.ElixFormsReact({
// ...props...
customFormFieldsConfiguration: (formFieldFactory) => (
<>
{formFieldFactory.createDropdownInput("COL0015", "Goals", [...options], true)}
{formFieldFactory.createTextInput("COL0002", "Campo STRING", true)}
{formFieldFactory.createTextAreaInput("COL0003", "Campo TEXTAREA", true)}
{formFieldFactory.createBooleanInput("COL0004", "Campo BOOLEAN", true)}
{formFieldFactory.createRadioInput("COL0005", "Campo RADIO", [...options], true)}
{formFieldFactory.createCheckboxInput("COL0006", "Campo CHECKBOX", [...options])}
</>
)
});
```
### Tipi di Campo Supportati
| Metodo Factory | Componente FluentUI | HTML Output |
|---|---|---|
| `createTextInput` | `TextField` | Text input |
| `createTextAreaInput` | `TextField` (multiline) | Textarea |
| `createNumberInput` | `TextField` (type=number) | Number input |
| `createBooleanInput` | `ChoiceGroup` (Sì/No) | Radio buttons |
| `createRadioInput` | `ChoiceGroup` | Radio buttons |
| `createCheckboxInput` | `Checkbox` + hidden input | Checkboxes |
| `createDropdownInput` | `Dropdown` | Select dropdown |
| `createHiddenInput` | Native `<input type="text">` | Hidden field |
## ElixFormsElement (Wrapper Layout)
`ElixFormsElement` è una classe che incapsula label + input in un layout a due colonne compatibile con il design system elixForms:
```tsx
class ElixFormsElement {
constructor(labelElement: ReactElement, inputElement: ReactElement, separator?: string);
render(): JSX.Element;
}
```
Genera markup con classi CSS specifiche di elixForms:
- `.iuFieldContainer` → container del campo
- `.attrDisplay_left`, `.attrDisplay_label` → colonna label
- `.attrDisplay_right`, `.attrDisplay_input` → colonna input
## QueryParamHelper
Utility statica per leggere parametri dalla URL corrente:
```typescript
class QueryParamHelper {
static getCheckedFromQuery(paramName: string): number[]; // Per checkbox (valori separati da virgola)
static getOptionFromQuery(paramName: string): number | undefined; // Per radio/dropdown
static getBooleanFromQuery(paramName: string): boolean | undefined;// Per boolean
static getDecodedTextFromQuery(paramName: string): string | undefined; // Per testo (URI decoded)
}
```
Usato per pre-popolare i campi form con i valori passati via query string dalla piattaforma elixForms.
## Campi Obbligatori elixForms
Il componente abstract gestisce automaticamente questi parametri come hidden inputs:
- `RWE2_MODULE_ID`
- `RWE2_REQUEST_ID`
- `custom-workflow-back-url`
- `custom-workflow-generic-id`
- `custom-workflow-current-tabrel-genid`
- `custom-workflow-source-field`
- `crc`
- `MODULE_TESTMODE_KEY`
- `ELANG`
## Note Importanti
- Lo **state management** attuale usa `React.Component` class-based con `this.state` e `this.setState`
- I checkbox gestiscono un `Map<string, number[]>` interno per tracciare i valori selezionati
- Il form fa POST a `https://procedure.unipr.it/rwe2/ComeBackToElixAndSave`
- L'encoding charset è `ISO-8859-1` per compatibilità con il backend
- La libreria usa **SCSS modules** per gli stili (`ElixFormsComponent.module.scss`)
- Il package `common` è di tipo `commonjs` (diverso dalle pagine che sono `module`)
+229
View File
@@ -0,0 +1,229 @@
---
name: elixforms-create-new-page
description: >
Guida step-by-step per creare una nuova pagina custom nel progetto elixForms.
Copre la struttura dei file, la configurazione Vite per single-bundle,
il setup TypeScript, l'importazione dei componenti common, e le convenzioni.
Attiva questa skill quando devi creare una nuova pagina, duplicare una pagina esistente,
o scaffoldare un nuovo micro-progetto nel monorepo.
---
# Creare una Nuova Pagina elixForms Custom
## Prerequisiti
- Node.js installato
- Dipendenze root installate: `cd react && npm install`
## Step 1: Scaffolding con Vite
```powershell
cd react
npm create vite@latest <nome-pagina> -- --template react
cd <nome-pagina>
npm install vite-plugin-css-injected-by-js
npm install -D sass-embedded
```
## Step 2: Configurare package.json
```json
{
"name": "@elixforms/<nome-pagina>",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"vite-plugin-css-injected-by-js": "^5.0.1"
},
"devDependencies": {
"@vitejs/plugin-react": "^6.0.1",
"sass-embedded": "^1.99.0",
"vite": "^8.0.10"
}
}
```
> **Nota**: React, ReactDOM e FluentUI NON vanno nel package.json della pagina.
> Sono nel `react/package.json` root e vengono risolti via hoisting di npm.
## Step 3: Configurare vite.config.js
```js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
import path from "path";
export default defineConfig({
plugins: [
react(),
cssInjectedByJsPlugin()
],
build: {
outDir: 'dist',
cssCodeSplit: false,
rollupOptions: {
input: 'src/main.jsx', // o main.tsx se TypeScript
output: {
codeSplitting: false,
manualChunks: undefined,
entryFileNames: '<nome-pagina>.js',
assetFileNames: '[name].[ext]'
},
},
sourcemap: true,
},
resolve: {
alias: {
"@common": path.resolve(__dirname, "../common"),
}
},
server: {
fs: {
allow: [".."]
}
}
})
```
## Step 4: Configurare tsconfig.json
```json
{
"extends": "../tsconfig.base.json",
"references": [
{ "path": "../common" }
],
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"allowJs": true,
"checkJs": true
},
"include": ["src/**/*"]
}
```
## Step 5: Creare il file di dichiarazioni globali
`src/global.d.ts`:
```typescript
declare module "*.css";
declare module "*.svg";
declare module "*.png";
```
## Step 6: Creare l'entry point
`src/main.jsx` (o `main.tsx`):
```jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
const rootElement = document.getElementById('root')
if (!rootElement) throw new Error('Root element not found')
createRoot(rootElement).render(
<StrictMode>
<App />
</StrictMode>,
)
```
## Step 7: Creare il componente App
`src/App.jsx` (o `App.tsx`):
```jsx
import './App.css'
import * as ElixForms from '@common/src/ElixFormsComponent';
function App() {
var myElixFormsReact = new ElixForms.ElixFormsReact({
description: "Descrizione pagina",
isDarkTheme: false,
environmentMessage: "",
hasTeamsContext: false,
userDisplayName: "",
customFormFieldsConfiguration: (formFieldFactory) => (
<>
{/* Aggiungi i tuoi campi qui usando formFieldFactory */}
</>
)
});
var elixFormsReactRender = myElixFormsReact.render();
return <>
<div className="container_12" id="pageBody">
<div className="grid_12">
<div id="userConsole" className="fe-behaviour">
{/* Header, contenuto e footer della pagina */}
<div className="grid_12 console_main">
<div className="inside">
<div className="receipt">
<div className="container">
<h1>Titolo Pagina</h1>
{elixFormsReactRender}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>;
}
export default App
```
## Step 8: Configurare i CSS
`src/App.css` — Importa i CSS remoti del design system elixForms:
```css
@import url('https://console-unipr.elixforms.it/elixFormsCustom/css/elixForms.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/bootstrap_adaptation.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/console_user.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/designitalia_adaptation.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/fonts.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style-bs.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style.css');
/* ... altri CSS necessari */
```
## Step 9: Configurare VS Code (opzionale)
Aggiungere configurazione di launch e task in `.vscode/launch.json` e `.vscode/tasks.json` seguendo il pattern esistente per `scelta-carriera`.
## Step 10: Build e Deploy
```powershell
npm run build
```
Output in `dist/`:
- `<nome-pagina>.js` — Singolo bundle JS pronto per il deploy
- Eventuali asset (immagini) referenziati
## Checklist Nuova Pagina
- [ ] Cartella creata in `react/<nome-pagina>/`
- [ ] `package.json` con `"name": "@elixforms/<nome-pagina>"`
- [ ] `vite.config.js` con `entryFileNames: '<nome-pagina>.js'`
- [ ] `tsconfig.json` estende `../tsconfig.base.json`
- [ ] Alias `@common` configurato in vite.config.js
- [ ] CSS remoti importati in App.css
- [ ] Entry point `main.jsx` con mount su `#root`
- [ ] `global.d.ts` con dichiarazioni per moduli CSS/SVG/PNG
- [ ] `npm install` eseguito nella cartella della pagina
- [ ] `npm run dev` funziona senza errori
- [ ] `npm run build` produce singolo bundle in `dist/`
+171
View File
@@ -0,0 +1,171 @@
---
name: elixforms-css-design-system
description: >
Strategia CSS e design system del progetto elixForms.
Documenta come vengono importati i CSS remoti della piattaforma,
il layout a griglia elixForms, le classi CSS specifiche della piattaforma,
gli stili SCSS modules nella libreria common, e la struttura visiva delle pagine.
Attiva questa skill quando lavori sugli stili, sul layout delle pagine,
o devi capire come funzionano le classi CSS del design system elixForms.
---
# CSS e Design System elixForms
## Strategia CSS
Il progetto utilizza **tre livelli di stili**:
1. **CSS remoti** — Design system dell'Università di Parma, caricati via `@import url(...)` da `console-unipr.elixforms.it`
2. **SCSS Modules** — Stili specifici dei componenti in `common/` (es. `ElixFormsComponent.module.scss`)
3. **CSS locali** — Stili specifici della pagina (es. `App.css`, `index.css`)
## CSS Remoti (Design System UniPR/elixForms)
Importati in `App.css` di ogni pagina:
```css
/* CSS principali del design system */
@import url('https://console-unipr.elixforms.it/elixFormsCustom/css/elixForms.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/bootstrap_adaptation.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/bootstrap_overrides.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/console_user.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/designitalia_adaptation.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/fonts.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/isipcss_overrides.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/iulib.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/print.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/responsive.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style-bs.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/util.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/plugins/modaldisplay.css');
@import url('https://console-unipr.elixforms.it/rwe2/css/themes/blu-italia.css');
@import url('https://console-unipr.elixforms.it/rwe2/isipcss/css/grid_fluid.css');
```
> **IMPORTANTE**: Questi CSS sono caricati a runtime dal CDN e **non vengono bundlati** nel JS.
> La pagina deve avere connessione internet per visualizzarsi correttamente.
## Layout della Pagina (Struttura HTML)
Le pagine seguono un layout standard elixForms con classi CSS specifiche:
```html
<div class="container_12" id="pageBody">
<div class="grid_12">
<div id="userConsole" class="fe-behaviour">
<!-- HEADER -->
<header>
<div class="neutral">
<div class="grid_12">
<div class="console_header">
<div class="leftDiv">
<h1>elixForms</h1>
<div class="logo"><!-- Logo UniPR --></div>
</div>
<div class="rightDiv">...</div>
</div>
</div>
</div>
</header>
<!-- CONTENUTO PRINCIPALE -->
<div class="grid_12 console_main">
<div class="inside">
<div class="fe-module-header-container">
<h2>Titolo Modulo</h2>
</div>
<div class="receipt">
<div class="container">
<!-- Contenuto custom della pagina -->
</div>
</div>
</div>
</div>
<!-- FOOTER -->
<footer class="it-footer">
<div class="it-footer-main">
<div class="container">
<div class="subfooter-top">powered by elixForms</div>
<div class="subfooter-bottom">versione X.XX.X</div>
</div>
</div>
</footer>
</div>
</div>
</div>
```
## Classi CSS del Design System
### Layout a Griglia
| Classe | Scopo |
|---|---|
| `container_12` | Container principale a 12 colonne |
| `grid_12` | Occupa tutte le 12 colonne |
| `clear` | Clearfix |
### Struttura Pagina
| Classe | Scopo |
|---|---|
| `fe-behaviour` | Container principale della console utente |
| `console_header` | Header della console |
| `console_main` | Area contenuto principale |
| `fe-module-header-container` | Header del modulo/form |
| `receipt` | Container del contenuto del form |
### Layout Campi Form (ElixFormsElement)
| Classe | Scopo |
|---|---|
| `iuFieldContainer` | Container di un campo form (label + input) |
| `attrDisplay_left` | Colonna sinistra (label) |
| `attrDisplay_right` | Colonna destra (input) |
| `attrDisplay_label` | Marcatore per la label |
| `attrDisplay_input` | Marcatore per l'input |
| `iuLabelContainer` | Container interno della label |
| `iuInputContainer` | Container interno dell'input |
| `iuTypeString` | Tipo di input stringa |
| `iuClearContainer` | Clearfix interno |
### Classi Input FluentUI Custom
| Classe | Componente |
|---|---|
| `isiportalPartialAdminFormFieldSingleLineText` | TextField singola riga |
| `isiportalPartialAdminFormFieldMultiLineText` | TextField multiriga |
| `isiportalPartialAdminFormFieldRadio` | ChoiceGroup radio |
| `isiportalPartialAdminFormFieldSelect` | Dropdown select |
| `isiportalPartialAdminCheckboxFieldItem` | Checkbox singolo |
## SCSS Modules (common/)
`ElixFormsComponent.module.scss`:
```scss
@import '~@fluentui/react/dist/sass/References.scss';
.elixFormsComponent {
overflow: hidden;
padding: 1em;
color: "[theme:bodyText, default: #323130]";
color: var(--bodyText);
&.teams {
font-family: $ms-font-family-fallbacks;
}
}
```
> **Nota**: Gli stili usano il **theming di FluentUI** con variabili CSS e placeholder di tema SharePoint.
## Logo e Risorse Esterne
- **Logo UniPR**: `https://console-unipr.elixforms.it/elixFormsCustom/images/logo.png`
- Le immagini locali vanno in `src/assets/` e importate come moduli ES
- Le immagini vengono copiate nella `dist/` durante la build
## Note Importanti
1. Il CSS di `index.css` nella pagina `scelta-carriera` è **attualmente tutto commentato** — si usano solo i CSS remoti
2. Il CSS injection plugin (`vite-plugin-css-injected-by-js`) gestisce solo i CSS locali e SCSS, non gli `@import url(...)` che restano come riferimenti esterni
3. Per funzionare correttamente, la pagina deployata deve poter raggiungere `console-unipr.elixforms.it`
+130
View File
@@ -0,0 +1,130 @@
---
name: elixforms-dev-workflow
description: >
Workflow di sviluppo, debug e deploy del progetto elixForms Custom Pages.
Copre i comandi per avviare il dev server, buildare, debuggare con VS Code,
e le configurazioni di launch/tasks.
Attiva questa skill quando devi eseguire comandi di sviluppo,
configurare il debug, o gestire il ciclo dev/build/deploy.
---
# Workflow di Sviluppo
## Comandi Principali
Tutti i comandi vanno eseguiti **dalla cartella della pagina** (es. `react/scelta-carriera/`):
```powershell
# Installare le dipendenze (prima volta o dopo modifiche a package.json)
cd react
npm install
cd scelta-carriera
npm install
# Avviare il dev server con HMR
npm run dev
# Build per produzione (singolo bundle JS)
npm run build
# Preview della build di produzione
npm run preview
# Lint del codice
npm run lint
```
## Configurazione VS Code
### Launch Configurations (`.vscode/launch.json`)
#### 1. "Scelta Carriere (BROWSER)" — Dev + Chrome Debug
- Avvia `npm run dev` nella cartella della pagina
- Apre automaticamente Chrome quando il server è pronto
- Pattern di rilevamento: `Local:\s+http://localhost:([0-9]+)/`
- WebRoot per sourcemaps: `${workspaceFolder}/react/scelta-carriera/src`
#### 2. "Scelta Carriere (DEBUG ONLY)" — Solo Dev Server
- Avvia `npm run dev` senza aprire il browser
- Utile per debug da terminale o browser già aperto
### Variabili d'Ambiente
- `NO_COLOR: "1"` — Disabilita colori ANSI nell'output (migliora leggibilità nel debug console)
### Skip Files nel Debug
```json
"skipFiles": [
"<node_internals>/**",
"**/node_modules/**",
"**/@vite/client/**"
]
```
### Tasks (`.vscode/tasks.json`)
#### "Start scelta-carriera (Vite App)"
- Task background che avvia `npm run dev`
- Problem matcher configurato per output Vite:
- `beginsPattern`: "VITE"
- `endsPattern`: "ready in"
## Aggiungere una Nuova Pagina al Debug
Per aggiungere una nuova configurazione di debug per una pagina:
```json
// In .vscode/launch.json, aggiungere in "configurations":
{
"name": "<Nome Pagina> (BROWSER)",
"cwd": "${workspaceFolder}/react/<nome-pagina>",
"env": { "NO_COLOR": "1" },
"outputCapture": "console",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"skipFiles": [
"<node_internals>/**",
"**/node_modules/**",
"**/@vite/client/**"
],
"serverReadyAction": {
"action": "debugWithChrome",
"pattern": "Local:\\s+http://localhost:([0-9]+)/",
"uriFormat": "http://localhost:%s",
"webRoot": "${workspaceFolder}/react/<nome-pagina>/src"
}
}
```
## Flusso di Deploy
1. `npm run build` nella cartella della pagina
2. Output: `dist/<nome-pagina>.js` (+ sourcemap e asset)
3. Caricare il file JS sul server remoto `console-unipr.elixforms.it`
4. La pagina viene servita dalla piattaforma elixForms con i parametri query appropriati
## Testing Locale con Query Parameters
Per testare localmente con i parametri elixForms, aggiungere query params all'URL del dev server:
```
http://localhost:5173/?RWE2_MODULE_ID=123&RWE2_REQUEST_ID=456&COL0002=test&COL0015=3
```
I valori verranno letti da `QueryParamHelper` e pre-popoleranno i campi del form.
## Struttura delle Dipendenze
```
react/ ← npm install qui per dipendenze condivise
├── node_modules/ ← react, react-dom, @fluentui/react
├── package.json
└── scelta-carriera/ ← npm install qui per devDependencies
├── node_modules/ ← vite, eslint, plugin, sass-embedded
└── package.json
```
> **Nota**: Le dipendenze runtime (React, FluentUI) sono nel `package.json` root.
> Le dipendenze di sviluppo (Vite, ESLint) sono nel `package.json` della pagina.
> npm risolve le dipendenze risalendo la gerarchia delle cartelle (hoisting).
@@ -0,0 +1,150 @@
---
name: elixforms-project-architecture
description: >
Architettura e struttura del progetto elixForms Custom Pages.
Descrive il monorepo, la libreria common condivisa, il pattern delle pagine autonome
e tutte le convenzioni di naming, configurazione e build.
Attiva questa skill quando lavori su qualsiasi parte del progetto,
crei nuove pagine, modifichi l'architettura o devi capire come è organizzato il codice.
---
# Architettura del Progetto elixForms Custom Pages
## Panoramica
Questo progetto è un **monorepo** per la creazione di pagine web React custom destinate alla piattaforma **elixForms** dell'Università di Parma (UniPR). Ogni pagina è un micro-progetto indipendente che produce un **singolo file JS bundle** contenente React, Fluent UI, CSS e il codice custom, da pubblicare su un server remoto.
## Struttura del Workspace
```
elixforms-custom-pages/ ← Root del workspace (VS Code)
├── .vscode/ ← Configurazioni VS Code (launch, tasks)
├── docs/ ← Documentazione (PDF manuali)
└── react/ ← Root del monorepo React
├── package.json ← Dipendenze condivise (React, ReactDOM, FluentUI)
├── tsconfig.base.json ← Configurazione TypeScript base condivisa
├── .gitignore
├── common/ ← Libreria condivisa di componenti ElixForms
│ ├── package.json
│ ├── tsconfig.json ← Estende tsconfig.base, composite: true
│ └── src/
│ ├── ElixFormsComponent.tsx
│ ├── ElixFormsComponentAbstract.tsx
│ ├── ElixFormsElement.tsx
│ ├── ElixFormsTypes.tsx
│ ├── ElixFormsComponent.module.scss
│ ├── IElixFormsComponentProperties.ts
│ ├── IElixFormsComponentFormState.tsx
│ ├── IElixFormsComponentCustomFormFieldFactory.ts
│ └── QueryParamHelper.tsx
└── scelta-carriera/ ← Esempio di pagina custom (micro-progetto)
├── package.json
├── tsconfig.json ← Estende tsconfig.base
├── vite.config.js
├── eslint.config.js
├── index.html
└── src/
├── main.jsx ← Entry point React
├── App.jsx ← Componente principale della pagina
├── App.css ← CSS (importa CSS remoti di elixForms)
├── index.css ← CSS globale (attualmente commentato)
├── global.d.ts ← Dichiarazioni di tipo per moduli
└── assets/ ← Immagini e risorse statiche
```
## Pattern Architetturale: Micro-Progetto per Pagina
Ogni pagina (es. `scelta-carriera/`) è un progetto Vite completamente autonomo che:
1. **Importa dalla libreria `common/`** tramite alias `@common` configurato in `vite.config.js`
2. **Ha le proprie dipendenze** nel suo `package.json` (solo devDependencies e plugin Vite)
3. **Condivide le dipendenze runtime** (React, ReactDOM, FluentUI) dal `package.json` root di `react/`
4. **Produce un singolo file JS** tramite la configurazione Rollup in `vite.config.js`
5. **Estende il tsconfig base** per avere configurazione TypeScript coerente
## Dipendenze Condivise (react/package.json)
Le dipendenze runtime sono hoisted al livello `react/`:
- `react` ^19.2.7
- `react-dom` ^19.2.5
- `@fluentui/react` ^8.125.6
## Configurazione TypeScript
### Base (tsconfig.base.json)
- `module`: ESNext
- `target`: ES2022
- `moduleResolution`: bundler
- `jsx`: react-jsx
- `strict`: true
- Path alias: `@common/*``./common/*`
### Common (common/tsconfig.json)
- `composite`: true (per project references)
- `declaration`: true, `declarationMap`: true
### Pagine (scelta-carriera/tsconfig.json)
- `extends`: `../tsconfig.base.json`
- `references`: `../common`
- `allowJs`: true, `checkJs`: true (supporto misto JSX/TSX)
## Configurazione Build (Vite)
Ogni pagina usa questa configurazione per generare un **singolo bundle JS**:
```js
// Plugin chiave
react() // Supporto React JSX
cssInjectedByJsPlugin() // CSS iniettato nel JS (no file .css separati)
// Build options
cssCodeSplit: false
rollupOptions.input: 'src/main.jsx'
rollupOptions.output:
codeSplitting: false
manualChunks: undefined
entryFileNames: '<nome-pagina>.js' // Nome descrittivo (es. 'scelta-carriera.js')
assetFileNames: '[name].[ext]'
sourcemap: true
```
## Resolve Alias
```js
resolve.alias: {
"@common": path.resolve(__dirname, "../common")
}
server.fs.allow: [".."] // Permette import da common/
```
## CSS Strategy
Le pagine caricano CSS dal server remoto elixForms tramite `@import url(...)` in `App.css`:
- CSS da `console-unipr.elixforms.it` per il design system dell'università
- I componenti FluentUI usano stili inline/theme nativi
- SCSS modules sono usati in `common/` per stili dei componenti condivisi
- `sass-embedded` è installato come devDependency per supporto SCSS
## Integrazione con elixForms
La pagina custom interagisce con la piattaforma elixForms tramite:
1. **Query parameters**: Parametri obbligatori passati nell'URL (es. `RWE2_MODULE_ID`, `RWE2_REQUEST_ID`, `crc`, etc.)
2. **Form POST**: Il form fa submit a `https://procedure.unipr.it/rwe2/ComeBackToElixAndSave`
3. **Hidden inputs**: I parametri query vengono inseriti come campi hidden nel form
4. **Encoding**: `acceptCharset="ISO-8859-1"` per compatibilità con il backend
## Convenzioni di Naming
- **Cartelle pagina**: kebab-case (es. `scelta-carriera`)
- **Package name**: `@elixforms/<nome-pagina>` (es. `@elixforms/scelta-carriera`)
- **Bundle output**: `<nome-pagina>.js` (es. `scelta-carriera.js`)
- **Componenti React**: PascalCase
- **File sorgente**: I file correnti sono `.jsx` ma il progetto ha pieno supporto TypeScript (`.tsx`)
- **Tipo moduli**: `"type": "module"` nelle pagine
## Ambiente di Sviluppo
- **Dev server**: `npm run dev` (Vite HMR)
- **Build**: `npm run build` → output in `dist/`
- **Debug VS Code**: Configurazioni in `.vscode/launch.json` per avviare Vite + Chrome
- **Lint**: ESLint con plugin react-hooks e react-refresh
@@ -0,0 +1,174 @@
---
name: elixforms-typescript-conventions
description: >
Convenzioni TypeScript e React per il progetto elixForms.
Copre la configurazione TypeScript, i pattern di codice, la gestione dei tipi,
l'uso di JSDoc per type annotations nei file .jsx, e le best practices.
Attiva questa skill quando scrivi codice TypeScript o React,
crei nuovi componenti, definisci interfacce, o gestisci i tipi nel progetto.
---
# Convenzioni TypeScript e React
## Configurazione TypeScript del Progetto
### Opzioni Compiler Principali
- **target**: ES2022
- **module**: ESNext
- **moduleResolution**: bundler
- **jsx**: react-jsx (trasformazione automatica JSX senza import React)
- **strict**: true
- **isolatedModules**: true
- **verbatimModuleSyntax**: true (usa `import type` per import solo di tipi)
### File Misti JSX/TSX
Il progetto supporta **sia file `.jsx` che `.tsx`** grazie a:
- `allowJs: true` e `checkJs: true` nel tsconfig delle pagine
- Type annotations via JSDoc nei file `.jsx`
#### Pattern JSDoc per Tipi nei File .jsx
```jsx
/** @type {import('@common/src/ElixFormsComponent').ElixFormsReact} */
var myElixFormsReact = new ElixForms.ElixFormsReact({...});
// Per i parametri di callback
customFormFieldsConfiguration: (
/** @type {import('../../common/src/IElixFormsComponentCustomFormFieldFactory').IElixFormsComponentCustomFormFieldFactory} */
formFieldFactory) => (...)
```
## Pattern React Correnti
### Class Components (common/)
La libreria `common/` usa **React Class Components** (non hooks):
```tsx
abstract class ElixFormsComponentAbstract
extends Component<IElixFormsComponentProperties, IElixFormsComponentFormState> {
constructor(props: IElixFormsComponentProperties) {
super(props);
this.state = { formData: {} };
}
// State gestito con this.setState()
this.setState({ formData: { ...this.state.formData, [name]: value } });
}
```
### Functional Components (pagine)
Le pagine usano **Functional Components** con hooks:
```jsx
import { useState } from 'react'
function App() {
const [count, setCount] = useState(0)
// ...
}
```
### Istanziazione del Componente ElixForms
Il componente ElixForms viene istanziato come classe (non come JSX):
```jsx
// ✅ Corretto - istanziazione diretta
var myElixFormsReact = new ElixForms.ElixFormsReact({...props...});
var elixFormsReactRender = myElixFormsReact.render();
// Poi nel JSX
return <>{elixFormsReactRender}</>;
// ❌ NON usare come componente JSX
return <ElixForms.ElixFormsReact {...props} />;
```
## Import Conventions
### Import Type-Only (verbatimModuleSyntax)
Con `verbatimModuleSyntax: true`, usare sempre `import type` per i tipi:
```tsx
// ✅ Corretto
import type { IElixFormsComponentProperties } from './IElixFormsComponentProperties';
import type { JSX } from 'react';
// ❌ Errato - causerà errore con verbatimModuleSyntax
import { IElixFormsComponentProperties } from './IElixFormsComponentProperties';
```
### Import di Moduli
```tsx
// Fluent UI - import tutto il namespace
import * as FluentUI from '@fluentui/react';
// React - import specifici
import { Component, type FormEvent, type JSX, useState } from 'react';
import React from 'react';
// Componenti common - import namespace
import * as ElixForms from '@common/src/ElixFormsComponent';
// CSS
import './App.css'
```
## Tipi Custom del Progetto
```typescript
// Opzioni per campi form
type ElixFormsCheckboxOption = { value: number; label: string; required?: boolean };
type ElixFormsDropdownOption = { value: number; label: string };
type ElixFormsRadioOption = { value: number; label: string };
// State del form
interface IElixFormsComponentFormState {
formData: { [key: string]: any };
}
```
## Dichiarazioni di Modulo
`global.d.ts` nella cartella `src/` di ogni pagina:
```typescript
declare module "*.css";
declare module "*.svg";
declare module "*.png";
```
## Best Practices per il Progetto
1. **Naming**:
- Interfacce: prefisso `I` (es. `IElixFormsComponentProperties`)
- Tipi: PascalCase (es. `ElixFormsCheckboxOption`)
- File interfacce: nome dell'interfaccia (es. `IElixFormsComponentProperties.ts`)
- File componenti: nome del componente (es. `ElixFormsComponent.tsx`)
2. **Estensioni file**:
- `.tsx` per componenti React con TypeScript (common/)
- `.ts` per moduli TypeScript puri (interfacce, tipi)
- `.jsx` per componenti React con JavaScript (pagine attuali)
- `.scss` per stili con moduli SCSS
3. **Export**:
- Named export per tipi e interfacce
- Default export per classi componente
- Re-export con alias: `export { ElixFormsComponent as ElixFormsReact }`
4. **State management**:
- Usare `this.setState` con spread operator per update immutabili
- I checkbox mantengono un `Map<string, number[]>` separato
5. **Fluent UI**:
- Usare Fluent UI React v8 (`@fluentui/react`)
- Componenti: `TextField`, `Checkbox`, `ChoiceGroup`, `Dropdown`, `Label`, `Stack`
- Le opzioni radio usano `IChoiceGroupOption[]`
- Le opzioni dropdown usano `IDropdownOption[]`
+136
View File
@@ -0,0 +1,136 @@
---
name: elixforms-vite-build-config
description: >
Configurazione Vite e Rollup per il progetto elixForms.
Copre la build single-bundle, i plugin utilizzati, gli alias,
il sourcemap, e la strategia CSS (injection nel JS).
Attiva questa skill quando devi modificare la build configuration,
risolvere problemi di bundling, aggiungere plugin Vite,
o ottimizzare l'output di produzione.
---
# Configurazione Vite e Build
## Stack di Build
- **Vite** v8.x — Dev server e bundler
- **@vitejs/plugin-react** v6.x — Supporto React/JSX
- **vite-plugin-css-injected-by-js** v5.x — Inietta CSS nel bundle JS
- **sass-embedded** v1.99.x — Compilazione SCSS (per common/)
- **Rollup** — Bundler interno di Vite (configurato via `rollupOptions`)
## Configurazione Vite Completa di Riferimento
```js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
import path from "path";
export default defineConfig({
plugins: [
react(),
cssInjectedByJsPlugin()
],
build: {
outDir: 'dist',
cssCodeSplit: false,
rollupOptions: {
input: 'src/main.jsx',
output: {
codeSplitting: false,
manualChunks: undefined,
entryFileNames: '<nome-pagina>.js',
assetFileNames: '[name].[ext]'
},
},
sourcemap: true,
},
resolve: {
alias: {
"@common": path.resolve(__dirname, "../common"),
}
},
server: {
fs: {
allow: [".."]
}
}
})
```
## Dettaglio Opzioni Critiche
### Single Bundle (Rollup)
| Opzione | Valore | Scopo |
|---|---|---|
| `codeSplitting` | `false` | Disabilita il code splitting |
| `manualChunks` | `undefined` | Impedisce separazione vendor/app |
| `entryFileNames` | `'<nome>.js'` | Nome deterministico senza hash |
| `assetFileNames` | `'[name].[ext]'` | Asset senza hash nei nomi |
### CSS Strategy
| Opzione | Valore | Scopo |
|---|---|---|
| `cssCodeSplit` | `false` | No file CSS separati |
| `cssInjectedByJsPlugin()` | plugin | CSS iniettato runtime nel DOM via JS |
### Alias di Import
| Alias | Path | Scopo |
|---|---|---|
| `@common` | `../common` | Accesso alla libreria condivisa |
### Dev Server
| Opzione | Valore | Scopo |
|---|---|---|
| `server.fs.allow` | `[".."]` | Permette import fuori dalla root del progetto |
## Comandi
```powershell
# Dev con HMR
npm run dev
# Build produzione
npm run build
# Preview build produzione
npm run preview
# Lint
npm run lint
```
## Output della Build
```
dist/
<nome-pagina>.js ← Bundle unico (React + FluentUI + CSS + codice)
<nome-pagina>.js.map ← Source map
*.png, *.svg ← Asset referenziati nel codice
```
## Risoluzione Problemi Comuni
### Errore: "The request url is outside of Vite serving allow list"
- **Causa**: Import di file dalla cartella `common/` o altre cartelle esterne
- **Fix**: Aggiungere `server.fs.allow: [".."]` nella configurazione Vite
### Errore: "Cannot find module '@common/...'"
- **Causa**: Alias non configurato o path errato
- **Fix**: Verificare `resolve.alias` e che la cartella `common/` esista
### Bundle troppo grande
- **Causa**: FluentUI include molti componenti
- **Fix possibili**:
- Tree shaking (import specifici: `import { TextField } from '@fluentui/react'`)
- Considerare `@fluentui/react-components` (v9) per bundle più piccoli
- Esternalizzare React se la pagina host lo fornisce già
### CSS non applicato in produzione
- **Causa**: `cssInjectedByJsPlugin` non installato o non configurato
- **Fix**: Verificare plugin in `vite.config.js` e `npm install vite-plugin-css-injected-by-js`
### SCSS non compilato
- **Causa**: `sass-embedded` mancante
- **Fix**: `npm install -D sass-embedded`
Binary file not shown.
+3 -2
View File
@@ -262,14 +262,14 @@
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/react": { "node_modules/react": {
"version": "19.2.7", "version": "19.2.7",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
"integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -279,6 +279,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
"integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"scheduler": "^0.27.0" "scheduler": "^0.27.0"
}, },
+42 -24
View File
@@ -54,6 +54,7 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.29.0", "@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0", "@babel/generator": "^7.29.0",
@@ -270,31 +271,10 @@
"devOptional": true, "devOptional": true,
"license": "(Apache-2.0 AND BSD-3-Clause)" "license": "(Apache-2.0 AND BSD-3-Clause)"
}, },
"node_modules/@emnapi/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": { "node_modules/@emnapi/wasi-threads": {
"version": "1.2.1", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
@@ -1078,6 +1058,37 @@
"node": "^20.19.0 || >=22.12.0" "node": "^20.19.0 || >=22.12.0"
} }
}, },
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
}
},
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": { "node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.0-rc.17", "version": "1.0.0-rc.17",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
@@ -1154,6 +1165,7 @@
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.2.2" "csstype": "^3.2.2"
} }
@@ -1200,6 +1212,7 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -1290,6 +1303,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.10.12", "baseline-browser-mapping": "^2.10.12",
"caniuse-lite": "^1.0.30001782", "caniuse-lite": "^1.0.30001782",
@@ -1447,6 +1461,7 @@
"integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==", "integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.2", "@eslint-community/regexpp": "^4.12.2",
@@ -2487,6 +2502,7 @@
"integrity": "sha512-gF/juR1aX02lZHkvwxdF80SapkQeg2fetoDF6gIQkNbSw5YEUFspMkyGTjPjgZSgIHuZpy+Wz4PlebKnLXMjdg==", "integrity": "sha512-gF/juR1aX02lZHkvwxdF80SapkQeg2fetoDF6gIQkNbSw5YEUFspMkyGTjPjgZSgIHuZpy+Wz4PlebKnLXMjdg==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@bufbuild/protobuf": "^2.5.0", "@bufbuild/protobuf": "^2.5.0",
"colorjs.io": "^0.5.0", "colorjs.io": "^0.5.0",
@@ -2981,6 +2997,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
"integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"lightningcss": "^1.32.0", "lightningcss": "^1.32.0",
"picomatch": "^4.0.4", "picomatch": "^4.0.4",
@@ -3114,6 +3131,7 @@
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }