loop-task 1.5.0 → 1.5.2

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.
@@ -0,0 +1,60 @@
1
+ import { useState, useEffect, useCallback, useRef } from "react";
2
+ import { useKeyboard } from "@opentui/react";
3
+ export function useTabNav(items, options) {
4
+ const initial = options?.initialIndex ?? 0;
5
+ const [focusIndex, setFocusIndex] = useState(Math.min(initial, Math.max(0, items.length - 1)));
6
+ const [enabled, setEnabled] = useState(true);
7
+ const focusIndexRef = useRef(focusIndex);
8
+ focusIndexRef.current = focusIndex;
9
+ const itemsRef = useRef(items);
10
+ itemsRef.current = items;
11
+ const onCycleOutRef = useRef(options?.onCycleOut);
12
+ onCycleOutRef.current = options?.onCycleOut;
13
+ const enabledRef = useRef(enabled);
14
+ enabledRef.current = enabled;
15
+ useEffect(() => {
16
+ setFocusIndex((i) => Math.min(i, Math.max(0, items.length - 1)));
17
+ }, [items.length]);
18
+ useKeyboard((key) => {
19
+ if (!enabledRef.current)
20
+ return;
21
+ if (key.name !== "tab")
22
+ return;
23
+ const currentItems = itemsRef.current;
24
+ const currentIdx = focusIndexRef.current;
25
+ const lastIndex = currentItems.length - 1;
26
+ if (lastIndex < 0)
27
+ return;
28
+ const direction = key.shift ? "left" : "right";
29
+ if (direction === "right") {
30
+ if (currentIdx >= lastIndex) {
31
+ if (onCycleOutRef.current) {
32
+ onCycleOutRef.current("right");
33
+ }
34
+ else {
35
+ setFocusIndex(0);
36
+ }
37
+ }
38
+ else {
39
+ setFocusIndex(currentIdx + 1);
40
+ }
41
+ }
42
+ else {
43
+ if (currentIdx <= 0) {
44
+ if (onCycleOutRef.current) {
45
+ onCycleOutRef.current("left");
46
+ }
47
+ else {
48
+ setFocusIndex(lastIndex);
49
+ }
50
+ }
51
+ else {
52
+ setFocusIndex(currentIdx - 1);
53
+ }
54
+ }
55
+ key.preventDefault();
56
+ });
57
+ const focusedItem = items[focusIndex];
58
+ const isFocused = useCallback((item) => item === focusedItem, [focusedItem]);
59
+ return { focusIndex, setFocusIndex, focusedItem, isFocused, enabled, setEnabled };
60
+ }
@@ -1,7 +1,6 @@
1
1
  import { useKeyboard } from "@opentui/react";
2
- import { nextTaskPanel } from "../components/TaskBrowser.js";
3
2
  export function useTaskKeybindings(params) {
4
- const { confirm, view, tasks, taskSelectedIndex, setTaskSelectedIndex, taskSelectedAction, setTaskSelectedAction, taskFocusedPanel, setTaskFocusedPanel, taskSearchActive, setTaskSearchActive, taskQuery, setTaskQuery, onTaskAction, onCancel, onCreateTask, onEnterHeader, onToggleContextHelp, selectable = true, } = params;
3
+ const { confirm, view, tasks, taskSelectedIndex, setTaskSelectedIndex, taskSelectedAction, setTaskSelectedAction, taskFocusedPanel, setTaskFocusedPanel, taskSearchActive, setTaskSearchActive, taskQuery, setTaskQuery, onTaskAction, onCancel, onCreateTask, onToggleContextHelp, headerFocused = false, selectable = true, } = params;
5
4
  const taskActions = selectable
6
5
  ? ["select", "edit", "delete"]
7
6
  : ["edit", "delete"];
@@ -9,6 +8,8 @@ export function useTaskKeybindings(params) {
9
8
  useKeyboard((key) => {
10
9
  if (view !== "task-list")
11
10
  return;
11
+ if (headerFocused)
12
+ return;
12
13
  const name = key.name;
13
14
  if (confirm)
14
15
  return;
@@ -51,34 +52,6 @@ export function useTaskKeybindings(params) {
51
52
  key.preventDefault();
52
53
  return;
53
54
  }
54
- if (name === "tab") {
55
- const direction = key.shift ? "left" : "right";
56
- if (taskFocusedPanel === "actions") {
57
- if (direction === "left" && taskSelectedAction === 0) {
58
- setTaskFocusedPanel((p) => nextTaskPanel(p, "left"));
59
- }
60
- else if (direction === "right" && taskSelectedAction === taskActionCount - 1) {
61
- onEnterHeader("right");
62
- key.preventDefault();
63
- return;
64
- }
65
- else {
66
- setTaskSelectedAction((i) => direction === "right"
67
- ? Math.min(taskActionCount - 1, i + 1)
68
- : Math.max(0, i - 1));
69
- }
70
- }
71
- else if (taskFocusedPanel === "search" && direction === "left") {
72
- onEnterHeader("left");
73
- key.preventDefault();
74
- return;
75
- }
76
- else {
77
- setTaskFocusedPanel((p) => nextTaskPanel(p, direction));
78
- }
79
- key.preventDefault();
80
- return;
81
- }
82
55
  if (taskFocusedPanel === "tasks") {
83
56
  if (name === "up" || name === "k") {
84
57
  setTaskSelectedIndex((i) => Math.max(0, i - 1));
package/dist/i18n/en.json CHANGED
@@ -297,6 +297,8 @@
297
297
  "board.toastTaskCreated": "Task created {id}",
298
298
  "board.toastTaskUpdated": "Task updated {id}",
299
299
  "board.toastTaskSelected": "Selected task \"{desc}\"",
300
+ "board.toastCopied": "Copied to clipboard",
301
+ "board.copyCommand": "Copy",
300
302
  "board.logWaiting": " Waiting for live output...",
301
303
  "board.logStreamError": "Log stream error: {message}",
302
304
  "board.hintSearch": "search",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loop-task",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Loop engineering toolkit. Run any command on a cadence, in the background, managed from a terminal board. Schedule tests, builds, syncs, or agent prompts.",
5
5
  "type": "module",
6
6
  "bin": {