clairo 1.0.9 → 1.1.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.
Files changed (2) hide show
  1. package/dist/cli.js +273 -271
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -4,11 +4,11 @@
4
4
  import meow from "meow";
5
5
 
6
6
  // src/app.tsx
7
- import { useCallback as useCallback10, useMemo as useMemo2, useState as useState17 } from "react";
7
+ import { useCallback as useCallback9, useMemo as useMemo2, useState as useState16 } from "react";
8
8
  import { Box as Box16, useApp, useInput as useInput13 } from "ink";
9
9
 
10
10
  // src/components/github/GitHubView.tsx
11
- import { useCallback as useCallback9, useEffect as useEffect9, useRef as useRef5, useState as useState12 } from "react";
11
+ import { useCallback as useCallback8, useEffect as useEffect8, useRef as useRef5, useState as useState11 } from "react";
12
12
  import { TitledBox as TitledBox3 } from "@mishieck/ink-titled-box";
13
13
  import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
14
14
 
@@ -173,6 +173,37 @@ function findRemoteWithBranch(branch) {
173
173
  import { exec } from "child_process";
174
174
  import { promisify } from "util";
175
175
  var execAsync = promisify(exec);
176
+ function resolveCheckStatus(check) {
177
+ const conclusion = check.conclusion ?? check.state;
178
+ if (conclusion === "SUCCESS" || check.status === "COMPLETED") return "success";
179
+ if (conclusion === "FAILURE" || conclusion === "ERROR") return "failure";
180
+ if (conclusion === "SKIPPED" || conclusion === "NEUTRAL") return "skipped";
181
+ if (conclusion === "PENDING" || check.status === "IN_PROGRESS" || check.status === "QUEUED" || check.status === "WAITING")
182
+ return "pending";
183
+ return "pending";
184
+ }
185
+ var CHECK_COLORS = {
186
+ success: "green",
187
+ failure: "red",
188
+ pending: "yellow",
189
+ skipped: "gray"
190
+ };
191
+ var CHECK_ICONS = { success: "\u2713", failure: "\u2717", pending: "\u25CF", skipped: "\u25CB" };
192
+ var CHECK_SORT_ORDER = { failure: 0, pending: 1, skipped: 2, success: 3 };
193
+ function resolveReviewDisplay(reviewDecision) {
194
+ const status = reviewDecision ?? "PENDING";
195
+ if (status === "APPROVED") return { text: status, color: "green" };
196
+ if (status === "CHANGES_REQUESTED") return { text: status, color: "red" };
197
+ return { text: status, color: "yellow" };
198
+ }
199
+ function resolveMergeDisplay(pr) {
200
+ if (!pr) return { text: "UNKNOWN", color: "yellow" };
201
+ if (pr.state === "MERGED") return { text: "MERGED", color: "magenta" };
202
+ if (pr.state === "CLOSED") return { text: "CLOSED", color: "red" };
203
+ if (pr.mergeable === "MERGEABLE") return { text: "MERGEABLE", color: "green" };
204
+ if (pr.mergeable === "CONFLICTING") return { text: "CONFLICTING", color: "red" };
205
+ return { text: pr.mergeable ?? "UNKNOWN", color: "yellow" };
206
+ }
176
207
  async function isGhInstalled() {
177
208
  try {
178
209
  await execAsync("gh --version");
@@ -393,6 +424,9 @@ function useGitRepo() {
393
424
  };
394
425
  }
395
426
 
427
+ // src/hooks/github/usePullRequests.ts
428
+ import { useCallback as useCallback3, useState as useState4 } from "react";
429
+
396
430
  // src/hooks/github/usePRPolling.ts
397
431
  import { useCallback as useCallback2, useEffect as useEffect3, useRef, useState as useState3 } from "react";
398
432
  function usePRPolling() {
@@ -455,11 +489,11 @@ function usePRPolling() {
455
489
  }
456
490
 
457
491
  // src/hooks/github/usePullRequests.ts
458
- import { useCallback as useCallback3, useState as useState4 } from "react";
459
492
  function usePullRequests() {
460
493
  const [prs, setPrs] = useState4([]);
461
494
  const [selectedPR, setSelectedPR] = useState4(null);
462
495
  const [prDetails, setPrDetails] = useState4(null);
496
+ const polling = usePRPolling();
463
497
  const [loading, setLoading] = useState4({
464
498
  prs: false,
465
499
  details: false
@@ -527,6 +561,26 @@ function usePullRequests() {
527
561
  const setError = useCallback3((key, message) => {
528
562
  setErrors((prev) => ({ ...prev, [key]: message }));
529
563
  }, []);
564
+ const pollForNewPR = useCallback3(
565
+ (options) => {
566
+ const existingPRNumbers = prs.map((pr) => pr.number);
567
+ polling.startPolling({
568
+ branch: options.branch,
569
+ repoSlug: options.repoSlug,
570
+ existingPRNumbers,
571
+ onPRsUpdated: (updatedPrs) => {
572
+ setPrs(updatedPrs);
573
+ },
574
+ onNewPR: (newPR) => {
575
+ var _a;
576
+ setSelectedPR(newPR);
577
+ refreshDetails(newPR, options.repoSlug);
578
+ (_a = options.onNewPR) == null ? void 0 : _a.call(options, newPR);
579
+ }
580
+ });
581
+ },
582
+ [prs, polling.startPolling, refreshDetails]
583
+ );
530
584
  return {
531
585
  prs,
532
586
  selectedPR,
@@ -538,9 +592,9 @@ function usePullRequests() {
538
592
  loading,
539
593
  errors,
540
594
  setError,
541
- // Expose setters for cases where external code needs to update state directly
542
- setPrs,
543
- setSelectedPR
595
+ pollForNewPR,
596
+ stopPolling: polling.stopPolling,
597
+ isPolling: polling.isPolling
544
598
  };
545
599
  }
546
600
 
@@ -1042,52 +1096,14 @@ function renderInlineToString(tokens) {
1042
1096
 
1043
1097
  // src/components/github/PRDetailsBox.tsx
1044
1098
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1045
- function getCheckColor(check) {
1046
- const conclusion = check.conclusion ?? check.state;
1047
- if (conclusion === "SUCCESS") return "green";
1048
- if (conclusion === "FAILURE" || conclusion === "ERROR") return "red";
1049
- if (conclusion === "SKIPPED" || conclusion === "NEUTRAL") return "gray";
1050
- if (conclusion === "PENDING" || check.status === "IN_PROGRESS" || check.status === "QUEUED" || check.status === "WAITING")
1051
- return "yellow";
1052
- if (check.status === "COMPLETED") return "green";
1053
- return void 0;
1054
- }
1055
- function getCheckIcon(check) {
1056
- const conclusion = check.conclusion ?? check.state;
1057
- if (conclusion === "SUCCESS") return "\u2713";
1058
- if (conclusion === "FAILURE" || conclusion === "ERROR") return "\u2717";
1059
- if (conclusion === "SKIPPED" || conclusion === "NEUTRAL") return "\u25CB";
1060
- if (conclusion === "PENDING" || check.status === "IN_PROGRESS" || check.status === "QUEUED" || check.status === "WAITING")
1061
- return "\u25CF";
1062
- if (check.status === "COMPLETED") return "\u2713";
1063
- return "?";
1064
- }
1065
- function getCheckSortOrder(check) {
1066
- const conclusion = check.conclusion ?? check.state;
1067
- if (conclusion === "FAILURE" || conclusion === "ERROR") return 0;
1068
- if (conclusion === "PENDING" || check.status === "IN_PROGRESS" || check.status === "QUEUED" || check.status === "WAITING")
1069
- return 1;
1070
- if (conclusion === "SKIPPED" || conclusion === "NEUTRAL") return 2;
1071
- if (conclusion === "SUCCESS" || check.status === "COMPLETED") return 3;
1072
- return 4;
1073
- }
1074
- function PRDetailsBox({ pr, loading, error, isFocused }) {
1099
+ function PRDetailsBox({ pr, loading, error, isActive }) {
1075
1100
  var _a, _b, _c, _d, _e, _f, _g;
1076
1101
  const scrollRef = useRef2(null);
1077
1102
  const title = "[3] PR Details";
1078
- const borderColor = isFocused ? "yellow" : void 0;
1103
+ const borderColor = isActive ? "yellow" : void 0;
1079
1104
  const displayTitle = pr ? `${title} - #${pr.number}` : title;
1080
- const reviewStatus = (pr == null ? void 0 : pr.reviewDecision) ?? "PENDING";
1081
- const reviewColor = reviewStatus === "APPROVED" ? "green" : reviewStatus === "CHANGES_REQUESTED" ? "red" : "yellow";
1082
- const getMergeDisplay = () => {
1083
- if (!pr) return { text: "UNKNOWN", color: "yellow" };
1084
- if (pr.state === "MERGED") return { text: "MERGED", color: "magenta" };
1085
- if (pr.state === "CLOSED") return { text: "CLOSED", color: "red" };
1086
- if (pr.mergeable === "MERGEABLE") return { text: "MERGEABLE", color: "green" };
1087
- if (pr.mergeable === "CONFLICTING") return { text: "CONFLICTING", color: "red" };
1088
- return { text: pr.mergeable ?? "UNKNOWN", color: "yellow" };
1089
- };
1090
- const mergeDisplay = getMergeDisplay();
1105
+ const reviewDisplay = resolveReviewDisplay((pr == null ? void 0 : pr.reviewDecision) ?? null);
1106
+ const mergeDisplay = resolveMergeDisplay(pr);
1091
1107
  useInput(
1092
1108
  (input, key) => {
1093
1109
  var _a2, _b2;
@@ -1102,7 +1118,7 @@ function PRDetailsBox({ pr, loading, error, isFocused }) {
1102
1118
  });
1103
1119
  }
1104
1120
  },
1105
- { isActive: isFocused }
1121
+ { isActive }
1106
1122
  );
1107
1123
  const { stdout } = useStdout();
1108
1124
  const terminalWidth = (stdout == null ? void 0 : stdout.columns) ?? 80;
@@ -1137,7 +1153,7 @@ function PRDetailsBox({ pr, loading, error, isFocused }) {
1137
1153
  ] }),
1138
1154
  /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
1139
1155
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Review: " }),
1140
- /* @__PURE__ */ jsx2(Text2, { color: reviewColor, children: reviewStatus }),
1156
+ /* @__PURE__ */ jsx2(Text2, { color: reviewDisplay.color, children: reviewDisplay.text }),
1141
1157
  /* @__PURE__ */ jsx2(Text2, { children: " | " }),
1142
1158
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Status: " }),
1143
1159
  /* @__PURE__ */ jsx2(Text2, { color: mergeDisplay.color, children: mergeDisplay.text })
@@ -1174,12 +1190,13 @@ function PRDetailsBox({ pr, loading, error, isFocused }) {
1174
1190
  }
1175
1191
  return acc;
1176
1192
  }, /* @__PURE__ */ new Map()).values()) ?? []
1177
- ).sort((a, b) => getCheckSortOrder(a) - getCheckSortOrder(b)).map((check, idx) => {
1193
+ ).sort((a, b) => CHECK_SORT_ORDER[resolveCheckStatus(a)] - CHECK_SORT_ORDER[resolveCheckStatus(b)]).map((check, idx) => {
1178
1194
  const jobName = check.name ?? check.context;
1179
1195
  const displayName = check.workflowName ? `${check.workflowName} / ${jobName}` : jobName;
1180
- return /* @__PURE__ */ jsxs2(Text2, { color: getCheckColor(check), children: [
1196
+ const status = resolveCheckStatus(check);
1197
+ return /* @__PURE__ */ jsxs2(Text2, { color: CHECK_COLORS[status], children: [
1181
1198
  " ",
1182
- getCheckIcon(check),
1199
+ CHECK_ICONS[status],
1183
1200
  " ",
1184
1201
  displayName
1185
1202
  ] }, idx);
@@ -1198,9 +1215,9 @@ function PRDetailsBox({ pr, loading, error, isFocused }) {
1198
1215
 
1199
1216
  // src/components/github/PullRequestsBox.tsx
1200
1217
  import open2 from "open";
1201
- import { useEffect as useEffect7, useState as useState10 } from "react";
1218
+ import { useState as useState10 } from "react";
1202
1219
  import { TitledBox } from "@mishieck/ink-titled-box";
1203
- import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
1220
+ import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
1204
1221
  import { ScrollView as ScrollView2 } from "ink-scroll-view";
1205
1222
 
1206
1223
  // src/hooks/jira/useJiraTickets.ts
@@ -1458,25 +1475,8 @@ function useModal() {
1458
1475
  }
1459
1476
 
1460
1477
  // src/hooks/useListNavigation.ts
1461
- import { useCallback as useCallback7, useState as useState8 } from "react";
1462
- function useListNavigation(length) {
1463
- const [index, setIndex] = useState8(0);
1464
- const prev = useCallback7(() => {
1465
- setIndex((i) => Math.max(0, i - 1));
1466
- }, []);
1467
- const next = useCallback7(() => {
1468
- setIndex((i) => Math.min(length - 1, i + 1));
1469
- }, [length]);
1470
- const clampedIndex = Math.min(index, Math.max(0, length - 1));
1471
- const reset = useCallback7(() => setIndex(0), []);
1472
- return {
1473
- index: length === 0 ? 0 : clampedIndex,
1474
- prev,
1475
- next,
1476
- reset,
1477
- setIndex
1478
- };
1479
- }
1478
+ import { useEffect as useEffect6, useState as useState8 } from "react";
1479
+ import { useInput as useInput2 } from "ink";
1480
1480
 
1481
1481
  // src/hooks/useScrollToIndex.ts
1482
1482
  import { useEffect as useEffect5, useRef as useRef4 } from "react";
@@ -1498,8 +1498,53 @@ function useScrollToIndex(index) {
1498
1498
  return scrollRef;
1499
1499
  }
1500
1500
 
1501
+ // src/hooks/useListNavigation.ts
1502
+ function useListNavigation({
1503
+ items,
1504
+ totalItems,
1505
+ selectedIndex,
1506
+ onSelect,
1507
+ isActive
1508
+ }) {
1509
+ const navLength = totalItems ?? items.length;
1510
+ const [highlightedIndex, setHighlightedIndex] = useState8(0);
1511
+ const scrollRef = useScrollToIndex(highlightedIndex);
1512
+ useEffect6(() => {
1513
+ if (selectedIndex !== void 0 && selectedIndex >= 0) {
1514
+ setHighlightedIndex(selectedIndex);
1515
+ }
1516
+ }, [selectedIndex]);
1517
+ const prev = () => setHighlightedIndex((i) => Math.max(0, i - 1));
1518
+ const next = () => setHighlightedIndex((i) => Math.min(navLength - 1, i + 1));
1519
+ useInput2(
1520
+ (input, key) => {
1521
+ if (navLength === 0) return;
1522
+ if (key.upArrow || input === "k") {
1523
+ prev();
1524
+ }
1525
+ if (key.downArrow || input === "j") {
1526
+ next();
1527
+ }
1528
+ if (input === " " && onSelect) {
1529
+ onSelect(highlightedIndex);
1530
+ }
1531
+ },
1532
+ { isActive: isActive === true }
1533
+ );
1534
+ const clampedIndex = navLength === 0 ? 0 : Math.min(highlightedIndex, Math.max(0, navLength - 1));
1535
+ return {
1536
+ highlightedIndex: clampedIndex,
1537
+ index: clampedIndex,
1538
+ scrollRef,
1539
+ prev,
1540
+ next,
1541
+ reset: () => setHighlightedIndex(0),
1542
+ setIndex: setHighlightedIndex
1543
+ };
1544
+ }
1545
+
1501
1546
  // src/hooks/useRubberDuck.ts
1502
- import { useCallback as useCallback8, useEffect as useEffect6, useState as useState9 } from "react";
1547
+ import { useCallback as useCallback7, useEffect as useEffect7, useState as useState9 } from "react";
1503
1548
  var DUCK_MESSAGES = [
1504
1549
  "Quack.",
1505
1550
  "Quack quack quack.",
@@ -1537,18 +1582,18 @@ function useRubberDuck() {
1537
1582
  visible: false,
1538
1583
  message: DUCK_MESSAGES[0]
1539
1584
  });
1540
- const getRandomMessage = useCallback8(() => {
1585
+ const getRandomMessage = useCallback7(() => {
1541
1586
  const index = Math.floor(Math.random() * DUCK_MESSAGES.length);
1542
1587
  return DUCK_MESSAGES[index];
1543
1588
  }, []);
1544
- const toggleDuck = useCallback8(() => {
1589
+ const toggleDuck = useCallback7(() => {
1545
1590
  setState((prev) => ({
1546
1591
  ...prev,
1547
1592
  visible: !prev.visible,
1548
1593
  message: !prev.visible ? getRandomMessage() : prev.message
1549
1594
  }));
1550
1595
  }, [getRandomMessage]);
1551
- const quack = useCallback8(() => {
1596
+ const quack = useCallback7(() => {
1552
1597
  if (state.visible) {
1553
1598
  setState((prev) => ({
1554
1599
  ...prev,
@@ -1556,11 +1601,11 @@ function useRubberDuck() {
1556
1601
  }));
1557
1602
  }
1558
1603
  }, [state.visible, getRandomMessage]);
1559
- const getReactionMessage = useCallback8((event) => {
1604
+ const getReactionMessage = useCallback7((event) => {
1560
1605
  const messages = REACTION_MESSAGES[event];
1561
1606
  return messages[Math.floor(Math.random() * messages.length)];
1562
1607
  }, []);
1563
- useEffect6(() => {
1608
+ useEffect7(() => {
1564
1609
  const unsubscribe = duckEvents.subscribe((event) => {
1565
1610
  setState((prev) => ({
1566
1611
  ...prev,
@@ -1610,32 +1655,25 @@ function PullRequestsBox({
1610
1655
  error,
1611
1656
  branch,
1612
1657
  repoSlug,
1613
- isFocused
1658
+ isActive
1614
1659
  }) {
1615
- const [highlightedIndex, setHighlightedIndex] = useState10(0);
1616
1660
  const [copied, setCopied] = useState10(false);
1617
- const scrollRef = useScrollToIndex(highlightedIndex);
1618
- const totalItems = prs.length + 1;
1619
- useEffect7(() => {
1620
- const idx = prs.findIndex((p) => p.number === (selectedPR == null ? void 0 : selectedPR.number));
1621
- if (idx >= 0) setHighlightedIndex(idx);
1622
- }, [selectedPR, prs]);
1623
- useInput2(
1624
- (input, key) => {
1625
- if (!isFocused) return;
1626
- if (key.upArrow || input === "k") {
1627
- setHighlightedIndex((prev) => Math.max(0, prev - 1));
1628
- }
1629
- if (key.downArrow || input === "j") {
1630
- setHighlightedIndex((prev) => Math.min(totalItems - 1, prev + 1));
1631
- }
1632
- if (input === " ") {
1633
- if (highlightedIndex === prs.length) {
1634
- onCreatePR();
1635
- } else if (prs[highlightedIndex]) {
1636
- onSelect(prs[highlightedIndex]);
1637
- }
1661
+ const selectedIndex = prs.findIndex((p) => p.number === (selectedPR == null ? void 0 : selectedPR.number));
1662
+ const { highlightedIndex, scrollRef } = useListNavigation({
1663
+ items: prs,
1664
+ totalItems: prs.length + 1,
1665
+ selectedIndex: selectedIndex >= 0 ? selectedIndex : void 0,
1666
+ onSelect: (index) => {
1667
+ if (index === prs.length) {
1668
+ onCreatePR();
1669
+ } else if (prs[index]) {
1670
+ onSelect(prs[index]);
1638
1671
  }
1672
+ },
1673
+ isActive
1674
+ });
1675
+ useInput3(
1676
+ (input) => {
1639
1677
  if (input === "y" && repoSlug && prs[highlightedIndex]) {
1640
1678
  const pr = prs[highlightedIndex];
1641
1679
  const url = `https://github.com/${repoSlug}/pull/${pr.number}`;
@@ -1650,12 +1688,12 @@ function PullRequestsBox({
1650
1688
  });
1651
1689
  }
1652
1690
  },
1653
- { isActive: isFocused }
1691
+ { isActive }
1654
1692
  );
1655
1693
  const title = "[2] Pull Requests";
1656
1694
  const subtitle = branch ? ` (${branch})` : "";
1657
1695
  const copiedIndicator = copied ? " [Copied!]" : "";
1658
- const borderColor = isFocused ? "yellow" : void 0;
1696
+ const borderColor = isActive ? "yellow" : void 0;
1659
1697
  return /* @__PURE__ */ jsx3(
1660
1698
  TitledBox,
1661
1699
  {
@@ -1669,7 +1707,7 @@ function PullRequestsBox({
1669
1707
  !loading && !error && /* @__PURE__ */ jsxs3(ScrollView2, { ref: scrollRef, children: [
1670
1708
  prs.length === 0 && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "No PRs for this branch" }, "empty"),
1671
1709
  prs.map((pr, idx) => {
1672
- const isHighlighted = isFocused && idx === highlightedIndex;
1710
+ const isHighlighted = isActive && idx === highlightedIndex;
1673
1711
  const isSelected = pr.number === (selectedPR == null ? void 0 : selectedPR.number);
1674
1712
  const cursor = isHighlighted ? ">" : " ";
1675
1713
  const indicator = isSelected ? " *" : "";
@@ -1689,7 +1727,7 @@ function PullRequestsBox({
1689
1727
  ] }, pr.number);
1690
1728
  }),
1691
1729
  /* @__PURE__ */ jsxs3(Text3, { color: "blue", children: [
1692
- isFocused && highlightedIndex === prs.length ? "> " : " ",
1730
+ isActive && highlightedIndex === prs.length ? "> " : " ",
1693
1731
  "+ Create new PR"
1694
1732
  ] }, "create")
1695
1733
  ] })
@@ -1699,41 +1737,26 @@ function PullRequestsBox({
1699
1737
  }
1700
1738
 
1701
1739
  // src/components/github/RemotesBox.tsx
1702
- import { useEffect as useEffect8, useState as useState11 } from "react";
1703
1740
  import { TitledBox as TitledBox2 } from "@mishieck/ink-titled-box";
1704
- import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
1741
+ import { Box as Box4, Text as Text4 } from "ink";
1705
1742
  import { ScrollView as ScrollView3 } from "ink-scroll-view";
1706
1743
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1707
- function RemotesBox({ remotes, selectedRemote, onSelect, loading, error, isFocused }) {
1708
- const [highlightedIndex, setHighlightedIndex] = useState11(0);
1709
- const scrollRef = useScrollToIndex(highlightedIndex);
1710
- useEffect8(() => {
1711
- const idx = remotes.findIndex((r) => r.name === selectedRemote);
1712
- if (idx >= 0) setHighlightedIndex(idx);
1713
- }, [selectedRemote, remotes]);
1714
- useInput3(
1715
- (input, key) => {
1716
- if (!isFocused || remotes.length === 0) return;
1717
- if (key.upArrow || input === "k") {
1718
- setHighlightedIndex((prev) => Math.max(0, prev - 1));
1719
- }
1720
- if (key.downArrow || input === "j") {
1721
- setHighlightedIndex((prev) => Math.min(remotes.length - 1, prev + 1));
1722
- }
1723
- if (input === " ") {
1724
- onSelect(remotes[highlightedIndex].name);
1725
- }
1726
- },
1727
- { isActive: isFocused }
1728
- );
1744
+ function RemotesBox({ remotes, selectedRemote, onSelect, loading, error, isActive }) {
1745
+ const selectedIndex = remotes.findIndex((r) => r.name === selectedRemote);
1746
+ const { highlightedIndex, scrollRef } = useListNavigation({
1747
+ items: remotes,
1748
+ selectedIndex: selectedIndex >= 0 ? selectedIndex : void 0,
1749
+ onSelect: (index) => onSelect(remotes[index].name),
1750
+ isActive
1751
+ });
1729
1752
  const title = "[1] Remotes";
1730
- const borderColor = isFocused ? "yellow" : void 0;
1753
+ const borderColor = isActive ? "yellow" : void 0;
1731
1754
  return /* @__PURE__ */ jsx4(TitledBox2, { borderStyle: "round", titles: [title], borderColor, height: 5, children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingX: 1, flexGrow: 1, overflow: "hidden", children: [
1732
1755
  loading && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Loading..." }),
1733
1756
  error && /* @__PURE__ */ jsx4(Text4, { color: "red", children: error }),
1734
1757
  !loading && !error && remotes.length === 0 && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No remotes configured" }),
1735
1758
  !loading && !error && remotes.length > 0 && /* @__PURE__ */ jsx4(ScrollView3, { ref: scrollRef, children: remotes.map((remote, idx) => {
1736
- const isHighlighted = isFocused && idx === highlightedIndex;
1759
+ const isHighlighted = isActive && idx === highlightedIndex;
1737
1760
  const isSelected = remote.name === selectedRemote;
1738
1761
  const cursor = isHighlighted ? ">" : " ";
1739
1762
  const indicator = isSelected ? " *" : "";
@@ -1756,89 +1779,70 @@ function RemotesBox({ remotes, selectedRemote, onSelect, loading, error, isFocus
1756
1779
 
1757
1780
  // src/components/github/GitHubView.tsx
1758
1781
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1759
- function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
1782
+ function GitHubView({ isActive, onFocusedBoxChange, onLogUpdated }) {
1760
1783
  const repo = useGitRepo();
1761
1784
  const pullRequests = usePullRequests();
1762
- const polling = usePRPolling();
1763
- const [focusedBox, setFocusedBox] = useState12("remotes");
1764
- const lastFetchedRef = useRef5(null);
1765
- useEffect9(() => {
1785
+ const [focusedBox, setFocusedBox] = useState11("remotes");
1786
+ useEffect8(() => {
1766
1787
  if (repo.loading || !repo.currentBranch || !repo.currentRepoSlug) return;
1767
- const current = { branch: repo.currentBranch, repoSlug: repo.currentRepoSlug };
1768
- const last = lastFetchedRef.current;
1769
- if (last && last.branch === current.branch && last.repoSlug === current.repoSlug) return;
1770
- lastFetchedRef.current = current;
1771
1788
  pullRequests.fetchPRsAndDetails(repo.currentBranch, repo.currentRepoSlug);
1772
1789
  }, [repo.loading, repo.currentBranch, repo.currentRepoSlug, pullRequests.fetchPRsAndDetails]);
1773
- useEffect9(() => {
1774
- if (isFocused) {
1790
+ useEffect8(() => {
1791
+ if (isActive) {
1775
1792
  repo.refreshBranch();
1776
1793
  }
1777
- }, [isFocused, repo.refreshBranch]);
1778
- useEffect9(() => {
1794
+ }, [isActive, repo.refreshBranch]);
1795
+ useEffect8(() => {
1779
1796
  onFocusedBoxChange == null ? void 0 : onFocusedBoxChange(focusedBox);
1780
1797
  }, [focusedBox, onFocusedBoxChange]);
1781
- const handleRemoteSelect = useCallback9(
1798
+ const handleRemoteSelect = useCallback8(
1782
1799
  (remoteName) => {
1783
1800
  repo.selectRemote(remoteName);
1784
- const remote = repo.remotes.find((r) => r.name === remoteName);
1785
- if (!remote || !repo.currentBranch) return;
1786
- const repoSlug = getRepoFromRemote(remote.url);
1787
- if (!repoSlug) return;
1788
- lastFetchedRef.current = { branch: repo.currentBranch, repoSlug };
1789
- pullRequests.fetchPRsAndDetails(repo.currentBranch, repoSlug);
1790
1801
  },
1791
- [repo.selectRemote, repo.remotes, repo.currentBranch, pullRequests.fetchPRsAndDetails]
1802
+ [repo.selectRemote]
1792
1803
  );
1793
- const handlePRSelect = useCallback9(
1804
+ const handlePRSelect = useCallback8(
1794
1805
  (pr) => {
1795
1806
  pullRequests.selectPR(pr, repo.currentRepoSlug);
1796
1807
  },
1797
1808
  [pullRequests.selectPR, repo.currentRepoSlug]
1798
1809
  );
1799
- const createPRContext = useRef5({ repo, pullRequests, onLogUpdated });
1800
- createPRContext.current = { repo, pullRequests, onLogUpdated };
1801
- const handleCreatePR = useCallback9(() => {
1802
- const { repo: repo2, pullRequests: pullRequests2 } = createPRContext.current;
1803
- if (!repo2.currentBranch) {
1804
- pullRequests2.setError("prs", "No branch detected");
1810
+ const onLogUpdatedRef = useRef5(onLogUpdated);
1811
+ onLogUpdatedRef.current = onLogUpdated;
1812
+ const handleCreatePR = useCallback8(() => {
1813
+ if (!repo.currentBranch) {
1814
+ pullRequests.setError("prs", "No branch detected");
1805
1815
  duckEvents.emit("error");
1806
1816
  return;
1807
1817
  }
1808
- const remoteResult = findRemoteWithBranch(repo2.currentBranch);
1818
+ const remoteResult = findRemoteWithBranch(repo.currentBranch);
1809
1819
  if (!remoteResult.success) {
1810
- pullRequests2.setError("prs", "Push your branch to a remote first");
1820
+ pullRequests.setError("prs", "Push your branch to a remote first");
1811
1821
  duckEvents.emit("error");
1812
1822
  return;
1813
1823
  }
1814
- openPRCreationPage(remoteResult.data.owner, repo2.currentBranch, (error) => {
1824
+ openPRCreationPage(remoteResult.data.owner, repo.currentBranch, (error) => {
1815
1825
  if (error) {
1816
- pullRequests2.setError("prs", `Failed to create PR: ${error.message}`);
1826
+ pullRequests.setError("prs", `Failed to create PR: ${error.message}`);
1817
1827
  duckEvents.emit("error");
1818
1828
  }
1819
1829
  });
1820
- if (!repo2.currentRepoSlug) return;
1821
- polling.startPolling({
1822
- branch: repo2.currentBranch,
1823
- repoSlug: repo2.currentRepoSlug,
1824
- existingPRNumbers: pullRequests2.prs.map((pr) => pr.number),
1825
- onPRsUpdated: (prs) => {
1826
- pullRequests2.setPrs(prs);
1827
- },
1830
+ if (!repo.currentRepoSlug) return;
1831
+ const repoPath = repo.repoPath;
1832
+ const branch = repo.currentBranch;
1833
+ const repoSlug = repo.currentRepoSlug;
1834
+ pullRequests.pollForNewPR({
1835
+ branch,
1836
+ repoSlug,
1828
1837
  onNewPR: (newPR) => {
1829
1838
  var _a;
1830
- const ctx = createPRContext.current;
1831
- const tickets = ctx.repo.repoPath && ctx.repo.currentBranch ? getLinkedTickets(ctx.repo.repoPath, ctx.repo.currentBranch).map((t) => t.key) : [];
1839
+ const tickets = repoPath && branch ? getLinkedTickets(repoPath, branch).map((t) => t.key) : [];
1832
1840
  logPRCreated(newPR.number, newPR.title, tickets);
1833
1841
  duckEvents.emit("pr:opened");
1834
- (_a = ctx.onLogUpdated) == null ? void 0 : _a.call(ctx);
1835
- ctx.pullRequests.setSelectedPR(newPR);
1836
- if (ctx.repo.currentRepoSlug) {
1837
- ctx.pullRequests.refreshDetails(newPR, ctx.repo.currentRepoSlug);
1838
- }
1842
+ (_a = onLogUpdatedRef.current) == null ? void 0 : _a.call(onLogUpdatedRef);
1839
1843
  }
1840
1844
  });
1841
- }, [polling.startPolling]);
1845
+ }, [repo.currentBranch, repo.currentRepoSlug, repo.repoPath, pullRequests.setError, pullRequests.pollForNewPR]);
1842
1846
  useInput4(
1843
1847
  (input) => {
1844
1848
  if (input === "1") setFocusedBox("remotes");
@@ -1857,7 +1861,7 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
1857
1861
  handleCreatePR();
1858
1862
  }
1859
1863
  },
1860
- { isActive: isFocused }
1864
+ { isActive }
1861
1865
  );
1862
1866
  if (repo.isRepo === false) {
1863
1867
  return /* @__PURE__ */ jsx5(TitledBox3, { borderStyle: "round", titles: ["Error"], flexGrow: 1, children: /* @__PURE__ */ jsx5(Text5, { color: "red", children: "Current directory is not a git repository" }) });
@@ -1871,7 +1875,7 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
1871
1875
  onSelect: handleRemoteSelect,
1872
1876
  loading: repo.loading,
1873
1877
  error: repo.error,
1874
- isFocused: isFocused && focusedBox === "remotes"
1878
+ isActive: isActive && focusedBox === "remotes"
1875
1879
  }
1876
1880
  ),
1877
1881
  /* @__PURE__ */ jsx5(
@@ -1885,7 +1889,7 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
1885
1889
  error: pullRequests.errors.prs,
1886
1890
  branch: repo.currentBranch,
1887
1891
  repoSlug: repo.currentRepoSlug,
1888
- isFocused: isFocused && focusedBox === "prs"
1892
+ isActive: isActive && focusedBox === "prs"
1889
1893
  }
1890
1894
  ),
1891
1895
  /* @__PURE__ */ jsx5(
@@ -1894,7 +1898,7 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
1894
1898
  pr: pullRequests.prDetails,
1895
1899
  loading: pullRequests.loading.details,
1896
1900
  error: pullRequests.errors.details,
1897
- isFocused: isFocused && focusedBox === "details"
1901
+ isActive: isActive && focusedBox === "details"
1898
1902
  }
1899
1903
  )
1900
1904
  ] });
@@ -1902,10 +1906,10 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
1902
1906
 
1903
1907
  // src/components/jira/JiraView.tsx
1904
1908
  import open3 from "open";
1905
- import { useEffect as useEffect11, useRef as useRef6 } from "react";
1909
+ import { useEffect as useEffect10, useRef as useRef6 } from "react";
1906
1910
 
1907
1911
  // src/components/jira/LinkTicketModal.tsx
1908
- import { useState as useState13 } from "react";
1912
+ import { useState as useState12 } from "react";
1909
1913
  import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
1910
1914
 
1911
1915
  // src/components/ui/TextInput.tsx
@@ -1940,7 +1944,7 @@ function TextInput({ value, onChange, placeholder, isActive, mask }) {
1940
1944
  // src/components/jira/LinkTicketModal.tsx
1941
1945
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1942
1946
  function LinkTicketModal({ onSubmit, onCancel, loading, error }) {
1943
- const [ticketInput, setTicketInput] = useState13("");
1947
+ const [ticketInput, setTicketInput] = useState12("");
1944
1948
  const canSubmit = ticketInput.trim().length > 0;
1945
1949
  useInput6(
1946
1950
  (_input, key) => {
@@ -1974,16 +1978,16 @@ import { TitledBox as TitledBox4 } from "@mishieck/ink-titled-box";
1974
1978
  import { Box as Box11, Text as Text11, useInput as useInput9 } from "ink";
1975
1979
 
1976
1980
  // src/components/jira/ChangeStatusModal.tsx
1977
- import { useEffect as useEffect10, useState as useState14 } from "react";
1981
+ import { useEffect as useEffect9, useState as useState13 } from "react";
1978
1982
  import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
1979
1983
  import SelectInput from "ink-select-input";
1980
1984
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1981
1985
  function ChangeStatusModal({ repoPath, ticketKey, currentStatus, onComplete, onCancel }) {
1982
- const [transitions, setTransitions] = useState14([]);
1983
- const [loading, setLoading] = useState14(true);
1984
- const [applying, setApplying] = useState14(false);
1985
- const [error, setError] = useState14(null);
1986
- useEffect10(() => {
1986
+ const [transitions, setTransitions] = useState13([]);
1987
+ const [loading, setLoading] = useState13(true);
1988
+ const [applying, setApplying] = useState13(false);
1989
+ const [error, setError] = useState13(null);
1990
+ useEffect9(() => {
1987
1991
  const fetchTransitions = async () => {
1988
1992
  const siteUrl = getJiraSiteUrl(repoPath);
1989
1993
  const creds = getJiraCredentials(repoPath);
@@ -2060,7 +2064,7 @@ function ChangeStatusModal({ repoPath, ticketKey, currentStatus, onComplete, onC
2060
2064
  }
2061
2065
 
2062
2066
  // src/components/jira/ConfigureJiraSiteModal.tsx
2063
- import { useState as useState15 } from "react";
2067
+ import { useState as useState14 } from "react";
2064
2068
  import { Box as Box9, Text as Text9, useInput as useInput8 } from "ink";
2065
2069
  import { ScrollView as ScrollView4 } from "ink-scroll-view";
2066
2070
 
@@ -2105,13 +2109,13 @@ function ConfigureJiraSiteModal({
2105
2109
  error
2106
2110
  }) {
2107
2111
  const hasExisting = existingConfigs.length > 0;
2108
- const [mode, setMode] = useState15(hasExisting ? "choose" : "manual");
2109
- const [selectedExisting, setSelectedExisting] = useState15(0);
2112
+ const [mode, setMode] = useState14(hasExisting ? "choose" : "manual");
2113
+ const [selectedExisting, setSelectedExisting] = useState14(0);
2110
2114
  const scrollRef = useScrollToIndex(selectedExisting);
2111
- const [siteUrl, setSiteUrl] = useState15(initialSiteUrl ?? "");
2112
- const [email, setEmail] = useState15(initialEmail ?? "");
2113
- const [apiToken, setApiToken] = useState15("");
2114
- const [selectedItem, setSelectedItem] = useState15("siteUrl");
2115
+ const [siteUrl, setSiteUrl] = useState14(initialSiteUrl ?? "");
2116
+ const [email, setEmail] = useState14(initialEmail ?? "");
2117
+ const [apiToken, setApiToken] = useState14("");
2118
+ const [selectedItem, setSelectedItem] = useState14("siteUrl");
2115
2119
  const items = ["siteUrl", "email", "apiToken", "submit"];
2116
2120
  const canSubmit = siteUrl.trim() && email.trim() && apiToken.trim();
2117
2121
  const chooseItems = existingConfigs.length + 1;
@@ -2212,7 +2216,7 @@ function ConfigureJiraSiteModal({
2212
2216
  config.siteUrl
2213
2217
  ] }),
2214
2218
  /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2215
- " ",
2219
+ " ",
2216
2220
  config.email
2217
2221
  ] })
2218
2222
  ] }, config.siteUrl + config.email);
@@ -2276,13 +2280,14 @@ function TicketItem({ ticketKey, summary, status, isHighlighted, isSelected }) {
2276
2280
 
2277
2281
  // src/components/jira/JiraView.tsx
2278
2282
  import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
2279
- function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated }) {
2283
+ function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated }) {
2280
2284
  const repo = useGitRepo();
2281
2285
  const jira = useJiraTickets();
2282
2286
  const modal = useModal();
2283
- const nav = useListNavigation(jira.tickets.length);
2287
+ const nav = useListNavigation({ items: jira.tickets });
2288
+ const currentTicket = jira.tickets[nav.index] ?? null;
2284
2289
  const lastInitRef = useRef6(null);
2285
- useEffect11(() => {
2290
+ useEffect10(() => {
2286
2291
  if (repo.loading || !repo.repoPath || !repo.currentBranch) return;
2287
2292
  const current = { branch: repo.currentBranch };
2288
2293
  const last = lastInitRef.current;
@@ -2290,17 +2295,17 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
2290
2295
  lastInitRef.current = current;
2291
2296
  jira.initializeJiraState(repo.repoPath, repo.currentBranch, repo.currentRepoSlug);
2292
2297
  }, [repo.loading, repo.repoPath, repo.currentBranch, repo.currentRepoSlug, jira.initializeJiraState]);
2293
- useEffect11(() => {
2294
- if (isFocused) {
2298
+ useEffect10(() => {
2299
+ if (isActive) {
2295
2300
  repo.refreshBranch();
2296
2301
  } else {
2297
2302
  modal.close();
2298
2303
  }
2299
- }, [isFocused, repo.refreshBranch, modal.close]);
2300
- useEffect11(() => {
2304
+ }, [isActive, repo.refreshBranch, modal.close]);
2305
+ useEffect10(() => {
2301
2306
  onModalChange == null ? void 0 : onModalChange(modal.isOpen);
2302
2307
  }, [modal.isOpen, onModalChange]);
2303
- useEffect11(() => {
2308
+ useEffect10(() => {
2304
2309
  onJiraStateChange == null ? void 0 : onJiraStateChange(jira.jiraState);
2305
2310
  }, [jira.jiraState, onJiraStateChange]);
2306
2311
  const handleConfigureSubmit = async (siteUrl, email, apiToken) => {
@@ -2313,38 +2318,30 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
2313
2318
  const success = await jira.linkTicket(repo.repoPath, repo.currentBranch, ticketInput);
2314
2319
  if (success) modal.close();
2315
2320
  };
2321
+ const getTicketUrl = () => {
2322
+ if (!repo.repoPath || !currentTicket) return null;
2323
+ const siteUrl = getJiraSiteUrl(repo.repoPath);
2324
+ return siteUrl ? `${siteUrl}/browse/${currentTicket.key}` : null;
2325
+ };
2316
2326
  const handleUnlinkTicket = () => {
2317
- if (!repo.repoPath || !repo.currentBranch || jira.tickets.length === 0) return;
2318
- const ticket = jira.tickets[nav.index];
2319
- if (ticket) {
2320
- jira.unlinkTicket(repo.repoPath, repo.currentBranch, ticket.key);
2321
- jira.refreshTickets(repo.repoPath, repo.currentBranch);
2322
- nav.prev();
2323
- }
2327
+ if (!repo.repoPath || !repo.currentBranch || !currentTicket) return;
2328
+ jira.unlinkTicket(repo.repoPath, repo.currentBranch, currentTicket.key);
2329
+ jira.refreshTickets(repo.repoPath, repo.currentBranch);
2330
+ nav.prev();
2324
2331
  };
2325
2332
  const handleOpenInBrowser = () => {
2326
- if (!repo.repoPath || jira.tickets.length === 0) return;
2327
- const ticket = jira.tickets[nav.index];
2328
- const siteUrl = getJiraSiteUrl(repo.repoPath);
2329
- if (ticket && siteUrl) {
2330
- open3(`${siteUrl}/browse/${ticket.key}`).catch(() => {
2331
- });
2332
- }
2333
+ const url = getTicketUrl();
2334
+ if (url) open3(url).catch(() => {
2335
+ });
2333
2336
  };
2334
2337
  const handleCopyLink = () => {
2335
- if (!repo.repoPath || jira.tickets.length === 0) return;
2336
- const ticket = jira.tickets[nav.index];
2337
- const siteUrl = getJiraSiteUrl(repo.repoPath);
2338
- if (ticket && siteUrl) {
2339
- copyToClipboard(`${siteUrl}/browse/${ticket.key}`);
2340
- }
2338
+ const url = getTicketUrl();
2339
+ if (url) copyToClipboard(url);
2341
2340
  };
2342
2341
  const handleStatusComplete = (newStatus) => {
2343
- if (!repo.repoPath || !repo.currentBranch) return;
2344
- const ticket = jira.tickets[nav.index];
2345
- if (!ticket) return;
2346
- updateTicketStatus(repo.repoPath, repo.currentBranch, ticket.key, newStatus);
2347
- logJiraStatusChanged(ticket.key, ticket.summary, ticket.status, newStatus);
2342
+ if (!repo.repoPath || !repo.currentBranch || !currentTicket) return;
2343
+ updateTicketStatus(repo.repoPath, repo.currentBranch, currentTicket.key, newStatus);
2344
+ logJiraStatusChanged(currentTicket.key, currentTicket.summary, currentTicket.status, newStatus);
2348
2345
  onLogUpdated == null ? void 0 : onLogUpdated();
2349
2346
  modal.close();
2350
2347
  jira.refreshTickets(repo.repoPath, repo.currentBranch);
@@ -2377,7 +2374,7 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
2377
2374
  if (input === "y") handleCopyLink();
2378
2375
  }
2379
2376
  },
2380
- { isActive: isFocused && !modal.isOpen }
2377
+ { isActive: isActive && !modal.isOpen }
2381
2378
  );
2382
2379
  if (repo.isRepo === false) {
2383
2380
  return /* @__PURE__ */ jsx11(TitledBox4, { borderStyle: "round", titles: ["Jira"], flexShrink: 0, children: /* @__PURE__ */ jsx11(Text11, { color: "red", children: "Not a git repository" }) });
@@ -2416,21 +2413,20 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
2416
2413
  }
2417
2414
  ) });
2418
2415
  }
2419
- if (modal.type === "status" && repo.repoPath && repo.currentBranch && jira.tickets[nav.index]) {
2420
- const ticket = jira.tickets[nav.index];
2416
+ if (modal.type === "status" && repo.repoPath && repo.currentBranch && currentTicket) {
2421
2417
  return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx11(
2422
2418
  ChangeStatusModal,
2423
2419
  {
2424
2420
  repoPath: repo.repoPath,
2425
- ticketKey: ticket.key,
2426
- currentStatus: ticket.status,
2421
+ ticketKey: currentTicket.key,
2422
+ currentStatus: currentTicket.status,
2427
2423
  onComplete: handleStatusComplete,
2428
2424
  onCancel: modal.close
2429
2425
  }
2430
2426
  ) });
2431
2427
  }
2432
2428
  const title = "[4] Jira";
2433
- const borderColor = isFocused ? "yellow" : void 0;
2429
+ const borderColor = isActive ? "yellow" : void 0;
2434
2430
  return /* @__PURE__ */ jsx11(TitledBox4, { borderStyle: "round", titles: [title], borderColor, flexShrink: 0, children: /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
2435
2431
  jira.jiraState === "not_configured" && /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "No Jira site configured" }),
2436
2432
  jira.jiraState === "no_tickets" && /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "No tickets linked to this branch" }),
@@ -2452,7 +2448,7 @@ import { useEffect as useEffect12 } from "react";
2452
2448
  import { Box as Box14, useInput as useInput12 } from "ink";
2453
2449
 
2454
2450
  // src/components/logs/LogViewerBox.tsx
2455
- import { useRef as useRef7, useState as useState16 } from "react";
2451
+ import { useEffect as useEffect11, useRef as useRef7, useState as useState15 } from "react";
2456
2452
  import { TitledBox as TitledBox5 } from "@mishieck/ink-titled-box";
2457
2453
  import { Box as Box12, Text as Text12, useInput as useInput10 } from "ink";
2458
2454
  import { ScrollView as ScrollView5 } from "ink-scroll-view";
@@ -2543,15 +2539,15 @@ Generate the standup notes:`;
2543
2539
 
2544
2540
  // src/components/logs/LogViewerBox.tsx
2545
2541
  import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2546
- function LogViewerBox({ date, content, isFocused, onRefresh, onLogCreated }) {
2542
+ function LogViewerBox({ date, content, isActive, onRefresh, onLogCreated }) {
2547
2543
  const scrollRef = useRef7(null);
2548
- const [isInputMode, setIsInputMode] = useState16(false);
2549
- const [inputValue, setInputValue] = useState16("");
2550
- const [isGeneratingStandup, setIsGeneratingStandup] = useState16(false);
2551
- const [standupResult, setStandupResult] = useState16(null);
2544
+ const [isInputMode, setIsInputMode] = useState15(false);
2545
+ const [inputValue, setInputValue] = useState15("");
2546
+ const [isGeneratingStandup, setIsGeneratingStandup] = useState15(false);
2547
+ const [standupResult, setStandupResult] = useState15(null);
2552
2548
  const claudeProcessRef = useRef7(null);
2553
2549
  const title = "[6] Log Content";
2554
- const borderColor = isFocused ? "yellow" : void 0;
2550
+ const borderColor = isActive ? "yellow" : void 0;
2555
2551
  const displayTitle = date ? `${title} - ${date}.md` : title;
2556
2552
  useInput10(
2557
2553
  (input, key) => {
@@ -2613,8 +2609,14 @@ function LogViewerBox({ date, content, isFocused, onRefresh, onLogCreated }) {
2613
2609
  });
2614
2610
  }
2615
2611
  },
2616
- { isActive: isFocused }
2612
+ { isActive }
2617
2613
  );
2614
+ useEffect11(() => {
2615
+ return () => {
2616
+ var _a;
2617
+ (_a = claudeProcessRef.current) == null ? void 0 : _a.cancel();
2618
+ };
2619
+ }, []);
2618
2620
  const handleInputSubmit = (value) => {
2619
2621
  if (!date || !value.trim()) {
2620
2622
  setIsInputMode(false);
@@ -2677,11 +2679,11 @@ function LogsHistoryBox({
2677
2679
  highlightedIndex,
2678
2680
  onHighlight,
2679
2681
  onSelect,
2680
- isFocused
2682
+ isActive
2681
2683
  }) {
2682
2684
  const scrollRef = useScrollToIndex(highlightedIndex);
2683
2685
  const title = "[5] Logs";
2684
- const borderColor = isFocused ? "yellow" : void 0;
2686
+ const borderColor = isActive ? "yellow" : void 0;
2685
2687
  useInput11(
2686
2688
  (input, key) => {
2687
2689
  if (logFiles.length === 0) return;
@@ -2691,14 +2693,14 @@ function LogsHistoryBox({
2691
2693
  if (key.downArrow || input === "j") {
2692
2694
  onHighlight(Math.min(logFiles.length - 1, highlightedIndex + 1));
2693
2695
  }
2694
- if (key.return) {
2696
+ if (input === " ") {
2695
2697
  const file = logFiles[highlightedIndex];
2696
2698
  if (file) {
2697
2699
  onSelect(file.date);
2698
2700
  }
2699
2701
  }
2700
2702
  },
2701
- { isActive: isFocused }
2703
+ { isActive }
2702
2704
  );
2703
2705
  return /* @__PURE__ */ jsx13(TitledBox6, { borderStyle: "round", titles: [title], borderColor, height: 5, children: /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, flexGrow: 1, overflow: "hidden", children: [
2704
2706
  logFiles.length === 0 && /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "No logs yet" }),
@@ -2722,7 +2724,7 @@ function LogsHistoryBox({
2722
2724
 
2723
2725
  // src/components/logs/LogsView.tsx
2724
2726
  import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2725
- function LogsView({ isFocused, refreshKey, focusedBox, onFocusedBoxChange }) {
2727
+ function LogsView({ isActive, refreshKey, focusedBox, onFocusedBoxChange }) {
2726
2728
  const logs = useLogs();
2727
2729
  useEffect12(() => {
2728
2730
  if (refreshKey !== void 0 && refreshKey > 0) {
@@ -2734,7 +2736,7 @@ function LogsView({ isFocused, refreshKey, focusedBox, onFocusedBoxChange }) {
2734
2736
  if (input === "5") onFocusedBoxChange("history");
2735
2737
  if (input === "6") onFocusedBoxChange("viewer");
2736
2738
  },
2737
- { isActive: isFocused }
2739
+ { isActive }
2738
2740
  );
2739
2741
  return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", flexGrow: 1, children: [
2740
2742
  /* @__PURE__ */ jsx14(
@@ -2745,7 +2747,7 @@ function LogsView({ isFocused, refreshKey, focusedBox, onFocusedBoxChange }) {
2745
2747
  highlightedIndex: logs.highlightedIndex,
2746
2748
  onHighlight: logs.setHighlightedIndex,
2747
2749
  onSelect: logs.selectDate,
2748
- isFocused: isFocused && focusedBox === "history"
2750
+ isActive: isActive && focusedBox === "history"
2749
2751
  }
2750
2752
  ),
2751
2753
  /* @__PURE__ */ jsx14(
@@ -2753,7 +2755,7 @@ function LogsView({ isFocused, refreshKey, focusedBox, onFocusedBoxChange }) {
2753
2755
  {
2754
2756
  date: logs.selectedDate,
2755
2757
  content: logs.logContent,
2756
- isFocused: isFocused && focusedBox === "viewer",
2758
+ isActive: isActive && focusedBox === "viewer",
2757
2759
  onRefresh: logs.refresh,
2758
2760
  onLogCreated: logs.handleLogCreated
2759
2761
  }
@@ -2820,7 +2822,7 @@ var JIRA_KEYBINDINGS = {
2820
2822
 
2821
2823
  // src/constants/logs.ts
2822
2824
  var LOGS_KEYBINDINGS = {
2823
- history: [{ key: "Enter", label: "Select" }],
2825
+ history: [{ key: "Space", label: "Select" }],
2824
2826
  viewer: [
2825
2827
  { key: "i", label: "Add Entry" },
2826
2828
  { key: "e", label: "Edit" },
@@ -2849,13 +2851,13 @@ function computeKeybindings(focusedView, state) {
2849
2851
  import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2850
2852
  function App() {
2851
2853
  const { exit } = useApp();
2852
- const [focusedView, setFocusedView] = useState17("github");
2853
- const [modalOpen, setModalOpen] = useState17(false);
2854
- const [logRefreshKey, setLogRefreshKey] = useState17(0);
2854
+ const [focusedView, setFocusedView] = useState16("github");
2855
+ const [modalOpen, setModalOpen] = useState16(false);
2856
+ const [logRefreshKey, setLogRefreshKey] = useState16(0);
2855
2857
  const duck = useRubberDuck();
2856
- const [githubFocusedBox, setGithubFocusedBox] = useState17("remotes");
2857
- const [jiraState, setJiraState] = useState17("not_configured");
2858
- const [logsFocusedBox, setLogsFocusedBox] = useState17("history");
2858
+ const [githubFocusedBox, setGithubFocusedBox] = useState16("remotes");
2859
+ const [jiraState, setJiraState] = useState16("not_configured");
2860
+ const [logsFocusedBox, setLogsFocusedBox] = useState16("history");
2859
2861
  const keybindings = useMemo2(
2860
2862
  () => computeKeybindings(focusedView, {
2861
2863
  github: { focusedBox: githubFocusedBox },
@@ -2864,7 +2866,7 @@ function App() {
2864
2866
  }),
2865
2867
  [focusedView, githubFocusedBox, jiraState, modalOpen, logsFocusedBox]
2866
2868
  );
2867
- const handleLogUpdated = useCallback10(() => {
2869
+ const handleLogUpdated = useCallback9(() => {
2868
2870
  setLogRefreshKey((prev) => prev + 1);
2869
2871
  }, []);
2870
2872
  useInput13(
@@ -2901,7 +2903,7 @@ function App() {
2901
2903
  /* @__PURE__ */ jsx16(
2902
2904
  GitHubView,
2903
2905
  {
2904
- isFocused: focusedView === "github",
2906
+ isActive: focusedView === "github",
2905
2907
  onFocusedBoxChange: setGithubFocusedBox,
2906
2908
  onLogUpdated: handleLogUpdated
2907
2909
  }
@@ -2909,7 +2911,7 @@ function App() {
2909
2911
  /* @__PURE__ */ jsx16(
2910
2912
  JiraView,
2911
2913
  {
2912
- isFocused: focusedView === "jira",
2914
+ isActive: focusedView === "jira",
2913
2915
  onModalChange: setModalOpen,
2914
2916
  onJiraStateChange: setJiraState,
2915
2917
  onLogUpdated: handleLogUpdated
@@ -2919,7 +2921,7 @@ function App() {
2919
2921
  /* @__PURE__ */ jsx16(Box16, { flexDirection: "column", flexGrow: 1, flexBasis: 0, children: /* @__PURE__ */ jsx16(
2920
2922
  LogsView,
2921
2923
  {
2922
- isFocused: focusedView === "logs",
2924
+ isActive: focusedView === "logs",
2923
2925
  refreshKey: logRefreshKey,
2924
2926
  focusedBox: logsFocusedBox,
2925
2927
  onFocusedBoxChange: setLogsFocusedBox
@@ -2941,13 +2943,13 @@ function App() {
2941
2943
  import { render as inkRender } from "ink";
2942
2944
 
2943
2945
  // src/lib/Screen.tsx
2944
- import { useCallback as useCallback11, useEffect as useEffect13, useState as useState18 } from "react";
2946
+ import { useCallback as useCallback10, useEffect as useEffect13, useState as useState17 } from "react";
2945
2947
  import { Box as Box17, useStdout as useStdout2 } from "ink";
2946
2948
  import { jsx as jsx17 } from "react/jsx-runtime";
2947
2949
  function Screen({ children }) {
2948
2950
  const { stdout } = useStdout2();
2949
- const getSize = useCallback11(() => ({ height: stdout.rows, width: stdout.columns }), [stdout]);
2950
- const [size, setSize] = useState18(getSize);
2951
+ const getSize = useCallback10(() => ({ height: stdout.rows, width: stdout.columns }), [stdout]);
2952
+ const [size, setSize] = useState17(getSize);
2951
2953
  useEffect13(() => {
2952
2954
  const onResize = () => setSize(getSize());
2953
2955
  stdout.on("resize", onResize);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clairo",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",