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 +6 -0
- package/README.md +20 -1
- package/dist/navigator-updates.js +237 -0
- package/dist/tui.js +20 -132
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
7650
|
-
|
|
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.
|
|
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
|
|
7669
|
-
|
|
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.
|
|
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.
|
|
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",
|