@kud/gh-ink 0.38.0 → 0.39.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/dist/index.d.ts +30 -8
- package/dist/index.js +127 -33
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,12 @@ type SidebarRow = {
|
|
|
46
46
|
live?: number;
|
|
47
47
|
/** Something under it is waiting on you. */
|
|
48
48
|
wantsYou?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Where ↵ takes you. Absent means the row is a label and nothing more — the
|
|
51
|
+
* cursor still lands on it, and pressing ↵ does nothing rather than flashing
|
|
52
|
+
* an error about a host decision the reader cannot change.
|
|
53
|
+
*/
|
|
54
|
+
url?: string;
|
|
49
55
|
};
|
|
50
56
|
type Sidebar = {
|
|
51
57
|
/** Heading for the rail. The host's vocabulary, never ours. */
|
|
@@ -53,27 +59,43 @@ type Sidebar = {
|
|
|
53
59
|
rows: SidebarRow[];
|
|
54
60
|
};
|
|
55
61
|
/**
|
|
56
|
-
* The rail's width, INCLUDING its
|
|
62
|
+
* The rail's width, INCLUDING its rule and padding. Exported because the list
|
|
57
63
|
* beside it has to shrink by exactly this much: every row in that list truncates
|
|
58
64
|
* against a budget, and a budget that does not know the rail is there overflows
|
|
59
65
|
* by the rail's whole width — which in a frame sized to fill the terminal
|
|
60
66
|
* scrolls the panel rather than clipping a row.
|
|
61
67
|
*/
|
|
62
|
-
declare const SIDEBAR_COLS =
|
|
68
|
+
declare const SIDEBAR_COLS = 40;
|
|
69
|
+
/**
|
|
70
|
+
* How many rows fit in `height` — one fewer than would physically fit whenever
|
|
71
|
+
* that means anything is left over, to buy the line that says so.
|
|
72
|
+
*
|
|
73
|
+
* Computed rather than left to the layout, because the layout's answer is to CUT,
|
|
74
|
+
* silently. A roadmap quietly missing its last three initiatives looks exactly
|
|
75
|
+
* like a roadmap that has none, and the whole reason the rail exists is that an
|
|
76
|
+
* initiative with nothing moving on it was invisible.
|
|
77
|
+
*/
|
|
78
|
+
declare const railCapacity: (height: number, rows: number) => number;
|
|
63
79
|
/**
|
|
64
80
|
* A right-hand rail of initiatives, standing beside the list.
|
|
65
81
|
*
|
|
66
82
|
* Two lines per row rather than one, because both halves are load-bearing and
|
|
67
83
|
* neither survives the other being cut: a key with no words cannot be read at a
|
|
68
|
-
* glance, and words with no key cannot be opened.
|
|
69
|
-
*
|
|
84
|
+
* glance, and words with no key cannot be opened. Forty columns is not enough for
|
|
85
|
+
* both on one line, so they stack.
|
|
70
86
|
*
|
|
71
|
-
* Presentational, like every other row renderer here
|
|
72
|
-
*
|
|
87
|
+
* Presentational, like every other row renderer here: it DRAWS a cursor but does
|
|
88
|
+
* not own one, and it never calls `useInput`. Where the cursor is, and whether
|
|
89
|
+
* the arrows are pointed at this rail at all, are the host's state — which is
|
|
90
|
+
* what lets a screen with two focus regions have exactly one of them lit.
|
|
73
91
|
*/
|
|
74
|
-
declare const SidePanel: ({ sidebar, height, }: {
|
|
92
|
+
declare const SidePanel: ({ sidebar, height, focused, cursor, }: {
|
|
75
93
|
sidebar: Sidebar;
|
|
76
94
|
height?: number;
|
|
95
|
+
/** The arrows are pointed here, so this rail draws the cursor. */
|
|
96
|
+
focused?: boolean;
|
|
97
|
+
/** Which row the cursor is on. Only drawn while `focused`. */
|
|
98
|
+
cursor?: number;
|
|
77
99
|
}) => React.JSX.Element;
|
|
78
100
|
|
|
79
101
|
declare const renderMarkdown: (raw: string, width: number, fileLink?: (path: string, line?: number) => string) => StyledLine[];
|
|
@@ -676,4 +698,4 @@ declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
|
|
|
676
698
|
*/
|
|
677
699
|
declare const parsePatterns: (value: string) => string[];
|
|
678
700
|
|
|
679
|
-
export { type Action, ActionMenu, type AnyItem, App, COLS, type CiStatus, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, type GHDetail, type GHItem, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxConfig, type InboxExtension, type JiraTransition, type OriginSplit, PIN_MARK, type RepoFilter, type RepoHeader, type RepoProfile, SIDEBAR_COLS, type Section, type ShowLess, type ShowMore, SidePanel, type Sidebar, type SidebarRow, type Standing, type SubgroupHeader, type TaskRow, budgetNotice, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
|
701
|
+
export { type Action, ActionMenu, type AnyItem, App, COLS, type CiStatus, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, type GHDetail, type GHItem, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxConfig, type InboxExtension, type JiraTransition, type OriginSplit, PIN_MARK, type RepoFilter, type RepoHeader, type RepoProfile, SIDEBAR_COLS, type Section, type ShowLess, type ShowMore, SidePanel, type Sidebar, type SidebarRow, type Standing, type SubgroupHeader, type TaskRow, budgetNotice, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, railCapacity, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
package/dist/index.js
CHANGED
|
@@ -584,36 +584,73 @@ var HealthPanel = ({
|
|
|
584
584
|
] });
|
|
585
585
|
};
|
|
586
586
|
var ACCENT = "#FF8700";
|
|
587
|
-
var SIDEBAR_COLS =
|
|
588
|
-
var
|
|
589
|
-
var
|
|
587
|
+
var SIDEBAR_COLS = 40;
|
|
588
|
+
var RULE = 1;
|
|
589
|
+
var PAD = 2;
|
|
590
|
+
var CONTENT = SIDEBAR_COLS - RULE - PAD;
|
|
591
|
+
var LABEL_INDENT = 2;
|
|
592
|
+
var RULE_COLOR = "gray";
|
|
590
593
|
var truncate = (text, max) => [...text].length <= max ? text : [...text].slice(0, Math.max(0, max - 1)).join("") + "\u2026";
|
|
594
|
+
var ROW_LINES = 3;
|
|
595
|
+
var HEADING_LINES = 2;
|
|
596
|
+
var railCapacity = (height, rows) => {
|
|
597
|
+
const fits = Math.max(0, Math.floor((height - HEADING_LINES) / ROW_LINES));
|
|
598
|
+
return fits >= rows ? rows : Math.max(0, fits - 1);
|
|
599
|
+
};
|
|
591
600
|
var SidePanel = ({
|
|
592
601
|
sidebar,
|
|
593
|
-
height
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
602
|
+
height,
|
|
603
|
+
focused = false,
|
|
604
|
+
cursor = 0
|
|
605
|
+
}) => {
|
|
606
|
+
const capacity = height === void 0 ? sidebar.rows.length : railCapacity(height, sidebar.rows.length);
|
|
607
|
+
const start = focused && cursor >= capacity ? Math.min(cursor - capacity + 1, sidebar.rows.length - capacity) : 0;
|
|
608
|
+
const shown = sidebar.rows.slice(start, start + capacity);
|
|
609
|
+
const hidden = sidebar.rows.length - shown.length;
|
|
610
|
+
return (
|
|
611
|
+
// A rule down the left rather than a full box: the rail's other three edges
|
|
612
|
+
// already have the frame's border a column or two away, and a second
|
|
613
|
+
// rectangle inside the first reads as a nested panel — something you could
|
|
614
|
+
// focus and act on, which this cannot be. One line is the whole claim: what
|
|
615
|
+
// is left of it is the list, what is right of it is not.
|
|
616
|
+
/* @__PURE__ */ jsxs(
|
|
617
|
+
Box,
|
|
618
|
+
{
|
|
619
|
+
flexDirection: "column",
|
|
620
|
+
width: SIDEBAR_COLS,
|
|
621
|
+
flexShrink: 0,
|
|
622
|
+
height,
|
|
623
|
+
borderStyle: "single",
|
|
624
|
+
borderColor: RULE_COLOR,
|
|
625
|
+
borderDimColor: true,
|
|
626
|
+
borderTop: false,
|
|
627
|
+
borderRight: false,
|
|
628
|
+
borderBottom: false,
|
|
629
|
+
paddingLeft: PAD,
|
|
630
|
+
children: [
|
|
631
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
632
|
+
/* @__PURE__ */ jsx(Text, { color: ACCENT, bold: true, children: "\xBB " }),
|
|
633
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: sidebar.title }),
|
|
634
|
+
focused ? /* @__PURE__ */ jsx(Text, { color: ACCENT, children: " \u25CF focus" }) : null
|
|
635
|
+
] }),
|
|
636
|
+
sidebar.rows.length === 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft: LABEL_INDENT, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "nothing open" }) }) : shown.map((row, i) => {
|
|
637
|
+
const active = focused && start + i === cursor;
|
|
638
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
639
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
640
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: active ? "\u276F " : " " }),
|
|
641
|
+
/* @__PURE__ */ jsx(Text, { color: ACCENT, bold: true, children: row.wantsYou ? "\u2190 " : " " }),
|
|
642
|
+
/* @__PURE__ */ jsx(Text, { color: ACCENT, bold: active, children: row.key }),
|
|
643
|
+
row.live !== void 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${row.live} live` }) : null
|
|
644
|
+
] }),
|
|
645
|
+
/* @__PURE__ */ jsx(Box, { paddingLeft: LABEL_INDENT + 2, children: /* @__PURE__ */ jsx(Text, { dimColor: !active, children: truncate(row.label, CONTENT - LABEL_INDENT - 2) }) })
|
|
646
|
+
] }, row.key);
|
|
647
|
+
}),
|
|
648
|
+
hidden > 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft: LABEL_INDENT, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: `+${hidden} more` }) }) : null
|
|
649
|
+
]
|
|
650
|
+
}
|
|
651
|
+
)
|
|
652
|
+
);
|
|
653
|
+
};
|
|
617
654
|
var healthDisplay = {
|
|
618
655
|
none: { glyph: " ", color: colors.muted },
|
|
619
656
|
draft: { glyph: "~", color: colors.muted },
|
|
@@ -2467,8 +2504,13 @@ var BrowseScreen = ({
|
|
|
2467
2504
|
out.add(s.id);
|
|
2468
2505
|
return out;
|
|
2469
2506
|
}, [localSections, transients]);
|
|
2470
|
-
const [railOpen, setRailOpen] = useState(
|
|
2507
|
+
const [railOpen, setRailOpen] = useState(true);
|
|
2471
2508
|
const showRail = railOpen && !!sidebar;
|
|
2509
|
+
const [railFocus, setRailFocus] = useState(false);
|
|
2510
|
+
const [railCursor, setRailCursor] = useState(0);
|
|
2511
|
+
const railRows = sidebar?.rows ?? [];
|
|
2512
|
+
const railActive = showRail && railFocus && railRows.length > 0;
|
|
2513
|
+
const railAt = Math.min(railCursor, Math.max(0, railRows.length - 1));
|
|
2472
2514
|
const listCols = showRail ? COLS - SIDEBAR_COLS : COLS;
|
|
2473
2515
|
const [sparkFrame, setSparkFrame] = useState(0);
|
|
2474
2516
|
const sparkling = (mergedUrls?.length ?? 0) > 0 || (leavingUrls?.length ?? 0) > 0 || !!transients?.size && section.items.some((item) => transientOf(transients, item, section.id));
|
|
@@ -2571,9 +2613,41 @@ var BrowseScreen = ({
|
|
|
2571
2613
|
return;
|
|
2572
2614
|
}
|
|
2573
2615
|
if (input === "i" && sidebar) {
|
|
2574
|
-
setRailOpen((open) =>
|
|
2616
|
+
setRailOpen((open) => {
|
|
2617
|
+
if (open) setRailFocus(false);
|
|
2618
|
+
return !open;
|
|
2619
|
+
});
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2622
|
+
if (key.tab && showRail) {
|
|
2623
|
+
setRailFocus((f) => !f);
|
|
2575
2624
|
return;
|
|
2576
2625
|
}
|
|
2626
|
+
if (railActive) {
|
|
2627
|
+
if (key.upArrow || input === "k") {
|
|
2628
|
+
setRailCursor((c) => Math.max(0, c - 1));
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
if (key.downArrow || input === "j") {
|
|
2632
|
+
setRailCursor((c) => Math.min(railRows.length - 1, c + 1));
|
|
2633
|
+
return;
|
|
2634
|
+
}
|
|
2635
|
+
if (key.escape) {
|
|
2636
|
+
setRailFocus(false);
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2639
|
+
if (key.return || input === "o") {
|
|
2640
|
+
const row = railRows[railAt];
|
|
2641
|
+
if (row?.url) {
|
|
2642
|
+
quietly`open ${row.url}`.catch(() => {
|
|
2643
|
+
});
|
|
2644
|
+
showFlash(`\u2197 Opened ${row.key}`);
|
|
2645
|
+
}
|
|
2646
|
+
return;
|
|
2647
|
+
}
|
|
2648
|
+
if (input !== "r" && input !== "q" && !key.leftArrow && !key.rightArrow)
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2577
2651
|
if (key.escape && search != null) {
|
|
2578
2652
|
setSearch(null);
|
|
2579
2653
|
return;
|
|
@@ -2846,7 +2920,19 @@ var BrowseScreen = ({
|
|
|
2846
2920
|
menu.open(actions);
|
|
2847
2921
|
}
|
|
2848
2922
|
});
|
|
2849
|
-
const hints =
|
|
2923
|
+
const hints = railActive ? (
|
|
2924
|
+
// The rail's own keymap while it holds focus, not the list's with a line
|
|
2925
|
+
// bolted on. A footer advertising `m actions` beside a cursor that cannot
|
|
2926
|
+
// reach a row is worse than a short footer: it names a key that does
|
|
2927
|
+
// nothing where you are standing.
|
|
2928
|
+
[
|
|
2929
|
+
["\u2191\u2193", "initiative"],
|
|
2930
|
+
["\u21B5", "open"],
|
|
2931
|
+
["\u21E5/esc", "back to list"],
|
|
2932
|
+
["?", "help"],
|
|
2933
|
+
["q", "quit"]
|
|
2934
|
+
]
|
|
2935
|
+
) : activeItem?.kind === "repo-header" ? [
|
|
2850
2936
|
["\u2191\u2193", "nav"],
|
|
2851
2937
|
["\u2190\u2192", "tab"],
|
|
2852
2938
|
["\u21B5", "open repo"],
|
|
@@ -2861,7 +2947,7 @@ var BrowseScreen = ({
|
|
|
2861
2947
|
["m", "actions"],
|
|
2862
2948
|
// Advertised only where it does something, and named for what it
|
|
2863
2949
|
// shows rather than for the furniture: nobody wants "a sidebar".
|
|
2864
|
-
...
|
|
2950
|
+
...showRail ? [["\u21E5", sidebar.title.toLowerCase()]] : sidebar ? [["i", sidebar.title.toLowerCase()]] : [],
|
|
2865
2951
|
["?", "help"],
|
|
2866
2952
|
["q", "quit"]
|
|
2867
2953
|
];
|
|
@@ -2985,7 +3071,15 @@ var BrowseScreen = ({
|
|
|
2985
3071
|
]
|
|
2986
3072
|
}
|
|
2987
3073
|
),
|
|
2988
|
-
showRail && sidebar ? /* @__PURE__ */ jsx(
|
|
3074
|
+
showRail && sidebar ? /* @__PURE__ */ jsx(
|
|
3075
|
+
SidePanel,
|
|
3076
|
+
{
|
|
3077
|
+
sidebar,
|
|
3078
|
+
height: listHeight,
|
|
3079
|
+
focused: railActive,
|
|
3080
|
+
cursor: railAt
|
|
3081
|
+
}
|
|
3082
|
+
) : null
|
|
2989
3083
|
] }),
|
|
2990
3084
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: flash ? /* @__PURE__ */ jsx(Text3, { color: "green", children: flash }) : /* @__PURE__ */ jsx(FooterHints, { hints }) })
|
|
2991
3085
|
]
|
|
@@ -3439,4 +3533,4 @@ var matchesFilter = (repo, filter) => {
|
|
|
3439
3533
|
};
|
|
3440
3534
|
var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
|
|
3441
3535
|
|
|
3442
|
-
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, PIN_MARK, SIDEBAR_COLS, SidePanel, budgetNotice, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate2 as truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
|
3536
|
+
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, PIN_MARK, SIDEBAR_COLS, SidePanel, budgetNotice, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, railCapacity, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate2 as truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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",
|