@kud/gh-ink 0.45.2 → 0.45.3

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 CHANGED
@@ -279,24 +279,52 @@ declare const runInPaneHorizontal: (cmd: string) => Promise<void>;
279
279
  declare const runHere: (cmd: string) => void;
280
280
  declare const COLS: number;
281
281
  /**
282
- * What the tab badge and the header total both count: rows that are somebody's
283
- * work, at the top level.
282
+ * What the tab badge and the header total both count.
284
283
  *
285
- * The exclusions are two different kinds of thing. Headers and the
286
- * show-more/show-less affordances are furniture — not entities at all, which is
287
- * why the search and repo filters drop them too. This function used to exclude
288
- * only the two header kinds while its three siblings excluded all four, so a
289
- * collapsed tail sitting at the top level counted as an item.
290
- * `role: "container"` rows ARE entities, selectable and openable; they simply
291
- * are not work. An epic is the case that forced the distinction: real, owned,
292
- * and on screen until it closes, but the work is the stories under it, and
284
+ * ONE INVARIANT: a piece of work counts once, at whatever depth the board
285
+ * happens to file it. Everything below follows from that, and the name says so
286
+ * because the old one — `workCount` — read as a fact about the tree and so
287
+ * nobody re-asked whether the tree still meant what it had when it was written.
288
+ * It did not, and the count silently went to zero.
289
+ *
290
+ * Furniture never counts: headers and the show-more/show-less affordances are
291
+ * not entities at all, which is why the search and repo filters drop them too.
292
+ * `role: "container"` rows ARE entities, selectable and openable; they are
293
+ * simply not work. An epic is the case that forced the distinction — real,
294
+ * owned, on screen until it closes, but the work is the stories under it, and
293
295
  * counting it as well reported five things to do where there were four.
294
296
  *
297
+ * Everything else counts when it is a LEAF — when nothing hangs under it. That
298
+ * is the only predicate under which the invariant holds. A PR on a tab of its
299
+ * own counts one; the same PR after its ticket becomes parseable, so that it now
300
+ * hangs under a story, still counts one. Counting every non-container row would
301
+ * make that transition raise the total, which is the epic bug one level down:
302
+ * nothing was created, the board merely got tidier, and the header claims you
303
+ * have more to do.
304
+ *
305
+ * It also settles the 0-to-1 case in the right direction. A story with no PR is
306
+ * its own leaf and counts one — a ticket in review with nothing to show is real
307
+ * work, arguably the most actionable row on the board. It gains its first PR and
308
+ * still counts one: the story stops being the item and its PR becomes the item.
309
+ * A row earns container-hood by HAVING children, which is why this lives in the
310
+ * predicate rather than in a flag the host has to remember to set.
311
+ *
312
+ * A collapsed tail contributes what it HIDES, and that line is load-bearing:
313
+ * pressing return on a `show-more` splices its rows into `items` in place, so
314
+ * without it the badge would rise on expanding and fall on collapsing — a key
315
+ * that only changes what is drawn would change how much work you have. With it,
316
+ * collapsed contributes `hidden.length` and expanded contributes the same rows
317
+ * as leaves, and the number holds still.
318
+ *
319
+ * Leaf-ness is read off the RENDERED list, so a search that hides a story's PRs
320
+ * turns that story back into a leaf and it counts itself. That is deliberate:
321
+ * the badge describes what is on screen to act on.
322
+ *
295
323
  * One function, both consumers, on purpose. The badge and the whole-board total
296
324
  * are the same claim at two scales; computed apart they drift, and a header that
297
325
  * disagrees with the sum of its own tabs is worse than either number alone.
298
326
  */
299
- declare const topLevelCount: (s: Section) => number;
327
+ declare const workCount: (s: Section) => number;
300
328
  declare const drillCmd: (item: AnyItem) => string | null;
301
329
  declare const explainGhAction: (e: unknown) => string;
302
330
  declare const buildActions: (item: AnyItem, login: string, showFlash: (msg: string) => void, jiraBase?: string, jiraKeyRe?: RegExp, jiraTransitions?: JiraTransition[], onRefresh?: () => void, onRemove?: (item: GHItem) => void, onOpenView?: (item: AnyItem) => boolean, ext?: {
@@ -418,4 +446,4 @@ declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
418
446
  */
419
447
  declare const parsePatterns: (value: string) => string[];
420
448
 
421
- export { type Action, ActionMenu, App, COLS, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxExtension, type JiraTransition, type RepoFilter, SIDEBAR_COLS, SidePanel, type Sidebar, type SidebarRow, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, toCiStatusState, topLevelCount, truncate, useActionMenu, windowCount, withHeaders, withoutItem, writeCache };
449
+ export { type Action, ActionMenu, App, COLS, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxExtension, type JiraTransition, type RepoFilter, SIDEBAR_COLS, SidePanel, type Sidebar, type SidebarRow, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, toCiStatusState, truncate, useActionMenu, windowCount, withHeaders, withoutItem, workCount, writeCache };
package/dist/index.js CHANGED
@@ -1352,9 +1352,23 @@ var Backdrop = ({
1352
1352
  children
1353
1353
  }
1354
1354
  ) : children });
1355
- var topLevelCount = (s) => s.items.filter(
1356
- (i) => i.kind !== "repo-header" && i.kind !== "subgroup-header" && i.kind !== "show-more" && i.kind !== "show-less" && !("role" in i && i.role === "container") && depthOf(i) === 0
1357
- ).length;
1355
+ var workCount = (s) => {
1356
+ let n = 0;
1357
+ for (let i = 0; i < s.items.length; i++) {
1358
+ const item = s.items[i];
1359
+ if (!item) continue;
1360
+ if (item.kind === "show-more") {
1361
+ n += item.hidden.length;
1362
+ continue;
1363
+ }
1364
+ if (item.kind === "repo-header" || item.kind === "subgroup-header" || item.kind === "show-less")
1365
+ continue;
1366
+ if ("role" in item && item.role === "container") continue;
1367
+ if (depthOf(s.items[i + 1]) > depthOf(item)) continue;
1368
+ n += 1;
1369
+ }
1370
+ return n;
1371
+ };
1358
1372
  var drillCmd = (item) => {
1359
1373
  if (item.kind === "task")
1360
1374
  return item.ticket ? `jira issue view ${item.ticket}` : null;
@@ -1701,7 +1715,7 @@ var InboxHeader = ({
1701
1715
  appliedSummary,
1702
1716
  fetchedAt
1703
1717
  }) => {
1704
- const total = sections.reduce((n, s) => n + topLevelCount(s), 0);
1718
+ const total = sections.reduce((n, s) => n + workCount(s), 0);
1705
1719
  const countSeg = loading ? " loading\u2026 " : quiet ? " " : ` ${String(total).padStart(3)} item${total !== 1 ? "s" : ""} \xB7 `;
1706
1720
  const userSeg = loading || quiet ? "" : `@${login} `;
1707
1721
  const workLabel = scopeLabel ? ` ${scopeLabel} ` : "";
@@ -2960,7 +2974,7 @@ var BrowseScreen = ({
2960
2974
  items: localSections.map((s) => ({
2961
2975
  value: s.id,
2962
2976
  label: s.label,
2963
- count: topLevelCount(s),
2977
+ count: workCount(s),
2964
2978
  // Its own cell, always two wide, so news arriving never slides the
2965
2979
  // bar — and free to animate for exactly that reason.
2966
2980
  // The ticker undivided — see TAB_PULSE. The row marks' /3 buys quiet
@@ -3514,4 +3528,4 @@ var matchesFilter = (repo, filter) => {
3514
3528
  };
3515
3529
  var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
3516
3530
 
3517
- export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, SIDEBAR_COLS, SidePanel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, toCiStatusState, topLevelCount, truncate2 as truncate, useActionMenu, windowCount, withHeaders, withoutItem, writeCache };
3531
+ export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, SIDEBAR_COLS, SidePanel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, toCiStatusState, truncate2 as truncate, useActionMenu, windowCount, withHeaders, withoutItem, workCount, writeCache };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.45.2",
3
+ "version": "0.45.3",
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",