@gaia-ai/addon-gaia-ui 0.9.2 → 0.11.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.
- package/dist/src/Component/footer-actions/index.d.ts +58 -0
- package/dist/src/Component/footer-actions/index.js +111 -0
- package/dist/src/Component/list-chrome/index.d.ts +19 -0
- package/dist/src/Component/list-chrome/index.js +61 -0
- package/dist/src/Component/markdown/index.d.ts +68 -0
- package/dist/src/Component/markdown/index.js +133 -0
- package/dist/src/Component/section-list/index.d.ts +115 -23
- package/dist/src/Component/section-list/index.js +120 -34
- package/dist/src/Component/top-tabs/index.d.ts +110 -7
- package/dist/src/Component/top-tabs/index.js +88 -9
- package/dist/src/Entity/collection.js +1 -1
- package/dist/src/Entity/conductor/Data/conductors.d.ts +4 -3
- package/dist/src/Entity/conductor/Data/conductors.js +76 -10
- package/dist/src/Entity/conductor/Data/own-conductor.js +7 -1
- package/dist/src/Entity/conductor/Screen/conductors.d.ts +26 -41
- package/dist/src/Entity/conductor/Screen/conductors.js +65 -123
- package/dist/src/Entity/project/Screen/project-picker.d.ts +20 -31
- package/dist/src/Entity/project/Screen/project-picker.js +48 -58
- package/dist/src/Entity/project/Screen/projects-screen.d.ts +4 -2
- package/dist/src/Entity/project/Screen/projects-screen.js +11 -8
- package/dist/src/Entity/run/Component/run-detail/index.js +15 -7
- package/dist/src/Entity/run/Data/query.d.ts +10 -0
- package/dist/src/Entity/run/Data/query.js +16 -1
- package/dist/src/Entity/run/Data/runs.d.ts +2 -0
- package/dist/src/Entity/run/Data/runs.js +19 -1
- package/dist/src/Entity/run/Screen/run-detail.d.ts +16 -23
- package/dist/src/Entity/run/Screen/run-detail.js +27 -34
- package/dist/src/Entity/run/Screen/runs.d.ts +7 -3
- package/dist/src/Entity/run/Screen/runs.js +31 -48
- package/dist/src/Entity/run/Screen/user-runs.d.ts +79 -0
- package/dist/src/Entity/run/Screen/user-runs.js +98 -0
- package/dist/src/Entity/statistics/Data/statistics.d.ts +111 -9
- package/dist/src/Entity/statistics/Data/statistics.js +121 -9
- package/dist/src/Entity/statistics/Screen/dashboard.d.ts +10 -2
- package/dist/src/Entity/statistics/Screen/dashboard.js +127 -36
- package/dist/src/Entity/team/Data/members.d.ts +30 -0
- package/dist/src/Entity/team/Data/members.js +83 -0
- package/dist/src/Entity/team/Data/team.d.ts +22 -0
- package/dist/src/Entity/team/Data/team.js +38 -0
- package/dist/src/Entity/team/Data/teams.d.ts +223 -0
- package/dist/src/Entity/team/Data/teams.js +267 -0
- package/dist/src/Entity/team/Screen/team-users.d.ts +76 -0
- package/dist/src/Entity/team/Screen/team-users.js +215 -0
- package/dist/src/Entity/team/Screen/teams-screen.d.ts +116 -0
- package/dist/src/Entity/team/Screen/teams-screen.js +349 -0
- package/dist/src/Entity/ticket/Component/comment-item/index.d.ts +14 -1
- package/dist/src/Entity/ticket/Component/comment-item/index.js +15 -1
- package/dist/src/Entity/ticket/Component/edit-form/index.d.ts +8 -3
- package/dist/src/Entity/ticket/Component/edit-form/index.js +4 -6
- package/dist/src/Entity/ticket/Component/sections/index.js +16 -1
- package/dist/src/Entity/ticket/Component/ticket-detail/index.d.ts +102 -13
- package/dist/src/Entity/ticket/Component/ticket-detail/index.js +440 -72
- package/dist/src/Entity/ticket/Data/detail.d.ts +19 -3
- package/dist/src/Entity/ticket/Data/detail.js +20 -4
- package/dist/src/Entity/ticket/Data/query.d.ts +7 -0
- package/dist/src/Entity/ticket/Data/query.js +18 -2
- package/dist/src/Entity/ticket/Data/tickets.d.ts +24 -0
- package/dist/src/Entity/ticket/Data/tickets.js +41 -1
- package/dist/src/Entity/ticket/Screen/create-ticket-form.d.ts +10 -4
- package/dist/src/Entity/ticket/Screen/create-ticket-form.js +37 -16
- package/dist/src/Entity/ticket/Screen/edit-assignment-form.d.ts +6 -4
- package/dist/src/Entity/ticket/Screen/edit-assignment-form.js +16 -16
- package/dist/src/Entity/ticket/Screen/parent-filter-form.d.ts +6 -4
- package/dist/src/Entity/ticket/Screen/parent-filter-form.js +14 -11
- package/dist/src/Entity/ticket/Screen/ticket-agents.d.ts +5 -2
- package/dist/src/Entity/ticket/Screen/ticket-agents.js +14 -14
- package/dist/src/Entity/ticket/Screen/ticket-detail.d.ts +89 -4
- package/dist/src/Entity/ticket/Screen/ticket-detail.js +295 -74
- package/dist/src/Entity/ticket/Screen/tickets.d.ts +66 -3
- package/dist/src/Entity/ticket/Screen/tickets.js +140 -76
- package/dist/src/Entity/ticket/Screen/user-tickets.d.ts +91 -0
- package/dist/src/Entity/ticket/Screen/user-tickets.js +94 -0
- package/dist/src/Entity/ticket/Screen/who-filter-form.d.ts +6 -4
- package/dist/src/Entity/ticket/Screen/who-filter-form.js +14 -11
- package/dist/src/Entity/transport.d.ts +14 -4
- package/dist/src/Entity/transport.js +2 -1
- package/dist/src/Entity/user/Data/users.d.ts +87 -0
- package/dist/src/Entity/user/Data/users.js +60 -0
- package/dist/src/Entity/user/Screen/user-overview.d.ts +33 -0
- package/dist/src/Entity/user/Screen/user-overview.js +159 -0
- package/dist/src/Entity/user/Screen/users.d.ts +39 -0
- package/dist/src/Entity/user/Screen/users.js +136 -0
- package/dist/src/Extension/contract.d.ts +58 -0
- package/dist/src/Extension/contract.js +1 -0
- package/dist/src/Extension/load.d.ts +10 -0
- package/dist/src/Extension/load.js +21 -0
- package/dist/src/Extension/store.d.ts +11 -0
- package/dist/src/Extension/store.js +44 -0
- package/dist/src/Kernel/chrome.d.ts +49 -12
- package/dist/src/Kernel/chrome.js +150 -57
- package/dist/src/Kernel/key-input.js +1 -1
- package/dist/src/Kernel/mount.d.ts +1 -1
- package/dist/src/Kernel/mount.js +1 -1
- package/dist/src/Kernel/regions.d.ts +64 -0
- package/dist/src/Kernel/regions.js +328 -0
- package/dist/src/Kernel/renderer.d.ts +25 -0
- package/dist/src/Kernel/renderer.js +142 -0
- package/dist/src/Kernel/splash.d.ts +27 -0
- package/dist/src/Kernel/splash.js +72 -0
- package/dist/src/Kernel/tui-kernel.js +139 -435
- package/dist/src/Routing/context.d.ts +5 -0
- package/dist/src/Routing/context.js +6 -0
- package/dist/src/Routing/registry.js +21 -2
- package/dist/src/Routing/router.js +5 -1
- package/dist/src/Routing/routes.js +19 -0
- package/dist/src/Screen/async-modal-screen.d.ts +26 -0
- package/dist/src/Screen/async-modal-screen.js +44 -0
- package/dist/src/Screen/async-screen.d.ts +32 -40
- package/dist/src/Screen/async-screen.js +45 -41
- package/dist/src/Screen/full-screen.d.ts +1 -0
- package/dist/src/Screen/full-screen.js +3 -0
- package/dist/src/Screen/list-screen.d.ts +7 -1
- package/dist/src/Screen/list-screen.js +17 -6
- package/dist/src/Screen/modal-screen.d.ts +6 -0
- package/dist/src/Screen/modal-screen.js +2 -0
- package/dist/src/Screen/screen.d.ts +42 -6
- package/dist/src/Screen/screen.js +2 -2
- package/dist/src/Widget/textarea.js +10 -14
- package/dist/src/Widget/types.d.ts +1 -1
- package/dist/src/launcher.d.ts +24 -26
- package/dist/src/launcher.js +11 -11
- package/dist/src/lib/format.js +1 -1
- package/dist/src/lib/markdown.d.ts +53 -0
- package/dist/src/lib/markdown.js +522 -0
- package/dist/src/lib/refresh-signal.d.ts +56 -0
- package/dist/src/lib/refresh-signal.js +85 -0
- package/dist/src/lib/theme.d.ts +36 -0
- package/dist/src/lib/theme.js +40 -0
- package/dist/src/lib/version.d.ts +5 -0
- package/dist/src/lib/version.js +46 -0
- package/dist/src/lib/vnode.d.ts +86 -63
- package/dist/src/lib/vnode.js +112 -85
- package/dist/src/types.d.ts +47 -12
- package/dist/src/ui.d.ts +2 -0
- package/dist/src/ui.js +1 -0
- package/package.json +4 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { actionItem } from '../../../Component/footer-actions/index.js';
|
|
1
2
|
import { ListCursor } from '../../../lib/cursor.js';
|
|
2
3
|
import { AsyncScreen } from '../../../Screen/async-screen.js';
|
|
3
|
-
import { presentation } from '../../../Screen/screen.js';
|
|
4
4
|
import { listRowIds, listTableTree } from '../../collection.js';
|
|
5
5
|
import { loadProjectRows, PROJECT_TABLE, } from '../Data/projects.js';
|
|
6
6
|
/**
|
|
@@ -69,19 +69,22 @@ export class ProjectsScreen extends AsyncScreen {
|
|
|
69
69
|
selectedIndex: this.selectedIndex,
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
chrome(context) {
|
|
73
73
|
const rows = this.rows;
|
|
74
|
-
return
|
|
74
|
+
return {
|
|
75
75
|
selectable: rows.length,
|
|
76
76
|
selectedIndex: this.selectedIndex,
|
|
77
|
-
//
|
|
78
|
-
// SAME configuration that keys the rows — so the click target and the row
|
|
79
|
-
// node cannot be spelled two ways (GAIA-345 D2).
|
|
77
|
+
// Click targets match listTableTree row ids (GAIA-345 D2).
|
|
80
78
|
rowIds: listRowIds(PROJECT_TABLE, rows),
|
|
81
79
|
title: 'GAIA projects',
|
|
82
80
|
meta: `${rows.length} registered project(s) · ${context.services?.baseUrl ?? ''}`,
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
actions() {
|
|
84
|
+
return [
|
|
85
|
+
actionItem('projects-new', 'n', 'new'),
|
|
86
|
+
actionItem('projects-tab', 'Tab', 'tabs'),
|
|
87
|
+
];
|
|
85
88
|
}
|
|
86
89
|
activate(index, context) {
|
|
87
90
|
const row = this.readyValue?.[index];
|
|
@@ -8,12 +8,20 @@
|
|
|
8
8
|
//
|
|
9
9
|
// GAIA-340 D1 — the FIRST block to hand its content over as renderables rather
|
|
10
10
|
// than as rows of text: the sections are built once, keyed, and turned into a
|
|
11
|
-
// VNode tree.
|
|
12
|
-
//
|
|
11
|
+
// VNode tree.
|
|
12
|
+
//
|
|
13
|
+
// GAIA-372 D9 — IT IS `markdownRow`'S ONLY CALLER NOW, and the ATX-only rule is
|
|
14
|
+
// THIS SCREEN'S rule rather than a rule shared with the ticket detail. A run log
|
|
15
|
+
// entry is MACHINE OUTPUT, not authored Markdown: an agent line full of `*`, `_`
|
|
16
|
+
// and backticks would be re-styled and have characters deleted by a renderer
|
|
17
|
+
// meant for prose. So the log keeps the narrow rule while the ticket detail's
|
|
18
|
+
// `gaia_rich` bodies go through `Component/markdown/`. Nothing about this screen's
|
|
19
|
+
// behaviour changed; routing the log through the full subset is a separate
|
|
20
|
+
// ticket, not a silent side effect of that one.
|
|
13
21
|
//
|
|
14
22
|
// Read-only by design (the ticket's own out-of-scope list): no write, no edit, no
|
|
15
23
|
// navigation out of it except Esc.
|
|
16
|
-
import { markdownRow } from '../../../../Component/
|
|
24
|
+
import { markdownRow } from '../../../../Component/markdown/index.js';
|
|
17
25
|
import { asString } from '../../../../lib/format.js';
|
|
18
26
|
import { columnNode, textNode } from '../../../../lib/vnode.js';
|
|
19
27
|
/** The sideloaded `gaia_run` with this id, or undefined. */
|
|
@@ -208,10 +216,10 @@ function flatRows(document, runId) {
|
|
|
208
216
|
{ key: `section-${section.key}`, text: section.heading, bold: true },
|
|
209
217
|
...section.rows.map((entry) => ({
|
|
210
218
|
key: entry.key,
|
|
211
|
-
// The
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
219
|
+
// The log's OWN minimal-markdown rule (GAIA-372 D9): an ATX heading inside
|
|
220
|
+
// a log entry renders bold with its markers stripped, and a log line
|
|
221
|
+
// starting with `#` must not render one way on screen and another way in a
|
|
222
|
+
// pipe. Nothing else in an entry is parsed, deliberately — see the header.
|
|
215
223
|
...markdownRow(entry.text),
|
|
216
224
|
})),
|
|
217
225
|
// The blank row between two sections, which the flattened surface pushed
|
|
@@ -20,3 +20,13 @@ export declare function buildRunQueryParams(projectId: string, criteria: RunCrit
|
|
|
20
20
|
size: number;
|
|
21
21
|
index: number;
|
|
22
22
|
}): DrupalJsonApiParams;
|
|
23
|
+
/**
|
|
24
|
+
* Build the JSON:API query for one assignee's runs (GAIA-377 D2).
|
|
25
|
+
*
|
|
26
|
+
* Runs carry no user of their own — attribution is
|
|
27
|
+
* `ticket_id.assignee_user_id.id`, matching StatisticsCounter::SOURCES.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildUserRunQueryParams(userId: string, criteria: RunCriteria, page?: {
|
|
30
|
+
size: number;
|
|
31
|
+
index: number;
|
|
32
|
+
}): DrupalJsonApiParams;
|
|
@@ -25,6 +25,22 @@ export function describeCriteria(criteria) {
|
|
|
25
25
|
export function buildRunQueryParams(projectId, criteria, page = { size: LIST_PAGE_SIZE, index: 0 }) {
|
|
26
26
|
const params = new DrupalJsonApiParams();
|
|
27
27
|
params.addFilter('ticket_id.project_id.id', projectId);
|
|
28
|
+
applyRunCriteria(params, criteria, page);
|
|
29
|
+
return params;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build the JSON:API query for one assignee's runs (GAIA-377 D2).
|
|
33
|
+
*
|
|
34
|
+
* Runs carry no user of their own — attribution is
|
|
35
|
+
* `ticket_id.assignee_user_id.id`, matching StatisticsCounter::SOURCES.
|
|
36
|
+
*/
|
|
37
|
+
export function buildUserRunQueryParams(userId, criteria, page = { size: LIST_PAGE_SIZE, index: 0 }) {
|
|
38
|
+
const params = new DrupalJsonApiParams();
|
|
39
|
+
params.addFilter('ticket_id.assignee_user_id.id', userId);
|
|
40
|
+
applyRunCriteria(params, criteria, page);
|
|
41
|
+
return params;
|
|
42
|
+
}
|
|
43
|
+
function applyRunCriteria(params, criteria, page) {
|
|
28
44
|
if (criteria.closed !== null)
|
|
29
45
|
params.addFilter('closed', criteria.closed ? '1' : '0');
|
|
30
46
|
params.addInclude(['ticket_id']);
|
|
@@ -37,5 +53,4 @@ export function buildRunQueryParams(projectId, criteria, page = { size: LIST_PAG
|
|
|
37
53
|
// Page one carries no offset, so its query is the shortest it can be.
|
|
38
54
|
if (page.index > 0)
|
|
39
55
|
params.addPageOffset(page.index * page.size);
|
|
40
|
-
return params;
|
|
41
56
|
}
|
|
@@ -219,6 +219,8 @@ export type RunPage = Page<RunRow>;
|
|
|
219
219
|
* `links.next`.
|
|
220
220
|
*/
|
|
221
221
|
export declare function loadProjectRuns(client: DropshClient, projectId: string, criteria: RunCriteria, page?: number): Promise<RunPage>;
|
|
222
|
+
/** One page of runs attributed to a user via ticket assignee (GAIA-377 D2). */
|
|
223
|
+
export declare function loadUserRuns(client: DropshClient, userId: string, criteria: RunCriteria, page?: number): Promise<RunPage>;
|
|
222
224
|
/**
|
|
223
225
|
* Newest-STARTED first, and TOTAL (GAIA-255 D5).
|
|
224
226
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// no-request read of the ticket document (spec D7).
|
|
8
8
|
import { loadPage, } from '../../collection.js';
|
|
9
9
|
import { LIST_PAGE_SIZE } from '../../cycle.js';
|
|
10
|
-
import { buildRunQueryParams } from './query.js';
|
|
10
|
+
import { buildRunQueryParams, buildUserRunQueryParams, } from './query.js';
|
|
11
11
|
import { deriveRunStatus, runStartedAge } from './status.js';
|
|
12
12
|
/** Read a value as a string, or `undefined` when it is not one. */
|
|
13
13
|
function str(value) {
|
|
@@ -217,6 +217,24 @@ export async function loadProjectRuns(client, projectId, criteria, page = 0) {
|
|
|
217
217
|
return data.map((item) => mapRunData(item, ticketsById));
|
|
218
218
|
}, page, window);
|
|
219
219
|
}
|
|
220
|
+
/** One page of runs attributed to a user via ticket assignee (GAIA-377 D2). */
|
|
221
|
+
export async function loadUserRuns(client, userId, criteria, page = 0) {
|
|
222
|
+
const window = {
|
|
223
|
+
offset: page * LIST_PAGE_SIZE,
|
|
224
|
+
limit: LIST_PAGE_SIZE,
|
|
225
|
+
};
|
|
226
|
+
const params = buildUserRunQueryParams(userId, criteria, {
|
|
227
|
+
size: window.limit,
|
|
228
|
+
index: page,
|
|
229
|
+
});
|
|
230
|
+
return loadPage(client, 'gaia_run/gaia_run', params, (data, included) => {
|
|
231
|
+
const ticketsById = new Map();
|
|
232
|
+
for (const inc of included)
|
|
233
|
+
if (inc.id)
|
|
234
|
+
ticketsById.set(inc.id, inc);
|
|
235
|
+
return data.map((item) => mapRunData(item, ticketsById));
|
|
236
|
+
}, page, window);
|
|
237
|
+
}
|
|
220
238
|
/**
|
|
221
239
|
* Newest-STARTED first, and TOTAL (GAIA-255 D5).
|
|
222
240
|
*
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { VNode } from '@opentui/core';
|
|
2
|
+
import { AsyncScreen } from '../../../Screen/async-screen.js';
|
|
3
|
+
import type { FullScreenChrome } from '../../../Screen/full-screen.js';
|
|
4
|
+
import type { ScreenContext } from '../../../Screen/screen.js';
|
|
2
5
|
import type { TicketDetailDocument } from '../../../types.js';
|
|
3
6
|
export interface RunDetailInput {
|
|
4
7
|
readonly document: TicketDetailDocument;
|
|
5
8
|
}
|
|
6
9
|
export declare const runDetailInputContext: import("../../../Routing/context.js").ContextKey<RunDetailInput>;
|
|
7
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Run detail — normal {@link AsyncScreen}. The document arrives via navigation
|
|
12
|
+
* context (or empty); refresh is the shared R / ui-refresh path.
|
|
13
|
+
*/
|
|
14
|
+
export declare class RunDetailScreen extends AsyncScreen<TicketDetailDocument> {
|
|
8
15
|
readonly frameId = "run";
|
|
9
16
|
topTab(): null;
|
|
10
17
|
static readonly route: {
|
|
@@ -17,20 +24,13 @@ export declare class RunDetailScreen extends BaseScreen {
|
|
|
17
24
|
context: (_params: Readonly<Record<string, string>>, state: unknown, base: import("../../../Routing/context.js").ContextBag) => import("../../../Routing/context.js").ContextBag;
|
|
18
25
|
};
|
|
19
26
|
readonly id: string;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
* `render(context): string[]` any more, because there is no second
|
|
28
|
-
* description of the screen to keep in step with this one — the bold section
|
|
29
|
-
* headings that were `boldRows` indices into a row surface are props on the
|
|
30
|
-
* rows' own nodes now (D3), and the surface those indices pointed into is
|
|
31
|
-
* gone.
|
|
32
|
-
*/
|
|
33
|
-
present(context: ScreenContext): import("../../../Screen/screen.js").ScreenPresentation;
|
|
27
|
+
reload(context: ScreenContext): void;
|
|
28
|
+
protected load(context: ScreenContext): Promise<TicketDetailDocument>;
|
|
29
|
+
protected renderLoading(): string[];
|
|
30
|
+
protected renderReady(document: TicketDetailDocument, context: ScreenContext): VNode;
|
|
31
|
+
protected chrome(context: ScreenContext): FullScreenChrome;
|
|
32
|
+
/** Read-only — kernel basics cover ↑↓ / Esc / q; no extra chips. */
|
|
33
|
+
protected actions(): never[];
|
|
34
34
|
/**
|
|
35
35
|
* F5 — the pipe's own output, and it is `string[]`.
|
|
36
36
|
*
|
|
@@ -41,13 +41,6 @@ export declare class RunDetailScreen extends BaseScreen {
|
|
|
41
41
|
* path lays the body out on a cell grid of a fixed width, and a pipe has no
|
|
42
42
|
* width — on run #4928 it clipped 420 transcript rows at 80 columns, losing
|
|
43
43
|
* 97.7 % of a 1.34 MB captured agent transcript.
|
|
44
|
-
*
|
|
45
|
-
* This is NOT a `ScreenPresentation.lines` coming back (R3): `present()` still
|
|
46
|
-
* hands over a tree and nothing else. Both shapes are projections of the block's
|
|
47
|
-
* ONE keyed traversal (`flatRows`), so there is no second description of the
|
|
48
|
-
* screen to keep in step — see `tests/Screen/run-detail-non-tty.test.ts`, which
|
|
49
|
-
* asserts the two row for row.
|
|
50
44
|
*/
|
|
51
45
|
renderNonInteractive(context: ScreenContext): string[];
|
|
52
|
-
reset(): void;
|
|
53
46
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { defineContext } from '../../../Routing/context.js';
|
|
2
|
-
import {
|
|
2
|
+
import { AsyncScreen } from '../../../Screen/async-screen.js';
|
|
3
3
|
import { runDetailRows, runDetailTitle, runDetailTree, } from '../Component/run-detail/index.js';
|
|
4
4
|
export const runDetailInputContext = defineContext('run-detail-input', 'input');
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Run detail — normal {@link AsyncScreen}. The document arrives via navigation
|
|
7
|
+
* context (or empty); refresh is the shared R / ui-refresh path.
|
|
8
|
+
*/
|
|
9
|
+
export class RunDetailScreen extends AsyncScreen {
|
|
6
10
|
frameId = 'run';
|
|
7
11
|
topTab() {
|
|
8
12
|
return null;
|
|
@@ -19,30 +23,28 @@ export class RunDetailScreen extends BaseScreen {
|
|
|
19
23
|
: base,
|
|
20
24
|
};
|
|
21
25
|
id = RunDetailScreen.route.id;
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
reload(context) {
|
|
27
|
+
this.retry(context);
|
|
28
|
+
}
|
|
29
|
+
load(context) {
|
|
24
30
|
if (context.contexts?.has(runDetailInputContext))
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
return Promise.resolve(context.contexts.get(runDetailInputContext).document);
|
|
32
|
+
return Promise.resolve({});
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return
|
|
42
|
-
title: runDetailTitle(this.document, runId),
|
|
43
|
-
meta: '↑↓ scroll · Esc back · q quit',
|
|
44
|
-
status: ' ↑↓ scroll · Esc back · q quit ',
|
|
45
|
-
});
|
|
34
|
+
renderLoading() {
|
|
35
|
+
return ['Loading run…'];
|
|
36
|
+
}
|
|
37
|
+
renderReady(document, context) {
|
|
38
|
+
return runDetailTree(document, context.params?.runId ?? '');
|
|
39
|
+
}
|
|
40
|
+
chrome(context) {
|
|
41
|
+
return {
|
|
42
|
+
title: runDetailTitle(this.readyValue ?? {}, context.params?.runId ?? ''),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Read-only — kernel basics cover ↑↓ / Esc / q; no extra chips. */
|
|
46
|
+
actions() {
|
|
47
|
+
return [];
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
48
50
|
* F5 — the pipe's own output, and it is `string[]`.
|
|
@@ -54,17 +56,8 @@ export class RunDetailScreen extends BaseScreen {
|
|
|
54
56
|
* path lays the body out on a cell grid of a fixed width, and a pipe has no
|
|
55
57
|
* width — on run #4928 it clipped 420 transcript rows at 80 columns, losing
|
|
56
58
|
* 97.7 % of a 1.34 MB captured agent transcript.
|
|
57
|
-
*
|
|
58
|
-
* This is NOT a `ScreenPresentation.lines` coming back (R3): `present()` still
|
|
59
|
-
* hands over a tree and nothing else. Both shapes are projections of the block's
|
|
60
|
-
* ONE keyed traversal (`flatRows`), so there is no second description of the
|
|
61
|
-
* screen to keep in step — see `tests/Screen/run-detail-non-tty.test.ts`, which
|
|
62
|
-
* asserts the two row for row.
|
|
63
59
|
*/
|
|
64
60
|
renderNonInteractive(context) {
|
|
65
|
-
return runDetailRows(this.
|
|
66
|
-
}
|
|
67
|
-
reset() {
|
|
68
|
-
this.document = {};
|
|
61
|
+
return runDetailRows(this.readyValue ?? {}, context.params?.runId ?? '');
|
|
69
62
|
}
|
|
70
63
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { FullScreenChrome, ListFilterBadge } from '../../../Screen/full-screen.js';
|
|
1
2
|
import { ListScreen } from '../../../Screen/list-screen.js';
|
|
2
|
-
import {
|
|
3
|
+
import type { ScreenContext } from '../../../Screen/screen.js';
|
|
3
4
|
import { type RunCriteria } from '../Data/query.js';
|
|
4
5
|
import { type RunPage, type RunRow } from '../Data/runs.js';
|
|
5
6
|
export declare class RunsScreen extends ListScreen<RunRow, number> {
|
|
6
|
-
readonly frameId = "
|
|
7
|
+
readonly frameId = "runs";
|
|
7
8
|
readonly topTabOverlay = true;
|
|
8
9
|
protected readonly table: {
|
|
9
10
|
readonly noun: "run";
|
|
@@ -78,7 +79,10 @@ export declare class RunsScreen extends ListScreen<RunRow, number> {
|
|
|
78
79
|
* clients/test doubles, as on the ticket list. */
|
|
79
80
|
protected filterRow(row: RunRow): boolean;
|
|
80
81
|
protected renderLoading(): string[];
|
|
81
|
-
|
|
82
|
+
/** Runs expose only the closed cycle — one badge, always shown (GAIA-375 A′). */
|
|
83
|
+
protected badges(): readonly ListFilterBadge[];
|
|
84
|
+
protected chrome(context: ScreenContext): FullScreenChrome;
|
|
85
|
+
protected actions(): import("../../../Component/footer-actions/index.js").ActionItem[];
|
|
82
86
|
activate(index: number, context: ScreenContext): boolean;
|
|
83
87
|
select(index: number, context: ScreenContext): boolean;
|
|
84
88
|
handleInput(input: string, context: ScreenContext): boolean;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { actionItem } from '../../../Component/footer-actions/index.js';
|
|
1
2
|
import { ListScreen } from '../../../Screen/list-screen.js';
|
|
2
|
-
import { presentation, } from '../../../Screen/screen.js';
|
|
3
3
|
import { CLOSED_FILTER_VALUES, cycleValue } from '../../cycle.js';
|
|
4
4
|
import { DEFAULT_RUN_CRITERIA } from '../Data/query.js';
|
|
5
5
|
import { loadProjectRuns, RUN_TABLE, selectedRunTicketId, } from '../Data/runs.js';
|
|
6
6
|
export class RunsScreen extends ListScreen {
|
|
7
|
-
frameId = '
|
|
7
|
+
frameId = 'runs';
|
|
8
8
|
topTabOverlay = true;
|
|
9
9
|
table = RUN_TABLE;
|
|
10
10
|
topTab() {
|
|
@@ -63,55 +63,38 @@ export class RunsScreen extends ListScreen {
|
|
|
63
63
|
renderLoading() {
|
|
64
64
|
return ['Loading runs…'];
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
: 'closed
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
// Built by `ListScreen` since GAIA-294, which is what makes "must not drift
|
|
88
|
-
// from the ticket list" a mechanism rather than a promise.
|
|
89
|
-
const paging = this.pagingSegments();
|
|
90
|
-
// The rows ON SCREEN and the size of the FILTERED COLLECTION, side by side:
|
|
91
|
-
// `7 of 1340 run(s)` when the server reported the total, `7 of ? run(s)`
|
|
92
|
-
// when it reported none (GAIA-292 D6c/D6d). Neither quantity replaces the
|
|
93
|
-
// other, so the row count never jumps; the total is read off the value the
|
|
94
|
-
// rows came with (D4/D4a) and from no second screen member — which is now
|
|
95
|
-
// structural rather than careful, because the server answered both from one
|
|
96
|
-
// query. Empty while the page loads (D6c), which is why the segments are
|
|
97
|
-
// joined rather than concatenated — an omitted count must not leave the line
|
|
98
|
-
// opening with a separator.
|
|
99
|
-
const count = this.countLabel();
|
|
100
|
-
return presentation(this.surface(context), {
|
|
101
|
-
selectable: rows.length,
|
|
66
|
+
/** Runs expose only the closed cycle — one badge, always shown (GAIA-375 A′). */
|
|
67
|
+
badges() {
|
|
68
|
+
if (this.criteria.closed === null) {
|
|
69
|
+
return [{ id: 'closed', label: 'x closed' }];
|
|
70
|
+
}
|
|
71
|
+
return [
|
|
72
|
+
{
|
|
73
|
+
id: 'closed',
|
|
74
|
+
label: 'x closed',
|
|
75
|
+
value: this.criteria.closed ? 'closed: only' : 'closed: hidden',
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
chrome(context) {
|
|
80
|
+
// Count + page in header meta; ←/→ in footer actions (GAIA-375 / GAIA-292).
|
|
81
|
+
const pagingLine = [this.countLabel(), ...this.pagingSegments()]
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
.join(' · ');
|
|
84
|
+
return {
|
|
85
|
+
selectable: this.rows.length,
|
|
102
86
|
selectedIndex: this.selectedIndex,
|
|
103
|
-
// The node a click on each row lands on (GAIA-340 Task 11).
|
|
104
87
|
rowIds: this.rowNodeIds,
|
|
105
88
|
title: `${context.project?.name ?? context.project?.slug ?? ''} · runs`,
|
|
106
|
-
meta:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
89
|
+
meta: pagingLine,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
actions() {
|
|
93
|
+
return [
|
|
94
|
+
actionItem('runs-enter', 'Enter', 'ticket'),
|
|
95
|
+
actionItem('runs-tab', 'Tab', 'tabs'),
|
|
96
|
+
...this.pagingActionItems(),
|
|
97
|
+
];
|
|
115
98
|
}
|
|
116
99
|
activate(index, context) {
|
|
117
100
|
const id = this.readyValue
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { FullScreenChrome, ListFilterBadge } from '../../../Screen/full-screen.js';
|
|
2
|
+
import { ListScreen } from '../../../Screen/list-screen.js';
|
|
3
|
+
import type { ScreenContext } from '../../../Screen/screen.js';
|
|
4
|
+
import { type RunPage, type RunRow } from '../Data/runs.js';
|
|
5
|
+
/** Runs attributed to one user via ticket assignee (GAIA-377 D2). */
|
|
6
|
+
export declare class UserRunsScreen extends ListScreen<RunRow, number> {
|
|
7
|
+
readonly frameId = "user-runs";
|
|
8
|
+
readonly topTabOverlay = true;
|
|
9
|
+
protected readonly table: {
|
|
10
|
+
readonly noun: "run";
|
|
11
|
+
readonly columns: readonly [{
|
|
12
|
+
readonly key: "run";
|
|
13
|
+
readonly label: "RUN";
|
|
14
|
+
readonly width: 8;
|
|
15
|
+
readonly fields: readonly ["drupal_internal__id"];
|
|
16
|
+
readonly get: (r: RunRow) => string;
|
|
17
|
+
}, {
|
|
18
|
+
readonly key: "ticket";
|
|
19
|
+
readonly label: "TICKET";
|
|
20
|
+
readonly width: 10;
|
|
21
|
+
readonly fields: readonly ["ticket_id"];
|
|
22
|
+
readonly get: (r: RunRow) => string;
|
|
23
|
+
}, {
|
|
24
|
+
readonly key: "state";
|
|
25
|
+
readonly label: "STATE";
|
|
26
|
+
readonly width: 8;
|
|
27
|
+
readonly fields: readonly ["state", "heartbeat", "claim_expires_at"];
|
|
28
|
+
readonly get: (r: RunRow, nowSeconds: number) => string;
|
|
29
|
+
}, {
|
|
30
|
+
readonly key: "phase";
|
|
31
|
+
readonly label: "PHASE";
|
|
32
|
+
readonly width: 13;
|
|
33
|
+
readonly fields: readonly ["state_at_start"];
|
|
34
|
+
readonly get: (r: RunRow) => string;
|
|
35
|
+
}, {
|
|
36
|
+
readonly key: "agent";
|
|
37
|
+
readonly label: "AGENT";
|
|
38
|
+
readonly width: 8;
|
|
39
|
+
readonly fields: readonly ["agent"];
|
|
40
|
+
readonly get: (r: RunRow) => string;
|
|
41
|
+
}, {
|
|
42
|
+
readonly key: "started";
|
|
43
|
+
readonly label: "STARTED";
|
|
44
|
+
readonly width: 0;
|
|
45
|
+
readonly grow: 1;
|
|
46
|
+
readonly fields: readonly ["started_at", "created"];
|
|
47
|
+
readonly get: (r: RunRow, nowSeconds: number) => string;
|
|
48
|
+
}];
|
|
49
|
+
readonly emptyMessage: "No runs for this project.";
|
|
50
|
+
};
|
|
51
|
+
topTab(): {
|
|
52
|
+
readonly level: "user";
|
|
53
|
+
readonly tab: "runs";
|
|
54
|
+
};
|
|
55
|
+
static readonly route: {
|
|
56
|
+
id: string;
|
|
57
|
+
path: string;
|
|
58
|
+
fallback: {
|
|
59
|
+
routeId: string;
|
|
60
|
+
};
|
|
61
|
+
tab: {
|
|
62
|
+
id: string;
|
|
63
|
+
label: string;
|
|
64
|
+
scope: string;
|
|
65
|
+
order: number;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
readonly id: string;
|
|
69
|
+
private criteria;
|
|
70
|
+
protected tableContext(context: ScreenContext): number;
|
|
71
|
+
protected loadRows(context: ScreenContext, page: number): Promise<RunPage>;
|
|
72
|
+
protected filterRow(row: RunRow): boolean;
|
|
73
|
+
protected renderLoading(): string[];
|
|
74
|
+
protected badges(): readonly ListFilterBadge[];
|
|
75
|
+
protected chrome(context: ScreenContext): FullScreenChrome;
|
|
76
|
+
protected actions(): import("../../../Component/footer-actions/index.js").ActionItem[];
|
|
77
|
+
activate(index: number, context: ScreenContext): boolean;
|
|
78
|
+
handleInput(input: string, context: ScreenContext): boolean;
|
|
79
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { actionItem } from '../../../Component/footer-actions/index.js';
|
|
2
|
+
import { ListScreen } from '../../../Screen/list-screen.js';
|
|
3
|
+
import { CLOSED_FILTER_VALUES, cycleValue } from '../../cycle.js';
|
|
4
|
+
import { DEFAULT_RUN_CRITERIA } from '../Data/query.js';
|
|
5
|
+
import { loadUserRuns, RUN_TABLE, selectedRunTicketId, } from '../Data/runs.js';
|
|
6
|
+
/** Runs attributed to one user via ticket assignee (GAIA-377 D2). */
|
|
7
|
+
export class UserRunsScreen extends ListScreen {
|
|
8
|
+
frameId = 'user-runs';
|
|
9
|
+
topTabOverlay = true;
|
|
10
|
+
table = RUN_TABLE;
|
|
11
|
+
topTab() {
|
|
12
|
+
return { level: 'user', tab: 'runs' };
|
|
13
|
+
}
|
|
14
|
+
static route = {
|
|
15
|
+
id: 'user.runs',
|
|
16
|
+
path: '/users/:userId/runs',
|
|
17
|
+
fallback: { routeId: 'users' },
|
|
18
|
+
tab: { id: 'runs', label: 'Runs', scope: 'user', order: 30 },
|
|
19
|
+
};
|
|
20
|
+
id = UserRunsScreen.route.id;
|
|
21
|
+
criteria = { ...DEFAULT_RUN_CRITERIA };
|
|
22
|
+
tableContext(context) {
|
|
23
|
+
return context.nowSeconds?.() ?? Math.floor(Date.now() / 1000);
|
|
24
|
+
}
|
|
25
|
+
loadRows(context, page) {
|
|
26
|
+
if (!context.services)
|
|
27
|
+
throw new Error('User runs requires services');
|
|
28
|
+
const userId = context.user?.id ?? context.params?.userId;
|
|
29
|
+
if (!userId)
|
|
30
|
+
throw new Error('User runs requires userId');
|
|
31
|
+
return loadUserRuns(context.services.client, userId, this.criteria, page);
|
|
32
|
+
}
|
|
33
|
+
filterRow(row) {
|
|
34
|
+
return this.criteria.closed === null || row.closed === this.criteria.closed;
|
|
35
|
+
}
|
|
36
|
+
renderLoading() {
|
|
37
|
+
return ['Loading runs…'];
|
|
38
|
+
}
|
|
39
|
+
badges() {
|
|
40
|
+
if (this.criteria.closed === null) {
|
|
41
|
+
return [{ id: 'closed', label: 'x closed' }];
|
|
42
|
+
}
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
id: 'closed',
|
|
46
|
+
label: 'x closed',
|
|
47
|
+
value: this.criteria.closed ? 'closed: only' : 'closed: hidden',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
chrome(context) {
|
|
52
|
+
const name = context.user?.name ?? context.params?.userName ?? 'user';
|
|
53
|
+
const pagingLine = [this.countLabel(), ...this.pagingSegments()]
|
|
54
|
+
.filter(Boolean)
|
|
55
|
+
.join(' · ');
|
|
56
|
+
return {
|
|
57
|
+
selectable: this.rows.length,
|
|
58
|
+
selectedIndex: this.selectedIndex,
|
|
59
|
+
rowIds: this.rowNodeIds,
|
|
60
|
+
title: `${name} · runs`,
|
|
61
|
+
meta: pagingLine,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
actions() {
|
|
65
|
+
return [
|
|
66
|
+
actionItem('user-runs-enter', 'Enter', 'ticket'),
|
|
67
|
+
actionItem('user-runs-tab', 'Tab', 'tabs'),
|
|
68
|
+
...this.pagingActionItems(),
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
activate(index, context) {
|
|
72
|
+
const id = this.readyValue
|
|
73
|
+
? selectedRunTicketId(this.readyValue.rows, index)
|
|
74
|
+
: null;
|
|
75
|
+
if (!id || !context.navigate)
|
|
76
|
+
return false;
|
|
77
|
+
context.navigate('ticket.detail', { ticketId: id, tab: 'runs' });
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
handleInput(input, context) {
|
|
81
|
+
if (input === 'x') {
|
|
82
|
+
this.criteria = {
|
|
83
|
+
...this.criteria,
|
|
84
|
+
closed: cycleValue(CLOSED_FILTER_VALUES, this.criteria.closed),
|
|
85
|
+
};
|
|
86
|
+
this.page = 0;
|
|
87
|
+
this.retry(context);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
if (this.handlePagingInput(input, context))
|
|
91
|
+
return true;
|
|
92
|
+
if (this.handleCursorInput(input, context))
|
|
93
|
+
return true;
|
|
94
|
+
if (input === 'enter')
|
|
95
|
+
return this.activate(this.selectedIndex, context);
|
|
96
|
+
return super.handleInput(input, context);
|
|
97
|
+
}
|
|
98
|
+
}
|