@kud/gh-ink 0.66.1 → 0.68.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 +322 -115
- 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,29 @@ var StatusStripLine = ({
|
|
|
1161
1161
|
age ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${age}` }) : null
|
|
1162
1162
|
] });
|
|
1163
1163
|
};
|
|
1164
|
+
var SEARCH_GLYPH_NERD = "\uF422";
|
|
1165
|
+
var searchGlyph = (nerd) => nerd ? SEARCH_GLYPH_NERD : "/";
|
|
1166
|
+
var searchBarState = ({
|
|
1167
|
+
typing,
|
|
1168
|
+
nerd
|
|
1169
|
+
}) => ({
|
|
1170
|
+
glyph: searchGlyph(nerd),
|
|
1171
|
+
glyphColor: typing ? colors.info : colors.muted,
|
|
1172
|
+
caret: typing,
|
|
1173
|
+
hints: typing ? "\u2191\u2193 move \xB7 \u21B5 keep \xB7 esc clear" : "filtered \xB7 keys active \xB7 esc clear"
|
|
1174
|
+
});
|
|
1175
|
+
var InboxChromeContext = createContext(null);
|
|
1176
|
+
var useChrome = (spec) => {
|
|
1177
|
+
const chrome = useContext(InboxChromeContext);
|
|
1178
|
+
const setChrome = chrome?.setChrome;
|
|
1179
|
+
const scope = spec.scope;
|
|
1180
|
+
const hintsKey = JSON.stringify(spec.hints ?? null);
|
|
1181
|
+
useEffect(() => {
|
|
1182
|
+
if (!setChrome) return;
|
|
1183
|
+
setChrome({ scope, hints: spec.hints });
|
|
1184
|
+
}, [setChrome, scope, hintsKey]);
|
|
1185
|
+
return chrome !== null;
|
|
1186
|
+
};
|
|
1164
1187
|
|
|
1165
1188
|
// src/inbox/cache.ts
|
|
1166
1189
|
var cacheTtlMs = () => inboxConfig().cacheTtlMs;
|
|
@@ -1412,6 +1435,7 @@ var reconcile = (prev, fresh, now, holdMs = HOLD_MS) => {
|
|
|
1412
1435
|
return out;
|
|
1413
1436
|
};
|
|
1414
1437
|
var quietly = $({ quiet: true });
|
|
1438
|
+
var isMenuGap = (row) => "gap" in row;
|
|
1415
1439
|
var healthSentence = (item) => {
|
|
1416
1440
|
const glyph = displayFor(item.health).glyph.trim();
|
|
1417
1441
|
const d = item.detail;
|
|
@@ -1898,6 +1922,14 @@ var drillLabel = (item) => {
|
|
|
1898
1922
|
if (item.kind === "pr") return "Open PR";
|
|
1899
1923
|
return "Drill in";
|
|
1900
1924
|
};
|
|
1925
|
+
var sameChrome = (a, b) => {
|
|
1926
|
+
if (a.scope !== b.scope) return false;
|
|
1927
|
+
if (a.hints === b.hints) return true;
|
|
1928
|
+
if (!a.hints || !b.hints || a.hints.length !== b.hints.length) return false;
|
|
1929
|
+
return a.hints.every(
|
|
1930
|
+
([key, label], i) => b.hints[i]?.[0] === key && b.hints[i]?.[1] === label
|
|
1931
|
+
);
|
|
1932
|
+
};
|
|
1901
1933
|
var UNSUBSCRIBE_MUTATION = `
|
|
1902
1934
|
mutation($id: ID!) {
|
|
1903
1935
|
updateSubscription(input: { subscribableId: $id, state: UNSUBSCRIBED }) {
|
|
@@ -1948,6 +1980,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1948
1980
|
const open = {
|
|
1949
1981
|
label: "Open in browser",
|
|
1950
1982
|
hint: "o",
|
|
1983
|
+
group: "open",
|
|
1984
|
+
icon: "\uF465",
|
|
1951
1985
|
run: () => {
|
|
1952
1986
|
quietly`open ${item.url}`.catch(() => {
|
|
1953
1987
|
});
|
|
@@ -1957,6 +1991,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1957
1991
|
const copyUrl = {
|
|
1958
1992
|
label: "Copy URL",
|
|
1959
1993
|
hint: "c",
|
|
1994
|
+
group: "copy",
|
|
1995
|
+
icon: "\uF44C",
|
|
1960
1996
|
run: () => {
|
|
1961
1997
|
clipboard(item.url);
|
|
1962
1998
|
const label = item.kind === "task" ? item.key : `#${item.number}`;
|
|
@@ -1968,6 +2004,11 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1968
2004
|
const drillAction = drill || mountable && onOpenView ? {
|
|
1969
2005
|
label: drillLabel(item),
|
|
1970
2006
|
hint: "d",
|
|
2007
|
+
group: "open",
|
|
2008
|
+
// Only the PR drill names its glyph: "View issue" and "View ticket"
|
|
2009
|
+
// have no nerd-font verb of their own, and an icon that says nothing
|
|
2010
|
+
// the label does not already say is decoration, not information.
|
|
2011
|
+
icon: item.kind === "pr" ? "\uF440" : void 0,
|
|
1971
2012
|
run: () => {
|
|
1972
2013
|
if (onOpenView?.(item)) return;
|
|
1973
2014
|
if (drill) {
|
|
@@ -1983,6 +2024,10 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1983
2024
|
base.push({
|
|
1984
2025
|
label: "Move status",
|
|
1985
2026
|
hint: "t",
|
|
2027
|
+
// The task's act: changing its state, the one mutation this menu owns.
|
|
2028
|
+
// Guarded by the caller's own condition above — without configured
|
|
2029
|
+
// transitions there is no such row, and none is invented here.
|
|
2030
|
+
group: "act",
|
|
1986
2031
|
run: () => {
|
|
1987
2032
|
},
|
|
1988
2033
|
subActions: jiraTransitions.map(
|
|
@@ -2016,22 +2061,40 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2016
2061
|
)
|
|
2017
2062
|
});
|
|
2018
2063
|
}
|
|
2019
|
-
return base;
|
|
2064
|
+
return byGroup(base);
|
|
2020
2065
|
}
|
|
2021
2066
|
const actions = drillAction ? [drillAction, open, copyUrl] : [open, copyUrl];
|
|
2022
2067
|
const closing = [];
|
|
2023
2068
|
actions.push({
|
|
2024
2069
|
label: "Copy repo name",
|
|
2025
2070
|
hint: "r",
|
|
2071
|
+
group: "copy",
|
|
2072
|
+
icon: "\uF401",
|
|
2026
2073
|
run: () => {
|
|
2027
2074
|
clipboard(item.repo);
|
|
2028
2075
|
showFlash(`\u2713 Copied ${item.repo}`);
|
|
2029
2076
|
}
|
|
2030
2077
|
});
|
|
2078
|
+
actions.push({
|
|
2079
|
+
label: "Unsubscribe",
|
|
2080
|
+
hint: "u",
|
|
2081
|
+
group: "quiet",
|
|
2082
|
+
icon: "\uF478",
|
|
2083
|
+
run: () => {
|
|
2084
|
+
showFlash(`\u22EF Unsubscribing from #${item.number}\u2026`);
|
|
2085
|
+
void unsubscribeFrom(item).then(() => showFlash(`\u2713 Unsubscribed from #${item.number}`)).catch((e) => showFlash(`\u2717 #${item.number}: ${explainGhAction(e)}`));
|
|
2086
|
+
}
|
|
2087
|
+
});
|
|
2031
2088
|
if (item.kind === "pr" && item.standing === "queued" && login) {
|
|
2032
2089
|
actions.push({
|
|
2033
2090
|
label: "Remove me as reviewer",
|
|
2034
2091
|
hint: "x",
|
|
2092
|
+
// Quiet, beside Unsubscribe: both are "less of this PR for me" — no more
|
|
2093
|
+
// review requests, no more notifications. Guarded by the caller's own
|
|
2094
|
+
// condition above, like "Move status" is by its: without a standing
|
|
2095
|
+
// review request there is no such row, and none is invented here.
|
|
2096
|
+
group: "quiet",
|
|
2097
|
+
icon: "\uF468",
|
|
2035
2098
|
run: () => {
|
|
2036
2099
|
onRemove?.(item);
|
|
2037
2100
|
showFlash(`\u22EF Removing you from #${item.number}\u2026`);
|
|
@@ -2053,18 +2116,12 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2053
2116
|
}
|
|
2054
2117
|
});
|
|
2055
2118
|
}
|
|
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
2119
|
if (item.kind === "pr" && item.branch) {
|
|
2065
2120
|
actions.push({
|
|
2066
2121
|
label: "Copy branch name",
|
|
2067
2122
|
hint: "b",
|
|
2123
|
+
group: "copy",
|
|
2124
|
+
icon: "\uF418",
|
|
2068
2125
|
run: () => {
|
|
2069
2126
|
clipboard(item.branch);
|
|
2070
2127
|
showFlash(`\u2713 Copied ${item.branch}`);
|
|
@@ -2075,6 +2132,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2075
2132
|
actions.push({
|
|
2076
2133
|
label: "Switch here",
|
|
2077
2134
|
hint: "s",
|
|
2135
|
+
group: "switch",
|
|
2136
|
+
icon: "\uEBCB",
|
|
2078
2137
|
run: () => {
|
|
2079
2138
|
const script = [
|
|
2080
2139
|
"delay 0.5",
|
|
@@ -2095,16 +2154,20 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2095
2154
|
}
|
|
2096
2155
|
if (item.kind === "issue") {
|
|
2097
2156
|
actions.push({
|
|
2098
|
-
label: "Open
|
|
2157
|
+
label: "Open in new tab",
|
|
2099
2158
|
hint: "j",
|
|
2159
|
+
group: "switch",
|
|
2160
|
+
icon: "\uEAE4",
|
|
2100
2161
|
run: () => {
|
|
2101
2162
|
showFlash(`\u22EF Opening ${item.repo}\u2026`);
|
|
2102
2163
|
void jumpToRepo(item.repo, "", login).then(() => showFlash(`\u2197 Opened ${item.repo} in new tab`)).catch(() => showFlash("\u2717 Jump failed"));
|
|
2103
2164
|
}
|
|
2104
2165
|
});
|
|
2105
2166
|
actions.push({
|
|
2106
|
-
label: "Open
|
|
2167
|
+
label: "Open in new pane",
|
|
2107
2168
|
hint: "p",
|
|
2169
|
+
group: "switch",
|
|
2170
|
+
icon: "\uEB56",
|
|
2108
2171
|
run: () => {
|
|
2109
2172
|
showFlash(`\u22EF Opening pane for ${item.repo}\u2026`);
|
|
2110
2173
|
void jumpToRepoPane(item.repo, "", login).then(() => showFlash(`\u2197 Opened ${item.repo} in new pane`)).catch(() => showFlash("\u2717 Pane failed"));
|
|
@@ -2113,12 +2176,15 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2113
2176
|
actions.push({
|
|
2114
2177
|
label: "Close issue",
|
|
2115
2178
|
hint: "",
|
|
2179
|
+
group: "close",
|
|
2180
|
+
icon: "\uF41D",
|
|
2116
2181
|
run: () => {
|
|
2117
2182
|
},
|
|
2118
2183
|
subActions: [
|
|
2119
2184
|
{
|
|
2120
2185
|
label: `Close #${item.number}`,
|
|
2121
2186
|
hint: "",
|
|
2187
|
+
group: "confirm",
|
|
2122
2188
|
run: () => {
|
|
2123
2189
|
onRemove?.(item);
|
|
2124
2190
|
showFlash(`\u2713 Closed #${item.number}`);
|
|
@@ -2137,7 +2203,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2137
2203
|
);
|
|
2138
2204
|
}
|
|
2139
2205
|
},
|
|
2140
|
-
{ label: "Cancel", hint: "", run: () => {
|
|
2206
|
+
{ label: "Cancel", hint: "", group: "confirm", run: () => {
|
|
2141
2207
|
} }
|
|
2142
2208
|
]
|
|
2143
2209
|
});
|
|
@@ -2146,6 +2212,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2146
2212
|
actions.push({
|
|
2147
2213
|
label: "Switch in new tab",
|
|
2148
2214
|
hint: "j",
|
|
2215
|
+
group: "switch",
|
|
2216
|
+
icon: "\uEAE4",
|
|
2149
2217
|
run: () => {
|
|
2150
2218
|
showFlash(`\u22EF Jumping to ${item.repo}\u2026`);
|
|
2151
2219
|
void jumpToRepo(item.repo, item.branch ?? "", login).then(() => showFlash(`\u2197 Opened ${item.repo} in new tab`)).catch(() => showFlash("\u2717 Jump failed"));
|
|
@@ -2154,6 +2222,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2154
2222
|
actions.push({
|
|
2155
2223
|
label: "Switch in new pane",
|
|
2156
2224
|
hint: "p",
|
|
2225
|
+
group: "switch",
|
|
2226
|
+
icon: "\uEB56",
|
|
2157
2227
|
run: () => {
|
|
2158
2228
|
showFlash(`\u22EF Opening pane for ${item.repo}\u2026`);
|
|
2159
2229
|
void jumpToRepoPane(item.repo, item.branch ?? "", login).then(() => showFlash(`\u2197 Opened ${item.repo} in new pane`)).catch(() => showFlash("\u2717 Pane failed"));
|
|
@@ -2165,6 +2235,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2165
2235
|
actions.push({
|
|
2166
2236
|
label: `Open ${jiraKey} in Jira`,
|
|
2167
2237
|
hint: "t",
|
|
2238
|
+
group: "open",
|
|
2168
2239
|
run: () => openInJira(jiraBase, jiraKey, showFlash)
|
|
2169
2240
|
});
|
|
2170
2241
|
}
|
|
@@ -2172,12 +2243,15 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2172
2243
|
closing.push({
|
|
2173
2244
|
label: "Close PR",
|
|
2174
2245
|
hint: "",
|
|
2246
|
+
group: "close",
|
|
2247
|
+
icon: "\uF4DC",
|
|
2175
2248
|
run: () => {
|
|
2176
2249
|
},
|
|
2177
2250
|
subActions: [
|
|
2178
2251
|
{
|
|
2179
2252
|
label: `Close #${item.number}`,
|
|
2180
2253
|
hint: "",
|
|
2254
|
+
group: "confirm",
|
|
2181
2255
|
run: () => {
|
|
2182
2256
|
onRemove?.(item);
|
|
2183
2257
|
showFlash(`\u2713 Closed #${item.number}`);
|
|
@@ -2196,20 +2270,25 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2196
2270
|
);
|
|
2197
2271
|
}
|
|
2198
2272
|
},
|
|
2199
|
-
{ label: "Cancel", hint: "", run: () => {
|
|
2273
|
+
{ label: "Cancel", hint: "", group: "confirm", run: () => {
|
|
2200
2274
|
} }
|
|
2201
2275
|
]
|
|
2202
2276
|
});
|
|
2203
2277
|
if (item.branch) {
|
|
2204
2278
|
closing.push({
|
|
2205
|
-
|
|
2279
|
+
// "and", not "+": the row is a sentence like every other, and the key
|
|
2280
|
+
// column carries the hint — symbols are not shortcuts here.
|
|
2281
|
+
label: "Close PR and delete branch",
|
|
2206
2282
|
hint: "",
|
|
2283
|
+
group: "close",
|
|
2284
|
+
icon: "\uF48E",
|
|
2207
2285
|
run: () => {
|
|
2208
2286
|
},
|
|
2209
2287
|
subActions: [
|
|
2210
2288
|
{
|
|
2211
|
-
label: `Close #${item.number}
|
|
2289
|
+
label: `Close #${item.number} and delete ${item.branch}`,
|
|
2212
2290
|
hint: "",
|
|
2291
|
+
group: "confirm",
|
|
2213
2292
|
run: () => {
|
|
2214
2293
|
onRemove?.(item);
|
|
2215
2294
|
showFlash(`\u2713 Closed #${item.number} and deleted ${item.branch}`);
|
|
@@ -2221,16 +2300,20 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2221
2300
|
});
|
|
2222
2301
|
}
|
|
2223
2302
|
},
|
|
2224
|
-
{ label: "Cancel", hint: "", run: () => {
|
|
2303
|
+
{ label: "Cancel", hint: "", group: "confirm", run: () => {
|
|
2225
2304
|
} }
|
|
2226
2305
|
]
|
|
2227
2306
|
});
|
|
2228
2307
|
}
|
|
2229
2308
|
}
|
|
2309
|
+
const extensionLabel = (e) => e.hint ? e.hint.charAt(0).toUpperCase() + e.hint.slice(1) : e.title;
|
|
2310
|
+
const extensionGroup = (e) => e.menuGroup === "act" ? "act" : e.menuGroup === "open" ? "open" : "quiet";
|
|
2230
2311
|
for (const e of itemExtensions(ext?.extensions))
|
|
2231
2312
|
actions.push({
|
|
2232
|
-
label: e
|
|
2313
|
+
label: extensionLabel(e),
|
|
2233
2314
|
hint: e.key,
|
|
2315
|
+
group: extensionGroup(e),
|
|
2316
|
+
icon: e.icon,
|
|
2234
2317
|
run: () => ext?.onOpenExt?.(e.id, {
|
|
2235
2318
|
item,
|
|
2236
2319
|
login,
|
|
@@ -2239,7 +2322,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
2239
2322
|
})
|
|
2240
2323
|
});
|
|
2241
2324
|
actions.push(...closing);
|
|
2242
|
-
return actions;
|
|
2325
|
+
return byGroup(actions);
|
|
2243
2326
|
};
|
|
2244
2327
|
var agoText = (ms) => {
|
|
2245
2328
|
const d = (Date.now() - ms) / 1e3;
|
|
@@ -2526,26 +2609,60 @@ var ItemRow = ({
|
|
|
2526
2609
|
}
|
|
2527
2610
|
);
|
|
2528
2611
|
};
|
|
2612
|
+
var GROUP_RANK = {
|
|
2613
|
+
act: 0,
|
|
2614
|
+
open: 1,
|
|
2615
|
+
switch: 2,
|
|
2616
|
+
copy: 3,
|
|
2617
|
+
quiet: 4,
|
|
2618
|
+
close: 5,
|
|
2619
|
+
confirm: 6
|
|
2620
|
+
};
|
|
2621
|
+
var groupRank = (action) => action.group === void 0 ? 7 : GROUP_RANK[action.group];
|
|
2622
|
+
var menuRowsOf = (actions) => {
|
|
2623
|
+
const sorted = byGroup(actions);
|
|
2624
|
+
const rows = [];
|
|
2625
|
+
sorted.forEach((action, i) => {
|
|
2626
|
+
if (i > 0 && sorted[i - 1].group !== action.group) rows.push({ gap: true });
|
|
2627
|
+
rows.push(action);
|
|
2628
|
+
});
|
|
2629
|
+
return rows;
|
|
2630
|
+
};
|
|
2631
|
+
var byGroup = (actions) => [...actions].sort((a, b) => groupRank(a) - groupRank(b));
|
|
2632
|
+
var isConfirmRows = (rows) => rows.length > 0 && rows.every((row) => isMenuGap(row) || row.group === "confirm");
|
|
2633
|
+
var menuTone = (row) => row.group === "close" || row.group === "confirm" && row.label !== "Cancel" ? "error" : void 0;
|
|
2529
2634
|
var useActionMenu = () => {
|
|
2530
2635
|
const [actions, setActions] = useState(null);
|
|
2531
2636
|
const [cursor, setCursor] = useState(0);
|
|
2532
2637
|
const open = (next) => {
|
|
2533
2638
|
if (next.length === 0) return;
|
|
2534
2639
|
setCursor(0);
|
|
2535
|
-
setActions(next);
|
|
2640
|
+
setActions(menuRowsOf(next));
|
|
2536
2641
|
};
|
|
2537
2642
|
const handleKey = (key) => {
|
|
2538
2643
|
if (!actions) return false;
|
|
2539
|
-
if (key.upArrow)
|
|
2540
|
-
|
|
2644
|
+
if (key.upArrow)
|
|
2645
|
+
setCursor((c) => {
|
|
2646
|
+
let n = c - 1;
|
|
2647
|
+
while (n > 0 && isMenuGap(actions[n])) n -= 1;
|
|
2648
|
+
return isMenuGap(actions[n]) ? c : n;
|
|
2649
|
+
});
|
|
2650
|
+
if (key.downArrow)
|
|
2651
|
+
setCursor((c) => {
|
|
2652
|
+
let n = c + 1;
|
|
2653
|
+
while (n < actions.length - 1 && isMenuGap(actions[n])) n += 1;
|
|
2654
|
+
return isMenuGap(actions[n]) ? c : n;
|
|
2655
|
+
});
|
|
2541
2656
|
if (key.return) {
|
|
2542
|
-
const
|
|
2543
|
-
if (
|
|
2544
|
-
|
|
2545
|
-
|
|
2657
|
+
const row = actions[cursor];
|
|
2658
|
+
if (!row || isMenuGap(row)) return true;
|
|
2659
|
+
if (row.subActions) {
|
|
2660
|
+
const rows = menuRowsOf(row.subActions);
|
|
2661
|
+
setCursor(isConfirmRows(rows) ? rows.length - 1 : 0);
|
|
2662
|
+
setActions(rows);
|
|
2546
2663
|
} else {
|
|
2547
2664
|
setActions(null);
|
|
2548
|
-
|
|
2665
|
+
row.run();
|
|
2549
2666
|
}
|
|
2550
2667
|
}
|
|
2551
2668
|
if (key.escape) setActions(null);
|
|
@@ -2556,29 +2673,81 @@ var useActionMenu = () => {
|
|
|
2556
2673
|
var ActionMenu = ({
|
|
2557
2674
|
item,
|
|
2558
2675
|
actions,
|
|
2559
|
-
cursor
|
|
2676
|
+
cursor,
|
|
2677
|
+
cols = COLS
|
|
2560
2678
|
}) => {
|
|
2561
|
-
const
|
|
2679
|
+
const showIcons = getIconMode() === "nerd" && actions.some((row) => !isMenuGap(row) && row.icon !== void 0);
|
|
2680
|
+
const keyWidth = Math.max(
|
|
2681
|
+
0,
|
|
2682
|
+
...actions.flatMap(
|
|
2683
|
+
(row) => isMenuGap(row) ? [] : [[...row.hint ?? ""].length]
|
|
2684
|
+
)
|
|
2685
|
+
);
|
|
2686
|
+
const contentWidth = Math.max(
|
|
2687
|
+
0,
|
|
2688
|
+
...actions.flatMap(
|
|
2689
|
+
(row) => isMenuGap(row) ? [] : [
|
|
2690
|
+
2 + (showIcons ? 2 : 0) + [...row.label].length + (keyWidth > 0 ? 1 + keyWidth : 0)
|
|
2691
|
+
]
|
|
2692
|
+
)
|
|
2693
|
+
);
|
|
2694
|
+
const width = Math.min(
|
|
2695
|
+
Math.max(contentWidth + 4, 40),
|
|
2696
|
+
56,
|
|
2697
|
+
Math.max(20, cols - 4)
|
|
2698
|
+
);
|
|
2699
|
+
const inner = Math.max(1, width - 4);
|
|
2700
|
+
const confirming = isConfirmRows(actions);
|
|
2701
|
+
const isCloseRow = (row) => menuTone(row) === "error";
|
|
2702
|
+
const number = item.kind === "pr" || item.kind === "issue" ? item.number : null;
|
|
2703
|
+
const leadNum = item.kind === "task" ? item.key : number !== null ? `#${number}` : "";
|
|
2704
|
+
const lead = leadNum ? `${leadNum} \xB7 ` : "";
|
|
2705
|
+
const rest = confirming ? `Close #${number}?` : item.kind === "task" ? item.summary : item.kind === "pr" || item.kind === "issue" ? item.title : "";
|
|
2706
|
+
const restText = [...lead].length + [...rest].length <= inner ? rest : [...rest].slice(0, Math.max(0, inner - [...lead].length - 1)).join("") + "\u2026";
|
|
2562
2707
|
return /* @__PURE__ */ jsxs(
|
|
2563
2708
|
Box,
|
|
2564
2709
|
{
|
|
2565
2710
|
flexDirection: "column",
|
|
2566
2711
|
flexShrink: 0,
|
|
2712
|
+
width,
|
|
2567
2713
|
borderStyle: "round",
|
|
2568
2714
|
borderColor: colors.info,
|
|
2569
2715
|
backgroundColor: OVERLAY_BG,
|
|
2570
2716
|
paddingX: 1,
|
|
2571
2717
|
marginTop: 1,
|
|
2572
2718
|
children: [
|
|
2573
|
-
/* @__PURE__ */ jsx(Text3, { color: colors.
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
/* @__PURE__ */ jsx(Text3, {
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2719
|
+
confirming ? /* @__PURE__ */ jsx(Text3, { color: colors.error, bold: true, children: restText }) : /* @__PURE__ */ jsxs(Text3, { children: [
|
|
2720
|
+
/* @__PURE__ */ jsx(Text3, { color: colors.info, bold: true, children: leadNum }),
|
|
2721
|
+
leadNum ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " \xB7 " }) : null,
|
|
2722
|
+
/* @__PURE__ */ jsx(Text3, { children: restText })
|
|
2723
|
+
] }),
|
|
2724
|
+
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: "\u2500".repeat(inner) }),
|
|
2725
|
+
actions.map(
|
|
2726
|
+
(row, i) => isMenuGap(row) ? /* @__PURE__ */ jsx(Text3, { children: " " }, `gap-${i}`) : /* @__PURE__ */ jsxs(Box, { width: inner, children: [
|
|
2727
|
+
/* @__PURE__ */ jsx(Box, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx(Text3, { color: colors.info, children: i === cursor ? "\u276F " : " " }) }),
|
|
2728
|
+
showIcons ? /* @__PURE__ */ jsx(Box, { width: 2, flexShrink: 0, children: row.icon ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: row.icon }) : /* @__PURE__ */ jsx(Text3, { children: " " }) }) : null,
|
|
2729
|
+
/* @__PURE__ */ jsx(Box, { flexGrow: 1, children: /* @__PURE__ */ jsx(
|
|
2730
|
+
Text3,
|
|
2731
|
+
{
|
|
2732
|
+
bold: i === cursor,
|
|
2733
|
+
color: isCloseRow(row) ? colors.error : void 0,
|
|
2734
|
+
children: row.label
|
|
2735
|
+
}
|
|
2736
|
+
) }),
|
|
2737
|
+
keyWidth > 0 ? /* @__PURE__ */ jsx(Box, { width: keyWidth, flexShrink: 0, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text3, { color: colors.secondary, children: row.hint ?? "" }) }) : null
|
|
2738
|
+
] }, `${i}:${row.label}`)
|
|
2739
|
+
),
|
|
2740
|
+
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: "\u2500".repeat(inner) }),
|
|
2741
|
+
/* @__PURE__ */ jsx(
|
|
2742
|
+
FooterHints,
|
|
2743
|
+
{
|
|
2744
|
+
hints: [
|
|
2745
|
+
["\u2191\u2193", "move"],
|
|
2746
|
+
["\u21B5", "run"],
|
|
2747
|
+
["esc", "close"]
|
|
2748
|
+
]
|
|
2749
|
+
}
|
|
2750
|
+
)
|
|
2582
2751
|
]
|
|
2583
2752
|
}
|
|
2584
2753
|
);
|
|
@@ -2854,6 +3023,7 @@ var BrowseScreen = ({
|
|
|
2854
3023
|
fetchedAt,
|
|
2855
3024
|
refreshError,
|
|
2856
3025
|
hidden,
|
|
3026
|
+
overlayBody,
|
|
2857
3027
|
peelRef,
|
|
2858
3028
|
onTyping,
|
|
2859
3029
|
onOpenPr,
|
|
@@ -3544,6 +3714,10 @@ var BrowseScreen = ({
|
|
|
3544
3714
|
const matchCount = section.items.filter(
|
|
3545
3715
|
(i) => i.kind !== "repo-header" && i.kind !== "subgroup-header"
|
|
3546
3716
|
).length;
|
|
3717
|
+
const searchBar = searchBarState({
|
|
3718
|
+
typing: searchInput,
|
|
3719
|
+
nerd: getIconMode() === "nerd"
|
|
3720
|
+
});
|
|
3547
3721
|
const launcher = useMemo(() => {
|
|
3548
3722
|
const run = /* @__PURE__ */ new Map();
|
|
3549
3723
|
if (palette === null)
|
|
@@ -3603,7 +3777,8 @@ var BrowseScreen = ({
|
|
|
3603
3777
|
}));
|
|
3604
3778
|
return { items, run, message };
|
|
3605
3779
|
}, [palette, activeItem, extensions, jiraBase, jiraKeyRe, jiraSetupHint]);
|
|
3606
|
-
|
|
3780
|
+
const chrome = useContext(InboxChromeContext);
|
|
3781
|
+
const overlayOpen = overlayBody != null;
|
|
3607
3782
|
const overlay = palette !== null ? /* @__PURE__ */ jsx(
|
|
3608
3783
|
CommandPalette,
|
|
3609
3784
|
{
|
|
@@ -3648,7 +3823,8 @@ var BrowseScreen = ({
|
|
|
3648
3823
|
{
|
|
3649
3824
|
item: activeItem,
|
|
3650
3825
|
actions: menu.actions,
|
|
3651
|
-
cursor: menu.cursor
|
|
3826
|
+
cursor: menu.cursor,
|
|
3827
|
+
cols: listCols
|
|
3652
3828
|
}
|
|
3653
3829
|
) : null;
|
|
3654
3830
|
return /* @__PURE__ */ jsxs(
|
|
@@ -3667,7 +3843,7 @@ var BrowseScreen = ({
|
|
|
3667
3843
|
brand,
|
|
3668
3844
|
sections: localSections,
|
|
3669
3845
|
login,
|
|
3670
|
-
scopeLabel: origin?.label,
|
|
3846
|
+
scopeLabel: overlayOpen ? chrome?.scope ?? origin?.label : origin?.label,
|
|
3671
3847
|
budgetLabel: budgetNotice(budget)?.label,
|
|
3672
3848
|
budgetCritical: budgetNotice(budget)?.critical,
|
|
3673
3849
|
refreshing,
|
|
@@ -3690,7 +3866,7 @@ var BrowseScreen = ({
|
|
|
3690
3866
|
/* @__PURE__ */ jsx(Text3, { bold: true, color: colors.accent, children: " \u26A1 auto-refresh paused to save API budget" }),
|
|
3691
3867
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: " r refreshes anyway" })
|
|
3692
3868
|
] }) : null,
|
|
3693
|
-
/* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(
|
|
3869
|
+
overlayOpen ? null : /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(
|
|
3694
3870
|
Tabs,
|
|
3695
3871
|
{
|
|
3696
3872
|
active: section.id,
|
|
@@ -3723,18 +3899,24 @@ var BrowseScreen = ({
|
|
|
3723
3899
|
}))
|
|
3724
3900
|
}
|
|
3725
3901
|
) }),
|
|
3726
|
-
search != null ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
3727
|
-
/* @__PURE__ */ jsx(Text3, { color:
|
|
3902
|
+
!overlayOpen && search != null ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
3903
|
+
/* @__PURE__ */ jsx(Text3, { color: searchBar.glyphColor, children: ` ${searchBar.glyph} ` }),
|
|
3728
3904
|
/* @__PURE__ */ jsx(Text3, { children: search }),
|
|
3729
|
-
|
|
3730
|
-
/* @__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: [
|
|
3905
|
+
searchBar.caret ? /* @__PURE__ */ jsx(Text3, { color: colors.info, children: "\u258F" }) : null,
|
|
3906
|
+
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: ` ${matchCount} match${matchCount !== 1 ? "es" : ""} ${searchInput ? filter.hints.map(([k, label]) => `${k} ${label}`).join(" \xB7 ") : searchBar.hints}` })
|
|
3907
|
+
] }) : !overlayOpen && repoFilter.size > 0 ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
3732
3908
|
/* @__PURE__ */ jsx(Text3, { color: colors.accent, children: " \u25C9 " }),
|
|
3733
3909
|
/* @__PURE__ */ jsx(Text3, { children: `${repoFilter.size} repo${repoFilter.size !== 1 ? "s" : ""}` }),
|
|
3734
3910
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: " f edit \xB7 esc clear" })
|
|
3735
3911
|
] }) : null,
|
|
3736
3912
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
3737
|
-
|
|
3913
|
+
overlayOpen ? (
|
|
3914
|
+
// The overlay owns this band: the same frame, the same header above
|
|
3915
|
+
// and footer below, the body where the list was. Fixed to the list's
|
|
3916
|
+
// own height so the frame holds still between browse and drill, and
|
|
3917
|
+
// clipped rather than spilling past the footer on a long body.
|
|
3918
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", height: listHeight, overflow: "hidden", children: overlayBody })
|
|
3919
|
+
) : /* @__PURE__ */ jsxs(
|
|
3738
3920
|
Box,
|
|
3739
3921
|
{
|
|
3740
3922
|
flexDirection: "column",
|
|
@@ -3794,7 +3976,7 @@ var BrowseScreen = ({
|
|
|
3794
3976
|
]
|
|
3795
3977
|
}
|
|
3796
3978
|
),
|
|
3797
|
-
showRail ? /* @__PURE__ */ jsx(
|
|
3979
|
+
showRail && !overlayOpen ? /* @__PURE__ */ jsx(
|
|
3798
3980
|
SidePanel,
|
|
3799
3981
|
{
|
|
3800
3982
|
sidebar: rails,
|
|
@@ -3806,7 +3988,13 @@ var BrowseScreen = ({
|
|
|
3806
3988
|
}
|
|
3807
3989
|
) : null
|
|
3808
3990
|
] }),
|
|
3809
|
-
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: flash ? /* @__PURE__ */ jsx(Text3, { color: colors.success, children: flash }) :
|
|
3991
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: flash ? /* @__PURE__ */ jsx(Text3, { color: colors.success, children: flash }) : overlayOpen ? (
|
|
3992
|
+
// The overlay's own verbs, plus the way back. `page="nested"` is what
|
|
3993
|
+
// appends `⌫ back` and `q quit`, so a body passes only its own keys
|
|
3994
|
+
// through `useChrome` — the same contract ink-ui's `Page` offers its
|
|
3995
|
+
// nested pages, without re-laying-out the browse frame as one.
|
|
3996
|
+
/* @__PURE__ */ jsx(FooterHints, { hints: chrome?.hints ?? [], page: "nested", help: false })
|
|
3997
|
+
) : /* @__PURE__ */ jsx(FooterHints, { hints }) })
|
|
3810
3998
|
]
|
|
3811
3999
|
}
|
|
3812
4000
|
);
|
|
@@ -3873,6 +4061,15 @@ var App = ({
|
|
|
3873
4061
|
const browsePeel = useRef(null);
|
|
3874
4062
|
const drillPeel = useRef(null);
|
|
3875
4063
|
const [typing, setTyping] = useState(false);
|
|
4064
|
+
const [chromeSpec, setChromeSpec] = useState({});
|
|
4065
|
+
const setChrome = useCallback(
|
|
4066
|
+
(next) => setChromeSpec((prev) => sameChrome(prev, next) ? prev : next),
|
|
4067
|
+
[]
|
|
4068
|
+
);
|
|
4069
|
+
const chromeValue = useMemo(
|
|
4070
|
+
() => ({ scope: chromeSpec.scope, hints: chromeSpec.hints, setChrome }),
|
|
4071
|
+
[chromeSpec, setChrome]
|
|
4072
|
+
);
|
|
3876
4073
|
useAppKeys({
|
|
3877
4074
|
isActive: !typing,
|
|
3878
4075
|
onBack: () => {
|
|
@@ -4236,7 +4433,10 @@ var App = ({
|
|
|
4236
4433
|
extId: state.extId,
|
|
4237
4434
|
target: state.target
|
|
4238
4435
|
} : null;
|
|
4239
|
-
const toBrowse = () =>
|
|
4436
|
+
const toBrowse = () => {
|
|
4437
|
+
setChromeSpec({});
|
|
4438
|
+
setState({ phase: "browse", sections: state.sections, login: state.login });
|
|
4439
|
+
};
|
|
4240
4440
|
const markLeaving = (target) => {
|
|
4241
4441
|
if (state.phase !== "browse") toBrowse();
|
|
4242
4442
|
if (!leavingSince.current.has(target.url))
|
|
@@ -4264,82 +4464,89 @@ var App = ({
|
|
|
4264
4464
|
);
|
|
4265
4465
|
}, MERGED_HOLD_MS);
|
|
4266
4466
|
};
|
|
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
|
-
|
|
4467
|
+
const overlayBody = overlay?.kind === "pr" || overlay?.kind === "issue" ? detailFor?.({
|
|
4468
|
+
item: overlay.item,
|
|
4469
|
+
kind: overlay.kind,
|
|
4470
|
+
login: state.login,
|
|
4471
|
+
registerPeel: (peel) => {
|
|
4472
|
+
drillPeel.current = peel;
|
|
4473
|
+
},
|
|
4474
|
+
onTyping: setTyping,
|
|
4475
|
+
onBack: toBrowse,
|
|
4476
|
+
onRefresh: applyOrRefresh,
|
|
4477
|
+
onRemove: markLeaving,
|
|
4478
|
+
onMerged: markMerged
|
|
4479
|
+
}) : overlay?.kind === "ext" ? extensions?.find((e) => e.id === overlay.extId)?.body(toBrowse, overlay.target) : null;
|
|
4480
|
+
return /* @__PURE__ */ jsx(InboxChromeContext.Provider, { value: chromeValue, children: /* @__PURE__ */ jsx(
|
|
4481
|
+
BrowseScreen,
|
|
4482
|
+
{
|
|
4483
|
+
brand: brandOf(title),
|
|
4484
|
+
sections: state.sections,
|
|
4485
|
+
login: state.login,
|
|
4486
|
+
jiraBase,
|
|
4487
|
+
jiraKeyRe,
|
|
4488
|
+
jiraSetupHint,
|
|
4489
|
+
jiraTransitions,
|
|
4490
|
+
onRefresh: applyOrRefresh,
|
|
4491
|
+
onActed,
|
|
4492
|
+
onSettled,
|
|
4493
|
+
refreshing,
|
|
4494
|
+
hasPending: pending !== null,
|
|
4495
|
+
pendingSummary,
|
|
4496
|
+
appliedSummary: transients.size > 0 ? appliedSummary : "",
|
|
4497
|
+
fetchedAt,
|
|
4498
|
+
refreshError: refreshError ?? void 0,
|
|
4499
|
+
origin,
|
|
4500
|
+
budget,
|
|
4501
|
+
skippedForBudget,
|
|
4502
|
+
hidden: overlay !== null,
|
|
4503
|
+
overlayBody: overlayBody ?? void 0,
|
|
4504
|
+
peelRef: browsePeel,
|
|
4505
|
+
onTyping: setTyping,
|
|
4506
|
+
mergedUrls,
|
|
4507
|
+
leavingUrls,
|
|
4508
|
+
transitSince,
|
|
4509
|
+
onLeave: markLeaving,
|
|
4510
|
+
transients,
|
|
4511
|
+
onTabChange: setVisibleTab,
|
|
4512
|
+
sidebar,
|
|
4513
|
+
liveLabel,
|
|
4514
|
+
ciStatusState: hasCiStatus ? ciStatusState : void 0,
|
|
4515
|
+
ciJob,
|
|
4516
|
+
stripState: hasStrip ? stripState : void 0,
|
|
4517
|
+
stripLabel,
|
|
4518
|
+
tabHelp,
|
|
4519
|
+
onOpenPr: (item) => {
|
|
4520
|
+
setChromeSpec({});
|
|
4521
|
+
setState({
|
|
4307
4522
|
phase: "pr",
|
|
4308
4523
|
item,
|
|
4309
4524
|
sections: state.sections,
|
|
4310
4525
|
login: state.login
|
|
4311
|
-
})
|
|
4312
|
-
|
|
4526
|
+
});
|
|
4527
|
+
},
|
|
4528
|
+
onOpenIssue: (item) => {
|
|
4529
|
+
setChromeSpec({});
|
|
4530
|
+
setState({
|
|
4313
4531
|
phase: "issue",
|
|
4314
4532
|
item,
|
|
4315
4533
|
sections: state.sections,
|
|
4316
4534
|
login: state.login
|
|
4317
|
-
})
|
|
4318
|
-
|
|
4535
|
+
});
|
|
4536
|
+
},
|
|
4537
|
+
onOpenExt: (id, target) => {
|
|
4538
|
+
setChromeSpec({});
|
|
4539
|
+
setState({
|
|
4319
4540
|
phase: "ext",
|
|
4320
4541
|
extId: id,
|
|
4321
4542
|
target,
|
|
4322
4543
|
sections: state.sections,
|
|
4323
4544
|
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;
|
|
4545
|
+
});
|
|
4334
4546
|
},
|
|
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
|
-
] });
|
|
4547
|
+
extensions
|
|
4548
|
+
}
|
|
4549
|
+
) });
|
|
4343
4550
|
};
|
|
4344
4551
|
|
|
4345
4552
|
// src/inbox/filter.ts
|
|
@@ -4356,4 +4563,4 @@ var matchesFilter = (repo, filter) => {
|
|
|
4356
4563
|
};
|
|
4357
4564
|
var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
|
|
4358
4565
|
|
|
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 };
|
|
4566
|
+
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.68.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
|
},
|