@hacky.software/opencode-todo-supervisor 0.3.8 → 0.3.9

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/README.md CHANGED
@@ -23,7 +23,7 @@ Tested against OpenCode `2.0.24`.
23
23
  ## Install
24
24
 
25
25
  ```sh
26
- opencode plugin add @hacky.software/opencode-todo-supervisor@0.3.8
26
+ opencode plugin add @hacky.software/opencode-todo-supervisor@0.3.9
27
27
  ```
28
28
 
29
29
  This also loads the bundled TUI entry. To pass options, use the object form:
@@ -198,9 +198,10 @@ effective set is `config.markdownFiles ∪ stored files − removed` per session
198
198
  without waiting for an event.
199
199
  - **Side panel** (palette "Todo: open side panel"): the same list docked to
200
200
  `session.panel` (`todo.todos`), with `f` for fullscreen. Its keys are live only
201
- while the panel is focused *and* no overlay is up (dialog, menu, completion
202
- list), so a docked panel never steals the arrow keys from the model picker,
203
- command palette or the prompt's completions.
201
+ while the panel is focused *and* no overlay is up (dialog, menu, completions,
202
+ composer), so a docked panel never steals the arrow keys from the model picker,
203
+ command palette or the prompt's completions. The gate is applied per key at
204
+ dispatch, not on the layer: the host ignores a layer-level `enabled`.
204
205
  - `/todos add <path>` and `/todos remove <path>` manage subscriptions from the
205
206
  prompt.
206
207
  - **Toasts** on `nudged` (idle vs stalled wording) and on the all-done transition.
@@ -383,7 +384,7 @@ There is no filesystem watcher. Instead:
383
384
  (source edits and config content changes; `touch` alone does not).
384
385
  - `opencode2 reload` forces reconciliation; `opencode2 service restart` only as a
385
386
  last resort (it interrupts sessions).
386
- - Tests: `bun test` (121 tests across parser, storage, tools, RPC, state machine,
387
+ - Tests: `bun test` (123 tests across parser, storage, tools, RPC, state machine,
387
388
  panel helpers).
388
389
  - Watch logs: `tail -f ~/.local/share/opencode/log/todo-supervisor.log`.
389
390
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hacky.software/opencode-todo-supervisor",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "OpenCode V2 plugin that gives every session a todo list (todo_write/todo_read), supervises idle sessions until the list is done, and can drive checklists in markdown files from a TUI panel.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/panel.test.ts CHANGED
@@ -206,20 +206,20 @@ describe("panel cursor", () => {
206
206
  // Normal session view: live while focused.
207
207
  expect(panelKeys(true, "base")).toBe(true)
208
208
  expect(panelKeys(false, "base")).toBe(false)
209
- // The model picker, confirmations and other dialogs run in `modal`.
210
- expect(panelKeys(true, "modal")).toBe(false)
211
- expect(panelKeys(true, "menu")).toBe(false)
212
- expect(panelKeys(true, "autocomplete")).toBe(false)
213
- // An unknown mode is not treated as an overlay.
214
- expect(panelKeys(true, "composer")).toBe(true)
209
+ // The model picker, dialogs, menus and completions all take the keyboard.
210
+ for (const mode of ["modal", "menu", "autocomplete", "composer"]) {
211
+ expect(panelKeys(true, mode)).toBe(false)
212
+ }
213
+ // Any other mode (including the plain session view) keeps the panel live.
214
+ expect(panelKeys(true, "base")).toBe(true)
215
215
  })
216
216
 
217
217
  test("the popover keeps the keyboard in its own dialog", () => {
218
218
  expect(popoverKeys("modal")).toBe(true)
219
219
  expect(popoverKeys("base")).toBe(true)
220
- expect(popoverKeys("composer")).toBe(true)
221
220
  expect(popoverKeys("menu")).toBe(false)
222
221
  expect(popoverKeys("autocomplete")).toBe(false)
222
+ expect(popoverKeys("composer")).toBe(false)
223
223
  })
224
224
 
225
225
  test("id and priority tags and their column widths", () => {
package/src/panel.ts CHANGED
@@ -325,16 +325,17 @@ export function dialogWidth(size: DialogSizeName, viewportWidth: number): number
325
325
 
326
326
  /**
327
327
  * Keymap modes that own the keyboard while they are up: `modal` (dialogs, the
328
- * model picker, confirmations), `menu` (the command/palette menus) and
329
- * `autocomplete` (the prompt's completion list). They all drive themselves with
330
- * the arrow keys.
328
+ * model picker, confirmations), `menu` (the command/palette menus),
329
+ * `autocomplete` (the prompt's completion list) and `composer` (composer
330
+ * overlays). They all drive themselves with the arrow keys.
331
331
  */
332
- const OVERLAY_MODES: ReadonlySet<string> = new Set(["modal", "menu", "autocomplete"])
332
+ const OVERLAY_MODES: ReadonlySet<string> = new Set(["modal", "menu", "autocomplete", "composer"])
333
333
 
334
334
  /**
335
- * Whether the side panel's keys should be live. The layer binds the arrows
336
- * globally at a high priority, so it must yield the moment an overlay opens —
337
- * otherwise a focused docked panel swallows the model picker's up/down.
335
+ * Whether the side panel's keys should be live. Only the overlay modes take the
336
+ * keyboard away; any other mode (the plain session view included) keeps the
337
+ * panel live while it is focused. Without this a focused docked panel swallows
338
+ * the model picker's up/down.
338
339
  */
339
340
  export function panelKeys(focused: boolean, mode: string): boolean {
340
341
  return focused && !OVERLAY_MODES.has(mode)
@@ -342,9 +343,8 @@ export function panelKeys(focused: boolean, mode: string): boolean {
342
343
 
343
344
  /**
344
345
  * Whether the popover's keys should be live. The popover *is* a dialog, so
345
- * `modal` is ours; only the non-dialog overlays (palette, completion list) take
346
- * the keyboard away from it.
346
+ * `modal` is ours; any other overlay takes the keyboard away from it.
347
347
  */
348
348
  export function popoverKeys(mode: string): boolean {
349
- return mode !== "menu" && mode !== "autocomplete"
349
+ return mode === "modal" || !OVERLAY_MODES.has(mode)
350
350
  }
package/src/tui.tsx CHANGED
@@ -411,8 +411,9 @@ export default Plugin.define({
411
411
  })
412
412
 
413
413
  context.keymap.layer(() => ({
414
+ // "global" keeps the layer mode-agnostic; the actual gating is done
415
+ // per command below.
414
416
  mode: "global",
415
- enabled: () => props.active(),
416
417
  priority: 10,
417
418
  commands: [
418
419
  { title: "Todo: select next", bind: "down", run: () => move(1) },
@@ -493,7 +494,12 @@ export default Plugin.define({
493
494
  }),
494
495
  },
495
496
  { title: "Todo: close", bind: "escape", run: () => props.close() },
496
- ],
497
+ // The host drops a *layer*-level `enabled`; the *command*-level one
498
+ // is consulted on every dispatch. Gate each key here so it yields —
499
+ // and falls through — the moment an overlay (model picker, palette,
500
+ // completion list, composer) owns the keyboard while a todo surface
501
+ // happens to still be focused.
502
+ ].map((command) => ({ ...command, enabled: () => props.active() })),
497
503
  }))
498
504
 
499
505
  const totals = () => {