@kud/gh-ink 0.41.0 → 0.42.1

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 +42 -30
  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
2002
  1,
2008
- cols - item.key.length - note.length - (item.pill ? pillWidth(item.pill) + 1 : 0) - (transitLabel2 ? pillWidth(transitLabel2) : 0) - prefix.length - 12
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,14 +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__ */ jsxs(Fragment, { children: [
2025
+ farewellLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
2031
2026
  /* @__PURE__ */ jsx(Text3, { children: " " }),
2032
- /* @__PURE__ */ jsx(Pill, { color: TRANSIT_COLOUR[transient], children: transitLabel2 })
2027
+ /* @__PURE__ */ jsx(Pill, { color: TRANSIT_COLOUR.out, children: farewellLabel })
2033
2028
  ] }) : null
2034
2029
  ] });
2035
2030
  }
2036
2031
  const { glyph: healthIcon, color: healthColor2 } = healthDisplay[item.health];
2037
- 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;
2038
2033
  const color = merged ? MERGED_COLOUR : transient ? TRANSIT_COLOUR[transient] : healthColor2;
2039
2034
  const spokeLast = !!login && !!item.lastActor && item.lastActor === login;
2040
2035
  const [turnIcon, turnColor] = item.pinned ? [PIN_MARK, "#FF8700"] : !login || !item.lastActor ? [" ", "white"] : spokeLast ? ["\u2192", "#888888"] : ["\u2190", "#FF8700"];
@@ -2042,9 +2037,8 @@ var ItemRow = ({
2042
2037
  const showAuthor = !!item.author && item.author !== login;
2043
2038
  const unresolvedLabel = item.unresolved > 0 ? `\uF086 ${item.unresolved}` : "";
2044
2039
  const ageLabel = item.activityAge && item.activityAge !== item.age ? `${item.activityAge} \xB7 ${item.age}` : item.age;
2045
- const mergedLabel = merged ? "MERGED" : "";
2046
- const transitLabel = merged || !transient ? "" : TRANSIT_LABEL[transient];
2047
- const pillCaps = [mergedLabel, transitLabel].filter(Boolean).length * 2;
2040
+ const mergedLabel = merged ? "merged" : "";
2041
+ const pillCaps = [mergedLabel, farewellLabel].filter(Boolean).length * 2;
2048
2042
  const repoLabel = depthOf(item) > 0 ? item.repo : "";
2049
2043
  const givingUp = { author: false, threads: false, age: false, repo: false };
2050
2044
  const widthOf2 = () => {
@@ -2052,8 +2046,7 @@ var ItemRow = ({
2052
2046
  givingUp.age ? "" : ageLabel || "",
2053
2047
  givingUp.threads ? "" : unresolvedLabel,
2054
2048
  showAuthor && !givingUp.author ? `by ${item.author}` : "",
2055
- mergedLabel,
2056
- transitLabel
2049
+ mergedLabel
2057
2050
  ].filter(Boolean).join(" ");
2058
2051
  return 2 + prefix.length + 2 + 2 + 7 + (givingUp.repo ? 0 : repoLabel.length) + suffix2.length + pillCaps + 6;
2059
2052
  };
@@ -2072,7 +2065,7 @@ var ItemRow = ({
2072
2065
  givingUp.threads ? "" : unresolvedLabel,
2073
2066
  showAuthor && !givingUp.author ? `by ${item.author}` : "",
2074
2067
  mergedLabel,
2075
- transitLabel
2068
+ farewellLabel
2076
2069
  ].filter(Boolean).join(" ");
2077
2070
  const titleMax = Math.max(1, cols - widthOf2());
2078
2071
  return /* @__PURE__ */ jsxs(Box, { children: [
@@ -2098,9 +2091,9 @@ var ItemRow = ({
2098
2091
  /* @__PURE__ */ jsx(Text3, { children: " " }),
2099
2092
  /* @__PURE__ */ jsx(Pill, { color: MERGED_COLOUR, children: mergedLabel })
2100
2093
  ] }) : null,
2101
- transitLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
2094
+ farewellLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
2102
2095
  /* @__PURE__ */ jsx(Text3, { children: " " }),
2103
- /* @__PURE__ */ jsx(Pill, { color: TRANSIT_COLOUR[transient], children: transitLabel })
2096
+ /* @__PURE__ */ jsx(Pill, { color: TRANSIT_COLOUR.out, children: farewellLabel })
2104
2097
  ] }) : null
2105
2098
  ] });
2106
2099
  };
@@ -2419,6 +2412,7 @@ var BrowseScreen = ({
2419
2412
  refreshing,
2420
2413
  hasPending,
2421
2414
  pendingSummary,
2415
+ appliedSummary,
2422
2416
  fetchedAt,
2423
2417
  refreshError,
2424
2418
  hidden,
@@ -2547,7 +2541,12 @@ var BrowseScreen = ({
2547
2541
  const railAt = Math.min(railCursor, Math.max(0, railRows.length - 1));
2548
2542
  const listCols = showRail ? COLS - SIDEBAR_COLS : COLS;
2549
2543
  const [sparkFrame, setSparkFrame] = useState(0);
2550
- 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));
2551
2550
  useEffect(() => {
2552
2551
  if (!sparkling) return;
2553
2552
  const id = setInterval(() => setSparkFrame((f) => f + 1), MERGED_FRAME_MS);
@@ -3029,6 +3028,7 @@ var BrowseScreen = ({
3029
3028
  refreshing,
3030
3029
  hasPending,
3031
3030
  pendingSummary,
3031
+ appliedSummary,
3032
3032
  fetchedAt
3033
3033
  }
3034
3034
  ),
@@ -3043,8 +3043,16 @@ var BrowseScreen = ({
3043
3043
  active: section.id,
3044
3044
  items: localSections.map((s) => ({
3045
3045
  value: s.id,
3046
- label: tabLabel(s.label, markedTabs, s.id),
3047
- 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"
3048
3056
  }))
3049
3057
  }
3050
3058
  ) }),
@@ -3203,7 +3211,8 @@ var App = ({
3203
3211
  setState({ phase: "browse", sections, login });
3204
3212
  return;
3205
3213
  }
3206
- const { transients: fresh, union } = diffSections(before, sections);
3214
+ const { transients: fresh, union, counts: counts2 } = diffSections(before, sections);
3215
+ setAppliedSummary(summariseDiff(counts2));
3207
3216
  const marks = new Map(fresh);
3208
3217
  for (const [key, mark] of transients)
3209
3218
  if (!marks.has(key)) marks.set(key, mark);
@@ -3211,6 +3220,7 @@ var App = ({
3211
3220
  for (const key of [...marks.keys()]) if (!tabs.has(key)) marks.delete(key);
3212
3221
  if (marks.size === 0) {
3213
3222
  transitTabs.current = /* @__PURE__ */ new Map();
3223
+ setAppliedSummary("");
3214
3224
  setTransients(NO_TRANSIENTS);
3215
3225
  setState({ phase: "browse", sections, login });
3216
3226
  return;
@@ -3407,6 +3417,7 @@ var App = ({
3407
3417
  const [budget, setBudget] = useState(
3408
3418
  () => cacheKey ? readCache(cacheKey)?.budget ?? null : null
3409
3419
  );
3420
+ const [appliedSummary, setAppliedSummary] = useState("");
3410
3421
  const [sidebar, setSidebar] = useState(void 0);
3411
3422
  const [skippedForBudget, setSkippedForBudget] = useState(false);
3412
3423
  const [mergedUrls, setMergedUrls] = useState([]);
@@ -3503,6 +3514,7 @@ var App = ({
3503
3514
  refreshing,
3504
3515
  hasPending: pending !== null,
3505
3516
  pendingSummary,
3517
+ appliedSummary: transients.size > 0 ? appliedSummary : "",
3506
3518
  fetchedAt,
3507
3519
  refreshError: refreshError ?? void 0,
3508
3520
  origin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.41.0",
3
+ "version": "0.42.1",
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.18.0",
52
+ "@kud/ink-ui": "0.19.0",
53
53
  "zx": "8.8.5"
54
54
  },
55
55
  "devDependencies": {