Come creare un'App con Vibecode
Vediamo come costruire una App su Vibecode, specificando gli step necessari per la sua creazione (nell’esempio: una semplice app che permette di salvare i libri di una biblioteca e tener traccia dei prestiti).
1. Creare cartella App
Come già visto in Getting started for DEV , creare la cartella della nuova App, sotto il percorso: C:\Vibecode\Current Version\Apps.
Creare le 3 sottocartelle: bin, res, vibs.
Duplicare, prendendo il template base da un’altra app, il file.vibs che si trova all’interno della cartella vibs. Rinominarlo con il nome della app che si sta creando; estensione file ‘.vibs’ → MediaLibrary.vibs. Si tratta del file principale della app.
![](../__attachments/1658912771/image-20220926-140558.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
Una nota sui nomi delle classi, delle proprietà,.. : dovranno sempre essere descritte in inglese, senza caratteri speciali come l'underscore per esempio. Eventuale eccezione: applicazioni custom per clienti non multilingua.
Elencare tramite comando #using “..” le altre app con le quali la nostra app interagisce (sicuramente Core e Base).
Le righe sottostanti, una volta sostituito il nome della precedente App (quella copiata) con il nome della mia applicazione (es. BookEngine ->MediaLibrary): costituiranno i percorsi/direttori della mia app: classi, servizi..etc
//*------------------------------*
//* Define Application Variables *
//*------------------------------*
#using "core"
#using "base"
variable name(bp.appname.MediaLibrary) value(MediaLibrary)
variable name(bp.baseappdir.MediaLibrary) value($$SchemaPath$$\apps\$$bp.appname.MediaLibrary$$)
variable name($$bp.appname.MediaLibrary$$.applocation) value($$SchemaPath$$\apps\vibs\$$bp.appname.MediaLibrary$$)
variable name($$bp.appname.MediaLibrary$$.classlocation) value($$bp.baseappdir.MediaLibrary$$\vibs\classes)
variable name($$bp.appname.MediaLibrary$$.servicelocation) value($$bp.baseappdir.MediaLibrary$$\vibs\services)
variable name($$bp.appname.MediaLibrary$$.functionlocation) value($$bp.baseappdir.MediaLibrary$$\vibs\functions)
variable name($$bp.appname.MediaLibrary$$.menulocation) value($$bp.baseappdir.MediaLibrary$$\vibs\menu)
variable name($$bp.appname.MediaLibrary$$.viewlocation) value($$bp.baseappdir.MediaLibrary$$\vibs\views)
variable name($$bp.appname.MediaLibrary$$.reportlocation) value($$bp.baseappdir.MediaLibrary$$\vibs\reports)
//*----------------------------------*
//* Define Application and Functions *
//*----------------------------------*
application name($$bp.appname.MediaLibrary$$) title(MediaLibrary) menu(mMediaLibrary) namespace(vp) debug(true)
//*---------------------------*
//* Define Class and Services *
//*---------------------------*
#include "autocoderules\acrMediaLibrary.vibs"
#include "classes\cMediaLibrary.vibs"
#include "events\eMediaLibrary.vibs"
#include "views\vMediaLibrary.vibs"
#include "reports\rMediaLibrary.vibs"
#include "services\sMediaLibrary.vibs"
#include "styles\styMediaLibrary.vibs"
#include "translations\tMediaLibrary.vibs"
#include "uielementmap\uiMediaLibrary.vibs"
2. Creare le Classi
La classe è un tipo di dato astratto, un costrutto utilizzato come modello per creare oggetti.
Creare sotto la cartella vibs la sottocartella “Classes” con al suo interno un notepad++, rinominato cMediaLibrary.vibs.
![](../__attachments/1658912771/image-20220927-145915.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
Definizione Classe
type name(tkISBN13) primitive(string) length(13) label(ISBN-13)
class name(kMediaLibrary_Author) inherits(kdatabasic) label(Authors)
property name(Name) type(tkName) label(Name) mandatory(true)
property name(Surname) type(tkSurname) label(Surname) mandatory(true)
property name(DateOfBirth) type(tkDate) label(Date Of Birth) mandatory(true)
property name(DateOfDeath) type(tkDate) label(Death Date) mandatory(false)
property name(Nationality) type(kCountry) label(Nationality) mandatory(true)
property name(Picture) type(tkAttachment) label(Picture) mandatory(false)
class name(kMediaLibrary_Book) inherits(kDataBasic) label(Books)
property name(Author) type(kMediaLibrary_Author) label(Author) mandatory(true) list(true) owned(false)
property name(PublishingHouse) type(kMediaLibrary_PublishingHouse) label(Publishing House) mandatory(true)
property name(PublishingYear) type(tkYear) label(Publishing Year) mandatory(true)
property name(Cover) type(tkAttachment) label(Cover) mandatory(true)
property name(ISBN13) type(tkISBN13) label(ISBN-13) mandatory(false)
property name(Language) type(kLanguage) label(Language) mandatory(false)
class name(kMediaLibrary_PublishingHouse) inherits(kDataBasic) label(Publishing Houses)
property name(Country) type(kCountry) label(Country) mandatory(true)
incrementalsearch class(kMediaLibrary_PublishingHouse) searchproperty(code,description) displayproperties(code,description)
class name(kMediaLibrary_Borrower) inherits(kDataBasic) label(Borrower)
property name(Name) type(tkName) label(Name) mandatory(true)
property name(Surname) type(tkSurname) label(Surname) mandatory(true)
property name(Address) type(tkAddressInfo) label(Address)
property name(TelephoneNumbers) type(kTelephoneNumber) list(true) owned(true) label(Telephone Numbers)
property name(eMailAddresses) type(kEmail) list(true) owned(true) label(Email Addresses)
class name(kMediaLibrary_Loan) inherits(kDataBasic) label(Loans)
property name(Borrower) type(kMediaLibrary_Borrower) label(Borrower) mandatory(true)
property name(Book) type(kMediaLibrary_Book) label(Book) mandatory(true)
property name(StartDate) type(tkDate) label(Start Date) mandatory(true)
property name(EndDate) type(tkDate) label(End Date) mandatory(false)
property name(ExpirationDate) type(tkDate) label(Expiration Date) mandatory(true)
searchpanel class(kMediaLibrary_Loan) name(kMediaLibrary_Loan_bybook) label(by Book)
searchpanelproperty propertyname(Book) searchmode(choice) label(Book) mandatory(true)
searchpanel class(kMediaLibrary_Loan) name(kMediaLibrary_Loan_byborrower) label(by Borrower)
searchpanelproperty propertyname(Borrower) searchmode(choice) label(Borrower) mandatory(true)
searchpanel class(kMediaLibrary_Book) name(kMediaLibrary_Book_byauthor) label(by Author)
searchpanelproperty propertyname(Author) searchmode(choice) label(Author) mandatory(true)
searchpanel class(kMediaLibrary_Book) name(kMediaLibrary_Book_bylanguage) label(by Language)
searchpanelproperty propertyname(Language) searchmode(choice) label(Language) mandatory(true)
searchpanel class(kMediaLibrary_Book) name(kMediaLibrary_Book_borrowed) label(Show only borrowed books) customfilter(field(Book@kMediaLibrary_Loan.EndDate) nu('') and(field(Book@kMediaLibrary_Loan.StartDate) nn('')))
Vediamo nel dettaglio alcuni punti:
Classe ed alcune proprietà/caratteristiche (Classextension)
Type
Search Panel
Autocoderole
Incremental Search
Le classi vengono sempre scritte nella seguente maniera: k + class name : es.
kMediaLibrary_Book
(R3) inherits(kDataBasic): le classi possono derivare da kDataBasic (vedi kDataBasic ) o kDataObject (vedi kDataObject). Entrambe sono delle classi astratte, non presenti nel database e che non possono essere usate direttamente. Quando derivate però in un’altra classe si portano dietro tutte le loro proprietà che non hanno quindi bisogno ogni volta di essere definite, in quanto già presenti nella classe padre.
![](../__attachments/1658912771/image-20221006-134459.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
(es R3) label: il nome con cui la classe verrà visualizzata su browser
(R4-R9) Una volta definita la classe verranno elencate tutte le proprietà che vogliamo facciano parte della classe. Ciascuna proprietà avrà almeno un name, una label e un type: quest’ultimo potrebbe essere un tipo o una classe a sua volta. Alcune classi standard sono già esistenti a livello di Core o Base (Getting started for DEV | 1.1. 📁 Apps ), per esempio la classe kLanguage. In questo caso non dovremo andare a creare un’altra classe di questo tipo: andando semplicemente a specificare “type(kLanguage)” la proprietà Language erediterà di conseguenza tutte le proprietà della suddetta classe.
Potrebbe essere necessario aggiungere però alcune proprietà non presenti nella classe kLanguage di base: queste proprietà verranno elencate sotto “Classextension” come si fa di solito per una normale classe.
Esempio:
classextension class(kLanguage)
property name(EUcode) type(tkCode) label(European Code)
(es. R13-14) Il comando mandatory (true o false) specifica se sarà obbligatorio o meno compilare questo campo a livello di browser (il campo da inserire verrà visualizzato in rosso)
![](../__attachments/1658912771/image-20221007-122546.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
(es. R12) list(true) owned(true): questo comando specifica che la classe di riferimento è una sottotabella, una lista le cui righe saranno esclusivamente di proprietà della tabella padre. Per fare un esempio concreto: le righe A, B, C della nota spese P di Pippo apparterranno esclusivamente alla nota spese P di Pippo. Il comando: list(true) owned(false) specifica invece che i campi appartenenti alla lista sono campi di un’altra lista creata precedentemente da qualche altra parte all’interno della app e questi campi vengono semplicemente richiamati nella tabella che si sta gestendo.
(R13): la classe kMediaLibrary_PublishingHouse
andrà a sua volta definita come classe con tutte le sue proprietà e via dicendo.. (R19-20)
Le proprietà possono essere di tipo classe o type:
(R2) property name(Author) type(kMediaLibrary_Author)
la proprietà specificata è di tipo “classe” k
(R4) property name(Address) type(tkAddressInfo)
: la proprietà specificata è di tipo “type” tk. Come per le classi, molti type generici (come quello nell’esempio) sono già definiti nella App Core o Base. Se un type non è già definito andremo noi a definirlo (Es. R16 e R1 → normalmente vengono scritti prima delle classi, nella parte in alto del nostro file).
Un tipo può anche ricondurre ad un primitivo. Es: type name(tkDate) primitive(date)
Un type può essere di due tipi: strutturato o enumerato (2 esempi relativi all’App delle Travel Expenses):
(R16) (type name(tkVehicle) structured(true)
element name(Model) type(tkDescription) label(Model)
element name(RegistrationPlate) type(tkDescription) label(Registration Plate)
element name(Allowance) type(tkPrice) label(Allowance)
![](../__attachments/1658912771/image-20220914-112743.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
type name(tkExpenseCategory) primitive(smallint) enumerated(true)
item name(Food) value(1) label(Food)
item name(Lodging) value(2) label(Lodging)
item name(Highway) value(3) label(Highway)
item name(Parking) value(4) label(Parking)
item name(Other) value(5) label(Other)
item name(Mileage) value(6) label(Mileage)
![](../__attachments/1658912771/image-20220914-112644.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
(R44-45) Definizione Search Panel : serve per definire una modalità di ricerca all'interno della tabella (classe) per il quale viene definito.
searchpanel class(kMediaLibrary_Book) name(kMediaLibrary_Book_byauthor) label(by Author)
searchpanelproperty propertyname(Author) searchmode(choice) label(Author) mandatory(true)
class: è il nome della classe su cui si sta creando il search panel
name: nome del search panel
label: nome con cui verrà visualizzato sul browser il search panel
searchpanelproperty propertyname(Author)
: indica la proprietà della classe per la quale i risultati verranno filtratisearchmode(choice)
: con choice ho la possibilità di selezionare la mia scelta tra il totale dei valori a disposizione (vedi sotto per le possibili alternative al choice).
![](../__attachments/1658912771/image-20221007-150931.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
Search Panel classe Books, per autore
Le proprietà legate al searchpanel sono molteplici. Consultare lo schema per tutti i comandi a disposizione:
command name(searchpanel) level(1)
parameter name(class) type(alfa) mandatory(true) refers(class,view)
parameter name(name) type(alfa) mandatory(true)
parameter name(label) type(alfa) translatable(true) mandatory(true)
parameter name(customfilter) type(alfa) mandatory(false)
command name(searchpanelproperty) level(2) ancestors(searchpanel)
parameter name(name) type(alfa) mandatory(false) // is usefull only as placeholder of serchpanel customfilter
parameter name(propertyname) type(alfa) mandatory(if cond(&type==\"\") true else false endif)
parameter name(customtypename) type(alfa) refers(class,type) mandatory(if cond(&property==\"\") true else false endif)
// in case of cluster or not cluster a combo will be dispaly return the id of cluster
parameter name(searchmode) type(alfa) values(equal,notequal,startwith,endwith,like,notlike,greater,less,greaterequal,lessequal,between,notbetween,in,notin,choice,incluster,notincluster)
parameter name(defaultvalue) type(alfa)
parameter name(visible) type(alfa)
parameter name(enabled) type(alfa)
parameter name(mandatory) type(alfa)
parameter name(label) type(alfa) translatable(true) mandatory(false)
parameter name(filter) type(alfa) mandatory(false)
parameter name(checkinview) type(alfa) mandatory(false) values(true,false)
parameter name(minvalue) type(alfa)
parameter name(maxvalue) type(alfa)
//parameter name(asview) type(alfa) mandatory(false)
E' possibile creare search panel personalizzati direttamente da browser. Consultare Search Panels e Griglie.
Gli Autocoderole possono essere definiti all’interno delle classi o in una cartella a parte, all’interno dei vibs. Vengono definiti per generare un codice in maniera automatica, senza necessità di inserimento.
![](../__attachments/1658912771/image-20221007-160008.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
Esempio1:
autocoderole class(kMediaLibrary_Author) property(description) ignoreuserinput(false)
codepart type(property) value(surname)
codepart type(fixed) value(" ")
codepart type(property) value(name)
autocoderole class: (specificare la classe di riferimento); property(description): è la proprietà che si sta valorizzando tramite l’autocoderole; ignoreuserinput(true,false): se a true il valore inserito dall’utente viene ignorato, se a false viene preso in considerazione.
codepart type: specifica come vogliamo valorizzare l’autocoderole e le varie parti che lo comporranno (se utilizzare una proprietà, se usare un carattere fisso.. etc)
Oltre che definibili tramite vibs, è possibile configurare gli autocoderole direttamente su browser, seguendo da menù il seguente percorso: Amministratore > Autocoderoles > Autocoderoles (kAutocoderole).
Consultare anche la voce: Autocoderole e sequence utilizzate da POSible
Le proprietà legate agli autocoderole sono molteplici. Consultare lo schema command per tutti i comandi a disposizione:
command name(autocoderole) level(1)
parameter name(class) type(alfa) mandatory(true) nested(false)
parameter name(property) type(alfa) mandatory(true) nested(false)
parameter name(when) type(alfa) mandatory(false) nested(false) values(insert,update)
parameter name(ignoreuserinput) type(alfa) mandatory(false) nested(false) values(true,false)
parameter name(validity) type(alfa)
parameter name(notvalidity) type(alfa)
command name(codepart) ancestors(autocoderole) level(2) doc(label(Autocompletamento campi) text())
parameter name(type) type(alfa) mandatory(true) nested(false) values(property,fixed,sequence,random,checksum,remove,guid) doc(label(Questo comando prevede ....) text(asdasd))
parameter name(value) type(alfa) mandatory(true) nested(false)
parameter name(start) type(numeric) mandatory(false) nested(false)
parameter name(length) type(numeric) mandatory(false) nested(false)
parameter name(fillchar) type(alfa) mandatory(false) nested(false)
parameter name(filldirection) type(alfa) mandatory(false) nested(false) values(left,right)
parameter name(sequencesuffix) type(alfa) mandatory(false) nested(false)
parameter name(prefix) type(alfa) mandatory(false) nested(false)
parameter name(limitlength) type(alfa) mandatory(false) nested(false) values(true,false) //It depends on the "length" parameter, default is True
command name(sequence) level(1)
parameter name(name) type(alfa) mandatory(true)
parameter name(period) type(alfa) mandatory(false) values(none,year,month,week,day)
(R22) Incremental search: permette di definire le regole con le quali vibecode costruisce la ricerca incrementale di una classe.
incrementalsearch class(kMediaLibrary_PublishingHouse) searchproperty(code,description) displayproperties(code,description)
incrementalsearch class (….): specifica la classe per cui si sta definendo la ricerca incrementale;
searchproperty (code, description): indica le proprietà visualizzabili nel menù di ricerca, a cui si accede cliccando sui tre puntini, alla destra del record.
displayproperties (campi che vedo nel menù a tendina della ricerca incrementale.)
Il displaytextproperties mostra invece il campo visualizzato quando il record è selezionato sulla barra di ricerca. Se non specificato (come nel caso in questione) l’ordine visualizzato sarà quello delle proprietà presenti all’interno del parametro searchproperties.
![](../__attachments/1658912771/image-20221011-165444.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
Sotto lo schema command con tutti i comandi a disposizione:
command name(incrementalsearch) level(1)
parameter name(class) type(alfa) mandatory(true) nested(false)
parameter name(searchproperty) type(alfa) mandatory(true) nested(false) // array of string
parameter name(displaytextproperties) type(alfa) mandatory(false) nested(false) list(true) // array of string
parameter name(displayproperties) type(alfa) mandatory(true) nested(false) list(true)
parameter name(displaypropertiesnames) type(alfa) mandatory(true) nested(false) list(true) // array of string, subset of displayproperties, stated the properties that will be shown with the properties title as prefix
parameter name(dependson) type(alfa) mandatory(false) nested(false) list(true)
parameter name(filter) type(alfa) mandatory(false) nested(false) list(false)
parameter name(additionalproperties) type(alfa) mandatory(false) // fields list separated by comma that will be added to incremental search result
parameter name(extraparams) type(alfa) mandatory(false) // DEPRECATED will be removed!! extra parameters key=value list separated by comma that will be added to incremental call
Per visualizzare ciascun comando e le relative proprietà, i parametri (obbligatori o meno) che lo compongono e i valori che questi ultimi possono assumere consultare il file Schema_Command.vibs (percorso file: C:\Vibecode\Current Version\Apps\Core\vibs\Commands).
3. Creare le Funzioni
Creare sotto la cartella vibs la sottocartella “Functions” con al suo interno tutte le function della nostra app: sia funzioni primitive (script che mi permettono di eseguire servizi), sia crud (ogni classe può avere il suo crud di riferimento in cui le proprietà vengono personalizzate). (Vedi nel menù: “Funzioni e CRUD”).
![](../__attachments/1658912771/image-20221011-151243.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
Il comando crud permette di definire quali campi di una determinata classe voglio visualizzare nell'interfaccia di vibecode e in quale modo.
Esempio di crud fMediaLibrary_Author
sulla classe kMediaLibrary_Author
:
crud name(fMediaLibrary_Author) classname(kMediaLibrary_Author) loaddefault(false) vieworder(field(description) direction(asc))
crudfield name(id) title(id) mandatory(false) insert(hide) update(hide) view(false) viewhide(true)
// kMediaLibrary_Author
crudfield name(Code) search(true) insert(protected) update(protected)
crudfield name(Description) search(true) insert(protected)
crudfield name(Name)
crudfield name(Surname)
crudfield name(DateOfBirth) view(false)
crudfield name(DateOfDeath) view(false)
crudfield name(Nationality)
crudfield name(Picture) view(false)
crudsectionbegin title(Remark) property(Remark)
crudfield name(Remark.Text) view(false)
crudsectionend
(R1) Le funzioni vengono sempre scritte nella seguente maniera: f + name della classe corrispondente ->fMediaLibrary_Author
All’interno del comando classname(..) verrà messo il riferimento alla classe per la quale si sta generando la funzione.
Il comando loaddefault(..) è facoltativo e può assumere valore true o false. Quando è a true significa che tutti i campi che non verranno specificati nel crud ma che appartengono comunque alla classe verranno visualizzati (nell’interfaccia grafica) in fondo alla pagina.
vieworder(field(…) direction(...)) indica in che ordine i record della tabella verranno visualizzati. Nel caso in considerazione: per descrizione, in ordine crescente.
(R2-14) Gli elementi del crud vengono introdotti dal parametro “crudfield(name), dove il name corrisponde esattamente al nome di quella proprietà all’interno della classe di riferimento.
Vediamo alcuni comandi, o particolarità:
(R2) I comandi insert (hide) update (hide) viewhide(true) indicano che non si ha la possibilità di inserire, modificare nè tantomeno visualizzare il campo in questione, cioè l’id: l’azione di inserimento e modifica è nascosta all’utente.
(R8) il comando view(false) indica che quel campo non dovrà essere visualizzato sulla griglia esterna della classe in questione. (Es. la data di nascita dell’autore è visibile in fase di editazione ma non nella griglia esterna, una volta aggiunto il record.)
![](../__attachments/1658912771/image-20221018-153151.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
![](../__attachments/1658912771/image-20221018-153339.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
(R12-14): crudsectionbegin…crudsectionend : le proprietà contenute all’interno costituiscono dei sottodettagli.
4. Creare il menù
Per poter visualizzare sul browser quando visto finora non ci resta che creare il menù.
Creare sotto la cartella vibs la sottocartella “Menu” con al suo interno un notepad++, rinominato mMediaLibrary.vibs: andremo a definire qui il menù della nostra app.
![](../__attachments/1658912771/image-20221018-155148.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
menu name(mainmenu) title(MediaLibrary)
menuitem name(item01) title(Books) parent() type(execfunction) data(app(MediaLibrary) name(fMediaLibrary_Book))
menuitem name(item02) title(Loans) parent() type(execfunction) data(app(MediaLibrary) name(fMediaLibrary_Loan))
menuitem name(item03) title(Master Data) parent() data()
menuitem name(item0301) title(Authors) parent(item03) type(execfunction) data(app(MediaLibrary) name(fMediaLibrary_Author))
menuitem name(item0302) title(Languages) parent(item03) type(execfunction) data(app(crud) name(kLanguage))
menuitem name(item0303) title(Publishing Houses) parent(item03) type(execfunction) data(app(MediaLibrary) name(fMediaLibrary_PublishingHouse))
menuitem name(item0304) title(Borrowers) parent(item03) type(execfunction) data(app(MediaLibrary) name(fMediaLibrary_Borrower))
menuitem name(item04) title(Dashboard Analytics) parent() type(execfunction) data(app(MediaLibrary) name(fMediaLibrary_Dashboard))
Di seguito la semplicissima sintassi da utilizzare per poter scrivere un menù su Vibecode:
(R1): Dichiariamo il menù e aggiungiamo il titolo che vorremmo visualizzare nell’interfaccia grafica: title(..)
(R2-R9): questa la struttura di ogni singola voce di menù:
menuitem name (…) : il nome che assegniamo alla voce di menù, un nome interno che serve a stabilire la gerarchia delle varie voci: es. item03; ad una sottovoce di quella riga di menù potremmo assegnare il nome item0301 (R5).
title (…): con che nome vogliamo vedere quella voce di menù nell’interfaccia grafica
parent (…): indica da quale voce di menù deriva la riga che stiamo aggiungendo. Es. la riga menuitem name(item0302)
(Languages) ha come parent(item03)
(Master Data).
type(exefunction) : indica che deve essere eseguita la funzione riportata sulla riga in questione.
data(app(MediaLibrary) name (fMediaLibrary_Book)): il primo valore riporta il nome della app, il name richiama la specifica funzione che vogliamo visualizzare in quella riga di menù.
![](../__attachments/1658912771/image-20221018-155956.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
Il menù della nostra app!
5. Far Partire Vibecode
Per visualizzare quanto appena realizzato non ci resterà che avviare vibecode tramite “Servizio”.
![](../__attachments/1658912771/image-20221028-142843.png?inst-v=f69edcf2-ba55-4797-bc4b-2115d052fa35)
App Media Library