Scrivere un plugin di attività
Un plugin di attività aggiunge un tipo di annotazione: cosa risponde un lettore (il body), come un amministratore del progetto imposta un’attività di quel tipo (la sua configurazione), il controllo del lettore e l’editor della procedura guidata. Questa guida costruisce il plugin modello, org.example.rating@1: una valutazione da una stella al massimo previsto dall’attività. Leggi prima I plugin e il catalogo dei plugin per sapere di cosa si affida a un plugin.
1. Copia il modello
Sezione intitolata “1. Copia il modello”cp -r examples/plugin-template my-pluginIl modello è un pacchetto del workspace del repository: per la build ha bisogno di @yggdrasil/plugin-api (i tipi del contratto e il kit per autori) e di Vite. In src/manifest.json e src/index.tsx, cambia l’ID org.example.rating con uno nel tuo namespace (il contrario di un dominio che controlli); il namespace ygg è riservato.
2. Definisci il body e la configurazione
Sezione intitolata “2. Definisci il body e la configurazione”export interface RatingBody { readonly stars: number } // what a reader answersexport interface RatingConfig { readonly max: number } // a task's settingsL’app non compila mai uno schema a runtime (la sua Content Security Policy vieta eval), quindi un plugin verifica body e configurazioni con funzioni. schemaCheck trasforma una funzione che elenca i problemi (un JSON pointer e un codice) nello SchemaCheck del contratto:
body: h.api.schemaCheck<RatingBody>("urn:org.example.rating:body:1", bodyProblems),config: h.api.schemaCheck<RatingConfig>("urn:org.example.rating:config:1", configProblems),I controlli che richiedono la configurazione vanno in validate (qui, al massimo max stelle). I body sono cifrati: li verificano solo le app dei membri, mai il server.
3. Scrivi il controllo del lettore e l’editor della procedura guidata
Sezione intitolata “3. Scrivi il controllo del lettore e l’editor della procedura guidata”I componenti ricevono React, React Intl e il design system dall’host, quindi condividono il React dell’app e ne hanno l’aspetto:
/** @jsxImportSource @yggdrasil/plugin-api */import { definePlugin, host } from "@yggdrasil/plugin-api/author";
function RatingInput({ config, value, onChange, label, required, disabled, errorMessage }) { const { RadioGroup, Radio } = host().ui; // …one radio button per star count; onChange(undefined) when cleared}- Il controllo riceve la configurazione dell’attività, il valore corrente (
undefinedsignifica senza risposta),onChange, il nome dell’attività comelabel, se è obbligatoria, e il messaggio di errore dell’app. Segnala ogni modifica: l’app la salva automaticamente, in una bozza cifrata sul dispositivo e poi sul server, e invia l’attività quando il lettore la invia. Non c’è un pulsante Salva. - L’editor riceve la configurazione e segnala ogni modifica;
defaultConfigè il punto di partenza di una nuova attività. - Il pragma
@jsxImportSource @yggdrasil/plugin-apicompila il JSX in chiamate al React dell’app. (Un plugin compilato fuori da questo repository può invece impostare"jsxImportSource": "@yggdrasil/plugin-api"nel suotsconfig.json.)
4. Descrivi il tipo
Sezione intitolata “4. Descrivi il tipo”definePlugin fornisce l’host alla factory e restituisce ciò che il plugin registra:
export default definePlugin((h) => ({ annotationTypes: [ratingType(h)] }));Il tipo (AnnotationTypePlugin) indica anche:
| Membro | Cosa fa |
|---|---|
levels |
I livelli di attività a cui si adatta: paziente, studio, serie, asset, regione. |
modalities |
Plugin di modalità integrati (ygg.image2d@1, …) le cui attività a livello di serie possono usarlo. Senza, il tipo è offerto a livello di paziente e di studio, e sulle modalità che lo elencano. |
selector |
Se una risposta porta con sé una forma. Un tipo con forma ne indica la geometria e le etichette (shape), e i visualizzatori la disegnano. |
conditionValue |
Il valore confrontato dalle condizioni show-if. |
aggregation, consensus |
Come si combinano le risposte di più lettori (qui la mediana). |
agreement |
Come la pagina delle statistiche confronta i lettori (qui una scala a intervalli). |
extent, compare |
Quanto è grande una risposta (un volume, un’area, una durata, un conteggio…) e come si confrontano a coppie le risposte di due lettori (sovrapposizione o distanza) quando nessuna scala si adatta. |
display |
La risposta come testo, mostrata dal pannello delle attività fuori dalla modalità di annotazione (API dei plugin 1.8.0). Senza, il pannello mostra il controllo disabilitato. |
numberKeys |
Cosa selezionano i tasti numerici. |
references, guidelines |
Gli ID della configurazione usati dalle risposte, e le linee guida per singola voce, per i tipi con opzioni o etichette. |
5. Testalo
Sezione intitolata “5. Testalo”pnpm --filter @yggdrasil/plugin-template test renderizza il controllo e l’editor con l’harness di test dell’API dei plugin e un host costruito con le librerie reali, e verifica che il plugin fornisca esattamente ciò che il suo manifest dichiara.
6. Compila il pacchetto
Sezione intitolata “6. Compila il pacchetto”pnpm --filter @yggdrasil/plugin-template builddist/ contiene poi il pacchetto: plugin.js (un modulo ES di pochi kilobyte, senza React al suo interno) e yggdrasil-plugin.json (src/manifest.json con l’integrità del modulo, scritto da scripts/package.ts). Ogni ricompilazione che cambia il modulo cambia l’integrità: dai a ogni release la sua versione.
7. Aggiungilo a un’installazione
Sezione intitolata “7. Aggiungilo a un’installazione”Un superamministratore aggiunge entrambi i file nella sezione Plugin della console, controlla i dettagli e abilita il plugin (I plugin e il catalogo dei plugin). Ricarica l’app; il tipo compare allora tra i tipi di attività della procedura guidata di configurazione, e il manifest di un progetto può indicarlo:
{ "id": "quality", "name": "Image quality", "type": "org.example.rating@1", "level": "patient", "config": { "max": 5 } }Il test end-to-end della piattaforma fa esattamente questo: compila il modello, lo aggiunge e lo abilita nella console, e un lettore risponde alla sua attività in un progetto.