@ondrej-svec/hog 1.10.0 → 1.12.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,88 @@ var init_action_log = __esm({
2784
2848
  }
2785
2849
  });
2786
2850
 
2787
- // src/board/components/detail-panel.tsx
2851
+ // src/board/components/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 buildTopLine(title, width) {
2855
+ const titlePart = `\u2500 ${title} `;
2856
+ const dashCount = Math.max(0, width - 2 - titlePart.length);
2857
+ return `\u256D${titlePart}${"\u2500".repeat(dashCount)}\u256E`;
2858
+ }
2859
+ function Panel({ title, isActive, width, height, flexGrow, children }) {
2860
+ const color = isActive ? "cyan" : "gray";
2861
+ const topLine = buildTopLine(title, width);
2862
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width, height, flexGrow, overflow: "hidden", children: [
2863
+ /* @__PURE__ */ jsx2(Text2, { color, children: topLine }),
2864
+ /* @__PURE__ */ jsx2(
2865
+ Box2,
2866
+ {
2867
+ borderStyle: "round",
2868
+ borderTop: false,
2869
+ borderColor: color,
2870
+ flexDirection: "column",
2871
+ flexGrow: 1,
2872
+ overflow: "hidden",
2873
+ width,
2874
+ children
2875
+ }
2876
+ )
2877
+ ] });
2794
2878
  }
2879
+ var init_panel = __esm({
2880
+ "src/board/components/panel.tsx"() {
2881
+ "use strict";
2882
+ }
2883
+ });
2884
+
2885
+ // src/board/components/activity-panel.tsx
2886
+ import { Box as Box3, Text as Text3 } from "ink";
2887
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2888
+ function ActivityPanel({
2889
+ events,
2890
+ selectedIdx,
2891
+ isActive,
2892
+ height,
2893
+ width
2894
+ }) {
2895
+ const maxRows = Math.max(1, height - 2);
2896
+ const visible = events.slice(0, maxRows);
2897
+ return /* @__PURE__ */ jsx3(Panel, { title: "[4] Activity", isActive, width, height, children: visible.length === 0 ? /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " No recent activity" }) : visible.map((event, i) => {
2898
+ const isSel = isActive && i === selectedIdx;
2899
+ const ago = timeAgo(event.timestamp);
2900
+ return /* @__PURE__ */ jsxs3(Box3, { children: [
2901
+ /* @__PURE__ */ jsxs3(Text3, { color: isSel ? "cyan" : "gray", bold: isSel, children: [
2902
+ isSel ? "\u25BA " : " ",
2903
+ ago
2904
+ ] }),
2905
+ /* @__PURE__ */ jsxs3(Text3, { color: isSel ? "white" : "gray", children: [
2906
+ " ",
2907
+ "@",
2908
+ event.actor,
2909
+ " ",
2910
+ event.summary,
2911
+ " "
2912
+ ] }),
2913
+ /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
2914
+ "(",
2915
+ event.repoShortName,
2916
+ ")"
2917
+ ] })
2918
+ ] }, `${event.repoShortName}:${event.issueNumber}:${i}`);
2919
+ }) });
2920
+ }
2921
+ var init_activity_panel = __esm({
2922
+ "src/board/components/activity-panel.tsx"() {
2923
+ "use strict";
2924
+ init_constants();
2925
+ init_panel();
2926
+ }
2927
+ });
2928
+
2929
+ // src/board/components/detail-panel.tsx
2930
+ import { Box as Box4, Text as Text4 } from "ink";
2931
+ import { useEffect as useEffect3 } from "react";
2932
+ import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2795
2933
  function stripMarkdown(text) {
2796
2934
  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
2935
  }
@@ -2810,11 +2948,11 @@ function BodySection({
2810
2948
  issueNumber
2811
2949
  }) {
2812
2950
  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: [
2951
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
2952
+ /* @__PURE__ */ jsx4(Text4, { children: "" }),
2953
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "--- Description ---" }),
2954
+ /* @__PURE__ */ jsx4(Text4, { wrap: "wrap", children: text }),
2955
+ remaining > 0 ? /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
2818
2956
  "... (",
2819
2957
  remaining,
2820
2958
  " more lines \u2014 gh issue view ",
@@ -2835,8 +2973,8 @@ function formatCommentAge(createdAt) {
2835
2973
  }
2836
2974
  function DetailPanel({
2837
2975
  issue,
2838
- task: task2,
2839
2976
  width,
2977
+ isActive,
2840
2978
  commentsState,
2841
2979
  fetchComments,
2842
2980
  issueRepo
@@ -2846,174 +2984,108 @@ function DetailPanel({
2846
2984
  if (commentsState !== null && commentsState !== void 0) return;
2847
2985
  fetchComments(issueRepo, issue.number);
2848
2986
  }, [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,
2865
- {
2866
- width,
2867
- borderStyle: "single",
2868
- borderColor: "cyan",
2869
- flexDirection: "column",
2870
- paddingX: 1,
2871
- 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 })
2930
- ]
2931
- }
2932
- );
2987
+ if (!issue) {
2988
+ return /* @__PURE__ */ jsx4(Panel, { title: "[0] Detail", isActive, width, children: /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "No item selected" }) });
2933
2989
  }
2934
- const t = task2;
2935
- return /* @__PURE__ */ jsxs2(
2936
- Box2,
2937
- {
2938
- width,
2939
- borderStyle: "single",
2940
- borderColor: "yellow",
2941
- flexDirection: "column",
2942
- paddingX: 1,
2943
- 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" })
2990
+ return /* @__PURE__ */ jsxs4(Panel, { title: "[0] Detail", isActive, width, children: [
2991
+ /* @__PURE__ */ jsxs4(Text4, { color: "cyan", bold: true, children: [
2992
+ "#",
2993
+ issue.number,
2994
+ " ",
2995
+ issue.title
2996
+ ] }),
2997
+ /* @__PURE__ */ jsx4(Text4, { children: "" }),
2998
+ /* @__PURE__ */ jsxs4(Box4, { children: [
2999
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "State: " }),
3000
+ /* @__PURE__ */ jsx4(Text4, { color: issue.state === "open" ? "green" : "red", children: issue.state })
3001
+ ] }),
3002
+ (issue.assignees ?? []).length > 0 ? /* @__PURE__ */ jsxs4(Box4, { children: [
3003
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Assignees: " }),
3004
+ /* @__PURE__ */ jsx4(Text4, { children: (issue.assignees ?? []).map((a) => a.login).join(", ") })
3005
+ ] }) : null,
3006
+ issue.labels.length > 0 ? /* @__PURE__ */ jsxs4(Box4, { children: [
3007
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Labels: " }),
3008
+ /* @__PURE__ */ jsx4(Text4, { children: issue.labels.map((l) => l.name).join(", ") })
3009
+ ] }) : null,
3010
+ issue.projectStatus ? /* @__PURE__ */ jsxs4(Box4, { children: [
3011
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Status: " }),
3012
+ /* @__PURE__ */ jsx4(Text4, { color: "magenta", children: issue.projectStatus })
3013
+ ] }) : null,
3014
+ issue.targetDate ? /* @__PURE__ */ jsxs4(Box4, { children: [
3015
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Target: " }),
3016
+ /* @__PURE__ */ jsx4(Text4, { children: issue.targetDate })
3017
+ ] }) : null,
3018
+ /* @__PURE__ */ jsxs4(Box4, { children: [
3019
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Updated: " }),
3020
+ /* @__PURE__ */ jsx4(Text4, { children: new Date(issue.updatedAt).toLocaleString() })
3021
+ ] }),
3022
+ issue.slackThreadUrl ? /* @__PURE__ */ jsxs4(Box4, { children: [
3023
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Slack: " }),
3024
+ /* @__PURE__ */ jsx4(Text4, { color: "blue", children: countSlackLinks(issue.body) > 1 ? `${countSlackLinks(issue.body)} links (s opens first)` : "thread (s to open)" })
3025
+ ] }) : null,
3026
+ issue.body ? /* @__PURE__ */ jsx4(BodySection, { body: issue.body, issueNumber: issue.number }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
3027
+ /* @__PURE__ */ jsx4(Text4, { children: "" }),
3028
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "(no description)" })
3029
+ ] }),
3030
+ /* @__PURE__ */ jsx4(Text4, { children: "" }),
3031
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "--- Comments ---" }),
3032
+ commentsState === "loading" ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "fetching comments..." }) : commentsState === "error" ? /* @__PURE__ */ jsx4(Text4, { color: "red", children: "could not load comments" }) : commentsState && commentsState.length === 0 ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No comments yet." }) : commentsState && commentsState.length > 0 ? commentsState.slice(-5).map((comment, i) => (
3033
+ // biome-ignore lint/suspicious/noArrayIndexKey: stable list
3034
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
3035
+ /* @__PURE__ */ jsxs4(Text4, { color: "cyan", children: [
3036
+ "@",
3037
+ comment.author.login,
3038
+ " \xB7 ",
3039
+ formatCommentAge(comment.createdAt)
2949
3040
  ] }),
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() })
2953
- ] }) : 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(", ") })
2957
- ] }) : null,
2958
- t.content ? /* @__PURE__ */ jsxs2(Fragment, { children: [
2959
- /* @__PURE__ */ jsx2(Text2, { children: "" }),
2960
- /* @__PURE__ */ jsx2(Text2, { children: truncateLines(t.content, 8) })
2961
- ] }) : 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
2976
- ]
2977
- }
2978
- );
3041
+ /* @__PURE__ */ jsxs4(Text4, { wrap: "wrap", children: [
3042
+ " ",
3043
+ comment.body.split("\n")[0]
3044
+ ] })
3045
+ ] }, i)
3046
+ )) : /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "fetching comments..." }),
3047
+ /* @__PURE__ */ jsx4(Text4, { children: "" }),
3048
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: issue.url })
3049
+ ] });
2979
3050
  }
2980
- var SLACK_URL_RE, PRIORITY_LABELS2;
3051
+ var SLACK_URL_RE;
2981
3052
  var init_detail_panel = __esm({
2982
3053
  "src/board/components/detail-panel.tsx"() {
2983
3054
  "use strict";
2984
- init_types();
3055
+ init_panel();
2985
3056
  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
3057
  }
2993
3058
  });
2994
3059
 
2995
3060
  // 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 }) {
3061
+ import { Box as Box5, Text as Text5 } from "ink";
3062
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
3063
+ function HintBar({
3064
+ uiMode,
3065
+ activePanelId,
3066
+ multiSelectCount,
3067
+ searchQuery,
3068
+ mineOnly,
3069
+ hasUndoable
3070
+ }) {
2999
3071
  if (uiMode === "multiSelect") {
3000
- return /* @__PURE__ */ jsxs3(Box3, { children: [
3001
- /* @__PURE__ */ jsxs3(Text3, { color: "cyan", bold: true, children: [
3072
+ return /* @__PURE__ */ jsxs5(Box5, { children: [
3073
+ /* @__PURE__ */ jsxs5(Text5, { color: "cyan", bold: true, children: [
3002
3074
  "[MULTI-SELECT] ",
3003
3075
  multiSelectCount,
3004
3076
  " selected"
3005
3077
  ] }),
3006
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " Space:toggle Enter:actions Esc:cancel" })
3078
+ /* @__PURE__ */ jsx5(Text5, { color: "gray", children: " Space:toggle Enter:actions Esc:cancel" })
3007
3079
  ] });
3008
3080
  }
3009
3081
  if (uiMode === "focus") {
3010
- return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: "magenta", bold: true, children: "[FOCUS] Focus mode \u2014 Esc to exit" }) });
3082
+ return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { color: "magenta", bold: true, children: "[FOCUS] Focus mode \u2014 Esc to exit" }) });
3011
3083
  }
3012
3084
  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: [
3085
+ return /* @__PURE__ */ jsxs5(Box5, { children: [
3086
+ /* @__PURE__ */ jsx5(Text5, { color: "yellow", bold: true, children: "[SEARCH]" }),
3087
+ /* @__PURE__ */ jsx5(Text5, { color: "gray", children: " type to filter Enter:confirm Esc:clear" }),
3088
+ searchQuery ? /* @__PURE__ */ jsxs5(Text5, { color: "yellow", children: [
3017
3089
  ' "',
3018
3090
  searchQuery,
3019
3091
  '"'
@@ -3021,19 +3093,22 @@ function HintBar({ uiMode, multiSelectCount, searchQuery, mineOnly, hasUndoable
3021
3093
  ] });
3022
3094
  }
3023
3095
  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" }) });
3096
+ return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { color: "gray", children: "\u2191\u2193/Ctrl-J/K:nav Enter:jump Esc:close" }) });
3025
3097
  }
3026
3098
  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: [
3099
+ return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { color: "gray", children: "j/k:nav Enter:select Esc:cancel" }) });
3100
+ }
3101
+ const panelHints = {
3102
+ 0: "j/k:scroll Esc:close ? help",
3103
+ 1: "j/k:move Enter:filter 0-4:panel ? help",
3104
+ 2: "j/k:move Enter:filter Esc:clear 0-4:panel ? help",
3105
+ 3: `j/k:move p:pick m:status c:comment /:search n:new 0-4:panel${hasUndoable ? " u:undo" : ""} ? help q:quit`,
3106
+ 4: "j/k:scroll Enter:jump r:refresh 0-4:panel ? help"
3107
+ };
3108
+ return /* @__PURE__ */ jsxs5(Box5, { children: [
3109
+ /* @__PURE__ */ jsx5(Text5, { color: "gray", children: panelHints[activePanelId] }),
3110
+ mineOnly ? /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: " filter:@me" }) : null,
3111
+ searchQuery ? /* @__PURE__ */ jsxs5(Text5, { color: "yellow", children: [
3037
3112
  ' filter:"',
3038
3113
  searchQuery,
3039
3114
  '"'
@@ -3047,9 +3122,9 @@ var init_hint_bar = __esm({
3047
3122
  });
3048
3123
 
3049
3124
  // 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";
3125
+ import { Box as Box6, Text as Text6, useInput as useInput2 } from "ink";
3126
+ import { useState as useState7 } from "react";
3127
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
3053
3128
  function getMenuItems(selectionType) {
3054
3129
  if (selectionType === "github") {
3055
3130
  return [
@@ -3068,7 +3143,7 @@ function getMenuItems(selectionType) {
3068
3143
  }
3069
3144
  function BulkActionMenu({ count, selectionType, onSelect, onCancel }) {
3070
3145
  const items = getMenuItems(selectionType);
3071
- const [selectedIdx, setSelectedIdx] = useState6(0);
3146
+ const [selectedIdx, setSelectedIdx] = useState7(0);
3072
3147
  useInput2((input2, key) => {
3073
3148
  if (key.escape) return onCancel();
3074
3149
  if (key.return) {
@@ -3084,13 +3159,13 @@ function BulkActionMenu({ count, selectionType, onSelect, onCancel }) {
3084
3159
  }
3085
3160
  });
3086
3161
  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" })
3162
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
3163
+ /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: "No bulk actions for mixed selection types." }),
3164
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Esc to cancel" })
3090
3165
  ] });
3091
3166
  }
3092
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
3093
- /* @__PURE__ */ jsxs4(Text4, { color: "cyan", bold: true, children: [
3167
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
3168
+ /* @__PURE__ */ jsxs6(Text6, { color: "cyan", bold: true, children: [
3094
3169
  "Bulk action (",
3095
3170
  count,
3096
3171
  " selected):"
@@ -3098,12 +3173,12 @@ function BulkActionMenu({ count, selectionType, onSelect, onCancel }) {
3098
3173
  items.map((item, i) => {
3099
3174
  const isSelected = i === selectedIdx;
3100
3175
  const prefix = isSelected ? "> " : " ";
3101
- return /* @__PURE__ */ jsxs4(Text4, { ...isSelected ? { color: "cyan" } : {}, children: [
3176
+ return /* @__PURE__ */ jsxs6(Text6, { ...isSelected ? { color: "cyan" } : {}, children: [
3102
3177
  prefix,
3103
3178
  item.label
3104
3179
  ] }, item.action.type);
3105
3180
  }),
3106
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "j/k:navigate Enter:select Esc:cancel" })
3181
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "j/k:navigate Enter:select Esc:cancel" })
3107
3182
  ] });
3108
3183
  }
3109
3184
  var init_bulk_action_menu = __esm({
@@ -3133,9 +3208,9 @@ import { mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as wr
3133
3208
  import { tmpdir } from "os";
3134
3209
  import { join as join4 } from "path";
3135
3210
  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";
3211
+ import { Box as Box7, Text as Text7, useInput as useInput3, useStdin } from "ink";
3212
+ import { useEffect as useEffect4, useRef as useRef7, useState as useState8 } from "react";
3213
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
3139
3214
  function CommentInput({
3140
3215
  issueNumber,
3141
3216
  onSubmit,
@@ -3143,8 +3218,8 @@ function CommentInput({
3143
3218
  onPauseRefresh,
3144
3219
  onResumeRefresh
3145
3220
  }) {
3146
- const [value, setValue] = useState7("");
3147
- const [editing, setEditing] = useState7(false);
3221
+ const [value, setValue] = useState8("");
3222
+ const [editing, setEditing] = useState8(false);
3148
3223
  const { setRawMode } = useStdin();
3149
3224
  const onSubmitRef = useRef7(onSubmit);
3150
3225
  const onCancelRef = useRef7(onCancel);
@@ -3202,19 +3277,19 @@ function CommentInput({
3202
3277
  }
3203
3278
  }, [editing, value, setRawMode]);
3204
3279
  if (editing) {
3205
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
3280
+ return /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text7, { color: "cyan", children: [
3206
3281
  "Opening editor for #",
3207
3282
  issueNumber,
3208
3283
  "\u2026"
3209
3284
  ] }) });
3210
3285
  }
3211
- return /* @__PURE__ */ jsxs5(Box5, { children: [
3212
- /* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
3286
+ return /* @__PURE__ */ jsxs7(Box7, { children: [
3287
+ /* @__PURE__ */ jsxs7(Text7, { color: "cyan", children: [
3213
3288
  "comment #",
3214
3289
  issueNumber,
3215
3290
  ": "
3216
3291
  ] }),
3217
- /* @__PURE__ */ jsx5(
3292
+ /* @__PURE__ */ jsx7(
3218
3293
  TextInput,
3219
3294
  {
3220
3295
  defaultValue: value,
@@ -3236,16 +3311,16 @@ var init_comment_input = __esm({
3236
3311
  });
3237
3312
 
3238
3313
  // 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";
3314
+ import { Box as Box8, Text as Text8, useInput as useInput4 } from "ink";
3315
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
3241
3316
  function ConfirmPrompt({ message, onConfirm, onCancel }) {
3242
3317
  useInput4((input2, key) => {
3243
3318
  if (input2 === "y" || input2 === "Y") return onConfirm();
3244
3319
  if (input2 === "n" || input2 === "N" || key.escape) return onCancel();
3245
3320
  });
3246
- return /* @__PURE__ */ jsxs6(Box6, { children: [
3247
- /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: message }),
3248
- /* @__PURE__ */ jsx6(Text6, { color: "gray", children: " (y/n)" })
3321
+ return /* @__PURE__ */ jsxs8(Box8, { children: [
3322
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: message }),
3323
+ /* @__PURE__ */ jsx8(Text8, { color: "gray", children: " (y/n)" })
3249
3324
  ] });
3250
3325
  }
3251
3326
  var init_confirm_prompt = __esm({
@@ -3256,9 +3331,9 @@ var init_confirm_prompt = __esm({
3256
3331
 
3257
3332
  // src/board/components/label-picker.tsx
3258
3333
  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";
3334
+ import { Box as Box9, Text as Text9, useInput as useInput5 } from "ink";
3335
+ import { useEffect as useEffect5, useRef as useRef8, useState as useState9 } from "react";
3336
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
3262
3337
  function LabelPicker({
3263
3338
  repo,
3264
3339
  currentLabels,
@@ -3267,11 +3342,11 @@ function LabelPicker({
3267
3342
  onCancel,
3268
3343
  onError
3269
3344
  }) {
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);
3345
+ const [labels, setLabels] = useState9(labelCache[repo] ?? null);
3346
+ const [loading, setLoading] = useState9(labels === null);
3347
+ const [fetchAttempted, setFetchAttempted] = useState9(false);
3348
+ const [selected, setSelected] = useState9(new Set(currentLabels));
3349
+ const [cursor, setCursor] = useState9(0);
3275
3350
  const submittedRef = useRef8(false);
3276
3351
  useEffect5(() => {
3277
3352
  if (labels !== null || fetchAttempted) return;
@@ -3333,21 +3408,21 @@ function LabelPicker({
3333
3408
  }
3334
3409
  });
3335
3410
  if (loading) {
3336
- return /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Spinner, { label: "Fetching labels..." }) });
3411
+ return /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Spinner, { label: "Fetching labels..." }) });
3337
3412
  }
3338
3413
  const allLabels = labels ?? [];
3339
3414
  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" })
3415
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
3416
+ /* @__PURE__ */ jsx9(Text9, { color: "cyan", bold: true, children: "Labels:" }),
3417
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "No labels in this repo" }),
3418
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Esc:cancel" })
3344
3419
  ] });
3345
3420
  }
3346
3421
  const repoLabelNames = new Set(allLabels.map((l) => l.name));
3347
3422
  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: [
3423
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
3424
+ /* @__PURE__ */ jsx9(Text9, { color: "cyan", bold: true, children: "Labels (Space:toggle Enter:confirm Esc:cancel):" }),
3425
+ orphanedLabels.map((name) => /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
3351
3426
  selected.has(name) ? "[x]" : "[ ]",
3352
3427
  " ",
3353
3428
  name,
@@ -3356,7 +3431,7 @@ function LabelPicker({
3356
3431
  allLabels.map((label, i) => {
3357
3432
  const isSel = i === cursor;
3358
3433
  const isChecked = selected.has(label.name);
3359
- return /* @__PURE__ */ jsxs7(Text7, { ...isSel ? { color: "cyan" } : {}, children: [
3434
+ return /* @__PURE__ */ jsxs9(Text9, { ...isSel ? { color: "cyan" } : {}, children: [
3360
3435
  isSel ? ">" : " ",
3361
3436
  " ",
3362
3437
  isChecked ? "[x]" : "[ ]",
@@ -3375,9 +3450,9 @@ var init_label_picker = __esm({
3375
3450
 
3376
3451
  // src/board/components/create-issue-form.tsx
3377
3452
  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";
3453
+ import { Box as Box10, Text as Text10, useInput as useInput6 } from "ink";
3454
+ import { useState as useState10 } from "react";
3455
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3381
3456
  function CreateIssueForm({
3382
3457
  repos,
3383
3458
  defaultRepo,
@@ -3389,9 +3464,9 @@ function CreateIssueForm({
3389
3464
  0,
3390
3465
  repos.findIndex((r) => r.name === defaultRepo)
3391
3466
  ) : 0;
3392
- const [repoIdx, setRepoIdx] = useState9(defaultRepoIdx);
3393
- const [title, setTitle] = useState9("");
3394
- const [field, setField] = useState9("title");
3467
+ const [repoIdx, setRepoIdx] = useState10(defaultRepoIdx);
3468
+ const [title, setTitle] = useState10("");
3469
+ const [field, setField] = useState10("title");
3395
3470
  useInput6((input2, key) => {
3396
3471
  if (field === "labels") return;
3397
3472
  if (key.escape) return onCancel();
@@ -3408,15 +3483,15 @@ function CreateIssueForm({
3408
3483
  });
3409
3484
  const selectedRepo = repos[repoIdx];
3410
3485
  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: [
3486
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
3487
+ /* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: "Create Issue \u2014 Add Labels (optional)" }),
3488
+ /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
3414
3489
  "Repo: ",
3415
3490
  selectedRepo.shortName,
3416
3491
  " Title: ",
3417
3492
  title
3418
3493
  ] }),
3419
- /* @__PURE__ */ jsx8(
3494
+ /* @__PURE__ */ jsx10(
3420
3495
  LabelPicker,
3421
3496
  {
3422
3497
  repo: selectedRepo.name,
@@ -3441,12 +3516,12 @@ function CreateIssueForm({
3441
3516
  )
3442
3517
  ] });
3443
3518
  }
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,
3519
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
3520
+ /* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: "Create Issue" }),
3521
+ /* @__PURE__ */ jsxs10(Box10, { children: [
3522
+ /* @__PURE__ */ jsx10(Text10, { dimColor: field !== "repo", children: "Repo: " }),
3523
+ repos.map((r, i) => /* @__PURE__ */ jsx10(
3524
+ Text10,
3450
3525
  {
3451
3526
  ...i === repoIdx ? { color: "cyan", bold: true } : {},
3452
3527
  dimColor: field !== "repo",
@@ -3454,11 +3529,11 @@ function CreateIssueForm({
3454
3529
  },
3455
3530
  r.name
3456
3531
  )),
3457
- field === "repo" ? /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " j/k:select Tab:next" }) : null
3532
+ field === "repo" ? /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " j/k:select Tab:next" }) : null
3458
3533
  ] }),
3459
- /* @__PURE__ */ jsxs8(Box8, { children: [
3460
- /* @__PURE__ */ jsx8(Text8, { dimColor: field !== "title", children: "Title: " }),
3461
- field === "title" ? /* @__PURE__ */ jsx8(
3534
+ /* @__PURE__ */ jsxs10(Box10, { children: [
3535
+ /* @__PURE__ */ jsx10(Text10, { dimColor: field !== "title", children: "Title: " }),
3536
+ field === "title" ? /* @__PURE__ */ jsx10(
3462
3537
  TextInput2,
3463
3538
  {
3464
3539
  defaultValue: title,
@@ -3475,9 +3550,9 @@ function CreateIssueForm({
3475
3550
  }
3476
3551
  }
3477
3552
  }
3478
- ) : /* @__PURE__ */ jsx8(Text8, { children: title || "(empty)" })
3553
+ ) : /* @__PURE__ */ jsx10(Text10, { children: title || "(empty)" })
3479
3554
  ] }),
3480
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Tab:switch fields Enter:next Esc:cancel" })
3555
+ /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Tab:switch fields Enter:next Esc:cancel" })
3481
3556
  ] });
3482
3557
  }
3483
3558
  var init_create_issue_form = __esm({
@@ -3492,9 +3567,9 @@ import { spawnSync as spawnSync2 } from "child_process";
3492
3567
  import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
3493
3568
  import { tmpdir as tmpdir2 } from "os";
3494
3569
  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";
3570
+ import { Box as Box11, Text as Text11, useStdin as useStdin2 } from "ink";
3571
+ import { useEffect as useEffect6, useRef as useRef9, useState as useState11 } from "react";
3572
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
3498
3573
  function buildEditorFile(issue, repoName, statusOptions, repoLabels) {
3499
3574
  const statusNames = statusOptions.map((o) => o.name).join(", ");
3500
3575
  const labelNames = repoLabels.map((l) => l.name).join(", ");
@@ -3577,7 +3652,7 @@ function EditIssueOverlay({
3577
3652
  onToastError,
3578
3653
  onPushEntry
3579
3654
  }) {
3580
- const [editing, setEditing] = useState10(true);
3655
+ const [editing, setEditing] = useState11(true);
3581
3656
  const { setRawMode } = useStdin2();
3582
3657
  const onDoneRef = useRef9(onDone);
3583
3658
  const onPauseRef = useRef9(onPauseRefresh);
@@ -3736,7 +3811,7 @@ function EditIssueOverlay({
3736
3811
  onPushEntry
3737
3812
  ]);
3738
3813
  if (!editing) return null;
3739
- return /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsxs9(Text9, { color: "cyan", children: [
3814
+ return /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsxs11(Text11, { color: "cyan", children: [
3740
3815
  "Opening editor for #",
3741
3816
  issue.number,
3742
3817
  "\u2026"
@@ -3752,17 +3827,17 @@ var init_edit_issue_overlay = __esm({
3752
3827
  });
3753
3828
 
3754
3829
  // 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";
3830
+ import { Box as Box12, Text as Text12, useInput as useInput7 } from "ink";
3831
+ import { useCallback as useCallback10, useEffect as useEffect7, useRef as useRef10, useState as useState12 } from "react";
3832
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
3758
3833
  function formatTime(secs) {
3759
3834
  const m = Math.floor(secs / 60);
3760
3835
  const s = secs % 60;
3761
3836
  return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
3762
3837
  }
3763
3838
  function FocusMode({ label, durationSec, onExit, onEndAction }) {
3764
- const [remaining, setRemaining] = useState11(durationSec);
3765
- const [timerDone, setTimerDone] = useState11(false);
3839
+ const [remaining, setRemaining] = useState12(durationSec);
3840
+ const [timerDone, setTimerDone] = useState12(false);
3766
3841
  const bellSentRef = useRef10(false);
3767
3842
  useEffect7(() => {
3768
3843
  if (timerDone) return;
@@ -3784,7 +3859,7 @@ function FocusMode({ label, durationSec, onExit, onEndAction }) {
3784
3859
  process.stdout.write("\x07");
3785
3860
  }
3786
3861
  }, [timerDone]);
3787
- const handleInput = useCallback9(
3862
+ const handleInput = useCallback10(
3788
3863
  (input2, key) => {
3789
3864
  if (key.escape) {
3790
3865
  if (timerDone) {
@@ -3811,23 +3886,23 @@ function FocusMode({ label, durationSec, onExit, onEndAction }) {
3811
3886
  );
3812
3887
  useInput7(handleInput);
3813
3888
  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: [
3889
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
3890
+ /* @__PURE__ */ jsxs12(Box12, { children: [
3891
+ /* @__PURE__ */ jsx12(Text12, { color: "green", bold: true, children: "Focus complete!" }),
3892
+ /* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
3818
3893
  " ",
3819
3894
  label
3820
3895
  ] })
3821
3896
  ] }),
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" })
3897
+ /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, children: [
3898
+ /* @__PURE__ */ jsx12(Text12, { color: "cyan", children: "[c]" }),
3899
+ /* @__PURE__ */ jsx12(Text12, { children: " Continue " }),
3900
+ /* @__PURE__ */ jsx12(Text12, { color: "cyan", children: "[b]" }),
3901
+ /* @__PURE__ */ jsx12(Text12, { children: " Break " }),
3902
+ /* @__PURE__ */ jsx12(Text12, { color: "cyan", children: "[d]" }),
3903
+ /* @__PURE__ */ jsx12(Text12, { children: " Done " }),
3904
+ /* @__PURE__ */ jsx12(Text12, { color: "gray", children: "[Esc]" }),
3905
+ /* @__PURE__ */ jsx12(Text12, { children: " Exit" })
3831
3906
  ] })
3832
3907
  ] });
3833
3908
  }
@@ -3835,21 +3910,21 @@ function FocusMode({ label, durationSec, onExit, onEndAction }) {
3835
3910
  const barWidth = 20;
3836
3911
  const filled = Math.round(progress * barWidth);
3837
3912
  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: [
3913
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
3914
+ /* @__PURE__ */ jsxs12(Box12, { children: [
3915
+ /* @__PURE__ */ jsxs12(Text12, { color: "magenta", bold: true, children: [
3841
3916
  "Focus:",
3842
3917
  " "
3843
3918
  ] }),
3844
- /* @__PURE__ */ jsx10(Text10, { children: label })
3919
+ /* @__PURE__ */ jsx12(Text12, { children: label })
3845
3920
  ] }),
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" })
3921
+ /* @__PURE__ */ jsxs12(Box12, { children: [
3922
+ /* @__PURE__ */ jsx12(Text12, { color: "magenta", children: bar }),
3923
+ /* @__PURE__ */ jsx12(Text12, { children: " " }),
3924
+ /* @__PURE__ */ jsx12(Text12, { bold: true, children: formatTime(remaining) }),
3925
+ /* @__PURE__ */ jsx12(Text12, { color: "gray", children: " remaining" })
3851
3926
  ] }),
3852
- /* @__PURE__ */ jsx10(Text10, { color: "gray", dimColor: true, children: "Esc to exit focus" })
3927
+ /* @__PURE__ */ jsx12(Text12, { color: "gray", dimColor: true, children: "Esc to exit focus" })
3853
3928
  ] });
3854
3929
  }
3855
3930
  var init_focus_mode = __esm({
@@ -3861,18 +3936,18 @@ var init_focus_mode = __esm({
3861
3936
  // src/board/components/fuzzy-picker.tsx
3862
3937
  import { TextInput as TextInput3 } from "@inkjs/ui";
3863
3938
  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";
3939
+ import { Box as Box13, Text as Text13, useInput as useInput8 } from "ink";
3940
+ import { useMemo as useMemo2, useState as useState13 } from "react";
3941
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
3867
3942
  function keepCursorVisible(cursor, offset, visible) {
3868
3943
  if (cursor < offset) return cursor;
3869
3944
  if (cursor >= offset + visible) return cursor - visible + 1;
3870
3945
  return offset;
3871
3946
  }
3872
3947
  function FuzzyPicker({ repos, onSelect, onClose }) {
3873
- const [query, setQuery] = useState12("");
3874
- const [cursor, setCursor] = useState12(0);
3875
- const [scrollOffset, setScrollOffset] = useState12(0);
3948
+ const [query, setQuery] = useState13("");
3949
+ const [cursor, setCursor] = useState13(0);
3950
+ const [scrollOffset, setScrollOffset] = useState13(0);
3876
3951
  const allIssues = useMemo2(() => {
3877
3952
  const items = [];
3878
3953
  for (const rd of repos) {
@@ -3955,13 +4030,13 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3955
4030
  });
3956
4031
  const visibleResults = results.slice(scrollOffset, scrollOffset + VISIBLE);
3957
4032
  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: [
4033
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4034
+ /* @__PURE__ */ jsxs13(Box13, { children: [
4035
+ /* @__PURE__ */ jsxs13(Text13, { color: "cyan", bold: true, children: [
3961
4036
  "Find issue",
3962
4037
  " "
3963
4038
  ] }),
3964
- /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
4039
+ /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
3965
4040
  "(",
3966
4041
  totalCount,
3967
4042
  " match",
@@ -3969,14 +4044,14 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3969
4044
  ")",
3970
4045
  " "
3971
4046
  ] }),
3972
- /* @__PURE__ */ jsx11(Text11, { color: "gray", dimColor: true, children: "\u2191\u2193/Ctrl-J/K nav Enter:jump Esc:close" })
4047
+ /* @__PURE__ */ jsx13(Text13, { color: "gray", dimColor: true, children: "\u2191\u2193/Ctrl-J/K nav Enter:jump Esc:close" })
3973
4048
  ] }),
3974
- /* @__PURE__ */ jsxs11(Box11, { children: [
3975
- /* @__PURE__ */ jsxs11(Text11, { color: "yellow", children: [
4049
+ /* @__PURE__ */ jsxs13(Box13, { children: [
4050
+ /* @__PURE__ */ jsxs13(Text13, { color: "yellow", children: [
3976
4051
  ">",
3977
4052
  " "
3978
4053
  ] }),
3979
- /* @__PURE__ */ jsx11(
4054
+ /* @__PURE__ */ jsx13(
3980
4055
  TextInput3,
3981
4056
  {
3982
4057
  defaultValue: query,
@@ -3993,7 +4068,7 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
3993
4068
  }
3994
4069
  )
3995
4070
  ] }),
3996
- scrollOffset > 0 ? /* @__PURE__ */ jsxs11(Text11, { color: "gray", dimColor: true, children: [
4071
+ scrollOffset > 0 ? /* @__PURE__ */ jsxs13(Text13, { color: "gray", dimColor: true, children: [
3997
4072
  "\u25B2 ",
3998
4073
  scrollOffset,
3999
4074
  " more above"
@@ -4002,7 +4077,7 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
4002
4077
  const isSelected = scrollOffset + idx === cursor;
4003
4078
  const labelStr = issue.labels ? ` [${issue.labels.split(" ").slice(0, 2).join("] [")}]` : "";
4004
4079
  const assigneeStr = issue.assignee ? ` @${issue.assignee.split(" ")[0]}` : "";
4005
- return /* @__PURE__ */ jsx11(Box11, { children: isSelected ? /* @__PURE__ */ jsxs11(Text11, { color: "cyan", bold: true, children: [
4080
+ return /* @__PURE__ */ jsx13(Box13, { children: isSelected ? /* @__PURE__ */ jsxs13(Text13, { color: "cyan", bold: true, children: [
4006
4081
  ">",
4007
4082
  " ",
4008
4083
  issue.repoShortName,
@@ -4012,7 +4087,7 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
4012
4087
  issue.title,
4013
4088
  labelStr,
4014
4089
  assigneeStr
4015
- ] }) : /* @__PURE__ */ jsxs11(Text11, { children: [
4090
+ ] }) : /* @__PURE__ */ jsxs13(Text13, { children: [
4016
4091
  " ",
4017
4092
  issue.repoShortName,
4018
4093
  "#",
@@ -4023,12 +4098,12 @@ function FuzzyPicker({ repos, onSelect, onClose }) {
4023
4098
  assigneeStr
4024
4099
  ] }) }, issue.navId);
4025
4100
  }),
4026
- totalCount === 0 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
4101
+ totalCount === 0 ? /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
4027
4102
  'No issues match "',
4028
4103
  query,
4029
4104
  '"'
4030
4105
  ] }) : null,
4031
- results.length > scrollOffset + VISIBLE ? /* @__PURE__ */ jsxs11(Text11, { color: "gray", dimColor: true, children: [
4106
+ results.length > scrollOffset + VISIBLE ? /* @__PURE__ */ jsxs13(Text13, { color: "gray", dimColor: true, children: [
4032
4107
  "\u25BC ",
4033
4108
  results.length - scrollOffset - VISIBLE,
4034
4109
  " more below"
@@ -4042,29 +4117,29 @@ var init_fuzzy_picker = __esm({
4042
4117
  });
4043
4118
 
4044
4119
  // 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";
4120
+ import { Box as Box14, Text as Text14, useInput as useInput9 } from "ink";
4121
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
4047
4122
  function HelpOverlay({ currentMode, onClose }) {
4048
4123
  useInput9((_input, key) => {
4049
4124
  if (key.escape) onClose();
4050
4125
  });
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: [
4126
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4127
+ /* @__PURE__ */ jsxs14(Box14, { justifyContent: "space-between", children: [
4128
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", bold: true, children: "Keyboard Shortcuts" }),
4129
+ /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
4055
4130
  "mode: ",
4056
4131
  currentMode
4057
4132
  ] })
4058
4133
  ] }),
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 })
4134
+ /* @__PURE__ */ jsx14(Text14, { children: " " }),
4135
+ SHORTCUTS.map((group) => /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginBottom: 1, children: [
4136
+ /* @__PURE__ */ jsx14(Text14, { color: "yellow", bold: true, children: group.category }),
4137
+ group.items.map((item) => /* @__PURE__ */ jsxs14(Box14, { children: [
4138
+ /* @__PURE__ */ jsx14(Box14, { width: 16, children: /* @__PURE__ */ jsx14(Text14, { color: "green", children: item.key }) }),
4139
+ /* @__PURE__ */ jsx14(Text14, { children: item.desc })
4065
4140
  ] }, item.key))
4066
4141
  ] }, group.category)),
4067
- /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Press ? or Esc to close" })
4142
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Press ? or Esc to close" })
4068
4143
  ] });
4069
4144
  }
4070
4145
  var SHORTCUTS;
@@ -4130,9 +4205,9 @@ import { mkdtempSync as mkdtempSync3, readFileSync as readFileSync6, rmSync as r
4130
4205
  import { tmpdir as tmpdir3 } from "os";
4131
4206
  import { join as join6 } from "path";
4132
4207
  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";
4208
+ import { Box as Box15, Text as Text15, useInput as useInput10, useStdin as useStdin3 } from "ink";
4209
+ import { useCallback as useCallback11, useEffect as useEffect8, useRef as useRef11, useState as useState14 } from "react";
4210
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
4136
4211
  function NlCreateOverlay({
4137
4212
  repos,
4138
4213
  defaultRepoName,
@@ -4143,13 +4218,13 @@ function NlCreateOverlay({
4143
4218
  onResumeRefresh,
4144
4219
  onLlmFallback
4145
4220
  }) {
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);
4221
+ const [, setInput] = useState14("");
4222
+ const [isParsing, setIsParsing] = useState14(false);
4223
+ const [parsed, setParsed] = useState14(null);
4224
+ const [parseError, setParseError] = useState14(null);
4225
+ const [step, setStep] = useState14("input");
4226
+ const [body, setBody] = useState14("");
4227
+ const [editingBody, setEditingBody] = useState14(false);
4153
4228
  const submittedRef = useRef11(false);
4154
4229
  const parseParamsRef = useRef11(null);
4155
4230
  const onSubmitRef = useRef11(onSubmit);
@@ -4165,7 +4240,7 @@ function NlCreateOverlay({
4165
4240
  0,
4166
4241
  repos.findIndex((r) => r.name === defaultRepoName)
4167
4242
  ) : 0;
4168
- const [repoIdx, setRepoIdx] = useState13(defaultRepoIdx);
4243
+ const [repoIdx, setRepoIdx] = useState14(defaultRepoIdx);
4169
4244
  const selectedRepo = repos[repoIdx];
4170
4245
  useInput10((inputChar, key) => {
4171
4246
  if (isParsing || editingBody) return;
@@ -4225,7 +4300,7 @@ function NlCreateOverlay({
4225
4300
  setEditingBody(false);
4226
4301
  }
4227
4302
  }, [editingBody, body, setRawMode]);
4228
- const handleInputSubmit = useCallback10(
4303
+ const handleInputSubmit = useCallback11(
4229
4304
  (text) => {
4230
4305
  const trimmed = text.trim();
4231
4306
  if (!trimmed) return;
@@ -4258,27 +4333,27 @@ function NlCreateOverlay({
4258
4333
  });
4259
4334
  }, [isParsing, onLlmFallback]);
4260
4335
  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..." })
4336
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4337
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4338
+ /* @__PURE__ */ jsx15(Spinner2, { label: "Parsing..." })
4264
4339
  ] });
4265
4340
  }
4266
4341
  if (parsed && step === "body") {
4267
4342
  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" })
4343
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4344
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4345
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", children: "Opening editor for body\u2026" })
4271
4346
  ] });
4272
4347
  }
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 })
4348
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4349
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4350
+ /* @__PURE__ */ jsxs15(Box15, { children: [
4351
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Title: " }),
4352
+ /* @__PURE__ */ jsx15(Text15, { children: parsed.title })
4278
4353
  ] }),
4279
- /* @__PURE__ */ jsxs13(Box13, { children: [
4280
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: "body: " }),
4281
- /* @__PURE__ */ jsx13(
4354
+ /* @__PURE__ */ jsxs15(Box15, { children: [
4355
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", children: "body: " }),
4356
+ /* @__PURE__ */ jsx15(
4282
4357
  TextInput4,
4283
4358
  {
4284
4359
  defaultValue: body,
@@ -4300,45 +4375,45 @@ function NlCreateOverlay({
4300
4375
  }
4301
4376
  )
4302
4377
  ] }),
4303
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Enter:create ctrl+e:editor Esc:back" })
4378
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Enter:create ctrl+e:editor Esc:back" })
4304
4379
  ] });
4305
4380
  }
4306
4381
  if (parsed) {
4307
4382
  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
4383
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4384
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
4385
+ /* @__PURE__ */ jsxs15(Box15, { children: [
4386
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Repo: " }),
4387
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", children: selectedRepo?.shortName ?? "(none)" }),
4388
+ repos.length > 1 ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: " r:cycle" }) : null
4314
4389
  ] }),
4315
- /* @__PURE__ */ jsxs13(Box13, { children: [
4316
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Title: " }),
4317
- /* @__PURE__ */ jsx13(Text13, { children: parsed.title })
4390
+ /* @__PURE__ */ jsxs15(Box15, { children: [
4391
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Title: " }),
4392
+ /* @__PURE__ */ jsx15(Text15, { children: parsed.title })
4318
4393
  ] }),
4319
- labels.length > 0 ? /* @__PURE__ */ jsxs13(Box13, { children: [
4320
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Labels: " }),
4321
- /* @__PURE__ */ jsx13(Text13, { children: labels.join(", ") })
4394
+ labels.length > 0 ? /* @__PURE__ */ jsxs15(Box15, { children: [
4395
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Labels: " }),
4396
+ /* @__PURE__ */ jsx15(Text15, { children: labels.join(", ") })
4322
4397
  ] }) : null,
4323
- parsed.assignee ? /* @__PURE__ */ jsxs13(Box13, { children: [
4324
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Assignee: " }),
4325
- /* @__PURE__ */ jsxs13(Text13, { children: [
4398
+ parsed.assignee ? /* @__PURE__ */ jsxs15(Box15, { children: [
4399
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Assignee: " }),
4400
+ /* @__PURE__ */ jsxs15(Text15, { children: [
4326
4401
  "@",
4327
4402
  parsed.assignee
4328
4403
  ] })
4329
4404
  ] }) : null,
4330
- parsed.dueDate ? /* @__PURE__ */ jsxs13(Box13, { children: [
4331
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Due: " }),
4332
- /* @__PURE__ */ jsx13(Text13, { children: formatDue(parsed.dueDate) })
4405
+ parsed.dueDate ? /* @__PURE__ */ jsxs15(Box15, { children: [
4406
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Due: " }),
4407
+ /* @__PURE__ */ jsx15(Text15, { children: formatDue(parsed.dueDate) })
4333
4408
  ] }) : null,
4334
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Enter:add body Esc:cancel" })
4409
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Enter:add body Esc:cancel" })
4335
4410
  ] });
4336
4411
  }
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(
4412
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4413
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: "\u2728 What do you need to do?" }),
4414
+ /* @__PURE__ */ jsxs15(Box15, { children: [
4415
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", children: "> " }),
4416
+ /* @__PURE__ */ jsx15(
4342
4417
  TextInput4,
4343
4418
  {
4344
4419
  placeholder: "fix login bug #bug #priority:high @me due friday",
@@ -4347,8 +4422,8 @@ function NlCreateOverlay({
4347
4422
  }
4348
4423
  )
4349
4424
  ] }),
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" })
4425
+ parseError ? /* @__PURE__ */ jsx15(Text15, { color: "red", children: parseError }) : null,
4426
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Tip: #label @user due <date> Enter:parse Esc:cancel" })
4352
4427
  ] });
4353
4428
  }
4354
4429
  function buildLabelList(parsed) {
@@ -4368,12 +4443,12 @@ var init_nl_create_overlay = __esm({
4368
4443
 
4369
4444
  // src/board/components/search-bar.tsx
4370
4445
  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";
4446
+ import { Box as Box16, Text as Text16 } from "ink";
4447
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
4373
4448
  function SearchBar({ defaultValue, onChange, onSubmit }) {
4374
- return /* @__PURE__ */ jsxs14(Box14, { children: [
4375
- /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: "/" }),
4376
- /* @__PURE__ */ jsx14(
4449
+ return /* @__PURE__ */ jsxs16(Box16, { children: [
4450
+ /* @__PURE__ */ jsx16(Text16, { color: "yellow", children: "/" }),
4451
+ /* @__PURE__ */ jsx16(
4377
4452
  TextInput5,
4378
4453
  {
4379
4454
  defaultValue,
@@ -4391,9 +4466,9 @@ var init_search_bar = __esm({
4391
4466
  });
4392
4467
 
4393
4468
  // 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";
4469
+ import { Box as Box17, Text as Text17, useInput as useInput11 } from "ink";
4470
+ import { useRef as useRef12, useState as useState15 } from "react";
4471
+ import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
4397
4472
  function isTerminal(name) {
4398
4473
  return TERMINAL_STATUS_RE.test(name);
4399
4474
  }
@@ -4440,11 +4515,11 @@ function StatusPicker({
4440
4515
  onCancel,
4441
4516
  showTerminalStatuses = true
4442
4517
  }) {
4443
- const [selectedIdx, setSelectedIdx] = useState14(() => {
4518
+ const [selectedIdx, setSelectedIdx] = useState15(() => {
4444
4519
  const idx = options.findIndex((o) => o.name === currentStatus);
4445
4520
  return idx >= 0 ? idx : 0;
4446
4521
  });
4447
- const [confirmingTerminal, setConfirmingTerminal] = useState14(false);
4522
+ const [confirmingTerminal, setConfirmingTerminal] = useState15(false);
4448
4523
  const submittedRef = useRef12(false);
4449
4524
  useInput11((input2, key) => {
4450
4525
  if (confirmingTerminal) {
@@ -4469,26 +4544,26 @@ function StatusPicker({
4469
4544
  });
4470
4545
  if (confirmingTerminal) {
4471
4546
  const opt = options[selectedIdx];
4472
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4473
- /* @__PURE__ */ jsxs15(Text15, { color: "yellow", bold: true, children: [
4547
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
4548
+ /* @__PURE__ */ jsxs17(Text17, { color: "yellow", bold: true, children: [
4474
4549
  "Mark as ",
4475
4550
  opt?.name,
4476
4551
  "?"
4477
4552
  ] }),
4478
- /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "This will close the issue on GitHub." }),
4479
- /* @__PURE__ */ jsx15(Text15, { children: "Continue? [y/n]" })
4553
+ /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "This will close the issue on GitHub." }),
4554
+ /* @__PURE__ */ jsx17(Text17, { children: "Continue? [y/n]" })
4480
4555
  ] });
4481
4556
  }
4482
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
4483
- /* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: "Move to status:" }),
4557
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
4558
+ /* @__PURE__ */ jsx17(Text17, { color: "cyan", bold: true, children: "Move to status:" }),
4484
4559
  options.map((opt, i) => {
4485
4560
  const isCurrent = opt.name === currentStatus;
4486
4561
  const isSelected = i === selectedIdx;
4487
4562
  const terminal = isTerminal(opt.name) && showTerminalStatuses;
4488
4563
  const prefix = isSelected ? "> " : " ";
4489
4564
  const suffix = isCurrent ? " (current)" : terminal ? " (Done)" : "";
4490
- return /* @__PURE__ */ jsxs15(
4491
- Text15,
4565
+ return /* @__PURE__ */ jsxs17(
4566
+ Text17,
4492
4567
  {
4493
4568
  ...isSelected ? { color: "cyan" } : terminal ? { color: "yellow" } : {},
4494
4569
  dimColor: isCurrent,
@@ -4501,7 +4576,7 @@ function StatusPicker({
4501
4576
  opt.id
4502
4577
  );
4503
4578
  }),
4504
- /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "j/k:navigate Enter:select Esc:cancel" })
4579
+ /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "j/k:navigate Enter:select Esc:cancel" })
4505
4580
  ] });
4506
4581
  }
4507
4582
  var init_status_picker = __esm({
@@ -4512,7 +4587,7 @@ var init_status_picker = __esm({
4512
4587
  });
4513
4588
 
4514
4589
  // src/board/components/overlay-renderer.tsx
4515
- import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
4590
+ import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
4516
4591
  function OverlayRenderer({
4517
4592
  uiState,
4518
4593
  config: config2,
@@ -4553,9 +4628,9 @@ function OverlayRenderer({
4553
4628
  onPushEntry
4554
4629
  }) {
4555
4630
  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(
4631
+ return /* @__PURE__ */ jsxs18(Fragment2, { children: [
4632
+ helpVisible ? /* @__PURE__ */ jsx18(HelpOverlay, { currentMode: mode, onClose: onToggleHelp }) : null,
4633
+ mode === "overlay:status" && selectedRepoStatusOptions.length > 0 ? /* @__PURE__ */ jsx18(
4559
4634
  StatusPicker,
4560
4635
  {
4561
4636
  options: selectedRepoStatusOptions,
@@ -4564,7 +4639,7 @@ function OverlayRenderer({
4564
4639
  onCancel: onExitOverlay
4565
4640
  }
4566
4641
  ) : null,
4567
- mode === "overlay:create" ? /* @__PURE__ */ jsx16(
4642
+ mode === "overlay:create" ? /* @__PURE__ */ jsx18(
4568
4643
  CreateIssueForm,
4569
4644
  {
4570
4645
  repos: config2.repos,
@@ -4574,7 +4649,7 @@ function OverlayRenderer({
4574
4649
  labelCache
4575
4650
  }
4576
4651
  ) : null,
4577
- mode === "overlay:confirmPick" ? /* @__PURE__ */ jsx16(
4652
+ mode === "overlay:confirmPick" ? /* @__PURE__ */ jsx18(
4578
4653
  ConfirmPrompt,
4579
4654
  {
4580
4655
  message: "Pick this issue?",
@@ -4582,7 +4657,7 @@ function OverlayRenderer({
4582
4657
  onCancel: onCancelPick
4583
4658
  }
4584
4659
  ) : null,
4585
- mode === "overlay:bulkAction" ? /* @__PURE__ */ jsx16(
4660
+ mode === "overlay:bulkAction" ? /* @__PURE__ */ jsx18(
4586
4661
  BulkActionMenu,
4587
4662
  {
4588
4663
  count: multiSelectCount,
@@ -4591,7 +4666,7 @@ function OverlayRenderer({
4591
4666
  onCancel: onExitOverlay
4592
4667
  }
4593
4668
  ) : null,
4594
- mode === "focus" && focusLabel ? /* @__PURE__ */ jsx16(
4669
+ mode === "focus" && focusLabel ? /* @__PURE__ */ jsx18(
4595
4670
  FocusMode,
4596
4671
  {
4597
4672
  label: focusLabel,
@@ -4601,7 +4676,7 @@ function OverlayRenderer({
4601
4676
  },
4602
4677
  focusKey
4603
4678
  ) : null,
4604
- mode === "overlay:label" && selectedIssue && defaultRepo ? /* @__PURE__ */ jsx16(
4679
+ mode === "overlay:label" && selectedIssue && defaultRepo ? /* @__PURE__ */ jsx18(
4605
4680
  LabelPicker,
4606
4681
  {
4607
4682
  repo: defaultRepo,
@@ -4612,8 +4687,8 @@ function OverlayRenderer({
4612
4687
  onError: onLabelError
4613
4688
  }
4614
4689
  ) : null,
4615
- mode === "search" ? /* @__PURE__ */ jsx16(SearchBar, { defaultValue: searchQuery, onChange: onSearchChange, onSubmit: onSearchSubmit }) : null,
4616
- mode === "overlay:comment" && selectedIssue ? /* @__PURE__ */ jsx16(
4690
+ mode === "search" ? /* @__PURE__ */ jsx18(SearchBar, { defaultValue: searchQuery, onChange: onSearchChange, onSubmit: onSearchSubmit }) : null,
4691
+ mode === "overlay:comment" && selectedIssue ? /* @__PURE__ */ jsx18(
4617
4692
  CommentInput,
4618
4693
  {
4619
4694
  issueNumber: selectedIssue.number,
@@ -4623,8 +4698,8 @@ function OverlayRenderer({
4623
4698
  onResumeRefresh
4624
4699
  }
4625
4700
  ) : null,
4626
- mode === "overlay:fuzzyPicker" ? /* @__PURE__ */ jsx16(FuzzyPicker, { repos, onSelect: onFuzzySelect, onClose: onFuzzyClose }) : null,
4627
- mode === "overlay:createNl" ? /* @__PURE__ */ jsx16(
4701
+ mode === "overlay:fuzzyPicker" ? /* @__PURE__ */ jsx18(FuzzyPicker, { repos, onSelect: onFuzzySelect, onClose: onFuzzyClose }) : null,
4702
+ mode === "overlay:createNl" ? /* @__PURE__ */ jsx18(
4628
4703
  NlCreateOverlay,
4629
4704
  {
4630
4705
  repos: config2.repos,
@@ -4637,7 +4712,7 @@ function OverlayRenderer({
4637
4712
  onLlmFallback
4638
4713
  }
4639
4714
  ) : null,
4640
- mode === "overlay:editIssue" && selectedIssue && selectedRepoName ? /* @__PURE__ */ jsx16(
4715
+ mode === "overlay:editIssue" && selectedIssue && selectedRepoName ? /* @__PURE__ */ jsx18(
4641
4716
  EditIssueOverlay,
4642
4717
  {
4643
4718
  issue: selectedIssue,
@@ -4673,9 +4748,104 @@ var init_overlay_renderer = __esm({
4673
4748
  }
4674
4749
  });
4675
4750
 
4751
+ // src/board/components/panel-layout.tsx
4752
+ import { Box as Box18 } from "ink";
4753
+ import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
4754
+ function getLayoutMode(cols) {
4755
+ if (cols >= WIDE_THRESHOLD) return "wide";
4756
+ if (cols >= MEDIUM_THRESHOLD) return "medium";
4757
+ return "stacked";
4758
+ }
4759
+ function getDetailWidth(cols) {
4760
+ return Math.floor(cols * 0.4);
4761
+ }
4762
+ function PanelLayout({
4763
+ cols,
4764
+ issuesPanelHeight,
4765
+ reposPanel,
4766
+ statusesPanel,
4767
+ issuesPanel,
4768
+ detailPanel,
4769
+ activityPanel
4770
+ }) {
4771
+ const mode = getLayoutMode(cols);
4772
+ if (mode === "wide") {
4773
+ const detailWidth = getDetailWidth(cols);
4774
+ return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
4775
+ /* @__PURE__ */ jsxs19(Box18, { height: issuesPanelHeight, children: [
4776
+ /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", width: LEFT_COL_WIDTH, children: [
4777
+ reposPanel,
4778
+ statusesPanel
4779
+ ] }),
4780
+ /* @__PURE__ */ jsx19(Box18, { flexGrow: 1, flexDirection: "column", children: issuesPanel }),
4781
+ /* @__PURE__ */ jsx19(Box18, { width: detailWidth, flexDirection: "column", children: detailPanel })
4782
+ ] }),
4783
+ /* @__PURE__ */ jsx19(Box18, { height: ACTIVITY_HEIGHT, children: activityPanel })
4784
+ ] });
4785
+ }
4786
+ if (mode === "medium") {
4787
+ return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
4788
+ /* @__PURE__ */ jsxs19(Box18, { height: issuesPanelHeight, children: [
4789
+ /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", width: LEFT_COL_WIDTH, children: [
4790
+ reposPanel,
4791
+ statusesPanel
4792
+ ] }),
4793
+ /* @__PURE__ */ jsx19(Box18, { flexGrow: 1, flexDirection: "column", children: issuesPanel })
4794
+ ] }),
4795
+ /* @__PURE__ */ jsx19(Box18, { height: ACTIVITY_HEIGHT, children: activityPanel })
4796
+ ] });
4797
+ }
4798
+ return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
4799
+ reposPanel,
4800
+ statusesPanel,
4801
+ /* @__PURE__ */ jsx19(Box18, { flexGrow: 1, flexDirection: "column", children: issuesPanel }),
4802
+ /* @__PURE__ */ jsx19(Box18, { height: ACTIVITY_HEIGHT, children: activityPanel })
4803
+ ] });
4804
+ }
4805
+ var WIDE_THRESHOLD, MEDIUM_THRESHOLD, LEFT_COL_WIDTH, ACTIVITY_HEIGHT;
4806
+ var init_panel_layout = __esm({
4807
+ "src/board/components/panel-layout.tsx"() {
4808
+ "use strict";
4809
+ WIDE_THRESHOLD = 160;
4810
+ MEDIUM_THRESHOLD = 100;
4811
+ LEFT_COL_WIDTH = 24;
4812
+ ACTIVITY_HEIGHT = 5;
4813
+ }
4814
+ });
4815
+
4816
+ // src/board/components/repos-panel.tsx
4817
+ import { Box as Box19, Text as Text18 } from "ink";
4818
+ import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
4819
+ function shortName(fullName) {
4820
+ return fullName.includes("/") ? fullName.split("/")[1] ?? fullName : fullName;
4821
+ }
4822
+ function ReposPanel({ repos, selectedIdx, isActive, width, flexGrow }) {
4823
+ const maxLabel = Math.max(4, width - 8);
4824
+ return /* @__PURE__ */ jsx20(Panel, { title: "[1] Repos", isActive, width, flexGrow, children: repos.length === 0 ? /* @__PURE__ */ jsx20(Text18, { color: "gray", children: "\u2014" }) : repos.map((repo, i) => {
4825
+ const isSel = i === selectedIdx;
4826
+ const label = shortName(repo.name).slice(0, maxLabel);
4827
+ return /* @__PURE__ */ jsxs20(Box19, { children: [
4828
+ /* @__PURE__ */ jsxs20(Text18, { color: isSel ? "cyan" : isActive ? "white" : "gray", bold: isSel, children: [
4829
+ isSel ? "\u25BA " : " ",
4830
+ label
4831
+ ] }),
4832
+ /* @__PURE__ */ jsxs20(Text18, { color: "gray", children: [
4833
+ " ",
4834
+ repo.openCount
4835
+ ] })
4836
+ ] }, repo.name);
4837
+ }) });
4838
+ }
4839
+ var init_repos_panel = __esm({
4840
+ "src/board/components/repos-panel.tsx"() {
4841
+ "use strict";
4842
+ init_panel();
4843
+ }
4844
+ });
4845
+
4676
4846
  // 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";
4847
+ import { Box as Box20, Text as Text19 } from "ink";
4848
+ import { Fragment as Fragment3, jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
4679
4849
  function truncate(s, max) {
4680
4850
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
4681
4851
  }
@@ -4716,30 +4886,30 @@ function IssueRow({ issue, selfLogin, isSelected }) {
4716
4886
  const labels = (issue.labels ?? []).slice(0, 2);
4717
4887
  const target = formatTargetDate(issue.targetDate);
4718
4888
  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: [
4889
+ return /* @__PURE__ */ jsxs21(Box20, { children: [
4890
+ isSelected ? /* @__PURE__ */ jsx21(Text19, { color: "cyan", bold: true, children: "\u25B6 " }) : /* @__PURE__ */ jsx21(Text19, { children: " " }),
4891
+ /* @__PURE__ */ jsxs21(Text19, { color: "cyan", children: [
4722
4892
  "#",
4723
4893
  String(issue.number).padEnd(5)
4724
4894
  ] }),
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: [
4895
+ /* @__PURE__ */ jsx21(Text19, { children: " " }),
4896
+ isSelected ? /* @__PURE__ */ jsx21(Text19, { color: "white", bold: true, children: titleStr }) : /* @__PURE__ */ jsx21(Text19, { children: titleStr }),
4897
+ /* @__PURE__ */ jsx21(Text19, { children: " " }),
4898
+ /* @__PURE__ */ jsx21(Box20, { width: LABEL_COL_WIDTH, children: labels.map((l, i) => /* @__PURE__ */ jsxs21(Text19, { children: [
4729
4899
  i > 0 ? " " : "",
4730
- /* @__PURE__ */ jsxs17(Text16, { color: labelColor(l.name), children: [
4900
+ /* @__PURE__ */ jsxs21(Text19, { color: labelColor(l.name), children: [
4731
4901
  "[",
4732
4902
  truncate(l.name, 12),
4733
4903
  "]"
4734
4904
  ] })
4735
4905
  ] }, 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 })
4906
+ /* @__PURE__ */ jsx21(Text19, { children: " " }),
4907
+ /* @__PURE__ */ jsx21(Text19, { color: assigneeColor, children: assigneeText.padEnd(14) }),
4908
+ /* @__PURE__ */ jsx21(Text19, { children: " " }),
4909
+ /* @__PURE__ */ jsx21(Text19, { color: "gray", children: timeAgo2(issue.updatedAt).padStart(4) }),
4910
+ target.text ? /* @__PURE__ */ jsxs21(Fragment3, { children: [
4911
+ /* @__PURE__ */ jsx21(Text19, { children: " " }),
4912
+ /* @__PURE__ */ jsx21(Text19, { color: target.color, children: target.text })
4743
4913
  ] }) : null
4744
4914
  ] });
4745
4915
  }
@@ -4762,68 +4932,21 @@ var init_issue_row = __esm({
4762
4932
  }
4763
4933
  });
4764
4934
 
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
4935
  // 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";
4936
+ import { Box as Box21, Text as Text20 } from "ink";
4937
+ import { jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
4815
4938
  function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4816
4939
  switch (row.type) {
4817
4940
  case "sectionHeader": {
4818
4941
  const arrow = row.isCollapsed ? "\u25B6" : "\u25BC";
4819
4942
  const isSel = selectedId === row.navId;
4820
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4821
- /* @__PURE__ */ jsxs19(Text18, { color: isSel ? "cyan" : "white", bold: true, children: [
4943
+ return /* @__PURE__ */ jsxs22(Box21, { children: [
4944
+ /* @__PURE__ */ jsxs22(Text20, { color: isSel ? "cyan" : "white", bold: true, children: [
4822
4945
  arrow,
4823
4946
  " ",
4824
4947
  row.label
4825
4948
  ] }),
4826
- /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
4949
+ /* @__PURE__ */ jsxs22(Text20, { color: "gray", children: [
4827
4950
  " ",
4828
4951
  "(",
4829
4952
  row.count,
@@ -4837,26 +4960,26 @@ function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4837
4960
  if (row.navId) {
4838
4961
  const arrow = row.isCollapsed ? "\u25B6" : "\u25BC";
4839
4962
  const isSel = selectedId === row.navId;
4840
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4841
- /* @__PURE__ */ jsxs19(Text18, { color: isSel ? "cyan" : "gray", children: [
4963
+ return /* @__PURE__ */ jsxs22(Box21, { children: [
4964
+ /* @__PURE__ */ jsxs22(Text20, { color: isSel ? "cyan" : "gray", children: [
4842
4965
  " ",
4843
4966
  arrow,
4844
4967
  " ",
4845
4968
  row.text
4846
4969
  ] }),
4847
- /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
4970
+ /* @__PURE__ */ jsxs22(Text20, { color: "gray", children: [
4848
4971
  " (",
4849
4972
  row.count,
4850
4973
  ")"
4851
4974
  ] })
4852
4975
  ] });
4853
4976
  }
4854
- return /* @__PURE__ */ jsxs19(Box18, { children: [
4855
- /* @__PURE__ */ jsxs19(Text18, { bold: true, color: "white", children: [
4977
+ return /* @__PURE__ */ jsxs22(Box21, { children: [
4978
+ /* @__PURE__ */ jsxs22(Text20, { bold: true, color: "white", children: [
4856
4979
  " ",
4857
4980
  row.text
4858
4981
  ] }),
4859
- row.count != null ? /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
4982
+ row.count != null ? /* @__PURE__ */ jsxs22(Text20, { color: "gray", children: [
4860
4983
  " (",
4861
4984
  row.count,
4862
4985
  ")"
@@ -4865,32 +4988,25 @@ function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4865
4988
  }
4866
4989
  case "issue": {
4867
4990
  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 })
4991
+ return /* @__PURE__ */ jsxs22(Box21, { children: [
4992
+ checkbox2 ? /* @__PURE__ */ jsx22(Text20, { color: isMultiSelected ? "cyan" : "gray", children: checkbox2 }) : null,
4993
+ /* @__PURE__ */ jsx22(IssueRow, { issue: row.issue, selfLogin, isSelected: selectedId === row.navId })
4878
4994
  ] });
4879
4995
  }
4880
4996
  case "activity": {
4881
- const ago = timeAgo(row.event.timestamp);
4882
- return /* @__PURE__ */ jsxs19(Text18, { dimColor: true, children: [
4997
+ const ago = new Date(row.event.timestamp).toLocaleTimeString();
4998
+ return /* @__PURE__ */ jsxs22(Text20, { dimColor: true, children: [
4883
4999
  " ",
4884
5000
  ago,
4885
5001
  ": ",
4886
- /* @__PURE__ */ jsxs19(Text18, { color: "gray", children: [
5002
+ /* @__PURE__ */ jsxs22(Text20, { color: "gray", children: [
4887
5003
  "@",
4888
5004
  row.event.actor
4889
5005
  ] }),
4890
5006
  " ",
4891
5007
  row.event.summary,
4892
5008
  " ",
4893
- /* @__PURE__ */ jsxs19(Text18, { dimColor: true, children: [
5009
+ /* @__PURE__ */ jsxs22(Text20, { dimColor: true, children: [
4894
5010
  "(",
4895
5011
  row.event.repoShortName,
4896
5012
  ")"
@@ -4898,85 +5014,71 @@ function RowRenderer({ row, selectedId, selfLogin, isMultiSelected }) {
4898
5014
  ] });
4899
5015
  }
4900
5016
  case "error":
4901
- return /* @__PURE__ */ jsxs19(Text18, { color: "red", children: [
5017
+ return /* @__PURE__ */ jsxs22(Text20, { color: "red", children: [
4902
5018
  " Error: ",
4903
5019
  row.text
4904
5020
  ] });
4905
5021
  case "gap":
4906
- return /* @__PURE__ */ jsx19(Text18, { children: "" });
5022
+ return /* @__PURE__ */ jsx22(Text20, { children: "" });
4907
5023
  }
4908
5024
  }
4909
5025
  var init_row_renderer = __esm({
4910
5026
  "src/board/components/row-renderer.tsx"() {
4911
5027
  "use strict";
4912
- init_constants();
4913
5028
  init_issue_row();
4914
- init_task_row();
4915
5029
  }
4916
5030
  });
4917
5031
 
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
- }) });
4934
- }
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);
5032
+ // src/board/components/statuses-panel.tsx
5033
+ import { Box as Box22, Text as Text21 } from "ink";
5034
+ import { jsx as jsx23, jsxs as jsxs23 } from "react/jsx-runtime";
5035
+ function StatusesPanel({
5036
+ groups,
5037
+ selectedIdx,
5038
+ isActive,
5039
+ width,
5040
+ flexGrow
5041
+ }) {
5042
+ const maxLabel = Math.max(4, width - 8);
5043
+ return /* @__PURE__ */ jsx23(Panel, { title: "[2] Statuses", isActive, width, flexGrow, children: groups.length === 0 ? /* @__PURE__ */ jsx23(Text21, { color: "gray", children: "\u2014" }) : groups.map((group, i) => {
5044
+ const isSel = i === selectedIdx;
5045
+ const label = group.label.slice(0, maxLabel);
5046
+ return /* @__PURE__ */ jsxs23(Box22, { children: [
5047
+ /* @__PURE__ */ jsxs23(Text21, { color: isSel ? "cyan" : isActive ? "white" : "gray", bold: isSel, children: [
5048
+ isSel ? "\u25BA " : " ",
5049
+ label
5050
+ ] }),
5051
+ /* @__PURE__ */ jsxs23(Text21, { color: "gray", children: [
5052
+ " ",
5053
+ group.count
5054
+ ] })
5055
+ ] }, group.id);
4955
5056
  }) });
4956
5057
  }
4957
- var init_tab_bar = __esm({
4958
- "src/board/components/tab-bar.tsx"() {
5058
+ var init_statuses_panel = __esm({
5059
+ "src/board/components/statuses-panel.tsx"() {
4959
5060
  "use strict";
5061
+ init_panel();
4960
5062
  }
4961
5063
  });
4962
5064
 
4963
5065
  // src/board/components/toast-container.tsx
4964
5066
  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";
5067
+ import { Box as Box23, Text as Text22 } from "ink";
5068
+ import { Fragment as Fragment4, jsx as jsx24, jsxs as jsxs24 } from "react/jsx-runtime";
4967
5069
  function ToastContainer({ toasts }) {
4968
5070
  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: [
5071
+ return /* @__PURE__ */ jsx24(Box23, { flexDirection: "column", children: toasts.map((t) => /* @__PURE__ */ jsx24(Box23, { children: t.type === "loading" ? /* @__PURE__ */ jsxs24(Fragment4, { children: [
5072
+ /* @__PURE__ */ jsx24(Spinner3, { label: "" }),
5073
+ /* @__PURE__ */ jsxs24(Text22, { color: "cyan", children: [
4972
5074
  " ",
4973
5075
  t.message
4974
5076
  ] })
4975
- ] }) : /* @__PURE__ */ jsxs22(Text21, { color: TYPE_COLORS[t.type], children: [
5077
+ ] }) : /* @__PURE__ */ jsxs24(Text22, { color: TYPE_COLORS[t.type], children: [
4976
5078
  TYPE_PREFIXES[t.type],
4977
5079
  " ",
4978
5080
  t.message,
4979
- t.type === "error" ? /* @__PURE__ */ jsx22(Text21, { color: "gray", children: t.retry ? " [r]etry [d]ismiss" : " [d]ismiss" }) : null
5081
+ t.type === "error" ? /* @__PURE__ */ jsx24(Text22, { color: "gray", children: t.retry ? " [r]etry [d]ismiss" : " [d]ismiss" }) : null
4980
5082
  ] }) }, t.id)) });
4981
5083
  }
4982
5084
  var TYPE_COLORS, TYPE_PREFIXES;
@@ -5000,9 +5102,9 @@ var init_toast_container = __esm({
5000
5102
  // src/board/components/dashboard.tsx
5001
5103
  import { execFileSync as execFileSync3, spawnSync as spawnSync4 } from "child_process";
5002
5104
  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";
5105
+ import { Box as Box24, Text as Text23, useApp, useStdout } from "ink";
5106
+ import { useCallback as useCallback12, useEffect as useEffect9, useMemo as useMemo3, useRef as useRef13, useState as useState16 } from "react";
5107
+ import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs25 } from "react/jsx-runtime";
5006
5108
  function resolveStatusGroups(statusOptions, configuredGroups) {
5007
5109
  if (configuredGroups && configuredGroups.length > 0) {
5008
5110
  return configuredGroups.map((entry) => {
@@ -5040,7 +5142,7 @@ function groupByStatus(issues) {
5040
5142
  }
5041
5143
  return groups;
5042
5144
  }
5043
- function buildBoardTree(repos, tasks, activity) {
5145
+ function buildBoardTree(repos, activity) {
5044
5146
  const sections = repos.map((rd) => {
5045
5147
  const sectionId = rd.repo.name;
5046
5148
  if (rd.error) {
@@ -5069,72 +5171,58 @@ function buildBoardTree(repos, tasks, activity) {
5069
5171
  }
5070
5172
  return { repo: rd.repo, sectionId, groups, error: null };
5071
5173
  });
5072
- return { activity, sections, tasks };
5174
+ return { activity, sections };
5073
5175
  }
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);
5176
+ function buildNavItemsForRepo(sections, repoName, statusGroupId) {
5177
+ if (!repoName) return [];
5178
+ const section = sections.find((s) => s.sectionId === repoName);
5092
5179
  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);
5104
- if (!section) return [];
5105
- const activeGroup = section.groups.find((g) => g.subId === activeStatusId) ?? section.groups[0];
5180
+ const activeGroup = section.groups.find((g) => g.subId === statusGroupId) ?? section.groups[0];
5106
5181
  if (!activeGroup) return [];
5107
5182
  return activeGroup.issues.map((issue) => ({
5108
5183
  id: `gh:${section.repo.name}:${issue.number}`,
5109
- section: tabId,
5184
+ section: repoName,
5110
5185
  type: "item"
5111
5186
  }));
5112
5187
  }
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);
5188
+ function buildFlatRowsForRepo(sections, repoName, statusGroupId) {
5189
+ if (!repoName) {
5190
+ return [
5191
+ {
5192
+ type: "subHeader",
5193
+ key: "select-repo",
5194
+ navId: null,
5195
+ text: "Select a repo in panel [1]"
5196
+ }
5197
+ ];
5198
+ }
5199
+ const section = sections.find((s) => s.sectionId === repoName);
5129
5200
  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)
5201
+ if (section.error) {
5202
+ return [{ type: "error", key: `error:${repoName}`, navId: null, text: section.error }];
5203
+ }
5204
+ if (section.groups.length === 0) {
5133
5205
  return [
5134
- { type: "subHeader", key: `empty:${tabId}`, navId: null, text: "No open issues" }
5206
+ {
5207
+ type: "subHeader",
5208
+ key: `empty:${repoName}`,
5209
+ navId: null,
5210
+ text: "No open issues"
5211
+ }
5135
5212
  ];
5136
- const activeGroup = section.groups.find((g) => g.subId === activeStatusId) ?? section.groups[0];
5213
+ }
5214
+ const activeGroup = section.groups.find((g) => g.subId === statusGroupId) ?? section.groups[0];
5137
5215
  if (!activeGroup) return [];
5216
+ if (activeGroup.issues.length === 0) {
5217
+ return [
5218
+ {
5219
+ type: "subHeader",
5220
+ key: `empty-group:${statusGroupId}`,
5221
+ navId: null,
5222
+ text: "No issues in this status group"
5223
+ }
5224
+ ];
5225
+ }
5138
5226
  return activeGroup.issues.map((issue) => ({
5139
5227
  type: "issue",
5140
5228
  key: `gh:${section.repo.name}:${issue.number}`,
@@ -5161,13 +5249,13 @@ function findSelectedIssueWithRepo(repos, selectedId) {
5161
5249
  return null;
5162
5250
  }
5163
5251
  function RefreshAge({ lastRefresh }) {
5164
- const [, setTick] = useState15(0);
5252
+ const [, setTick] = useState16(0);
5165
5253
  useEffect9(() => {
5166
5254
  const id = setInterval(() => setTick((t) => t + 1), 1e4);
5167
5255
  return () => clearInterval(id);
5168
5256
  }, []);
5169
5257
  if (!lastRefresh) return null;
5170
- return /* @__PURE__ */ jsxs23(Text22, { color: refreshAgeColor(lastRefresh), children: [
5258
+ return /* @__PURE__ */ jsxs25(Text23, { color: refreshAgeColor(lastRefresh), children: [
5171
5259
  "Updated ",
5172
5260
  timeAgo(lastRefresh)
5173
5261
  ] });
@@ -5191,19 +5279,16 @@ function Dashboard({ config: config2, options, activeProfile }) {
5191
5279
  clearPendingMutation
5192
5280
  } = useData(config2, options, refreshMs);
5193
5281
  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
5282
  const allActivity = useMemo3(() => data?.activity ?? [], [data?.activity]);
5199
5283
  const ui = useUIState();
5200
- const [searchQuery, setSearchQuery] = useState15("");
5201
- const [mineOnly, setMineOnly] = useState15(false);
5202
- const handleToggleMine = useCallback11(() => {
5284
+ const panelFocus = usePanelFocus(3);
5285
+ const [searchQuery, setSearchQuery] = useState16("");
5286
+ const [mineOnly, setMineOnly] = useState16(false);
5287
+ const handleToggleMine = useCallback12(() => {
5203
5288
  setMineOnly((prev) => !prev);
5204
5289
  }, []);
5205
5290
  const { toasts, toast, handleErrorAction } = useToast();
5206
- const [logVisible, setLogVisible] = useState15(false);
5291
+ const [logVisible, setLogVisible] = useState16(false);
5207
5292
  const { entries: logEntries, pushEntry, undoLast, hasUndoable } = useActionLog(toast, refresh);
5208
5293
  useEffect9(() => {
5209
5294
  const last = logEntries[logEntries.length - 1];
@@ -5227,69 +5312,75 @@ function Dashboard({ config: config2, options, activeProfile }) {
5227
5312
  const q = searchQuery.toLowerCase();
5228
5313
  return filtered.map((rd) => ({ ...rd, issues: rd.issues.filter((i) => i.title.toLowerCase().includes(q)) })).filter((rd) => rd.issues.length > 0);
5229
5314
  }, [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]
5315
+ const boardTree = useMemo3(() => buildBoardTree(repos, allActivity), [repos, allActivity]);
5316
+ const [selectedRepoIdx, setSelectedRepoIdx] = useState16(0);
5317
+ const clampedRepoIdx = Math.min(selectedRepoIdx, Math.max(0, boardTree.sections.length - 1));
5318
+ const reposNav = {
5319
+ moveUp: useCallback12(() => setSelectedRepoIdx((i) => Math.max(0, i - 1)), []),
5320
+ moveDown: useCallback12(
5321
+ () => setSelectedRepoIdx((i) => Math.min(Math.max(0, boardTree.sections.length - 1), i + 1)),
5322
+ [boardTree.sections.length]
5323
+ )
5324
+ };
5325
+ const [selectedStatusIdx, setSelectedStatusIdx] = useState16(0);
5326
+ const selectedSection = boardTree.sections[clampedRepoIdx] ?? null;
5327
+ const clampedStatusIdx = Math.min(
5328
+ selectedStatusIdx,
5329
+ Math.max(0, (selectedSection?.groups.length ?? 1) - 1)
5262
5330
  );
5263
- const [activeStatusId, setActiveStatusId] = useState15(null);
5264
- const statusTabs = useMemo3(
5265
- () => buildStatusTabs(effectiveTabId, boardTree),
5266
- [effectiveTabId, boardTree]
5331
+ const statusesNav = {
5332
+ moveUp: useCallback12(() => setSelectedStatusIdx((i) => Math.max(0, i - 1)), []),
5333
+ moveDown: useCallback12(
5334
+ () => setSelectedStatusIdx(
5335
+ (i) => Math.min(Math.max(0, (selectedSection?.groups.length ?? 1) - 1), i + 1)
5336
+ ),
5337
+ [selectedSection?.groups.length]
5338
+ )
5339
+ };
5340
+ const [activitySelectedIdx, setActivitySelectedIdx] = useState16(0);
5341
+ const clampedActivityIdx = Math.min(
5342
+ activitySelectedIdx,
5343
+ Math.max(0, boardTree.activity.length - 1)
5267
5344
  );
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
5345
+ const activityNav = {
5346
+ moveUp: useCallback12(() => setActivitySelectedIdx((i) => Math.max(0, i - 1)), []),
5347
+ moveDown: useCallback12(
5348
+ () => setActivitySelectedIdx((i) => Math.min(Math.max(0, boardTree.activity.length - 1), i + 1)),
5349
+ [boardTree.activity.length]
5350
+ )
5351
+ };
5352
+ const selectedRepoName = selectedSection?.sectionId ?? null;
5353
+ const selectedStatusGroup = selectedSection?.groups[clampedStatusIdx] ?? null;
5354
+ const selectedStatusGroupId = selectedStatusGroup?.subId ?? null;
5355
+ const onRepoEnter = useCallback12(() => {
5356
+ setSelectedStatusIdx(0);
5357
+ panelFocus.focusPanel(3);
5358
+ }, [panelFocus]);
5359
+ const onStatusEnter = useCallback12(() => {
5360
+ panelFocus.focusPanel(3);
5361
+ }, [panelFocus]);
5362
+ const onActivityEnter = useCallback12(() => {
5363
+ const event = boardTree.activity[clampedActivityIdx];
5364
+ if (!event) return;
5365
+ const repoIdx = boardTree.sections.findIndex(
5366
+ (s) => s.repo.shortName === event.repoShortName || s.sectionId.endsWith(`/${event.repoShortName}`)
5280
5367
  );
5281
- }, [activeStatusIdx, statusTabs]);
5368
+ if (repoIdx >= 0) {
5369
+ setSelectedRepoIdx(repoIdx);
5370
+ setSelectedStatusIdx(0);
5371
+ panelFocus.focusPanel(3);
5372
+ }
5373
+ }, [boardTree, clampedActivityIdx, panelFocus]);
5282
5374
  const navItems = useMemo3(
5283
- () => buildNavItemsForTab(effectiveTabId ?? "", boardTree, effectiveStatusId),
5284
- [effectiveTabId, boardTree, effectiveStatusId]
5375
+ () => buildNavItemsForRepo(boardTree.sections, selectedRepoName, selectedStatusGroupId),
5376
+ [boardTree.sections, selectedRepoName, selectedStatusGroupId]
5285
5377
  );
5286
5378
  const nav = useNavigation(navItems);
5287
- const getRepoForId = useCallback11((id) => {
5379
+ const getRepoForId = useCallback12((id) => {
5288
5380
  if (id.startsWith("gh:")) {
5289
5381
  const parts = id.split(":");
5290
5382
  return parts.length >= 3 ? `${parts[1]}` : null;
5291
5383
  }
5292
- if (id.startsWith("tt:")) return "ticktick";
5293
5384
  return null;
5294
5385
  }, []);
5295
5386
  const multiSelect = useMultiSelect(getRepoForId);
@@ -5313,8 +5404,8 @@ function Dashboard({ config: config2, options, activeProfile }) {
5313
5404
  const pendingPickRef = useRef13(null);
5314
5405
  const labelCacheRef = useRef13({});
5315
5406
  const commentCacheRef = useRef13({});
5316
- const [commentTick, setCommentTick] = useState15(0);
5317
- const handleFetchComments = useCallback11((repo, issueNumber) => {
5407
+ const [commentTick, setCommentTick] = useState16(0);
5408
+ const handleFetchComments = useCallback12((repo, issueNumber) => {
5318
5409
  const key = `${repo}:${issueNumber}`;
5319
5410
  if (commentCacheRef.current[key] !== void 0) return;
5320
5411
  commentCacheRef.current[key] = "loading";
@@ -5327,7 +5418,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5327
5418
  setCommentTick((t) => t + 1);
5328
5419
  });
5329
5420
  }, []);
5330
- const handleCreateIssueWithPrompt = useCallback11(
5421
+ const handleCreateIssueWithPrompt = useCallback12(
5331
5422
  (repo, title, body, dueDate, labels) => {
5332
5423
  actions.handleCreateIssue(repo, title, body, dueDate, labels).then((result) => {
5333
5424
  if (result) {
@@ -5338,7 +5429,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5338
5429
  },
5339
5430
  [actions, ui]
5340
5431
  );
5341
- const handleConfirmPick = useCallback11(() => {
5432
+ const handleConfirmPick = useCallback12(() => {
5342
5433
  const pending = pendingPickRef.current;
5343
5434
  pendingPickRef.current = null;
5344
5435
  ui.exitOverlay();
@@ -5356,12 +5447,12 @@ function Dashboard({ config: config2, options, activeProfile }) {
5356
5447
  })
5357
5448
  );
5358
5449
  }, [config2, toast, refresh, ui]);
5359
- const handleCancelPick = useCallback11(() => {
5450
+ const handleCancelPick = useCallback12(() => {
5360
5451
  pendingPickRef.current = null;
5361
5452
  ui.exitOverlay();
5362
5453
  }, [ui]);
5363
- const [focusLabel, setFocusLabel] = useState15(null);
5364
- const handleEnterFocus = useCallback11(() => {
5454
+ const [focusLabel, setFocusLabel] = useState16(null);
5455
+ const handleEnterFocus = useCallback12(() => {
5365
5456
  const id = nav.selectedId;
5366
5457
  if (!id || isHeaderId(id)) return;
5367
5458
  let label = "";
@@ -5371,20 +5462,16 @@ function Dashboard({ config: config2, options, activeProfile }) {
5371
5462
  const rc = config2.repos.find((r) => r.name === found.repoName);
5372
5463
  label = `${rc?.shortName ?? found.repoName}#${found.issue.number} \u2014 ${found.issue.title}`;
5373
5464
  }
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
5465
  }
5379
5466
  if (!label) return;
5380
5467
  setFocusLabel(label);
5381
5468
  ui.enterFocus();
5382
- }, [nav.selectedId, repos, tasks, config2.repos, ui]);
5383
- const handleFocusExit = useCallback11(() => {
5469
+ }, [nav.selectedId, repos, config2.repos, ui]);
5470
+ const handleFocusExit = useCallback12(() => {
5384
5471
  setFocusLabel(null);
5385
5472
  ui.exitToNormal();
5386
5473
  }, [ui]);
5387
- const handleFocusEndAction = useCallback11(
5474
+ const handleFocusEndAction = useCallback12(
5388
5475
  (action) => {
5389
5476
  switch (action) {
5390
5477
  case "restart":
@@ -5410,9 +5497,9 @@ function Dashboard({ config: config2, options, activeProfile }) {
5410
5497
  },
5411
5498
  [toast, ui]
5412
5499
  );
5413
- const [focusKey, setFocusKey] = useState15(0);
5500
+ const [focusKey, setFocusKey] = useState16(0);
5414
5501
  const { stdout } = useStdout();
5415
- const [termSize, setTermSize] = useState15({
5502
+ const [termSize, setTermSize] = useState16({
5416
5503
  cols: stdout?.columns ?? 80,
5417
5504
  rows: stdout?.rows ?? 24
5418
5505
  });
@@ -5424,24 +5511,33 @@ function Dashboard({ config: config2, options, activeProfile }) {
5424
5511
  stdout.off("resize", onResize);
5425
5512
  };
5426
5513
  }, [stdout]);
5427
- const showDetailPanel = termSize.cols >= 120;
5428
- const detailPanelWidth = showDetailPanel ? Math.floor(termSize.cols * 0.35) : 0;
5514
+ const layoutMode = getLayoutMode(termSize.cols);
5515
+ const detailPanelWidth = layoutMode === "wide" ? getDetailWidth(termSize.cols) : Math.floor(termSize.cols * 0.35);
5516
+ const showDetailPanel = layoutMode === "wide";
5517
+ const usableWidth = termSize.cols - 2;
5518
+ const issuesPanelWidth = Math.max(
5519
+ 20,
5520
+ layoutMode === "wide" ? usableWidth - LEFT_COL_WIDTH - getDetailWidth(termSize.cols) : layoutMode === "medium" ? usableWidth - LEFT_COL_WIDTH : usableWidth
5521
+ );
5522
+ const activityPanelWidth = usableWidth;
5429
5523
  const overlayBarRows = ui.state.mode === "search" || ui.state.mode === "overlay:comment" ? 1 : 0;
5430
5524
  const toastRows = toasts.length;
5431
5525
  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
5526
+ const totalPanelHeight = Math.max(
5527
+ 8,
5528
+ termSize.rows - CHROME_ROWS - overlayBarRows - toastRows - logPaneRows
5436
5529
  );
5530
+ const issuesPanelHeight = Math.max(5, totalPanelHeight - ACTIVITY_HEIGHT);
5437
5531
  const flatRows = useMemo3(
5438
- () => buildFlatRowsForTab(effectiveTabId ?? "", boardTree, effectiveStatusId),
5439
- [effectiveTabId, boardTree, effectiveStatusId]
5532
+ () => buildFlatRowsForRepo(boardTree.sections, selectedRepoName, selectedStatusGroupId),
5533
+ [boardTree.sections, selectedRepoName, selectedStatusGroupId]
5440
5534
  );
5441
5535
  const scrollRef = useRef13(0);
5442
- const prevTabIdRef = useRef13(null);
5443
- if (effectiveTabId !== prevTabIdRef.current) {
5444
- prevTabIdRef.current = effectiveTabId;
5536
+ const prevRepoRef = useRef13(null);
5537
+ const prevStatusRef = useRef13(null);
5538
+ if (selectedRepoName !== prevRepoRef.current || selectedStatusGroupId !== prevStatusRef.current) {
5539
+ prevRepoRef.current = selectedRepoName;
5540
+ prevStatusRef.current = selectedStatusGroupId;
5445
5541
  scrollRef.current = 0;
5446
5542
  }
5447
5543
  const selectedRowIdx = useMemo3(
@@ -5451,35 +5547,29 @@ function Dashboard({ config: config2, options, activeProfile }) {
5451
5547
  if (selectedRowIdx >= 0) {
5452
5548
  if (selectedRowIdx < scrollRef.current) {
5453
5549
  scrollRef.current = selectedRowIdx;
5454
- } else if (selectedRowIdx >= scrollRef.current + viewportHeight) {
5455
- scrollRef.current = selectedRowIdx - viewportHeight + 1;
5550
+ } else if (selectedRowIdx >= scrollRef.current + issuesPanelHeight) {
5551
+ scrollRef.current = selectedRowIdx - issuesPanelHeight + 1;
5456
5552
  }
5457
5553
  }
5458
- const maxOffset = Math.max(0, flatRows.length - viewportHeight);
5554
+ const maxOffset = Math.max(0, flatRows.length - issuesPanelHeight);
5459
5555
  scrollRef.current = Math.max(0, Math.min(scrollRef.current, maxOffset));
5460
- const visibleRows = flatRows.slice(scrollRef.current, scrollRef.current + viewportHeight);
5556
+ const visibleRows = flatRows.slice(scrollRef.current, scrollRef.current + issuesPanelHeight);
5461
5557
  const hasMoreAbove = scrollRef.current > 0;
5462
- const hasMoreBelow = scrollRef.current + viewportHeight < flatRows.length;
5558
+ const hasMoreBelow = scrollRef.current + issuesPanelHeight < flatRows.length;
5463
5559
  const aboveCount = scrollRef.current;
5464
- const belowCount = flatRows.length - scrollRef.current - viewportHeight;
5560
+ const belowCount = flatRows.length - scrollRef.current - issuesPanelHeight;
5465
5561
  const selectedItem = useMemo3(() => {
5466
5562
  const id = nav.selectedId;
5467
- if (!id || isHeaderId(id)) return { issue: null, task: null, repoName: null };
5563
+ if (!id || isHeaderId(id)) return { issue: null, repoName: null };
5468
5564
  if (id.startsWith("gh:")) {
5469
5565
  for (const rd of repos) {
5470
5566
  for (const issue of rd.issues) {
5471
- if (`gh:${rd.repo.name}:${issue.number}` === id)
5472
- return { issue, task: null, repoName: rd.repo.name };
5567
+ if (`gh:${rd.repo.name}:${issue.number}` === id) return { issue, repoName: rd.repo.name };
5473
5568
  }
5474
5569
  }
5475
5570
  }
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]);
5571
+ return { issue: null, repoName: null };
5572
+ }, [nav.selectedId, repos]);
5483
5573
  const currentCommentsState = useMemo3(() => {
5484
5574
  if (!(selectedItem.issue && selectedItem.repoName)) return null;
5485
5575
  return commentCacheRef.current[`${selectedItem.repoName}:${selectedItem.issue.number}`] ?? null;
@@ -5490,20 +5580,20 @@ function Dashboard({ config: config2, options, activeProfile }) {
5490
5580
  }, [selectedItem.repoName, config2.repos]);
5491
5581
  const selectedRepoStatusOptions = useMemo3(() => {
5492
5582
  const repoName = multiSelect.count > 0 ? multiSelect.constrainedRepo : selectedItem.repoName;
5493
- if (!repoName || repoName === "ticktick") return [];
5583
+ if (!repoName) return [];
5494
5584
  const rd = repos.find((r) => r.repo.name === repoName);
5495
5585
  return rd?.statusOptions ?? [];
5496
5586
  }, [selectedItem.repoName, repos, multiSelect.count, multiSelect.constrainedRepo]);
5497
- const handleOpen = useCallback11(() => {
5587
+ const handleOpen = useCallback12(() => {
5498
5588
  const found = findSelectedIssueWithRepo(repos, nav.selectedId);
5499
5589
  if (found) openInBrowser(found.issue.url);
5500
5590
  }, [repos, nav.selectedId]);
5501
- const handleSlack = useCallback11(() => {
5591
+ const handleSlack = useCallback12(() => {
5502
5592
  const found = findSelectedIssueWithRepo(repos, nav.selectedId);
5503
5593
  if (!found?.issue.slackThreadUrl) return;
5504
5594
  openInBrowser(found.issue.slackThreadUrl);
5505
5595
  }, [repos, nav.selectedId]);
5506
- const handleCopyLink = useCallback11(() => {
5596
+ const handleCopyLink = useCallback12(() => {
5507
5597
  const found = findSelectedIssueWithRepo(repos, nav.selectedId);
5508
5598
  if (!found) return;
5509
5599
  const rc = config2.repos.find((r) => r.name === found.repoName);
@@ -5529,17 +5619,12 @@ function Dashboard({ config: config2, options, activeProfile }) {
5529
5619
  }
5530
5620
  }, [repos, nav.selectedId, config2.repos, toast]);
5531
5621
  const multiSelectType = useMemo3(() => {
5532
- let hasGh = false;
5533
- let hasTt = false;
5534
5622
  for (const id of multiSelect.selected) {
5535
- if (id.startsWith("gh:")) hasGh = true;
5536
- if (id.startsWith("tt:")) hasTt = true;
5623
+ if (id.startsWith("tt:")) return "ticktick";
5537
5624
  }
5538
- if (hasGh && hasTt) return "mixed";
5539
- if (hasTt) return "ticktick";
5540
5625
  return "github";
5541
5626
  }, [multiSelect.selected]);
5542
- const handleBulkAction = useCallback11(
5627
+ const handleBulkAction = useCallback12(
5543
5628
  (action) => {
5544
5629
  const ids = multiSelect.selected;
5545
5630
  switch (action.type) {
@@ -5572,10 +5657,9 @@ function Dashboard({ config: config2, options, activeProfile }) {
5572
5657
  case "statusChange":
5573
5658
  ui.enterStatus();
5574
5659
  return;
5575
- // status picker will call handleBulkStatusSelect on select
5576
5660
  case "complete":
5577
5661
  case "delete":
5578
- toast.info(`Bulk ${action.type} not yet implemented for TickTick`);
5662
+ toast.info(`Bulk ${action.type} not yet implemented`);
5579
5663
  ui.exitOverlay();
5580
5664
  multiSelect.clear();
5581
5665
  return;
@@ -5583,7 +5667,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5583
5667
  },
5584
5668
  [multiSelect, actions, ui, toast]
5585
5669
  );
5586
- const handleBulkStatusSelect = useCallback11(
5670
+ const handleBulkStatusSelect = useCallback12(
5587
5671
  (optionId) => {
5588
5672
  const ids = multiSelect.selected;
5589
5673
  ui.exitOverlay();
@@ -5599,30 +5683,28 @@ function Dashboard({ config: config2, options, activeProfile }) {
5599
5683
  },
5600
5684
  [multiSelect, actions, ui]
5601
5685
  );
5602
- const handleFuzzySelect = useCallback11(
5686
+ const handleFuzzySelect = useCallback12(
5603
5687
  (navId) => {
5604
5688
  nav.select(navId);
5605
5689
  if (navId.startsWith("gh:")) {
5606
5690
  const parts = navId.split(":");
5607
5691
  const repoName = parts[1];
5608
5692
  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);
5693
+ const repoIdx = boardTree.sections.findIndex((s) => s.sectionId === repoName);
5694
+ if (repoIdx >= 0) {
5695
+ setSelectedRepoIdx(repoIdx);
5696
+ const section = boardTree.sections[repoIdx];
5697
+ const issueNum = parts[2] ? Number(parts[2]) : null;
5698
+ const groupIdx = section?.groups.findIndex((g) => g.issues.some((iss) => iss.number === issueNum)) ?? -1;
5699
+ setSelectedStatusIdx(Math.max(0, groupIdx));
5700
+ }
5616
5701
  }
5617
- } else if (navId.startsWith("tt:")) {
5618
- setActiveTabId("ticktick");
5619
- setActiveStatusId(null);
5620
5702
  }
5621
5703
  ui.exitToNormal();
5622
5704
  },
5623
5705
  [nav, ui, boardTree]
5624
5706
  );
5625
- const onSearchEscape = useCallback11(() => {
5707
+ const onSearchEscape = useCallback12(() => {
5626
5708
  ui.exitOverlay();
5627
5709
  setSearchQuery("");
5628
5710
  }, [ui]);
@@ -5652,11 +5734,16 @@ function Dashboard({ config: config2, options, activeProfile }) {
5652
5734
  handleToggleLog: () => setLogVisible((v) => !v)
5653
5735
  },
5654
5736
  onSearchEscape,
5655
- tabNav: { next: nextTab, prev: prevTab, jumpTo: jumpToTab, count: tabs.length },
5656
- statusNav: isRepoTab(effectiveTabId) ? { next: nextStatus, prev: prevStatus } : null
5737
+ panelFocus,
5738
+ reposNav,
5739
+ statusesNav,
5740
+ activityNav,
5741
+ onRepoEnter,
5742
+ onStatusEnter,
5743
+ onActivityEnter
5657
5744
  });
5658
5745
  if (status === "loading" && !data) {
5659
- return /* @__PURE__ */ jsx23(Box22, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx23(Spinner4, { label: "Loading dashboard..." }) });
5746
+ return /* @__PURE__ */ jsx25(Box24, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx25(Spinner4, { label: "Loading dashboard..." }) });
5660
5747
  }
5661
5748
  const now = data?.fetchedAt ?? /* @__PURE__ */ new Date();
5662
5749
  const dateStr = now.toLocaleDateString("en-US", {
@@ -5664,36 +5751,120 @@ function Dashboard({ config: config2, options, activeProfile }) {
5664
5751
  day: "numeric",
5665
5752
  year: "numeric"
5666
5753
  });
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: [
5754
+ const reposData = boardTree.sections.map(({ repo, groups }) => ({
5755
+ name: repo.name,
5756
+ openCount: groups.reduce((s, g) => s + g.issues.length, 0)
5757
+ }));
5758
+ const statusesData = (selectedSection?.groups ?? []).map(({ label, subId, issues }) => ({
5759
+ id: subId,
5760
+ label,
5761
+ count: issues.length
5762
+ }));
5763
+ const reposPanel = /* @__PURE__ */ jsx25(
5764
+ ReposPanel,
5765
+ {
5766
+ repos: reposData,
5767
+ selectedIdx: clampedRepoIdx,
5768
+ isActive: panelFocus.activePanelId === 1,
5769
+ width: LEFT_COL_WIDTH
5770
+ }
5771
+ );
5772
+ const statusesPanel = /* @__PURE__ */ jsx25(
5773
+ StatusesPanel,
5774
+ {
5775
+ groups: statusesData,
5776
+ selectedIdx: clampedStatusIdx,
5777
+ isActive: panelFocus.activePanelId === 2,
5778
+ width: LEFT_COL_WIDTH,
5779
+ flexGrow: 1
5780
+ }
5781
+ );
5782
+ const issuesPanelTitle = `[3] Issues${selectedSection ? ` \u2014 ${selectedSection.repo.shortName}` : ""}${selectedStatusGroup ? ` / ${selectedStatusGroup.label}` : ""}`;
5783
+ const issuesPanel = /* @__PURE__ */ jsxs25(
5784
+ Panel,
5785
+ {
5786
+ title: issuesPanelTitle,
5787
+ isActive: panelFocus.activePanelId === 3,
5788
+ width: issuesPanelWidth,
5789
+ flexGrow: 1,
5790
+ children: [
5791
+ hasMoreAbove ? /* @__PURE__ */ jsxs25(Text23, { color: "gray", dimColor: true, children: [
5792
+ " ",
5793
+ "\u25B2",
5794
+ " ",
5795
+ aboveCount,
5796
+ " more above"
5797
+ ] }) : null,
5798
+ visibleRows.map((row) => /* @__PURE__ */ jsx25(
5799
+ RowRenderer,
5800
+ {
5801
+ row,
5802
+ selectedId: nav.selectedId,
5803
+ selfLogin: config2.board.assignee,
5804
+ isMultiSelected: ui.state.mode === "multiSelect" && row.navId ? multiSelect.isSelected(row.navId) : void 0
5805
+ },
5806
+ row.key
5807
+ )),
5808
+ hasMoreBelow ? /* @__PURE__ */ jsxs25(Text23, { color: "gray", dimColor: true, children: [
5809
+ " ",
5810
+ "\u25BC",
5811
+ " ",
5812
+ belowCount,
5813
+ " more below"
5814
+ ] }) : null
5815
+ ]
5816
+ }
5817
+ );
5818
+ const detailPanel = showDetailPanel ? /* @__PURE__ */ jsx25(
5819
+ DetailPanel,
5820
+ {
5821
+ issue: selectedItem.issue,
5822
+ width: detailPanelWidth,
5823
+ isActive: panelFocus.activePanelId === 0,
5824
+ issueRepo: selectedItem.repoName,
5825
+ fetchComments: handleFetchComments,
5826
+ commentsState: currentCommentsState
5827
+ }
5828
+ ) : null;
5829
+ const activityPanel = /* @__PURE__ */ jsx25(
5830
+ ActivityPanel,
5831
+ {
5832
+ events: boardTree.activity,
5833
+ selectedIdx: clampedActivityIdx,
5834
+ isActive: panelFocus.activePanelId === 4,
5835
+ height: ACTIVITY_HEIGHT,
5836
+ width: activityPanelWidth
5837
+ }
5838
+ );
5839
+ return /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", paddingX: 1, children: [
5840
+ /* @__PURE__ */ jsxs25(Box24, { children: [
5841
+ /* @__PURE__ */ jsx25(Text23, { color: "cyan", bold: true, children: "HOG BOARD" }),
5842
+ activeProfile ? /* @__PURE__ */ jsxs25(Text23, { color: "yellow", children: [
5671
5843
  " [",
5672
5844
  activeProfile,
5673
5845
  "]"
5674
5846
  ] }) : null,
5675
- /* @__PURE__ */ jsxs23(Text22, { color: "gray", children: [
5847
+ /* @__PURE__ */ jsxs25(Text23, { color: "gray", children: [
5676
5848
  " ",
5677
5849
  "\u2014",
5678
5850
  " ",
5679
5851
  dateStr
5680
5852
  ] }),
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
5853
+ /* @__PURE__ */ jsx25(Text23, { children: " " }),
5854
+ isRefreshing ? /* @__PURE__ */ jsxs25(Fragment5, { children: [
5855
+ /* @__PURE__ */ jsx25(Spinner4, { label: "" }),
5856
+ /* @__PURE__ */ jsx25(Text23, { color: "cyan", children: " Refreshing..." })
5857
+ ] }) : /* @__PURE__ */ jsxs25(Fragment5, { children: [
5858
+ /* @__PURE__ */ jsx25(RefreshAge, { lastRefresh }),
5859
+ consecutiveFailures > 0 ? /* @__PURE__ */ jsx25(Text23, { color: "red", children: " (!)" }) : null
5688
5860
  ] }),
5689
- autoRefreshPaused ? /* @__PURE__ */ jsx23(Text22, { color: "yellow", children: " Auto-refresh paused \u2014 press r to retry" }) : null
5861
+ autoRefreshPaused ? /* @__PURE__ */ jsx25(Text23, { color: "yellow", children: " Auto-refresh paused \u2014 press r to retry" }) : null
5690
5862
  ] }),
5691
- error ? /* @__PURE__ */ jsxs23(Text22, { color: "red", children: [
5863
+ error ? /* @__PURE__ */ jsxs25(Text23, { color: "red", children: [
5692
5864
  "Error: ",
5693
5865
  error
5694
5866
  ] }) : null,
5695
- /* @__PURE__ */ jsx23(TabBar, { tabs, activeTabId: effectiveTabId, totalWidth: termSize.cols }),
5696
- /* @__PURE__ */ jsx23(
5867
+ /* @__PURE__ */ jsx25(
5697
5868
  OverlayRenderer,
5698
5869
  {
5699
5870
  uiState: ui.state,
@@ -5735,61 +5906,25 @@ function Dashboard({ config: config2, options, activeProfile }) {
5735
5906
  onPushEntry: pushEntry
5736
5907
  }
5737
5908
  ),
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(
5909
+ !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__ */ jsx25(
5910
+ PanelLayout,
5911
+ {
5912
+ cols: termSize.cols,
5913
+ issuesPanelHeight,
5914
+ reposPanel,
5915
+ statusesPanel,
5916
+ issuesPanel,
5917
+ detailPanel,
5918
+ activityPanel
5919
+ }
5920
+ ) : null,
5921
+ /* @__PURE__ */ jsx25(ToastContainer, { toasts }),
5922
+ logVisible ? /* @__PURE__ */ jsx25(ActionLog, { entries: logEntries }) : null,
5923
+ /* @__PURE__ */ jsx25(
5790
5924
  HintBar,
5791
5925
  {
5792
5926
  uiMode: ui.state.mode,
5927
+ activePanelId: panelFocus.activePanelId,
5793
5928
  multiSelectCount: multiSelect.count,
5794
5929
  searchQuery,
5795
5930
  mineOnly,
@@ -5798,7 +5933,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5798
5933
  )
5799
5934
  ] });
5800
5935
  }
5801
- var PRIORITY_RANK, CHROME_ROWS_REPO, CHROME_ROWS_OTHER;
5936
+ var PRIORITY_RANK, CHROME_ROWS;
5802
5937
  var init_dashboard = __esm({
5803
5938
  "src/board/components/dashboard.tsx"() {
5804
5939
  "use strict";
@@ -5811,15 +5946,19 @@ var init_dashboard = __esm({
5811
5946
  init_use_keyboard();
5812
5947
  init_use_multi_select();
5813
5948
  init_use_navigation();
5949
+ init_use_panel_focus();
5814
5950
  init_use_toast();
5815
5951
  init_use_ui_state();
5816
5952
  init_action_log();
5953
+ init_activity_panel();
5817
5954
  init_detail_panel();
5818
5955
  init_hint_bar();
5819
5956
  init_overlay_renderer();
5957
+ init_panel();
5958
+ init_panel_layout();
5959
+ init_repos_panel();
5820
5960
  init_row_renderer();
5821
- init_status_tab_bar();
5822
- init_tab_bar();
5961
+ init_statuses_panel();
5823
5962
  init_toast_container();
5824
5963
  PRIORITY_RANK = {
5825
5964
  "priority:critical": 0,
@@ -5827,8 +5966,7 @@ var init_dashboard = __esm({
5827
5966
  "priority:medium": 2,
5828
5967
  "priority:low": 3
5829
5968
  };
5830
- CHROME_ROWS_REPO = 6;
5831
- CHROME_ROWS_OTHER = 5;
5969
+ CHROME_ROWS = 3;
5832
5970
  }
5833
5971
  });
5834
5972
 
@@ -5838,10 +5976,10 @@ __export(live_exports, {
5838
5976
  runLiveDashboard: () => runLiveDashboard
5839
5977
  });
5840
5978
  import { render } from "ink";
5841
- import { jsx as jsx24 } from "react/jsx-runtime";
5979
+ import { jsx as jsx26 } from "react/jsx-runtime";
5842
5980
  async function runLiveDashboard(config2, options, activeProfile) {
5843
5981
  const instance = render(
5844
- /* @__PURE__ */ jsx24(Dashboard, { config: config2, options, activeProfile: activeProfile ?? null })
5982
+ /* @__PURE__ */ jsx26(Dashboard, { config: config2, options, activeProfile: activeProfile ?? null })
5845
5983
  );
5846
5984
  setInkInstance(instance);
5847
5985
  await instance.waitUntilExit();
@@ -5868,7 +6006,7 @@ function extractSlackUrl(body) {
5868
6006
  const match = body.match(SLACK_URL_RE2);
5869
6007
  return match?.[0];
5870
6008
  }
5871
- function fetchRecentActivity(repoName, shortName) {
6009
+ function fetchRecentActivity(repoName, shortName2) {
5872
6010
  try {
5873
6011
  const output = execFileSync4(
5874
6012
  "gh",
@@ -5922,7 +6060,7 @@ function fetchRecentActivity(repoName, shortName) {
5922
6060
  }
5923
6061
  events.push({
5924
6062
  type: eventType,
5925
- repoShortName: shortName,
6063
+ repoShortName: shortName2,
5926
6064
  issueNumber: ev.number,
5927
6065
  actor: ev.actor,
5928
6066
  summary,
@@ -6064,7 +6202,7 @@ __export(format_static_exports, {
6064
6202
  renderBoardJson: () => renderBoardJson,
6065
6203
  renderStaticBoard: () => renderStaticBoard
6066
6204
  });
6067
- function truncate3(s, max) {
6205
+ function truncate2(s, max) {
6068
6206
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
6069
6207
  }
6070
6208
  function issueAssignee(issue, selfLogin) {
@@ -6077,13 +6215,13 @@ function issueAssignee(issue, selfLogin) {
6077
6215
  }
6078
6216
  function formatIssueLine(issue, selfLogin, maxTitle) {
6079
6217
  const num = theme.text.accent(`#${String(issue.number).padEnd(5)}`);
6080
- const title = truncate3(issue.title, maxTitle);
6218
+ const title = truncate2(issue.title, maxTitle);
6081
6219
  const assignee = issueAssignee(issue, selfLogin);
6082
6220
  return ` ${num} ${title.padEnd(maxTitle)} ${assignee}`;
6083
6221
  }
6084
6222
  function formatTaskLine(task2, maxTitle) {
6085
6223
  const pri = task2.priority === 5 /* High */ ? theme.priority.high("[!]") : task2.priority === 3 /* Medium */ ? theme.priority.medium("[~]") : " ";
6086
- const title = truncate3(task2.title, maxTitle);
6224
+ const title = truncate2(task2.title, maxTitle);
6087
6225
  const due = task2.dueDate ? formatDueDate(task2.dueDate) : "";
6088
6226
  return ` ${pri} ${title.padEnd(maxTitle)} ${theme.text.secondary(due)}`;
6089
6227
  }
@@ -6504,13 +6642,13 @@ Configuring ${repoName}...`);
6504
6642
  } else {
6505
6643
  completionAction = { type: "closeIssue" };
6506
6644
  }
6507
- const shortName = await input({
6645
+ const shortName2 = await input({
6508
6646
  message: ` Short name for ${repoName}:`,
6509
6647
  default: name
6510
6648
  });
6511
6649
  repos.push({
6512
6650
  name: repoName,
6513
- shortName,
6651
+ shortName: shortName2,
6514
6652
  projectNumber,
6515
6653
  statusFieldId,
6516
6654
  ...dueDateFieldId ? { dueDateFieldId } : {},
@@ -7004,7 +7142,7 @@ function resolveProjectId(projectId) {
7004
7142
  process.exit(1);
7005
7143
  }
7006
7144
  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) => {
7145
+ program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.12.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
7008
7146
  const opts = thisCommand.opts();
7009
7147
  if (opts.json) setFormat("json");
7010
7148
  if (opts.human) setFormat("human");
@@ -7199,7 +7337,7 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7199
7337
  console.error(`Repo "${name}" is already configured.`);
7200
7338
  process.exit(1);
7201
7339
  }
7202
- const shortName = name.split("/")[1] ?? name;
7340
+ const shortName2 = name.split("/")[1] ?? name;
7203
7341
  let completionAction;
7204
7342
  switch (opts.completionType) {
7205
7343
  case "addLabel":
@@ -7227,7 +7365,7 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7227
7365
  }
7228
7366
  const newRepo = {
7229
7367
  name,
7230
- shortName,
7368
+ shortName: shortName2,
7231
7369
  projectNumber: Number.parseInt(opts.projectNumber, 10),
7232
7370
  statusFieldId: opts.statusFieldId,
7233
7371
  completionAction
@@ -7237,7 +7375,7 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7237
7375
  if (useJson()) {
7238
7376
  jsonOut({ ok: true, message: `Added ${name}`, data: newRepo });
7239
7377
  } else {
7240
- console.log(`Added ${shortName} \u2192 ${name}`);
7378
+ console.log(`Added ${shortName2} \u2192 ${name}`);
7241
7379
  }
7242
7380
  });
7243
7381
  config.command("repos:rm <name>").description("Remove a repository from tracking").action((name) => {