@gaia-ai/addon-gaia-ui 0.15.2 → 0.16.1
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/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/store.js +1 -1
- 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-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/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 +6 -0
- package/dist/src/Screen/async-screen.js +9 -1
- 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/ui.d.ts +0 -1
- package/dist/src/ui.js +0 -1
- package/package.json +3 -6
- 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
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RGBA, VNode } from '@opentui/core';
|
|
2
|
+
export type BadgeVariant = 'default' | 'success' | 'warning' | 'error' | 'info' | 'secondary';
|
|
3
|
+
/** The unbordered badge — the only form the TUI draws. */
|
|
4
|
+
export interface BadgeProps {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
variant?: BadgeVariant;
|
|
8
|
+
color?: RGBA;
|
|
9
|
+
bold?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const Badge: ({ id, label, variant, color, bold, }: BadgeProps) => VNode;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FOREGROUND, FOREGROUND_HIGHLIGHT } from '../../lib/theme.js';
|
|
2
|
+
import { BOLD, paragraphNode, plainRun } from '../../lib/vnode.js';
|
|
3
|
+
// Every status variant shares the highlight slot: the palette names roles, not
|
|
4
|
+
// hues (`lib/theme.ts`).
|
|
5
|
+
const variantColor = (variant) => variant === 'secondary' ? FOREGROUND : FOREGROUND_HIGHLIGHT;
|
|
6
|
+
export const Badge = ({ id, label, variant = 'default', color, bold = false, }) => paragraphNode({
|
|
7
|
+
id,
|
|
8
|
+
runs: [plainRun(label, bold ? BOLD : 0)],
|
|
9
|
+
fg: color ?? variantColor(variant),
|
|
10
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type RGBA, type VNode } from '@opentui/core';
|
|
2
|
+
export interface BarChartItem {
|
|
3
|
+
label: string;
|
|
4
|
+
value: number;
|
|
5
|
+
color?: RGBA;
|
|
6
|
+
}
|
|
7
|
+
/** The horizontal chart — the only direction the TUI draws. */
|
|
8
|
+
export interface BarChartProps {
|
|
9
|
+
id: string;
|
|
10
|
+
data: readonly BarChartItem[];
|
|
11
|
+
width?: number;
|
|
12
|
+
showValues?: boolean;
|
|
13
|
+
title?: string;
|
|
14
|
+
maxValue?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const BarChart: ({ id, data, width, showValues, title, maxValue: suppliedMax, }: BarChartProps) => VNode;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { BoxRenderable, h } from '@opentui/core';
|
|
2
|
+
import { FOREGROUND, FOREGROUND_HIGHLIGHT } from '../../lib/theme.js';
|
|
3
|
+
import { BOLD, DIM, paragraphNode, plainRun } from '../../lib/vnode.js';
|
|
4
|
+
const BAR_CHAR = '█';
|
|
5
|
+
const EMPTY_CHAR = '░';
|
|
6
|
+
const pad = (str, length) => {
|
|
7
|
+
if (str.length >= length) {
|
|
8
|
+
return str.slice(0, length);
|
|
9
|
+
}
|
|
10
|
+
return str.padEnd(length);
|
|
11
|
+
};
|
|
12
|
+
const padStart = (str, length) => {
|
|
13
|
+
if (str.length >= length) {
|
|
14
|
+
return str.slice(0, length);
|
|
15
|
+
}
|
|
16
|
+
return ' '.repeat(length - str.length) + str;
|
|
17
|
+
};
|
|
18
|
+
export const BarChart = ({ id, data, width = 30, showValues = true, title, maxValue: suppliedMax, }) => {
|
|
19
|
+
if (data.length === 0)
|
|
20
|
+
return paragraphNode({
|
|
21
|
+
id,
|
|
22
|
+
runs: [plainRun('No data', DIM)],
|
|
23
|
+
fg: FOREGROUND,
|
|
24
|
+
});
|
|
25
|
+
const maxValue = suppliedMax ?? Math.max(...data.map((d) => d.value));
|
|
26
|
+
const maxLabelLen = Math.max(...data.map((d) => d.label.length));
|
|
27
|
+
const maxValLen = Math.max(...data.map((d) => String(d.value).length));
|
|
28
|
+
const barWidth = width - maxLabelLen - maxValLen - 3;
|
|
29
|
+
return h(BoxRenderable, { id, flexDirection: 'column' }, ...(title
|
|
30
|
+
? [
|
|
31
|
+
paragraphNode({
|
|
32
|
+
id: `${id}-title`,
|
|
33
|
+
runs: [plainRun(title, BOLD)],
|
|
34
|
+
fg: FOREGROUND_HIGHLIGHT,
|
|
35
|
+
}),
|
|
36
|
+
]
|
|
37
|
+
: []),
|
|
38
|
+
// One bar per data item, in the caller's order.
|
|
39
|
+
...data.map((item, index) => {
|
|
40
|
+
const row = `${id}-${index}`;
|
|
41
|
+
const filled = maxValue === 0
|
|
42
|
+
? 0
|
|
43
|
+
: Math.round((item.value / maxValue) * Math.max(1, barWidth));
|
|
44
|
+
const empty = Math.max(0, barWidth - filled);
|
|
45
|
+
return h(BoxRenderable, { id: row, flexDirection: 'row', gap: 1 }, paragraphNode({
|
|
46
|
+
id: `${row}-label`,
|
|
47
|
+
runs: [plainRun(pad(item.label, maxLabelLen))],
|
|
48
|
+
fg: FOREGROUND,
|
|
49
|
+
}), paragraphNode({
|
|
50
|
+
id: `${row}-bar`,
|
|
51
|
+
runs: [plainRun(BAR_CHAR.repeat(filled) + EMPTY_CHAR.repeat(empty))],
|
|
52
|
+
fg: item.color ?? FOREGROUND_HIGHLIGHT,
|
|
53
|
+
}), ...(showValues
|
|
54
|
+
? [
|
|
55
|
+
paragraphNode({
|
|
56
|
+
id: `${row}-value`,
|
|
57
|
+
runs: [plainRun(padStart(String(item.value), maxValLen), DIM)],
|
|
58
|
+
fg: FOREGROUND,
|
|
59
|
+
}),
|
|
60
|
+
]
|
|
61
|
+
: []));
|
|
62
|
+
}));
|
|
63
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type RGBA, type VNode } from '@opentui/core';
|
|
2
|
+
export type BorderStyle = 'single' | 'double' | 'rounded' | 'heavy';
|
|
3
|
+
export interface CardProps {
|
|
4
|
+
/** Entity-derived; the card's parts derive their ids from it. */
|
|
5
|
+
id: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
children: readonly VNode[];
|
|
9
|
+
borderColor?: RGBA;
|
|
10
|
+
width?: number | `${number}%`;
|
|
11
|
+
onMouseDown?: () => void;
|
|
12
|
+
borderStyle?: BorderStyle;
|
|
13
|
+
paddingX?: number;
|
|
14
|
+
paddingY?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const Card: ({ id, title, subtitle, children, borderColor, width, onMouseDown, borderStyle, paddingX, paddingY, }: CardProps) => VNode;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BoxRenderable, h } from '@opentui/core';
|
|
2
|
+
import { BORDER, FOREGROUND } from '../../lib/theme.js';
|
|
3
|
+
import { BOLD, DIM, paragraphNode, plainRun, pressHandler, } from '../../lib/vnode.js';
|
|
4
|
+
export const Card = ({ id, title, subtitle, children, borderColor, width, onMouseDown, borderStyle = 'rounded', paddingX = 1, paddingY = 0, }) => {
|
|
5
|
+
const heading = [];
|
|
6
|
+
if (title)
|
|
7
|
+
heading.push(paragraphNode({
|
|
8
|
+
id: `${id}-title`,
|
|
9
|
+
runs: [plainRun(title, BOLD)],
|
|
10
|
+
fg: FOREGROUND,
|
|
11
|
+
}));
|
|
12
|
+
if (subtitle)
|
|
13
|
+
heading.push(paragraphNode({
|
|
14
|
+
id: `${id}-subtitle`,
|
|
15
|
+
runs: [plainRun(subtitle, DIM)],
|
|
16
|
+
fg: FOREGROUND,
|
|
17
|
+
}));
|
|
18
|
+
return h(BoxRenderable, {
|
|
19
|
+
id,
|
|
20
|
+
flexDirection: 'column',
|
|
21
|
+
border: true,
|
|
22
|
+
borderStyle,
|
|
23
|
+
borderColor: borderColor ?? BORDER,
|
|
24
|
+
width: width ?? 'auto',
|
|
25
|
+
paddingLeft: paddingX,
|
|
26
|
+
paddingRight: paddingX,
|
|
27
|
+
paddingTop: paddingY,
|
|
28
|
+
paddingBottom: paddingY,
|
|
29
|
+
onMouseDown: pressHandler(onMouseDown),
|
|
30
|
+
}, ...(heading.length > 0
|
|
31
|
+
? [
|
|
32
|
+
h(BoxRenderable, {
|
|
33
|
+
id: `${id}-heading`,
|
|
34
|
+
flexDirection: 'column',
|
|
35
|
+
paddingBottom: 1,
|
|
36
|
+
}, ...heading),
|
|
37
|
+
]
|
|
38
|
+
: []), h(BoxRenderable, { id: `${id}-content`, flexDirection: 'column' }, ...children));
|
|
39
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BoxRenderable, h } from '@opentui/core';
|
|
2
|
+
import { BORDER } from '../../lib/theme.js';
|
|
3
|
+
// One array for every divider: `border` re-applies Yoga borders when it is
|
|
4
|
+
// handed a different value, and a patch restates it on every draw.
|
|
5
|
+
const TOP = ['top'];
|
|
6
|
+
/** The horizontal, unlabelled rule — the only shape the TUI draws. */
|
|
7
|
+
export const Divider = ({ id, color }) => h(BoxRenderable, { id, flexDirection: 'row' }, h(BoxRenderable, {
|
|
8
|
+
id: `${id}-rule`,
|
|
9
|
+
flexGrow: 1,
|
|
10
|
+
borderStyle: 'single',
|
|
11
|
+
borderColor: color ?? BORDER,
|
|
12
|
+
border: TOP,
|
|
13
|
+
}));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type RGBA, type VNode } from '@opentui/core';
|
|
2
|
+
export interface HeatMapProps {
|
|
3
|
+
id: string;
|
|
4
|
+
data: number[][];
|
|
5
|
+
rowLabels?: string[];
|
|
6
|
+
colLabels?: string[];
|
|
7
|
+
cellLabels?: string[][];
|
|
8
|
+
/** Low to high; indexed palette slots only. */
|
|
9
|
+
colorScale?: readonly RGBA[];
|
|
10
|
+
cellWidth?: number;
|
|
11
|
+
showValues?: boolean;
|
|
12
|
+
showLegend?: boolean;
|
|
13
|
+
minValue?: number;
|
|
14
|
+
maxValue?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Low → high intensity as three palette slots, so the scale follows the
|
|
18
|
+
* operator's terminal theme like every other colour the cockpit paints.
|
|
19
|
+
*/
|
|
20
|
+
export declare const HEAT_SCALE: readonly RGBA[];
|
|
21
|
+
export declare const HeatMap: ({ id, data, rowLabels, colLabels, cellLabels, colorScale, cellWidth, showValues, showLegend, minValue, maxValue, }: HeatMapProps) => VNode;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { BoxRenderable, h } from '@opentui/core';
|
|
2
|
+
import { FOREGROUND, FOREGROUND_ACTIVE, FOREGROUND_HIGHLIGHT, } from '../../lib/theme.js';
|
|
3
|
+
import { DIM, paragraphNode, plainRun, } from '../../lib/vnode.js';
|
|
4
|
+
/**
|
|
5
|
+
* Low → high intensity as three palette slots, so the scale follows the
|
|
6
|
+
* operator's terminal theme like every other colour the cockpit paints.
|
|
7
|
+
*/
|
|
8
|
+
export const HEAT_SCALE = [
|
|
9
|
+
FOREGROUND,
|
|
10
|
+
FOREGROUND_HIGHLIGHT,
|
|
11
|
+
FOREGROUND_ACTIVE,
|
|
12
|
+
];
|
|
13
|
+
const SHADE_CHARS = [' ', '░', '▒', '▓', '█'];
|
|
14
|
+
const getColorForValue = (value, min, max, scale) => {
|
|
15
|
+
if (max === min)
|
|
16
|
+
return scale[Math.floor(scale.length / 2)] ?? FOREGROUND;
|
|
17
|
+
const t = (value - min) / (max - min);
|
|
18
|
+
const index = Math.min(scale.length - 1, Math.max(0, Math.round(t * (scale.length - 1))));
|
|
19
|
+
return scale[index] ?? scale[0] ?? FOREGROUND;
|
|
20
|
+
};
|
|
21
|
+
const getShadeForValue = (value, min, max) => {
|
|
22
|
+
if (max === min)
|
|
23
|
+
return SHADE_CHARS[2] ?? '▒';
|
|
24
|
+
const t = (value - min) / (max - min);
|
|
25
|
+
const index = Math.min(SHADE_CHARS.length - 1, Math.max(0, Math.round(t * (SHADE_CHARS.length - 1))));
|
|
26
|
+
return SHADE_CHARS[index] ?? SHADE_CHARS[0] ?? ' ';
|
|
27
|
+
};
|
|
28
|
+
const padCenter = (value, width) => {
|
|
29
|
+
if (value.length >= width)
|
|
30
|
+
return value;
|
|
31
|
+
const total = width - value.length;
|
|
32
|
+
const left = Math.floor(total / 2);
|
|
33
|
+
return ' '.repeat(left) + value + ' '.repeat(total - left);
|
|
34
|
+
};
|
|
35
|
+
const padStart = (value, width) => value.length >= width ? value.slice(0, width) : value.padStart(width);
|
|
36
|
+
/** One grid row as one text: the cells touch, so their runs are the row. */
|
|
37
|
+
const gridRow = (id, runs) => paragraphNode({
|
|
38
|
+
id,
|
|
39
|
+
runs,
|
|
40
|
+
fg: FOREGROUND,
|
|
41
|
+
wrapMode: 'none',
|
|
42
|
+
flexShrink: 0,
|
|
43
|
+
});
|
|
44
|
+
export const HeatMap = ({ id, data, rowLabels, colLabels, cellLabels, colorScale = HEAT_SCALE, cellWidth = 5, showValues = false, showLegend = true, minValue, maxValue, }) => {
|
|
45
|
+
const firstRow = data[0];
|
|
46
|
+
if (data.length === 0 || !firstRow || firstRow.length === 0)
|
|
47
|
+
return paragraphNode({
|
|
48
|
+
id,
|
|
49
|
+
runs: [plainRun('No data', DIM)],
|
|
50
|
+
fg: FOREGROUND,
|
|
51
|
+
});
|
|
52
|
+
const numCols = firstRow.length;
|
|
53
|
+
const allValues = data.flat();
|
|
54
|
+
const min = minValue ?? Math.min(...allValues);
|
|
55
|
+
const max = maxValue ?? Math.max(...allValues);
|
|
56
|
+
const rowLabelWidth = rowLabels
|
|
57
|
+
? Math.max(...rowLabels.map((label) => label.length)) + 1
|
|
58
|
+
: 0;
|
|
59
|
+
const rows = [];
|
|
60
|
+
if (colLabels)
|
|
61
|
+
rows.push(gridRow(`${id}-columns`, [
|
|
62
|
+
...(rowLabelWidth > 0
|
|
63
|
+
? [plainRun(' '.repeat(rowLabelWidth + 1), DIM, FOREGROUND)]
|
|
64
|
+
: []),
|
|
65
|
+
...Array.from({ length: numCols }, (_, column) => plainRun(padCenter(colLabels[column] ?? String(column), cellWidth), DIM, FOREGROUND)),
|
|
66
|
+
]));
|
|
67
|
+
data.forEach((row, rowIndex) => {
|
|
68
|
+
rows.push(gridRow(`${id}-row-${rowIndex}`, [
|
|
69
|
+
...(rowLabels
|
|
70
|
+
? [
|
|
71
|
+
plainRun(`${padStart(rowLabels[rowIndex] ?? String(rowIndex), rowLabelWidth)} `, DIM, FOREGROUND),
|
|
72
|
+
]
|
|
73
|
+
: []),
|
|
74
|
+
...row.map((value, column) => plainRun(showValues
|
|
75
|
+
? padCenter(cellLabels?.[rowIndex]?.[column] ?? String(Math.round(value)), cellWidth)
|
|
76
|
+
: getShadeForValue(value, min, max).repeat(cellWidth), 0, getColorForValue(value, min, max, colorScale))),
|
|
77
|
+
]));
|
|
78
|
+
});
|
|
79
|
+
if (showLegend)
|
|
80
|
+
rows.push(h(BoxRenderable, { id: `${id}-legend`, flexDirection: 'row', marginTop: 1 }, gridRow(`${id}-legend-scale`, [
|
|
81
|
+
plainRun('Low ', DIM, FOREGROUND),
|
|
82
|
+
...colorScale.map((colour) => plainRun('█ ', 0, colour)),
|
|
83
|
+
plainRun('High', DIM, FOREGROUND),
|
|
84
|
+
])));
|
|
85
|
+
return h(BoxRenderable, { id, flexDirection: 'column' }, ...rows);
|
|
86
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type RGBA, type VNode } from '@opentui/core';
|
|
2
|
+
export interface KeyValueItem {
|
|
3
|
+
key: string;
|
|
4
|
+
value: string;
|
|
5
|
+
/** Drawn underlined — the value is a link the click acts on. */
|
|
6
|
+
underline?: boolean;
|
|
7
|
+
color?: RGBA;
|
|
8
|
+
onMouseDown?: () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface KeyValueProps {
|
|
11
|
+
id: string;
|
|
12
|
+
items: readonly KeyValueItem[];
|
|
13
|
+
keyWidth?: number;
|
|
14
|
+
separator?: string;
|
|
15
|
+
keyColor?: RGBA;
|
|
16
|
+
valueColor?: RGBA;
|
|
17
|
+
}
|
|
18
|
+
export declare const KeyValue: ({ id, items, keyWidth, separator, keyColor, valueColor, }: KeyValueProps) => VNode;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BoxRenderable, h } from '@opentui/core';
|
|
2
|
+
import { FOREGROUND } from '../../lib/theme.js';
|
|
3
|
+
import { paragraphNode, plainRun, UNDERLINE } from '../../lib/vnode.js';
|
|
4
|
+
export const KeyValue = ({ id, items, keyWidth, separator = ':', keyColor, valueColor, }) => {
|
|
5
|
+
const resolvedKeyWidth = keyWidth ?? Math.max(0, ...items.map((item) => item.key.length)) + 1;
|
|
6
|
+
const resolvedKeyColor = keyColor ?? FOREGROUND;
|
|
7
|
+
const resolvedValueColor = valueColor ?? FOREGROUND;
|
|
8
|
+
return h(BoxRenderable, { id, flexDirection: 'column', width: '100%' },
|
|
9
|
+
// A fixed list per owner, rebuilt whole from the model on every draw, so
|
|
10
|
+
// the position is a sound key for its rows.
|
|
11
|
+
...items.map((item, index) => {
|
|
12
|
+
const row = `${id}-${index}`;
|
|
13
|
+
return h(BoxRenderable, { id: row, flexDirection: 'row', gap: 1, width: '100%' }, paragraphNode({
|
|
14
|
+
id: `${row}-key`,
|
|
15
|
+
runs: [plainRun(item.key.padEnd(resolvedKeyWidth, ' '))],
|
|
16
|
+
fg: resolvedKeyColor,
|
|
17
|
+
}), paragraphNode({
|
|
18
|
+
id: `${row}-separator`,
|
|
19
|
+
runs: [plainRun(separator)],
|
|
20
|
+
fg: resolvedKeyColor,
|
|
21
|
+
}), paragraphNode({
|
|
22
|
+
id: `${row}-value`,
|
|
23
|
+
runs: [plainRun(item.value, item.underline ? UNDERLINE : 0)],
|
|
24
|
+
fg: item.color ?? resolvedValueColor,
|
|
25
|
+
flexGrow: 1,
|
|
26
|
+
flexShrink: 1,
|
|
27
|
+
onMouseDown: item.onMouseDown,
|
|
28
|
+
}));
|
|
29
|
+
}));
|
|
30
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type VNode } from '@opentui/core';
|
|
1
2
|
import type { MarkdownLine, MarkdownSpan } from '../../lib/markdown.js';
|
|
2
3
|
import { type StyledLine, type StyledRun } from '../../lib/vnode.js';
|
|
3
4
|
/**
|
|
@@ -24,11 +25,6 @@ export declare const styledRunOf: (span: MarkdownSpan) => StyledRun;
|
|
|
24
25
|
* reads a span's flags.
|
|
25
26
|
*/
|
|
26
27
|
export declare const styledLineOf: (line: MarkdownLine) => StyledLine;
|
|
27
|
-
/**
|
|
28
|
-
* The inverse of {@link styledLineOf}: already-painted runs as parser lines,
|
|
29
|
-
* so a React painter can consume production rows without a source round trip.
|
|
30
|
-
*/
|
|
31
|
-
export declare const markdownLineFromStyled: (line: StyledLine) => MarkdownLine;
|
|
32
28
|
/** A `gaia_rich` body as the physical rows a surface draws or prints — D1. */
|
|
33
29
|
export declare function markdownLines(body: string): StyledLine[];
|
|
34
30
|
/** A line NOTHING parses — one unstyled run (D3's plain path, as a value). */
|
|
@@ -71,3 +67,37 @@ export declare function markdownRow(line: string): {
|
|
|
71
67
|
text: string;
|
|
72
68
|
bold: boolean;
|
|
73
69
|
};
|
|
70
|
+
export interface MarkdownProps {
|
|
71
|
+
id: string;
|
|
72
|
+
/** Markdown source, parsed with the production line/span parser. */
|
|
73
|
+
source: string;
|
|
74
|
+
width?: number;
|
|
75
|
+
}
|
|
76
|
+
export interface MarkdownLinesProps {
|
|
77
|
+
id: string;
|
|
78
|
+
/** Rows as {@link markdownLines} renders them. */
|
|
79
|
+
lines: readonly StyledLine[];
|
|
80
|
+
width?: number;
|
|
81
|
+
/** Brighter body copy — terminal cells have no font size. */
|
|
82
|
+
emphasis?: boolean;
|
|
83
|
+
}
|
|
84
|
+
/** Styled rows as one column of wrapped paragraphs, blank rows kept one high. */
|
|
85
|
+
export declare const MarkdownLines: ({ id, lines, width, emphasis, }: MarkdownLinesProps) => VNode;
|
|
86
|
+
/** A Markdown source, parsed and painted. */
|
|
87
|
+
export declare const Markdown: ({ id, source, width }: MarkdownProps) => VNode;
|
|
88
|
+
export interface HeadlineMarkdownProps {
|
|
89
|
+
id: string;
|
|
90
|
+
headline: string;
|
|
91
|
+
children: readonly VNode[];
|
|
92
|
+
paddingBottom?: number;
|
|
93
|
+
paddingLeft?: number;
|
|
94
|
+
/**
|
|
95
|
+
* The id the HEADLINE row carries — a selectable entry's row id, which the
|
|
96
|
+
* kernel scrolls to and binds clicks on. The headline text inside it owns
|
|
97
|
+
* {@link onMouseDown} and consumes the press.
|
|
98
|
+
*/
|
|
99
|
+
headlineId?: string;
|
|
100
|
+
onMouseDown?: () => void;
|
|
101
|
+
}
|
|
102
|
+
/** A `* headline` row above an indented body. */
|
|
103
|
+
export declare const HeadlineMarkdown: ({ id, headline, children, paddingBottom, paddingLeft, headlineId, onMouseDown, }: HeadlineMarkdownProps) => VNode;
|
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
// is DATA — a {@link StyledLine}: rendered text, resolved runs, and the row's own
|
|
29
29
|
// weight — and `Component/section-list`'s `lineText` keeps working unchanged.
|
|
30
30
|
//
|
|
31
|
+
// THE PAINTER sits here too (`Markdown`, `MarkdownLines`, `HeadlineMarkdown`):
|
|
32
|
+
// it turns those same styled rows into renderables, so there is one style rule
|
|
33
|
+
// for a body whether it is piped or drawn, and no second mapping of span kinds
|
|
34
|
+
// to colours anywhere else.
|
|
35
|
+
//
|
|
31
36
|
// WHY `plainLine` AND `indentLine` LIVE HERE rather than in the parser: neither
|
|
32
37
|
// parses. `plainLine` is D3's plain path expressed as a value, and `indentLine`
|
|
33
38
|
// prepends blanks to an ALREADY RENDERED line — which is the whole point of it
|
|
@@ -35,8 +40,9 @@
|
|
|
35
40
|
// CommonMark, so applying the indent after rendering is what makes that
|
|
36
41
|
// impossible rather than merely unlikely, Fact 9). Both are shell work on a
|
|
37
42
|
// rendered line, so they sit with the shell.
|
|
43
|
+
import { BoxRenderable, h } from '@opentui/core';
|
|
38
44
|
import { markdownLines as parseMarkdown } from '../../lib/markdown.js';
|
|
39
|
-
import { BOLD, BORDER, DIM, ITALIC, } from '../../lib/vnode.js';
|
|
45
|
+
import { BOLD, BORDER, DIM, FOREGROUND, FOREGROUND_HIGHLIGHT, ITALIC, paragraphNode, plainRun, } from '../../lib/vnode.js';
|
|
40
46
|
/**
|
|
41
47
|
* ONE SPAN AS THE RUN A RENDERER CAN PAINT — D7/D11, and the mapping this
|
|
42
48
|
* component owns.
|
|
@@ -74,21 +80,6 @@ export const styledLineOf = (line) => ({
|
|
|
74
80
|
runs: line.spans.map(styledRunOf),
|
|
75
81
|
heading: line.heading,
|
|
76
82
|
});
|
|
77
|
-
/**
|
|
78
|
-
* The inverse of {@link styledLineOf}: already-painted runs as parser lines,
|
|
79
|
-
* so a React painter can consume production rows without a source round trip.
|
|
80
|
-
*/
|
|
81
|
-
export const markdownLineFromStyled = (line) => ({
|
|
82
|
-
text: line.text,
|
|
83
|
-
heading: line.heading,
|
|
84
|
-
spans: line.runs.map((run) => ({
|
|
85
|
-
text: run.text,
|
|
86
|
-
...((run.attributes & BOLD) !== 0 ? { bold: true } : {}),
|
|
87
|
-
...((run.attributes & ITALIC) !== 0 ? { italic: true } : {}),
|
|
88
|
-
...((run.attributes & DIM) !== 0 ? { dim: true } : {}),
|
|
89
|
-
...(run.fg !== undefined ? { code: true } : {}),
|
|
90
|
-
})),
|
|
91
|
-
});
|
|
92
83
|
/** A `gaia_rich` body as the physical rows a surface draws or prints — D1. */
|
|
93
84
|
export function markdownLines(body) {
|
|
94
85
|
return parseMarkdown(body).map(styledLineOf);
|
|
@@ -146,3 +137,68 @@ export function markdownRow(line) {
|
|
|
146
137
|
const md = /^(#{1,6})\s+(.*)$/.exec(line);
|
|
147
138
|
return md ? { text: md[2] ?? '', bold: true } : { text: line, bold: false };
|
|
148
139
|
}
|
|
140
|
+
/** Collapse blank runs to one blank row and trim blank rows at either end. */
|
|
141
|
+
const compactLines = (lines) => {
|
|
142
|
+
const compacted = [];
|
|
143
|
+
for (const line of lines) {
|
|
144
|
+
if (line.text === '' && compacted.at(-1)?.text === '')
|
|
145
|
+
continue;
|
|
146
|
+
compacted.push(line);
|
|
147
|
+
}
|
|
148
|
+
while (compacted.at(0)?.text === '')
|
|
149
|
+
compacted.shift();
|
|
150
|
+
while (compacted.at(-1)?.text === '')
|
|
151
|
+
compacted.pop();
|
|
152
|
+
return compacted;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* A row's runs as painted: a heading or an emphasised body is bold and
|
|
156
|
+
* highlighted, and a run's own colour (code) wins over both.
|
|
157
|
+
*/
|
|
158
|
+
const paintedRuns = (line, emphasis) => {
|
|
159
|
+
const strong = line.heading || emphasis;
|
|
160
|
+
return line.runs.map((run) => plainRun(run.text, run.attributes | (strong ? BOLD : 0), run.fg ?? (strong ? FOREGROUND_HIGHLIGHT : FOREGROUND)));
|
|
161
|
+
};
|
|
162
|
+
/** Styled rows as one column of wrapped paragraphs, blank rows kept one high. */
|
|
163
|
+
export const MarkdownLines = ({ id, lines, width, emphasis = false, }) => h(BoxRenderable, { id, flexDirection: 'column', width: width ?? '100%' },
|
|
164
|
+
// The rows of one body, in order: their position is their identity, and
|
|
165
|
+
// every row restates all of its props.
|
|
166
|
+
...compactLines(lines).map((line, index) => line.text === ''
|
|
167
|
+
? h(BoxRenderable, { id: `${id}-${index}`, height: 1 })
|
|
168
|
+
: paragraphNode({
|
|
169
|
+
id: `${id}-${index}`,
|
|
170
|
+
runs: paintedRuns(line, emphasis),
|
|
171
|
+
fg: FOREGROUND,
|
|
172
|
+
width: '100%',
|
|
173
|
+
flexShrink: 0,
|
|
174
|
+
})));
|
|
175
|
+
/** A Markdown source, parsed and painted. */
|
|
176
|
+
export const Markdown = ({ id, source, width }) => MarkdownLines({
|
|
177
|
+
id,
|
|
178
|
+
lines: markdownLines(source),
|
|
179
|
+
...(width === undefined ? {} : { width }),
|
|
180
|
+
});
|
|
181
|
+
/** A `* headline` row above an indented body. */
|
|
182
|
+
export const HeadlineMarkdown = ({ id, headline, children, paddingBottom = 1, paddingLeft = 1, headlineId, onMouseDown, }) => {
|
|
183
|
+
const row = headlineId ?? `${id}-headline`;
|
|
184
|
+
return h(BoxRenderable, {
|
|
185
|
+
id,
|
|
186
|
+
flexDirection: 'column',
|
|
187
|
+
width: '100%',
|
|
188
|
+
flexShrink: 0,
|
|
189
|
+
paddingBottom,
|
|
190
|
+
}, h(BoxRenderable, { id: row, flexDirection: 'row', flexShrink: 0 }, paragraphNode({
|
|
191
|
+
id: `${row}-text`,
|
|
192
|
+
runs: [plainRun(`* ${headline}`)],
|
|
193
|
+
fg: FOREGROUND,
|
|
194
|
+
width: '100%',
|
|
195
|
+
flexShrink: 0,
|
|
196
|
+
onMouseDown,
|
|
197
|
+
})), h(BoxRenderable, {
|
|
198
|
+
id: `${id}-body`,
|
|
199
|
+
flexDirection: 'column',
|
|
200
|
+
width: '100%',
|
|
201
|
+
flexShrink: 0,
|
|
202
|
+
paddingLeft,
|
|
203
|
+
}, ...children));
|
|
204
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { StatisticFigureGroup } from '../../../Statistics/client.js';
|
|
2
|
+
/**
|
|
3
|
+
* Local example data for the optional Statistics demo tab.
|
|
4
|
+
*
|
|
5
|
+
* Shaped as the server's configured-figure contract, so the demo paints through
|
|
6
|
+
* the same `StatisticsFigures` composition the production Stats screens use
|
|
7
|
+
* rather than through a separate set of demo-only widgets. No value here is
|
|
8
|
+
* read from or written to a control plane.
|
|
9
|
+
*/
|
|
10
|
+
export interface StatisticExample {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly groups: readonly StatisticFigureGroup[];
|
|
13
|
+
}
|
|
14
|
+
export declare const STATISTIC_EXAMPLES: readonly StatisticExample[];
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const DAYS = [
|
|
2
|
+
'2026-09-21',
|
|
3
|
+
'2026-09-22',
|
|
4
|
+
'2026-09-23',
|
|
5
|
+
'2026-09-24',
|
|
6
|
+
'2026-09-25',
|
|
7
|
+
'2026-09-26',
|
|
8
|
+
'2026-09-27',
|
|
9
|
+
];
|
|
10
|
+
const WEEKDAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
11
|
+
const STATES = ['Ready', 'Coding', 'Review', 'Done'];
|
|
12
|
+
const figure = (id, label, widget, categories, measurements) => {
|
|
13
|
+
const empty = measurements.every((measurement) => Object.values(measurement.values).every((value) => value === null));
|
|
14
|
+
return {
|
|
15
|
+
id,
|
|
16
|
+
label,
|
|
17
|
+
description: '',
|
|
18
|
+
widget,
|
|
19
|
+
generated: '2026-09-27T00:00:00Z',
|
|
20
|
+
filters: {},
|
|
21
|
+
categories,
|
|
22
|
+
measurements: measurements.map((measurement) => ({
|
|
23
|
+
value_type: 'integer',
|
|
24
|
+
unit: 'count',
|
|
25
|
+
...measurement,
|
|
26
|
+
})),
|
|
27
|
+
empty: {
|
|
28
|
+
is_empty: empty,
|
|
29
|
+
message: empty ? 'No example values for this scenario.' : null,
|
|
30
|
+
},
|
|
31
|
+
cache: { max_age: 0, contexts: [], tags: [] },
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
const example = (name, states, trend) => {
|
|
35
|
+
const value = (values, index) => values[index] ?? null;
|
|
36
|
+
return {
|
|
37
|
+
name,
|
|
38
|
+
groups: [
|
|
39
|
+
{
|
|
40
|
+
id: 'demo',
|
|
41
|
+
label: 'Example statistics',
|
|
42
|
+
figures: [
|
|
43
|
+
figure('demo-states', 'Tickets by state', 'matrix', STATES.map((state, index) => ({
|
|
44
|
+
key: `state-${index}`,
|
|
45
|
+
label: state,
|
|
46
|
+
})), [
|
|
47
|
+
{
|
|
48
|
+
id: 'tickets',
|
|
49
|
+
label: 'Tickets',
|
|
50
|
+
values: Object.fromEntries(STATES.map((_, index) => [
|
|
51
|
+
`state-${index}`,
|
|
52
|
+
value(states, index),
|
|
53
|
+
])),
|
|
54
|
+
},
|
|
55
|
+
]),
|
|
56
|
+
figure('demo-share', 'Share by state', 'grouped_horizontal_bars', STATES.map((state, index) => ({
|
|
57
|
+
key: `share-${index}`,
|
|
58
|
+
label: state,
|
|
59
|
+
})), [
|
|
60
|
+
{
|
|
61
|
+
id: 'share',
|
|
62
|
+
label: 'Tickets',
|
|
63
|
+
values: Object.fromEntries(STATES.map((_, index) => [
|
|
64
|
+
`share-${index}`,
|
|
65
|
+
value(states, index),
|
|
66
|
+
])),
|
|
67
|
+
},
|
|
68
|
+
]),
|
|
69
|
+
figure('demo-completed', 'Completed per day', 'line', DAYS.map((day, index) => ({
|
|
70
|
+
key: day,
|
|
71
|
+
label: WEEKDAYS[index] ?? day,
|
|
72
|
+
})), [
|
|
73
|
+
{
|
|
74
|
+
id: 'completed',
|
|
75
|
+
label: 'Completed',
|
|
76
|
+
values: Object.fromEntries(DAYS.map((day, index) => [day, value(trend, index)])),
|
|
77
|
+
},
|
|
78
|
+
]),
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
export const STATISTIC_EXAMPLES = [
|
|
85
|
+
example('Steady week', [12, 7, 4, 26], [3, 5, 4, 8, 6, 9, 11]),
|
|
86
|
+
example('Busy week', [5, 14, 8, 42], [5, 8, 6, 13, 9, 15, 18]),
|
|
87
|
+
example('Empty project', [], []),
|
|
88
|
+
];
|
|
@@ -1,22 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
readonly
|
|
5
|
-
readonly
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
readonly
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
readonly states: readonly [];
|
|
16
|
-
readonly trend: readonly [];
|
|
17
|
-
readonly progress: 0;
|
|
18
|
-
readonly budget: 0;
|
|
19
|
-
}];
|
|
20
|
-
export declare function ReactStatistics({ scenario }: {
|
|
21
|
-
scenario: number;
|
|
22
|
-
}): import("react").ReactNode;
|
|
1
|
+
import { type VNode } from '@opentui/core';
|
|
2
|
+
export { STATISTIC_EXAMPLES } from './demo-statistics.js';
|
|
3
|
+
export interface DemoStatisticsProps {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly scenario: number;
|
|
6
|
+
/** The figure grid's last measured width, and where a new one is reported. */
|
|
7
|
+
readonly width: number;
|
|
8
|
+
readonly onResize: (width: number) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The demo's chart area: a labelled local scenario painted through the same
|
|
12
|
+
* `StatisticsFigures` the production Stats screens use.
|
|
13
|
+
*/
|
|
14
|
+
export declare function DemoStatistics({ id, scenario, width, onResize, }: DemoStatisticsProps): VNode;
|