clairo 1.2.3 → 2.0.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/README.md +40 -5
- package/dist/cli.js +1612 -246
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import meow from "meow";
|
|
5
5
|
|
|
6
6
|
// src/app.tsx
|
|
7
|
-
import { useCallback as
|
|
8
|
-
import { Box as
|
|
7
|
+
import { useCallback as useCallback11, useMemo as useMemo4, useState as useState21 } from "react";
|
|
8
|
+
import { Box as Box22, Text as Text19, useApp, useInput as useInput18 } from "ink";
|
|
9
9
|
|
|
10
10
|
// src/components/github/GitHubView.tsx
|
|
11
11
|
import { useCallback as useCallback8, useEffect as useEffect8, useRef as useRef5, useState as useState11 } from "react";
|
|
@@ -866,7 +866,8 @@ function createAuthHeader(email, apiToken) {
|
|
|
866
866
|
return `Basic ${credentials}`;
|
|
867
867
|
}
|
|
868
868
|
async function jiraFetch(auth, endpoint, options) {
|
|
869
|
-
const
|
|
869
|
+
const prefix = (options == null ? void 0 : options.apiPrefix) ?? "/rest/api/3";
|
|
870
|
+
const url = `${auth.siteUrl}${prefix}${endpoint}`;
|
|
870
871
|
const method = (options == null ? void 0 : options.method) ?? "GET";
|
|
871
872
|
try {
|
|
872
873
|
const headers = {
|
|
@@ -911,6 +912,21 @@ async function validateCredentials(auth) {
|
|
|
911
912
|
}
|
|
912
913
|
return { success: true, data: result.data };
|
|
913
914
|
}
|
|
915
|
+
async function getCurrentUser(auth) {
|
|
916
|
+
const result = await jiraFetch(auth, "/myself");
|
|
917
|
+
if (!result.ok) {
|
|
918
|
+
if (result.status === 401 || result.status === 403) {
|
|
919
|
+
return { success: false, error: "Authentication failed", errorType: "auth_error" };
|
|
920
|
+
}
|
|
921
|
+
return {
|
|
922
|
+
success: false,
|
|
923
|
+
error: result.error ?? "Failed to fetch current user",
|
|
924
|
+
errorType: "api_error"
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
const data = result.data;
|
|
928
|
+
return { success: true, data: { accountId: data.accountId, displayName: data.displayName } };
|
|
929
|
+
}
|
|
914
930
|
async function getIssue(auth, ticketKey) {
|
|
915
931
|
const result = await jiraFetch(auth, `/issue/${ticketKey}?fields=summary,status`);
|
|
916
932
|
if (!result.ok) {
|
|
@@ -990,6 +1006,377 @@ async function applyTransition(auth, ticketKey, transitionId) {
|
|
|
990
1006
|
}
|
|
991
1007
|
return { success: true, data: null };
|
|
992
1008
|
}
|
|
1009
|
+
async function getIssueDetail(auth, issueKey) {
|
|
1010
|
+
const result = await jiraFetch(auth, `/issue/${issueKey}?fields=summary,status,description,assignee,comment`);
|
|
1011
|
+
if (!result.ok) {
|
|
1012
|
+
if (result.status === 401 || result.status === 403) {
|
|
1013
|
+
return { success: false, error: "Authentication failed", errorType: "auth_error" };
|
|
1014
|
+
}
|
|
1015
|
+
if (result.status === 404) {
|
|
1016
|
+
return { success: false, error: `Issue ${issueKey} not found`, errorType: "invalid_ticket" };
|
|
1017
|
+
}
|
|
1018
|
+
return {
|
|
1019
|
+
success: false,
|
|
1020
|
+
error: result.error ?? "Failed to fetch issue details",
|
|
1021
|
+
errorType: "api_error"
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
return { success: true, data: result.data };
|
|
1025
|
+
}
|
|
1026
|
+
async function assignIssue(auth, issueKey, accountId) {
|
|
1027
|
+
const result = await jiraFetch(auth, `/issue/${issueKey}/assignee`, {
|
|
1028
|
+
method: "PUT",
|
|
1029
|
+
body: { accountId }
|
|
1030
|
+
});
|
|
1031
|
+
if (!result.ok) {
|
|
1032
|
+
if (result.status === 401 || result.status === 403) {
|
|
1033
|
+
return { success: false, error: "Authentication failed", errorType: "auth_error" };
|
|
1034
|
+
}
|
|
1035
|
+
if (result.status === 404) {
|
|
1036
|
+
return { success: false, error: `Issue ${issueKey} not found`, errorType: "invalid_ticket" };
|
|
1037
|
+
}
|
|
1038
|
+
return {
|
|
1039
|
+
success: false,
|
|
1040
|
+
error: result.error ?? "Failed to assign issue",
|
|
1041
|
+
errorType: "api_error"
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
return { success: true, data: null };
|
|
1045
|
+
}
|
|
1046
|
+
async function unassignIssue(auth, issueKey) {
|
|
1047
|
+
const result = await jiraFetch(auth, `/issue/${issueKey}/assignee`, {
|
|
1048
|
+
method: "PUT",
|
|
1049
|
+
body: { accountId: null }
|
|
1050
|
+
});
|
|
1051
|
+
if (!result.ok) {
|
|
1052
|
+
if (result.status === 401 || result.status === 403) {
|
|
1053
|
+
return { success: false, error: "Authentication failed", errorType: "auth_error" };
|
|
1054
|
+
}
|
|
1055
|
+
if (result.status === 404) {
|
|
1056
|
+
return { success: false, error: `Issue ${issueKey} not found`, errorType: "invalid_ticket" };
|
|
1057
|
+
}
|
|
1058
|
+
return {
|
|
1059
|
+
success: false,
|
|
1060
|
+
error: result.error ?? "Failed to unassign issue",
|
|
1061
|
+
errorType: "api_error"
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
return { success: true, data: null };
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/lib/jira/search.ts
|
|
1068
|
+
async function searchIssues(auth, jql, opts) {
|
|
1069
|
+
const params = new URLSearchParams({
|
|
1070
|
+
jql,
|
|
1071
|
+
fields: "summary,status,assignee,priority,issuetype,sprint,closedSprints",
|
|
1072
|
+
startAt: String((opts == null ? void 0 : opts.startAt) ?? 0),
|
|
1073
|
+
maxResults: String((opts == null ? void 0 : opts.maxResults) ?? 50)
|
|
1074
|
+
});
|
|
1075
|
+
const result = await jiraFetch(auth, `/search?${params.toString()}`);
|
|
1076
|
+
if (!result.ok) {
|
|
1077
|
+
if (result.status === 401 || result.status === 403) {
|
|
1078
|
+
return { success: false, error: "Authentication failed", errorType: "auth_error" };
|
|
1079
|
+
}
|
|
1080
|
+
return {
|
|
1081
|
+
success: false,
|
|
1082
|
+
error: result.error ?? "Failed to search issues",
|
|
1083
|
+
errorType: "api_error"
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
return { success: true, data: result.data };
|
|
1087
|
+
}
|
|
1088
|
+
async function getFilterJql(auth, filterId) {
|
|
1089
|
+
const result = await jiraFetch(auth, `/filter/${filterId}`);
|
|
1090
|
+
if (!result.ok) {
|
|
1091
|
+
if (result.status === 401 || result.status === 403) {
|
|
1092
|
+
return { success: false, error: "Authentication failed", errorType: "auth_error" };
|
|
1093
|
+
}
|
|
1094
|
+
if (result.status === 404) {
|
|
1095
|
+
return { success: false, error: `Filter ${filterId} not found`, errorType: "api_error" };
|
|
1096
|
+
}
|
|
1097
|
+
return {
|
|
1098
|
+
success: false,
|
|
1099
|
+
error: result.error ?? "Failed to fetch filter",
|
|
1100
|
+
errorType: "api_error"
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
const data = result.data;
|
|
1104
|
+
return { success: true, data: data.jql };
|
|
1105
|
+
}
|
|
1106
|
+
async function getBoardIssues(auth, boardId, opts) {
|
|
1107
|
+
const params = new URLSearchParams({
|
|
1108
|
+
fields: "summary,status,assignee,priority,issuetype,sprint,closedSprints",
|
|
1109
|
+
startAt: String((opts == null ? void 0 : opts.startAt) ?? 0),
|
|
1110
|
+
maxResults: String((opts == null ? void 0 : opts.maxResults) ?? 50)
|
|
1111
|
+
});
|
|
1112
|
+
if (opts == null ? void 0 : opts.jql) {
|
|
1113
|
+
params.set("jql", opts.jql);
|
|
1114
|
+
}
|
|
1115
|
+
const result = await jiraFetch(auth, `/board/${boardId}/issue?${params.toString()}`, {
|
|
1116
|
+
apiPrefix: "/rest/agile/1.0"
|
|
1117
|
+
});
|
|
1118
|
+
if (!result.ok) {
|
|
1119
|
+
if (result.status === 401 || result.status === 403) {
|
|
1120
|
+
return { success: false, error: "Authentication failed", errorType: "auth_error" };
|
|
1121
|
+
}
|
|
1122
|
+
if (result.status === 404) {
|
|
1123
|
+
return { success: false, error: `Board ${boardId} not found`, errorType: "api_error" };
|
|
1124
|
+
}
|
|
1125
|
+
return {
|
|
1126
|
+
success: false,
|
|
1127
|
+
error: result.error ?? "Failed to fetch board issues",
|
|
1128
|
+
errorType: "api_error"
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
return { success: true, data: result.data };
|
|
1132
|
+
}
|
|
1133
|
+
function escapeJql(text) {
|
|
1134
|
+
return text.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1135
|
+
}
|
|
1136
|
+
function appendTextSearch(jql, searchText) {
|
|
1137
|
+
const escaped = escapeJql(searchText);
|
|
1138
|
+
return `(${jql}) AND text ~ "${escaped}"`;
|
|
1139
|
+
}
|
|
1140
|
+
async function fetchViewIssues(auth, view, opts) {
|
|
1141
|
+
const { searchText, ...pageOpts } = opts ?? {};
|
|
1142
|
+
switch (view.source.type) {
|
|
1143
|
+
case "jql": {
|
|
1144
|
+
const jql = searchText ? appendTextSearch(view.source.jql, searchText) : view.source.jql;
|
|
1145
|
+
return searchIssues(auth, jql, pageOpts);
|
|
1146
|
+
}
|
|
1147
|
+
case "filter": {
|
|
1148
|
+
const filterResult = await getFilterJql(auth, view.source.filterId);
|
|
1149
|
+
if (!filterResult.success) return filterResult;
|
|
1150
|
+
const jql = searchText ? appendTextSearch(filterResult.data, searchText) : filterResult.data;
|
|
1151
|
+
return searchIssues(auth, jql, pageOpts);
|
|
1152
|
+
}
|
|
1153
|
+
case "board": {
|
|
1154
|
+
const jql = searchText ? `text ~ "${escapeJql(searchText)}"` : void 0;
|
|
1155
|
+
return getBoardIssues(auth, view.source.boardId, { ...pageOpts, jql });
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// src/lib/jira/views.ts
|
|
1161
|
+
function getSavedViews(repoPath) {
|
|
1162
|
+
const config = getRepoConfig(repoPath);
|
|
1163
|
+
return config.savedJiraViews ?? [];
|
|
1164
|
+
}
|
|
1165
|
+
function addSavedView(repoPath, name, url, source) {
|
|
1166
|
+
const views = getSavedViews(repoPath);
|
|
1167
|
+
const view = {
|
|
1168
|
+
id: Date.now().toString(36),
|
|
1169
|
+
name,
|
|
1170
|
+
url,
|
|
1171
|
+
source,
|
|
1172
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1173
|
+
};
|
|
1174
|
+
updateRepoConfig(repoPath, { savedJiraViews: [...views, view] });
|
|
1175
|
+
return view;
|
|
1176
|
+
}
|
|
1177
|
+
function renameSavedView(repoPath, viewId, newName) {
|
|
1178
|
+
const views = getSavedViews(repoPath);
|
|
1179
|
+
updateRepoConfig(repoPath, {
|
|
1180
|
+
savedJiraViews: views.map((v) => v.id === viewId ? { ...v, name: newName } : v)
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
function removeSavedView(repoPath, viewId) {
|
|
1184
|
+
const views = getSavedViews(repoPath);
|
|
1185
|
+
updateRepoConfig(repoPath, {
|
|
1186
|
+
savedJiraViews: views.filter((v) => v.id !== viewId)
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// src/lib/jira/url-parser.ts
|
|
1191
|
+
function parseJiraUrl(input) {
|
|
1192
|
+
const trimmed = input.trim();
|
|
1193
|
+
let url;
|
|
1194
|
+
try {
|
|
1195
|
+
url = new URL(trimmed);
|
|
1196
|
+
} catch {
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
const filterId = url.searchParams.get("filter");
|
|
1200
|
+
if (filterId && /^\d+$/.test(filterId)) {
|
|
1201
|
+
return { type: "filter", filterId };
|
|
1202
|
+
}
|
|
1203
|
+
const jql = url.searchParams.get("jql");
|
|
1204
|
+
if (jql) {
|
|
1205
|
+
return { type: "jql", jql };
|
|
1206
|
+
}
|
|
1207
|
+
const boardMatch = url.pathname.match(/\/boards\/(\d+)/);
|
|
1208
|
+
if (boardMatch) {
|
|
1209
|
+
return { type: "board", boardId: boardMatch[1] };
|
|
1210
|
+
}
|
|
1211
|
+
return null;
|
|
1212
|
+
}
|
|
1213
|
+
function generateViewName(input) {
|
|
1214
|
+
const source = parseJiraUrl(input);
|
|
1215
|
+
if (!source) return "Jira View";
|
|
1216
|
+
switch (source.type) {
|
|
1217
|
+
case "filter":
|
|
1218
|
+
return `Filter #${source.filterId}`;
|
|
1219
|
+
case "jql":
|
|
1220
|
+
return truncate(source.jql, 40);
|
|
1221
|
+
case "board":
|
|
1222
|
+
return `Board #${source.boardId}`;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
function truncate(str, maxLen) {
|
|
1226
|
+
if (str.length <= maxLen) return str;
|
|
1227
|
+
return str.slice(0, maxLen - 1) + "\u2026";
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// src/lib/jira/adf-to-markdown.ts
|
|
1231
|
+
function adfToMarkdown(node) {
|
|
1232
|
+
if (!node || typeof node !== "object") return "";
|
|
1233
|
+
return processNode(node).trimEnd();
|
|
1234
|
+
}
|
|
1235
|
+
function processNode(node, indent = "") {
|
|
1236
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1237
|
+
switch (node.type) {
|
|
1238
|
+
case "doc":
|
|
1239
|
+
return processChildren(node, indent);
|
|
1240
|
+
case "paragraph":
|
|
1241
|
+
return processChildren(node, indent) + "\n\n";
|
|
1242
|
+
case "heading": {
|
|
1243
|
+
const level = ((_a = node.attrs) == null ? void 0 : _a.level) ?? 1;
|
|
1244
|
+
const prefix = "#".repeat(level);
|
|
1245
|
+
return `${prefix} ${processChildren(node, indent)}
|
|
1246
|
+
|
|
1247
|
+
`;
|
|
1248
|
+
}
|
|
1249
|
+
case "text":
|
|
1250
|
+
return applyMarks(node.text ?? "", node.marks);
|
|
1251
|
+
case "hardBreak":
|
|
1252
|
+
return "\n";
|
|
1253
|
+
case "bulletList":
|
|
1254
|
+
return processList(node, indent, "bullet") + "\n";
|
|
1255
|
+
case "orderedList":
|
|
1256
|
+
return processList(node, indent, "ordered") + "\n";
|
|
1257
|
+
case "listItem":
|
|
1258
|
+
return processChildren(node, indent);
|
|
1259
|
+
case "codeBlock": {
|
|
1260
|
+
const lang = ((_b = node.attrs) == null ? void 0 : _b.language) ?? "";
|
|
1261
|
+
const code = processChildren(node, indent);
|
|
1262
|
+
return `\`\`\`${lang}
|
|
1263
|
+
${code}
|
|
1264
|
+
\`\`\`
|
|
1265
|
+
|
|
1266
|
+
`;
|
|
1267
|
+
}
|
|
1268
|
+
case "blockquote": {
|
|
1269
|
+
const content = processChildren(node, indent);
|
|
1270
|
+
return content.split("\n").map((line) => `> ${line}`).join("\n") + "\n\n";
|
|
1271
|
+
}
|
|
1272
|
+
case "rule":
|
|
1273
|
+
return "---\n\n";
|
|
1274
|
+
case "mention":
|
|
1275
|
+
return `@${((_c = node.attrs) == null ? void 0 : _c.text) ?? "unknown"}`;
|
|
1276
|
+
case "emoji":
|
|
1277
|
+
return ((_d = node.attrs) == null ? void 0 : _d.shortName) ?? ((_e = node.attrs) == null ? void 0 : _e.text) ?? "";
|
|
1278
|
+
case "inlineCard": {
|
|
1279
|
+
const url = (_f = node.attrs) == null ? void 0 : _f.url;
|
|
1280
|
+
return url ? `[${url}](${url})` : "";
|
|
1281
|
+
}
|
|
1282
|
+
case "mediaSingle":
|
|
1283
|
+
case "mediaGroup":
|
|
1284
|
+
return processChildren(node, indent);
|
|
1285
|
+
case "media":
|
|
1286
|
+
return "[Attachment]\n\n";
|
|
1287
|
+
case "table":
|
|
1288
|
+
return processTable(node) + "\n";
|
|
1289
|
+
case "panel": {
|
|
1290
|
+
const panelType = ((_g = node.attrs) == null ? void 0 : _g.panelType) ?? "info";
|
|
1291
|
+
const label = panelType.charAt(0).toUpperCase() + panelType.slice(1);
|
|
1292
|
+
return `[${label}] ${processChildren(node, indent)}
|
|
1293
|
+
`;
|
|
1294
|
+
}
|
|
1295
|
+
default:
|
|
1296
|
+
if (node.content) {
|
|
1297
|
+
return processChildren(node, indent);
|
|
1298
|
+
}
|
|
1299
|
+
return node.text ?? "";
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function processChildren(node, indent = "") {
|
|
1303
|
+
if (!node.content) return "";
|
|
1304
|
+
return node.content.map((child) => processNode(child, indent)).join("");
|
|
1305
|
+
}
|
|
1306
|
+
function processList(node, indent, style) {
|
|
1307
|
+
if (!node.content) return "";
|
|
1308
|
+
return node.content.map((item, idx) => {
|
|
1309
|
+
const prefix = style === "bullet" ? "- " : `${idx + 1}. `;
|
|
1310
|
+
const childContent = (item.content ?? []).map((child) => {
|
|
1311
|
+
if (child.type === "bulletList" || child.type === "orderedList") {
|
|
1312
|
+
return processList(child, indent + " ", child.type === "bulletList" ? "bullet" : "ordered");
|
|
1313
|
+
}
|
|
1314
|
+
if (child.type === "paragraph") {
|
|
1315
|
+
return processChildren(child, indent);
|
|
1316
|
+
}
|
|
1317
|
+
return processNode(child, indent + " ");
|
|
1318
|
+
}).join("");
|
|
1319
|
+
return `${indent}${prefix}${childContent}`;
|
|
1320
|
+
}).join("\n");
|
|
1321
|
+
}
|
|
1322
|
+
function processTable(node) {
|
|
1323
|
+
if (!node.content) return "";
|
|
1324
|
+
const rows = [];
|
|
1325
|
+
let hasHeader = false;
|
|
1326
|
+
for (const row of node.content) {
|
|
1327
|
+
if (row.type !== "tableRow") continue;
|
|
1328
|
+
const cells = [];
|
|
1329
|
+
for (const cell of row.content ?? []) {
|
|
1330
|
+
if (cell.type === "tableHeader") hasHeader = true;
|
|
1331
|
+
const text = processChildren(cell).replace(/\n/g, " ").trim();
|
|
1332
|
+
cells.push(text);
|
|
1333
|
+
}
|
|
1334
|
+
rows.push(cells);
|
|
1335
|
+
}
|
|
1336
|
+
if (rows.length === 0) return "";
|
|
1337
|
+
const colCount = Math.max(...rows.map((r) => r.length));
|
|
1338
|
+
const lines = [];
|
|
1339
|
+
for (let i = 0; i < rows.length; i++) {
|
|
1340
|
+
const row = rows[i];
|
|
1341
|
+
while (row.length < colCount) row.push("");
|
|
1342
|
+
lines.push(`| ${row.join(" | ")} |`);
|
|
1343
|
+
if (i === 0 && hasHeader) {
|
|
1344
|
+
lines.push(`| ${Array(colCount).fill("---").join(" | ")} |`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
return lines.join("\n") + "\n";
|
|
1348
|
+
}
|
|
1349
|
+
function applyMarks(text, marks) {
|
|
1350
|
+
var _a;
|
|
1351
|
+
if (!marks || marks.length === 0) return text;
|
|
1352
|
+
let result = text;
|
|
1353
|
+
for (const mark of marks) {
|
|
1354
|
+
switch (mark.type) {
|
|
1355
|
+
case "strong":
|
|
1356
|
+
result = `**${result}**`;
|
|
1357
|
+
break;
|
|
1358
|
+
case "em":
|
|
1359
|
+
result = `*${result}*`;
|
|
1360
|
+
break;
|
|
1361
|
+
case "code":
|
|
1362
|
+
result = `\`${result}\``;
|
|
1363
|
+
break;
|
|
1364
|
+
case "strike":
|
|
1365
|
+
result = `~~${result}~~`;
|
|
1366
|
+
break;
|
|
1367
|
+
case "link": {
|
|
1368
|
+
const href = (_a = mark.attrs) == null ? void 0 : _a.href;
|
|
1369
|
+
if (href) {
|
|
1370
|
+
result = `[${result}](${href})`;
|
|
1371
|
+
}
|
|
1372
|
+
break;
|
|
1373
|
+
}
|
|
1374
|
+
case "underline":
|
|
1375
|
+
break;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return result;
|
|
1379
|
+
}
|
|
993
1380
|
|
|
994
1381
|
// src/lib/logs/index.ts
|
|
995
1382
|
import { spawnSync } from "child_process";
|
|
@@ -1116,6 +1503,18 @@ function logJiraStatusChanged(ticketKey, ticketName, oldStatus, newStatus) {
|
|
|
1116
1503
|
${ticketKey}: ${ticketName}
|
|
1117
1504
|
${oldStatus} \u2192 ${newStatus}
|
|
1118
1505
|
|
|
1506
|
+
`;
|
|
1507
|
+
appendToLog(today, entry);
|
|
1508
|
+
}
|
|
1509
|
+
function logJiraAssigneeChanged(ticketKey, ticketName, action, displayName) {
|
|
1510
|
+
const timestamp = formatTimestamp();
|
|
1511
|
+
const today = getTodayDate();
|
|
1512
|
+
const detail = action === "assigned" && displayName ? `Assigned to ${displayName}` : "Unassigned";
|
|
1513
|
+
const entry = `## ${timestamp} - Updated Jira ticket
|
|
1514
|
+
|
|
1515
|
+
${ticketKey}: ${ticketName}
|
|
1516
|
+
${detail}
|
|
1517
|
+
|
|
1119
1518
|
`;
|
|
1120
1519
|
appendToLog(today, entry);
|
|
1121
1520
|
}
|
|
@@ -1659,13 +2058,13 @@ function useLogs() {
|
|
|
1659
2058
|
import { useCallback as useCallback6, useState as useState7 } from "react";
|
|
1660
2059
|
function useModal() {
|
|
1661
2060
|
const [modalType, setModalType] = useState7("none");
|
|
1662
|
-
const
|
|
2061
|
+
const open6 = useCallback6((type) => setModalType(type), []);
|
|
1663
2062
|
const close = useCallback6(() => setModalType("none"), []);
|
|
1664
2063
|
const isOpen = modalType !== "none";
|
|
1665
2064
|
return {
|
|
1666
2065
|
type: modalType,
|
|
1667
2066
|
isOpen,
|
|
1668
|
-
open:
|
|
2067
|
+
open: open6,
|
|
1669
2068
|
close
|
|
1670
2069
|
};
|
|
1671
2070
|
}
|
|
@@ -1771,7 +2170,9 @@ var REACTION_MESSAGES = {
|
|
|
1771
2170
|
error: ["Uh oh...", "There there...", "*concerned quacking*", "Quack... not good."],
|
|
1772
2171
|
"jira:transition": ["Ticket moving!", "Progress!", "Workflow in motion!"],
|
|
1773
2172
|
"jira:linked": ["Ticket linked!", "Jira connection made!", "Tracking enabled!"],
|
|
1774
|
-
"jira:configured": ["Jira ready!", "Integration complete!", "Connected to Jira!"]
|
|
2173
|
+
"jira:configured": ["Jira ready!", "Integration complete!", "Connected to Jira!"],
|
|
2174
|
+
"jira:assigned": ["Assigned!", "On it!", "Claimed!"],
|
|
2175
|
+
"jira:unassigned": ["Unassigned!", "Free agent!", "Released!"]
|
|
1775
2176
|
};
|
|
1776
2177
|
function useRubberDuck() {
|
|
1777
2178
|
const [state, setState] = useState9({
|
|
@@ -2244,11 +2645,11 @@ ${body}`;
|
|
|
2244
2645
|
] });
|
|
2245
2646
|
}
|
|
2246
2647
|
|
|
2247
|
-
// src/components/jira/
|
|
2248
|
-
import
|
|
2249
|
-
import {
|
|
2648
|
+
// src/components/jira-browser/JiraBrowserView.tsx
|
|
2649
|
+
import { useCallback as useCallback10, useEffect as useEffect12, useMemo as useMemo3, useRef as useRef7, useState as useState16 } from "react";
|
|
2650
|
+
import { Box as Box12, useInput as useInput10 } from "ink";
|
|
2250
2651
|
|
|
2251
|
-
// src/components/jira/
|
|
2652
|
+
// src/components/jira-browser/AddViewModal.tsx
|
|
2252
2653
|
import { useState as useState12 } from "react";
|
|
2253
2654
|
import { Box as Box8, Text as Text7, useInput as useInput6 } from "ink";
|
|
2254
2655
|
|
|
@@ -2267,8 +2668,11 @@ function TextInput({ value, onChange, placeholder, isActive, mask }) {
|
|
|
2267
2668
|
if (key.return || key.escape || key.upArrow || key.downArrow || key.tab) {
|
|
2268
2669
|
return;
|
|
2269
2670
|
}
|
|
2270
|
-
if (input && input.length
|
|
2271
|
-
|
|
2671
|
+
if (input && input.length > 0) {
|
|
2672
|
+
const printable = input.replace(/[^\x20-\x7E\u00A0-\uFFFF]/g, "");
|
|
2673
|
+
if (printable.length > 0) {
|
|
2674
|
+
onChange(value + printable);
|
|
2675
|
+
}
|
|
2272
2676
|
}
|
|
2273
2677
|
},
|
|
2274
2678
|
{ isActive }
|
|
@@ -2281,11 +2685,13 @@ function TextInput({ value, onChange, placeholder, isActive, mask }) {
|
|
|
2281
2685
|
] }) });
|
|
2282
2686
|
}
|
|
2283
2687
|
|
|
2284
|
-
// src/components/jira/
|
|
2688
|
+
// src/components/jira-browser/AddViewModal.tsx
|
|
2285
2689
|
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2286
|
-
function
|
|
2287
|
-
const [
|
|
2288
|
-
const
|
|
2690
|
+
function AddViewModal({ onSubmit, onCancel, loading, error }) {
|
|
2691
|
+
const [url, setUrl] = useState12("");
|
|
2692
|
+
const [name, setName] = useState12("");
|
|
2693
|
+
const [activeField, setActiveField] = useState12("url");
|
|
2694
|
+
const canSubmit = url.trim().length > 0;
|
|
2289
2695
|
useInput6(
|
|
2290
2696
|
(_input, key) => {
|
|
2291
2697
|
if (loading) return;
|
|
@@ -2293,120 +2699,1014 @@ function LinkTicketModal({ onSubmit, onCancel, loading, error }) {
|
|
|
2293
2699
|
onCancel();
|
|
2294
2700
|
return;
|
|
2295
2701
|
}
|
|
2702
|
+
if (key.tab) {
|
|
2703
|
+
setActiveField((f) => f === "url" ? "name" : "url");
|
|
2704
|
+
return;
|
|
2705
|
+
}
|
|
2296
2706
|
if (key.return && canSubmit) {
|
|
2297
|
-
|
|
2707
|
+
const viewName = name.trim() || generateViewName(url.trim());
|
|
2708
|
+
onSubmit(url.trim(), viewName);
|
|
2298
2709
|
}
|
|
2299
2710
|
},
|
|
2300
2711
|
{ isActive: !loading }
|
|
2301
2712
|
);
|
|
2302
2713
|
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, paddingY: 1, children: [
|
|
2303
|
-
/* @__PURE__ */ jsx8(Text7, { bold: true, color: "yellow", children: "
|
|
2304
|
-
/* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "
|
|
2714
|
+
/* @__PURE__ */ jsx8(Text7, { bold: true, color: "yellow", children: "Add Jira View" }),
|
|
2715
|
+
/* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "Tab to switch fields, Enter to save, Esc to cancel" }),
|
|
2305
2716
|
/* @__PURE__ */ jsx8(Box8, { marginTop: 1 }),
|
|
2306
2717
|
error && /* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsx8(Text7, { color: "red", children: error }) }),
|
|
2307
2718
|
/* @__PURE__ */ jsxs7(Box8, { children: [
|
|
2308
|
-
/* @__PURE__ */ jsx8(Text7, { color: "blue", children: "
|
|
2309
|
-
/* @__PURE__ */ jsx8(
|
|
2719
|
+
/* @__PURE__ */ jsx8(Text7, { color: "blue", children: "URL: " }),
|
|
2720
|
+
/* @__PURE__ */ jsx8(
|
|
2721
|
+
TextInput,
|
|
2722
|
+
{
|
|
2723
|
+
value: url,
|
|
2724
|
+
onChange: setUrl,
|
|
2725
|
+
placeholder: "https://company.atlassian.net/issues/?filter=12345",
|
|
2726
|
+
isActive: !loading && activeField === "url"
|
|
2727
|
+
}
|
|
2728
|
+
)
|
|
2729
|
+
] }),
|
|
2730
|
+
/* @__PURE__ */ jsxs7(Box8, { marginTop: 1, children: [
|
|
2731
|
+
/* @__PURE__ */ jsx8(Text7, { color: "blue", children: "Name: " }),
|
|
2732
|
+
/* @__PURE__ */ jsx8(
|
|
2733
|
+
TextInput,
|
|
2734
|
+
{
|
|
2735
|
+
value: name,
|
|
2736
|
+
onChange: setName,
|
|
2737
|
+
placeholder: "(auto-generated from URL)",
|
|
2738
|
+
isActive: !loading && activeField === "name"
|
|
2739
|
+
}
|
|
2740
|
+
)
|
|
2310
2741
|
] }),
|
|
2311
|
-
loading && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { color: "yellow", children: "
|
|
2312
|
-
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "
|
|
2742
|
+
loading && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { color: "yellow", children: "Validating view..." }) }),
|
|
2743
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "Supports: filter URLs, JQL URLs, board URLs" }) })
|
|
2313
2744
|
] });
|
|
2314
2745
|
}
|
|
2315
2746
|
|
|
2316
|
-
// src/components/jira/
|
|
2747
|
+
// src/components/jira-browser/JiraSavedViewBrowserBox.tsx
|
|
2748
|
+
import open4 from "open";
|
|
2749
|
+
import { useCallback as useCallback9, useEffect as useEffect10, useMemo as useMemo2, useState as useState14 } from "react";
|
|
2317
2750
|
import { TitledBox as TitledBox4 } from "@mishieck/ink-titled-box";
|
|
2318
|
-
import { Box as
|
|
2751
|
+
import { Box as Box10, Text as Text9, useInput as useInput8 } from "ink";
|
|
2752
|
+
import { ScrollView as ScrollView6 } from "ink-scroll-view";
|
|
2753
|
+
import Spinner3 from "ink-spinner";
|
|
2319
2754
|
|
|
2320
|
-
// src/components/jira/
|
|
2321
|
-
import
|
|
2755
|
+
// src/components/jira-browser/JiraIssueDetailView.tsx
|
|
2756
|
+
import open3 from "open";
|
|
2757
|
+
import { useEffect as useEffect9, useRef as useRef6, useState as useState13 } from "react";
|
|
2322
2758
|
import { Box as Box9, Text as Text8, useInput as useInput7 } from "ink";
|
|
2759
|
+
import { ScrollView as ScrollView5 } from "ink-scroll-view";
|
|
2323
2760
|
import SelectInput from "ink-select-input";
|
|
2761
|
+
import Spinner2 from "ink-spinner";
|
|
2324
2762
|
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2325
|
-
function
|
|
2326
|
-
|
|
2763
|
+
function JiraIssueDetailView({
|
|
2764
|
+
issueKey,
|
|
2765
|
+
issueSummary,
|
|
2766
|
+
auth,
|
|
2767
|
+
myAccountId,
|
|
2768
|
+
myDisplayName,
|
|
2769
|
+
isActive,
|
|
2770
|
+
onClose,
|
|
2771
|
+
onIssueUpdated,
|
|
2772
|
+
onLogUpdated
|
|
2773
|
+
}) {
|
|
2774
|
+
var _a;
|
|
2775
|
+
const scrollRef = useRef6(null);
|
|
2776
|
+
const [detail, setDetail] = useState13(null);
|
|
2327
2777
|
const [loading, setLoading] = useState13(true);
|
|
2328
|
-
const [applying, setApplying] = useState13(false);
|
|
2329
2778
|
const [error, setError] = useState13(null);
|
|
2779
|
+
const [mode, setMode] = useState13("normal");
|
|
2780
|
+
const [transitions, setTransitions] = useState13([]);
|
|
2781
|
+
const [transitionsLoading, setTransitionsLoading] = useState13(false);
|
|
2782
|
+
const [actionLoading, setActionLoading] = useState13(null);
|
|
2783
|
+
const [actionError, setActionError] = useState13(null);
|
|
2330
2784
|
useEffect9(() => {
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
if (!siteUrl || !creds.email || !creds.apiToken) {
|
|
2335
|
-
setError("Jira not configured");
|
|
2336
|
-
duckEvents.emit("error");
|
|
2337
|
-
setLoading(false);
|
|
2338
|
-
return;
|
|
2339
|
-
}
|
|
2340
|
-
const auth = { siteUrl, email: creds.email, apiToken: creds.apiToken };
|
|
2341
|
-
const result = await getTransitions(auth, ticketKey);
|
|
2785
|
+
setLoading(true);
|
|
2786
|
+
setError(null);
|
|
2787
|
+
getIssueDetail(auth, issueKey).then((result) => {
|
|
2342
2788
|
if (result.success) {
|
|
2343
|
-
|
|
2789
|
+
setDetail(result.data);
|
|
2344
2790
|
} else {
|
|
2345
2791
|
setError(result.error);
|
|
2346
|
-
duckEvents.emit("error");
|
|
2347
2792
|
}
|
|
2348
2793
|
setLoading(false);
|
|
2349
|
-
};
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
const
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
return;
|
|
2794
|
+
});
|
|
2795
|
+
}, [issueKey, auth.siteUrl]);
|
|
2796
|
+
const getIssueUrl = () => `${auth.siteUrl}/browse/${issueKey}`;
|
|
2797
|
+
const openTransitionPicker = async () => {
|
|
2798
|
+
setTransitionsLoading(true);
|
|
2799
|
+
setActionError(null);
|
|
2800
|
+
const result = await getTransitions(auth, issueKey);
|
|
2801
|
+
if (result.success) {
|
|
2802
|
+
setTransitions(result.data);
|
|
2803
|
+
setMode("transitions");
|
|
2804
|
+
} else {
|
|
2805
|
+
setActionError(result.error);
|
|
2362
2806
|
}
|
|
2363
|
-
|
|
2364
|
-
|
|
2807
|
+
setTransitionsLoading(false);
|
|
2808
|
+
};
|
|
2809
|
+
const handleTransitionSelect = async (item) => {
|
|
2810
|
+
setMode("normal");
|
|
2811
|
+
setActionLoading("Updating status...");
|
|
2812
|
+
setActionError(null);
|
|
2813
|
+
const result = await applyTransition(auth, issueKey, item.value);
|
|
2365
2814
|
if (result.success) {
|
|
2366
2815
|
const transition = transitions.find((t) => t.id === item.value);
|
|
2367
2816
|
const newStatus = (transition == null ? void 0 : transition.to.name) ?? item.label;
|
|
2817
|
+
const oldStatus = (detail == null ? void 0 : detail.fields.status.name) ?? "Unknown";
|
|
2818
|
+
setDetail((prev) => prev ? { ...prev, fields: { ...prev.fields, status: { name: newStatus } } } : prev);
|
|
2819
|
+
onIssueUpdated(issueKey, { status: newStatus });
|
|
2368
2820
|
duckEvents.emit("jira:transition");
|
|
2369
|
-
|
|
2821
|
+
logJiraStatusChanged(issueKey, issueSummary, oldStatus, newStatus);
|
|
2822
|
+
onLogUpdated == null ? void 0 : onLogUpdated();
|
|
2370
2823
|
} else {
|
|
2371
|
-
|
|
2824
|
+
setActionError(result.error);
|
|
2825
|
+
duckEvents.emit("error");
|
|
2826
|
+
}
|
|
2827
|
+
setActionLoading(null);
|
|
2828
|
+
};
|
|
2829
|
+
const handleAssignToMe = async () => {
|
|
2830
|
+
if (!myAccountId || !myDisplayName) return;
|
|
2831
|
+
setActionLoading("Assigning...");
|
|
2832
|
+
setActionError(null);
|
|
2833
|
+
const result = await assignIssue(auth, issueKey, myAccountId);
|
|
2834
|
+
if (result.success) {
|
|
2835
|
+
const assignee = { accountId: myAccountId, displayName: myDisplayName };
|
|
2836
|
+
setDetail((prev) => prev ? { ...prev, fields: { ...prev.fields, assignee } } : prev);
|
|
2837
|
+
onIssueUpdated(issueKey, { assignee });
|
|
2838
|
+
duckEvents.emit("jira:assigned");
|
|
2839
|
+
logJiraAssigneeChanged(issueKey, issueSummary, "assigned", myDisplayName);
|
|
2840
|
+
onLogUpdated == null ? void 0 : onLogUpdated();
|
|
2841
|
+
} else {
|
|
2842
|
+
setActionError(result.error);
|
|
2372
2843
|
duckEvents.emit("error");
|
|
2373
|
-
setApplying(false);
|
|
2374
2844
|
}
|
|
2845
|
+
setActionLoading(null);
|
|
2846
|
+
};
|
|
2847
|
+
const handleUnassign = async () => {
|
|
2848
|
+
setActionLoading("Unassigning...");
|
|
2849
|
+
setActionError(null);
|
|
2850
|
+
const result = await unassignIssue(auth, issueKey);
|
|
2851
|
+
if (result.success) {
|
|
2852
|
+
setDetail((prev) => prev ? { ...prev, fields: { ...prev.fields, assignee: null } } : prev);
|
|
2853
|
+
onIssueUpdated(issueKey, { assignee: null });
|
|
2854
|
+
duckEvents.emit("jira:unassigned");
|
|
2855
|
+
logJiraAssigneeChanged(issueKey, issueSummary, "unassigned");
|
|
2856
|
+
onLogUpdated == null ? void 0 : onLogUpdated();
|
|
2857
|
+
} else {
|
|
2858
|
+
setActionError(result.error);
|
|
2859
|
+
duckEvents.emit("error");
|
|
2860
|
+
}
|
|
2861
|
+
setActionLoading(null);
|
|
2375
2862
|
};
|
|
2376
2863
|
useInput7(
|
|
2377
|
-
(
|
|
2378
|
-
|
|
2379
|
-
|
|
2864
|
+
(input, key) => {
|
|
2865
|
+
var _a2, _b;
|
|
2866
|
+
if (mode === "transitions") {
|
|
2867
|
+
if (key.escape) {
|
|
2868
|
+
setMode("normal");
|
|
2869
|
+
}
|
|
2870
|
+
return;
|
|
2380
2871
|
}
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2872
|
+
if (key.escape && !actionLoading) {
|
|
2873
|
+
onClose();
|
|
2874
|
+
return;
|
|
2875
|
+
}
|
|
2876
|
+
if (key.upArrow || input === "k") {
|
|
2877
|
+
(_a2 = scrollRef.current) == null ? void 0 : _a2.scrollBy(-1);
|
|
2878
|
+
}
|
|
2879
|
+
if (key.downArrow || input === "j") {
|
|
2880
|
+
(_b = scrollRef.current) == null ? void 0 : _b.scrollBy(1);
|
|
2881
|
+
}
|
|
2882
|
+
if (input === "o") {
|
|
2883
|
+
open3(getIssueUrl()).catch(() => {
|
|
2884
|
+
});
|
|
2885
|
+
}
|
|
2886
|
+
if (input === "y") {
|
|
2887
|
+
copyToClipboard(getIssueUrl());
|
|
2888
|
+
}
|
|
2889
|
+
if (input === "s" && !actionLoading) {
|
|
2890
|
+
openTransitionPicker();
|
|
2891
|
+
}
|
|
2892
|
+
if (input === "a" && !actionLoading && myAccountId) {
|
|
2893
|
+
handleAssignToMe();
|
|
2894
|
+
}
|
|
2895
|
+
if (input === "A" && !actionLoading) {
|
|
2896
|
+
handleUnassign();
|
|
2897
|
+
}
|
|
2898
|
+
},
|
|
2899
|
+
{ isActive }
|
|
2900
|
+
);
|
|
2901
|
+
const statusColor = getStatusColor((detail == null ? void 0 : detail.fields.status.name) ?? "");
|
|
2902
|
+
const descriptionMd = (detail == null ? void 0 : detail.fields.description) ? adfToMarkdown(detail.fields.description) : null;
|
|
2903
|
+
const comments = (detail == null ? void 0 : detail.fields.comment.comments) ?? [];
|
|
2904
|
+
const totalComments = (detail == null ? void 0 : detail.fields.comment.total) ?? 0;
|
|
2905
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", flexGrow: 1, children: [
|
|
2906
|
+
/* @__PURE__ */ jsxs8(Box9, { flexGrow: 1, flexBasis: 0, overflow: "hidden", children: [
|
|
2907
|
+
loading && /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
|
|
2908
|
+
/* @__PURE__ */ jsx9(Spinner2, { type: "dots" }),
|
|
2909
|
+
" Loading issue details..."
|
|
2910
|
+
] }) }),
|
|
2911
|
+
error && /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "red", children: error }) }),
|
|
2912
|
+
!loading && !error && detail && /* @__PURE__ */ jsx9(ScrollView5, { ref: scrollRef, children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: 1, children: [
|
|
2913
|
+
/* @__PURE__ */ jsxs8(Box9, { children: [
|
|
2914
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, color: "blue", children: detail.key }),
|
|
2915
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
|
|
2916
|
+
" ",
|
|
2917
|
+
detail.fields.summary
|
|
2918
|
+
] })
|
|
2919
|
+
] }),
|
|
2920
|
+
/* @__PURE__ */ jsxs8(Box9, { gap: 1, children: [
|
|
2921
|
+
/* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "Status:" }),
|
|
2922
|
+
/* @__PURE__ */ jsx9(Text8, { color: statusColor, children: detail.fields.status.name }),
|
|
2923
|
+
/* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "Assignee:" }),
|
|
2924
|
+
/* @__PURE__ */ jsx9(Text8, { children: ((_a = detail.fields.assignee) == null ? void 0 : _a.displayName) ?? "Unassigned" })
|
|
2925
|
+
] }),
|
|
2926
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Divider, {}) }),
|
|
2927
|
+
/* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
2928
|
+
/* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "Description:" }),
|
|
2929
|
+
descriptionMd ? /* @__PURE__ */ jsx9(Markdown, { children: descriptionMd }) : /* @__PURE__ */ jsx9(Text8, { dimColor: true, italic: true, children: "No description" })
|
|
2930
|
+
] }),
|
|
2931
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Divider, {}) }),
|
|
2932
|
+
/* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
2933
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
2934
|
+
"Comments (",
|
|
2935
|
+
totalComments,
|
|
2936
|
+
"):"
|
|
2937
|
+
] }),
|
|
2938
|
+
comments.length === 0 && /* @__PURE__ */ jsx9(Text8, { dimColor: true, italic: true, children: "No comments" }),
|
|
2939
|
+
comments.map((comment) => /* @__PURE__ */ jsx9(CommentBlock, { comment }, comment.id)),
|
|
2940
|
+
comments.length < totalComments && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
2941
|
+
"Showing ",
|
|
2942
|
+
comments.length,
|
|
2943
|
+
" of ",
|
|
2944
|
+
totalComments,
|
|
2945
|
+
" comments. Open in browser to see all."
|
|
2946
|
+
] }) })
|
|
2947
|
+
] })
|
|
2948
|
+
] }) })
|
|
2949
|
+
] }),
|
|
2950
|
+
mode === "transitions" && /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
2951
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, color: "yellow", children: "Change Status" }),
|
|
2952
|
+
/* @__PURE__ */ jsx9(
|
|
2953
|
+
SelectInput,
|
|
2954
|
+
{
|
|
2955
|
+
items: transitions.map((t) => ({ label: t.name, value: t.id })),
|
|
2956
|
+
onSelect: handleTransitionSelect
|
|
2957
|
+
}
|
|
2958
|
+
),
|
|
2959
|
+
/* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "Esc to cancel" })
|
|
2960
|
+
] }),
|
|
2961
|
+
/* @__PURE__ */ jsxs8(Box9, { paddingX: 1, flexDirection: "column", children: [
|
|
2962
|
+
actionLoading && /* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
|
|
2963
|
+
/* @__PURE__ */ jsx9(Spinner2, { type: "dots" }),
|
|
2964
|
+
" ",
|
|
2965
|
+
actionLoading
|
|
2966
|
+
] }),
|
|
2967
|
+
actionError && /* @__PURE__ */ jsx9(Text8, { color: "red", children: actionError }),
|
|
2968
|
+
transitionsLoading && /* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
|
|
2969
|
+
/* @__PURE__ */ jsx9(Spinner2, { type: "dots" }),
|
|
2970
|
+
" Loading transitions..."
|
|
2971
|
+
] }),
|
|
2972
|
+
!actionLoading && !transitionsLoading && mode === "normal" && /* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "Esc close \xB7 j/k scroll \xB7 s status \xB7 a assign \xB7 A unassign \xB7 o open \xB7 y copy" })
|
|
2973
|
+
] })
|
|
2974
|
+
] });
|
|
2975
|
+
}
|
|
2976
|
+
function CommentBlock({ comment }) {
|
|
2977
|
+
const bodyMd = adfToMarkdown(comment.body);
|
|
2978
|
+
return /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
2979
|
+
/* @__PURE__ */ jsxs8(Box9, { gap: 1, children: [
|
|
2980
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, children: comment.author.displayName }),
|
|
2981
|
+
/* @__PURE__ */ jsx9(Text8, { dimColor: true, children: timeAgo(comment.created) })
|
|
2982
|
+
] }),
|
|
2983
|
+
bodyMd ? /* @__PURE__ */ jsx9(Markdown, { children: bodyMd }) : /* @__PURE__ */ jsx9(Text8, { dimColor: true, italic: true, children: "Empty comment" })
|
|
2984
|
+
] });
|
|
2985
|
+
}
|
|
2986
|
+
function getStatusColor(status) {
|
|
2987
|
+
const lower = status.toLowerCase();
|
|
2988
|
+
if (lower === "done" || lower === "closed" || lower === "resolved") return "green";
|
|
2989
|
+
if (lower === "in progress" || lower === "in review") return "yellow";
|
|
2990
|
+
return "gray";
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
// src/components/jira-browser/JiraSavedViewBrowserBox.tsx
|
|
2994
|
+
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2995
|
+
function groupBySprint(issues) {
|
|
2996
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2997
|
+
for (const issue of issues) {
|
|
2998
|
+
const sprint = issue.fields.sprint ?? null;
|
|
2999
|
+
const key = sprint ? String(sprint.id) : "__backlog__";
|
|
3000
|
+
if (!groups.has(key)) {
|
|
3001
|
+
groups.set(key, { sprint, issues: [] });
|
|
3002
|
+
}
|
|
3003
|
+
groups.get(key).issues.push(issue);
|
|
3004
|
+
}
|
|
3005
|
+
const stateOrder = { active: 0, future: 1, closed: 2 };
|
|
3006
|
+
const entries = [...groups.values()];
|
|
3007
|
+
entries.sort((a, b) => {
|
|
3008
|
+
if (!a.sprint) return 1;
|
|
3009
|
+
if (!b.sprint) return -1;
|
|
3010
|
+
const aOrder = stateOrder[a.sprint.state] ?? 3;
|
|
3011
|
+
const bOrder = stateOrder[b.sprint.state] ?? 3;
|
|
3012
|
+
return aOrder - bOrder;
|
|
3013
|
+
});
|
|
3014
|
+
return entries;
|
|
3015
|
+
}
|
|
3016
|
+
function buildRows(groups) {
|
|
3017
|
+
var _a;
|
|
3018
|
+
const hasSprints = groups.some((g) => g.sprint !== null);
|
|
3019
|
+
if (!hasSprints) {
|
|
3020
|
+
return groups.flatMap((g) => g.issues.map((issue) => ({ type: "issue", issue })));
|
|
3021
|
+
}
|
|
3022
|
+
const rows = [];
|
|
3023
|
+
for (const group of groups) {
|
|
3024
|
+
const label = group.sprint ? group.sprint.name : "Backlog";
|
|
3025
|
+
const state = ((_a = group.sprint) == null ? void 0 : _a.state) ?? null;
|
|
3026
|
+
rows.push({ type: "header", label, state });
|
|
3027
|
+
for (const issue of group.issues) {
|
|
3028
|
+
rows.push({ type: "issue", issue });
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
return rows;
|
|
3032
|
+
}
|
|
3033
|
+
function JiraSavedViewBrowserBox({
|
|
3034
|
+
view,
|
|
3035
|
+
auth,
|
|
3036
|
+
myAccountId,
|
|
3037
|
+
myDisplayName,
|
|
3038
|
+
isActive,
|
|
3039
|
+
onInputModeChange,
|
|
3040
|
+
onLogUpdated
|
|
3041
|
+
}) {
|
|
3042
|
+
const [issues, setIssues] = useState14([]);
|
|
3043
|
+
const [loading, setLoading] = useState14(false);
|
|
3044
|
+
const [error, setError] = useState14(null);
|
|
3045
|
+
const [total, setTotal] = useState14(0);
|
|
3046
|
+
const [highlightedIndex, setHighlightedIndex] = useState14(0);
|
|
3047
|
+
const [inputText, setInputText] = useState14("");
|
|
3048
|
+
const [searchText, setSearchText] = useState14("");
|
|
3049
|
+
const [isFiltering, setIsFiltering] = useState14(false);
|
|
3050
|
+
const [assigneeFilter, setAssigneeFilter] = useState14("all");
|
|
3051
|
+
const [detailIssue, setDetailIssue] = useState14(null);
|
|
3052
|
+
useEffect10(() => {
|
|
3053
|
+
onInputModeChange == null ? void 0 : onInputModeChange(isFiltering || detailIssue !== null);
|
|
3054
|
+
}, [isFiltering, detailIssue, onInputModeChange]);
|
|
3055
|
+
const title = "[6] Issues";
|
|
3056
|
+
const borderColor = isActive ? "yellow" : void 0;
|
|
3057
|
+
const displayTitle = view ? `${title} - ${view.name}` : title;
|
|
3058
|
+
const filteredIssues = useMemo2(() => {
|
|
3059
|
+
if (assigneeFilter === "unassigned") {
|
|
3060
|
+
return issues.filter((issue) => !issue.fields.assignee);
|
|
3061
|
+
}
|
|
3062
|
+
if (assigneeFilter === "me" && myAccountId) {
|
|
3063
|
+
return issues.filter((issue) => {
|
|
3064
|
+
var _a;
|
|
3065
|
+
return ((_a = issue.fields.assignee) == null ? void 0 : _a.accountId) === myAccountId;
|
|
3066
|
+
});
|
|
3067
|
+
}
|
|
3068
|
+
return issues;
|
|
3069
|
+
}, [issues, assigneeFilter, myAccountId]);
|
|
3070
|
+
const rows = useMemo2(() => {
|
|
3071
|
+
const groups = groupBySprint(filteredIssues);
|
|
3072
|
+
return buildRows(groups);
|
|
3073
|
+
}, [filteredIssues]);
|
|
3074
|
+
const navigableIndices = useMemo2(
|
|
3075
|
+
() => rows.map((r, i) => r.type === "issue" ? i : -1).filter((i) => i >= 0),
|
|
3076
|
+
[rows]
|
|
3077
|
+
);
|
|
3078
|
+
const scrollRef = useScrollToIndex(navigableIndices.length > 0 ? navigableIndices[highlightedIndex] ?? 0 : 0);
|
|
3079
|
+
const currentIssue = useMemo2(() => {
|
|
3080
|
+
if (navigableIndices.length === 0) return null;
|
|
3081
|
+
const rowIdx = navigableIndices[highlightedIndex];
|
|
3082
|
+
if (rowIdx === void 0) return null;
|
|
3083
|
+
const row = rows[rowIdx];
|
|
3084
|
+
return (row == null ? void 0 : row.type) === "issue" ? row.issue : null;
|
|
3085
|
+
}, [rows, navigableIndices, highlightedIndex]);
|
|
3086
|
+
const hasMore = issues.length < total;
|
|
3087
|
+
const doFetch = useCallback9(
|
|
3088
|
+
async (search, startAt = 0, append = false) => {
|
|
3089
|
+
if (!view || !auth) return;
|
|
3090
|
+
setLoading(true);
|
|
3091
|
+
setError(null);
|
|
3092
|
+
const result = await fetchViewIssues(auth, view, {
|
|
3093
|
+
startAt,
|
|
3094
|
+
maxResults: 50,
|
|
3095
|
+
searchText: search || void 0
|
|
3096
|
+
});
|
|
3097
|
+
if (result.success) {
|
|
3098
|
+
setIssues((prev) => append ? [...prev, ...result.data.issues] : result.data.issues);
|
|
3099
|
+
setTotal(result.data.total);
|
|
3100
|
+
if (!append) setHighlightedIndex(0);
|
|
3101
|
+
} else {
|
|
3102
|
+
setError(result.error);
|
|
3103
|
+
if (!append) {
|
|
3104
|
+
setIssues([]);
|
|
3105
|
+
setTotal(0);
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
setLoading(false);
|
|
3109
|
+
},
|
|
3110
|
+
[view, auth]
|
|
3111
|
+
);
|
|
3112
|
+
useEffect10(() => {
|
|
3113
|
+
if (view && auth) {
|
|
3114
|
+
setSearchText("");
|
|
3115
|
+
setInputText("");
|
|
3116
|
+
doFetch("");
|
|
3117
|
+
} else {
|
|
3118
|
+
setIssues([]);
|
|
3119
|
+
setTotal(0);
|
|
3120
|
+
setError(null);
|
|
3121
|
+
}
|
|
3122
|
+
}, [view == null ? void 0 : view.id, auth == null ? void 0 : auth.siteUrl]);
|
|
3123
|
+
const getIssueUrl = (issue) => {
|
|
3124
|
+
if (!auth) return null;
|
|
3125
|
+
return `${auth.siteUrl}/browse/${issue.key}`;
|
|
3126
|
+
};
|
|
3127
|
+
const handleIssueUpdated = useCallback9(
|
|
3128
|
+
(key, updates) => {
|
|
3129
|
+
setIssues(
|
|
3130
|
+
(prev) => prev.map((issue) => {
|
|
3131
|
+
if (issue.key !== key) return issue;
|
|
3132
|
+
const updated = { ...issue, fields: { ...issue.fields } };
|
|
3133
|
+
if (updates.status !== void 0) {
|
|
3134
|
+
updated.fields.status = { name: updates.status };
|
|
3135
|
+
}
|
|
3136
|
+
if ("assignee" in updates) {
|
|
3137
|
+
updated.fields.assignee = updates.assignee;
|
|
3138
|
+
}
|
|
3139
|
+
return updated;
|
|
3140
|
+
})
|
|
3141
|
+
);
|
|
3142
|
+
},
|
|
3143
|
+
[]
|
|
3144
|
+
);
|
|
3145
|
+
const hasActiveFilters = searchText.length > 0 || assigneeFilter !== "all";
|
|
3146
|
+
useInput8(
|
|
3147
|
+
(input, key) => {
|
|
3148
|
+
if (detailIssue) return;
|
|
3149
|
+
if (isFiltering) {
|
|
3150
|
+
if (key.escape) {
|
|
3151
|
+
setIsFiltering(false);
|
|
3152
|
+
setInputText(searchText);
|
|
3153
|
+
return;
|
|
3154
|
+
}
|
|
3155
|
+
if (key.return) {
|
|
3156
|
+
setIsFiltering(false);
|
|
3157
|
+
const newSearch = inputText.trim();
|
|
3158
|
+
if (newSearch !== searchText) {
|
|
3159
|
+
setSearchText(newSearch);
|
|
3160
|
+
doFetch(newSearch);
|
|
3161
|
+
}
|
|
3162
|
+
return;
|
|
3163
|
+
}
|
|
3164
|
+
if (key.backspace || key.delete) {
|
|
3165
|
+
setInputText((prev) => prev.slice(0, -1));
|
|
3166
|
+
return;
|
|
3167
|
+
}
|
|
3168
|
+
if (input && input.length > 0) {
|
|
3169
|
+
const printable = input.replace(/[^\x20-\x7E\u00A0-\uFFFF]/g, "");
|
|
3170
|
+
if (printable.length > 0) {
|
|
3171
|
+
setInputText((prev) => prev + printable);
|
|
3172
|
+
}
|
|
3173
|
+
return;
|
|
3174
|
+
}
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
if (navigableIndices.length > 0) {
|
|
3178
|
+
if (key.upArrow || input === "k") {
|
|
3179
|
+
setHighlightedIndex((i) => Math.max(0, i - 1));
|
|
3180
|
+
}
|
|
3181
|
+
if (key.downArrow || input === "j") {
|
|
3182
|
+
setHighlightedIndex((i) => Math.min(navigableIndices.length - 1, i + 1));
|
|
3183
|
+
}
|
|
3184
|
+
if (input === "o" && currentIssue) {
|
|
3185
|
+
const url = getIssueUrl(currentIssue);
|
|
3186
|
+
if (url) open4(url).catch(() => {
|
|
3187
|
+
});
|
|
3188
|
+
}
|
|
3189
|
+
if (input === "y" && currentIssue) {
|
|
3190
|
+
const url = getIssueUrl(currentIssue);
|
|
3191
|
+
if (url) copyToClipboard(url);
|
|
3192
|
+
}
|
|
3193
|
+
if (key.return && currentIssue && auth) {
|
|
3194
|
+
setDetailIssue({ key: currentIssue.key, summary: currentIssue.fields.summary });
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
if (input === "/") {
|
|
3198
|
+
setIsFiltering(true);
|
|
3199
|
+
setInputText(searchText);
|
|
3200
|
+
return;
|
|
3201
|
+
}
|
|
3202
|
+
if (input === "u") {
|
|
3203
|
+
setAssigneeFilter((f) => f === "unassigned" ? "all" : "unassigned");
|
|
3204
|
+
setHighlightedIndex(0);
|
|
3205
|
+
return;
|
|
3206
|
+
}
|
|
3207
|
+
if (input === "m") {
|
|
3208
|
+
setAssigneeFilter((f) => f === "me" ? "all" : "me");
|
|
3209
|
+
setHighlightedIndex(0);
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
if (input === "x") {
|
|
3213
|
+
setAssigneeFilter("all");
|
|
3214
|
+
if (searchText) {
|
|
3215
|
+
setSearchText("");
|
|
3216
|
+
setInputText("");
|
|
3217
|
+
doFetch("");
|
|
3218
|
+
}
|
|
3219
|
+
setHighlightedIndex(0);
|
|
3220
|
+
return;
|
|
3221
|
+
}
|
|
3222
|
+
if (input === "l" && hasMore) {
|
|
3223
|
+
doFetch(searchText, issues.length, true);
|
|
3224
|
+
return;
|
|
3225
|
+
}
|
|
3226
|
+
if (input === "r") {
|
|
3227
|
+
doFetch(searchText);
|
|
3228
|
+
}
|
|
3229
|
+
},
|
|
3230
|
+
{ isActive }
|
|
3231
|
+
);
|
|
3232
|
+
const filterParts = [];
|
|
3233
|
+
if (assigneeFilter === "unassigned") filterParts.push("unassigned");
|
|
3234
|
+
if (assigneeFilter === "me") filterParts.push("mine");
|
|
3235
|
+
if (searchText) filterParts.push(`"${searchText}"`);
|
|
3236
|
+
return /* @__PURE__ */ jsx10(TitledBox4, { borderStyle: "round", titles: [displayTitle], borderColor, flexGrow: 1, children: /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", flexGrow: 1, children: detailIssue && auth ? /* @__PURE__ */ jsx10(
|
|
3237
|
+
JiraIssueDetailView,
|
|
3238
|
+
{
|
|
3239
|
+
issueKey: detailIssue.key,
|
|
3240
|
+
issueSummary: detailIssue.summary,
|
|
3241
|
+
auth,
|
|
3242
|
+
myAccountId,
|
|
3243
|
+
myDisplayName,
|
|
3244
|
+
isActive,
|
|
3245
|
+
onClose: () => setDetailIssue(null),
|
|
3246
|
+
onIssueUpdated: handleIssueUpdated,
|
|
3247
|
+
onLogUpdated
|
|
3248
|
+
}
|
|
3249
|
+
) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
3250
|
+
(isFiltering || hasActiveFilters) && /* @__PURE__ */ jsxs9(Box10, { paddingX: 1, children: [
|
|
3251
|
+
/* @__PURE__ */ jsx10(Text9, { color: "blue", children: "Search: " }),
|
|
3252
|
+
isFiltering ? /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
3253
|
+
/* @__PURE__ */ jsx10(Text9, { children: inputText }),
|
|
3254
|
+
/* @__PURE__ */ jsx10(Text9, { backgroundColor: "yellow", children: " " })
|
|
3255
|
+
] }) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
3256
|
+
/* @__PURE__ */ jsx10(Text9, { children: filterParts.join(" + ") }),
|
|
3257
|
+
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
3258
|
+
" ",
|
|
3259
|
+
"(",
|
|
3260
|
+
filteredIssues.length,
|
|
3261
|
+
"/",
|
|
3262
|
+
total,
|
|
3263
|
+
")"
|
|
3264
|
+
] })
|
|
3265
|
+
] })
|
|
3266
|
+
] }),
|
|
3267
|
+
/* @__PURE__ */ jsxs9(Box10, { flexGrow: 1, flexBasis: 0, overflow: "hidden", children: [
|
|
3268
|
+
!view && /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "Select a view to browse issues" }) }),
|
|
3269
|
+
view && loading && issues.length === 0 && /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsxs9(Text9, { color: "yellow", children: [
|
|
3270
|
+
/* @__PURE__ */ jsx10(Spinner3, { type: "dots" }),
|
|
3271
|
+
" Loading issues..."
|
|
3272
|
+
] }) }),
|
|
3273
|
+
view && error && /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { color: "red", children: error }) }),
|
|
3274
|
+
view && !loading && !error && issues.length === 0 && /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: searchText ? "No issues match search" : "No issues found" }) }),
|
|
3275
|
+
view && !loading && !error && filteredIssues.length === 0 && issues.length > 0 && /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "No issues match filter" }) }),
|
|
3276
|
+
rows.length > 0 && /* @__PURE__ */ jsx10(ScrollView6, { ref: scrollRef, children: rows.map((row, rowIdx) => {
|
|
3277
|
+
if (row.type === "header") {
|
|
3278
|
+
const stateLabel = row.state === "active" ? " (active)" : "";
|
|
3279
|
+
return /* @__PURE__ */ jsx10(Box10, { paddingX: 1, marginTop: rowIdx > 0 ? 1 : 0, children: /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "magenta", children: [
|
|
3280
|
+
row.label,
|
|
3281
|
+
stateLabel && /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: stateLabel })
|
|
3282
|
+
] }) }, `header-${row.label}`);
|
|
3283
|
+
}
|
|
3284
|
+
const navIdx = navigableIndices.indexOf(rowIdx);
|
|
3285
|
+
const isHighlighted = navIdx === highlightedIndex;
|
|
3286
|
+
const cursor = isHighlighted ? ">" : " ";
|
|
3287
|
+
const statusColor = getStatusColor2(row.issue.fields.status.name);
|
|
3288
|
+
return /* @__PURE__ */ jsxs9(Box10, { paddingX: 1, children: [
|
|
3289
|
+
/* @__PURE__ */ jsxs9(Text9, { color: isHighlighted ? "yellow" : void 0, children: [
|
|
3290
|
+
cursor,
|
|
3291
|
+
" "
|
|
3292
|
+
] }),
|
|
3293
|
+
/* @__PURE__ */ jsx10(Text9, { bold: true, color: "blue", children: row.issue.key }),
|
|
3294
|
+
/* @__PURE__ */ jsxs9(Text9, { children: [
|
|
3295
|
+
" ",
|
|
3296
|
+
row.issue.fields.summary
|
|
3297
|
+
] }),
|
|
3298
|
+
/* @__PURE__ */ jsxs9(Text9, { color: statusColor, children: [
|
|
3299
|
+
" [",
|
|
3300
|
+
row.issue.fields.status.name,
|
|
3301
|
+
"]"
|
|
3302
|
+
] })
|
|
3303
|
+
] }, row.issue.key);
|
|
3304
|
+
}) })
|
|
3305
|
+
] }),
|
|
3306
|
+
view && !loading && issues.length > 0 && /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
3307
|
+
issues.length,
|
|
3308
|
+
" of ",
|
|
3309
|
+
total,
|
|
3310
|
+
" loaded",
|
|
3311
|
+
hasMore && " \xB7 l to load more"
|
|
3312
|
+
] }) }),
|
|
3313
|
+
view && loading && issues.length > 0 && /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "Loading more..." }) })
|
|
3314
|
+
] }) }) });
|
|
3315
|
+
}
|
|
3316
|
+
function getStatusColor2(status) {
|
|
3317
|
+
const lower = status.toLowerCase();
|
|
3318
|
+
if (lower === "done" || lower === "closed" || lower === "resolved") return "green";
|
|
3319
|
+
if (lower === "in progress" || lower === "in review") return "yellow";
|
|
3320
|
+
return "gray";
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
// src/components/jira-browser/JiraSavedViewsBox.tsx
|
|
3324
|
+
import { useEffect as useEffect11, useState as useState15 } from "react";
|
|
3325
|
+
import { TitledBox as TitledBox5 } from "@mishieck/ink-titled-box";
|
|
3326
|
+
import { Box as Box11, Text as Text10, useInput as useInput9 } from "ink";
|
|
3327
|
+
import { ScrollView as ScrollView7 } from "ink-scroll-view";
|
|
3328
|
+
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3329
|
+
function JiraSavedViewsBox({
|
|
3330
|
+
views,
|
|
3331
|
+
selectedViewId,
|
|
3332
|
+
highlightedIndex,
|
|
3333
|
+
onHighlight,
|
|
3334
|
+
onSelect,
|
|
3335
|
+
onAdd,
|
|
3336
|
+
onDelete,
|
|
3337
|
+
onRename,
|
|
3338
|
+
isActive,
|
|
3339
|
+
onInputModeChange
|
|
3340
|
+
}) {
|
|
3341
|
+
const scrollRef = useScrollToIndex(highlightedIndex);
|
|
3342
|
+
const [renaming, setRenaming] = useState15(null);
|
|
3343
|
+
const [renameValue, setRenameValue] = useState15("");
|
|
3344
|
+
useEffect11(() => {
|
|
3345
|
+
onInputModeChange == null ? void 0 : onInputModeChange(renaming !== null);
|
|
3346
|
+
}, [renaming, onInputModeChange]);
|
|
3347
|
+
const title = "[5] Views";
|
|
3348
|
+
const borderColor = isActive ? "yellow" : void 0;
|
|
3349
|
+
useInput9(
|
|
3350
|
+
(input, key) => {
|
|
3351
|
+
if (renaming) {
|
|
3352
|
+
if (key.escape) {
|
|
3353
|
+
setRenaming(null);
|
|
3354
|
+
setRenameValue("");
|
|
3355
|
+
return;
|
|
3356
|
+
}
|
|
3357
|
+
if (key.return) {
|
|
3358
|
+
const trimmed = renameValue.trim();
|
|
3359
|
+
if (trimmed.length > 0) {
|
|
3360
|
+
onRename(renaming, trimmed);
|
|
3361
|
+
}
|
|
3362
|
+
setRenaming(null);
|
|
3363
|
+
setRenameValue("");
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
return;
|
|
3367
|
+
}
|
|
3368
|
+
if (views.length === 0) {
|
|
3369
|
+
if (input === "a") onAdd();
|
|
3370
|
+
return;
|
|
3371
|
+
}
|
|
3372
|
+
if (key.upArrow || input === "k") {
|
|
3373
|
+
onHighlight(Math.max(0, highlightedIndex - 1));
|
|
3374
|
+
}
|
|
3375
|
+
if (key.downArrow || input === "j") {
|
|
3376
|
+
onHighlight(Math.min(views.length - 1, highlightedIndex + 1));
|
|
3377
|
+
}
|
|
3378
|
+
if (input === " ") {
|
|
3379
|
+
const view = views[highlightedIndex];
|
|
3380
|
+
if (view) onSelect(view.id);
|
|
3381
|
+
}
|
|
3382
|
+
if (input === "a") onAdd();
|
|
3383
|
+
if (input === "e") {
|
|
3384
|
+
const view = views[highlightedIndex];
|
|
3385
|
+
if (view) {
|
|
3386
|
+
setRenaming(view.id);
|
|
3387
|
+
setRenameValue(view.name);
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
if (input === "d") {
|
|
3391
|
+
const view = views[highlightedIndex];
|
|
3392
|
+
if (view) onDelete(view.id);
|
|
3393
|
+
}
|
|
3394
|
+
},
|
|
3395
|
+
{ isActive }
|
|
3396
|
+
);
|
|
3397
|
+
return /* @__PURE__ */ jsx11(TitledBox5, { borderStyle: "round", titles: [title], borderColor, height: 5, children: /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingX: 1, flexGrow: 1, overflow: "hidden", children: [
|
|
3398
|
+
views.length === 0 && /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: "No saved views" }),
|
|
3399
|
+
views.length > 0 && /* @__PURE__ */ jsx11(ScrollView7, { ref: scrollRef, children: views.map((view, idx) => {
|
|
3400
|
+
const isHighlighted = isActive && idx === highlightedIndex;
|
|
3401
|
+
const isSelected = view.id === selectedViewId;
|
|
3402
|
+
const isRenaming = view.id === renaming;
|
|
3403
|
+
const cursor = isHighlighted ? ">" : " ";
|
|
3404
|
+
const nameColor = isSelected ? "green" : void 0;
|
|
3405
|
+
const indicator = isSelected ? " *" : "";
|
|
3406
|
+
return /* @__PURE__ */ jsxs10(Box11, { children: [
|
|
3407
|
+
/* @__PURE__ */ jsxs10(Text10, { color: isHighlighted ? "yellow" : void 0, children: [
|
|
3408
|
+
cursor,
|
|
3409
|
+
" "
|
|
3410
|
+
] }),
|
|
3411
|
+
isRenaming ? /* @__PURE__ */ jsx11(TextInput, { value: renameValue, onChange: setRenameValue, isActive: true }) : /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
3412
|
+
/* @__PURE__ */ jsx11(Text10, { color: nameColor, children: view.name }),
|
|
3413
|
+
/* @__PURE__ */ jsx11(Text10, { dimColor: true, children: indicator })
|
|
3414
|
+
] })
|
|
3415
|
+
] }, view.id);
|
|
3416
|
+
}) })
|
|
3417
|
+
] }) });
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3420
|
+
// src/components/jira-browser/JiraBrowserView.tsx
|
|
3421
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3422
|
+
function JiraBrowserView({
|
|
3423
|
+
isActive,
|
|
3424
|
+
focusedBox,
|
|
3425
|
+
onFocusedBoxChange,
|
|
3426
|
+
onModalChange,
|
|
3427
|
+
onLogUpdated
|
|
3428
|
+
}) {
|
|
3429
|
+
const repo = useGitRepo();
|
|
3430
|
+
const modal = useModal();
|
|
3431
|
+
const [views, setViews] = useState16([]);
|
|
3432
|
+
const [selectedViewId, setSelectedViewId] = useState16(null);
|
|
3433
|
+
const [highlightedIndex, setHighlightedIndex] = useState16(0);
|
|
3434
|
+
const [addError, setAddError] = useState16(void 0);
|
|
3435
|
+
const [addLoading, setAddLoading] = useState16(false);
|
|
3436
|
+
const [myAccountId, setMyAccountId] = useState16(null);
|
|
3437
|
+
const [myDisplayName, setMyDisplayName] = useState16(null);
|
|
3438
|
+
const [inputModeActive, setInputModeActive] = useState16(false);
|
|
3439
|
+
const lastRepoRef = useRef7(null);
|
|
3440
|
+
const auth = useMemo3(() => {
|
|
3441
|
+
if (!repo.repoPath || !isJiraConfigured(repo.repoPath)) return null;
|
|
3442
|
+
const siteUrl = getJiraSiteUrl(repo.repoPath);
|
|
3443
|
+
const creds = getJiraCredentials(repo.repoPath);
|
|
3444
|
+
if (!siteUrl || !creds.email || !creds.apiToken) return null;
|
|
3445
|
+
return { siteUrl, email: creds.email, apiToken: creds.apiToken };
|
|
3446
|
+
}, [repo.repoPath]);
|
|
3447
|
+
const selectedView = useMemo3(() => views.find((v) => v.id === selectedViewId) ?? null, [views, selectedViewId]);
|
|
3448
|
+
useEffect12(() => {
|
|
3449
|
+
if (!repo.repoPath || repo.repoPath === lastRepoRef.current) return;
|
|
3450
|
+
lastRepoRef.current = repo.repoPath;
|
|
3451
|
+
const loaded = getSavedViews(repo.repoPath);
|
|
3452
|
+
setViews(loaded);
|
|
3453
|
+
if (loaded.length > 0 && !selectedViewId) {
|
|
3454
|
+
setSelectedViewId(loaded[0].id);
|
|
3455
|
+
}
|
|
3456
|
+
}, [repo.repoPath]);
|
|
3457
|
+
useEffect12(() => {
|
|
3458
|
+
if (!auth) {
|
|
3459
|
+
setMyAccountId(null);
|
|
3460
|
+
return;
|
|
3461
|
+
}
|
|
3462
|
+
getCurrentUser(auth).then((result) => {
|
|
3463
|
+
if (result.success) {
|
|
3464
|
+
setMyAccountId(result.data.accountId);
|
|
3465
|
+
setMyDisplayName(result.data.displayName);
|
|
3466
|
+
}
|
|
3467
|
+
});
|
|
3468
|
+
}, [auth == null ? void 0 : auth.siteUrl, auth == null ? void 0 : auth.email]);
|
|
3469
|
+
useEffect12(() => {
|
|
3470
|
+
onModalChange == null ? void 0 : onModalChange(modal.isOpen || inputModeActive);
|
|
3471
|
+
}, [modal.isOpen, inputModeActive, onModalChange]);
|
|
3472
|
+
useEffect12(() => {
|
|
3473
|
+
if (!isActive) modal.close();
|
|
3474
|
+
}, [isActive, modal.close]);
|
|
3475
|
+
const refreshViews = useCallback10(() => {
|
|
3476
|
+
if (!repo.repoPath) return;
|
|
3477
|
+
setViews(getSavedViews(repo.repoPath));
|
|
3478
|
+
}, [repo.repoPath]);
|
|
3479
|
+
const handleSelectView = useCallback10((viewId) => {
|
|
3480
|
+
setSelectedViewId(viewId);
|
|
3481
|
+
}, []);
|
|
3482
|
+
const handleAddView = useCallback10(
|
|
3483
|
+
async (url, name) => {
|
|
3484
|
+
if (!repo.repoPath) return;
|
|
3485
|
+
const source = parseJiraUrl(url);
|
|
3486
|
+
if (!source) {
|
|
3487
|
+
setAddError("Unrecognized Jira URL format");
|
|
3488
|
+
return;
|
|
3489
|
+
}
|
|
3490
|
+
setAddLoading(true);
|
|
3491
|
+
setAddError(void 0);
|
|
3492
|
+
try {
|
|
3493
|
+
const view = addSavedView(repo.repoPath, name, url, source);
|
|
3494
|
+
refreshViews();
|
|
3495
|
+
setSelectedViewId(view.id);
|
|
3496
|
+
modal.close();
|
|
3497
|
+
} catch {
|
|
3498
|
+
setAddError("Failed to save view");
|
|
3499
|
+
} finally {
|
|
3500
|
+
setAddLoading(false);
|
|
3501
|
+
}
|
|
3502
|
+
},
|
|
3503
|
+
[repo.repoPath, refreshViews, modal.close]
|
|
3504
|
+
);
|
|
3505
|
+
const handleRenameView = useCallback10(
|
|
3506
|
+
(viewId, newName) => {
|
|
3507
|
+
if (!repo.repoPath) return;
|
|
3508
|
+
renameSavedView(repo.repoPath, viewId, newName);
|
|
3509
|
+
refreshViews();
|
|
3510
|
+
},
|
|
3511
|
+
[repo.repoPath, refreshViews]
|
|
3512
|
+
);
|
|
3513
|
+
const handleDeleteView = useCallback10(
|
|
3514
|
+
(viewId) => {
|
|
3515
|
+
if (!repo.repoPath) return;
|
|
3516
|
+
removeSavedView(repo.repoPath, viewId);
|
|
3517
|
+
refreshViews();
|
|
3518
|
+
if (selectedViewId === viewId) {
|
|
3519
|
+
const remaining = getSavedViews(repo.repoPath);
|
|
3520
|
+
setSelectedViewId(remaining.length > 0 ? remaining[0].id : null);
|
|
3521
|
+
}
|
|
3522
|
+
setHighlightedIndex((i) => Math.max(0, i - 1));
|
|
3523
|
+
},
|
|
3524
|
+
[repo.repoPath, selectedViewId, refreshViews]
|
|
3525
|
+
);
|
|
3526
|
+
useInput10(
|
|
3527
|
+
(input) => {
|
|
3528
|
+
if (input === "5") onFocusedBoxChange("saved-views");
|
|
3529
|
+
if (input === "6") onFocusedBoxChange("browser");
|
|
3530
|
+
},
|
|
3531
|
+
{ isActive: isActive && !modal.isOpen }
|
|
3532
|
+
);
|
|
3533
|
+
if (modal.type === "add") {
|
|
3534
|
+
return /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx12(
|
|
3535
|
+
AddViewModal,
|
|
3536
|
+
{
|
|
3537
|
+
onSubmit: handleAddView,
|
|
3538
|
+
onCancel: () => {
|
|
3539
|
+
modal.close();
|
|
3540
|
+
setAddError(void 0);
|
|
3541
|
+
},
|
|
3542
|
+
loading: addLoading,
|
|
3543
|
+
error: addError
|
|
3544
|
+
}
|
|
3545
|
+
) });
|
|
3546
|
+
}
|
|
3547
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", flexGrow: 1, children: [
|
|
3548
|
+
/* @__PURE__ */ jsx12(
|
|
3549
|
+
JiraSavedViewsBox,
|
|
3550
|
+
{
|
|
3551
|
+
views,
|
|
3552
|
+
selectedViewId,
|
|
3553
|
+
highlightedIndex,
|
|
3554
|
+
onHighlight: setHighlightedIndex,
|
|
3555
|
+
onSelect: handleSelectView,
|
|
3556
|
+
onAdd: () => modal.open("add"),
|
|
3557
|
+
onDelete: handleDeleteView,
|
|
3558
|
+
onRename: handleRenameView,
|
|
3559
|
+
isActive: isActive && focusedBox === "saved-views",
|
|
3560
|
+
onInputModeChange: setInputModeActive
|
|
3561
|
+
}
|
|
3562
|
+
),
|
|
3563
|
+
/* @__PURE__ */ jsx12(
|
|
3564
|
+
JiraSavedViewBrowserBox,
|
|
3565
|
+
{
|
|
3566
|
+
view: selectedView,
|
|
3567
|
+
auth,
|
|
3568
|
+
myAccountId,
|
|
3569
|
+
myDisplayName,
|
|
3570
|
+
isActive: isActive && focusedBox === "browser",
|
|
3571
|
+
onInputModeChange: setInputModeActive,
|
|
3572
|
+
onLogUpdated
|
|
3573
|
+
}
|
|
3574
|
+
)
|
|
3575
|
+
] });
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
// src/components/jira/JiraView.tsx
|
|
3579
|
+
import open5 from "open";
|
|
3580
|
+
import { useEffect as useEffect14, useRef as useRef8 } from "react";
|
|
3581
|
+
|
|
3582
|
+
// src/components/jira/LinkTicketModal.tsx
|
|
3583
|
+
import { useState as useState17 } from "react";
|
|
3584
|
+
import { Box as Box13, Text as Text11, useInput as useInput11 } from "ink";
|
|
3585
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3586
|
+
function LinkTicketModal({ onSubmit, onCancel, loading, error }) {
|
|
3587
|
+
const [ticketInput, setTicketInput] = useState17("");
|
|
3588
|
+
const canSubmit = ticketInput.trim().length > 0;
|
|
3589
|
+
useInput11(
|
|
3590
|
+
(_input, key) => {
|
|
3591
|
+
if (loading) return;
|
|
3592
|
+
if (key.escape) {
|
|
3593
|
+
onCancel();
|
|
3594
|
+
return;
|
|
3595
|
+
}
|
|
3596
|
+
if (key.return && canSubmit) {
|
|
3597
|
+
onSubmit(ticketInput.trim());
|
|
3598
|
+
}
|
|
3599
|
+
},
|
|
3600
|
+
{ isActive: !loading }
|
|
3601
|
+
);
|
|
3602
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, paddingY: 1, children: [
|
|
3603
|
+
/* @__PURE__ */ jsx13(Text11, { bold: true, color: "yellow", children: "Link Jira Ticket" }),
|
|
3604
|
+
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Type ticket ID, Enter to submit, Esc to cancel" }),
|
|
3605
|
+
/* @__PURE__ */ jsx13(Box13, { marginTop: 1 }),
|
|
3606
|
+
error && /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { color: "red", children: error }) }),
|
|
3607
|
+
/* @__PURE__ */ jsxs12(Box13, { children: [
|
|
3608
|
+
/* @__PURE__ */ jsx13(Text11, { color: "blue", children: "Ticket: " }),
|
|
3609
|
+
/* @__PURE__ */ jsx13(TextInput, { value: ticketInput, onChange: setTicketInput, placeholder: "PROJ-123", isActive: !loading })
|
|
3610
|
+
] }),
|
|
3611
|
+
loading && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { color: "yellow", children: "Fetching ticket..." }) }),
|
|
3612
|
+
/* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Examples: PROJ-123 or https://company.atlassian.net/browse/PROJ-123" }) })
|
|
3613
|
+
] });
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
// src/components/jira/JiraView.tsx
|
|
3617
|
+
import { TitledBox as TitledBox6 } from "@mishieck/ink-titled-box";
|
|
3618
|
+
import { Box as Box17, Text as Text15, useInput as useInput14 } from "ink";
|
|
3619
|
+
|
|
3620
|
+
// src/components/jira/ChangeStatusModal.tsx
|
|
3621
|
+
import { useEffect as useEffect13, useState as useState18 } from "react";
|
|
3622
|
+
import { Box as Box14, Text as Text12, useInput as useInput12 } from "ink";
|
|
3623
|
+
import SelectInput2 from "ink-select-input";
|
|
3624
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3625
|
+
function ChangeStatusModal({ repoPath, ticketKey, currentStatus, onComplete, onCancel }) {
|
|
3626
|
+
const [transitions, setTransitions] = useState18([]);
|
|
3627
|
+
const [loading, setLoading] = useState18(true);
|
|
3628
|
+
const [applying, setApplying] = useState18(false);
|
|
3629
|
+
const [error, setError] = useState18(null);
|
|
3630
|
+
useEffect13(() => {
|
|
3631
|
+
const fetchTransitions = async () => {
|
|
3632
|
+
const siteUrl = getJiraSiteUrl(repoPath);
|
|
3633
|
+
const creds = getJiraCredentials(repoPath);
|
|
3634
|
+
if (!siteUrl || !creds.email || !creds.apiToken) {
|
|
3635
|
+
setError("Jira not configured");
|
|
3636
|
+
duckEvents.emit("error");
|
|
3637
|
+
setLoading(false);
|
|
3638
|
+
return;
|
|
3639
|
+
}
|
|
3640
|
+
const auth = { siteUrl, email: creds.email, apiToken: creds.apiToken };
|
|
3641
|
+
const result = await getTransitions(auth, ticketKey);
|
|
3642
|
+
if (result.success) {
|
|
3643
|
+
setTransitions(result.data);
|
|
3644
|
+
} else {
|
|
3645
|
+
setError(result.error);
|
|
3646
|
+
duckEvents.emit("error");
|
|
3647
|
+
}
|
|
3648
|
+
setLoading(false);
|
|
3649
|
+
};
|
|
3650
|
+
fetchTransitions();
|
|
3651
|
+
}, [repoPath, ticketKey]);
|
|
3652
|
+
const handleSelect = async (item) => {
|
|
3653
|
+
setApplying(true);
|
|
3654
|
+
setError(null);
|
|
3655
|
+
const siteUrl = getJiraSiteUrl(repoPath);
|
|
3656
|
+
const creds = getJiraCredentials(repoPath);
|
|
3657
|
+
if (!siteUrl || !creds.email || !creds.apiToken) {
|
|
3658
|
+
setError("Jira not configured");
|
|
3659
|
+
duckEvents.emit("error");
|
|
3660
|
+
setApplying(false);
|
|
3661
|
+
return;
|
|
3662
|
+
}
|
|
3663
|
+
const auth = { siteUrl, email: creds.email, apiToken: creds.apiToken };
|
|
3664
|
+
const result = await applyTransition(auth, ticketKey, item.value);
|
|
3665
|
+
if (result.success) {
|
|
3666
|
+
const transition = transitions.find((t) => t.id === item.value);
|
|
3667
|
+
const newStatus = (transition == null ? void 0 : transition.to.name) ?? item.label;
|
|
3668
|
+
duckEvents.emit("jira:transition");
|
|
3669
|
+
onComplete(newStatus);
|
|
3670
|
+
} else {
|
|
3671
|
+
setError(result.error);
|
|
3672
|
+
duckEvents.emit("error");
|
|
3673
|
+
setApplying(false);
|
|
3674
|
+
}
|
|
3675
|
+
};
|
|
3676
|
+
useInput12(
|
|
3677
|
+
(_input, key) => {
|
|
3678
|
+
if (key.escape && !applying) {
|
|
3679
|
+
onCancel();
|
|
3680
|
+
}
|
|
3681
|
+
},
|
|
3682
|
+
{ isActive: !applying }
|
|
3683
|
+
);
|
|
3684
|
+
const items = transitions.map((t) => ({
|
|
3685
|
+
label: t.name,
|
|
3686
|
+
value: t.id
|
|
3687
|
+
}));
|
|
3688
|
+
const initialIndex = Math.max(
|
|
3689
|
+
0,
|
|
3690
|
+
transitions.findIndex((t) => t.to.name === currentStatus)
|
|
3691
|
+
);
|
|
3692
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, paddingY: 1, children: [
|
|
3693
|
+
/* @__PURE__ */ jsxs13(Text12, { bold: true, color: "yellow", children: [
|
|
3694
|
+
"Change Status: ",
|
|
2395
3695
|
ticketKey
|
|
2396
3696
|
] }),
|
|
2397
|
-
loading && /* @__PURE__ */
|
|
2398
|
-
error && /* @__PURE__ */
|
|
2399
|
-
!loading && !error && transitions.length === 0 && /* @__PURE__ */
|
|
2400
|
-
!loading && !error && transitions.length > 0 && !applying && /* @__PURE__ */
|
|
2401
|
-
applying && /* @__PURE__ */
|
|
2402
|
-
/* @__PURE__ */
|
|
3697
|
+
loading && /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Loading transitions..." }),
|
|
3698
|
+
error && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { color: "red", children: error }) }),
|
|
3699
|
+
!loading && !error && transitions.length === 0 && /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "No available transitions" }),
|
|
3700
|
+
!loading && !error && transitions.length > 0 && !applying && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx14(SelectInput2, { items, initialIndex, onSelect: handleSelect }) }),
|
|
3701
|
+
applying && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { color: "yellow", children: "Updating status..." }) }),
|
|
3702
|
+
/* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Esc to cancel" }) })
|
|
2403
3703
|
] });
|
|
2404
3704
|
}
|
|
2405
3705
|
|
|
2406
3706
|
// src/components/jira/ConfigureJiraSiteModal.tsx
|
|
2407
|
-
import { useState as
|
|
2408
|
-
import { Box as
|
|
2409
|
-
import { ScrollView as
|
|
3707
|
+
import { useState as useState19 } from "react";
|
|
3708
|
+
import { Box as Box15, Text as Text13, useInput as useInput13 } from "ink";
|
|
3709
|
+
import { ScrollView as ScrollView8 } from "ink-scroll-view";
|
|
2410
3710
|
|
|
2411
3711
|
// src/lib/editor.ts
|
|
2412
3712
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
@@ -2437,7 +3737,7 @@ function openInEditor(content, filename) {
|
|
|
2437
3737
|
}
|
|
2438
3738
|
|
|
2439
3739
|
// src/components/jira/ConfigureJiraSiteModal.tsx
|
|
2440
|
-
import { jsx as
|
|
3740
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2441
3741
|
var MAX_VISIBLE_ITEMS = 4;
|
|
2442
3742
|
function ConfigureJiraSiteModal({
|
|
2443
3743
|
initialSiteUrl,
|
|
@@ -2449,17 +3749,17 @@ function ConfigureJiraSiteModal({
|
|
|
2449
3749
|
error
|
|
2450
3750
|
}) {
|
|
2451
3751
|
const hasExisting = existingConfigs.length > 0;
|
|
2452
|
-
const [mode, setMode] =
|
|
2453
|
-
const [selectedExisting, setSelectedExisting] =
|
|
3752
|
+
const [mode, setMode] = useState19(hasExisting ? "choose" : "manual");
|
|
3753
|
+
const [selectedExisting, setSelectedExisting] = useState19(0);
|
|
2454
3754
|
const scrollRef = useScrollToIndex(selectedExisting);
|
|
2455
|
-
const [siteUrl, setSiteUrl] =
|
|
2456
|
-
const [email, setEmail] =
|
|
2457
|
-
const [apiToken, setApiToken] =
|
|
2458
|
-
const [selectedItem, setSelectedItem] =
|
|
3755
|
+
const [siteUrl, setSiteUrl] = useState19(initialSiteUrl ?? "");
|
|
3756
|
+
const [email, setEmail] = useState19(initialEmail ?? "");
|
|
3757
|
+
const [apiToken, setApiToken] = useState19("");
|
|
3758
|
+
const [selectedItem, setSelectedItem] = useState19("siteUrl");
|
|
2459
3759
|
const items = ["siteUrl", "email", "apiToken", "submit"];
|
|
2460
3760
|
const canSubmit = siteUrl.trim() && email.trim() && apiToken.trim();
|
|
2461
3761
|
const chooseItems = existingConfigs.length + 1;
|
|
2462
|
-
|
|
3762
|
+
useInput13(
|
|
2463
3763
|
(input, key) => {
|
|
2464
3764
|
if (loading) return;
|
|
2465
3765
|
if (key.escape) {
|
|
@@ -2531,38 +3831,38 @@ function ConfigureJiraSiteModal({
|
|
|
2531
3831
|
const prefix = isSelected ? "> " : " ";
|
|
2532
3832
|
const color = isSelected ? "yellow" : void 0;
|
|
2533
3833
|
const displayValue = isSensitive && value ? "*".repeat(Math.min(value.length, 20)) : value;
|
|
2534
|
-
return /* @__PURE__ */
|
|
2535
|
-
/* @__PURE__ */
|
|
3834
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
3835
|
+
/* @__PURE__ */ jsxs14(Text13, { color, bold: isSelected, children: [
|
|
2536
3836
|
prefix,
|
|
2537
3837
|
label
|
|
2538
3838
|
] }),
|
|
2539
|
-
value !== void 0 && /* @__PURE__ */
|
|
3839
|
+
value !== void 0 && /* @__PURE__ */ jsx15(Box15, { marginLeft: 4, children: /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: displayValue || "(empty - press Enter to edit)" }) })
|
|
2540
3840
|
] });
|
|
2541
3841
|
};
|
|
2542
3842
|
if (mode === "choose") {
|
|
2543
3843
|
const totalItems = existingConfigs.length + 1;
|
|
2544
3844
|
const listHeight = Math.min(totalItems * 2, MAX_VISIBLE_ITEMS * 2);
|
|
2545
|
-
return /* @__PURE__ */
|
|
2546
|
-
/* @__PURE__ */
|
|
2547
|
-
/* @__PURE__ */
|
|
2548
|
-
/* @__PURE__ */
|
|
2549
|
-
error && /* @__PURE__ */
|
|
2550
|
-
/* @__PURE__ */
|
|
3845
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, paddingY: 1, children: [
|
|
3846
|
+
/* @__PURE__ */ jsx15(Text13, { bold: true, color: "cyan", children: "Configure Jira Site" }),
|
|
3847
|
+
/* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Select an existing configuration or enter new credentials" }),
|
|
3848
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1 }),
|
|
3849
|
+
error && /* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text13, { color: "red", children: error }) }),
|
|
3850
|
+
/* @__PURE__ */ jsx15(Box15, { height: listHeight, overflow: "hidden", children: /* @__PURE__ */ jsxs14(ScrollView8, { ref: scrollRef, children: [
|
|
2551
3851
|
existingConfigs.map((config, idx) => {
|
|
2552
3852
|
const isSelected = selectedExisting === idx;
|
|
2553
|
-
return /* @__PURE__ */
|
|
2554
|
-
/* @__PURE__ */
|
|
3853
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
3854
|
+
/* @__PURE__ */ jsxs14(Text13, { color: isSelected ? "yellow" : void 0, bold: isSelected, children: [
|
|
2555
3855
|
isSelected ? "> " : " ",
|
|
2556
3856
|
config.siteUrl
|
|
2557
3857
|
] }),
|
|
2558
|
-
/* @__PURE__ */
|
|
3858
|
+
/* @__PURE__ */ jsxs14(Text13, { dimColor: true, children: [
|
|
2559
3859
|
" ",
|
|
2560
3860
|
config.email
|
|
2561
3861
|
] })
|
|
2562
3862
|
] }, config.siteUrl + config.email);
|
|
2563
3863
|
}),
|
|
2564
|
-
/* @__PURE__ */
|
|
2565
|
-
|
|
3864
|
+
/* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsxs14(
|
|
3865
|
+
Text13,
|
|
2566
3866
|
{
|
|
2567
3867
|
color: selectedExisting === existingConfigs.length ? "yellow" : void 0,
|
|
2568
3868
|
bold: selectedExisting === existingConfigs.length,
|
|
@@ -2573,44 +3873,44 @@ function ConfigureJiraSiteModal({
|
|
|
2573
3873
|
}
|
|
2574
3874
|
) })
|
|
2575
3875
|
] }) }),
|
|
2576
|
-
loading && /* @__PURE__ */
|
|
3876
|
+
loading && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text13, { color: "yellow", children: "Validating credentials..." }) })
|
|
2577
3877
|
] });
|
|
2578
3878
|
}
|
|
2579
|
-
return /* @__PURE__ */
|
|
2580
|
-
/* @__PURE__ */
|
|
2581
|
-
/* @__PURE__ */
|
|
3879
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, paddingY: 1, children: [
|
|
3880
|
+
/* @__PURE__ */ jsx15(Text13, { bold: true, color: "cyan", children: "Configure Jira Site" }),
|
|
3881
|
+
/* @__PURE__ */ jsxs14(Text13, { dimColor: true, children: [
|
|
2582
3882
|
"Up/Down to select, Enter to edit, Esc to ",
|
|
2583
3883
|
hasExisting ? "go back" : "cancel"
|
|
2584
3884
|
] }),
|
|
2585
|
-
/* @__PURE__ */
|
|
2586
|
-
error && /* @__PURE__ */
|
|
3885
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1 }),
|
|
3886
|
+
error && /* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text13, { color: "red", children: error }) }),
|
|
2587
3887
|
renderItem("siteUrl", "Site URL (e.g., https://company.atlassian.net)", siteUrl),
|
|
2588
|
-
/* @__PURE__ */
|
|
3888
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1 }),
|
|
2589
3889
|
renderItem("email", "Email", email),
|
|
2590
|
-
/* @__PURE__ */
|
|
3890
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1 }),
|
|
2591
3891
|
renderItem("apiToken", "API Token", apiToken, true),
|
|
2592
|
-
/* @__PURE__ */
|
|
2593
|
-
/* @__PURE__ */
|
|
3892
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1 }),
|
|
3893
|
+
/* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsxs14(Text13, { color: selectedItem === "submit" ? "green" : void 0, bold: selectedItem === "submit", children: [
|
|
2594
3894
|
selectedItem === "submit" ? "> " : " ",
|
|
2595
3895
|
canSubmit ? "[Save Configuration]" : "[Fill all fields first]"
|
|
2596
3896
|
] }) }),
|
|
2597
|
-
loading && /* @__PURE__ */
|
|
2598
|
-
/* @__PURE__ */
|
|
3897
|
+
loading && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text13, { color: "yellow", children: "Validating credentials..." }) }),
|
|
3898
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Get your API token from: https://id.atlassian.com/manage-profile/security/api-tokens" }) })
|
|
2599
3899
|
] });
|
|
2600
3900
|
}
|
|
2601
3901
|
|
|
2602
3902
|
// src/components/jira/TicketItem.tsx
|
|
2603
|
-
import { Box as
|
|
2604
|
-
import { jsx as
|
|
3903
|
+
import { Box as Box16, Text as Text14 } from "ink";
|
|
3904
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2605
3905
|
function TicketItem({ ticketKey, summary, status, isHighlighted, isSelected }) {
|
|
2606
3906
|
const prefix = isHighlighted ? "> " : isSelected ? "\u25CF " : " ";
|
|
2607
3907
|
const textColor = isSelected ? "green" : void 0;
|
|
2608
|
-
return /* @__PURE__ */
|
|
3908
|
+
return /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsxs15(Text14, { color: textColor, children: [
|
|
2609
3909
|
prefix,
|
|
2610
|
-
/* @__PURE__ */
|
|
3910
|
+
/* @__PURE__ */ jsx16(Text14, { bold: true, color: "blue", children: ticketKey }),
|
|
2611
3911
|
" ",
|
|
2612
3912
|
summary,
|
|
2613
|
-
status && /* @__PURE__ */
|
|
3913
|
+
status && /* @__PURE__ */ jsxs15(Text14, { dimColor: true, children: [
|
|
2614
3914
|
" [",
|
|
2615
3915
|
status,
|
|
2616
3916
|
"]"
|
|
@@ -2619,15 +3919,15 @@ function TicketItem({ ticketKey, summary, status, isHighlighted, isSelected }) {
|
|
|
2619
3919
|
}
|
|
2620
3920
|
|
|
2621
3921
|
// src/components/jira/JiraView.tsx
|
|
2622
|
-
import { jsx as
|
|
3922
|
+
import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2623
3923
|
function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated }) {
|
|
2624
3924
|
const repo = useGitRepo();
|
|
2625
3925
|
const jira = useJiraTickets();
|
|
2626
3926
|
const modal = useModal();
|
|
2627
3927
|
const nav = useListNavigation({ items: jira.tickets });
|
|
2628
3928
|
const currentTicket = jira.tickets[nav.index] ?? null;
|
|
2629
|
-
const lastInitRef =
|
|
2630
|
-
|
|
3929
|
+
const lastInitRef = useRef8(null);
|
|
3930
|
+
useEffect14(() => {
|
|
2631
3931
|
if (repo.loading || !repo.repoPath || !repo.currentBranch) return;
|
|
2632
3932
|
const current = { branch: repo.currentBranch };
|
|
2633
3933
|
const last = lastInitRef.current;
|
|
@@ -2635,17 +3935,17 @@ function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2635
3935
|
lastInitRef.current = current;
|
|
2636
3936
|
jira.initializeJiraState(repo.repoPath, repo.currentBranch, repo.currentRepoSlug);
|
|
2637
3937
|
}, [repo.loading, repo.repoPath, repo.currentBranch, repo.currentRepoSlug, jira.initializeJiraState]);
|
|
2638
|
-
|
|
3938
|
+
useEffect14(() => {
|
|
2639
3939
|
if (isActive) {
|
|
2640
3940
|
repo.refreshBranch();
|
|
2641
3941
|
} else {
|
|
2642
3942
|
modal.close();
|
|
2643
3943
|
}
|
|
2644
3944
|
}, [isActive, repo.refreshBranch, modal.close]);
|
|
2645
|
-
|
|
3945
|
+
useEffect14(() => {
|
|
2646
3946
|
onModalChange == null ? void 0 : onModalChange(modal.isOpen);
|
|
2647
3947
|
}, [modal.isOpen, onModalChange]);
|
|
2648
|
-
|
|
3948
|
+
useEffect14(() => {
|
|
2649
3949
|
onJiraStateChange == null ? void 0 : onJiraStateChange(jira.jiraState);
|
|
2650
3950
|
}, [jira.jiraState, onJiraStateChange]);
|
|
2651
3951
|
const handleConfigureSubmit = async (siteUrl, email, apiToken) => {
|
|
@@ -2671,7 +3971,7 @@ function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2671
3971
|
};
|
|
2672
3972
|
const handleOpenInBrowser = () => {
|
|
2673
3973
|
const url = getTicketUrl();
|
|
2674
|
-
if (url)
|
|
3974
|
+
if (url) open5(url).catch(() => {
|
|
2675
3975
|
});
|
|
2676
3976
|
};
|
|
2677
3977
|
const handleCopyLink = () => {
|
|
@@ -2691,7 +3991,7 @@ function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2691
3991
|
clearJiraConfig(repo.repoPath);
|
|
2692
3992
|
jira.initializeJiraState(repo.repoPath, repo.currentBranch, repo.currentRepoSlug);
|
|
2693
3993
|
};
|
|
2694
|
-
|
|
3994
|
+
useInput14(
|
|
2695
3995
|
(input, key) => {
|
|
2696
3996
|
if (input === "c" && jira.jiraState === "not_configured") {
|
|
2697
3997
|
modal.open("configure");
|
|
@@ -2717,13 +4017,13 @@ function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2717
4017
|
{ isActive: isActive && !modal.isOpen }
|
|
2718
4018
|
);
|
|
2719
4019
|
if (repo.isRepo === false) {
|
|
2720
|
-
return /* @__PURE__ */
|
|
4020
|
+
return /* @__PURE__ */ jsx17(TitledBox6, { borderStyle: "round", titles: ["Jira"], flexShrink: 0, children: /* @__PURE__ */ jsx17(Text15, { color: "red", children: "Not a git repository" }) });
|
|
2721
4021
|
}
|
|
2722
4022
|
if (modal.type === "configure") {
|
|
2723
4023
|
const siteUrl = repo.repoPath ? getJiraSiteUrl(repo.repoPath) : void 0;
|
|
2724
4024
|
const creds = repo.repoPath ? getJiraCredentials(repo.repoPath) : { email: null, apiToken: null };
|
|
2725
4025
|
const existingConfigs = getExistingJiraConfigs(repo.repoPath ?? void 0);
|
|
2726
|
-
return /* @__PURE__ */
|
|
4026
|
+
return /* @__PURE__ */ jsx17(Box17, { flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx17(
|
|
2727
4027
|
ConfigureJiraSiteModal,
|
|
2728
4028
|
{
|
|
2729
4029
|
initialSiteUrl: siteUrl ?? void 0,
|
|
@@ -2740,7 +4040,7 @@ function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2740
4040
|
) });
|
|
2741
4041
|
}
|
|
2742
4042
|
if (modal.type === "link") {
|
|
2743
|
-
return /* @__PURE__ */
|
|
4043
|
+
return /* @__PURE__ */ jsx17(Box17, { flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx17(
|
|
2744
4044
|
LinkTicketModal,
|
|
2745
4045
|
{
|
|
2746
4046
|
onSubmit: handleLinkSubmit,
|
|
@@ -2754,7 +4054,7 @@ function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2754
4054
|
) });
|
|
2755
4055
|
}
|
|
2756
4056
|
if (modal.type === "status" && repo.repoPath && repo.currentBranch && currentTicket) {
|
|
2757
|
-
return /* @__PURE__ */
|
|
4057
|
+
return /* @__PURE__ */ jsx17(Box17, { flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx17(
|
|
2758
4058
|
ChangeStatusModal,
|
|
2759
4059
|
{
|
|
2760
4060
|
repoPath: repo.repoPath,
|
|
@@ -2767,10 +4067,10 @@ function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2767
4067
|
}
|
|
2768
4068
|
const title = "[4] Jira";
|
|
2769
4069
|
const borderColor = isActive ? "yellow" : void 0;
|
|
2770
|
-
return /* @__PURE__ */
|
|
2771
|
-
jira.jiraState === "not_configured" && /* @__PURE__ */
|
|
2772
|
-
jira.jiraState === "no_tickets" && /* @__PURE__ */
|
|
2773
|
-
jira.jiraState === "has_tickets" && jira.tickets.map((ticket, idx) => /* @__PURE__ */
|
|
4070
|
+
return /* @__PURE__ */ jsx17(TitledBox6, { borderStyle: "round", titles: [title], borderColor, flexShrink: 0, children: /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", paddingX: 1, children: [
|
|
4071
|
+
jira.jiraState === "not_configured" && /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "No Jira site configured" }),
|
|
4072
|
+
jira.jiraState === "no_tickets" && /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "No tickets linked to this branch" }),
|
|
4073
|
+
jira.jiraState === "has_tickets" && jira.tickets.map((ticket, idx) => /* @__PURE__ */ jsx17(
|
|
2774
4074
|
TicketItem,
|
|
2775
4075
|
{
|
|
2776
4076
|
ticketKey: ticket.key,
|
|
@@ -2784,28 +4084,28 @@ function JiraView({ isActive, onModalChange, onJiraStateChange, onLogUpdated })
|
|
|
2784
4084
|
}
|
|
2785
4085
|
|
|
2786
4086
|
// src/components/logs/LogsView.tsx
|
|
2787
|
-
import { useEffect as
|
|
2788
|
-
import { Box as
|
|
4087
|
+
import { useEffect as useEffect16 } from "react";
|
|
4088
|
+
import { Box as Box20, useInput as useInput17 } from "ink";
|
|
2789
4089
|
|
|
2790
4090
|
// src/components/logs/LogViewerBox.tsx
|
|
2791
|
-
import { useEffect as
|
|
2792
|
-
import { TitledBox as
|
|
2793
|
-
import { Box as
|
|
2794
|
-
import { ScrollView as
|
|
2795
|
-
import
|
|
4091
|
+
import { useEffect as useEffect15, useRef as useRef9, useState as useState20 } from "react";
|
|
4092
|
+
import { TitledBox as TitledBox7 } from "@mishieck/ink-titled-box";
|
|
4093
|
+
import { Box as Box18, Text as Text16, useInput as useInput15 } from "ink";
|
|
4094
|
+
import { ScrollView as ScrollView9 } from "ink-scroll-view";
|
|
4095
|
+
import Spinner4 from "ink-spinner";
|
|
2796
4096
|
import TextInput2 from "ink-text-input";
|
|
2797
|
-
import { jsx as
|
|
4097
|
+
import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2798
4098
|
function LogViewerBox({ date, content, isActive, onRefresh, onLogCreated }) {
|
|
2799
|
-
const scrollRef =
|
|
2800
|
-
const [isInputMode, setIsInputMode] =
|
|
2801
|
-
const [inputValue, setInputValue] =
|
|
2802
|
-
const [isGeneratingStandup, setIsGeneratingStandup] =
|
|
2803
|
-
const [standupResult, setStandupResult] =
|
|
2804
|
-
const claudeProcessRef =
|
|
4099
|
+
const scrollRef = useRef9(null);
|
|
4100
|
+
const [isInputMode, setIsInputMode] = useState20(false);
|
|
4101
|
+
const [inputValue, setInputValue] = useState20("");
|
|
4102
|
+
const [isGeneratingStandup, setIsGeneratingStandup] = useState20(false);
|
|
4103
|
+
const [standupResult, setStandupResult] = useState20(null);
|
|
4104
|
+
const claudeProcessRef = useRef9(null);
|
|
2805
4105
|
const title = "[6] Log Content";
|
|
2806
4106
|
const borderColor = isActive ? "yellow" : void 0;
|
|
2807
4107
|
const displayTitle = date ? `${title} - ${date}.md` : title;
|
|
2808
|
-
|
|
4108
|
+
useInput15(
|
|
2809
4109
|
(input, key) => {
|
|
2810
4110
|
var _a, _b, _c;
|
|
2811
4111
|
if (key.escape && isInputMode) {
|
|
@@ -2867,7 +4167,7 @@ function LogViewerBox({ date, content, isActive, onRefresh, onLogCreated }) {
|
|
|
2867
4167
|
},
|
|
2868
4168
|
{ isActive }
|
|
2869
4169
|
);
|
|
2870
|
-
|
|
4170
|
+
useEffect15(() => {
|
|
2871
4171
|
return () => {
|
|
2872
4172
|
var _a;
|
|
2873
4173
|
(_a = claudeProcessRef.current) == null ? void 0 : _a.cancel();
|
|
@@ -2890,14 +4190,14 @@ ${value.trim()}
|
|
|
2890
4190
|
setIsInputMode(false);
|
|
2891
4191
|
onRefresh();
|
|
2892
4192
|
};
|
|
2893
|
-
return /* @__PURE__ */
|
|
2894
|
-
/* @__PURE__ */
|
|
2895
|
-
!date && /* @__PURE__ */
|
|
2896
|
-
date && content === null && /* @__PURE__ */
|
|
2897
|
-
date && content !== null && content.trim() === "" && /* @__PURE__ */
|
|
2898
|
-
date && content && content.trim() !== "" && /* @__PURE__ */
|
|
4193
|
+
return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", flexGrow: 1, children: [
|
|
4194
|
+
/* @__PURE__ */ jsx18(TitledBox7, { borderStyle: "round", titles: [displayTitle], borderColor, flexGrow: 1, children: /* @__PURE__ */ jsx18(Box18, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx18(ScrollView9, { ref: scrollRef, children: /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", paddingX: 1, children: [
|
|
4195
|
+
!date && /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Select a log file to view" }),
|
|
4196
|
+
date && content === null && /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Log file not found" }),
|
|
4197
|
+
date && content !== null && content.trim() === "" && /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Empty log file" }),
|
|
4198
|
+
date && content && content.trim() !== "" && /* @__PURE__ */ jsx18(Markdown, { children: content })
|
|
2899
4199
|
] }) }) }) }),
|
|
2900
|
-
isInputMode && /* @__PURE__ */
|
|
4200
|
+
isInputMode && /* @__PURE__ */ jsx18(TitledBox7, { borderStyle: "round", titles: ["Add Entry"], borderColor: "yellow", children: /* @__PURE__ */ jsx18(Box18, { paddingX: 1, children: /* @__PURE__ */ jsx18(
|
|
2901
4201
|
TextInput2,
|
|
2902
4202
|
{
|
|
2903
4203
|
value: inputValue,
|
|
@@ -2905,22 +4205,22 @@ ${value.trim()}
|
|
|
2905
4205
|
onSubmit: handleInputSubmit
|
|
2906
4206
|
}
|
|
2907
4207
|
) }) }),
|
|
2908
|
-
isGeneratingStandup && /* @__PURE__ */
|
|
2909
|
-
/* @__PURE__ */
|
|
2910
|
-
/* @__PURE__ */
|
|
4208
|
+
isGeneratingStandup && /* @__PURE__ */ jsx18(TitledBox7, { borderStyle: "round", titles: ["Standup Notes"], borderColor: "yellow", children: /* @__PURE__ */ jsxs17(Box18, { paddingX: 1, flexDirection: "column", children: [
|
|
4209
|
+
/* @__PURE__ */ jsxs17(Text16, { color: "yellow", children: [
|
|
4210
|
+
/* @__PURE__ */ jsx18(Spinner4, { type: "dots" }),
|
|
2911
4211
|
" Generating standup notes..."
|
|
2912
4212
|
] }),
|
|
2913
|
-
/* @__PURE__ */
|
|
4213
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Press Esc to cancel" })
|
|
2914
4214
|
] }) }),
|
|
2915
|
-
standupResult && /* @__PURE__ */
|
|
2916
|
-
|
|
4215
|
+
standupResult && /* @__PURE__ */ jsx18(
|
|
4216
|
+
TitledBox7,
|
|
2917
4217
|
{
|
|
2918
4218
|
borderStyle: "round",
|
|
2919
4219
|
titles: ["Standup Notes"],
|
|
2920
4220
|
borderColor: standupResult.type === "error" ? "red" : "green",
|
|
2921
|
-
children: /* @__PURE__ */
|
|
2922
|
-
standupResult.type === "error" ? /* @__PURE__ */
|
|
2923
|
-
/* @__PURE__ */
|
|
4221
|
+
children: /* @__PURE__ */ jsxs17(Box18, { paddingX: 1, flexDirection: "column", children: [
|
|
4222
|
+
standupResult.type === "error" ? /* @__PURE__ */ jsx18(Text16, { color: "red", children: standupResult.message }) : /* @__PURE__ */ jsx18(Markdown, { children: standupResult.message }),
|
|
4223
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Press Esc to dismiss" })
|
|
2924
4224
|
] })
|
|
2925
4225
|
}
|
|
2926
4226
|
)
|
|
@@ -2928,10 +4228,10 @@ ${value.trim()}
|
|
|
2928
4228
|
}
|
|
2929
4229
|
|
|
2930
4230
|
// src/components/logs/LogsHistoryBox.tsx
|
|
2931
|
-
import { TitledBox as
|
|
2932
|
-
import { Box as
|
|
2933
|
-
import { ScrollView as
|
|
2934
|
-
import { jsx as
|
|
4231
|
+
import { TitledBox as TitledBox8 } from "@mishieck/ink-titled-box";
|
|
4232
|
+
import { Box as Box19, Text as Text17, useInput as useInput16 } from "ink";
|
|
4233
|
+
import { ScrollView as ScrollView10 } from "ink-scroll-view";
|
|
4234
|
+
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2935
4235
|
function LogsHistoryBox({
|
|
2936
4236
|
logFiles,
|
|
2937
4237
|
selectedDate,
|
|
@@ -2943,7 +4243,7 @@ function LogsHistoryBox({
|
|
|
2943
4243
|
const scrollRef = useScrollToIndex(highlightedIndex);
|
|
2944
4244
|
const title = "[5] Logs";
|
|
2945
4245
|
const borderColor = isActive ? "yellow" : void 0;
|
|
2946
|
-
|
|
4246
|
+
useInput16(
|
|
2947
4247
|
(input, key) => {
|
|
2948
4248
|
if (logFiles.length === 0) return;
|
|
2949
4249
|
if (key.upArrow || input === "k") {
|
|
@@ -2961,44 +4261,44 @@ function LogsHistoryBox({
|
|
|
2961
4261
|
},
|
|
2962
4262
|
{ isActive }
|
|
2963
4263
|
);
|
|
2964
|
-
return /* @__PURE__ */
|
|
2965
|
-
logFiles.length === 0 && /* @__PURE__ */
|
|
2966
|
-
logFiles.length > 0 && /* @__PURE__ */
|
|
4264
|
+
return /* @__PURE__ */ jsx19(TitledBox8, { borderStyle: "round", titles: [title], borderColor, height: 5, children: /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", paddingX: 1, flexGrow: 1, overflow: "hidden", children: [
|
|
4265
|
+
logFiles.length === 0 && /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: "No logs yet" }),
|
|
4266
|
+
logFiles.length > 0 && /* @__PURE__ */ jsx19(ScrollView10, { ref: scrollRef, children: logFiles.map((file, idx) => {
|
|
2967
4267
|
const isHighlighted = idx === highlightedIndex;
|
|
2968
4268
|
const isSelected = file.date === selectedDate;
|
|
2969
4269
|
const cursor = isHighlighted ? ">" : " ";
|
|
2970
4270
|
const indicator = isSelected ? " *" : "";
|
|
2971
|
-
return /* @__PURE__ */
|
|
2972
|
-
/* @__PURE__ */
|
|
4271
|
+
return /* @__PURE__ */ jsxs18(Box19, { children: [
|
|
4272
|
+
/* @__PURE__ */ jsxs18(Text17, { color: isHighlighted ? "yellow" : void 0, children: [
|
|
2973
4273
|
cursor,
|
|
2974
4274
|
" "
|
|
2975
4275
|
] }),
|
|
2976
|
-
/* @__PURE__ */
|
|
2977
|
-
file.isToday && /* @__PURE__ */
|
|
2978
|
-
/* @__PURE__ */
|
|
4276
|
+
/* @__PURE__ */ jsx19(Text17, { color: file.isToday ? "green" : void 0, bold: file.isToday, children: file.date }),
|
|
4277
|
+
file.isToday && /* @__PURE__ */ jsx19(Text17, { color: "green", children: " (today)" }),
|
|
4278
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: indicator })
|
|
2979
4279
|
] }, file.date);
|
|
2980
4280
|
}) })
|
|
2981
4281
|
] }) });
|
|
2982
4282
|
}
|
|
2983
4283
|
|
|
2984
4284
|
// src/components/logs/LogsView.tsx
|
|
2985
|
-
import { jsx as
|
|
4285
|
+
import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2986
4286
|
function LogsView({ isActive, refreshKey, focusedBox, onFocusedBoxChange }) {
|
|
2987
4287
|
const logs = useLogs();
|
|
2988
|
-
|
|
4288
|
+
useEffect16(() => {
|
|
2989
4289
|
if (refreshKey !== void 0 && refreshKey > 0) {
|
|
2990
4290
|
logs.handleExternalLogUpdate();
|
|
2991
4291
|
}
|
|
2992
4292
|
}, [refreshKey, logs.handleExternalLogUpdate]);
|
|
2993
|
-
|
|
4293
|
+
useInput17(
|
|
2994
4294
|
(input) => {
|
|
2995
4295
|
if (input === "5") onFocusedBoxChange("history");
|
|
2996
4296
|
if (input === "6") onFocusedBoxChange("viewer");
|
|
2997
4297
|
},
|
|
2998
4298
|
{ isActive }
|
|
2999
4299
|
);
|
|
3000
|
-
return /* @__PURE__ */
|
|
3001
|
-
/* @__PURE__ */
|
|
4300
|
+
return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", flexGrow: 1, children: [
|
|
4301
|
+
/* @__PURE__ */ jsx20(
|
|
3002
4302
|
LogsHistoryBox,
|
|
3003
4303
|
{
|
|
3004
4304
|
logFiles: logs.logFiles,
|
|
@@ -3009,7 +4309,7 @@ function LogsView({ isActive, refreshKey, focusedBox, onFocusedBoxChange }) {
|
|
|
3009
4309
|
isActive: isActive && focusedBox === "history"
|
|
3010
4310
|
}
|
|
3011
4311
|
),
|
|
3012
|
-
/* @__PURE__ */
|
|
4312
|
+
/* @__PURE__ */ jsx20(
|
|
3013
4313
|
LogViewerBox,
|
|
3014
4314
|
{
|
|
3015
4315
|
date: logs.selectedDate,
|
|
@@ -3023,10 +4323,11 @@ function LogsView({ isActive, refreshKey, focusedBox, onFocusedBoxChange }) {
|
|
|
3023
4323
|
}
|
|
3024
4324
|
|
|
3025
4325
|
// src/components/ui/KeybindingsBar.tsx
|
|
3026
|
-
import { Box as
|
|
3027
|
-
import { jsx as
|
|
4326
|
+
import { Box as Box21, Text as Text18 } from "ink";
|
|
4327
|
+
import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3028
4328
|
var globalBindings = [
|
|
3029
4329
|
{ key: "1-4", label: "Focus" },
|
|
4330
|
+
{ key: "Tab", label: "Switch Tab" },
|
|
3030
4331
|
{ key: "j/k", label: "Navigate" },
|
|
3031
4332
|
{ key: "Ctrl+C", label: "Quit" }
|
|
3032
4333
|
];
|
|
@@ -3034,18 +4335,24 @@ var modalBindings = [{ key: "Esc", label: "Cancel" }];
|
|
|
3034
4335
|
var DUCK_ASCII = "<(')___";
|
|
3035
4336
|
function KeybindingsBar({ contextBindings = [], modalOpen = false, duck }) {
|
|
3036
4337
|
const allBindings = modalOpen ? [...contextBindings, ...modalBindings] : [...contextBindings, ...globalBindings];
|
|
3037
|
-
return /* @__PURE__ */
|
|
3038
|
-
allBindings.map((binding) => /* @__PURE__ */
|
|
3039
|
-
/* @__PURE__ */
|
|
3040
|
-
/* @__PURE__ */
|
|
4338
|
+
return /* @__PURE__ */ jsxs20(Box21, { flexShrink: 0, paddingX: 1, gap: 2, children: [
|
|
4339
|
+
allBindings.map((binding) => /* @__PURE__ */ jsxs20(Box21, { gap: 1, children: [
|
|
4340
|
+
/* @__PURE__ */ jsx21(Text18, { bold: true, color: binding.color ?? "yellow", children: binding.key }),
|
|
4341
|
+
/* @__PURE__ */ jsx21(Text18, { dimColor: true, children: binding.label })
|
|
3041
4342
|
] }, binding.key)),
|
|
3042
|
-
(duck == null ? void 0 : duck.visible) && /* @__PURE__ */
|
|
3043
|
-
/* @__PURE__ */
|
|
3044
|
-
/* @__PURE__ */
|
|
4343
|
+
(duck == null ? void 0 : duck.visible) && /* @__PURE__ */ jsxs20(Box21, { flexGrow: 1, justifyContent: "flex-end", gap: 1, children: [
|
|
4344
|
+
/* @__PURE__ */ jsx21(Text18, { children: DUCK_ASCII }),
|
|
4345
|
+
/* @__PURE__ */ jsx21(Text18, { dimColor: true, children: duck.message })
|
|
3045
4346
|
] })
|
|
3046
4347
|
] });
|
|
3047
4348
|
}
|
|
3048
4349
|
|
|
4350
|
+
// src/constants/tabs.ts
|
|
4351
|
+
var COLUMN2_TABS = [
|
|
4352
|
+
{ id: "logs", label: "Logs" },
|
|
4353
|
+
{ id: "jira-browser", label: "Jira" }
|
|
4354
|
+
];
|
|
4355
|
+
|
|
3049
4356
|
// src/constants/github.ts
|
|
3050
4357
|
var GITHUB_KEYBINDINGS = {
|
|
3051
4358
|
remotes: [{ key: "Space", label: "Select Remote" }],
|
|
@@ -3063,6 +4370,27 @@ var GITHUB_KEYBINDINGS = {
|
|
|
3063
4370
|
]
|
|
3064
4371
|
};
|
|
3065
4372
|
|
|
4373
|
+
// src/constants/jira-browser.ts
|
|
4374
|
+
var JIRA_BROWSER_KEYBINDINGS = {
|
|
4375
|
+
"saved-views": [
|
|
4376
|
+
{ key: "Space", label: "Select" },
|
|
4377
|
+
{ key: "a", label: "Add View", color: "green" },
|
|
4378
|
+
{ key: "e", label: "Rename" },
|
|
4379
|
+
{ key: "d", label: "Delete", color: "red" }
|
|
4380
|
+
],
|
|
4381
|
+
browser: [
|
|
4382
|
+
{ key: "Enter", label: "Details" },
|
|
4383
|
+
{ key: "/", label: "Filter" },
|
|
4384
|
+
{ key: "u", label: "Unassigned" },
|
|
4385
|
+
{ key: "m", label: "Mine" },
|
|
4386
|
+
{ key: "x", label: "Clear Filters" },
|
|
4387
|
+
{ key: "l", label: "Load More" },
|
|
4388
|
+
{ key: "o", label: "Open", color: "green" },
|
|
4389
|
+
{ key: "y", label: "Copy Link" },
|
|
4390
|
+
{ key: "r", label: "Refresh" }
|
|
4391
|
+
]
|
|
4392
|
+
};
|
|
4393
|
+
|
|
3066
4394
|
// src/constants/jira.ts
|
|
3067
4395
|
var JIRA_KEYBINDINGS = {
|
|
3068
4396
|
not_configured: [{ key: "c", label: "Configure Jira" }],
|
|
@@ -3102,34 +4430,42 @@ function computeKeybindings(focusedView, state) {
|
|
|
3102
4430
|
return JIRA_KEYBINDINGS[state.jira.jiraState];
|
|
3103
4431
|
case "logs":
|
|
3104
4432
|
return LOGS_KEYBINDINGS[state.logs.focusedBox];
|
|
4433
|
+
case "jira-browser":
|
|
4434
|
+
if (state["jira-browser"].modalOpen) return [];
|
|
4435
|
+
return JIRA_BROWSER_KEYBINDINGS[state["jira-browser"].focusedBox];
|
|
3105
4436
|
default:
|
|
3106
4437
|
return [];
|
|
3107
4438
|
}
|
|
3108
4439
|
}
|
|
3109
4440
|
|
|
3110
4441
|
// src/app.tsx
|
|
3111
|
-
import { jsx as
|
|
4442
|
+
import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
3112
4443
|
function App() {
|
|
3113
4444
|
const { exit } = useApp();
|
|
3114
|
-
const [focusedView, setFocusedView] =
|
|
3115
|
-
const [modalOpen, setModalOpen] =
|
|
3116
|
-
const [logRefreshKey, setLogRefreshKey] =
|
|
4445
|
+
const [focusedView, setFocusedView] = useState21("github");
|
|
4446
|
+
const [modalOpen, setModalOpen] = useState21(false);
|
|
4447
|
+
const [logRefreshKey, setLogRefreshKey] = useState21(0);
|
|
4448
|
+
const [activeTab, setActiveTab] = useState21("logs");
|
|
3117
4449
|
const duck = useRubberDuck();
|
|
3118
|
-
const [githubFocusedBox, setGithubFocusedBox] =
|
|
3119
|
-
const [jiraState, setJiraState] =
|
|
3120
|
-
const [logsFocusedBox, setLogsFocusedBox] =
|
|
3121
|
-
const
|
|
4450
|
+
const [githubFocusedBox, setGithubFocusedBox] = useState21("remotes");
|
|
4451
|
+
const [jiraState, setJiraState] = useState21("not_configured");
|
|
4452
|
+
const [logsFocusedBox, setLogsFocusedBox] = useState21("history");
|
|
4453
|
+
const [jiraBrowserFocusedBox, setJiraBrowserFocusedBox] = useState21("saved-views");
|
|
4454
|
+
const [jiraBrowserModalOpen, setJiraBrowserModalOpen] = useState21(false);
|
|
4455
|
+
const keybindings = useMemo4(
|
|
3122
4456
|
() => computeKeybindings(focusedView, {
|
|
3123
4457
|
github: { focusedBox: githubFocusedBox },
|
|
3124
4458
|
jira: { jiraState, modalOpen },
|
|
3125
|
-
logs: { focusedBox: logsFocusedBox }
|
|
4459
|
+
logs: { focusedBox: logsFocusedBox },
|
|
4460
|
+
"jira-browser": { focusedBox: jiraBrowserFocusedBox, modalOpen: jiraBrowserModalOpen }
|
|
3126
4461
|
}),
|
|
3127
|
-
[focusedView, githubFocusedBox, jiraState, modalOpen, logsFocusedBox]
|
|
4462
|
+
[focusedView, githubFocusedBox, jiraState, modalOpen, logsFocusedBox, jiraBrowserFocusedBox, jiraBrowserModalOpen]
|
|
3128
4463
|
);
|
|
3129
|
-
const handleLogUpdated =
|
|
4464
|
+
const handleLogUpdated = useCallback11(() => {
|
|
3130
4465
|
setLogRefreshKey((prev) => prev + 1);
|
|
3131
4466
|
}, []);
|
|
3132
|
-
|
|
4467
|
+
const anyModalOpen = modalOpen || jiraBrowserModalOpen;
|
|
4468
|
+
useInput18(
|
|
3133
4469
|
(input, key) => {
|
|
3134
4470
|
if (key.ctrl && input === "c") {
|
|
3135
4471
|
exit();
|
|
@@ -3141,12 +4477,23 @@ function App() {
|
|
|
3141
4477
|
setFocusedView("jira");
|
|
3142
4478
|
}
|
|
3143
4479
|
if (input === "5") {
|
|
3144
|
-
setFocusedView(
|
|
3145
|
-
setLogsFocusedBox("history");
|
|
4480
|
+
setFocusedView(activeTab);
|
|
4481
|
+
if (activeTab === "logs") setLogsFocusedBox("history");
|
|
4482
|
+
if (activeTab === "jira-browser") setJiraBrowserFocusedBox("saved-views");
|
|
3146
4483
|
}
|
|
3147
4484
|
if (input === "6") {
|
|
3148
|
-
setFocusedView(
|
|
3149
|
-
setLogsFocusedBox("viewer");
|
|
4485
|
+
setFocusedView(activeTab);
|
|
4486
|
+
if (activeTab === "logs") setLogsFocusedBox("viewer");
|
|
4487
|
+
if (activeTab === "jira-browser") setJiraBrowserFocusedBox("browser");
|
|
4488
|
+
}
|
|
4489
|
+
if (key.tab) {
|
|
4490
|
+
setActiveTab((current) => {
|
|
4491
|
+
const idx = COLUMN2_TABS.findIndex((t) => t.id === current);
|
|
4492
|
+
const next = (idx + 1) % COLUMN2_TABS.length;
|
|
4493
|
+
const nextTab = COLUMN2_TABS[next];
|
|
4494
|
+
setFocusedView(nextTab.id);
|
|
4495
|
+
return nextTab.id;
|
|
4496
|
+
});
|
|
3150
4497
|
}
|
|
3151
4498
|
if (input === "d") {
|
|
3152
4499
|
duck.toggleDuck();
|
|
@@ -3155,12 +4502,19 @@ function App() {
|
|
|
3155
4502
|
duck.quack();
|
|
3156
4503
|
}
|
|
3157
4504
|
},
|
|
3158
|
-
{ isActive: !
|
|
4505
|
+
{ isActive: !anyModalOpen }
|
|
3159
4506
|
);
|
|
3160
|
-
return /* @__PURE__ */
|
|
3161
|
-
/* @__PURE__ */
|
|
3162
|
-
/* @__PURE__ */
|
|
3163
|
-
|
|
4507
|
+
return /* @__PURE__ */ jsxs21(Box22, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: [
|
|
4508
|
+
/* @__PURE__ */ jsxs21(Box22, { height: 1, flexDirection: "row", columnGap: 1, children: [
|
|
4509
|
+
/* @__PURE__ */ jsx22(Box22, { flexGrow: 1, paddingX: 1, flexBasis: 0, children: /* @__PURE__ */ jsx22(Text19, { color: "gray", children: "Current branch" }) }),
|
|
4510
|
+
/* @__PURE__ */ jsxs21(Box22, { flexGrow: 1, gap: 1, flexBasis: 0, children: [
|
|
4511
|
+
/* @__PURE__ */ jsx22(Text19, { color: "gray", children: "Dashboards" }),
|
|
4512
|
+
COLUMN2_TABS.map((tab) => /* @__PURE__ */ jsx22(Text19, { bold: true, dimColor: activeTab !== tab.id, children: tab.label }, tab.id))
|
|
4513
|
+
] })
|
|
4514
|
+
] }),
|
|
4515
|
+
/* @__PURE__ */ jsxs21(Box22, { flexGrow: 1, flexDirection: "row", columnGap: 1, children: [
|
|
4516
|
+
/* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", flexGrow: 1, flexBasis: 0, children: [
|
|
4517
|
+
/* @__PURE__ */ jsx22(
|
|
3164
4518
|
GitHubView,
|
|
3165
4519
|
{
|
|
3166
4520
|
isActive: focusedView === "github",
|
|
@@ -3168,7 +4522,7 @@ function App() {
|
|
|
3168
4522
|
onLogUpdated: handleLogUpdated
|
|
3169
4523
|
}
|
|
3170
4524
|
),
|
|
3171
|
-
/* @__PURE__ */
|
|
4525
|
+
/* @__PURE__ */ jsx22(
|
|
3172
4526
|
JiraView,
|
|
3173
4527
|
{
|
|
3174
4528
|
isActive: focusedView === "jira",
|
|
@@ -3178,21 +4532,33 @@ function App() {
|
|
|
3178
4532
|
}
|
|
3179
4533
|
)
|
|
3180
4534
|
] }),
|
|
3181
|
-
/* @__PURE__ */
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
4535
|
+
/* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", flexGrow: 1, flexBasis: 0, children: [
|
|
4536
|
+
activeTab === "logs" && /* @__PURE__ */ jsx22(
|
|
4537
|
+
LogsView,
|
|
4538
|
+
{
|
|
4539
|
+
isActive: focusedView === "logs",
|
|
4540
|
+
refreshKey: logRefreshKey,
|
|
4541
|
+
focusedBox: logsFocusedBox,
|
|
4542
|
+
onFocusedBoxChange: setLogsFocusedBox
|
|
4543
|
+
}
|
|
4544
|
+
),
|
|
4545
|
+
activeTab === "jira-browser" && /* @__PURE__ */ jsx22(
|
|
4546
|
+
JiraBrowserView,
|
|
4547
|
+
{
|
|
4548
|
+
isActive: focusedView === "jira-browser",
|
|
4549
|
+
focusedBox: jiraBrowserFocusedBox,
|
|
4550
|
+
onFocusedBoxChange: setJiraBrowserFocusedBox,
|
|
4551
|
+
onModalChange: setJiraBrowserModalOpen,
|
|
4552
|
+
onLogUpdated: handleLogUpdated
|
|
4553
|
+
}
|
|
4554
|
+
)
|
|
4555
|
+
] })
|
|
3190
4556
|
] }),
|
|
3191
|
-
/* @__PURE__ */
|
|
4557
|
+
/* @__PURE__ */ jsx22(
|
|
3192
4558
|
KeybindingsBar,
|
|
3193
4559
|
{
|
|
3194
4560
|
contextBindings: keybindings,
|
|
3195
|
-
modalOpen,
|
|
4561
|
+
modalOpen: anyModalOpen,
|
|
3196
4562
|
duck: { visible: duck.visible, message: duck.message }
|
|
3197
4563
|
}
|
|
3198
4564
|
)
|
|
@@ -3203,31 +4569,31 @@ function App() {
|
|
|
3203
4569
|
import { render as inkRender } from "ink";
|
|
3204
4570
|
|
|
3205
4571
|
// src/lib/Screen.tsx
|
|
3206
|
-
import { useCallback as
|
|
3207
|
-
import { Box as
|
|
3208
|
-
import { jsx as
|
|
4572
|
+
import { useCallback as useCallback12, useEffect as useEffect17, useState as useState22 } from "react";
|
|
4573
|
+
import { Box as Box23, useStdout as useStdout2 } from "ink";
|
|
4574
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
3209
4575
|
function Screen({ children }) {
|
|
3210
4576
|
const { stdout } = useStdout2();
|
|
3211
|
-
const getSize =
|
|
3212
|
-
const [size, setSize] =
|
|
3213
|
-
|
|
4577
|
+
const getSize = useCallback12(() => ({ height: stdout.rows, width: stdout.columns }), [stdout]);
|
|
4578
|
+
const [size, setSize] = useState22(getSize);
|
|
4579
|
+
useEffect17(() => {
|
|
3214
4580
|
const onResize = () => setSize(getSize());
|
|
3215
4581
|
stdout.on("resize", onResize);
|
|
3216
4582
|
return () => {
|
|
3217
4583
|
stdout.off("resize", onResize);
|
|
3218
4584
|
};
|
|
3219
4585
|
}, [stdout, getSize]);
|
|
3220
|
-
return /* @__PURE__ */
|
|
4586
|
+
return /* @__PURE__ */ jsx23(Box23, { height: size.height, width: size.width, children });
|
|
3221
4587
|
}
|
|
3222
4588
|
|
|
3223
4589
|
// src/lib/render.tsx
|
|
3224
|
-
import { jsx as
|
|
4590
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
3225
4591
|
var ENTER_ALT_BUFFER = "\x1B[?1049h";
|
|
3226
4592
|
var EXIT_ALT_BUFFER = "\x1B[?1049l";
|
|
3227
4593
|
var CLEAR_SCREEN = "\x1B[2J\x1B[H";
|
|
3228
4594
|
function render(node, options) {
|
|
3229
4595
|
process.stdout.write(ENTER_ALT_BUFFER + CLEAR_SCREEN);
|
|
3230
|
-
const element = /* @__PURE__ */
|
|
4596
|
+
const element = /* @__PURE__ */ jsx24(Screen, { children: node });
|
|
3231
4597
|
const instance = inkRender(element, options);
|
|
3232
4598
|
setImmediate(() => instance.rerender(element));
|
|
3233
4599
|
const cleanup = () => process.stdout.write(EXIT_ALT_BUFFER);
|
|
@@ -3248,7 +4614,7 @@ function render(node, options) {
|
|
|
3248
4614
|
}
|
|
3249
4615
|
|
|
3250
4616
|
// src/cli.tsx
|
|
3251
|
-
import { jsx as
|
|
4617
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
3252
4618
|
var cli = meow(
|
|
3253
4619
|
`
|
|
3254
4620
|
Usage
|
|
@@ -3281,4 +4647,4 @@ if (cli.flags.cwd) {
|
|
|
3281
4647
|
process.exit(1);
|
|
3282
4648
|
}
|
|
3283
4649
|
}
|
|
3284
|
-
render(/* @__PURE__ */
|
|
4650
|
+
render(/* @__PURE__ */ jsx25(App, {}));
|