@ondrej-svec/hog 1.10.0 → 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,8 +2008,13 @@ function useKeyboard({
1999
2008
  selectedRepoStatusOptionsLength,
2000
2009
  actions,
2001
2010
  onSearchEscape,
2002
- tabNav,
2003
- statusNav
2011
+ panelFocus,
2012
+ reposNav,
2013
+ statusesNav,
2014
+ activityNav,
2015
+ onRepoEnter,
2016
+ onStatusEnter,
2017
+ onActivityEnter
2004
2018
  }) {
2005
2019
  const {
2006
2020
  exit,
@@ -2036,19 +2050,41 @@ function useKeyboard({
2036
2050
  }
2037
2051
  if (ui.canNavigate) {
2038
2052
  if (input2 === "j" || key.downArrow) {
2039
- 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
+ }
2040
2069
  return;
2041
2070
  }
2042
2071
  if (input2 === "k" || key.upArrow) {
2043
- nav.moveUp();
2044
- return;
2045
- }
2046
- if (key.tab) {
2047
- if (ui.state.mode === "multiSelect") {
2048
- multiSelect.clear();
2049
- 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;
2050
2087
  }
2051
- key.shift ? tabNav.prev() : tabNav.next();
2052
2088
  return;
2053
2089
  }
2054
2090
  }
@@ -2078,8 +2114,8 @@ function useKeyboard({
2078
2114
  if (input2 === "r" && handleErrorAction("retry")) return;
2079
2115
  if (ui.canAct) {
2080
2116
  const digit = parseInt(input2, 10);
2081
- if (!Number.isNaN(digit) && digit >= 1 && digit <= tabNav.count) {
2082
- tabNav.jumpTo(digit - 1);
2117
+ if (!Number.isNaN(digit) && digit >= 0 && digit <= 4) {
2118
+ panelFocus.focusPanel(digit);
2083
2119
  return;
2084
2120
  }
2085
2121
  if (input2 === "/") {
@@ -2096,14 +2132,6 @@ function useKeyboard({
2096
2132
  refresh();
2097
2133
  return;
2098
2134
  }
2099
- if (input2 === "s") {
2100
- statusNav?.next();
2101
- return;
2102
- }
2103
- if (input2 === "S") {
2104
- statusNav?.prev();
2105
- return;
2106
- }
2107
2135
  if (input2 === "o") {
2108
2136
  handleSlack();
2109
2137
  return;
@@ -2187,7 +2215,22 @@ function useKeyboard({
2187
2215
  return;
2188
2216
  }
2189
2217
  if (key.return) {
2190
- 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
+ }
2191
2234
  return;
2192
2235
  }
2193
2236
  }
@@ -2195,8 +2238,13 @@ function useKeyboard({
2195
2238
  [
2196
2239
  ui,
2197
2240
  nav,
2198
- tabNav,
2199
- statusNav,
2241
+ panelFocus,
2242
+ reposNav,
2243
+ statusesNav,
2244
+ activityNav,
2245
+ onRepoEnter,
2246
+ onStatusEnter,
2247
+ onActivityEnter,
2200
2248
  exit,
2201
2249
  refresh,
2202
2250
  handleSlack,
@@ -2502,26 +2550,42 @@ var init_use_navigation = __esm({
2502
2550
  }
2503
2551
  });
2504
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
+
2505
2569
  // src/board/hooks/use-toast.ts
2506
- 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";
2507
2571
  function useToast() {
2508
- const [toasts, setToasts] = useState4([]);
2572
+ const [toasts, setToasts] = useState5([]);
2509
2573
  const timersRef = useRef6(/* @__PURE__ */ new Map());
2510
- const clearTimer = useCallback7((id) => {
2574
+ const clearTimer = useCallback8((id) => {
2511
2575
  const timer = timersRef.current.get(id);
2512
2576
  if (timer) {
2513
2577
  clearTimeout(timer);
2514
2578
  timersRef.current.delete(id);
2515
2579
  }
2516
2580
  }, []);
2517
- const removeToast = useCallback7(
2581
+ const removeToast = useCallback8(
2518
2582
  (id) => {
2519
2583
  clearTimer(id);
2520
2584
  setToasts((prev) => prev.filter((t) => t.id !== id));
2521
2585
  },
2522
2586
  [clearTimer]
2523
2587
  );
2524
- const addToast = useCallback7(
2588
+ const addToast = useCallback8(
2525
2589
  (t) => {
2526
2590
  const id = `toast-${++nextId}`;
2527
2591
  const newToast = { ...t, id, createdAt: Date.now() };
@@ -2550,25 +2614,25 @@ function useToast() {
2550
2614
  [removeToast, clearTimer]
2551
2615
  );
2552
2616
  const toast = {
2553
- info: useCallback7(
2617
+ info: useCallback8(
2554
2618
  (message) => {
2555
2619
  addToast({ type: "info", message });
2556
2620
  },
2557
2621
  [addToast]
2558
2622
  ),
2559
- success: useCallback7(
2623
+ success: useCallback8(
2560
2624
  (message) => {
2561
2625
  addToast({ type: "success", message });
2562
2626
  },
2563
2627
  [addToast]
2564
2628
  ),
2565
- error: useCallback7(
2629
+ error: useCallback8(
2566
2630
  (message, retry) => {
2567
2631
  addToast(retry ? { type: "error", message, retry } : { type: "error", message });
2568
2632
  },
2569
2633
  [addToast]
2570
2634
  ),
2571
- loading: useCallback7(
2635
+ loading: useCallback8(
2572
2636
  (message) => {
2573
2637
  const id = addToast({ type: "loading", message });
2574
2638
  return {
@@ -2585,7 +2649,7 @@ function useToast() {
2585
2649
  [addToast, removeToast]
2586
2650
  )
2587
2651
  };
2588
- const handleErrorAction = useCallback7(
2652
+ const handleErrorAction = useCallback8(
2589
2653
  (action) => {
2590
2654
  const errorToast = toasts.find((t) => t.type === "error");
2591
2655
  if (!errorToast) return false;
@@ -2615,7 +2679,7 @@ var init_use_toast = __esm({
2615
2679
  });
2616
2680
 
2617
2681
  // src/board/hooks/use-ui-state.ts
2618
- import { useCallback as useCallback8, useReducer as useReducer2 } from "react";
2682
+ import { useCallback as useCallback9, useReducer as useReducer2 } from "react";
2619
2683
  function enterStatusMode(state) {
2620
2684
  if (state.mode !== "normal" && state.mode !== "overlay:bulkAction") return state;
2621
2685
  const previousMode = state.mode === "overlay:bulkAction" ? "multiSelect" : "normal";
@@ -2689,22 +2753,22 @@ function useUIState() {
2689
2753
  const [state, dispatch] = useReducer2(uiReducer, INITIAL_STATE2);
2690
2754
  return {
2691
2755
  state,
2692
- enterSearch: useCallback8(() => dispatch({ type: "ENTER_SEARCH" }), []),
2693
- enterComment: useCallback8(() => dispatch({ type: "ENTER_COMMENT" }), []),
2694
- enterStatus: useCallback8(() => dispatch({ type: "ENTER_STATUS" }), []),
2695
- enterCreate: useCallback8(() => dispatch({ type: "ENTER_CREATE" }), []),
2696
- enterCreateNl: useCallback8(() => dispatch({ type: "ENTER_CREATE_NL" }), []),
2697
- enterLabel: useCallback8(() => dispatch({ type: "ENTER_LABEL" }), []),
2698
- enterMultiSelect: useCallback8(() => dispatch({ type: "ENTER_MULTI_SELECT" }), []),
2699
- enterBulkAction: useCallback8(() => dispatch({ type: "ENTER_BULK_ACTION" }), []),
2700
- enterConfirmPick: useCallback8(() => dispatch({ type: "ENTER_CONFIRM_PICK" }), []),
2701
- enterFocus: useCallback8(() => dispatch({ type: "ENTER_FOCUS" }), []),
2702
- enterFuzzyPicker: useCallback8(() => dispatch({ type: "ENTER_FUZZY_PICKER" }), []),
2703
- enterEditIssue: useCallback8(() => dispatch({ type: "ENTER_EDIT_ISSUE" }), []),
2704
- toggleHelp: useCallback8(() => dispatch({ type: "TOGGLE_HELP" }), []),
2705
- exitOverlay: useCallback8(() => dispatch({ type: "EXIT_OVERLAY" }), []),
2706
- exitToNormal: useCallback8(() => dispatch({ type: "EXIT_TO_NORMAL" }), []),
2707
- 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" }), []),
2708
2772
  canNavigate: canNavigate(state),
2709
2773
  canAct: canAct(state),
2710
2774
  isOverlay: isOverlay(state)
@@ -2724,7 +2788,7 @@ var init_use_ui_state = __esm({
2724
2788
 
2725
2789
  // src/board/components/action-log.tsx
2726
2790
  import { Box, Text } from "ink";
2727
- import { useEffect as useEffect2, useState as useState5 } from "react";
2791
+ import { useEffect as useEffect2, useState as useState6 } from "react";
2728
2792
  import { jsx, jsxs } from "react/jsx-runtime";
2729
2793
  function relativeTime(ago) {
2730
2794
  const seconds = Math.floor((Date.now() - ago) / 1e3);
@@ -2745,7 +2809,7 @@ function statusColor(status) {
2745
2809
  return "yellow";
2746
2810
  }
2747
2811
  function ActionLog({ entries }) {
2748
- const [, setTick] = useState5(0);
2812
+ const [, setTick] = useState6(0);
2749
2813
  useEffect2(() => {
2750
2814
  const id = setInterval(() => setTick((t) => t + 1), 5e3);
2751
2815
  return () => clearInterval(id);
@@ -2784,14 +2848,61 @@ var init_action_log = __esm({
2784
2848
  }
2785
2849
  });
2786
2850
 
2787
- // src/board/components/detail-panel.tsx
2851
+ // src/board/components/activity-panel.tsx
2788
2852
  import { Box as Box2, Text as Text2 } from "ink";
2789
- import { useEffect as useEffect3 } from "react";
2790
- import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2791
- function truncateLines(text, maxLines) {
2792
- const lines = text.split("\n").slice(0, maxLines);
2793
- 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
+ );
2794
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";
2795
2906
  function stripMarkdown(text) {
2796
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();
2797
2908
  }
@@ -2810,11 +2921,11 @@ function BodySection({
2810
2921
  issueNumber
2811
2922
  }) {
2812
2923
  const { text, remaining } = formatBody(body, 15);
2813
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
2814
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2815
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "--- Description ---" }),
2816
- /* @__PURE__ */ jsx2(Text2, { wrap: "wrap", children: text }),
2817
- 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: [
2818
2929
  "... (",
2819
2930
  remaining,
2820
2931
  " more lines \u2014 gh issue view ",
@@ -2835,185 +2946,143 @@ function formatCommentAge(createdAt) {
2835
2946
  }
2836
2947
  function DetailPanel({
2837
2948
  issue,
2838
- task: task2,
2839
2949
  width,
2950
+ isActive,
2840
2951
  commentsState,
2841
2952
  fetchComments,
2842
2953
  issueRepo
2843
2954
  }) {
2955
+ const borderColor = isActive ? "cyan" : "gray";
2844
2956
  useEffect3(() => {
2845
2957
  if (!(issue && fetchComments && issueRepo)) return;
2846
2958
  if (commentsState !== null && commentsState !== void 0) return;
2847
2959
  fetchComments(issueRepo, issue.number);
2848
2960
  }, [issue, issueRepo, fetchComments, commentsState]);
2849
- if (!(issue || task2)) {
2850
- return /* @__PURE__ */ jsx2(
2851
- Box2,
2852
- {
2853
- width,
2854
- borderStyle: "single",
2855
- borderColor: "gray",
2856
- flexDirection: "column",
2857
- paddingX: 1,
2858
- children: /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "No item selected" })
2859
- }
2860
- );
2861
- }
2862
- if (issue) {
2863
- return /* @__PURE__ */ jsxs2(
2864
- Box2,
2961
+ if (!issue) {
2962
+ return /* @__PURE__ */ jsxs3(
2963
+ Box3,
2865
2964
  {
2866
2965
  width,
2867
2966
  borderStyle: "single",
2868
- borderColor: "cyan",
2967
+ borderColor,
2869
2968
  flexDirection: "column",
2870
2969
  paddingX: 1,
2871
2970
  children: [
2872
- /* @__PURE__ */ jsxs2(Text2, { color: "cyan", bold: true, children: [
2873
- "#",
2874
- issue.number,
2875
- " ",
2876
- issue.title
2877
- ] }),
2878
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2879
- /* @__PURE__ */ jsxs2(Box2, { children: [
2880
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "State: " }),
2881
- /* @__PURE__ */ jsx2(Text2, { color: issue.state === "open" ? "green" : "red", children: issue.state })
2882
- ] }),
2883
- (issue.assignees ?? []).length > 0 ? /* @__PURE__ */ jsxs2(Box2, { children: [
2884
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Assignees: " }),
2885
- /* @__PURE__ */ jsx2(Text2, { children: (issue.assignees ?? []).map((a) => a.login).join(", ") })
2886
- ] }) : null,
2887
- issue.labels.length > 0 ? /* @__PURE__ */ jsxs2(Box2, { children: [
2888
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Labels: " }),
2889
- /* @__PURE__ */ jsx2(Text2, { children: issue.labels.map((l) => l.name).join(", ") })
2890
- ] }) : null,
2891
- issue.projectStatus ? /* @__PURE__ */ jsxs2(Box2, { children: [
2892
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Status: " }),
2893
- /* @__PURE__ */ jsx2(Text2, { color: "magenta", children: issue.projectStatus })
2894
- ] }) : null,
2895
- issue.targetDate ? /* @__PURE__ */ jsxs2(Box2, { children: [
2896
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Target: " }),
2897
- /* @__PURE__ */ jsx2(Text2, { children: issue.targetDate })
2898
- ] }) : null,
2899
- /* @__PURE__ */ jsxs2(Box2, { children: [
2900
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Updated: " }),
2901
- /* @__PURE__ */ jsx2(Text2, { children: new Date(issue.updatedAt).toLocaleString() })
2902
- ] }),
2903
- issue.slackThreadUrl ? /* @__PURE__ */ jsxs2(Box2, { children: [
2904
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Slack: " }),
2905
- /* @__PURE__ */ jsx2(Text2, { color: "blue", children: countSlackLinks(issue.body) > 1 ? `${countSlackLinks(issue.body)} links (s opens first)` : "thread (s to open)" })
2906
- ] }) : null,
2907
- issue.body ? /* @__PURE__ */ jsx2(BodySection, { body: issue.body, issueNumber: issue.number }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
2908
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2909
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "(no description)" })
2910
- ] }),
2911
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2912
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "--- Comments ---" }),
2913
- 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) => (
2914
- // biome-ignore lint/suspicious/noArrayIndexKey: stable list
2915
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
2916
- /* @__PURE__ */ jsxs2(Text2, { color: "cyan", children: [
2917
- "@",
2918
- comment.author.login,
2919
- " \xB7 ",
2920
- formatCommentAge(comment.createdAt)
2921
- ] }),
2922
- /* @__PURE__ */ jsxs2(Text2, { wrap: "wrap", children: [
2923
- " ",
2924
- comment.body.split("\n")[0]
2925
- ] })
2926
- ] }, i)
2927
- )) : /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "fetching comments..." }),
2928
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2929
- /* @__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" })
2930
2973
  ]
2931
2974
  }
2932
2975
  );
2933
2976
  }
2934
- const t = task2;
2935
- return /* @__PURE__ */ jsxs2(
2936
- Box2,
2977
+ return /* @__PURE__ */ jsxs3(
2978
+ Box3,
2937
2979
  {
2938
2980
  width,
2939
2981
  borderStyle: "single",
2940
- borderColor: "yellow",
2982
+ borderColor,
2941
2983
  flexDirection: "column",
2942
2984
  paddingX: 1,
2943
2985
  children: [
2944
- /* @__PURE__ */ jsx2(Text2, { color: "yellow", bold: true, children: t.title }),
2945
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2946
- /* @__PURE__ */ jsxs2(Box2, { children: [
2947
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Priority: " }),
2948
- /* @__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
2949
2992
  ] }),
2950
- t.dueDate ? /* @__PURE__ */ jsxs2(Box2, { children: [
2951
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Due: " }),
2952
- /* @__PURE__ */ jsx2(Text2, { children: new Date(t.dueDate).toLocaleDateString() })
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 })
2997
+ ] }),
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(", ") })
2953
3001
  ] }) : null,
2954
- (t.tags ?? []).length > 0 ? /* @__PURE__ */ jsxs2(Box2, { children: [
2955
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Tags: " }),
2956
- /* @__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(", ") })
2957
3005
  ] }) : null,
2958
- t.content ? /* @__PURE__ */ jsxs2(Fragment, { children: [
2959
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2960
- /* @__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 })
2961
3009
  ] }) : null,
2962
- (t.items ?? []).length > 0 ? /* @__PURE__ */ jsxs2(Fragment, { children: [
2963
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2964
- /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Checklist:" }),
2965
- t.items.slice(0, 5).map((item) => /* @__PURE__ */ jsxs2(Text2, { children: [
2966
- item.status === 2 ? "\u2611" : "\u2610",
2967
- " ",
2968
- item.title
2969
- ] }, item.id)),
2970
- t.items.length > 5 ? /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
2971
- "...and ",
2972
- t.items.length - 5,
2973
- " more"
2974
- ] }) : null
2975
- ] }) : 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 })
2976
3045
  ]
2977
3046
  }
2978
3047
  );
2979
3048
  }
2980
- var SLACK_URL_RE, PRIORITY_LABELS2;
3049
+ var SLACK_URL_RE;
2981
3050
  var init_detail_panel = __esm({
2982
3051
  "src/board/components/detail-panel.tsx"() {
2983
3052
  "use strict";
2984
- init_types();
2985
3053
  SLACK_URL_RE = /https:\/\/[^/]+\.slack\.com\/archives\/[A-Z0-9]+\/p[0-9]+/gi;
2986
- PRIORITY_LABELS2 = {
2987
- [5 /* High */]: "High",
2988
- [3 /* Medium */]: "Medium",
2989
- [1 /* Low */]: "Low",
2990
- [0 /* None */]: "None"
2991
- };
2992
3054
  }
2993
3055
  });
2994
3056
 
2995
3057
  // src/board/components/hint-bar.tsx
2996
- import { Box as Box3, Text as Text3 } from "ink";
2997
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2998
- 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
+ }) {
2999
3068
  if (uiMode === "multiSelect") {
3000
- return /* @__PURE__ */ jsxs3(Box3, { children: [
3001
- /* @__PURE__ */ jsxs3(Text3, { color: "cyan", bold: true, children: [
3069
+ return /* @__PURE__ */ jsxs4(Box4, { children: [
3070
+ /* @__PURE__ */ jsxs4(Text4, { color: "cyan", bold: true, children: [
3002
3071
  "[MULTI-SELECT] ",
3003
3072
  multiSelectCount,
3004
3073
  " selected"
3005
3074
  ] }),
3006
- /* @__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" })
3007
3076
  ] });
3008
3077
  }
3009
3078
  if (uiMode === "focus") {
3010
- 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" }) });
3011
3080
  }
3012
3081
  if (uiMode === "search") {
3013
- return /* @__PURE__ */ jsxs3(Box3, { children: [
3014
- /* @__PURE__ */ jsx3(Text3, { color: "yellow", bold: true, children: "[SEARCH]" }),
3015
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " type to filter Enter:confirm Esc:clear" }),
3016
- 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: [
3017
3086
  ' "',
3018
3087
  searchQuery,
3019
3088
  '"'
@@ -3021,19 +3090,22 @@ function HintBar({ uiMode, multiSelectCount, searchQuery, mineOnly, hasUndoable
3021
3090
  ] });
3022
3091
  }
3023
3092
  if (uiMode === "overlay:fuzzyPicker") {
3024
- 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" }) });
3025
3094
  }
3026
3095
  if (uiMode.startsWith("overlay:")) {
3027
- return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "j/k:nav Enter:select Esc:cancel" }) });
3028
- }
3029
- return /* @__PURE__ */ jsxs3(Box3, { children: [
3030
- /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
3031
- "j/k:nav Tab:repo s/S:status Enter:open m:status c:comment F:find t:@me e:edit",
3032
- hasUndoable ? " u:undo" : "",
3033
- " ?:more q:quit"
3034
- ] }),
3035
- mineOnly ? /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: " filter:@me" }) : null,
3036
- 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: [
3037
3109
  ' filter:"',
3038
3110
  searchQuery,
3039
3111
  '"'
@@ -3047,9 +3119,9 @@ var init_hint_bar = __esm({
3047
3119
  });
3048
3120
 
3049
3121
  // src/board/components/bulk-action-menu.tsx
3050
- import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
3051
- import { useState as useState6 } from "react";
3052
- 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";
3053
3125
  function getMenuItems(selectionType) {
3054
3126
  if (selectionType === "github") {
3055
3127
  return [
@@ -3068,7 +3140,7 @@ function getMenuItems(selectionType) {
3068
3140
  }
3069
3141
  function BulkActionMenu({ count, selectionType, onSelect, onCancel }) {
3070
3142
  const items = getMenuItems(selectionType);
3071
- const [selectedIdx, setSelectedIdx] = useState6(0);
3143
+ const [selectedIdx, setSelectedIdx] = useState7(0);
3072
3144
  useInput2((input2, key) => {
3073
3145
  if (key.escape) return onCancel();
3074
3146
  if (key.return) {
@@ -3084,13 +3156,13 @@ function BulkActionMenu({ count, selectionType, onSelect, onCancel }) {
3084
3156
  }
3085
3157
  });
3086
3158
  if (items.length === 0) {
3087
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
3088
- /* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "No bulk actions for mixed selection types." }),
3089
- /* @__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" })
3090
3162
  ] });
3091
3163
  }
3092
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
3093
- /* @__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: [
3094
3166
  "Bulk action (",
3095
3167
  count,
3096
3168
  " selected):"
@@ -3098,12 +3170,12 @@ function BulkActionMenu({ count, selectionType, onSelect, onCancel }) {
3098
3170
  items.map((item, i) => {
3099
3171
  const isSelected = i === selectedIdx;
3100
3172
  const prefix = isSelected ? "> " : " ";
3101
- return /* @__PURE__ */ jsxs4(Text4, { ...isSelected ? { color: "cyan" } : {}, children: [
3173
+ return /* @__PURE__ */ jsxs5(Text5, { ...isSelected ? { color: "cyan" } : {}, children: [
3102
3174
  prefix,
3103
3175
  item.label
3104
3176
  ] }, item.action.type);
3105
3177
  }),
3106
- /* @__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" })
3107
3179
  ] });
3108
3180
  }
3109
3181
  var init_bulk_action_menu = __esm({
@@ -3133,9 +3205,9 @@ import { mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as wr
3133
3205
  import { tmpdir } from "os";
3134
3206
  import { join as join4 } from "path";
3135
3207
  import { TextInput } from "@inkjs/ui";
3136
- import { Box as Box5, Text as Text5, useInput as useInput3, useStdin } from "ink";
3137
- import { useEffect as useEffect4, useRef as useRef7, useState as useState7 } from "react";
3138
- 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";
3139
3211
  function CommentInput({
3140
3212
  issueNumber,
3141
3213
  onSubmit,
@@ -3143,8 +3215,8 @@ function CommentInput({
3143
3215
  onPauseRefresh,
3144
3216
  onResumeRefresh
3145
3217
  }) {
3146
- const [value, setValue] = useState7("");
3147
- const [editing, setEditing] = useState7(false);
3218
+ const [value, setValue] = useState8("");
3219
+ const [editing, setEditing] = useState8(false);
3148
3220
  const { setRawMode } = useStdin();
3149
3221
  const onSubmitRef = useRef7(onSubmit);
3150
3222
  const onCancelRef = useRef7(onCancel);
@@ -3202,19 +3274,19 @@ function CommentInput({
3202
3274
  }
3203
3275
  }, [editing, value, setRawMode]);
3204
3276
  if (editing) {
3205
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
3277
+ return /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsxs6(Text6, { color: "cyan", children: [
3206
3278
  "Opening editor for #",
3207
3279
  issueNumber,
3208
3280
  "\u2026"
3209
3281
  ] }) });
3210
3282
  }
3211
- return /* @__PURE__ */ jsxs5(Box5, { children: [
3212
- /* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
3283
+ return /* @__PURE__ */ jsxs6(Box6, { children: [
3284
+ /* @__PURE__ */ jsxs6(Text6, { color: "cyan", children: [
3213
3285
  "comment #",
3214
3286
  issueNumber,
3215
3287
  ": "
3216
3288
  ] }),
3217
- /* @__PURE__ */ jsx5(
3289
+ /* @__PURE__ */ jsx6(
3218
3290
  TextInput,
3219
3291
  {
3220
3292
  defaultValue: value,
@@ -3236,16 +3308,16 @@ var init_comment_input = __esm({
3236
3308
  });
3237
3309
 
3238
3310
  // src/board/components/confirm-prompt.tsx
3239
- import { Box as Box6, Text as Text6, useInput as useInput4 } from "ink";
3240
- 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";
3241
3313
  function ConfirmPrompt({ message, onConfirm, onCancel }) {
3242
3314
  useInput4((input2, key) => {
3243
3315
  if (input2 === "y" || input2 === "Y") return onConfirm();
3244
3316
  if (input2 === "n" || input2 === "N" || key.escape) return onCancel();
3245
3317
  });
3246
- return /* @__PURE__ */ jsxs6(Box6, { children: [
3247
- /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: message }),
3248
- /* @__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)" })
3249
3321
  ] });
3250
3322
  }
3251
3323
  var init_confirm_prompt = __esm({
@@ -3256,9 +3328,9 @@ var init_confirm_prompt = __esm({
3256
3328
 
3257
3329
  // src/board/components/label-picker.tsx
3258
3330
  import { Spinner } from "@inkjs/ui";
3259
- import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
3260
- import { useEffect as useEffect5, useRef as useRef8, useState as useState8 } from "react";
3261
- 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";
3262
3334
  function LabelPicker({
3263
3335
  repo,
3264
3336
  currentLabels,
@@ -3267,11 +3339,11 @@ function LabelPicker({
3267
3339
  onCancel,
3268
3340
  onError
3269
3341
  }) {
3270
- const [labels, setLabels] = useState8(labelCache[repo] ?? null);
3271
- const [loading, setLoading] = useState8(labels === null);
3272
- const [fetchAttempted, setFetchAttempted] = useState8(false);
3273
- const [selected, setSelected] = useState8(new Set(currentLabels));
3274
- 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);
3275
3347
  const submittedRef = useRef8(false);
3276
3348
  useEffect5(() => {
3277
3349
  if (labels !== null || fetchAttempted) return;
@@ -3333,21 +3405,21 @@ function LabelPicker({
3333
3405
  }
3334
3406
  });
3335
3407
  if (loading) {
3336
- return /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Spinner, { label: "Fetching labels..." }) });
3408
+ return /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Spinner, { label: "Fetching labels..." }) });
3337
3409
  }
3338
3410
  const allLabels = labels ?? [];
3339
3411
  if (allLabels.length === 0) {
3340
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
3341
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: true, children: "Labels:" }),
3342
- /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "No labels in this repo" }),
3343
- /* @__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" })
3344
3416
  ] });
3345
3417
  }
3346
3418
  const repoLabelNames = new Set(allLabels.map((l) => l.name));
3347
3419
  const orphanedLabels = currentLabels.filter((l) => !repoLabelNames.has(l));
3348
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
3349
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: true, children: "Labels (Space:toggle Enter:confirm Esc:cancel):" }),
3350
- 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: [
3351
3423
  selected.has(name) ? "[x]" : "[ ]",
3352
3424
  " ",
3353
3425
  name,
@@ -3356,7 +3428,7 @@ function LabelPicker({
3356
3428
  allLabels.map((label, i) => {
3357
3429
  const isSel = i === cursor;
3358
3430
  const isChecked = selected.has(label.name);
3359
- return /* @__PURE__ */ jsxs7(Text7, { ...isSel ? { color: "cyan" } : {}, children: [
3431
+ return /* @__PURE__ */ jsxs8(Text8, { ...isSel ? { color: "cyan" } : {}, children: [
3360
3432
  isSel ? ">" : " ",
3361
3433
  " ",
3362
3434
  isChecked ? "[x]" : "[ ]",
@@ -3375,9 +3447,9 @@ var init_label_picker = __esm({
3375
3447
 
3376
3448
  // src/board/components/create-issue-form.tsx
3377
3449
  import { TextInput as TextInput2 } from "@inkjs/ui";
3378
- import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
3379
- import { useState as useState9 } from "react";
3380
- 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";
3381
3453
  function CreateIssueForm({
3382
3454
  repos,
3383
3455
  defaultRepo,
@@ -3389,9 +3461,9 @@ function CreateIssueForm({
3389
3461
  0,
3390
3462
  repos.findIndex((r) => r.name === defaultRepo)
3391
3463
  ) : 0;
3392
- const [repoIdx, setRepoIdx] = useState9(defaultRepoIdx);
3393
- const [title, setTitle] = useState9("");
3394
- const [field, setField] = useState9("title");
3464
+ const [repoIdx, setRepoIdx] = useState10(defaultRepoIdx);
3465
+ const [title, setTitle] = useState10("");
3466
+ const [field, setField] = useState10("title");
3395
3467
  useInput6((input2, key) => {
3396
3468
  if (field === "labels") return;
3397
3469
  if (key.escape) return onCancel();
@@ -3408,15 +3480,15 @@ function CreateIssueForm({
3408
3480
  });
3409
3481
  const selectedRepo = repos[repoIdx];
3410
3482
  if (field === "labels" && selectedRepo) {
3411
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3412
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "Create Issue \u2014 Add Labels (optional)" }),
3413
- /* @__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: [
3414
3486
  "Repo: ",
3415
3487
  selectedRepo.shortName,
3416
3488
  " Title: ",
3417
3489
  title
3418
3490
  ] }),
3419
- /* @__PURE__ */ jsx8(
3491
+ /* @__PURE__ */ jsx9(
3420
3492
  LabelPicker,
3421
3493
  {
3422
3494
  repo: selectedRepo.name,
@@ -3441,12 +3513,12 @@ function CreateIssueForm({
3441
3513
  )
3442
3514
  ] });
3443
3515
  }
3444
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3445
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "Create Issue" }),
3446
- /* @__PURE__ */ jsxs8(Box8, { children: [
3447
- /* @__PURE__ */ jsx8(Text8, { dimColor: field !== "repo", children: "Repo: " }),
3448
- repos.map((r, i) => /* @__PURE__ */ jsx8(
3449
- 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,
3450
3522
  {
3451
3523
  ...i === repoIdx ? { color: "cyan", bold: true } : {},
3452
3524
  dimColor: field !== "repo",
@@ -3454,11 +3526,11 @@ function CreateIssueForm({
3454
3526
  },
3455
3527
  r.name
3456
3528
  )),
3457
- 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
3458
3530
  ] }),
3459
- /* @__PURE__ */ jsxs8(Box8, { children: [
3460
- /* @__PURE__ */ jsx8(Text8, { dimColor: field !== "title", children: "Title: " }),
3461
- field === "title" ? /* @__PURE__ */ jsx8(
3531
+ /* @__PURE__ */ jsxs9(Box9, { children: [
3532
+ /* @__PURE__ */ jsx9(Text9, { dimColor: field !== "title", children: "Title: " }),
3533
+ field === "title" ? /* @__PURE__ */ jsx9(
3462
3534
  TextInput2,
3463
3535
  {
3464
3536
  defaultValue: title,
@@ -3475,9 +3547,9 @@ function CreateIssueForm({
3475
3547
  }
3476
3548
  }
3477
3549
  }
3478
- ) : /* @__PURE__ */ jsx8(Text8, { children: title || "(empty)" })
3550
+ ) : /* @__PURE__ */ jsx9(Text9, { children: title || "(empty)" })
3479
3551
  ] }),
3480
- /* @__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" })
3481
3553
  ] });
3482
3554
  }
3483
3555
  var init_create_issue_form = __esm({
@@ -3492,9 +3564,9 @@ import { spawnSync as spawnSync2 } from "child_process";
3492
3564
  import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
3493
3565
  import { tmpdir as tmpdir2 } from "os";
3494
3566
  import { join as join5 } from "path";
3495
- import { Box as Box9, Text as Text9, useStdin as useStdin2 } from "ink";
3496
- import { useEffect as useEffect6, useRef as useRef9, useState as useState10 } from "react";
3497
- 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";
3498
3570
  function buildEditorFile(issue, repoName, statusOptions, repoLabels) {
3499
3571
  const statusNames = statusOptions.map((o) => o.name).join(", ");
3500
3572
  const labelNames = repoLabels.map((l) => l.name).join(", ");
@@ -3577,7 +3649,7 @@ function EditIssueOverlay({
3577
3649
  onToastError,
3578
3650
  onPushEntry
3579
3651
  }) {
3580
- const [editing, setEditing] = useState10(true);
3652
+ const [editing, setEditing] = useState11(true);
3581
3653
  const { setRawMode } = useStdin2();
3582
3654
  const onDoneRef = useRef9(onDone);
3583
3655
  const onPauseRef = useRef9(onPauseRefresh);
@@ -3736,7 +3808,7 @@ function EditIssueOverlay({
3736
3808
  onPushEntry
3737
3809
  ]);
3738
3810
  if (!editing) return null;
3739
- return /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsxs9(Text9, { color: "cyan", children: [
3811
+ return /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsxs10(Text10, { color: "cyan", children: [
3740
3812
  "Opening editor for #",
3741
3813
  issue.number,
3742
3814
  "\u2026"
@@ -3752,17 +3824,17 @@ var init_edit_issue_overlay = __esm({
3752
3824
  });
3753
3825
 
3754
3826
  // src/board/components/focus-mode.tsx
3755
- import { Box as Box10, Text as Text10, useInput as useInput7 } from "ink";
3756
- import { useCallback as useCallback9, useEffect as useEffect7, useRef as useRef10, useState as useState11 } from "react";
3757
- 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";
3758
3830
  function formatTime(secs) {
3759
3831
  const m = Math.floor(secs / 60);
3760
3832
  const s = secs % 60;
3761
3833
  return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
3762
3834
  }
3763
3835
  function FocusMode({ label, durationSec, onExit, onEndAction }) {
3764
- const [remaining, setRemaining] = useState11(durationSec);
3765
- const [timerDone, setTimerDone] = useState11(false);
3836
+ const [remaining, setRemaining] = useState12(durationSec);
3837
+ const [timerDone, setTimerDone] = useState12(false);
3766
3838
  const bellSentRef = useRef10(false);
3767
3839
  useEffect7(() => {
3768
3840
  if (timerDone) return;
@@ -3784,7 +3856,7 @@ function FocusMode({ label, durationSec, onExit, onEndAction }) {
3784
3856
  process.stdout.write("\x07");
3785
3857
  }
3786
3858
  }, [timerDone]);
3787
- const handleInput = useCallback9(
3859
+ const handleInput = useCallback10(
3788
3860
  (input2, key) => {
3789
3861
  if (key.escape) {
3790
3862
  if (timerDone) {
@@ -3811,23 +3883,23 @@ function FocusMode({ label, durationSec, onExit, onEndAction }) {
3811
3883
  );
3812
3884
  useInput7(handleInput);
3813
3885
  if (timerDone) {
3814
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
3815
- /* @__PURE__ */ jsxs10(Box10, { children: [
3816
- /* @__PURE__ */ jsx10(Text10, { color: "green", bold: true, children: "Focus complete!" }),
3817
- /* @__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: [
3818
3890
  " ",
3819
3891
  label
3820
3892
  ] })
3821
3893
  ] }),
3822
- /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
3823
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "[c]" }),
3824
- /* @__PURE__ */ jsx10(Text10, { children: " Continue " }),
3825
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "[b]" }),
3826
- /* @__PURE__ */ jsx10(Text10, { children: " Break " }),
3827
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "[d]" }),
3828
- /* @__PURE__ */ jsx10(Text10, { children: " Done " }),
3829
- /* @__PURE__ */ jsx10(Text10, { color: "gray", children: "[Esc]" }),
3830
- /* @__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" })
3831
3903
  ] })
3832
3904
  ] });
3833
3905
  }
@@ -3835,21 +3907,21 @@ function FocusMode({ label, durationSec, onExit, onEndAction }) {
3835
3907
  const barWidth = 20;
3836
3908
  const filled = Math.round(progress * barWidth);
3837
3909
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
3838
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
3839
- /* @__PURE__ */ jsxs10(Box10, { children: [
3840
- /* @__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: [
3841
3913
  "Focus:",
3842
3914
  " "
3843
3915
  ] }),
3844
- /* @__PURE__ */ jsx10(Text10, { children: label })
3916
+ /* @__PURE__ */ jsx11(Text11, { children: label })
3845
3917
  ] }),
3846
- /* @__PURE__ */ jsxs10(Box10, { children: [
3847
- /* @__PURE__ */ jsx10(Text10, { color: "magenta", children: bar }),
3848
- /* @__PURE__ */ jsx10(Text10, { children: " " }),
3849
- /* @__PURE__ */ jsx10(Text10, { bold: true, children: formatTime(remaining) }),
3850
- /* @__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" })
3851
3923
  ] }),
3852
- /* @__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" })
3853
3925
  ] });
3854
3926
  }
3855
3927
  var init_focus_mode = __esm({
@@ -3861,18 +3933,18 @@ var init_focus_mode = __esm({
3861
3933
  // src/board/components/fuzzy-picker.tsx
3862
3934
  import { TextInput as TextInput3 } from "@inkjs/ui";
3863
3935
  import { Fzf } from "fzf";
3864
- import { Box as Box11, Text as Text11, useInput as useInput8 } from "ink";
3865
- import { useMemo as useMemo2, useState as useState12 } from "react";
3866
- 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";
3867
3939
  function keepCursorVisible(cursor, offset, visible) {
3868
3940
  if (cursor < offset) return cursor;
3869
3941
  if (cursor >= offset + visible) return cursor - visible + 1;
3870
3942
  return offset;
3871
3943
  }
3872
3944
  function FuzzyPicker({ repos, onSelect, onClose }) {
3873
- const [query, setQuery] = useState12("");
3874
- const [cursor, setCursor] = useState12(0);
3875
- const [scrollOffset, setScrollOffset] = useState12(0);
3945
+ const [query, setQuery] = useState13("");
3946
+ const [cursor, setCursor] = useState13(0);
3947
+ const [scrollOffset, setScrollOffset] = useState13(0);
3876
3948
  const allIssues = useMemo2(() => {
3877
3949
  const items = [];
3878
3950
  for (const rd of repos) {
@@ -3955,13 +4027,13 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3955
4027
  });
3956
4028
  const visibleResults = results.slice(scrollOffset, scrollOffset + VISIBLE);
3957
4029
  const totalCount = results.length;
3958
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
3959
- /* @__PURE__ */ jsxs11(Box11, { children: [
3960
- /* @__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: [
3961
4033
  "Find issue",
3962
4034
  " "
3963
4035
  ] }),
3964
- /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
4036
+ /* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
3965
4037
  "(",
3966
4038
  totalCount,
3967
4039
  " match",
@@ -3969,14 +4041,14 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3969
4041
  ")",
3970
4042
  " "
3971
4043
  ] }),
3972
- /* @__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" })
3973
4045
  ] }),
3974
- /* @__PURE__ */ jsxs11(Box11, { children: [
3975
- /* @__PURE__ */ jsxs11(Text11, { color: "yellow", children: [
4046
+ /* @__PURE__ */ jsxs12(Box12, { children: [
4047
+ /* @__PURE__ */ jsxs12(Text12, { color: "yellow", children: [
3976
4048
  ">",
3977
4049
  " "
3978
4050
  ] }),
3979
- /* @__PURE__ */ jsx11(
4051
+ /* @__PURE__ */ jsx12(
3980
4052
  TextInput3,
3981
4053
  {
3982
4054
  defaultValue: query,
@@ -3993,7 +4065,7 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3993
4065
  }
3994
4066
  )
3995
4067
  ] }),
3996
- scrollOffset > 0 ? /* @__PURE__ */ jsxs11(Text11, { color: "gray", dimColor: true, children: [
4068
+ scrollOffset > 0 ? /* @__PURE__ */ jsxs12(Text12, { color: "gray", dimColor: true, children: [
3997
4069
  "\u25B2 ",
3998
4070
  scrollOffset,
3999
4071
  " more above"
@@ -4002,7 +4074,7 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
4002
4074
  const isSelected = scrollOffset + idx === cursor;
4003
4075
  const labelStr = issue.labels ? ` [${issue.labels.split(" ").slice(0, 2).join("] [")}]` : "";
4004
4076
  const assigneeStr = issue.assignee ? ` @${issue.assignee.split(" ")[0]}` : "";
4005
- 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: [
4006
4078
  ">",
4007
4079
  " ",
4008
4080
  issue.repoShortName,
@@ -4012,7 +4084,7 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
4012
4084
  issue.title,
4013
4085
  labelStr,
4014
4086
  assigneeStr
4015
- ] }) : /* @__PURE__ */ jsxs11(Text11, { children: [
4087
+ ] }) : /* @__PURE__ */ jsxs12(Text12, { children: [
4016
4088
  " ",
4017
4089
  issue.repoShortName,
4018
4090
  "#",
@@ -4023,12 +4095,12 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
4023
4095
  assigneeStr
4024
4096
  ] }) }, issue.navId);
4025
4097
  }),
4026
- totalCount === 0 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
4098
+ totalCount === 0 ? /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
4027
4099
  'No issues match "',
4028
4100
  query,
4029
4101
  '"'
4030
4102
  ] }) : null,
4031
- 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: [
4032
4104
  "\u25BC ",
4033
4105
  results.length - scrollOffset - VISIBLE,
4034
4106
  " more below"
@@ -4042,29 +4114,29 @@ var init_fuzzy_picker = __esm({
4042
4114
  });
4043
4115
 
4044
4116
  // src/board/components/help-overlay.tsx
4045
- import { Box as Box12, Text as Text12, useInput as useInput9 } from "ink";
4046
- 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";
4047
4119
  function HelpOverlay({ currentMode, onClose }) {
4048
4120
  useInput9((_input, key) => {
4049
4121
  if (key.escape) onClose();
4050
4122
  });
4051
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4052
- /* @__PURE__ */ jsxs12(Box12, { justifyContent: "space-between", children: [
4053
- /* @__PURE__ */ jsx12(Text12, { color: "cyan", bold: true, children: "Keyboard Shortcuts" }),
4054
- /* @__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: [
4055
4127
  "mode: ",
4056
4128
  currentMode
4057
4129
  ] })
4058
4130
  ] }),
4059
- /* @__PURE__ */ jsx12(Text12, { children: " " }),
4060
- SHORTCUTS.map((group) => /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
4061
- /* @__PURE__ */ jsx12(Text12, { color: "yellow", bold: true, children: group.category }),
4062
- group.items.map((item) => /* @__PURE__ */ jsxs12(Box12, { children: [
4063
- /* @__PURE__ */ jsx12(Box12, { width: 16, children: /* @__PURE__ */ jsx12(Text12, { color: "green", children: item.key }) }),
4064
- /* @__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 })
4065
4137
  ] }, item.key))
4066
4138
  ] }, group.category)),
4067
- /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Press ? or Esc to close" })
4139
+ /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Press ? or Esc to close" })
4068
4140
  ] });
4069
4141
  }
4070
4142
  var SHORTCUTS;
@@ -4130,9 +4202,9 @@ import { mkdtempSync as mkdtempSync3, readFileSync as readFileSync6, rmSync as r
4130
4202
  import { tmpdir as tmpdir3 } from "os";
4131
4203
  import { join as join6 } from "path";
4132
4204
  import { Spinner as Spinner2, TextInput as TextInput4 } from "@inkjs/ui";
4133
- import { Box as Box13, Text as Text13, useInput as useInput10, useStdin as useStdin3 } from "ink";
4134
- import { useCallback as useCallback10, useEffect as useEffect8, useRef as useRef11, useState as useState13 } from "react";
4135
- 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";
4136
4208
  function NlCreateOverlay({
4137
4209
  repos,
4138
4210
  defaultRepoName,
@@ -4143,13 +4215,13 @@ function NlCreateOverlay({
4143
4215
  onResumeRefresh,
4144
4216
  onLlmFallback
4145
4217
  }) {
4146
- const [, setInput] = useState13("");
4147
- const [isParsing, setIsParsing] = useState13(false);
4148
- const [parsed, setParsed] = useState13(null);
4149
- const [parseError, setParseError] = useState13(null);
4150
- const [step, setStep] = useState13("input");
4151
- const [body, setBody] = useState13("");
4152
- 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);
4153
4225
  const submittedRef = useRef11(false);
4154
4226
  const parseParamsRef = useRef11(null);
4155
4227
  const onSubmitRef = useRef11(onSubmit);
@@ -4165,7 +4237,7 @@ function NlCreateOverlay({
4165
4237
  0,
4166
4238
  repos.findIndex((r) => r.name === defaultRepoName)
4167
4239
  ) : 0;
4168
- const [repoIdx, setRepoIdx] = useState13(defaultRepoIdx);
4240
+ const [repoIdx, setRepoIdx] = useState14(defaultRepoIdx);
4169
4241
  const selectedRepo = repos[repoIdx];
4170
4242
  useInput10((inputChar, key) => {
4171
4243
  if (isParsing || editingBody) return;
@@ -4225,7 +4297,7 @@ function NlCreateOverlay({
4225
4297
  setEditingBody(false);
4226
4298
  }
4227
4299
  }, [editingBody, body, setRawMode]);
4228
- const handleInputSubmit = useCallback10(
4300
+ const handleInputSubmit = useCallback11(
4229
4301
  (text) => {
4230
4302
  const trimmed = text.trim();
4231
4303
  if (!trimmed) return;
@@ -4258,27 +4330,27 @@ function NlCreateOverlay({
4258
4330
  });
4259
4331
  }, [isParsing, onLlmFallback]);
4260
4332
  if (isParsing) {
4261
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4262
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4263
- /* @__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..." })
4264
4336
  ] });
4265
4337
  }
4266
4338
  if (parsed && step === "body") {
4267
4339
  if (editingBody) {
4268
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4269
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4270
- /* @__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" })
4271
4343
  ] });
4272
4344
  }
4273
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4274
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4275
- /* @__PURE__ */ jsxs13(Box13, { children: [
4276
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Title: " }),
4277
- /* @__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 })
4278
4350
  ] }),
4279
- /* @__PURE__ */ jsxs13(Box13, { children: [
4280
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: "body: " }),
4281
- /* @__PURE__ */ jsx13(
4351
+ /* @__PURE__ */ jsxs14(Box14, { children: [
4352
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: "body: " }),
4353
+ /* @__PURE__ */ jsx14(
4282
4354
  TextInput4,
4283
4355
  {
4284
4356
  defaultValue: body,
@@ -4300,45 +4372,45 @@ function NlCreateOverlay({
4300
4372
  }
4301
4373
  )
4302
4374
  ] }),
4303
- /* @__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" })
4304
4376
  ] });
4305
4377
  }
4306
4378
  if (parsed) {
4307
4379
  const labels = [...parsed.labels];
4308
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4309
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4310
- /* @__PURE__ */ jsxs13(Box13, { children: [
4311
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Repo: " }),
4312
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: selectedRepo?.shortName ?? "(none)" }),
4313
- 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
4314
4386
  ] }),
4315
- /* @__PURE__ */ jsxs13(Box13, { children: [
4316
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Title: " }),
4317
- /* @__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 })
4318
4390
  ] }),
4319
- labels.length > 0 ? /* @__PURE__ */ jsxs13(Box13, { children: [
4320
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Labels: " }),
4321
- /* @__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(", ") })
4322
4394
  ] }) : null,
4323
- parsed.assignee ? /* @__PURE__ */ jsxs13(Box13, { children: [
4324
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Assignee: " }),
4325
- /* @__PURE__ */ jsxs13(Text13, { children: [
4395
+ parsed.assignee ? /* @__PURE__ */ jsxs14(Box14, { children: [
4396
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Assignee: " }),
4397
+ /* @__PURE__ */ jsxs14(Text14, { children: [
4326
4398
  "@",
4327
4399
  parsed.assignee
4328
4400
  ] })
4329
4401
  ] }) : null,
4330
- parsed.dueDate ? /* @__PURE__ */ jsxs13(Box13, { children: [
4331
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Due: " }),
4332
- /* @__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) })
4333
4405
  ] }) : null,
4334
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Enter:add body Esc:cancel" })
4406
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Enter:add body Esc:cancel" })
4335
4407
  ] });
4336
4408
  }
4337
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4338
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", bold: true, children: "\u2728 What do you need to do?" }),
4339
- /* @__PURE__ */ jsxs13(Box13, { children: [
4340
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: "> " }),
4341
- /* @__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(
4342
4414
  TextInput4,
4343
4415
  {
4344
4416
  placeholder: "fix login bug #bug #priority:high @me due friday",
@@ -4347,8 +4419,8 @@ function NlCreateOverlay({
4347
4419
  }
4348
4420
  )
4349
4421
  ] }),
4350
- parseError ? /* @__PURE__ */ jsx13(Text13, { color: "red", children: parseError }) : null,
4351
- /* @__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" })
4352
4424
  ] });
4353
4425
  }
4354
4426
  function buildLabelList(parsed) {
@@ -4368,12 +4440,12 @@ var init_nl_create_overlay = __esm({
4368
4440
 
4369
4441
  // src/board/components/search-bar.tsx
4370
4442
  import { TextInput as TextInput5 } from "@inkjs/ui";
4371
- import { Box as Box14, Text as Text14 } from "ink";
4372
- 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";
4373
4445
  function SearchBar({ defaultValue, onChange, onSubmit }) {
4374
- return /* @__PURE__ */ jsxs14(Box14, { children: [
4375
- /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: "/" }),
4376
- /* @__PURE__ */ jsx14(
4446
+ return /* @__PURE__ */ jsxs15(Box15, { children: [
4447
+ /* @__PURE__ */ jsx15(Text15, { color: "yellow", children: "/" }),
4448
+ /* @__PURE__ */ jsx15(
4377
4449
  TextInput5,
4378
4450
  {
4379
4451
  defaultValue,
@@ -4391,9 +4463,9 @@ var init_search_bar = __esm({
4391
4463
  });
4392
4464
 
4393
4465
  // src/board/components/status-picker.tsx
4394
- import { Box as Box15, Text as Text15, useInput as useInput11 } from "ink";
4395
- import { useRef as useRef12, useState as useState14 } from "react";
4396
- 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";
4397
4469
  function isTerminal(name) {
4398
4470
  return TERMINAL_STATUS_RE.test(name);
4399
4471
  }
@@ -4440,11 +4512,11 @@ function StatusPicker({
4440
4512
  onCancel,
4441
4513
  showTerminalStatuses = true
4442
4514
  }) {
4443
- const [selectedIdx, setSelectedIdx] = useState14(() => {
4515
+ const [selectedIdx, setSelectedIdx] = useState15(() => {
4444
4516
  const idx = options.findIndex((o) => o.name === currentStatus);
4445
4517
  return idx >= 0 ? idx : 0;
4446
4518
  });
4447
- const [confirmingTerminal, setConfirmingTerminal] = useState14(false);
4519
+ const [confirmingTerminal, setConfirmingTerminal] = useState15(false);
4448
4520
  const submittedRef = useRef12(false);
4449
4521
  useInput11((input2, key) => {
4450
4522
  if (confirmingTerminal) {
@@ -4469,26 +4541,26 @@ function StatusPicker({
4469
4541
  });
4470
4542
  if (confirmingTerminal) {
4471
4543
  const opt = options[selectedIdx];
4472
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4473
- /* @__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: [
4474
4546
  "Mark as ",
4475
4547
  opt?.name,
4476
4548
  "?"
4477
4549
  ] }),
4478
- /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "This will close the issue on GitHub." }),
4479
- /* @__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]" })
4480
4552
  ] });
4481
4553
  }
4482
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4483
- /* @__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:" }),
4484
4556
  options.map((opt, i) => {
4485
4557
  const isCurrent = opt.name === currentStatus;
4486
4558
  const isSelected = i === selectedIdx;
4487
4559
  const terminal = isTerminal(opt.name) && showTerminalStatuses;
4488
4560
  const prefix = isSelected ? "> " : " ";
4489
4561
  const suffix = isCurrent ? " (current)" : terminal ? " (Done)" : "";
4490
- return /* @__PURE__ */ jsxs15(
4491
- Text15,
4562
+ return /* @__PURE__ */ jsxs16(
4563
+ Text16,
4492
4564
  {
4493
4565
  ...isSelected ? { color: "cyan" } : terminal ? { color: "yellow" } : {},
4494
4566
  dimColor: isCurrent,
@@ -4501,7 +4573,7 @@ function StatusPicker({
4501
4573
  opt.id
4502
4574
  );
4503
4575
  }),
4504
- /* @__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" })
4505
4577
  ] });
4506
4578
  }
4507
4579
  var init_status_picker = __esm({
@@ -4512,7 +4584,7 @@ var init_status_picker = __esm({
4512
4584
  });
4513
4585
 
4514
4586
  // src/board/components/overlay-renderer.tsx
4515
- 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";
4516
4588
  function OverlayRenderer({
4517
4589
  uiState,
4518
4590
  config: config2,
@@ -4553,9 +4625,9 @@ function OverlayRenderer({
4553
4625
  onPushEntry
4554
4626
  }) {
4555
4627
  const { mode, helpVisible } = uiState;
4556
- return /* @__PURE__ */ jsxs16(Fragment2, { children: [
4557
- helpVisible ? /* @__PURE__ */ jsx16(HelpOverlay, { currentMode: mode, onClose: onToggleHelp }) : null,
4558
- 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(
4559
4631
  StatusPicker,
4560
4632
  {
4561
4633
  options: selectedRepoStatusOptions,
@@ -4564,7 +4636,7 @@ function OverlayRenderer({
4564
4636
  onCancel: onExitOverlay
4565
4637
  }
4566
4638
  ) : null,
4567
- mode === "overlay:create" ? /* @__PURE__ */ jsx16(
4639
+ mode === "overlay:create" ? /* @__PURE__ */ jsx17(
4568
4640
  CreateIssueForm,
4569
4641
  {
4570
4642
  repos: config2.repos,
@@ -4574,7 +4646,7 @@ function OverlayRenderer({
4574
4646
  labelCache
4575
4647
  }
4576
4648
  ) : null,
4577
- mode === "overlay:confirmPick" ? /* @__PURE__ */ jsx16(
4649
+ mode === "overlay:confirmPick" ? /* @__PURE__ */ jsx17(
4578
4650
  ConfirmPrompt,
4579
4651
  {
4580
4652
  message: "Pick this issue?",
@@ -4582,7 +4654,7 @@ function OverlayRenderer({
4582
4654
  onCancel: onCancelPick
4583
4655
  }
4584
4656
  ) : null,
4585
- mode === "overlay:bulkAction" ? /* @__PURE__ */ jsx16(
4657
+ mode === "overlay:bulkAction" ? /* @__PURE__ */ jsx17(
4586
4658
  BulkActionMenu,
4587
4659
  {
4588
4660
  count: multiSelectCount,
@@ -4591,7 +4663,7 @@ function OverlayRenderer({
4591
4663
  onCancel: onExitOverlay
4592
4664
  }
4593
4665
  ) : null,
4594
- mode === "focus" && focusLabel ? /* @__PURE__ */ jsx16(
4666
+ mode === "focus" && focusLabel ? /* @__PURE__ */ jsx17(
4595
4667
  FocusMode,
4596
4668
  {
4597
4669
  label: focusLabel,
@@ -4601,7 +4673,7 @@ function OverlayRenderer({
4601
4673
  },
4602
4674
  focusKey
4603
4675
  ) : null,
4604
- mode === "overlay:label" && selectedIssue && defaultRepo ? /* @__PURE__ */ jsx16(
4676
+ mode === "overlay:label" && selectedIssue && defaultRepo ? /* @__PURE__ */ jsx17(
4605
4677
  LabelPicker,
4606
4678
  {
4607
4679
  repo: defaultRepo,
@@ -4612,8 +4684,8 @@ function OverlayRenderer({
4612
4684
  onError: onLabelError
4613
4685
  }
4614
4686
  ) : null,
4615
- mode === "search" ? /* @__PURE__ */ jsx16(SearchBar, { defaultValue: searchQuery, onChange: onSearchChange, onSubmit: onSearchSubmit }) : null,
4616
- 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(
4617
4689
  CommentInput,
4618
4690
  {
4619
4691
  issueNumber: selectedIssue.number,
@@ -4623,8 +4695,8 @@ function OverlayRenderer({
4623
4695
  onResumeRefresh
4624
4696
  }
4625
4697
  ) : null,
4626
- mode === "overlay:fuzzyPicker" ? /* @__PURE__ */ jsx16(FuzzyPicker, { repos, onSelect: onFuzzySelect, onClose: onFuzzyClose }) : null,
4627
- mode === "overlay:createNl" ? /* @__PURE__ */ jsx16(
4698
+ mode === "overlay:fuzzyPicker" ? /* @__PURE__ */ jsx17(FuzzyPicker, { repos, onSelect: onFuzzySelect, onClose: onFuzzyClose }) : null,
4699
+ mode === "overlay:createNl" ? /* @__PURE__ */ jsx17(
4628
4700
  NlCreateOverlay,
4629
4701
  {
4630
4702
  repos: config2.repos,
@@ -4637,7 +4709,7 @@ function OverlayRenderer({
4637
4709
  onLlmFallback
4638
4710
  }
4639
4711
  ) : null,
4640
- mode === "overlay:editIssue" && selectedIssue && selectedRepoName ? /* @__PURE__ */ jsx16(
4712
+ mode === "overlay:editIssue" && selectedIssue && selectedRepoName ? /* @__PURE__ */ jsx17(
4641
4713
  EditIssueOverlay,
4642
4714
  {
4643
4715
  issue: selectedIssue,
@@ -4673,9 +4745,106 @@ var init_overlay_renderer = __esm({
4673
4745
  }
4674
4746
  });
4675
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
+
4676
4845
  // src/board/components/issue-row.tsx
4677
- import { Box as Box16, Text as Text16 } from "ink";
4678
- 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";
4679
4848
  function truncate(s, max) {
4680
4849
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
4681
4850
  }
@@ -4716,30 +4885,30 @@ function IssueRow({ issue, selfLogin, isSelected }) {
4716
4885
  const labels = (issue.labels ?? []).slice(0, 2);
4717
4886
  const target = formatTargetDate(issue.targetDate);
4718
4887
  const titleStr = truncate(issue.title, 42).padEnd(42);
4719
- return /* @__PURE__ */ jsxs17(Box16, { children: [
4720
- isSelected ? /* @__PURE__ */ jsx17(Text16, { color: "cyan", bold: true, children: "\u25B6 " }) : /* @__PURE__ */ jsx17(Text16, { children: " " }),
4721
- /* @__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: [
4722
4891
  "#",
4723
4892
  String(issue.number).padEnd(5)
4724
4893
  ] }),
4725
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4726
- isSelected ? /* @__PURE__ */ jsx17(Text16, { color: "white", bold: true, children: titleStr }) : /* @__PURE__ */ jsx17(Text16, { children: titleStr }),
4727
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4728
- /* @__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: [
4729
4898
  i > 0 ? " " : "",
4730
- /* @__PURE__ */ jsxs17(Text16, { color: labelColor(l.name), children: [
4899
+ /* @__PURE__ */ jsxs20(Text18, { color: labelColor(l.name), children: [
4731
4900
  "[",
4732
4901
  truncate(l.name, 12),
4733
4902
  "]"
4734
4903
  ] })
4735
4904
  ] }, l.name)) }),
4736
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4737
- /* @__PURE__ */ jsx17(Text16, { color: assigneeColor, children: assigneeText.padEnd(14) }),
4738
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4739
- /* @__PURE__ */ jsx17(Text16, { color: "gray", children: timeAgo2(issue.updatedAt).padStart(4) }),
4740
- target.text ? /* @__PURE__ */ jsxs17(Fragment3, { children: [
4741
- /* @__PURE__ */ jsx17(Text16, { children: " " }),
4742
- /* @__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 })
4743
4912
  ] }) : null
4744
4913
  ] });
4745
4914
  }
@@ -4762,68 +4931,21 @@ var init_issue_row = __esm({
4762
4931
  }
4763
4932
  });
4764
4933
 
4765
- // src/board/components/task-row.tsx
4766
- import { Box as Box17, Text as Text17 } from "ink";
4767
- import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
4768
- function truncate2(s, max) {
4769
- return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
4770
- }
4771
- function formatDue2(dateStr) {
4772
- if (!dateStr) return { text: "", color: "gray" };
4773
- const d = new Date(dateStr);
4774
- const days = Math.ceil((d.getTime() - Date.now()) / 864e5);
4775
- if (days < 0) return { text: `${Math.abs(days)}d overdue`, color: "red" };
4776
- if (days === 0) return { text: "today", color: "yellow" };
4777
- if (days === 1) return { text: "tomorrow", color: "white" };
4778
- if (days <= 7) return { text: `in ${days}d`, color: "white" };
4779
- return {
4780
- text: d.toLocaleDateString("en-US", { month: "short", day: "numeric" }),
4781
- color: "gray"
4782
- };
4783
- }
4784
- function TaskRow({ task: task2, isSelected }) {
4785
- const pri = PRIORITY_INDICATORS[task2.priority] ?? DEFAULT_PRIORITY;
4786
- const due = formatDue2(task2.dueDate);
4787
- const titleStr = truncate2(task2.title, 45).padEnd(45);
4788
- return /* @__PURE__ */ jsxs18(Box17, { children: [
4789
- isSelected ? /* @__PURE__ */ jsx18(Text17, { color: "cyan", bold: true, children: "\u25B6 " }) : /* @__PURE__ */ jsx18(Text17, { children: " " }),
4790
- /* @__PURE__ */ jsx18(Text17, { color: pri.color, children: pri.text }),
4791
- /* @__PURE__ */ jsx18(Text17, { children: " " }),
4792
- isSelected ? /* @__PURE__ */ jsx18(Text17, { color: "white", bold: true, children: titleStr }) : /* @__PURE__ */ jsx18(Text17, { children: titleStr }),
4793
- /* @__PURE__ */ jsx18(Text17, { children: " " }),
4794
- /* @__PURE__ */ jsx18(Text17, { color: due.color, children: due.text })
4795
- ] });
4796
- }
4797
- var PRIORITY_INDICATORS, DEFAULT_PRIORITY;
4798
- var init_task_row = __esm({
4799
- "src/board/components/task-row.tsx"() {
4800
- "use strict";
4801
- init_types();
4802
- PRIORITY_INDICATORS = {
4803
- [5 /* High */]: { text: "[!]", color: "red" },
4804
- [3 /* Medium */]: { text: "[~]", color: "yellow" },
4805
- [1 /* Low */]: { text: "[\u2193]", color: "blue" },
4806
- [0 /* None */]: { text: " ", color: "gray" }
4807
- };
4808
- DEFAULT_PRIORITY = { text: " ", color: "gray" };
4809
- }
4810
- });
4811
-
4812
4934
  // src/board/components/row-renderer.tsx
4813
- import { Box as Box18, Text as Text18 } from "ink";
4814
- 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";
4815
4937
  function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4816
4938
  switch (row.type) {
4817
4939
  case "sectionHeader": {
4818
4940
  const arrow = row.isCollapsed ? "\u25B6" : "\u25BC";
4819
4941
  const isSel = selectedId === row.navId;
4820
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4821
- /* @__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: [
4822
4944
  arrow,
4823
4945
  " ",
4824
4946
  row.label
4825
4947
  ] }),
4826
- /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
4948
+ /* @__PURE__ */ jsxs21(Text19, { color: "gray", children: [
4827
4949
  " ",
4828
4950
  "(",
4829
4951
  row.count,
@@ -4837,26 +4959,26 @@ function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4837
4959
  if (row.navId) {
4838
4960
  const arrow = row.isCollapsed ? "\u25B6" : "\u25BC";
4839
4961
  const isSel = selectedId === row.navId;
4840
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4841
- /* @__PURE__ */ jsxs19(Text18, { color: isSel ? "cyan" : "gray", children: [
4962
+ return /* @__PURE__ */ jsxs21(Box20, { children: [
4963
+ /* @__PURE__ */ jsxs21(Text19, { color: isSel ? "cyan" : "gray", children: [
4842
4964
  " ",
4843
4965
  arrow,
4844
4966
  " ",
4845
4967
  row.text
4846
4968
  ] }),
4847
- /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
4969
+ /* @__PURE__ */ jsxs21(Text19, { color: "gray", children: [
4848
4970
  " (",
4849
4971
  row.count,
4850
4972
  ")"
4851
4973
  ] })
4852
4974
  ] });
4853
4975
  }
4854
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4855
- /* @__PURE__ */ jsxs19(Text18, { bold: true, color: "white", children: [
4976
+ return /* @__PURE__ */ jsxs21(Box20, { children: [
4977
+ /* @__PURE__ */ jsxs21(Text19, { bold: true, color: "white", children: [
4856
4978
  " ",
4857
4979
  row.text
4858
4980
  ] }),
4859
- row.count != null ? /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
4981
+ row.count != null ? /* @__PURE__ */ jsxs21(Text19, { color: "gray", children: [
4860
4982
  " (",
4861
4983
  row.count,
4862
4984
  ")"
@@ -4865,32 +4987,25 @@ function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4865
4987
  }
4866
4988
  case "issue": {
4867
4989
  const checkbox2 = isMultiSelected != null ? isMultiSelected ? "\u2611 " : "\u2610 " : "";
4868
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4869
- checkbox2 ? /* @__PURE__ */ jsx19(Text18, { color: isMultiSelected ? "cyan" : "gray", children: checkbox2 }) : null,
4870
- /* @__PURE__ */ jsx19(IssueRow, { issue: row.issue, selfLogin, isSelected: selectedId === row.navId })
4871
- ] });
4872
- }
4873
- case "task": {
4874
- const checkbox2 = isMultiSelected != null ? isMultiSelected ? "\u2611 " : "\u2610 " : "";
4875
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4876
- checkbox2 ? /* @__PURE__ */ jsx19(Text18, { color: isMultiSelected ? "cyan" : "gray", children: checkbox2 }) : null,
4877
- /* @__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 })
4878
4993
  ] });
4879
4994
  }
4880
4995
  case "activity": {
4881
- const ago = timeAgo(row.event.timestamp);
4882
- return /* @__PURE__ */ jsxs19(Text18, { dimColor: true, children: [
4996
+ const ago = new Date(row.event.timestamp).toLocaleTimeString();
4997
+ return /* @__PURE__ */ jsxs21(Text19, { dimColor: true, children: [
4883
4998
  " ",
4884
4999
  ago,
4885
5000
  ": ",
4886
- /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
5001
+ /* @__PURE__ */ jsxs21(Text19, { color: "gray", children: [
4887
5002
  "@",
4888
5003
  row.event.actor
4889
5004
  ] }),
4890
5005
  " ",
4891
5006
  row.event.summary,
4892
5007
  " ",
4893
- /* @__PURE__ */ jsxs19(Text18, { dimColor: true, children: [
5008
+ /* @__PURE__ */ jsxs21(Text19, { dimColor: true, children: [
4894
5009
  "(",
4895
5010
  row.event.repoShortName,
4896
5011
  ")"
@@ -4898,85 +5013,66 @@ function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4898
5013
  ] });
4899
5014
  }
4900
5015
  case "error":
4901
- return /* @__PURE__ */ jsxs19(Text18, { color: "red", children: [
5016
+ return /* @__PURE__ */ jsxs21(Text19, { color: "red", children: [
4902
5017
  " Error: ",
4903
5018
  row.text
4904
5019
  ] });
4905
5020
  case "gap":
4906
- return /* @__PURE__ */ jsx19(Text18, { children: "" });
5021
+ return /* @__PURE__ */ jsx21(Text19, { children: "" });
4907
5022
  }
4908
5023
  }
4909
5024
  var init_row_renderer = __esm({
4910
5025
  "src/board/components/row-renderer.tsx"() {
4911
5026
  "use strict";
4912
- init_constants();
4913
5027
  init_issue_row();
4914
- init_task_row();
4915
5028
  }
4916
5029
  });
4917
5030
 
4918
- // src/board/components/status-tab-bar.tsx
4919
- import { Box as Box19, Text as Text19 } from "ink";
4920
- import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
4921
- function StatusTabBar({ tabs, activeTabId, totalWidth }) {
4922
- if (tabs.length === 0) return null;
4923
- return /* @__PURE__ */ jsx20(Box19, { width: totalWidth, children: tabs.map((tab) => {
4924
- const isActive = tab.id === activeTabId;
4925
- return /* @__PURE__ */ jsx20(Box19, { marginRight: 2, children: /* @__PURE__ */ jsxs20(Text19, { bold: isActive, color: isActive ? "cyan" : "gray", children: [
4926
- "[",
4927
- isActive ? "\u25BA " : "",
4928
- tab.label,
4929
- " ",
4930
- tab.count,
4931
- "]"
4932
- ] }) }, tab.id);
4933
- }) });
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
+ ] });
4934
5052
  }
4935
- var init_status_tab_bar = __esm({
4936
- "src/board/components/status-tab-bar.tsx"() {
4937
- "use strict";
4938
- }
4939
- });
4940
-
4941
- // src/board/components/tab-bar.tsx
4942
- import { Box as Box20, Text as Text20 } from "ink";
4943
- import { jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
4944
- function TabBar({ tabs, activeTabId, totalWidth }) {
4945
- return /* @__PURE__ */ jsx21(Box20, { width: totalWidth, children: tabs.map((tab, i) => {
4946
- const isActive = tab.id === activeTabId;
4947
- return /* @__PURE__ */ jsx21(Box20, { marginRight: 2, children: /* @__PURE__ */ jsxs21(Text20, { bold: isActive, color: isActive ? "cyan" : "gray", children: [
4948
- i + 1,
4949
- ":",
4950
- tab.label,
4951
- " (",
4952
- tab.count,
4953
- ")"
4954
- ] }) }, tab.id);
4955
- }) });
4956
- }
4957
- var init_tab_bar = __esm({
4958
- "src/board/components/tab-bar.tsx"() {
5053
+ var init_statuses_panel = __esm({
5054
+ "src/board/components/statuses-panel.tsx"() {
4959
5055
  "use strict";
4960
5056
  }
4961
5057
  });
4962
5058
 
4963
5059
  // src/board/components/toast-container.tsx
4964
5060
  import { Spinner as Spinner3 } from "@inkjs/ui";
4965
- import { Box as Box21, Text as Text21 } from "ink";
4966
- import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs22 } 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";
4967
5063
  function ToastContainer({ toasts }) {
4968
5064
  if (toasts.length === 0) return null;
4969
- return /* @__PURE__ */ jsx22(Box21, { flexDirection: "column", children: toasts.map((t) => /* @__PURE__ */ jsx22(Box21, { children: t.type === "loading" ? /* @__PURE__ */ jsxs22(Fragment4, { children: [
4970
- /* @__PURE__ */ jsx22(Spinner3, { label: "" }),
4971
- /* @__PURE__ */ jsxs22(Text21, { 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: [
4972
5068
  " ",
4973
5069
  t.message
4974
5070
  ] })
4975
- ] }) : /* @__PURE__ */ jsxs22(Text21, { color: TYPE_COLORS[t.type], children: [
5071
+ ] }) : /* @__PURE__ */ jsxs23(Text21, { color: TYPE_COLORS[t.type], children: [
4976
5072
  TYPE_PREFIXES[t.type],
4977
5073
  " ",
4978
5074
  t.message,
4979
- t.type === "error" ? /* @__PURE__ */ jsx22(Text21, { 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
4980
5076
  ] }) }, t.id)) });
4981
5077
  }
4982
5078
  var TYPE_COLORS, TYPE_PREFIXES;
@@ -5000,9 +5096,9 @@ var init_toast_container = __esm({
5000
5096
  // src/board/components/dashboard.tsx
5001
5097
  import { execFileSync as execFileSync3, spawnSync as spawnSync4 } from "child_process";
5002
5098
  import { Spinner as Spinner4 } from "@inkjs/ui";
5003
- import { Box as Box22, Text as Text22, useApp, useStdout } from "ink";
5004
- import { useCallback as useCallback11, useEffect as useEffect9, useMemo as useMemo3, useRef as useRef13, useState as useState15 } from "react";
5005
- import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs23 } 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";
5006
5102
  function resolveStatusGroups(statusOptions, configuredGroups) {
5007
5103
  if (configuredGroups && configuredGroups.length > 0) {
5008
5104
  return configuredGroups.map((entry) => {
@@ -5040,7 +5136,7 @@ function groupByStatus(issues) {
5040
5136
  }
5041
5137
  return groups;
5042
5138
  }
5043
- function buildBoardTree(repos, tasks, activity) {
5139
+ function buildBoardTree(repos, activity) {
5044
5140
  const sections = repos.map((rd) => {
5045
5141
  const sectionId = rd.repo.name;
5046
5142
  if (rd.error) {
@@ -5069,72 +5165,58 @@ function buildBoardTree(repos, tasks, activity) {
5069
5165
  }
5070
5166
  return { repo: rd.repo, sectionId, groups, error: null };
5071
5167
  });
5072
- return { activity, sections, tasks };
5168
+ return { activity, sections };
5073
5169
  }
5074
- function buildTabs(tree) {
5075
- const tabs = tree.sections.map(({ repo, groups }) => ({
5076
- id: repo.name,
5077
- label: repo.shortName,
5078
- count: groups.reduce((s, g) => s + g.issues.length, 0)
5079
- }));
5080
- if (tree.activity.length > 0)
5081
- tabs.push({ id: "activity", label: "Activity", count: tree.activity.length });
5082
- if (tree.tasks.length > 0)
5083
- tabs.push({ id: "ticktick", label: "Tasks", count: tree.tasks.length });
5084
- return tabs;
5085
- }
5086
- function isRepoTab(tabId) {
5087
- return tabId !== null && tabId !== "activity" && tabId !== "ticktick";
5088
- }
5089
- function buildStatusTabs(tabId, tree) {
5090
- if (!isRepoTab(tabId)) return [];
5091
- const section = tree.sections.find((s) => s.sectionId === tabId);
5092
- if (!section) return [];
5093
- return section.groups.map((g) => ({ id: g.subId, label: g.label, count: g.issues.length }));
5094
- }
5095
- function buildNavItemsForTab(tabId, tree, activeStatusId) {
5096
- if (tabId === "activity") return [];
5097
- if (tabId === "ticktick")
5098
- return tree.tasks.map((task2) => ({
5099
- id: `tt:${task2.id}`,
5100
- section: tabId,
5101
- type: "item"
5102
- }));
5103
- 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);
5104
5173
  if (!section) return [];
5105
- const activeGroup = section.groups.find((g) => g.subId === activeStatusId) ?? section.groups[0];
5174
+ const activeGroup = section.groups.find((g) => g.subId === statusGroupId) ?? section.groups[0];
5106
5175
  if (!activeGroup) return [];
5107
5176
  return activeGroup.issues.map((issue) => ({
5108
5177
  id: `gh:${section.repo.name}:${issue.number}`,
5109
- section: tabId,
5178
+ section: repoName,
5110
5179
  type: "item"
5111
5180
  }));
5112
5181
  }
5113
- function buildFlatRowsForTab(tabId, tree, activeStatusId) {
5114
- if (tabId === "activity")
5115
- return tree.activity.map((event, i) => ({
5116
- type: "activity",
5117
- key: `act:${i}`,
5118
- navId: null,
5119
- event
5120
- }));
5121
- if (tabId === "ticktick")
5122
- return tree.tasks.map((task2) => ({
5123
- type: "task",
5124
- key: `tt:${task2.id}`,
5125
- navId: `tt:${task2.id}`,
5126
- task: task2
5127
- }));
5128
- 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);
5129
5194
  if (!section) return [];
5130
- if (section.error)
5131
- return [{ type: "error", key: `error:${tabId}`, navId: null, text: section.error }];
5132
- 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) {
5133
5199
  return [
5134
- { 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
+ }
5135
5206
  ];
5136
- const activeGroup = section.groups.find((g) => g.subId === activeStatusId) ?? section.groups[0];
5207
+ }
5208
+ const activeGroup = section.groups.find((g) => g.subId === statusGroupId) ?? section.groups[0];
5137
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
+ }
5138
5220
  return activeGroup.issues.map((issue) => ({
5139
5221
  type: "issue",
5140
5222
  key: `gh:${section.repo.name}:${issue.number}`,
@@ -5161,13 +5243,13 @@ function findSelectedIssueWithRepo(repos, selectedId) {
5161
5243
  return null;
5162
5244
  }
5163
5245
  function RefreshAge({ lastRefresh }) {
5164
- const [, setTick] = useState15(0);
5246
+ const [, setTick] = useState16(0);
5165
5247
  useEffect9(() => {
5166
5248
  const id = setInterval(() => setTick((t) => t + 1), 1e4);
5167
5249
  return () => clearInterval(id);
5168
5250
  }, []);
5169
5251
  if (!lastRefresh) return null;
5170
- return /* @__PURE__ */ jsxs23(Text22, { color: refreshAgeColor(lastRefresh), children: [
5252
+ return /* @__PURE__ */ jsxs24(Text22, { color: refreshAgeColor(lastRefresh), children: [
5171
5253
  "Updated ",
5172
5254
  timeAgo(lastRefresh)
5173
5255
  ] });
@@ -5191,19 +5273,16 @@ function Dashboard({ config: config2, options, activeProfile }) {
5191
5273
  clearPendingMutation
5192
5274
  } = useData(config2, options, refreshMs);
5193
5275
  const allRepos = useMemo3(() => data?.repos ?? [], [data?.repos]);
5194
- const allTasks = useMemo3(
5195
- () => config2.ticktick.enabled ? data?.ticktick ?? [] : [],
5196
- [data?.ticktick, config2.ticktick.enabled]
5197
- );
5198
5276
  const allActivity = useMemo3(() => data?.activity ?? [], [data?.activity]);
5199
5277
  const ui = useUIState();
5200
- const [searchQuery, setSearchQuery] = useState15("");
5201
- const [mineOnly, setMineOnly] = useState15(false);
5202
- const handleToggleMine = useCallback11(() => {
5278
+ const panelFocus = usePanelFocus(3);
5279
+ const [searchQuery, setSearchQuery] = useState16("");
5280
+ const [mineOnly, setMineOnly] = useState16(false);
5281
+ const handleToggleMine = useCallback12(() => {
5203
5282
  setMineOnly((prev) => !prev);
5204
5283
  }, []);
5205
5284
  const { toasts, toast, handleErrorAction } = useToast();
5206
- const [logVisible, setLogVisible] = useState15(false);
5285
+ const [logVisible, setLogVisible] = useState16(false);
5207
5286
  const { entries: logEntries, pushEntry, undoLast, hasUndoable } = useActionLog(toast, refresh);
5208
5287
  useEffect9(() => {
5209
5288
  const last = logEntries[logEntries.length - 1];
@@ -5227,69 +5306,75 @@ function Dashboard({ config: config2, options, activeProfile }) {
5227
5306
  const q = searchQuery.toLowerCase();
5228
5307
  return filtered.map((rd) => ({ ...rd, issues: rd.issues.filter((i) => i.title.toLowerCase().includes(q)) })).filter((rd) => rd.issues.length > 0);
5229
5308
  }, [allRepos, searchQuery, mineOnly, config2.board.assignee]);
5230
- const tasks = useMemo3(() => {
5231
- if (!searchQuery) return allTasks;
5232
- const q = searchQuery.toLowerCase();
5233
- return allTasks.filter((t) => t.title.toLowerCase().includes(q));
5234
- }, [allTasks, searchQuery]);
5235
- const boardTree = useMemo3(
5236
- () => buildBoardTree(repos, tasks, allActivity),
5237
- [repos, tasks, allActivity]
5238
- );
5239
- const tabs = useMemo3(() => buildTabs(boardTree), [boardTree]);
5240
- const [activeTabId, setActiveTabId] = useState15(null);
5241
- const effectiveTabId = activeTabId ?? tabs[0]?.id ?? null;
5242
- const activeTabIdx = tabs.findIndex((t) => t.id === effectiveTabId);
5243
- const nextTab = useCallback11(() => {
5244
- if (tabs.length === 0) return;
5245
- setActiveTabId(tabs[(Math.max(activeTabIdx, 0) + 1) % tabs.length]?.id ?? null);
5246
- setActiveStatusId(null);
5247
- }, [activeTabIdx, tabs]);
5248
- const prevTab = useCallback11(() => {
5249
- if (tabs.length === 0) return;
5250
- setActiveTabId(tabs[(Math.max(activeTabIdx, 0) - 1 + tabs.length) % tabs.length]?.id ?? null);
5251
- setActiveStatusId(null);
5252
- }, [activeTabIdx, tabs]);
5253
- const jumpToTab = useCallback11(
5254
- (idx) => {
5255
- const tab = tabs[idx];
5256
- if (tab) {
5257
- setActiveTabId(tab.id);
5258
- setActiveStatusId(null);
5259
- }
5260
- },
5261
- [tabs]
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)
5262
5324
  );
5263
- const [activeStatusId, setActiveStatusId] = useState15(null);
5264
- const statusTabs = useMemo3(
5265
- () => buildStatusTabs(effectiveTabId, boardTree),
5266
- [effectiveTabId, boardTree]
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)
5267
5338
  );
5268
- const effectiveStatusId = activeStatusId ?? statusTabs[0]?.id ?? null;
5269
- const activeStatusIdx = statusTabs.findIndex((t) => t.id === effectiveStatusId);
5270
- const nextStatus = useCallback11(() => {
5271
- if (statusTabs.length === 0) return;
5272
- setActiveStatusId(
5273
- statusTabs[(Math.max(activeStatusIdx, 0) + 1) % statusTabs.length]?.id ?? null
5274
- );
5275
- }, [activeStatusIdx, statusTabs]);
5276
- const prevStatus = useCallback11(() => {
5277
- if (statusTabs.length === 0) return;
5278
- setActiveStatusId(
5279
- statusTabs[(Math.max(activeStatusIdx, 0) - 1 + statusTabs.length) % statusTabs.length]?.id ?? null
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}`)
5280
5361
  );
5281
- }, [activeStatusIdx, statusTabs]);
5362
+ if (repoIdx >= 0) {
5363
+ setSelectedRepoIdx(repoIdx);
5364
+ setSelectedStatusIdx(0);
5365
+ panelFocus.focusPanel(3);
5366
+ }
5367
+ }, [boardTree, clampedActivityIdx, panelFocus]);
5282
5368
  const navItems = useMemo3(
5283
- () => buildNavItemsForTab(effectiveTabId ?? "", boardTree, effectiveStatusId),
5284
- [effectiveTabId, boardTree, effectiveStatusId]
5369
+ () => buildNavItemsForRepo(boardTree.sections, selectedRepoName, selectedStatusGroupId),
5370
+ [boardTree.sections, selectedRepoName, selectedStatusGroupId]
5285
5371
  );
5286
5372
  const nav = useNavigation(navItems);
5287
- const getRepoForId = useCallback11((id) => {
5373
+ const getRepoForId = useCallback12((id) => {
5288
5374
  if (id.startsWith("gh:")) {
5289
5375
  const parts = id.split(":");
5290
5376
  return parts.length >= 3 ? `${parts[1]}` : null;
5291
5377
  }
5292
- if (id.startsWith("tt:")) return "ticktick";
5293
5378
  return null;
5294
5379
  }, []);
5295
5380
  const multiSelect = useMultiSelect(getRepoForId);
@@ -5313,8 +5398,8 @@ function Dashboard({ config: config2, options, activeProfile }) {
5313
5398
  const pendingPickRef = useRef13(null);
5314
5399
  const labelCacheRef = useRef13({});
5315
5400
  const commentCacheRef = useRef13({});
5316
- const [commentTick, setCommentTick] = useState15(0);
5317
- const handleFetchComments = useCallback11((repo, issueNumber) => {
5401
+ const [commentTick, setCommentTick] = useState16(0);
5402
+ const handleFetchComments = useCallback12((repo, issueNumber) => {
5318
5403
  const key = `${repo}:${issueNumber}`;
5319
5404
  if (commentCacheRef.current[key] !== void 0) return;
5320
5405
  commentCacheRef.current[key] = "loading";
@@ -5327,7 +5412,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5327
5412
  setCommentTick((t) => t + 1);
5328
5413
  });
5329
5414
  }, []);
5330
- const handleCreateIssueWithPrompt = useCallback11(
5415
+ const handleCreateIssueWithPrompt = useCallback12(
5331
5416
  (repo, title, body, dueDate, labels) => {
5332
5417
  actions.handleCreateIssue(repo, title, body, dueDate, labels).then((result) => {
5333
5418
  if (result) {
@@ -5338,7 +5423,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5338
5423
  },
5339
5424
  [actions, ui]
5340
5425
  );
5341
- const handleConfirmPick = useCallback11(() => {
5426
+ const handleConfirmPick = useCallback12(() => {
5342
5427
  const pending = pendingPickRef.current;
5343
5428
  pendingPickRef.current = null;
5344
5429
  ui.exitOverlay();
@@ -5356,12 +5441,12 @@ function Dashboard({ config: config2, options, activeProfile }) {
5356
5441
  })
5357
5442
  );
5358
5443
  }, [config2, toast, refresh, ui]);
5359
- const handleCancelPick = useCallback11(() => {
5444
+ const handleCancelPick = useCallback12(() => {
5360
5445
  pendingPickRef.current = null;
5361
5446
  ui.exitOverlay();
5362
5447
  }, [ui]);
5363
- const [focusLabel, setFocusLabel] = useState15(null);
5364
- const handleEnterFocus = useCallback11(() => {
5448
+ const [focusLabel, setFocusLabel] = useState16(null);
5449
+ const handleEnterFocus = useCallback12(() => {
5365
5450
  const id = nav.selectedId;
5366
5451
  if (!id || isHeaderId(id)) return;
5367
5452
  let label = "";
@@ -5371,20 +5456,16 @@ function Dashboard({ config: config2, options, activeProfile }) {
5371
5456
  const rc = config2.repos.find((r) => r.name === found.repoName);
5372
5457
  label = `${rc?.shortName ?? found.repoName}#${found.issue.number} \u2014 ${found.issue.title}`;
5373
5458
  }
5374
- } else if (id.startsWith("tt:")) {
5375
- const taskId = id.slice(3);
5376
- const task2 = tasks.find((t) => t.id === taskId);
5377
- if (task2) label = task2.title;
5378
5459
  }
5379
5460
  if (!label) return;
5380
5461
  setFocusLabel(label);
5381
5462
  ui.enterFocus();
5382
- }, [nav.selectedId, repos, tasks, config2.repos, ui]);
5383
- const handleFocusExit = useCallback11(() => {
5463
+ }, [nav.selectedId, repos, config2.repos, ui]);
5464
+ const handleFocusExit = useCallback12(() => {
5384
5465
  setFocusLabel(null);
5385
5466
  ui.exitToNormal();
5386
5467
  }, [ui]);
5387
- const handleFocusEndAction = useCallback11(
5468
+ const handleFocusEndAction = useCallback12(
5388
5469
  (action) => {
5389
5470
  switch (action) {
5390
5471
  case "restart":
@@ -5410,9 +5491,9 @@ function Dashboard({ config: config2, options, activeProfile }) {
5410
5491
  },
5411
5492
  [toast, ui]
5412
5493
  );
5413
- const [focusKey, setFocusKey] = useState15(0);
5494
+ const [focusKey, setFocusKey] = useState16(0);
5414
5495
  const { stdout } = useStdout();
5415
- const [termSize, setTermSize] = useState15({
5496
+ const [termSize, setTermSize] = useState16({
5416
5497
  cols: stdout?.columns ?? 80,
5417
5498
  rows: stdout?.rows ?? 24
5418
5499
  });
@@ -5424,24 +5505,27 @@ function Dashboard({ config: config2, options, activeProfile }) {
5424
5505
  stdout.off("resize", onResize);
5425
5506
  };
5426
5507
  }, [stdout]);
5427
- const showDetailPanel = termSize.cols >= 120;
5428
- 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";
5429
5511
  const overlayBarRows = ui.state.mode === "search" || ui.state.mode === "overlay:comment" ? 1 : 0;
5430
5512
  const toastRows = toasts.length;
5431
5513
  const logPaneRows = logVisible ? 4 : 0;
5432
- const chromeRows = isRepoTab(effectiveTabId) ? CHROME_ROWS_REPO : CHROME_ROWS_OTHER;
5433
- const viewportHeight = Math.max(
5434
- 5,
5435
- termSize.rows - chromeRows - overlayBarRows - toastRows - logPaneRows
5514
+ const totalPanelHeight = Math.max(
5515
+ 8,
5516
+ termSize.rows - CHROME_ROWS - overlayBarRows - toastRows - logPaneRows
5436
5517
  );
5518
+ const issuesPanelHeight = Math.max(5, totalPanelHeight - ACTIVITY_HEIGHT);
5437
5519
  const flatRows = useMemo3(
5438
- () => buildFlatRowsForTab(effectiveTabId ?? "", boardTree, effectiveStatusId),
5439
- [effectiveTabId, boardTree, effectiveStatusId]
5520
+ () => buildFlatRowsForRepo(boardTree.sections, selectedRepoName, selectedStatusGroupId),
5521
+ [boardTree.sections, selectedRepoName, selectedStatusGroupId]
5440
5522
  );
5441
5523
  const scrollRef = useRef13(0);
5442
- const prevTabIdRef = useRef13(null);
5443
- if (effectiveTabId !== prevTabIdRef.current) {
5444
- 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;
5445
5529
  scrollRef.current = 0;
5446
5530
  }
5447
5531
  const selectedRowIdx = useMemo3(
@@ -5451,35 +5535,29 @@ function Dashboard({ config: config2, options, activeProfile }) {
5451
5535
  if (selectedRowIdx >= 0) {
5452
5536
  if (selectedRowIdx < scrollRef.current) {
5453
5537
  scrollRef.current = selectedRowIdx;
5454
- } else if (selectedRowIdx >= scrollRef.current + viewportHeight) {
5455
- scrollRef.current = selectedRowIdx - viewportHeight + 1;
5538
+ } else if (selectedRowIdx >= scrollRef.current + issuesPanelHeight) {
5539
+ scrollRef.current = selectedRowIdx - issuesPanelHeight + 1;
5456
5540
  }
5457
5541
  }
5458
- const maxOffset = Math.max(0, flatRows.length - viewportHeight);
5542
+ const maxOffset = Math.max(0, flatRows.length - issuesPanelHeight);
5459
5543
  scrollRef.current = Math.max(0, Math.min(scrollRef.current, maxOffset));
5460
- const visibleRows = flatRows.slice(scrollRef.current, scrollRef.current + viewportHeight);
5544
+ const visibleRows = flatRows.slice(scrollRef.current, scrollRef.current + issuesPanelHeight);
5461
5545
  const hasMoreAbove = scrollRef.current > 0;
5462
- const hasMoreBelow = scrollRef.current + viewportHeight < flatRows.length;
5546
+ const hasMoreBelow = scrollRef.current + issuesPanelHeight < flatRows.length;
5463
5547
  const aboveCount = scrollRef.current;
5464
- const belowCount = flatRows.length - scrollRef.current - viewportHeight;
5548
+ const belowCount = flatRows.length - scrollRef.current - issuesPanelHeight;
5465
5549
  const selectedItem = useMemo3(() => {
5466
5550
  const id = nav.selectedId;
5467
- if (!id || isHeaderId(id)) return { issue: null, task: null, repoName: null };
5551
+ if (!id || isHeaderId(id)) return { issue: null, repoName: null };
5468
5552
  if (id.startsWith("gh:")) {
5469
5553
  for (const rd of repos) {
5470
5554
  for (const issue of rd.issues) {
5471
- if (`gh:${rd.repo.name}:${issue.number}` === id)
5472
- return { issue, task: null, repoName: rd.repo.name };
5555
+ if (`gh:${rd.repo.name}:${issue.number}` === id) return { issue, repoName: rd.repo.name };
5473
5556
  }
5474
5557
  }
5475
5558
  }
5476
- if (id.startsWith("tt:")) {
5477
- const taskId = id.slice(3);
5478
- const task2 = tasks.find((t) => t.id === taskId);
5479
- if (task2) return { issue: null, task: task2, repoName: null };
5480
- }
5481
- return { issue: null, task: null, repoName: null };
5482
- }, [nav.selectedId, repos, tasks]);
5559
+ return { issue: null, repoName: null };
5560
+ }, [nav.selectedId, repos]);
5483
5561
  const currentCommentsState = useMemo3(() => {
5484
5562
  if (!(selectedItem.issue && selectedItem.repoName)) return null;
5485
5563
  return commentCacheRef.current[`${selectedItem.repoName}:${selectedItem.issue.number}`] ?? null;
@@ -5490,20 +5568,20 @@ function Dashboard({ config: config2, options, activeProfile }) {
5490
5568
  }, [selectedItem.repoName, config2.repos]);
5491
5569
  const selectedRepoStatusOptions = useMemo3(() => {
5492
5570
  const repoName = multiSelect.count > 0 ? multiSelect.constrainedRepo : selectedItem.repoName;
5493
- if (!repoName || repoName === "ticktick") return [];
5571
+ if (!repoName) return [];
5494
5572
  const rd = repos.find((r) => r.repo.name === repoName);
5495
5573
  return rd?.statusOptions ?? [];
5496
5574
  }, [selectedItem.repoName, repos, multiSelect.count, multiSelect.constrainedRepo]);
5497
- const handleOpen = useCallback11(() => {
5575
+ const handleOpen = useCallback12(() => {
5498
5576
  const found = findSelectedIssueWithRepo(repos, nav.selectedId);
5499
5577
  if (found) openInBrowser(found.issue.url);
5500
5578
  }, [repos, nav.selectedId]);
5501
- const handleSlack = useCallback11(() => {
5579
+ const handleSlack = useCallback12(() => {
5502
5580
  const found = findSelectedIssueWithRepo(repos, nav.selectedId);
5503
5581
  if (!found?.issue.slackThreadUrl) return;
5504
5582
  openInBrowser(found.issue.slackThreadUrl);
5505
5583
  }, [repos, nav.selectedId]);
5506
- const handleCopyLink = useCallback11(() => {
5584
+ const handleCopyLink = useCallback12(() => {
5507
5585
  const found = findSelectedIssueWithRepo(repos, nav.selectedId);
5508
5586
  if (!found) return;
5509
5587
  const rc = config2.repos.find((r) => r.name === found.repoName);
@@ -5529,17 +5607,12 @@ function Dashboard({ config: config2, options, activeProfile }) {
5529
5607
  }
5530
5608
  }, [repos, nav.selectedId, config2.repos, toast]);
5531
5609
  const multiSelectType = useMemo3(() => {
5532
- let hasGh = false;
5533
- let hasTt = false;
5534
5610
  for (const id of multiSelect.selected) {
5535
- if (id.startsWith("gh:")) hasGh = true;
5536
- if (id.startsWith("tt:")) hasTt = true;
5611
+ if (id.startsWith("tt:")) return "ticktick";
5537
5612
  }
5538
- if (hasGh && hasTt) return "mixed";
5539
- if (hasTt) return "ticktick";
5540
5613
  return "github";
5541
5614
  }, [multiSelect.selected]);
5542
- const handleBulkAction = useCallback11(
5615
+ const handleBulkAction = useCallback12(
5543
5616
  (action) => {
5544
5617
  const ids = multiSelect.selected;
5545
5618
  switch (action.type) {
@@ -5572,10 +5645,9 @@ function Dashboard({ config: config2, options, activeProfile }) {
5572
5645
  case "statusChange":
5573
5646
  ui.enterStatus();
5574
5647
  return;
5575
- // status picker will call handleBulkStatusSelect on select
5576
5648
  case "complete":
5577
5649
  case "delete":
5578
- toast.info(`Bulk ${action.type} not yet implemented for TickTick`);
5650
+ toast.info(`Bulk ${action.type} not yet implemented`);
5579
5651
  ui.exitOverlay();
5580
5652
  multiSelect.clear();
5581
5653
  return;
@@ -5583,7 +5655,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5583
5655
  },
5584
5656
  [multiSelect, actions, ui, toast]
5585
5657
  );
5586
- const handleBulkStatusSelect = useCallback11(
5658
+ const handleBulkStatusSelect = useCallback12(
5587
5659
  (optionId) => {
5588
5660
  const ids = multiSelect.selected;
5589
5661
  ui.exitOverlay();
@@ -5599,30 +5671,28 @@ function Dashboard({ config: config2, options, activeProfile }) {
5599
5671
  },
5600
5672
  [multiSelect, actions, ui]
5601
5673
  );
5602
- const handleFuzzySelect = useCallback11(
5674
+ const handleFuzzySelect = useCallback12(
5603
5675
  (navId) => {
5604
5676
  nav.select(navId);
5605
5677
  if (navId.startsWith("gh:")) {
5606
5678
  const parts = navId.split(":");
5607
5679
  const repoName = parts[1];
5608
5680
  if (parts.length >= 3 && repoName) {
5609
- setActiveTabId(repoName);
5610
- const section = boardTree.sections.find((s) => s.sectionId === repoName);
5611
- const issueNum = parts[2] ? Number(parts[2]) : null;
5612
- const targetGroup = section?.groups.find(
5613
- (g) => g.issues.some((iss) => iss.number === issueNum)
5614
- );
5615
- setActiveStatusId(targetGroup?.subId ?? null);
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
+ }
5616
5689
  }
5617
- } else if (navId.startsWith("tt:")) {
5618
- setActiveTabId("ticktick");
5619
- setActiveStatusId(null);
5620
5690
  }
5621
5691
  ui.exitToNormal();
5622
5692
  },
5623
5693
  [nav, ui, boardTree]
5624
5694
  );
5625
- const onSearchEscape = useCallback11(() => {
5695
+ const onSearchEscape = useCallback12(() => {
5626
5696
  ui.exitOverlay();
5627
5697
  setSearchQuery("");
5628
5698
  }, [ui]);
@@ -5652,11 +5722,16 @@ function Dashboard({ config: config2, options, activeProfile }) {
5652
5722
  handleToggleLog: () => setLogVisible((v) => !v)
5653
5723
  },
5654
5724
  onSearchEscape,
5655
- tabNav: { next: nextTab, prev: prevTab, jumpTo: jumpToTab, count: tabs.length },
5656
- statusNav: isRepoTab(effectiveTabId) ? { next: nextStatus, prev: prevStatus } : null
5725
+ panelFocus,
5726
+ reposNav,
5727
+ statusesNav,
5728
+ activityNav,
5729
+ onRepoEnter,
5730
+ onStatusEnter,
5731
+ onActivityEnter
5657
5732
  });
5658
5733
  if (status === "loading" && !data) {
5659
- return /* @__PURE__ */ jsx23(Box22, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx23(Spinner4, { label: "Loading dashboard..." }) });
5734
+ return /* @__PURE__ */ jsx24(Box23, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx24(Spinner4, { label: "Loading dashboard..." }) });
5660
5735
  }
5661
5736
  const now = data?.fetchedAt ?? /* @__PURE__ */ new Date();
5662
5737
  const dateStr = now.toLocaleDateString("en-US", {
@@ -5664,36 +5739,122 @@ function Dashboard({ config: config2, options, activeProfile }) {
5664
5739
  day: "numeric",
5665
5740
  year: "numeric"
5666
5741
  });
5667
- return /* @__PURE__ */ jsxs23(Box22, { flexDirection: "column", paddingX: 1, children: [
5668
- /* @__PURE__ */ jsxs23(Box22, { children: [
5669
- /* @__PURE__ */ jsx23(Text22, { color: "cyan", bold: true, children: "HOG BOARD" }),
5670
- activeProfile ? /* @__PURE__ */ jsxs23(Text22, { 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: [
5671
5833
  " [",
5672
5834
  activeProfile,
5673
5835
  "]"
5674
5836
  ] }) : null,
5675
- /* @__PURE__ */ jsxs23(Text22, { color: "gray", children: [
5837
+ /* @__PURE__ */ jsxs24(Text22, { color: "gray", children: [
5676
5838
  " ",
5677
5839
  "\u2014",
5678
5840
  " ",
5679
5841
  dateStr
5680
5842
  ] }),
5681
- /* @__PURE__ */ jsx23(Text22, { children: " " }),
5682
- isRefreshing ? /* @__PURE__ */ jsxs23(Fragment5, { children: [
5683
- /* @__PURE__ */ jsx23(Spinner4, { label: "" }),
5684
- /* @__PURE__ */ jsx23(Text22, { color: "cyan", children: " Refreshing..." })
5685
- ] }) : /* @__PURE__ */ jsxs23(Fragment5, { children: [
5686
- /* @__PURE__ */ jsx23(RefreshAge, { lastRefresh }),
5687
- consecutiveFailures > 0 ? /* @__PURE__ */ jsx23(Text22, { 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
5688
5850
  ] }),
5689
- autoRefreshPaused ? /* @__PURE__ */ jsx23(Text22, { 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
5690
5852
  ] }),
5691
- error ? /* @__PURE__ */ jsxs23(Text22, { color: "red", children: [
5853
+ error ? /* @__PURE__ */ jsxs24(Text22, { color: "red", children: [
5692
5854
  "Error: ",
5693
5855
  error
5694
5856
  ] }) : null,
5695
- /* @__PURE__ */ jsx23(TabBar, { tabs, activeTabId: effectiveTabId, totalWidth: termSize.cols }),
5696
- /* @__PURE__ */ jsx23(
5857
+ /* @__PURE__ */ jsx24(
5697
5858
  OverlayRenderer,
5698
5859
  {
5699
5860
  uiState: ui.state,
@@ -5735,61 +5896,25 @@ function Dashboard({ config: config2, options, activeProfile }) {
5735
5896
  onPushEntry: pushEntry
5736
5897
  }
5737
5898
  ),
5738
- !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__ */ jsxs23(Fragment5, { children: [
5739
- isRepoTab(effectiveTabId) ? /* @__PURE__ */ jsx23(
5740
- StatusTabBar,
5741
- {
5742
- tabs: statusTabs,
5743
- activeTabId: effectiveStatusId,
5744
- totalWidth: termSize.cols
5745
- }
5746
- ) : null,
5747
- /* @__PURE__ */ jsxs23(Box22, { height: viewportHeight, children: [
5748
- /* @__PURE__ */ jsxs23(Box22, { flexDirection: "column", flexGrow: 1, children: [
5749
- hasMoreAbove ? /* @__PURE__ */ jsxs23(Text22, { color: "gray", dimColor: true, children: [
5750
- " ",
5751
- "\u25B2",
5752
- " ",
5753
- aboveCount,
5754
- " more above"
5755
- ] }) : null,
5756
- visibleRows.map((row) => /* @__PURE__ */ jsx23(
5757
- RowRenderer,
5758
- {
5759
- row,
5760
- selectedId: nav.selectedId,
5761
- selfLogin: config2.board.assignee,
5762
- isMultiSelected: ui.state.mode === "multiSelect" && row.navId ? multiSelect.isSelected(row.navId) : void 0
5763
- },
5764
- row.key
5765
- )),
5766
- hasMoreBelow ? /* @__PURE__ */ jsxs23(Text22, { color: "gray", dimColor: true, children: [
5767
- " ",
5768
- "\u25BC",
5769
- " ",
5770
- belowCount,
5771
- " more below"
5772
- ] }) : null
5773
- ] }),
5774
- showDetailPanel ? /* @__PURE__ */ jsx23(Box22, { marginLeft: 1, width: detailPanelWidth, children: /* @__PURE__ */ jsx23(
5775
- DetailPanel,
5776
- {
5777
- issue: selectedItem.issue,
5778
- task: selectedItem.task,
5779
- width: detailPanelWidth,
5780
- issueRepo: selectedItem.repoName,
5781
- fetchComments: handleFetchComments,
5782
- commentsState: currentCommentsState
5783
- }
5784
- ) }) : null
5785
- ] })
5786
- ] }) : null,
5787
- /* @__PURE__ */ jsx23(ToastContainer, { toasts }),
5788
- logVisible ? /* @__PURE__ */ jsx23(ActionLog, { entries: logEntries }) : null,
5789
- /* @__PURE__ */ jsx23(
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(
5790
5914
  HintBar,
5791
5915
  {
5792
5916
  uiMode: ui.state.mode,
5917
+ activePanelId: panelFocus.activePanelId,
5793
5918
  multiSelectCount: multiSelect.count,
5794
5919
  searchQuery,
5795
5920
  mineOnly,
@@ -5798,7 +5923,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5798
5923
  )
5799
5924
  ] });
5800
5925
  }
5801
- var PRIORITY_RANK, CHROME_ROWS_REPO, CHROME_ROWS_OTHER;
5926
+ var PRIORITY_RANK, CHROME_ROWS;
5802
5927
  var init_dashboard = __esm({
5803
5928
  "src/board/components/dashboard.tsx"() {
5804
5929
  "use strict";
@@ -5811,15 +5936,18 @@ var init_dashboard = __esm({
5811
5936
  init_use_keyboard();
5812
5937
  init_use_multi_select();
5813
5938
  init_use_navigation();
5939
+ init_use_panel_focus();
5814
5940
  init_use_toast();
5815
5941
  init_use_ui_state();
5816
5942
  init_action_log();
5943
+ init_activity_panel();
5817
5944
  init_detail_panel();
5818
5945
  init_hint_bar();
5819
5946
  init_overlay_renderer();
5947
+ init_panel_layout();
5948
+ init_repos_panel();
5820
5949
  init_row_renderer();
5821
- init_status_tab_bar();
5822
- init_tab_bar();
5950
+ init_statuses_panel();
5823
5951
  init_toast_container();
5824
5952
  PRIORITY_RANK = {
5825
5953
  "priority:critical": 0,
@@ -5827,8 +5955,7 @@ var init_dashboard = __esm({
5827
5955
  "priority:medium": 2,
5828
5956
  "priority:low": 3
5829
5957
  };
5830
- CHROME_ROWS_REPO = 6;
5831
- CHROME_ROWS_OTHER = 5;
5958
+ CHROME_ROWS = 3;
5832
5959
  }
5833
5960
  });
5834
5961
 
@@ -5838,10 +5965,10 @@ __export(live_exports, {
5838
5965
  runLiveDashboard: () => runLiveDashboard
5839
5966
  });
5840
5967
  import { render } from "ink";
5841
- import { jsx as jsx24 } from "react/jsx-runtime";
5968
+ import { jsx as jsx25 } from "react/jsx-runtime";
5842
5969
  async function runLiveDashboard(config2, options, activeProfile) {
5843
5970
  const instance = render(
5844
- /* @__PURE__ */ jsx24(Dashboard, { config: config2, options, activeProfile: activeProfile ?? null })
5971
+ /* @__PURE__ */ jsx25(Dashboard, { config: config2, options, activeProfile: activeProfile ?? null })
5845
5972
  );
5846
5973
  setInkInstance(instance);
5847
5974
  await instance.waitUntilExit();
@@ -5868,7 +5995,7 @@ function extractSlackUrl(body) {
5868
5995
  const match = body.match(SLACK_URL_RE2);
5869
5996
  return match?.[0];
5870
5997
  }
5871
- function fetchRecentActivity(repoName, shortName) {
5998
+ function fetchRecentActivity(repoName, shortName2) {
5872
5999
  try {
5873
6000
  const output = execFileSync4(
5874
6001
  "gh",
@@ -5922,7 +6049,7 @@ function fetchRecentActivity(repoName, shortName) {
5922
6049
  }
5923
6050
  events.push({
5924
6051
  type: eventType,
5925
- repoShortName: shortName,
6052
+ repoShortName: shortName2,
5926
6053
  issueNumber: ev.number,
5927
6054
  actor: ev.actor,
5928
6055
  summary,
@@ -6064,7 +6191,7 @@ __export(format_static_exports, {
6064
6191
  renderBoardJson: () => renderBoardJson,
6065
6192
  renderStaticBoard: () => renderStaticBoard
6066
6193
  });
6067
- function truncate3(s, max) {
6194
+ function truncate2(s, max) {
6068
6195
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
6069
6196
  }
6070
6197
  function issueAssignee(issue, selfLogin) {
@@ -6077,13 +6204,13 @@ function issueAssignee(issue, selfLogin) {
6077
6204
  }
6078
6205
  function formatIssueLine(issue, selfLogin, maxTitle) {
6079
6206
  const num = theme.text.accent(`#${String(issue.number).padEnd(5)}`);
6080
- const title = truncate3(issue.title, maxTitle);
6207
+ const title = truncate2(issue.title, maxTitle);
6081
6208
  const assignee = issueAssignee(issue, selfLogin);
6082
6209
  return ` ${num} ${title.padEnd(maxTitle)} ${assignee}`;
6083
6210
  }
6084
6211
  function formatTaskLine(task2, maxTitle) {
6085
6212
  const pri = task2.priority === 5 /* High */ ? theme.priority.high("[!]") : task2.priority === 3 /* Medium */ ? theme.priority.medium("[~]") : " ";
6086
- const title = truncate3(task2.title, maxTitle);
6213
+ const title = truncate2(task2.title, maxTitle);
6087
6214
  const due = task2.dueDate ? formatDueDate(task2.dueDate) : "";
6088
6215
  return ` ${pri} ${title.padEnd(maxTitle)} ${theme.text.secondary(due)}`;
6089
6216
  }
@@ -6504,13 +6631,13 @@ Configuring ${repoName}...`);
6504
6631
  } else {
6505
6632
  completionAction = { type: "closeIssue" };
6506
6633
  }
6507
- const shortName = await input({
6634
+ const shortName2 = await input({
6508
6635
  message: ` Short name for ${repoName}:`,
6509
6636
  default: name
6510
6637
  });
6511
6638
  repos.push({
6512
6639
  name: repoName,
6513
- shortName,
6640
+ shortName: shortName2,
6514
6641
  projectNumber,
6515
6642
  statusFieldId,
6516
6643
  ...dueDateFieldId ? { dueDateFieldId } : {},
@@ -7004,7 +7131,7 @@ function resolveProjectId(projectId) {
7004
7131
  process.exit(1);
7005
7132
  }
7006
7133
  var program = new Command();
7007
- program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.10.0").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) => {
7008
7135
  const opts = thisCommand.opts();
7009
7136
  if (opts.json) setFormat("json");
7010
7137
  if (opts.human) setFormat("human");
@@ -7199,7 +7326,7 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7199
7326
  console.error(`Repo "${name}" is already configured.`);
7200
7327
  process.exit(1);
7201
7328
  }
7202
- const shortName = name.split("/")[1] ?? name;
7329
+ const shortName2 = name.split("/")[1] ?? name;
7203
7330
  let completionAction;
7204
7331
  switch (opts.completionType) {
7205
7332
  case "addLabel":
@@ -7227,7 +7354,7 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7227
7354
  }
7228
7355
  const newRepo = {
7229
7356
  name,
7230
- shortName,
7357
+ shortName: shortName2,
7231
7358
  projectNumber: Number.parseInt(opts.projectNumber, 10),
7232
7359
  statusFieldId: opts.statusFieldId,
7233
7360
  completionAction
@@ -7237,7 +7364,7 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7237
7364
  if (useJson()) {
7238
7365
  jsonOut({ ok: true, message: `Added ${name}`, data: newRepo });
7239
7366
  } else {
7240
- console.log(`Added ${shortName} \u2192 ${name}`);
7367
+ console.log(`Added ${shortName2} \u2192 ${name}`);
7241
7368
  }
7242
7369
  });
7243
7370
  config.command("repos:rm <name>").description("Remove a repository from tracking").action((name) => {