clairo 1.0.8 → 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 +390 -275
- 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
|
|
|
@@ -573,6 +627,26 @@ function extractTicketKey(text) {
|
|
|
573
627
|
}
|
|
574
628
|
|
|
575
629
|
// src/lib/jira/config.ts
|
|
630
|
+
function getExistingJiraConfigs(excludeRepoPath) {
|
|
631
|
+
const config = loadConfig();
|
|
632
|
+
const repos = config.repositories ?? {};
|
|
633
|
+
const configs = [];
|
|
634
|
+
const seen = /* @__PURE__ */ new Set();
|
|
635
|
+
for (const [repoPath, repoConfig] of Object.entries(repos)) {
|
|
636
|
+
if (repoPath === excludeRepoPath) continue;
|
|
637
|
+
if (!repoConfig.jiraSiteUrl || !repoConfig.jiraEmail || !repoConfig.jiraApiToken) continue;
|
|
638
|
+
const key = `${repoConfig.jiraSiteUrl}|${repoConfig.jiraEmail}`;
|
|
639
|
+
if (seen.has(key)) continue;
|
|
640
|
+
seen.add(key);
|
|
641
|
+
configs.push({
|
|
642
|
+
repoPath,
|
|
643
|
+
siteUrl: repoConfig.jiraSiteUrl,
|
|
644
|
+
email: repoConfig.jiraEmail,
|
|
645
|
+
apiToken: repoConfig.jiraApiToken
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
return configs;
|
|
649
|
+
}
|
|
576
650
|
function isJiraConfigured(repoPath) {
|
|
577
651
|
const config = getRepoConfig(repoPath);
|
|
578
652
|
return !!(config.jiraSiteUrl && config.jiraEmail && config.jiraApiToken);
|
|
@@ -594,6 +668,13 @@ function getJiraCredentials(repoPath) {
|
|
|
594
668
|
function setJiraCredentials(repoPath, email, apiToken) {
|
|
595
669
|
updateRepoConfig(repoPath, { jiraEmail: email, jiraApiToken: apiToken });
|
|
596
670
|
}
|
|
671
|
+
function clearJiraConfig(repoPath) {
|
|
672
|
+
updateRepoConfig(repoPath, {
|
|
673
|
+
jiraSiteUrl: void 0,
|
|
674
|
+
jiraEmail: void 0,
|
|
675
|
+
jiraApiToken: void 0
|
|
676
|
+
});
|
|
677
|
+
}
|
|
597
678
|
function getLinkedTickets(repoPath, branch) {
|
|
598
679
|
var _a;
|
|
599
680
|
const config = getRepoConfig(repoPath);
|
|
@@ -1015,52 +1096,14 @@ function renderInlineToString(tokens) {
|
|
|
1015
1096
|
|
|
1016
1097
|
// src/components/github/PRDetailsBox.tsx
|
|
1017
1098
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1018
|
-
function
|
|
1019
|
-
const conclusion = check.conclusion ?? check.state;
|
|
1020
|
-
if (conclusion === "SUCCESS") return "green";
|
|
1021
|
-
if (conclusion === "FAILURE" || conclusion === "ERROR") return "red";
|
|
1022
|
-
if (conclusion === "SKIPPED" || conclusion === "NEUTRAL") return "gray";
|
|
1023
|
-
if (conclusion === "PENDING" || check.status === "IN_PROGRESS" || check.status === "QUEUED" || check.status === "WAITING")
|
|
1024
|
-
return "yellow";
|
|
1025
|
-
if (check.status === "COMPLETED") return "green";
|
|
1026
|
-
return void 0;
|
|
1027
|
-
}
|
|
1028
|
-
function getCheckIcon(check) {
|
|
1029
|
-
const conclusion = check.conclusion ?? check.state;
|
|
1030
|
-
if (conclusion === "SUCCESS") return "\u2713";
|
|
1031
|
-
if (conclusion === "FAILURE" || conclusion === "ERROR") return "\u2717";
|
|
1032
|
-
if (conclusion === "SKIPPED" || conclusion === "NEUTRAL") return "\u25CB";
|
|
1033
|
-
if (conclusion === "PENDING" || check.status === "IN_PROGRESS" || check.status === "QUEUED" || check.status === "WAITING")
|
|
1034
|
-
return "\u25CF";
|
|
1035
|
-
if (check.status === "COMPLETED") return "\u2713";
|
|
1036
|
-
return "?";
|
|
1037
|
-
}
|
|
1038
|
-
function getCheckSortOrder(check) {
|
|
1039
|
-
const conclusion = check.conclusion ?? check.state;
|
|
1040
|
-
if (conclusion === "FAILURE" || conclusion === "ERROR") return 0;
|
|
1041
|
-
if (conclusion === "PENDING" || check.status === "IN_PROGRESS" || check.status === "QUEUED" || check.status === "WAITING")
|
|
1042
|
-
return 1;
|
|
1043
|
-
if (conclusion === "SKIPPED" || conclusion === "NEUTRAL") return 2;
|
|
1044
|
-
if (conclusion === "SUCCESS" || check.status === "COMPLETED") return 3;
|
|
1045
|
-
return 4;
|
|
1046
|
-
}
|
|
1047
|
-
function PRDetailsBox({ pr, loading, error, isFocused }) {
|
|
1099
|
+
function PRDetailsBox({ pr, loading, error, isActive }) {
|
|
1048
1100
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
1049
1101
|
const scrollRef = useRef2(null);
|
|
1050
1102
|
const title = "[3] PR Details";
|
|
1051
|
-
const borderColor =
|
|
1103
|
+
const borderColor = isActive ? "yellow" : void 0;
|
|
1052
1104
|
const displayTitle = pr ? `${title} - #${pr.number}` : title;
|
|
1053
|
-
const
|
|
1054
|
-
const
|
|
1055
|
-
const getMergeDisplay = () => {
|
|
1056
|
-
if (!pr) return { text: "UNKNOWN", color: "yellow" };
|
|
1057
|
-
if (pr.state === "MERGED") return { text: "MERGED", color: "magenta" };
|
|
1058
|
-
if (pr.state === "CLOSED") return { text: "CLOSED", color: "red" };
|
|
1059
|
-
if (pr.mergeable === "MERGEABLE") return { text: "MERGEABLE", color: "green" };
|
|
1060
|
-
if (pr.mergeable === "CONFLICTING") return { text: "CONFLICTING", color: "red" };
|
|
1061
|
-
return { text: pr.mergeable ?? "UNKNOWN", color: "yellow" };
|
|
1062
|
-
};
|
|
1063
|
-
const mergeDisplay = getMergeDisplay();
|
|
1105
|
+
const reviewDisplay = resolveReviewDisplay((pr == null ? void 0 : pr.reviewDecision) ?? null);
|
|
1106
|
+
const mergeDisplay = resolveMergeDisplay(pr);
|
|
1064
1107
|
useInput(
|
|
1065
1108
|
(input, key) => {
|
|
1066
1109
|
var _a2, _b2;
|
|
@@ -1075,7 +1118,7 @@ function PRDetailsBox({ pr, loading, error, isFocused }) {
|
|
|
1075
1118
|
});
|
|
1076
1119
|
}
|
|
1077
1120
|
},
|
|
1078
|
-
{ isActive
|
|
1121
|
+
{ isActive }
|
|
1079
1122
|
);
|
|
1080
1123
|
const { stdout } = useStdout();
|
|
1081
1124
|
const terminalWidth = (stdout == null ? void 0 : stdout.columns) ?? 80;
|
|
@@ -1110,7 +1153,7 @@ function PRDetailsBox({ pr, loading, error, isFocused }) {
|
|
|
1110
1153
|
] }),
|
|
1111
1154
|
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
1112
1155
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Review: " }),
|
|
1113
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
1156
|
+
/* @__PURE__ */ jsx2(Text2, { color: reviewDisplay.color, children: reviewDisplay.text }),
|
|
1114
1157
|
/* @__PURE__ */ jsx2(Text2, { children: " | " }),
|
|
1115
1158
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Status: " }),
|
|
1116
1159
|
/* @__PURE__ */ jsx2(Text2, { color: mergeDisplay.color, children: mergeDisplay.text })
|
|
@@ -1147,12 +1190,13 @@ function PRDetailsBox({ pr, loading, error, isFocused }) {
|
|
|
1147
1190
|
}
|
|
1148
1191
|
return acc;
|
|
1149
1192
|
}, /* @__PURE__ */ new Map()).values()) ?? []
|
|
1150
|
-
).sort((a, b) =>
|
|
1193
|
+
).sort((a, b) => CHECK_SORT_ORDER[resolveCheckStatus(a)] - CHECK_SORT_ORDER[resolveCheckStatus(b)]).map((check, idx) => {
|
|
1151
1194
|
const jobName = check.name ?? check.context;
|
|
1152
1195
|
const displayName = check.workflowName ? `${check.workflowName} / ${jobName}` : jobName;
|
|
1153
|
-
|
|
1196
|
+
const status = resolveCheckStatus(check);
|
|
1197
|
+
return /* @__PURE__ */ jsxs2(Text2, { color: CHECK_COLORS[status], children: [
|
|
1154
1198
|
" ",
|
|
1155
|
-
|
|
1199
|
+
CHECK_ICONS[status],
|
|
1156
1200
|
" ",
|
|
1157
1201
|
displayName
|
|
1158
1202
|
] }, idx);
|
|
@@ -1171,9 +1215,9 @@ function PRDetailsBox({ pr, loading, error, isFocused }) {
|
|
|
1171
1215
|
|
|
1172
1216
|
// src/components/github/PullRequestsBox.tsx
|
|
1173
1217
|
import open2 from "open";
|
|
1174
|
-
import {
|
|
1218
|
+
import { useState as useState10 } from "react";
|
|
1175
1219
|
import { TitledBox } from "@mishieck/ink-titled-box";
|
|
1176
|
-
import { Box as Box3, Text as Text3, useInput as
|
|
1220
|
+
import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
|
|
1177
1221
|
import { ScrollView as ScrollView2 } from "ink-scroll-view";
|
|
1178
1222
|
|
|
1179
1223
|
// src/hooks/jira/useJiraTickets.ts
|
|
@@ -1431,25 +1475,8 @@ function useModal() {
|
|
|
1431
1475
|
}
|
|
1432
1476
|
|
|
1433
1477
|
// src/hooks/useListNavigation.ts
|
|
1434
|
-
import {
|
|
1435
|
-
|
|
1436
|
-
const [index, setIndex] = useState8(0);
|
|
1437
|
-
const prev = useCallback7(() => {
|
|
1438
|
-
setIndex((i) => Math.max(0, i - 1));
|
|
1439
|
-
}, []);
|
|
1440
|
-
const next = useCallback7(() => {
|
|
1441
|
-
setIndex((i) => Math.min(length - 1, i + 1));
|
|
1442
|
-
}, [length]);
|
|
1443
|
-
const clampedIndex = Math.min(index, Math.max(0, length - 1));
|
|
1444
|
-
const reset = useCallback7(() => setIndex(0), []);
|
|
1445
|
-
return {
|
|
1446
|
-
index: length === 0 ? 0 : clampedIndex,
|
|
1447
|
-
prev,
|
|
1448
|
-
next,
|
|
1449
|
-
reset,
|
|
1450
|
-
setIndex
|
|
1451
|
-
};
|
|
1452
|
-
}
|
|
1478
|
+
import { useEffect as useEffect6, useState as useState8 } from "react";
|
|
1479
|
+
import { useInput as useInput2 } from "ink";
|
|
1453
1480
|
|
|
1454
1481
|
// src/hooks/useScrollToIndex.ts
|
|
1455
1482
|
import { useEffect as useEffect5, useRef as useRef4 } from "react";
|
|
@@ -1471,8 +1498,53 @@ function useScrollToIndex(index) {
|
|
|
1471
1498
|
return scrollRef;
|
|
1472
1499
|
}
|
|
1473
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
|
+
|
|
1474
1546
|
// src/hooks/useRubberDuck.ts
|
|
1475
|
-
import { useCallback as
|
|
1547
|
+
import { useCallback as useCallback7, useEffect as useEffect7, useState as useState9 } from "react";
|
|
1476
1548
|
var DUCK_MESSAGES = [
|
|
1477
1549
|
"Quack.",
|
|
1478
1550
|
"Quack quack quack.",
|
|
@@ -1510,18 +1582,18 @@ function useRubberDuck() {
|
|
|
1510
1582
|
visible: false,
|
|
1511
1583
|
message: DUCK_MESSAGES[0]
|
|
1512
1584
|
});
|
|
1513
|
-
const getRandomMessage =
|
|
1585
|
+
const getRandomMessage = useCallback7(() => {
|
|
1514
1586
|
const index = Math.floor(Math.random() * DUCK_MESSAGES.length);
|
|
1515
1587
|
return DUCK_MESSAGES[index];
|
|
1516
1588
|
}, []);
|
|
1517
|
-
const toggleDuck =
|
|
1589
|
+
const toggleDuck = useCallback7(() => {
|
|
1518
1590
|
setState((prev) => ({
|
|
1519
1591
|
...prev,
|
|
1520
1592
|
visible: !prev.visible,
|
|
1521
1593
|
message: !prev.visible ? getRandomMessage() : prev.message
|
|
1522
1594
|
}));
|
|
1523
1595
|
}, [getRandomMessage]);
|
|
1524
|
-
const quack =
|
|
1596
|
+
const quack = useCallback7(() => {
|
|
1525
1597
|
if (state.visible) {
|
|
1526
1598
|
setState((prev) => ({
|
|
1527
1599
|
...prev,
|
|
@@ -1529,11 +1601,11 @@ function useRubberDuck() {
|
|
|
1529
1601
|
}));
|
|
1530
1602
|
}
|
|
1531
1603
|
}, [state.visible, getRandomMessage]);
|
|
1532
|
-
const getReactionMessage =
|
|
1604
|
+
const getReactionMessage = useCallback7((event) => {
|
|
1533
1605
|
const messages = REACTION_MESSAGES[event];
|
|
1534
1606
|
return messages[Math.floor(Math.random() * messages.length)];
|
|
1535
1607
|
}, []);
|
|
1536
|
-
|
|
1608
|
+
useEffect7(() => {
|
|
1537
1609
|
const unsubscribe = duckEvents.subscribe((event) => {
|
|
1538
1610
|
setState((prev) => ({
|
|
1539
1611
|
...prev,
|
|
@@ -1583,32 +1655,25 @@ function PullRequestsBox({
|
|
|
1583
1655
|
error,
|
|
1584
1656
|
branch,
|
|
1585
1657
|
repoSlug,
|
|
1586
|
-
|
|
1658
|
+
isActive
|
|
1587
1659
|
}) {
|
|
1588
|
-
const [highlightedIndex, setHighlightedIndex] = useState10(0);
|
|
1589
1660
|
const [copied, setCopied] = useState10(false);
|
|
1590
|
-
const
|
|
1591
|
-
const
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
if (
|
|
1599
|
-
|
|
1600
|
-
setHighlightedIndex((prev) => Math.max(0, prev - 1));
|
|
1601
|
-
}
|
|
1602
|
-
if (key.downArrow || input === "j") {
|
|
1603
|
-
setHighlightedIndex((prev) => Math.min(totalItems - 1, prev + 1));
|
|
1604
|
-
}
|
|
1605
|
-
if (input === " ") {
|
|
1606
|
-
if (highlightedIndex === prs.length) {
|
|
1607
|
-
onCreatePR();
|
|
1608
|
-
} else if (prs[highlightedIndex]) {
|
|
1609
|
-
onSelect(prs[highlightedIndex]);
|
|
1610
|
-
}
|
|
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]);
|
|
1611
1671
|
}
|
|
1672
|
+
},
|
|
1673
|
+
isActive
|
|
1674
|
+
});
|
|
1675
|
+
useInput3(
|
|
1676
|
+
(input) => {
|
|
1612
1677
|
if (input === "y" && repoSlug && prs[highlightedIndex]) {
|
|
1613
1678
|
const pr = prs[highlightedIndex];
|
|
1614
1679
|
const url = `https://github.com/${repoSlug}/pull/${pr.number}`;
|
|
@@ -1623,12 +1688,12 @@ function PullRequestsBox({
|
|
|
1623
1688
|
});
|
|
1624
1689
|
}
|
|
1625
1690
|
},
|
|
1626
|
-
{ isActive
|
|
1691
|
+
{ isActive }
|
|
1627
1692
|
);
|
|
1628
1693
|
const title = "[2] Pull Requests";
|
|
1629
1694
|
const subtitle = branch ? ` (${branch})` : "";
|
|
1630
1695
|
const copiedIndicator = copied ? " [Copied!]" : "";
|
|
1631
|
-
const borderColor =
|
|
1696
|
+
const borderColor = isActive ? "yellow" : void 0;
|
|
1632
1697
|
return /* @__PURE__ */ jsx3(
|
|
1633
1698
|
TitledBox,
|
|
1634
1699
|
{
|
|
@@ -1642,7 +1707,7 @@ function PullRequestsBox({
|
|
|
1642
1707
|
!loading && !error && /* @__PURE__ */ jsxs3(ScrollView2, { ref: scrollRef, children: [
|
|
1643
1708
|
prs.length === 0 && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "No PRs for this branch" }, "empty"),
|
|
1644
1709
|
prs.map((pr, idx) => {
|
|
1645
|
-
const isHighlighted =
|
|
1710
|
+
const isHighlighted = isActive && idx === highlightedIndex;
|
|
1646
1711
|
const isSelected = pr.number === (selectedPR == null ? void 0 : selectedPR.number);
|
|
1647
1712
|
const cursor = isHighlighted ? ">" : " ";
|
|
1648
1713
|
const indicator = isSelected ? " *" : "";
|
|
@@ -1662,7 +1727,7 @@ function PullRequestsBox({
|
|
|
1662
1727
|
] }, pr.number);
|
|
1663
1728
|
}),
|
|
1664
1729
|
/* @__PURE__ */ jsxs3(Text3, { color: "blue", children: [
|
|
1665
|
-
|
|
1730
|
+
isActive && highlightedIndex === prs.length ? "> " : " ",
|
|
1666
1731
|
"+ Create new PR"
|
|
1667
1732
|
] }, "create")
|
|
1668
1733
|
] })
|
|
@@ -1672,41 +1737,26 @@ function PullRequestsBox({
|
|
|
1672
1737
|
}
|
|
1673
1738
|
|
|
1674
1739
|
// src/components/github/RemotesBox.tsx
|
|
1675
|
-
import { useEffect as useEffect8, useState as useState11 } from "react";
|
|
1676
1740
|
import { TitledBox as TitledBox2 } from "@mishieck/ink-titled-box";
|
|
1677
|
-
import { Box as Box4, Text as Text4
|
|
1741
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
1678
1742
|
import { ScrollView as ScrollView3 } from "ink-scroll-view";
|
|
1679
1743
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1680
|
-
function RemotesBox({ remotes, selectedRemote, onSelect, loading, error,
|
|
1681
|
-
const
|
|
1682
|
-
const scrollRef =
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
(input, key) => {
|
|
1689
|
-
if (!isFocused || remotes.length === 0) return;
|
|
1690
|
-
if (key.upArrow || input === "k") {
|
|
1691
|
-
setHighlightedIndex((prev) => Math.max(0, prev - 1));
|
|
1692
|
-
}
|
|
1693
|
-
if (key.downArrow || input === "j") {
|
|
1694
|
-
setHighlightedIndex((prev) => Math.min(remotes.length - 1, prev + 1));
|
|
1695
|
-
}
|
|
1696
|
-
if (input === " ") {
|
|
1697
|
-
onSelect(remotes[highlightedIndex].name);
|
|
1698
|
-
}
|
|
1699
|
-
},
|
|
1700
|
-
{ isActive: isFocused }
|
|
1701
|
-
);
|
|
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
|
+
});
|
|
1702
1752
|
const title = "[1] Remotes";
|
|
1703
|
-
const borderColor =
|
|
1753
|
+
const borderColor = isActive ? "yellow" : void 0;
|
|
1704
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: [
|
|
1705
1755
|
loading && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Loading..." }),
|
|
1706
1756
|
error && /* @__PURE__ */ jsx4(Text4, { color: "red", children: error }),
|
|
1707
1757
|
!loading && !error && remotes.length === 0 && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No remotes configured" }),
|
|
1708
1758
|
!loading && !error && remotes.length > 0 && /* @__PURE__ */ jsx4(ScrollView3, { ref: scrollRef, children: remotes.map((remote, idx) => {
|
|
1709
|
-
const isHighlighted =
|
|
1759
|
+
const isHighlighted = isActive && idx === highlightedIndex;
|
|
1710
1760
|
const isSelected = remote.name === selectedRemote;
|
|
1711
1761
|
const cursor = isHighlighted ? ">" : " ";
|
|
1712
1762
|
const indicator = isSelected ? " *" : "";
|
|
@@ -1729,89 +1779,70 @@ function RemotesBox({ remotes, selectedRemote, onSelect, loading, error, isFocus
|
|
|
1729
1779
|
|
|
1730
1780
|
// src/components/github/GitHubView.tsx
|
|
1731
1781
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1732
|
-
function GitHubView({
|
|
1782
|
+
function GitHubView({ isActive, onFocusedBoxChange, onLogUpdated }) {
|
|
1733
1783
|
const repo = useGitRepo();
|
|
1734
1784
|
const pullRequests = usePullRequests();
|
|
1735
|
-
const
|
|
1736
|
-
|
|
1737
|
-
const lastFetchedRef = useRef5(null);
|
|
1738
|
-
useEffect9(() => {
|
|
1785
|
+
const [focusedBox, setFocusedBox] = useState11("remotes");
|
|
1786
|
+
useEffect8(() => {
|
|
1739
1787
|
if (repo.loading || !repo.currentBranch || !repo.currentRepoSlug) return;
|
|
1740
|
-
const current = { branch: repo.currentBranch, repoSlug: repo.currentRepoSlug };
|
|
1741
|
-
const last = lastFetchedRef.current;
|
|
1742
|
-
if (last && last.branch === current.branch && last.repoSlug === current.repoSlug) return;
|
|
1743
|
-
lastFetchedRef.current = current;
|
|
1744
1788
|
pullRequests.fetchPRsAndDetails(repo.currentBranch, repo.currentRepoSlug);
|
|
1745
1789
|
}, [repo.loading, repo.currentBranch, repo.currentRepoSlug, pullRequests.fetchPRsAndDetails]);
|
|
1746
|
-
|
|
1747
|
-
if (
|
|
1790
|
+
useEffect8(() => {
|
|
1791
|
+
if (isActive) {
|
|
1748
1792
|
repo.refreshBranch();
|
|
1749
1793
|
}
|
|
1750
|
-
}, [
|
|
1751
|
-
|
|
1794
|
+
}, [isActive, repo.refreshBranch]);
|
|
1795
|
+
useEffect8(() => {
|
|
1752
1796
|
onFocusedBoxChange == null ? void 0 : onFocusedBoxChange(focusedBox);
|
|
1753
1797
|
}, [focusedBox, onFocusedBoxChange]);
|
|
1754
|
-
const handleRemoteSelect =
|
|
1798
|
+
const handleRemoteSelect = useCallback8(
|
|
1755
1799
|
(remoteName) => {
|
|
1756
1800
|
repo.selectRemote(remoteName);
|
|
1757
|
-
const remote = repo.remotes.find((r) => r.name === remoteName);
|
|
1758
|
-
if (!remote || !repo.currentBranch) return;
|
|
1759
|
-
const repoSlug = getRepoFromRemote(remote.url);
|
|
1760
|
-
if (!repoSlug) return;
|
|
1761
|
-
lastFetchedRef.current = { branch: repo.currentBranch, repoSlug };
|
|
1762
|
-
pullRequests.fetchPRsAndDetails(repo.currentBranch, repoSlug);
|
|
1763
1801
|
},
|
|
1764
|
-
[repo.selectRemote
|
|
1802
|
+
[repo.selectRemote]
|
|
1765
1803
|
);
|
|
1766
|
-
const handlePRSelect =
|
|
1804
|
+
const handlePRSelect = useCallback8(
|
|
1767
1805
|
(pr) => {
|
|
1768
1806
|
pullRequests.selectPR(pr, repo.currentRepoSlug);
|
|
1769
1807
|
},
|
|
1770
1808
|
[pullRequests.selectPR, repo.currentRepoSlug]
|
|
1771
1809
|
);
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1774
|
-
const handleCreatePR =
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
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");
|
|
1778
1815
|
duckEvents.emit("error");
|
|
1779
1816
|
return;
|
|
1780
1817
|
}
|
|
1781
|
-
const remoteResult = findRemoteWithBranch(
|
|
1818
|
+
const remoteResult = findRemoteWithBranch(repo.currentBranch);
|
|
1782
1819
|
if (!remoteResult.success) {
|
|
1783
|
-
|
|
1820
|
+
pullRequests.setError("prs", "Push your branch to a remote first");
|
|
1784
1821
|
duckEvents.emit("error");
|
|
1785
1822
|
return;
|
|
1786
1823
|
}
|
|
1787
|
-
openPRCreationPage(remoteResult.data.owner,
|
|
1824
|
+
openPRCreationPage(remoteResult.data.owner, repo.currentBranch, (error) => {
|
|
1788
1825
|
if (error) {
|
|
1789
|
-
|
|
1826
|
+
pullRequests.setError("prs", `Failed to create PR: ${error.message}`);
|
|
1790
1827
|
duckEvents.emit("error");
|
|
1791
1828
|
}
|
|
1792
1829
|
});
|
|
1793
|
-
if (!
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
},
|
|
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,
|
|
1801
1837
|
onNewPR: (newPR) => {
|
|
1802
1838
|
var _a;
|
|
1803
|
-
const
|
|
1804
|
-
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) : [];
|
|
1805
1840
|
logPRCreated(newPR.number, newPR.title, tickets);
|
|
1806
1841
|
duckEvents.emit("pr:opened");
|
|
1807
|
-
(_a =
|
|
1808
|
-
ctx.pullRequests.setSelectedPR(newPR);
|
|
1809
|
-
if (ctx.repo.currentRepoSlug) {
|
|
1810
|
-
ctx.pullRequests.refreshDetails(newPR, ctx.repo.currentRepoSlug);
|
|
1811
|
-
}
|
|
1842
|
+
(_a = onLogUpdatedRef.current) == null ? void 0 : _a.call(onLogUpdatedRef);
|
|
1812
1843
|
}
|
|
1813
1844
|
});
|
|
1814
|
-
}, [
|
|
1845
|
+
}, [repo.currentBranch, repo.currentRepoSlug, repo.repoPath, pullRequests.setError, pullRequests.pollForNewPR]);
|
|
1815
1846
|
useInput4(
|
|
1816
1847
|
(input) => {
|
|
1817
1848
|
if (input === "1") setFocusedBox("remotes");
|
|
@@ -1830,7 +1861,7 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
|
|
|
1830
1861
|
handleCreatePR();
|
|
1831
1862
|
}
|
|
1832
1863
|
},
|
|
1833
|
-
{ isActive
|
|
1864
|
+
{ isActive }
|
|
1834
1865
|
);
|
|
1835
1866
|
if (repo.isRepo === false) {
|
|
1836
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" }) });
|
|
@@ -1844,7 +1875,7 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
|
|
|
1844
1875
|
onSelect: handleRemoteSelect,
|
|
1845
1876
|
loading: repo.loading,
|
|
1846
1877
|
error: repo.error,
|
|
1847
|
-
|
|
1878
|
+
isActive: isActive && focusedBox === "remotes"
|
|
1848
1879
|
}
|
|
1849
1880
|
),
|
|
1850
1881
|
/* @__PURE__ */ jsx5(
|
|
@@ -1858,7 +1889,7 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
|
|
|
1858
1889
|
error: pullRequests.errors.prs,
|
|
1859
1890
|
branch: repo.currentBranch,
|
|
1860
1891
|
repoSlug: repo.currentRepoSlug,
|
|
1861
|
-
|
|
1892
|
+
isActive: isActive && focusedBox === "prs"
|
|
1862
1893
|
}
|
|
1863
1894
|
),
|
|
1864
1895
|
/* @__PURE__ */ jsx5(
|
|
@@ -1867,7 +1898,7 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
|
|
|
1867
1898
|
pr: pullRequests.prDetails,
|
|
1868
1899
|
loading: pullRequests.loading.details,
|
|
1869
1900
|
error: pullRequests.errors.details,
|
|
1870
|
-
|
|
1901
|
+
isActive: isActive && focusedBox === "details"
|
|
1871
1902
|
}
|
|
1872
1903
|
)
|
|
1873
1904
|
] });
|
|
@@ -1875,10 +1906,10 @@ function GitHubView({ isFocused, onFocusedBoxChange, onLogUpdated }) {
|
|
|
1875
1906
|
|
|
1876
1907
|
// src/components/jira/JiraView.tsx
|
|
1877
1908
|
import open3 from "open";
|
|
1878
|
-
import { useEffect as
|
|
1909
|
+
import { useEffect as useEffect10, useRef as useRef6 } from "react";
|
|
1879
1910
|
|
|
1880
1911
|
// src/components/jira/LinkTicketModal.tsx
|
|
1881
|
-
import { useState as
|
|
1912
|
+
import { useState as useState12 } from "react";
|
|
1882
1913
|
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
1883
1914
|
|
|
1884
1915
|
// src/components/ui/TextInput.tsx
|
|
@@ -1913,7 +1944,7 @@ function TextInput({ value, onChange, placeholder, isActive, mask }) {
|
|
|
1913
1944
|
// src/components/jira/LinkTicketModal.tsx
|
|
1914
1945
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1915
1946
|
function LinkTicketModal({ onSubmit, onCancel, loading, error }) {
|
|
1916
|
-
const [ticketInput, setTicketInput] =
|
|
1947
|
+
const [ticketInput, setTicketInput] = useState12("");
|
|
1917
1948
|
const canSubmit = ticketInput.trim().length > 0;
|
|
1918
1949
|
useInput6(
|
|
1919
1950
|
(_input, key) => {
|
|
@@ -1947,16 +1978,16 @@ import { TitledBox as TitledBox4 } from "@mishieck/ink-titled-box";
|
|
|
1947
1978
|
import { Box as Box11, Text as Text11, useInput as useInput9 } from "ink";
|
|
1948
1979
|
|
|
1949
1980
|
// src/components/jira/ChangeStatusModal.tsx
|
|
1950
|
-
import { useEffect as
|
|
1981
|
+
import { useEffect as useEffect9, useState as useState13 } from "react";
|
|
1951
1982
|
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
1952
1983
|
import SelectInput from "ink-select-input";
|
|
1953
1984
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1954
1985
|
function ChangeStatusModal({ repoPath, ticketKey, currentStatus, onComplete, onCancel }) {
|
|
1955
|
-
const [transitions, setTransitions] =
|
|
1956
|
-
const [loading, setLoading] =
|
|
1957
|
-
const [applying, setApplying] =
|
|
1958
|
-
const [error, setError] =
|
|
1959
|
-
|
|
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(() => {
|
|
1960
1991
|
const fetchTransitions = async () => {
|
|
1961
1992
|
const siteUrl = getJiraSiteUrl(repoPath);
|
|
1962
1993
|
const creds = getJiraCredentials(repoPath);
|
|
@@ -2033,8 +2064,9 @@ function ChangeStatusModal({ repoPath, ticketKey, currentStatus, onComplete, onC
|
|
|
2033
2064
|
}
|
|
2034
2065
|
|
|
2035
2066
|
// src/components/jira/ConfigureJiraSiteModal.tsx
|
|
2036
|
-
import { useState as
|
|
2067
|
+
import { useState as useState14 } from "react";
|
|
2037
2068
|
import { Box as Box9, Text as Text9, useInput as useInput8 } from "ink";
|
|
2069
|
+
import { ScrollView as ScrollView4 } from "ink-scroll-view";
|
|
2038
2070
|
|
|
2039
2071
|
// src/lib/editor.ts
|
|
2040
2072
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
@@ -2066,27 +2098,57 @@ function openInEditor(content, filename) {
|
|
|
2066
2098
|
|
|
2067
2099
|
// src/components/jira/ConfigureJiraSiteModal.tsx
|
|
2068
2100
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2101
|
+
var MAX_VISIBLE_ITEMS = 4;
|
|
2069
2102
|
function ConfigureJiraSiteModal({
|
|
2070
2103
|
initialSiteUrl,
|
|
2071
2104
|
initialEmail,
|
|
2105
|
+
existingConfigs = [],
|
|
2072
2106
|
onSubmit,
|
|
2073
2107
|
onCancel,
|
|
2074
2108
|
loading,
|
|
2075
2109
|
error
|
|
2076
2110
|
}) {
|
|
2077
|
-
const
|
|
2078
|
-
const [
|
|
2079
|
-
const [
|
|
2080
|
-
const
|
|
2111
|
+
const hasExisting = existingConfigs.length > 0;
|
|
2112
|
+
const [mode, setMode] = useState14(hasExisting ? "choose" : "manual");
|
|
2113
|
+
const [selectedExisting, setSelectedExisting] = useState14(0);
|
|
2114
|
+
const scrollRef = useScrollToIndex(selectedExisting);
|
|
2115
|
+
const [siteUrl, setSiteUrl] = useState14(initialSiteUrl ?? "");
|
|
2116
|
+
const [email, setEmail] = useState14(initialEmail ?? "");
|
|
2117
|
+
const [apiToken, setApiToken] = useState14("");
|
|
2118
|
+
const [selectedItem, setSelectedItem] = useState14("siteUrl");
|
|
2081
2119
|
const items = ["siteUrl", "email", "apiToken", "submit"];
|
|
2082
2120
|
const canSubmit = siteUrl.trim() && email.trim() && apiToken.trim();
|
|
2121
|
+
const chooseItems = existingConfigs.length + 1;
|
|
2083
2122
|
useInput8(
|
|
2084
2123
|
(input, key) => {
|
|
2085
2124
|
if (loading) return;
|
|
2086
2125
|
if (key.escape) {
|
|
2126
|
+
if (mode === "manual" && hasExisting) {
|
|
2127
|
+
setMode("choose");
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2087
2130
|
onCancel();
|
|
2088
2131
|
return;
|
|
2089
2132
|
}
|
|
2133
|
+
if (mode === "choose") {
|
|
2134
|
+
if (key.upArrow || input === "k") {
|
|
2135
|
+
setSelectedExisting((prev) => Math.max(0, prev - 1));
|
|
2136
|
+
return;
|
|
2137
|
+
}
|
|
2138
|
+
if (key.downArrow || input === "j") {
|
|
2139
|
+
setSelectedExisting((prev) => Math.min(chooseItems - 1, prev + 1));
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
if (key.return) {
|
|
2143
|
+
if (selectedExisting < existingConfigs.length) {
|
|
2144
|
+
const config = existingConfigs[selectedExisting];
|
|
2145
|
+
onSubmit(config.siteUrl, config.email, config.apiToken);
|
|
2146
|
+
} else {
|
|
2147
|
+
setMode("manual");
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
2090
2152
|
if (key.upArrow || input === "k") {
|
|
2091
2153
|
setSelectedItem((prev) => {
|
|
2092
2154
|
const idx = items.indexOf(prev);
|
|
@@ -2137,9 +2199,49 @@ function ConfigureJiraSiteModal({
|
|
|
2137
2199
|
value !== void 0 && /* @__PURE__ */ jsx9(Box9, { marginLeft: 4, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: displayValue || "(empty - press Enter to edit)" }) })
|
|
2138
2200
|
] });
|
|
2139
2201
|
};
|
|
2202
|
+
if (mode === "choose") {
|
|
2203
|
+
const totalItems = existingConfigs.length + 1;
|
|
2204
|
+
const listHeight = Math.min(totalItems * 2, MAX_VISIBLE_ITEMS * 2);
|
|
2205
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, paddingY: 1, children: [
|
|
2206
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: "cyan", children: "Configure Jira Site" }),
|
|
2207
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Select an existing configuration or enter new credentials" }),
|
|
2208
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1 }),
|
|
2209
|
+
error && /* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "red", children: error }) }),
|
|
2210
|
+
/* @__PURE__ */ jsx9(Box9, { height: listHeight, overflow: "hidden", children: /* @__PURE__ */ jsxs9(ScrollView4, { ref: scrollRef, children: [
|
|
2211
|
+
existingConfigs.map((config, idx) => {
|
|
2212
|
+
const isSelected = selectedExisting === idx;
|
|
2213
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
2214
|
+
/* @__PURE__ */ jsxs9(Text9, { color: isSelected ? "yellow" : void 0, bold: isSelected, children: [
|
|
2215
|
+
isSelected ? "> " : " ",
|
|
2216
|
+
config.siteUrl
|
|
2217
|
+
] }),
|
|
2218
|
+
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
2219
|
+
" ",
|
|
2220
|
+
config.email
|
|
2221
|
+
] })
|
|
2222
|
+
] }, config.siteUrl + config.email);
|
|
2223
|
+
}),
|
|
2224
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsxs9(
|
|
2225
|
+
Text9,
|
|
2226
|
+
{
|
|
2227
|
+
color: selectedExisting === existingConfigs.length ? "yellow" : void 0,
|
|
2228
|
+
bold: selectedExisting === existingConfigs.length,
|
|
2229
|
+
children: [
|
|
2230
|
+
selectedExisting === existingConfigs.length ? "> " : " ",
|
|
2231
|
+
"Enter new credentials..."
|
|
2232
|
+
]
|
|
2233
|
+
}
|
|
2234
|
+
) })
|
|
2235
|
+
] }) }),
|
|
2236
|
+
loading && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "Validating credentials..." }) })
|
|
2237
|
+
] });
|
|
2238
|
+
}
|
|
2140
2239
|
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, paddingY: 1, children: [
|
|
2141
2240
|
/* @__PURE__ */ jsx9(Text9, { bold: true, color: "cyan", children: "Configure Jira Site" }),
|
|
2142
|
-
/* @__PURE__ */
|
|
2241
|
+
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
2242
|
+
"Up/Down to select, Enter to edit, Esc to ",
|
|
2243
|
+
hasExisting ? "go back" : "cancel"
|
|
2244
|
+
] }),
|
|
2143
2245
|
/* @__PURE__ */ jsx9(Box9, { marginTop: 1 }),
|
|
2144
2246
|
error && /* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "red", children: error }) }),
|
|
2145
2247
|
renderItem("siteUrl", "Site URL (e.g., https://company.atlassian.net)", siteUrl),
|
|
@@ -2178,13 +2280,14 @@ function TicketItem({ ticketKey, summary, status, isHighlighted, isSelected }) {
|
|
|
2178
2280
|
|
|
2179
2281
|
// src/components/jira/JiraView.tsx
|
|
2180
2282
|
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2181
|
-
function JiraView({
|
|
2283
|
+
function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated }) {
|
|
2182
2284
|
const repo = useGitRepo();
|
|
2183
2285
|
const jira = useJiraTickets();
|
|
2184
2286
|
const modal = useModal();
|
|
2185
|
-
const nav = useListNavigation(jira.tickets
|
|
2287
|
+
const nav = useListNavigation({ items: jira.tickets });
|
|
2288
|
+
const currentTicket = jira.tickets[nav.index] ?? null;
|
|
2186
2289
|
const lastInitRef = useRef6(null);
|
|
2187
|
-
|
|
2290
|
+
useEffect10(() => {
|
|
2188
2291
|
if (repo.loading || !repo.repoPath || !repo.currentBranch) return;
|
|
2189
2292
|
const current = { branch: repo.currentBranch };
|
|
2190
2293
|
const last = lastInitRef.current;
|
|
@@ -2192,17 +2295,17 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2192
2295
|
lastInitRef.current = current;
|
|
2193
2296
|
jira.initializeJiraState(repo.repoPath, repo.currentBranch, repo.currentRepoSlug);
|
|
2194
2297
|
}, [repo.loading, repo.repoPath, repo.currentBranch, repo.currentRepoSlug, jira.initializeJiraState]);
|
|
2195
|
-
|
|
2196
|
-
if (
|
|
2298
|
+
useEffect10(() => {
|
|
2299
|
+
if (isActive) {
|
|
2197
2300
|
repo.refreshBranch();
|
|
2198
2301
|
} else {
|
|
2199
2302
|
modal.close();
|
|
2200
2303
|
}
|
|
2201
|
-
}, [
|
|
2202
|
-
|
|
2304
|
+
}, [isActive, repo.refreshBranch, modal.close]);
|
|
2305
|
+
useEffect10(() => {
|
|
2203
2306
|
onModalChange == null ? void 0 : onModalChange(modal.isOpen);
|
|
2204
2307
|
}, [modal.isOpen, onModalChange]);
|
|
2205
|
-
|
|
2308
|
+
useEffect10(() => {
|
|
2206
2309
|
onJiraStateChange == null ? void 0 : onJiraStateChange(jira.jiraState);
|
|
2207
2310
|
}, [jira.jiraState, onJiraStateChange]);
|
|
2208
2311
|
const handleConfigureSubmit = async (siteUrl, email, apiToken) => {
|
|
@@ -2215,42 +2318,39 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2215
2318
|
const success = await jira.linkTicket(repo.repoPath, repo.currentBranch, ticketInput);
|
|
2216
2319
|
if (success) modal.close();
|
|
2217
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
|
+
};
|
|
2218
2326
|
const handleUnlinkTicket = () => {
|
|
2219
|
-
if (!repo.repoPath || !repo.currentBranch ||
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
jira.refreshTickets(repo.repoPath, repo.currentBranch);
|
|
2224
|
-
nav.prev();
|
|
2225
|
-
}
|
|
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();
|
|
2226
2331
|
};
|
|
2227
2332
|
const handleOpenInBrowser = () => {
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
if (ticket && siteUrl) {
|
|
2232
|
-
open3(`${siteUrl}/browse/${ticket.key}`).catch(() => {
|
|
2233
|
-
});
|
|
2234
|
-
}
|
|
2333
|
+
const url = getTicketUrl();
|
|
2334
|
+
if (url) open3(url).catch(() => {
|
|
2335
|
+
});
|
|
2235
2336
|
};
|
|
2236
2337
|
const handleCopyLink = () => {
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
const siteUrl = getJiraSiteUrl(repo.repoPath);
|
|
2240
|
-
if (ticket && siteUrl) {
|
|
2241
|
-
copyToClipboard(`${siteUrl}/browse/${ticket.key}`);
|
|
2242
|
-
}
|
|
2338
|
+
const url = getTicketUrl();
|
|
2339
|
+
if (url) copyToClipboard(url);
|
|
2243
2340
|
};
|
|
2244
2341
|
const handleStatusComplete = (newStatus) => {
|
|
2245
|
-
if (!repo.repoPath || !repo.currentBranch) return;
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
updateTicketStatus(repo.repoPath, repo.currentBranch, ticket.key, newStatus);
|
|
2249
|
-
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);
|
|
2250
2345
|
onLogUpdated == null ? void 0 : onLogUpdated();
|
|
2251
2346
|
modal.close();
|
|
2252
2347
|
jira.refreshTickets(repo.repoPath, repo.currentBranch);
|
|
2253
2348
|
};
|
|
2349
|
+
const handleRemoveConfig = () => {
|
|
2350
|
+
if (!repo.repoPath || !repo.currentBranch) return;
|
|
2351
|
+
clearJiraConfig(repo.repoPath);
|
|
2352
|
+
jira.initializeJiraState(repo.repoPath, repo.currentBranch, repo.currentRepoSlug);
|
|
2353
|
+
};
|
|
2254
2354
|
useInput9(
|
|
2255
2355
|
(input, key) => {
|
|
2256
2356
|
if (input === "c" && jira.jiraState === "not_configured") {
|
|
@@ -2261,6 +2361,10 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2261
2361
|
modal.open("link");
|
|
2262
2362
|
return;
|
|
2263
2363
|
}
|
|
2364
|
+
if (input === "r" && jira.jiraState !== "not_configured") {
|
|
2365
|
+
handleRemoveConfig();
|
|
2366
|
+
return;
|
|
2367
|
+
}
|
|
2264
2368
|
if (jira.jiraState === "has_tickets") {
|
|
2265
2369
|
if (key.upArrow || input === "k") nav.prev();
|
|
2266
2370
|
if (key.downArrow || input === "j") nav.next();
|
|
@@ -2270,7 +2374,7 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2270
2374
|
if (input === "y") handleCopyLink();
|
|
2271
2375
|
}
|
|
2272
2376
|
},
|
|
2273
|
-
{ isActive:
|
|
2377
|
+
{ isActive: isActive && !modal.isOpen }
|
|
2274
2378
|
);
|
|
2275
2379
|
if (repo.isRepo === false) {
|
|
2276
2380
|
return /* @__PURE__ */ jsx11(TitledBox4, { borderStyle: "round", titles: ["Jira"], flexShrink: 0, children: /* @__PURE__ */ jsx11(Text11, { color: "red", children: "Not a git repository" }) });
|
|
@@ -2278,11 +2382,13 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2278
2382
|
if (modal.type === "configure") {
|
|
2279
2383
|
const siteUrl = repo.repoPath ? getJiraSiteUrl(repo.repoPath) : void 0;
|
|
2280
2384
|
const creds = repo.repoPath ? getJiraCredentials(repo.repoPath) : { email: null, apiToken: null };
|
|
2385
|
+
const existingConfigs = getExistingJiraConfigs(repo.repoPath ?? void 0);
|
|
2281
2386
|
return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx11(
|
|
2282
2387
|
ConfigureJiraSiteModal,
|
|
2283
2388
|
{
|
|
2284
2389
|
initialSiteUrl: siteUrl ?? void 0,
|
|
2285
2390
|
initialEmail: creds.email ?? void 0,
|
|
2391
|
+
existingConfigs,
|
|
2286
2392
|
onSubmit: handleConfigureSubmit,
|
|
2287
2393
|
onCancel: () => {
|
|
2288
2394
|
modal.close();
|
|
@@ -2307,21 +2413,20 @@ function JiraView({ isFocused, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2307
2413
|
}
|
|
2308
2414
|
) });
|
|
2309
2415
|
}
|
|
2310
|
-
if (modal.type === "status" && repo.repoPath && repo.currentBranch &&
|
|
2311
|
-
const ticket = jira.tickets[nav.index];
|
|
2416
|
+
if (modal.type === "status" && repo.repoPath && repo.currentBranch && currentTicket) {
|
|
2312
2417
|
return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx11(
|
|
2313
2418
|
ChangeStatusModal,
|
|
2314
2419
|
{
|
|
2315
2420
|
repoPath: repo.repoPath,
|
|
2316
|
-
ticketKey:
|
|
2317
|
-
currentStatus:
|
|
2421
|
+
ticketKey: currentTicket.key,
|
|
2422
|
+
currentStatus: currentTicket.status,
|
|
2318
2423
|
onComplete: handleStatusComplete,
|
|
2319
2424
|
onCancel: modal.close
|
|
2320
2425
|
}
|
|
2321
2426
|
) });
|
|
2322
2427
|
}
|
|
2323
2428
|
const title = "[4] Jira";
|
|
2324
|
-
const borderColor =
|
|
2429
|
+
const borderColor = isActive ? "yellow" : void 0;
|
|
2325
2430
|
return /* @__PURE__ */ jsx11(TitledBox4, { borderStyle: "round", titles: [title], borderColor, flexShrink: 0, children: /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
|
|
2326
2431
|
jira.jiraState === "not_configured" && /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "No Jira site configured" }),
|
|
2327
2432
|
jira.jiraState === "no_tickets" && /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "No tickets linked to this branch" }),
|
|
@@ -2343,10 +2448,10 @@ import { useEffect as useEffect12 } from "react";
|
|
|
2343
2448
|
import { Box as Box14, useInput as useInput12 } from "ink";
|
|
2344
2449
|
|
|
2345
2450
|
// src/components/logs/LogViewerBox.tsx
|
|
2346
|
-
import { useRef as useRef7, useState as
|
|
2451
|
+
import { useEffect as useEffect11, useRef as useRef7, useState as useState15 } from "react";
|
|
2347
2452
|
import { TitledBox as TitledBox5 } from "@mishieck/ink-titled-box";
|
|
2348
2453
|
import { Box as Box12, Text as Text12, useInput as useInput10 } from "ink";
|
|
2349
|
-
import { ScrollView as
|
|
2454
|
+
import { ScrollView as ScrollView5 } from "ink-scroll-view";
|
|
2350
2455
|
import TextInput2 from "ink-text-input";
|
|
2351
2456
|
|
|
2352
2457
|
// src/lib/claude/api.ts
|
|
@@ -2434,15 +2539,15 @@ Generate the standup notes:`;
|
|
|
2434
2539
|
|
|
2435
2540
|
// src/components/logs/LogViewerBox.tsx
|
|
2436
2541
|
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2437
|
-
function LogViewerBox({ date, content,
|
|
2542
|
+
function LogViewerBox({ date, content, isActive, onRefresh, onLogCreated }) {
|
|
2438
2543
|
const scrollRef = useRef7(null);
|
|
2439
|
-
const [isInputMode, setIsInputMode] =
|
|
2440
|
-
const [inputValue, setInputValue] =
|
|
2441
|
-
const [isGeneratingStandup, setIsGeneratingStandup] =
|
|
2442
|
-
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);
|
|
2443
2548
|
const claudeProcessRef = useRef7(null);
|
|
2444
2549
|
const title = "[6] Log Content";
|
|
2445
|
-
const borderColor =
|
|
2550
|
+
const borderColor = isActive ? "yellow" : void 0;
|
|
2446
2551
|
const displayTitle = date ? `${title} - ${date}.md` : title;
|
|
2447
2552
|
useInput10(
|
|
2448
2553
|
(input, key) => {
|
|
@@ -2504,8 +2609,14 @@ function LogViewerBox({ date, content, isFocused, onRefresh, onLogCreated }) {
|
|
|
2504
2609
|
});
|
|
2505
2610
|
}
|
|
2506
2611
|
},
|
|
2507
|
-
{ isActive
|
|
2612
|
+
{ isActive }
|
|
2508
2613
|
);
|
|
2614
|
+
useEffect11(() => {
|
|
2615
|
+
return () => {
|
|
2616
|
+
var _a;
|
|
2617
|
+
(_a = claudeProcessRef.current) == null ? void 0 : _a.cancel();
|
|
2618
|
+
};
|
|
2619
|
+
}, []);
|
|
2509
2620
|
const handleInputSubmit = (value) => {
|
|
2510
2621
|
if (!date || !value.trim()) {
|
|
2511
2622
|
setIsInputMode(false);
|
|
@@ -2524,7 +2635,7 @@ ${value.trim()}
|
|
|
2524
2635
|
onRefresh();
|
|
2525
2636
|
};
|
|
2526
2637
|
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", flexGrow: 1, children: [
|
|
2527
|
-
/* @__PURE__ */ jsx12(TitledBox5, { borderStyle: "round", titles: [displayTitle], borderColor, flexGrow: 1, children: /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx12(
|
|
2638
|
+
/* @__PURE__ */ jsx12(TitledBox5, { borderStyle: "round", titles: [displayTitle], borderColor, flexGrow: 1, children: /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx12(ScrollView5, { ref: scrollRef, children: /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, children: [
|
|
2528
2639
|
!date && /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Select a log file to view" }),
|
|
2529
2640
|
date && content === null && /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Log file not found" }),
|
|
2530
2641
|
date && content !== null && content.trim() === "" && /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Empty log file" }),
|
|
@@ -2560,7 +2671,7 @@ ${value.trim()}
|
|
|
2560
2671
|
// src/components/logs/LogsHistoryBox.tsx
|
|
2561
2672
|
import { TitledBox as TitledBox6 } from "@mishieck/ink-titled-box";
|
|
2562
2673
|
import { Box as Box13, Text as Text13, useInput as useInput11 } from "ink";
|
|
2563
|
-
import { ScrollView as
|
|
2674
|
+
import { ScrollView as ScrollView6 } from "ink-scroll-view";
|
|
2564
2675
|
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2565
2676
|
function LogsHistoryBox({
|
|
2566
2677
|
logFiles,
|
|
@@ -2568,11 +2679,11 @@ function LogsHistoryBox({
|
|
|
2568
2679
|
highlightedIndex,
|
|
2569
2680
|
onHighlight,
|
|
2570
2681
|
onSelect,
|
|
2571
|
-
|
|
2682
|
+
isActive
|
|
2572
2683
|
}) {
|
|
2573
2684
|
const scrollRef = useScrollToIndex(highlightedIndex);
|
|
2574
2685
|
const title = "[5] Logs";
|
|
2575
|
-
const borderColor =
|
|
2686
|
+
const borderColor = isActive ? "yellow" : void 0;
|
|
2576
2687
|
useInput11(
|
|
2577
2688
|
(input, key) => {
|
|
2578
2689
|
if (logFiles.length === 0) return;
|
|
@@ -2582,18 +2693,18 @@ function LogsHistoryBox({
|
|
|
2582
2693
|
if (key.downArrow || input === "j") {
|
|
2583
2694
|
onHighlight(Math.min(logFiles.length - 1, highlightedIndex + 1));
|
|
2584
2695
|
}
|
|
2585
|
-
if (
|
|
2696
|
+
if (input === " ") {
|
|
2586
2697
|
const file = logFiles[highlightedIndex];
|
|
2587
2698
|
if (file) {
|
|
2588
2699
|
onSelect(file.date);
|
|
2589
2700
|
}
|
|
2590
2701
|
}
|
|
2591
2702
|
},
|
|
2592
|
-
{ isActive
|
|
2703
|
+
{ isActive }
|
|
2593
2704
|
);
|
|
2594
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: [
|
|
2595
2706
|
logFiles.length === 0 && /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "No logs yet" }),
|
|
2596
|
-
logFiles.length > 0 && /* @__PURE__ */ jsx13(
|
|
2707
|
+
logFiles.length > 0 && /* @__PURE__ */ jsx13(ScrollView6, { ref: scrollRef, children: logFiles.map((file, idx) => {
|
|
2597
2708
|
const isHighlighted = idx === highlightedIndex;
|
|
2598
2709
|
const isSelected = file.date === selectedDate;
|
|
2599
2710
|
const cursor = isHighlighted ? ">" : " ";
|
|
@@ -2613,7 +2724,7 @@ function LogsHistoryBox({
|
|
|
2613
2724
|
|
|
2614
2725
|
// src/components/logs/LogsView.tsx
|
|
2615
2726
|
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2616
|
-
function LogsView({
|
|
2727
|
+
function LogsView({ isActive, refreshKey, focusedBox, onFocusedBoxChange }) {
|
|
2617
2728
|
const logs = useLogs();
|
|
2618
2729
|
useEffect12(() => {
|
|
2619
2730
|
if (refreshKey !== void 0 && refreshKey > 0) {
|
|
@@ -2625,7 +2736,7 @@ function LogsView({ isFocused, refreshKey, focusedBox, onFocusedBoxChange }) {
|
|
|
2625
2736
|
if (input === "5") onFocusedBoxChange("history");
|
|
2626
2737
|
if (input === "6") onFocusedBoxChange("viewer");
|
|
2627
2738
|
},
|
|
2628
|
-
{ isActive
|
|
2739
|
+
{ isActive }
|
|
2629
2740
|
);
|
|
2630
2741
|
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", flexGrow: 1, children: [
|
|
2631
2742
|
/* @__PURE__ */ jsx14(
|
|
@@ -2636,7 +2747,7 @@ function LogsView({ isFocused, refreshKey, focusedBox, onFocusedBoxChange }) {
|
|
|
2636
2747
|
highlightedIndex: logs.highlightedIndex,
|
|
2637
2748
|
onHighlight: logs.setHighlightedIndex,
|
|
2638
2749
|
onSelect: logs.selectDate,
|
|
2639
|
-
|
|
2750
|
+
isActive: isActive && focusedBox === "history"
|
|
2640
2751
|
}
|
|
2641
2752
|
),
|
|
2642
2753
|
/* @__PURE__ */ jsx14(
|
|
@@ -2644,7 +2755,7 @@ function LogsView({ isFocused, refreshKey, focusedBox, onFocusedBoxChange }) {
|
|
|
2644
2755
|
{
|
|
2645
2756
|
date: logs.selectedDate,
|
|
2646
2757
|
content: logs.logContent,
|
|
2647
|
-
|
|
2758
|
+
isActive: isActive && focusedBox === "viewer",
|
|
2648
2759
|
onRefresh: logs.refresh,
|
|
2649
2760
|
onLogCreated: logs.handleLogCreated
|
|
2650
2761
|
}
|
|
@@ -2695,19 +2806,23 @@ var GITHUB_KEYBINDINGS = {
|
|
|
2695
2806
|
// src/constants/jira.ts
|
|
2696
2807
|
var JIRA_KEYBINDINGS = {
|
|
2697
2808
|
not_configured: [{ key: "c", label: "Configure Jira" }],
|
|
2698
|
-
no_tickets: [
|
|
2809
|
+
no_tickets: [
|
|
2810
|
+
{ key: "l", label: "Link Ticket" },
|
|
2811
|
+
{ key: "r", label: "Remove Config", color: "red" }
|
|
2812
|
+
],
|
|
2699
2813
|
has_tickets: [
|
|
2700
2814
|
{ key: "l", label: "Link" },
|
|
2701
2815
|
{ key: "s", label: "Status" },
|
|
2702
2816
|
{ key: "d", label: "Unlink", color: "red" },
|
|
2703
2817
|
{ key: "o", label: "Open", color: "green" },
|
|
2704
|
-
{ key: "y", label: "Copy Link" }
|
|
2818
|
+
{ key: "y", label: "Copy Link" },
|
|
2819
|
+
{ key: "r", label: "Remove Config", color: "red" }
|
|
2705
2820
|
]
|
|
2706
2821
|
};
|
|
2707
2822
|
|
|
2708
2823
|
// src/constants/logs.ts
|
|
2709
2824
|
var LOGS_KEYBINDINGS = {
|
|
2710
|
-
history: [{ key: "
|
|
2825
|
+
history: [{ key: "Space", label: "Select" }],
|
|
2711
2826
|
viewer: [
|
|
2712
2827
|
{ key: "i", label: "Add Entry" },
|
|
2713
2828
|
{ key: "e", label: "Edit" },
|
|
@@ -2736,13 +2851,13 @@ function computeKeybindings(focusedView, state) {
|
|
|
2736
2851
|
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2737
2852
|
function App() {
|
|
2738
2853
|
const { exit } = useApp();
|
|
2739
|
-
const [focusedView, setFocusedView] =
|
|
2740
|
-
const [modalOpen, setModalOpen] =
|
|
2741
|
-
const [logRefreshKey, setLogRefreshKey] =
|
|
2854
|
+
const [focusedView, setFocusedView] = useState16("github");
|
|
2855
|
+
const [modalOpen, setModalOpen] = useState16(false);
|
|
2856
|
+
const [logRefreshKey, setLogRefreshKey] = useState16(0);
|
|
2742
2857
|
const duck = useRubberDuck();
|
|
2743
|
-
const [githubFocusedBox, setGithubFocusedBox] =
|
|
2744
|
-
const [jiraState, setJiraState] =
|
|
2745
|
-
const [logsFocusedBox, setLogsFocusedBox] =
|
|
2858
|
+
const [githubFocusedBox, setGithubFocusedBox] = useState16("remotes");
|
|
2859
|
+
const [jiraState, setJiraState] = useState16("not_configured");
|
|
2860
|
+
const [logsFocusedBox, setLogsFocusedBox] = useState16("history");
|
|
2746
2861
|
const keybindings = useMemo2(
|
|
2747
2862
|
() => computeKeybindings(focusedView, {
|
|
2748
2863
|
github: { focusedBox: githubFocusedBox },
|
|
@@ -2751,7 +2866,7 @@ function App() {
|
|
|
2751
2866
|
}),
|
|
2752
2867
|
[focusedView, githubFocusedBox, jiraState, modalOpen, logsFocusedBox]
|
|
2753
2868
|
);
|
|
2754
|
-
const handleLogUpdated =
|
|
2869
|
+
const handleLogUpdated = useCallback9(() => {
|
|
2755
2870
|
setLogRefreshKey((prev) => prev + 1);
|
|
2756
2871
|
}, []);
|
|
2757
2872
|
useInput13(
|
|
@@ -2788,7 +2903,7 @@ function App() {
|
|
|
2788
2903
|
/* @__PURE__ */ jsx16(
|
|
2789
2904
|
GitHubView,
|
|
2790
2905
|
{
|
|
2791
|
-
|
|
2906
|
+
isActive: focusedView === "github",
|
|
2792
2907
|
onFocusedBoxChange: setGithubFocusedBox,
|
|
2793
2908
|
onLogUpdated: handleLogUpdated
|
|
2794
2909
|
}
|
|
@@ -2796,7 +2911,7 @@ function App() {
|
|
|
2796
2911
|
/* @__PURE__ */ jsx16(
|
|
2797
2912
|
JiraView,
|
|
2798
2913
|
{
|
|
2799
|
-
|
|
2914
|
+
isActive: focusedView === "jira",
|
|
2800
2915
|
onModalChange: setModalOpen,
|
|
2801
2916
|
onJiraStateChange: setJiraState,
|
|
2802
2917
|
onLogUpdated: handleLogUpdated
|
|
@@ -2806,7 +2921,7 @@ function App() {
|
|
|
2806
2921
|
/* @__PURE__ */ jsx16(Box16, { flexDirection: "column", flexGrow: 1, flexBasis: 0, children: /* @__PURE__ */ jsx16(
|
|
2807
2922
|
LogsView,
|
|
2808
2923
|
{
|
|
2809
|
-
|
|
2924
|
+
isActive: focusedView === "logs",
|
|
2810
2925
|
refreshKey: logRefreshKey,
|
|
2811
2926
|
focusedBox: logsFocusedBox,
|
|
2812
2927
|
onFocusedBoxChange: setLogsFocusedBox
|
|
@@ -2828,13 +2943,13 @@ function App() {
|
|
|
2828
2943
|
import { render as inkRender } from "ink";
|
|
2829
2944
|
|
|
2830
2945
|
// src/lib/Screen.tsx
|
|
2831
|
-
import { useCallback as
|
|
2946
|
+
import { useCallback as useCallback10, useEffect as useEffect13, useState as useState17 } from "react";
|
|
2832
2947
|
import { Box as Box17, useStdout as useStdout2 } from "ink";
|
|
2833
2948
|
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2834
2949
|
function Screen({ children }) {
|
|
2835
2950
|
const { stdout } = useStdout2();
|
|
2836
|
-
const getSize =
|
|
2837
|
-
const [size, setSize] =
|
|
2951
|
+
const getSize = useCallback10(() => ({ height: stdout.rows, width: stdout.columns }), [stdout]);
|
|
2952
|
+
const [size, setSize] = useState17(getSize);
|
|
2838
2953
|
useEffect13(() => {
|
|
2839
2954
|
const onResize = () => setSize(getSize());
|
|
2840
2955
|
stdout.on("resize", onResize);
|