@gaia-ai/addon-gaia-ui 0.16.0 → 0.16.2
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/badge/index.d.ts +11 -0
- package/dist/src/Component/badge/index.js +10 -0
- package/dist/src/Component/bar-chart/index.d.ts +16 -0
- package/dist/src/Component/bar-chart/index.js +63 -0
- package/dist/src/Component/card/index.d.ts +16 -0
- package/dist/src/Component/card/index.js +39 -0
- package/dist/src/Component/divider/index.d.ts +7 -0
- package/dist/src/Component/divider/index.js +13 -0
- package/dist/src/Component/heat-map/index.d.ts +21 -0
- package/dist/src/Component/heat-map/index.js +86 -0
- package/dist/src/Component/key-value/index.d.ts +18 -0
- package/dist/src/Component/key-value/index.js +30 -0
- package/dist/src/Component/markdown/index.d.ts +35 -5
- package/dist/src/Component/markdown/index.js +72 -16
- package/dist/src/Entity/team/Data/workload.d.ts +3 -1
- package/dist/src/Entity/team/Data/workload.js +6 -4
- package/dist/src/Entity/team/Screen/team-work.d.ts +12 -1
- package/dist/src/Entity/team/Screen/team-work.js +47 -2
- package/dist/src/Entity/ticket/Component/demo-statistics.d.ts +14 -0
- package/dist/src/Entity/ticket/Component/demo-statistics.js +88 -0
- package/dist/src/Entity/ticket/Component/react-statistics.d.ts +14 -22
- package/dist/src/Entity/ticket/Component/react-statistics.js +21 -42
- package/dist/src/Entity/ticket/Component/ticket-content/index.d.ts +28 -3
- package/dist/src/Entity/ticket/Component/ticket-content/index.js +331 -157
- package/dist/src/Entity/ticket/Data/parents.js +1 -1
- package/dist/src/Entity/ticket/Data/query.d.ts +2 -2
- package/dist/src/Entity/ticket/Data/query.js +13 -9
- package/dist/src/Entity/ticket/Data/store.js +1 -1
- package/dist/src/Entity/ticket/Data/tickets.d.ts +1 -1
- package/dist/src/Entity/ticket/Data/tickets.js +1 -1
- package/dist/src/Entity/ticket/Form/edit.d.ts +13 -0
- package/dist/src/Entity/ticket/Form/edit.js +17 -7
- package/dist/src/Entity/ticket/Screen/conductor-tickets.d.ts +7 -1
- package/dist/src/Entity/ticket/Screen/conductor-tickets.js +30 -6
- package/dist/src/Entity/ticket/Screen/edit-assignment-form.d.ts +3 -1
- package/dist/src/Entity/ticket/Screen/edit-assignment-form.js +21 -6
- package/dist/src/Entity/ticket/Screen/my-work.d.ts +8 -1
- package/dist/src/Entity/ticket/Screen/my-work.js +37 -4
- package/dist/src/Entity/ticket/Screen/ticket-comments.d.ts +6 -12
- package/dist/src/Entity/ticket/Screen/ticket-comments.js +11 -16
- package/dist/src/Entity/ticket/Screen/ticket-content-screen.d.ts +25 -44
- package/dist/src/Entity/ticket/Screen/ticket-content-screen.js +79 -87
- package/dist/src/Entity/ticket/Screen/ticket-detail.d.ts +6 -11
- package/dist/src/Entity/ticket/Screen/ticket-detail.js +14 -19
- package/dist/src/Entity/ticket/Screen/ticket-filter-bar.js +2 -9
- package/dist/src/Entity/ticket/Screen/ticket-planning.d.ts +6 -12
- package/dist/src/Entity/ticket/Screen/ticket-planning.js +11 -16
- package/dist/src/Entity/ticket/Screen/ticket-react-markdown.d.ts +9 -7
- package/dist/src/Entity/ticket/Screen/ticket-react-markdown.js +36 -13
- package/dist/src/Entity/ticket/Screen/ticket-react.d.ts +21 -8
- package/dist/src/Entity/ticket/Screen/ticket-react.js +109 -34
- package/dist/src/Entity/ticket/Screen/ticket-search.d.ts +44 -0
- package/dist/src/Entity/ticket/Screen/ticket-search.js +97 -0
- package/dist/src/Entity/ticket/Screen/tickets.d.ts +9 -7
- package/dist/src/Entity/ticket/Screen/tickets.js +25 -85
- package/dist/src/Entity/ticket/Screen/user-tickets.d.ts +7 -0
- package/dist/src/Entity/ticket/Screen/user-tickets.js +30 -2
- package/dist/src/Kernel/renderer.d.ts +1 -3
- package/dist/src/Kernel/renderer.js +2 -5
- package/dist/src/Kernel/tui-kernel.js +7 -16
- package/dist/src/Screen/async-screen.d.ts +13 -0
- package/dist/src/Screen/async-screen.js +34 -5
- package/dist/src/Screen/list-screen.d.ts +11 -2
- package/dist/src/Screen/list-screen.js +20 -2
- package/dist/src/Screen/screen.d.ts +6 -2
- package/dist/src/Statistics/comparison-chart.d.ts +4 -2
- package/dist/src/Statistics/comparison-chart.js +14 -6
- package/dist/src/Statistics/figures.d.ts +17 -3
- package/dist/src/Statistics/figures.js +104 -41
- package/dist/src/Statistics/presentation.d.ts +3 -5
- package/dist/src/Statistics/presentation.js +3 -5
- package/dist/src/Statistics/screen.d.ts +16 -5
- package/dist/src/Statistics/screen.js +59 -26
- package/dist/src/lib/theme.d.ts +0 -56
- package/dist/src/lib/theme.js +0 -42
- package/dist/src/lib/vnode.d.ts +29 -0
- package/dist/src/lib/vnode.js +69 -0
- package/dist/src/route.js +37 -0
- package/dist/src/types.d.ts +5 -1
- package/dist/src/ui.d.ts +0 -1
- package/dist/src/ui.js +0 -1
- package/package.json +2 -2
- package/dist/src/Agent/Component/agent-list/index.d.ts +0 -6
- package/dist/src/Agent/Component/agent-list/index.js +0 -14
- package/dist/src/Component/agent-list/index.d.ts +0 -6
- package/dist/src/Component/agent-list/index.js +0 -14
- package/dist/src/Component/comment-item/index.d.ts +0 -21
- package/dist/src/Component/comment-item/index.js +0 -39
- package/dist/src/Component/create-form/index.d.ts +0 -3
- package/dist/src/Component/create-form/index.js +0 -26
- package/dist/src/Component/dashboard-list/index.d.ts +0 -8
- package/dist/src/Component/dashboard-list/index.js +0 -7
- package/dist/src/Component/project-picker/index.d.ts +0 -15
- package/dist/src/Component/project-picker/index.js +0 -42
- package/dist/src/Component/run-table/index.d.ts +0 -10
- package/dist/src/Component/run-table/index.js +0 -20
- package/dist/src/Component/tab-bar/index.d.ts +0 -20
- package/dist/src/Component/tab-bar/index.js +0 -42
- package/dist/src/Component/ticket-detail/index.d.ts +0 -38
- package/dist/src/Component/ticket-detail/index.js +0 -193
- package/dist/src/Component/ticket-table/index.d.ts +0 -5
- package/dist/src/Component/ticket-table/index.js +0 -14
- package/dist/src/Component/ticket-teaser/index.d.ts +0 -7
- package/dist/src/Component/ticket-teaser/index.js +0 -76
- package/dist/src/Deriver/liveness.d.ts +0 -12
- package/dist/src/Deriver/liveness.js +0 -34
- package/dist/src/Deriver/run-status.d.ts +0 -12
- package/dist/src/Deriver/run-status.js +0 -36
- package/dist/src/Entity/conductor/Component/conductor-list/index.d.ts +0 -25
- package/dist/src/Entity/conductor/Component/conductor-list/index.js +0 -60
- package/dist/src/Entity/project/Component/dashboard-list/index.d.ts +0 -8
- package/dist/src/Entity/project/Component/dashboard-list/index.js +0 -7
- package/dist/src/Entity/project/Component/project-list/index.d.ts +0 -8
- package/dist/src/Entity/project/Component/project-list/index.js +0 -7
- package/dist/src/Entity/project/Component/project-picker/index.d.ts +0 -15
- package/dist/src/Entity/project/Component/project-picker/index.js +0 -42
- package/dist/src/Entity/project/Screen/project-work.d.ts +0 -33
- package/dist/src/Entity/project/Screen/project-work.js +0 -114
- package/dist/src/Entity/run/Component/run-table/index.d.ts +0 -8
- package/dist/src/Entity/run/Component/run-table/index.js +0 -11
- package/dist/src/Entity/statistics/Data/statistics.d.ts +0 -147
- package/dist/src/Entity/statistics/Data/statistics.js +0 -235
- package/dist/src/Entity/statistics/Screen/dashboard.d.ts +0 -70
- package/dist/src/Entity/statistics/Screen/dashboard.js +0 -216
- package/dist/src/Entity/statistics/render.d.ts +0 -6
- package/dist/src/Entity/statistics/render.js +0 -71
- package/dist/src/Entity/ticket/Component/tab-bar/index.d.ts +0 -20
- package/dist/src/Entity/ticket/Component/tab-bar/index.js +0 -71
- package/dist/src/Entity/ticket/Component/ticket-table/index.d.ts +0 -7
- package/dist/src/Entity/ticket/Component/ticket-table/index.js +0 -10
- package/dist/src/Entity/user/Screen/user-overview.d.ts +0 -33
- package/dist/src/Entity/user/Screen/user-overview.js +0 -80
- package/dist/src/Extension/contract.d.ts +0 -204
- package/dist/src/Extension/contract.js +0 -1
- package/dist/src/Kernel/react-body.d.ts +0 -9
- package/dist/src/Kernel/react-body.js +0 -42
- package/dist/src/Screen/async-react-screen.d.ts +0 -45
- package/dist/src/Screen/async-react-screen.js +0 -120
- package/dist/src/Screen/dashboard.d.ts +0 -1
- package/dist/src/Screen/dashboard.js +0 -1
- package/dist/src/Screen/project.d.ts +0 -1
- package/dist/src/Screen/project.js +0 -1
- package/dist/src/Screen/projects.d.ts +0 -1
- package/dist/src/Screen/projects.js +0 -1
- package/dist/src/Screen/react-screen.d.ts +0 -14
- package/dist/src/Screen/react-screen.js +0 -34
- package/dist/src/Screen/ticket.d.ts +0 -1
- package/dist/src/Screen/ticket.js +0 -1
- package/dist/src/Service/agent.d.ts +0 -17
- package/dist/src/Service/agent.js +0 -28
- package/dist/src/Service/create-form-data.d.ts +0 -10
- package/dist/src/Service/create-form-data.js +0 -52
- package/dist/src/Service/create-form.d.ts +0 -52
- package/dist/src/Service/create-form.js +0 -98
- package/dist/src/Service/project-data.d.ts +0 -48
- package/dist/src/Service/project-data.js +0 -92
- package/dist/src/Service/projects.d.ts +0 -21
- package/dist/src/Service/projects.js +0 -80
- package/dist/src/Service/route.d.ts +0 -11
- package/dist/src/Service/route.js +0 -215
- package/dist/src/Service/run-data.d.ts +0 -35
- package/dist/src/Service/run-data.js +0 -62
- package/dist/src/Service/ticket-data.d.ts +0 -6
- package/dist/src/Service/ticket-data.js +0 -7
- package/dist/src/Tui/controller.d.ts +0 -13
- package/dist/src/Tui/controller.js +0 -1794
- package/dist/src/Vendor/termcn/hooks/use-motion.d.ts +0 -5
- package/dist/src/Vendor/termcn/hooks/use-motion.js +0 -7
- package/dist/src/Vendor/termcn/hooks/use-theme.d.ts +0 -5
- package/dist/src/Vendor/termcn/hooks/use-theme.js +0 -10
- package/dist/src/Vendor/termcn/hooks/use-unicode.d.ts +0 -5
- package/dist/src/Vendor/termcn/hooks/use-unicode.js +0 -34
- package/dist/src/Vendor/termcn/lib/dither-chart-utils.d.ts +0 -130
- package/dist/src/Vendor/termcn/lib/dither-chart-utils.js +0 -357
- package/dist/src/Vendor/termcn/lib/dither-line-chart-utils.d.ts +0 -2
- package/dist/src/Vendor/termcn/lib/dither-line-chart-utils.js +0 -72
- package/dist/src/Vendor/termcn/lib/dither-radar-chart-utils.d.ts +0 -2
- package/dist/src/Vendor/termcn/lib/dither-radar-chart-utils.js +0 -113
- package/dist/src/Vendor/termcn/themes/default.d.ts +0 -1
- package/dist/src/Vendor/termcn/themes/default.js +0 -3
- package/dist/src/Vendor/termcn/ui/badge.d.ts +0 -13
- package/dist/src/Vendor/termcn/ui/badge.js +0 -33
- package/dist/src/Vendor/termcn/ui/bar-chart.d.ts +0 -16
- package/dist/src/Vendor/termcn/ui/bar-chart.js +0 -61
- package/dist/src/Vendor/termcn/ui/card.d.ts +0 -18
- package/dist/src/Vendor/termcn/ui/card.js +0 -7
- package/dist/src/Vendor/termcn/ui/dither-chart.d.ts +0 -118
- package/dist/src/Vendor/termcn/ui/dither-chart.js +0 -418
- package/dist/src/Vendor/termcn/ui/dither-line-chart.d.ts +0 -10
- package/dist/src/Vendor/termcn/ui/dither-line-chart.js +0 -11
- package/dist/src/Vendor/termcn/ui/dither-radar-chart.d.ts +0 -21
- package/dist/src/Vendor/termcn/ui/dither-radar-chart.js +0 -11
- package/dist/src/Vendor/termcn/ui/divider.d.ts +0 -14
- package/dist/src/Vendor/termcn/ui/divider.js +0 -25
- package/dist/src/Vendor/termcn/ui/gauge.d.ts +0 -12
- package/dist/src/Vendor/termcn/ui/gauge.js +0 -48
- package/dist/src/Vendor/termcn/ui/heat-map.d.ts +0 -13
- package/dist/src/Vendor/termcn/ui/heat-map.js +0 -59
- package/dist/src/Vendor/termcn/ui/key-value.d.ts +0 -16
- package/dist/src/Vendor/termcn/ui/key-value.js +0 -24
- package/dist/src/Vendor/termcn/ui/line-chart.d.ts +0 -15
- package/dist/src/Vendor/termcn/ui/line-chart.js +0 -102
- package/dist/src/Vendor/termcn/ui/markdown.d.ts +0 -24
- package/dist/src/Vendor/termcn/ui/markdown.js +0 -39
- package/dist/src/Vendor/termcn/ui/progress-bar.d.ts +0 -13
- package/dist/src/Vendor/termcn/ui/progress-bar.js +0 -13
- package/dist/src/Vendor/termcn/ui/sparkline.d.ts +0 -8
- package/dist/src/Vendor/termcn/ui/sparkline.js +0 -30
- package/dist/src/Vendor/termcn/ui/types.d.ts +0 -82
- package/dist/src/Vendor/termcn/ui/types.js +0 -1
- package/dist/src/lib/ansi.d.ts +0 -34
- package/dist/src/lib/ansi.js +0 -97
- package/dist/src/lib/hit-test.d.ts +0 -138
- package/dist/src/lib/hit-test.js +0 -138
- package/dist/src/lib/mouse.d.ts +0 -35
- package/dist/src/lib/mouse.js +0 -89
- package/dist/src/lib/table.d.ts +0 -37
- package/dist/src/lib/table.js +0 -20
- package/dist/src/lib/ticket-query.d.ts +0 -21
- package/dist/src/lib/ticket-query.js +0 -72
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type VNode } from '@opentui/core';
|
|
2
|
+
import { type ListFilterBadge } from '../Component/list-chrome/index.js';
|
|
2
3
|
import type { SectionRow } from '../Component/section-list/index.js';
|
|
3
|
-
import {
|
|
4
|
+
import type { FullScreenChrome } from '../Screen/async-screen.js';
|
|
5
|
+
import { AsyncScreen } from '../Screen/async-screen.js';
|
|
4
6
|
import type { ScreenContext } from '../Screen/screen.js';
|
|
5
7
|
import type { GroupStatisticsResult, StatisticsFilters } from './client.js';
|
|
6
|
-
export declare abstract class StatisticsScreen<T> extends
|
|
8
|
+
export declare abstract class StatisticsScreen<T> extends AsyncScreen<T> {
|
|
7
9
|
readonly bodyFrame = false;
|
|
10
|
+
/** The figure grid's laid-out width, as the layout last reported it. */
|
|
11
|
+
private gridWidth;
|
|
8
12
|
private period;
|
|
9
13
|
private activeGroupId;
|
|
10
14
|
protected abstract statistics(value: T): GroupStatisticsResult;
|
|
11
15
|
protected abstract readyRows(value: T, context: ScreenContext): SectionRow[];
|
|
16
|
+
protected chrome(_context: ScreenContext): FullScreenChrome;
|
|
12
17
|
private groups;
|
|
13
18
|
private overviewGroup;
|
|
14
19
|
private tabbedGroups;
|
|
@@ -25,14 +30,20 @@ export declare abstract class StatisticsScreen<T> extends AsyncReactScreen<T> {
|
|
|
25
30
|
* One boxed filter chip cycling the three fixed period+granularity presets
|
|
26
31
|
* (GAIA-548 scope change 7, replacing the former two independent
|
|
27
32
|
* period/granularity chips of addendum 4). Rendered via
|
|
28
|
-
*
|
|
33
|
+
* `filterBadgeStrip` inside `renderReady`, below Overview — see the
|
|
29
34
|
* override of {@link badges} just below for why.
|
|
30
35
|
*/
|
|
31
36
|
private statisticsBadges;
|
|
32
37
|
protected badges(): readonly ListFilterBadge[];
|
|
33
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Both grids share one measured width. A report of a new width is drawn on
|
|
40
|
+
* the next turn, never from inside the layout pass that reported it.
|
|
41
|
+
*/
|
|
42
|
+
private figures;
|
|
43
|
+
protected renderReady(value: T, context: ScreenContext): VNode;
|
|
34
44
|
protected readyLines(value: T, context: ScreenContext): string[];
|
|
35
45
|
handleInput(key: string, context: ScreenContext): boolean;
|
|
46
|
+
renderNonInteractive(context: ScreenContext): string[];
|
|
36
47
|
leave(): void;
|
|
37
48
|
reset(): void;
|
|
38
49
|
}
|
|
@@ -1,27 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BoxRenderable, h } from '@opentui/core';
|
|
2
|
+
import { filterBadgeStrip, } from '../Component/list-chrome/index.js';
|
|
2
3
|
import { tabPartsFor } from '../Component/top-tabs/index.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { FOREGROUND, paragraphNode, plainRun } from '../lib/vnode.js';
|
|
5
|
+
import { AsyncScreen } from '../Screen/async-screen.js';
|
|
5
6
|
import { StatisticsFigures } from './figures.js';
|
|
6
7
|
import { groupedFigureLines } from './presentation.js';
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* only exposes ONE mount point for React content (`react-screen-host`) and
|
|
11
|
-
* places its own `filterBadgeStrip` output above the WHOLE of it — always
|
|
12
|
-
* above Overview too, which GAIA-548 addendum 6 forbids. `badges()` is
|
|
13
|
-
* therefore overridden to return `[]` (suppressing that framework-level
|
|
14
|
-
* strip) and this component renders the same chip data — from the same
|
|
15
|
-
* {@link StatisticsScreen.statisticsBadges} source, no cycling logic
|
|
16
|
-
* duplicated — directly inside `renderReady`, below Overview.
|
|
17
|
-
*/
|
|
18
|
-
function ChipBadgeStrip({ badges }) {
|
|
19
|
-
if (badges.length === 0)
|
|
20
|
-
return null;
|
|
21
|
-
return (_jsx("box", { flexDirection: "row", flexWrap: "wrap", gap: 1, flexShrink: 0, children: badges.map((badge) => (_jsx("box", { borderStyle: "rounded", borderColor: BORDER, backgroundColor: DEFAULT_BACKGROUND, paddingLeft: 2, paddingRight: 2, flexShrink: 0, children: _jsx("text", { fg: FOREGROUND, children: badge.label }) }, badge.id))) }));
|
|
22
|
-
}
|
|
23
|
-
export class StatisticsScreen extends AsyncReactScreen {
|
|
8
|
+
/** Grid width before the first layout has measured one — a pipe's width. */
|
|
9
|
+
const DEFAULT_COLUMNS = 80;
|
|
10
|
+
export class StatisticsScreen extends AsyncScreen {
|
|
24
11
|
bodyFrame = false;
|
|
12
|
+
/** The figure grid's laid-out width, as the layout last reported it. */
|
|
13
|
+
gridWidth;
|
|
25
14
|
period;
|
|
26
15
|
// GAIA-548 addendum 5: each figure group (Overview, Breakdown, Tickets,
|
|
27
16
|
// Runs, Tokens) is its own navigable sub-tab within the Stats screen,
|
|
@@ -30,6 +19,9 @@ export class StatisticsScreen extends AsyncReactScreen {
|
|
|
30
19
|
// already omits empty groups, so the active tab tracks a group id rather
|
|
31
20
|
// than a fixed index.
|
|
32
21
|
activeGroupId;
|
|
22
|
+
chrome(_context) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
33
25
|
groups(value) {
|
|
34
26
|
const result = this.statistics(value);
|
|
35
27
|
return result.kind === 'ok' ? result.groups : [];
|
|
@@ -97,7 +89,7 @@ export class StatisticsScreen extends AsyncReactScreen {
|
|
|
97
89
|
* One boxed filter chip cycling the three fixed period+granularity presets
|
|
98
90
|
* (GAIA-548 scope change 7, replacing the former two independent
|
|
99
91
|
* period/granularity chips of addendum 4). Rendered via
|
|
100
|
-
*
|
|
92
|
+
* `filterBadgeStrip` inside `renderReady`, below Overview — see the
|
|
101
93
|
* override of {@link badges} just below for why.
|
|
102
94
|
*/
|
|
103
95
|
statisticsBadges() {
|
|
@@ -111,13 +103,30 @@ export class StatisticsScreen extends AsyncReactScreen {
|
|
|
111
103
|
},
|
|
112
104
|
];
|
|
113
105
|
}
|
|
114
|
-
// GAIA-548 addendum 6:
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
106
|
+
// GAIA-548 addendum 6: AsyncScreen's own `badges()` hook renders its strip
|
|
107
|
+
// above the ENTIRE body, which would put it above Overview too. Suppress it
|
|
108
|
+
// here; `renderReady` renders the same data (statisticsBadges()) itself,
|
|
109
|
+
// positioned below Overview.
|
|
118
110
|
badges() {
|
|
119
111
|
return [];
|
|
120
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Both grids share one measured width. A report of a new width is drawn on
|
|
115
|
+
* the next turn, never from inside the layout pass that reported it.
|
|
116
|
+
*/
|
|
117
|
+
figures(id, groups, context) {
|
|
118
|
+
return StatisticsFigures({
|
|
119
|
+
id,
|
|
120
|
+
groups,
|
|
121
|
+
width: this.gridWidth ?? context.terminalColumns ?? DEFAULT_COLUMNS,
|
|
122
|
+
onResize: (width) => {
|
|
123
|
+
if (width <= 0 || width === this.gridWidth)
|
|
124
|
+
return;
|
|
125
|
+
this.gridWidth = width;
|
|
126
|
+
queueMicrotask(() => this.requestActiveDraw(context));
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
121
130
|
renderReady(value, context) {
|
|
122
131
|
const overview = this.overviewGroup(value);
|
|
123
132
|
const groups = this.tabbedGroups(value);
|
|
@@ -129,7 +138,26 @@ export class StatisticsScreen extends AsyncReactScreen {
|
|
|
129
138
|
tabs: groups.map((group) => ({ id: group.id, label: group.label })),
|
|
130
139
|
})
|
|
131
140
|
: undefined;
|
|
132
|
-
|
|
141
|
+
const badges = this.statisticsBadges();
|
|
142
|
+
const line = (id, text) => paragraphNode({ id, runs: [plainRun(text)], fg: FOREGROUND });
|
|
143
|
+
return h(BoxRenderable, {
|
|
144
|
+
id: `${this.id}-statistics`,
|
|
145
|
+
flexDirection: 'column',
|
|
146
|
+
width: '100%',
|
|
147
|
+
gap: 1,
|
|
148
|
+
}, ...this.readyRows(value, context).map((row) => line(`${this.id}-row-${row.key}`, row.text)),
|
|
149
|
+
// Overview renders first, unconditionally, outside the tab group and
|
|
150
|
+
// with no chip bar above it (it reacts to neither period nor
|
|
151
|
+
// granularity) — GAIA-548 addendum 6.
|
|
152
|
+
...(overview
|
|
153
|
+
? [this.figures('statistics-overview-grid', [overview], context)]
|
|
154
|
+
: []), ...(badges.length > 0
|
|
155
|
+
? [filterBadgeStrip(`${this.id}-filter`, badges)]
|
|
156
|
+
: []), ...(tabBar
|
|
157
|
+
? [
|
|
158
|
+
line(`${this.id}-group-tabs`, tabBar.map((part) => part.text).join('')),
|
|
159
|
+
]
|
|
160
|
+
: []), ...(active ? [this.figures('statistics-grid', [active], context)] : []));
|
|
133
161
|
}
|
|
134
162
|
readyLines(value, context) {
|
|
135
163
|
const statistics = this.statistics(value);
|
|
@@ -160,6 +188,11 @@ export class StatisticsScreen extends AsyncReactScreen {
|
|
|
160
188
|
}
|
|
161
189
|
return super.handleInput(key, context);
|
|
162
190
|
}
|
|
191
|
+
renderNonInteractive(context) {
|
|
192
|
+
return this.readyValue === undefined
|
|
193
|
+
? (this.noticeRows(context) ?? [])
|
|
194
|
+
: this.readyLines(this.readyValue, context);
|
|
195
|
+
}
|
|
163
196
|
leave() {
|
|
164
197
|
this.period = undefined;
|
|
165
198
|
this.activeGroupId = undefined;
|
package/dist/src/lib/theme.d.ts
CHANGED
|
@@ -57,59 +57,3 @@ export declare const DEFAULT_BACKGROUND: RGBA;
|
|
|
57
57
|
* Ordinary cockpit text uses {@link FOREGROUND}.
|
|
58
58
|
*/
|
|
59
59
|
export declare const DEFAULT_FOREGROUND: RGBA;
|
|
60
|
-
/**
|
|
61
|
-
* The termcn framework `Theme` object, filled entirely from the cockpit slots
|
|
62
|
-
* above — `lib/theme.ts` is the ONLY termcn palette. The vendored default
|
|
63
|
-
* (`Vendor/termcn/themes/default.ts`) re-exports this rather than carrying its
|
|
64
|
-
* own upstream shadcn hex, so the framework's chrome and our native chrome
|
|
65
|
-
* paint the same indexed wires. Status tokens reuse the highlight slot; there
|
|
66
|
-
* are no named hues.
|
|
67
|
-
*/
|
|
68
|
-
export declare const TERMCN_THEME: {
|
|
69
|
-
border: {
|
|
70
|
-
color: RGBA;
|
|
71
|
-
focusColor: RGBA;
|
|
72
|
-
style: "rounded";
|
|
73
|
-
};
|
|
74
|
-
colors: {
|
|
75
|
-
accent: RGBA;
|
|
76
|
-
accentForeground: RGBA;
|
|
77
|
-
background: RGBA;
|
|
78
|
-
border: RGBA;
|
|
79
|
-
error: RGBA;
|
|
80
|
-
errorForeground: RGBA;
|
|
81
|
-
focusRing: RGBA;
|
|
82
|
-
foreground: RGBA;
|
|
83
|
-
info: RGBA;
|
|
84
|
-
infoForeground: RGBA;
|
|
85
|
-
muted: RGBA;
|
|
86
|
-
mutedForeground: RGBA;
|
|
87
|
-
primary: RGBA;
|
|
88
|
-
primaryForeground: RGBA;
|
|
89
|
-
secondary: RGBA;
|
|
90
|
-
secondaryForeground: RGBA;
|
|
91
|
-
selection: RGBA;
|
|
92
|
-
selectionForeground: RGBA;
|
|
93
|
-
success: RGBA;
|
|
94
|
-
successForeground: RGBA;
|
|
95
|
-
warning: RGBA;
|
|
96
|
-
warningForeground: RGBA;
|
|
97
|
-
};
|
|
98
|
-
name: string;
|
|
99
|
-
spacing: {
|
|
100
|
-
0: number;
|
|
101
|
-
1: number;
|
|
102
|
-
2: number;
|
|
103
|
-
3: number;
|
|
104
|
-
4: number;
|
|
105
|
-
6: number;
|
|
106
|
-
8: number;
|
|
107
|
-
};
|
|
108
|
-
typography: {
|
|
109
|
-
base: string;
|
|
110
|
-
bold: boolean;
|
|
111
|
-
lg: string;
|
|
112
|
-
sm: string;
|
|
113
|
-
xl: string;
|
|
114
|
-
};
|
|
115
|
-
};
|
package/dist/src/lib/theme.js
CHANGED
|
@@ -61,45 +61,3 @@ export const DEFAULT_BACKGROUND = RGBA.defaultBackground();
|
|
|
61
61
|
* Ordinary cockpit text uses {@link FOREGROUND}.
|
|
62
62
|
*/
|
|
63
63
|
export const DEFAULT_FOREGROUND = RGBA.defaultForeground();
|
|
64
|
-
/**
|
|
65
|
-
* The termcn framework `Theme` object, filled entirely from the cockpit slots
|
|
66
|
-
* above — `lib/theme.ts` is the ONLY termcn palette. The vendored default
|
|
67
|
-
* (`Vendor/termcn/themes/default.ts`) re-exports this rather than carrying its
|
|
68
|
-
* own upstream shadcn hex, so the framework's chrome and our native chrome
|
|
69
|
-
* paint the same indexed wires. Status tokens reuse the highlight slot; there
|
|
70
|
-
* are no named hues.
|
|
71
|
-
*/
|
|
72
|
-
export const TERMCN_THEME = {
|
|
73
|
-
border: {
|
|
74
|
-
color: BORDER,
|
|
75
|
-
focusColor: BORDER_EMPHASIS,
|
|
76
|
-
style: 'rounded',
|
|
77
|
-
},
|
|
78
|
-
colors: {
|
|
79
|
-
accent: BORDER,
|
|
80
|
-
accentForeground: FOREGROUND_ACTIVE,
|
|
81
|
-
background: BACKGROUND,
|
|
82
|
-
border: BORDER,
|
|
83
|
-
error: FOREGROUND_HIGHLIGHT,
|
|
84
|
-
errorForeground: FOREGROUND_ACTIVE,
|
|
85
|
-
focusRing: BORDER_EMPHASIS,
|
|
86
|
-
foreground: FOREGROUND,
|
|
87
|
-
info: FOREGROUND_HIGHLIGHT,
|
|
88
|
-
infoForeground: FOREGROUND_ACTIVE,
|
|
89
|
-
muted: BACKGROUND_ACTIVE,
|
|
90
|
-
mutedForeground: BACKGROUND_ACTIVE,
|
|
91
|
-
primary: FOREGROUND_HIGHLIGHT,
|
|
92
|
-
primaryForeground: FOREGROUND_ACTIVE,
|
|
93
|
-
secondary: FOREGROUND,
|
|
94
|
-
secondaryForeground: FOREGROUND_ACTIVE,
|
|
95
|
-
selection: FOREGROUND_ACTIVE,
|
|
96
|
-
selectionForeground: FOREGROUND_ACTIVE,
|
|
97
|
-
success: FOREGROUND_HIGHLIGHT,
|
|
98
|
-
successForeground: FOREGROUND_ACTIVE,
|
|
99
|
-
warning: FOREGROUND_HIGHLIGHT,
|
|
100
|
-
warningForeground: FOREGROUND_ACTIVE,
|
|
101
|
-
},
|
|
102
|
-
name: 'gaia',
|
|
103
|
-
spacing: { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 6: 6, 8: 8 },
|
|
104
|
-
typography: { base: '', bold: true, lg: 'bold', sm: 'dim', xl: 'bold' },
|
|
105
|
-
};
|
package/dist/src/lib/vnode.d.ts
CHANGED
|
@@ -242,3 +242,32 @@ export declare const messageTree: (owner: string, lines: readonly string[]) => V
|
|
|
242
242
|
* the content, so nobody has to measure them back off a wrap budget again.
|
|
243
243
|
*/
|
|
244
244
|
export declare const indentedColumn: (id: string, columns: number, ...children: readonly VNode[]) => VNode;
|
|
245
|
+
/** A UNDERLINE run — a linked value in a key/value list. */
|
|
246
|
+
export declare const UNDERLINE: number;
|
|
247
|
+
/** One unstyled run, so callers never spell the struct for plain text. */
|
|
248
|
+
export declare const plainRun: (text: string, attributes?: number, fg?: RGBA) => StyledRun;
|
|
249
|
+
/**
|
|
250
|
+
* A left press that the node consumes — the presentation-level `onMouseDown`
|
|
251
|
+
* translated once. Propagation stops so the kernel's row binding on an
|
|
252
|
+
* enclosing node (`ScreenPresentation.rowIds`) does not act on the same click.
|
|
253
|
+
*/
|
|
254
|
+
export declare const pressHandler: (action: (() => void) | undefined) => ((event: MouseEvent) => void) | undefined;
|
|
255
|
+
export interface ParagraphInput {
|
|
256
|
+
/** Entity-derived and unique in the tree, like every node's. */
|
|
257
|
+
readonly id: string;
|
|
258
|
+
readonly runs: readonly StyledRun[];
|
|
259
|
+
readonly fg: RGBA;
|
|
260
|
+
readonly attributes?: number;
|
|
261
|
+
readonly width?: number | `${number}%` | 'auto';
|
|
262
|
+
readonly wrapMode?: 'word' | 'none';
|
|
263
|
+
readonly flexGrow?: number;
|
|
264
|
+
readonly flexShrink?: number;
|
|
265
|
+
readonly onMouseDown?: (() => void) | undefined;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* A paragraph: AUTO height and word-wrapped, unlike {@link textNode}, which is
|
|
269
|
+
* one physical row by contract. Every mutable prop is stated on every call —
|
|
270
|
+
* including an absent click handler — because a patch cannot clear what the
|
|
271
|
+
* next tree omits.
|
|
272
|
+
*/
|
|
273
|
+
export declare const paragraphNode: ({ id, runs, fg, attributes, width, wrapMode, flexGrow, flexShrink, onMouseDown, }: ParagraphInput) => VNode;
|
package/dist/src/lib/vnode.js
CHANGED
|
@@ -484,3 +484,72 @@ export const messageTree = (owner, lines) => columnNode(`${owner}-message`, ...l
|
|
|
484
484
|
* the content, so nobody has to measure them back off a wrap budget again.
|
|
485
485
|
*/
|
|
486
486
|
export const indentedColumn = (id, columns, ...children) => h(BoxRenderable, { id, flexDirection: 'column', flexShrink: 0, paddingLeft: columns }, ...children);
|
|
487
|
+
/** A UNDERLINE run — a linked value in a key/value list. */
|
|
488
|
+
export const UNDERLINE = createTextAttributes({ underline: true });
|
|
489
|
+
/** One unstyled run, so callers never spell the struct for plain text. */
|
|
490
|
+
export const plainRun = (text, attributes = 0, fg) => ({ text, attributes, ...(fg === undefined ? {} : { fg }) });
|
|
491
|
+
/**
|
|
492
|
+
* The last content each paragraph was handed, by node id.
|
|
493
|
+
*
|
|
494
|
+
* `TextRenderable.content` re-buffers its text whenever it is handed a
|
|
495
|
+
* DIFFERENT object, and a string is converted to a new one on every write. A
|
|
496
|
+
* patch restates every prop on every draw, so without this a 500-line
|
|
497
|
+
* description would re-buffer every paragraph on every keypress. Handing back
|
|
498
|
+
* the identical `StyledText` for an unchanged paragraph makes that setter a
|
|
499
|
+
* no-op.
|
|
500
|
+
*
|
|
501
|
+
* ponytail: bounded by clearing, not LRU — a session that opens thousands of
|
|
502
|
+
* tickets rebuilds the cache once per 4096 paragraphs.
|
|
503
|
+
*/
|
|
504
|
+
const paragraphContent = new Map();
|
|
505
|
+
const PARAGRAPH_CACHE_LIMIT = 4096;
|
|
506
|
+
const colourKey = (fg) => fg === undefined ? '' : String(fg.buffer);
|
|
507
|
+
const styledRuns = (id, runs) => {
|
|
508
|
+
const key = runs
|
|
509
|
+
.map((run) => `${run.attributes}\u0001${colourKey(run.fg)}\u0001${run.text}`)
|
|
510
|
+
.join('\u0002');
|
|
511
|
+
const cached = paragraphContent.get(id);
|
|
512
|
+
if (cached?.key === key)
|
|
513
|
+
return cached.text;
|
|
514
|
+
if (paragraphContent.size >= PARAGRAPH_CACHE_LIMIT)
|
|
515
|
+
paragraphContent.clear();
|
|
516
|
+
const text = new StyledText(runs.map((run) => ({
|
|
517
|
+
__isChunk: true,
|
|
518
|
+
text: run.text,
|
|
519
|
+
attributes: run.attributes,
|
|
520
|
+
...(run.fg === undefined ? {} : { fg: run.fg }),
|
|
521
|
+
})));
|
|
522
|
+
paragraphContent.set(id, { key, text });
|
|
523
|
+
return text;
|
|
524
|
+
};
|
|
525
|
+
/**
|
|
526
|
+
* A left press that the node consumes — the presentation-level `onMouseDown`
|
|
527
|
+
* translated once. Propagation stops so the kernel's row binding on an
|
|
528
|
+
* enclosing node (`ScreenPresentation.rowIds`) does not act on the same click.
|
|
529
|
+
*/
|
|
530
|
+
export const pressHandler = (action) => action === undefined
|
|
531
|
+
? undefined
|
|
532
|
+
: (event) => {
|
|
533
|
+
if (!isLeftPress(event))
|
|
534
|
+
return;
|
|
535
|
+
event.stopPropagation();
|
|
536
|
+
action();
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* A paragraph: AUTO height and word-wrapped, unlike {@link textNode}, which is
|
|
540
|
+
* one physical row by contract. Every mutable prop is stated on every call —
|
|
541
|
+
* including an absent click handler — because a patch cannot clear what the
|
|
542
|
+
* next tree omits.
|
|
543
|
+
*/
|
|
544
|
+
export const paragraphNode = ({ id, runs, fg, attributes = 0, width = 'auto', wrapMode = 'word', flexGrow = 0, flexShrink = 1, onMouseDown, }) => h(TextRenderable, {
|
|
545
|
+
id,
|
|
546
|
+
content: styledRuns(id, runs),
|
|
547
|
+
fg,
|
|
548
|
+
attributes,
|
|
549
|
+
width,
|
|
550
|
+
wrapMode,
|
|
551
|
+
flexGrow,
|
|
552
|
+
flexShrink,
|
|
553
|
+
selectable: false,
|
|
554
|
+
onMouseDown: pressHandler(onMouseDown),
|
|
555
|
+
});
|
package/dist/src/route.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// projects screen — a stderr line and exit 1 (AC-3).
|
|
11
11
|
import { DrupalJsonApiParams } from 'drupal-jsonapi-params';
|
|
12
12
|
import { asString } from './lib/format.js';
|
|
13
|
+
import { serverMessage } from './lib/jsonapi-error.js';
|
|
13
14
|
import { normalizeRepoUrl } from './lib/repo-url.js';
|
|
14
15
|
/** `" on <baseUrl>"`, or '' when the caller did not supply one. */
|
|
15
16
|
function on(ctx) {
|
|
@@ -67,6 +68,40 @@ async function resolveTicketByIdentifier(client, route, ctx) {
|
|
|
67
68
|
}
|
|
68
69
|
return { screen: 'ticket', ticketId: id, view: route.view ?? 'full' };
|
|
69
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* A ticket by its origin issue (GAIA-544). The server owns the grammar: the
|
|
73
|
+
* reference goes out verbatim as `filter[search]`, and an unknown or ambiguous
|
|
74
|
+
* alias comes back as a 400 whose message is passed on unchanged. Several
|
|
75
|
+
* tickets may share one origin; like an identifier collision, that is named
|
|
76
|
+
* with the candidates rather than guessed.
|
|
77
|
+
*/
|
|
78
|
+
async function resolveTicketByOrigin(client, route, ctx) {
|
|
79
|
+
const params = new DrupalJsonApiParams();
|
|
80
|
+
params.addFilter('search', route.value);
|
|
81
|
+
params.addFields('gaia_ticket--gaia_ticket', ['identifier', 'title']);
|
|
82
|
+
params.addPageLimit(20);
|
|
83
|
+
const doc = (await client
|
|
84
|
+
.get('gaia_ticket/gaia_ticket', params)
|
|
85
|
+
.catch((error) => {
|
|
86
|
+
throw new Error(`${route.value}${on(ctx)}: ${serverMessage(error)}`);
|
|
87
|
+
}));
|
|
88
|
+
const rows = doc?.data ?? [];
|
|
89
|
+
if (rows.length === 0) {
|
|
90
|
+
throw new Error(`no ticket has the origin ${route.value}${on(ctx)}.`);
|
|
91
|
+
}
|
|
92
|
+
if (rows.length > 1) {
|
|
93
|
+
const candidates = rows
|
|
94
|
+
.map((r) => ` ticket:${r.id} — ${asString(r.attributes?.identifier) ?? ''} ${asString(r.attributes?.title) ?? ''}`)
|
|
95
|
+
.join('\n');
|
|
96
|
+
throw new Error(`${route.value} is the origin of ${rows.length} tickets${on(ctx)}. ` +
|
|
97
|
+
`Name one with 'ticket:<uuid>':\n${candidates}`);
|
|
98
|
+
}
|
|
99
|
+
const id = rows[0]?.id;
|
|
100
|
+
if (!id) {
|
|
101
|
+
throw new Error(`no ticket has the origin ${route.value}${on(ctx)}.`);
|
|
102
|
+
}
|
|
103
|
+
return { screen: 'ticket', ticketId: id, view: route.view ?? 'full' };
|
|
104
|
+
}
|
|
70
105
|
/**
|
|
71
106
|
* The repo URLs a `gaia_project` row declares, normalised. `repository` is a
|
|
72
107
|
* single-cardinality `gaia_repo` field: JSON:API serves it as one object
|
|
@@ -206,6 +241,8 @@ export function resolveRoute(client, route, ctx) {
|
|
|
206
241
|
return resolveRun(client, route, ctx);
|
|
207
242
|
if (route.kind === 'project')
|
|
208
243
|
return resolveProject(client, route, ctx);
|
|
244
|
+
if (route.by === 'origin')
|
|
245
|
+
return resolveTicketByOrigin(client, route, ctx);
|
|
209
246
|
return route.by === 'uuid'
|
|
210
247
|
? resolveTicketByUuid(client, route, ctx)
|
|
211
248
|
: resolveTicketByIdentifier(client, route, ctx);
|
package/dist/src/types.d.ts
CHANGED
|
@@ -177,7 +177,11 @@ export interface UiInitialRoute {
|
|
|
177
177
|
* value is a NORMALISED remote URL (`host/path`), matched client-side against
|
|
178
178
|
* `gaia_project.repository.url` (spec Decision 3).
|
|
179
179
|
*/
|
|
180
|
-
|
|
180
|
+
/**
|
|
181
|
+
* `'origin'` (GAIA-544) is a ticket looked up by its origin issue — `#19`,
|
|
182
|
+
* `gaia#19` or the issue URL — which the server's `filter[search]` resolves.
|
|
183
|
+
*/
|
|
184
|
+
by: 'uuid' | 'identifier' | 'name' | 'repo' | 'origin';
|
|
181
185
|
value: string;
|
|
182
186
|
/** Drupal view mode for a ticket target; default 'full'. */
|
|
183
187
|
view?: UiViewMode | undefined;
|
package/dist/src/ui.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ export { ListCursor } from './lib/cursor.js';
|
|
|
14
14
|
export { columnNode, textNode } from './lib/vnode.js';
|
|
15
15
|
export type { RouteMetadata, ScreenClass, TabMetadata, TabReference, } from './Routing/route.js';
|
|
16
16
|
export { AsyncScreen } from './Screen/async-screen.js';
|
|
17
|
-
export { ReactScreen } from './Screen/react-screen.js';
|
|
18
17
|
export { type RowActionOptions, runRowAction } from './Screen/row-action.js';
|
|
19
18
|
export type { Screen, ScreenAction, ScreenContext } from './Screen/screen.js';
|
|
20
19
|
export type { TicketDetailDocument } from './types.js';
|
package/dist/src/ui.js
CHANGED
|
@@ -12,5 +12,4 @@ export { copyThrough } from './lib/clipboard.js';
|
|
|
12
12
|
export { ListCursor } from './lib/cursor.js';
|
|
13
13
|
export { columnNode, textNode } from './lib/vnode.js';
|
|
14
14
|
export { AsyncScreen } from './Screen/async-screen.js';
|
|
15
|
-
export { ReactScreen } from './Screen/react-screen.js';
|
|
16
15
|
export { runRowAction } from './Screen/row-action.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaia-ai/addon-gaia-ui",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
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",
|
|
@@ -29,6 +29,6 @@
|
|
|
29
29
|
"zustand": "^5.0.15"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@gaia-ai/core": "^0.16.
|
|
32
|
+
"@gaia-ai/core": "^0.16.2"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -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,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
|
-
}
|