[{"data":1,"prerenderedAt":1521},["ShallowReactive",2],{"doc-global_essentials\u002Fadvanced\u002Fcustom_vue":3},{"id":4,"title":5,"body":6,"description":43,"extension":1512,"meta":1513,"navigation":124,"path":1517,"seo":1518,"stem":1519,"__hash__":1520},"docs\u002Fdocs\u002Fglobal_essentials\u002Fadvanced\u002Fcustom_vue.md","Custom Vue Features",{"type":7,"value":8,"toc":1502},"minimark",[9,13,18,22,31,37,76,81,91,96,101,186,191,204,209,217,220,223,226,229,233,276,281,327,332,394,398,406,410,418,420,423,440,445,470,489,494,516,644,649,710,732,736,752,756,765,767,770,773,781,818,833,838,863,868,913,930,939,985,1004,1009,1136,1141,1148,1153,1231,1235,1249,1253,1262,1264,1267,1270,1274,1369,1373,1381,1385,1393,1402,1404,1408,1498],[10,11,5],"h1",{"id":12},"custom-vue-features",[14,15,17],"h2",{"id":16},"directives","Directives",[19,20,21],"h3",{"id":21},"v-persist",[23,24,25,26,30],"p",{},"Keeps images in browser memory cache after they load. Prevents the browser from evicting decoded image data when elements are removed from the DOM (e.g., when closing a panel that uses ",[27,28,29],"code",{},"v-if",").",[23,32,33],{},[34,35,36],"strong",{},"Usage:",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-html shiki shiki-themes github-light github-dark","\u003Cimg :src=\"iconPath\" v-persist \u002F>\n","html","",[27,45,46],{"__ignoreMap":43},[47,48,51,55,59,63,66,70,73],"span",{"class":49,"line":50},"line",1,[47,52,54],{"class":53},"sVt8B","\u003C",[47,56,58],{"class":57},"s9eBZ","img",[47,60,62],{"class":61},"sScJk"," :src",[47,64,65],{"class":53},"=",[47,67,69],{"class":68},"sZZnC","\"iconPath\"",[47,71,72],{"class":61}," v-persist",[47,74,75],{"class":53}," \u002F>\n",[23,77,78],{},[34,79,80],{},"Why it exists:",[23,82,83,84,86,87,90],{},"When a Vue component is destroyed (via ",[27,85,29],{},"), all its ",[27,88,89],{},"\u003Cimg>"," elements are removed from the DOM. The browser may then evict the decoded image data from memory. When the component is recreated, the browser needs to re-decode the image from disk cache, causing a brief visual delay.",[23,92,93,95],{},[27,94,21],{}," creates a hidden JavaScript reference to each loaded image, telling the browser to keep the decoded data in memory.",[23,97,98],{},[34,99,100],{},"Example - Item display component:",[38,102,106],{"className":103,"code":104,"language":105,"meta":43,"style":43},"language-javascript shiki shiki-themes github-light github-dark","const { vue, game } = window.engine;\nconst { defineComponent, computed } = vue;\n\nconst ItemIcon = defineComponent({\n  props: ['item'],\n  setup(props) {\n    const icon = computed(() => props.item.getTrait('image'));\n    return { icon };\n  },\n  template: \u002F*html*\u002F`\n    \u003Cimg v-if=\"icon\" :src=\"icon\" class=\"item-icon\" v-persist \u002F>\n  `\n});\n","javascript",[27,107,108,113,119,126,132,138,144,150,156,162,168,174,180],{"__ignoreMap":43},[47,109,110],{"class":49,"line":50},[47,111,112],{},"const { vue, game } = window.engine;\n",[47,114,116],{"class":49,"line":115},2,[47,117,118],{},"const { defineComponent, computed } = vue;\n",[47,120,122],{"class":49,"line":121},3,[47,123,125],{"emptyLinePlaceholder":124},true,"\n",[47,127,129],{"class":49,"line":128},4,[47,130,131],{},"const ItemIcon = defineComponent({\n",[47,133,135],{"class":49,"line":134},5,[47,136,137],{},"  props: ['item'],\n",[47,139,141],{"class":49,"line":140},6,[47,142,143],{},"  setup(props) {\n",[47,145,147],{"class":49,"line":146},7,[47,148,149],{},"    const icon = computed(() => props.item.getTrait('image'));\n",[47,151,153],{"class":49,"line":152},8,[47,154,155],{},"    return { icon };\n",[47,157,159],{"class":49,"line":158},9,[47,160,161],{},"  },\n",[47,163,165],{"class":49,"line":164},10,[47,166,167],{},"  template: \u002F*html*\u002F`\n",[47,169,171],{"class":49,"line":170},11,[47,172,173],{},"    \u003Cimg v-if=\"icon\" :src=\"icon\" class=\"item-icon\" v-persist \u002F>\n",[47,175,177],{"class":49,"line":176},12,[47,178,179],{},"  `\n",[47,181,183],{"class":49,"line":182},13,[47,184,185],{},"});\n",[23,187,188],{},[34,189,190],{},"When to use:",[192,193,194,198,201],"ul",{},[195,196,197],"li",{},"Images that appear in panels or screens that open\u002Fclose frequently (character sheets, inventory, etc.)",[195,199,200],{},"Character portraits and doll layers",[195,202,203],{},"Any image that should always display instantly when its container is reopened",[23,205,206],{},[34,207,208],{},"When not needed:",[192,210,211,214],{},[195,212,213],{},"Images that are always visible (static backgrounds, persistent UI elements)",[195,215,216],{},"Images shown only once (splash screens, one-time animations)",[23,218,219],{},"The cache holds up to 600 images. When full, the oldest entry is removed to make room for new ones.",[221,222],"hr",{},[19,224,225],{"id":225},"v-fit",[23,227,228],{},"Shrinks font size so text fits within its container without clipping. Reacts to text changes and container resizes automatically.",[23,230,231],{},[34,232,36],{},[38,234,236],{"className":40,"code":235,"language":42,"meta":43,"style":43},"\u003Cdiv v-fit>{{ characterName }}\u003C\u002Fdiv>\n\u003Cdiv v-fit=\"{ min: 8 }\">{{ longTitle }}\u003C\u002Fdiv>\n",[27,237,238,256],{"__ignoreMap":43},[47,239,240,242,245,248,251,253],{"class":49,"line":50},[47,241,54],{"class":53},[47,243,244],{"class":57},"div",[47,246,247],{"class":61}," v-fit",[47,249,250],{"class":53},">{{ characterName }}\u003C\u002F",[47,252,244],{"class":57},[47,254,255],{"class":53},">\n",[47,257,258,260,262,264,266,269,272,274],{"class":49,"line":115},[47,259,54],{"class":53},[47,261,244],{"class":57},[47,263,247],{"class":61},[47,265,65],{"class":53},[47,267,268],{"class":68},"\"{ min: 8 }\"",[47,270,271],{"class":53},">{{ longTitle }}\u003C\u002F",[47,273,244],{"class":57},[47,275,255],{"class":53},[23,277,278],{},[34,279,280],{},"Options:",[282,283,284,303],"table",{},[285,286,287],"thead",{},[288,289,290,294,297,300],"tr",{},[291,292,293],"th",{},"Option",[291,295,296],{},"Type",[291,298,299],{},"Default",[291,301,302],{},"Description",[304,305,306],"tbody",{},[288,307,308,314,319,324],{},[309,310,311],"td",{},[27,312,313],{},"min",[309,315,316],{},[27,317,318],{},"number",[309,320,321],{},[27,322,323],{},"6",[309,325,326],{},"Minimum font size in px",[23,328,329],{},[34,330,331],{},"Example - Character name badge:",[38,333,335],{"className":103,"code":334,"language":105,"meta":43,"style":43},"const { vue, game } = window.engine;\nconst { defineComponent, computed } = vue;\n\nconst CharacterBadge = defineComponent({\n  props: ['character'],\n  setup(props) {\n    const name = computed(() => props.character.getTrait('name'));\n    return { name };\n  },\n  template: \u002F*html*\u002F`\n    \u003Cdiv class=\"badge\" v-fit>{{ name }}\u003C\u002Fdiv>\n  `\n});\n",[27,336,337,341,345,349,354,359,363,368,373,377,381,386,390],{"__ignoreMap":43},[47,338,339],{"class":49,"line":50},[47,340,112],{},[47,342,343],{"class":49,"line":115},[47,344,118],{},[47,346,347],{"class":49,"line":121},[47,348,125],{"emptyLinePlaceholder":124},[47,350,351],{"class":49,"line":128},[47,352,353],{},"const CharacterBadge = defineComponent({\n",[47,355,356],{"class":49,"line":134},[47,357,358],{},"  props: ['character'],\n",[47,360,361],{"class":49,"line":140},[47,362,143],{},[47,364,365],{"class":49,"line":146},[47,366,367],{},"    const name = computed(() => props.character.getTrait('name'));\n",[47,369,370],{"class":49,"line":152},[47,371,372],{},"    return { name };\n",[47,374,375],{"class":49,"line":158},[47,376,161],{},[47,378,379],{"class":49,"line":164},[47,380,167],{},[47,382,383],{"class":49,"line":170},[47,384,385],{},"    \u003Cdiv class=\"badge\" v-fit>{{ name }}\u003C\u002Fdiv>\n",[47,387,388],{"class":49,"line":176},[47,389,179],{},[47,391,392],{"class":49,"line":182},[47,393,185],{},[23,395,396],{},[34,397,190],{},[192,399,400,403],{},[195,401,402],{},"Name labels on fixed-width containers (character portraits, item slots)",[195,404,405],{},"Any single-line text that must not clip or overflow",[23,407,408],{},[34,409,208],{},[192,411,412,415],{},[195,413,414],{},"Text that can wrap to multiple lines",[195,416,417],{},"Text in containers that grow to fit content",[221,419],{},[19,421,422],{"id":422},"v-script",[23,424,425,426,429,430,433,434,439],{},"Renders DryadScript text on any element with full lore-link interactivity. Resolves the input through the engine's text pipeline by default and attaches the hover\u002Fclick event delegation needed to open lore tooltip popups on ",[27,427,428],{},"[[record_id]]"," references. Use this anywhere you display engine-resolved text in your own templates — plain ",[27,431,432],{},"v-html"," will render visually but the lore links won't react to hover or click. See ",[435,436,438],"a",{"href":437},"\u002Fdocs\u002Fglobal_essentials\u002Fmiscellaneous\u002Flore","Lore & Encyclopedia"," for the full lore system.",[23,441,442],{},[34,443,444],{},"Usage (string form):",[38,446,448],{"className":40,"code":447,"language":42,"meta":43,"style":43},"\u003Cdiv v-script=\"rawText\" \u002F>\n",[27,449,450],{"__ignoreMap":43},[47,451,452,454,456,459,461,464,468],{"class":49,"line":50},[47,453,54],{"class":53},[47,455,244],{"class":57},[47,457,458],{"class":61}," v-script",[47,460,65],{"class":53},[47,462,463],{"class":68},"\"rawText\"",[47,465,467],{"class":466},"s7hpK"," \u002F",[47,469,255],{"class":53},[23,471,472,473,476,477,480,481,484,485,488],{},"Resolves ",[27,474,475],{},"rawText"," through the text pipeline (placeholders, ",[27,478,479],{},"if{}",", ",[27,482,483],{},"[[lore-links]]",", etc.) and renders it. Equivalent to ",[27,486,487],{},"v-script=\"{ html: rawText }\"",".",[23,490,491],{},[34,492,493],{},"Usage (object form):",[38,495,497],{"className":40,"code":496,"language":42,"meta":43,"style":43},"\u003Cdiv v-script=\"{ html, resolver, navMode, onNavigate, disabled }\" \u002F>\n",[27,498,499],{"__ignoreMap":43},[47,500,501,503,505,507,509,512,514],{"class":49,"line":50},[47,502,54],{"class":53},[47,504,244],{"class":57},[47,506,458],{"class":61},[47,508,65],{"class":53},[47,510,511],{"class":68},"\"{ html, resolver, navMode, onNavigate, disabled }\"",[47,513,467],{"class":466},[47,515,255],{"class":53},[282,517,518,530],{},[285,519,520],{},[288,521,522,524,526,528],{},[291,523,293],{},[291,525,296],{},[291,527,299],{},[291,529,302],{},[304,531,532,549,583,605,626],{},[288,533,534,538,543,546],{},[309,535,536],{},[27,537,42],{},[309,539,540],{},[27,541,542],{},"string",[309,544,545],{},"required",[309,547,548],{},"text to render",[288,550,551,556,561,566],{},[309,552,553],{},[27,554,555],{},"resolver",[309,557,558],{},[27,559,560],{},"boolean",[309,562,563],{},[27,564,565],{},"true",[309,567,568,569,571,572,575,576,579,580,582],{},"run ",[27,570,42],{}," through ",[27,573,574],{},"game.resolveString(html, true).output"," (noExecuteActions=true; rendering must never fire side effects). Set ",[27,577,578],{},"false"," if ",[27,581,42],{}," is already resolved upstream.",[288,584,585,590,594,598],{},[309,586,587],{},[27,588,589],{},"navMode",[309,591,592],{},[27,593,560],{},[309,595,596],{},[27,597,578],{},[309,599,600,601,604],{},"for in-place navigation (e.g., the Encyclopedia tab): clicks call ",[27,602,603],{},"onNavigate(recordId)"," instead of opening a popup. Hover is suppressed in this mode.",[288,606,607,612,617,620],{},[309,608,609],{},[27,610,611],{},"onNavigate",[309,613,614],{},[27,615,616],{},"(recordId: string) => void",[309,618,619],{},"—",[309,621,622,623,625],{},"callback for ",[27,624,589],{}," clicks",[288,627,628,633,637,641],{},[309,629,630],{},[27,631,632],{},"disabled",[309,634,635],{},[27,636,560],{},[309,638,639],{},[27,640,578],{},[309,642,643],{},"suppress all hover\u002Fclick handling. Use during DOM-unstable phases like a typing animation that re-parses HTML each frame, so the popup doesn't latch onto a stale anchor.",[23,645,646],{},[34,647,648],{},"Example - choice description:",[38,650,652],{"className":103,"code":651,"language":105,"meta":43,"style":43},"const { vue } = window.engine;\nconst { defineComponent } = vue;\n\nconst ChoiceCard = defineComponent({\n  props: ['choice'],\n  template: \u002F*html*\u002F`\n    \u003Cdiv class=\"choice\">\n      \u003Ch3>{{ choice.name }}\u003C\u002Fh3>\n      \u003Cdiv v-if=\"choice.description\" v-script=\"choice.description\" class=\"choice-description\">\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  `\n});\n",[27,653,654,659,664,668,673,678,682,687,692,697,702,706],{"__ignoreMap":43},[47,655,656],{"class":49,"line":50},[47,657,658],{},"const { vue } = window.engine;\n",[47,660,661],{"class":49,"line":115},[47,662,663],{},"const { defineComponent } = vue;\n",[47,665,666],{"class":49,"line":121},[47,667,125],{"emptyLinePlaceholder":124},[47,669,670],{"class":49,"line":128},[47,671,672],{},"const ChoiceCard = defineComponent({\n",[47,674,675],{"class":49,"line":134},[47,676,677],{},"  props: ['choice'],\n",[47,679,680],{"class":49,"line":140},[47,681,167],{},[47,683,684],{"class":49,"line":146},[47,685,686],{},"    \u003Cdiv class=\"choice\">\n",[47,688,689],{"class":49,"line":152},[47,690,691],{},"      \u003Ch3>{{ choice.name }}\u003C\u002Fh3>\n",[47,693,694],{"class":49,"line":158},[47,695,696],{},"      \u003Cdiv v-if=\"choice.description\" v-script=\"choice.description\" class=\"choice-description\">\u003C\u002Fdiv>\n",[47,698,699],{"class":49,"line":164},[47,700,701],{},"    \u003C\u002Fdiv>\n",[47,703,704],{"class":49,"line":170},[47,705,179],{},[47,707,708],{"class":49,"line":176},[47,709,185],{},[23,711,712,715,716,720,721,724,725,728,729,488],{},[34,713,714],{},"Reactivity caveat:"," the directive re-renders only when its bound value changes. Reactive dependencies ",[717,718,719],"em",{},"inside"," ",[27,722,723],{},"resolveString"," (e.g., a placeholder that reads a ref the player can change mid-session) won't trigger a re-render automatically. For those cases, wrap in your own ",[27,726,727],{},"computed(() => game.resolveString(text).output)"," and pass with ",[27,730,731],{},"{ resolver: false }",[23,733,734],{},[34,735,190],{},[192,737,738,749],{},[195,739,740,741,744,745,748],{},"Any custom component that displays user-authored text containing ",[27,742,743],{},"[[record]]"," references, ",[27,746,747],{},"|placeholders|",", or other DryadScript syntax",[195,750,751],{},"Choice descriptions, item descriptions, status descriptions in plugin UIs",[23,753,754],{},[34,755,208],{},[192,757,758],{},[195,759,760,761,764],{},"Plain text without any DryadScript syntax — use ",[27,762,763],{},"{{ text }}"," interpolation instead.",[221,766],{},[19,768,769],{"id":769},"v-popover",[23,771,772],{},"Floating UI based hover popover that just works on any element.",[23,774,775],{},[34,776,777,778,780],{},"Usage (string form — HTML rendered via ",[27,779,422],{},"):",[38,782,784],{"className":40,"code":783,"language":42,"meta":43,"style":43},"\u003Cbutton v-popover=\"'\u003Cb>Strength\u003C\u002Fb>: raises [[stat_attack]]'\">Stats\u003C\u002Fbutton>\n",[27,785,786],{"__ignoreMap":43},[47,787,788,790,793,796,798,801,803,806,808,811,814,816],{"class":49,"line":50},[47,789,54],{"class":53},[47,791,792],{"class":57},"button",[47,794,795],{"class":61}," v-popover",[47,797,65],{"class":53},[47,799,800],{"class":68},"\"'",[47,802,54],{"class":466},[47,804,805],{"class":68},"b>Strength",[47,807,54],{"class":466},[47,809,810],{"class":68},"\u002Fb>: raises [[stat_attack]]'\"",[47,812,813],{"class":53},">Stats\u003C\u002F",[47,815,792],{"class":57},[47,817,255],{"class":53},[23,819,820,821,823,824,826,827,829,830,832],{},"The string is treated as DryadScript HTML and rendered through ",[27,822,422],{},", so ",[27,825,743],{}," lore-links, ",[27,828,747],{},", and ",[27,831,479],{}," blocks all work inside the popover.",[23,834,835],{},[34,836,837],{},"Usage (object form — html mode):",[38,839,841],{"className":40,"code":840,"language":42,"meta":43,"style":43},"\u003Cdiv v-popover=\"{ html: skill.description, width: 400 }\">{{ skill.name }}\u003C\u002Fdiv>\n",[27,842,843],{"__ignoreMap":43},[47,844,845,847,849,851,853,856,859,861],{"class":49,"line":50},[47,846,54],{"class":53},[47,848,244],{"class":57},[47,850,795],{"class":61},[47,852,65],{"class":53},[47,854,855],{"class":68},"\"{ html: skill.description, width: 400 }\"",[47,857,858],{"class":53},">{{ skill.name }}\u003C\u002F",[47,860,244],{"class":57},[47,862,255],{"class":53},[23,864,865],{},[34,866,867],{},"Usage (object form — Vue component mode):",[38,869,871],{"className":40,"code":870,"language":42,"meta":43,"style":43},"\u003Cdiv v-popover=\"{ component: ItemCard, props: { item }, width: 360 }\">\n  \u003Cimg :src=\"item.icon\">\n\u003C\u002Fdiv>\n",[27,872,873,888,904],{"__ignoreMap":43},[47,874,875,877,879,881,883,886],{"class":49,"line":50},[47,876,54],{"class":53},[47,878,244],{"class":57},[47,880,795],{"class":61},[47,882,65],{"class":53},[47,884,885],{"class":68},"\"{ component: ItemCard, props: { item }, width: 360 }\"",[47,887,255],{"class":53},[47,889,890,893,895,897,899,902],{"class":49,"line":115},[47,891,892],{"class":53},"  \u003C",[47,894,58],{"class":57},[47,896,62],{"class":61},[47,898,65],{"class":53},[47,900,901],{"class":68},"\"item.icon\"",[47,903,255],{"class":53},[47,905,906,909,911],{"class":49,"line":121},[47,907,908],{"class":53},"\u003C\u002F",[47,910,244],{"class":57},[47,912,255],{"class":53},[23,914,915,916,919,920,922,923,926,927,488],{},"When ",[27,917,918],{},"component"," is supplied it wins over ",[27,921,42],{},". The component receives ",[27,924,925],{},"props"," via ",[27,928,929],{},"v-bind",[23,931,932],{},[34,933,934,935,938],{},"Placement modifiers (sugar for the ",[27,936,937],{},"placement"," option):",[38,940,942],{"className":40,"code":941,"language":42,"meta":43,"style":43},"\u003Cdiv v-popover.bottom=\"content\">Below\u003C\u002Fdiv>\n\u003Cdiv v-popover.right=\"content\">To the right\u003C\u002Fdiv>\n",[27,943,944,965],{"__ignoreMap":43},[47,945,946,948,950,953,955,958,961,963],{"class":49,"line":50},[47,947,54],{"class":53},[47,949,244],{"class":57},[47,951,952],{"class":61}," v-popover.bottom",[47,954,65],{"class":53},[47,956,957],{"class":68},"\"content\"",[47,959,960],{"class":53},">Below\u003C\u002F",[47,962,244],{"class":57},[47,964,255],{"class":53},[47,966,967,969,971,974,976,978,981,983],{"class":49,"line":115},[47,968,54],{"class":53},[47,970,244],{"class":57},[47,972,973],{"class":61}," v-popover.right",[47,975,65],{"class":53},[47,977,957],{"class":68},[47,979,980],{"class":53},">To the right\u003C\u002F",[47,982,244],{"class":57},[47,984,255],{"class":53},[23,986,987,988,991,992,995,996,999,1000,1003],{},"Equivalent to ",[27,989,990],{},"v-popover=\"{ ..., placement: 'bottom' }\"",". The default placement is ",[27,993,994],{},"'top'",". Floating UI's ",[27,997,998],{},"flip()"," and ",[27,1001,1002],{},"shift()"," middleware automatically rescue placement near viewport edges.",[23,1005,1006],{},[34,1007,1008],{},"Binding shape:",[282,1010,1011,1024],{},[285,1012,1013],{},[288,1014,1015,1018,1020,1022],{},[291,1016,1017],{},"Field",[291,1019,296],{},[291,1021,299],{},[291,1023,302],{},[304,1025,1026,1043,1062,1085,1108],{},[288,1027,1028,1032,1036,1038],{},[309,1029,1030],{},[27,1031,42],{},[309,1033,1034],{},[27,1035,542],{},[309,1037,619],{},[309,1039,1040,1041],{},"content to render through ",[27,1042,422],{},[288,1044,1045,1049,1054,1056],{},[309,1046,1047],{},[27,1048,918],{},[309,1050,1051],{},[27,1052,1053],{},"Component",[309,1055,619],{},[309,1057,1058,1059,1061],{},"Vue component to render inside (wins over ",[27,1060,42],{},")",[288,1063,1064,1068,1073,1078],{},[309,1065,1066],{},[27,1067,925],{},[309,1069,1070],{},[27,1071,1072],{},"object",[309,1074,1075],{},[27,1076,1077],{},"{}",[309,1079,1080,1081,926,1083],{},"props passed to ",[27,1082,918],{},[27,1084,929],{},[288,1086,1087,1092,1097,1102],{},[309,1088,1089],{},[27,1090,1091],{},"width",[309,1093,1094],{},[27,1095,1096],{},"number | string",[309,1098,1099],{},[27,1100,1101],{},"'300px'",[309,1103,1104,1105],{},"popover width; numbers become ",[27,1106,1107],{},"${n}px",[288,1109,1110,1114,1119,1123],{},[309,1111,1112],{},[27,1113,937],{},[309,1115,1116],{},[27,1117,1118],{},"Placement",[309,1120,1121],{},[27,1122,994],{},[309,1124,1125,1126,480,1129,480,1132,1135],{},"any Floating UI placement (",[27,1127,1128],{},"top",[27,1130,1131],{},"bottom-start",[27,1133,1134],{},"left-end",", etc.)",[23,1137,1138],{},[34,1139,1140],{},"Hover handoff:",[23,1142,1143,1144,1147],{},"The popover sits at ",[27,1145,1146],{},"offset: 0"," against the target side, and a 100 ms close-debounce covers the cursor's transition between target and popover. Hovering the popover keeps it open; leaving the popover closes it after 100 ms.",[23,1149,1150],{},[34,1151,1152],{},"Example — character status with rich popover content:",[38,1154,1156],{"className":103,"code":1155,"language":105,"meta":43,"style":43},"const { vue, game } = window.engine;\nconst { defineComponent } = vue;\nconst { ItemCard } = window.engine.components;\n\nconst InventoryRow = defineComponent({\n  props: ['item'],\n  setup(props) {\n    return { item: props.item, ItemCard };\n  },\n  template: \u002F*html*\u002F`\n    \u003Cdiv class=\"row\" v-popover=\"{ component: ItemCard, props: { item }, placement: 'right' }\">\n      \u003Cimg :src=\"item.icon\">\n      \u003Cspan>{{ item.name }}\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n  `\n});\n",[27,1157,1158,1162,1166,1171,1175,1180,1184,1188,1193,1197,1201,1206,1211,1216,1221,1226],{"__ignoreMap":43},[47,1159,1160],{"class":49,"line":50},[47,1161,112],{},[47,1163,1164],{"class":49,"line":115},[47,1165,663],{},[47,1167,1168],{"class":49,"line":121},[47,1169,1170],{},"const { ItemCard } = window.engine.components;\n",[47,1172,1173],{"class":49,"line":128},[47,1174,125],{"emptyLinePlaceholder":124},[47,1176,1177],{"class":49,"line":134},[47,1178,1179],{},"const InventoryRow = defineComponent({\n",[47,1181,1182],{"class":49,"line":140},[47,1183,137],{},[47,1185,1186],{"class":49,"line":146},[47,1187,143],{},[47,1189,1190],{"class":49,"line":152},[47,1191,1192],{},"    return { item: props.item, ItemCard };\n",[47,1194,1195],{"class":49,"line":158},[47,1196,161],{},[47,1198,1199],{"class":49,"line":164},[47,1200,167],{},[47,1202,1203],{"class":49,"line":170},[47,1204,1205],{},"    \u003Cdiv class=\"row\" v-popover=\"{ component: ItemCard, props: { item }, placement: 'right' }\">\n",[47,1207,1208],{"class":49,"line":176},[47,1209,1210],{},"      \u003Cimg :src=\"item.icon\">\n",[47,1212,1213],{"class":49,"line":182},[47,1214,1215],{},"      \u003Cspan>{{ item.name }}\u003C\u002Fspan>\n",[47,1217,1219],{"class":49,"line":1218},14,[47,1220,701],{},[47,1222,1224],{"class":49,"line":1223},15,[47,1225,179],{},[47,1227,1229],{"class":49,"line":1228},16,[47,1230,185],{},[23,1232,1233],{},[34,1234,190],{},[192,1236,1237,1240,1243],{},[195,1238,1239],{},"Rich hover-over information panels (item details, ability tooltips, character cards)",[195,1241,1242],{},"Anywhere you'd reach for a tooltip but need clickable content inside",[195,1244,1245,1246,1248],{},"Any place game text with ",[27,1247,483],{}," should appear on hover",[23,1250,1251],{},[34,1252,208],{},[192,1254,1255],{},[195,1256,1257,1258,1261],{},"Plain text tooltips — use ",[27,1259,1260],{},"v-tooltip"," (PrimeVue), it's lighter",[221,1263],{},[19,1265,1266],{"id":1266},"v-dragscroll",[23,1268,1269],{},"Enables drag-to-scroll on any scrollable container. Click and drag to scroll horizontally, vertically, or both.",[23,1271,1272],{},[34,1273,36],{},[38,1275,1277],{"className":40,"code":1276,"language":42,"meta":43,"style":43},"\u003C!-- Horizontal only -->\n\u003Cdiv class=\"scroll-container\" v-dragscroll.x>...\u003C\u002Fdiv>\n\n\u003C!-- Vertical only -->\n\u003Cdiv class=\"scroll-container\" v-dragscroll.y>...\u003C\u002Fdiv>\n\n\u003C!-- Both directions (default) -->\n\u003Cdiv class=\"scroll-container\" v-dragscroll>...\u003C\u002Fdiv>\n",[27,1278,1279,1285,1309,1313,1318,1339,1343,1348],{"__ignoreMap":43},[47,1280,1281],{"class":49,"line":50},[47,1282,1284],{"class":1283},"sJ8bj","\u003C!-- Horizontal only -->\n",[47,1286,1287,1289,1291,1294,1296,1299,1302,1305,1307],{"class":49,"line":115},[47,1288,54],{"class":53},[47,1290,244],{"class":57},[47,1292,1293],{"class":61}," class",[47,1295,65],{"class":53},[47,1297,1298],{"class":68},"\"scroll-container\"",[47,1300,1301],{"class":61}," v-dragscroll.x",[47,1303,1304],{"class":53},">...\u003C\u002F",[47,1306,244],{"class":57},[47,1308,255],{"class":53},[47,1310,1311],{"class":49,"line":121},[47,1312,125],{"emptyLinePlaceholder":124},[47,1314,1315],{"class":49,"line":128},[47,1316,1317],{"class":1283},"\u003C!-- Vertical only -->\n",[47,1319,1320,1322,1324,1326,1328,1330,1333,1335,1337],{"class":49,"line":134},[47,1321,54],{"class":53},[47,1323,244],{"class":57},[47,1325,1293],{"class":61},[47,1327,65],{"class":53},[47,1329,1298],{"class":68},[47,1331,1332],{"class":61}," v-dragscroll.y",[47,1334,1304],{"class":53},[47,1336,244],{"class":57},[47,1338,255],{"class":53},[47,1340,1341],{"class":49,"line":140},[47,1342,125],{"emptyLinePlaceholder":124},[47,1344,1345],{"class":49,"line":146},[47,1346,1347],{"class":1283},"\u003C!-- Both directions (default) -->\n",[47,1349,1350,1352,1354,1356,1358,1360,1363,1365,1367],{"class":49,"line":152},[47,1351,54],{"class":53},[47,1353,244],{"class":57},[47,1355,1293],{"class":61},[47,1357,65],{"class":53},[47,1359,1298],{"class":68},[47,1361,1362],{"class":61}," v-dragscroll",[47,1364,1304],{"class":53},[47,1366,244],{"class":57},[47,1368,255],{"class":53},[23,1370,1371],{},[34,1372,190],{},[192,1374,1375,1378],{},[195,1376,1377],{},"Horizontal card\u002Fcharacter lists that overflow their container",[195,1379,1380],{},"Any scrollable area where drag-to-scroll improves UX (especially touch devices)",[23,1382,1383],{},[34,1384,208],{},[192,1386,1387,1390],{},[195,1388,1389],{},"Containers that don't overflow",[195,1391,1392],{},"Areas where drag conflicts with other interactions (text selection, sliders)",[23,1394,1395,1396,488],{},"Powered by ",[435,1397,1401],{"href":1398,"rel":1399},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fvue-dragscroll",[1400],"nofollow","vue-dragscroll",[221,1403],{},[14,1405,1407],{"id":1406},"quick-reference","Quick Reference",[282,1409,1410,1423],{},[285,1411,1412],{},[288,1413,1414,1417,1420],{},[291,1415,1416],{},"Directive",[291,1418,1419],{},"Element",[291,1421,1422],{},"Purpose",[304,1424,1425,1438,1450,1465,1476,1487],{},[288,1426,1427,1431,1435],{},[309,1428,1429],{},[27,1430,21],{},[309,1432,1433],{},[27,1434,89],{},[309,1436,1437],{},"Keep loaded images in browser memory cache",[288,1439,1440,1444,1447],{},[309,1441,1442],{},[27,1443,225],{},[309,1445,1446],{},"Any",[309,1448,1449],{},"Shrink font size so text fits without clipping",[288,1451,1452,1456,1458],{},[309,1453,1454],{},[27,1455,422],{},[309,1457,1446],{},[309,1459,1460,1461,1464],{},"Render DryadScript text with ",[27,1462,1463],{},"[[lore-link]]"," interactivity",[288,1466,1467,1471,1473],{},[309,1468,1469],{},[27,1470,769],{},[309,1472,1446],{},[309,1474,1475],{},"Hover popover with HTML or Vue-component content (Floating UI)",[288,1477,1478,1482,1484],{},[309,1479,1480],{},[27,1481,1266],{},[309,1483,1446],{},[309,1485,1486],{},"Drag-to-scroll on scrollable containers",[288,1488,1489,1493,1495],{},[309,1490,1491],{},[27,1492,1260],{},[309,1494,1446],{},[309,1496,1497],{},"Show tooltip on hover (from PrimeVue)",[1499,1500,1501],"style",{},"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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}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 pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":43,"searchDepth":115,"depth":115,"links":1503},[1504,1511],{"id":16,"depth":115,"text":17,"children":1505},[1506,1507,1508,1509,1510],{"id":21,"depth":121,"text":21},{"id":225,"depth":121,"text":225},{"id":422,"depth":121,"text":422},{"id":769,"depth":121,"text":769},{"id":1266,"depth":121,"text":1266},{"id":1406,"depth":115,"text":1407},"md",{"plugin":1514,"category":1515,"page":1516},"global_essentials","advanced","custom_vue","\u002Fdocs\u002Fglobal_essentials\u002Fadvanced\u002Fcustom_vue",{"title":5,"description":43},"docs\u002Fglobal_essentials\u002Fadvanced\u002Fcustom_vue","dKq3sXI6HaZ7FXBlCAfLS4_XhMfLx3NMBHvdxjwBrXk",1779582262088]