@bojackduy/opencode-telescope 0.1.23 → 0.1.24

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/telescope.tsx +69 -40
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@bojackduy/opencode-telescope",
4
- "version": "0.1.23",
4
+ "version": "0.1.24",
5
5
  "description": "OpenCode TUI plugin for fuzzy and semantic search across local conversation history, session transcripts, and AI coding chats",
6
6
  "type": "module",
7
7
  "license": "MIT",
package/telescope.tsx CHANGED
@@ -98,6 +98,8 @@ export const Telescope = (props: { api: TuiPluginApi; config: TelescopeConfig; o
98
98
  let resultPreviousTimer: ReturnType<typeof setTimeout> | undefined
99
99
  let searchTimer: ReturnType<typeof setTimeout> | undefined
100
100
  let lastFiredQuery = ""
101
+ let searchRequestId = 0
102
+ let searchWorker: Worker | undefined
101
103
  const SEARCH_DEBOUNCE_MS = 200
102
104
  type PreviewBeforeIntent = "passive-prefetch" | "explicit-scroll"
103
105
  type PendingPreviewBefore = {
@@ -131,10 +133,60 @@ export const Telescope = (props: { api: TuiPluginApi; config: TelescopeConfig; o
131
133
  setPrefetchingPreviewAfter(false)
132
134
  setLoadingPreviewMore(false)
133
135
  }
136
+ const ensureSearchWorker = () => {
137
+ if (searchWorker) return true
138
+ try {
139
+ searchWorker = new Worker(new URL("./search-worker.ts", import.meta.url))
140
+ searchWorker.onmessage = (event: MessageEvent) => {
141
+ const msg = event.data
142
+ if (msg.type === "search-result") {
143
+ if (msg.id !== searchRequestId) return
144
+ const batch = msg.result
145
+ const limit = msg.limit
146
+ debug.log("bootstrap:search:done", { rows: batch.length, limit, mode: searchMode() })
147
+ solidBatch(() => {
148
+ setResults(batch)
149
+ setResultBaseOffset(0)
150
+ setNextResultOffset(batch.length)
151
+ setHasMore(batch.length >= limit)
152
+ setResultPageInfo({ loadedUntil: batch.length, hasMore: batch.length >= limit, pageSize: limit, lastOffset: 0, lastAdded: batch.length })
153
+ setSelected(0)
154
+ })
155
+ setBusy(false)
156
+ setLoading(false)
157
+ debug.timeEnd("query:search")
158
+ }
159
+ if (msg.type === "error") {
160
+ if (msg.id !== searchRequestId) return
161
+ debug.log("bootstrap:search:error", msg.error)
162
+ solidBatch(() => {
163
+ setResults([])
164
+ setResultBaseOffset(0)
165
+ setNextResultOffset(0)
166
+ setResultPageInfo({ loadedUntil: 0, hasMore: false, pageSize: 0, lastOffset: 0, lastAdded: 0 })
167
+ })
168
+ setError(msg.error)
169
+ setBusy(false)
170
+ setLoading(false)
171
+ debug.timeEnd("query:search")
172
+ }
173
+ }
174
+ searchWorker.onerror = (err) => {
175
+ debug.log("worker:error", err.message)
176
+ }
177
+ return true
178
+ } catch (err) {
179
+ debug.log("worker:create-error", err instanceof Error ? err.message : String(err))
180
+ return false
181
+ }
182
+ }
183
+
134
184
  onCleanup(() => {
135
185
  if (resultPrefetchTimer) clearTimeout(resultPrefetchTimer)
136
186
  if (resultPreviousTimer) clearTimeout(resultPreviousTimer)
137
187
  cancelPreviewPrefetch()
188
+ searchWorker?.terminate()
189
+ searchWorker = undefined
138
190
  })
139
191
 
140
192
  const resultRowHeight = createMemo(() => leftWidth() >= 48 ? 3 : 4)
@@ -205,41 +257,31 @@ export const Telescope = (props: { api: TuiPluginApi; config: TelescopeConfig; o
205
257
  return config.disableVector ? "keyword" as const : "hybrid" as const
206
258
  }
207
259
 
208
- const executeSearch = async (q: string, role: SearchRole | undefined, db: string, dir: string) => {
260
+ const executeSearch = (q: string, role: SearchRole | undefined, db: string, dir: string) => {
209
261
  lastFiredQuery = q
210
262
  const limit = q ? Math.min(searchBatchSize(), 80) : recentBatchSize()
263
+ setError("")
264
+ setHasMore(true)
265
+ advanceSelectionAfterLoad = false
266
+ advanceSelectionBeforeLoad = false
267
+ resultNavigationStarted = false
268
+ setLoadingMore(false)
269
+ setLoadingPreviousResults(false)
270
+ setPrefetchingResults(false)
211
271
  setLoading(true)
212
272
  if (q) setBusy(true)
213
273
  setSearchMode(detectSearchMode())
214
274
  debug.time("query:search")
215
- try {
216
- debug.log("bootstrap:search:start", { limit, directory: dir, role, query: q || "(all recent)" })
217
- const batch = q
218
- ? await performSearch(q, { limit, offset: 0, dbPath: db, directory: dir, role })
219
- : recentSessionMessages({ limit, offset: 0, dbPath: db, directory: dir, role })
220
- debug.log("bootstrap:search:done", { rows: batch.length, limit, mode: searchMode() })
221
- solidBatch(() => {
222
- setResults(batch)
223
- setResultBaseOffset(0)
224
- setNextResultOffset(batch.length)
225
- setHasMore(batch.length >= limit)
226
- setResultPageInfo({ loadedUntil: batch.length, hasMore: batch.length >= limit, pageSize: limit, lastOffset: 0, lastAdded: batch.length })
227
- setSelected(0)
228
- })
229
- } catch (err) {
230
- debug.log("bootstrap:search:error", err instanceof Error ? err.message : String(err))
231
- solidBatch(() => {
232
- setResults([])
233
- setResultBaseOffset(0)
234
- setNextResultOffset(0)
235
- setResultPageInfo({ loadedUntil: 0, hasMore: false, pageSize: limit, lastOffset: 0, lastAdded: 0 })
236
- })
237
- setError(err instanceof Error ? err.message : String(err))
238
- } finally {
275
+ const id = ++searchRequestId
276
+ if (!ensureSearchWorker()) {
239
277
  debug.timeEnd("query:search")
240
- setBusy(false)
241
- setLoading(false)
278
+ return
242
279
  }
280
+ const msg = q
281
+ ? { type: "search" as const, id, query: q, limit, offset: 0, directory: dir, role, dbPath: db }
282
+ : { type: "recent" as const, id, limit, offset: 0, directory: dir, role, dbPath: db }
283
+ debug.log("bootstrap:search:start", { limit, directory: dir, role, query: q || "(all recent)", worker: true })
284
+ searchWorker!.postMessage(msg)
243
285
  }
244
286
 
245
287
  const scheduleSearch = (q: string, role: SearchRole | undefined, db: string, dir: string) => {
@@ -254,21 +296,8 @@ export const Telescope = (props: { api: TuiPluginApi; config: TelescopeConfig; o
254
296
  createEffect(() => {
255
297
  const q = query().trim()
256
298
  const role = ownerRole()
257
- setError("")
258
- setHasMore(true)
259
- if (resultPrefetchTimer) clearTimeout(resultPrefetchTimer)
260
- if (resultPreviousTimer) clearTimeout(resultPreviousTimer)
261
- resultPrefetchTimer = undefined
262
- resultPreviousTimer = undefined
263
- advanceSelectionAfterLoad = false
264
- advanceSelectionBeforeLoad = false
265
- resultNavigationStarted = false
266
- setLoadingMore(false)
267
- setLoadingPreviousResults(false)
268
- setPrefetchingResults(false)
269
299
  const db = dbPath()
270
300
  const dir = directory
271
-
272
301
  scheduleSearch(q, role, db, dir)
273
302
  onCleanup(() => {
274
303
  if (searchTimer) clearTimeout(searchTimer)