@kud/gh-ink 0.14.0 → 0.16.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 +91 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -607,7 +607,7 @@ var healthLegend = [
607
607
  ["closed", "Closed"]
608
608
  ];
609
609
  var CACHE_TTL_MS = 12e4;
610
- var CACHE_VERSION = 1;
610
+ var CACHE_VERSION = 2;
611
611
  var cacheDir = () => join(process.env.XDG_CACHE_HOME || join(homedir(), ".cache"), "ambre");
612
612
  var cacheFile = (key) => join(cacheDir(), `${key.replace(/[^a-z0-9._-]/gi, "-")}.json`);
613
613
  var readCache = (key) => {
@@ -733,6 +733,15 @@ var transientOf = (transients, item) => {
733
733
  const key = keyOf(item);
734
734
  return key ? transients.get(key) : void 0;
735
735
  };
736
+ var tabsOfMarks = (sections, marks) => {
737
+ const out = /* @__PURE__ */ new Map();
738
+ for (const section of sections)
739
+ for (const item of section.items) {
740
+ const key = keyOf(item);
741
+ if (key && marks.has(key)) out.set(key, section.id);
742
+ }
743
+ return out;
744
+ };
736
745
  var quietly = $({ quiet: true });
737
746
  var relativeTime = (iso) => {
738
747
  const diff = (Date.now() - new Date(iso).getTime()) / 1e3;
@@ -1575,6 +1584,8 @@ var TRANSIT_LABEL = {
1575
1584
  out: "GONE",
1576
1585
  changed: "UPDATED"
1577
1586
  };
1587
+ var TAB_MARK = "\u25CF";
1588
+ var tabLabel = (label, marked, id) => marked.size === 0 ? label : `${marked.has(id) ? TAB_MARK : " "} ${label}`;
1578
1589
  var ItemRow = ({
1579
1590
  item,
1580
1591
  active,
@@ -1607,12 +1618,27 @@ var ItemRow = ({
1607
1618
  ] });
1608
1619
  if (item.kind === "task") {
1609
1620
  const note = item.note ?? "";
1610
- const titleMax2 = Math.max(20, COLS - item.key.length - note.length - 10);
1621
+ const transitIcon = !transient ? " " : transient === "out" ? TRANSIT_OUT_FRAMES[sparkFrame % TRANSIT_OUT_FRAMES.length] : transient === "in" ? TRANSIT_IN_FRAMES[sparkFrame % TRANSIT_IN_FRAMES.length] : "\u25C9";
1622
+ const transitLabel2 = transient ? TRANSIT_LABEL[transient] : "";
1623
+ const titleMax2 = Math.max(
1624
+ 20,
1625
+ COLS - item.key.length - note.length - transitLabel2.length - 12
1626
+ );
1611
1627
  return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
1612
1628
  /* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
1629
+ /* @__PURE__ */ jsx(Text2, { bold: true, color: transient ? TRANSIT_COLOUR[transient] : void 0, children: transitIcon + " " }),
1613
1630
  /* @__PURE__ */ jsx(Text2, { color: "#FF8700", bold: active, children: item.key + " " }),
1614
- /* @__PURE__ */ jsx(Text2, { bold: active, children: truncate(item.summary, titleMax2) }),
1615
- note ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: ` ${note}` }) : null
1631
+ /* @__PURE__ */ jsx(
1632
+ Text2,
1633
+ {
1634
+ bold: active || transient === "in",
1635
+ dimColor: transient === "out",
1636
+ strikethrough: transient === "out",
1637
+ children: truncate(item.summary, titleMax2)
1638
+ }
1639
+ ),
1640
+ note ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: ` ${note}` }) : null,
1641
+ transitLabel2 ? /* @__PURE__ */ jsx(Text2, { bold: true, color: TRANSIT_COLOUR[transient], children: " " + transitLabel2 }) : null
1616
1642
  ] });
1617
1643
  }
1618
1644
  const { glyph: healthIcon, color: healthColor2 } = healthDisplay[item.health];
@@ -1947,7 +1973,8 @@ var BrowseScreen = ({
1947
1973
  initialIncludeWork,
1948
1974
  brand,
1949
1975
  mergedUrls,
1950
- transients
1976
+ transients,
1977
+ onTabChange
1951
1978
  }) => {
1952
1979
  const { rows } = useWindowSize();
1953
1980
  const [includeWork, setIncludeWork] = useState(
@@ -1963,6 +1990,9 @@ var BrowseScreen = ({
1963
1990
  const [viewStarts, setViewStarts] = useState({});
1964
1991
  const safeTabIdx = Math.min(tabIdx, Math.max(0, localSections.length - 1));
1965
1992
  const activeId = localSections[safeTabIdx]?.id ?? "";
1993
+ useEffect(() => {
1994
+ onTabChange?.(activeId);
1995
+ }, [activeId]);
1966
1996
  const [flash, setFlash] = useState(null);
1967
1997
  const menu = useActionMenu();
1968
1998
  const [search, setSearch] = useState(null);
@@ -2037,8 +2067,15 @@ var BrowseScreen = ({
2037
2067
  setFlash(msg);
2038
2068
  setTimeout(() => setFlash(null), 2e3);
2039
2069
  };
2070
+ const markedTabs = useMemo(() => {
2071
+ const out = /* @__PURE__ */ new Set();
2072
+ if (!transients?.size) return out;
2073
+ for (const s of localSections)
2074
+ if (s.items.some((item) => transientOf(transients, item))) out.add(s.id);
2075
+ return out;
2076
+ }, [localSections, transients]);
2040
2077
  const [sparkFrame, setSparkFrame] = useState(0);
2041
- const sparkling = (mergedUrls?.length ?? 0) > 0 || (transients?.size ?? 0) > 0;
2078
+ const sparkling = (mergedUrls?.length ?? 0) > 0 || !!transients?.size && section.items.some((item) => transientOf(transients, item));
2042
2079
  useEffect(() => {
2043
2080
  if (!sparkling) return;
2044
2081
  const id = setInterval(() => setSparkFrame((f) => f + 1), MERGED_FRAME_MS);
@@ -2387,7 +2424,7 @@ var BrowseScreen = ({
2387
2424
  active: section.id,
2388
2425
  items: localSections.map((s) => ({
2389
2426
  value: s.id,
2390
- label: s.label,
2427
+ label: tabLabel(s.label, markedTabs, s.id),
2391
2428
  count: topLevelCount(s)
2392
2429
  }))
2393
2430
  }
@@ -2515,13 +2552,8 @@ var App = ({
2515
2552
  const displayedKey = useRef("");
2516
2553
  const displayedSections = useRef([]);
2517
2554
  const [transients, setTransients] = useState(NO_TRANSIENTS);
2518
- const transitTimer = useRef(null);
2519
- useEffect(
2520
- () => () => {
2521
- if (transitTimer.current) clearTimeout(transitTimer.current);
2522
- },
2523
- []
2524
- );
2555
+ const transitTabs = useRef(/* @__PURE__ */ new Map());
2556
+ const [visibleTab, setVisibleTab] = useState("");
2525
2557
  const showData = (sections, login) => {
2526
2558
  const before = displayedSections.current;
2527
2559
  displayedSections.current = sections;
@@ -2529,25 +2561,61 @@ var App = ({
2529
2561
  setPending(null);
2530
2562
  setFetchedAt(Date.now());
2531
2563
  if (before.length === 0) {
2564
+ transitTabs.current = /* @__PURE__ */ new Map();
2532
2565
  setTransients(NO_TRANSIENTS);
2533
2566
  setState({ phase: "browse", sections, login });
2534
2567
  return;
2535
2568
  }
2536
- const { transients: marks, union } = diffSections(before, sections);
2569
+ const { transients: fresh, union } = diffSections(before, sections);
2570
+ const marks = new Map(fresh);
2571
+ for (const [key, mark] of transients)
2572
+ if (!marks.has(key)) marks.set(key, mark);
2573
+ const tabs = tabsOfMarks(union, marks);
2574
+ for (const key of [...marks.keys()]) if (!tabs.has(key)) marks.delete(key);
2537
2575
  if (marks.size === 0) {
2576
+ transitTabs.current = /* @__PURE__ */ new Map();
2577
+ setTransients(NO_TRANSIENTS);
2538
2578
  setState({ phase: "browse", sections, login });
2539
2579
  return;
2540
2580
  }
2581
+ transitTabs.current = tabs;
2541
2582
  setTransients(marks);
2542
2583
  setState({ phase: "browse", sections: union, login });
2543
- if (transitTimer.current) clearTimeout(transitTimer.current);
2544
- transitTimer.current = setTimeout(() => {
2545
- setTransients(NO_TRANSIENTS);
2546
- setState(
2547
- (prev) => prev.phase === "browse" && prev.sections === union ? { ...prev, sections } : prev
2548
- );
2549
- }, TRANSIT_HOLD_MS);
2550
2584
  };
2585
+ const settleTab = (tabId, marks) => {
2586
+ const keys = [...transitTabs.current].filter(([, tab]) => tab === tabId).map(([key]) => key);
2587
+ for (const key of keys) transitTabs.current.delete(key);
2588
+ const gone = new Set(keys.filter((key) => marks.get(key) === "out"));
2589
+ setTransients((prev) => {
2590
+ const next = new Map(prev);
2591
+ for (const key of keys) next.delete(key);
2592
+ return next.size === 0 ? NO_TRANSIENTS : next;
2593
+ });
2594
+ setState(
2595
+ (prev) => prev.phase !== "browse" ? prev : {
2596
+ ...prev,
2597
+ sections: prev.sections.map(
2598
+ (section) => section.id !== tabId ? section : {
2599
+ ...section,
2600
+ items: section.items.filter((item) => {
2601
+ const key = keyOf(item);
2602
+ return !(key && gone.has(key));
2603
+ })
2604
+ }
2605
+ )
2606
+ }
2607
+ );
2608
+ };
2609
+ useEffect(() => {
2610
+ if (transients.size === 0) return;
2611
+ if (state.phase !== "browse" || !visibleTab) return;
2612
+ if (![...transitTabs.current.values()].includes(visibleTab)) return;
2613
+ const timer = setTimeout(
2614
+ () => settleTab(visibleTab, transients),
2615
+ TRANSIT_HOLD_MS
2616
+ );
2617
+ return () => clearTimeout(timer);
2618
+ }, [transients, visibleTab, state.phase]);
2551
2619
  const pendingSummary = useMemo(
2552
2620
  () => pending ? summariseDiff(
2553
2621
  diffSections(displayedSections.current, pending.sections).counts
@@ -2732,6 +2800,7 @@ var App = ({
2732
2800
  hidden: overlay !== null,
2733
2801
  mergedUrls,
2734
2802
  transients,
2803
+ onTabChange: setVisibleTab,
2735
2804
  ciStatusState: hasCiStatus ? ciStatusState : void 0,
2736
2805
  ciJob,
2737
2806
  tabHelp,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.14.0",
3
+ "version": "0.16.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",