@ondrej-svec/hog 1.9.2 → 1.10.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
@@ -1999,7 +1999,8 @@ function useKeyboard({
1999
1999
  selectedRepoStatusOptionsLength,
2000
2000
  actions,
2001
2001
  onSearchEscape,
2002
- tabNav
2002
+ tabNav,
2003
+ statusNav
2003
2004
  }) {
2004
2005
  const {
2005
2006
  exit,
@@ -2096,6 +2097,14 @@ function useKeyboard({
2096
2097
  return;
2097
2098
  }
2098
2099
  if (input2 === "s") {
2100
+ statusNav?.next();
2101
+ return;
2102
+ }
2103
+ if (input2 === "S") {
2104
+ statusNav?.prev();
2105
+ return;
2106
+ }
2107
+ if (input2 === "o") {
2099
2108
  handleSlack();
2100
2109
  return;
2101
2110
  }
@@ -2187,6 +2196,7 @@ function useKeyboard({
2187
2196
  ui,
2188
2197
  nav,
2189
2198
  tabNav,
2199
+ statusNav,
2190
2200
  exit,
2191
2201
  refresh,
2192
2202
  handleSlack,
@@ -3018,7 +3028,7 @@ function HintBar({ uiMode, multiSelectCount, searchQuery, mineOnly, hasUndoable
3018
3028
  }
3019
3029
  return /* @__PURE__ */ jsxs3(Box3, { children: [
3020
3030
  /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
3021
- "j/k:nav Tab:next-tab 1-9:jump Enter:open m:status c:comment F:find t:@me e:edit",
3031
+ "j/k:nav Tab:repo s/S:status Enter:open m:status c:comment F:find t:@me e:edit",
3022
3032
  hasUndoable ? " u:undo" : "",
3023
3033
  " ?:more q:quit"
3024
3034
  ] }),
@@ -4067,9 +4077,10 @@ var init_help_overlay = __esm({
4067
4077
  items: [
4068
4078
  { key: "j / Down", desc: "Move down" },
4069
4079
  { key: "k / Up", desc: "Move up" },
4070
- { key: "Tab", desc: "Next tab" },
4071
- { key: "Shift+Tab", desc: "Previous tab" },
4072
- { key: "1-9", desc: "Jump to tab by number" }
4080
+ { key: "Tab", desc: "Next repo tab" },
4081
+ { key: "Shift+Tab", desc: "Previous repo tab" },
4082
+ { key: "1-9", desc: "Jump to repo tab by number" },
4083
+ { key: "s / S", desc: "Next / prev status tab" }
4073
4084
  ]
4074
4085
  },
4075
4086
  {
@@ -4094,7 +4105,7 @@ var init_help_overlay = __esm({
4094
4105
  { key: "c", desc: "Comment on issue" },
4095
4106
  { key: "m", desc: "Move status" },
4096
4107
  { key: "e", desc: "Edit issue in $EDITOR" },
4097
- { key: "s", desc: "Open Slack thread" },
4108
+ { key: "o", desc: "Open Slack thread" },
4098
4109
  { key: "y", desc: "Copy issue link to clipboard" },
4099
4110
  { key: "n", desc: "Create new issue" },
4100
4111
  { key: "I", desc: "Natural-language issue create" },
@@ -4904,13 +4915,36 @@ var init_row_renderer = __esm({
4904
4915
  }
4905
4916
  });
4906
4917
 
4907
- // src/board/components/tab-bar.tsx
4918
+ // src/board/components/status-tab-bar.tsx
4908
4919
  import { Box as Box19, Text as Text19 } from "ink";
4909
4920
  import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
4910
- function TabBar({ tabs, activeTabId, totalWidth }) {
4911
- return /* @__PURE__ */ jsx20(Box19, { width: totalWidth, children: tabs.map((tab, i) => {
4921
+ function StatusTabBar({ tabs, activeTabId, totalWidth }) {
4922
+ if (tabs.length === 0) return null;
4923
+ return /* @__PURE__ */ jsx20(Box19, { width: totalWidth, children: tabs.map((tab) => {
4912
4924
  const isActive = tab.id === activeTabId;
4913
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: [
4914
4948
  i + 1,
4915
4949
  ":",
4916
4950
  tab.label,
@@ -4928,21 +4962,21 @@ var init_tab_bar = __esm({
4928
4962
 
4929
4963
  // src/board/components/toast-container.tsx
4930
4964
  import { Spinner as Spinner3 } from "@inkjs/ui";
4931
- import { Box as Box20, Text as Text20 } from "ink";
4932
- import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
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";
4933
4967
  function ToastContainer({ toasts }) {
4934
4968
  if (toasts.length === 0) return null;
4935
- return /* @__PURE__ */ jsx21(Box20, { flexDirection: "column", children: toasts.map((t) => /* @__PURE__ */ jsx21(Box20, { children: t.type === "loading" ? /* @__PURE__ */ jsxs21(Fragment4, { children: [
4936
- /* @__PURE__ */ jsx21(Spinner3, { label: "" }),
4937
- /* @__PURE__ */ jsxs21(Text20, { color: "cyan", children: [
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: [
4938
4972
  " ",
4939
4973
  t.message
4940
4974
  ] })
4941
- ] }) : /* @__PURE__ */ jsxs21(Text20, { color: TYPE_COLORS[t.type], children: [
4975
+ ] }) : /* @__PURE__ */ jsxs22(Text21, { color: TYPE_COLORS[t.type], children: [
4942
4976
  TYPE_PREFIXES[t.type],
4943
4977
  " ",
4944
4978
  t.message,
4945
- t.type === "error" ? /* @__PURE__ */ jsx21(Text20, { color: "gray", children: t.retry ? " [r]etry [d]ismiss" : " [d]ismiss" }) : null
4979
+ t.type === "error" ? /* @__PURE__ */ jsx22(Text21, { color: "gray", children: t.retry ? " [r]etry [d]ismiss" : " [d]ismiss" }) : null
4946
4980
  ] }) }, t.id)) });
4947
4981
  }
4948
4982
  var TYPE_COLORS, TYPE_PREFIXES;
@@ -4966,9 +5000,9 @@ var init_toast_container = __esm({
4966
5000
  // src/board/components/dashboard.tsx
4967
5001
  import { execFileSync as execFileSync3, spawnSync as spawnSync4 } from "child_process";
4968
5002
  import { Spinner as Spinner4 } from "@inkjs/ui";
4969
- import { Box as Box21, Text as Text21, useApp, useStdout } from "ink";
5003
+ import { Box as Box22, Text as Text22, useApp, useStdout } from "ink";
4970
5004
  import { useCallback as useCallback11, useEffect as useEffect9, useMemo as useMemo3, useRef as useRef13, useState as useState15 } from "react";
4971
- import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
5005
+ import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs23 } from "react/jsx-runtime";
4972
5006
  function resolveStatusGroups(statusOptions, configuredGroups) {
4973
5007
  if (configuredGroups && configuredGroups.length > 0) {
4974
5008
  return configuredGroups.map((entry) => {
@@ -5049,7 +5083,16 @@ function buildTabs(tree) {
5049
5083
  tabs.push({ id: "ticktick", label: "Tasks", count: tree.tasks.length });
5050
5084
  return tabs;
5051
5085
  }
5052
- function buildNavItemsForTab(tabId, tree) {
5086
+ function isRepoTab(tabId) {
5087
+ return tabId !== null && tabId !== "activity" && tabId !== "ticktick";
5088
+ }
5089
+ function buildStatusTabs(tabId, tree) {
5090
+ if (!isRepoTab(tabId)) return [];
5091
+ const section = tree.sections.find((s) => s.sectionId === tabId);
5092
+ if (!section) return [];
5093
+ return section.groups.map((g) => ({ id: g.subId, label: g.label, count: g.issues.length }));
5094
+ }
5095
+ function buildNavItemsForTab(tabId, tree, activeStatusId) {
5053
5096
  if (tabId === "activity") return [];
5054
5097
  if (tabId === "ticktick")
5055
5098
  return tree.tasks.map((task2) => ({
@@ -5059,15 +5102,15 @@ function buildNavItemsForTab(tabId, tree) {
5059
5102
  }));
5060
5103
  const section = tree.sections.find((s) => s.sectionId === tabId);
5061
5104
  if (!section) return [];
5062
- return section.groups.flatMap(
5063
- (group) => group.issues.map((issue) => ({
5064
- id: `gh:${section.repo.name}:${issue.number}`,
5065
- section: tabId,
5066
- type: "item"
5067
- }))
5068
- );
5105
+ const activeGroup = section.groups.find((g) => g.subId === activeStatusId) ?? section.groups[0];
5106
+ if (!activeGroup) return [];
5107
+ return activeGroup.issues.map((issue) => ({
5108
+ id: `gh:${section.repo.name}:${issue.number}`,
5109
+ section: tabId,
5110
+ type: "item"
5111
+ }));
5069
5112
  }
5070
- function buildFlatRowsForTab(tabId, tree) {
5113
+ function buildFlatRowsForTab(tabId, tree, activeStatusId) {
5071
5114
  if (tabId === "activity")
5072
5115
  return tree.activity.map((event, i) => ({
5073
5116
  type: "activity",
@@ -5090,30 +5133,15 @@ function buildFlatRowsForTab(tabId, tree) {
5090
5133
  return [
5091
5134
  { type: "subHeader", key: `empty:${tabId}`, navId: null, text: "No open issues" }
5092
5135
  ];
5093
- const rows = [];
5094
- let isFirst = true;
5095
- for (const group of section.groups) {
5096
- if (!isFirst)
5097
- rows.push({ type: "gap", key: `gap:${tabId}:${group.label}`, navId: null });
5098
- isFirst = false;
5099
- rows.push({
5100
- type: "subHeader",
5101
- key: group.subId,
5102
- navId: null,
5103
- text: group.label,
5104
- count: group.issues.length,
5105
- isCollapsed: false
5106
- });
5107
- for (const issue of group.issues)
5108
- rows.push({
5109
- type: "issue",
5110
- key: `gh:${section.repo.name}:${issue.number}`,
5111
- navId: `gh:${section.repo.name}:${issue.number}`,
5112
- issue,
5113
- repoName: section.repo.name
5114
- });
5115
- }
5116
- return rows;
5136
+ const activeGroup = section.groups.find((g) => g.subId === activeStatusId) ?? section.groups[0];
5137
+ if (!activeGroup) return [];
5138
+ return activeGroup.issues.map((issue) => ({
5139
+ type: "issue",
5140
+ key: `gh:${section.repo.name}:${issue.number}`,
5141
+ navId: `gh:${section.repo.name}:${issue.number}`,
5142
+ issue,
5143
+ repoName: section.repo.name
5144
+ }));
5117
5145
  }
5118
5146
  function openInBrowser(url) {
5119
5147
  if (!(url.startsWith("https://") || url.startsWith("http://"))) return;
@@ -5139,7 +5167,7 @@ function RefreshAge({ lastRefresh }) {
5139
5167
  return () => clearInterval(id);
5140
5168
  }, []);
5141
5169
  if (!lastRefresh) return null;
5142
- return /* @__PURE__ */ jsxs22(Text21, { color: refreshAgeColor(lastRefresh), children: [
5170
+ return /* @__PURE__ */ jsxs23(Text22, { color: refreshAgeColor(lastRefresh), children: [
5143
5171
  "Updated ",
5144
5172
  timeAgo(lastRefresh)
5145
5173
  ] });
@@ -5215,22 +5243,46 @@ function Dashboard({ config: config2, options, activeProfile }) {
5215
5243
  const nextTab = useCallback11(() => {
5216
5244
  if (tabs.length === 0) return;
5217
5245
  setActiveTabId(tabs[(Math.max(activeTabIdx, 0) + 1) % tabs.length]?.id ?? null);
5246
+ setActiveStatusId(null);
5218
5247
  }, [activeTabIdx, tabs]);
5219
5248
  const prevTab = useCallback11(() => {
5220
5249
  if (tabs.length === 0) return;
5221
5250
  setActiveTabId(tabs[(Math.max(activeTabIdx, 0) - 1 + tabs.length) % tabs.length]?.id ?? null);
5251
+ setActiveStatusId(null);
5222
5252
  }, [activeTabIdx, tabs]);
5223
5253
  const jumpToTab = useCallback11(
5224
5254
  (idx) => {
5225
5255
  const tab = tabs[idx];
5226
- if (tab) setActiveTabId(tab.id);
5256
+ if (tab) {
5257
+ setActiveTabId(tab.id);
5258
+ setActiveStatusId(null);
5259
+ }
5227
5260
  },
5228
5261
  [tabs]
5229
5262
  );
5230
- const navItems = useMemo3(
5231
- () => buildNavItemsForTab(effectiveTabId ?? "", boardTree),
5263
+ const [activeStatusId, setActiveStatusId] = useState15(null);
5264
+ const statusTabs = useMemo3(
5265
+ () => buildStatusTabs(effectiveTabId, boardTree),
5232
5266
  [effectiveTabId, boardTree]
5233
5267
  );
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
5280
+ );
5281
+ }, [activeStatusIdx, statusTabs]);
5282
+ const navItems = useMemo3(
5283
+ () => buildNavItemsForTab(effectiveTabId ?? "", boardTree, effectiveStatusId),
5284
+ [effectiveTabId, boardTree, effectiveStatusId]
5285
+ );
5234
5286
  const nav = useNavigation(navItems);
5235
5287
  const getRepoForId = useCallback11((id) => {
5236
5288
  if (id.startsWith("gh:")) {
@@ -5377,13 +5429,14 @@ function Dashboard({ config: config2, options, activeProfile }) {
5377
5429
  const overlayBarRows = ui.state.mode === "search" || ui.state.mode === "overlay:comment" ? 1 : 0;
5378
5430
  const toastRows = toasts.length;
5379
5431
  const logPaneRows = logVisible ? 4 : 0;
5432
+ const chromeRows = isRepoTab(effectiveTabId) ? CHROME_ROWS_REPO : CHROME_ROWS_OTHER;
5380
5433
  const viewportHeight = Math.max(
5381
5434
  5,
5382
- termSize.rows - CHROME_ROWS - overlayBarRows - toastRows - logPaneRows
5435
+ termSize.rows - chromeRows - overlayBarRows - toastRows - logPaneRows
5383
5436
  );
5384
5437
  const flatRows = useMemo3(
5385
- () => buildFlatRowsForTab(effectiveTabId ?? "", boardTree),
5386
- [effectiveTabId, boardTree]
5438
+ () => buildFlatRowsForTab(effectiveTabId ?? "", boardTree, effectiveStatusId),
5439
+ [effectiveTabId, boardTree, effectiveStatusId]
5387
5440
  );
5388
5441
  const scrollRef = useRef13(0);
5389
5442
  const prevTabIdRef = useRef13(null);
@@ -5401,20 +5454,6 @@ function Dashboard({ config: config2, options, activeProfile }) {
5401
5454
  } else if (selectedRowIdx >= scrollRef.current + viewportHeight) {
5402
5455
  scrollRef.current = selectedRowIdx - viewportHeight + 1;
5403
5456
  }
5404
- if (scrollRef.current > 0 && scrollRef.current === selectedRowIdx) {
5405
- let subIdx = -1;
5406
- for (let i = selectedRowIdx - 1; i >= 0; i--) {
5407
- const row = flatRows[i];
5408
- if (!row || row.type === "sectionHeader") break;
5409
- if (row.type === "subHeader") {
5410
- subIdx = i;
5411
- break;
5412
- }
5413
- }
5414
- if (subIdx >= 0 && selectedRowIdx - subIdx < viewportHeight) {
5415
- scrollRef.current = subIdx;
5416
- }
5417
- }
5418
5457
  }
5419
5458
  const maxOffset = Math.max(0, flatRows.length - viewportHeight);
5420
5459
  scrollRef.current = Math.max(0, Math.min(scrollRef.current, maxOffset));
@@ -5565,13 +5604,23 @@ function Dashboard({ config: config2, options, activeProfile }) {
5565
5604
  nav.select(navId);
5566
5605
  if (navId.startsWith("gh:")) {
5567
5606
  const parts = navId.split(":");
5568
- if (parts.length >= 3 && parts[1]) setActiveTabId(parts[1]);
5607
+ const repoName = parts[1];
5608
+ 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);
5616
+ }
5569
5617
  } else if (navId.startsWith("tt:")) {
5570
5618
  setActiveTabId("ticktick");
5619
+ setActiveStatusId(null);
5571
5620
  }
5572
5621
  ui.exitToNormal();
5573
5622
  },
5574
- [nav, ui]
5623
+ [nav, ui, boardTree]
5575
5624
  );
5576
5625
  const onSearchEscape = useCallback11(() => {
5577
5626
  ui.exitOverlay();
@@ -5603,10 +5652,11 @@ function Dashboard({ config: config2, options, activeProfile }) {
5603
5652
  handleToggleLog: () => setLogVisible((v) => !v)
5604
5653
  },
5605
5654
  onSearchEscape,
5606
- tabNav: { next: nextTab, prev: prevTab, jumpTo: jumpToTab, count: tabs.length }
5655
+ tabNav: { next: nextTab, prev: prevTab, jumpTo: jumpToTab, count: tabs.length },
5656
+ statusNav: isRepoTab(effectiveTabId) ? { next: nextStatus, prev: prevStatus } : null
5607
5657
  });
5608
5658
  if (status === "loading" && !data) {
5609
- return /* @__PURE__ */ jsx22(Box21, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx22(Spinner4, { label: "Loading dashboard..." }) });
5659
+ return /* @__PURE__ */ jsx23(Box22, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx23(Spinner4, { label: "Loading dashboard..." }) });
5610
5660
  }
5611
5661
  const now = data?.fetchedAt ?? /* @__PURE__ */ new Date();
5612
5662
  const dateStr = now.toLocaleDateString("en-US", {
@@ -5614,36 +5664,36 @@ function Dashboard({ config: config2, options, activeProfile }) {
5614
5664
  day: "numeric",
5615
5665
  year: "numeric"
5616
5666
  });
5617
- return /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", paddingX: 1, children: [
5618
- /* @__PURE__ */ jsxs22(Box21, { children: [
5619
- /* @__PURE__ */ jsx22(Text21, { color: "cyan", bold: true, children: "HOG BOARD" }),
5620
- activeProfile ? /* @__PURE__ */ jsxs22(Text21, { color: "yellow", children: [
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: [
5621
5671
  " [",
5622
5672
  activeProfile,
5623
5673
  "]"
5624
5674
  ] }) : null,
5625
- /* @__PURE__ */ jsxs22(Text21, { color: "gray", children: [
5675
+ /* @__PURE__ */ jsxs23(Text22, { color: "gray", children: [
5626
5676
  " ",
5627
5677
  "\u2014",
5628
5678
  " ",
5629
5679
  dateStr
5630
5680
  ] }),
5631
- /* @__PURE__ */ jsx22(Text21, { children: " " }),
5632
- isRefreshing ? /* @__PURE__ */ jsxs22(Fragment5, { children: [
5633
- /* @__PURE__ */ jsx22(Spinner4, { label: "" }),
5634
- /* @__PURE__ */ jsx22(Text21, { color: "cyan", children: " Refreshing..." })
5635
- ] }) : /* @__PURE__ */ jsxs22(Fragment5, { children: [
5636
- /* @__PURE__ */ jsx22(RefreshAge, { lastRefresh }),
5637
- consecutiveFailures > 0 ? /* @__PURE__ */ jsx22(Text21, { color: "red", children: " (!)" }) : null
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
5638
5688
  ] }),
5639
- autoRefreshPaused ? /* @__PURE__ */ jsx22(Text21, { color: "yellow", children: " Auto-refresh paused \u2014 press r to retry" }) : null
5689
+ autoRefreshPaused ? /* @__PURE__ */ jsx23(Text22, { color: "yellow", children: " Auto-refresh paused \u2014 press r to retry" }) : null
5640
5690
  ] }),
5641
- error ? /* @__PURE__ */ jsxs22(Text21, { color: "red", children: [
5691
+ error ? /* @__PURE__ */ jsxs23(Text22, { color: "red", children: [
5642
5692
  "Error: ",
5643
5693
  error
5644
5694
  ] }) : null,
5645
- /* @__PURE__ */ jsx22(TabBar, { tabs, activeTabId: effectiveTabId, totalWidth: termSize.cols }),
5646
- /* @__PURE__ */ jsx22(
5695
+ /* @__PURE__ */ jsx23(TabBar, { tabs, activeTabId: effectiveTabId, totalWidth: termSize.cols }),
5696
+ /* @__PURE__ */ jsx23(
5647
5697
  OverlayRenderer,
5648
5698
  {
5649
5699
  uiState: ui.state,
@@ -5685,48 +5735,58 @@ function Dashboard({ config: config2, options, activeProfile }) {
5685
5735
  onPushEntry: pushEntry
5686
5736
  }
5687
5737
  ),
5688
- !ui.state.helpVisible && ui.state.mode !== "overlay:status" && ui.state.mode !== "overlay:create" && ui.state.mode !== "overlay:createNl" && ui.state.mode !== "overlay:bulkAction" && ui.state.mode !== "overlay:confirmPick" && ui.state.mode !== "focus" ? /* @__PURE__ */ jsxs22(Box21, { height: viewportHeight, children: [
5689
- /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", flexGrow: 1, children: [
5690
- hasMoreAbove ? /* @__PURE__ */ jsxs22(Text21, { color: "gray", dimColor: true, children: [
5691
- " ",
5692
- "\u25B2",
5693
- " ",
5694
- aboveCount,
5695
- " more above"
5696
- ] }) : null,
5697
- visibleRows.map((row) => /* @__PURE__ */ jsx22(
5698
- RowRenderer,
5699
- {
5700
- row,
5701
- selectedId: nav.selectedId,
5702
- selfLogin: config2.board.assignee,
5703
- isMultiSelected: ui.state.mode === "multiSelect" && row.navId ? multiSelect.isSelected(row.navId) : void 0
5704
- },
5705
- row.key
5706
- )),
5707
- hasMoreBelow ? /* @__PURE__ */ jsxs22(Text21, { color: "gray", dimColor: true, children: [
5708
- " ",
5709
- "\u25BC",
5710
- " ",
5711
- belowCount,
5712
- " more below"
5713
- ] }) : null
5714
- ] }),
5715
- showDetailPanel ? /* @__PURE__ */ jsx22(Box21, { marginLeft: 1, width: detailPanelWidth, children: /* @__PURE__ */ jsx22(
5716
- DetailPanel,
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,
5717
5741
  {
5718
- issue: selectedItem.issue,
5719
- task: selectedItem.task,
5720
- width: detailPanelWidth,
5721
- issueRepo: selectedItem.repoName,
5722
- fetchComments: handleFetchComments,
5723
- commentsState: currentCommentsState
5742
+ tabs: statusTabs,
5743
+ activeTabId: effectiveStatusId,
5744
+ totalWidth: termSize.cols
5724
5745
  }
5725
- ) }) : null
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
+ ] })
5726
5786
  ] }) : null,
5727
- /* @__PURE__ */ jsx22(ToastContainer, { toasts }),
5728
- logVisible ? /* @__PURE__ */ jsx22(ActionLog, { entries: logEntries }) : null,
5729
- /* @__PURE__ */ jsx22(
5787
+ /* @__PURE__ */ jsx23(ToastContainer, { toasts }),
5788
+ logVisible ? /* @__PURE__ */ jsx23(ActionLog, { entries: logEntries }) : null,
5789
+ /* @__PURE__ */ jsx23(
5730
5790
  HintBar,
5731
5791
  {
5732
5792
  uiMode: ui.state.mode,
@@ -5738,7 +5798,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5738
5798
  )
5739
5799
  ] });
5740
5800
  }
5741
- var PRIORITY_RANK, CHROME_ROWS;
5801
+ var PRIORITY_RANK, CHROME_ROWS_REPO, CHROME_ROWS_OTHER;
5742
5802
  var init_dashboard = __esm({
5743
5803
  "src/board/components/dashboard.tsx"() {
5744
5804
  "use strict";
@@ -5758,6 +5818,7 @@ var init_dashboard = __esm({
5758
5818
  init_hint_bar();
5759
5819
  init_overlay_renderer();
5760
5820
  init_row_renderer();
5821
+ init_status_tab_bar();
5761
5822
  init_tab_bar();
5762
5823
  init_toast_container();
5763
5824
  PRIORITY_RANK = {
@@ -5766,7 +5827,8 @@ var init_dashboard = __esm({
5766
5827
  "priority:medium": 2,
5767
5828
  "priority:low": 3
5768
5829
  };
5769
- CHROME_ROWS = 5;
5830
+ CHROME_ROWS_REPO = 6;
5831
+ CHROME_ROWS_OTHER = 5;
5770
5832
  }
5771
5833
  });
5772
5834
 
@@ -5776,10 +5838,10 @@ __export(live_exports, {
5776
5838
  runLiveDashboard: () => runLiveDashboard
5777
5839
  });
5778
5840
  import { render } from "ink";
5779
- import { jsx as jsx23 } from "react/jsx-runtime";
5841
+ import { jsx as jsx24 } from "react/jsx-runtime";
5780
5842
  async function runLiveDashboard(config2, options, activeProfile) {
5781
5843
  const instance = render(
5782
- /* @__PURE__ */ jsx23(Dashboard, { config: config2, options, activeProfile: activeProfile ?? null })
5844
+ /* @__PURE__ */ jsx24(Dashboard, { config: config2, options, activeProfile: activeProfile ?? null })
5783
5845
  );
5784
5846
  setInkInstance(instance);
5785
5847
  await instance.waitUntilExit();
@@ -6942,7 +7004,7 @@ function resolveProjectId(projectId) {
6942
7004
  process.exit(1);
6943
7005
  }
6944
7006
  var program = new Command();
6945
- program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.9.2").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
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) => {
6946
7008
  const opts = thisCommand.opts();
6947
7009
  if (opts.json) setFormat("json");
6948
7010
  if (opts.human) setFormat("human");