@kud/gh-ink 0.66.1 → 0.67.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 +29 -0
- package/dist/index.d.ts +48 -4
- package/dist/index.js +311 -112
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -32,6 +32,35 @@ the same panel drops into a full-screen CLI or a single pane of a dashboard. The
|
|
|
32
32
|
core (`@kud/gh`) decides the semantic token; this layer maps it to a colour. Same
|
|
33
33
|
seam as `@kud/jenkins` → `@kud/jenkins-ink`.
|
|
34
34
|
|
|
35
|
+
## Inbox extensions
|
|
36
|
+
|
|
37
|
+
A host contributes verbs and screens to the inbox through `InboxExtension`:
|
|
38
|
+
|
|
39
|
+
| Field | What |
|
|
40
|
+
| ----------- | ----------------------------------------------------------------------------------------------------------- |
|
|
41
|
+
| `key` | The keypress that opens it, matched after the inbox's own bindings, so an extension never shadows them. |
|
|
42
|
+
| `scope` | `"item"` acts on the selected row and earns a place in its action menu; `"global"` does not. |
|
|
43
|
+
| `menuGroup` | `"act"` (does something to the row) or `"open"` (opens something); absent or `"other"` joins the quiet end. |
|
|
44
|
+
| `hint` | Short footer label. The action menu capitalises it for the row ("submit" → "Submit"); the legend keeps `title`. |
|
|
45
|
+
| `icon` | Nerd-font glyph for the menu row, as a `"\uF4FA"` escape. Shown only in nerd icon mode (see below). |
|
|
46
|
+
| `body` | `(onExit, target) => ReactNode` — rendered inside the inbox frame; claims header/footer via `useChrome`. |
|
|
47
|
+
|
|
48
|
+
Nerd-font icons render only when the host calls `setIconMode("nerd")` from
|
|
49
|
+
`@kud/ink-ui` before the first render. In the default text mode the menu's icon
|
|
50
|
+
column is dropped entirely — no stand-in, nothing to align — so a terminal
|
|
51
|
+
without a Nerd Font loses nothing but decoration. The built-ins use these
|
|
52
|
+
codepoints; sibling host verbs should match them rather than invent neighbours:
|
|
53
|
+
|
|
54
|
+
| Verb | Codepoint |
|
|
55
|
+
| ----------------------------- | --------- |
|
|
56
|
+
| Submit / Resubmit | U+F4FA / U+F46A |
|
|
57
|
+
| Land | U+F419 |
|
|
58
|
+
| Open PR / in browser / ticket | U+F440 / U+F465 / U+F41B |
|
|
59
|
+
| Switch here / tab / pane | U+EBCB / U+EAE4 / U+EB56 |
|
|
60
|
+
| Copy URL / repo / branch | U+F44C / U+F401 / U+F418 |
|
|
61
|
+
| Unsubscribe / Mute / Remove reviewer | U+F478 / U+F466 / U+F468 |
|
|
62
|
+
| Close PR / and delete / issue | U+F4DC / U+F48E / U+F41D |
|
|
63
|
+
|
|
35
64
|
## Development
|
|
36
65
|
|
|
37
66
|
```sh
|
package/dist/index.d.ts
CHANGED
|
@@ -417,6 +417,25 @@ declare const healthLegend: [Health, string][];
|
|
|
417
417
|
*/
|
|
418
418
|
declare const truncate: (str: string, max: number) => string;
|
|
419
419
|
|
|
420
|
+
type ChromeHints = [string, string][];
|
|
421
|
+
type ChromeSpec = {
|
|
422
|
+
scope?: string;
|
|
423
|
+
hints?: ChromeHints;
|
|
424
|
+
};
|
|
425
|
+
type ChromeState = ChromeSpec & {
|
|
426
|
+
setChrome: (spec: ChromeSpec) => void;
|
|
427
|
+
};
|
|
428
|
+
/**
|
|
429
|
+
* Claim a word in the inbox chrome while mounted: `scope` becomes the header
|
|
430
|
+
* breadcrumb (e.g. `Resubmit · acme/api-gateway#1234`), `hints` the footer's
|
|
431
|
+
* verbs. The shell appends its own back/quit tail, so a body passes only its
|
|
432
|
+
* own keys — never `esc`, `q` or `?`.
|
|
433
|
+
*
|
|
434
|
+
* Returns whether a shell claimed it: a view mounted outside the inbox (where
|
|
435
|
+
* the context is null) draws its own chrome instead, so the same component
|
|
436
|
+
* renders framed standalone and chromeless inside the shell.
|
|
437
|
+
*/
|
|
438
|
+
declare const useChrome: (spec: ChromeSpec) => boolean;
|
|
420
439
|
type ExtensionTarget = {
|
|
421
440
|
item?: AnyItem;
|
|
422
441
|
ciJob?: string;
|
|
@@ -438,6 +457,8 @@ interface InboxExtension {
|
|
|
438
457
|
key: string;
|
|
439
458
|
hint?: string;
|
|
440
459
|
scope?: "item" | "global";
|
|
460
|
+
menuGroup?: "act" | "open" | "other";
|
|
461
|
+
icon?: string;
|
|
441
462
|
drills?: AnyItem["kind"][];
|
|
442
463
|
body: (onExit: () => void, target?: ExtensionTarget) => ReactNode;
|
|
443
464
|
commands?: (target: ExtensionTarget) => Command[];
|
|
@@ -525,12 +546,20 @@ type SettledResult = {
|
|
|
525
546
|
ok: false;
|
|
526
547
|
reason: string;
|
|
527
548
|
};
|
|
549
|
+
type ActionGroup = "act" | "open" | "switch" | "copy" | "quiet" | "close" | "confirm";
|
|
528
550
|
type Action = {
|
|
529
551
|
label: string;
|
|
530
552
|
hint: string;
|
|
531
553
|
run: () => void;
|
|
532
554
|
subActions?: Action[];
|
|
555
|
+
group?: ActionGroup;
|
|
556
|
+
icon?: string;
|
|
533
557
|
};
|
|
558
|
+
type MenuGap = {
|
|
559
|
+
gap: true;
|
|
560
|
+
};
|
|
561
|
+
type MenuRow = Action | MenuGap;
|
|
562
|
+
declare const isMenuGap: (row: MenuRow) => row is MenuGap;
|
|
534
563
|
type JiraTransition = {
|
|
535
564
|
/** What the action menu shows. Yours to word; nothing matches on it. */
|
|
536
565
|
label: string;
|
|
@@ -701,8 +730,16 @@ declare const CiStatusLine: ({ state, job, }: {
|
|
|
701
730
|
state: CiStatusState;
|
|
702
731
|
job?: string;
|
|
703
732
|
}) => React.JSX.Element;
|
|
733
|
+
declare const menuRowsOf: (actions: Action[]) => MenuRow[];
|
|
734
|
+
/**
|
|
735
|
+
* What tone a menu row wears: `"error"` for the close rows (top-level and
|
|
736
|
+
* confirm alike, never `Cancel`), nothing for everything else. The menu's one
|
|
737
|
+
* meaningful colour, and the only one — every other distinction on the menu
|
|
738
|
+
* is glyph, word or weight, never hue alone.
|
|
739
|
+
*/
|
|
740
|
+
declare const menuTone: (row: Action) => "error" | undefined;
|
|
704
741
|
declare const useActionMenu: () => {
|
|
705
|
-
actions:
|
|
742
|
+
actions: MenuRow[] | null;
|
|
706
743
|
cursor: number;
|
|
707
744
|
open: (next: Action[]) => void;
|
|
708
745
|
close: () => void;
|
|
@@ -713,10 +750,17 @@ declare const useActionMenu: () => {
|
|
|
713
750
|
escape?: boolean;
|
|
714
751
|
}) => boolean;
|
|
715
752
|
};
|
|
716
|
-
declare const ActionMenu: ({ item, actions, cursor, }: {
|
|
753
|
+
declare const ActionMenu: ({ item, actions, cursor, cols, }: {
|
|
717
754
|
item: AnyItem;
|
|
718
|
-
actions:
|
|
755
|
+
actions: MenuRow[];
|
|
719
756
|
cursor: number;
|
|
757
|
+
/**
|
|
758
|
+
* Content columns the menu may take. The width is fixed from the longest
|
|
759
|
+
* row — clamp(longest + 4, 40, 56), capped at `cols - 4` — so it never
|
|
760
|
+
* resizes as the cursor moves. A host beside a rail passes what the list
|
|
761
|
+
* has; everywhere else the frame's own width is the right answer.
|
|
762
|
+
*/
|
|
763
|
+
cols?: number;
|
|
720
764
|
}) => React.JSX.Element;
|
|
721
765
|
declare const signatureOf: (sections: Section[]) => string;
|
|
722
766
|
declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jiraKeyRe, jiraSetupHint, jiraTransitions, hasCiStatus, ciJob, ciFetcher, ciPollMs, stripFetcher, stripLabel, stripPollMs, watchPath, watchDebounceMs, watchJitterMs, extensions, tabHelp, emptyHint, liveLabel, onSettled, }: {
|
|
@@ -827,4 +871,4 @@ declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
|
|
|
827
871
|
*/
|
|
828
872
|
declare const parsePatterns: (value: string) => string[];
|
|
829
873
|
|
|
830
|
-
export { type Action, ActionMenu, App, COLS, CiStatusLine, type CiStatusState, type Command, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxExtension, type JiraTransition, type LiveLabel, MIN_SIDEBAR_COLS, PrRow, type PrRowProps, type Rails, type RepoFilter, type RowAnnouncement, type RowMotion, SIDEBAR_COLS, type SettledResult, SidePanel, type Sidebar, type SidebarRow, type StatusStrip, StatusStripLine, type StripItem, type StripState, type StripStatusState, type TrailingColumns, type TrailingShed, ageLabel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, numberColumnsOf, openInTab, parsePatterns, railCapacity, railHeights, railRowsOf, railWidth, railsOf, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sectionShedOf, signatureOf, stripGlyph, stripLayout, stripSignature, toCiStatusState, trailingColumnsOf, truncate, useActionMenu, windowCount, withHeaders, withoutItem, workCount, writeCache };
|
|
874
|
+
export { type Action, type ActionGroup, ActionMenu, App, COLS, type ChromeHints, type ChromeSpec, type ChromeState, CiStatusLine, type CiStatusState, type Command, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxExtension, type JiraTransition, type LiveLabel, MIN_SIDEBAR_COLS, type MenuGap, type MenuRow, PrRow, type PrRowProps, type Rails, type RepoFilter, type RowAnnouncement, type RowMotion, SIDEBAR_COLS, type SettledResult, SidePanel, type Sidebar, type SidebarRow, type StatusStrip, StatusStripLine, type StripItem, type StripState, type StripStatusState, type TrailingColumns, type TrailingShed, ageLabel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, isMenuGap, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, menuRowsOf, menuTone, moveCursor, numberColumnsOf, openInTab, parsePatterns, railCapacity, railHeights, railRowsOf, railWidth, railsOf, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sectionShedOf, signatureOf, stripGlyph, stripLayout, stripSignature, toCiStatusState, trailingColumnsOf, truncate, useActionMenu, useChrome, windowCount, withHeaders, withoutItem, workCount, writeCache };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React2, { createContext, useState, useRef, useEffect,
|
|
1
|
+
import React2, { createContext, useState, useRef, useContext, useEffect, useCallback, useMemo } from 'react';
|
|
2
2
|
import { useWindowSize, useInput, Text, Box } from 'ink';
|
|
3
|
-
import { colors, ScrollView, TextInput, Pill, useAppKeys, LoadingScreen, StatusMessage,
|
|
3
|
+
import { colors, ScrollView, TextInput, Pill, getIconMode, FooterHints, useAppKeys, LoadingScreen, StatusMessage, useFilterMode, useListCursor, CommandPalette, Tabs, ProgressBar, pillWidth } from '@kud/ink-ui';
|
|
4
4
|
import { isPassCheck, isFailCheck, isInconclusiveCheck, resolveThread, unresolveThread, replyToThread, rerunFailedRun, mergePr, reRequestReviewer } from '@kud/gh';
|
|
5
5
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
6
6
|
import { PIN_MARK, sizeOf, sizePartsOf, depthOf, impliedLabels, labelPriority, checkoutDirs, profileOf, inboxConfig, relativeTime, filterBySearch, filterByRepos, filterByOrigin } from '@kud/gh-workflow';
|
|
@@ -1161,6 +1161,18 @@ var StatusStripLine = ({
|
|
|
1161
1161
|
age ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${age}` }) : null
|
|
1162
1162
|
] });
|
|
1163
1163
|
};
|
|
1164
|
+
var InboxChromeContext = createContext(null);
|
|
1165
|
+
var useChrome = (spec) => {
|
|
1166
|
+
const chrome = useContext(InboxChromeContext);
|
|
1167
|
+
const setChrome = chrome?.setChrome;
|
|
1168
|
+
const scope = spec.scope;
|
|
1169
|
+
const hintsKey = JSON.stringify(spec.hints ?? null);
|
|
1170
|
+
useEffect(() => {
|
|
1171
|
+
if (!setChrome) return;
|
|
1172
|
+
setChrome({ scope, hints: spec.hints });
|
|
1173
|
+
}, [setChrome, scope, hintsKey]);
|
|
1174
|
+
return chrome !== null;
|
|
1175
|
+
};
|
|
1164
1176
|
|
|
1165
1177
|
// src/inbox/cache.ts
|
|
1166
1178
|
var cacheTtlMs = () => inboxConfig().cacheTtlMs;
|
|
@@ -1412,6 +1424,7 @@ var reconcile = (prev, fresh, now, holdMs = HOLD_MS) => {
|
|
|
1412
1424
|
return out;
|
|
1413
1425
|
};
|
|
1414
1426
|
var quietly = $({ quiet: true });
|
|
1427
|
+
var isMenuGap = (row) => "gap" in row;
|
|
1415
1428
|
var healthSentence = (item) => {
|
|
1416
1429
|
const glyph = displayFor(item.health).glyph.trim();
|
|
1417
1430
|
const d = item.detail;
|
|
@@ -1898,6 +1911,14 @@ var drillLabel = (item) => {
|
|
|
1898
1911
|
if (item.kind === "pr") return "Open PR";
|
|
1899
1912
|
return "Drill in";
|
|
1900
1913
|
};
|
|
1914
|
+
var sameChrome = (a, b) => {
|
|
1915
|
+
if (a.scope !== b.scope) return false;
|
|
1916
|
+
if (a.hints === b.hints) return true;
|
|
1917
|
+
if (!a.hints || !b.hints || a.hints.length !== b.hints.length) return false;
|
|
1918
|
+
return a.hints.every(
|
|
1919
|
+
([key, label], i) => b.hints[i]?.[0] === key && b.hints[i]?.[1] === label
|
|
1920
|
+
);
|
|
1921
|
+
};
|
|
1901
1922
|
var UNSUBSCRIBE_MUTATION = `
|
|
1902
1923
|
mutation($id: ID!) {
|
|
1903
1924
|
updateSubscription(input: { subscribableId: $id, state: UNSUBSCRIBED }) {
|
|
@@ -1948,6 +1969,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1948
1969
|
const open = {
|
|
1949
1970
|
label: "Open in browser",
|
|
1950
1971
|
hint: "o",
|
|
1972
|
+
group: "open",
|
|
1973
|
+
icon: "\uF465",
|
|
1951
1974
|
run: () => {
|
|
1952
1975
|
quietly`open ${item.url}`.catch(() => {
|
|
1953
1976
|
});
|
|
@@ -1957,6 +1980,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1957
1980
|
const copyUrl = {
|
|
1958
1981
|
label: "Copy URL",
|
|
1959
1982
|
hint: "c",
|
|
1983
|
+
group: "copy",
|
|
1984
|
+
icon: "\uF44C",
|
|
1960
1985
|
run: () => {
|
|
1961
1986
|
clipboard(item.url);
|
|
1962
1987
|
const label = item.kind === "task" ? item.key : `#${item.number}`;
|
|
@@ -1968,6 +1993,11 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1968
1993
|
const drillAction = drill || mountable && onOpenView ? {
|
|
1969
1994
|
label: drillLabel(item),
|
|
1970
1995
|
hint: "d",
|
|
1996
|
+
group: "open",
|
|
1997
|
+
// Only the PR drill names its glyph: "View issue" and "View ticket"
|
|
1998
|
+
// have no nerd-font verb of their own, and an icon that says nothing
|
|
1999
|
+
// the label does not already say is decoration, not information.
|
|
2000
|
+
icon: item.kind === "pr" ? "\uF440" : void 0,
|
|
1971
2001
|
run: () => {
|
|
1972
2002
|
if (onOpenView?.(item)) return;
|
|
1973
2003
|
if (drill) {
|
|
@@ -1983,6 +2013,10 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1983
2013
|
base.push({
|
|
1984
2014
|
label: "Move status",
|
|
1985
2015
|
hint: "t",
|
|
2016
|
+
// The task's act: changing its state, the one mutation this menu owns.
|
|
2017
|
+
// Guarded by the caller's own condition above — without configured
|
|
2018
|
+
// transitions there is no such row, and none is invented here.
|
|
2019
|
+
group: "act",
|
|
1986
2020
|
run: () => {
|
|
1987
2021
|
},
|
|
1988
2022
|
subActions: jiraTransitions.map(
|
|
@@ -2016,22 +2050,40 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2016
2050
|
)
|
|
2017
2051
|
});
|
|
2018
2052
|
}
|
|
2019
|
-
return base;
|
|
2053
|
+
return byGroup(base);
|
|
2020
2054
|
}
|
|
2021
2055
|
const actions = drillAction ? [drillAction, open, copyUrl] : [open, copyUrl];
|
|
2022
2056
|
const closing = [];
|
|
2023
2057
|
actions.push({
|
|
2024
2058
|
label: "Copy repo name",
|
|
2025
2059
|
hint: "r",
|
|
2060
|
+
group: "copy",
|
|
2061
|
+
icon: "\uF401",
|
|
2026
2062
|
run: () => {
|
|
2027
2063
|
clipboard(item.repo);
|
|
2028
2064
|
showFlash(`\u2713 Copied ${item.repo}`);
|
|
2029
2065
|
}
|
|
2030
2066
|
});
|
|
2067
|
+
actions.push({
|
|
2068
|
+
label: "Unsubscribe",
|
|
2069
|
+
hint: "u",
|
|
2070
|
+
group: "quiet",
|
|
2071
|
+
icon: "\uF478",
|
|
2072
|
+
run: () => {
|
|
2073
|
+
showFlash(`\u22EF Unsubscribing from #${item.number}\u2026`);
|
|
2074
|
+
void unsubscribeFrom(item).then(() => showFlash(`\u2713 Unsubscribed from #${item.number}`)).catch((e) => showFlash(`\u2717 #${item.number}: ${explainGhAction(e)}`));
|
|
2075
|
+
}
|
|
2076
|
+
});
|
|
2031
2077
|
if (item.kind === "pr" && item.standing === "queued" && login) {
|
|
2032
2078
|
actions.push({
|
|
2033
2079
|
label: "Remove me as reviewer",
|
|
2034
2080
|
hint: "x",
|
|
2081
|
+
// Quiet, beside Unsubscribe: both are "less of this PR for me" — no more
|
|
2082
|
+
// review requests, no more notifications. Guarded by the caller's own
|
|
2083
|
+
// condition above, like "Move status" is by its: without a standing
|
|
2084
|
+
// review request there is no such row, and none is invented here.
|
|
2085
|
+
group: "quiet",
|
|
2086
|
+
icon: "\uF468",
|
|
2035
2087
|
run: () => {
|
|
2036
2088
|
onRemove?.(item);
|
|
2037
2089
|
showFlash(`\u22EF Removing you from #${item.number}\u2026`);
|
|
@@ -2053,18 +2105,12 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2053
2105
|
}
|
|
2054
2106
|
});
|
|
2055
2107
|
}
|
|
2056
|
-
actions.push({
|
|
2057
|
-
label: "Unsubscribe",
|
|
2058
|
-
hint: "u",
|
|
2059
|
-
run: () => {
|
|
2060
|
-
showFlash(`\u22EF Unsubscribing from #${item.number}\u2026`);
|
|
2061
|
-
void unsubscribeFrom(item).then(() => showFlash(`\u2713 Unsubscribed from #${item.number}`)).catch((e) => showFlash(`\u2717 #${item.number}: ${explainGhAction(e)}`));
|
|
2062
|
-
}
|
|
2063
|
-
});
|
|
2064
2108
|
if (item.kind === "pr" && item.branch) {
|
|
2065
2109
|
actions.push({
|
|
2066
2110
|
label: "Copy branch name",
|
|
2067
2111
|
hint: "b",
|
|
2112
|
+
group: "copy",
|
|
2113
|
+
icon: "\uF418",
|
|
2068
2114
|
run: () => {
|
|
2069
2115
|
clipboard(item.branch);
|
|
2070
2116
|
showFlash(`\u2713 Copied ${item.branch}`);
|
|
@@ -2075,6 +2121,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2075
2121
|
actions.push({
|
|
2076
2122
|
label: "Switch here",
|
|
2077
2123
|
hint: "s",
|
|
2124
|
+
group: "switch",
|
|
2125
|
+
icon: "\uEBCB",
|
|
2078
2126
|
run: () => {
|
|
2079
2127
|
const script = [
|
|
2080
2128
|
"delay 0.5",
|
|
@@ -2095,16 +2143,20 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2095
2143
|
}
|
|
2096
2144
|
if (item.kind === "issue") {
|
|
2097
2145
|
actions.push({
|
|
2098
|
-
label: "Open
|
|
2146
|
+
label: "Open in new tab",
|
|
2099
2147
|
hint: "j",
|
|
2148
|
+
group: "switch",
|
|
2149
|
+
icon: "\uEAE4",
|
|
2100
2150
|
run: () => {
|
|
2101
2151
|
showFlash(`\u22EF Opening ${item.repo}\u2026`);
|
|
2102
2152
|
void jumpToRepo(item.repo, "", login).then(() => showFlash(`\u2197 Opened ${item.repo} in new tab`)).catch(() => showFlash("\u2717 Jump failed"));
|
|
2103
2153
|
}
|
|
2104
2154
|
});
|
|
2105
2155
|
actions.push({
|
|
2106
|
-
label: "Open
|
|
2156
|
+
label: "Open in new pane",
|
|
2107
2157
|
hint: "p",
|
|
2158
|
+
group: "switch",
|
|
2159
|
+
icon: "\uEB56",
|
|
2108
2160
|
run: () => {
|
|
2109
2161
|
showFlash(`\u22EF Opening pane for ${item.repo}\u2026`);
|
|
2110
2162
|
void jumpToRepoPane(item.repo, "", login).then(() => showFlash(`\u2197 Opened ${item.repo} in new pane`)).catch(() => showFlash("\u2717 Pane failed"));
|
|
@@ -2113,12 +2165,15 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2113
2165
|
actions.push({
|
|
2114
2166
|
label: "Close issue",
|
|
2115
2167
|
hint: "",
|
|
2168
|
+
group: "close",
|
|
2169
|
+
icon: "\uF41D",
|
|
2116
2170
|
run: () => {
|
|
2117
2171
|
},
|
|
2118
2172
|
subActions: [
|
|
2119
2173
|
{
|
|
2120
2174
|
label: `Close #${item.number}`,
|
|
2121
2175
|
hint: "",
|
|
2176
|
+
group: "confirm",
|
|
2122
2177
|
run: () => {
|
|
2123
2178
|
onRemove?.(item);
|
|
2124
2179
|
showFlash(`\u2713 Closed #${item.number}`);
|
|
@@ -2137,7 +2192,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2137
2192
|
);
|
|
2138
2193
|
}
|
|
2139
2194
|
},
|
|
2140
|
-
{ label: "Cancel", hint: "", run: () => {
|
|
2195
|
+
{ label: "Cancel", hint: "", group: "confirm", run: () => {
|
|
2141
2196
|
} }
|
|
2142
2197
|
]
|
|
2143
2198
|
});
|
|
@@ -2146,6 +2201,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2146
2201
|
actions.push({
|
|
2147
2202
|
label: "Switch in new tab",
|
|
2148
2203
|
hint: "j",
|
|
2204
|
+
group: "switch",
|
|
2205
|
+
icon: "\uEAE4",
|
|
2149
2206
|
run: () => {
|
|
2150
2207
|
showFlash(`\u22EF Jumping to ${item.repo}\u2026`);
|
|
2151
2208
|
void jumpToRepo(item.repo, item.branch ?? "", login).then(() => showFlash(`\u2197 Opened ${item.repo} in new tab`)).catch(() => showFlash("\u2717 Jump failed"));
|
|
@@ -2154,6 +2211,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2154
2211
|
actions.push({
|
|
2155
2212
|
label: "Switch in new pane",
|
|
2156
2213
|
hint: "p",
|
|
2214
|
+
group: "switch",
|
|
2215
|
+
icon: "\uEB56",
|
|
2157
2216
|
run: () => {
|
|
2158
2217
|
showFlash(`\u22EF Opening pane for ${item.repo}\u2026`);
|
|
2159
2218
|
void jumpToRepoPane(item.repo, item.branch ?? "", login).then(() => showFlash(`\u2197 Opened ${item.repo} in new pane`)).catch(() => showFlash("\u2717 Pane failed"));
|
|
@@ -2165,6 +2224,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2165
2224
|
actions.push({
|
|
2166
2225
|
label: `Open ${jiraKey} in Jira`,
|
|
2167
2226
|
hint: "t",
|
|
2227
|
+
group: "open",
|
|
2168
2228
|
run: () => openInJira(jiraBase, jiraKey, showFlash)
|
|
2169
2229
|
});
|
|
2170
2230
|
}
|
|
@@ -2172,12 +2232,15 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2172
2232
|
closing.push({
|
|
2173
2233
|
label: "Close PR",
|
|
2174
2234
|
hint: "",
|
|
2235
|
+
group: "close",
|
|
2236
|
+
icon: "\uF4DC",
|
|
2175
2237
|
run: () => {
|
|
2176
2238
|
},
|
|
2177
2239
|
subActions: [
|
|
2178
2240
|
{
|
|
2179
2241
|
label: `Close #${item.number}`,
|
|
2180
2242
|
hint: "",
|
|
2243
|
+
group: "confirm",
|
|
2181
2244
|
run: () => {
|
|
2182
2245
|
onRemove?.(item);
|
|
2183
2246
|
showFlash(`\u2713 Closed #${item.number}`);
|
|
@@ -2196,20 +2259,25 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2196
2259
|
);
|
|
2197
2260
|
}
|
|
2198
2261
|
},
|
|
2199
|
-
{ label: "Cancel", hint: "", run: () => {
|
|
2262
|
+
{ label: "Cancel", hint: "", group: "confirm", run: () => {
|
|
2200
2263
|
} }
|
|
2201
2264
|
]
|
|
2202
2265
|
});
|
|
2203
2266
|
if (item.branch) {
|
|
2204
2267
|
closing.push({
|
|
2205
|
-
|
|
2268
|
+
// "and", not "+": the row is a sentence like every other, and the key
|
|
2269
|
+
// column carries the hint — symbols are not shortcuts here.
|
|
2270
|
+
label: "Close PR and delete branch",
|
|
2206
2271
|
hint: "",
|
|
2272
|
+
group: "close",
|
|
2273
|
+
icon: "\uF48E",
|
|
2207
2274
|
run: () => {
|
|
2208
2275
|
},
|
|
2209
2276
|
subActions: [
|
|
2210
2277
|
{
|
|
2211
|
-
label: `Close #${item.number}
|
|
2278
|
+
label: `Close #${item.number} and delete ${item.branch}`,
|
|
2212
2279
|
hint: "",
|
|
2280
|
+
group: "confirm",
|
|
2213
2281
|
run: () => {
|
|
2214
2282
|
onRemove?.(item);
|
|
2215
2283
|
showFlash(`\u2713 Closed #${item.number} and deleted ${item.branch}`);
|
|
@@ -2221,16 +2289,20 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2221
2289
|
});
|
|
2222
2290
|
}
|
|
2223
2291
|
},
|
|
2224
|
-
{ label: "Cancel", hint: "", run: () => {
|
|
2292
|
+
{ label: "Cancel", hint: "", group: "confirm", run: () => {
|
|
2225
2293
|
} }
|
|
2226
2294
|
]
|
|
2227
2295
|
});
|
|
2228
2296
|
}
|
|
2229
2297
|
}
|
|
2298
|
+
const extensionLabel = (e) => e.hint ? e.hint.charAt(0).toUpperCase() + e.hint.slice(1) : e.title;
|
|
2299
|
+
const extensionGroup = (e) => e.menuGroup === "act" ? "act" : e.menuGroup === "open" ? "open" : "quiet";
|
|
2230
2300
|
for (const e of itemExtensions(ext?.extensions))
|
|
2231
2301
|
actions.push({
|
|
2232
|
-
label: e
|
|
2302
|
+
label: extensionLabel(e),
|
|
2233
2303
|
hint: e.key,
|
|
2304
|
+
group: extensionGroup(e),
|
|
2305
|
+
icon: e.icon,
|
|
2234
2306
|
run: () => ext?.onOpenExt?.(e.id, {
|
|
2235
2307
|
item,
|
|
2236
2308
|
login,
|
|
@@ -2239,7 +2311,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2239
2311
|
})
|
|
2240
2312
|
});
|
|
2241
2313
|
actions.push(...closing);
|
|
2242
|
-
return actions;
|
|
2314
|
+
return byGroup(actions);
|
|
2243
2315
|
};
|
|
2244
2316
|
var agoText = (ms) => {
|
|
2245
2317
|
const d = (Date.now() - ms) / 1e3;
|
|
@@ -2526,26 +2598,60 @@ var ItemRow = ({
|
|
|
2526
2598
|
}
|
|
2527
2599
|
);
|
|
2528
2600
|
};
|
|
2601
|
+
var GROUP_RANK = {
|
|
2602
|
+
act: 0,
|
|
2603
|
+
open: 1,
|
|
2604
|
+
switch: 2,
|
|
2605
|
+
copy: 3,
|
|
2606
|
+
quiet: 4,
|
|
2607
|
+
close: 5,
|
|
2608
|
+
confirm: 6
|
|
2609
|
+
};
|
|
2610
|
+
var groupRank = (action) => action.group === void 0 ? 7 : GROUP_RANK[action.group];
|
|
2611
|
+
var menuRowsOf = (actions) => {
|
|
2612
|
+
const sorted = byGroup(actions);
|
|
2613
|
+
const rows = [];
|
|
2614
|
+
sorted.forEach((action, i) => {
|
|
2615
|
+
if (i > 0 && sorted[i - 1].group !== action.group) rows.push({ gap: true });
|
|
2616
|
+
rows.push(action);
|
|
2617
|
+
});
|
|
2618
|
+
return rows;
|
|
2619
|
+
};
|
|
2620
|
+
var byGroup = (actions) => [...actions].sort((a, b) => groupRank(a) - groupRank(b));
|
|
2621
|
+
var isConfirmRows = (rows) => rows.length > 0 && rows.every((row) => isMenuGap(row) || row.group === "confirm");
|
|
2622
|
+
var menuTone = (row) => row.group === "close" || row.group === "confirm" && row.label !== "Cancel" ? "error" : void 0;
|
|
2529
2623
|
var useActionMenu = () => {
|
|
2530
2624
|
const [actions, setActions] = useState(null);
|
|
2531
2625
|
const [cursor, setCursor] = useState(0);
|
|
2532
2626
|
const open = (next) => {
|
|
2533
2627
|
if (next.length === 0) return;
|
|
2534
2628
|
setCursor(0);
|
|
2535
|
-
setActions(next);
|
|
2629
|
+
setActions(menuRowsOf(next));
|
|
2536
2630
|
};
|
|
2537
2631
|
const handleKey = (key) => {
|
|
2538
2632
|
if (!actions) return false;
|
|
2539
|
-
if (key.upArrow)
|
|
2540
|
-
|
|
2633
|
+
if (key.upArrow)
|
|
2634
|
+
setCursor((c) => {
|
|
2635
|
+
let n = c - 1;
|
|
2636
|
+
while (n > 0 && isMenuGap(actions[n])) n -= 1;
|
|
2637
|
+
return isMenuGap(actions[n]) ? c : n;
|
|
2638
|
+
});
|
|
2639
|
+
if (key.downArrow)
|
|
2640
|
+
setCursor((c) => {
|
|
2641
|
+
let n = c + 1;
|
|
2642
|
+
while (n < actions.length - 1 && isMenuGap(actions[n])) n += 1;
|
|
2643
|
+
return isMenuGap(actions[n]) ? c : n;
|
|
2644
|
+
});
|
|
2541
2645
|
if (key.return) {
|
|
2542
|
-
const
|
|
2543
|
-
if (
|
|
2544
|
-
|
|
2545
|
-
|
|
2646
|
+
const row = actions[cursor];
|
|
2647
|
+
if (!row || isMenuGap(row)) return true;
|
|
2648
|
+
if (row.subActions) {
|
|
2649
|
+
const rows = menuRowsOf(row.subActions);
|
|
2650
|
+
setCursor(isConfirmRows(rows) ? rows.length - 1 : 0);
|
|
2651
|
+
setActions(rows);
|
|
2546
2652
|
} else {
|
|
2547
2653
|
setActions(null);
|
|
2548
|
-
|
|
2654
|
+
row.run();
|
|
2549
2655
|
}
|
|
2550
2656
|
}
|
|
2551
2657
|
if (key.escape) setActions(null);
|
|
@@ -2556,29 +2662,81 @@ var useActionMenu = () => {
|
|
|
2556
2662
|
var ActionMenu = ({
|
|
2557
2663
|
item,
|
|
2558
2664
|
actions,
|
|
2559
|
-
cursor
|
|
2665
|
+
cursor,
|
|
2666
|
+
cols = COLS
|
|
2560
2667
|
}) => {
|
|
2561
|
-
const
|
|
2668
|
+
const showIcons = getIconMode() === "nerd" && actions.some((row) => !isMenuGap(row) && row.icon !== void 0);
|
|
2669
|
+
const keyWidth = Math.max(
|
|
2670
|
+
0,
|
|
2671
|
+
...actions.flatMap(
|
|
2672
|
+
(row) => isMenuGap(row) ? [] : [[...row.hint ?? ""].length]
|
|
2673
|
+
)
|
|
2674
|
+
);
|
|
2675
|
+
const contentWidth = Math.max(
|
|
2676
|
+
0,
|
|
2677
|
+
...actions.flatMap(
|
|
2678
|
+
(row) => isMenuGap(row) ? [] : [
|
|
2679
|
+
2 + (showIcons ? 2 : 0) + [...row.label].length + (keyWidth > 0 ? 1 + keyWidth : 0)
|
|
2680
|
+
]
|
|
2681
|
+
)
|
|
2682
|
+
);
|
|
2683
|
+
const width = Math.min(
|
|
2684
|
+
Math.max(contentWidth + 4, 40),
|
|
2685
|
+
56,
|
|
2686
|
+
Math.max(20, cols - 4)
|
|
2687
|
+
);
|
|
2688
|
+
const inner = Math.max(1, width - 4);
|
|
2689
|
+
const confirming = isConfirmRows(actions);
|
|
2690
|
+
const isCloseRow = (row) => menuTone(row) === "error";
|
|
2691
|
+
const number = item.kind === "pr" || item.kind === "issue" ? item.number : null;
|
|
2692
|
+
const leadNum = item.kind === "task" ? item.key : number !== null ? `#${number}` : "";
|
|
2693
|
+
const lead = leadNum ? `${leadNum} \xB7 ` : "";
|
|
2694
|
+
const rest = confirming ? `Close #${number}?` : item.kind === "task" ? item.summary : item.kind === "pr" || item.kind === "issue" ? item.title : "";
|
|
2695
|
+
const restText = [...lead].length + [...rest].length <= inner ? rest : [...rest].slice(0, Math.max(0, inner - [...lead].length - 1)).join("") + "\u2026";
|
|
2562
2696
|
return /* @__PURE__ */ jsxs(
|
|
2563
2697
|
Box,
|
|
2564
2698
|
{
|
|
2565
2699
|
flexDirection: "column",
|
|
2566
2700
|
flexShrink: 0,
|
|
2701
|
+
width,
|
|
2567
2702
|
borderStyle: "round",
|
|
2568
2703
|
borderColor: colors.info,
|
|
2569
2704
|
backgroundColor: OVERLAY_BG,
|
|
2570
2705
|
paddingX: 1,
|
|
2571
2706
|
marginTop: 1,
|
|
2572
2707
|
children: [
|
|
2573
|
-
/* @__PURE__ */ jsx(Text3, { color: colors.
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
/* @__PURE__ */ jsx(Text3, {
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2708
|
+
confirming ? /* @__PURE__ */ jsx(Text3, { color: colors.error, bold: true, children: restText }) : /* @__PURE__ */ jsxs(Text3, { children: [
|
|
2709
|
+
/* @__PURE__ */ jsx(Text3, { color: colors.info, bold: true, children: leadNum }),
|
|
2710
|
+
leadNum ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " \xB7 " }) : null,
|
|
2711
|
+
/* @__PURE__ */ jsx(Text3, { children: restText })
|
|
2712
|
+
] }),
|
|
2713
|
+
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: "\u2500".repeat(inner) }),
|
|
2714
|
+
actions.map(
|
|
2715
|
+
(row, i) => isMenuGap(row) ? /* @__PURE__ */ jsx(Text3, { children: " " }, `gap-${i}`) : /* @__PURE__ */ jsxs(Box, { width: inner, children: [
|
|
2716
|
+
/* @__PURE__ */ jsx(Box, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx(Text3, { color: colors.info, children: i === cursor ? "\u276F " : " " }) }),
|
|
2717
|
+
showIcons ? /* @__PURE__ */ jsx(Box, { width: 2, flexShrink: 0, children: row.icon ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: row.icon }) : /* @__PURE__ */ jsx(Text3, { children: " " }) }) : null,
|
|
2718
|
+
/* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(
|
|
2719
|
+
Text3,
|
|
2720
|
+
{
|
|
2721
|
+
bold: i === cursor,
|
|
2722
|
+
color: isCloseRow(row) ? colors.error : void 0,
|
|
2723
|
+
children: row.label
|
|
2724
|
+
}
|
|
2725
|
+
) }),
|
|
2726
|
+
keyWidth > 0 ? /* @__PURE__ */ jsx(Box, { width: keyWidth, flexShrink: 0, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text3, { color: colors.secondary, children: row.hint ?? "" }) }) : null
|
|
2727
|
+
] }, `${i}:${row.label}`)
|
|
2728
|
+
),
|
|
2729
|
+
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: "\u2500".repeat(inner) }),
|
|
2730
|
+
/* @__PURE__ */ jsx(
|
|
2731
|
+
FooterHints,
|
|
2732
|
+
{
|
|
2733
|
+
hints: [
|
|
2734
|
+
["\u2191\u2193", "move"],
|
|
2735
|
+
["\u21B5", "run"],
|
|
2736
|
+
["esc", "close"]
|
|
2737
|
+
]
|
|
2738
|
+
}
|
|
2739
|
+
)
|
|
2582
2740
|
]
|
|
2583
2741
|
}
|
|
2584
2742
|
);
|
|
@@ -2854,6 +3012,7 @@ var BrowseScreen = ({
|
|
|
2854
3012
|
fetchedAt,
|
|
2855
3013
|
refreshError,
|
|
2856
3014
|
hidden,
|
|
3015
|
+
overlayBody,
|
|
2857
3016
|
peelRef,
|
|
2858
3017
|
onTyping,
|
|
2859
3018
|
onOpenPr,
|
|
@@ -3603,7 +3762,8 @@ var BrowseScreen = ({
|
|
|
3603
3762
|
}));
|
|
3604
3763
|
return { items, run, message };
|
|
3605
3764
|
}, [palette, activeItem, extensions, jiraBase, jiraKeyRe, jiraSetupHint]);
|
|
3606
|
-
|
|
3765
|
+
const chrome = useContext(InboxChromeContext);
|
|
3766
|
+
const overlayOpen = overlayBody != null;
|
|
3607
3767
|
const overlay = palette !== null ? /* @__PURE__ */ jsx(
|
|
3608
3768
|
CommandPalette,
|
|
3609
3769
|
{
|
|
@@ -3648,7 +3808,8 @@ var BrowseScreen = ({
|
|
|
3648
3808
|
{
|
|
3649
3809
|
item: activeItem,
|
|
3650
3810
|
actions: menu.actions,
|
|
3651
|
-
cursor: menu.cursor
|
|
3811
|
+
cursor: menu.cursor,
|
|
3812
|
+
cols: listCols
|
|
3652
3813
|
}
|
|
3653
3814
|
) : null;
|
|
3654
3815
|
return /* @__PURE__ */ jsxs(
|
|
@@ -3667,7 +3828,7 @@ var BrowseScreen = ({
|
|
|
3667
3828
|
brand,
|
|
3668
3829
|
sections: localSections,
|
|
3669
3830
|
login,
|
|
3670
|
-
scopeLabel: origin?.label,
|
|
3831
|
+
scopeLabel: overlayOpen ? chrome?.scope ?? origin?.label : origin?.label,
|
|
3671
3832
|
budgetLabel: budgetNotice(budget)?.label,
|
|
3672
3833
|
budgetCritical: budgetNotice(budget)?.critical,
|
|
3673
3834
|
refreshing,
|
|
@@ -3690,7 +3851,7 @@ var BrowseScreen = ({
|
|
|
3690
3851
|
/* @__PURE__ */ jsx(Text3, { bold: true, color: colors.accent, children: " \u26A1 auto-refresh paused to save API budget" }),
|
|
3691
3852
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: " r refreshes anyway" })
|
|
3692
3853
|
] }) : null,
|
|
3693
|
-
/* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(
|
|
3854
|
+
overlayOpen ? null : /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(
|
|
3694
3855
|
Tabs,
|
|
3695
3856
|
{
|
|
3696
3857
|
active: section.id,
|
|
@@ -3723,18 +3884,24 @@ var BrowseScreen = ({
|
|
|
3723
3884
|
}))
|
|
3724
3885
|
}
|
|
3725
3886
|
) }),
|
|
3726
|
-
search != null ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
3887
|
+
!overlayOpen && search != null ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
3727
3888
|
/* @__PURE__ */ jsx(Text3, { color: colors.info, children: " / " }),
|
|
3728
3889
|
/* @__PURE__ */ jsx(Text3, { children: search }),
|
|
3729
3890
|
searchInput ? /* @__PURE__ */ jsx(Text3, { color: colors.info, children: "\u258F" }) : null,
|
|
3730
3891
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: ` ${matchCount} match${matchCount !== 1 ? "es" : ""} ${filter.hints.map(([k, label]) => `${k} ${label}`).join(" \xB7 ")}` })
|
|
3731
|
-
] }) : repoFilter.size > 0 ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
3892
|
+
] }) : !overlayOpen && repoFilter.size > 0 ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
3732
3893
|
/* @__PURE__ */ jsx(Text3, { color: colors.accent, children: " \u25C9 " }),
|
|
3733
3894
|
/* @__PURE__ */ jsx(Text3, { children: `${repoFilter.size} repo${repoFilter.size !== 1 ? "s" : ""}` }),
|
|
3734
3895
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: " f edit \xB7 esc clear" })
|
|
3735
3896
|
] }) : null,
|
|
3736
3897
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
3737
|
-
|
|
3898
|
+
overlayOpen ? (
|
|
3899
|
+
// The overlay owns this band: the same frame, the same header above
|
|
3900
|
+
// and footer below, the body where the list was. Fixed to the list's
|
|
3901
|
+
// own height so the frame holds still between browse and drill, and
|
|
3902
|
+
// clipped rather than spilling past the footer on a long body.
|
|
3903
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", height: listHeight, overflow: "hidden", children: overlayBody })
|
|
3904
|
+
) : /* @__PURE__ */ jsxs(
|
|
3738
3905
|
Box,
|
|
3739
3906
|
{
|
|
3740
3907
|
flexDirection: "column",
|
|
@@ -3794,7 +3961,7 @@ var BrowseScreen = ({
|
|
|
3794
3961
|
]
|
|
3795
3962
|
}
|
|
3796
3963
|
),
|
|
3797
|
-
showRail ? /* @__PURE__ */ jsx(
|
|
3964
|
+
showRail && !overlayOpen ? /* @__PURE__ */ jsx(
|
|
3798
3965
|
SidePanel,
|
|
3799
3966
|
{
|
|
3800
3967
|
sidebar: rails,
|
|
@@ -3806,7 +3973,20 @@ var BrowseScreen = ({
|
|
|
3806
3973
|
}
|
|
3807
3974
|
) : null
|
|
3808
3975
|
] }),
|
|
3809
|
-
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: flash ? /* @__PURE__ */ jsx(Text3, { color: colors.success, children: flash }) :
|
|
3976
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: flash ? /* @__PURE__ */ jsx(Text3, { color: colors.success, children: flash }) : overlayOpen ? (
|
|
3977
|
+
// The overlay's own verbs, plus the way back. `page="nested"` is what
|
|
3978
|
+
// appends `⌫ back` and `q quit`, so a body passes only its own keys
|
|
3979
|
+
// through `useChrome` — the same contract ink-ui's `Page` offers its
|
|
3980
|
+
// nested pages, without re-laying-out the browse frame as one.
|
|
3981
|
+
/* @__PURE__ */ jsx(
|
|
3982
|
+
FooterHints,
|
|
3983
|
+
{
|
|
3984
|
+
hints: chrome?.hints ?? [],
|
|
3985
|
+
page: "nested",
|
|
3986
|
+
help: false
|
|
3987
|
+
}
|
|
3988
|
+
)
|
|
3989
|
+
) : /* @__PURE__ */ jsx(FooterHints, { hints }) })
|
|
3810
3990
|
]
|
|
3811
3991
|
}
|
|
3812
3992
|
);
|
|
@@ -3873,6 +4053,15 @@ var App = ({
|
|
|
3873
4053
|
const browsePeel = useRef(null);
|
|
3874
4054
|
const drillPeel = useRef(null);
|
|
3875
4055
|
const [typing, setTyping] = useState(false);
|
|
4056
|
+
const [chromeSpec, setChromeSpec] = useState({});
|
|
4057
|
+
const setChrome = useCallback(
|
|
4058
|
+
(next) => setChromeSpec((prev) => sameChrome(prev, next) ? prev : next),
|
|
4059
|
+
[]
|
|
4060
|
+
);
|
|
4061
|
+
const chromeValue = useMemo(
|
|
4062
|
+
() => ({ scope: chromeSpec.scope, hints: chromeSpec.hints, setChrome }),
|
|
4063
|
+
[chromeSpec, setChrome]
|
|
4064
|
+
);
|
|
3876
4065
|
useAppKeys({
|
|
3877
4066
|
isActive: !typing,
|
|
3878
4067
|
onBack: () => {
|
|
@@ -4236,7 +4425,10 @@ var App = ({
|
|
|
4236
4425
|
extId: state.extId,
|
|
4237
4426
|
target: state.target
|
|
4238
4427
|
} : null;
|
|
4239
|
-
const toBrowse = () =>
|
|
4428
|
+
const toBrowse = () => {
|
|
4429
|
+
setChromeSpec({});
|
|
4430
|
+
setState({ phase: "browse", sections: state.sections, login: state.login });
|
|
4431
|
+
};
|
|
4240
4432
|
const markLeaving = (target) => {
|
|
4241
4433
|
if (state.phase !== "browse") toBrowse();
|
|
4242
4434
|
if (!leavingSince.current.has(target.url))
|
|
@@ -4264,82 +4456,89 @@ var App = ({
|
|
|
4264
4456
|
);
|
|
4265
4457
|
}, MERGED_HOLD_MS);
|
|
4266
4458
|
};
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4459
|
+
const overlayBody = overlay?.kind === "pr" || overlay?.kind === "issue" ? detailFor?.({
|
|
4460
|
+
item: overlay.item,
|
|
4461
|
+
kind: overlay.kind,
|
|
4462
|
+
login: state.login,
|
|
4463
|
+
registerPeel: (peel) => {
|
|
4464
|
+
drillPeel.current = peel;
|
|
4465
|
+
},
|
|
4466
|
+
onTyping: setTyping,
|
|
4467
|
+
onBack: toBrowse,
|
|
4468
|
+
onRefresh: applyOrRefresh,
|
|
4469
|
+
onRemove: markLeaving,
|
|
4470
|
+
onMerged: markMerged
|
|
4471
|
+
}) : overlay?.kind === "ext" ? extensions?.find((e) => e.id === overlay.extId)?.body(toBrowse, overlay.target) : null;
|
|
4472
|
+
return /* @__PURE__ */ jsx(InboxChromeContext.Provider, { value: chromeValue, children: /* @__PURE__ */ jsx(
|
|
4473
|
+
BrowseScreen,
|
|
4474
|
+
{
|
|
4475
|
+
brand: brandOf(title),
|
|
4476
|
+
sections: state.sections,
|
|
4477
|
+
login: state.login,
|
|
4478
|
+
jiraBase,
|
|
4479
|
+
jiraKeyRe,
|
|
4480
|
+
jiraSetupHint,
|
|
4481
|
+
jiraTransitions,
|
|
4482
|
+
onRefresh: applyOrRefresh,
|
|
4483
|
+
onActed,
|
|
4484
|
+
onSettled,
|
|
4485
|
+
refreshing,
|
|
4486
|
+
hasPending: pending !== null,
|
|
4487
|
+
pendingSummary,
|
|
4488
|
+
appliedSummary: transients.size > 0 ? appliedSummary : "",
|
|
4489
|
+
fetchedAt,
|
|
4490
|
+
refreshError: refreshError ?? void 0,
|
|
4491
|
+
origin,
|
|
4492
|
+
budget,
|
|
4493
|
+
skippedForBudget,
|
|
4494
|
+
hidden: overlay !== null,
|
|
4495
|
+
overlayBody: overlayBody ?? void 0,
|
|
4496
|
+
peelRef: browsePeel,
|
|
4497
|
+
onTyping: setTyping,
|
|
4498
|
+
mergedUrls,
|
|
4499
|
+
leavingUrls,
|
|
4500
|
+
transitSince,
|
|
4501
|
+
onLeave: markLeaving,
|
|
4502
|
+
transients,
|
|
4503
|
+
onTabChange: setVisibleTab,
|
|
4504
|
+
sidebar,
|
|
4505
|
+
liveLabel,
|
|
4506
|
+
ciStatusState: hasCiStatus ? ciStatusState : void 0,
|
|
4507
|
+
ciJob,
|
|
4508
|
+
stripState: hasStrip ? stripState : void 0,
|
|
4509
|
+
stripLabel,
|
|
4510
|
+
tabHelp,
|
|
4511
|
+
onOpenPr: (item) => {
|
|
4512
|
+
setChromeSpec({});
|
|
4513
|
+
setState({
|
|
4307
4514
|
phase: "pr",
|
|
4308
4515
|
item,
|
|
4309
4516
|
sections: state.sections,
|
|
4310
4517
|
login: state.login
|
|
4311
|
-
})
|
|
4312
|
-
|
|
4518
|
+
});
|
|
4519
|
+
},
|
|
4520
|
+
onOpenIssue: (item) => {
|
|
4521
|
+
setChromeSpec({});
|
|
4522
|
+
setState({
|
|
4313
4523
|
phase: "issue",
|
|
4314
4524
|
item,
|
|
4315
4525
|
sections: state.sections,
|
|
4316
4526
|
login: state.login
|
|
4317
|
-
})
|
|
4318
|
-
|
|
4527
|
+
});
|
|
4528
|
+
},
|
|
4529
|
+
onOpenExt: (id, target) => {
|
|
4530
|
+
setChromeSpec({});
|
|
4531
|
+
setState({
|
|
4319
4532
|
phase: "ext",
|
|
4320
4533
|
extId: id,
|
|
4321
4534
|
target,
|
|
4322
4535
|
sections: state.sections,
|
|
4323
4536
|
login: state.login
|
|
4324
|
-
})
|
|
4325
|
-
extensions
|
|
4326
|
-
}
|
|
4327
|
-
),
|
|
4328
|
-
(overlay?.kind === "pr" || overlay?.kind === "issue") && detailFor?.({
|
|
4329
|
-
item: overlay.item,
|
|
4330
|
-
kind: overlay.kind,
|
|
4331
|
-
login: state.login,
|
|
4332
|
-
registerPeel: (peel) => {
|
|
4333
|
-
drillPeel.current = peel;
|
|
4537
|
+
});
|
|
4334
4538
|
},
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
onRemove: markLeaving,
|
|
4339
|
-
onMerged: markMerged
|
|
4340
|
-
}),
|
|
4341
|
-
overlay?.kind === "ext" && extensions?.find((e) => e.id === overlay.extId)?.body(toBrowse, overlay.target)
|
|
4342
|
-
] });
|
|
4539
|
+
extensions
|
|
4540
|
+
}
|
|
4541
|
+
) });
|
|
4343
4542
|
};
|
|
4344
4543
|
|
|
4345
4544
|
// src/inbox/filter.ts
|
|
@@ -4356,4 +4555,4 @@ var matchesFilter = (repo, filter) => {
|
|
|
4356
4555
|
};
|
|
4357
4556
|
var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
|
|
4358
4557
|
|
|
4359
|
-
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, MIN_SIDEBAR_COLS, PrRow, SIDEBAR_COLS, SidePanel, StatusStripLine, ageLabel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, numberColumnsOf, openInTab, parsePatterns, railCapacity, railHeights, railRowsOf, railWidth, railsOf, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sectionShedOf, signatureOf, stripGlyph, stripLayout, stripSignature, toCiStatusState, trailingColumnsOf, truncate, useActionMenu, windowCount, withHeaders, withoutItem, workCount, writeCache };
|
|
4558
|
+
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, MIN_SIDEBAR_COLS, PrRow, SIDEBAR_COLS, SidePanel, StatusStripLine, ageLabel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, isMenuGap, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, menuRowsOf, menuTone, moveCursor, numberColumnsOf, openInTab, parsePatterns, railCapacity, railHeights, railRowsOf, railWidth, railsOf, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sectionShedOf, signatureOf, stripGlyph, stripLayout, stripSignature, toCiStatusState, trailingColumnsOf, truncate, useActionMenu, useChrome, windowCount, withHeaders, withoutItem, workCount, writeCache };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.67.0",
|
|
4
4
|
"description": "Ink components for rendering GitHub PR review comments and health — controlled, presentation-only, built on @kud/ink-ui and fed by @kud/gh.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
"react": ">=19"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@kud/gh": "0.
|
|
52
|
-
"@kud/gh-workflow": "0.14.
|
|
51
|
+
"@kud/gh": "0.20.0",
|
|
52
|
+
"@kud/gh-workflow": "0.14.1",
|
|
53
53
|
"@kud/ink-ui": "0.33.0",
|
|
54
54
|
"zx": "8.8.5"
|
|
55
55
|
},
|