@kud/gh-ink 0.38.1 → 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 +26 -3
- package/dist/index.js +115 -32
- 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,13 +59,13 @@ 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;
|
|
63
69
|
/**
|
|
64
70
|
* How many rows fit in `height` — one fewer than would physically fit whenever
|
|
65
71
|
* that means anything is left over, to buy the line that says so.
|
|
@@ -70,9 +76,26 @@ declare const SIDEBAR_COLS = 30;
|
|
|
70
76
|
* initiative with nothing moving on it was invisible.
|
|
71
77
|
*/
|
|
72
78
|
declare const railCapacity: (height: number, rows: number) => number;
|
|
73
|
-
|
|
79
|
+
/**
|
|
80
|
+
* A right-hand rail of initiatives, standing beside the list.
|
|
81
|
+
*
|
|
82
|
+
* Two lines per row rather than one, because both halves are load-bearing and
|
|
83
|
+
* neither survives the other being cut: a key with no words cannot be read at a
|
|
84
|
+
* glance, and words with no key cannot be opened. Forty columns is not enough for
|
|
85
|
+
* both on one line, so they stack.
|
|
86
|
+
*
|
|
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.
|
|
91
|
+
*/
|
|
92
|
+
declare const SidePanel: ({ sidebar, height, focused, cursor, }: {
|
|
74
93
|
sidebar: Sidebar;
|
|
75
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;
|
|
76
99
|
}) => React.JSX.Element;
|
|
77
100
|
|
|
78
101
|
declare const renderMarkdown: (raw: string, width: number, fileLink?: (path: string, line?: number) => string) => StyledLine[];
|
package/dist/index.js
CHANGED
|
@@ -584,9 +584,12 @@ 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";
|
|
591
594
|
var ROW_LINES = 3;
|
|
592
595
|
var HEADING_LINES = 2;
|
|
@@ -596,33 +599,56 @@ var railCapacity = (height, rows) => {
|
|
|
596
599
|
};
|
|
597
600
|
var SidePanel = ({
|
|
598
601
|
sidebar,
|
|
599
|
-
height
|
|
602
|
+
height,
|
|
603
|
+
focused = false,
|
|
604
|
+
cursor = 0
|
|
600
605
|
}) => {
|
|
601
|
-
const
|
|
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);
|
|
602
609
|
const hidden = sidebar.rows.length - shown.length;
|
|
603
|
-
return
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
|
620
635
|
] }),
|
|
621
|
-
/* @__PURE__ */ jsx(Box, { paddingLeft:
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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
|
+
)
|
|
626
652
|
);
|
|
627
653
|
};
|
|
628
654
|
var healthDisplay = {
|
|
@@ -2478,8 +2504,13 @@ var BrowseScreen = ({
|
|
|
2478
2504
|
out.add(s.id);
|
|
2479
2505
|
return out;
|
|
2480
2506
|
}, [localSections, transients]);
|
|
2481
|
-
const [railOpen, setRailOpen] = useState(
|
|
2507
|
+
const [railOpen, setRailOpen] = useState(true);
|
|
2482
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));
|
|
2483
2514
|
const listCols = showRail ? COLS - SIDEBAR_COLS : COLS;
|
|
2484
2515
|
const [sparkFrame, setSparkFrame] = useState(0);
|
|
2485
2516
|
const sparkling = (mergedUrls?.length ?? 0) > 0 || (leavingUrls?.length ?? 0) > 0 || !!transients?.size && section.items.some((item) => transientOf(transients, item, section.id));
|
|
@@ -2582,9 +2613,41 @@ var BrowseScreen = ({
|
|
|
2582
2613
|
return;
|
|
2583
2614
|
}
|
|
2584
2615
|
if (input === "i" && sidebar) {
|
|
2585
|
-
setRailOpen((open) =>
|
|
2616
|
+
setRailOpen((open) => {
|
|
2617
|
+
if (open) setRailFocus(false);
|
|
2618
|
+
return !open;
|
|
2619
|
+
});
|
|
2586
2620
|
return;
|
|
2587
2621
|
}
|
|
2622
|
+
if (key.tab && showRail) {
|
|
2623
|
+
setRailFocus((f) => !f);
|
|
2624
|
+
return;
|
|
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
|
+
}
|
|
2588
2651
|
if (key.escape && search != null) {
|
|
2589
2652
|
setSearch(null);
|
|
2590
2653
|
return;
|
|
@@ -2857,7 +2920,19 @@ var BrowseScreen = ({
|
|
|
2857
2920
|
menu.open(actions);
|
|
2858
2921
|
}
|
|
2859
2922
|
});
|
|
2860
|
-
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" ? [
|
|
2861
2936
|
["\u2191\u2193", "nav"],
|
|
2862
2937
|
["\u2190\u2192", "tab"],
|
|
2863
2938
|
["\u21B5", "open repo"],
|
|
@@ -2872,7 +2947,7 @@ var BrowseScreen = ({
|
|
|
2872
2947
|
["m", "actions"],
|
|
2873
2948
|
// Advertised only where it does something, and named for what it
|
|
2874
2949
|
// shows rather than for the furniture: nobody wants "a sidebar".
|
|
2875
|
-
...
|
|
2950
|
+
...showRail ? [["\u21E5", sidebar.title.toLowerCase()]] : sidebar ? [["i", sidebar.title.toLowerCase()]] : [],
|
|
2876
2951
|
["?", "help"],
|
|
2877
2952
|
["q", "quit"]
|
|
2878
2953
|
];
|
|
@@ -2996,7 +3071,15 @@ var BrowseScreen = ({
|
|
|
2996
3071
|
]
|
|
2997
3072
|
}
|
|
2998
3073
|
),
|
|
2999
|
-
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
|
|
3000
3083
|
] }),
|
|
3001
3084
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: flash ? /* @__PURE__ */ jsx(Text3, { color: "green", children: flash }) : /* @__PURE__ */ jsx(FooterHints, { hints }) })
|
|
3002
3085
|
]
|
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",
|