@hacky.software/opencode-todo-supervisor 0.3.7 → 0.3.8
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 +5 -2
- package/package.json +1 -1
- package/src/panel.test.ts +22 -0
- package/src/panel.ts +26 -0
- package/src/tui.tsx +4 -2
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.
|
|
26
|
+
opencode plugin add @hacky.software/opencode-todo-supervisor@0.3.8
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
This also loads the bundled TUI entry. To pass options, use the object form:
|
|
@@ -197,7 +197,10 @@ effective set is `config.markdownFiles ∪ stored files − removed` per session
|
|
|
197
197
|
- it re-reads every 3 s while open, so edits made outside OpenCode show up
|
|
198
198
|
without waiting for an event.
|
|
199
199
|
- **Side panel** (palette "Todo: open side panel"): the same list docked to
|
|
200
|
-
`session.panel` (`todo.todos`), with `f` for fullscreen.
|
|
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
204
|
- `/todos add <path>` and `/todos remove <path>` manage subscriptions from the
|
|
202
205
|
prompt.
|
|
203
206
|
- **Toasts** on `nudged` (idle vs stalled wording) and on the all-done transition.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hacky.software/opencode-todo-supervisor",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
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
|
@@ -10,7 +10,9 @@ import {
|
|
|
10
10
|
isSelectable,
|
|
11
11
|
itemColumns,
|
|
12
12
|
moveCursor,
|
|
13
|
+
panelKeys,
|
|
13
14
|
popoverBodyHeight,
|
|
15
|
+
popoverKeys,
|
|
14
16
|
priorityTag,
|
|
15
17
|
progressBar,
|
|
16
18
|
quietLabel,
|
|
@@ -200,6 +202,26 @@ describe("panel cursor", () => {
|
|
|
200
202
|
expect(dialogWidth("xlarge", 20)).toBe(20)
|
|
201
203
|
})
|
|
202
204
|
|
|
205
|
+
test("the panel layer yields to overlays that own the keyboard", () => {
|
|
206
|
+
// Normal session view: live while focused.
|
|
207
|
+
expect(panelKeys(true, "base")).toBe(true)
|
|
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)
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
test("the popover keeps the keyboard in its own dialog", () => {
|
|
218
|
+
expect(popoverKeys("modal")).toBe(true)
|
|
219
|
+
expect(popoverKeys("base")).toBe(true)
|
|
220
|
+
expect(popoverKeys("composer")).toBe(true)
|
|
221
|
+
expect(popoverKeys("menu")).toBe(false)
|
|
222
|
+
expect(popoverKeys("autocomplete")).toBe(false)
|
|
223
|
+
})
|
|
224
|
+
|
|
203
225
|
test("id and priority tags and their column widths", () => {
|
|
204
226
|
expect(idTag(undefined)).toBe("")
|
|
205
227
|
expect(idTag("12")).toBe("#12")
|
package/src/panel.ts
CHANGED
|
@@ -322,3 +322,29 @@ const DIALOG_WIDTH: Record<DialogSizeName, number> = { medium: 60, large: 88, xl
|
|
|
322
322
|
export function dialogWidth(size: DialogSizeName, viewportWidth: number): number {
|
|
323
323
|
return Math.min(DIALOG_WIDTH[size], Math.max(20, viewportWidth - 2))
|
|
324
324
|
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
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.
|
|
331
|
+
*/
|
|
332
|
+
const OVERLAY_MODES: ReadonlySet<string> = new Set(["modal", "menu", "autocomplete"])
|
|
333
|
+
|
|
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.
|
|
338
|
+
*/
|
|
339
|
+
export function panelKeys(focused: boolean, mode: string): boolean {
|
|
340
|
+
return focused && !OVERLAY_MODES.has(mode)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* 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.
|
|
347
|
+
*/
|
|
348
|
+
export function popoverKeys(mode: string): boolean {
|
|
349
|
+
return mode !== "menu" && mode !== "autocomplete"
|
|
350
|
+
}
|
package/src/tui.tsx
CHANGED
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
idTag,
|
|
27
27
|
itemColumns,
|
|
28
28
|
moveCursor,
|
|
29
|
+
panelKeys,
|
|
29
30
|
popoverBodyHeight,
|
|
31
|
+
popoverKeys,
|
|
30
32
|
priorityTag,
|
|
31
33
|
progressBar,
|
|
32
34
|
rowIndex,
|
|
@@ -625,7 +627,7 @@ export default Plugin.define({
|
|
|
625
627
|
return (
|
|
626
628
|
<TodoList
|
|
627
629
|
sessionID={props.sessionID}
|
|
628
|
-
active={() =>
|
|
630
|
+
active={() => popoverKeys(context.keymap.mode.current())}
|
|
629
631
|
width={() => dialogWidth(size(), viewportWidth())}
|
|
630
632
|
height={() => popoverBodyHeight(viewportHeight())}
|
|
631
633
|
close={() => context.ui.dialog.clear()}
|
|
@@ -660,7 +662,7 @@ export default Plugin.define({
|
|
|
660
662
|
<Show when={panel.name === "todo.todos"}>
|
|
661
663
|
<TodoList
|
|
662
664
|
sessionID={panel.sessionID}
|
|
663
|
-
active={() => panel.focused}
|
|
665
|
+
active={() => panelKeys(panel.focused, context.keymap.mode.current())}
|
|
664
666
|
width={() => panel.width}
|
|
665
667
|
close={() => panel.close()}
|
|
666
668
|
cycleSize={() => panel.toggleFullscreen()}
|