@ondrej-svec/hog 1.9.3 → 1.11.0

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/dist/cli.js CHANGED
@@ -740,10 +740,11 @@ function fetchProjectEnrichment(repo, projectNumber) {
740
740
  const [owner] = repo.split("/");
741
741
  if (!owner) return /* @__PURE__ */ new Map();
742
742
  const query = `
743
- query($owner: String!, $projectNumber: Int!) {
743
+ query($owner: String!, $projectNumber: Int!, $cursor: String) {
744
744
  organization(login: $owner) {
745
745
  projectV2(number: $projectNumber) {
746
- items(first: 100) {
746
+ items(first: 100, after: $cursor) {
747
+ pageInfo { hasNextPage endCursor }
747
748
  nodes {
748
749
  content {
749
750
  ... on Issue {
@@ -769,33 +770,41 @@ function fetchProjectEnrichment(repo, projectNumber) {
769
770
  }
770
771
  `;
771
772
  try {
772
- const result = runGhJson([
773
- "api",
774
- "graphql",
775
- "-f",
776
- `query=${query}`,
777
- "-F",
778
- `owner=${owner}`,
779
- "-F",
780
- `projectNumber=${String(projectNumber)}`
781
- ]);
782
- const items = result?.data?.organization?.projectV2?.items?.nodes ?? [];
783
773
  const enrichMap = /* @__PURE__ */ new Map();
784
- for (const item of items) {
785
- if (!item?.content?.number) continue;
786
- const enrichment = {};
787
- const fieldValues = item.fieldValues?.nodes ?? [];
788
- for (const fv of fieldValues) {
789
- if (!fv) continue;
790
- if ("date" in fv && fv.date && DATE_FIELD_NAME_RE2.test(fv.field?.name ?? "")) {
791
- enrichment.targetDate = fv.date;
792
- }
793
- if ("name" in fv && fv.field?.name === "Status" && fv.name) {
794
- enrichment.projectStatus = fv.name;
774
+ let cursor = null;
775
+ do {
776
+ const args = [
777
+ "api",
778
+ "graphql",
779
+ "-f",
780
+ `query=${query}`,
781
+ "-F",
782
+ `owner=${owner}`,
783
+ "-F",
784
+ `projectNumber=${String(projectNumber)}`
785
+ ];
786
+ if (cursor) args.push("-f", `cursor=${cursor}`);
787
+ const result = runGhJson(args);
788
+ const page = result?.data?.organization?.projectV2?.items;
789
+ const nodes = page?.nodes ?? [];
790
+ for (const item of nodes) {
791
+ if (!item?.content?.number) continue;
792
+ const enrichment = {};
793
+ const fieldValues = item.fieldValues?.nodes ?? [];
794
+ for (const fv of fieldValues) {
795
+ if (!fv) continue;
796
+ if ("date" in fv && fv.date && DATE_FIELD_NAME_RE2.test(fv.field?.name ?? "")) {
797
+ enrichment.targetDate = fv.date;
798
+ }
799
+ if ("name" in fv && fv.field?.name === "Status" && fv.name) {
800
+ enrichment.projectStatus = fv.name;
801
+ }
795
802
  }
803
+ enrichMap.set(item.content.number, enrichment);
796
804
  }
797
- enrichMap.set(item.content.number, enrichment);
798
- }
805
+ if (!page?.pageInfo?.hasNextPage) break;
806
+ cursor = page.pageInfo.endCursor ?? null;
807
+ } while (cursor);
799
808
  return enrichMap;
800
809
  } catch {
801
810
  return /* @__PURE__ */ new Map();
@@ -1630,7 +1639,7 @@ ${dueLine}` : dueLine;
1630
1639
  const output = await createIssueAsync(repo, title, effectiveBody, labels);
1631
1640
  const match = output.match(/\/(\d+)$/);
1632
1641
  const issueNumber = match?.[1] ? parseInt(match[1], 10) : 0;
1633
- const shortName = repoConfig?.shortName ?? repo;
1642
+ const shortName2 = repoConfig?.shortName ?? repo;
1634
1643
  if (issueNumber > 0 && dueDate && repoConfig?.dueDateFieldId) {
1635
1644
  const dueDateConfig = {
1636
1645
  projectNumber: repoConfig.projectNumber,
@@ -1639,7 +1648,7 @@ ${dueLine}` : dueLine;
1639
1648
  updateProjectItemDateAsync(repo, issueNumber, dueDateConfig, dueDate).catch(() => {
1640
1649
  });
1641
1650
  }
1642
- t.resolve(`Created ${shortName}#${issueNumber}`);
1651
+ t.resolve(`Created ${shortName2}#${issueNumber}`);
1643
1652
  refresh();
1644
1653
  onOverlayDone();
1645
1654
  return issueNumber > 0 ? { repo, issueNumber } : null;
@@ -1999,7 +2008,13 @@ function useKeyboard({
1999
2008
  selectedRepoStatusOptionsLength,
2000
2009
  actions,
2001
2010
  onSearchEscape,
2002
- tabNav
2011
+ panelFocus,
2012
+ reposNav,
2013
+ statusesNav,
2014
+ activityNav,
2015
+ onRepoEnter,
2016
+ onStatusEnter,
2017
+ onActivityEnter
2003
2018
  }) {
2004
2019
  const {
2005
2020
  exit,
@@ -2035,19 +2050,41 @@ function useKeyboard({
2035
2050
  }
2036
2051
  if (ui.canNavigate) {
2037
2052
  if (input2 === "j" || key.downArrow) {
2038
- nav.moveDown();
2053
+ switch (panelFocus.activePanelId) {
2054
+ case 1:
2055
+ reposNav.moveDown();
2056
+ break;
2057
+ case 2:
2058
+ statusesNav.moveDown();
2059
+ break;
2060
+ case 3:
2061
+ nav.moveDown();
2062
+ break;
2063
+ case 4:
2064
+ activityNav.moveDown();
2065
+ break;
2066
+ default:
2067
+ break;
2068
+ }
2039
2069
  return;
2040
2070
  }
2041
2071
  if (input2 === "k" || key.upArrow) {
2042
- nav.moveUp();
2043
- return;
2044
- }
2045
- if (key.tab) {
2046
- if (ui.state.mode === "multiSelect") {
2047
- multiSelect.clear();
2048
- ui.clearMultiSelect();
2072
+ switch (panelFocus.activePanelId) {
2073
+ case 1:
2074
+ reposNav.moveUp();
2075
+ break;
2076
+ case 2:
2077
+ statusesNav.moveUp();
2078
+ break;
2079
+ case 3:
2080
+ nav.moveUp();
2081
+ break;
2082
+ case 4:
2083
+ activityNav.moveUp();
2084
+ break;
2085
+ default:
2086
+ break;
2049
2087
  }
2050
- key.shift ? tabNav.prev() : tabNav.next();
2051
2088
  return;
2052
2089
  }
2053
2090
  }
@@ -2077,8 +2114,8 @@ function useKeyboard({
2077
2114
  if (input2 === "r" && handleErrorAction("retry")) return;
2078
2115
  if (ui.canAct) {
2079
2116
  const digit = parseInt(input2, 10);
2080
- if (!Number.isNaN(digit) && digit >= 1 && digit <= tabNav.count) {
2081
- tabNav.jumpTo(digit - 1);
2117
+ if (!Number.isNaN(digit) && digit >= 0 && digit <= 4) {
2118
+ panelFocus.focusPanel(digit);
2082
2119
  return;
2083
2120
  }
2084
2121
  if (input2 === "/") {
@@ -2095,7 +2132,7 @@ function useKeyboard({
2095
2132
  refresh();
2096
2133
  return;
2097
2134
  }
2098
- if (input2 === "s") {
2135
+ if (input2 === "o") {
2099
2136
  handleSlack();
2100
2137
  return;
2101
2138
  }
@@ -2178,7 +2215,22 @@ function useKeyboard({
2178
2215
  return;
2179
2216
  }
2180
2217
  if (key.return) {
2181
- handleOpen();
2218
+ switch (panelFocus.activePanelId) {
2219
+ case 1:
2220
+ onRepoEnter();
2221
+ break;
2222
+ case 2:
2223
+ onStatusEnter();
2224
+ break;
2225
+ case 3:
2226
+ handleOpen();
2227
+ break;
2228
+ case 4:
2229
+ onActivityEnter();
2230
+ break;
2231
+ default:
2232
+ break;
2233
+ }
2182
2234
  return;
2183
2235
  }
2184
2236
  }
@@ -2186,7 +2238,13 @@ function useKeyboard({
2186
2238
  [
2187
2239
  ui,
2188
2240
  nav,
2189
- tabNav,
2241
+ panelFocus,
2242
+ reposNav,
2243
+ statusesNav,
2244
+ activityNav,
2245
+ onRepoEnter,
2246
+ onStatusEnter,
2247
+ onActivityEnter,
2190
2248
  exit,
2191
2249
  refresh,
2192
2250
  handleSlack,
@@ -2492,26 +2550,42 @@ var init_use_navigation = __esm({
2492
2550
  }
2493
2551
  });
2494
2552
 
2553
+ // src/board/hooks/use-panel-focus.ts
2554
+ import { useCallback as useCallback7, useState as useState4 } from "react";
2555
+ function usePanelFocus(initialPanel = 3) {
2556
+ const [activePanelId, setActivePanelId] = useState4(initialPanel);
2557
+ const focusPanel = useCallback7((id) => {
2558
+ setActivePanelId(id);
2559
+ }, []);
2560
+ const isPanelActive = useCallback7((id) => activePanelId === id, [activePanelId]);
2561
+ return { activePanelId, focusPanel, isPanelActive };
2562
+ }
2563
+ var init_use_panel_focus = __esm({
2564
+ "src/board/hooks/use-panel-focus.ts"() {
2565
+ "use strict";
2566
+ }
2567
+ });
2568
+
2495
2569
  // src/board/hooks/use-toast.ts
2496
- import { useCallback as useCallback7, useRef as useRef6, useState as useState4 } from "react";
2570
+ import { useCallback as useCallback8, useRef as useRef6, useState as useState5 } from "react";
2497
2571
  function useToast() {
2498
- const [toasts, setToasts] = useState4([]);
2572
+ const [toasts, setToasts] = useState5([]);
2499
2573
  const timersRef = useRef6(/* @__PURE__ */ new Map());
2500
- const clearTimer = useCallback7((id) => {
2574
+ const clearTimer = useCallback8((id) => {
2501
2575
  const timer = timersRef.current.get(id);
2502
2576
  if (timer) {
2503
2577
  clearTimeout(timer);
2504
2578
  timersRef.current.delete(id);
2505
2579
  }
2506
2580
  }, []);
2507
- const removeToast = useCallback7(
2581
+ const removeToast = useCallback8(
2508
2582
  (id) => {
2509
2583
  clearTimer(id);
2510
2584
  setToasts((prev) => prev.filter((t) => t.id !== id));
2511
2585
  },
2512
2586
  [clearTimer]
2513
2587
  );
2514
- const addToast = useCallback7(
2588
+ const addToast = useCallback8(
2515
2589
  (t) => {
2516
2590
  const id = `toast-${++nextId}`;
2517
2591
  const newToast = { ...t, id, createdAt: Date.now() };
@@ -2540,25 +2614,25 @@ function useToast() {
2540
2614
  [removeToast, clearTimer]
2541
2615
  );
2542
2616
  const toast = {
2543
- info: useCallback7(
2617
+ info: useCallback8(
2544
2618
  (message) => {
2545
2619
  addToast({ type: "info", message });
2546
2620
  },
2547
2621
  [addToast]
2548
2622
  ),
2549
- success: useCallback7(
2623
+ success: useCallback8(
2550
2624
  (message) => {
2551
2625
  addToast({ type: "success", message });
2552
2626
  },
2553
2627
  [addToast]
2554
2628
  ),
2555
- error: useCallback7(
2629
+ error: useCallback8(
2556
2630
  (message, retry) => {
2557
2631
  addToast(retry ? { type: "error", message, retry } : { type: "error", message });
2558
2632
  },
2559
2633
  [addToast]
2560
2634
  ),
2561
- loading: useCallback7(
2635
+ loading: useCallback8(
2562
2636
  (message) => {
2563
2637
  const id = addToast({ type: "loading", message });
2564
2638
  return {
@@ -2575,7 +2649,7 @@ function useToast() {
2575
2649
  [addToast, removeToast]
2576
2650
  )
2577
2651
  };
2578
- const handleErrorAction = useCallback7(
2652
+ const handleErrorAction = useCallback8(
2579
2653
  (action) => {
2580
2654
  const errorToast = toasts.find((t) => t.type === "error");
2581
2655
  if (!errorToast) return false;
@@ -2605,7 +2679,7 @@ var init_use_toast = __esm({
2605
2679
  });
2606
2680
 
2607
2681
  // src/board/hooks/use-ui-state.ts
2608
- import { useCallback as useCallback8, useReducer as useReducer2 } from "react";
2682
+ import { useCallback as useCallback9, useReducer as useReducer2 } from "react";
2609
2683
  function enterStatusMode(state) {
2610
2684
  if (state.mode !== "normal" && state.mode !== "overlay:bulkAction") return state;
2611
2685
  const previousMode = state.mode === "overlay:bulkAction" ? "multiSelect" : "normal";
@@ -2679,22 +2753,22 @@ function useUIState() {
2679
2753
  const [state, dispatch] = useReducer2(uiReducer, INITIAL_STATE2);
2680
2754
  return {
2681
2755
  state,
2682
- enterSearch: useCallback8(() => dispatch({ type: "ENTER_SEARCH" }), []),
2683
- enterComment: useCallback8(() => dispatch({ type: "ENTER_COMMENT" }), []),
2684
- enterStatus: useCallback8(() => dispatch({ type: "ENTER_STATUS" }), []),
2685
- enterCreate: useCallback8(() => dispatch({ type: "ENTER_CREATE" }), []),
2686
- enterCreateNl: useCallback8(() => dispatch({ type: "ENTER_CREATE_NL" }), []),
2687
- enterLabel: useCallback8(() => dispatch({ type: "ENTER_LABEL" }), []),
2688
- enterMultiSelect: useCallback8(() => dispatch({ type: "ENTER_MULTI_SELECT" }), []),
2689
- enterBulkAction: useCallback8(() => dispatch({ type: "ENTER_BULK_ACTION" }), []),
2690
- enterConfirmPick: useCallback8(() => dispatch({ type: "ENTER_CONFIRM_PICK" }), []),
2691
- enterFocus: useCallback8(() => dispatch({ type: "ENTER_FOCUS" }), []),
2692
- enterFuzzyPicker: useCallback8(() => dispatch({ type: "ENTER_FUZZY_PICKER" }), []),
2693
- enterEditIssue: useCallback8(() => dispatch({ type: "ENTER_EDIT_ISSUE" }), []),
2694
- toggleHelp: useCallback8(() => dispatch({ type: "TOGGLE_HELP" }), []),
2695
- exitOverlay: useCallback8(() => dispatch({ type: "EXIT_OVERLAY" }), []),
2696
- exitToNormal: useCallback8(() => dispatch({ type: "EXIT_TO_NORMAL" }), []),
2697
- clearMultiSelect: useCallback8(() => dispatch({ type: "CLEAR_MULTI_SELECT" }), []),
2756
+ enterSearch: useCallback9(() => dispatch({ type: "ENTER_SEARCH" }), []),
2757
+ enterComment: useCallback9(() => dispatch({ type: "ENTER_COMMENT" }), []),
2758
+ enterStatus: useCallback9(() => dispatch({ type: "ENTER_STATUS" }), []),
2759
+ enterCreate: useCallback9(() => dispatch({ type: "ENTER_CREATE" }), []),
2760
+ enterCreateNl: useCallback9(() => dispatch({ type: "ENTER_CREATE_NL" }), []),
2761
+ enterLabel: useCallback9(() => dispatch({ type: "ENTER_LABEL" }), []),
2762
+ enterMultiSelect: useCallback9(() => dispatch({ type: "ENTER_MULTI_SELECT" }), []),
2763
+ enterBulkAction: useCallback9(() => dispatch({ type: "ENTER_BULK_ACTION" }), []),
2764
+ enterConfirmPick: useCallback9(() => dispatch({ type: "ENTER_CONFIRM_PICK" }), []),
2765
+ enterFocus: useCallback9(() => dispatch({ type: "ENTER_FOCUS" }), []),
2766
+ enterFuzzyPicker: useCallback9(() => dispatch({ type: "ENTER_FUZZY_PICKER" }), []),
2767
+ enterEditIssue: useCallback9(() => dispatch({ type: "ENTER_EDIT_ISSUE" }), []),
2768
+ toggleHelp: useCallback9(() => dispatch({ type: "TOGGLE_HELP" }), []),
2769
+ exitOverlay: useCallback9(() => dispatch({ type: "EXIT_OVERLAY" }), []),
2770
+ exitToNormal: useCallback9(() => dispatch({ type: "EXIT_TO_NORMAL" }), []),
2771
+ clearMultiSelect: useCallback9(() => dispatch({ type: "CLEAR_MULTI_SELECT" }), []),
2698
2772
  canNavigate: canNavigate(state),
2699
2773
  canAct: canAct(state),
2700
2774
  isOverlay: isOverlay(state)
@@ -2714,7 +2788,7 @@ var init_use_ui_state = __esm({
2714
2788
 
2715
2789
  // src/board/components/action-log.tsx
2716
2790
  import { Box, Text } from "ink";
2717
- import { useEffect as useEffect2, useState as useState5 } from "react";
2791
+ import { useEffect as useEffect2, useState as useState6 } from "react";
2718
2792
  import { jsx, jsxs } from "react/jsx-runtime";
2719
2793
  function relativeTime(ago) {
2720
2794
  const seconds = Math.floor((Date.now() - ago) / 1e3);
@@ -2735,7 +2809,7 @@ function statusColor(status) {
2735
2809
  return "yellow";
2736
2810
  }
2737
2811
  function ActionLog({ entries }) {
2738
- const [, setTick] = useState5(0);
2812
+ const [, setTick] = useState6(0);
2739
2813
  useEffect2(() => {
2740
2814
  const id = setInterval(() => setTick((t) => t + 1), 5e3);
2741
2815
  return () => clearInterval(id);
@@ -2774,14 +2848,61 @@ var init_action_log = __esm({
2774
2848
  }
2775
2849
  });
2776
2850
 
2777
- // src/board/components/detail-panel.tsx
2851
+ // src/board/components/activity-panel.tsx
2778
2852
  import { Box as Box2, Text as Text2 } from "ink";
2779
- import { useEffect as useEffect3 } from "react";
2780
- import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2781
- function truncateLines(text, maxLines) {
2782
- const lines = text.split("\n").slice(0, maxLines);
2783
- return lines.join("\n");
2853
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2854
+ function ActivityPanel({ events, selectedIdx, isActive, height }) {
2855
+ const borderColor = isActive ? "cyan" : "gray";
2856
+ const maxRows = Math.max(1, height - 2);
2857
+ const visible = events.slice(0, maxRows);
2858
+ return /* @__PURE__ */ jsxs2(
2859
+ Box2,
2860
+ {
2861
+ borderStyle: "single",
2862
+ borderColor,
2863
+ flexDirection: "column",
2864
+ height,
2865
+ overflow: "hidden",
2866
+ children: [
2867
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: isActive ? "cyan" : "white", children: "[4] Activity" }),
2868
+ visible.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: "gray", children: " No recent activity" }) : visible.map((event, i) => {
2869
+ const isSel = isActive && i === selectedIdx;
2870
+ const ago = timeAgo(event.timestamp);
2871
+ return /* @__PURE__ */ jsxs2(Box2, { children: [
2872
+ /* @__PURE__ */ jsxs2(Text2, { color: isSel ? "cyan" : "gray", bold: isSel, children: [
2873
+ isSel ? "\u25B6 " : " ",
2874
+ ago
2875
+ ] }),
2876
+ /* @__PURE__ */ jsxs2(Text2, { color: isSel ? "white" : "gray", children: [
2877
+ " ",
2878
+ "@",
2879
+ event.actor,
2880
+ " ",
2881
+ event.summary,
2882
+ " "
2883
+ ] }),
2884
+ /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
2885
+ "(",
2886
+ event.repoShortName,
2887
+ ")"
2888
+ ] })
2889
+ ] }, `${event.repoShortName}:${event.issueNumber}:${i}`);
2890
+ })
2891
+ ]
2892
+ }
2893
+ );
2784
2894
  }
2895
+ var init_activity_panel = __esm({
2896
+ "src/board/components/activity-panel.tsx"() {
2897
+ "use strict";
2898
+ init_constants();
2899
+ }
2900
+ });
2901
+
2902
+ // src/board/components/detail-panel.tsx
2903
+ import { Box as Box3, Text as Text3 } from "ink";
2904
+ import { useEffect as useEffect3 } from "react";
2905
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2785
2906
  function stripMarkdown(text) {
2786
2907
  return text.replace(/^#{1,6}\s+/gm, "").replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/__(.+?)__/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1").replace(/`{1,3}[^`]*`{1,3}/g, (m) => m.replace(/`/g, "")).replace(/^\s*[-*+]\s+/gm, " - ").replace(/^\s*\d+\.\s+/gm, " ").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/!\[([^\]]*)\]\([^)]+\)/g, "[$1]").replace(/^>\s+/gm, " ").replace(/---+/g, "").replace(/\n{3,}/g, "\n\n").trim();
2787
2908
  }
@@ -2800,11 +2921,11 @@ function BodySection({
2800
2921
  issueNumber
2801
2922
  }) {
2802
2923
  const { text, remaining } = formatBody(body, 15);
2803
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
2804
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2805
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "--- Description ---" }),
2806
- /* @__PURE__ */ jsx2(Text2, { wrap: "wrap", children: text }),
2807
- remaining > 0 ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
2924
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
2925
+ /* @__PURE__ */ jsx3(Text3, { children: "" }),
2926
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "--- Description ---" }),
2927
+ /* @__PURE__ */ jsx3(Text3, { wrap: "wrap", children: text }),
2928
+ remaining > 0 ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
2808
2929
  "... (",
2809
2930
  remaining,
2810
2931
  " more lines \u2014 gh issue view ",
@@ -2825,185 +2946,143 @@ function formatCommentAge(createdAt) {
2825
2946
  }
2826
2947
  function DetailPanel({
2827
2948
  issue,
2828
- task: task2,
2829
2949
  width,
2950
+ isActive,
2830
2951
  commentsState,
2831
2952
  fetchComments,
2832
2953
  issueRepo
2833
2954
  }) {
2955
+ const borderColor = isActive ? "cyan" : "gray";
2834
2956
  useEffect3(() => {
2835
2957
  if (!(issue && fetchComments && issueRepo)) return;
2836
2958
  if (commentsState !== null && commentsState !== void 0) return;
2837
2959
  fetchComments(issueRepo, issue.number);
2838
2960
  }, [issue, issueRepo, fetchComments, commentsState]);
2839
- if (!(issue || task2)) {
2840
- return /* @__PURE__ */ jsx2(
2841
- Box2,
2842
- {
2843
- width,
2844
- borderStyle: "single",
2845
- borderColor: "gray",
2846
- flexDirection: "column",
2847
- paddingX: 1,
2848
- children: /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "No item selected" })
2849
- }
2850
- );
2851
- }
2852
- if (issue) {
2853
- return /* @__PURE__ */ jsxs2(
2854
- Box2,
2961
+ if (!issue) {
2962
+ return /* @__PURE__ */ jsxs3(
2963
+ Box3,
2855
2964
  {
2856
2965
  width,
2857
2966
  borderStyle: "single",
2858
- borderColor: "cyan",
2967
+ borderColor,
2859
2968
  flexDirection: "column",
2860
2969
  paddingX: 1,
2861
2970
  children: [
2862
- /* @__PURE__ */ jsxs2(Text2, { color: "cyan", bold: true, children: [
2863
- "#",
2864
- issue.number,
2865
- " ",
2866
- issue.title
2867
- ] }),
2868
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2869
- /* @__PURE__ */ jsxs2(Box2, { children: [
2870
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "State: " }),
2871
- /* @__PURE__ */ jsx2(Text2, { color: issue.state === "open" ? "green" : "red", children: issue.state })
2872
- ] }),
2873
- (issue.assignees ?? []).length > 0 ? /* @__PURE__ */ jsxs2(Box2, { children: [
2874
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Assignees: " }),
2875
- /* @__PURE__ */ jsx2(Text2, { children: (issue.assignees ?? []).map((a) => a.login).join(", ") })
2876
- ] }) : null,
2877
- issue.labels.length > 0 ? /* @__PURE__ */ jsxs2(Box2, { children: [
2878
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Labels: " }),
2879
- /* @__PURE__ */ jsx2(Text2, { children: issue.labels.map((l) => l.name).join(", ") })
2880
- ] }) : null,
2881
- issue.projectStatus ? /* @__PURE__ */ jsxs2(Box2, { children: [
2882
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Status: " }),
2883
- /* @__PURE__ */ jsx2(Text2, { color: "magenta", children: issue.projectStatus })
2884
- ] }) : null,
2885
- issue.targetDate ? /* @__PURE__ */ jsxs2(Box2, { children: [
2886
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Target: " }),
2887
- /* @__PURE__ */ jsx2(Text2, { children: issue.targetDate })
2888
- ] }) : null,
2889
- /* @__PURE__ */ jsxs2(Box2, { children: [
2890
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Updated: " }),
2891
- /* @__PURE__ */ jsx2(Text2, { children: new Date(issue.updatedAt).toLocaleString() })
2892
- ] }),
2893
- issue.slackThreadUrl ? /* @__PURE__ */ jsxs2(Box2, { children: [
2894
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Slack: " }),
2895
- /* @__PURE__ */ jsx2(Text2, { color: "blue", children: countSlackLinks(issue.body) > 1 ? `${countSlackLinks(issue.body)} links (s opens first)` : "thread (s to open)" })
2896
- ] }) : null,
2897
- issue.body ? /* @__PURE__ */ jsx2(BodySection, { body: issue.body, issueNumber: issue.number }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
2898
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2899
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "(no description)" })
2900
- ] }),
2901
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2902
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "--- Comments ---" }),
2903
- commentsState === "loading" ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "fetching comments..." }) : commentsState === "error" ? /* @__PURE__ */ jsx2(Text2, { color: "red", children: "could not load comments" }) : commentsState && commentsState.length === 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "No comments yet." }) : commentsState && commentsState.length > 0 ? commentsState.slice(-5).map((comment, i) => (
2904
- // biome-ignore lint/suspicious/noArrayIndexKey: stable list
2905
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
2906
- /* @__PURE__ */ jsxs2(Text2, { color: "cyan", children: [
2907
- "@",
2908
- comment.author.login,
2909
- " \xB7 ",
2910
- formatCommentAge(comment.createdAt)
2911
- ] }),
2912
- /* @__PURE__ */ jsxs2(Text2, { wrap: "wrap", children: [
2913
- " ",
2914
- comment.body.split("\n")[0]
2915
- ] })
2916
- ] }, i)
2917
- )) : /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "fetching comments..." }),
2918
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2919
- /* @__PURE__ */ jsx2(Text2, { color: "gray", dimColor: true, children: issue.url })
2971
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: isActive ? "cyan" : "white", children: "[0] Detail" }),
2972
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "No item selected" })
2920
2973
  ]
2921
2974
  }
2922
2975
  );
2923
2976
  }
2924
- const t = task2;
2925
- return /* @__PURE__ */ jsxs2(
2926
- Box2,
2977
+ return /* @__PURE__ */ jsxs3(
2978
+ Box3,
2927
2979
  {
2928
2980
  width,
2929
2981
  borderStyle: "single",
2930
- borderColor: "yellow",
2982
+ borderColor,
2931
2983
  flexDirection: "column",
2932
2984
  paddingX: 1,
2933
2985
  children: [
2934
- /* @__PURE__ */ jsx2(Text2, { color: "yellow", bold: true, children: t.title }),
2935
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2936
- /* @__PURE__ */ jsxs2(Box2, { children: [
2937
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Priority: " }),
2938
- /* @__PURE__ */ jsx2(Text2, { children: PRIORITY_LABELS2[t.priority] ?? "None" })
2986
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: isActive ? "cyan" : "white", children: "[0] Detail" }),
2987
+ /* @__PURE__ */ jsxs3(Text3, { color: "cyan", bold: true, children: [
2988
+ "#",
2989
+ issue.number,
2990
+ " ",
2991
+ issue.title
2992
+ ] }),
2993
+ /* @__PURE__ */ jsx3(Text3, { children: "" }),
2994
+ /* @__PURE__ */ jsxs3(Box3, { children: [
2995
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "State: " }),
2996
+ /* @__PURE__ */ jsx3(Text3, { color: issue.state === "open" ? "green" : "red", children: issue.state })
2939
2997
  ] }),
2940
- t.dueDate ? /* @__PURE__ */ jsxs2(Box2, { children: [
2941
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Due: " }),
2942
- /* @__PURE__ */ jsx2(Text2, { children: new Date(t.dueDate).toLocaleDateString() })
2998
+ (issue.assignees ?? []).length > 0 ? /* @__PURE__ */ jsxs3(Box3, { children: [
2999
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Assignees: " }),
3000
+ /* @__PURE__ */ jsx3(Text3, { children: (issue.assignees ?? []).map((a) => a.login).join(", ") })
2943
3001
  ] }) : null,
2944
- (t.tags ?? []).length > 0 ? /* @__PURE__ */ jsxs2(Box2, { children: [
2945
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Tags: " }),
2946
- /* @__PURE__ */ jsx2(Text2, { children: t.tags.join(", ") })
3002
+ issue.labels.length > 0 ? /* @__PURE__ */ jsxs3(Box3, { children: [
3003
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Labels: " }),
3004
+ /* @__PURE__ */ jsx3(Text3, { children: issue.labels.map((l) => l.name).join(", ") })
2947
3005
  ] }) : null,
2948
- t.content ? /* @__PURE__ */ jsxs2(Fragment, { children: [
2949
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2950
- /* @__PURE__ */ jsx2(Text2, { children: truncateLines(t.content, 8) })
3006
+ issue.projectStatus ? /* @__PURE__ */ jsxs3(Box3, { children: [
3007
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Status: " }),
3008
+ /* @__PURE__ */ jsx3(Text3, { color: "magenta", children: issue.projectStatus })
2951
3009
  ] }) : null,
2952
- (t.items ?? []).length > 0 ? /* @__PURE__ */ jsxs2(Fragment, { children: [
2953
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2954
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Checklist:" }),
2955
- t.items.slice(0, 5).map((item) => /* @__PURE__ */ jsxs2(Text2, { children: [
2956
- item.status === 2 ? "\u2611" : "\u2610",
2957
- " ",
2958
- item.title
2959
- ] }, item.id)),
2960
- t.items.length > 5 ? /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
2961
- "...and ",
2962
- t.items.length - 5,
2963
- " more"
2964
- ] }) : null
2965
- ] }) : null
3010
+ issue.targetDate ? /* @__PURE__ */ jsxs3(Box3, { children: [
3011
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Target: " }),
3012
+ /* @__PURE__ */ jsx3(Text3, { children: issue.targetDate })
3013
+ ] }) : null,
3014
+ /* @__PURE__ */ jsxs3(Box3, { children: [
3015
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Updated: " }),
3016
+ /* @__PURE__ */ jsx3(Text3, { children: new Date(issue.updatedAt).toLocaleString() })
3017
+ ] }),
3018
+ issue.slackThreadUrl ? /* @__PURE__ */ jsxs3(Box3, { children: [
3019
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Slack: " }),
3020
+ /* @__PURE__ */ jsx3(Text3, { color: "blue", children: countSlackLinks(issue.body) > 1 ? `${countSlackLinks(issue.body)} links (s opens first)` : "thread (s to open)" })
3021
+ ] }) : null,
3022
+ issue.body ? /* @__PURE__ */ jsx3(BodySection, { body: issue.body, issueNumber: issue.number }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
3023
+ /* @__PURE__ */ jsx3(Text3, { children: "" }),
3024
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "(no description)" })
3025
+ ] }),
3026
+ /* @__PURE__ */ jsx3(Text3, { children: "" }),
3027
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "--- Comments ---" }),
3028
+ commentsState === "loading" ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "fetching comments..." }) : commentsState === "error" ? /* @__PURE__ */ jsx3(Text3, { color: "red", children: "could not load comments" }) : commentsState && commentsState.length === 0 ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "No comments yet." }) : commentsState && commentsState.length > 0 ? commentsState.slice(-5).map((comment, i) => (
3029
+ // biome-ignore lint/suspicious/noArrayIndexKey: stable list
3030
+ /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginBottom: 1, children: [
3031
+ /* @__PURE__ */ jsxs3(Text3, { color: "cyan", children: [
3032
+ "@",
3033
+ comment.author.login,
3034
+ " \xB7 ",
3035
+ formatCommentAge(comment.createdAt)
3036
+ ] }),
3037
+ /* @__PURE__ */ jsxs3(Text3, { wrap: "wrap", children: [
3038
+ " ",
3039
+ comment.body.split("\n")[0]
3040
+ ] })
3041
+ ] }, i)
3042
+ )) : /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "fetching comments..." }),
3043
+ /* @__PURE__ */ jsx3(Text3, { children: "" }),
3044
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", dimColor: true, children: issue.url })
2966
3045
  ]
2967
3046
  }
2968
3047
  );
2969
3048
  }
2970
- var SLACK_URL_RE, PRIORITY_LABELS2;
3049
+ var SLACK_URL_RE;
2971
3050
  var init_detail_panel = __esm({
2972
3051
  "src/board/components/detail-panel.tsx"() {
2973
3052
  "use strict";
2974
- init_types();
2975
3053
  SLACK_URL_RE = /https:\/\/[^/]+\.slack\.com\/archives\/[A-Z0-9]+\/p[0-9]+/gi;
2976
- PRIORITY_LABELS2 = {
2977
- [5 /* High */]: "High",
2978
- [3 /* Medium */]: "Medium",
2979
- [1 /* Low */]: "Low",
2980
- [0 /* None */]: "None"
2981
- };
2982
3054
  }
2983
3055
  });
2984
3056
 
2985
3057
  // src/board/components/hint-bar.tsx
2986
- import { Box as Box3, Text as Text3 } from "ink";
2987
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2988
- function HintBar({ uiMode, multiSelectCount, searchQuery, mineOnly, hasUndoable }) {
3058
+ import { Box as Box4, Text as Text4 } from "ink";
3059
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3060
+ function HintBar({
3061
+ uiMode,
3062
+ activePanelId,
3063
+ multiSelectCount,
3064
+ searchQuery,
3065
+ mineOnly,
3066
+ hasUndoable
3067
+ }) {
2989
3068
  if (uiMode === "multiSelect") {
2990
- return /* @__PURE__ */ jsxs3(Box3, { children: [
2991
- /* @__PURE__ */ jsxs3(Text3, { color: "cyan", bold: true, children: [
3069
+ return /* @__PURE__ */ jsxs4(Box4, { children: [
3070
+ /* @__PURE__ */ jsxs4(Text4, { color: "cyan", bold: true, children: [
2992
3071
  "[MULTI-SELECT] ",
2993
3072
  multiSelectCount,
2994
3073
  " selected"
2995
3074
  ] }),
2996
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " Space:toggle Enter:actions Esc:cancel" })
3075
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: " Space:toggle Enter:actions Esc:cancel" })
2997
3076
  ] });
2998
3077
  }
2999
3078
  if (uiMode === "focus") {
3000
- return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: "magenta", bold: true, children: "[FOCUS] Focus mode \u2014 Esc to exit" }) });
3079
+ return /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text4, { color: "magenta", bold: true, children: "[FOCUS] Focus mode \u2014 Esc to exit" }) });
3001
3080
  }
3002
3081
  if (uiMode === "search") {
3003
- return /* @__PURE__ */ jsxs3(Box3, { children: [
3004
- /* @__PURE__ */ jsx3(Text3, { color: "yellow", bold: true, children: "[SEARCH]" }),
3005
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " type to filter Enter:confirm Esc:clear" }),
3006
- searchQuery ? /* @__PURE__ */ jsxs3(Text3, { color: "yellow", children: [
3082
+ return /* @__PURE__ */ jsxs4(Box4, { children: [
3083
+ /* @__PURE__ */ jsx4(Text4, { color: "yellow", bold: true, children: "[SEARCH]" }),
3084
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: " type to filter Enter:confirm Esc:clear" }),
3085
+ searchQuery ? /* @__PURE__ */ jsxs4(Text4, { color: "yellow", children: [
3007
3086
  ' "',
3008
3087
  searchQuery,
3009
3088
  '"'
@@ -3011,19 +3090,22 @@ function HintBar({ uiMode, multiSelectCount, searchQuery, mineOnly, hasUndoable
3011
3090
  ] });
3012
3091
  }
3013
3092
  if (uiMode === "overlay:fuzzyPicker") {
3014
- return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "\u2191\u2193/Ctrl-J/K:nav Enter:jump Esc:close" }) });
3093
+ return /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "\u2191\u2193/Ctrl-J/K:nav Enter:jump Esc:close" }) });
3015
3094
  }
3016
3095
  if (uiMode.startsWith("overlay:")) {
3017
- return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "j/k:nav Enter:select Esc:cancel" }) });
3018
- }
3019
- return /* @__PURE__ */ jsxs3(Box3, { children: [
3020
- /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
3021
- "j/k:nav Tab:next-tab 1-9:jump Enter:open m:status c:comment F:find t:@me e:edit",
3022
- hasUndoable ? " u:undo" : "",
3023
- " ?:more q:quit"
3024
- ] }),
3025
- mineOnly ? /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: " filter:@me" }) : null,
3026
- searchQuery ? /* @__PURE__ */ jsxs3(Text3, { color: "yellow", children: [
3096
+ return /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "j/k:nav Enter:select Esc:cancel" }) });
3097
+ }
3098
+ const panelHints = {
3099
+ 0: "j/k:scroll Esc:close ? help",
3100
+ 1: "j/k:move Enter:filter 0-4:panel ? help",
3101
+ 2: "j/k:move Enter:filter Esc:clear 0-4:panel ? help",
3102
+ 3: `j/k:move p:pick m:status c:comment /:search n:new 0-4:panel${hasUndoable ? " u:undo" : ""} ? help q:quit`,
3103
+ 4: "j/k:scroll Enter:jump r:refresh 0-4:panel ? help"
3104
+ };
3105
+ return /* @__PURE__ */ jsxs4(Box4, { children: [
3106
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: panelHints[activePanelId] }),
3107
+ mineOnly ? /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: " filter:@me" }) : null,
3108
+ searchQuery ? /* @__PURE__ */ jsxs4(Text4, { color: "yellow", children: [
3027
3109
  ' filter:"',
3028
3110
  searchQuery,
3029
3111
  '"'
@@ -3037,9 +3119,9 @@ var init_hint_bar = __esm({
3037
3119
  });
3038
3120
 
3039
3121
  // src/board/components/bulk-action-menu.tsx
3040
- import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
3041
- import { useState as useState6 } from "react";
3042
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3122
+ import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
3123
+ import { useState as useState7 } from "react";
3124
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
3043
3125
  function getMenuItems(selectionType) {
3044
3126
  if (selectionType === "github") {
3045
3127
  return [
@@ -3058,7 +3140,7 @@ function getMenuItems(selectionType) {
3058
3140
  }
3059
3141
  function BulkActionMenu({ count, selectionType, onSelect, onCancel }) {
3060
3142
  const items = getMenuItems(selectionType);
3061
- const [selectedIdx, setSelectedIdx] = useState6(0);
3143
+ const [selectedIdx, setSelectedIdx] = useState7(0);
3062
3144
  useInput2((input2, key) => {
3063
3145
  if (key.escape) return onCancel();
3064
3146
  if (key.return) {
@@ -3074,13 +3156,13 @@ function BulkActionMenu({ count, selectionType, onSelect, onCancel }) {
3074
3156
  }
3075
3157
  });
3076
3158
  if (items.length === 0) {
3077
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
3078
- /* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "No bulk actions for mixed selection types." }),
3079
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Esc to cancel" })
3159
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
3160
+ /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "No bulk actions for mixed selection types." }),
3161
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Esc to cancel" })
3080
3162
  ] });
3081
3163
  }
3082
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
3083
- /* @__PURE__ */ jsxs4(Text4, { color: "cyan", bold: true, children: [
3164
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
3165
+ /* @__PURE__ */ jsxs5(Text5, { color: "cyan", bold: true, children: [
3084
3166
  "Bulk action (",
3085
3167
  count,
3086
3168
  " selected):"
@@ -3088,12 +3170,12 @@ function BulkActionMenu({ count, selectionType, onSelect, onCancel }) {
3088
3170
  items.map((item, i) => {
3089
3171
  const isSelected = i === selectedIdx;
3090
3172
  const prefix = isSelected ? "> " : " ";
3091
- return /* @__PURE__ */ jsxs4(Text4, { ...isSelected ? { color: "cyan" } : {}, children: [
3173
+ return /* @__PURE__ */ jsxs5(Text5, { ...isSelected ? { color: "cyan" } : {}, children: [
3092
3174
  prefix,
3093
3175
  item.label
3094
3176
  ] }, item.action.type);
3095
3177
  }),
3096
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "j/k:navigate Enter:select Esc:cancel" })
3178
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "j/k:navigate Enter:select Esc:cancel" })
3097
3179
  ] });
3098
3180
  }
3099
3181
  var init_bulk_action_menu = __esm({
@@ -3123,9 +3205,9 @@ import { mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as wr
3123
3205
  import { tmpdir } from "os";
3124
3206
  import { join as join4 } from "path";
3125
3207
  import { TextInput } from "@inkjs/ui";
3126
- import { Box as Box5, Text as Text5, useInput as useInput3, useStdin } from "ink";
3127
- import { useEffect as useEffect4, useRef as useRef7, useState as useState7 } from "react";
3128
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
3208
+ import { Box as Box6, Text as Text6, useInput as useInput3, useStdin } from "ink";
3209
+ import { useEffect as useEffect4, useRef as useRef7, useState as useState8 } from "react";
3210
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
3129
3211
  function CommentInput({
3130
3212
  issueNumber,
3131
3213
  onSubmit,
@@ -3133,8 +3215,8 @@ function CommentInput({
3133
3215
  onPauseRefresh,
3134
3216
  onResumeRefresh
3135
3217
  }) {
3136
- const [value, setValue] = useState7("");
3137
- const [editing, setEditing] = useState7(false);
3218
+ const [value, setValue] = useState8("");
3219
+ const [editing, setEditing] = useState8(false);
3138
3220
  const { setRawMode } = useStdin();
3139
3221
  const onSubmitRef = useRef7(onSubmit);
3140
3222
  const onCancelRef = useRef7(onCancel);
@@ -3192,19 +3274,19 @@ function CommentInput({
3192
3274
  }
3193
3275
  }, [editing, value, setRawMode]);
3194
3276
  if (editing) {
3195
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
3277
+ return /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsxs6(Text6, { color: "cyan", children: [
3196
3278
  "Opening editor for #",
3197
3279
  issueNumber,
3198
3280
  "\u2026"
3199
3281
  ] }) });
3200
3282
  }
3201
- return /* @__PURE__ */ jsxs5(Box5, { children: [
3202
- /* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
3283
+ return /* @__PURE__ */ jsxs6(Box6, { children: [
3284
+ /* @__PURE__ */ jsxs6(Text6, { color: "cyan", children: [
3203
3285
  "comment #",
3204
3286
  issueNumber,
3205
3287
  ": "
3206
3288
  ] }),
3207
- /* @__PURE__ */ jsx5(
3289
+ /* @__PURE__ */ jsx6(
3208
3290
  TextInput,
3209
3291
  {
3210
3292
  defaultValue: value,
@@ -3226,16 +3308,16 @@ var init_comment_input = __esm({
3226
3308
  });
3227
3309
 
3228
3310
  // src/board/components/confirm-prompt.tsx
3229
- import { Box as Box6, Text as Text6, useInput as useInput4 } from "ink";
3230
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
3311
+ import { Box as Box7, Text as Text7, useInput as useInput4 } from "ink";
3312
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
3231
3313
  function ConfirmPrompt({ message, onConfirm, onCancel }) {
3232
3314
  useInput4((input2, key) => {
3233
3315
  if (input2 === "y" || input2 === "Y") return onConfirm();
3234
3316
  if (input2 === "n" || input2 === "N" || key.escape) return onCancel();
3235
3317
  });
3236
- return /* @__PURE__ */ jsxs6(Box6, { children: [
3237
- /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: message }),
3238
- /* @__PURE__ */ jsx6(Text6, { color: "gray", children: " (y/n)" })
3318
+ return /* @__PURE__ */ jsxs7(Box7, { children: [
3319
+ /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: message }),
3320
+ /* @__PURE__ */ jsx7(Text7, { color: "gray", children: " (y/n)" })
3239
3321
  ] });
3240
3322
  }
3241
3323
  var init_confirm_prompt = __esm({
@@ -3246,9 +3328,9 @@ var init_confirm_prompt = __esm({
3246
3328
 
3247
3329
  // src/board/components/label-picker.tsx
3248
3330
  import { Spinner } from "@inkjs/ui";
3249
- import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
3250
- import { useEffect as useEffect5, useRef as useRef8, useState as useState8 } from "react";
3251
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
3331
+ import { Box as Box8, Text as Text8, useInput as useInput5 } from "ink";
3332
+ import { useEffect as useEffect5, useRef as useRef8, useState as useState9 } from "react";
3333
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
3252
3334
  function LabelPicker({
3253
3335
  repo,
3254
3336
  currentLabels,
@@ -3257,11 +3339,11 @@ function LabelPicker({
3257
3339
  onCancel,
3258
3340
  onError
3259
3341
  }) {
3260
- const [labels, setLabels] = useState8(labelCache[repo] ?? null);
3261
- const [loading, setLoading] = useState8(labels === null);
3262
- const [fetchAttempted, setFetchAttempted] = useState8(false);
3263
- const [selected, setSelected] = useState8(new Set(currentLabels));
3264
- const [cursor, setCursor] = useState8(0);
3342
+ const [labels, setLabels] = useState9(labelCache[repo] ?? null);
3343
+ const [loading, setLoading] = useState9(labels === null);
3344
+ const [fetchAttempted, setFetchAttempted] = useState9(false);
3345
+ const [selected, setSelected] = useState9(new Set(currentLabels));
3346
+ const [cursor, setCursor] = useState9(0);
3265
3347
  const submittedRef = useRef8(false);
3266
3348
  useEffect5(() => {
3267
3349
  if (labels !== null || fetchAttempted) return;
@@ -3323,21 +3405,21 @@ function LabelPicker({
3323
3405
  }
3324
3406
  });
3325
3407
  if (loading) {
3326
- return /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Spinner, { label: "Fetching labels..." }) });
3408
+ return /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Spinner, { label: "Fetching labels..." }) });
3327
3409
  }
3328
3410
  const allLabels = labels ?? [];
3329
3411
  if (allLabels.length === 0) {
3330
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
3331
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: true, children: "Labels:" }),
3332
- /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "No labels in this repo" }),
3333
- /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Esc:cancel" })
3412
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3413
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "Labels:" }),
3414
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "No labels in this repo" }),
3415
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Esc:cancel" })
3334
3416
  ] });
3335
3417
  }
3336
3418
  const repoLabelNames = new Set(allLabels.map((l) => l.name));
3337
3419
  const orphanedLabels = currentLabels.filter((l) => !repoLabelNames.has(l));
3338
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
3339
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: true, children: "Labels (Space:toggle Enter:confirm Esc:cancel):" }),
3340
- orphanedLabels.map((name) => /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
3420
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3421
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "Labels (Space:toggle Enter:confirm Esc:cancel):" }),
3422
+ orphanedLabels.map((name) => /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
3341
3423
  selected.has(name) ? "[x]" : "[ ]",
3342
3424
  " ",
3343
3425
  name,
@@ -3346,7 +3428,7 @@ function LabelPicker({
3346
3428
  allLabels.map((label, i) => {
3347
3429
  const isSel = i === cursor;
3348
3430
  const isChecked = selected.has(label.name);
3349
- return /* @__PURE__ */ jsxs7(Text7, { ...isSel ? { color: "cyan" } : {}, children: [
3431
+ return /* @__PURE__ */ jsxs8(Text8, { ...isSel ? { color: "cyan" } : {}, children: [
3350
3432
  isSel ? ">" : " ",
3351
3433
  " ",
3352
3434
  isChecked ? "[x]" : "[ ]",
@@ -3365,9 +3447,9 @@ var init_label_picker = __esm({
3365
3447
 
3366
3448
  // src/board/components/create-issue-form.tsx
3367
3449
  import { TextInput as TextInput2 } from "@inkjs/ui";
3368
- import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
3369
- import { useState as useState9 } from "react";
3370
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
3450
+ import { Box as Box9, Text as Text9, useInput as useInput6 } from "ink";
3451
+ import { useState as useState10 } from "react";
3452
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
3371
3453
  function CreateIssueForm({
3372
3454
  repos,
3373
3455
  defaultRepo,
@@ -3379,9 +3461,9 @@ function CreateIssueForm({
3379
3461
  0,
3380
3462
  repos.findIndex((r) => r.name === defaultRepo)
3381
3463
  ) : 0;
3382
- const [repoIdx, setRepoIdx] = useState9(defaultRepoIdx);
3383
- const [title, setTitle] = useState9("");
3384
- const [field, setField] = useState9("title");
3464
+ const [repoIdx, setRepoIdx] = useState10(defaultRepoIdx);
3465
+ const [title, setTitle] = useState10("");
3466
+ const [field, setField] = useState10("title");
3385
3467
  useInput6((input2, key) => {
3386
3468
  if (field === "labels") return;
3387
3469
  if (key.escape) return onCancel();
@@ -3398,15 +3480,15 @@ function CreateIssueForm({
3398
3480
  });
3399
3481
  const selectedRepo = repos[repoIdx];
3400
3482
  if (field === "labels" && selectedRepo) {
3401
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3402
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "Create Issue \u2014 Add Labels (optional)" }),
3403
- /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
3483
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
3484
+ /* @__PURE__ */ jsx9(Text9, { color: "cyan", bold: true, children: "Create Issue \u2014 Add Labels (optional)" }),
3485
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
3404
3486
  "Repo: ",
3405
3487
  selectedRepo.shortName,
3406
3488
  " Title: ",
3407
3489
  title
3408
3490
  ] }),
3409
- /* @__PURE__ */ jsx8(
3491
+ /* @__PURE__ */ jsx9(
3410
3492
  LabelPicker,
3411
3493
  {
3412
3494
  repo: selectedRepo.name,
@@ -3431,12 +3513,12 @@ function CreateIssueForm({
3431
3513
  )
3432
3514
  ] });
3433
3515
  }
3434
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3435
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "Create Issue" }),
3436
- /* @__PURE__ */ jsxs8(Box8, { children: [
3437
- /* @__PURE__ */ jsx8(Text8, { dimColor: field !== "repo", children: "Repo: " }),
3438
- repos.map((r, i) => /* @__PURE__ */ jsx8(
3439
- Text8,
3516
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
3517
+ /* @__PURE__ */ jsx9(Text9, { color: "cyan", bold: true, children: "Create Issue" }),
3518
+ /* @__PURE__ */ jsxs9(Box9, { children: [
3519
+ /* @__PURE__ */ jsx9(Text9, { dimColor: field !== "repo", children: "Repo: " }),
3520
+ repos.map((r, i) => /* @__PURE__ */ jsx9(
3521
+ Text9,
3440
3522
  {
3441
3523
  ...i === repoIdx ? { color: "cyan", bold: true } : {},
3442
3524
  dimColor: field !== "repo",
@@ -3444,11 +3526,11 @@ function CreateIssueForm({
3444
3526
  },
3445
3527
  r.name
3446
3528
  )),
3447
- field === "repo" ? /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " j/k:select Tab:next" }) : null
3529
+ field === "repo" ? /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " j/k:select Tab:next" }) : null
3448
3530
  ] }),
3449
- /* @__PURE__ */ jsxs8(Box8, { children: [
3450
- /* @__PURE__ */ jsx8(Text8, { dimColor: field !== "title", children: "Title: " }),
3451
- field === "title" ? /* @__PURE__ */ jsx8(
3531
+ /* @__PURE__ */ jsxs9(Box9, { children: [
3532
+ /* @__PURE__ */ jsx9(Text9, { dimColor: field !== "title", children: "Title: " }),
3533
+ field === "title" ? /* @__PURE__ */ jsx9(
3452
3534
  TextInput2,
3453
3535
  {
3454
3536
  defaultValue: title,
@@ -3465,9 +3547,9 @@ function CreateIssueForm({
3465
3547
  }
3466
3548
  }
3467
3549
  }
3468
- ) : /* @__PURE__ */ jsx8(Text8, { children: title || "(empty)" })
3550
+ ) : /* @__PURE__ */ jsx9(Text9, { children: title || "(empty)" })
3469
3551
  ] }),
3470
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Tab:switch fields Enter:next Esc:cancel" })
3552
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Tab:switch fields Enter:next Esc:cancel" })
3471
3553
  ] });
3472
3554
  }
3473
3555
  var init_create_issue_form = __esm({
@@ -3482,9 +3564,9 @@ import { spawnSync as spawnSync2 } from "child_process";
3482
3564
  import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
3483
3565
  import { tmpdir as tmpdir2 } from "os";
3484
3566
  import { join as join5 } from "path";
3485
- import { Box as Box9, Text as Text9, useStdin as useStdin2 } from "ink";
3486
- import { useEffect as useEffect6, useRef as useRef9, useState as useState10 } from "react";
3487
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
3567
+ import { Box as Box10, Text as Text10, useStdin as useStdin2 } from "ink";
3568
+ import { useEffect as useEffect6, useRef as useRef9, useState as useState11 } from "react";
3569
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3488
3570
  function buildEditorFile(issue, repoName, statusOptions, repoLabels) {
3489
3571
  const statusNames = statusOptions.map((o) => o.name).join(", ");
3490
3572
  const labelNames = repoLabels.map((l) => l.name).join(", ");
@@ -3567,7 +3649,7 @@ function EditIssueOverlay({
3567
3649
  onToastError,
3568
3650
  onPushEntry
3569
3651
  }) {
3570
- const [editing, setEditing] = useState10(true);
3652
+ const [editing, setEditing] = useState11(true);
3571
3653
  const { setRawMode } = useStdin2();
3572
3654
  const onDoneRef = useRef9(onDone);
3573
3655
  const onPauseRef = useRef9(onPauseRefresh);
@@ -3726,7 +3808,7 @@ function EditIssueOverlay({
3726
3808
  onPushEntry
3727
3809
  ]);
3728
3810
  if (!editing) return null;
3729
- return /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsxs9(Text9, { color: "cyan", children: [
3811
+ return /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsxs10(Text10, { color: "cyan", children: [
3730
3812
  "Opening editor for #",
3731
3813
  issue.number,
3732
3814
  "\u2026"
@@ -3742,17 +3824,17 @@ var init_edit_issue_overlay = __esm({
3742
3824
  });
3743
3825
 
3744
3826
  // src/board/components/focus-mode.tsx
3745
- import { Box as Box10, Text as Text10, useInput as useInput7 } from "ink";
3746
- import { useCallback as useCallback9, useEffect as useEffect7, useRef as useRef10, useState as useState11 } from "react";
3747
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3827
+ import { Box as Box11, Text as Text11, useInput as useInput7 } from "ink";
3828
+ import { useCallback as useCallback10, useEffect as useEffect7, useRef as useRef10, useState as useState12 } from "react";
3829
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
3748
3830
  function formatTime(secs) {
3749
3831
  const m = Math.floor(secs / 60);
3750
3832
  const s = secs % 60;
3751
3833
  return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
3752
3834
  }
3753
3835
  function FocusMode({ label, durationSec, onExit, onEndAction }) {
3754
- const [remaining, setRemaining] = useState11(durationSec);
3755
- const [timerDone, setTimerDone] = useState11(false);
3836
+ const [remaining, setRemaining] = useState12(durationSec);
3837
+ const [timerDone, setTimerDone] = useState12(false);
3756
3838
  const bellSentRef = useRef10(false);
3757
3839
  useEffect7(() => {
3758
3840
  if (timerDone) return;
@@ -3774,7 +3856,7 @@ function FocusMode({ label, durationSec, onExit, onEndAction }) {
3774
3856
  process.stdout.write("\x07");
3775
3857
  }
3776
3858
  }, [timerDone]);
3777
- const handleInput = useCallback9(
3859
+ const handleInput = useCallback10(
3778
3860
  (input2, key) => {
3779
3861
  if (key.escape) {
3780
3862
  if (timerDone) {
@@ -3801,23 +3883,23 @@ function FocusMode({ label, durationSec, onExit, onEndAction }) {
3801
3883
  );
3802
3884
  useInput7(handleInput);
3803
3885
  if (timerDone) {
3804
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
3805
- /* @__PURE__ */ jsxs10(Box10, { children: [
3806
- /* @__PURE__ */ jsx10(Text10, { color: "green", bold: true, children: "Focus complete!" }),
3807
- /* @__PURE__ */ jsxs10(Text10, { color: "gray", children: [
3886
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
3887
+ /* @__PURE__ */ jsxs11(Box11, { children: [
3888
+ /* @__PURE__ */ jsx11(Text11, { color: "green", bold: true, children: "Focus complete!" }),
3889
+ /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
3808
3890
  " ",
3809
3891
  label
3810
3892
  ] })
3811
3893
  ] }),
3812
- /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
3813
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "[c]" }),
3814
- /* @__PURE__ */ jsx10(Text10, { children: " Continue " }),
3815
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "[b]" }),
3816
- /* @__PURE__ */ jsx10(Text10, { children: " Break " }),
3817
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "[d]" }),
3818
- /* @__PURE__ */ jsx10(Text10, { children: " Done " }),
3819
- /* @__PURE__ */ jsx10(Text10, { color: "gray", children: "[Esc]" }),
3820
- /* @__PURE__ */ jsx10(Text10, { children: " Exit" })
3894
+ /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
3895
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "[c]" }),
3896
+ /* @__PURE__ */ jsx11(Text11, { children: " Continue " }),
3897
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "[b]" }),
3898
+ /* @__PURE__ */ jsx11(Text11, { children: " Break " }),
3899
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "[d]" }),
3900
+ /* @__PURE__ */ jsx11(Text11, { children: " Done " }),
3901
+ /* @__PURE__ */ jsx11(Text11, { color: "gray", children: "[Esc]" }),
3902
+ /* @__PURE__ */ jsx11(Text11, { children: " Exit" })
3821
3903
  ] })
3822
3904
  ] });
3823
3905
  }
@@ -3825,21 +3907,21 @@ function FocusMode({ label, durationSec, onExit, onEndAction }) {
3825
3907
  const barWidth = 20;
3826
3908
  const filled = Math.round(progress * barWidth);
3827
3909
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
3828
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
3829
- /* @__PURE__ */ jsxs10(Box10, { children: [
3830
- /* @__PURE__ */ jsxs10(Text10, { color: "magenta", bold: true, children: [
3910
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
3911
+ /* @__PURE__ */ jsxs11(Box11, { children: [
3912
+ /* @__PURE__ */ jsxs11(Text11, { color: "magenta", bold: true, children: [
3831
3913
  "Focus:",
3832
3914
  " "
3833
3915
  ] }),
3834
- /* @__PURE__ */ jsx10(Text10, { children: label })
3916
+ /* @__PURE__ */ jsx11(Text11, { children: label })
3835
3917
  ] }),
3836
- /* @__PURE__ */ jsxs10(Box10, { children: [
3837
- /* @__PURE__ */ jsx10(Text10, { color: "magenta", children: bar }),
3838
- /* @__PURE__ */ jsx10(Text10, { children: " " }),
3839
- /* @__PURE__ */ jsx10(Text10, { bold: true, children: formatTime(remaining) }),
3840
- /* @__PURE__ */ jsx10(Text10, { color: "gray", children: " remaining" })
3918
+ /* @__PURE__ */ jsxs11(Box11, { children: [
3919
+ /* @__PURE__ */ jsx11(Text11, { color: "magenta", children: bar }),
3920
+ /* @__PURE__ */ jsx11(Text11, { children: " " }),
3921
+ /* @__PURE__ */ jsx11(Text11, { bold: true, children: formatTime(remaining) }),
3922
+ /* @__PURE__ */ jsx11(Text11, { color: "gray", children: " remaining" })
3841
3923
  ] }),
3842
- /* @__PURE__ */ jsx10(Text10, { color: "gray", dimColor: true, children: "Esc to exit focus" })
3924
+ /* @__PURE__ */ jsx11(Text11, { color: "gray", dimColor: true, children: "Esc to exit focus" })
3843
3925
  ] });
3844
3926
  }
3845
3927
  var init_focus_mode = __esm({
@@ -3851,18 +3933,18 @@ var init_focus_mode = __esm({
3851
3933
  // src/board/components/fuzzy-picker.tsx
3852
3934
  import { TextInput as TextInput3 } from "@inkjs/ui";
3853
3935
  import { Fzf } from "fzf";
3854
- import { Box as Box11, Text as Text11, useInput as useInput8 } from "ink";
3855
- import { useMemo as useMemo2, useState as useState12 } from "react";
3856
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
3936
+ import { Box as Box12, Text as Text12, useInput as useInput8 } from "ink";
3937
+ import { useMemo as useMemo2, useState as useState13 } from "react";
3938
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
3857
3939
  function keepCursorVisible(cursor, offset, visible) {
3858
3940
  if (cursor < offset) return cursor;
3859
3941
  if (cursor >= offset + visible) return cursor - visible + 1;
3860
3942
  return offset;
3861
3943
  }
3862
3944
  function FuzzyPicker({ repos, onSelect, onClose }) {
3863
- const [query, setQuery] = useState12("");
3864
- const [cursor, setCursor] = useState12(0);
3865
- const [scrollOffset, setScrollOffset] = useState12(0);
3945
+ const [query, setQuery] = useState13("");
3946
+ const [cursor, setCursor] = useState13(0);
3947
+ const [scrollOffset, setScrollOffset] = useState13(0);
3866
3948
  const allIssues = useMemo2(() => {
3867
3949
  const items = [];
3868
3950
  for (const rd of repos) {
@@ -3945,13 +4027,13 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3945
4027
  });
3946
4028
  const visibleResults = results.slice(scrollOffset, scrollOffset + VISIBLE);
3947
4029
  const totalCount = results.length;
3948
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
3949
- /* @__PURE__ */ jsxs11(Box11, { children: [
3950
- /* @__PURE__ */ jsxs11(Text11, { color: "cyan", bold: true, children: [
4030
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4031
+ /* @__PURE__ */ jsxs12(Box12, { children: [
4032
+ /* @__PURE__ */ jsxs12(Text12, { color: "cyan", bold: true, children: [
3951
4033
  "Find issue",
3952
4034
  " "
3953
4035
  ] }),
3954
- /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
4036
+ /* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
3955
4037
  "(",
3956
4038
  totalCount,
3957
4039
  " match",
@@ -3959,14 +4041,14 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3959
4041
  ")",
3960
4042
  " "
3961
4043
  ] }),
3962
- /* @__PURE__ */ jsx11(Text11, { color: "gray", dimColor: true, children: "\u2191\u2193/Ctrl-J/K nav Enter:jump Esc:close" })
4044
+ /* @__PURE__ */ jsx12(Text12, { color: "gray", dimColor: true, children: "\u2191\u2193/Ctrl-J/K nav Enter:jump Esc:close" })
3963
4045
  ] }),
3964
- /* @__PURE__ */ jsxs11(Box11, { children: [
3965
- /* @__PURE__ */ jsxs11(Text11, { color: "yellow", children: [
4046
+ /* @__PURE__ */ jsxs12(Box12, { children: [
4047
+ /* @__PURE__ */ jsxs12(Text12, { color: "yellow", children: [
3966
4048
  ">",
3967
4049
  " "
3968
4050
  ] }),
3969
- /* @__PURE__ */ jsx11(
4051
+ /* @__PURE__ */ jsx12(
3970
4052
  TextInput3,
3971
4053
  {
3972
4054
  defaultValue: query,
@@ -3983,7 +4065,7 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3983
4065
  }
3984
4066
  )
3985
4067
  ] }),
3986
- scrollOffset > 0 ? /* @__PURE__ */ jsxs11(Text11, { color: "gray", dimColor: true, children: [
4068
+ scrollOffset > 0 ? /* @__PURE__ */ jsxs12(Text12, { color: "gray", dimColor: true, children: [
3987
4069
  "\u25B2 ",
3988
4070
  scrollOffset,
3989
4071
  " more above"
@@ -3992,7 +4074,7 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3992
4074
  const isSelected = scrollOffset + idx === cursor;
3993
4075
  const labelStr = issue.labels ? ` [${issue.labels.split(" ").slice(0, 2).join("] [")}]` : "";
3994
4076
  const assigneeStr = issue.assignee ? ` @${issue.assignee.split(" ")[0]}` : "";
3995
- return /* @__PURE__ */ jsx11(Box11, { children: isSelected ? /* @__PURE__ */ jsxs11(Text11, { color: "cyan", bold: true, children: [
4077
+ return /* @__PURE__ */ jsx12(Box12, { children: isSelected ? /* @__PURE__ */ jsxs12(Text12, { color: "cyan", bold: true, children: [
3996
4078
  ">",
3997
4079
  " ",
3998
4080
  issue.repoShortName,
@@ -4002,7 +4084,7 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
4002
4084
  issue.title,
4003
4085
  labelStr,
4004
4086
  assigneeStr
4005
- ] }) : /* @__PURE__ */ jsxs11(Text11, { children: [
4087
+ ] }) : /* @__PURE__ */ jsxs12(Text12, { children: [
4006
4088
  " ",
4007
4089
  issue.repoShortName,
4008
4090
  "#",
@@ -4013,12 +4095,12 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
4013
4095
  assigneeStr
4014
4096
  ] }) }, issue.navId);
4015
4097
  }),
4016
- totalCount === 0 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
4098
+ totalCount === 0 ? /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
4017
4099
  'No issues match "',
4018
4100
  query,
4019
4101
  '"'
4020
4102
  ] }) : null,
4021
- results.length > scrollOffset + VISIBLE ? /* @__PURE__ */ jsxs11(Text11, { color: "gray", dimColor: true, children: [
4103
+ results.length > scrollOffset + VISIBLE ? /* @__PURE__ */ jsxs12(Text12, { color: "gray", dimColor: true, children: [
4022
4104
  "\u25BC ",
4023
4105
  results.length - scrollOffset - VISIBLE,
4024
4106
  " more below"
@@ -4032,29 +4114,29 @@ var init_fuzzy_picker = __esm({
4032
4114
  });
4033
4115
 
4034
4116
  // src/board/components/help-overlay.tsx
4035
- import { Box as Box12, Text as Text12, useInput as useInput9 } from "ink";
4036
- import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
4117
+ import { Box as Box13, Text as Text13, useInput as useInput9 } from "ink";
4118
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
4037
4119
  function HelpOverlay({ currentMode, onClose }) {
4038
4120
  useInput9((_input, key) => {
4039
4121
  if (key.escape) onClose();
4040
4122
  });
4041
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4042
- /* @__PURE__ */ jsxs12(Box12, { justifyContent: "space-between", children: [
4043
- /* @__PURE__ */ jsx12(Text12, { color: "cyan", bold: true, children: "Keyboard Shortcuts" }),
4044
- /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
4123
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4124
+ /* @__PURE__ */ jsxs13(Box13, { justifyContent: "space-between", children: [
4125
+ /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "Keyboard Shortcuts" }),
4126
+ /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
4045
4127
  "mode: ",
4046
4128
  currentMode
4047
4129
  ] })
4048
4130
  ] }),
4049
- /* @__PURE__ */ jsx12(Text12, { children: " " }),
4050
- SHORTCUTS.map((group) => /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
4051
- /* @__PURE__ */ jsx12(Text12, { color: "yellow", bold: true, children: group.category }),
4052
- group.items.map((item) => /* @__PURE__ */ jsxs12(Box12, { children: [
4053
- /* @__PURE__ */ jsx12(Box12, { width: 16, children: /* @__PURE__ */ jsx12(Text12, { color: "green", children: item.key }) }),
4054
- /* @__PURE__ */ jsx12(Text12, { children: item.desc })
4131
+ /* @__PURE__ */ jsx13(Text13, { children: " " }),
4132
+ SHORTCUTS.map((group) => /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
4133
+ /* @__PURE__ */ jsx13(Text13, { color: "yellow", bold: true, children: group.category }),
4134
+ group.items.map((item) => /* @__PURE__ */ jsxs13(Box13, { children: [
4135
+ /* @__PURE__ */ jsx13(Box13, { width: 16, children: /* @__PURE__ */ jsx13(Text13, { color: "green", children: item.key }) }),
4136
+ /* @__PURE__ */ jsx13(Text13, { children: item.desc })
4055
4137
  ] }, item.key))
4056
4138
  ] }, group.category)),
4057
- /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Press ? or Esc to close" })
4139
+ /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Press ? or Esc to close" })
4058
4140
  ] });
4059
4141
  }
4060
4142
  var SHORTCUTS;
@@ -4067,9 +4149,10 @@ var init_help_overlay = __esm({
4067
4149
  items: [
4068
4150
  { key: "j / Down", desc: "Move down" },
4069
4151
  { key: "k / Up", desc: "Move up" },
4070
- { key: "Tab", desc: "Next tab" },
4071
- { key: "Shift+Tab", desc: "Previous tab" },
4072
- { key: "1-9", desc: "Jump to tab by number" }
4152
+ { key: "Tab", desc: "Next repo tab" },
4153
+ { key: "Shift+Tab", desc: "Previous repo tab" },
4154
+ { key: "1-9", desc: "Jump to repo tab by number" },
4155
+ { key: "s / S", desc: "Next / prev status tab" }
4073
4156
  ]
4074
4157
  },
4075
4158
  {
@@ -4094,7 +4177,7 @@ var init_help_overlay = __esm({
4094
4177
  { key: "c", desc: "Comment on issue" },
4095
4178
  { key: "m", desc: "Move status" },
4096
4179
  { key: "e", desc: "Edit issue in $EDITOR" },
4097
- { key: "s", desc: "Open Slack thread" },
4180
+ { key: "o", desc: "Open Slack thread" },
4098
4181
  { key: "y", desc: "Copy issue link to clipboard" },
4099
4182
  { key: "n", desc: "Create new issue" },
4100
4183
  { key: "I", desc: "Natural-language issue create" },
@@ -4119,9 +4202,9 @@ import { mkdtempSync as mkdtempSync3, readFileSync as readFileSync6, rmSync as r
4119
4202
  import { tmpdir as tmpdir3 } from "os";
4120
4203
  import { join as join6 } from "path";
4121
4204
  import { Spinner as Spinner2, TextInput as TextInput4 } from "@inkjs/ui";
4122
- import { Box as Box13, Text as Text13, useInput as useInput10, useStdin as useStdin3 } from "ink";
4123
- import { useCallback as useCallback10, useEffect as useEffect8, useRef as useRef11, useState as useState13 } from "react";
4124
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
4205
+ import { Box as Box14, Text as Text14, useInput as useInput10, useStdin as useStdin3 } from "ink";
4206
+ import { useCallback as useCallback11, useEffect as useEffect8, useRef as useRef11, useState as useState14 } from "react";
4207
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
4125
4208
  function NlCreateOverlay({
4126
4209
  repos,
4127
4210
  defaultRepoName,
@@ -4132,13 +4215,13 @@ function NlCreateOverlay({
4132
4215
  onResumeRefresh,
4133
4216
  onLlmFallback
4134
4217
  }) {
4135
- const [, setInput] = useState13("");
4136
- const [isParsing, setIsParsing] = useState13(false);
4137
- const [parsed, setParsed] = useState13(null);
4138
- const [parseError, setParseError] = useState13(null);
4139
- const [step, setStep] = useState13("input");
4140
- const [body, setBody] = useState13("");
4141
- const [editingBody, setEditingBody] = useState13(false);
4218
+ const [, setInput] = useState14("");
4219
+ const [isParsing, setIsParsing] = useState14(false);
4220
+ const [parsed, setParsed] = useState14(null);
4221
+ const [parseError, setParseError] = useState14(null);
4222
+ const [step, setStep] = useState14("input");
4223
+ const [body, setBody] = useState14("");
4224
+ const [editingBody, setEditingBody] = useState14(false);
4142
4225
  const submittedRef = useRef11(false);
4143
4226
  const parseParamsRef = useRef11(null);
4144
4227
  const onSubmitRef = useRef11(onSubmit);
@@ -4154,7 +4237,7 @@ function NlCreateOverlay({
4154
4237
  0,
4155
4238
  repos.findIndex((r) => r.name === defaultRepoName)
4156
4239
  ) : 0;
4157
- const [repoIdx, setRepoIdx] = useState13(defaultRepoIdx);
4240
+ const [repoIdx, setRepoIdx] = useState14(defaultRepoIdx);
4158
4241
  const selectedRepo = repos[repoIdx];
4159
4242
  useInput10((inputChar, key) => {
4160
4243
  if (isParsing || editingBody) return;
@@ -4214,7 +4297,7 @@ function NlCreateOverlay({
4214
4297
  setEditingBody(false);
4215
4298
  }
4216
4299
  }, [editingBody, body, setRawMode]);
4217
- const handleInputSubmit = useCallback10(
4300
+ const handleInputSubmit = useCallback11(
4218
4301
  (text) => {
4219
4302
  const trimmed = text.trim();
4220
4303
  if (!trimmed) return;
@@ -4247,27 +4330,27 @@ function NlCreateOverlay({
4247
4330
  });
4248
4331
  }, [isParsing, onLlmFallback]);
4249
4332
  if (isParsing) {
4250
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4251
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4252
- /* @__PURE__ */ jsx13(Spinner2, { label: "Parsing..." })
4333
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
4334
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4335
+ /* @__PURE__ */ jsx14(Spinner2, { label: "Parsing..." })
4253
4336
  ] });
4254
4337
  }
4255
4338
  if (parsed && step === "body") {
4256
4339
  if (editingBody) {
4257
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4258
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4259
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: "Opening editor for body\u2026" })
4340
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
4341
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4342
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: "Opening editor for body\u2026" })
4260
4343
  ] });
4261
4344
  }
4262
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4263
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4264
- /* @__PURE__ */ jsxs13(Box13, { children: [
4265
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Title: " }),
4266
- /* @__PURE__ */ jsx13(Text13, { children: parsed.title })
4345
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
4346
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4347
+ /* @__PURE__ */ jsxs14(Box14, { children: [
4348
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Title: " }),
4349
+ /* @__PURE__ */ jsx14(Text14, { children: parsed.title })
4267
4350
  ] }),
4268
- /* @__PURE__ */ jsxs13(Box13, { children: [
4269
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: "body: " }),
4270
- /* @__PURE__ */ jsx13(
4351
+ /* @__PURE__ */ jsxs14(Box14, { children: [
4352
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: "body: " }),
4353
+ /* @__PURE__ */ jsx14(
4271
4354
  TextInput4,
4272
4355
  {
4273
4356
  defaultValue: body,
@@ -4289,45 +4372,45 @@ function NlCreateOverlay({
4289
4372
  }
4290
4373
  )
4291
4374
  ] }),
4292
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Enter:create ctrl+e:editor Esc:back" })
4375
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Enter:create ctrl+e:editor Esc:back" })
4293
4376
  ] });
4294
4377
  }
4295
4378
  if (parsed) {
4296
4379
  const labels = [...parsed.labels];
4297
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4298
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4299
- /* @__PURE__ */ jsxs13(Box13, { children: [
4300
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Repo: " }),
4301
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: selectedRepo?.shortName ?? "(none)" }),
4302
- repos.length > 1 ? /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " r:cycle" }) : null
4380
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
4381
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4382
+ /* @__PURE__ */ jsxs14(Box14, { children: [
4383
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Repo: " }),
4384
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: selectedRepo?.shortName ?? "(none)" }),
4385
+ repos.length > 1 ? /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: " r:cycle" }) : null
4303
4386
  ] }),
4304
- /* @__PURE__ */ jsxs13(Box13, { children: [
4305
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Title: " }),
4306
- /* @__PURE__ */ jsx13(Text13, { children: parsed.title })
4387
+ /* @__PURE__ */ jsxs14(Box14, { children: [
4388
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Title: " }),
4389
+ /* @__PURE__ */ jsx14(Text14, { children: parsed.title })
4307
4390
  ] }),
4308
- labels.length > 0 ? /* @__PURE__ */ jsxs13(Box13, { children: [
4309
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Labels: " }),
4310
- /* @__PURE__ */ jsx13(Text13, { children: labels.join(", ") })
4391
+ labels.length > 0 ? /* @__PURE__ */ jsxs14(Box14, { children: [
4392
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Labels: " }),
4393
+ /* @__PURE__ */ jsx14(Text14, { children: labels.join(", ") })
4311
4394
  ] }) : null,
4312
- parsed.assignee ? /* @__PURE__ */ jsxs13(Box13, { children: [
4313
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Assignee: " }),
4314
- /* @__PURE__ */ jsxs13(Text13, { children: [
4395
+ parsed.assignee ? /* @__PURE__ */ jsxs14(Box14, { children: [
4396
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Assignee: " }),
4397
+ /* @__PURE__ */ jsxs14(Text14, { children: [
4315
4398
  "@",
4316
4399
  parsed.assignee
4317
4400
  ] })
4318
4401
  ] }) : null,
4319
- parsed.dueDate ? /* @__PURE__ */ jsxs13(Box13, { children: [
4320
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Due: " }),
4321
- /* @__PURE__ */ jsx13(Text13, { children: formatDue(parsed.dueDate) })
4402
+ parsed.dueDate ? /* @__PURE__ */ jsxs14(Box14, { children: [
4403
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Due: " }),
4404
+ /* @__PURE__ */ jsx14(Text14, { children: formatDue(parsed.dueDate) })
4322
4405
  ] }) : null,
4323
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Enter:add body Esc:cancel" })
4406
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Enter:add body Esc:cancel" })
4324
4407
  ] });
4325
4408
  }
4326
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4327
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 What do you need to do?" }),
4328
- /* @__PURE__ */ jsxs13(Box13, { children: [
4329
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: "> " }),
4330
- /* @__PURE__ */ jsx13(
4409
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
4410
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", bold: true, children: "\u2728 What do you need to do?" }),
4411
+ /* @__PURE__ */ jsxs14(Box14, { children: [
4412
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: "> " }),
4413
+ /* @__PURE__ */ jsx14(
4331
4414
  TextInput4,
4332
4415
  {
4333
4416
  placeholder: "fix login bug #bug #priority:high @me due friday",
@@ -4336,8 +4419,8 @@ function NlCreateOverlay({
4336
4419
  }
4337
4420
  )
4338
4421
  ] }),
4339
- parseError ? /* @__PURE__ */ jsx13(Text13, { color: "red", children: parseError }) : null,
4340
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Tip: #label @user due <date> Enter:parse Esc:cancel" })
4422
+ parseError ? /* @__PURE__ */ jsx14(Text14, { color: "red", children: parseError }) : null,
4423
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Tip: #label @user due <date> Enter:parse Esc:cancel" })
4341
4424
  ] });
4342
4425
  }
4343
4426
  function buildLabelList(parsed) {
@@ -4357,12 +4440,12 @@ var init_nl_create_overlay = __esm({
4357
4440
 
4358
4441
  // src/board/components/search-bar.tsx
4359
4442
  import { TextInput as TextInput5 } from "@inkjs/ui";
4360
- import { Box as Box14, Text as Text14 } from "ink";
4361
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
4443
+ import { Box as Box15, Text as Text15 } from "ink";
4444
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
4362
4445
  function SearchBar({ defaultValue, onChange, onSubmit }) {
4363
- return /* @__PURE__ */ jsxs14(Box14, { children: [
4364
- /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: "/" }),
4365
- /* @__PURE__ */ jsx14(
4446
+ return /* @__PURE__ */ jsxs15(Box15, { children: [
4447
+ /* @__PURE__ */ jsx15(Text15, { color: "yellow", children: "/" }),
4448
+ /* @__PURE__ */ jsx15(
4366
4449
  TextInput5,
4367
4450
  {
4368
4451
  defaultValue,
@@ -4380,9 +4463,9 @@ var init_search_bar = __esm({
4380
4463
  });
4381
4464
 
4382
4465
  // src/board/components/status-picker.tsx
4383
- import { Box as Box15, Text as Text15, useInput as useInput11 } from "ink";
4384
- import { useRef as useRef12, useState as useState14 } from "react";
4385
- import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
4466
+ import { Box as Box16, Text as Text16, useInput as useInput11 } from "ink";
4467
+ import { useRef as useRef12, useState as useState15 } from "react";
4468
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
4386
4469
  function isTerminal(name) {
4387
4470
  return TERMINAL_STATUS_RE.test(name);
4388
4471
  }
@@ -4429,11 +4512,11 @@ function StatusPicker({
4429
4512
  onCancel,
4430
4513
  showTerminalStatuses = true
4431
4514
  }) {
4432
- const [selectedIdx, setSelectedIdx] = useState14(() => {
4515
+ const [selectedIdx, setSelectedIdx] = useState15(() => {
4433
4516
  const idx = options.findIndex((o) => o.name === currentStatus);
4434
4517
  return idx >= 0 ? idx : 0;
4435
4518
  });
4436
- const [confirmingTerminal, setConfirmingTerminal] = useState14(false);
4519
+ const [confirmingTerminal, setConfirmingTerminal] = useState15(false);
4437
4520
  const submittedRef = useRef12(false);
4438
4521
  useInput11((input2, key) => {
4439
4522
  if (confirmingTerminal) {
@@ -4458,26 +4541,26 @@ function StatusPicker({
4458
4541
  });
4459
4542
  if (confirmingTerminal) {
4460
4543
  const opt = options[selectedIdx];
4461
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4462
- /* @__PURE__ */ jsxs15(Text15, { color: "yellow", bold: true, children: [
4544
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
4545
+ /* @__PURE__ */ jsxs16(Text16, { color: "yellow", bold: true, children: [
4463
4546
  "Mark as ",
4464
4547
  opt?.name,
4465
4548
  "?"
4466
4549
  ] }),
4467
- /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "This will close the issue on GitHub." }),
4468
- /* @__PURE__ */ jsx15(Text15, { children: "Continue? [y/n]" })
4550
+ /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "This will close the issue on GitHub." }),
4551
+ /* @__PURE__ */ jsx16(Text16, { children: "Continue? [y/n]" })
4469
4552
  ] });
4470
4553
  }
4471
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4472
- /* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: "Move to status:" }),
4554
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
4555
+ /* @__PURE__ */ jsx16(Text16, { color: "cyan", bold: true, children: "Move to status:" }),
4473
4556
  options.map((opt, i) => {
4474
4557
  const isCurrent = opt.name === currentStatus;
4475
4558
  const isSelected = i === selectedIdx;
4476
4559
  const terminal = isTerminal(opt.name) && showTerminalStatuses;
4477
4560
  const prefix = isSelected ? "> " : " ";
4478
4561
  const suffix = isCurrent ? " (current)" : terminal ? " (Done)" : "";
4479
- return /* @__PURE__ */ jsxs15(
4480
- Text15,
4562
+ return /* @__PURE__ */ jsxs16(
4563
+ Text16,
4481
4564
  {
4482
4565
  ...isSelected ? { color: "cyan" } : terminal ? { color: "yellow" } : {},
4483
4566
  dimColor: isCurrent,
@@ -4490,7 +4573,7 @@ function StatusPicker({
4490
4573
  opt.id
4491
4574
  );
4492
4575
  }),
4493
- /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "j/k:navigate Enter:select Esc:cancel" })
4576
+ /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "j/k:navigate Enter:select Esc:cancel" })
4494
4577
  ] });
4495
4578
  }
4496
4579
  var init_status_picker = __esm({
@@ -4501,7 +4584,7 @@ var init_status_picker = __esm({
4501
4584
  });
4502
4585
 
4503
4586
  // src/board/components/overlay-renderer.tsx
4504
- import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
4587
+ import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
4505
4588
  function OverlayRenderer({
4506
4589
  uiState,
4507
4590
  config: config2,
@@ -4542,9 +4625,9 @@ function OverlayRenderer({
4542
4625
  onPushEntry
4543
4626
  }) {
4544
4627
  const { mode, helpVisible } = uiState;
4545
- return /* @__PURE__ */ jsxs16(Fragment2, { children: [
4546
- helpVisible ? /* @__PURE__ */ jsx16(HelpOverlay, { currentMode: mode, onClose: onToggleHelp }) : null,
4547
- mode === "overlay:status" && selectedRepoStatusOptions.length > 0 ? /* @__PURE__ */ jsx16(
4628
+ return /* @__PURE__ */ jsxs17(Fragment2, { children: [
4629
+ helpVisible ? /* @__PURE__ */ jsx17(HelpOverlay, { currentMode: mode, onClose: onToggleHelp }) : null,
4630
+ mode === "overlay:status" && selectedRepoStatusOptions.length > 0 ? /* @__PURE__ */ jsx17(
4548
4631
  StatusPicker,
4549
4632
  {
4550
4633
  options: selectedRepoStatusOptions,
@@ -4553,7 +4636,7 @@ function OverlayRenderer({
4553
4636
  onCancel: onExitOverlay
4554
4637
  }
4555
4638
  ) : null,
4556
- mode === "overlay:create" ? /* @__PURE__ */ jsx16(
4639
+ mode === "overlay:create" ? /* @__PURE__ */ jsx17(
4557
4640
  CreateIssueForm,
4558
4641
  {
4559
4642
  repos: config2.repos,
@@ -4563,7 +4646,7 @@ function OverlayRenderer({
4563
4646
  labelCache
4564
4647
  }
4565
4648
  ) : null,
4566
- mode === "overlay:confirmPick" ? /* @__PURE__ */ jsx16(
4649
+ mode === "overlay:confirmPick" ? /* @__PURE__ */ jsx17(
4567
4650
  ConfirmPrompt,
4568
4651
  {
4569
4652
  message: "Pick this issue?",
@@ -4571,7 +4654,7 @@ function OverlayRenderer({
4571
4654
  onCancel: onCancelPick
4572
4655
  }
4573
4656
  ) : null,
4574
- mode === "overlay:bulkAction" ? /* @__PURE__ */ jsx16(
4657
+ mode === "overlay:bulkAction" ? /* @__PURE__ */ jsx17(
4575
4658
  BulkActionMenu,
4576
4659
  {
4577
4660
  count: multiSelectCount,
@@ -4580,7 +4663,7 @@ function OverlayRenderer({
4580
4663
  onCancel: onExitOverlay
4581
4664
  }
4582
4665
  ) : null,
4583
- mode === "focus" && focusLabel ? /* @__PURE__ */ jsx16(
4666
+ mode === "focus" && focusLabel ? /* @__PURE__ */ jsx17(
4584
4667
  FocusMode,
4585
4668
  {
4586
4669
  label: focusLabel,
@@ -4590,7 +4673,7 @@ function OverlayRenderer({
4590
4673
  },
4591
4674
  focusKey
4592
4675
  ) : null,
4593
- mode === "overlay:label" && selectedIssue && defaultRepo ? /* @__PURE__ */ jsx16(
4676
+ mode === "overlay:label" && selectedIssue && defaultRepo ? /* @__PURE__ */ jsx17(
4594
4677
  LabelPicker,
4595
4678
  {
4596
4679
  repo: defaultRepo,
@@ -4601,8 +4684,8 @@ function OverlayRenderer({
4601
4684
  onError: onLabelError
4602
4685
  }
4603
4686
  ) : null,
4604
- mode === "search" ? /* @__PURE__ */ jsx16(SearchBar, { defaultValue: searchQuery, onChange: onSearchChange, onSubmit: onSearchSubmit }) : null,
4605
- mode === "overlay:comment" && selectedIssue ? /* @__PURE__ */ jsx16(
4687
+ mode === "search" ? /* @__PURE__ */ jsx17(SearchBar, { defaultValue: searchQuery, onChange: onSearchChange, onSubmit: onSearchSubmit }) : null,
4688
+ mode === "overlay:comment" && selectedIssue ? /* @__PURE__ */ jsx17(
4606
4689
  CommentInput,
4607
4690
  {
4608
4691
  issueNumber: selectedIssue.number,
@@ -4612,8 +4695,8 @@ function OverlayRenderer({
4612
4695
  onResumeRefresh
4613
4696
  }
4614
4697
  ) : null,
4615
- mode === "overlay:fuzzyPicker" ? /* @__PURE__ */ jsx16(FuzzyPicker, { repos, onSelect: onFuzzySelect, onClose: onFuzzyClose }) : null,
4616
- mode === "overlay:createNl" ? /* @__PURE__ */ jsx16(
4698
+ mode === "overlay:fuzzyPicker" ? /* @__PURE__ */ jsx17(FuzzyPicker, { repos, onSelect: onFuzzySelect, onClose: onFuzzyClose }) : null,
4699
+ mode === "overlay:createNl" ? /* @__PURE__ */ jsx17(
4617
4700
  NlCreateOverlay,
4618
4701
  {
4619
4702
  repos: config2.repos,
@@ -4626,7 +4709,7 @@ function OverlayRenderer({
4626
4709
  onLlmFallback
4627
4710
  }
4628
4711
  ) : null,
4629
- mode === "overlay:editIssue" && selectedIssue && selectedRepoName ? /* @__PURE__ */ jsx16(
4712
+ mode === "overlay:editIssue" && selectedIssue && selectedRepoName ? /* @__PURE__ */ jsx17(
4630
4713
  EditIssueOverlay,
4631
4714
  {
4632
4715
  issue: selectedIssue,
@@ -4662,9 +4745,106 @@ var init_overlay_renderer = __esm({
4662
4745
  }
4663
4746
  });
4664
4747
 
4748
+ // src/board/components/panel-layout.tsx
4749
+ import { Box as Box17 } from "ink";
4750
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
4751
+ function getLayoutMode(cols) {
4752
+ if (cols >= WIDE_THRESHOLD) return "wide";
4753
+ if (cols >= MEDIUM_THRESHOLD) return "medium";
4754
+ return "stacked";
4755
+ }
4756
+ function getDetailWidth(cols) {
4757
+ return Math.floor(cols * 0.4);
4758
+ }
4759
+ function PanelLayout({
4760
+ cols,
4761
+ issuesPanelHeight,
4762
+ reposPanel,
4763
+ statusesPanel,
4764
+ issuesPanel,
4765
+ detailPanel,
4766
+ activityPanel
4767
+ }) {
4768
+ const mode = getLayoutMode(cols);
4769
+ if (mode === "wide") {
4770
+ const detailWidth = getDetailWidth(cols);
4771
+ return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
4772
+ /* @__PURE__ */ jsxs18(Box17, { height: issuesPanelHeight, children: [
4773
+ /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", width: LEFT_COL_WIDTH, children: [
4774
+ reposPanel,
4775
+ statusesPanel
4776
+ ] }),
4777
+ /* @__PURE__ */ jsx18(Box17, { flexGrow: 1, flexDirection: "column", children: issuesPanel }),
4778
+ /* @__PURE__ */ jsx18(Box17, { width: detailWidth, flexDirection: "column", children: detailPanel })
4779
+ ] }),
4780
+ /* @__PURE__ */ jsx18(Box17, { height: ACTIVITY_HEIGHT, children: activityPanel })
4781
+ ] });
4782
+ }
4783
+ if (mode === "medium") {
4784
+ return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
4785
+ /* @__PURE__ */ jsxs18(Box17, { height: issuesPanelHeight, children: [
4786
+ /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", width: LEFT_COL_WIDTH, children: [
4787
+ reposPanel,
4788
+ statusesPanel
4789
+ ] }),
4790
+ /* @__PURE__ */ jsx18(Box17, { flexGrow: 1, flexDirection: "column", children: issuesPanel })
4791
+ ] }),
4792
+ /* @__PURE__ */ jsx18(Box17, { height: ACTIVITY_HEIGHT, children: activityPanel })
4793
+ ] });
4794
+ }
4795
+ return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
4796
+ reposPanel,
4797
+ statusesPanel,
4798
+ /* @__PURE__ */ jsx18(Box17, { flexGrow: 1, flexDirection: "column", children: issuesPanel }),
4799
+ /* @__PURE__ */ jsx18(Box17, { height: ACTIVITY_HEIGHT, children: activityPanel })
4800
+ ] });
4801
+ }
4802
+ var WIDE_THRESHOLD, MEDIUM_THRESHOLD, LEFT_COL_WIDTH, ACTIVITY_HEIGHT;
4803
+ var init_panel_layout = __esm({
4804
+ "src/board/components/panel-layout.tsx"() {
4805
+ "use strict";
4806
+ WIDE_THRESHOLD = 160;
4807
+ MEDIUM_THRESHOLD = 100;
4808
+ LEFT_COL_WIDTH = 24;
4809
+ ACTIVITY_HEIGHT = 3;
4810
+ }
4811
+ });
4812
+
4813
+ // src/board/components/repos-panel.tsx
4814
+ import { Box as Box18, Text as Text17 } from "ink";
4815
+ import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
4816
+ function shortName(fullName) {
4817
+ return fullName.includes("/") ? fullName.split("/")[1] ?? fullName : fullName;
4818
+ }
4819
+ function ReposPanel({ repos, selectedIdx, isActive }) {
4820
+ const borderColor = isActive ? "cyan" : "gray";
4821
+ return /* @__PURE__ */ jsxs19(Box18, { borderStyle: "single", borderColor, flexDirection: "column", flexGrow: 1, children: [
4822
+ /* @__PURE__ */ jsx19(Text17, { bold: true, color: isActive ? "cyan" : "white", children: "[1] Repos" }),
4823
+ repos.length === 0 ? /* @__PURE__ */ jsx19(Text17, { color: "gray", children: " \u2014" }) : repos.map((repo, i) => {
4824
+ const isSel = i === selectedIdx;
4825
+ const label = shortName(repo.name);
4826
+ return /* @__PURE__ */ jsxs19(Box18, { children: [
4827
+ /* @__PURE__ */ jsxs19(Text17, { color: isSel ? "cyan" : isActive ? "white" : "gray", bold: isSel, children: [
4828
+ isSel ? "\u25B6 " : " ",
4829
+ label
4830
+ ] }),
4831
+ /* @__PURE__ */ jsxs19(Text17, { color: "gray", children: [
4832
+ " ",
4833
+ repo.openCount
4834
+ ] })
4835
+ ] }, repo.name);
4836
+ })
4837
+ ] });
4838
+ }
4839
+ var init_repos_panel = __esm({
4840
+ "src/board/components/repos-panel.tsx"() {
4841
+ "use strict";
4842
+ }
4843
+ });
4844
+
4665
4845
  // src/board/components/issue-row.tsx
4666
- import { Box as Box16, Text as Text16 } from "ink";
4667
- import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
4846
+ import { Box as Box19, Text as Text18 } from "ink";
4847
+ import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
4668
4848
  function truncate(s, max) {
4669
4849
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
4670
4850
  }
@@ -4705,30 +4885,30 @@ function IssueRow({ issue, selfLogin, isSelected }) {
4705
4885
  const labels = (issue.labels ?? []).slice(0, 2);
4706
4886
  const target = formatTargetDate(issue.targetDate);
4707
4887
  const titleStr = truncate(issue.title, 42).padEnd(42);
4708
- return /* @__PURE__ */ jsxs17(Box16, { children: [
4709
- isSelected ? /* @__PURE__ */ jsx17(Text16, { color: "cyan", bold: true, children: "\u25B6 " }) : /* @__PURE__ */ jsx17(Text16, { children: " " }),
4710
- /* @__PURE__ */ jsxs17(Text16, { color: "cyan", children: [
4888
+ return /* @__PURE__ */ jsxs20(Box19, { children: [
4889
+ isSelected ? /* @__PURE__ */ jsx20(Text18, { color: "cyan", bold: true, children: "\u25B6 " }) : /* @__PURE__ */ jsx20(Text18, { children: " " }),
4890
+ /* @__PURE__ */ jsxs20(Text18, { color: "cyan", children: [
4711
4891
  "#",
4712
4892
  String(issue.number).padEnd(5)
4713
4893
  ] }),
4714
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4715
- isSelected ? /* @__PURE__ */ jsx17(Text16, { color: "white", bold: true, children: titleStr }) : /* @__PURE__ */ jsx17(Text16, { children: titleStr }),
4716
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4717
- /* @__PURE__ */ jsx17(Box16, { width: LABEL_COL_WIDTH, children: labels.map((l, i) => /* @__PURE__ */ jsxs17(Text16, { children: [
4894
+ /* @__PURE__ */ jsx20(Text18, { children: " " }),
4895
+ isSelected ? /* @__PURE__ */ jsx20(Text18, { color: "white", bold: true, children: titleStr }) : /* @__PURE__ */ jsx20(Text18, { children: titleStr }),
4896
+ /* @__PURE__ */ jsx20(Text18, { children: " " }),
4897
+ /* @__PURE__ */ jsx20(Box19, { width: LABEL_COL_WIDTH, children: labels.map((l, i) => /* @__PURE__ */ jsxs20(Text18, { children: [
4718
4898
  i > 0 ? " " : "",
4719
- /* @__PURE__ */ jsxs17(Text16, { color: labelColor(l.name), children: [
4899
+ /* @__PURE__ */ jsxs20(Text18, { color: labelColor(l.name), children: [
4720
4900
  "[",
4721
4901
  truncate(l.name, 12),
4722
4902
  "]"
4723
4903
  ] })
4724
4904
  ] }, l.name)) }),
4725
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4726
- /* @__PURE__ */ jsx17(Text16, { color: assigneeColor, children: assigneeText.padEnd(14) }),
4727
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4728
- /* @__PURE__ */ jsx17(Text16, { color: "gray", children: timeAgo2(issue.updatedAt).padStart(4) }),
4729
- target.text ? /* @__PURE__ */ jsxs17(Fragment3, { children: [
4730
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4731
- /* @__PURE__ */ jsx17(Text16, { color: target.color, children: target.text })
4905
+ /* @__PURE__ */ jsx20(Text18, { children: " " }),
4906
+ /* @__PURE__ */ jsx20(Text18, { color: assigneeColor, children: assigneeText.padEnd(14) }),
4907
+ /* @__PURE__ */ jsx20(Text18, { children: " " }),
4908
+ /* @__PURE__ */ jsx20(Text18, { color: "gray", children: timeAgo2(issue.updatedAt).padStart(4) }),
4909
+ target.text ? /* @__PURE__ */ jsxs20(Fragment3, { children: [
4910
+ /* @__PURE__ */ jsx20(Text18, { children: " " }),
4911
+ /* @__PURE__ */ jsx20(Text18, { color: target.color, children: target.text })
4732
4912
  ] }) : null
4733
4913
  ] });
4734
4914
  }
@@ -4751,68 +4931,21 @@ var init_issue_row = __esm({
4751
4931
  }
4752
4932
  });
4753
4933
 
4754
- // src/board/components/task-row.tsx
4755
- import { Box as Box17, Text as Text17 } from "ink";
4756
- import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
4757
- function truncate2(s, max) {
4758
- return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
4759
- }
4760
- function formatDue2(dateStr) {
4761
- if (!dateStr) return { text: "", color: "gray" };
4762
- const d = new Date(dateStr);
4763
- const days = Math.ceil((d.getTime() - Date.now()) / 864e5);
4764
- if (days < 0) return { text: `${Math.abs(days)}d overdue`, color: "red" };
4765
- if (days === 0) return { text: "today", color: "yellow" };
4766
- if (days === 1) return { text: "tomorrow", color: "white" };
4767
- if (days <= 7) return { text: `in ${days}d`, color: "white" };
4768
- return {
4769
- text: d.toLocaleDateString("en-US", { month: "short", day: "numeric" }),
4770
- color: "gray"
4771
- };
4772
- }
4773
- function TaskRow({ task: task2, isSelected }) {
4774
- const pri = PRIORITY_INDICATORS[task2.priority] ?? DEFAULT_PRIORITY;
4775
- const due = formatDue2(task2.dueDate);
4776
- const titleStr = truncate2(task2.title, 45).padEnd(45);
4777
- return /* @__PURE__ */ jsxs18(Box17, { children: [
4778
- isSelected ? /* @__PURE__ */ jsx18(Text17, { color: "cyan", bold: true, children: "\u25B6 " }) : /* @__PURE__ */ jsx18(Text17, { children: " " }),
4779
- /* @__PURE__ */ jsx18(Text17, { color: pri.color, children: pri.text }),
4780
- /* @__PURE__ */ jsx18(Text17, { children: " " }),
4781
- isSelected ? /* @__PURE__ */ jsx18(Text17, { color: "white", bold: true, children: titleStr }) : /* @__PURE__ */ jsx18(Text17, { children: titleStr }),
4782
- /* @__PURE__ */ jsx18(Text17, { children: " " }),
4783
- /* @__PURE__ */ jsx18(Text17, { color: due.color, children: due.text })
4784
- ] });
4785
- }
4786
- var PRIORITY_INDICATORS, DEFAULT_PRIORITY;
4787
- var init_task_row = __esm({
4788
- "src/board/components/task-row.tsx"() {
4789
- "use strict";
4790
- init_types();
4791
- PRIORITY_INDICATORS = {
4792
- [5 /* High */]: { text: "[!]", color: "red" },
4793
- [3 /* Medium */]: { text: "[~]", color: "yellow" },
4794
- [1 /* Low */]: { text: "[\u2193]", color: "blue" },
4795
- [0 /* None */]: { text: " ", color: "gray" }
4796
- };
4797
- DEFAULT_PRIORITY = { text: " ", color: "gray" };
4798
- }
4799
- });
4800
-
4801
4934
  // src/board/components/row-renderer.tsx
4802
- import { Box as Box18, Text as Text18 } from "ink";
4803
- import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
4935
+ import { Box as Box20, Text as Text19 } from "ink";
4936
+ import { jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
4804
4937
  function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4805
4938
  switch (row.type) {
4806
4939
  case "sectionHeader": {
4807
4940
  const arrow = row.isCollapsed ? "\u25B6" : "\u25BC";
4808
4941
  const isSel = selectedId === row.navId;
4809
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4810
- /* @__PURE__ */ jsxs19(Text18, { color: isSel ? "cyan" : "white", bold: true, children: [
4942
+ return /* @__PURE__ */ jsxs21(Box20, { children: [
4943
+ /* @__PURE__ */ jsxs21(Text19, { color: isSel ? "cyan" : "white", bold: true, children: [
4811
4944
  arrow,
4812
4945
  " ",
4813
4946
  row.label
4814
4947
  ] }),
4815
- /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
4948
+ /* @__PURE__ */ jsxs21(Text19, { color: "gray", children: [
4816
4949
  " ",
4817
4950
  "(",
4818
4951
  row.count,
@@ -4826,26 +4959,26 @@ function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4826
4959
  if (row.navId) {
4827
4960
  const arrow = row.isCollapsed ? "\u25B6" : "\u25BC";
4828
4961
  const isSel = selectedId === row.navId;
4829
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4830
- /* @__PURE__ */ jsxs19(Text18, { color: isSel ? "cyan" : "gray", children: [
4962
+ return /* @__PURE__ */ jsxs21(Box20, { children: [
4963
+ /* @__PURE__ */ jsxs21(Text19, { color: isSel ? "cyan" : "gray", children: [
4831
4964
  " ",
4832
4965
  arrow,
4833
4966
  " ",
4834
4967
  row.text
4835
4968
  ] }),
4836
- /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
4969
+ /* @__PURE__ */ jsxs21(Text19, { color: "gray", children: [
4837
4970
  " (",
4838
4971
  row.count,
4839
4972
  ")"
4840
4973
  ] })
4841
4974
  ] });
4842
4975
  }
4843
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4844
- /* @__PURE__ */ jsxs19(Text18, { bold: true, color: "white", children: [
4976
+ return /* @__PURE__ */ jsxs21(Box20, { children: [
4977
+ /* @__PURE__ */ jsxs21(Text19, { bold: true, color: "white", children: [
4845
4978
  " ",
4846
4979
  row.text
4847
4980
  ] }),
4848
- row.count != null ? /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
4981
+ row.count != null ? /* @__PURE__ */ jsxs21(Text19, { color: "gray", children: [
4849
4982
  " (",
4850
4983
  row.count,
4851
4984
  ")"
@@ -4854,32 +4987,25 @@ function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4854
4987
  }
4855
4988
  case "issue": {
4856
4989
  const checkbox2 = isMultiSelected != null ? isMultiSelected ? "\u2611 " : "\u2610 " : "";
4857
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4858
- checkbox2 ? /* @__PURE__ */ jsx19(Text18, { color: isMultiSelected ? "cyan" : "gray", children: checkbox2 }) : null,
4859
- /* @__PURE__ */ jsx19(IssueRow, { issue: row.issue, selfLogin, isSelected: selectedId === row.navId })
4860
- ] });
4861
- }
4862
- case "task": {
4863
- const checkbox2 = isMultiSelected != null ? isMultiSelected ? "\u2611 " : "\u2610 " : "";
4864
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4865
- checkbox2 ? /* @__PURE__ */ jsx19(Text18, { color: isMultiSelected ? "cyan" : "gray", children: checkbox2 }) : null,
4866
- /* @__PURE__ */ jsx19(TaskRow, { task: row.task, isSelected: selectedId === row.navId })
4990
+ return /* @__PURE__ */ jsxs21(Box20, { children: [
4991
+ checkbox2 ? /* @__PURE__ */ jsx21(Text19, { color: isMultiSelected ? "cyan" : "gray", children: checkbox2 }) : null,
4992
+ /* @__PURE__ */ jsx21(IssueRow, { issue: row.issue, selfLogin, isSelected: selectedId === row.navId })
4867
4993
  ] });
4868
4994
  }
4869
4995
  case "activity": {
4870
- const ago = timeAgo(row.event.timestamp);
4871
- return /* @__PURE__ */ jsxs19(Text18, { dimColor: true, children: [
4996
+ const ago = new Date(row.event.timestamp).toLocaleTimeString();
4997
+ return /* @__PURE__ */ jsxs21(Text19, { dimColor: true, children: [
4872
4998
  " ",
4873
4999
  ago,
4874
5000
  ": ",
4875
- /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
5001
+ /* @__PURE__ */ jsxs21(Text19, { color: "gray", children: [
4876
5002
  "@",
4877
5003
  row.event.actor
4878
5004
  ] }),
4879
5005
  " ",
4880
5006
  row.event.summary,
4881
5007
  " ",
4882
- /* @__PURE__ */ jsxs19(Text18, { dimColor: true, children: [
5008
+ /* @__PURE__ */ jsxs21(Text19, { dimColor: true, children: [
4883
5009
  "(",
4884
5010
  row.event.repoShortName,
4885
5011
  ")"
@@ -4887,62 +5013,66 @@ function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4887
5013
  ] });
4888
5014
  }
4889
5015
  case "error":
4890
- return /* @__PURE__ */ jsxs19(Text18, { color: "red", children: [
5016
+ return /* @__PURE__ */ jsxs21(Text19, { color: "red", children: [
4891
5017
  " Error: ",
4892
5018
  row.text
4893
5019
  ] });
4894
5020
  case "gap":
4895
- return /* @__PURE__ */ jsx19(Text18, { children: "" });
5021
+ return /* @__PURE__ */ jsx21(Text19, { children: "" });
4896
5022
  }
4897
5023
  }
4898
5024
  var init_row_renderer = __esm({
4899
5025
  "src/board/components/row-renderer.tsx"() {
4900
5026
  "use strict";
4901
- init_constants();
4902
5027
  init_issue_row();
4903
- init_task_row();
4904
5028
  }
4905
5029
  });
4906
5030
 
4907
- // src/board/components/tab-bar.tsx
4908
- import { Box as Box19, Text as Text19 } from "ink";
4909
- import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
4910
- function TabBar({ tabs, activeTabId, totalWidth }) {
4911
- return /* @__PURE__ */ jsx20(Box19, { width: totalWidth, children: tabs.map((tab, i) => {
4912
- const isActive = tab.id === activeTabId;
4913
- return /* @__PURE__ */ jsx20(Box19, { marginRight: 2, children: /* @__PURE__ */ jsxs20(Text19, { bold: isActive, color: isActive ? "cyan" : "gray", children: [
4914
- i + 1,
4915
- ":",
4916
- tab.label,
4917
- " (",
4918
- tab.count,
4919
- ")"
4920
- ] }) }, tab.id);
4921
- }) });
4922
- }
4923
- var init_tab_bar = __esm({
4924
- "src/board/components/tab-bar.tsx"() {
5031
+ // src/board/components/statuses-panel.tsx
5032
+ import { Box as Box21, Text as Text20 } from "ink";
5033
+ import { jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
5034
+ function StatusesPanel({ groups, selectedIdx, isActive }) {
5035
+ const borderColor = isActive ? "cyan" : "gray";
5036
+ return /* @__PURE__ */ jsxs22(Box21, { borderStyle: "single", borderColor, flexDirection: "column", flexGrow: 1, children: [
5037
+ /* @__PURE__ */ jsx22(Text20, { bold: true, color: isActive ? "cyan" : "white", children: "[2] Statuses" }),
5038
+ groups.length === 0 ? /* @__PURE__ */ jsx22(Text20, { color: "gray", children: " \u2014" }) : groups.map((group, i) => {
5039
+ const isSel = i === selectedIdx;
5040
+ return /* @__PURE__ */ jsxs22(Box21, { children: [
5041
+ /* @__PURE__ */ jsxs22(Text20, { color: isSel ? "cyan" : isActive ? "white" : "gray", bold: isSel, children: [
5042
+ isSel ? "\u25B6 " : " ",
5043
+ group.label
5044
+ ] }),
5045
+ /* @__PURE__ */ jsxs22(Text20, { color: "gray", children: [
5046
+ " ",
5047
+ group.count
5048
+ ] })
5049
+ ] }, group.id);
5050
+ })
5051
+ ] });
5052
+ }
5053
+ var init_statuses_panel = __esm({
5054
+ "src/board/components/statuses-panel.tsx"() {
4925
5055
  "use strict";
4926
5056
  }
4927
5057
  });
4928
5058
 
4929
5059
  // src/board/components/toast-container.tsx
4930
5060
  import { Spinner as Spinner3 } from "@inkjs/ui";
4931
- import { Box as Box20, Text as Text20 } from "ink";
4932
- import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
5061
+ import { Box as Box22, Text as Text21 } from "ink";
5062
+ import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs23 } from "react/jsx-runtime";
4933
5063
  function ToastContainer({ toasts }) {
4934
5064
  if (toasts.length === 0) return null;
4935
- return /* @__PURE__ */ jsx21(Box20, { flexDirection: "column", children: toasts.map((t) => /* @__PURE__ */ jsx21(Box20, { children: t.type === "loading" ? /* @__PURE__ */ jsxs21(Fragment4, { children: [
4936
- /* @__PURE__ */ jsx21(Spinner3, { label: "" }),
4937
- /* @__PURE__ */ jsxs21(Text20, { color: "cyan", children: [
5065
+ return /* @__PURE__ */ jsx23(Box22, { flexDirection: "column", children: toasts.map((t) => /* @__PURE__ */ jsx23(Box22, { children: t.type === "loading" ? /* @__PURE__ */ jsxs23(Fragment4, { children: [
5066
+ /* @__PURE__ */ jsx23(Spinner3, { label: "" }),
5067
+ /* @__PURE__ */ jsxs23(Text21, { color: "cyan", children: [
4938
5068
  " ",
4939
5069
  t.message
4940
5070
  ] })
4941
- ] }) : /* @__PURE__ */ jsxs21(Text20, { color: TYPE_COLORS[t.type], children: [
5071
+ ] }) : /* @__PURE__ */ jsxs23(Text21, { color: TYPE_COLORS[t.type], children: [
4942
5072
  TYPE_PREFIXES[t.type],
4943
5073
  " ",
4944
5074
  t.message,
4945
- t.type === "error" ? /* @__PURE__ */ jsx21(Text20, { color: "gray", children: t.retry ? " [r]etry [d]ismiss" : " [d]ismiss" }) : null
5075
+ t.type === "error" ? /* @__PURE__ */ jsx23(Text21, { color: "gray", children: t.retry ? " [r]etry [d]ismiss" : " [d]ismiss" }) : null
4946
5076
  ] }) }, t.id)) });
4947
5077
  }
4948
5078
  var TYPE_COLORS, TYPE_PREFIXES;
@@ -4966,9 +5096,9 @@ var init_toast_container = __esm({
4966
5096
  // src/board/components/dashboard.tsx
4967
5097
  import { execFileSync as execFileSync3, spawnSync as spawnSync4 } from "child_process";
4968
5098
  import { Spinner as Spinner4 } from "@inkjs/ui";
4969
- import { Box as Box21, Text as Text21, useApp, useStdout } from "ink";
4970
- import { useCallback as useCallback11, useEffect as useEffect9, useMemo as useMemo3, useRef as useRef13, useState as useState15 } from "react";
4971
- import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
5099
+ import { Box as Box23, Text as Text22, useApp, useStdout } from "ink";
5100
+ import { useCallback as useCallback12, useEffect as useEffect9, useMemo as useMemo3, useRef as useRef13, useState as useState16 } from "react";
5101
+ import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs24 } from "react/jsx-runtime";
4972
5102
  function resolveStatusGroups(statusOptions, configuredGroups) {
4973
5103
  if (configuredGroups && configuredGroups.length > 0) {
4974
5104
  return configuredGroups.map((entry) => {
@@ -5006,7 +5136,7 @@ function groupByStatus(issues) {
5006
5136
  }
5007
5137
  return groups;
5008
5138
  }
5009
- function buildBoardTree(repos, tasks, activity) {
5139
+ function buildBoardTree(repos, activity) {
5010
5140
  const sections = repos.map((rd) => {
5011
5141
  const sectionId = rd.repo.name;
5012
5142
  if (rd.error) {
@@ -5035,85 +5165,65 @@ function buildBoardTree(repos, tasks, activity) {
5035
5165
  }
5036
5166
  return { repo: rd.repo, sectionId, groups, error: null };
5037
5167
  });
5038
- return { activity, sections, tasks };
5168
+ return { activity, sections };
5039
5169
  }
5040
- function buildTabs(tree) {
5041
- const tabs = tree.sections.map(({ repo, groups }) => ({
5042
- id: repo.name,
5043
- label: repo.shortName,
5044
- count: groups.reduce((s, g) => s + g.issues.length, 0)
5045
- }));
5046
- if (tree.activity.length > 0)
5047
- tabs.push({ id: "activity", label: "Activity", count: tree.activity.length });
5048
- if (tree.tasks.length > 0)
5049
- tabs.push({ id: "ticktick", label: "Tasks", count: tree.tasks.length });
5050
- return tabs;
5051
- }
5052
- function buildNavItemsForTab(tabId, tree) {
5053
- if (tabId === "activity") return [];
5054
- if (tabId === "ticktick")
5055
- return tree.tasks.map((task2) => ({
5056
- id: `tt:${task2.id}`,
5057
- section: tabId,
5058
- type: "item"
5059
- }));
5060
- const section = tree.sections.find((s) => s.sectionId === tabId);
5170
+ function buildNavItemsForRepo(sections, repoName, statusGroupId) {
5171
+ if (!repoName) return [];
5172
+ const section = sections.find((s) => s.sectionId === repoName);
5061
5173
  if (!section) return [];
5062
- return section.groups.flatMap(
5063
- (group) => group.issues.map((issue) => ({
5064
- id: `gh:${section.repo.name}:${issue.number}`,
5065
- section: tabId,
5066
- type: "item"
5067
- }))
5068
- );
5174
+ const activeGroup = section.groups.find((g) => g.subId === statusGroupId) ?? section.groups[0];
5175
+ if (!activeGroup) return [];
5176
+ return activeGroup.issues.map((issue) => ({
5177
+ id: `gh:${section.repo.name}:${issue.number}`,
5178
+ section: repoName,
5179
+ type: "item"
5180
+ }));
5069
5181
  }
5070
- function buildFlatRowsForTab(tabId, tree) {
5071
- if (tabId === "activity")
5072
- return tree.activity.map((event, i) => ({
5073
- type: "activity",
5074
- key: `act:${i}`,
5075
- navId: null,
5076
- event
5077
- }));
5078
- if (tabId === "ticktick")
5079
- return tree.tasks.map((task2) => ({
5080
- type: "task",
5081
- key: `tt:${task2.id}`,
5082
- navId: `tt:${task2.id}`,
5083
- task: task2
5084
- }));
5085
- const section = tree.sections.find((s) => s.sectionId === tabId);
5182
+ function buildFlatRowsForRepo(sections, repoName, statusGroupId) {
5183
+ if (!repoName) {
5184
+ return [
5185
+ {
5186
+ type: "subHeader",
5187
+ key: "select-repo",
5188
+ navId: null,
5189
+ text: "Select a repo in panel [1]"
5190
+ }
5191
+ ];
5192
+ }
5193
+ const section = sections.find((s) => s.sectionId === repoName);
5086
5194
  if (!section) return [];
5087
- if (section.error)
5088
- return [{ type: "error", key: `error:${tabId}`, navId: null, text: section.error }];
5089
- if (section.groups.length === 0)
5195
+ if (section.error) {
5196
+ return [{ type: "error", key: `error:${repoName}`, navId: null, text: section.error }];
5197
+ }
5198
+ if (section.groups.length === 0) {
5090
5199
  return [
5091
- { type: "subHeader", key: `empty:${tabId}`, navId: null, text: "No open issues" }
5200
+ {
5201
+ type: "subHeader",
5202
+ key: `empty:${repoName}`,
5203
+ navId: null,
5204
+ text: "No open issues"
5205
+ }
5092
5206
  ];
5093
- const rows = [];
5094
- let isFirst = true;
5095
- for (const group of section.groups) {
5096
- if (!isFirst)
5097
- rows.push({ type: "gap", key: `gap:${tabId}:${group.label}`, navId: null });
5098
- isFirst = false;
5099
- rows.push({
5100
- type: "subHeader",
5101
- key: group.subId,
5102
- navId: null,
5103
- text: group.label,
5104
- count: group.issues.length,
5105
- isCollapsed: false
5106
- });
5107
- for (const issue of group.issues)
5108
- rows.push({
5109
- type: "issue",
5110
- key: `gh:${section.repo.name}:${issue.number}`,
5111
- navId: `gh:${section.repo.name}:${issue.number}`,
5112
- issue,
5113
- repoName: section.repo.name
5114
- });
5115
5207
  }
5116
- return rows;
5208
+ const activeGroup = section.groups.find((g) => g.subId === statusGroupId) ?? section.groups[0];
5209
+ if (!activeGroup) return [];
5210
+ if (activeGroup.issues.length === 0) {
5211
+ return [
5212
+ {
5213
+ type: "subHeader",
5214
+ key: `empty-group:${statusGroupId}`,
5215
+ navId: null,
5216
+ text: "No issues in this status group"
5217
+ }
5218
+ ];
5219
+ }
5220
+ return activeGroup.issues.map((issue) => ({
5221
+ type: "issue",
5222
+ key: `gh:${section.repo.name}:${issue.number}`,
5223
+ navId: `gh:${section.repo.name}:${issue.number}`,
5224
+ issue,
5225
+ repoName: section.repo.name
5226
+ }));
5117
5227
  }
5118
5228
  function openInBrowser(url) {
5119
5229
  if (!(url.startsWith("https://") || url.startsWith("http://"))) return;
@@ -5133,13 +5243,13 @@ function findSelectedIssueWithRepo(repos, selectedId) {
5133
5243
  return null;
5134
5244
  }
5135
5245
  function RefreshAge({ lastRefresh }) {
5136
- const [, setTick] = useState15(0);
5246
+ const [, setTick] = useState16(0);
5137
5247
  useEffect9(() => {
5138
5248
  const id = setInterval(() => setTick((t) => t + 1), 1e4);
5139
5249
  return () => clearInterval(id);
5140
5250
  }, []);
5141
5251
  if (!lastRefresh) return null;
5142
- return /* @__PURE__ */ jsxs22(Text21, { color: refreshAgeColor(lastRefresh), children: [
5252
+ return /* @__PURE__ */ jsxs24(Text22, { color: refreshAgeColor(lastRefresh), children: [
5143
5253
  "Updated ",
5144
5254
  timeAgo(lastRefresh)
5145
5255
  ] });
@@ -5163,19 +5273,16 @@ function Dashboard({ config: config2, options, activeProfile }) {
5163
5273
  clearPendingMutation
5164
5274
  } = useData(config2, options, refreshMs);
5165
5275
  const allRepos = useMemo3(() => data?.repos ?? [], [data?.repos]);
5166
- const allTasks = useMemo3(
5167
- () => config2.ticktick.enabled ? data?.ticktick ?? [] : [],
5168
- [data?.ticktick, config2.ticktick.enabled]
5169
- );
5170
5276
  const allActivity = useMemo3(() => data?.activity ?? [], [data?.activity]);
5171
5277
  const ui = useUIState();
5172
- const [searchQuery, setSearchQuery] = useState15("");
5173
- const [mineOnly, setMineOnly] = useState15(false);
5174
- const handleToggleMine = useCallback11(() => {
5278
+ const panelFocus = usePanelFocus(3);
5279
+ const [searchQuery, setSearchQuery] = useState16("");
5280
+ const [mineOnly, setMineOnly] = useState16(false);
5281
+ const handleToggleMine = useCallback12(() => {
5175
5282
  setMineOnly((prev) => !prev);
5176
5283
  }, []);
5177
5284
  const { toasts, toast, handleErrorAction } = useToast();
5178
- const [logVisible, setLogVisible] = useState15(false);
5285
+ const [logVisible, setLogVisible] = useState16(false);
5179
5286
  const { entries: logEntries, pushEntry, undoLast, hasUndoable } = useActionLog(toast, refresh);
5180
5287
  useEffect9(() => {
5181
5288
  const last = logEntries[logEntries.length - 1];
@@ -5199,45 +5306,75 @@ function Dashboard({ config: config2, options, activeProfile }) {
5199
5306
  const q = searchQuery.toLowerCase();
5200
5307
  return filtered.map((rd) => ({ ...rd, issues: rd.issues.filter((i) => i.title.toLowerCase().includes(q)) })).filter((rd) => rd.issues.length > 0);
5201
5308
  }, [allRepos, searchQuery, mineOnly, config2.board.assignee]);
5202
- const tasks = useMemo3(() => {
5203
- if (!searchQuery) return allTasks;
5204
- const q = searchQuery.toLowerCase();
5205
- return allTasks.filter((t) => t.title.toLowerCase().includes(q));
5206
- }, [allTasks, searchQuery]);
5207
- const boardTree = useMemo3(
5208
- () => buildBoardTree(repos, tasks, allActivity),
5209
- [repos, tasks, allActivity]
5309
+ const boardTree = useMemo3(() => buildBoardTree(repos, allActivity), [repos, allActivity]);
5310
+ const [selectedRepoIdx, setSelectedRepoIdx] = useState16(0);
5311
+ const clampedRepoIdx = Math.min(selectedRepoIdx, Math.max(0, boardTree.sections.length - 1));
5312
+ const reposNav = {
5313
+ moveUp: useCallback12(() => setSelectedRepoIdx((i) => Math.max(0, i - 1)), []),
5314
+ moveDown: useCallback12(
5315
+ () => setSelectedRepoIdx((i) => Math.min(Math.max(0, boardTree.sections.length - 1), i + 1)),
5316
+ [boardTree.sections.length]
5317
+ )
5318
+ };
5319
+ const [selectedStatusIdx, setSelectedStatusIdx] = useState16(0);
5320
+ const selectedSection = boardTree.sections[clampedRepoIdx] ?? null;
5321
+ const clampedStatusIdx = Math.min(
5322
+ selectedStatusIdx,
5323
+ Math.max(0, (selectedSection?.groups.length ?? 1) - 1)
5210
5324
  );
5211
- const tabs = useMemo3(() => buildTabs(boardTree), [boardTree]);
5212
- const [activeTabId, setActiveTabId] = useState15(null);
5213
- const effectiveTabId = activeTabId ?? tabs[0]?.id ?? null;
5214
- const activeTabIdx = tabs.findIndex((t) => t.id === effectiveTabId);
5215
- const nextTab = useCallback11(() => {
5216
- if (tabs.length === 0) return;
5217
- setActiveTabId(tabs[(Math.max(activeTabIdx, 0) + 1) % tabs.length]?.id ?? null);
5218
- }, [activeTabIdx, tabs]);
5219
- const prevTab = useCallback11(() => {
5220
- if (tabs.length === 0) return;
5221
- setActiveTabId(tabs[(Math.max(activeTabIdx, 0) - 1 + tabs.length) % tabs.length]?.id ?? null);
5222
- }, [activeTabIdx, tabs]);
5223
- const jumpToTab = useCallback11(
5224
- (idx) => {
5225
- const tab = tabs[idx];
5226
- if (tab) setActiveTabId(tab.id);
5227
- },
5228
- [tabs]
5325
+ const statusesNav = {
5326
+ moveUp: useCallback12(() => setSelectedStatusIdx((i) => Math.max(0, i - 1)), []),
5327
+ moveDown: useCallback12(
5328
+ () => setSelectedStatusIdx(
5329
+ (i) => Math.min(Math.max(0, (selectedSection?.groups.length ?? 1) - 1), i + 1)
5330
+ ),
5331
+ [selectedSection?.groups.length]
5332
+ )
5333
+ };
5334
+ const [activitySelectedIdx, setActivitySelectedIdx] = useState16(0);
5335
+ const clampedActivityIdx = Math.min(
5336
+ activitySelectedIdx,
5337
+ Math.max(0, boardTree.activity.length - 1)
5229
5338
  );
5339
+ const activityNav = {
5340
+ moveUp: useCallback12(() => setActivitySelectedIdx((i) => Math.max(0, i - 1)), []),
5341
+ moveDown: useCallback12(
5342
+ () => setActivitySelectedIdx((i) => Math.min(Math.max(0, boardTree.activity.length - 1), i + 1)),
5343
+ [boardTree.activity.length]
5344
+ )
5345
+ };
5346
+ const selectedRepoName = selectedSection?.sectionId ?? null;
5347
+ const selectedStatusGroup = selectedSection?.groups[clampedStatusIdx] ?? null;
5348
+ const selectedStatusGroupId = selectedStatusGroup?.subId ?? null;
5349
+ const onRepoEnter = useCallback12(() => {
5350
+ setSelectedStatusIdx(0);
5351
+ panelFocus.focusPanel(3);
5352
+ }, [panelFocus]);
5353
+ const onStatusEnter = useCallback12(() => {
5354
+ panelFocus.focusPanel(3);
5355
+ }, [panelFocus]);
5356
+ const onActivityEnter = useCallback12(() => {
5357
+ const event = boardTree.activity[clampedActivityIdx];
5358
+ if (!event) return;
5359
+ const repoIdx = boardTree.sections.findIndex(
5360
+ (s) => s.repo.shortName === event.repoShortName || s.sectionId.endsWith(`/${event.repoShortName}`)
5361
+ );
5362
+ if (repoIdx >= 0) {
5363
+ setSelectedRepoIdx(repoIdx);
5364
+ setSelectedStatusIdx(0);
5365
+ panelFocus.focusPanel(3);
5366
+ }
5367
+ }, [boardTree, clampedActivityIdx, panelFocus]);
5230
5368
  const navItems = useMemo3(
5231
- () => buildNavItemsForTab(effectiveTabId ?? "", boardTree),
5232
- [effectiveTabId, boardTree]
5369
+ () => buildNavItemsForRepo(boardTree.sections, selectedRepoName, selectedStatusGroupId),
5370
+ [boardTree.sections, selectedRepoName, selectedStatusGroupId]
5233
5371
  );
5234
5372
  const nav = useNavigation(navItems);
5235
- const getRepoForId = useCallback11((id) => {
5373
+ const getRepoForId = useCallback12((id) => {
5236
5374
  if (id.startsWith("gh:")) {
5237
5375
  const parts = id.split(":");
5238
5376
  return parts.length >= 3 ? `${parts[1]}` : null;
5239
5377
  }
5240
- if (id.startsWith("tt:")) return "ticktick";
5241
5378
  return null;
5242
5379
  }, []);
5243
5380
  const multiSelect = useMultiSelect(getRepoForId);
@@ -5261,8 +5398,8 @@ function Dashboard({ config: config2, options, activeProfile }) {
5261
5398
  const pendingPickRef = useRef13(null);
5262
5399
  const labelCacheRef = useRef13({});
5263
5400
  const commentCacheRef = useRef13({});
5264
- const [commentTick, setCommentTick] = useState15(0);
5265
- const handleFetchComments = useCallback11((repo, issueNumber) => {
5401
+ const [commentTick, setCommentTick] = useState16(0);
5402
+ const handleFetchComments = useCallback12((repo, issueNumber) => {
5266
5403
  const key = `${repo}:${issueNumber}`;
5267
5404
  if (commentCacheRef.current[key] !== void 0) return;
5268
5405
  commentCacheRef.current[key] = "loading";
@@ -5275,7 +5412,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5275
5412
  setCommentTick((t) => t + 1);
5276
5413
  });
5277
5414
  }, []);
5278
- const handleCreateIssueWithPrompt = useCallback11(
5415
+ const handleCreateIssueWithPrompt = useCallback12(
5279
5416
  (repo, title, body, dueDate, labels) => {
5280
5417
  actions.handleCreateIssue(repo, title, body, dueDate, labels).then((result) => {
5281
5418
  if (result) {
@@ -5286,7 +5423,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5286
5423
  },
5287
5424
  [actions, ui]
5288
5425
  );
5289
- const handleConfirmPick = useCallback11(() => {
5426
+ const handleConfirmPick = useCallback12(() => {
5290
5427
  const pending = pendingPickRef.current;
5291
5428
  pendingPickRef.current = null;
5292
5429
  ui.exitOverlay();
@@ -5304,12 +5441,12 @@ function Dashboard({ config: config2, options, activeProfile }) {
5304
5441
  })
5305
5442
  );
5306
5443
  }, [config2, toast, refresh, ui]);
5307
- const handleCancelPick = useCallback11(() => {
5444
+ const handleCancelPick = useCallback12(() => {
5308
5445
  pendingPickRef.current = null;
5309
5446
  ui.exitOverlay();
5310
5447
  }, [ui]);
5311
- const [focusLabel, setFocusLabel] = useState15(null);
5312
- const handleEnterFocus = useCallback11(() => {
5448
+ const [focusLabel, setFocusLabel] = useState16(null);
5449
+ const handleEnterFocus = useCallback12(() => {
5313
5450
  const id = nav.selectedId;
5314
5451
  if (!id || isHeaderId(id)) return;
5315
5452
  let label = "";
@@ -5319,20 +5456,16 @@ function Dashboard({ config: config2, options, activeProfile }) {
5319
5456
  const rc = config2.repos.find((r) => r.name === found.repoName);
5320
5457
  label = `${rc?.shortName ?? found.repoName}#${found.issue.number} \u2014 ${found.issue.title}`;
5321
5458
  }
5322
- } else if (id.startsWith("tt:")) {
5323
- const taskId = id.slice(3);
5324
- const task2 = tasks.find((t) => t.id === taskId);
5325
- if (task2) label = task2.title;
5326
5459
  }
5327
5460
  if (!label) return;
5328
5461
  setFocusLabel(label);
5329
5462
  ui.enterFocus();
5330
- }, [nav.selectedId, repos, tasks, config2.repos, ui]);
5331
- const handleFocusExit = useCallback11(() => {
5463
+ }, [nav.selectedId, repos, config2.repos, ui]);
5464
+ const handleFocusExit = useCallback12(() => {
5332
5465
  setFocusLabel(null);
5333
5466
  ui.exitToNormal();
5334
5467
  }, [ui]);
5335
- const handleFocusEndAction = useCallback11(
5468
+ const handleFocusEndAction = useCallback12(
5336
5469
  (action) => {
5337
5470
  switch (action) {
5338
5471
  case "restart":
@@ -5358,9 +5491,9 @@ function Dashboard({ config: config2, options, activeProfile }) {
5358
5491
  },
5359
5492
  [toast, ui]
5360
5493
  );
5361
- const [focusKey, setFocusKey] = useState15(0);
5494
+ const [focusKey, setFocusKey] = useState16(0);
5362
5495
  const { stdout } = useStdout();
5363
- const [termSize, setTermSize] = useState15({
5496
+ const [termSize, setTermSize] = useState16({
5364
5497
  cols: stdout?.columns ?? 80,
5365
5498
  rows: stdout?.rows ?? 24
5366
5499
  });
@@ -5372,69 +5505,59 @@ function Dashboard({ config: config2, options, activeProfile }) {
5372
5505
  stdout.off("resize", onResize);
5373
5506
  };
5374
5507
  }, [stdout]);
5375
- const showDetailPanel = termSize.cols >= 120;
5376
- const detailPanelWidth = showDetailPanel ? Math.floor(termSize.cols * 0.35) : 0;
5508
+ const layoutMode = getLayoutMode(termSize.cols);
5509
+ const detailPanelWidth = layoutMode === "wide" ? getDetailWidth(termSize.cols) : Math.floor(termSize.cols * 0.35);
5510
+ const showDetailPanel = layoutMode === "wide";
5377
5511
  const overlayBarRows = ui.state.mode === "search" || ui.state.mode === "overlay:comment" ? 1 : 0;
5378
5512
  const toastRows = toasts.length;
5379
5513
  const logPaneRows = logVisible ? 4 : 0;
5380
- const viewportHeight = Math.max(
5381
- 5,
5514
+ const totalPanelHeight = Math.max(
5515
+ 8,
5382
5516
  termSize.rows - CHROME_ROWS - overlayBarRows - toastRows - logPaneRows
5383
5517
  );
5518
+ const issuesPanelHeight = Math.max(5, totalPanelHeight - ACTIVITY_HEIGHT);
5384
5519
  const flatRows = useMemo3(
5385
- () => buildFlatRowsForTab(effectiveTabId ?? "", boardTree),
5386
- [effectiveTabId, boardTree]
5520
+ () => buildFlatRowsForRepo(boardTree.sections, selectedRepoName, selectedStatusGroupId),
5521
+ [boardTree.sections, selectedRepoName, selectedStatusGroupId]
5387
5522
  );
5388
5523
  const scrollRef = useRef13(0);
5389
- const prevTabIdRef = useRef13(null);
5390
- if (effectiveTabId !== prevTabIdRef.current) {
5391
- prevTabIdRef.current = effectiveTabId;
5524
+ const prevRepoRef = useRef13(null);
5525
+ const prevStatusRef = useRef13(null);
5526
+ if (selectedRepoName !== prevRepoRef.current || selectedStatusGroupId !== prevStatusRef.current) {
5527
+ prevRepoRef.current = selectedRepoName;
5528
+ prevStatusRef.current = selectedStatusGroupId;
5392
5529
  scrollRef.current = 0;
5393
5530
  }
5394
5531
  const selectedRowIdx = useMemo3(
5395
5532
  () => flatRows.findIndex((r) => r.navId === nav.selectedId),
5396
5533
  [flatRows, nav.selectedId]
5397
5534
  );
5398
- const stickyHeader = useMemo3(() => {
5399
- if (selectedRowIdx < 0) return null;
5400
- for (let i = selectedRowIdx; i >= 0; i--) {
5401
- const row = flatRows[i];
5402
- if (row?.type === "subHeader") return { text: row.text, count: row.count };
5403
- }
5404
- return null;
5405
- }, [flatRows, selectedRowIdx]);
5406
5535
  if (selectedRowIdx >= 0) {
5407
5536
  if (selectedRowIdx < scrollRef.current) {
5408
5537
  scrollRef.current = selectedRowIdx;
5409
- } else if (selectedRowIdx >= scrollRef.current + viewportHeight) {
5410
- scrollRef.current = selectedRowIdx - viewportHeight + 1;
5538
+ } else if (selectedRowIdx >= scrollRef.current + issuesPanelHeight) {
5539
+ scrollRef.current = selectedRowIdx - issuesPanelHeight + 1;
5411
5540
  }
5412
5541
  }
5413
- const maxOffset = Math.max(0, flatRows.length - viewportHeight);
5542
+ const maxOffset = Math.max(0, flatRows.length - issuesPanelHeight);
5414
5543
  scrollRef.current = Math.max(0, Math.min(scrollRef.current, maxOffset));
5415
- const visibleRows = flatRows.slice(scrollRef.current, scrollRef.current + viewportHeight);
5544
+ const visibleRows = flatRows.slice(scrollRef.current, scrollRef.current + issuesPanelHeight);
5416
5545
  const hasMoreAbove = scrollRef.current > 0;
5417
- const hasMoreBelow = scrollRef.current + viewportHeight < flatRows.length;
5546
+ const hasMoreBelow = scrollRef.current + issuesPanelHeight < flatRows.length;
5418
5547
  const aboveCount = scrollRef.current;
5419
- const belowCount = flatRows.length - scrollRef.current - viewportHeight;
5548
+ const belowCount = flatRows.length - scrollRef.current - issuesPanelHeight;
5420
5549
  const selectedItem = useMemo3(() => {
5421
5550
  const id = nav.selectedId;
5422
- if (!id || isHeaderId(id)) return { issue: null, task: null, repoName: null };
5551
+ if (!id || isHeaderId(id)) return { issue: null, repoName: null };
5423
5552
  if (id.startsWith("gh:")) {
5424
5553
  for (const rd of repos) {
5425
5554
  for (const issue of rd.issues) {
5426
- if (`gh:${rd.repo.name}:${issue.number}` === id)
5427
- return { issue, task: null, repoName: rd.repo.name };
5555
+ if (`gh:${rd.repo.name}:${issue.number}` === id) return { issue, repoName: rd.repo.name };
5428
5556
  }
5429
5557
  }
5430
5558
  }
5431
- if (id.startsWith("tt:")) {
5432
- const taskId = id.slice(3);
5433
- const task2 = tasks.find((t) => t.id === taskId);
5434
- if (task2) return { issue: null, task: task2, repoName: null };
5435
- }
5436
- return { issue: null, task: null, repoName: null };
5437
- }, [nav.selectedId, repos, tasks]);
5559
+ return { issue: null, repoName: null };
5560
+ }, [nav.selectedId, repos]);
5438
5561
  const currentCommentsState = useMemo3(() => {
5439
5562
  if (!(selectedItem.issue && selectedItem.repoName)) return null;
5440
5563
  return commentCacheRef.current[`${selectedItem.repoName}:${selectedItem.issue.number}`] ?? null;
@@ -5445,20 +5568,20 @@ function Dashboard({ config: config2, options, activeProfile }) {
5445
5568
  }, [selectedItem.repoName, config2.repos]);
5446
5569
  const selectedRepoStatusOptions = useMemo3(() => {
5447
5570
  const repoName = multiSelect.count > 0 ? multiSelect.constrainedRepo : selectedItem.repoName;
5448
- if (!repoName || repoName === "ticktick") return [];
5571
+ if (!repoName) return [];
5449
5572
  const rd = repos.find((r) => r.repo.name === repoName);
5450
5573
  return rd?.statusOptions ?? [];
5451
5574
  }, [selectedItem.repoName, repos, multiSelect.count, multiSelect.constrainedRepo]);
5452
- const handleOpen = useCallback11(() => {
5575
+ const handleOpen = useCallback12(() => {
5453
5576
  const found = findSelectedIssueWithRepo(repos, nav.selectedId);
5454
5577
  if (found) openInBrowser(found.issue.url);
5455
5578
  }, [repos, nav.selectedId]);
5456
- const handleSlack = useCallback11(() => {
5579
+ const handleSlack = useCallback12(() => {
5457
5580
  const found = findSelectedIssueWithRepo(repos, nav.selectedId);
5458
5581
  if (!found?.issue.slackThreadUrl) return;
5459
5582
  openInBrowser(found.issue.slackThreadUrl);
5460
5583
  }, [repos, nav.selectedId]);
5461
- const handleCopyLink = useCallback11(() => {
5584
+ const handleCopyLink = useCallback12(() => {
5462
5585
  const found = findSelectedIssueWithRepo(repos, nav.selectedId);
5463
5586
  if (!found) return;
5464
5587
  const rc = config2.repos.find((r) => r.name === found.repoName);
@@ -5484,17 +5607,12 @@ function Dashboard({ config: config2, options, activeProfile }) {
5484
5607
  }
5485
5608
  }, [repos, nav.selectedId, config2.repos, toast]);
5486
5609
  const multiSelectType = useMemo3(() => {
5487
- let hasGh = false;
5488
- let hasTt = false;
5489
5610
  for (const id of multiSelect.selected) {
5490
- if (id.startsWith("gh:")) hasGh = true;
5491
- if (id.startsWith("tt:")) hasTt = true;
5611
+ if (id.startsWith("tt:")) return "ticktick";
5492
5612
  }
5493
- if (hasGh && hasTt) return "mixed";
5494
- if (hasTt) return "ticktick";
5495
5613
  return "github";
5496
5614
  }, [multiSelect.selected]);
5497
- const handleBulkAction = useCallback11(
5615
+ const handleBulkAction = useCallback12(
5498
5616
  (action) => {
5499
5617
  const ids = multiSelect.selected;
5500
5618
  switch (action.type) {
@@ -5527,10 +5645,9 @@ function Dashboard({ config: config2, options, activeProfile }) {
5527
5645
  case "statusChange":
5528
5646
  ui.enterStatus();
5529
5647
  return;
5530
- // status picker will call handleBulkStatusSelect on select
5531
5648
  case "complete":
5532
5649
  case "delete":
5533
- toast.info(`Bulk ${action.type} not yet implemented for TickTick`);
5650
+ toast.info(`Bulk ${action.type} not yet implemented`);
5534
5651
  ui.exitOverlay();
5535
5652
  multiSelect.clear();
5536
5653
  return;
@@ -5538,7 +5655,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5538
5655
  },
5539
5656
  [multiSelect, actions, ui, toast]
5540
5657
  );
5541
- const handleBulkStatusSelect = useCallback11(
5658
+ const handleBulkStatusSelect = useCallback12(
5542
5659
  (optionId) => {
5543
5660
  const ids = multiSelect.selected;
5544
5661
  ui.exitOverlay();
@@ -5554,20 +5671,28 @@ function Dashboard({ config: config2, options, activeProfile }) {
5554
5671
  },
5555
5672
  [multiSelect, actions, ui]
5556
5673
  );
5557
- const handleFuzzySelect = useCallback11(
5674
+ const handleFuzzySelect = useCallback12(
5558
5675
  (navId) => {
5559
5676
  nav.select(navId);
5560
5677
  if (navId.startsWith("gh:")) {
5561
5678
  const parts = navId.split(":");
5562
- if (parts.length >= 3 && parts[1]) setActiveTabId(parts[1]);
5563
- } else if (navId.startsWith("tt:")) {
5564
- setActiveTabId("ticktick");
5679
+ const repoName = parts[1];
5680
+ if (parts.length >= 3 && repoName) {
5681
+ const repoIdx = boardTree.sections.findIndex((s) => s.sectionId === repoName);
5682
+ if (repoIdx >= 0) {
5683
+ setSelectedRepoIdx(repoIdx);
5684
+ const section = boardTree.sections[repoIdx];
5685
+ const issueNum = parts[2] ? Number(parts[2]) : null;
5686
+ const groupIdx = section?.groups.findIndex((g) => g.issues.some((iss) => iss.number === issueNum)) ?? -1;
5687
+ setSelectedStatusIdx(Math.max(0, groupIdx));
5688
+ }
5689
+ }
5565
5690
  }
5566
5691
  ui.exitToNormal();
5567
5692
  },
5568
- [nav, ui]
5693
+ [nav, ui, boardTree]
5569
5694
  );
5570
- const onSearchEscape = useCallback11(() => {
5695
+ const onSearchEscape = useCallback12(() => {
5571
5696
  ui.exitOverlay();
5572
5697
  setSearchQuery("");
5573
5698
  }, [ui]);
@@ -5597,10 +5722,16 @@ function Dashboard({ config: config2, options, activeProfile }) {
5597
5722
  handleToggleLog: () => setLogVisible((v) => !v)
5598
5723
  },
5599
5724
  onSearchEscape,
5600
- tabNav: { next: nextTab, prev: prevTab, jumpTo: jumpToTab, count: tabs.length }
5725
+ panelFocus,
5726
+ reposNav,
5727
+ statusesNav,
5728
+ activityNav,
5729
+ onRepoEnter,
5730
+ onStatusEnter,
5731
+ onActivityEnter
5601
5732
  });
5602
5733
  if (status === "loading" && !data) {
5603
- return /* @__PURE__ */ jsx22(Box21, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx22(Spinner4, { label: "Loading dashboard..." }) });
5734
+ return /* @__PURE__ */ jsx24(Box23, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx24(Spinner4, { label: "Loading dashboard..." }) });
5604
5735
  }
5605
5736
  const now = data?.fetchedAt ?? /* @__PURE__ */ new Date();
5606
5737
  const dateStr = now.toLocaleDateString("en-US", {
@@ -5608,36 +5739,122 @@ function Dashboard({ config: config2, options, activeProfile }) {
5608
5739
  day: "numeric",
5609
5740
  year: "numeric"
5610
5741
  });
5611
- return /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", paddingX: 1, children: [
5612
- /* @__PURE__ */ jsxs22(Box21, { children: [
5613
- /* @__PURE__ */ jsx22(Text21, { color: "cyan", bold: true, children: "HOG BOARD" }),
5614
- activeProfile ? /* @__PURE__ */ jsxs22(Text21, { color: "yellow", children: [
5742
+ const reposData = boardTree.sections.map(({ repo, groups }) => ({
5743
+ name: repo.name,
5744
+ openCount: groups.reduce((s, g) => s + g.issues.length, 0)
5745
+ }));
5746
+ const statusesData = (selectedSection?.groups ?? []).map(({ label, subId, issues }) => ({
5747
+ id: subId,
5748
+ label,
5749
+ count: issues.length
5750
+ }));
5751
+ const reposPanel = /* @__PURE__ */ jsx24(
5752
+ ReposPanel,
5753
+ {
5754
+ repos: reposData,
5755
+ selectedIdx: clampedRepoIdx,
5756
+ isActive: panelFocus.activePanelId === 1
5757
+ }
5758
+ );
5759
+ const statusesPanel = /* @__PURE__ */ jsx24(
5760
+ StatusesPanel,
5761
+ {
5762
+ groups: statusesData,
5763
+ selectedIdx: clampedStatusIdx,
5764
+ isActive: panelFocus.activePanelId === 2
5765
+ }
5766
+ );
5767
+ const issuesBorderColor = panelFocus.activePanelId === 3 ? "cyan" : "gray";
5768
+ const issuesPanel = /* @__PURE__ */ jsxs24(
5769
+ Box23,
5770
+ {
5771
+ borderStyle: "single",
5772
+ borderColor: issuesBorderColor,
5773
+ flexDirection: "column",
5774
+ flexGrow: 1,
5775
+ overflow: "hidden",
5776
+ children: [
5777
+ /* @__PURE__ */ jsxs24(Text22, { bold: true, color: panelFocus.activePanelId === 3 ? "cyan" : "white", children: [
5778
+ "[3] Issues",
5779
+ selectedSection ? ` \u2014 ${selectedSection.repo.shortName}` : "",
5780
+ selectedStatusGroup ? ` / ${selectedStatusGroup.label}` : ""
5781
+ ] }),
5782
+ hasMoreAbove ? /* @__PURE__ */ jsxs24(Text22, { color: "gray", dimColor: true, children: [
5783
+ " ",
5784
+ "\u25B2",
5785
+ " ",
5786
+ aboveCount,
5787
+ " more above"
5788
+ ] }) : null,
5789
+ visibleRows.map((row) => /* @__PURE__ */ jsx24(
5790
+ RowRenderer,
5791
+ {
5792
+ row,
5793
+ selectedId: nav.selectedId,
5794
+ selfLogin: config2.board.assignee,
5795
+ isMultiSelected: ui.state.mode === "multiSelect" && row.navId ? multiSelect.isSelected(row.navId) : void 0
5796
+ },
5797
+ row.key
5798
+ )),
5799
+ hasMoreBelow ? /* @__PURE__ */ jsxs24(Text22, { color: "gray", dimColor: true, children: [
5800
+ " ",
5801
+ "\u25BC",
5802
+ " ",
5803
+ belowCount,
5804
+ " more below"
5805
+ ] }) : null
5806
+ ]
5807
+ }
5808
+ );
5809
+ const detailPanel = showDetailPanel ? /* @__PURE__ */ jsx24(
5810
+ DetailPanel,
5811
+ {
5812
+ issue: selectedItem.issue,
5813
+ width: detailPanelWidth,
5814
+ isActive: panelFocus.activePanelId === 0,
5815
+ issueRepo: selectedItem.repoName,
5816
+ fetchComments: handleFetchComments,
5817
+ commentsState: currentCommentsState
5818
+ }
5819
+ ) : null;
5820
+ const activityPanel = /* @__PURE__ */ jsx24(
5821
+ ActivityPanel,
5822
+ {
5823
+ events: boardTree.activity,
5824
+ selectedIdx: clampedActivityIdx,
5825
+ isActive: panelFocus.activePanelId === 4,
5826
+ height: ACTIVITY_HEIGHT
5827
+ }
5828
+ );
5829
+ return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", paddingX: 1, children: [
5830
+ /* @__PURE__ */ jsxs24(Box23, { children: [
5831
+ /* @__PURE__ */ jsx24(Text22, { color: "cyan", bold: true, children: "HOG BOARD" }),
5832
+ activeProfile ? /* @__PURE__ */ jsxs24(Text22, { color: "yellow", children: [
5615
5833
  " [",
5616
5834
  activeProfile,
5617
5835
  "]"
5618
5836
  ] }) : null,
5619
- /* @__PURE__ */ jsxs22(Text21, { color: "gray", children: [
5837
+ /* @__PURE__ */ jsxs24(Text22, { color: "gray", children: [
5620
5838
  " ",
5621
5839
  "\u2014",
5622
5840
  " ",
5623
5841
  dateStr
5624
5842
  ] }),
5625
- /* @__PURE__ */ jsx22(Text21, { children: " " }),
5626
- isRefreshing ? /* @__PURE__ */ jsxs22(Fragment5, { children: [
5627
- /* @__PURE__ */ jsx22(Spinner4, { label: "" }),
5628
- /* @__PURE__ */ jsx22(Text21, { color: "cyan", children: " Refreshing..." })
5629
- ] }) : /* @__PURE__ */ jsxs22(Fragment5, { children: [
5630
- /* @__PURE__ */ jsx22(RefreshAge, { lastRefresh }),
5631
- consecutiveFailures > 0 ? /* @__PURE__ */ jsx22(Text21, { color: "red", children: " (!)" }) : null
5843
+ /* @__PURE__ */ jsx24(Text22, { children: " " }),
5844
+ isRefreshing ? /* @__PURE__ */ jsxs24(Fragment5, { children: [
5845
+ /* @__PURE__ */ jsx24(Spinner4, { label: "" }),
5846
+ /* @__PURE__ */ jsx24(Text22, { color: "cyan", children: " Refreshing..." })
5847
+ ] }) : /* @__PURE__ */ jsxs24(Fragment5, { children: [
5848
+ /* @__PURE__ */ jsx24(RefreshAge, { lastRefresh }),
5849
+ consecutiveFailures > 0 ? /* @__PURE__ */ jsx24(Text22, { color: "red", children: " (!)" }) : null
5632
5850
  ] }),
5633
- autoRefreshPaused ? /* @__PURE__ */ jsx22(Text21, { color: "yellow", children: " Auto-refresh paused \u2014 press r to retry" }) : null
5851
+ autoRefreshPaused ? /* @__PURE__ */ jsx24(Text22, { color: "yellow", children: " Auto-refresh paused \u2014 press r to retry" }) : null
5634
5852
  ] }),
5635
- error ? /* @__PURE__ */ jsxs22(Text21, { color: "red", children: [
5853
+ error ? /* @__PURE__ */ jsxs24(Text22, { color: "red", children: [
5636
5854
  "Error: ",
5637
5855
  error
5638
5856
  ] }) : null,
5639
- /* @__PURE__ */ jsx22(TabBar, { tabs, activeTabId: effectiveTabId, totalWidth: termSize.cols }),
5640
- /* @__PURE__ */ jsx22(
5857
+ /* @__PURE__ */ jsx24(
5641
5858
  OverlayRenderer,
5642
5859
  {
5643
5860
  uiState: ui.state,
@@ -5679,64 +5896,25 @@ function Dashboard({ config: config2, options, activeProfile }) {
5679
5896
  onPushEntry: pushEntry
5680
5897
  }
5681
5898
  ),
5682
- !ui.state.helpVisible && ui.state.mode !== "overlay:status" && ui.state.mode !== "overlay:create" && ui.state.mode !== "overlay:createNl" && ui.state.mode !== "overlay:bulkAction" && ui.state.mode !== "overlay:confirmPick" && ui.state.mode !== "focus" ? /* @__PURE__ */ jsxs22(Fragment5, { children: [
5683
- /* @__PURE__ */ jsx22(Box21, { children: stickyHeader ? /* @__PURE__ */ jsxs22(Fragment5, { children: [
5684
- /* @__PURE__ */ jsxs22(Text21, { bold: true, color: "white", children: [
5685
- " ",
5686
- stickyHeader.text
5687
- ] }),
5688
- stickyHeader.count != null ? /* @__PURE__ */ jsxs22(Text21, { color: "gray", children: [
5689
- " (",
5690
- stickyHeader.count,
5691
- ")"
5692
- ] }) : null
5693
- ] }) : null }),
5694
- /* @__PURE__ */ jsxs22(Box21, { height: viewportHeight, children: [
5695
- /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", flexGrow: 1, children: [
5696
- hasMoreAbove ? /* @__PURE__ */ jsxs22(Text21, { color: "gray", dimColor: true, children: [
5697
- " ",
5698
- "\u25B2",
5699
- " ",
5700
- aboveCount,
5701
- " more above"
5702
- ] }) : null,
5703
- visibleRows.map((row) => /* @__PURE__ */ jsx22(
5704
- RowRenderer,
5705
- {
5706
- row,
5707
- selectedId: nav.selectedId,
5708
- selfLogin: config2.board.assignee,
5709
- isMultiSelected: ui.state.mode === "multiSelect" && row.navId ? multiSelect.isSelected(row.navId) : void 0
5710
- },
5711
- row.key
5712
- )),
5713
- hasMoreBelow ? /* @__PURE__ */ jsxs22(Text21, { color: "gray", dimColor: true, children: [
5714
- " ",
5715
- "\u25BC",
5716
- " ",
5717
- belowCount,
5718
- " more below"
5719
- ] }) : null
5720
- ] }),
5721
- showDetailPanel ? /* @__PURE__ */ jsx22(Box21, { marginLeft: 1, width: detailPanelWidth, children: /* @__PURE__ */ jsx22(
5722
- DetailPanel,
5723
- {
5724
- issue: selectedItem.issue,
5725
- task: selectedItem.task,
5726
- width: detailPanelWidth,
5727
- issueRepo: selectedItem.repoName,
5728
- fetchComments: handleFetchComments,
5729
- commentsState: currentCommentsState
5730
- }
5731
- ) }) : null
5732
- ] })
5733
- ] }) : null,
5734
- /* @__PURE__ */ jsx22(ToastContainer, { toasts }),
5735
- logVisible ? /* @__PURE__ */ jsx22(ActionLog, { entries: logEntries }) : null,
5736
- /* @__PURE__ */ jsx22(
5899
+ !ui.state.helpVisible && ui.state.mode !== "overlay:status" && ui.state.mode !== "overlay:create" && ui.state.mode !== "overlay:createNl" && ui.state.mode !== "overlay:bulkAction" && ui.state.mode !== "overlay:confirmPick" && ui.state.mode !== "focus" ? /* @__PURE__ */ jsx24(
5900
+ PanelLayout,
5901
+ {
5902
+ cols: termSize.cols,
5903
+ issuesPanelHeight,
5904
+ reposPanel,
5905
+ statusesPanel,
5906
+ issuesPanel,
5907
+ detailPanel,
5908
+ activityPanel
5909
+ }
5910
+ ) : null,
5911
+ /* @__PURE__ */ jsx24(ToastContainer, { toasts }),
5912
+ logVisible ? /* @__PURE__ */ jsx24(ActionLog, { entries: logEntries }) : null,
5913
+ /* @__PURE__ */ jsx24(
5737
5914
  HintBar,
5738
5915
  {
5739
5916
  uiMode: ui.state.mode,
5917
+ activePanelId: panelFocus.activePanelId,
5740
5918
  multiSelectCount: multiSelect.count,
5741
5919
  searchQuery,
5742
5920
  mineOnly,
@@ -5758,14 +5936,18 @@ var init_dashboard = __esm({
5758
5936
  init_use_keyboard();
5759
5937
  init_use_multi_select();
5760
5938
  init_use_navigation();
5939
+ init_use_panel_focus();
5761
5940
  init_use_toast();
5762
5941
  init_use_ui_state();
5763
5942
  init_action_log();
5943
+ init_activity_panel();
5764
5944
  init_detail_panel();
5765
5945
  init_hint_bar();
5766
5946
  init_overlay_renderer();
5947
+ init_panel_layout();
5948
+ init_repos_panel();
5767
5949
  init_row_renderer();
5768
- init_tab_bar();
5950
+ init_statuses_panel();
5769
5951
  init_toast_container();
5770
5952
  PRIORITY_RANK = {
5771
5953
  "priority:critical": 0,
@@ -5773,7 +5955,7 @@ var init_dashboard = __esm({
5773
5955
  "priority:medium": 2,
5774
5956
  "priority:low": 3
5775
5957
  };
5776
- CHROME_ROWS = 6;
5958
+ CHROME_ROWS = 3;
5777
5959
  }
5778
5960
  });
5779
5961
 
@@ -5783,10 +5965,10 @@ __export(live_exports, {
5783
5965
  runLiveDashboard: () => runLiveDashboard
5784
5966
  });
5785
5967
  import { render } from "ink";
5786
- import { jsx as jsx23 } from "react/jsx-runtime";
5968
+ import { jsx as jsx25 } from "react/jsx-runtime";
5787
5969
  async function runLiveDashboard(config2, options, activeProfile) {
5788
5970
  const instance = render(
5789
- /* @__PURE__ */ jsx23(Dashboard, { config: config2, options, activeProfile: activeProfile ?? null })
5971
+ /* @__PURE__ */ jsx25(Dashboard, { config: config2, options, activeProfile: activeProfile ?? null })
5790
5972
  );
5791
5973
  setInkInstance(instance);
5792
5974
  await instance.waitUntilExit();
@@ -5813,7 +5995,7 @@ function extractSlackUrl(body) {
5813
5995
  const match = body.match(SLACK_URL_RE2);
5814
5996
  return match?.[0];
5815
5997
  }
5816
- function fetchRecentActivity(repoName, shortName) {
5998
+ function fetchRecentActivity(repoName, shortName2) {
5817
5999
  try {
5818
6000
  const output = execFileSync4(
5819
6001
  "gh",
@@ -5867,7 +6049,7 @@ function fetchRecentActivity(repoName, shortName) {
5867
6049
  }
5868
6050
  events.push({
5869
6051
  type: eventType,
5870
- repoShortName: shortName,
6052
+ repoShortName: shortName2,
5871
6053
  issueNumber: ev.number,
5872
6054
  actor: ev.actor,
5873
6055
  summary,
@@ -6009,7 +6191,7 @@ __export(format_static_exports, {
6009
6191
  renderBoardJson: () => renderBoardJson,
6010
6192
  renderStaticBoard: () => renderStaticBoard
6011
6193
  });
6012
- function truncate3(s, max) {
6194
+ function truncate2(s, max) {
6013
6195
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
6014
6196
  }
6015
6197
  function issueAssignee(issue, selfLogin) {
@@ -6022,13 +6204,13 @@ function issueAssignee(issue, selfLogin) {
6022
6204
  }
6023
6205
  function formatIssueLine(issue, selfLogin, maxTitle) {
6024
6206
  const num = theme.text.accent(`#${String(issue.number).padEnd(5)}`);
6025
- const title = truncate3(issue.title, maxTitle);
6207
+ const title = truncate2(issue.title, maxTitle);
6026
6208
  const assignee = issueAssignee(issue, selfLogin);
6027
6209
  return ` ${num} ${title.padEnd(maxTitle)} ${assignee}`;
6028
6210
  }
6029
6211
  function formatTaskLine(task2, maxTitle) {
6030
6212
  const pri = task2.priority === 5 /* High */ ? theme.priority.high("[!]") : task2.priority === 3 /* Medium */ ? theme.priority.medium("[~]") : " ";
6031
- const title = truncate3(task2.title, maxTitle);
6213
+ const title = truncate2(task2.title, maxTitle);
6032
6214
  const due = task2.dueDate ? formatDueDate(task2.dueDate) : "";
6033
6215
  return ` ${pri} ${title.padEnd(maxTitle)} ${theme.text.secondary(due)}`;
6034
6216
  }
@@ -6449,13 +6631,13 @@ Configuring ${repoName}...`);
6449
6631
  } else {
6450
6632
  completionAction = { type: "closeIssue" };
6451
6633
  }
6452
- const shortName = await input({
6634
+ const shortName2 = await input({
6453
6635
  message: ` Short name for ${repoName}:`,
6454
6636
  default: name
6455
6637
  });
6456
6638
  repos.push({
6457
6639
  name: repoName,
6458
- shortName,
6640
+ shortName: shortName2,
6459
6641
  projectNumber,
6460
6642
  statusFieldId,
6461
6643
  ...dueDateFieldId ? { dueDateFieldId } : {},
@@ -6949,7 +7131,7 @@ function resolveProjectId(projectId) {
6949
7131
  process.exit(1);
6950
7132
  }
6951
7133
  var program = new Command();
6952
- program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.9.3").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
7134
+ program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.11.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
6953
7135
  const opts = thisCommand.opts();
6954
7136
  if (opts.json) setFormat("json");
6955
7137
  if (opts.human) setFormat("human");
@@ -7144,7 +7326,7 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7144
7326
  console.error(`Repo "${name}" is already configured.`);
7145
7327
  process.exit(1);
7146
7328
  }
7147
- const shortName = name.split("/")[1] ?? name;
7329
+ const shortName2 = name.split("/")[1] ?? name;
7148
7330
  let completionAction;
7149
7331
  switch (opts.completionType) {
7150
7332
  case "addLabel":
@@ -7172,7 +7354,7 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7172
7354
  }
7173
7355
  const newRepo = {
7174
7356
  name,
7175
- shortName,
7357
+ shortName: shortName2,
7176
7358
  projectNumber: Number.parseInt(opts.projectNumber, 10),
7177
7359
  statusFieldId: opts.statusFieldId,
7178
7360
  completionAction
@@ -7182,7 +7364,7 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7182
7364
  if (useJson()) {
7183
7365
  jsonOut({ ok: true, message: `Added ${name}`, data: newRepo });
7184
7366
  } else {
7185
- console.log(`Added ${shortName} \u2192 ${name}`);
7367
+ console.log(`Added ${shortName2} \u2192 ${name}`);
7186
7368
  }
7187
7369
  });
7188
7370
  config.command("repos:rm <name>").description("Remove a repository from tracking").action((name) => {