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.
- package/dist/cli.js +273 -271
- 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
|
|
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
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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
|
|
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 =
|
|
1103
|
+
const borderColor = isActive ? "yellow" : void 0;
|
|
1079
1104
|
const displayTitle = pr ? `${title} - #${pr.number}` : title;
|
|
1080
|
-
const
|
|
1081
|
-
const
|
|
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
|
|
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:
|
|
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) =>
|
|
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
|
-
|
|
1196
|
+
const status = resolveCheckStatus(check);
|
|
1197
|
+
return /* @__PURE__ */ jsxs2(Text2, { color: CHECK_COLORS[status], children: [
|
|
1181
1198
|
" ",
|
|
1182
|
-
|
|
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 {
|
|
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
|
|
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 {
|
|
1462
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1658
|
+
isActive
|
|
1614
1659
|
}) {
|
|
1615
|
-
const [highlightedIndex, setHighlightedIndex] = useState10(0);
|
|
1616
1660
|
const [copied, setCopied] = useState10(false);
|
|
1617
|
-
const
|
|
1618
|
-
const
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
if (
|
|
1626
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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,
|
|
1708
|
-
const
|
|
1709
|
-
const scrollRef =
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
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 =
|
|
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 =
|
|
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({
|
|
1782
|
+
function GitHubView({ isActive, onFocusedBoxChange, onLogUpdated }) {
|
|
1760
1783
|
const repo = useGitRepo();
|
|
1761
1784
|
const pullRequests = usePullRequests();
|
|
1762
|
-
const
|
|
1763
|
-
|
|
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
|
-
|
|
1774
|
-
if (
|
|
1790
|
+
useEffect8(() => {
|
|
1791
|
+
if (isActive) {
|
|
1775
1792
|
repo.refreshBranch();
|
|
1776
1793
|
}
|
|
1777
|
-
}, [
|
|
1778
|
-
|
|
1794
|
+
}, [isActive, repo.refreshBranch]);
|
|
1795
|
+
useEffect8(() => {
|
|
1779
1796
|
onFocusedBoxChange == null ? void 0 : onFocusedBoxChange(focusedBox);
|
|
1780
1797
|
}, [focusedBox, onFocusedBoxChange]);
|
|
1781
|
-
const handleRemoteSelect =
|
|
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
|
|
1802
|
+
[repo.selectRemote]
|
|
1792
1803
|
);
|
|
1793
|
-
const handlePRSelect =
|
|
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
|
|
1800
|
-
|
|
1801
|
-
const handleCreatePR =
|
|
1802
|
-
|
|
1803
|
-
|
|
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(
|
|
1818
|
+
const remoteResult = findRemoteWithBranch(repo.currentBranch);
|
|
1809
1819
|
if (!remoteResult.success) {
|
|
1810
|
-
|
|
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,
|
|
1824
|
+
openPRCreationPage(remoteResult.data.owner, repo.currentBranch, (error) => {
|
|
1815
1825
|
if (error) {
|
|
1816
|
-
|
|
1826
|
+
pullRequests.setError("prs", `Failed to create PR: ${error.message}`);
|
|
1817
1827
|
duckEvents.emit("error");
|
|
1818
1828
|
}
|
|
1819
1829
|
});
|
|
1820
|
-
if (!
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
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
|
|
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 =
|
|
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
|
-
}, [
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1909
|
+
import { useEffect as useEffect10, useRef as useRef6 } from "react";
|
|
1906
1910
|
|
|
1907
1911
|
// src/components/jira/LinkTicketModal.tsx
|
|
1908
|
-
import { useState as
|
|
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] =
|
|
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
|
|
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] =
|
|
1983
|
-
const [loading, setLoading] =
|
|
1984
|
-
const [applying, setApplying] =
|
|
1985
|
-
const [error, setError] =
|
|
1986
|
-
|
|
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
|
|
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] =
|
|
2109
|
-
const [selectedExisting, setSelectedExisting] =
|
|
2112
|
+
const [mode, setMode] = useState14(hasExisting ? "choose" : "manual");
|
|
2113
|
+
const [selectedExisting, setSelectedExisting] = useState14(0);
|
|
2110
2114
|
const scrollRef = useScrollToIndex(selectedExisting);
|
|
2111
|
-
const [siteUrl, setSiteUrl] =
|
|
2112
|
-
const [email, setEmail] =
|
|
2113
|
-
const [apiToken, setApiToken] =
|
|
2114
|
-
const [selectedItem, setSelectedItem] =
|
|
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({
|
|
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
|
|
2287
|
+
const nav = useListNavigation({ items: jira.tickets });
|
|
2288
|
+
const currentTicket = jira.tickets[nav.index] ?? null;
|
|
2284
2289
|
const lastInitRef = useRef6(null);
|
|
2285
|
-
|
|
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
|
-
|
|
2294
|
-
if (
|
|
2298
|
+
useEffect10(() => {
|
|
2299
|
+
if (isActive) {
|
|
2295
2300
|
repo.refreshBranch();
|
|
2296
2301
|
} else {
|
|
2297
2302
|
modal.close();
|
|
2298
2303
|
}
|
|
2299
|
-
}, [
|
|
2300
|
-
|
|
2304
|
+
}, [isActive, repo.refreshBranch, modal.close]);
|
|
2305
|
+
useEffect10(() => {
|
|
2301
2306
|
onModalChange == null ? void 0 : onModalChange(modal.isOpen);
|
|
2302
2307
|
}, [modal.isOpen, onModalChange]);
|
|
2303
|
-
|
|
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 ||
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
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
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
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
|
-
|
|
2336
|
-
|
|
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
|
-
|
|
2345
|
-
|
|
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:
|
|
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 &&
|
|
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:
|
|
2426
|
-
currentStatus:
|
|
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 =
|
|
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
|
|
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,
|
|
2542
|
+
function LogViewerBox({ date, content, isActive, onRefresh, onLogCreated }) {
|
|
2547
2543
|
const scrollRef = useRef7(null);
|
|
2548
|
-
const [isInputMode, setIsInputMode] =
|
|
2549
|
-
const [inputValue, setInputValue] =
|
|
2550
|
-
const [isGeneratingStandup, setIsGeneratingStandup] =
|
|
2551
|
-
const [standupResult, setStandupResult] =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
2682
|
+
isActive
|
|
2681
2683
|
}) {
|
|
2682
2684
|
const scrollRef = useScrollToIndex(highlightedIndex);
|
|
2683
2685
|
const title = "[5] Logs";
|
|
2684
|
-
const borderColor =
|
|
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 (
|
|
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
|
|
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({
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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] =
|
|
2853
|
-
const [modalOpen, setModalOpen] =
|
|
2854
|
-
const [logRefreshKey, setLogRefreshKey] =
|
|
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] =
|
|
2857
|
-
const [jiraState, setJiraState] =
|
|
2858
|
-
const [logsFocusedBox, setLogsFocusedBox] =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
2950
|
-
const [size, setSize] =
|
|
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);
|