[{"data":1,"prerenderedAt":773},["ShallowReactive",2],{"\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fstudio":3,"section:\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fstudio":772},{"id":4,"title":5,"body":6,"description":753,"extension":754,"meta":755,"navigation":756,"path":768,"seo":769,"stem":770,"__hash__":771},"docs\u002F2.next\u002F3.dodock\u002F10.developpeurs\u002Fstudio.md","Studio - Référence développeur",{"type":7,"value":8,"toc":735},"minimark",[9,13,23,35,40,48,51,82,93,99,106,122,128,132,149,153,160,251,258,264,294,311,342,346,352,360,383,387,390,442,445,449,555,559,588,592,595,609,613,689,693,708,711,731],[10,11,5],"h1",{"id":12},"studio-référence-développeur",[14,15,16,17,22],"p",{},"Studio est un module de Dodock qui construit et publie des applications Vue de façon visuelle. Cette page documente les commandes, hooks et procédures d'intégration pour les développeurs d'applications. Pour l'utilisation de l'éditeur, voir ",[18,19,21],"a",{"href":20},"\u002Fnext\u002Fdodock\u002Fpersonnalisation\u002Fstudio","Studio",".",[24,25,28],"callout",{"color":26,"icon":27},"amber","i-mdi-flask-outline",[14,29,30,34],{},[31,32,33],"strong",{},"Aperçu."," Studio est présent seulement dans la version en développement de Dodock. Il n'est pas encore disponible sur les sites en production, ni en v5.",[36,37,39],"h2",{"id":38},"commandes-bench","Commandes bench",[41,42,44],"h3",{"id":43},"bench-build-studio-app-app",[45,46,47],"code",{},"bench build-studio-app \u003Capp>",[14,49,50],{},"Compile une application Studio spécifique pour la production.",[52,53,58],"pre",{"className":54,"code":55,"language":56,"meta":57,"style":57},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","bench --site mon_site build-studio-app mon_app_studio\n","bash","",[45,59,60],{"__ignoreMap":57},[61,62,65,69,73,76,79],"span",{"class":63,"line":64},"line",1,[61,66,68],{"class":67},"sBMFI","bench",[61,70,72],{"class":71},"sfazB"," --site",[61,74,75],{"class":71}," mon_site",[61,77,78],{"class":71}," build-studio-app",[61,80,81],{"class":71}," mon_app_studio\n",[14,83,84,85,88,89,92],{},"L'argument est le nom du document ",[31,86,87],{},"Studio App",", pas celui de l'application Python. L'option ",[45,90,91],{},"--site"," est facultative si un site par défaut est défini. En cas d'échec, la commande affiche la référence du journal d'erreurs (Error Log) et se termine avec le code 1.",[41,94,96],{"id":95},"bench-site-site-watch-studio",[45,97,98],{},"bench --site \u003Csite> watch-studio",[14,100,101,102,105],{},"Surveille les dossiers d'applications Studio exportées (",[45,103,104],{},"\u003Capp>\u002Fstudio\u002F",") et importe automatiquement les définitions JSON dans le site lorsque les fichiers changent sur le disque.",[52,107,109],{"className":54,"code":108,"language":56,"meta":57,"style":57},"bench --site mon_site watch-studio\n",[45,110,111],{"__ignoreMap":57},[61,112,113,115,117,119],{"class":63,"line":64},[61,114,68],{"class":67},[61,116,72],{"class":71},[61,118,75],{"class":71},[61,120,121],{"class":71}," watch-studio\n",[14,123,124,125,22],{},"Cette commande est un processus dédié : elle n'est pas démarrée automatiquement par ",[45,126,127],{},"bench start",[41,129,131],{"id":130},"build-de-léditeur","Build de l'éditeur",[14,133,134,135,138,139,142,143,146,147,22],{},"L'éditeur Studio est une application Vite (",[45,136,137],{},"studio\u002Ffrontend\u002F"," dans le dépôt du framework). ",[45,140,141],{},"bench build"," le construit avec les assets de ",[45,144,145],{},"frappe",". Un échec de ce build fait échouer ",[45,148,141],{},[36,150,152],{"id":151},"hooks-dintégration","Hooks d'intégration",[14,154,155,156,159],{},"Studio s'intègre au framework via ",[45,157,158],{},"frappe\u002Fhooks.py"," :",[161,162,163,176],"table",{},[164,165,166],"thead",{},[167,168,169,173],"tr",{},[170,171,172],"th",{},"Hook",[170,174,175],{},"Description",[177,178,179,193,207,221,231,241],"tbody",{},[167,180,181,187],{},[182,183,184],"td",{},[45,185,186],{},"page_renderer",[182,188,189,192],{},[45,190,191],{},"StudioAppRenderer"," affiche les pages d'une application publiée.",[167,194,195,200],{},[182,196,197],{},[45,198,199],{},"website_route_rules",[182,201,202,203,206],{},"La route ",[45,204,205],{},"\u002Fstudio\u002F\u003Cpath>"," sert l'éditeur Studio, permettant le rechargement des routes internes de l'éditeur.",[167,208,209,214],{},[182,210,211],{},[45,212,213],{},"after_app_install",[182,215,216,217,220],{},"Importe les applications Studio exportées présentes dans le dossier ",[45,218,219],{},"studio\u002F"," de l'application venant d'être installée.",[167,222,223,228],{},[182,224,225],{},[45,226,227],{},"after_migrate",[182,229,230],{},"Importe les applications Studio exportées de toutes les applications, supprime les enregistrements dont les fichiers sources ont disparu, et reconstruit les applications publiées.",[167,232,233,238],{},[182,234,235],{},[45,236,237],{},"after_app_build",[182,239,240],{},"Compile les applications Studio exportées des applications dont le build vient d'être effectué.",[167,242,243,248],{},[182,244,245],{},[45,246,247],{},"before_app_uninstall",[182,249,250],{},"Supprime les applications Studio appartenant à l'application en cours de désinstallation.",[41,252,254,255],{"id":253},"hook-personnalisé-studio_app_boot","Hook personnalisé : ",[45,256,257],{},"studio_app_boot",[14,259,260,261,263],{},"Les applications peuvent définir un hook ",[45,262,257],{}," pour fournir des données au démarrage d'une application Studio publiée :",[52,265,269],{"className":266,"code":267,"language":268,"meta":57,"style":57},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# hooks.py de votre application\nstudio_app_boot = {\n    \"mon_app_studio\": \"mon_app.studio.boot.get_boot_data\"\n}\n","python",[45,270,271,276,282,288],{"__ignoreMap":57},[61,272,273],{"class":63,"line":64},[61,274,275],{},"# hooks.py de votre application\n",[61,277,279],{"class":63,"line":278},2,[61,280,281],{},"studio_app_boot = {\n",[61,283,285],{"class":63,"line":284},3,[61,286,287],{},"    \"mon_app_studio\": \"mon_app.studio.boot.get_boot_data\"\n",[61,289,291],{"class":63,"line":290},4,[61,292,293],{},"}\n",[14,295,296,297,299,300,303,304,307,308,310],{},"La clé est le nom du document ",[31,298,87],{},". La méthode est appelée ",[31,301,302],{},"sans argument",". Elle retourne un dictionnaire, disponible sous ",[45,305,306],{},"boot"," dans l'application publiée. Si plusieurs applications déclarent la même clé, la dernière méthode déclarée est utilisée. Si la méthode lève une exception, l'erreur est enregistrée dans le journal des erreurs et ",[45,309,306],{}," est vide.",[52,312,314],{"className":266,"code":313,"language":268,"meta":57,"style":57},"def get_boot_data():\n    return {\n        \"api_base\": frappe.utils.get_url(),\n        \"feature_flags\": get_feature_flags(),\n    }\n",[45,315,316,321,326,331,336],{"__ignoreMap":57},[61,317,318],{"class":63,"line":64},[61,319,320],{},"def get_boot_data():\n",[61,322,323],{"class":63,"line":278},[61,324,325],{},"    return {\n",[61,327,328],{"class":63,"line":284},[61,329,330],{},"        \"api_base\": frappe.utils.get_url(),\n",[61,332,333],{"class":63,"line":290},[61,334,335],{},"        \"feature_flags\": get_feature_flags(),\n",[61,337,339],{"class":63,"line":338},5,[61,340,341],{},"    }\n",[36,343,345],{"id":344},"structure-des-applications-exportées","Structure des applications exportées",[14,347,348,349,351],{},"Une application Studio exportée se trouve dans le dossier ",[45,350,219],{}," de votre application :",[52,353,358],{"className":354,"code":356,"language":357},[355],"language-text","mon_app\u002F\n└── studio\u002F\n    └── mon_app_studio\u002F\n        ├── mon_app_studio.json      # Définition de l'application\n        ├── studio_page\u002F             # Un dossier par page\n        │   └── accueil\u002F\n        │       ├── accueil.json     # Définition de la page\n        │       └── accueil.ts       # Script de la page\n        └── studio_components\u002F       # Composants réutilisables\n            └── mon_composant.json\n","text",[45,359,356],{"__ignoreMap":57},[14,361,362,363,366,367,370,371,374,375,378,379,382],{},"Un dossier est reconnu comme application Studio lorsqu'il contient un fichier ",[45,364,365],{},"\u003Cnom_du_dossier>.json"," à sa racine. Le nom du dossier est le nom de l'application normalisé (",[45,368,369],{},"mon-app"," devient ",[45,372,373],{},"mon_app","). Pour une page exportée, le script est lu dans le fichier ",[45,376,377],{},".ts"," : le champ ",[45,380,381],{},"script"," de la base reste vide.",[36,384,386],{"id":385},"migration-depuis-lapplication-studio-autonome","Migration depuis l'application Studio autonome",[14,388,389],{},"Si votre site utilisait l'application Studio autonome (avant son intégration au framework), la migration est automatique lors de la mise à jour :",[391,392,393,400,435],"ol",{},[394,395,396,397],"li",{},"Mettez à jour Dokos et exécutez ",[45,398,399],{},"bench migrate",[394,401,402,403,406,407],{},"Le patch ",[45,404,405],{},"frappe.patches.v16_0.merge_studio_into_frappe"," effectue les opérations suivantes :\n",[408,409,410,420,425,432],"ul",{},[394,411,412,413,416,417],{},"Transfère les enregistrements de patch de l'ancien module ",[45,414,415],{},"studio.studio.doctype.*"," vers ",[45,418,419],{},"frappe.studio.doctype.*",[394,421,422,423],{},"Réassigne le module « Studio » à l'application ",[45,424,145],{},[394,426,427,428,431],{},"Retire ",[45,429,430],{},"studio"," de la liste des applications installées",[394,433,434],{},"Efface le cache et reconstruit la carte des modules",[394,436,437,438,441],{},"Une fois tous les sites du bench migrés, exécutez ",[45,439,440],{},"bench remove-app studio"," pour supprimer définitivement l'ancienne application",[14,443,444],{},"Les applications, pages et composants existants sont conservés ; seules les appartenances de module changent.",[36,446,448],{"id":447},"doctypes-studio","DocTypes Studio",[161,450,451,460],{},[164,452,453],{},[167,454,455,458],{},[170,456,457],{},"DocType",[170,459,175],{},[177,461,462,471,481,491,501,511,521,531,541],{},[167,463,464,468],{},[182,465,466],{},[31,467,87],{},[182,469,470],{},"Application Studio (titre, icône, route de publication, statut publié)",[167,472,473,478],{},[182,474,475],{},[31,476,477],{},"Studio Page",[182,479,480],{},"Page d'une application (route, arbre de blocs, scripts, ressources, variables)",[167,482,483,488],{},[182,484,485],{},[31,486,487],{},"Studio Component",[182,489,490],{},"Composant réutilisable (propriétés, styles, code Vue)",[167,492,493,498],{},[182,494,495],{},[31,496,497],{},"Studio Component Input",[182,499,500],{},"Champ de saisie d'un composant personnalisé",[167,502,503,508],{},[182,504,505],{},[31,506,507],{},"Studio Page Resource",[182,509,510],{},"Source de données d'une page (Document List, Document, API Resource)",[167,512,513,518],{},[182,514,515],{},[31,516,517],{},"Studio Page Variable",[182,519,520],{},"Variable d'une page (nom, type, valeur par défaut)",[167,522,523,528],{},[182,524,525],{},[31,526,527],{},"Studio AI Session",[182,529,530],{},"Session de conversation avec l'assistant IA",[167,532,533,538],{},[182,534,535],{},[31,536,537],{},"Studio AI Message",[182,539,540],{},"Message individuel dans une session IA",[167,542,543,548],{},[182,544,545],{},[31,546,547],{},"Studio Settings",[182,549,550,551,554],{},"Paramètres globaux de Studio (clé API du fournisseur IA, ",[45,552,553],{},"ai_api_key",")",[36,556,558],{"id":557},"architecture","Architecture",[408,560,561,571,579],{},[394,562,563,566,567,570],{},[31,564,565],{},"Backend"," : ",[45,568,569],{},"frappe\u002Fstudio\u002F"," contient les DocTypes, l'API Python, la synchronisation des fichiers JSON, le build et les fonctionnalités IA.",[394,572,573,566,576,578],{},[31,574,575],{},"Frontend éditeur",[45,577,137],{}," (à la racine du dépôt du framework) contient l'éditeur Vue (Vite), le renderer et le build par application. Il possède son propre lockfile.",[394,580,581,584,585,587],{},[31,582,583],{},"Rendu des applications publiées"," : les pages sont rendues côté serveur via ",[45,586,191],{}," et servies comme des pages web.",[36,589,591],{"id":590},"dépendances-ia","Dépendances IA",[14,593,594],{},"Les fonctionnalités d'assistant IA nécessitent les dépendances Python suivantes dans l'environnement :",[408,596,597,603],{},[394,598,599,602],{},[45,600,601],{},"litellm"," (appel aux modèles de langage)",[394,604,605,608],{},[45,606,607],{},"json_repair"," (réparation des réponses JSON des modèles)",[36,610,612],{"id":611},"sécurité","Sécurité",[408,614,615,621,627,636,679],{},[394,616,617,620],{},[31,618,619],{},"Sessions IA"," : chaque utilisateur ne peut accéder qu'à ses propres sessions et messages. Les sessions et messages ne sont pas créés depuis l'interface desk.",[394,622,623,626],{},[31,624,625],{},"Exécution IA"," : une session IA (une par page) traite une seule demande à la fois. La demande est exécutée en tâche de fond. Une deuxième demande sur la même session reçoit une réponse HTTP 429 (« Another AI request is still processing »).",[394,628,629,632,633,635],{},[31,630,631],{},"Clé API"," : la clé du fournisseur (OpenRouter) se saisit dans ",[31,634,547],{},". Sans clé, l'assistant affiche une erreur.",[394,637,638,641,642,645,646,645,649,645,652,655,656,658,659,645,661,645,664,645,667,670,671,674,675,678],{},[31,639,640],{},"Fichiers"," : l'explorateur de fichiers de Studio (",[45,643,644],{},"list_studio_files",", ",[45,647,648],{},"read_studio_file",[45,650,651],{},"write_studio_file",[45,653,654],{},"delete_studio_file",") ne fonctionne qu'en mode développeur. Il demande la permission de lecture (ou d'écriture) sur ",[31,657,87],{},". Le chemin demandé est résolu, liens symboliques compris, et doit rester dans le dossier de l'application exportée. Seules les extensions ",[45,660,377],{},[45,662,663],{},".js",[45,665,666],{},".vue",[45,668,669],{},".json"," et ",[45,672,673],{},".css"," sont acceptées : aucun fichier ",[45,676,677],{},".py"," ne peut être lu ni écrit.",[394,680,681,684,685,688],{},[31,682,683],{},"Applications standard"," : une application marquée ",[31,686,687],{},"Is Standard"," ne peut être modifiée dans l'éditeur qu'en mode développeur.",[36,690,692],{"id":691},"permissions","Permissions",[14,694,695,696,670,699,702,703,705,706,22],{},"L'accès à Studio est contrôlé par les permissions sur les DocTypes Studio. Par défaut, les rôles ",[31,697,698],{},"System Manager",[31,700,701],{},"Studio User"," ont accès aux applications, pages et sessions IA. Seul ",[31,704,698],{}," a accès à ",[31,707,547],{},[14,709,710],{},"Pour restreindre l'accès à Studio :",[391,712,713,719,728],{},[394,714,715,716],{},"Allez dans ",[31,717,718],{},"Gestion des rôles",[394,720,721,722,645,724,645,726],{},"Modifiez les permissions du rôle concerné pour les DocTypes ",[45,723,87],{},[45,725,477],{},[45,727,487],{},[394,729,730],{},"Cochez ou décochez les droits de lecture, écriture, création et suppression selon les besoins",[732,733,734],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":57,"searchDepth":64,"depth":278,"links":736},[737,742,746,747,748,749,750,751,752],{"id":38,"depth":278,"text":39,"children":738},[739,740,741],{"id":43,"depth":284,"text":47},{"id":95,"depth":284,"text":98},{"id":130,"depth":284,"text":131},{"id":151,"depth":278,"text":152,"children":743},[744],{"id":253,"depth":284,"text":745},"Hook personnalisé : studio_app_boot",{"id":344,"depth":278,"text":345},{"id":385,"depth":278,"text":386},{"id":447,"depth":278,"text":448},{"id":557,"depth":278,"text":558},{"id":590,"depth":278,"text":591},{"id":611,"depth":278,"text":612},{"id":691,"depth":278,"text":692},"Intégrer Studio dans vos applications Dokos - commandes bench, hooks et migration","md",{"reviewed":756,"last_verified":757,"sources":758},true,"2026-11-04",[759,760,761,762,763,764,765,766,767],"dodock@develop:frappe\u002Fstudio\u002Fcommands\u002F__init__.py","dodock@develop:frappe\u002Fhooks.py","dodock@develop:frappe\u002Fstudio\u002Fsync.py","dodock@develop:frappe\u002Fstudio\u002Futils.py","dodock@develop:frappe\u002Fstudio\u002Fapi.py","dodock@develop:frappe\u002Fstudio\u002Fdoctype\u002Fstudio_app\u002Fstudio_app.py","dodock@develop:frappe\u002Fpatches\u002Fv16_0\u002Fmerge_studio_into_frappe.py","dodock@develop:esbuild\u002Fesbuild.js","dodock@develop:pyproject.toml","\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fstudio",{"title":5,"description":753},"2.next\u002F3.dodock\u002F10.developpeurs\u002Fstudio","vsGI8rEQme1lzXVj3alZuDUdB-tA62aNe0uNQrXwZLg",{},1793871752108]