[{"data":1,"prerenderedAt":2594},["ShallowReactive",2],{"doc-global_essentials\u002Fadvanced\u002Fvue":3},{"id":4,"title":5,"body":6,"description":2585,"extension":2586,"meta":2587,"navigation":209,"path":2590,"seo":2591,"stem":2592,"__hash__":2593},"docs\u002Fdocs\u002Fglobal_essentials\u002Fadvanced\u002Fvue.md","Vue Components",{"type":7,"value":8,"toc":2567},"minimark",[9,14,23,31,34,39,42,47,107,112,142,145,147,151,158,423,428,439,668,672,724,728,735,811,852,859,958,964,1131,1133,1137,1143,1208,1213,1365,1367,1371,1378,1384,1450,1454,1465,1470,1676,1680,1687,1695,1697,1701,1704,2118,2121,2123,2127,2134,2137,2356,2358,2362,2365,2384,2389,2444,2446,2450,2534,2536,2540,2563],[10,11,13],"h1",{"id":12},"vue-3-modern-way-to-build-complex-ui","Vue 3 - Modern Way to Build Complex UI",[15,16,17,18,22],"p",{},"Dryad Engine is developed with ",[19,20,21],"strong",{},"Vue 3",", a modern JavaScript framework for building user interfaces. This means all the engine's UI components - from character sheets to inventory screens to dialogue overlays - are Vue components that you can extend, replace, or build upon.",[15,24,25,26,30],{},"The engine exposes Vue and its APIs through ",[27,28,29],"code",{},"window.engine",", so you can create custom components that integrate seamlessly with the existing UI without setting up any build tools.",[32,33],"hr",{},[35,36,38],"h2",{"id":37},"what-is-vue","What is Vue?",[15,40,41],{},"Vue is a framework that makes building interactive UIs simple and organized. Instead of manually manipulating HTML elements, you describe what the UI should look like based on your data, and Vue keeps everything in sync automatically.",[15,43,44],{},[19,45,46],{},"Key concepts:",[48,49,50,63],"table",{},[51,52,53],"thead",{},[54,55,56,60],"tr",{},[57,58,59],"th",{},"Concept",[57,61,62],{},"What it does",[64,65,66,77,87,97],"tbody",{},[54,67,68,74],{},[69,70,71],"td",{},[19,72,73],{},"Components",[69,75,76],{},"Reusable UI building blocks (buttons, panels, entire screens)",[54,78,79,84],{},[69,80,81],{},[19,82,83],{},"Reactive data",[69,85,86],{},"When data changes, the UI updates automatically",[54,88,89,94],{},[69,90,91],{},[19,92,93],{},"Templates",[69,95,96],{},"HTML-like syntax to describe your component's structure",[54,98,99,104],{},[69,100,101],{},[19,102,103],{},"Props",[69,105,106],{},"Pass data from parent to child components",[15,108,109],{},[19,110,111],{},"Learning resources:",[113,114,115,126,134],"ul",{},[116,117,118,125],"li",{},[119,120,124],"a",{"href":121,"rel":122},"https:\u002F\u002Fvuejs.org\u002Ftutorial\u002F",[123],"nofollow","Vue 3 Official Tutorial"," - Interactive beginner tutorial",[116,127,128,133],{},[119,129,132],{"href":130,"rel":131},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fintroduction.html",[123],"Vue 3 Documentation"," - Complete reference",[116,135,136,141],{},[119,137,140],{"href":138,"rel":139},"https:\u002F\u002Fwww.vuemastery.com\u002F",[123],"Vue Mastery"," - Video courses",[15,143,144],{},"You don't need deep Vue knowledge to customize Dryad Engine - the patterns shown here will cover most use cases.",[32,146],{},[35,148,150],{"id":149},"creating-custom-components","Creating Custom Components",[15,152,153,154,157],{},"Components are defined using ",[27,155,156],{},"defineComponent()",":",[159,160,165],"pre",{"className":161,"code":162,"language":163,"meta":164,"style":164},"language-javascript shiki shiki-themes github-light github-dark","const { game, vue, primeVue } = window.engine;\n\n\u002F\u002F Import ref and defineComponent from vue to use in components\nconst { ref, defineComponent } = vue;\n\nconst MyComponent = defineComponent({\n  \u002F\u002F Register other components to use in template\n  components: {\n    Button: primeVue.Button\n  },\n  setup() {\n    const message = ref(\"Hello!\");\n\n    function onClick() {\n      message.value = \"Clicked!\";\n    }\n\n    return { message, onClick };\n  },\n  template: \u002F*html*\u002F`\n    \u003Cdiv class=\"my-component\">\n      \u003Cp>{{ message }}\u003C\u002Fp>\n      \u003CButton label=\"Click me\" @click=\"onClick\" \u002F>\n    \u003C\u002Fdiv>\n  `\n});\n","javascript","",[27,166,167,204,211,218,240,245,263,269,275,281,287,296,320,325,336,350,356,361,370,375,387,393,399,405,411,417],{"__ignoreMap":164},[168,169,172,176,180,184,187,190,192,195,198,201],"span",{"class":170,"line":171},"line",1,[168,173,175],{"class":174},"szBVR","const",[168,177,179],{"class":178},"sVt8B"," { ",[168,181,183],{"class":182},"sj4cs","game",[168,185,186],{"class":178},", ",[168,188,189],{"class":182},"vue",[168,191,186],{"class":178},[168,193,194],{"class":182},"primeVue",[168,196,197],{"class":178}," } ",[168,199,200],{"class":174},"=",[168,202,203],{"class":178}," window.engine;\n",[168,205,207],{"class":170,"line":206},2,[168,208,210],{"emptyLinePlaceholder":209},true,"\n",[168,212,214],{"class":170,"line":213},3,[168,215,217],{"class":216},"sJ8bj","\u002F\u002F Import ref and defineComponent from vue to use in components\n",[168,219,221,223,225,228,230,233,235,237],{"class":170,"line":220},4,[168,222,175],{"class":174},[168,224,179],{"class":178},[168,226,227],{"class":182},"ref",[168,229,186],{"class":178},[168,231,232],{"class":182},"defineComponent",[168,234,197],{"class":178},[168,236,200],{"class":174},[168,238,239],{"class":178}," vue;\n",[168,241,243],{"class":170,"line":242},5,[168,244,210],{"emptyLinePlaceholder":209},[168,246,248,250,253,256,260],{"class":170,"line":247},6,[168,249,175],{"class":174},[168,251,252],{"class":182}," MyComponent",[168,254,255],{"class":174}," =",[168,257,259],{"class":258},"sScJk"," defineComponent",[168,261,262],{"class":178},"({\n",[168,264,266],{"class":170,"line":265},7,[168,267,268],{"class":216},"  \u002F\u002F Register other components to use in template\n",[168,270,272],{"class":170,"line":271},8,[168,273,274],{"class":178},"  components: {\n",[168,276,278],{"class":170,"line":277},9,[168,279,280],{"class":178},"    Button: primeVue.Button\n",[168,282,284],{"class":170,"line":283},10,[168,285,286],{"class":178},"  },\n",[168,288,290,293],{"class":170,"line":289},11,[168,291,292],{"class":258},"  setup",[168,294,295],{"class":178},"() {\n",[168,297,299,302,305,307,310,313,317],{"class":170,"line":298},12,[168,300,301],{"class":174},"    const",[168,303,304],{"class":182}," message",[168,306,255],{"class":174},[168,308,309],{"class":258}," ref",[168,311,312],{"class":178},"(",[168,314,316],{"class":315},"sZZnC","\"Hello!\"",[168,318,319],{"class":178},");\n",[168,321,323],{"class":170,"line":322},13,[168,324,210],{"emptyLinePlaceholder":209},[168,326,328,331,334],{"class":170,"line":327},14,[168,329,330],{"class":174},"    function",[168,332,333],{"class":258}," onClick",[168,335,295],{"class":178},[168,337,339,342,344,347],{"class":170,"line":338},15,[168,340,341],{"class":178},"      message.value ",[168,343,200],{"class":174},[168,345,346],{"class":315}," \"Clicked!\"",[168,348,349],{"class":178},";\n",[168,351,353],{"class":170,"line":352},16,[168,354,355],{"class":178},"    }\n",[168,357,359],{"class":170,"line":358},17,[168,360,210],{"emptyLinePlaceholder":209},[168,362,364,367],{"class":170,"line":363},18,[168,365,366],{"class":174},"    return",[168,368,369],{"class":178}," { message, onClick };\n",[168,371,373],{"class":170,"line":372},19,[168,374,286],{"class":178},[168,376,378,381,384],{"class":170,"line":377},20,[168,379,380],{"class":178},"  template: ",[168,382,383],{"class":216},"\u002F*html*\u002F",[168,385,386],{"class":315},"`\n",[168,388,390],{"class":170,"line":389},21,[168,391,392],{"class":315},"    \u003Cdiv class=\"my-component\">\n",[168,394,396],{"class":170,"line":395},22,[168,397,398],{"class":315},"      \u003Cp>{{ message }}\u003C\u002Fp>\n",[168,400,402],{"class":170,"line":401},23,[168,403,404],{"class":315},"      \u003CButton label=\"Click me\" @click=\"onClick\" \u002F>\n",[168,406,408],{"class":170,"line":407},24,[168,409,410],{"class":315},"    \u003C\u002Fdiv>\n",[168,412,414],{"class":170,"line":413},25,[168,415,416],{"class":315},"  `\n",[168,418,420],{"class":170,"line":419},26,[168,421,422],{"class":178},"});\n",[424,425,427],"h3",{"id":426},"props-passing-data-to-components","Props - Passing Data to Components",[15,429,430,431,434,435,438],{},"Props let parent components pass data to children. The engine's ",[27,432,433],{},"CharacterFace"," component uses a ",[27,436,437],{},"character"," prop:",[159,440,442],{"className":161,"code":441,"language":163,"meta":164,"style":164},"const { vue, components, game } = window.engine;\nconst { defineComponent } = vue;\nconst { CharacterFace } = components;\n\n\u002F\u002F create a custom CharacterCard component that utilizes the built-in CharacterFace component\nconst CharacterCard = defineComponent({\n  \u002F\u002F Register engine's CharacterFace component to use in template\n  components: { CharacterFace },\n  \u002F\u002F Declare what props this component accepts\n  props: ['character'],\n  setup(props) {\n    \u002F\u002F Access props.character in setup\n    const name = props.character.getName();\n    return { name };\n  },\n  template: \u002F*html*\u002F`\n    \u003Cdiv class=\"character-card\">\n      \u003C!-- Pass character prop to CharacterFace -->\n      \u003CCharacterFace :character=\"character\" \u002F>\n      \u003Cspan>{{ name }}\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n  `\n});\n\n\u002F\u002F Usage: pass a character object as prop\nconst alice = game.getCharacter(\"alice\");\n\u002F\u002F In another component's template: \u003CCharacterCard :character=\"alice\" \u002F>\n",[27,443,444,467,481,496,500,505,518,523,528,533,544,557,562,580,587,591,599,604,609,614,619,623,627,631,635,640,662],{"__ignoreMap":164},[168,445,446,448,450,452,454,457,459,461,463,465],{"class":170,"line":171},[168,447,175],{"class":174},[168,449,179],{"class":178},[168,451,189],{"class":182},[168,453,186],{"class":178},[168,455,456],{"class":182},"components",[168,458,186],{"class":178},[168,460,183],{"class":182},[168,462,197],{"class":178},[168,464,200],{"class":174},[168,466,203],{"class":178},[168,468,469,471,473,475,477,479],{"class":170,"line":206},[168,470,175],{"class":174},[168,472,179],{"class":178},[168,474,232],{"class":182},[168,476,197],{"class":178},[168,478,200],{"class":174},[168,480,239],{"class":178},[168,482,483,485,487,489,491,493],{"class":170,"line":213},[168,484,175],{"class":174},[168,486,179],{"class":178},[168,488,433],{"class":182},[168,490,197],{"class":178},[168,492,200],{"class":174},[168,494,495],{"class":178}," components;\n",[168,497,498],{"class":170,"line":220},[168,499,210],{"emptyLinePlaceholder":209},[168,501,502],{"class":170,"line":242},[168,503,504],{"class":216},"\u002F\u002F create a custom CharacterCard component that utilizes the built-in CharacterFace component\n",[168,506,507,509,512,514,516],{"class":170,"line":247},[168,508,175],{"class":174},[168,510,511],{"class":182}," CharacterCard",[168,513,255],{"class":174},[168,515,259],{"class":258},[168,517,262],{"class":178},[168,519,520],{"class":170,"line":265},[168,521,522],{"class":216},"  \u002F\u002F Register engine's CharacterFace component to use in template\n",[168,524,525],{"class":170,"line":271},[168,526,527],{"class":178},"  components: { CharacterFace },\n",[168,529,530],{"class":170,"line":277},[168,531,532],{"class":216},"  \u002F\u002F Declare what props this component accepts\n",[168,534,535,538,541],{"class":170,"line":283},[168,536,537],{"class":178},"  props: [",[168,539,540],{"class":315},"'character'",[168,542,543],{"class":178},"],\n",[168,545,546,548,550,554],{"class":170,"line":289},[168,547,292],{"class":258},[168,549,312],{"class":178},[168,551,553],{"class":552},"s4XuR","props",[168,555,556],{"class":178},") {\n",[168,558,559],{"class":170,"line":298},[168,560,561],{"class":216},"    \u002F\u002F Access props.character in setup\n",[168,563,564,566,569,571,574,577],{"class":170,"line":322},[168,565,301],{"class":174},[168,567,568],{"class":182}," name",[168,570,255],{"class":174},[168,572,573],{"class":178}," props.character.",[168,575,576],{"class":258},"getName",[168,578,579],{"class":178},"();\n",[168,581,582,584],{"class":170,"line":327},[168,583,366],{"class":174},[168,585,586],{"class":178}," { name };\n",[168,588,589],{"class":170,"line":338},[168,590,286],{"class":178},[168,592,593,595,597],{"class":170,"line":352},[168,594,380],{"class":178},[168,596,383],{"class":216},[168,598,386],{"class":315},[168,600,601],{"class":170,"line":358},[168,602,603],{"class":315},"    \u003Cdiv class=\"character-card\">\n",[168,605,606],{"class":170,"line":363},[168,607,608],{"class":315},"      \u003C!-- Pass character prop to CharacterFace -->\n",[168,610,611],{"class":170,"line":372},[168,612,613],{"class":315},"      \u003CCharacterFace :character=\"character\" \u002F>\n",[168,615,616],{"class":170,"line":377},[168,617,618],{"class":315},"      \u003Cspan>{{ name }}\u003C\u002Fspan>\n",[168,620,621],{"class":170,"line":389},[168,622,410],{"class":315},[168,624,625],{"class":170,"line":395},[168,626,416],{"class":315},[168,628,629],{"class":170,"line":401},[168,630,422],{"class":178},[168,632,633],{"class":170,"line":407},[168,634,210],{"emptyLinePlaceholder":209},[168,636,637],{"class":170,"line":413},[168,638,639],{"class":216},"\u002F\u002F Usage: pass a character object as prop\n",[168,641,642,644,647,649,652,655,657,660],{"class":170,"line":419},[168,643,175],{"class":174},[168,645,646],{"class":182}," alice",[168,648,255],{"class":174},[168,650,651],{"class":178}," game.",[168,653,654],{"class":258},"getCharacter",[168,656,312],{"class":178},[168,658,659],{"class":315},"\"alice\"",[168,661,319],{"class":178},[168,663,665],{"class":170,"line":664},27,[168,666,667],{"class":216},"\u002F\u002F In another component's template: \u003CCharacterCard :character=\"alice\" \u002F>\n",[424,669,671],{"id":670},"anatomy-of-a-component","Anatomy of a Component",[48,673,674,684],{},[51,675,676],{},[54,677,678,681],{},[57,679,680],{},"Part",[57,682,683],{},"Purpose",[64,685,686,696,706,715],{},[54,687,688,693],{},[69,689,690],{},[27,691,692],{},"setup()",[69,694,695],{},"Where you define reactive data, functions, and logic",[54,697,698,703],{},[69,699,700],{},[27,701,702],{},"template",[69,704,705],{},"HTML structure that displays your data",[54,707,708,712],{},[69,709,710],{},[27,711,456],{},[69,713,714],{},"Register other components to use inside this one",[54,716,717,721],{},[69,718,719],{},[27,720,553],{},[69,722,723],{},"Declare data this component receives from its parent",[424,725,727],{"id":726},"reactive-data","Reactive Data",[15,729,730,731,734],{},"Reactive data means the UI automatically updates when the data changes. In Dryad Engine, ",[19,732,733],{},"game objects are already reactive"," - characters, inventories, party, flags, and states are Vue reactive objects under the hood. When you mutate them, the UI updates automatically:",[159,736,738],{"className":161,"code":737,"language":163,"meta":164,"style":164},"setup() {\n  \u002F\u002F Game objects are reactive - pass them to template directly without the need to wrap in ref() or reactive()\n  const mc = game.getCharacter(\"mc\");\n  const party = game.getParty();\n\n  \u002F\u002F When mc or party change, the template updates automatically\n  return { mc, party };\n}\n",[27,739,740,747,752,773,789,793,798,806],{"__ignoreMap":164},[168,741,742,745],{"class":170,"line":171},[168,743,744],{"class":258},"setup",[168,746,295],{"class":178},[168,748,749],{"class":170,"line":206},[168,750,751],{"class":216},"  \u002F\u002F Game objects are reactive - pass them to template directly without the need to wrap in ref() or reactive()\n",[168,753,754,757,760,762,764,766,768,771],{"class":170,"line":213},[168,755,756],{"class":174},"  const",[168,758,759],{"class":182}," mc",[168,761,255],{"class":174},[168,763,651],{"class":178},[168,765,654],{"class":258},[168,767,312],{"class":178},[168,769,770],{"class":315},"\"mc\"",[168,772,319],{"class":178},[168,774,775,777,780,782,784,787],{"class":170,"line":220},[168,776,756],{"class":174},[168,778,779],{"class":182}," party",[168,781,255],{"class":174},[168,783,651],{"class":178},[168,785,786],{"class":258},"getParty",[168,788,579],{"class":178},[168,790,791],{"class":170,"line":242},[168,792,210],{"emptyLinePlaceholder":209},[168,794,795],{"class":170,"line":247},[168,796,797],{"class":216},"  \u002F\u002F When mc or party change, the template updates automatically\n",[168,799,800,803],{"class":170,"line":265},[168,801,802],{"class":174},"  return",[168,804,805],{"class":178}," { mc, party };\n",[168,807,808],{"class":170,"line":271},[168,809,810],{"class":178},"}\n",[159,812,816],{"className":813,"code":814,"language":815,"meta":164,"style":164},"language-html shiki shiki-themes github-light github-dark","\u003C!-- In template: access reactive properties directly -->\n\u003Cp>Health: {{ mc.getResource(\"health\") }} \u002F {{ mc.getStat(\"health\") }}\u003C\u002Fp>\n\u003Cp>Name: {{ mc.getTrait(\"name\") }}\u003C\u002Fp>\n","html",[27,817,818,823,839],{"__ignoreMap":164},[168,819,820],{"class":170,"line":171},[168,821,822],{"class":216},"\u003C!-- In template: access reactive properties directly -->\n",[168,824,825,828,831,834,836],{"class":170,"line":206},[168,826,827],{"class":178},"\u003C",[168,829,15],{"class":830},"s9eBZ",[168,832,833],{"class":178},">Health: {{ mc.getResource(\"health\") }} \u002F {{ mc.getStat(\"health\") }}\u003C\u002F",[168,835,15],{"class":830},[168,837,838],{"class":178},">\n",[168,840,841,843,845,848,850],{"class":170,"line":213},[168,842,827],{"class":178},[168,844,15],{"class":830},[168,846,847],{"class":178},">Name: {{ mc.getTrait(\"name\") }}\u003C\u002F",[168,849,15],{"class":830},[168,851,838],{"class":178},[15,853,854,855,858],{},"Use ",[27,856,857],{},"ref()"," for your own local component state:",[159,860,862],{"className":161,"code":861,"language":163,"meta":164,"style":164},"const { ref } = vue;\n\nsetup() {\n  \u002F\u002F Use ref() for local UI state you create\n  const isMenuOpen = ref(false);\n\n  function toggleMenu() {\n    isMenuOpen.value = !isMenuOpen.value;\n  }\n\n  return { isMenuOpen, toggleMenu };\n}\n",[27,863,864,878,882,888,893,911,915,925,938,943,947,954],{"__ignoreMap":164},[168,865,866,868,870,872,874,876],{"class":170,"line":171},[168,867,175],{"class":174},[168,869,179],{"class":178},[168,871,227],{"class":182},[168,873,197],{"class":178},[168,875,200],{"class":174},[168,877,239],{"class":178},[168,879,880],{"class":170,"line":206},[168,881,210],{"emptyLinePlaceholder":209},[168,883,884,886],{"class":170,"line":213},[168,885,744],{"class":258},[168,887,295],{"class":178},[168,889,890],{"class":170,"line":220},[168,891,892],{"class":216},"  \u002F\u002F Use ref() for local UI state you create\n",[168,894,895,897,900,902,904,906,909],{"class":170,"line":242},[168,896,756],{"class":174},[168,898,899],{"class":182}," isMenuOpen",[168,901,255],{"class":174},[168,903,309],{"class":258},[168,905,312],{"class":178},[168,907,908],{"class":182},"false",[168,910,319],{"class":178},[168,912,913],{"class":170,"line":247},[168,914,210],{"emptyLinePlaceholder":209},[168,916,917,920,923],{"class":170,"line":265},[168,918,919],{"class":174},"  function",[168,921,922],{"class":258}," toggleMenu",[168,924,295],{"class":178},[168,926,927,930,932,935],{"class":170,"line":271},[168,928,929],{"class":178},"    isMenuOpen.value ",[168,931,200],{"class":174},[168,933,934],{"class":174}," !",[168,936,937],{"class":178},"isMenuOpen.value;\n",[168,939,940],{"class":170,"line":277},[168,941,942],{"class":178},"  }\n",[168,944,945],{"class":170,"line":283},[168,946,210],{"emptyLinePlaceholder":209},[168,948,949,951],{"class":170,"line":289},[168,950,802],{"class":174},[168,952,953],{"class":178}," { isMenuOpen, toggleMenu };\n",[168,955,956],{"class":170,"line":298},[168,957,810],{"class":178},[15,959,854,960,963],{},[27,961,962],{},"computed()"," for derived values:",[159,965,967],{"className":161,"code":966,"language":163,"meta":164,"style":164},"const { computed } = vue;\n\nsetup() {\n  const mc = game.getCharacter(\"mc\");\n\n  \u002F\u002F Computed recalculates when dependencies change\n  const healthPercent = computed(() => {\n    const current = mc.getResource(\"health\");\n    const max = mc.getStat(\"health\");\n    return Math.round((current \u002F max) * 100);\n  });\n\n  return { mc, healthPercent };\n}\n",[27,968,969,984,988,994,1012,1016,1021,1042,1064,1084,1111,1116,1120,1127],{"__ignoreMap":164},[168,970,971,973,975,978,980,982],{"class":170,"line":171},[168,972,175],{"class":174},[168,974,179],{"class":178},[168,976,977],{"class":182},"computed",[168,979,197],{"class":178},[168,981,200],{"class":174},[168,983,239],{"class":178},[168,985,986],{"class":170,"line":206},[168,987,210],{"emptyLinePlaceholder":209},[168,989,990,992],{"class":170,"line":213},[168,991,744],{"class":258},[168,993,295],{"class":178},[168,995,996,998,1000,1002,1004,1006,1008,1010],{"class":170,"line":220},[168,997,756],{"class":174},[168,999,759],{"class":182},[168,1001,255],{"class":174},[168,1003,651],{"class":178},[168,1005,654],{"class":258},[168,1007,312],{"class":178},[168,1009,770],{"class":315},[168,1011,319],{"class":178},[168,1013,1014],{"class":170,"line":242},[168,1015,210],{"emptyLinePlaceholder":209},[168,1017,1018],{"class":170,"line":247},[168,1019,1020],{"class":216},"  \u002F\u002F Computed recalculates when dependencies change\n",[168,1022,1023,1025,1028,1030,1033,1036,1039],{"class":170,"line":265},[168,1024,756],{"class":174},[168,1026,1027],{"class":182}," healthPercent",[168,1029,255],{"class":174},[168,1031,1032],{"class":258}," computed",[168,1034,1035],{"class":178},"(() ",[168,1037,1038],{"class":174},"=>",[168,1040,1041],{"class":178}," {\n",[168,1043,1044,1046,1049,1051,1054,1057,1059,1062],{"class":170,"line":271},[168,1045,301],{"class":174},[168,1047,1048],{"class":182}," current",[168,1050,255],{"class":174},[168,1052,1053],{"class":178}," mc.",[168,1055,1056],{"class":258},"getResource",[168,1058,312],{"class":178},[168,1060,1061],{"class":315},"\"health\"",[168,1063,319],{"class":178},[168,1065,1066,1068,1071,1073,1075,1078,1080,1082],{"class":170,"line":277},[168,1067,301],{"class":174},[168,1069,1070],{"class":182}," max",[168,1072,255],{"class":174},[168,1074,1053],{"class":178},[168,1076,1077],{"class":258},"getStat",[168,1079,312],{"class":178},[168,1081,1061],{"class":315},[168,1083,319],{"class":178},[168,1085,1086,1088,1091,1094,1097,1100,1103,1106,1109],{"class":170,"line":283},[168,1087,366],{"class":174},[168,1089,1090],{"class":178}," Math.",[168,1092,1093],{"class":258},"round",[168,1095,1096],{"class":178},"((current ",[168,1098,1099],{"class":174},"\u002F",[168,1101,1102],{"class":178}," max) ",[168,1104,1105],{"class":174},"*",[168,1107,1108],{"class":182}," 100",[168,1110,319],{"class":178},[168,1112,1113],{"class":170,"line":289},[168,1114,1115],{"class":178},"  });\n",[168,1117,1118],{"class":170,"line":298},[168,1119,210],{"emptyLinePlaceholder":209},[168,1121,1122,1124],{"class":170,"line":322},[168,1123,802],{"class":174},[168,1125,1126],{"class":178}," { mc, healthPercent };\n",[168,1128,1129],{"class":170,"line":327},[168,1130,810],{"class":178},[32,1132],{},[35,1134,1136],{"id":1135},"using-engine-components","Using Engine Components",[15,1138,1139,1140,157],{},"The engine exports reusable components through ",[27,1141,1142],{},"window.engine.components",[159,1144,1146],{"className":161,"code":1145,"language":163,"meta":164,"style":164},"const { vue, components, game } = window.engine;\nconst { defineComponent } = vue;\nconst { CharacterFace, CharacterDoll, BackgroundAsset } = components;\n",[27,1147,1148,1170,1184],{"__ignoreMap":164},[168,1149,1150,1152,1154,1156,1158,1160,1162,1164,1166,1168],{"class":170,"line":171},[168,1151,175],{"class":174},[168,1153,179],{"class":178},[168,1155,189],{"class":182},[168,1157,186],{"class":178},[168,1159,456],{"class":182},[168,1161,186],{"class":178},[168,1163,183],{"class":182},[168,1165,197],{"class":178},[168,1167,200],{"class":174},[168,1169,203],{"class":178},[168,1171,1172,1174,1176,1178,1180,1182],{"class":170,"line":206},[168,1173,175],{"class":174},[168,1175,179],{"class":178},[168,1177,232],{"class":182},[168,1179,197],{"class":178},[168,1181,200],{"class":174},[168,1183,239],{"class":178},[168,1185,1186,1188,1190,1192,1194,1197,1199,1202,1204,1206],{"class":170,"line":213},[168,1187,175],{"class":174},[168,1189,179],{"class":178},[168,1191,433],{"class":182},[168,1193,186],{"class":178},[168,1195,1196],{"class":182},"CharacterDoll",[168,1198,186],{"class":178},[168,1200,1201],{"class":182},"BackgroundAsset",[168,1203,197],{"class":178},[168,1205,200],{"class":174},[168,1207,495],{"class":178},[15,1209,1210],{},[19,1211,1212],{},"Example - Custom panel showing party faces:",[159,1214,1216],{"className":161,"code":1215,"language":163,"meta":164,"style":164},"const { vue, components, game } = window.engine;\nconst { defineComponent } = vue;\nconst { CharacterFace } = components;\n\nconst PartyFaces = defineComponent({\n  components: { CharacterFace },\n  setup() {\n    const party = game.getParty();\n    return { party };\n  },\n  template: \u002F*html*\u002F`\n    \u003Cdiv class=\"party-faces\">\n      \u003Cdiv v-for=\"char in party\" :key=\"char.id\" class=\"face-slot\">\n        \u003CCharacterFace :character=\"char\" \u002F>\n        \u003Cspan>{{ char.getName() }}\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  `\n});\n",[27,1217,1218,1240,1254,1268,1272,1285,1289,1295,1309,1316,1320,1328,1333,1338,1343,1348,1353,1357,1361],{"__ignoreMap":164},[168,1219,1220,1222,1224,1226,1228,1230,1232,1234,1236,1238],{"class":170,"line":171},[168,1221,175],{"class":174},[168,1223,179],{"class":178},[168,1225,189],{"class":182},[168,1227,186],{"class":178},[168,1229,456],{"class":182},[168,1231,186],{"class":178},[168,1233,183],{"class":182},[168,1235,197],{"class":178},[168,1237,200],{"class":174},[168,1239,203],{"class":178},[168,1241,1242,1244,1246,1248,1250,1252],{"class":170,"line":206},[168,1243,175],{"class":174},[168,1245,179],{"class":178},[168,1247,232],{"class":182},[168,1249,197],{"class":178},[168,1251,200],{"class":174},[168,1253,239],{"class":178},[168,1255,1256,1258,1260,1262,1264,1266],{"class":170,"line":213},[168,1257,175],{"class":174},[168,1259,179],{"class":178},[168,1261,433],{"class":182},[168,1263,197],{"class":178},[168,1265,200],{"class":174},[168,1267,495],{"class":178},[168,1269,1270],{"class":170,"line":220},[168,1271,210],{"emptyLinePlaceholder":209},[168,1273,1274,1276,1279,1281,1283],{"class":170,"line":242},[168,1275,175],{"class":174},[168,1277,1278],{"class":182}," PartyFaces",[168,1280,255],{"class":174},[168,1282,259],{"class":258},[168,1284,262],{"class":178},[168,1286,1287],{"class":170,"line":247},[168,1288,527],{"class":178},[168,1290,1291,1293],{"class":170,"line":265},[168,1292,292],{"class":258},[168,1294,295],{"class":178},[168,1296,1297,1299,1301,1303,1305,1307],{"class":170,"line":271},[168,1298,301],{"class":174},[168,1300,779],{"class":182},[168,1302,255],{"class":174},[168,1304,651],{"class":178},[168,1306,786],{"class":258},[168,1308,579],{"class":178},[168,1310,1311,1313],{"class":170,"line":277},[168,1312,366],{"class":174},[168,1314,1315],{"class":178}," { party };\n",[168,1317,1318],{"class":170,"line":283},[168,1319,286],{"class":178},[168,1321,1322,1324,1326],{"class":170,"line":289},[168,1323,380],{"class":178},[168,1325,383],{"class":216},[168,1327,386],{"class":315},[168,1329,1330],{"class":170,"line":298},[168,1331,1332],{"class":315},"    \u003Cdiv class=\"party-faces\">\n",[168,1334,1335],{"class":170,"line":322},[168,1336,1337],{"class":315},"      \u003Cdiv v-for=\"char in party\" :key=\"char.id\" class=\"face-slot\">\n",[168,1339,1340],{"class":170,"line":327},[168,1341,1342],{"class":315},"        \u003CCharacterFace :character=\"char\" \u002F>\n",[168,1344,1345],{"class":170,"line":338},[168,1346,1347],{"class":315},"        \u003Cspan>{{ char.getName() }}\u003C\u002Fspan>\n",[168,1349,1350],{"class":170,"line":352},[168,1351,1352],{"class":315},"      \u003C\u002Fdiv>\n",[168,1354,1355],{"class":170,"line":358},[168,1356,410],{"class":315},[168,1358,1359],{"class":170,"line":363},[168,1360,416],{"class":315},[168,1362,1363],{"class":170,"line":372},[168,1364,422],{"class":178},[32,1366],{},[35,1368,1370],{"id":1369},"the-slot-system","The Slot System",[15,1372,1373,1374,1377],{},"Dryad Engine uses a ",[19,1375,1376],{},"slot system"," to organize where components appear in the UI. Think of slots as designated areas where you can plug in your own components - like LEGO bricks snapping into place.",[15,1379,854,1380,1383],{},[27,1381,1382],{},"game.addComponent()"," to register your component into a slot:",[159,1385,1387],{"className":161,"code":1386,"language":163,"meta":164,"style":164},"game.addComponent({\n  id: \"my-component-id\",\n  slot: \"slot-name\",\n  title: \"Display Title\",\n  component: MyComponent,\n  order: 1  \u002F\u002F Optional: controls position (lower = earlier)\n});\n",[27,1388,1389,1399,1410,1420,1430,1435,1446],{"__ignoreMap":164},[168,1390,1391,1394,1397],{"class":170,"line":171},[168,1392,1393],{"class":178},"game.",[168,1395,1396],{"class":258},"addComponent",[168,1398,262],{"class":178},[168,1400,1401,1404,1407],{"class":170,"line":206},[168,1402,1403],{"class":178},"  id: ",[168,1405,1406],{"class":315},"\"my-component-id\"",[168,1408,1409],{"class":178},",\n",[168,1411,1412,1415,1418],{"class":170,"line":213},[168,1413,1414],{"class":178},"  slot: ",[168,1416,1417],{"class":315},"\"slot-name\"",[168,1419,1409],{"class":178},[168,1421,1422,1425,1428],{"class":170,"line":220},[168,1423,1424],{"class":178},"  title: ",[168,1426,1427],{"class":315},"\"Display Title\"",[168,1429,1409],{"class":178},[168,1431,1432],{"class":170,"line":242},[168,1433,1434],{"class":178},"  component: MyComponent,\n",[168,1436,1437,1440,1443],{"class":170,"line":247},[168,1438,1439],{"class":178},"  order: ",[168,1441,1442],{"class":182},"1",[168,1444,1445],{"class":216},"  \u002F\u002F Optional: controls position (lower = earlier)\n",[168,1447,1448],{"class":170,"line":265},[168,1449,422],{"class":178},[424,1451,1453],{"id":1452},"state-based-slots","State-Based Slots",[15,1455,1456,1457,1460,1461,1464],{},"State-based slots show ",[19,1458,1459],{},"one component at a time"," based on a state value. The slot renders whichever component's ",[27,1462,1463],{},"id"," matches the current state.",[15,1466,1467],{},[19,1468,1469],{},"Example - Custom game state:",[159,1471,1473],{"className":161,"code":1472,"language":163,"meta":164,"style":164},"const { vue, game } = window.engine;\nconst { defineComponent } = vue;\n\nconst BattleScreen = defineComponent({\n  setup() {\n    function exitBattle() {\n      game.setState(\"game_state\", \"exploration\");\n    }\n    return { exitBattle };\n  },\n  template: \u002F*html*\u002F`\n    \u003Cdiv class=\"battle-screen\">\n      \u003Ch1>Battle!\u003C\u002Fh1>\n      \u003Cbutton @click=\"exitBattle\">Retreat\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  `\n});\n\ngame.addComponent({\n  id: \"battle\",           \u002F\u002F When game_state = \"battle\", this shows\n  slot: \"game_state\",\n  component: BattleScreen\n});\n\n\u002F\u002F Trigger it from anywhere:\ngame.setState(\"game_state\", \"battle\");\n",[27,1474,1475,1493,1507,1511,1524,1530,1539,1559,1563,1570,1574,1582,1587,1592,1597,1601,1605,1609,1613,1621,1634,1642,1647,1651,1655,1660],{"__ignoreMap":164},[168,1476,1477,1479,1481,1483,1485,1487,1489,1491],{"class":170,"line":171},[168,1478,175],{"class":174},[168,1480,179],{"class":178},[168,1482,189],{"class":182},[168,1484,186],{"class":178},[168,1486,183],{"class":182},[168,1488,197],{"class":178},[168,1490,200],{"class":174},[168,1492,203],{"class":178},[168,1494,1495,1497,1499,1501,1503,1505],{"class":170,"line":206},[168,1496,175],{"class":174},[168,1498,179],{"class":178},[168,1500,232],{"class":182},[168,1502,197],{"class":178},[168,1504,200],{"class":174},[168,1506,239],{"class":178},[168,1508,1509],{"class":170,"line":213},[168,1510,210],{"emptyLinePlaceholder":209},[168,1512,1513,1515,1518,1520,1522],{"class":170,"line":220},[168,1514,175],{"class":174},[168,1516,1517],{"class":182}," BattleScreen",[168,1519,255],{"class":174},[168,1521,259],{"class":258},[168,1523,262],{"class":178},[168,1525,1526,1528],{"class":170,"line":242},[168,1527,292],{"class":258},[168,1529,295],{"class":178},[168,1531,1532,1534,1537],{"class":170,"line":247},[168,1533,330],{"class":174},[168,1535,1536],{"class":258}," exitBattle",[168,1538,295],{"class":178},[168,1540,1541,1544,1547,1549,1552,1554,1557],{"class":170,"line":265},[168,1542,1543],{"class":178},"      game.",[168,1545,1546],{"class":258},"setState",[168,1548,312],{"class":178},[168,1550,1551],{"class":315},"\"game_state\"",[168,1553,186],{"class":178},[168,1555,1556],{"class":315},"\"exploration\"",[168,1558,319],{"class":178},[168,1560,1561],{"class":170,"line":271},[168,1562,355],{"class":178},[168,1564,1565,1567],{"class":170,"line":277},[168,1566,366],{"class":174},[168,1568,1569],{"class":178}," { exitBattle };\n",[168,1571,1572],{"class":170,"line":283},[168,1573,286],{"class":178},[168,1575,1576,1578,1580],{"class":170,"line":289},[168,1577,380],{"class":178},[168,1579,383],{"class":216},[168,1581,386],{"class":315},[168,1583,1584],{"class":170,"line":298},[168,1585,1586],{"class":315},"    \u003Cdiv class=\"battle-screen\">\n",[168,1588,1589],{"class":170,"line":322},[168,1590,1591],{"class":315},"      \u003Ch1>Battle!\u003C\u002Fh1>\n",[168,1593,1594],{"class":170,"line":327},[168,1595,1596],{"class":315},"      \u003Cbutton @click=\"exitBattle\">Retreat\u003C\u002Fbutton>\n",[168,1598,1599],{"class":170,"line":338},[168,1600,410],{"class":315},[168,1602,1603],{"class":170,"line":352},[168,1604,416],{"class":315},[168,1606,1607],{"class":170,"line":358},[168,1608,422],{"class":178},[168,1610,1611],{"class":170,"line":363},[168,1612,210],{"emptyLinePlaceholder":209},[168,1614,1615,1617,1619],{"class":170,"line":372},[168,1616,1393],{"class":178},[168,1618,1396],{"class":258},[168,1620,262],{"class":178},[168,1622,1623,1625,1628,1631],{"class":170,"line":377},[168,1624,1403],{"class":178},[168,1626,1627],{"class":315},"\"battle\"",[168,1629,1630],{"class":178},",           ",[168,1632,1633],{"class":216},"\u002F\u002F When game_state = \"battle\", this shows\n",[168,1635,1636,1638,1640],{"class":170,"line":389},[168,1637,1414],{"class":178},[168,1639,1551],{"class":315},[168,1641,1409],{"class":178},[168,1643,1644],{"class":170,"line":395},[168,1645,1646],{"class":178},"  component: BattleScreen\n",[168,1648,1649],{"class":170,"line":401},[168,1650,422],{"class":178},[168,1652,1653],{"class":170,"line":407},[168,1654,210],{"emptyLinePlaceholder":209},[168,1656,1657],{"class":170,"line":413},[168,1658,1659],{"class":216},"\u002F\u002F Trigger it from anywhere:\n",[168,1661,1662,1664,1666,1668,1670,1672,1674],{"class":170,"line":419},[168,1663,1393],{"class":178},[168,1665,1546],{"class":258},[168,1667,312],{"class":178},[168,1669,1551],{"class":315},[168,1671,186],{"class":178},[168,1673,1627],{"class":315},[168,1675,319],{"class":178},[424,1677,1679],{"id":1678},"injection-slots","Injection Slots",[15,1681,1682,1683,1686],{},"Injection slots render ",[19,1684,1685],{},"all registered components"," together. They act as extension points throughout the UI - tabs, toolbars, panels, and more.",[15,1688,1689,1690,1694],{},"See ",[119,1691,1693],{"href":1692},"\u002Fdocs\u002Fglobal_essentials\u002Fbuiltins\u002Fcomponent_slots","Component Slots"," for the full list of available slots and their default components.",[32,1696],{},[35,1698,1700],{"id":1699},"example-adding-a-character-tab","Example: Adding a Character Tab",[15,1702,1703],{},"Add a custom tab to the character sheet that shows alongside Stats, Skills, and Inventory:",[159,1705,1707],{"className":161,"code":1706,"language":163,"meta":164,"style":164},"const { vue, game } = window.engine;\nconst { defineComponent, computed } = vue;\n\nconst NotesTab = defineComponent({\n  setup() {\n    \u002F\u002F Get the currently viewed character\n    const character = computed(() => {\n      const charId = game.getState(\"selected_character\");\n      return game.getCharacter(charId);\n    });\n\n    \u002F\u002F Store notes in a game store for persistence\n    const notesStore = game.createStore(\"character_notes\");\n\n    const currentNote = computed(() => {\n      if (!character.value) return \"\";\n      return notesStore.get(character.value.id) || \"\";\n    });\n\n    function saveNote(event) {\n      if (character.value) {\n        notesStore.set(character.value.id, event.target.value);\n      }\n    }\n\n    return { character, currentNote, saveNote };\n  },\n  template: \u002F*html*\u002F`\n    \u003Cdiv class=\"notes-tab\">\n      \u003Ch3>Notes for {{ character?.getName() }}\u003C\u002Fh3>\n      \u003Ctextarea\n        :value=\"currentNote\"\n        @input=\"saveNote\"\n        placeholder=\"Write notes about this character...\"\n      >\u003C\u002Ftextarea>\n    \u003C\u002Fdiv>\n  `\n});\n\ngame.addComponent({\n  id: \"notes\",\n  slot: \"character-tabs\",\n  title: \"Notes\",\n  component: NotesTab,\n  order: 4  \u002F\u002F After inventory (which is 3)\n});\n",[27,1708,1709,1727,1745,1749,1762,1768,1773,1790,1812,1824,1829,1833,1838,1859,1863,1880,1902,1922,1926,1930,1944,1951,1962,1967,1971,1975,1982,1986,1995,2001,2007,2013,2019,2025,2031,2037,2042,2047,2052,2057,2066,2076,2086,2096,2102,2113],{"__ignoreMap":164},[168,1710,1711,1713,1715,1717,1719,1721,1723,1725],{"class":170,"line":171},[168,1712,175],{"class":174},[168,1714,179],{"class":178},[168,1716,189],{"class":182},[168,1718,186],{"class":178},[168,1720,183],{"class":182},[168,1722,197],{"class":178},[168,1724,200],{"class":174},[168,1726,203],{"class":178},[168,1728,1729,1731,1733,1735,1737,1739,1741,1743],{"class":170,"line":206},[168,1730,175],{"class":174},[168,1732,179],{"class":178},[168,1734,232],{"class":182},[168,1736,186],{"class":178},[168,1738,977],{"class":182},[168,1740,197],{"class":178},[168,1742,200],{"class":174},[168,1744,239],{"class":178},[168,1746,1747],{"class":170,"line":213},[168,1748,210],{"emptyLinePlaceholder":209},[168,1750,1751,1753,1756,1758,1760],{"class":170,"line":220},[168,1752,175],{"class":174},[168,1754,1755],{"class":182}," NotesTab",[168,1757,255],{"class":174},[168,1759,259],{"class":258},[168,1761,262],{"class":178},[168,1763,1764,1766],{"class":170,"line":242},[168,1765,292],{"class":258},[168,1767,295],{"class":178},[168,1769,1770],{"class":170,"line":247},[168,1771,1772],{"class":216},"    \u002F\u002F Get the currently viewed character\n",[168,1774,1775,1777,1780,1782,1784,1786,1788],{"class":170,"line":265},[168,1776,301],{"class":174},[168,1778,1779],{"class":182}," character",[168,1781,255],{"class":174},[168,1783,1032],{"class":258},[168,1785,1035],{"class":178},[168,1787,1038],{"class":174},[168,1789,1041],{"class":178},[168,1791,1792,1795,1798,1800,1802,1805,1807,1810],{"class":170,"line":271},[168,1793,1794],{"class":174},"      const",[168,1796,1797],{"class":182}," charId",[168,1799,255],{"class":174},[168,1801,651],{"class":178},[168,1803,1804],{"class":258},"getState",[168,1806,312],{"class":178},[168,1808,1809],{"class":315},"\"selected_character\"",[168,1811,319],{"class":178},[168,1813,1814,1817,1819,1821],{"class":170,"line":277},[168,1815,1816],{"class":174},"      return",[168,1818,651],{"class":178},[168,1820,654],{"class":258},[168,1822,1823],{"class":178},"(charId);\n",[168,1825,1826],{"class":170,"line":283},[168,1827,1828],{"class":178},"    });\n",[168,1830,1831],{"class":170,"line":289},[168,1832,210],{"emptyLinePlaceholder":209},[168,1834,1835],{"class":170,"line":298},[168,1836,1837],{"class":216},"    \u002F\u002F Store notes in a game store for persistence\n",[168,1839,1840,1842,1845,1847,1849,1852,1854,1857],{"class":170,"line":322},[168,1841,301],{"class":174},[168,1843,1844],{"class":182}," notesStore",[168,1846,255],{"class":174},[168,1848,651],{"class":178},[168,1850,1851],{"class":258},"createStore",[168,1853,312],{"class":178},[168,1855,1856],{"class":315},"\"character_notes\"",[168,1858,319],{"class":178},[168,1860,1861],{"class":170,"line":327},[168,1862,210],{"emptyLinePlaceholder":209},[168,1864,1865,1867,1870,1872,1874,1876,1878],{"class":170,"line":338},[168,1866,301],{"class":174},[168,1868,1869],{"class":182}," currentNote",[168,1871,255],{"class":174},[168,1873,1032],{"class":258},[168,1875,1035],{"class":178},[168,1877,1038],{"class":174},[168,1879,1041],{"class":178},[168,1881,1882,1885,1888,1891,1894,1897,1900],{"class":170,"line":352},[168,1883,1884],{"class":174},"      if",[168,1886,1887],{"class":178}," (",[168,1889,1890],{"class":174},"!",[168,1892,1893],{"class":178},"character.value) ",[168,1895,1896],{"class":174},"return",[168,1898,1899],{"class":315}," \"\"",[168,1901,349],{"class":178},[168,1903,1904,1906,1909,1912,1915,1918,1920],{"class":170,"line":358},[168,1905,1816],{"class":174},[168,1907,1908],{"class":178}," notesStore.",[168,1910,1911],{"class":258},"get",[168,1913,1914],{"class":178},"(character.value.id) ",[168,1916,1917],{"class":174},"||",[168,1919,1899],{"class":315},[168,1921,349],{"class":178},[168,1923,1924],{"class":170,"line":363},[168,1925,1828],{"class":178},[168,1927,1928],{"class":170,"line":372},[168,1929,210],{"emptyLinePlaceholder":209},[168,1931,1932,1934,1937,1939,1942],{"class":170,"line":377},[168,1933,330],{"class":174},[168,1935,1936],{"class":258}," saveNote",[168,1938,312],{"class":178},[168,1940,1941],{"class":552},"event",[168,1943,556],{"class":178},[168,1945,1946,1948],{"class":170,"line":389},[168,1947,1884],{"class":174},[168,1949,1950],{"class":178}," (character.value) {\n",[168,1952,1953,1956,1959],{"class":170,"line":395},[168,1954,1955],{"class":178},"        notesStore.",[168,1957,1958],{"class":258},"set",[168,1960,1961],{"class":178},"(character.value.id, event.target.value);\n",[168,1963,1964],{"class":170,"line":401},[168,1965,1966],{"class":178},"      }\n",[168,1968,1969],{"class":170,"line":407},[168,1970,355],{"class":178},[168,1972,1973],{"class":170,"line":413},[168,1974,210],{"emptyLinePlaceholder":209},[168,1976,1977,1979],{"class":170,"line":419},[168,1978,366],{"class":174},[168,1980,1981],{"class":178}," { character, currentNote, saveNote };\n",[168,1983,1984],{"class":170,"line":664},[168,1985,286],{"class":178},[168,1987,1989,1991,1993],{"class":170,"line":1988},28,[168,1990,380],{"class":178},[168,1992,383],{"class":216},[168,1994,386],{"class":315},[168,1996,1998],{"class":170,"line":1997},29,[168,1999,2000],{"class":315},"    \u003Cdiv class=\"notes-tab\">\n",[168,2002,2004],{"class":170,"line":2003},30,[168,2005,2006],{"class":315},"      \u003Ch3>Notes for {{ character?.getName() }}\u003C\u002Fh3>\n",[168,2008,2010],{"class":170,"line":2009},31,[168,2011,2012],{"class":315},"      \u003Ctextarea\n",[168,2014,2016],{"class":170,"line":2015},32,[168,2017,2018],{"class":315},"        :value=\"currentNote\"\n",[168,2020,2022],{"class":170,"line":2021},33,[168,2023,2024],{"class":315},"        @input=\"saveNote\"\n",[168,2026,2028],{"class":170,"line":2027},34,[168,2029,2030],{"class":315},"        placeholder=\"Write notes about this character...\"\n",[168,2032,2034],{"class":170,"line":2033},35,[168,2035,2036],{"class":315},"      >\u003C\u002Ftextarea>\n",[168,2038,2040],{"class":170,"line":2039},36,[168,2041,410],{"class":315},[168,2043,2045],{"class":170,"line":2044},37,[168,2046,416],{"class":315},[168,2048,2050],{"class":170,"line":2049},38,[168,2051,422],{"class":178},[168,2053,2055],{"class":170,"line":2054},39,[168,2056,210],{"emptyLinePlaceholder":209},[168,2058,2060,2062,2064],{"class":170,"line":2059},40,[168,2061,1393],{"class":178},[168,2063,1396],{"class":258},[168,2065,262],{"class":178},[168,2067,2069,2071,2074],{"class":170,"line":2068},41,[168,2070,1403],{"class":178},[168,2072,2073],{"class":315},"\"notes\"",[168,2075,1409],{"class":178},[168,2077,2079,2081,2084],{"class":170,"line":2078},42,[168,2080,1414],{"class":178},[168,2082,2083],{"class":315},"\"character-tabs\"",[168,2085,1409],{"class":178},[168,2087,2089,2091,2094],{"class":170,"line":2088},43,[168,2090,1424],{"class":178},[168,2092,2093],{"class":315},"\"Notes\"",[168,2095,1409],{"class":178},[168,2097,2099],{"class":170,"line":2098},44,[168,2100,2101],{"class":178},"  component: NotesTab,\n",[168,2103,2105,2107,2110],{"class":170,"line":2104},45,[168,2106,1439],{"class":178},[168,2108,2109],{"class":182},"4",[168,2111,2112],{"class":216},"  \u002F\u002F After inventory (which is 3)\n",[168,2114,2116],{"class":170,"line":2115},46,[168,2117,422],{"class":178},[15,2119,2120],{},"The tab appears automatically in the character sheet navigation.",[32,2122],{},[35,2124,2126],{"id":2125},"example-adding-a-toolbar-button","Example: Adding a Toolbar Button",[15,2128,2129],{},[2130,2131],"img",{"alt":2132,"src":2133},"buttons","\u002Fplay\u002Fassets\u002Fengine_files\u002Fplugins\u002Fglobal_essentials\u002Fdocs\u002Fen\u002Fimages\u002Fbuttons.png",[15,2135,2136],{},"Add a custom button to the navigation toolbar:",[159,2138,2140],{"className":161,"code":2139,"language":163,"meta":164,"style":164},"const { vue, primeVue, game } = window.engine;\nconst { defineComponent } = vue;\n\nconst QuickSaveButton = defineComponent({\n  components: {\n    Button: primeVue.Button\n  },\n  setup() {\n    function quickSave() {\n      game.saveGame(\"quicksave\");\n      game.showNotification(\"Game saved!\");\n    }\n\n    return { quickSave };\n  },\n  template: \u002F*html*\u002F`\n    \u003CButton\n      icon=\"pi pi-save\"\n      @click=\"quickSave\"\n      v-tooltip.bottom=\"'Quick Save'\"\n    \u002F>\n  `\n});\n\ngame.addComponent({\n  id: \"toolbar-quicksave\",\n  slot: \"navigation-toolbar\",\n  component: QuickSaveButton,\n  order: 10\n});\n",[27,2141,2142,2164,2178,2182,2195,2199,2203,2207,2213,2222,2236,2250,2254,2258,2265,2269,2277,2282,2287,2292,2297,2302,2306,2310,2314,2322,2331,2340,2345,2352],{"__ignoreMap":164},[168,2143,2144,2146,2148,2150,2152,2154,2156,2158,2160,2162],{"class":170,"line":171},[168,2145,175],{"class":174},[168,2147,179],{"class":178},[168,2149,189],{"class":182},[168,2151,186],{"class":178},[168,2153,194],{"class":182},[168,2155,186],{"class":178},[168,2157,183],{"class":182},[168,2159,197],{"class":178},[168,2161,200],{"class":174},[168,2163,203],{"class":178},[168,2165,2166,2168,2170,2172,2174,2176],{"class":170,"line":206},[168,2167,175],{"class":174},[168,2169,179],{"class":178},[168,2171,232],{"class":182},[168,2173,197],{"class":178},[168,2175,200],{"class":174},[168,2177,239],{"class":178},[168,2179,2180],{"class":170,"line":213},[168,2181,210],{"emptyLinePlaceholder":209},[168,2183,2184,2186,2189,2191,2193],{"class":170,"line":220},[168,2185,175],{"class":174},[168,2187,2188],{"class":182}," QuickSaveButton",[168,2190,255],{"class":174},[168,2192,259],{"class":258},[168,2194,262],{"class":178},[168,2196,2197],{"class":170,"line":242},[168,2198,274],{"class":178},[168,2200,2201],{"class":170,"line":247},[168,2202,280],{"class":178},[168,2204,2205],{"class":170,"line":265},[168,2206,286],{"class":178},[168,2208,2209,2211],{"class":170,"line":271},[168,2210,292],{"class":258},[168,2212,295],{"class":178},[168,2214,2215,2217,2220],{"class":170,"line":277},[168,2216,330],{"class":174},[168,2218,2219],{"class":258}," quickSave",[168,2221,295],{"class":178},[168,2223,2224,2226,2229,2231,2234],{"class":170,"line":283},[168,2225,1543],{"class":178},[168,2227,2228],{"class":258},"saveGame",[168,2230,312],{"class":178},[168,2232,2233],{"class":315},"\"quicksave\"",[168,2235,319],{"class":178},[168,2237,2238,2240,2243,2245,2248],{"class":170,"line":289},[168,2239,1543],{"class":178},[168,2241,2242],{"class":258},"showNotification",[168,2244,312],{"class":178},[168,2246,2247],{"class":315},"\"Game saved!\"",[168,2249,319],{"class":178},[168,2251,2252],{"class":170,"line":298},[168,2253,355],{"class":178},[168,2255,2256],{"class":170,"line":322},[168,2257,210],{"emptyLinePlaceholder":209},[168,2259,2260,2262],{"class":170,"line":327},[168,2261,366],{"class":174},[168,2263,2264],{"class":178}," { quickSave };\n",[168,2266,2267],{"class":170,"line":338},[168,2268,286],{"class":178},[168,2270,2271,2273,2275],{"class":170,"line":352},[168,2272,380],{"class":178},[168,2274,383],{"class":216},[168,2276,386],{"class":315},[168,2278,2279],{"class":170,"line":358},[168,2280,2281],{"class":315},"    \u003CButton\n",[168,2283,2284],{"class":170,"line":363},[168,2285,2286],{"class":315},"      icon=\"pi pi-save\"\n",[168,2288,2289],{"class":170,"line":372},[168,2290,2291],{"class":315},"      @click=\"quickSave\"\n",[168,2293,2294],{"class":170,"line":377},[168,2295,2296],{"class":315},"      v-tooltip.bottom=\"'Quick Save'\"\n",[168,2298,2299],{"class":170,"line":389},[168,2300,2301],{"class":315},"    \u002F>\n",[168,2303,2304],{"class":170,"line":395},[168,2305,416],{"class":315},[168,2307,2308],{"class":170,"line":401},[168,2309,422],{"class":178},[168,2311,2312],{"class":170,"line":407},[168,2313,210],{"emptyLinePlaceholder":209},[168,2315,2316,2318,2320],{"class":170,"line":413},[168,2317,1393],{"class":178},[168,2319,1396],{"class":258},[168,2321,262],{"class":178},[168,2323,2324,2326,2329],{"class":170,"line":419},[168,2325,1403],{"class":178},[168,2327,2328],{"class":315},"\"toolbar-quicksave\"",[168,2330,1409],{"class":178},[168,2332,2333,2335,2338],{"class":170,"line":664},[168,2334,1414],{"class":178},[168,2336,2337],{"class":315},"\"navigation-toolbar\"",[168,2339,1409],{"class":178},[168,2341,2342],{"class":170,"line":1988},[168,2343,2344],{"class":178},"  component: QuickSaveButton,\n",[168,2346,2347,2349],{"class":170,"line":1997},[168,2348,1439],{"class":178},[168,2350,2351],{"class":182},"10\n",[168,2353,2354],{"class":170,"line":2003},[168,2355,422],{"class":178},[32,2357],{},[35,2359,2361],{"id":2360},"removing-or-replacing-components","Removing or Replacing Components",[15,2363,2364],{},"Remove a default component:",[159,2366,2368],{"className":161,"code":2367,"language":163,"meta":164,"style":164},"game.removeComponent(\"default-inventory-header\");\n",[27,2369,2370],{"__ignoreMap":164},[168,2371,2372,2374,2377,2379,2382],{"class":170,"line":171},[168,2373,1393],{"class":178},[168,2375,2376],{"class":258},"removeComponent",[168,2378,312],{"class":178},[168,2380,2381],{"class":315},"\"default-inventory-header\"",[168,2383,319],{"class":178},[15,2385,2386,2387,157],{},"Replace by using the same ",[27,2388,1463],{},[159,2390,2392],{"className":161,"code":2391,"language":163,"meta":164,"style":164},"game.addComponent({\n  id: \"stats\",  \u002F\u002F Same ID as default stats tab\n  slot: \"character-tabs\",\n  title: \"Character Stats\",\n  component: MyCustomStatsTab  \u002F\u002F Your replacement\n});\n",[27,2393,2394,2402,2415,2423,2432,2440],{"__ignoreMap":164},[168,2395,2396,2398,2400],{"class":170,"line":171},[168,2397,1393],{"class":178},[168,2399,1396],{"class":258},[168,2401,262],{"class":178},[168,2403,2404,2406,2409,2412],{"class":170,"line":206},[168,2405,1403],{"class":178},[168,2407,2408],{"class":315},"\"stats\"",[168,2410,2411],{"class":178},",  ",[168,2413,2414],{"class":216},"\u002F\u002F Same ID as default stats tab\n",[168,2416,2417,2419,2421],{"class":170,"line":213},[168,2418,1414],{"class":178},[168,2420,2083],{"class":315},[168,2422,1409],{"class":178},[168,2424,2425,2427,2430],{"class":170,"line":220},[168,2426,1424],{"class":178},[168,2428,2429],{"class":315},"\"Character Stats\"",[168,2431,1409],{"class":178},[168,2433,2434,2437],{"class":170,"line":242},[168,2435,2436],{"class":178},"  component: MyCustomStatsTab  ",[168,2438,2439],{"class":216},"\u002F\u002F Your replacement\n",[168,2441,2442],{"class":170,"line":247},[168,2443,422],{"class":178},[32,2445],{},[35,2447,2449],{"id":2448},"quick-reference","Quick Reference",[48,2451,2452,2462],{},[51,2453,2454],{},[54,2455,2456,2459],{},[57,2457,2458],{},"I want to...",[57,2460,2461],{},"Do this",[64,2463,2464,2474,2484,2494,2504,2514,2524],{},[54,2465,2466,2469],{},[69,2467,2468],{},"Import Vue helpers",[69,2470,2471],{},[27,2472,2473],{},"const { ref, computed, defineComponent } = vue",[54,2475,2476,2479],{},[69,2477,2478],{},"Create reactive value",[69,2480,2481],{},[27,2482,2483],{},"const x = ref(initialValue)",[54,2485,2486,2489],{},[69,2487,2488],{},"Create computed value",[69,2490,2491],{},[27,2492,2493],{},"const x = computed(() => ...)",[54,2495,2496,2499],{},[69,2497,2498],{},"Use engine component",[69,2500,2501],{},[27,2502,2503],{},"const { CharacterFace } = components",[54,2505,2506,2509],{},[69,2507,2508],{},"Add component to slot",[69,2510,2511],{},[27,2512,2513],{},"game.addComponent({ id, slot, component })",[54,2515,2516,2519],{},[69,2517,2518],{},"Remove component",[69,2520,2521],{},[27,2522,2523],{},"game.removeComponent(\"component-id\")",[54,2525,2526,2529],{},[69,2527,2528],{},"Change state-based slot",[69,2530,2531],{},[27,2532,2533],{},"game.setState(\"state_name\", \"component_id\")",[32,2535],{},[35,2537,2539],{"id":2538},"next-steps","Next Steps",[113,2541,2542,2549,2556],{},[116,2543,2544,2548],{},[119,2545,2547],{"href":2546},"\u002Fdocs\u002Fglobal_essentials\u002Fadvanced\u002F3rd_party","3rd Party Libraries"," - PrimeVue, VueUse, and other libraries",[116,2550,2551,2555],{},[119,2552,2554],{"href":2553},"\u002Fdocs\u002Fglobal_essentials\u002Fadvanced\u002Fset_up_coding","Set Up Coding Environment"," - VS Code setup for autocomplete",[116,2557,2558,2562],{},[119,2559,2561],{"href":2560},"\u002Fdocs\u002Fglobal_essentials\u002Fbuiltins\u002Fgame_emitters","Game Emitters"," - Emitters you can listen to",[2564,2565,2566],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":164,"searchDepth":206,"depth":206,"links":2568},[2569,2570,2575,2576,2580,2581,2582,2583,2584],{"id":37,"depth":206,"text":38},{"id":149,"depth":206,"text":150,"children":2571},[2572,2573,2574],{"id":426,"depth":213,"text":427},{"id":670,"depth":213,"text":671},{"id":726,"depth":213,"text":727},{"id":1135,"depth":206,"text":1136},{"id":1369,"depth":206,"text":1370,"children":2577},[2578,2579],{"id":1452,"depth":213,"text":1453},{"id":1678,"depth":213,"text":1679},{"id":1699,"depth":206,"text":1700},{"id":2125,"depth":206,"text":2126},{"id":2360,"depth":206,"text":2361},{"id":2448,"depth":206,"text":2449},{"id":2538,"depth":206,"text":2539},"Dryad Engine is developed with Vue 3, a modern JavaScript framework for building user interfaces. This means all the engine's UI components - from character sheets to inventory screens to dialogue overlays - are Vue components that you can extend, replace, or build upon.","md",{"plugin":2588,"category":2589,"page":189},"global_essentials","advanced","\u002Fdocs\u002Fglobal_essentials\u002Fadvanced\u002Fvue",{"title":5,"description":2585},"docs\u002Fglobal_essentials\u002Fadvanced\u002Fvue","5lrqAzyR8uyjN58l_HoTmWcoXnfA4BU_Tp3WOmhQsKY",1779582261838]