patchrelay 0.41.4 → 0.41.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/build-info.json
CHANGED
|
@@ -1,18 +1,45 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useReducer } from "react";
|
|
3
3
|
import { Box, Text, useStdout } from "ink";
|
|
4
|
-
import { IssueRow } from "./IssueRow.js";
|
|
4
|
+
import { IssueRow, estimateIssueRowHeight } from "./IssueRow.js";
|
|
5
5
|
import { StatusBar } from "./StatusBar.js";
|
|
6
6
|
import { HelpBar } from "./HelpBar.js";
|
|
7
7
|
const FIXED_COLS = 8;
|
|
8
8
|
const CHROME_ROWS = 4;
|
|
9
|
-
|
|
9
|
+
export function computeVisibleWindow(issues, selectedIndex, maxRows, cols, titleWidth) {
|
|
10
|
+
if (issues.length === 0)
|
|
11
|
+
return { start: 0, end: 0 };
|
|
12
|
+
const clampedSelected = Math.max(0, Math.min(selectedIndex, issues.length - 1));
|
|
13
|
+
const heights = issues.map((issue, index) => estimateIssueRowHeight(issue, index === clampedSelected, cols, titleWidth));
|
|
14
|
+
let start = clampedSelected;
|
|
15
|
+
let end = clampedSelected + 1;
|
|
16
|
+
let usedRows = heights[clampedSelected] ?? 1;
|
|
17
|
+
while (true) {
|
|
18
|
+
const canAddAbove = start > 0 && usedRows + (heights[start - 1] ?? 1) <= maxRows;
|
|
19
|
+
const canAddBelow = end < issues.length && usedRows + (heights[end] ?? 1) <= maxRows;
|
|
20
|
+
if (!canAddAbove && !canAddBelow)
|
|
21
|
+
break;
|
|
22
|
+
const aboveDistance = clampedSelected - start;
|
|
23
|
+
const belowDistance = end - 1 - clampedSelected;
|
|
24
|
+
const preferAbove = canAddAbove && (!canAddBelow || aboveDistance <= belowDistance);
|
|
25
|
+
if (preferAbove) {
|
|
26
|
+
start -= 1;
|
|
27
|
+
usedRows += heights[start] ?? 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (canAddBelow) {
|
|
31
|
+
usedRows += heights[end] ?? 1;
|
|
32
|
+
end += 1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { start, end };
|
|
36
|
+
}
|
|
10
37
|
export function IssueListView({ issues, allIssues, selectedIndex, connected, lastServerMessageAt, filter, totalCount, frozen, }) {
|
|
11
38
|
const { stdout } = useStdout();
|
|
12
39
|
const cols = stdout?.columns ?? 80;
|
|
13
40
|
const rows = stdout?.rows ?? 24;
|
|
14
41
|
const titleWidth = Math.max(0, cols - FIXED_COLS);
|
|
15
|
-
const
|
|
42
|
+
const maxVisibleRows = Math.max(1, rows - CHROME_ROWS);
|
|
16
43
|
// Periodic refresh for elapsed times
|
|
17
44
|
const [, tick] = useReducer((c) => c + 1, 0);
|
|
18
45
|
useEffect(() => {
|
|
@@ -21,12 +48,9 @@ export function IssueListView({ issues, allIssues, selectedIndex, connected, las
|
|
|
21
48
|
const id = setInterval(tick, 5000);
|
|
22
49
|
return () => clearInterval(id);
|
|
23
50
|
}, [frozen]);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), issues.length - maxVisible));
|
|
27
|
-
}
|
|
28
|
-
const visible = issues.slice(startIndex, startIndex + maxVisible);
|
|
51
|
+
const { start: startIndex, end: endIndex } = computeVisibleWindow(issues, selectedIndex, maxVisibleRows, cols, titleWidth);
|
|
52
|
+
const visible = issues.slice(startIndex, endIndex);
|
|
29
53
|
const hiddenAbove = startIndex;
|
|
30
|
-
const hiddenBelow = Math.max(0, issues.length -
|
|
54
|
+
const hiddenBelow = Math.max(0, issues.length - endIndex);
|
|
31
55
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { issues: issues, totalCount: totalCount, filter: filter, connected: connected, lastServerMessageAt: lastServerMessageAt, allIssues: allIssues, frozen: frozen ?? false }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: issues.length === 0 ? (_jsx(Text, { dimColor: true, children: "No issues match the current filter." })) : (_jsxs(_Fragment, { children: [hiddenAbove > 0 && _jsxs(Text, { dimColor: true, children: [" ", hiddenAbove, " more above"] }), visible.map((issue, i) => (_jsx(IssueRow, { issue: issue, selected: startIndex + i === selectedIndex, titleWidth: titleWidth }, issue.issueKey ?? `${issue.projectId}-${startIndex + i}`))), hiddenBelow > 0 && _jsxs(Text, { dimColor: true, children: [" ", hiddenBelow, " more below"] })] })) }), _jsx(Box, { marginTop: 1, children: _jsx(HelpBar, { view: "list" }) })] }));
|
|
32
56
|
}
|
|
@@ -3,6 +3,7 @@ import { Box, Text } from "ink";
|
|
|
3
3
|
import { hasOpenPr } from "../../pr-state.js";
|
|
4
4
|
import { summarizeIssueStatusNote } from "./issue-status-note.js";
|
|
5
5
|
import { relativeTime, truncate } from "./format-utils.js";
|
|
6
|
+
import { measureRenderedTextRows } from "./layout-measure.js";
|
|
6
7
|
import { hasDisplayPrBlocker, isApprovedReviewState, isAwaitingReviewState, isChangesRequestedReviewState, isRereviewNeeded, prChecksFact, } from "./pr-status.js";
|
|
7
8
|
// ─── State display ──────────────────────────────────────────────
|
|
8
9
|
const TERMINAL_STATES = new Set(["done", "failed", "escalated"]);
|
|
@@ -167,3 +168,35 @@ export function IssueRow({ issue, selected, titleWidth }) {
|
|
|
167
168
|
}
|
|
168
169
|
return (_jsxs(Box, { flexDirection: "column", marginBottom: detail ? 1 : 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: selected ? "blueBright" : "gray", children: selected ? "\u25b8" : " " }), _jsx(Text, { bold: true, children: ` ${key}` }), _jsx(Text, { dimColor: true, children: ` ${relativeTime(issue.updatedAt).padStart(4)}` }), _jsx(Text, { children: ` ` }), _jsx(Text, { color: session.color, children: session.label }), facts.length > 0 && (_jsx(Text, { dimColor: true, children: ` \u00b7 ` })), facts.map((fact, i) => (_jsxs(Text, { children: [i > 0 ? _jsx(Text, { dimColor: true, children: ` \u00b7 ` }) : null, _jsx(Text, { color: fact.color ?? "white", dimColor: !fact.color, children: fact.text })] }, i)))] }), title ? (_jsx(Box, { paddingLeft: 2, children: _jsx(Text, { dimColor: true, children: title }) })) : null, blocker ? (_jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: "yellow", children: blocker }) })) : null, detail ? (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: detail }) })) : null, selected && issue.factoryState && issue.sessionState ? (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { dimColor: true, children: `Debug stage: ${stageLabel(issue)}` }) })) : null] }));
|
|
169
170
|
}
|
|
171
|
+
export function estimateIssueRowHeight(issue, selected, cols, titleWidth) {
|
|
172
|
+
const width = Math.max(20, cols);
|
|
173
|
+
const key = issue.issueKey ?? issue.projectId;
|
|
174
|
+
const tw = titleWidth ?? 60;
|
|
175
|
+
const title = issue.title ? truncate(issue.title, tw) : "";
|
|
176
|
+
const detail = selected ? summarizeIssueStatusNote(issue.statusNote) : undefined;
|
|
177
|
+
const session = sessionDisplay(issue);
|
|
178
|
+
const facts = buildFacts(issue, selected);
|
|
179
|
+
const blocker = selected ? blockerText(issue) : null;
|
|
180
|
+
const isTerminal = TERMINAL_STATES.has(effectiveState(issue));
|
|
181
|
+
if (isTerminal && !selected) {
|
|
182
|
+
return 1;
|
|
183
|
+
}
|
|
184
|
+
const line1Parts = [
|
|
185
|
+
`${selected ? "\u25b8" : " "} ${key}`,
|
|
186
|
+
relativeTime(issue.updatedAt).padStart(4),
|
|
187
|
+
session.label,
|
|
188
|
+
...facts.map((fact) => fact.text),
|
|
189
|
+
];
|
|
190
|
+
let rows = measureRenderedTextRows(line1Parts.join(" · "), width);
|
|
191
|
+
if (title)
|
|
192
|
+
rows += measureRenderedTextRows(title, Math.max(8, width - 2));
|
|
193
|
+
if (blocker)
|
|
194
|
+
rows += measureRenderedTextRows(blocker, Math.max(8, width - 2));
|
|
195
|
+
if (detail)
|
|
196
|
+
rows += measureRenderedTextRows(detail, Math.max(8, width - 4));
|
|
197
|
+
if (selected && issue.factoryState && issue.sessionState)
|
|
198
|
+
rows += 1;
|
|
199
|
+
if (detail)
|
|
200
|
+
rows += 1;
|
|
201
|
+
return Math.max(1, rows);
|
|
202
|
+
}
|