@kud/ink-ui 0.17.0 → 0.19.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/AGENTS.md +7 -0
- package/dist/index.d.ts +36 -8
- package/dist/index.js +30 -14
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -79,6 +79,13 @@ import { colors, spacing } from "@kud/ink-ui"
|
|
|
79
79
|
A literal like `color="orange"` or `color="#FF8C00"` is wrong even when it
|
|
80
80
|
renders identically — it breaks the moment a token moves.
|
|
81
81
|
|
|
82
|
+
The one exception is `<Pill color>`, for a surface mirroring an external
|
|
83
|
+
system whose colours ARE its vocabulary — GitHub's merged purple, a CI
|
|
84
|
+
provider's result colours. The test is whether the hue is a **fact about the
|
|
85
|
+
thing being labelled**; a colour picked because it looks right is still a token
|
|
86
|
+
job. The pill inks itself against whatever fill it is given, so a caller reaching
|
|
87
|
+
for this never picks a foreground.
|
|
88
|
+
|
|
82
89
|
**State is never signalled by colour alone.** Every status carries a shape, a
|
|
83
90
|
glyph or a weight as well, because a colourblind reader cannot see the hue and
|
|
84
91
|
a piped terminal has no colour at all. `SelectableRow` marks the active row
|
package/dist/index.d.ts
CHANGED
|
@@ -19,16 +19,26 @@ type PillVariant = "success" | "error" | "warning" | "info" | "accent" | "muted"
|
|
|
19
19
|
type PillProps = {
|
|
20
20
|
children: string;
|
|
21
21
|
variant?: PillVariant;
|
|
22
|
+
/**
|
|
23
|
+
* An explicit fill, for a caller that owns a palette of its own — one
|
|
24
|
+
* mirroring an external system whose colours ARE the vocabulary (GitHub's
|
|
25
|
+
* merged purple, a CI provider's result colours). Overrides `variant`.
|
|
26
|
+
*
|
|
27
|
+
* Not a way round the token rule. A colour picked because it looks nice is
|
|
28
|
+
* still wrong here; the test is whether the hue is a fact about the thing
|
|
29
|
+
* being labelled rather than a preference about the label.
|
|
30
|
+
*/
|
|
31
|
+
color?: string;
|
|
22
32
|
};
|
|
23
33
|
/**
|
|
24
|
-
* A filled, rounded label — a category the thing belongs to,
|
|
25
|
-
*
|
|
34
|
+
* A filled, rounded label — a category the thing belongs to, or an event that
|
|
35
|
+
* has just happened to it.
|
|
26
36
|
*
|
|
27
|
-
* Reach for it when the word IS the information (`epic`, `draft`, `blocked
|
|
28
|
-
* you want it to read as one object rather than as more prose. For a
|
|
29
|
-
* the reader is meant to follow — a ticket key, a repo — leave the
|
|
30
|
-
* fill gives a breadcrumb a weight it has not earned, and once
|
|
31
|
-
* pill none of them is.
|
|
37
|
+
* Reach for it when the word IS the information (`epic`, `draft`, `blocked`,
|
|
38
|
+
* `NEW`) and you want it to read as one object rather than as more prose. For a
|
|
39
|
+
* reference the reader is meant to follow — a ticket key, a repo — leave the
|
|
40
|
+
* text dim: a fill gives a breadcrumb a weight it has not earned, and once
|
|
41
|
+
* everything is a pill none of them is.
|
|
32
42
|
*
|
|
33
43
|
* The WORD carries the meaning and the colour only reinforces it, so a pill
|
|
34
44
|
* survives being read in monochrome, piped, or by someone who cannot separate
|
|
@@ -43,7 +53,7 @@ type PillProps = {
|
|
|
43
53
|
* `NO_COLOR` is the one case that does fall back to brackets: with the fill
|
|
44
54
|
* stripped, the caps would be drawing the outline of a pill that is not there.
|
|
45
55
|
*/
|
|
46
|
-
declare const Pill: ({ children, variant }: PillProps) => React__default.JSX.Element;
|
|
56
|
+
declare const Pill: ({ children, variant, color }: PillProps) => React__default.JSX.Element;
|
|
47
57
|
/**
|
|
48
58
|
* How many columns `<Pill>` occupies for `text` — the label plus its two caps.
|
|
49
59
|
*
|
|
@@ -239,6 +249,24 @@ type TabItem<T extends string = string> = {
|
|
|
239
249
|
value: T;
|
|
240
250
|
label: string;
|
|
241
251
|
count?: number;
|
|
252
|
+
/**
|
|
253
|
+
* A marker drawn immediately before the label, in its own colour.
|
|
254
|
+
*
|
|
255
|
+
* Give every tab one of the SAME WIDTH, or none at all. A marker that appears
|
|
256
|
+
* on one tab alone pushes every tab after it sideways — which is the whole
|
|
257
|
+
* reason this is a field rather than something a caller prepends to `label`:
|
|
258
|
+
* a bar that shifts when news arrives is a bar you have to re-find. A blank of
|
|
259
|
+
* the right width is how you say "not this one".
|
|
260
|
+
*
|
|
261
|
+
* Its own `Text` because the label's colour answers "is this tab active" and a
|
|
262
|
+
* marker usually answers something else; folded together, the marker would
|
|
263
|
+
* have to borrow the answer to the wrong question.
|
|
264
|
+
*
|
|
265
|
+
* Animating one costs nothing: the cell is already reserved, so a caller
|
|
266
|
+
* cycling the glyph or the colour per frame moves no layout at all.
|
|
267
|
+
*/
|
|
268
|
+
marker?: string;
|
|
269
|
+
markerColor?: string;
|
|
242
270
|
};
|
|
243
271
|
type TabsProps<T extends string> = {
|
|
244
272
|
active: T;
|
package/dist/index.js
CHANGED
|
@@ -55,8 +55,19 @@ var INK = {
|
|
|
55
55
|
accent: "black",
|
|
56
56
|
muted: "white"
|
|
57
57
|
};
|
|
58
|
-
var
|
|
59
|
-
const
|
|
58
|
+
var inkFor = (fill) => {
|
|
59
|
+
const hex = /^#([0-9a-f]{6})$/i.exec(fill)?.[1];
|
|
60
|
+
if (!hex) return "white";
|
|
61
|
+
const channel = (i) => {
|
|
62
|
+
const c = parseInt(hex.slice(i, i + 2), 16) / 255;
|
|
63
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
64
|
+
};
|
|
65
|
+
const luminance = 0.2126 * channel(0) + 0.7152 * channel(2) + 0.0722 * channel(4);
|
|
66
|
+
return (luminance + 0.05) / 0.05 >= 1.05 / (luminance + 0.05) ? "black" : "white";
|
|
67
|
+
};
|
|
68
|
+
var Pill = ({ children, variant = "muted", color }) => {
|
|
69
|
+
const fill = color ?? FILL[variant];
|
|
70
|
+
const ink = color ? inkFor(color) : INK[variant];
|
|
60
71
|
if (process.env["NO_COLOR"]) return /* @__PURE__ */ jsxs(Text, { color: fill, children: [
|
|
61
72
|
"[",
|
|
62
73
|
children,
|
|
@@ -64,7 +75,7 @@ var Pill = ({ children, variant = "muted" }) => {
|
|
|
64
75
|
] });
|
|
65
76
|
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
66
77
|
/* @__PURE__ */ jsx(Text, { color: fill, children: glyphs.plCapLeft }),
|
|
67
|
-
/* @__PURE__ */ jsx(Text, { backgroundColor: fill, color:
|
|
78
|
+
/* @__PURE__ */ jsx(Text, { backgroundColor: fill, color: ink, children }),
|
|
68
79
|
/* @__PURE__ */ jsx(Text, { color: fill, children: glyphs.plCapRight })
|
|
69
80
|
] });
|
|
70
81
|
};
|
|
@@ -646,21 +657,26 @@ var ScrollView = ({
|
|
|
646
657
|
var Tabs = ({ active, items }) => {
|
|
647
658
|
const cells = items.map((item) => ({
|
|
648
659
|
key: item.value,
|
|
660
|
+
marker: item.marker ?? "",
|
|
661
|
+
markerColor: item.markerColor,
|
|
649
662
|
text: item.count !== void 0 ? `${item.label} (${item.count})` : item.label,
|
|
650
663
|
isActive: item.value === active
|
|
651
664
|
}));
|
|
665
|
+
const widthOf = (cell) => [...cell.marker].length + [...cell.text].length;
|
|
652
666
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
653
|
-
/* @__PURE__ */ jsx(Box, { gap: 2, children: cells.map((cell) => /* @__PURE__ */
|
|
654
|
-
Text,
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
667
|
+
/* @__PURE__ */ jsx(Box, { gap: 2, children: cells.map((cell) => /* @__PURE__ */ jsxs(Box, { children: [
|
|
668
|
+
cell.marker ? /* @__PURE__ */ jsx(Text, { color: cell.markerColor, children: cell.marker }) : null,
|
|
669
|
+
/* @__PURE__ */ jsx(
|
|
670
|
+
Text,
|
|
671
|
+
{
|
|
672
|
+
bold: cell.isActive,
|
|
673
|
+
color: cell.isActive ? colors.accent : void 0,
|
|
674
|
+
dimColor: !cell.isActive,
|
|
675
|
+
children: cell.text
|
|
676
|
+
}
|
|
677
|
+
)
|
|
678
|
+
] }, cell.key)) }),
|
|
679
|
+
/* @__PURE__ */ jsx(Box, { gap: 2, children: cells.map((cell) => /* @__PURE__ */ jsx(Text, { color: colors.accent, children: (cell.isActive ? "\u2500" : " ").repeat(widthOf(cell)) }, cell.key)) })
|
|
664
680
|
] });
|
|
665
681
|
};
|
|
666
682
|
var useTabs = (items, { initial, isActive = true } = {}) => {
|