opencode-navigator 0.21.3 → 0.21.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## [0.21.4] - 2026-10-07
6
+
7
+ - Add `c` to Navigator shortcuts for a manual update check and `u` to check then confirm a Navigator update. Report available, current, or unavailable results, preserve the installation method and scope, and require a restart after updating.
8
+ - Ignore cancelled, outdated, and repeated update confirmations, and recover from stalled update checks without blocking later checks.
9
+
5
10
  ## [0.21.3] - 2026-10-06
6
11
 
7
12
  - Replace block quota meters with full-width adaptive thin-line scales and keep quota rows inside the sidebar's padding after resize. Show only reset time below each meter, removing technical account and window-duration suffixes.
@@ -90,6 +95,7 @@
90
95
 
91
96
  Earlier release notes are available on [GitHub Releases](https://github.com/St1ggy/opencode-navigator/releases).
92
97
 
98
+ [0.21.4]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.3...v0.21.4
93
99
  [0.21.3]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.2...v0.21.3
94
100
  [0.21.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.1...v0.21.2
95
101
  [0.21.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.0...v0.21.1
package/README.md CHANGED
@@ -33,7 +33,7 @@ See the [changelog](CHANGELOG.md) for release-by-release changes.
33
33
  - Save visibility, expansion, and order globally or for the current worktree
34
34
  - Copy the selected scope's layout and MCP states as versioned JSON, then validate and preview imports
35
35
  - Focus the sidebar with `Ctrl+Shift+F`, then navigate with arrows or `j`/`k`
36
- - Open sidebar shortcut mode with `Ctrl+Shift+B`; press `h` to toggle the panel
36
+ - Open sidebar shortcut mode with `Ctrl+Shift+B`; press `h` to toggle the panel, `c` to check Navigator updates, or `u` to check and confirm an update
37
37
  - Optionally open new sessions directly in an empty chat with the sidebar, skipping Home on startup and from New session
38
38
  - Keep OpenCode's compact footer while showing host and Navigator versions with independent update indicators; click a version label or its update icon to confirm an in-place update through the current installation method and scope
39
39
 
@@ -636,6 +636,25 @@ The command palette also exposes `Focus sidebar` and direct commands for Todo,
636
636
  Subagents, Skills, Quick Actions, LSP, and MCP. Both sidebar shortcuts can be
637
637
  changed at runtime in settings.
638
638
 
639
+ Press `Ctrl+Shift+B` to enter Navigator shortcuts, then choose an action:
640
+
641
+ | Key | Action |
642
+ | --- | --- |
643
+ | `h` | Toggle sidebar visibility |
644
+ | `t` / `a` / `s` / `q` / `l` / `m` | Focus Todo / Subagents / Skills / Quick Actions / LSP / MCP |
645
+ | `c` | Check for a Navigator update and report whether one is available |
646
+ | `u` | Check for a Navigator update, then open its confirmation if available |
647
+ | `Escape` | Cancel shortcut mode |
648
+
649
+ The mode ends after an action or five seconds of inactivity. Its toast lists only
650
+ actions supported by the current host; LSP is omitted on OpenCode 2.x. Change the
651
+ entry shortcut under **Settings → Behavior**. The command palette also offers
652
+ **Check Navigator updates** and **Update Navigator**.
653
+
654
+ Manual checks report when Navigator is current or the update source is unavailable.
655
+ Updates require confirmation, preserve the existing installation method and scope,
656
+ and show a restart reminder after completion. Background check failures stay quiet.
657
+
639
658
  ### Search Everything
640
659
 
641
660
  Press `Ctrl+Shift+K` or run **Search Everything** from the command palette. Use
@@ -0,0 +1,237 @@
1
+ import {PLUGIN_ID
2
+ } from "./chunk-cmmnfes2.js";
3
+ import {createSignal} from "solid-js"
4
+ function versionParts(value){const match=/^v?(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?/.exec(value)
5
+ return match?{numbers:match.slice(1,4).map(Number),prerelease:match[4]||undefined}:undefined
6
+ }
7
+ function isNewerVersion(latest,current){const next=versionParts(latest)
8
+ const installed=versionParts(current)
9
+ if(!next||!installed)
10
+ return false
11
+ for(let index=0;index<3; index++){if(next.numbers[index]!==installed.numbers[index])
12
+ return next.numbers[index]>installed.numbers[index]
13
+ }
14
+ return installed.prerelease!==undefined&&next.prerelease===undefined
15
+ }
16
+ var NAVIGATOR_PACKAGE_URL="https://registry.npmjs.org/opencode-navigator/latest"
17
+ var REQUEST_TIMEOUT_MS=5000
18
+ async function readNavigatorVersion(request,signal){const response=await request(NAVIGATOR_PACKAGE_URL,{headers:{accept:"application/json"},signal})
19
+ if(!response.ok)
20
+ throw new Error("Update source unavailable")
21
+ const metadata=await response.json()
22
+ if(typeof metadata.version!=="string"||!/^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(metadata.version))
23
+ throw new Error("Invalid update version")
24
+ return metadata.version
25
+ }
26
+ function createVersionStatus(api,navigatorVersion,request=fetch,timeoutMs=REQUEST_TIMEOUT_MS){const [openCodeUpdate,setOpenCodeUpdate]=createSignal()
27
+ const [navigatorUpdate,setNavigatorUpdate]=createSignal()
28
+ let controller
29
+ let pending
30
+ let disposed=false
31
+ const unsubscribe=api.event.on("installation.update-available",(event)=>{const version=event.properties.version
32
+ setOpenCodeUpdate(isNewerVersion(version,api.app.version)?version:undefined)
33
+ })
34
+ api.lifecycle.onDispose(()=>{disposed=true
35
+ controller?.abort()
36
+ unsubscribe()
37
+ })
38
+ function refreshNavigator(){if(disposed)
39
+ return Promise.resolve("unavailable")
40
+ if(pending)
41
+ return pending
42
+ const active=new AbortController
43
+ controller=active
44
+ const timeout=setTimeout(()=>active.abort(),timeoutMs)
45
+ let abort
46
+ const cancelled=new Promise((_,reject)=>{abort=()=>reject(new Error("Update check interrupted"))
47
+ active.signal.addEventListener("abort",abort,{once:true})
48
+ })
49
+ const reading=Promise.race([readNavigatorVersion(request,active.signal),cancelled]).then((version)=>{if(disposed||active.signal.aborted)
50
+ return "unavailable"
51
+ const newer=isNewerVersion(version,navigatorVersion)
52
+ setNavigatorUpdate(newer?version:undefined)
53
+ return newer?"update":"current"
54
+ }).catch(()=>"unavailable").finally(()=>{clearTimeout(timeout)
55
+ active.signal.removeEventListener("abort",abort)
56
+ if(controller===active)
57
+ controller=undefined
58
+ if(pending===reading)
59
+ pending=undefined
60
+ })
61
+ pending=reading
62
+ return reading
63
+ }
64
+ refreshNavigator()
65
+ return{openCodeUpdate,navigatorUpdate,refreshNavigator}}
66
+ import {effect as _$effect3} from "@opentui/solid"
67
+ import {insertNode as _$insertNode3} from "@opentui/solid"
68
+ import {createComponent as _$createComponent2} from "@opentui/solid"
69
+ import {insert as _$insert3} from "@opentui/solid"
70
+ import {setProp as _$setProp3} from "@opentui/solid"
71
+ import {createElement as _$createElement3} from "@opentui/solid"
72
+ import {effect as _$effect2} from "@opentui/solid"
73
+ import {insert as _$insert2} from "@opentui/solid"
74
+ import {createTextNode as _$createTextNode2} from "@opentui/solid"
75
+ import {insertNode as _$insertNode2} from "@opentui/solid"
76
+ import {createComponent as _$createComponent} from "@opentui/solid"
77
+ import {setProp as _$setProp2} from "@opentui/solid"
78
+ import {createElement as _$createElement2} from "@opentui/solid"
79
+ import {effect as _$effect} from "@opentui/solid"
80
+ import {memo as _$memo} from "@opentui/solid"
81
+ import {createTextNode as _$createTextNode} from "@opentui/solid"
82
+ import {insertNode as _$insertNode} from "@opentui/solid"
83
+ import {insert as _$insert} from "@opentui/solid"
84
+ import {setProp as _$setProp} from "@opentui/solid"
85
+ import {createElement as _$createElement} from "@opentui/solid"
86
+ import {useIcons} from "./tui.js"
87
+ import {createComponent as _$createComponent3} from "@opentui/solid"
88
+ function createManualUpdateCheck(api,status,current,onUpdate){let checking=false
89
+ async function read(){try{return await status.refreshNavigator?.()??"unavailable"
90
+ } catch{return "unavailable"
91
+ }}
92
+ return{checking:()=>checking,async run(offerUpdate=false){if(!current()||checking)
93
+ return
94
+ checking=true
95
+ api.ui.toast({variant:"info",title:"Navigator update",message:"Checking for updates\u2026"})
96
+ try{const result=await read()
97
+ if(!current())
98
+ return
99
+ if(result==="update"&&offerUpdate){onUpdate()
100
+ return
101
+ }
102
+ api.ui.toast({variant:result==="unavailable"?"warning":"info",title:"Navigator update",message:{update:`Navigator ${status.navigatorUpdate()} is available. Use u in Navigator shortcuts to update.`,current:"Navigator is up to date.",unavailable:"Could not check Navigator updates. Try again."
103
+ }[result]
104
+ })
105
+ } finally{checking=false
106
+ }}}}
107
+ function createVersionUpdateActions(api,status,updater,rememberNavigatorVersion){const busy=new Set
108
+ const installed={}
109
+ let disposed=false
110
+ let confirmation=0
111
+ function target(kind){return kind==="openCode"?status.openCodeUpdate():status.navigatorUpdate()
112
+ }
113
+ function label(kind){return kind==="openCode"?"OpenCode":"Navigator"
114
+ }
115
+ function update(kind,version){if(disposed||busy.has(kind))
116
+ return
117
+ if(version!==target(kind)){api.ui.toast({variant:"warning",title:"Update changed",message:"Check for updates again before confirming."
118
+ })
119
+ return
120
+ }
121
+ api.ui.dialog.clear()
122
+ busy.add(kind)
123
+ api.ui.toast({variant:"info",title:`Updating ${label(kind)}`,message:`Installing ${version}...`
124
+ });
125
+ (async ()=>{if(kind==="navigator")
126
+ await rememberNavigatorVersion?.()
127
+ if(disposed||version!==target(kind))
128
+ throw new Error("Update target changed; check again")
129
+ await updater[kind](version)
130
+ })().then(()=>{installed[kind]=version
131
+ if(disposed)
132
+ return
133
+ api.ui.toast({variant:"success",title:`${label(kind)} updated`,message:"Restart OpenCode to use the new version.",duration:6000
134
+ })
135
+ }).catch((error)=>{if(disposed)
136
+ return
137
+ api.ui.toast({variant:"error",title:`Could not update ${label(kind)}`,message:error instanceof Error?error.message:"Update failed",duration:6000
138
+ })
139
+ }).finally(()=>busy.delete(kind))
140
+ }
141
+ function open(kind,version=target(kind)){if(disposed||!version||version!==target(kind)||busy.has(kind))
142
+ return
143
+ if(installed[kind]===version){api.ui.toast({variant:"info",title:`${label(kind)} updated`,message:"Restart OpenCode to use the installed update."
144
+ })
145
+ return
146
+ }
147
+ const id=++confirmation
148
+ api.ui.dialog.replace(()=>_$createComponent3(api.ui.DialogConfirm,{get title(){return `Update ${label(kind)}`
149
+ },message:`Update to ${version} using the current installation method? OpenCode must be restarted afterward.`,onConfirm:()=>{if(id!==confirmation)
150
+ return
151
+ confirmation++
152
+ update(kind,version)
153
+ },onCancel:()=>{if(id!==confirmation)
154
+ return
155
+ confirmation++
156
+ api.ui.dialog.clear()
157
+ }}))
158
+ }
159
+ const manual=createManualUpdateCheck(api,status,()=>!disposed&&!busy.has("navigator"),()=>open("navigator"))
160
+ const unregister=api.keymap.registerLayer({commands:[{name:`${PLUGIN_ID}.check-update`,title:"Check Navigator updates",category:"Navigator",namespace:"palette",enabled:()=>!manual.checking()&&!busy.has("navigator"),run:()=>void manual.run()
161
+ },{name:`${PLUGIN_ID}.update-opencode`,title:"Update OpenCode",category:"Navigator",namespace:"palette",enabled:()=>Boolean(target("openCode"))&&!busy.has("openCode"),run:()=>open("openCode")
162
+ },{name:`${PLUGIN_ID}.update-navigator`,title:"Update Navigator",category:"Navigator",namespace:"palette",enabled:()=>!manual.checking()&&!busy.has("navigator"),run:()=>void manual.run(true)
163
+ }]
164
+ })
165
+ api.lifecycle.onDispose(()=>{disposed=true
166
+ unregister()
167
+ })
168
+ return{openCode:(version)=>open("openCode",version),navigator:(version)=>open("navigator",version)
169
+ }}
170
+ import {homedir} from "os"
171
+ import {basename,dirname,isAbsolute,join} from "path"
172
+ import {fileURLToPath} from "url"
173
+ var run=async (command)=>{const child=Bun.spawn([...command],{stdout:"ignore",stderr:"pipe"})
174
+ const error=new Response(child.stderr).text()
175
+ const exitCode=await child.exited
176
+ if(exitCode===0)
177
+ return
178
+ const message=await error
179
+ throw new Error(message.trim()||`Command failed with exit code ${child.exitCode}`)
180
+ }
181
+ var readConfig=async (path)=>{const file=Bun.file(path)
182
+ return await file.exists()?Bun.JSONC.parse(await file.text()):undefined
183
+ }
184
+ function pinnedUpdate(wrapper,target,execute){if(basename(wrapper)!=="tui.js"||basename(dirname(wrapper))!=="opencode-navigator")
185
+ throw new Error("Unrecognized local Navigator source")
186
+ const installer=fileURLToPath(new URL("../scripts/update-local-snapshot.mjs",import.meta.url))
187
+ return execute(["node",installer,wrapper,target])
188
+ }
189
+ function configHasPlugin(value,spec){if(!value||typeof value!=="object")
190
+ return false
191
+ const plugins=value.plugin
192
+ return Array.isArray(plugins)&&plugins.some((item)=>{let configured=item
193
+ if(Array.isArray(item))
194
+ configured=item[0]
195
+ else if(item&&typeof item==="object")
196
+ configured=item.package
197
+ return configured===spec
198
+ })
199
+ }
200
+ async function v1Global(api,spec,read){const configRoot=process.env.OPENCODE_CONFIG_DIR??join(process.env.XDG_CONFIG_HOME??join(homedir(),".config"),"opencode")
201
+ const global=join(configRoot,"tui.json")
202
+ const configured=api.state.path.config.endsWith(".json")?api.state.path.config:join(api.state.path.config,"tui.json")
203
+ const local=new Set([configured,join(api.state.path.worktree,".opencode","tui.json")])
204
+ local.delete(global)
205
+ for(const path of local)
206
+ if(configHasPlugin(await read(path),spec))
207
+ return false
208
+ return configHasPlugin(await read(global),spec)
209
+ }
210
+ function createOpenCodeV1VersionUpdater(api,meta,execute=run,read=readConfig){return{openCode:(target)=>execute([process.execPath,"upgrade",target]),async navigator(target){if(meta.source==="file"){const wrapper=meta.spec.startsWith("file://")?fileURLToPath(meta.spec):meta.spec
211
+ return pinnedUpdate(wrapper,target,execute)
212
+ }
213
+ if(meta.source!=="npm")
214
+ throw new Error("Unrecognized local Navigator source")
215
+ const result=await api.plugins.install(`opencode-navigator@${target}`,{global:await v1Global(api,meta.spec,read)
216
+ })
217
+ if(!result.ok)
218
+ throw new Error(result.message)
219
+ }}}
220
+ function createOpenCodeV2VersionUpdater(context,execute=run){return{openCode:(target)=>execute([process.execPath,"upgrade",target]),async navigator(target){const response=await context.client.plugin.list(context.location?{location:context.location}:{})
221
+ const item=response.data.find((plugin)=>plugin.id===PLUGIN_ID)
222
+ if(!item)
223
+ throw new Error("Navigator is not in the active plugin list")
224
+ if(item.source.type==="local")
225
+ return pinnedUpdate(basename(item.source.path)==="tui.js"?item.source.path:join(item.source.path,"tui.js"),target,execute)
226
+ if(item.source.type!=="package")
227
+ throw new Error("Unrecognized local Navigator source")
228
+ if(isAbsolute(item.source.target))
229
+ return pinnedUpdate(join(item.source.target,"tui.js"),target,execute)
230
+ if(item.source.target.startsWith("file://"))
231
+ return pinnedUpdate(join(fileURLToPath(item.source.target),"tui.js"),target,execute)
232
+ if(item.source.target.startsWith("."))
233
+ throw new Error("Unrecognized local Navigator source")
234
+ await context.client.plugin.update({location:response.location,targets:[item.source.target]})
235
+ }}}
236
+ export{createOpenCodeV1VersionUpdater,createOpenCodeV2VersionUpdater,createVersionStatus,createVersionUpdateActions
237
+ }
package/dist/tui.js CHANGED
@@ -1337,7 +1337,7 @@ Object.assign(desiredMcpStates,layer.desiredMcpStates)
1337
1337
  return{behavior,layout:{sections,expanded,order},desiredMcpStates}}
1338
1338
  import {randomUUID} from "crypto"
1339
1339
  import {link,mkdir,readFile,readdir,rename,rm,unlink,writeFile,readFile as readFile2} from "fs/promises"
1340
- import {join,dirname,join as join2,join as join3,basename,dirname as dirname2,isAbsolute,join as join4} from "path"
1340
+ import {join,dirname,join as join2,join as join3} from "path"
1341
1341
  var LOCK_TIMEOUT_MS=3000
1342
1342
  function errno(error,code){return error instanceof Error&&"code" in error&&error.code===code
1343
1343
  }
@@ -2904,7 +2904,7 @@ return{groups,rows:groups.flatMap((group)=>group.items.map((item)=>({item,group:
2904
2904
 
2905
2905
  function KeyboardHelpDialog(props){const icons=useIcons()
2906
2906
  const theme=()=>props.api.theme.current
2907
- const hints=()=>[`${icons.key("up")}/k and ${icons.key("down")}/j move \xB7 ${icons.key("enter")} activates`,`${icons.key("enter")} on a filter starts typing \xB7 ${icons.key("esc")} returns`,`Todo: ${icons.key("enter")} on All / Active / Finished changes the view`,`MCP: ${icons.key("enter")} toggles a group`,"Show all / Show less expands or limits the filtered list",`Skills: ${icons.icon("info")} opens source \xB7 ${icons.icon("recent")} marks recent skills`,"Navigator Settings: Ctrl+,",`Search Everything: shortcut in Settings ${icons.icon("right")} Behavior`,`Search: type \xB7 ${icons.key("up/down")} results \xB7 ${icons.key("tab/shift+tab")} tabs \xB7 ${icons.key("enter")} run`,`${icons.key("esc")} leaves the sidebar \xB7 ${icons.key("?")} opens this help`]
2907
+ const hints=()=>[`${icons.key("up")}/k and ${icons.key("down")}/j move \xB7 ${icons.key("enter")} activates`,`${icons.key("enter")} on a filter starts typing \xB7 ${icons.key("esc")} returns`,`Todo: ${icons.key("enter")} on All / Active / Finished changes the view`,`MCP: ${icons.key("enter")} toggles a group`,"Show all / Show less expands or limits the filtered list",`Skills: ${icons.icon("info")} opens source \xB7 ${icons.icon("recent")} marks recent skills`,"Navigator Settings: Ctrl+,","Navigator shortcuts: c checks for updates \xB7 u updates Navigator after confirmation",`Search Everything: shortcut in Settings ${icons.icon("right")} Behavior`,`Search: type \xB7 ${icons.key("up/down")} results \xB7 ${icons.key("tab/shift+tab")} tabs \xB7 ${icons.key("enter")} run`,`${icons.key("esc")} leaves the sidebar \xB7 ${icons.key("?")} opens this help`]
2908
2908
  return _$createComponent(DialogSurface,{get api(){return props.api
2909
2909
  },get children(){return [(()=>{var _el$=_$createElement("text"),_el$2=_$createTextNode(` Navigator keyboard help`)
2910
2910
  _$insertNode(_el$,_el$2)
@@ -4901,27 +4901,6 @@ clearTimeout(pending)
4901
4901
  return _$createElement("box")
4902
4902
  }
4903
4903
 
4904
- var NAVIGATOR_PACKAGE_URL="https://registry.npmjs.org/opencode-navigator/latest"
4905
- var REQUEST_TIMEOUT_MS=5000
4906
- function createVersionStatus(api,navigatorVersion,request=fetch){const [openCodeUpdate,setOpenCodeUpdate]=createSignal17()
4907
- const [navigatorUpdate,setNavigatorUpdate]=createSignal17()
4908
- const controller=new AbortController
4909
- const timeout=setTimeout(()=>controller.abort(),REQUEST_TIMEOUT_MS)
4910
- const unsubscribe=api.event.on("installation.update-available",(event)=>{const version=event.properties.version
4911
- setOpenCodeUpdate(isNewerVersion(version,api.app.version)?version:undefined)
4912
- })
4913
- api.lifecycle.onDispose(()=>{clearTimeout(timeout)
4914
- controller.abort()
4915
- unsubscribe()
4916
- })
4917
- request(NAVIGATOR_PACKAGE_URL,{headers:{accept:"application/json"},signal:controller.signal
4918
- }).then(async (response)=>{if(!response.ok)
4919
- return
4920
- const metadata=await response.json()
4921
- if(typeof metadata.version==="string")
4922
- setNavigatorUpdate(isNewerVersion(metadata.version,navigatorVersion)?metadata.version:undefined)
4923
- }).catch(()=>{}).finally(()=>clearTimeout(timeout))
4924
- return{openCodeUpdate,navigatorUpdate}}
4925
4904
 
4926
4905
 
4927
4906
 
@@ -5011,39 +4990,6 @@ return _el$
5011
4990
  })()
5012
4991
  }
5013
4992
 
5014
- function createVersionUpdateActions(api,status,updater,rememberNavigatorVersion){const busy=new Set
5015
- function target(kind){return kind==="openCode"?status.openCodeUpdate():status.navigatorUpdate()
5016
- }
5017
- function label(kind){return kind==="openCode"?"OpenCode":"Navigator"
5018
- }
5019
- function update(kind,version){if(busy.has(kind))
5020
- return
5021
- api.ui.dialog.clear()
5022
- busy.add(kind)
5023
- api.ui.toast({variant:"info",title:`Updating ${label(kind)}`,message:`Installing ${version}...`
5024
- });
5025
- (async ()=>{if(kind==="navigator")
5026
- await rememberNavigatorVersion?.()
5027
- await updater[kind](version)
5028
- })().then(()=>{api.ui.toast({variant:"success",title:`${label(kind)} updated`,message:"Restart OpenCode to use the new version.",duration:6000
5029
- })
5030
- }).catch((error)=>{api.ui.toast({variant:"error",title:`Could not update ${label(kind)}`,message:error instanceof Error?error.message:"Update failed",duration:6000
5031
- })
5032
- }).finally(()=>busy.delete(kind))
5033
- }
5034
- function open(kind,version=target(kind)){if(!version||busy.has(kind))
5035
- return
5036
- api.ui.dialog.replace(()=>_$createComponent(api.ui.DialogConfirm,{get title(){return `Update ${label(kind)}`
5037
- },message:`Update to ${version} using the current installation method? OpenCode must be restarted afterward.`,onConfirm:()=>update(kind,version),onCancel:()=>api.ui.dialog.clear()
5038
- }))
5039
- }
5040
- const unregister=api.keymap.registerLayer({commands:[{name:`${PLUGIN_ID}.update-opencode`,title:"Update OpenCode",category:"Navigator",namespace:"palette",enabled:()=>Boolean(target("openCode"))&&!busy.has("openCode"),run:()=>open("openCode")
5041
- },{name:`${PLUGIN_ID}.update-navigator`,title:"Update Navigator",category:"Navigator",namespace:"palette",enabled:()=>Boolean(target("navigator"))&&!busy.has("navigator"),run:()=>open("navigator")
5042
- }]
5043
- })
5044
- api.lifecycle.onDispose(unregister)
5045
- return{openCode:(version)=>open("openCode",version),navigator:(version)=>open("navigator",version)
5046
- }}
5047
4993
  function matchesFilter(query,...values){const needle=query.trim().toLocaleLowerCase()
5048
4994
  return !needle||values.some((value)=>value?.toLocaleLowerCase().includes(needle))
5049
4995
  }
@@ -6363,8 +6309,9 @@ var ENTRY_COMMAND=`${PLUGIN_ID}.sidebar-shortcuts.enter`
6363
6309
  var CANCEL_COMMAND=`${PLUGIN_ID}.sidebar-shortcuts.cancel`
6364
6310
  var MODE=`${PLUGIN_ID}.sidebar-shortcuts`
6365
6311
  var DEFAULT_TIMEOUT_MS=5000
6366
- var ACTIONS=[{key:"h",command:"session.sidebar.toggle",section:undefined},{key:"t",command:`${PLUGIN_ID}.focus.todo`,section:"todo"},{key:"a",command:`${PLUGIN_ID}.focus.subagents`,section:"subagents"},{key:"s",command:`${PLUGIN_ID}.focus.skills`,section:"skills"},{key:"q",command:`${PLUGIN_ID}.focus.quick_actions`,section:"quick_actions"},{key:"l",command:`${PLUGIN_ID}.focus.lsp`,section:"lsp"},{key:"m",command:`${PLUGIN_ID}.focus.mcp`,section:"mcp"}]
6312
+ var ACTIONS=[{key:"h",hint:"toggle",command:"session.sidebar.toggle",section:undefined},{key:"t",hint:"todo",command:`${PLUGIN_ID}.focus.todo`,section:"todo"},{key:"a",hint:"agents",command:`${PLUGIN_ID}.focus.subagents`,section:"subagents"},{key:"s",hint:"skills",command:`${PLUGIN_ID}.focus.skills`,section:"skills"},{key:"q",hint:"actions",command:`${PLUGIN_ID}.focus.quick_actions`,section:"quick_actions"},{key:"l",hint:"LSP",command:`${PLUGIN_ID}.focus.lsp`,section:"lsp"},{key:"m",hint:"MCP",command:`${PLUGIN_ID}.focus.mcp`,section:"mcp"},{key:"c",hint:"check update",command:`${PLUGIN_ID}.check-update`,section:undefined},{key:"u",hint:"update",command:`${PLUGIN_ID}.update-navigator`,section:undefined}]
6367
6313
  function createSidebarShortcutMode(api,options={}){const actions=ACTIONS.filter(({section})=>!section||supportsSidebarSection(api,section))
6314
+ const hints=actions.map(({key,hint})=>`${key} ${hint}`).join(" | ")
6368
6315
  const timeoutMs=options.timeoutMs??DEFAULT_TIMEOUT_MS
6369
6316
  let shortcutKey=""
6370
6317
  let unregisterBinding
@@ -6391,7 +6338,7 @@ unregisterMode=api.keymap.registerLayer({mode:MODE,priority:2000,commands:[{name
6391
6338
  })
6392
6339
  leaveMode=api.mode.push(MODE)
6393
6340
  timeout=setTimeout(cancel,timeoutMs)
6394
- api.ui?.toast?.({variant:"info",title:"Navigator shortcuts",message:"h toggle | t todo | a agents | s skills | q actions | l LSP | m MCP | Esc cancel",duration:timeoutMs
6341
+ api.ui?.toast?.({variant:"info",title:"Navigator shortcuts",message:`${hints} | Esc cancel`,duration:timeoutMs
6395
6342
  })
6396
6343
  return true
6397
6344
  }
@@ -7221,7 +7168,7 @@ props.controller.refresh()
7221
7168
  onCleanup25(()=>clearInterval(timer))
7222
7169
  return _$createElement("box")
7223
7170
  }
7224
- import {homedir,homedir as homedir2} from "os"
7171
+ import {homedir} from "os"
7225
7172
 
7226
7173
  function location(input){return input.directory?{location:{directory:input.directory}}:{}}
7227
7174
  function mcpItem(server){return{name:server.name,status:server.status.status,...server.status.error&&{error:server.status.error}}}
@@ -7511,72 +7458,9 @@ for(let index=disposers.length-1;index>=0; index--)
7511
7458
  await disposers[index]()
7512
7459
  disposers.length=0
7513
7460
  }}}
7514
-
7515
-
7516
- import {fileURLToPath} from "url"
7517
- var run=async (command)=>{const child=Bun.spawn([...command],{stdout:"ignore",stderr:"pipe"})
7518
- const error=new Response(child.stderr).text()
7519
- const exitCode=await child.exited
7520
- if(exitCode===0)
7521
- return
7522
- const message=await error
7523
- throw new Error(message.trim()||`Command failed with exit code ${child.exitCode}`)
7524
- }
7525
- var readConfig=async (path)=>{const file=Bun.file(path)
7526
- return await file.exists()?Bun.JSONC.parse(await file.text()):undefined
7461
+ function loadNavigatorUpdates(){const path=import.meta.url.includes("/dist/tui.js")?"./navigator-updates.js":"../navigator-updates"
7462
+ return import(path)
7527
7463
  }
7528
- function pinnedUpdate(wrapper,target,execute){if(basename(wrapper)!=="tui.js"||basename(dirname2(wrapper))!=="opencode-navigator")
7529
- throw new Error("Unrecognized local Navigator source")
7530
- const installer=fileURLToPath(new URL("../scripts/update-local-snapshot.mjs",import.meta.url))
7531
- return execute(["node",installer,wrapper,target])
7532
- }
7533
- function configHasPlugin(value,spec){if(!value||typeof value!=="object")
7534
- return false
7535
- const plugins=value.plugin
7536
- return Array.isArray(plugins)&&plugins.some((item)=>{let configured=item
7537
- if(Array.isArray(item))
7538
- configured=item[0]
7539
- else if(item&&typeof item==="object")
7540
- configured=item.package
7541
- return configured===spec
7542
- })
7543
- }
7544
- async function v1Global(api,spec,read){const configRoot=process.env.OPENCODE_CONFIG_DIR??join4(process.env.XDG_CONFIG_HOME??join4(homedir2(),".config"),"opencode")
7545
- const global=join4(configRoot,"tui.json")
7546
- const configured=api.state.path.config.endsWith(".json")?api.state.path.config:join4(api.state.path.config,"tui.json")
7547
- const local=new Set([configured,join4(api.state.path.worktree,".opencode","tui.json")])
7548
- local.delete(global)
7549
- for(const path of local)
7550
- if(configHasPlugin(await read(path),spec))
7551
- return false
7552
- return configHasPlugin(await read(global),spec)
7553
- }
7554
- function createOpenCodeV1VersionUpdater(api,meta,execute=run,read=readConfig){return{openCode:(target)=>execute([process.execPath,"upgrade",target]),async navigator(target){if(meta.source==="file"){const wrapper=meta.spec.startsWith("file://")?fileURLToPath(meta.spec):meta.spec
7555
- return pinnedUpdate(wrapper,target,execute)
7556
- }
7557
- if(meta.source!=="npm")
7558
- throw new Error("Unrecognized local Navigator source")
7559
- const result=await api.plugins.install(`opencode-navigator@${target}`,{global:await v1Global(api,meta.spec,read)
7560
- })
7561
- if(!result.ok)
7562
- throw new Error(result.message)
7563
- }}}
7564
- function createOpenCodeV2VersionUpdater(context,execute=run){return{openCode:(target)=>execute([process.execPath,"upgrade",target]),async navigator(target){const response=await context.client.plugin.list(context.location?{location:context.location}:{})
7565
- const item=response.data.find((plugin)=>plugin.id===PLUGIN_ID)
7566
- if(!item)
7567
- throw new Error("Navigator is not in the active plugin list")
7568
- if(item.source.type==="local")
7569
- return pinnedUpdate(basename(item.source.path)==="tui.js"?item.source.path:join4(item.source.path,"tui.js"),target,execute)
7570
- if(item.source.type!=="package")
7571
- throw new Error("Unrecognized local Navigator source")
7572
- if(isAbsolute(item.source.target))
7573
- return pinnedUpdate(join4(item.source.target,"tui.js"),target,execute)
7574
- if(item.source.target.startsWith("file://"))
7575
- return pinnedUpdate(join4(fileURLToPath(item.source.target),"tui.js"),target,execute)
7576
- if(item.source.target.startsWith("."))
7577
- throw new Error("Unrecognized local Navigator source")
7578
- await context.client.plugin.update({location:response.location,targets:[item.source.target]})
7579
- }}}
7580
7464
  async function setupNavigator(api,options,readProject,managedTodo,hostContext){const config=pluginConfig(undefined)
7581
7465
  const configured=await loadConfiguredDefaults(api,options,readProject)
7582
7466
  const todo=managedTodo??createTodoController(api)
@@ -7587,7 +7471,7 @@ const limitsPath=import.meta.url.includes("/dist/tui.js")?"./provider-limits.js"
7587
7471
  const limitsModule=await import(limitsPath)
7588
7472
  const LimitsSection=limitsModule.ProviderLimitsSection
7589
7473
  const{controller:limits,codex,accountSource,selectModel,modelSelection
7590
- }=limitsModule.createProviderQuotaIntegration(api,preferences,"0.21.3",hostContext)
7474
+ }=limitsModule.createProviderQuotaIntegration(api,preferences,"0.21.4",hostContext)
7591
7475
  const mcp=createMcpController(api,preferences.persistMcp,preferences)
7592
7476
  const interaction=createSidebarInteraction(api,()=>openKeyboardHelp(api,preferences.lspIconStyle,preferences.cornerFont))
7593
7477
  const startup=createStartupSessionController(api,preferences)
@@ -7615,7 +7499,7 @@ api.slots.register({order:100,slots:{app(){return _$createComponent(IconProvider
7615
7499
  },interaction,skills,subagents,mcp
7616
7500
  }),_$createComponent(FirstRunWizardPersistence,{api,preferences
7617
7501
  }),_$createComponent(StartupSessionBinding,{api,controller:startup
7618
- }),_$createComponent(UpgradeNotesBinding,{api,preferences,version:"0.21.3"
7502
+ }),_$createComponent(UpgradeNotesBinding,{api,preferences,version:"0.21.4"
7619
7503
  }),_$createComponent(McpPersistence,{api,controller:mcp
7620
7504
  })]
7621
7505
  }})
@@ -7646,12 +7530,14 @@ throw result.error
7646
7530
  }
7647
7531
  return result.data?.type==="text"?result.data.content:undefined
7648
7532
  })
7649
- const versionStatus=createVersionStatus(api,"0.21.3")
7650
- const versionUpdates=createVersionUpdateActions(api,versionStatus,createOpenCodeV1VersionUpdater(api,meta),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.21.3"))
7533
+ const{createVersionStatus,createVersionUpdateActions,createOpenCodeV1VersionUpdater
7534
+ }=await loadNavigatorUpdates()
7535
+ const versionStatus=createVersionStatus(api,"0.21.4")
7536
+ const versionUpdates=createVersionUpdateActions(api,versionStatus,createOpenCodeV1VersionUpdater(api,meta),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.21.4"))
7651
7537
  api.slots.register({order:90,slots:{sidebar_footer(_context,props){return _$createComponent(IconProvider,{get style(){return preferences.lspIconStyle
7652
7538
  },get multilineCorners(){return preferences.cornerFont
7653
7539
  },get children(){return _$createComponent(VersionFooter,{api,get sessionID(){return props.session_id
7654
- },navigatorVersion:"0.21.3",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7540
+ },navigatorVersion:"0.21.4",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7655
7541
  },get onNavigatorUpdate(){return versionUpdates.navigator
7656
7542
  }})
7657
7543
  }})
@@ -7665,15 +7551,17 @@ return
7665
7551
  return new TextDecoder().decode(await context.client.file.read({location:context.location,path:".opencode/navigator.json"
7666
7552
  }))
7667
7553
  },todo,context)
7668
- const versionStatus=createVersionStatus(adapter.api,"0.21.3")
7669
- const versionUpdates=createVersionUpdateActions(adapter.api,versionStatus,createOpenCodeV2VersionUpdater(context),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.21.3"))
7554
+ const{createVersionStatus,createVersionUpdateActions,createOpenCodeV2VersionUpdater
7555
+ }=await loadNavigatorUpdates()
7556
+ const versionStatus=createVersionStatus(adapter.api,"0.21.4")
7557
+ const versionUpdates=createVersionUpdateActions(adapter.api,versionStatus,createOpenCodeV2VersionUpdater(context),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.21.4"))
7670
7558
  const disposeFooter=context.ui.slot({append:"sidebar.footer",render:()=>_$createComponent(IconProvider,{get style(){return preferences.lspIconStyle
7671
7559
  },get multilineCorners(){return preferences.cornerFont
7672
7560
  },get children(){var _el$=_$createElement("box")
7673
7561
  _$setProp(_el$,"flexDirection","row")
7674
7562
  _$setProp(_el$,"justifyContent","space-between")
7675
7563
  _$insert(_el$,_$createComponent(VersionSummary,{get api(){return adapter.api
7676
- },navigatorVersion:"0.21.3",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7564
+ },navigatorVersion:"0.21.4",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7677
7565
  },get onNavigatorUpdate(){return versionUpdates.navigator
7678
7566
  }}),null)
7679
7567
  _$insert(_el$,_$createComponent(SettingsFooterButton,{get api(){return adapter.api
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "opencode-navigator",
4
- "version": "0.21.3",
4
+ "version": "0.21.4",
5
5
  "description": "Search, navigate, and manage OpenCode sessions from the TUI",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.4.2",