@kud/gh-ink 0.40.0 → 0.42.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.
Files changed (2) hide show
  1. package/dist/index.js +79 -37
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1842,6 +1842,7 @@ var InboxHeader = ({
1842
1842
  refreshing,
1843
1843
  hasPending,
1844
1844
  pendingSummary,
1845
+ appliedSummary,
1845
1846
  fetchedAt
1846
1847
  }) => {
1847
1848
  const total = sections.reduce((n, s) => n + topLevelCount(s), 0);
@@ -1849,7 +1850,7 @@ var InboxHeader = ({
1849
1850
  const userSeg = loading || quiet ? "" : `@${login} `;
1850
1851
  const workLabel = scopeLabel ? ` ${scopeLabel} ` : "";
1851
1852
  const budgetSeg = budgetLabel ? budgetLabel + " " : "";
1852
- const [statusText, statusColor] = hasPending ? [`\u25CF ${pendingSummary || "new"} \xB7 r apply`, "#FF8700"] : refreshing ? ["\u21BB refreshing\u2026", "cyan"] : fetchedAt ? [`updated ${agoText(fetchedAt)}`, void 0] : ["", void 0];
1853
+ const [statusText, statusColor] = hasPending ? [`\u25CF ${pendingSummary || "new"} \xB7 r apply`, "#FF8700"] : refreshing ? ["\u21BB refreshing\u2026", "cyan"] : appliedSummary ? [`\u25C9 ${appliedSummary}`, "#FF8700"] : fetchedAt ? [`updated ${agoText(fetchedAt)}`, void 0] : ["", void 0];
1853
1854
  const statusSeg = statusText ? statusText + " " : "";
1854
1855
  const fill = Math.max(
1855
1856
  4,
@@ -1934,6 +1935,7 @@ var MERGED_FRAME_MS = 150;
1934
1935
  var MERGED_FRAMES = ["\u2726", "\u2727", "\u2736", "\u2727"];
1935
1936
  var MERGED_COLOUR = "#A371F7";
1936
1937
  var TRANSIT_HOLD_MS = 7e3;
1938
+ var TRANSIT_FRAME_TICKS = 3;
1937
1939
  var NO_TRANSIENTS = /* @__PURE__ */ new Map();
1938
1940
  var NO_HOLDS = /* @__PURE__ */ new Map();
1939
1941
  var TRANSIT_OUT_FRAMES = ["\u25C9", "\u25CE", "\u25CB", "\xB7"];
@@ -1950,20 +1952,12 @@ var TRANSIT_COLOUR = {
1950
1952
  "moved-out": "#FF8700"
1951
1953
  };
1952
1954
  var TRANSIT_LABEL = {
1953
- in: "NEW",
1954
- out: "GONE",
1955
- changed: "UPDATED",
1956
- // Never GONE. The row is still on the board, one tab over, and telling you it
1957
- // has gone is the marker lying about work you still own — which was the whole
1958
- // failure: an epic moving tab said nothing at all and simply vanished.
1959
- "moved-in": "MOVED",
1960
- "moved-out": "MOVED"
1961
- };
1955
+ out: "GONE"};
1962
1956
  var isDeparture = (t) => t === "out" || t === "moved-out";
1963
1957
  var isArrival = (t) => t === "in" || t === "moved-in";
1964
1958
  var LEAVING_HOLD_MS = 2500;
1965
- var TAB_MARK = "\u25CF";
1966
- var tabLabel = (label, marked, id) => marked.size === 0 ? label : `${marked.has(id) ? TAB_MARK : " "} ${label}`;
1959
+ var tabMarker = (marked, id, frame = 0) => marked.has(id) ? `${TAB_PULSE[frame % TAB_PULSE.length]} ` : " ";
1960
+ var TAB_PULSE = ["\xB7", "\u25CB", "\u25CE", "\u25C9"];
1967
1961
  var ItemRow = ({
1968
1962
  item,
1969
1963
  active,
@@ -1978,6 +1972,8 @@ var ItemRow = ({
1978
1972
  cols = COLS
1979
1973
  }) => {
1980
1974
  const transient = leaving ? "out" : refreshMark;
1975
+ const transitFrame = Math.floor(sparkFrame / TRANSIT_FRAME_TICKS);
1976
+ const farewellLabel = leaving ? TRANSIT_LABEL.out : "";
1981
1977
  if (item.kind === "repo-header")
1982
1978
  return /* @__PURE__ */ jsx(RepoHeaderRow, { repo: item.repo, gap: gap ?? false, active });
1983
1979
  if (item.kind === "subgroup-header")
@@ -2001,11 +1997,10 @@ var ItemRow = ({
2001
1997
  ] });
2002
1998
  if (item.kind === "task") {
2003
1999
  const note = item.note ?? "";
2004
- const transitIcon = !transient ? " " : isDeparture(transient) ? TRANSIT_OUT_FRAMES[sparkFrame % TRANSIT_OUT_FRAMES.length] : isArrival(transient) ? TRANSIT_IN_FRAMES[sparkFrame % TRANSIT_IN_FRAMES.length] : "\u25C9";
2005
- const transitLabel2 = transient ? TRANSIT_LABEL[transient] : "";
2000
+ const transitIcon = !transient ? " " : isDeparture(transient) ? TRANSIT_OUT_FRAMES[transitFrame % TRANSIT_OUT_FRAMES.length] : isArrival(transient) ? TRANSIT_IN_FRAMES[transitFrame % TRANSIT_IN_FRAMES.length] : "\u25C9";
2006
2001
  const titleMax2 = Math.max(
2007
- 20,
2008
- cols - item.key.length - note.length - (item.pill ? pillWidth(item.pill) + 1 : 0) - transitLabel2.length - prefix.length - 12
2002
+ 1,
2003
+ cols - item.key.length - note.length - (item.pill ? pillWidth(item.pill) + 1 : 0) - (farewellLabel ? pillWidth(farewellLabel) + 2 : 0) - prefix.length - 12
2009
2004
  );
2010
2005
  return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
2011
2006
  /* @__PURE__ */ jsx(Text3, { color: "cyan", children: active ? "\u276F " : " " }),
@@ -2027,11 +2022,14 @@ var ItemRow = ({
2027
2022
  /* @__PURE__ */ jsx(Pill, { variant: item.pillVariant ?? "muted", children: item.pill })
2028
2023
  ] }) : null,
2029
2024
  note ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: ` ${note}` }) : null,
2030
- transitLabel2 ? /* @__PURE__ */ jsx(Text3, { bold: true, color: TRANSIT_COLOUR[transient], children: " " + transitLabel2 }) : null
2025
+ farewellLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
2026
+ /* @__PURE__ */ jsx(Text3, { children: " " }),
2027
+ /* @__PURE__ */ jsx(Pill, { color: TRANSIT_COLOUR.out, children: farewellLabel })
2028
+ ] }) : null
2031
2029
  ] });
2032
2030
  }
2033
2031
  const { glyph: healthIcon, color: healthColor2 } = healthDisplay[item.health];
2034
- const icon = merged ? MERGED_FRAMES[sparkFrame % MERGED_FRAMES.length] : transient && isDeparture(transient) ? TRANSIT_OUT_FRAMES[sparkFrame % TRANSIT_OUT_FRAMES.length] : transient && isArrival(transient) ? TRANSIT_IN_FRAMES[sparkFrame % TRANSIT_IN_FRAMES.length] : healthIcon;
2032
+ const icon = merged ? MERGED_FRAMES[sparkFrame % MERGED_FRAMES.length] : transient && isDeparture(transient) ? TRANSIT_OUT_FRAMES[transitFrame % TRANSIT_OUT_FRAMES.length] : transient && isArrival(transient) ? TRANSIT_IN_FRAMES[transitFrame % TRANSIT_IN_FRAMES.length] : healthIcon;
2035
2033
  const color = merged ? MERGED_COLOUR : transient ? TRANSIT_COLOUR[transient] : healthColor2;
2036
2034
  const spokeLast = !!login && !!item.lastActor && item.lastActor === login;
2037
2035
  const [turnIcon, turnColor] = item.pinned ? [PIN_MARK, "#FF8700"] : !login || !item.lastActor ? [" ", "white"] : spokeLast ? ["\u2192", "#888888"] : ["\u2190", "#FF8700"];
@@ -2040,17 +2038,36 @@ var ItemRow = ({
2040
2038
  const unresolvedLabel = item.unresolved > 0 ? `\uF086 ${item.unresolved}` : "";
2041
2039
  const ageLabel = item.activityAge && item.activityAge !== item.age ? `${item.activityAge} \xB7 ${item.age}` : item.age;
2042
2040
  const mergedLabel = merged ? "MERGED" : "";
2043
- const transitLabel = merged || !transient ? "" : TRANSIT_LABEL[transient];
2044
- const suffix = [
2045
- ageLabel || "",
2046
- unresolvedLabel,
2047
- showAuthor ? `by ${item.author}` : "",
2041
+ const pillCaps = [mergedLabel, farewellLabel].filter(Boolean).length * 2;
2042
+ const repoLabel = depthOf(item) > 0 ? item.repo : "";
2043
+ const givingUp = { author: false, threads: false, age: false, repo: false };
2044
+ const widthOf2 = () => {
2045
+ const suffix2 = [
2046
+ givingUp.age ? "" : ageLabel || "",
2047
+ givingUp.threads ? "" : unresolvedLabel,
2048
+ showAuthor && !givingUp.author ? `by ${item.author}` : "",
2049
+ mergedLabel
2050
+ ].filter(Boolean).join(" ");
2051
+ return 2 + prefix.length + 2 + 2 + 7 + (givingUp.repo ? 0 : repoLabel.length) + suffix2.length + pillCaps + 6;
2052
+ };
2053
+ const MIN_TITLE = 24;
2054
+ for (const give of [
2055
+ () => givingUp.author = true,
2056
+ () => givingUp.threads = true,
2057
+ () => givingUp.repo = true,
2058
+ () => givingUp.age = true
2059
+ ]) {
2060
+ if (cols - widthOf2() >= MIN_TITLE) break;
2061
+ give();
2062
+ }
2063
+ [
2064
+ givingUp.age ? "" : ageLabel || "",
2065
+ givingUp.threads ? "" : unresolvedLabel,
2066
+ showAuthor && !givingUp.author ? `by ${item.author}` : "",
2048
2067
  mergedLabel,
2049
- transitLabel
2068
+ farewellLabel
2050
2069
  ].filter(Boolean).join(" ");
2051
- const repoLabel = depthOf(item) > 0 ? item.repo : "";
2052
- const fixedWidth = 2 + prefix.length + 2 + 2 + 7 + repoLabel.length + suffix.length + 6;
2053
- const titleMax = Math.max(20, cols - fixedWidth);
2070
+ const titleMax = Math.max(1, cols - widthOf2());
2054
2071
  return /* @__PURE__ */ jsxs(Box, { children: [
2055
2072
  /* @__PURE__ */ jsx(Text3, { color: "cyan", children: active ? "\u276F " : " " }),
2056
2073
  /* @__PURE__ */ jsx(Text3, { dimColor: true, children: prefix }),
@@ -2066,12 +2083,18 @@ var ItemRow = ({
2066
2083
  children: truncate2(item.title, titleMax) + " "
2067
2084
  }
2068
2085
  ),
2069
- repoLabel ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: repoLabel }) : null,
2070
- unresolvedLabel ? /* @__PURE__ */ jsx(Text3, { bold: !spokeLast, color: spokeLast ? "#888888" : "#FF8700", children: " " + unresolvedLabel }) : null,
2071
- showAuthor ? /* @__PURE__ */ jsx(Text3, { dimColor: true, italic: true, children: " by " + item.author }) : null,
2072
- ageLabel ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " + ageLabel }) : null,
2073
- mergedLabel ? /* @__PURE__ */ jsx(Text3, { bold: true, color: MERGED_COLOUR, children: " " + mergedLabel }) : null,
2074
- transitLabel ? /* @__PURE__ */ jsx(Text3, { bold: true, color: TRANSIT_COLOUR[transient], children: " " + transitLabel }) : null
2086
+ repoLabel && !givingUp.repo ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: repoLabel }) : null,
2087
+ unresolvedLabel && !givingUp.threads ? /* @__PURE__ */ jsx(Text3, { bold: !spokeLast, color: spokeLast ? "#888888" : "#FF8700", children: " " + unresolvedLabel }) : null,
2088
+ showAuthor && !givingUp.author ? /* @__PURE__ */ jsx(Text3, { dimColor: true, italic: true, children: " by " + item.author }) : null,
2089
+ ageLabel && !givingUp.age ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " + ageLabel }) : null,
2090
+ mergedLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
2091
+ /* @__PURE__ */ jsx(Text3, { children: " " }),
2092
+ /* @__PURE__ */ jsx(Pill, { color: MERGED_COLOUR, children: mergedLabel })
2093
+ ] }) : null,
2094
+ farewellLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
2095
+ /* @__PURE__ */ jsx(Text3, { children: " " }),
2096
+ /* @__PURE__ */ jsx(Pill, { color: TRANSIT_COLOUR.out, children: farewellLabel })
2097
+ ] }) : null
2075
2098
  ] });
2076
2099
  };
2077
2100
  var useActionMenu = () => {
@@ -2389,6 +2412,7 @@ var BrowseScreen = ({
2389
2412
  refreshing,
2390
2413
  hasPending,
2391
2414
  pendingSummary,
2415
+ appliedSummary,
2392
2416
  fetchedAt,
2393
2417
  refreshError,
2394
2418
  hidden,
@@ -2517,7 +2541,12 @@ var BrowseScreen = ({
2517
2541
  const railAt = Math.min(railCursor, Math.max(0, railRows.length - 1));
2518
2542
  const listCols = showRail ? COLS - SIDEBAR_COLS : COLS;
2519
2543
  const [sparkFrame, setSparkFrame] = useState(0);
2520
- const sparkling = (mergedUrls?.length ?? 0) > 0 || (leavingUrls?.length ?? 0) > 0 || !!transients?.size && section.items.some((item) => transientOf(transients, item, section.id));
2544
+ const sparkling = (mergedUrls?.length ?? 0) > 0 || (leavingUrls?.length ?? 0) > 0 || // Any MARKED TAB, not just rows on screen. The scope used to be this tab's
2545
+ // own items, which was right while the animation lived on the rows and is
2546
+ // exactly wrong now it lives on the bar: a tab you are not looking at is the
2547
+ // whole case the pulse exists for — a marker inside it cannot be seen at all
2548
+ // — and a ticker gated on the visible tab would leave that dot sitting still.
2549
+ markedTabs.size > 0 || !!transients?.size && section.items.some((item) => transientOf(transients, item, section.id));
2521
2550
  useEffect(() => {
2522
2551
  if (!sparkling) return;
2523
2552
  const id = setInterval(() => setSparkFrame((f) => f + 1), MERGED_FRAME_MS);
@@ -2999,6 +3028,7 @@ var BrowseScreen = ({
2999
3028
  refreshing,
3000
3029
  hasPending,
3001
3030
  pendingSummary,
3031
+ appliedSummary,
3002
3032
  fetchedAt
3003
3033
  }
3004
3034
  ),
@@ -3013,8 +3043,16 @@ var BrowseScreen = ({
3013
3043
  active: section.id,
3014
3044
  items: localSections.map((s) => ({
3015
3045
  value: s.id,
3016
- label: tabLabel(s.label, markedTabs, s.id),
3017
- count: topLevelCount(s)
3046
+ label: s.label,
3047
+ count: topLevelCount(s),
3048
+ // Its own cell, always two wide, so news arriving never slides the
3049
+ // bar — and free to animate for exactly that reason.
3050
+ marker: tabMarker(
3051
+ markedTabs,
3052
+ s.id,
3053
+ Math.floor(sparkFrame / TRANSIT_FRAME_TICKS)
3054
+ ),
3055
+ markerColor: "#FF8700"
3018
3056
  }))
3019
3057
  }
3020
3058
  ) }),
@@ -3173,7 +3211,8 @@ var App = ({
3173
3211
  setState({ phase: "browse", sections, login });
3174
3212
  return;
3175
3213
  }
3176
- const { transients: fresh, union } = diffSections(before, sections);
3214
+ const { transients: fresh, union, counts: counts2 } = diffSections(before, sections);
3215
+ setAppliedSummary(summariseDiff(counts2));
3177
3216
  const marks = new Map(fresh);
3178
3217
  for (const [key, mark] of transients)
3179
3218
  if (!marks.has(key)) marks.set(key, mark);
@@ -3181,6 +3220,7 @@ var App = ({
3181
3220
  for (const key of [...marks.keys()]) if (!tabs.has(key)) marks.delete(key);
3182
3221
  if (marks.size === 0) {
3183
3222
  transitTabs.current = /* @__PURE__ */ new Map();
3223
+ setAppliedSummary("");
3184
3224
  setTransients(NO_TRANSIENTS);
3185
3225
  setState({ phase: "browse", sections, login });
3186
3226
  return;
@@ -3377,6 +3417,7 @@ var App = ({
3377
3417
  const [budget, setBudget] = useState(
3378
3418
  () => cacheKey ? readCache(cacheKey)?.budget ?? null : null
3379
3419
  );
3420
+ const [appliedSummary, setAppliedSummary] = useState("");
3380
3421
  const [sidebar, setSidebar] = useState(void 0);
3381
3422
  const [skippedForBudget, setSkippedForBudget] = useState(false);
3382
3423
  const [mergedUrls, setMergedUrls] = useState([]);
@@ -3473,6 +3514,7 @@ var App = ({
3473
3514
  refreshing,
3474
3515
  hasPending: pending !== null,
3475
3516
  pendingSummary,
3517
+ appliedSummary: transients.size > 0 ? appliedSummary : "",
3476
3518
  fetchedAt,
3477
3519
  refreshError: refreshError ?? void 0,
3478
3520
  origin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.40.0",
3
+ "version": "0.42.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",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@kud/gh": "0.9.0",
52
- "@kud/ink-ui": "0.17.0",
52
+ "@kud/ink-ui": "0.19.0",
53
53
  "zx": "8.8.5"
54
54
  },
55
55
  "devDependencies": {