[{"data":1,"prerenderedAt":1443},["ShallowReactive",2],{"docs-sections":3,"docs-page-\u002Fdocs\u002Fvue-composables":123},[4,9,13,17,22,26,30,34,38,42,46,50,55,59,63,67,72,76,80,85,89,93,97,102,106,110,114,119],{"path":5,"title":6,"description":7,"group":8},"\u002Fdocs","Introduction","What tunables is, the problem it solves, and the principles it holds to.","Get started",{"path":10,"title":11,"description":12,"group":8},"\u002Fdocs\u002Fquickstart","Quickstart","Take one group of settings from constants to a screen with a history, in a Nuxt app on Postgres.",{"path":14,"title":15,"description":16,"group":8},"\u002Fdocs\u002Fpatterns","What it is for","The kinds of value tunables keeps — live limits, plans, per-workspace and per-person values, model choices, values that bound each other, costly switches — and the feature that does each.",{"path":18,"title":19,"description":20,"group":21},"\u002Fdocs\u002Fsettings-and-groups","Settings and groups","Declare each setting once, in a group edited beside what it affects, and read it with types.","Concepts",{"path":23,"title":24,"description":25,"group":21},"\u002Fdocs\u002Fkinds-and-units","Kinds and units","The eight kinds of setting, the built-in units, your own units, and how values are checked and read.",{"path":27,"title":28,"description":29,"group":21},"\u002Fdocs\u002Fchecks-and-guards","Checks and guards","Rules across settings, guards for what your app keeps, within for presets, confirmations for costly changes, and notes.",{"path":31,"title":32,"description":33,"group":21},"\u002Fdocs\u002Fhistory-and-restore","History and restore","Every change is a revision with who, when, why and each value before and after; restoring one runs the same checks as any save.",{"path":35,"title":36,"description":37,"group":21},"\u002Fdocs\u002Fpresets","Presets","Named, reusable configurations with one default per slot, assigned by reference and bounded by the settings.",{"path":39,"title":40,"description":41,"group":21},"\u002Fdocs\u002Fscopes","Scopes","Values per workspace and per person, through layers — the system, a plan, the workspace, the person — each bounded by what it inherits.",{"path":43,"title":44,"description":45,"group":21},"\u002Fdocs\u002Fcatalogs","Catalogs","Lists of allowed choices, such as permitted models, whose entries are retired rather than deleted and cannot be retired while in use.",{"path":47,"title":48,"description":49,"group":21},"\u002Fdocs\u002Fdeployment-values","Deployment values","Values a deployment sets in its environment and nobody tunes while the app runs, declared once and listed in .env.example.",{"path":51,"title":52,"description":53,"group":54},"\u002Fdocs\u002Fstorage","Storage","Postgres through your own client, the four tables and how to create them, joining your transactions, and the storage contract behind it.","Integrate",{"path":56,"title":57,"description":58,"group":54},"\u002Fdocs\u002Fserver","Server and access","Mount tunables' API as a fetch handler in Nitro, Hono, Bun or Deno, as Express middleware or as an oRPC router, with access decided by your app.",{"path":60,"title":61,"description":62,"group":54},"\u002Fdocs\u002Faccess","Who can do what","End users bound by values they never see, staff who tune groups, owners and members who change only their own scope, and the terminal.",{"path":64,"title":65,"description":66,"group":54},"\u002Fdocs\u002Ftesting","Testing","Your app's tunables in memory for its own tests, with values set where a test needs them, and how to test rules, guards and confirmations.",{"path":68,"title":69,"description":70,"group":71},"\u002Fdocs\u002Fnuxt-layer","The Nuxt layer","Ready screens for a Nuxt UI app — a group's form, All settings, history, presets, assignments and catalogs — placed beside what they affect.","Screens",{"path":73,"title":74,"description":75,"group":71},"\u002Fdocs\u002Fvue-composables","Vue composables","Headless composables for building tunables' screens in any Vue app, on any design system.",{"path":77,"title":78,"description":79,"group":71},"\u002Fdocs\u002Ftheming","Theming","The screens bring no colors, fonts or CSS of their own; they take your Nuxt UI theme.",{"path":81,"title":82,"description":83,"group":84},"\u002Fdocs\u002Fcli","The CLI","The tunables command, tunables.config.ts, and which commands need a database.","CLI",{"path":86,"title":87,"description":88,"group":84},"\u002Fdocs\u002Fcli-check","Check and generate","The commands that need no database — checking definitions, writing .env.example, and keeping explanations and defaults in step with a pdd-kit Product Model.",{"path":90,"title":91,"description":92,"group":84},"\u002Fdocs\u002Fcli-values","Read and change values","The commands that read and change stored values, through the same checks and history as the screens.",{"path":94,"title":95,"description":96,"group":84},"\u002Fdocs\u002Fcli-skills","Install skills","Install tunables' agent skills for Claude Code, Codex CLI, Cursor, Gemini CLI and GitHub Copilot, and keep them current.",{"path":98,"title":99,"description":100,"group":101},"\u002Fdocs\u002Fskills","Agent skills","Three skills that let a coding agent adopt tunables, define settings the right way and verify that every value has one home.","Skills",{"path":103,"title":104,"description":105,"group":101},"\u002Fdocs\u002Fskill-adopt","tunables-adopt","Bring tunables into an app that has none yet, from an inventory of every value to a wired, synced app with the old homes removed.",{"path":107,"title":108,"description":109,"group":101},"\u002Fdocs\u002Fskill-define","tunables-define","Add, change, rename or remove one value in an app that uses tunables, with its home, its checks, its reads and what happens to stored values.",{"path":111,"title":112,"description":113,"group":101},"\u002Fdocs\u002Fskill-verify","tunables-verify","Audit an app's tunables against their rules, report every finding with where it is and the rule it breaks, and fix only the findings you choose.",{"path":115,"title":116,"description":117,"group":118},"\u002Fdocs\u002Fapi","API reference","Every export of the tunables package, by the subpath it is imported from.","Reference",{"path":120,"title":121,"description":122,"group":118},"\u002Fdocs\u002Ftasklane-example","Tasklane, the example app","A todo app with workspaces and people as tunables — two groups, plans as a layer, a catalog of models, scopes, a rule, a confirmation, and the playground that runs it.",{"id":124,"title":74,"body":125,"description":75,"extension":1438,"group":71,"meta":1439,"navigation":215,"order":792,"path":73,"seo":1440,"stem":1441,"__hash__":1442},"docs\u002Fdocs\u002Fvue-composables.md",{"type":126,"value":127,"toc":1431},"minimark",[128,132,140,145,148,250,253,371,406,421,440,444,447,906,1057,1063,1068,1076,1128,1137,1205,1246,1250,1418,1421,1427],[129,130,74],"h1",{"id":131},"vue-composables",[133,134,135,139],"p",{},[136,137,138],"code",{},"tunables\u002Fvue"," holds what a form, a history or a catalog screen needs, with no\nmarkup: state, drafts, problems and the calls that change things. The Nuxt\nlayer's screens are built on these; build your own on them when you use\nanother design system, or Vue without Nuxt.",[141,142,144],"h2",{"id":143},"providing-a-client","Providing a client",[133,146,147],{},"Every composable calls tunables through the client provided above it. Provide\none once, in your root component:",[149,150,155],"pre",{"className":151,"code":152,"language":153,"meta":154,"style":154},"language-ts shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","import { fetchClient } from 'tunables\u002Fclient'\nimport { provideTunables } from 'tunables\u002Fvue'\n\nprovideTunables(fetchClient({ baseURL: '\u002Fapi\u002Ftunables' }))\n","ts","",[136,156,157,190,210,217],{"__ignoreMap":154},[158,159,162,166,170,174,177,180,183,187],"span",{"class":160,"line":161},"line",1,[158,163,165],{"class":164},"sZ4mS","import",[158,167,169],{"class":168},"sp9ci"," {",[158,171,173],{"class":172},"sVz71"," fetchClient ",[158,175,176],{"class":168},"}",[158,178,179],{"class":164}," from",[158,181,182],{"class":168}," '",[158,184,186],{"class":185},"sjCzm","tunables\u002Fclient",[158,188,189],{"class":168},"'\n",[158,191,193,195,197,200,202,204,206,208],{"class":160,"line":192},2,[158,194,165],{"class":164},[158,196,169],{"class":168},[158,198,199],{"class":172}," provideTunables ",[158,201,176],{"class":168},[158,203,179],{"class":164},[158,205,182],{"class":168},[158,207,138],{"class":185},[158,209,189],{"class":168},[158,211,213],{"class":160,"line":212},3,[158,214,216],{"emptyLinePlaceholder":215},true,"\n",[158,218,220,224,227,230,233,236,239,241,244,247],{"class":160,"line":219},4,[158,221,223],{"class":222},"sOThN","provideTunables",[158,225,226],{"class":168},"(",[158,228,229],{"class":222},"fetchClient",[158,231,232],{"class":168},"({",[158,234,235],{"class":172}," baseURL",[158,237,238],{"class":168},":",[158,240,182],{"class":168},[158,242,243],{"class":185},"\u002Fapi\u002Ftunables",[158,245,246],{"class":168},"'",[158,248,249],{"class":168}," }))\n",[133,251,252],{},"Or at the app level, with the injection key:",[149,254,256],{"className":151,"code":255,"language":153,"meta":154,"style":154},"import { createApp } from 'vue'\nimport { fetchClient } from 'tunables\u002Fclient'\nimport { TUNABLES_CLIENT } from 'tunables\u002Fvue'\n\ncreateApp(App).provide(TUNABLES_CLIENT, fetchClient()).mount('#app')\n",[136,257,258,278,296,315,319],{"__ignoreMap":154},[158,259,260,262,264,267,269,271,273,276],{"class":160,"line":161},[158,261,165],{"class":164},[158,263,169],{"class":168},[158,265,266],{"class":172}," createApp ",[158,268,176],{"class":168},[158,270,179],{"class":164},[158,272,182],{"class":168},[158,274,275],{"class":185},"vue",[158,277,189],{"class":168},[158,279,280,282,284,286,288,290,292,294],{"class":160,"line":192},[158,281,165],{"class":164},[158,283,169],{"class":168},[158,285,173],{"class":172},[158,287,176],{"class":168},[158,289,179],{"class":164},[158,291,182],{"class":168},[158,293,186],{"class":185},[158,295,189],{"class":168},[158,297,298,300,302,305,307,309,311,313],{"class":160,"line":212},[158,299,165],{"class":164},[158,301,169],{"class":168},[158,303,304],{"class":172}," TUNABLES_CLIENT ",[158,306,176],{"class":168},[158,308,179],{"class":164},[158,310,182],{"class":168},[158,312,138],{"class":185},[158,314,189],{"class":168},[158,316,317],{"class":160,"line":219},[158,318,216],{"emptyLinePlaceholder":215},[158,320,322,325,327,330,333,337,340,342,345,348,351,354,356,359,361,363,366,368],{"class":160,"line":321},5,[158,323,324],{"class":222},"createApp",[158,326,226],{"class":168},[158,328,329],{"class":172},"App",[158,331,332],{"class":168},")",[158,334,336],{"class":335},"sBd0f",".",[158,338,339],{"class":222},"provide",[158,341,226],{"class":168},[158,343,344],{"class":172},"TUNABLES_CLIENT",[158,346,347],{"class":168},",",[158,349,350],{"class":222}," fetchClient",[158,352,353],{"class":168},"())",[158,355,336],{"class":335},[158,357,358],{"class":222},"mount",[158,360,226],{"class":168},[158,362,246],{"class":168},[158,364,365],{"class":185},"#app",[158,367,246],{"class":168},[158,369,370],{"class":168},")\n",[133,372,373,375,376,379,380,382,383,386,387,390,391,379,394,397,398,401,402,336],{},[136,374,229],{}," takes a ",[136,377,378],{},"baseURL"," (",[136,381,243],{}," by default), a ",[136,384,385],{},"fetch"," of your\nown, ",[136,388,389],{},"headers"," for every request (an object or a function, such as one that\nadds a CSRF token) and ",[136,392,393],{},"credentials",[136,395,396],{},"same-origin"," by default). ",[136,399,400],{},"orpcClient","\ndoes the same over an oRPC client; see\n",[403,404,69],"a",{"href":405},".\u002Fnuxt-layer#another-client",[133,407,408,409,412,413,416,417,420],{},"The client's ",[136,410,411],{},"group(id, scope?)",", ",[136,414,415],{},"save(id, { values, scope? })"," and\n",[136,418,419],{},"reset(id, { keys?, scope? })"," take a scope path too, for code that calls it\ndirectly.",[133,422,423,424,427,428,431,432,436,437,336],{},"A request the server refused or could not answer throws a\n",[136,425,426],{},"TunablesClientError"," with its ",[136,429,430],{},"status",". A refused ",[433,434,435],"em",{},"change"," does not throw: it\ncomes back with its ",[136,438,439],{},"problems",[141,441,443],{"id":442},"usegroupform","useGroupForm",[133,445,446],{},"One group's form.",[149,448,451],{"className":449,"code":450,"language":275,"meta":154,"style":154},"language-vue shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","\u003Cscript setup lang=\"ts\">\nimport { useGroupForm, useUnloadGuard } from 'tunables\u002Fvue'\n\nconst form = useGroupForm('limits')\nuseUnloadGuard(form.dirty)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cform v-if=\"form.group.value\" @submit.prevent=\"form.save()\">\n    \u003Clabel v-for=\"field in form.group.value.fields\" :key=\"field.key\">\n      {{ field.label }}\n      \u003Cinput v-model.number=\"form.draft[field.key]\" type=\"number\">\n      \u003Csmall v-for=\"problem in form.fieldProblems(field.key)\" :key=\"problem\">{{ problem }}\u003C\u002Fsmall>\n    \u003C\u002Flabel>\n    \u003Cinput v-model=\"form.reason.value\" placeholder=\"Why\">\n    \u003Cbutton :disabled=\"!form.dirty.value || form.saving.value\">\n      Save\n    \u003C\u002Fbutton>\n    \u003Cp v-if=\"form.confirm.value\">\n      {{ form.confirm.value }}\n      \u003Cbutton type=\"button\" @click=\"form.confirmAndSave()\">\n        Save anyway\n      \u003C\u002Fbutton>\n    \u003C\u002Fp>\n  \u003C\u002Fform>\n\u003C\u002Ftemplate>\n",[136,452,453,480,504,508,531,548,558,563,573,607,642,648,683,725,735,768,790,796,805,825,831,862,868,878,887,897],{"__ignoreMap":154},[158,454,455,458,461,464,467,470,473,475,477],{"class":160,"line":161},[158,456,457],{"class":168},"\u003C",[158,459,460],{"class":185},"script",[158,462,463],{"class":222}," setup",[158,465,466],{"class":222}," lang",[158,468,469],{"class":168},"=",[158,471,472],{"class":168},"\"",[158,474,153],{"class":185},[158,476,472],{"class":168},[158,478,479],{"class":168},">\n",[158,481,482,484,486,489,491,494,496,498,500,502],{"class":160,"line":192},[158,483,165],{"class":164},[158,485,169],{"class":168},[158,487,488],{"class":172}," useGroupForm",[158,490,347],{"class":168},[158,492,493],{"class":172}," useUnloadGuard ",[158,495,176],{"class":168},[158,497,179],{"class":164},[158,499,182],{"class":168},[158,501,138],{"class":185},[158,503,189],{"class":168},[158,505,506],{"class":160,"line":212},[158,507,216],{"emptyLinePlaceholder":215},[158,509,510,513,516,518,520,522,524,527,529],{"class":160,"line":219},[158,511,512],{"class":164},"const",[158,514,515],{"class":172}," form ",[158,517,469],{"class":168},[158,519,488],{"class":222},[158,521,226],{"class":168},[158,523,246],{"class":168},[158,525,526],{"class":185},"limits",[158,528,246],{"class":168},[158,530,370],{"class":168},[158,532,533,536,538,541,543,546],{"class":160,"line":321},[158,534,535],{"class":222},"useUnloadGuard",[158,537,226],{"class":168},[158,539,540],{"class":172},"form",[158,542,336],{"class":168},[158,544,545],{"class":172},"dirty",[158,547,370],{"class":168},[158,549,551,554,556],{"class":160,"line":550},6,[158,552,553],{"class":168},"\u003C\u002F",[158,555,460],{"class":185},[158,557,479],{"class":168},[158,559,561],{"class":160,"line":560},7,[158,562,216],{"emptyLinePlaceholder":215},[158,564,566,568,571],{"class":160,"line":565},8,[158,567,457],{"class":168},[158,569,570],{"class":185},"template",[158,572,479],{"class":168},[158,574,576,579,581,584,586,588,591,593,596,598,600,603,605],{"class":160,"line":575},9,[158,577,578],{"class":168},"  \u003C",[158,580,540],{"class":185},[158,582,583],{"class":222}," v-if",[158,585,469],{"class":168},[158,587,472],{"class":168},[158,589,590],{"class":185},"form.group.value",[158,592,472],{"class":168},[158,594,595],{"class":222}," @submit.prevent",[158,597,469],{"class":168},[158,599,472],{"class":168},[158,601,602],{"class":185},"form.save()",[158,604,472],{"class":168},[158,606,479],{"class":168},[158,608,610,613,616,619,621,623,626,628,631,633,635,638,640],{"class":160,"line":609},10,[158,611,612],{"class":168},"    \u003C",[158,614,615],{"class":185},"label",[158,617,618],{"class":222}," v-for",[158,620,469],{"class":168},[158,622,472],{"class":168},[158,624,625],{"class":185},"field in form.group.value.fields",[158,627,472],{"class":168},[158,629,630],{"class":222}," :key",[158,632,469],{"class":168},[158,634,472],{"class":168},[158,636,637],{"class":185},"field.key",[158,639,472],{"class":168},[158,641,479],{"class":168},[158,643,645],{"class":160,"line":644},11,[158,646,647],{"class":172},"      {{ field.label }}\n",[158,649,651,654,657,660,662,664,667,669,672,674,676,679,681],{"class":160,"line":650},12,[158,652,653],{"class":168},"      \u003C",[158,655,656],{"class":185},"input",[158,658,659],{"class":222}," v-model.number",[158,661,469],{"class":168},[158,663,472],{"class":168},[158,665,666],{"class":185},"form.draft[field.key]",[158,668,472],{"class":168},[158,670,671],{"class":222}," type",[158,673,469],{"class":168},[158,675,472],{"class":168},[158,677,678],{"class":185},"number",[158,680,472],{"class":168},[158,682,479],{"class":168},[158,684,686,688,691,693,695,697,700,702,704,706,708,711,713,716,719,721,723],{"class":160,"line":685},13,[158,687,653],{"class":168},[158,689,690],{"class":185},"small",[158,692,618],{"class":222},[158,694,469],{"class":168},[158,696,472],{"class":168},[158,698,699],{"class":185},"problem in form.fieldProblems(field.key)",[158,701,472],{"class":168},[158,703,630],{"class":222},[158,705,469],{"class":168},[158,707,472],{"class":168},[158,709,710],{"class":185},"problem",[158,712,472],{"class":168},[158,714,715],{"class":168},">",[158,717,718],{"class":172},"{{ problem }}",[158,720,553],{"class":168},[158,722,690],{"class":185},[158,724,479],{"class":168},[158,726,728,731,733],{"class":160,"line":727},14,[158,729,730],{"class":168},"    \u003C\u002F",[158,732,615],{"class":185},[158,734,479],{"class":168},[158,736,738,740,742,745,747,749,752,754,757,759,761,764,766],{"class":160,"line":737},15,[158,739,612],{"class":168},[158,741,656],{"class":185},[158,743,744],{"class":222}," v-model",[158,746,469],{"class":168},[158,748,472],{"class":168},[158,750,751],{"class":185},"form.reason.value",[158,753,472],{"class":168},[158,755,756],{"class":222}," placeholder",[158,758,469],{"class":168},[158,760,472],{"class":168},[158,762,763],{"class":185},"Why",[158,765,472],{"class":168},[158,767,479],{"class":168},[158,769,771,773,776,779,781,783,786,788],{"class":160,"line":770},16,[158,772,612],{"class":168},[158,774,775],{"class":185},"button",[158,777,778],{"class":222}," :disabled",[158,780,469],{"class":168},[158,782,472],{"class":168},[158,784,785],{"class":185},"!form.dirty.value || form.saving.value",[158,787,472],{"class":168},[158,789,479],{"class":168},[158,791,793],{"class":160,"line":792},17,[158,794,795],{"class":172},"      Save\n",[158,797,799,801,803],{"class":160,"line":798},18,[158,800,730],{"class":168},[158,802,775],{"class":185},[158,804,479],{"class":168},[158,806,808,810,812,814,816,818,821,823],{"class":160,"line":807},19,[158,809,612],{"class":168},[158,811,133],{"class":185},[158,813,583],{"class":222},[158,815,469],{"class":168},[158,817,472],{"class":168},[158,819,820],{"class":185},"form.confirm.value",[158,822,472],{"class":168},[158,824,479],{"class":168},[158,826,828],{"class":160,"line":827},20,[158,829,830],{"class":172},"      {{ form.confirm.value }}\n",[158,832,834,836,838,840,842,844,846,848,851,853,855,858,860],{"class":160,"line":833},21,[158,835,653],{"class":168},[158,837,775],{"class":185},[158,839,671],{"class":222},[158,841,469],{"class":168},[158,843,472],{"class":168},[158,845,775],{"class":185},[158,847,472],{"class":168},[158,849,850],{"class":222}," @click",[158,852,469],{"class":168},[158,854,472],{"class":168},[158,856,857],{"class":185},"form.confirmAndSave()",[158,859,472],{"class":168},[158,861,479],{"class":168},[158,863,865],{"class":160,"line":864},22,[158,866,867],{"class":172},"        Save anyway\n",[158,869,871,874,876],{"class":160,"line":870},23,[158,872,873],{"class":168},"      \u003C\u002F",[158,875,775],{"class":185},[158,877,479],{"class":168},[158,879,881,883,885],{"class":160,"line":880},24,[158,882,730],{"class":168},[158,884,133],{"class":185},[158,886,479],{"class":168},[158,888,890,893,895],{"class":160,"line":889},25,[158,891,892],{"class":168},"  \u003C\u002F",[158,894,540],{"class":185},[158,896,479],{"class":168},[158,898,900,902,904],{"class":160,"line":899},26,[158,901,553],{"class":168},[158,903,570],{"class":185},[158,905,479],{"class":168},[907,908,909,922],"table",{},[910,911,912],"thead",{},[913,914,915,919],"tr",{},[916,917,918],"th",{},"Returns",[916,920,921],{},"What it is",[923,924,925,936,946,956,968,983,993,1003,1019,1035],"tbody",{},[913,926,927,933],{},[928,929,930],"td",{},[136,931,932],{},"group",[928,934,935],{},"The group's description: title, sections, fields with labels, help, units and defaults.",[913,937,938,943],{},[928,939,940],{},[136,941,942],{},"entry",[928,944,945],{},"The stored entry.",[913,947,948,953],{},[928,949,950],{},[136,951,952],{},"draft",[928,954,955],{},"A reactive copy of the values, to bind inputs to.",[913,957,958,965],{},[928,959,960,412,962],{},[136,961,545],{},[136,963,964],{},"dirtyKeys",[928,966,967],{},"Whether, and which, fields differ from what is stored.",[913,969,970,980],{},[928,971,972,412,974,412,977],{},[136,973,439],{},[136,975,976],{},"fieldProblems(key)",[136,978,979],{},"generalProblems",[928,981,982],{},"What the last save answered, all, per field, or about the whole change.",[913,984,985,990],{},[928,986,987],{},[136,988,989],{},"confirm",[928,991,992],{},"The question the last save asked, if any.",[913,994,995,1000],{},[928,996,997],{},[136,998,999],{},"reason",[928,1001,1002],{},"The reason to send with the save.",[913,1004,1005,1016],{},[928,1006,1007,412,1010,412,1013],{},[136,1008,1009],{},"save()",[136,1011,1012],{},"confirmAndSave()",[136,1014,1015],{},"cancelConfirm()",[928,1017,1018],{},"Save the changed fields; send them again confirmed; drop the question.",[913,1020,1021,1032],{},[928,1022,1023,412,1026,412,1029],{},[136,1024,1025],{},"isDefault(field)",[136,1027,1028],{},"toDefault(field)",[136,1030,1031],{},"discard()",[928,1033,1034],{},"Check a field against its default; put its draft back to it; drop every unsaved change.",[913,1036,1037,1054],{},[928,1038,1039,412,1042,412,1045,412,1048,412,1051],{},[136,1040,1041],{},"loading",[136,1043,1044],{},"saving",[136,1046,1047],{},"error",[136,1049,1050],{},"lastSaved",[136,1052,1053],{},"refresh()",[928,1055,1056],{},"State, the last outcome, and a reload.",[133,1058,1059,1062],{},[136,1060,1061],{},"save"," sends only the fields that changed, and resolves to whether they were\nsaved.",[1064,1065,1067],"h3",{"id":1066},"at-a-scope","At a scope",[133,1069,1070,1071,1075],{},"Pass a ",[403,1072,1074],{"href":1073},".\u002Fscopes","scope"," to edit that scope's own values, such as a\nworkspace's owner on their workspace's page:",[149,1077,1079],{"className":151,"code":1078,"language":153,"meta":154,"style":154},"const form = useGroupForm('limits', { scope: { workspace: 'acme' } })\n",[136,1080,1081],{"__ignoreMap":154},[158,1082,1083,1085,1087,1089,1091,1093,1095,1097,1099,1101,1103,1106,1108,1110,1113,1115,1117,1120,1122,1125],{"class":160,"line":161},[158,1084,512],{"class":164},[158,1086,515],{"class":172},[158,1088,469],{"class":168},[158,1090,488],{"class":222},[158,1092,226],{"class":168},[158,1094,246],{"class":168},[158,1096,526],{"class":185},[158,1098,246],{"class":168},[158,1100,347],{"class":168},[158,1102,169],{"class":168},[158,1104,1105],{"class":172}," scope",[158,1107,238],{"class":168},[158,1109,169],{"class":168},[158,1111,1112],{"class":172}," workspace",[158,1114,238],{"class":168},[158,1116,182],{"class":168},[158,1118,1119],{"class":185},"acme",[158,1121,246],{"class":168},[158,1123,1124],{"class":168}," }",[158,1126,1127],{"class":168}," })\n",[133,1129,1130,1132,1133,1136],{},[136,1131,1074],{}," may be a ref; the form reloads when it changes. The entry then carries\n",[136,1134,1135],{},"layers",", and the form adds:",[907,1138,1139,1147],{},[910,1140,1141],{},[913,1142,1143,1145],{},[916,1144,918],{},[916,1146,921],{},[923,1148,1149,1158,1168,1178,1191],{},[913,1150,1151,1155],{},[928,1152,1153],{},[136,1154,1074],{},[928,1156,1157],{},"The path, or undefined for the whole system.",[913,1159,1160,1165],{},[928,1161,1162],{},[136,1163,1164],{},"editable",[928,1166,1167],{},"The fields this scope may set. Show only these.",[913,1169,1170,1175],{},[928,1171,1172],{},[136,1173,1174],{},"inheriting",[928,1176,1177],{},"The fields that will use the inherited value again on save.",[913,1179,1180,1185],{},[928,1181,1182],{},[136,1183,1184],{},"useInherited(key)",[928,1186,1187,1188,336],{},"Stop setting a field here: its draft goes back to what it inherits, and the save sends ",[136,1189,1190],{},"null",[913,1192,1193,1198],{},[928,1194,1195],{},[136,1196,1197],{},"setHere(key)",[928,1199,1200,1201,1204],{},"Set a field here again, after ",[136,1202,1203],{},"useInherited",". Call it when the person edits the field.",[133,1206,1207,1210,1211,1214,1215,1218,1219,1222,1223,1226,1227,1230,1231,1234,1235,1214,1238,1241,1242,1245],{},[136,1208,1209],{},"entry.value.layers[key]"," says, per field, what it inherits (",[136,1212,1213],{},"inherited",",\n",[136,1216,1217],{},"inheritedDisplay","), from where (",[136,1220,1221],{},"fromLabel",", such as ",[433,1224,1225],{},"Pro plan","), whether\nthis scope sets its own (",[136,1228,1229],{},"overridden","), and its bound (",[136,1232,1233],{},"rule",": ",[136,1236,1237],{},"free",[136,1239,1240],{},"at-most"," or ",[136,1243,1244],{},"at-least",").",[141,1247,1249],{"id":1248},"the-others","The others",[907,1251,1252,1262],{},[910,1253,1254],{},[913,1255,1256,1259],{},[916,1257,1258],{},"Composable",[916,1260,1261],{},"Gives",[923,1263,1264,1284,1303,1336,1367,1395,1408],{},[913,1265,1266,1271],{},[928,1267,1268],{},[136,1269,1270],{},"useTunablesDescription()",[928,1272,1273,1276,1277,412,1279,412,1282,336],{},[136,1274,1275],{},"description"," of every group, preset set and catalog, fetched once per client; ",[136,1278,1047],{},[136,1280,1281],{},"pending",[136,1283,1053],{},[913,1285,1286,1291],{},[928,1287,1288],{},[136,1289,1290],{},"useOverview()",[928,1292,1293,1296,1297,412,1299,412,1301,336],{},[136,1294,1295],{},"entries"," of every group, for All settings; ",[136,1298,1041],{},[136,1300,1047],{},[136,1302,1053],{},[913,1304,1305,1310],{},[928,1306,1307],{},[136,1308,1309],{},"useHistory(target, { pageSize })",[928,1311,1312,1315,1316,412,1319,412,1322,1324,1325,1328,1329,1332,1333,336],{},[136,1313,1314],{},"revisions"," newest first, ",[136,1317,1318],{},"more",[136,1320,1321],{},"loadMore()",[136,1323,1053],{},", and ",[136,1326,1327],{},"restore(revision, { reason, confirmed })",". For a scope's history, the target's ",[136,1330,1331],{},"id"," is the scope key: ",[136,1334,1335],{},"{ kind: 'group', name: 'limits', id: 'workspace=acme' }",[913,1337,1338,1343],{},[928,1339,1340],{},[136,1341,1342],{},"usePresets(set)",[928,1344,1345,1348,1349,412,1352,412,1355,412,1358,412,1361,412,1364,336],{},[136,1346,1347],{},"presets"," with how many are assigned each; ",[136,1350,1351],{},"create",[136,1353,1354],{},"update",[136,1356,1357],{},"remove",[136,1359,1360],{},"makeDefault",[136,1362,1363],{},"assign",[136,1365,1366],{},"assignments",[913,1368,1369,1374],{},[928,1370,1371],{},[136,1372,1373],{},"useCatalog(catalog)",[928,1375,1376,1378,1379,1382,1383,412,1386,412,1389,412,1392,336],{},[136,1377,1295],{}," with what uses each, ",[136,1380,1381],{},"available","; ",[136,1384,1385],{},"add",[136,1387,1388],{},"edit",[136,1390,1391],{},"retire",[136,1393,1394],{},"reinstate",[913,1396,1397,1402],{},[928,1398,1399],{},[136,1400,1401],{},"useUnloadGuard(dirty)",[928,1403,1404,1405,1407],{},"Asks before the page unloads while ",[136,1406,545],{}," is true. Add your router's own leave guard beside it.",[913,1409,1410,1415],{},[928,1411,1412],{},[136,1413,1414],{},"useTunablesClient()",[928,1416,1417],{},"The provided client, for anything the composables do not cover.",[133,1419,1420],{},"Each composable that takes an id also takes a ref, and reloads when it\nchanges. Each change resolves to its outcome and refreshes the list when it\nwas saved.",[133,1422,1423,1426],{},[136,1424,1425],{},"displayValue(field, value)"," reads a value the way the server does, from the\nfield's description, for a value still being edited.",[1428,1429,1430],"style",{},"html pre.shiki code .sZ4mS, html code.shiki .sZ4mS{--shiki-light:#115E59;--shiki-default:#115E59;--shiki-dark:#5EEAD4}html pre.shiki code .sp9ci, html code.shiki .sp9ci{--shiki-light:#4F5D63;--shiki-default:#4F5D63;--shiki-dark:#A3AFB4}html pre.shiki code .sVz71, html code.shiki .sVz71{--shiki-light:#182025;--shiki-default:#182025;--shiki-dark:#E6EBEC}html pre.shiki code .sjCzm, html code.shiki .sjCzm{--shiki-light:#92400E;--shiki-default:#92400E;--shiki-dark:#FCD34D}html pre.shiki code .sOThN, html code.shiki .sOThN{--shiki-light:#2A3439;--shiki-light-font-weight:bold;--shiki-default:#2A3439;--shiki-default-font-weight:bold;--shiki-dark:#F6F8F8;--shiki-dark-font-weight:bold}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);}html pre.shiki code .sBd0f, html code.shiki .sBd0f{--shiki-light:#4F5D63;--shiki-light-font-weight:bold;--shiki-default:#4F5D63;--shiki-default-font-weight:bold;--shiki-dark:#A3AFB4;--shiki-dark-font-weight:bold}",{"title":154,"searchDepth":192,"depth":192,"links":1432},[1433,1434,1437],{"id":143,"depth":192,"text":144},{"id":442,"depth":192,"text":443,"children":1435},[1436],{"id":1066,"depth":212,"text":1067},{"id":1248,"depth":192,"text":1249},"md",{},{"title":74,"description":75},"docs\u002Fvue-composables","n_aoZ5PZvMusi2IM60ThmlXfWpk02TzrhquS5xxC_Fk",1792845119929]