[{"data":1,"prerenderedAt":797},["ShallowReactive",2],{"docs-\u002Fdocs\u002Fv2\u002Fextending\u002Fcommands-and-keys":3},{"id":4,"title":5,"body":6,"description":790,"extension":791,"meta":792,"navigation":713,"path":793,"seo":794,"stem":795,"__hash__":796},"docs\u002Fdocs\u002Fv2\u002Fextending\u002Fcommands-and-keys.md","Commands & Keys",{"type":7,"value":8,"toc":776},"minimark",[9,13,22,27,30,116,122,127,130,203,217,221,224,303,313,317,363,377,381,432,447,450,454,457,564,567,571,592,595,599,611,617,625,632,638,642,752,758,762,772],[10,11,5],"h1",{"id":12},"commands-keys",[14,15,16,17,21],"p",{},"Two ways to reach something you built: type it in the ",[18,19,20],"code",{},":"," palette, or press a key.",[23,24,26],"h2",{"id":25},"commands","Commands",[14,28,29],{},"A command is a name, an action, and a description.",[31,32,37],"pre",{"className":33,"code":34,"language":35,"meta":36,"style":36},"language-ts shiki shiki-themes dark-plus","commands.register(\"deploy\", async () => {\n    await commands.run(\"agent restart\")\n}, { description: \"Restart and redeploy\" })\n","ts","",[18,38,39,78,101],{"__ignoreMap":36},[40,41,44,47,51,55,58,62,65,69,72,75],"span",{"class":42,"line":43},"line",1,[40,45,25],{"class":46},"s3F5K",[40,48,50],{"class":49},"sTNBD",".",[40,52,54],{"class":53},"sCudf","register",[40,56,57],{"class":49},"(",[40,59,61],{"class":60},"sKc5r","\"deploy\"",[40,63,64],{"class":49},", ",[40,66,68],{"class":67},"scz_3","async",[40,70,71],{"class":49}," () ",[40,73,74],{"class":67},"=>",[40,76,77],{"class":49}," {\n",[40,79,81,85,88,90,93,95,98],{"class":42,"line":80},2,[40,82,84],{"class":83},"sYbnZ","    await",[40,86,87],{"class":46}," commands",[40,89,50],{"class":49},[40,91,92],{"class":53},"run",[40,94,57],{"class":49},[40,96,97],{"class":60},"\"agent restart\"",[40,99,100],{"class":49},")\n",[40,102,104,107,110,113],{"class":42,"line":103},3,[40,105,106],{"class":49},"}, { ",[40,108,109],{"class":46},"description:",[40,111,112],{"class":60}," \"Restart and redeploy\"",[40,114,115],{"class":49}," })\n",[14,117,118,119,121],{},"It appears in the ",[18,120,20],{}," palette beside the built-ins — filterable, tab-completable, with\nyour description shown next to it.",[123,124,126],"h3",{"id":125},"nested-paths","Nested paths",[14,128,129],{},"Pass an array to nest. Commands sharing a prefix share the group automatically:",[31,131,133],{"className":33,"code":132,"language":35,"meta":36,"style":36},"commands.register([\"git\", \"push\"], async () => { \u002F* ... *\u002F })\ncommands.register([\"git\", \"pull\"], async () => { \u002F* ... *\u002F })\n",[18,134,135,172],{"__ignoreMap":36},[40,136,137,139,141,143,146,149,151,154,157,159,161,163,166,170],{"class":42,"line":43},[40,138,25],{"class":46},[40,140,50],{"class":49},[40,142,54],{"class":53},[40,144,145],{"class":49},"([",[40,147,148],{"class":60},"\"git\"",[40,150,64],{"class":49},[40,152,153],{"class":60},"\"push\"",[40,155,156],{"class":49},"], ",[40,158,68],{"class":67},[40,160,71],{"class":49},[40,162,74],{"class":67},[40,164,165],{"class":49}," { ",[40,167,169],{"class":168},"sOLPB","\u002F* ... *\u002F",[40,171,115],{"class":49},[40,173,174,176,178,180,182,184,186,189,191,193,195,197,199,201],{"class":42,"line":80},[40,175,25],{"class":46},[40,177,50],{"class":49},[40,179,54],{"class":53},[40,181,145],{"class":49},[40,183,148],{"class":60},[40,185,64],{"class":49},[40,187,188],{"class":60},"\"pull\"",[40,190,156],{"class":49},[40,192,68],{"class":67},[40,194,71],{"class":49},[40,196,74],{"class":67},[40,198,165],{"class":49},[40,200,169],{"class":168},[40,202,115],{"class":49},[14,204,205,208,209,212,213,216],{},[18,206,207],{},":git "," now descends into a group with both. That is the same mechanism the built-in\n",[18,210,211],{},":provider"," and ",[18,214,215],{},":open"," groups use — there is nothing to declare, the group exists\nbecause two paths share a segment.",[123,218,220],{"id":219},"long-running-commands","Long-running commands",[14,222,223],{},"Return a promise and the palette holds a working row until it settles.",[31,225,227],{"className":33,"code":226,"language":35,"meta":36,"style":36},"commands.register(\"sync\", async signal => {\n    await longRunningThing({ signal })\n}, {\n    description: \"Sync everything\",\n    workingMessage: \"syncing...\",\n})\n",[18,228,229,254,269,274,286,297],{"__ignoreMap":36},[40,230,231,233,235,237,239,242,244,246,249,252],{"class":42,"line":43},[40,232,25],{"class":46},[40,234,50],{"class":49},[40,236,54],{"class":53},[40,238,57],{"class":49},[40,240,241],{"class":60},"\"sync\"",[40,243,64],{"class":49},[40,245,68],{"class":67},[40,247,248],{"class":46}," signal",[40,250,251],{"class":67}," =>",[40,253,77],{"class":49},[40,255,256,258,261,264,267],{"class":42,"line":80},[40,257,84],{"class":83},[40,259,260],{"class":53}," longRunningThing",[40,262,263],{"class":49},"({ ",[40,265,266],{"class":46},"signal",[40,268,115],{"class":49},[40,270,271],{"class":42,"line":103},[40,272,273],{"class":49},"}, {\n",[40,275,277,280,283],{"class":42,"line":276},4,[40,278,279],{"class":46},"    description:",[40,281,282],{"class":60}," \"Sync everything\"",[40,284,285],{"class":49},",\n",[40,287,289,292,295],{"class":42,"line":288},5,[40,290,291],{"class":46},"    workingMessage:",[40,293,294],{"class":60}," \"syncing...\"",[40,296,285],{"class":49},[40,298,300],{"class":42,"line":299},6,[40,301,302],{"class":49},"})\n",[14,304,305,306,308,309,312],{},"The ",[18,307,266],{}," aborts when the user presses ",[18,310,311],{},"Escape"," mid-flight. An action that cannot be\ncancelled can ignore it; one that can should honour it.",[123,314,316],{"id":315},"running-a-command-from-code","Running a command from code",[31,318,320],{"className":33,"code":319,"language":35,"meta":36,"style":36},"await commands.run(\"clear\")\nawait commands.run([\"agent\", \"restart\"])\n",[18,321,322,340],{"__ignoreMap":36},[40,323,324,327,329,331,333,335,338],{"class":42,"line":43},[40,325,326],{"class":83},"await",[40,328,87],{"class":46},[40,330,50],{"class":49},[40,332,92],{"class":53},[40,334,57],{"class":49},[40,336,337],{"class":60},"\"clear\"",[40,339,100],{"class":49},[40,341,342,344,346,348,350,352,355,357,360],{"class":42,"line":80},[40,343,326],{"class":83},[40,345,87],{"class":46},[40,347,50],{"class":49},[40,349,92],{"class":53},[40,351,145],{"class":49},[40,353,354],{"class":60},"\"agent\"",[40,356,64],{"class":49},[40,358,359],{"class":60},"\"restart\"",[40,361,362],{"class":49},"])\n",[14,364,365,366,370,371,374,375,50],{},"This reaches ",[367,368,369],"strong",{},"any"," command, including built-ins. It is the shortest path to\nfunctionality that has a command but no dedicated API — rather than re-implementing what\n",[18,372,373],{},":clear"," does, run ",[18,376,373],{},[23,378,380],{"id":379},"keys","Keys",[31,382,384],{"className":33,"code":383,"language":35,"meta":36,"style":36},"keys.register(\"ctrl+p\", async () => {\n    await palette.open(\"files\")\n})\n",[18,385,386,409,428],{"__ignoreMap":36},[40,387,388,390,392,394,396,399,401,403,405,407],{"class":42,"line":43},[40,389,379],{"class":46},[40,391,50],{"class":49},[40,393,54],{"class":53},[40,395,57],{"class":49},[40,397,398],{"class":60},"\"ctrl+p\"",[40,400,64],{"class":49},[40,402,68],{"class":67},[40,404,71],{"class":49},[40,406,74],{"class":67},[40,408,77],{"class":49},[40,410,411,413,416,418,421,423,426],{"class":42,"line":80},[40,412,84],{"class":83},[40,414,415],{"class":46}," palette",[40,417,50],{"class":49},[40,419,420],{"class":53},"open",[40,422,57],{"class":49},[40,424,425],{"class":60},"\"files\"",[40,427,100],{"class":49},[40,429,430],{"class":42,"line":103},[40,431,302],{"class":49},[14,433,434,435,64,438,285,441,64,444,50],{},"Chords are written the way you read them everywhere else: ",[18,436,437],{},"ctrl+o",[18,439,440],{},"ctrl+shift+p",[18,442,443],{},"shift+tab",[18,445,446],{},"f5",[14,448,449],{},"Handlers are fire-and-forget — nothing waits on your code while a key is held. If yours\nthrows, the chord and the failure flash on the status row rather than disappearing\nsilently.",[123,451,453],{"id":452},"keys-axon-keeps","Keys Axon keeps",[14,455,456],{},"Some chords belong to the terminal and cannot be rebound:",[458,459,460,473],"table",{},[461,462,463],"thead",{},[464,465,466,470],"tr",{},[467,468,469],"th",{},"Reserved",[467,471,472],{},"Why",[474,475,476,490,500,513,527],"tbody",{},[464,477,478,487],{},[479,480,481,64,484],"td",{},[18,482,483],{},"ctrl+c",[18,485,486],{},"ctrl+d",[479,488,489],{},"Quit — a config must never be able to make Axon unquittable",[464,491,492,497],{},[479,493,494],{},[18,495,496],{},"escape",[479,498,499],{},"Cancel, and back out of any mode",[464,501,502,510],{},[479,503,504,64,507],{},[18,505,506],{},"enter",[18,508,509],{},"tab",[479,511,512],{},"Submit and complete",[464,514,515,524],{},[479,516,517,518,64,521],{},"arrow keys, ",[18,519,520],{},"backspace",[18,522,523],{},"delete",[479,525,526],{},"Palette navigation and editing",[464,528,529,561],{},[479,530,531,533,534,533,537,533,540,533,543,533,546,533,549,533,552,533,555,533,558],{},[18,532,20],{}," ",[18,535,536],{},"~",[18,538,539],{},"\u002F",[18,541,542],{},"^",[18,544,545],{},"*",[18,547,548],{},"%",[18,550,551],{},"#",[18,553,554],{},"?",[18,556,557],{},">",[18,559,560],{},"!",[479,562,563],{},"Built-in mode keys",[14,565,566],{},"Registering one of these throws immediately, naming the chord. It fails at registration\nrather than silently not firing — a binding that does nothing is indistinguishable from\none that was never written, and that is a miserable thing to debug in your own config.",[123,568,570],{"id":569},"sending-a-key","Sending a key",[31,572,574],{"className":33,"code":573,"language":35,"meta":36,"style":36},"keys.send(\"ctrl+o\")\n",[18,575,576],{"__ignoreMap":36},[40,577,578,580,582,585,587,590],{"class":42,"line":43},[40,579,379],{"class":46},[40,581,50],{"class":49},[40,583,584],{"class":53},"send",[40,586,57],{"class":49},[40,588,589],{"class":60},"\"ctrl+o\"",[40,591,100],{"class":49},[14,593,594],{},"Delivered as though you had pressed it. This is the escape hatch: anything the API does\nnot expose directly is still reachable, because everything in the TUI is reachable from\nthe keyboard.",[23,596,598],{"id":597},"when-two-things-want-the-same-name","When two things want the same name",[14,600,601,604,605,607,608,610],{},[367,602,603],{},"Built-ins always win."," Registering ",[18,606,373],{}," or ",[18,609,483],{}," throws — the documented\nsurface cannot be replaced from a config file.",[14,612,613,616],{},[367,614,615],{},"Between your own registrations, the first one wins",", and load order decides which is\nfirst:",[31,618,623],{"className":619,"code":621,"language":622},[620],"language-text","main.ts (and anything it imports)\n  → plugins\u002F alphabetically\n    → each extension, in profile.config.ts order\n","text",[18,624,621],{"__ignoreMap":36},[14,626,627,628,631],{},"Your own config loads first, so it beats every extension. Between two extensions, the\none listed earlier in ",[18,629,630],{},"profile.config.ts"," wins.",[14,633,634,635,637],{},"A collision throws, naming both sides. Nothing is ever silently shadowed — \"which of my\ntwo extensions bound ",[18,636,437],{},"\" should never be a question you answer by bisecting your\nconfig.",[23,639,641],{"id":640},"reference","Reference",[31,643,645],{"className":33,"code":644,"language":35,"meta":36,"style":36},"commands.register(path, action, options?)  \u002F\u002F → Disposer\ncommands.run(path)                          \u002F\u002F → Promise\u003Cvoid>\ncommands.list()                             \u002F\u002F → string[][]\n\nkeys.register(chord, handler)               \u002F\u002F → Disposer\nkeys.send(chord)\n",[18,646,647,676,694,709,715,738],{"__ignoreMap":36},[40,648,649,651,653,655,657,660,662,665,667,670,673],{"class":42,"line":43},[40,650,25],{"class":46},[40,652,50],{"class":49},[40,654,54],{"class":53},[40,656,57],{"class":49},[40,658,659],{"class":46},"path",[40,661,64],{"class":49},[40,663,664],{"class":46},"action",[40,666,64],{"class":49},[40,668,669],{"class":46},"options",[40,671,672],{"class":49},"?)  ",[40,674,675],{"class":168},"\u002F\u002F → Disposer\n",[40,677,678,680,682,684,686,688,691],{"class":42,"line":80},[40,679,25],{"class":46},[40,681,50],{"class":49},[40,683,92],{"class":53},[40,685,57],{"class":49},[40,687,659],{"class":46},[40,689,690],{"class":49},")                          ",[40,692,693],{"class":168},"\u002F\u002F → Promise\u003Cvoid>\n",[40,695,696,698,700,703,706],{"class":42,"line":103},[40,697,25],{"class":46},[40,699,50],{"class":49},[40,701,702],{"class":53},"list",[40,704,705],{"class":49},"()                             ",[40,707,708],{"class":168},"\u002F\u002F → string[][]\n",[40,710,711],{"class":42,"line":276},[40,712,714],{"emptyLinePlaceholder":713},true,"\n",[40,716,717,719,721,723,725,728,730,733,736],{"class":42,"line":288},[40,718,379],{"class":46},[40,720,50],{"class":49},[40,722,54],{"class":53},[40,724,57],{"class":49},[40,726,727],{"class":46},"chord",[40,729,64],{"class":49},[40,731,732],{"class":46},"handler",[40,734,735],{"class":49},")               ",[40,737,675],{"class":168},[40,739,740,742,744,746,748,750],{"class":42,"line":299},[40,741,379],{"class":46},[40,743,50],{"class":49},[40,745,584],{"class":53},[40,747,57],{"class":49},[40,749,727],{"class":46},[40,751,100],{"class":49},[14,753,754,755,757],{},"Every ",[18,756,54],{}," returns a function that undoes it. You rarely need it — Axon disposes\neverything your config registered before a reload — but it is there when you want to\nunbind something conditionally.",[23,759,761],{"id":760},"whats-next","What's next",[14,763,764,771],{},[367,765,766],{},[767,768,770],"a",{"href":769},"\u002Fdocs\u002Fv2\u002Fextending\u002Fpalettes","Palettes"," — your own lists, and asking the user a\nquestion.",[773,774,775],"style",{},"html pre.shiki code .s3F5K, html code.shiki .s3F5K{--shiki-default:#9CDCFE}html pre.shiki code .sTNBD, html code.shiki .sTNBD{--shiki-default:#D4D4D4}html pre.shiki code .sCudf, html code.shiki .sCudf{--shiki-default:#DCDCAA}html pre.shiki code .sKc5r, html code.shiki .sKc5r{--shiki-default:#CE9178}html pre.shiki code .scz_3, html code.shiki .scz_3{--shiki-default:#569CD6}html pre.shiki code .sYbnZ, html code.shiki .sYbnZ{--shiki-default:#C586C0}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 pre.shiki code .sOLPB, html code.shiki .sOLPB{--shiki-default:#6A9955}",{"title":36,"searchDepth":80,"depth":80,"links":777},[778,783,787,788,789],{"id":25,"depth":80,"text":26,"children":779},[780,781,782],{"id":125,"depth":103,"text":126},{"id":219,"depth":103,"text":220},{"id":315,"depth":103,"text":316},{"id":379,"depth":80,"text":380,"children":784},[785,786],{"id":452,"depth":103,"text":453},{"id":569,"depth":103,"text":570},{"id":597,"depth":80,"text":598},{"id":640,"depth":80,"text":641},{"id":760,"depth":80,"text":761},"Two ways to reach something you built: type it in the : palette, or press a key.","md",{},"\u002Fdocs\u002Fv2\u002Fextending\u002Fcommands-and-keys",{"title":5,"description":790},"docs\u002Fv2\u002Fextending\u002Fcommands-and-keys","Yb9BIVK2nj6Ql0uX901KQFvzojzqdVSYVpIjO5Uf2lE",1786520846933]