@gaia-ai/addon-gaia-ui 0.11.2 → 0.11.4

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 (95) hide show
  1. package/dist/src/Component/top-tabs/index.d.ts +18 -1
  2. package/dist/src/Component/top-tabs/index.js +15 -2
  3. package/dist/src/Entity/conductor/Data/conductors.d.ts +174 -15
  4. package/dist/src/Entity/conductor/Data/conductors.js +74 -16
  5. package/dist/src/Entity/conductor/Data/own-conductor.js +17 -3
  6. package/dist/src/Entity/conductor/Data/process.d.ts +29 -2
  7. package/dist/src/Entity/conductor/Data/process.js +40 -4
  8. package/dist/src/Entity/conductor/Screen/conductors.d.ts +43 -0
  9. package/dist/src/Entity/conductor/Screen/conductors.js +113 -32
  10. package/dist/src/Entity/conductor/Screen/project-conductors.d.ts +87 -0
  11. package/dist/src/Entity/conductor/Screen/project-conductors.js +245 -0
  12. package/dist/src/Entity/project/Data/projects.d.ts +7 -5
  13. package/dist/src/Entity/project/Data/projects.js +8 -6
  14. package/dist/src/Kernel/splash.d.ts +8 -0
  15. package/dist/src/Kernel/splash.js +11 -1
  16. package/dist/src/Routing/routes.js +2 -0
  17. package/package.json +2 -2
  18. package/dist/src/Agent/Component/agent-list/index.d.ts +0 -6
  19. package/dist/src/Agent/Component/agent-list/index.js +0 -14
  20. package/dist/src/Component/agent-list/index.d.ts +0 -6
  21. package/dist/src/Component/agent-list/index.js +0 -14
  22. package/dist/src/Component/comment-item/index.d.ts +0 -21
  23. package/dist/src/Component/comment-item/index.js +0 -39
  24. package/dist/src/Component/create-form/index.d.ts +0 -3
  25. package/dist/src/Component/create-form/index.js +0 -26
  26. package/dist/src/Component/dashboard-list/index.d.ts +0 -8
  27. package/dist/src/Component/dashboard-list/index.js +0 -7
  28. package/dist/src/Component/project-picker/index.d.ts +0 -15
  29. package/dist/src/Component/project-picker/index.js +0 -42
  30. package/dist/src/Component/run-table/index.d.ts +0 -10
  31. package/dist/src/Component/run-table/index.js +0 -20
  32. package/dist/src/Component/tab-bar/index.d.ts +0 -20
  33. package/dist/src/Component/tab-bar/index.js +0 -42
  34. package/dist/src/Component/ticket-detail/index.d.ts +0 -38
  35. package/dist/src/Component/ticket-detail/index.js +0 -193
  36. package/dist/src/Component/ticket-table/index.d.ts +0 -5
  37. package/dist/src/Component/ticket-table/index.js +0 -14
  38. package/dist/src/Component/ticket-teaser/index.d.ts +0 -7
  39. package/dist/src/Component/ticket-teaser/index.js +0 -76
  40. package/dist/src/Deriver/liveness.d.ts +0 -12
  41. package/dist/src/Deriver/liveness.js +0 -34
  42. package/dist/src/Deriver/run-status.d.ts +0 -12
  43. package/dist/src/Deriver/run-status.js +0 -36
  44. package/dist/src/Entity/conductor/Component/conductor-list/index.d.ts +0 -25
  45. package/dist/src/Entity/conductor/Component/conductor-list/index.js +0 -60
  46. package/dist/src/Entity/project/Component/dashboard-list/index.d.ts +0 -8
  47. package/dist/src/Entity/project/Component/dashboard-list/index.js +0 -7
  48. package/dist/src/Entity/project/Component/project-list/index.d.ts +0 -8
  49. package/dist/src/Entity/project/Component/project-list/index.js +0 -7
  50. package/dist/src/Entity/project/Component/project-picker/index.d.ts +0 -15
  51. package/dist/src/Entity/project/Component/project-picker/index.js +0 -42
  52. package/dist/src/Entity/run/Component/run-table/index.d.ts +0 -8
  53. package/dist/src/Entity/run/Component/run-table/index.js +0 -11
  54. package/dist/src/Entity/ticket/Component/tab-bar/index.d.ts +0 -20
  55. package/dist/src/Entity/ticket/Component/tab-bar/index.js +0 -71
  56. package/dist/src/Entity/ticket/Component/ticket-table/index.d.ts +0 -7
  57. package/dist/src/Entity/ticket/Component/ticket-table/index.js +0 -10
  58. package/dist/src/Entity/user/Screen/users.d.ts +0 -39
  59. package/dist/src/Entity/user/Screen/users.js +0 -136
  60. package/dist/src/Screen/dashboard.d.ts +0 -1
  61. package/dist/src/Screen/dashboard.js +0 -1
  62. package/dist/src/Screen/project.d.ts +0 -1
  63. package/dist/src/Screen/project.js +0 -1
  64. package/dist/src/Screen/projects.d.ts +0 -1
  65. package/dist/src/Screen/projects.js +0 -1
  66. package/dist/src/Screen/ticket.d.ts +0 -1
  67. package/dist/src/Screen/ticket.js +0 -1
  68. package/dist/src/Service/agent.d.ts +0 -17
  69. package/dist/src/Service/agent.js +0 -28
  70. package/dist/src/Service/create-form-data.d.ts +0 -10
  71. package/dist/src/Service/create-form-data.js +0 -52
  72. package/dist/src/Service/create-form.d.ts +0 -52
  73. package/dist/src/Service/create-form.js +0 -98
  74. package/dist/src/Service/project-data.d.ts +0 -48
  75. package/dist/src/Service/project-data.js +0 -92
  76. package/dist/src/Service/projects.d.ts +0 -21
  77. package/dist/src/Service/projects.js +0 -80
  78. package/dist/src/Service/route.d.ts +0 -11
  79. package/dist/src/Service/route.js +0 -215
  80. package/dist/src/Service/run-data.d.ts +0 -35
  81. package/dist/src/Service/run-data.js +0 -62
  82. package/dist/src/Service/ticket-data.d.ts +0 -6
  83. package/dist/src/Service/ticket-data.js +0 -7
  84. package/dist/src/Tui/controller.d.ts +0 -13
  85. package/dist/src/Tui/controller.js +0 -1794
  86. package/dist/src/lib/ansi.d.ts +0 -34
  87. package/dist/src/lib/ansi.js +0 -97
  88. package/dist/src/lib/hit-test.d.ts +0 -138
  89. package/dist/src/lib/hit-test.js +0 -138
  90. package/dist/src/lib/mouse.d.ts +0 -35
  91. package/dist/src/lib/mouse.js +0 -89
  92. package/dist/src/lib/table.d.ts +0 -37
  93. package/dist/src/lib/table.js +0 -20
  94. package/dist/src/lib/ticket-query.d.ts +0 -21
  95. package/dist/src/lib/ticket-query.js +0 -72
@@ -92,10 +92,12 @@ export declare const PROJECT_TABLE: {
92
92
  * A CLOSED VOCABULARY, and deliberately unbounded for that reason — the
93
93
  * same shape of argument the run list's STATE gives.
94
94
  *
95
- * `ConductorStatus` is `running | stale | offline | registry-only`, whose
96
- * longest member is 13 characters, so a width of 14 already shows every one
97
- * of them whole. A `max` would be a constant nobody reads and a `grow` would
98
- * have nothing left to reach.
95
+ * `ConductorLiveness` is `online | stale | offline` (GAIA-450 D11 renamed
96
+ * the fresh state from `running`; the `registry-only` fourth state went
97
+ * with the local registry), whose longest member is 7 characters, so the
98
+ * width of 14 this column has always had shows every one of them whole
99
+ * with room to spare. A `max` would be a constant nobody reads and a
100
+ * `grow` would have nothing left to reach.
99
101
  */
100
102
  readonly key: "status";
101
103
  readonly label: "STATUS";
@@ -133,7 +135,7 @@ export declare const PROJECT_TABLE: {
133
135
  * `gaia_conductor` entities (read is owner-gated, so this is already scoped to
134
136
  * the current user's conductors — no local registry needed), sideload their
135
137
  * `project_id`, group by project name, and report each project's liveliest
136
- * conductor (running > stale > offline). Everything is a Drupal entity read
138
+ * conductor (online > stale > offline). Everything is a Drupal entity read
137
139
  * through `services.client` (AC-5); the screen itself stays a screen/route.
138
140
  */
139
141
  export declare function loadProjectRows({ client, nowSeconds, }: {
@@ -22,7 +22,7 @@ function singleRelId(rel) {
22
22
  // Most-alive-first ranking so a project with several conductors reports its
23
23
  // liveliest one, not whichever the collection happened to return first.
24
24
  const STATUS_RANK = {
25
- running: 0,
25
+ online: 0,
26
26
  stale: 1,
27
27
  offline: 2,
28
28
  'registry-only': 3,
@@ -101,10 +101,12 @@ export const PROJECT_TABLE = {
101
101
  * A CLOSED VOCABULARY, and deliberately unbounded for that reason — the
102
102
  * same shape of argument the run list's STATE gives.
103
103
  *
104
- * `ConductorStatus` is `running | stale | offline | registry-only`, whose
105
- * longest member is 13 characters, so a width of 14 already shows every one
106
- * of them whole. A `max` would be a constant nobody reads and a `grow` would
107
- * have nothing left to reach.
104
+ * `ConductorLiveness` is `online | stale | offline` (GAIA-450 D11 renamed
105
+ * the fresh state from `running`; the `registry-only` fourth state went
106
+ * with the local registry), whose longest member is 7 characters, so the
107
+ * width of 14 this column has always had shows every one of them whole
108
+ * with room to spare. A `max` would be a constant nobody reads and a
109
+ * `grow` would have nothing left to reach.
108
110
  */
109
111
  key: 'status',
110
112
  label: 'STATUS',
@@ -144,7 +146,7 @@ export const PROJECT_TABLE = {
144
146
  * `gaia_conductor` entities (read is owner-gated, so this is already scoped to
145
147
  * the current user's conductors — no local registry needed), sideload their
146
148
  * `project_id`, group by project name, and report each project's liveliest
147
- * conductor (running > stale > offline). Everything is a Drupal entity read
149
+ * conductor (online > stale > offline). Everything is a Drupal entity read
148
150
  * through `services.client` (AC-5); the screen itself stays a screen/route.
149
151
  */
150
152
  export async function loadProjectRows({ client, nowSeconds, }) {
@@ -15,6 +15,14 @@ export declare const SPLASH_FRAME_WIDTH: number;
15
15
  /**
16
16
  * Centered BORDER box with an ASCIIFont GAIA mark — modal-like, not a wash.
17
17
  * Font is OpenTUI block letters; the terminal typeface is unchanged.
18
+ *
19
+ * `position: 'absolute'` is load-bearing: the splash is mounted into the SAME
20
+ * `shell` that `createRegions` has already filled with `region-chrome`,
21
+ * `region-body` (`flexGrow: 1`) and `region-footer`. A column child would take a
22
+ * SLICE of the terminal beside the body's grow and land off-centre; out of the
23
+ * flow it fills the whole shell (`inset 0`, `100%` × `100%`) and its own
24
+ * `justifyContent`/`alignItems: center` place the frame in the true middle —
25
+ * the same treatment `region-modal` gets (`regions.ts`).
18
26
  */
19
27
  export declare const splashTree: () => VNode;
20
28
  export type SplashWait = (ms: number) => Promise<void>;
@@ -21,11 +21,21 @@ export const SPLASH_FRAME_WIDTH = splashMarkSize.width + SPLASH_FRAME_PAD_X * 2
21
21
  /**
22
22
  * Centered BORDER box with an ASCIIFont GAIA mark — modal-like, not a wash.
23
23
  * Font is OpenTUI block letters; the terminal typeface is unchanged.
24
+ *
25
+ * `position: 'absolute'` is load-bearing: the splash is mounted into the SAME
26
+ * `shell` that `createRegions` has already filled with `region-chrome`,
27
+ * `region-body` (`flexGrow: 1`) and `region-footer`. A column child would take a
28
+ * SLICE of the terminal beside the body's grow and land off-centre; out of the
29
+ * flow it fills the whole shell (`inset 0`, `100%` × `100%`) and its own
30
+ * `justifyContent`/`alignItems: center` place the frame in the true middle —
31
+ * the same treatment `region-modal` gets (`regions.ts`).
24
32
  */
25
33
  export const splashTree = () => h(BoxRenderable, {
26
34
  id: 'splash-root',
35
+ position: 'absolute',
36
+ left: 0,
37
+ top: 0,
27
38
  flexDirection: 'column',
28
- flexGrow: 1,
29
39
  width: '100%',
30
40
  height: '100%',
31
41
  justifyContent: 'center',
@@ -1,4 +1,5 @@
1
1
  import { ConductorsScreen } from '../Entity/conductor/Screen/conductors.js';
2
+ import { ProjectConductorsScreen } from '../Entity/conductor/Screen/project-conductors.js';
2
3
  import { ProjectPickerScreen } from '../Entity/project/Screen/project-picker.js';
3
4
  import { ProjectsScreen } from '../Entity/project/Screen/projects-screen.js';
4
5
  import { RunDetailScreen } from '../Entity/run/Screen/run-detail.js';
@@ -65,6 +66,7 @@ export const routes = defineRoutes([
65
66
  TicketAgentsScreen,
66
67
  WhoFilterScreen,
67
68
  RunsScreen,
69
+ ProjectConductorsScreen,
68
70
  TicketDetailScreen,
69
71
  RunDetailScreen,
70
72
  // No instance-wide `UsersScreen` any more: the users LIST is the team
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaia-ai/addon-gaia-ui",
3
- "version": "0.11.2",
3
+ "version": "0.11.4",
4
4
  "description": "GAIA TUI: interactive terminal UI (dashboard, projects, tickets) as a dropsh `tui` renderer plugin.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,6 +26,6 @@
26
26
  "drupal-jsonapi-params": "^3.0.1"
27
27
  },
28
28
  "peerDependencies": {
29
- "@gaia-ai/core": "^0.11.2"
29
+ "@gaia-ai/core": "^0.11.4"
30
30
  }
31
31
  }
@@ -1,6 +0,0 @@
1
- import type { HostedAgent } from '../../../types.js';
2
- /** A fixed-column flat table for the Ticket-Agents screen: header + one line per
3
- * live agent (its label · the project/group it runs for). One physical row per
4
- * entry, so the Kernel's `1 + index` selection math holds. The agents are the
5
- * host's live set (from `list(group)`); a finished agent drops out on refresh. */
6
- export declare function agentListRows(agents: HostedAgent[]): string[];
@@ -1,14 +0,0 @@
1
- import { pad } from '../../../lib/format.js';
2
- /** A fixed-column flat table for the Ticket-Agents screen: header + one line per
3
- * live agent (its label · the project/group it runs for). One physical row per
4
- * entry, so the Kernel's `1 + index` selection math holds. The agents are the
5
- * host's live set (from `list(group)`); a finished agent drops out on refresh. */
6
- export function agentListRows(agents) {
7
- const header = `${pad('AGENT', 32)} PROJECT`;
8
- if (agents.length === 0)
9
- return [header, 'No ticket agents.'];
10
- return [
11
- header,
12
- ...agents.map((a) => `${pad(a.label || a.id, 32)} ${a.group}`),
13
- ];
14
- }
@@ -1,6 +0,0 @@
1
- import type { HostedAgent } from '../../types.js';
2
- /** A fixed-column flat table for the Ticket-Agents screen: header + one line per
3
- * live agent (its label · the project/group it runs for). One physical row per
4
- * entry, so the Kernel's `1 + index` selection math holds. The agents are the
5
- * host's live set (from `list(group)`); a finished agent drops out on refresh. */
6
- export declare function agentListRows(agents: HostedAgent[]): string[];
@@ -1,14 +0,0 @@
1
- import { pad } from '../../lib/format.js';
2
- /** A fixed-column flat table for the Ticket-Agents screen: header + one line per
3
- * live agent (its label · the project/group it runs for). One physical row per
4
- * entry, so the Kernel's `1 + index` selection math holds. The agents are the
5
- * host's live set (from `list(group)`); a finished agent drops out on refresh. */
6
- export function agentListRows(agents) {
7
- const header = `${pad('AGENT', 32)} PROJECT`;
8
- if (agents.length === 0)
9
- return [header, 'No ticket agents.'];
10
- return [
11
- header,
12
- ...agents.map((a) => `${pad(a.label || a.id, 32)} ${a.group}`),
13
- ];
14
- }
@@ -1,21 +0,0 @@
1
- /** One collapsible comment entry for the Comments tab. */
2
- export interface CommentItem {
3
- type: string;
4
- author: string;
5
- created: string | null;
6
- bodyLines: string[];
7
- preview: string;
8
- }
9
- /** The rendered Comments-tab view. */
10
- export interface CommentsTabView {
11
- lines: string[];
12
- boldRows: Set<number>;
13
- headerRows: number[];
14
- selectable: number;
15
- }
16
- /** The relative age of a comment's ISO `created`, or '—' when unknown. */
17
- export declare function relativeCreated(created: string | null | undefined, nowSeconds: number): string;
18
- export declare function commentsTabView(items: CommentItem[], { expanded, nowSeconds, }?: {
19
- expanded?: Set<number>;
20
- nowSeconds?: number;
21
- }): CommentsTabView;
@@ -1,39 +0,0 @@
1
- import { humanizeAge } from '../../Deriver/liveness.js';
2
- /** The relative age of a comment's ISO `created`, or '—' when unknown. */
3
- export function relativeCreated(created, nowSeconds) {
4
- const ms = created ? Date.parse(created) : Number.NaN;
5
- return Number.isNaN(ms)
6
- ? '—'
7
- : humanizeAge(Math.floor(ms / 1000), nowSeconds);
8
- }
9
- // The Comments tab: one collapsible entry per comment. Collapsed = a single
10
- // caret header row (`▸ type · author · age · preview`); expanded (`▾`) appends
11
- // the full body, one physical row per body line. `headerRows[i]` is the row
12
- // index of comment i's header (for selection highlight + scroll).
13
- export function commentsTabView(items, { expanded = new Set(), nowSeconds = 0, } = {}) {
14
- if (!items.length) {
15
- return {
16
- lines: ['No comments.'],
17
- boldRows: new Set(),
18
- headerRows: [],
19
- selectable: 0,
20
- };
21
- }
22
- const lines = [];
23
- const boldRows = new Set();
24
- const headerRows = [];
25
- items.forEach((item, i) => {
26
- headerRows.push(lines.length);
27
- boldRows.add(lines.length);
28
- const caret = expanded.has(i) ? '▾' : '▸';
29
- const age = relativeCreated(item.created, nowSeconds);
30
- const author = item.author || '—';
31
- lines.push(`${caret} ${item.type} · ${author} · ${age} · ${item.preview}`);
32
- if (expanded.has(i)) {
33
- for (const bodyLine of item.bodyLines)
34
- lines.push(` ${bodyLine}`);
35
- lines.push('');
36
- }
37
- });
38
- return { lines, boldRows, headerRows, selectable: items.length };
39
- }
@@ -1,3 +0,0 @@
1
- import type { CreateFormOptions, CreateFormState } from '../../Service/create-form.js';
2
- /** Render the "New ticket" popup form as plain rows (the kernel frames it). */
3
- export declare function createFormLines(s: CreateFormState, opts: CreateFormOptions): string[];
@@ -1,26 +0,0 @@
1
- const labelOf = (list, id) => list.find((o) => o.id === id)?.label ?? '—';
2
- /** Render the "New ticket" popup form as plain rows (the kernel frames it). */
3
- export function createFormLines(s, opts) {
4
- const mark = (i) => (s.active === i ? '▸ ' : ' ');
5
- const lines = [];
6
- lines.push(`${mark(0)}Description:`);
7
- const body = s.description.length ? s.description.split('\n') : ['<empty>'];
8
- for (const line of body)
9
- lines.push(` ${line}`);
10
- lines.push(`${mark(1)}Assignee: < ${labelOf(opts.users, s.assignee)} >`);
11
- lines.push(`${mark(2)}Conductor: < ${labelOf(opts.conductors, s.conductor)} >`);
12
- lines.push(`${mark(3)}Labels:`);
13
- if (opts.labels.length === 0) {
14
- lines.push(' (none available)');
15
- }
16
- else {
17
- opts.labels.forEach((o, i) => {
18
- const box = s.labels.has(o.id) ? '[x]' : '[ ]';
19
- const cursor = s.active === 3 && s.labelCursor === i ? '›' : ' ';
20
- lines.push(` ${cursor}${box} ${o.label}`);
21
- });
22
- }
23
- lines.push(`${mark(4)}Workflow: < ${labelOf(opts.workflows, s.workflow)} >`);
24
- lines.push(`${mark(5)}[ Create ticket ]`);
25
- return lines;
26
- }
@@ -1,8 +0,0 @@
1
- /** The minimum shape a dashboard row needs to render. */
2
- export interface DashboardRowInput {
3
- project: string;
4
- derivedStatus: string;
5
- ageLabel: string;
6
- }
7
- /** One line per project: name, derived status, relative age. */
8
- export declare function dashboardRows(projects: DashboardRowInput[]): string[];
@@ -1,7 +0,0 @@
1
- import { pad } from '../../lib/format.js';
2
- /** One line per project: name, derived status, relative age. */
3
- export function dashboardRows(projects) {
4
- if (projects.length === 0)
5
- return ['No projects.'];
6
- return projects.map((p) => `${pad(p.project, 24)} ${pad(p.derivedStatus, 14)} ${p.ageLabel}`);
7
- }
@@ -1,15 +0,0 @@
1
- /** A project that claims the repo. */
2
- export interface PickerCandidate {
3
- id: string;
4
- name: string;
5
- }
6
- /**
7
- * The picker screen's rows: a heading naming the repo, then one row per
8
- * candidate with `▸` on the selected one.
9
- */
10
- export declare function projectPickerRows(candidates: PickerCandidate[], repo: string, selected: number): string[];
11
- /**
12
- * The non-TTY form (spec Decision 11): the candidate list plus the direct
13
- * command, printed once so `gaia ui --here | cat` stays scriptable — no prompt.
14
- */
15
- export declare function projectPickerHint(candidates: PickerCandidate[], repo: string): string;
@@ -1,42 +0,0 @@
1
- // GAIA-223 AC-8 (spec Decision 14) — the picker `gaia ui --here` shows when the
2
- // cwd's repo is claimed by MORE THAN ONE gaia project.
3
- //
4
- // One repo mapping to several projects is designed for, not an edge case:
5
- // GAIA-126/137 let `.gaia/` hold one conductor config per project, which is how
6
- // a Drupal multisite repo maps each site to its own GAIA project. Erroring on
7
- // N>1 would fail permanently, by design, in exactly the repos the feature is
8
- // most useful in — so the ambiguity is shown rather than resolved behind the
9
- // user's back.
10
- //
11
- // Pure, like every other Component: candidates in, rows out. The kernel owns
12
- // selection state, Enter and Esc.
13
- /** Clamp a selection index into the candidate range (0 when there are none). */
14
- function clamp(selected, length) {
15
- if (length <= 0)
16
- return 0;
17
- return Math.max(0, Math.min(Math.trunc(selected), length - 1));
18
- }
19
- /**
20
- * The picker screen's rows: a heading naming the repo, then one row per
21
- * candidate with `▸` on the selected one.
22
- */
23
- export function projectPickerRows(candidates, repo, selected) {
24
- const heading = [`Several gaia projects claim ${repo} — which one?`, ''];
25
- if (candidates.length === 0) {
26
- return [...heading, 'No projects.'];
27
- }
28
- const index = clamp(selected, candidates.length);
29
- return [
30
- ...heading,
31
- ...candidates.map((c, i) => `${i === index ? '▸' : ' '} ${c.name}`),
32
- ];
33
- }
34
- /**
35
- * The non-TTY form (spec Decision 11): the candidate list plus the direct
36
- * command, printed once so `gaia ui --here | cat` stays scriptable — no prompt.
37
- */
38
- export function projectPickerHint(candidates, repo) {
39
- const names = candidates.map((c) => ` ${c.name}`).join('\n');
40
- return (`Several gaia projects claim ${repo}:\n${names}\n` +
41
- "Open one directly with 'gaia ui project:<name>'.");
42
- }
@@ -1,10 +0,0 @@
1
- import type { RunRow } from '../../Service/run-data.js';
2
- /**
3
- * A fixed-column flat table of a project's runs: header + one line per run.
4
- * Columns RUN · TICKET · STATE · PHASE · AGENT · STARTED, mirroring
5
- * `ticketTableRows` (1 logical entry = 1 physical row). STATE is the derived
6
- * liveness status and STARTED is the relative start age.
7
- */
8
- export declare function runTableRows(runs: RunRow[], nowSeconds: number, { emptyMessage }?: {
9
- emptyMessage?: string | undefined;
10
- }): string[];
@@ -1,20 +0,0 @@
1
- import { deriveRunStatus, runStartedAge } from '../../Deriver/run-status.js';
2
- import { pad } from '../../lib/format.js';
3
- /**
4
- * A fixed-column flat table of a project's runs: header + one line per run.
5
- * Columns RUN · TICKET · STATE · PHASE · AGENT · STARTED, mirroring
6
- * `ticketTableRows` (1 logical entry = 1 physical row). STATE is the derived
7
- * liveness status and STARTED is the relative start age.
8
- */
9
- export function runTableRows(runs, nowSeconds, { emptyMessage } = {}) {
10
- const header = `${pad('RUN', 8)} ${pad('TICKET', 10)} ${pad('STATE', 8)} ${pad('PHASE', 10)} ${pad('AGENT', 8)} STARTED`;
11
- if (runs.length === 0)
12
- return [header, emptyMessage ?? 'No runs for this project.'];
13
- return [
14
- header,
15
- ...runs.map((r) => {
16
- const run = `#${r.drupalId ?? r.id.slice(0, 6)}`;
17
- return `${pad(run, 8)} ${pad(r.ticketIdentifier, 10)} ${pad(deriveRunStatus(r, nowSeconds), 8)} ${pad(r.phase, 10)} ${pad(r.agent, 8)} ${runStartedAge(r, nowSeconds)}`;
18
- }),
19
- ];
20
- }
@@ -1,20 +0,0 @@
1
- /** One ticket-detail tab: its id, label, and the section headings it groups. */
2
- export interface DetailTab {
3
- id: string;
4
- label: string;
5
- headings: string[];
6
- }
7
- export declare const DETAIL_TABS: DetailTab[];
8
- /** The tab bar as a single row; the active tab is bracketed. */
9
- export declare function tabBar(activeIndex: number): string;
10
- /** Where one detail tab sits inside the bar: `[start, end)` in display columns
11
- * from the bar's own first column. */
12
- export interface DetailTabSpan {
13
- index: number;
14
- start: number;
15
- end: number;
16
- }
17
- /** The bar's own hit map, built from the SAME `segment`/`SEPARATOR` the rendered
18
- * bar is joined from — see `Component/top-tabs/index.ts`'s `topTabSpans` for the
19
- * full argument. */
20
- export declare function detailTabSpans(activeIndex: number): DetailTabSpan[];
@@ -1,42 +0,0 @@
1
- import { displayWidth } from '../../lib/width.js';
2
- // The ticket-detail screen is split into tabs so only one tab's content draws at
3
- // a time (keeps the fixed-height window small) — Sub-tickets + Referenced share a
4
- // Related tab, Comments is its own tab with collapsible entries (owned by the
5
- // plugin's draw loop, which needs the interactive expand state).
6
- export const DETAIL_TABS = [
7
- {
8
- id: 'overview',
9
- label: 'Overview',
10
- headings: ['Description', 'Branch', 'Links', 'Environments'],
11
- },
12
- { id: 'comments', label: 'Comments', headings: [] },
13
- {
14
- id: 'related',
15
- label: 'Related',
16
- headings: ['Sub-tickets', 'Referenced tickets'],
17
- },
18
- { id: 'runs', label: 'Runs', headings: ['Runs'] },
19
- ];
20
- /** What joins two tabs in the rendered bar — part of neither tab's span. */
21
- const SEPARATOR = ' ';
22
- /** One tab's rendered segment: bracketed when active, space-padded otherwise. */
23
- const segment = (tab, index, activeIndex) => index === activeIndex ? `[${tab.label}]` : ` ${tab.label} `;
24
- /** The tab bar as a single row; the active tab is bracketed. */
25
- export function tabBar(activeIndex) {
26
- return DETAIL_TABS.map((t, i) => segment(t, i, activeIndex)).join(SEPARATOR);
27
- }
28
- /** The bar's own hit map, built from the SAME `segment`/`SEPARATOR` the rendered
29
- * bar is joined from — see `Component/top-tabs/index.ts`'s `topTabSpans` for the
30
- * full argument. */
31
- export function detailTabSpans(activeIndex) {
32
- const spans = [];
33
- let start = 0;
34
- DETAIL_TABS.forEach((tab, index) => {
35
- if (index > 0)
36
- start += displayWidth(SEPARATOR);
37
- const width = displayWidth(segment(tab, index, activeIndex));
38
- spans.push({ index, start, end: start + width });
39
- start += width;
40
- });
41
- return spans;
42
- }
@@ -1,38 +0,0 @@
1
- import type { TicketDetailDocument } from '../../types.js';
2
- import type { CommentItem } from '../comment-item/index.js';
3
- import type { Section } from '../section-list/index.js';
4
- /**
5
- * The `expandedComments` set a freshly opened ticket detail starts with: the
6
- * newest comment expanded, every older one collapsed.
7
- *
8
- * It lives beside the sort on purpose — "index 0 is the newest" is one module's
9
- * invariant, so the kernel never needs to know which index that is.
10
- */
11
- export declare function initialExpandedComments(document: TicketDetailDocument): Set<number>;
12
- /**
13
- * The ticket detail sections, each with an explicit empty state:
14
- * Description, Comments, Sub-tickets, Runs, Referenced tickets, Links,
15
- * Branch, Environments.
16
- */
17
- export declare function detailSections(document: TicketDetailDocument): Section[];
18
- /** The detailSections that belong to a given (non-comments) tab, in tab order. */
19
- export declare function sectionsForTab(document: TicketDetailDocument, tabIndex: number): Section[];
20
- /**
21
- * One item per comment for the collapsible Comments tab: type, author, created
22
- * (raw ISO — the plugin formats the relative age), the body split into physical
23
- * lines, and a first-line preview for the collapsed header.
24
- */
25
- export declare function commentItems(document: TicketDetailDocument): CommentItem[];
26
- export declare function detailLines(detail?: TicketDetailDocument | null): {
27
- lines: string[];
28
- boldRows: Set<number>;
29
- };
30
- export declare function tabViewLines(detail: TicketDetailDocument | null | undefined, tabIndex: number, { expanded, nowSeconds, }?: {
31
- expanded?: Set<number>;
32
- nowSeconds?: number;
33
- }): {
34
- lines: string[];
35
- boldRows: Set<number>;
36
- headerRows: number[];
37
- selectable: number;
38
- };