patchrelay 0.41.3 → 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
|
+
}
|
|
@@ -120,6 +120,7 @@ export class DesiredStageRecorder {
|
|
|
120
120
|
triggerEvent: params.normalized.triggerEvent,
|
|
121
121
|
delegated,
|
|
122
122
|
});
|
|
123
|
+
const terminalRunRelease = effectiveRunRelease.release && terminal;
|
|
123
124
|
const commitIssueUpdate = () => {
|
|
124
125
|
const record = this.db.issues.upsertIssue({
|
|
125
126
|
projectId: params.project.id,
|
|
@@ -148,6 +149,7 @@ export class DesiredStageRecorder {
|
|
|
148
149
|
...(clearPending ? { pendingRunType: null, pendingRunContextJson: null } : {}),
|
|
149
150
|
...(agentSessionId !== undefined ? { agentSessionId } : {}),
|
|
150
151
|
...(effectiveRunRelease.release ? { activeRunId: null } : {}),
|
|
152
|
+
...(terminalRunRelease ? { factoryState: "done", pendingRunType: null, pendingRunContextJson: null } : {}),
|
|
151
153
|
...(blockerPausedImplementation ? { factoryState: "delegated" } : {}),
|
|
152
154
|
...(undelegation.factoryState ? { factoryState: undelegation.factoryState } : {}),
|
|
153
155
|
});
|