@flyingrobots/bijou-tui 0.5.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,14 +4,11 @@ TEA runtime for terminal UIs — model/update/view with physics-based animation,
4
4
 
5
5
  Inspired by [Bubble Tea](https://github.com/charmbracelet/bubbletea) (Go) and [GSAP](https://gsap.com/) animation.
6
6
 
7
- ## What's New in 0.4.0?
7
+ ## What's New in 0.6.0?
8
8
 
9
- - **`composite()`** — ANSI-safe overlay compositing with dim background support
10
- - **`modal()`** — centered dialog overlay with title, body, hint, and auto-centering
11
- - **`toast()`** — anchored notification overlay with success/error/info variants
12
- - **`pager()`** — scrollable content viewer with status line and convenience keymap
13
- - **`interactiveAccordion()`** — navigable accordion with focus tracking and expand/collapse
14
- - **`createPanelGroup()`** — multi-pane focus management with hotkey switching
9
+ - **`navigableTable()`** — keyboard-navigable table with focus management, vertical scrolling, and vim-style keybindings
10
+ - **`browsableList()`** — navigable list with focus tracking, scroll viewport, descriptions, and convenience keymap
11
+ - **`filePicker()`** — directory browser with focus navigation, extension filtering, and `IOPort` integration
15
12
 
16
13
  See the [CHANGELOG](https://github.com/flyingrobots/bijou/blob/main/docs/CHANGELOG.md) for the full release history.
17
14
 
@@ -286,6 +283,51 @@ const output = composite(backgroundView, [dialog, notification], { dim: true });
286
283
 
287
284
  Each overlay is a `{ content, row, col }` object. `composite()` splices them onto the background using painter's algorithm (last overlay wins on overlap). The `dim` option fades the background with ANSI dim.
288
285
 
286
+ ## Building Blocks
287
+
288
+ Reusable stateful components that follow the TEA state + pure transformers + sync render + convenience keymap pattern:
289
+
290
+ ### Navigable Table
291
+
292
+ ```typescript
293
+ import {
294
+ createNavigableTableState, navigableTable, navTableFocusNext,
295
+ navTableKeyMap, helpShort,
296
+ } from '@flyingrobots/bijou-tui';
297
+
298
+ const state = createNavigableTableState({ columns, rows, height: 10 });
299
+ const output = navigableTable(state, { ctx });
300
+ const next = navTableFocusNext(state);
301
+ ```
302
+
303
+ ### Browsable List
304
+
305
+ ```typescript
306
+ import {
307
+ createBrowsableListState, browsableList, listFocusNext,
308
+ browsableListKeyMap,
309
+ } from '@flyingrobots/bijou-tui';
310
+
311
+ const state = createBrowsableListState({ items, height: 10 });
312
+ const output = browsableList(state);
313
+ ```
314
+
315
+ ### File Picker
316
+
317
+ ```typescript
318
+ import {
319
+ createFilePickerState, filePicker, fpFocusNext, fpEnter, fpBack,
320
+ filePickerKeyMap,
321
+ } from '@flyingrobots/bijou-tui';
322
+ import { nodeIO } from '@flyingrobots/bijou-node';
323
+
324
+ const io = nodeIO();
325
+ const state = createFilePickerState({ cwd: process.cwd(), io, height: 15 });
326
+ const output = filePicker(state);
327
+ ```
328
+
329
+ All building blocks include `*KeyMap()` factories for preconfigured vim-style keybindings.
330
+
289
331
  ## Related Packages
290
332
 
291
333
  - [`@flyingrobots/bijou`](https://www.npmjs.com/package/@flyingrobots/bijou) — Zero-dependency core with all components and theme engine
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Browsable list building block — a scrollable, navigable list with focus tracking.
3
+ *
4
+ * Provides state transformers for focus and page navigation, a pure render
5
+ * function with viewport clipping, and a convenience keymap for vim-style
6
+ * navigation.
7
+ *
8
+ * ```ts
9
+ * // In TEA init:
10
+ * const listState = createBrowsableListState({ items, height: 10 });
11
+ *
12
+ * // In TEA view:
13
+ * const output = browsableList(model.listState);
14
+ *
15
+ * // In TEA update:
16
+ * case 'focus-next':
17
+ * return [{ ...model, listState: listFocusNext(model.listState) }, []];
18
+ * case 'select':
19
+ * const selected = model.listState.items[model.listState.focusIndex];
20
+ * // handle selection...
21
+ * ```
22
+ */
23
+ import type { BijouContext } from '@flyingrobots/bijou';
24
+ import { type KeyMap } from './keybindings.js';
25
+ export interface BrowsableListItem<T = string> {
26
+ label: string;
27
+ value: T;
28
+ description?: string;
29
+ }
30
+ export interface BrowsableListState<T = string> {
31
+ readonly items: readonly BrowsableListItem<T>[];
32
+ readonly focusIndex: number;
33
+ readonly scrollY: number;
34
+ readonly height: number;
35
+ }
36
+ export interface BrowsableListOptions<T = string> {
37
+ readonly items: readonly BrowsableListItem<T>[];
38
+ readonly height?: number;
39
+ }
40
+ export interface BrowsableListRenderOptions {
41
+ readonly focusIndicator?: string;
42
+ readonly ctx?: BijouContext;
43
+ }
44
+ /**
45
+ * Create initial browsable list state from items and optional height.
46
+ * Focus starts at index 0 with scroll at the top.
47
+ */
48
+ export declare function createBrowsableListState<T = string>(options: BrowsableListOptions<T>): BrowsableListState<T>;
49
+ /** Move focus to the next item (wraps around). */
50
+ export declare function listFocusNext<T>(state: BrowsableListState<T>): BrowsableListState<T>;
51
+ /** Move focus to the previous item (wraps around). */
52
+ export declare function listFocusPrev<T>(state: BrowsableListState<T>): BrowsableListState<T>;
53
+ /** Move focus down by one page (clamps to last item). */
54
+ export declare function listPageDown<T>(state: BrowsableListState<T>): BrowsableListState<T>;
55
+ /** Move focus up by one page (clamps to first item). */
56
+ export declare function listPageUp<T>(state: BrowsableListState<T>): BrowsableListState<T>;
57
+ /**
58
+ * Render the browsable list — visible items within the viewport with a
59
+ * focus indicator on the currently focused item.
60
+ *
61
+ * Items with a `description` field render as `label — description`.
62
+ */
63
+ export declare function browsableList<T>(state: BrowsableListState<T>, options?: BrowsableListRenderOptions): string;
64
+ /**
65
+ * Create a preconfigured KeyMap for browsable list navigation.
66
+ *
67
+ * The caller provides their own message types for each action:
68
+ * ```ts
69
+ * const keys = browsableListKeyMap({
70
+ * focusNext: { type: 'next' },
71
+ * focusPrev: { type: 'prev' },
72
+ * pageDown: { type: 'page-down' },
73
+ * pageUp: { type: 'page-up' },
74
+ * select: { type: 'select' },
75
+ * quit: { type: 'quit' },
76
+ * });
77
+ * ```
78
+ */
79
+ export declare function browsableListKeyMap<Msg>(actions: {
80
+ focusNext: Msg;
81
+ focusPrev: Msg;
82
+ pageDown: Msg;
83
+ pageUp: Msg;
84
+ select: Msg;
85
+ quit: Msg;
86
+ }): KeyMap<Msg>;
87
+ //# sourceMappingURL=browsable-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browsable-list.d.ts","sourceRoot":"","sources":["../src/browsable-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAM7D,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,MAAM;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC;IACT,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,MAAM;IAC5C,QAAQ,CAAC,KAAK,EAAE,SAAS,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,GAAG,MAAM;IAC9C,QAAQ,CAAC,KAAK,EAAE,SAAS,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;IAChD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC;CAC7B;AAMD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,MAAM,EACjD,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC/B,kBAAkB,CAAC,CAAC,CAAC,CAQvB;AAMD,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAIpF;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAIpF;AAED,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAInF;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAIjF;AAqBD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC5B,OAAO,CAAC,EAAE,0BAA0B,GACnC,MAAM,CAkBR;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE;IAChD,SAAS,EAAE,GAAG,CAAC;IACf,SAAS,EAAE,GAAG,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,MAAM,EAAE,GAAG,CAAC;IACZ,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC;CACX,GAAG,MAAM,CAAC,GAAG,CAAC,CAed"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Browsable list building block — a scrollable, navigable list with focus tracking.
3
+ *
4
+ * Provides state transformers for focus and page navigation, a pure render
5
+ * function with viewport clipping, and a convenience keymap for vim-style
6
+ * navigation.
7
+ *
8
+ * ```ts
9
+ * // In TEA init:
10
+ * const listState = createBrowsableListState({ items, height: 10 });
11
+ *
12
+ * // In TEA view:
13
+ * const output = browsableList(model.listState);
14
+ *
15
+ * // In TEA update:
16
+ * case 'focus-next':
17
+ * return [{ ...model, listState: listFocusNext(model.listState) }, []];
18
+ * case 'select':
19
+ * const selected = model.listState.items[model.listState.focusIndex];
20
+ * // handle selection...
21
+ * ```
22
+ */
23
+ import { createKeyMap } from './keybindings.js';
24
+ // ---------------------------------------------------------------------------
25
+ // State creation
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Create initial browsable list state from items and optional height.
29
+ * Focus starts at index 0 with scroll at the top.
30
+ */
31
+ export function createBrowsableListState(options) {
32
+ const height = Math.max(1, options.height ?? 10);
33
+ return {
34
+ items: [...options.items],
35
+ focusIndex: 0,
36
+ scrollY: 0,
37
+ height,
38
+ };
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // State transformers
42
+ // ---------------------------------------------------------------------------
43
+ /** Move focus to the next item (wraps around). */
44
+ export function listFocusNext(state) {
45
+ if (state.items.length === 0)
46
+ return state;
47
+ const focusIndex = (state.focusIndex + 1) % state.items.length;
48
+ return { ...state, focusIndex, scrollY: adjustScroll(focusIndex, state.scrollY, state.height, state.items.length) };
49
+ }
50
+ /** Move focus to the previous item (wraps around). */
51
+ export function listFocusPrev(state) {
52
+ if (state.items.length === 0)
53
+ return state;
54
+ const focusIndex = (state.focusIndex - 1 + state.items.length) % state.items.length;
55
+ return { ...state, focusIndex, scrollY: adjustScroll(focusIndex, state.scrollY, state.height, state.items.length) };
56
+ }
57
+ /** Move focus down by one page (clamps to last item). */
58
+ export function listPageDown(state) {
59
+ if (state.items.length === 0)
60
+ return state;
61
+ const focusIndex = Math.min(state.focusIndex + state.height, state.items.length - 1);
62
+ return { ...state, focusIndex, scrollY: adjustScroll(focusIndex, state.scrollY, state.height, state.items.length) };
63
+ }
64
+ /** Move focus up by one page (clamps to first item). */
65
+ export function listPageUp(state) {
66
+ if (state.items.length === 0)
67
+ return state;
68
+ const focusIndex = Math.max(state.focusIndex - state.height, 0);
69
+ return { ...state, focusIndex, scrollY: adjustScroll(focusIndex, state.scrollY, state.height, state.items.length) };
70
+ }
71
+ // ---------------------------------------------------------------------------
72
+ // Scroll helper
73
+ // ---------------------------------------------------------------------------
74
+ function adjustScroll(focusIndex, scrollY, height, totalItems) {
75
+ let newScrollY = scrollY;
76
+ if (focusIndex < newScrollY) {
77
+ newScrollY = focusIndex;
78
+ }
79
+ else if (focusIndex >= newScrollY + height) {
80
+ newScrollY = focusIndex - height + 1;
81
+ }
82
+ const maxScroll = Math.max(0, totalItems - height);
83
+ return Math.min(newScrollY, maxScroll);
84
+ }
85
+ // ---------------------------------------------------------------------------
86
+ // Render
87
+ // ---------------------------------------------------------------------------
88
+ /**
89
+ * Render the browsable list — visible items within the viewport with a
90
+ * focus indicator on the currently focused item.
91
+ *
92
+ * Items with a `description` field render as `label — description`.
93
+ */
94
+ export function browsableList(state, options) {
95
+ if (state.items.length === 0)
96
+ return '';
97
+ const indicator = options?.focusIndicator ?? '\u25b8';
98
+ const pad = ' '.repeat(indicator.length);
99
+ const visibleItems = state.items.slice(state.scrollY, state.scrollY + state.height);
100
+ const lines = [];
101
+ for (let i = 0; i < visibleItems.length; i++) {
102
+ const item = visibleItems[i];
103
+ const globalIndex = state.scrollY + i;
104
+ const prefix = globalIndex === state.focusIndex ? indicator : pad;
105
+ const desc = item.description ? ` \u2014 ${item.description}` : '';
106
+ lines.push(`${prefix} ${item.label}${desc}`);
107
+ }
108
+ return lines.join('\n');
109
+ }
110
+ // ---------------------------------------------------------------------------
111
+ // Convenience keymap
112
+ // ---------------------------------------------------------------------------
113
+ /**
114
+ * Create a preconfigured KeyMap for browsable list navigation.
115
+ *
116
+ * The caller provides their own message types for each action:
117
+ * ```ts
118
+ * const keys = browsableListKeyMap({
119
+ * focusNext: { type: 'next' },
120
+ * focusPrev: { type: 'prev' },
121
+ * pageDown: { type: 'page-down' },
122
+ * pageUp: { type: 'page-up' },
123
+ * select: { type: 'select' },
124
+ * quit: { type: 'quit' },
125
+ * });
126
+ * ```
127
+ */
128
+ export function browsableListKeyMap(actions) {
129
+ return createKeyMap()
130
+ .group('Navigation', (g) => g
131
+ .bind('j', 'Next item', actions.focusNext)
132
+ .bind('down', 'Next item', actions.focusNext)
133
+ .bind('k', 'Previous item', actions.focusPrev)
134
+ .bind('up', 'Previous item', actions.focusPrev)
135
+ .bind('d', 'Page down', actions.pageDown)
136
+ .bind('pagedown', 'Page down', actions.pageDown)
137
+ .bind('u', 'Page up', actions.pageUp)
138
+ .bind('pageup', 'Page up', actions.pageUp))
139
+ .bind('enter', 'Select', actions.select)
140
+ .bind('q', 'Quit', actions.quit)
141
+ .bind('ctrl+c', 'Quit', actions.quit);
142
+ }
143
+ //# sourceMappingURL=browsable-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browsable-list.js","sourceRoot":"","sources":["../src/browsable-list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Command palette building block — a filterable, navigable action list.
3
+ *
4
+ * Provides state transformers for filtering, focus, and page navigation,
5
+ * a pure render function with viewport clipping, and a convenience keymap.
6
+ *
7
+ * ```ts
8
+ * // In TEA init:
9
+ * const cpState = createCommandPaletteState(items);
10
+ *
11
+ * // In TEA view:
12
+ * const output = commandPalette(model.cpState, { width: 60 });
13
+ *
14
+ * // In TEA update:
15
+ * case 'filter':
16
+ * return [{ ...model, cpState: cpFilter(model.cpState, msg.query) }, []];
17
+ * case 'select':
18
+ * const selected = cpSelectedItem(model.cpState);
19
+ * // handle selection...
20
+ * ```
21
+ */
22
+ import type { BijouContext } from '@flyingrobots/bijou';
23
+ import { type KeyMap } from './keybindings.js';
24
+ export interface CommandPaletteItem {
25
+ readonly id: string;
26
+ readonly label: string;
27
+ readonly description?: string;
28
+ readonly category?: string;
29
+ readonly shortcut?: string;
30
+ }
31
+ export interface CommandPaletteState {
32
+ readonly items: readonly CommandPaletteItem[];
33
+ readonly filteredItems: readonly CommandPaletteItem[];
34
+ readonly query: string;
35
+ readonly focusIndex: number;
36
+ readonly scrollY: number;
37
+ readonly height: number;
38
+ }
39
+ export interface CommandPaletteOptions {
40
+ readonly width: number;
41
+ readonly showCategory?: boolean;
42
+ readonly showShortcut?: boolean;
43
+ readonly ctx?: BijouContext;
44
+ }
45
+ /**
46
+ * Create initial command palette state from items and optional height.
47
+ * Focus starts at index 0 with empty query showing all items.
48
+ */
49
+ export declare function createCommandPaletteState(items: readonly CommandPaletteItem[], height?: number): CommandPaletteState;
50
+ /** Filter items by case-insensitive substring match. Resets focus to 0. */
51
+ export declare function cpFilter(state: CommandPaletteState, query: string): CommandPaletteState;
52
+ /** Move focus to the next item (wraps around on filteredItems). */
53
+ export declare function cpFocusNext(state: CommandPaletteState): CommandPaletteState;
54
+ /** Move focus to the previous item (wraps around on filteredItems). */
55
+ export declare function cpFocusPrev(state: CommandPaletteState): CommandPaletteState;
56
+ /** Move focus down by half a page (vim Ctrl+D convention, clamps to last item). */
57
+ export declare function cpPageDown(state: CommandPaletteState): CommandPaletteState;
58
+ /** Move focus up by half a page (vim Ctrl+U convention, clamps to first item). */
59
+ export declare function cpPageUp(state: CommandPaletteState): CommandPaletteState;
60
+ /** Get the currently focused item, or undefined if no items match. */
61
+ export declare function cpSelectedItem(state: CommandPaletteState): CommandPaletteItem | undefined;
62
+ /**
63
+ * Render the command palette — a search line followed by filtered items
64
+ * in the viewport with a focus indicator.
65
+ *
66
+ * Layout:
67
+ * - Line 1: `> {query}` search input
68
+ * - Lines 2+: filtered items in viewport
69
+ * - Each item: `[category] label description shortcut`
70
+ * - Focus indicator: `▸` on focused item
71
+ */
72
+ export declare function commandPalette(state: CommandPaletteState, options: CommandPaletteOptions): string;
73
+ /**
74
+ * Create a preconfigured KeyMap for command palette navigation.
75
+ *
76
+ * Bindings: Ctrl+N/Down (next), Ctrl+P/Up (prev), Ctrl+D/PageDown (page down),
77
+ * Ctrl+U/PageUp (page up), Enter (select), Escape (close).
78
+ *
79
+ * Ctrl+D and Ctrl+U follow vim half-page scroll conventions. In raw-mode TUIs
80
+ * these do not conflict with shell behavior (EOF / line-clear).
81
+ *
82
+ * ```ts
83
+ * const keys = commandPaletteKeyMap({
84
+ * focusNext: { type: 'cp-next' },
85
+ * focusPrev: { type: 'cp-prev' },
86
+ * pageDown: { type: 'cp-page-down' },
87
+ * pageUp: { type: 'cp-page-up' },
88
+ * select: { type: 'cp-select' },
89
+ * close: { type: 'cp-close' },
90
+ * });
91
+ * ```
92
+ */
93
+ export declare function commandPaletteKeyMap<Msg>(actions: {
94
+ focusNext: Msg;
95
+ focusPrev: Msg;
96
+ pageDown: Msg;
97
+ pageUp: Msg;
98
+ select: Msg;
99
+ close: Msg;
100
+ }): KeyMap<Msg>;
101
+ //# sourceMappingURL=command-palette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-palette.d.ts","sourceRoot":"","sources":["../src/command-palette.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAO7D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC9C,QAAQ,CAAC,aAAa,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC;CAC7B;AAMD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,MAAM,SAAK,GACV,mBAAmB,CAWrB;AAgBD,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAkBvF;AAaD,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,KAAK,EAAE,mBAAmB,GAAG,mBAAmB,CAI3E;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,KAAK,EAAE,mBAAmB,GAAG,mBAAmB,CAI3E;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,KAAK,EAAE,mBAAmB,GAAG,mBAAmB,CAK1E;AAED,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,mBAAmB,GAAG,mBAAmB,CAKxE;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,kBAAkB,GAAG,SAAS,CAEzF;AAMD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,qBAAqB,GAC7B,MAAM,CAiDR;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE;IACjD,SAAS,EAAE,GAAG,CAAC;IACf,SAAS,EAAE,GAAG,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,MAAM,EAAE,GAAG,CAAC;IACZ,MAAM,EAAE,GAAG,CAAC;IACZ,KAAK,EAAE,GAAG,CAAC;CACZ,GAAG,MAAM,CAAC,GAAG,CAAC,CAcd"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Command palette building block — a filterable, navigable action list.
3
+ *
4
+ * Provides state transformers for filtering, focus, and page navigation,
5
+ * a pure render function with viewport clipping, and a convenience keymap.
6
+ *
7
+ * ```ts
8
+ * // In TEA init:
9
+ * const cpState = createCommandPaletteState(items);
10
+ *
11
+ * // In TEA view:
12
+ * const output = commandPalette(model.cpState, { width: 60 });
13
+ *
14
+ * // In TEA update:
15
+ * case 'filter':
16
+ * return [{ ...model, cpState: cpFilter(model.cpState, msg.query) }, []];
17
+ * case 'select':
18
+ * const selected = cpSelectedItem(model.cpState);
19
+ * // handle selection...
20
+ * ```
21
+ */
22
+ import { createKeyMap } from './keybindings.js';
23
+ import { clipToWidth } from './viewport.js';
24
+ // ---------------------------------------------------------------------------
25
+ // State creation
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Create initial command palette state from items and optional height.
29
+ * Focus starts at index 0 with empty query showing all items.
30
+ */
31
+ export function createCommandPaletteState(items, height = 10) {
32
+ const h = Math.max(1, height);
33
+ const copied = [...items];
34
+ return {
35
+ items: copied,
36
+ filteredItems: copied,
37
+ query: '',
38
+ focusIndex: 0,
39
+ scrollY: 0,
40
+ height: h,
41
+ };
42
+ }
43
+ // ---------------------------------------------------------------------------
44
+ // Filtering
45
+ // ---------------------------------------------------------------------------
46
+ function matchesQuery(item, query) {
47
+ const q = query.toLowerCase();
48
+ if (item.label.toLowerCase().includes(q))
49
+ return true;
50
+ if (item.description?.toLowerCase().includes(q))
51
+ return true;
52
+ if (item.category?.toLowerCase().includes(q))
53
+ return true;
54
+ if (item.id.toLowerCase().includes(q))
55
+ return true;
56
+ if (item.shortcut?.toLowerCase().includes(q))
57
+ return true;
58
+ return false;
59
+ }
60
+ /** Filter items by case-insensitive substring match. Resets focus to 0. */
61
+ export function cpFilter(state, query) {
62
+ if (query === '') {
63
+ return {
64
+ ...state,
65
+ query: '',
66
+ filteredItems: [...state.items],
67
+ focusIndex: 0,
68
+ scrollY: 0,
69
+ };
70
+ }
71
+ const filtered = state.items.filter(item => matchesQuery(item, query));
72
+ return {
73
+ ...state,
74
+ query,
75
+ filteredItems: filtered,
76
+ focusIndex: 0,
77
+ scrollY: 0,
78
+ };
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Focus navigation
82
+ // ---------------------------------------------------------------------------
83
+ function adjustScroll(focusIndex, scrollY, height, total) {
84
+ let s = scrollY;
85
+ if (focusIndex < s)
86
+ s = focusIndex;
87
+ else if (focusIndex >= s + height)
88
+ s = focusIndex - height + 1;
89
+ return Math.min(s, Math.max(0, total - height));
90
+ }
91
+ /** Move focus to the next item (wraps around on filteredItems). */
92
+ export function cpFocusNext(state) {
93
+ if (state.filteredItems.length === 0)
94
+ return state;
95
+ const focusIndex = (state.focusIndex + 1) % state.filteredItems.length;
96
+ return { ...state, focusIndex, scrollY: adjustScroll(focusIndex, state.scrollY, state.height, state.filteredItems.length) };
97
+ }
98
+ /** Move focus to the previous item (wraps around on filteredItems). */
99
+ export function cpFocusPrev(state) {
100
+ if (state.filteredItems.length === 0)
101
+ return state;
102
+ const focusIndex = (state.focusIndex - 1 + state.filteredItems.length) % state.filteredItems.length;
103
+ return { ...state, focusIndex, scrollY: adjustScroll(focusIndex, state.scrollY, state.height, state.filteredItems.length) };
104
+ }
105
+ /** Move focus down by half a page (vim Ctrl+D convention, clamps to last item). */
106
+ export function cpPageDown(state) {
107
+ if (state.filteredItems.length === 0)
108
+ return state;
109
+ const half = Math.max(1, Math.floor(state.height / 2));
110
+ const focusIndex = Math.min(state.focusIndex + half, state.filteredItems.length - 1);
111
+ return { ...state, focusIndex, scrollY: adjustScroll(focusIndex, state.scrollY, state.height, state.filteredItems.length) };
112
+ }
113
+ /** Move focus up by half a page (vim Ctrl+U convention, clamps to first item). */
114
+ export function cpPageUp(state) {
115
+ if (state.filteredItems.length === 0)
116
+ return state;
117
+ const half = Math.max(1, Math.floor(state.height / 2));
118
+ const focusIndex = Math.max(state.focusIndex - half, 0);
119
+ return { ...state, focusIndex, scrollY: adjustScroll(focusIndex, state.scrollY, state.height, state.filteredItems.length) };
120
+ }
121
+ /** Get the currently focused item, or undefined if no items match. */
122
+ export function cpSelectedItem(state) {
123
+ return state.filteredItems[state.focusIndex];
124
+ }
125
+ // ---------------------------------------------------------------------------
126
+ // Render
127
+ // ---------------------------------------------------------------------------
128
+ /**
129
+ * Render the command palette — a search line followed by filtered items
130
+ * in the viewport with a focus indicator.
131
+ *
132
+ * Layout:
133
+ * - Line 1: `> {query}` search input
134
+ * - Lines 2+: filtered items in viewport
135
+ * - Each item: `[category] label description shortcut`
136
+ * - Focus indicator: `▸` on focused item
137
+ */
138
+ export function commandPalette(state, options) {
139
+ const { width, showCategory = true, showShortcut = true, ctx } = options;
140
+ const lines = [];
141
+ // Search line
142
+ const searchPrefix = '> ';
143
+ const queryLine = searchPrefix + state.query;
144
+ lines.push(clipToWidth(queryLine, width));
145
+ if (state.filteredItems.length === 0) {
146
+ lines.push(clipToWidth(' No matches', width));
147
+ return lines.join('\n');
148
+ }
149
+ // Visible items in viewport
150
+ const visible = state.filteredItems.slice(state.scrollY, state.scrollY + state.height);
151
+ const indicator = '\u25b8'; // ▸
152
+ const pad = ' ';
153
+ const muted = (text) => ctx ? ctx.style.styled(ctx.theme.theme.semantic.muted, text) : text;
154
+ for (let i = 0; i < visible.length; i++) {
155
+ const item = visible[i];
156
+ const globalIndex = state.scrollY + i;
157
+ const prefix = globalIndex === state.focusIndex ? indicator : pad;
158
+ const parts = [];
159
+ if (showCategory && item.category) {
160
+ parts.push(muted(`[${item.category}]`));
161
+ }
162
+ parts.push(item.label);
163
+ if (item.description) {
164
+ parts.push(muted(item.description));
165
+ }
166
+ if (showShortcut && item.shortcut) {
167
+ parts.push(muted(item.shortcut));
168
+ }
169
+ const content = parts.join(' ');
170
+ const line = `${prefix} ${content}`;
171
+ lines.push(clipToWidth(line, width));
172
+ }
173
+ return lines.join('\n');
174
+ }
175
+ // ---------------------------------------------------------------------------
176
+ // Convenience keymap
177
+ // ---------------------------------------------------------------------------
178
+ /**
179
+ * Create a preconfigured KeyMap for command palette navigation.
180
+ *
181
+ * Bindings: Ctrl+N/Down (next), Ctrl+P/Up (prev), Ctrl+D/PageDown (page down),
182
+ * Ctrl+U/PageUp (page up), Enter (select), Escape (close).
183
+ *
184
+ * Ctrl+D and Ctrl+U follow vim half-page scroll conventions. In raw-mode TUIs
185
+ * these do not conflict with shell behavior (EOF / line-clear).
186
+ *
187
+ * ```ts
188
+ * const keys = commandPaletteKeyMap({
189
+ * focusNext: { type: 'cp-next' },
190
+ * focusPrev: { type: 'cp-prev' },
191
+ * pageDown: { type: 'cp-page-down' },
192
+ * pageUp: { type: 'cp-page-up' },
193
+ * select: { type: 'cp-select' },
194
+ * close: { type: 'cp-close' },
195
+ * });
196
+ * ```
197
+ */
198
+ export function commandPaletteKeyMap(actions) {
199
+ return createKeyMap()
200
+ .group('Navigation', (g) => g
201
+ .bind('ctrl+n', 'Next item', actions.focusNext)
202
+ .bind('down', 'Next item', actions.focusNext)
203
+ .bind('ctrl+p', 'Previous item', actions.focusPrev)
204
+ .bind('up', 'Previous item', actions.focusPrev)
205
+ .bind('ctrl+d', 'Half page down', actions.pageDown)
206
+ .bind('pagedown', 'Half page down', actions.pageDown)
207
+ .bind('ctrl+u', 'Half page up', actions.pageUp)
208
+ .bind('pageup', 'Half page up', actions.pageUp))
209
+ .bind('enter', 'Select', actions.select)
210
+ .bind('escape', 'Close', actions.close);
211
+ }
212
+ //# sourceMappingURL=command-palette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-palette.js","sourceRoot":"","sources":["../src/command-palette.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,YAAY,EAAe,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AA8B5C,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAoC,EACpC,MAAM,GAAG,EAAE;IAEX,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IAC1B,OAAO;QACL,KAAK,EAAE,MAAM;QACb,aAAa,EAAE,MAAM;QACrB,KAAK,EAAE,EAAE;QACT,UAAU,EAAE,CAAC;QACb,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,CAAC;KACV,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,SAAS,YAAY,CAAC,IAAwB,EAAE,KAAa;IAC3D,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAC9B,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,IAAI,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,IAAI,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,IAAI,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAC,KAA0B,EAAE,KAAa;IAChE,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACjB,OAAO;YACL,GAAG,KAAK;YACR,KAAK,EAAE,EAAE;YACT,aAAa,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;YAC/B,UAAU,EAAE,CAAC;YACb,OAAO,EAAE,CAAC;SACX,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IACvE,OAAO;QACL,GAAG,KAAK;QACR,KAAK;QACL,aAAa,EAAE,QAAQ;QACvB,UAAU,EAAE,CAAC;QACb,OAAO,EAAE,CAAC;KACX,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,SAAS,YAAY,CAAC,UAAkB,EAAE,OAAe,EAAE,MAAc,EAAE,KAAa;IACtF,IAAI,CAAC,GAAG,OAAO,CAAC;IAChB,IAAI,UAAU,GAAG,CAAC;QAAE,CAAC,GAAG,UAAU,CAAC;SAC9B,IAAI,UAAU,IAAI,CAAC,GAAG,MAAM;QAAE,CAAC,GAAG,UAAU,GAAG,MAAM,GAAG,CAAC,CAAC;IAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,WAAW,CAAC,KAA0B;IACpD,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC;IACvE,OAAO,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9H,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,WAAW,CAAC,KAA0B;IACpD,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC;IACpG,OAAO,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9H,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,UAAU,CAAC,KAA0B;IACnD,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrF,OAAO,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9H,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,QAAQ,CAAC,KAA0B;IACjD,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9H,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,cAAc,CAAC,KAA0B;IACvD,OAAO,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,KAA0B,EAC1B,OAA8B;IAE9B,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,EAAE,YAAY,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IACzE,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,cAAc;IACd,MAAM,YAAY,GAAG,IAAI,CAAC;IAC1B,MAAM,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC;IAC7C,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IAE1C,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,4BAA4B;IAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAEvF,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,IAAI;IAChC,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAC7B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEtE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC;QACzB,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,WAAW,KAAK,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;QAElE,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,YAAY,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QAC1C,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEvB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;QACtC,CAAC;QAED,IAAI,YAAY,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,oBAAoB,CAAM,OAOzC;IACC,OAAO,YAAY,EAAO;SACvB,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;SAC1B,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC;SAC9C,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC;SAC5C,IAAI,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,CAAC,SAAS,CAAC;SAClD,IAAI,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,SAAS,CAAC;SAC9C,IAAI,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,CAAC,QAAQ,CAAC;SAClD,IAAI,CAAC,UAAU,EAAE,gBAAgB,EAAE,OAAO,CAAC,QAAQ,CAAC;SACpD,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC;SAC9C,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,CAChD;SACA,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC;SACvC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;AAC5C,CAAC"}