opencode-navigator 0.21.2 → 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,15 @@
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
+
10
+ ## [0.21.3] - 2026-10-06
11
+
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.
13
+
5
14
  ## [0.21.2] - 2026-10-06
6
15
 
7
16
  - Make Limits compact with clickable provider/model and variant controls, remaining-headroom progress rows, and muted reset times. Move Refresh and Banked Resets into the section header, remove the section counter and Provider sources action, and keep reset consumption behind a separate confirmation.
@@ -86,6 +95,8 @@
86
95
 
87
96
  Earlier release notes are available on [GitHub Releases](https://github.com/St1ggy/opencode-navigator/releases).
88
97
 
98
+ [0.21.4]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.3...v0.21.4
99
+ [0.21.3]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.2...v0.21.3
89
100
  [0.21.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.1...v0.21.2
90
101
  [0.21.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.0...v0.21.1
91
102
  [0.21.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.20.0...v0.21.0
package/PROVIDERS.md CHANGED
@@ -14,7 +14,7 @@ permissions, region, transport, and supported host APIs.
14
14
 
15
15
  | Provider | Native source | Scope and prerequisites | Reference |
16
16
  | --- | --- | --- | --- |
17
- | OpenAI / Codex ChatGPT | Codex app-server account/rate-limit RPC and manual reset credits | Verified account identity; ordinary `codex` quota explicitly labelled account-scoped even with null model metadata; separate model buckets need explicit association | [Codex protocol](https://github.com/openai/codex/tree/main/codex-rs/app-server-protocol) |
17
+ | OpenAI / Codex ChatGPT | Codex app-server account/rate-limit RPC and manual reset credits | Verified account identity; ordinary `codex` quota is account-scoped even with null model metadata; separate model buckets need explicit association; native durations describe quota windows, not time until reset | [Codex protocol](https://github.com/openai/codex/tree/main/codex-rs/app-server-protocol) |
18
18
  | OpenAI API | HTTP request/token/project-token rate-limit headers | Actual response for the selected model/account; HTTP transport | [Rate limits](https://developers.openai.com/api/docs/guides/rate-limits) |
19
19
  | Anthropic API | Native response headers and organization rate-limits API | Per-model headers; configured limits need an eligible org/admin credential and explicit model association | [Rate limits API](https://platform.claude.com/docs/en/manage-claude/rate-limits-api) |
20
20
  | Azure OpenAI | Native remaining request/token headers | Actual deployment response; does not expose a ChatGPT subscription | [Quotas and limits](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/quotas-limits) |
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
 
@@ -365,7 +365,7 @@ Codex 0.160.0 protocol also exposes the separate
365
365
  `account/rateLimitResetCredit/consume` RPC. Navigator requires explicit
366
366
  confirmation before using it and never redeems credits in the background.
367
367
  Limits follows the model and verified connection selected in the current OpenCode
368
- session. Codex's ordinary `codex` windows appear as **Codex account** quota, shared
368
+ session. Codex's ordinary `codex` windows describe verified account quota shared
369
369
  across models. The protocol can return `normalModelSlug: null` for this ordinary
370
370
  bucket; that field is display metadata for quota aliases, not a prerequisite for
371
371
  account usage. Separate model-specific buckets require explicit model association.
@@ -428,9 +428,11 @@ bindings are reused; conflicting bindings remain isolated until a deliberate rel
428
428
 
429
429
  Limits starts with **[Provider name] Model name · variant**. Activate the model or
430
430
  variant with the mouse or keyboard to open OpenCode's native selector. Each quota
431
- window uses two compact rows: remaining headroom with a muted progress scale,
432
- then a muted reset time and short scope/window label. Healthy views omit account
433
- IDs, source explanations, and update timestamps. Balances retain their native
431
+ window uses two compact rows: remaining headroom with an adaptive muted thin-line
432
+ scale spanning the available sidebar content width, then a muted reset time.
433
+ Both rows stay within the sidebar's padding when it resizes. Healthy views omit
434
+ account IDs, technical scope/window-duration suffixes, source explanations, and update timestamps. Account scope and native window durations remain in the data model;
435
+ durations describe provider quota windows, not time remaining until reset. Balances retain their native
434
436
  currency or point unit; a missing total does not become a full progress scale.
435
437
 
436
438
  The header has no section counter. Its compact **Refresh** control requests a
@@ -634,6 +636,25 @@ The command palette also exposes `Focus sidebar` and direct commands for Todo,
634
636
  Subagents, Skills, Quick Actions, LSP, and MCP. Both sidebar shortcuts can be
635
637
  changed at runtime in settings.
636
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
+
637
658
  ### Search Everything
638
659
 
639
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
+ }
@@ -364,15 +364,8 @@ import {insertNode as _$insertNode} from "@opentui/solid"
364
364
  import {insert as _$insert} from "@opentui/solid"
365
365
  import {setProp as _$setProp} from "@opentui/solid"
366
366
  import {createElement as _$createElement} from "@opentui/solid"
367
+ import {createSignal as createSignal4} from "solid-js"
367
368
  import {useIcons} from "./tui.js"
368
- function duration(minutes){if(minutes%10080===0)
369
- return `${minutes / 10080}w`
370
- if(minutes%1440===0)
371
- return `${minutes / 1440}d`
372
- if(minutes%60===0)
373
- return `${minutes / 60}h`
374
- return `${minutes}m`
375
- }
376
369
  function measurement(window,remaining){if(remaining!==undefined)
377
370
  return `${remaining}${window.unit === "%" ? "" : " "}${window.unit} left`
378
371
  if(window.used!==undefined)
@@ -390,28 +383,38 @@ return `Resets ${date.toLocaleDateString(undefined, { month: "short", day: "nume
390
383
  function quotaPresentation(window,now=Date.now()){const total=window.unit==="%"?100:window.total
391
384
  const remaining=window.remaining??(total!==undefined&&window.used!==undefined?total-window.used:undefined)
392
385
  const ratio=remaining!==undefined&&total!==undefined&&total>0?Math.max(0,Math.min(1,remaining / total)):undefined
393
- const account=window.scope==="account"?"Account":undefined
394
- const scope=window.kind==="rate_limit"?"API":account
395
- const period=window.durationMinutes?duration(window.durationMinutes):undefined
396
- const context=[scope,window.kind?window.label:period??window.label,window.kind?period:undefined].filter(Boolean).join(" \xB7 ")
397
- return{value:measurement(window,remaining),ratio,reset:`${resetTime(window.resetsAt, now)} \xB7 ${context}`}}
386
+ return{value:measurement(window,remaining),ratio,reset:resetTime(window.resetsAt,now)}}
398
387
  function QuotaWindowRow(props){const icons=useIcons()
388
+ const [columns,setColumns]=createSignal4(0)
399
389
  const view=()=>quotaPresentation(props.window)
400
390
  const scale=()=>{const ratio=view().ratio
401
391
  if(ratio===undefined)
402
392
  return " \xB7 scale unknown"
403
- const filled=Math.round(ratio*10)
404
- const symbols=icons.style()==="text"?["#","-"]:["\u2588","\u2591"]
405
- return ` [${symbols[0].repeat(filled)}${symbols[1].repeat(10 - filled)}]`
393
+ const width=Math.max(0,columns()-Bun.stringWidth(view().value)-1)
394
+ const filled=Math.round(ratio*width)
395
+ const symbols=icons.style()==="text"?["=","-"]:["\u2501","\u2500"]
396
+ return width?` ${symbols[0].repeat(filled)}${symbols[1].repeat(width - filled)}`:""
406
397
  }
407
398
  return(()=>{var _el$=_$createElement("box"),_el$2=_$createElement("text"),_el$3=_$createElement("span"),_el$4=_$createElement("text")
408
399
  _$insertNode(_el$,_el$2)
409
400
  _$insertNode(_el$,_el$4)
401
+ _$setProp(_el$,"width","100%")
402
+ _$setProp(_el$,"minWidth",0)
403
+ _$setProp(_el$,"flexShrink",1)
404
+ _$setProp(_el$,"overflow","hidden")
405
+ _$setProp(_el$,"onSizeChange",function(){setColumns(this.width)
406
+ })
410
407
  _$insertNode(_el$2,_el$3)
408
+ _$setProp(_el$2,"width","100%")
409
+ _$setProp(_el$2,"minWidth",0)
410
+ _$setProp(_el$2,"height",1)
411
411
  _$setProp(_el$2,"wrapMode","none")
412
412
  _$setProp(_el$2,"truncate",true)
413
413
  _$insert(_el$3,()=>view().value)
414
414
  _$insert(_el$2,scale,null)
415
+ _$setProp(_el$4,"width","100%")
416
+ _$setProp(_el$4,"minWidth",0)
417
+ _$setProp(_el$4,"height",1)
415
418
  _$setProp(_el$4,"wrapMode","none")
416
419
  _$setProp(_el$4,"truncate",true)
417
420
  _$insert(_el$4,()=>view().reset)
@@ -613,7 +616,7 @@ import {insert as _$insert3} from "@opentui/solid"
613
616
  import {setProp as _$setProp3} from "@opentui/solid"
614
617
  import {createElement as _$createElement3} from "@opentui/solid"
615
618
  import {SelectionBox} from "./tui.js"
616
- import {createSignal as createSignal4,onCleanup} from "solid-js"
619
+ import {createSignal as createSignal5,onCleanup} from "solid-js"
617
620
  function sidebarInteractiveColors(theme,state,normalColor=theme.text,mode="row"){const highlighted=!state.disabled&&(state.focused||mode==="control"&&state.hovered)
618
621
  let backgroundColor=theme.backgroundPanel
619
622
  let foregroundColor=normalColor
@@ -629,7 +632,7 @@ else if(highlighted)
629
632
  foregroundColor=theme.selectedListItemText
630
633
  return{backgroundColor,foregroundColor}}
631
634
  function useSidebarItem(api,interaction,descriptor,normalColor=()=>api.theme.current.text,mode="row"){let unregister
632
- const [hovered,setHovered]=createSignal4(false)
635
+ const [hovered,setHovered]=createSignal5(false)
633
636
  const disabled=()=>descriptor.disabled?.()===true
634
637
  const focused=()=>Boolean(interaction?.ownsFocus()&&interaction.isSelected(descriptor.id))
635
638
  const colors=()=>sidebarInteractiveColors(api.theme.current,{focused:focused(),hovered:hovered(),disabled:disabled()},normalColor(),mode)
@@ -696,7 +699,7 @@ import {insert as _$insert4} from "@opentui/solid"
696
699
  import {setProp as _$setProp5} from "@opentui/solid"
697
700
  import {createElement as _$createElement5} from "@opentui/solid"
698
701
  import {memo as _$memo3} from "@opentui/solid"
699
- import {createEffect as createEffect2,createSignal as createSignal5,on} from "solid-js"
702
+ import {createEffect as createEffect2,createSignal as createSignal6,on} from "solid-js"
700
703
  import {SelectionBox as SelectionBox2} from "./tui.js"
701
704
  var graphemes=new Intl.Segmenter(undefined,{granularity:"grapheme"
702
705
  })
@@ -713,7 +716,7 @@ result += segment
713
716
  }
714
717
  return result + ellipsis
715
718
  }
716
- function SectionHeaderControl(props){const [width,setWidth]=createSignal5()
719
+ function SectionHeaderControl(props){const [width,setWidth]=createSignal6()
717
720
  const control=useSidebarItem(props.api,props.interaction,{id:props.action.id,position:()=>props.position,disabled:props.action.disabled,activate:props.action.onActivate
718
721
  },()=>props.api.theme.current.accent,"control")
719
722
  createEffect2(on(()=>[props.action.label(),props.resetWidth],()=>setWidth(undefined)))
@@ -842,10 +845,10 @@ import {memo as _$memo6} from "@opentui/solid"
842
845
  import {insert as _$insert7} from "@opentui/solid"
843
846
  import {setProp as _$setProp8} from "@opentui/solid"
844
847
  import {createElement as _$createElement8} from "@opentui/solid"
845
- import {Show as Show5,createSignal as createSignal6} from "solid-js"
848
+ import {Show as Show5,createSignal as createSignal7} from "solid-js"
846
849
  import {SelectionBox as SelectionBox3,useIcons as useIcons4} from "./tui.js"
847
850
  function Section(props){const icons=useIcons4()
848
- const [headerWidth,setHeaderWidth]=createSignal6(0)
851
+ const [headerWidth,setHeaderWidth]=createSignal7(0)
849
852
  const icon=()=>props.section?icons.section(props.section):props.icon
850
853
  const theme=()=>props.api.theme.current
851
854
  const item=useSidebarItem(props.api,props.interaction,{id:props.sectionId??`section.${props.title}`,position:()=>({section:props.navigationSection??0,row:0,column:0
@@ -854,7 +857,7 @@ const item=useSidebarItem(props.api,props.interaction,{id:props.sectionId??`sect
854
857
  return(()=>{var _el$=_$createElement8("box")
855
858
  _$setProp8(_el$,"gap",1)
856
859
  _$insert7(_el$,_$createComponent11(SelectionBox3,{ref:(node)=>item.ref(node),get id(){return props.sectionId
857
- },flexDirection:"row",height:1,justifyContent:"space-between",gap:1,paddingLeft:1,paddingRight:1,get backgroundColor(){return item.backgroundColor()
860
+ },flexDirection:"row",height:1,justifyContent:"space-between",gap:1,get backgroundColor(){return item.backgroundColor()
858
861
  },get onMouseOver(){return item.onMouseOver
859
862
  },get onMouseOut(){return item.onMouseOut
860
863
  },onMouseDown:(event)=>item.activate(event),onSizeChange:function(){setHeaderWidth(this.width)
@@ -949,6 +952,10 @@ _$effect9((_$p)=>_$setProp9(_el$,"fg",props.api.theme.current.textMuted,_$p))
949
952
  return _el$
950
953
  })()
951
954
  },children:(selected)=>(()=>{var _el$3=_$createElement9("box")
955
+ _$setProp9(_el$3,"width","100%")
956
+ _$setProp9(_el$3,"minWidth",0)
957
+ _$setProp9(_el$3,"flexShrink",1)
958
+ _$setProp9(_el$3,"overflow","hidden")
952
959
  _$insert8(_el$3,_$createComponent12(LimitsModelRow,_$mergeProps(props,{get model(){return selected()
953
960
  },get selection(){return props.modelSelection
954
961
  }})),null)
@@ -975,7 +982,7 @@ const model=api.state.session.get(sessionID)?.model
975
982
  if(!model)
976
983
  return
977
984
  return{sessionID,providerID:model.providerID,modelID:model.id,...model.variant&&{variant:model.variant}}}
978
- import {createSignal as createSignal7,untrack as untrack2} from "solid-js"
985
+ import {createSignal as createSignal8,untrack as untrack2} from "solid-js"
979
986
  var CHATGPT_METHODS=new Set(["chatgpt-browser","chatgpt-headless"])
980
987
  function providerAccountIdentity(provider,credentials){if(!provider.integrationID||provider.activation==="disabled")
981
988
  return{status:"unsupported"}
@@ -997,7 +1004,7 @@ const header=Object.entries(provider.headers??{}).find(([name])=>name.toLowerCas
997
1004
  if(accountID&&header&&header!==accountID)
998
1005
  return{status:"loading"}
999
1006
  return{status:"ready",id,method:"oauth",providerID,...accountID&&{accountID}}}
1000
- function createOpenCodeV2AccountSource(context,lifecycle){const [connections,setConnections]=createSignal7({})
1007
+ function createOpenCodeV2AccountSource(context,lifecycle){const [connections,setConnections]=createSignal8({})
1001
1008
  let generation=0
1002
1009
  let disposed=false
1003
1010
  let lastModel
package/dist/tui.js CHANGED
@@ -3,7 +3,7 @@ import {insert as _$insert,setProp as _$setProp,createElement as _$createElement
3
3
 
4
4
 
5
5
 
6
- import {createSignal as createSignal2,createSignal,createEffect,onCleanup,createContext,useContext,createContext as createContext2,createSignal as createSignal3,useContext as useContext2,createEffect as createEffect2,splitProps,untrack,splitProps as splitProps2,For,createEffect as createEffect3,onCleanup as onCleanup2,createEffect as createEffect4,Show,splitProps as splitProps3,createSignal as createSignal4,createSignal as createSignal5,createSignal as createSignal6,onCleanup as onCleanup3,batch,createSignal as createSignal8,createSignal as createSignal7,createEffect as createEffect5,createSignal as createSignal9,onCleanup as onCleanup4,createSignal as createSignal10,createSignal as createSignal11,Index,createSignal as createSignal12,onCleanup as onCleanup5,createEffect as createEffect7,onCleanup as onCleanup7,batch as batch2,createEffect as createEffect6,createMemo,createSignal as createSignal13,onCleanup as onCleanup6,onMount,For as For2,For as For3,Show as Show2,createMemo as createMemo2,Show as Show3,createSignal as createSignal14,onCleanup as onCleanup8,For as For4,For as For5,For as For8,createMemo as createMemo4,onMount as onMount3,For as For7,createMemo as createMemo3,onMount as onMount2,Show as Show5,createEffect as createEffect8,createSignal as createSignal15,onCleanup as onCleanup9,Show as Show4,For as For6,For as For9,createEffect as createEffect9,createMemo as createMemo5,onCleanup as onCleanup10,createEffect as createEffect10,createMemo as createMemo6,untrack as untrack2,For as For10,createEffect as createEffect11,createMemo as createMemo8,onCleanup as onCleanup11,onMount as onMount4,createMemo as createMemo7,Show as Show6,onCleanup as onCleanup12,createEffect as createEffect12,onCleanup as onCleanup13,createSignal as createSignal16,createMemo as createMemo9,createEffect as createEffect13,onCleanup as onCleanup14,onMount as onMount5,onCleanup as onCleanup15,onMount as onMount6,For as For11,createSignal as createSignal17,createSignal as createSignal18,Show as Show7,createSignal as createSignal20,createSignal as createSignal19,onCleanup as onCleanup16,For as For12,Show as Show10,createMemo as createMemo11,createComputed,createMemo as createMemo10,createSignal as createSignal21,untrack as untrack3,Show as Show8,createComputed as createComputed2,untrack as untrack4,Show as Show9,createSignal as createSignal23,createEffect as createEffect14,createSignal as createSignal22,on,createMemo as createMemo13,createSignal as createSignal26,createMemo as createMemo12,For as For13,Show as Show14,Show as Show11,Show as Show12,createSignal as createSignal24,createContext as createContext3,useContext as useContext3,Show as Show13,createSignal as createSignal25,onCleanup as onCleanup17,Show as Show19,createSignal as createSignal27,untrack as untrack5,createSignal as createSignal28,createEffect as createEffect15,For as For14,Show as Show15,Show as Show16,Show as Show17,Show as Show18,For as For15,Show as Show21,createMemo as createMemo15,Show as Show20,createMemo as createMemo14,createSignal as createSignal29,createEffect as createEffect16,onCleanup as onCleanup18,For as For20,Match,Switch,createMemo as createMemo19,onCleanup as onCleanup22,For as For16,Show as Show22,createSignal as createSignal30,createEffect as createEffect17,createMemo as createMemo16,onCleanup as onCleanup19,untrack as untrack6,For as For18,Show as Show25,createEffect as createEffect18,createMemo as createMemo17,createSignal as createSignal31,onCleanup as onCleanup20,untrack as untrack7,For as For17,Show as Show24,Show as Show23,Show as Show29,createEffect as createEffect19,createMemo as createMemo18,createSignal as createSignal32,onCleanup as onCleanup21,untrack as untrack8,For as For19,Show as Show27,Show as Show26,Show as Show28,createEffect as createEffect20,createMemo as createMemo20,onCleanup as onCleanup23,untrack as untrack9,Show as Show30,createMemo as createMemo21,onCleanup as onCleanup24,createEffect as createEffect21,onCleanup as onCleanup25,untrack as untrack10,createSignal as createSignal33,onCleanup as onCleanup26,onMount as onMount7} from "solid-js"
6
+ import {createSignal as createSignal2,createSignal,createEffect,onCleanup,createContext,useContext,createContext as createContext2,createSignal as createSignal3,useContext as useContext2,createEffect as createEffect2,splitProps,untrack,splitProps as splitProps2,For,createEffect as createEffect3,onCleanup as onCleanup2,createEffect as createEffect4,Show,splitProps as splitProps3,createSignal as createSignal4,createSignal as createSignal5,createSignal as createSignal6,onCleanup as onCleanup3,batch,createSignal as createSignal8,createSignal as createSignal7,createEffect as createEffect5,createSignal as createSignal9,onCleanup as onCleanup4,createSignal as createSignal10,createSignal as createSignal11,Index,createSignal as createSignal12,onCleanup as onCleanup5,createEffect as createEffect7,onCleanup as onCleanup7,batch as batch2,createEffect as createEffect6,createMemo,createSignal as createSignal13,onCleanup as onCleanup6,onMount,For as For2,For as For3,Show as Show2,createMemo as createMemo2,Show as Show3,createSignal as createSignal14,onCleanup as onCleanup8,For as For4,For as For5,For as For8,createMemo as createMemo4,onMount as onMount3,For as For7,createMemo as createMemo3,onMount as onMount2,Show as Show5,createEffect as createEffect8,createSignal as createSignal15,onCleanup as onCleanup9,Show as Show4,For as For6,For as For9,createEffect as createEffect9,createMemo as createMemo5,onCleanup as onCleanup10,createEffect as createEffect10,createMemo as createMemo6,untrack as untrack2,For as For10,createEffect as createEffect11,createMemo as createMemo8,onCleanup as onCleanup11,onMount as onMount4,createMemo as createMemo7,Show as Show6,onCleanup as onCleanup12,createEffect as createEffect12,onCleanup as onCleanup13,createSignal as createSignal16,createMemo as createMemo9,createEffect as createEffect13,onCleanup as onCleanup14,onMount as onMount5,onCleanup as onCleanup15,onMount as onMount6,For as For11,createSignal as createSignal17,createSignal as createSignal18,Show as Show7,createSignal as createSignal20,createSignal as createSignal19,onCleanup as onCleanup16,For as For12,Show as Show10,createMemo as createMemo11,createComputed,createMemo as createMemo10,createSignal as createSignal21,untrack as untrack3,Show as Show8,createComputed as createComputed2,untrack as untrack4,Show as Show9,createSignal as createSignal23,createEffect as createEffect14,createSignal as createSignal22,on,createMemo as createMemo13,createSignal as createSignal26,createMemo as createMemo12,For as For13,Show as Show14,Show as Show11,Show as Show12,createSignal as createSignal24,createContext as createContext3,useContext as useContext3,Show as Show13,createSignal as createSignal25,onCleanup as onCleanup17,Show as Show19,createSignal as createSignal27,untrack as untrack5,createSignal as createSignal28,createSignal as createSignal29,createEffect as createEffect15,For as For14,Show as Show15,Show as Show16,Show as Show17,Show as Show18,For as For15,Show as Show21,createMemo as createMemo15,Show as Show20,createMemo as createMemo14,createSignal as createSignal30,createEffect as createEffect16,onCleanup as onCleanup18,For as For20,Match,Switch,createMemo as createMemo19,onCleanup as onCleanup22,For as For16,Show as Show22,createSignal as createSignal31,createEffect as createEffect17,createMemo as createMemo16,onCleanup as onCleanup19,untrack as untrack6,For as For18,Show as Show25,createEffect as createEffect18,createMemo as createMemo17,createSignal as createSignal32,onCleanup as onCleanup20,untrack as untrack7,For as For17,Show as Show24,Show as Show23,Show as Show29,createEffect as createEffect19,createMemo as createMemo18,createSignal as createSignal33,onCleanup as onCleanup21,untrack as untrack8,For as For19,Show as Show27,Show as Show26,Show as Show28,createEffect as createEffect20,createMemo as createMemo20,onCleanup as onCleanup23,untrack as untrack9,Show as Show30,createMemo as createMemo21,onCleanup as onCleanup24,createEffect as createEffect21,onCleanup as onCleanup25,untrack as untrack10,createSignal as createSignal34,onCleanup as onCleanup26,onMount as onMount7} from "solid-js"
7
7
  var SIDEBAR_SECTIONS=["limits","todo","subagents","skills","quick_actions","lsp","mcp"]
8
8
  var QUICK_ACTION_IDS=["session.rename","session.timeline","session.copy","session.export","session.compact","session.list","session.new","session.fork","messages.copy","session.first","session.last","session.messages_last_user","session.message.next","session.message.previous","permission.mode"]
9
9
  function preferencesScope(path){return path.worktree||path.directory||"global"
@@ -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
  }
@@ -5520,7 +5466,7 @@ const item=useSidebarItem(props.api,props.interaction,{id:props.sectionId??`sect
5520
5466
  return(()=>{var _el$=_$createElement("box")
5521
5467
  _$setProp(_el$,"gap",1)
5522
5468
  _$insert(_el$,_$createComponent(SelectionBox,{ref:(node)=>item.ref(node),get id(){return props.sectionId
5523
- },flexDirection:"row",height:1,justifyContent:"space-between",gap:1,paddingLeft:1,paddingRight:1,get backgroundColor(){return item.backgroundColor()
5469
+ },flexDirection:"row",height:1,justifyContent:"space-between",gap:1,get backgroundColor(){return item.backgroundColor()
5524
5470
  },get onMouseOver(){return item.onMouseOver
5525
5471
  },get onMouseOut(){return item.onMouseOut
5526
5472
  },onMouseDown:(event)=>item.activate(event),onSizeChange:function(){setHeaderWidth(this.width)
@@ -6209,13 +6155,14 @@ return _$createComponent(SectionWithHeaderAction,{get api(){return props.api
6209
6155
 
6210
6156
 
6211
6157
 
6158
+
6212
6159
 
6213
6160
 
6214
6161
  function QuickActionRow(props){const{api,interaction
6215
6162
  }=useSidebarList()
6216
6163
  const icons=useIcons()
6217
6164
  const theme=()=>api.theme.current
6218
- const [width,setWidth]=createSignal29(0)
6165
+ const [width,setWidth]=createSignal30(0)
6219
6166
  const shortcut=createMemo14(()=>{const bindings=api.keymap.getCommandBindings({visibility:"registered",commands:[props.action.command]
6220
6167
  })
6221
6168
  return api.keys.formatBindings(bindings.get(props.action.command))
@@ -6362,8 +6309,9 @@ var ENTRY_COMMAND=`${PLUGIN_ID}.sidebar-shortcuts.enter`
6362
6309
  var CANCEL_COMMAND=`${PLUGIN_ID}.sidebar-shortcuts.cancel`
6363
6310
  var MODE=`${PLUGIN_ID}.sidebar-shortcuts`
6364
6311
  var DEFAULT_TIMEOUT_MS=5000
6365
- 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}]
6366
6313
  function createSidebarShortcutMode(api,options={}){const actions=ACTIONS.filter(({section})=>!section||supportsSidebarSection(api,section))
6314
+ const hints=actions.map(({key,hint})=>`${key} ${hint}`).join(" | ")
6367
6315
  const timeoutMs=options.timeoutMs??DEFAULT_TIMEOUT_MS
6368
6316
  let shortcutKey=""
6369
6317
  let unregisterBinding
@@ -6390,7 +6338,7 @@ unregisterMode=api.keymap.registerLayer({mode:MODE,priority:2000,commands:[{name
6390
6338
  })
6391
6339
  leaveMode=api.mode.push(MODE)
6392
6340
  timeout=setTimeout(cancel,timeoutMs)
6393
- 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
6394
6342
  })
6395
6343
  return true
6396
6344
  }
@@ -6510,7 +6458,7 @@ return _el$
6510
6458
  }})
6511
6459
  }
6512
6460
  function SkillsSection(props){const dialogs=useDialogs(props.api)
6513
- const [query,setQuery]=createSignal30("")
6461
+ const [query,setQuery]=createSignal31("")
6514
6462
  const model=createSkillSectionModel(props,query)
6515
6463
  const navigationSection=()=>props.navigationSection??3
6516
6464
  function selectSkill(item){if(!props.preferences.shouldConfirmSkill(item)){model.useSkill(item)
@@ -6599,8 +6547,8 @@ return _el$
6599
6547
  function createSubagentSectionModel(props){const list=createMemo17(()=>props.controller.list(props.sessionID))
6600
6548
  const recent=createMemo17(()=>props.controller.recent?.(props.sessionID)??[])
6601
6549
  const rows=createMemo17(()=>buildSubagentView(list(),recent()))
6602
- const [query,setQuery]=createSignal31("")
6603
- const [mode,setMode]=createSignal31("all")
6550
+ const [query,setQuery]=createSignal32("")
6551
+ const [mode,setMode]=createSignal32("all")
6604
6552
  const counts=createMemo17(()=>countSubagentView(rows(),query()))
6605
6553
  const tabs=createMemo17(()=>["all","active","recent",...counts().errors>0?["errors"]:[]])
6606
6554
  const filtered=createMemo17(()=>filterSubagentView(rows(),mode(),query()))
@@ -6974,7 +6922,7 @@ function TodoSection(props){const list=createMemo18(()=>props.controller.list(pr
6974
6922
  const icons=useIcons()
6975
6923
  const unavailable=()=>hostCapabilityUnavailable(props.api,"todo")
6976
6924
  const managed=()=>("guidance" in props.controller)?props.controller:undefined
6977
- const [mode,setMode]=createSignal32("all")
6925
+ const [mode,setMode]=createSignal33("all")
6978
6926
  const targetKey=createMemo18(()=>props.controller.target?.(props.sessionID).key??props.sessionID)
6979
6927
  createEffect19(()=>{targetKey()
6980
6928
  setMode("all")
@@ -7220,7 +7168,7 @@ props.controller.refresh()
7220
7168
  onCleanup25(()=>clearInterval(timer))
7221
7169
  return _$createElement("box")
7222
7170
  }
7223
- import {homedir,homedir as homedir2} from "os"
7171
+ import {homedir} from "os"
7224
7172
 
7225
7173
  function location(input){return input.directory?{location:{directory:input.directory}}:{}}
7226
7174
  function mcpItem(server){return{name:server.name,status:server.status.status,...server.status.error&&{error:server.status.error}}}
@@ -7313,7 +7261,7 @@ return false
7313
7261
  }
7314
7262
  function enabled(value){return typeof value==="function"?value():value!==false
7315
7263
  }
7316
- function createV2Keymap(context){const [revision,setRevision]=createSignal33(0)
7264
+ function createV2Keymap(context){const [revision,setRevision]=createSignal34(0)
7317
7265
  const layers=new Map
7318
7266
  let sequence=0
7319
7267
  function layerActive(layer){const mode=layer.mode??"base"
@@ -7510,72 +7458,9 @@ for(let index=disposers.length-1;index>=0; index--)
7510
7458
  await disposers[index]()
7511
7459
  disposers.length=0
7512
7460
  }}}
7513
-
7514
-
7515
- import {fileURLToPath} from "url"
7516
- var run=async (command)=>{const child=Bun.spawn([...command],{stdout:"ignore",stderr:"pipe"})
7517
- const error=new Response(child.stderr).text()
7518
- const exitCode=await child.exited
7519
- if(exitCode===0)
7520
- return
7521
- const message=await error
7522
- throw new Error(message.trim()||`Command failed with exit code ${child.exitCode}`)
7523
- }
7524
- var readConfig=async (path)=>{const file=Bun.file(path)
7525
- 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)
7526
7463
  }
7527
- function pinnedUpdate(wrapper,target,execute){if(basename(wrapper)!=="tui.js"||basename(dirname2(wrapper))!=="opencode-navigator")
7528
- throw new Error("Unrecognized local Navigator source")
7529
- const installer=fileURLToPath(new URL("../scripts/update-local-snapshot.mjs",import.meta.url))
7530
- return execute(["node",installer,wrapper,target])
7531
- }
7532
- function configHasPlugin(value,spec){if(!value||typeof value!=="object")
7533
- return false
7534
- const plugins=value.plugin
7535
- return Array.isArray(plugins)&&plugins.some((item)=>{let configured=item
7536
- if(Array.isArray(item))
7537
- configured=item[0]
7538
- else if(item&&typeof item==="object")
7539
- configured=item.package
7540
- return configured===spec
7541
- })
7542
- }
7543
- async function v1Global(api,spec,read){const configRoot=process.env.OPENCODE_CONFIG_DIR??join4(process.env.XDG_CONFIG_HOME??join4(homedir2(),".config"),"opencode")
7544
- const global=join4(configRoot,"tui.json")
7545
- const configured=api.state.path.config.endsWith(".json")?api.state.path.config:join4(api.state.path.config,"tui.json")
7546
- const local=new Set([configured,join4(api.state.path.worktree,".opencode","tui.json")])
7547
- local.delete(global)
7548
- for(const path of local)
7549
- if(configHasPlugin(await read(path),spec))
7550
- return false
7551
- return configHasPlugin(await read(global),spec)
7552
- }
7553
- 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
7554
- return pinnedUpdate(wrapper,target,execute)
7555
- }
7556
- if(meta.source!=="npm")
7557
- throw new Error("Unrecognized local Navigator source")
7558
- const result=await api.plugins.install(`opencode-navigator@${target}`,{global:await v1Global(api,meta.spec,read)
7559
- })
7560
- if(!result.ok)
7561
- throw new Error(result.message)
7562
- }}}
7563
- 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}:{})
7564
- const item=response.data.find((plugin)=>plugin.id===PLUGIN_ID)
7565
- if(!item)
7566
- throw new Error("Navigator is not in the active plugin list")
7567
- if(item.source.type==="local")
7568
- return pinnedUpdate(basename(item.source.path)==="tui.js"?item.source.path:join4(item.source.path,"tui.js"),target,execute)
7569
- if(item.source.type!=="package")
7570
- throw new Error("Unrecognized local Navigator source")
7571
- if(isAbsolute(item.source.target))
7572
- return pinnedUpdate(join4(item.source.target,"tui.js"),target,execute)
7573
- if(item.source.target.startsWith("file://"))
7574
- return pinnedUpdate(join4(fileURLToPath(item.source.target),"tui.js"),target,execute)
7575
- if(item.source.target.startsWith("."))
7576
- throw new Error("Unrecognized local Navigator source")
7577
- await context.client.plugin.update({location:response.location,targets:[item.source.target]})
7578
- }}}
7579
7464
  async function setupNavigator(api,options,readProject,managedTodo,hostContext){const config=pluginConfig(undefined)
7580
7465
  const configured=await loadConfiguredDefaults(api,options,readProject)
7581
7466
  const todo=managedTodo??createTodoController(api)
@@ -7586,7 +7471,7 @@ const limitsPath=import.meta.url.includes("/dist/tui.js")?"./provider-limits.js"
7586
7471
  const limitsModule=await import(limitsPath)
7587
7472
  const LimitsSection=limitsModule.ProviderLimitsSection
7588
7473
  const{controller:limits,codex,accountSource,selectModel,modelSelection
7589
- }=limitsModule.createProviderQuotaIntegration(api,preferences,"0.21.2",hostContext)
7474
+ }=limitsModule.createProviderQuotaIntegration(api,preferences,"0.21.4",hostContext)
7590
7475
  const mcp=createMcpController(api,preferences.persistMcp,preferences)
7591
7476
  const interaction=createSidebarInteraction(api,()=>openKeyboardHelp(api,preferences.lspIconStyle,preferences.cornerFont))
7592
7477
  const startup=createStartupSessionController(api,preferences)
@@ -7614,7 +7499,7 @@ api.slots.register({order:100,slots:{app(){return _$createComponent(IconProvider
7614
7499
  },interaction,skills,subagents,mcp
7615
7500
  }),_$createComponent(FirstRunWizardPersistence,{api,preferences
7616
7501
  }),_$createComponent(StartupSessionBinding,{api,controller:startup
7617
- }),_$createComponent(UpgradeNotesBinding,{api,preferences,version:"0.21.2"
7502
+ }),_$createComponent(UpgradeNotesBinding,{api,preferences,version:"0.21.4"
7618
7503
  }),_$createComponent(McpPersistence,{api,controller:mcp
7619
7504
  })]
7620
7505
  }})
@@ -7645,12 +7530,14 @@ throw result.error
7645
7530
  }
7646
7531
  return result.data?.type==="text"?result.data.content:undefined
7647
7532
  })
7648
- const versionStatus=createVersionStatus(api,"0.21.2")
7649
- const versionUpdates=createVersionUpdateActions(api,versionStatus,createOpenCodeV1VersionUpdater(api,meta),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.21.2"))
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"))
7650
7537
  api.slots.register({order:90,slots:{sidebar_footer(_context,props){return _$createComponent(IconProvider,{get style(){return preferences.lspIconStyle
7651
7538
  },get multilineCorners(){return preferences.cornerFont
7652
7539
  },get children(){return _$createComponent(VersionFooter,{api,get sessionID(){return props.session_id
7653
- },navigatorVersion:"0.21.2",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7540
+ },navigatorVersion:"0.21.4",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7654
7541
  },get onNavigatorUpdate(){return versionUpdates.navigator
7655
7542
  }})
7656
7543
  }})
@@ -7664,15 +7551,17 @@ return
7664
7551
  return new TextDecoder().decode(await context.client.file.read({location:context.location,path:".opencode/navigator.json"
7665
7552
  }))
7666
7553
  },todo,context)
7667
- const versionStatus=createVersionStatus(adapter.api,"0.21.2")
7668
- const versionUpdates=createVersionUpdateActions(adapter.api,versionStatus,createOpenCodeV2VersionUpdater(context),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.21.2"))
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"))
7669
7558
  const disposeFooter=context.ui.slot({append:"sidebar.footer",render:()=>_$createComponent(IconProvider,{get style(){return preferences.lspIconStyle
7670
7559
  },get multilineCorners(){return preferences.cornerFont
7671
7560
  },get children(){var _el$=_$createElement("box")
7672
7561
  _$setProp(_el$,"flexDirection","row")
7673
7562
  _$setProp(_el$,"justifyContent","space-between")
7674
7563
  _$insert(_el$,_$createComponent(VersionSummary,{get api(){return adapter.api
7675
- },navigatorVersion:"0.21.2",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7564
+ },navigatorVersion:"0.21.4",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
7676
7565
  },get onNavigatorUpdate(){return versionUpdates.navigator
7677
7566
  }}),null)
7678
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.2",
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",