@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.
Files changed (220) hide show
  1. package/dist/src/Component/badge/index.d.ts +11 -0
  2. package/dist/src/Component/badge/index.js +10 -0
  3. package/dist/src/Component/bar-chart/index.d.ts +16 -0
  4. package/dist/src/Component/bar-chart/index.js +63 -0
  5. package/dist/src/Component/card/index.d.ts +16 -0
  6. package/dist/src/Component/card/index.js +39 -0
  7. package/dist/src/Component/divider/index.d.ts +7 -0
  8. package/dist/src/Component/divider/index.js +13 -0
  9. package/dist/src/Component/heat-map/index.d.ts +21 -0
  10. package/dist/src/Component/heat-map/index.js +86 -0
  11. package/dist/src/Component/key-value/index.d.ts +18 -0
  12. package/dist/src/Component/key-value/index.js +30 -0
  13. package/dist/src/Component/markdown/index.d.ts +35 -5
  14. package/dist/src/Component/markdown/index.js +72 -16
  15. package/dist/src/Entity/team/Data/workload.d.ts +3 -1
  16. package/dist/src/Entity/team/Data/workload.js +6 -4
  17. package/dist/src/Entity/team/Screen/team-work.d.ts +12 -1
  18. package/dist/src/Entity/team/Screen/team-work.js +47 -2
  19. package/dist/src/Entity/ticket/Component/demo-statistics.d.ts +14 -0
  20. package/dist/src/Entity/ticket/Component/demo-statistics.js +88 -0
  21. package/dist/src/Entity/ticket/Component/react-statistics.d.ts +14 -22
  22. package/dist/src/Entity/ticket/Component/react-statistics.js +21 -42
  23. package/dist/src/Entity/ticket/Component/ticket-content/index.d.ts +28 -3
  24. package/dist/src/Entity/ticket/Component/ticket-content/index.js +331 -157
  25. package/dist/src/Entity/ticket/Data/parents.js +1 -1
  26. package/dist/src/Entity/ticket/Data/query.d.ts +2 -2
  27. package/dist/src/Entity/ticket/Data/query.js +13 -9
  28. package/dist/src/Entity/ticket/Data/store.js +1 -1
  29. package/dist/src/Entity/ticket/Data/tickets.d.ts +1 -1
  30. package/dist/src/Entity/ticket/Data/tickets.js +1 -1
  31. package/dist/src/Entity/ticket/Form/edit.d.ts +13 -0
  32. package/dist/src/Entity/ticket/Form/edit.js +17 -7
  33. package/dist/src/Entity/ticket/Screen/conductor-tickets.d.ts +7 -1
  34. package/dist/src/Entity/ticket/Screen/conductor-tickets.js +30 -6
  35. package/dist/src/Entity/ticket/Screen/edit-assignment-form.d.ts +3 -1
  36. package/dist/src/Entity/ticket/Screen/edit-assignment-form.js +21 -6
  37. package/dist/src/Entity/ticket/Screen/my-work.d.ts +8 -1
  38. package/dist/src/Entity/ticket/Screen/my-work.js +37 -4
  39. package/dist/src/Entity/ticket/Screen/ticket-comments.d.ts +6 -12
  40. package/dist/src/Entity/ticket/Screen/ticket-comments.js +11 -16
  41. package/dist/src/Entity/ticket/Screen/ticket-content-screen.d.ts +25 -44
  42. package/dist/src/Entity/ticket/Screen/ticket-content-screen.js +79 -87
  43. package/dist/src/Entity/ticket/Screen/ticket-detail.d.ts +6 -11
  44. package/dist/src/Entity/ticket/Screen/ticket-detail.js +14 -19
  45. package/dist/src/Entity/ticket/Screen/ticket-filter-bar.js +2 -9
  46. package/dist/src/Entity/ticket/Screen/ticket-planning.d.ts +6 -12
  47. package/dist/src/Entity/ticket/Screen/ticket-planning.js +11 -16
  48. package/dist/src/Entity/ticket/Screen/ticket-react-markdown.d.ts +9 -7
  49. package/dist/src/Entity/ticket/Screen/ticket-react-markdown.js +36 -13
  50. package/dist/src/Entity/ticket/Screen/ticket-react.d.ts +21 -8
  51. package/dist/src/Entity/ticket/Screen/ticket-react.js +109 -34
  52. package/dist/src/Entity/ticket/Screen/ticket-search.d.ts +44 -0
  53. package/dist/src/Entity/ticket/Screen/ticket-search.js +97 -0
  54. package/dist/src/Entity/ticket/Screen/tickets.d.ts +9 -7
  55. package/dist/src/Entity/ticket/Screen/tickets.js +25 -85
  56. package/dist/src/Entity/ticket/Screen/user-tickets.d.ts +7 -0
  57. package/dist/src/Entity/ticket/Screen/user-tickets.js +30 -2
  58. package/dist/src/Kernel/renderer.d.ts +1 -3
  59. package/dist/src/Kernel/renderer.js +2 -5
  60. package/dist/src/Kernel/tui-kernel.js +7 -16
  61. package/dist/src/Screen/async-screen.d.ts +13 -0
  62. package/dist/src/Screen/async-screen.js +34 -5
  63. package/dist/src/Screen/list-screen.d.ts +11 -2
  64. package/dist/src/Screen/list-screen.js +20 -2
  65. package/dist/src/Screen/screen.d.ts +6 -2
  66. package/dist/src/Statistics/comparison-chart.d.ts +4 -2
  67. package/dist/src/Statistics/comparison-chart.js +14 -6
  68. package/dist/src/Statistics/figures.d.ts +17 -3
  69. package/dist/src/Statistics/figures.js +104 -41
  70. package/dist/src/Statistics/presentation.d.ts +3 -5
  71. package/dist/src/Statistics/presentation.js +3 -5
  72. package/dist/src/Statistics/screen.d.ts +16 -5
  73. package/dist/src/Statistics/screen.js +59 -26
  74. package/dist/src/lib/theme.d.ts +0 -56
  75. package/dist/src/lib/theme.js +0 -42
  76. package/dist/src/lib/vnode.d.ts +29 -0
  77. package/dist/src/lib/vnode.js +69 -0
  78. package/dist/src/route.js +37 -0
  79. package/dist/src/types.d.ts +5 -1
  80. package/dist/src/ui.d.ts +0 -1
  81. package/dist/src/ui.js +0 -1
  82. package/package.json +2 -2
  83. package/dist/src/Agent/Component/agent-list/index.d.ts +0 -6
  84. package/dist/src/Agent/Component/agent-list/index.js +0 -14
  85. package/dist/src/Component/agent-list/index.d.ts +0 -6
  86. package/dist/src/Component/agent-list/index.js +0 -14
  87. package/dist/src/Component/comment-item/index.d.ts +0 -21
  88. package/dist/src/Component/comment-item/index.js +0 -39
  89. package/dist/src/Component/create-form/index.d.ts +0 -3
  90. package/dist/src/Component/create-form/index.js +0 -26
  91. package/dist/src/Component/dashboard-list/index.d.ts +0 -8
  92. package/dist/src/Component/dashboard-list/index.js +0 -7
  93. package/dist/src/Component/project-picker/index.d.ts +0 -15
  94. package/dist/src/Component/project-picker/index.js +0 -42
  95. package/dist/src/Component/run-table/index.d.ts +0 -10
  96. package/dist/src/Component/run-table/index.js +0 -20
  97. package/dist/src/Component/tab-bar/index.d.ts +0 -20
  98. package/dist/src/Component/tab-bar/index.js +0 -42
  99. package/dist/src/Component/ticket-detail/index.d.ts +0 -38
  100. package/dist/src/Component/ticket-detail/index.js +0 -193
  101. package/dist/src/Component/ticket-table/index.d.ts +0 -5
  102. package/dist/src/Component/ticket-table/index.js +0 -14
  103. package/dist/src/Component/ticket-teaser/index.d.ts +0 -7
  104. package/dist/src/Component/ticket-teaser/index.js +0 -76
  105. package/dist/src/Deriver/liveness.d.ts +0 -12
  106. package/dist/src/Deriver/liveness.js +0 -34
  107. package/dist/src/Deriver/run-status.d.ts +0 -12
  108. package/dist/src/Deriver/run-status.js +0 -36
  109. package/dist/src/Entity/conductor/Component/conductor-list/index.d.ts +0 -25
  110. package/dist/src/Entity/conductor/Component/conductor-list/index.js +0 -60
  111. package/dist/src/Entity/project/Component/dashboard-list/index.d.ts +0 -8
  112. package/dist/src/Entity/project/Component/dashboard-list/index.js +0 -7
  113. package/dist/src/Entity/project/Component/project-list/index.d.ts +0 -8
  114. package/dist/src/Entity/project/Component/project-list/index.js +0 -7
  115. package/dist/src/Entity/project/Component/project-picker/index.d.ts +0 -15
  116. package/dist/src/Entity/project/Component/project-picker/index.js +0 -42
  117. package/dist/src/Entity/project/Screen/project-work.d.ts +0 -33
  118. package/dist/src/Entity/project/Screen/project-work.js +0 -114
  119. package/dist/src/Entity/run/Component/run-table/index.d.ts +0 -8
  120. package/dist/src/Entity/run/Component/run-table/index.js +0 -11
  121. package/dist/src/Entity/statistics/Data/statistics.d.ts +0 -147
  122. package/dist/src/Entity/statistics/Data/statistics.js +0 -235
  123. package/dist/src/Entity/statistics/Screen/dashboard.d.ts +0 -70
  124. package/dist/src/Entity/statistics/Screen/dashboard.js +0 -216
  125. package/dist/src/Entity/statistics/render.d.ts +0 -6
  126. package/dist/src/Entity/statistics/render.js +0 -71
  127. package/dist/src/Entity/ticket/Component/tab-bar/index.d.ts +0 -20
  128. package/dist/src/Entity/ticket/Component/tab-bar/index.js +0 -71
  129. package/dist/src/Entity/ticket/Component/ticket-table/index.d.ts +0 -7
  130. package/dist/src/Entity/ticket/Component/ticket-table/index.js +0 -10
  131. package/dist/src/Entity/user/Screen/user-overview.d.ts +0 -33
  132. package/dist/src/Entity/user/Screen/user-overview.js +0 -80
  133. package/dist/src/Extension/contract.d.ts +0 -204
  134. package/dist/src/Extension/contract.js +0 -1
  135. package/dist/src/Kernel/react-body.d.ts +0 -9
  136. package/dist/src/Kernel/react-body.js +0 -42
  137. package/dist/src/Screen/async-react-screen.d.ts +0 -45
  138. package/dist/src/Screen/async-react-screen.js +0 -120
  139. package/dist/src/Screen/dashboard.d.ts +0 -1
  140. package/dist/src/Screen/dashboard.js +0 -1
  141. package/dist/src/Screen/project.d.ts +0 -1
  142. package/dist/src/Screen/project.js +0 -1
  143. package/dist/src/Screen/projects.d.ts +0 -1
  144. package/dist/src/Screen/projects.js +0 -1
  145. package/dist/src/Screen/react-screen.d.ts +0 -14
  146. package/dist/src/Screen/react-screen.js +0 -34
  147. package/dist/src/Screen/ticket.d.ts +0 -1
  148. package/dist/src/Screen/ticket.js +0 -1
  149. package/dist/src/Service/agent.d.ts +0 -17
  150. package/dist/src/Service/agent.js +0 -28
  151. package/dist/src/Service/create-form-data.d.ts +0 -10
  152. package/dist/src/Service/create-form-data.js +0 -52
  153. package/dist/src/Service/create-form.d.ts +0 -52
  154. package/dist/src/Service/create-form.js +0 -98
  155. package/dist/src/Service/project-data.d.ts +0 -48
  156. package/dist/src/Service/project-data.js +0 -92
  157. package/dist/src/Service/projects.d.ts +0 -21
  158. package/dist/src/Service/projects.js +0 -80
  159. package/dist/src/Service/route.d.ts +0 -11
  160. package/dist/src/Service/route.js +0 -215
  161. package/dist/src/Service/run-data.d.ts +0 -35
  162. package/dist/src/Service/run-data.js +0 -62
  163. package/dist/src/Service/ticket-data.d.ts +0 -6
  164. package/dist/src/Service/ticket-data.js +0 -7
  165. package/dist/src/Tui/controller.d.ts +0 -13
  166. package/dist/src/Tui/controller.js +0 -1794
  167. package/dist/src/Vendor/termcn/hooks/use-motion.d.ts +0 -5
  168. package/dist/src/Vendor/termcn/hooks/use-motion.js +0 -7
  169. package/dist/src/Vendor/termcn/hooks/use-theme.d.ts +0 -5
  170. package/dist/src/Vendor/termcn/hooks/use-theme.js +0 -10
  171. package/dist/src/Vendor/termcn/hooks/use-unicode.d.ts +0 -5
  172. package/dist/src/Vendor/termcn/hooks/use-unicode.js +0 -34
  173. package/dist/src/Vendor/termcn/lib/dither-chart-utils.d.ts +0 -130
  174. package/dist/src/Vendor/termcn/lib/dither-chart-utils.js +0 -357
  175. package/dist/src/Vendor/termcn/lib/dither-line-chart-utils.d.ts +0 -2
  176. package/dist/src/Vendor/termcn/lib/dither-line-chart-utils.js +0 -72
  177. package/dist/src/Vendor/termcn/lib/dither-radar-chart-utils.d.ts +0 -2
  178. package/dist/src/Vendor/termcn/lib/dither-radar-chart-utils.js +0 -113
  179. package/dist/src/Vendor/termcn/themes/default.d.ts +0 -1
  180. package/dist/src/Vendor/termcn/themes/default.js +0 -3
  181. package/dist/src/Vendor/termcn/ui/badge.d.ts +0 -13
  182. package/dist/src/Vendor/termcn/ui/badge.js +0 -33
  183. package/dist/src/Vendor/termcn/ui/bar-chart.d.ts +0 -16
  184. package/dist/src/Vendor/termcn/ui/bar-chart.js +0 -61
  185. package/dist/src/Vendor/termcn/ui/card.d.ts +0 -18
  186. package/dist/src/Vendor/termcn/ui/card.js +0 -7
  187. package/dist/src/Vendor/termcn/ui/dither-chart.d.ts +0 -118
  188. package/dist/src/Vendor/termcn/ui/dither-chart.js +0 -418
  189. package/dist/src/Vendor/termcn/ui/dither-line-chart.d.ts +0 -10
  190. package/dist/src/Vendor/termcn/ui/dither-line-chart.js +0 -11
  191. package/dist/src/Vendor/termcn/ui/dither-radar-chart.d.ts +0 -21
  192. package/dist/src/Vendor/termcn/ui/dither-radar-chart.js +0 -11
  193. package/dist/src/Vendor/termcn/ui/divider.d.ts +0 -14
  194. package/dist/src/Vendor/termcn/ui/divider.js +0 -25
  195. package/dist/src/Vendor/termcn/ui/gauge.d.ts +0 -12
  196. package/dist/src/Vendor/termcn/ui/gauge.js +0 -48
  197. package/dist/src/Vendor/termcn/ui/heat-map.d.ts +0 -13
  198. package/dist/src/Vendor/termcn/ui/heat-map.js +0 -59
  199. package/dist/src/Vendor/termcn/ui/key-value.d.ts +0 -16
  200. package/dist/src/Vendor/termcn/ui/key-value.js +0 -24
  201. package/dist/src/Vendor/termcn/ui/line-chart.d.ts +0 -15
  202. package/dist/src/Vendor/termcn/ui/line-chart.js +0 -102
  203. package/dist/src/Vendor/termcn/ui/markdown.d.ts +0 -24
  204. package/dist/src/Vendor/termcn/ui/markdown.js +0 -39
  205. package/dist/src/Vendor/termcn/ui/progress-bar.d.ts +0 -13
  206. package/dist/src/Vendor/termcn/ui/progress-bar.js +0 -13
  207. package/dist/src/Vendor/termcn/ui/sparkline.d.ts +0 -8
  208. package/dist/src/Vendor/termcn/ui/sparkline.js +0 -30
  209. package/dist/src/Vendor/termcn/ui/types.d.ts +0 -82
  210. package/dist/src/Vendor/termcn/ui/types.js +0 -1
  211. package/dist/src/lib/ansi.d.ts +0 -34
  212. package/dist/src/lib/ansi.js +0 -97
  213. package/dist/src/lib/hit-test.d.ts +0 -138
  214. package/dist/src/lib/hit-test.js +0 -138
  215. package/dist/src/lib/mouse.d.ts +0 -35
  216. package/dist/src/lib/mouse.js +0 -89
  217. package/dist/src/lib/table.d.ts +0 -37
  218. package/dist/src/lib/table.js +0 -20
  219. package/dist/src/lib/ticket-query.d.ts +0 -21
  220. 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,7 @@
1
+ import { type RGBA, type VNode } from '@opentui/core';
2
+ export interface DividerProps {
3
+ id: string;
4
+ color?: RGBA;
5
+ }
6
+ /** The horizontal, unlabelled rule — the only shape the TUI draws. */
7
+ export declare const Divider: ({ id, color }: DividerProps) => VNode;
@@ -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
+ };
@@ -20,4 +20,6 @@ export interface TeamWorkloadResult {
20
20
  waiting: number;
21
21
  };
22
22
  }
23
- export declare function loadTeamWorkload(client: DropshClient, teamId: string): Promise<TeamWorkloadResult>;
23
+ export declare function loadTeamWorkload(client: DropshClient, teamId: string,
24
+ /** The `/` line: the workload counts only the tickets it finds. */
25
+ query?: string): Promise<TeamWorkloadResult>;
@@ -4,11 +4,11 @@ import { classifyWorkload } from '../../ticket/Data/workload.js';
4
4
  import { listTeamMemberIds } from './members.js';
5
5
  import { listLinkedProjectIds } from './projects.js';
6
6
  import { resolveTeam } from './team.js';
7
- async function listProjectTickets(client, projectId) {
7
+ async function listProjectTickets(client, projectId, query) {
8
8
  const rows = [];
9
9
  let pageIndex = 0;
10
10
  while (true) {
11
- const page = await loadFilteredTickets(client, projectId, DEFAULT_TICKET_CRITERIA, pageIndex);
11
+ const page = await loadFilteredTickets(client, projectId, { ...DEFAULT_TICKET_CRITERIA, query }, pageIndex);
12
12
  if (page.withheld) {
13
13
  throw new Error('the control plane withheld project ticket rows');
14
14
  }
@@ -18,7 +18,9 @@ async function listProjectTickets(client, projectId) {
18
18
  pageIndex++;
19
19
  }
20
20
  }
21
- export async function loadTeamWorkload(client, teamId) {
21
+ export async function loadTeamWorkload(client, teamId,
22
+ /** The `/` line: the workload counts only the tickets it finds. */
23
+ query = '') {
22
24
  const [team, membership, projectIds] = await Promise.all([
23
25
  resolveTeam(client, teamId),
24
26
  listTeamMemberIds(client, teamId),
@@ -27,7 +29,7 @@ export async function loadTeamWorkload(client, teamId) {
27
29
  if (membership.ids === null) {
28
30
  throw new Error(membership.error ?? 'The team membership is unavailable.');
29
31
  }
30
- const tickets = (await Promise.all(projectIds.map((projectId) => listProjectTickets(client, projectId)))).flat();
32
+ const tickets = (await Promise.all(projectIds.map((projectId) => listProjectTickets(client, projectId, query)))).flat();
31
33
  const memberSet = new Set(membership.ids);
32
34
  const assigned = tickets.filter((ticket) => ticket.assigneeId && memberSet.has(ticket.assigneeId));
33
35
  const members = membership.ids.map((userId) => {
@@ -1,6 +1,6 @@
1
1
  import type { VNode } from '@opentui/core';
2
2
  import { AsyncScreen } from '../../../Screen/async-screen.js';
3
- import type { FullScreenChrome } from '../../../Screen/full-screen.js';
3
+ import type { FullScreenChrome, ListFilterBadge } from '../../../Screen/full-screen.js';
4
4
  import type { ScreenContext } from '../../../Screen/screen.js';
5
5
  import { type TeamWorkloadResult } from '../Data/workload.js';
6
6
  export declare class TeamWorkScreen extends AsyncScreen<TeamWorkloadResult> {
@@ -19,10 +19,21 @@ export declare class TeamWorkScreen extends AsyncScreen<TeamWorkloadResult> {
19
19
  };
20
20
  };
21
21
  readonly id: string;
22
+ searchQuery: string;
23
+ private readonly search;
24
+ /** No ticket rows here, so only a refusal lands: nothing was found, and the
25
+ * counts say so instead of standing over the previous search. */
26
+ showRows(): void;
27
+ reloadSearch(context: ScreenContext): Promise<boolean>;
22
28
  reload(context: ScreenContext): void;
23
29
  protected load(context: ScreenContext): Promise<TeamWorkloadResult>;
24
30
  protected renderLoading(): string[];
25
31
  protected renderReady(result: TeamWorkloadResult): VNode;
26
32
  protected chrome(context: ScreenContext): FullScreenChrome;
33
+ protected badges(): readonly ListFilterBadge[];
34
+ protected notice(): string | undefined;
35
+ handleInput(input: string, context: ScreenContext): boolean;
36
+ leave(): void;
37
+ reset(): void;
27
38
  protected actions(): import("../../../Component/footer-actions/index.js").ActionItem[];
28
39
  }
@@ -3,6 +3,7 @@ import { sectionRowNode, sectionRows, } from '../../../Component/section-list/in
3
3
  import { workloadSummaryRows } from '../../../Component/workload-summary/index.js';
4
4
  import { columnNode } from '../../../lib/vnode.js';
5
5
  import { AsyncScreen } from '../../../Screen/async-screen.js';
6
+ import { TicketSearch } from '../../ticket/Screen/ticket-search.js';
6
7
  import { loadTeamWorkload } from '../Data/workload.js';
7
8
  export class TeamWorkScreen extends AsyncScreen {
8
9
  frameId = 'team-work';
@@ -13,6 +14,24 @@ export class TeamWorkScreen extends AsyncScreen {
13
14
  fallback: { routeId: 'teams' },
14
15
  };
15
16
  id = TeamWorkScreen.route.id;
17
+ searchQuery = '';
18
+ search = new TicketSearch(this);
19
+ /** No ticket rows here, so only a refusal lands: nothing was found, and the
20
+ * counts say so instead of standing over the previous search. */
21
+ showRows() {
22
+ const current = this.readyValue;
23
+ if (!current)
24
+ return;
25
+ const none = { open: 0, running: 0, waiting: 0 };
26
+ this.replaceReady({
27
+ ...current,
28
+ members: current.members.map((member) => ({ ...member, ...none })),
29
+ total: none,
30
+ });
31
+ }
32
+ reloadSearch(context) {
33
+ return this.reloadInPlace(context);
34
+ }
16
35
  reload(context) {
17
36
  this.retry(context);
18
37
  }
@@ -22,7 +41,7 @@ export class TeamWorkScreen extends AsyncScreen {
22
41
  const teamId = context.params?.teamId;
23
42
  if (!teamId)
24
43
  throw new Error('Team Work requires teamId');
25
- return loadTeamWorkload(context.services.client, teamId);
44
+ return loadTeamWorkload(context.services.client, teamId, this.searchQuery);
26
45
  }
27
46
  renderLoading() {
28
47
  return ['Loading team work…'];
@@ -58,9 +77,35 @@ export class TeamWorkScreen extends AsyncScreen {
58
77
  'Team';
59
78
  return {
60
79
  title: `${teamName} · work`,
61
- meta: `${this.readyValue?.team.projectCount ?? 0} project(s) · ${context.services?.baseUrl ?? ''}`,
80
+ meta: [
81
+ `${this.readyValue?.team.projectCount ?? 0} project(s)`,
82
+ context.services?.baseUrl ?? '',
83
+ this.search.hint(),
84
+ ]
85
+ .filter(Boolean)
86
+ .join(' · '),
62
87
  };
63
88
  }
89
+ badges() {
90
+ return [this.search.badge()];
91
+ }
92
+ notice() {
93
+ return this.search.status || undefined;
94
+ }
95
+ handleInput(input, context) {
96
+ if (super.handleInput(input, context))
97
+ return true;
98
+ return this.search.handleKey(input, context);
99
+ }
100
+ leave() {
101
+ this.search.close();
102
+ super.leave();
103
+ }
104
+ reset() {
105
+ this.searchQuery = '';
106
+ this.search.reset();
107
+ super.reset();
108
+ }
64
109
  actions() {
65
110
  return [actionItem('team-work-tab', 'Tab', 'tabs')];
66
111
  }