@flyingrobots/bijou-tui 0.3.0 → 0.4.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,9 +4,14 @@ 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.3.0?
7
+ ## What's New in 0.4.0?
8
8
 
9
- - Lock-step version bump with `@flyingrobots/bijou` v0.3.0 (DAG renderer)
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
10
15
 
11
16
  See the [CHANGELOG](https://github.com/flyingrobots/bijou/blob/main/docs/CHANGELOG.md) for the full release history.
12
17
 
@@ -250,6 +255,37 @@ stack.remove(modalId);
250
255
 
251
256
  `KeyMap` implements `InputHandler`, so it plugs directly into the input stack.
252
257
 
258
+ ## Overlay Compositing
259
+
260
+ Paint overlays (modals, toasts) on top of existing content:
261
+
262
+ ```typescript
263
+ import { composite, modal, toast } from '@flyingrobots/bijou-tui';
264
+
265
+ // Create a centered dialog
266
+ const dialog = modal({
267
+ title: 'Confirm',
268
+ body: 'Delete this item?',
269
+ hint: 'y/n',
270
+ screenWidth: 80,
271
+ screenHeight: 24,
272
+ });
273
+
274
+ // Create a toast notification
275
+ const notification = toast({
276
+ message: 'Saved successfully',
277
+ variant: 'success', // 'success' | 'error' | 'info'
278
+ anchor: 'bottom-right', // 'top-right' | 'bottom-right' | 'bottom-left' | 'top-left'
279
+ screenWidth: 80,
280
+ screenHeight: 24,
281
+ });
282
+
283
+ // Paint overlays onto background content
284
+ const output = composite(backgroundView, [dialog, notification], { dim: true });
285
+ ```
286
+
287
+ 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
+
253
289
  ## Related Packages
254
290
 
255
291
  - [`@flyingrobots/bijou`](https://www.npmjs.com/package/@flyingrobots/bijou) — Zero-dependency core with all components and theme engine
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Interactive accordion building block.
3
+ *
4
+ * Wraps the static `accordion()` from bijou core with focus management
5
+ * and expand/collapse state transformers.
6
+ *
7
+ * ```ts
8
+ * // In TEA init:
9
+ * const accState = createAccordionState(sections);
10
+ *
11
+ * // In TEA view:
12
+ * const output = interactiveAccordion(model.accordion);
13
+ *
14
+ * // In TEA update:
15
+ * case 'focus-next':
16
+ * return [{ ...model, accordion: focusNext(model.accordion) }, []];
17
+ * case 'toggle':
18
+ * return [{ ...model, accordion: toggleFocused(model.accordion) }, []];
19
+ * ```
20
+ */
21
+ import { type AccordionSection, type AccordionOptions, type BijouContext } from '@flyingrobots/bijou';
22
+ import { type KeyMap } from './keybindings.js';
23
+ export type { AccordionSection } from '@flyingrobots/bijou';
24
+ export interface AccordionState {
25
+ readonly sections: readonly AccordionSection[];
26
+ readonly focusIndex: number;
27
+ }
28
+ export interface InteractiveAccordionOptions {
29
+ /** Token for the expand/collapse indicator. */
30
+ readonly indicatorToken?: AccordionOptions['indicatorToken'];
31
+ /** Token for section titles. */
32
+ readonly titleToken?: AccordionOptions['titleToken'];
33
+ /** Character for the focus indicator. Default: '>' */
34
+ readonly focusChar?: string;
35
+ readonly ctx?: BijouContext;
36
+ }
37
+ /**
38
+ * Create initial accordion state from sections.
39
+ * Preserves existing `expanded` flags; focus starts at index 0.
40
+ */
41
+ export declare function createAccordionState(sections: AccordionSection[]): AccordionState;
42
+ /** Move focus to the next section (wraps around). */
43
+ export declare function focusNext(state: AccordionState): AccordionState;
44
+ /** Move focus to the previous section (wraps around). */
45
+ export declare function focusPrev(state: AccordionState): AccordionState;
46
+ /** Toggle the expanded state of the focused section. */
47
+ export declare function toggleFocused(state: AccordionState): AccordionState;
48
+ /** Expand all sections. */
49
+ export declare function expandAll(state: AccordionState): AccordionState;
50
+ /** Collapse all sections. */
51
+ export declare function collapseAll(state: AccordionState): AccordionState;
52
+ /**
53
+ * Render the interactive accordion with a focus indicator on the
54
+ * currently focused section.
55
+ *
56
+ * Delegates to the static `accordion()` for the actual rendering,
57
+ * then prepends a focus indicator to the focused section's line.
58
+ */
59
+ export declare function interactiveAccordion(state: AccordionState, options?: InteractiveAccordionOptions): string;
60
+ /**
61
+ * Create a preconfigured KeyMap for accordion navigation.
62
+ */
63
+ export declare function accordionKeyMap<Msg>(actions: {
64
+ focusNext: Msg;
65
+ focusPrev: Msg;
66
+ toggle: Msg;
67
+ quit: Msg;
68
+ }): KeyMap<Msg>;
69
+ //# sourceMappingURL=accordion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accordion.d.ts","sourceRoot":"","sources":["../src/accordion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG7D,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAM5D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC/C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,2BAA2B;IAC1C,+CAA+C;IAC/C,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IAC7D,gCAAgC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAC;IACrD,sDAAsD;IACtD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC;CAC7B;AAMD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,cAAc,CAKjF;AAMD,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAM/D;AAED,yDAAyD;AACzD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAM/D;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAMnE;AAED,2BAA2B;AAC3B,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAE/D;AAED,6BAA6B;AAC7B,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAEjE;AAMD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,cAAc,EACrB,OAAO,CAAC,EAAE,2BAA2B,GACpC,MAAM,CA4BR;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE;IAC5C,SAAS,EAAE,GAAG,CAAC;IACf,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC;CACX,GAAG,MAAM,CAAC,GAAG,CAAC,CAcd"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Interactive accordion building block.
3
+ *
4
+ * Wraps the static `accordion()` from bijou core with focus management
5
+ * and expand/collapse state transformers.
6
+ *
7
+ * ```ts
8
+ * // In TEA init:
9
+ * const accState = createAccordionState(sections);
10
+ *
11
+ * // In TEA view:
12
+ * const output = interactiveAccordion(model.accordion);
13
+ *
14
+ * // In TEA update:
15
+ * case 'focus-next':
16
+ * return [{ ...model, accordion: focusNext(model.accordion) }, []];
17
+ * case 'toggle':
18
+ * return [{ ...model, accordion: toggleFocused(model.accordion) }, []];
19
+ * ```
20
+ */
21
+ import { accordion, } from '@flyingrobots/bijou';
22
+ import { createKeyMap } from './keybindings.js';
23
+ // ---------------------------------------------------------------------------
24
+ // State creation
25
+ // ---------------------------------------------------------------------------
26
+ /**
27
+ * Create initial accordion state from sections.
28
+ * Preserves existing `expanded` flags; focus starts at index 0.
29
+ */
30
+ export function createAccordionState(sections) {
31
+ return {
32
+ sections: sections.map((s) => ({ ...s })),
33
+ focusIndex: 0,
34
+ };
35
+ }
36
+ // ---------------------------------------------------------------------------
37
+ // State transformers
38
+ // ---------------------------------------------------------------------------
39
+ /** Move focus to the next section (wraps around). */
40
+ export function focusNext(state) {
41
+ if (state.sections.length === 0)
42
+ return state;
43
+ return {
44
+ ...state,
45
+ focusIndex: (state.focusIndex + 1) % state.sections.length,
46
+ };
47
+ }
48
+ /** Move focus to the previous section (wraps around). */
49
+ export function focusPrev(state) {
50
+ if (state.sections.length === 0)
51
+ return state;
52
+ return {
53
+ ...state,
54
+ focusIndex: (state.focusIndex - 1 + state.sections.length) % state.sections.length,
55
+ };
56
+ }
57
+ /** Toggle the expanded state of the focused section. */
58
+ export function toggleFocused(state) {
59
+ if (state.sections.length === 0)
60
+ return state;
61
+ const sections = state.sections.map((s, i) => i === state.focusIndex ? { ...s, expanded: !s.expanded } : s);
62
+ return { ...state, sections };
63
+ }
64
+ /** Expand all sections. */
65
+ export function expandAll(state) {
66
+ return { ...state, sections: state.sections.map((s) => ({ ...s, expanded: true })) };
67
+ }
68
+ /** Collapse all sections. */
69
+ export function collapseAll(state) {
70
+ return { ...state, sections: state.sections.map((s) => ({ ...s, expanded: false })) };
71
+ }
72
+ // ---------------------------------------------------------------------------
73
+ // Render
74
+ // ---------------------------------------------------------------------------
75
+ /**
76
+ * Render the interactive accordion with a focus indicator on the
77
+ * currently focused section.
78
+ *
79
+ * Delegates to the static `accordion()` for the actual rendering,
80
+ * then prepends a focus indicator to the focused section's line.
81
+ */
82
+ export function interactiveAccordion(state, options) {
83
+ if (state.sections.length === 0)
84
+ return '';
85
+ const focusChar = options?.focusChar ?? '>';
86
+ // Normalize focusIndex to avoid stale/out-of-range values
87
+ const focusIndex = ((state.focusIndex % state.sections.length) + state.sections.length) % state.sections.length;
88
+ // Continuation-line padding must match the focus prefix width
89
+ const padWidth = focusChar.length + 1; // focusChar + space
90
+ const continuationPad = ' '.repeat(padWidth);
91
+ // Render each section individually so we can prepend the focus indicator
92
+ const renderedSections = state.sections.map((section, i) => {
93
+ const rendered = accordion([section], {
94
+ indicatorToken: options?.indicatorToken,
95
+ titleToken: options?.titleToken,
96
+ ctx: options?.ctx,
97
+ });
98
+ const prefix = i === focusIndex ? `${focusChar} ` : continuationPad;
99
+ // Prepend the focus indicator to each line of the rendered section
100
+ return rendered
101
+ .split('\n')
102
+ .map((line, lineIdx) => (lineIdx === 0 ? prefix + line : continuationPad + line))
103
+ .join('\n');
104
+ });
105
+ return renderedSections.join('\n\n');
106
+ }
107
+ // ---------------------------------------------------------------------------
108
+ // Convenience keymap
109
+ // ---------------------------------------------------------------------------
110
+ /**
111
+ * Create a preconfigured KeyMap for accordion navigation.
112
+ */
113
+ export function accordionKeyMap(actions) {
114
+ return createKeyMap()
115
+ .group('Navigation', (g) => g
116
+ .bind('j', 'Next section', actions.focusNext)
117
+ .bind('down', 'Next section', actions.focusNext)
118
+ .bind('k', 'Previous section', actions.focusPrev)
119
+ .bind('up', 'Previous section', actions.focusPrev))
120
+ .group('Actions', (g) => g
121
+ .bind('enter', 'Toggle section', actions.toggle)
122
+ .bind('space', 'Toggle section', actions.toggle))
123
+ .bind('q', 'Quit', actions.quit)
124
+ .bind('ctrl+c', 'Quit', actions.quit);
125
+ }
126
+ //# sourceMappingURL=accordion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accordion.js","sourceRoot":"","sources":["../src/accordion.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -8,10 +8,14 @@ export { type BusMsg, type EventBus, createEventBus, } from './eventbus.js';
8
8
  export { vstack, hstack } from './layout.js';
9
9
  export { type SpringConfig, type SpringPreset, type SpringState, SPRING_PRESETS, springStep, createSpringState, resolveSpringConfig, type EasingFn, EASINGS, type TweenConfig, type TweenState, tweenStep, createTweenState, resolveTweenConfig, } from './spring.js';
10
10
  export { type SpringAnimateOptions, type TweenAnimateOptions, type AnimateOptions, animate, sequence, } from './animate.js';
11
- export { type ViewportOptions, type ScrollState, viewport, createScrollState, scrollBy, scrollTo, scrollToTop, scrollToBottom, pageDown, pageUp, } from './viewport.js';
11
+ export { type ViewportOptions, type ScrollState, viewport, createScrollState, scrollBy, scrollTo, scrollToTop, scrollToBottom, pageDown, pageUp, stripAnsi, visibleLength, clipToWidth, sliceAnsi, scrollByX, scrollToX, } from './viewport.js';
12
12
  export { type Position, type SpringTrackDef, type TweenTrackDef, type TrackDef, type TimelineState, type TimelineBuilder, type Timeline, timeline, } from './timeline.js';
13
13
  export { type FlexOptions, type FlexChild, flex, } from './flex.js';
14
14
  export { type KeyCombo, type BindingInfo, type KeyMap, type KeyMapGroup, createKeyMap, parseKeyCombo, formatKeyCombo, } from './keybindings.js';
15
15
  export { type BindingSource, type HelpOptions, helpView, helpShort, helpFor, } from './help.js';
16
16
  export { type InputHandler, type LayerOptions, type LayerInfo, type InputStack, createInputStack, } from './inputstack.js';
17
+ export { type PagerState, type PagerOptions, type PagerRenderOptions, createPagerState, pager, pagerScrollBy, pagerScrollTo, pagerScrollToTop, pagerScrollToBottom, pagerPageDown, pagerPageUp, pagerSetContent, pagerKeyMap, } from './pager.js';
18
+ export { type PanelDef, type PanelGroupOptions, type PanelGroup, createPanelGroup, } from './panels.js';
19
+ export { type Overlay, type CompositeOptions, type ModalOptions, type ToastVariant, type ToastAnchor, type ToastOptions, composite, modal, toast, } from './overlay.js';
20
+ export { type AccordionState, type InteractiveAccordionOptions, createAccordionState, interactiveAccordion, focusNext, focusPrev, toggleFocused, expandAll, collapseAll, accordionKeyMap, } from './accordion.js';
17
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtF,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGlC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,IAAI,GACL,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAGlD,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAGnC,OAAO,EACL,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,cAAc,GACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,cAAc,EACd,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,QAAQ,EACb,OAAO,EACP,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,SAAS,EACT,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,OAAO,EACP,QAAQ,GACT,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,QAAQ,EACR,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,cAAc,EACd,QAAQ,EACR,MAAM,GACP,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,QAAQ,EACb,QAAQ,GACT,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,IAAI,GACL,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,YAAY,EACZ,aAAa,EACb,cAAc,GACf,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,QAAQ,EACR,SAAS,EACT,OAAO,GACR,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,gBAAgB,GACjB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ export { vstack, hstack } from './layout.js';
15
15
  export { SPRING_PRESETS, springStep, createSpringState, resolveSpringConfig, EASINGS, tweenStep, createTweenState, resolveTweenConfig, } from './spring.js';
16
16
  export { animate, sequence, } from './animate.js';
17
17
  // Viewport — scrollable content pane
18
- export { viewport, createScrollState, scrollBy, scrollTo, scrollToTop, scrollToBottom, pageDown, pageUp, } from './viewport.js';
18
+ export { viewport, createScrollState, scrollBy, scrollTo, scrollToTop, scrollToBottom, pageDown, pageUp, stripAnsi, visibleLength, clipToWidth, sliceAnsi, scrollByX, scrollToX, } from './viewport.js';
19
19
  // Timeline — GSAP-style animation orchestration
20
20
  export { timeline, } from './timeline.js';
21
21
  // Flexbox layout
@@ -26,4 +26,12 @@ export { createKeyMap, parseKeyCombo, formatKeyCombo, } from './keybindings.js';
26
26
  export { helpView, helpShort, helpFor, } from './help.js';
27
27
  // Input stack
28
28
  export { createInputStack, } from './inputstack.js';
29
+ // Pager — scrollable text viewer
30
+ export { createPagerState, pager, pagerScrollBy, pagerScrollTo, pagerScrollToTop, pagerScrollToBottom, pagerPageDown, pagerPageUp, pagerSetContent, pagerKeyMap, } from './pager.js';
31
+ // Panel group — multi-pane focus management
32
+ export { createPanelGroup, } from './panels.js';
33
+ // Overlay compositing
34
+ export { composite, modal, toast, } from './overlay.js';
35
+ // Interactive accordion
36
+ export { createAccordionState, interactiveAccordion, focusNext, focusPrev, toggleFocused, expandAll, collapseAll, accordionKeyMap, } from './accordion.js';
29
37
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,cAAc;AACd,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,iBAAiB;AACjB,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,IAAI,GACL,MAAM,aAAa,CAAC;AAErB,WAAW;AACX,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAElD,UAAU;AACV,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,YAAY;AACZ,OAAO,EAGL,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,SAAS;AACT,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE7C,sCAAsC;AACtC,OAAO,EAIL,cAAc,EACd,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EAEnB,OAAO,EAGP,SAAS,EACT,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAIL,OAAO,EACP,QAAQ,GACT,MAAM,cAAc,CAAC;AAEtB,qCAAqC;AACrC,OAAO,EAGL,QAAQ,EACR,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,cAAc,EACd,QAAQ,EACR,MAAM,GACP,MAAM,eAAe,CAAC;AAEvB,gDAAgD;AAChD,OAAO,EAQL,QAAQ,GACT,MAAM,eAAe,CAAC;AAEvB,iBAAiB;AACjB,OAAO,EAGL,IAAI,GACL,MAAM,WAAW,CAAC;AAEnB,qBAAqB;AACrB,OAAO,EAKL,YAAY,EACZ,aAAa,EACb,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,kBAAkB;AAClB,OAAO,EAGL,QAAQ,EACR,SAAS,EACT,OAAO,GACR,MAAM,WAAW,CAAC;AAEnB,cAAc;AACd,OAAO,EAKL,gBAAgB,GACjB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,cAAc;AACd,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,iBAAiB;AACjB,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,IAAI,GACL,MAAM,aAAa,CAAC;AAErB,WAAW;AACX,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAElD,UAAU;AACV,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,YAAY;AACZ,OAAO,EAGL,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,SAAS;AACT,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE7C,sCAAsC;AACtC,OAAO,EAIL,cAAc,EACd,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EAEnB,OAAO,EAGP,SAAS,EACT,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAIL,OAAO,EACP,QAAQ,GACT,MAAM,cAAc,CAAC;AAEtB,qCAAqC;AACrC,OAAO,EAGL,QAAQ,EACR,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,cAAc,EACd,QAAQ,EACR,MAAM,EACN,SAAS,EACT,aAAa,EACb,WAAW,EACX,SAAS,EACT,SAAS,EACT,SAAS,GACV,MAAM,eAAe,CAAC;AAEvB,gDAAgD;AAChD,OAAO,EAQL,QAAQ,GACT,MAAM,eAAe,CAAC;AAEvB,iBAAiB;AACjB,OAAO,EAGL,IAAI,GACL,MAAM,WAAW,CAAC;AAEnB,qBAAqB;AACrB,OAAO,EAKL,YAAY,EACZ,aAAa,EACb,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,kBAAkB;AAClB,OAAO,EAGL,QAAQ,EACR,SAAS,EACT,OAAO,GACR,MAAM,WAAW,CAAC;AAEnB,cAAc;AACd,OAAO,EAKL,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAEzB,iCAAiC;AACjC,OAAO,EAIL,gBAAgB,EAChB,KAAK,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,WAAW,EACX,eAAe,EACf,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,4CAA4C;AAC5C,OAAO,EAIL,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,sBAAsB;AACtB,OAAO,EAOL,SAAS,EACT,KAAK,EACL,KAAK,GACN,MAAM,cAAc,CAAC;AAEtB,wBAAwB;AACxB,OAAO,EAGL,oBAAoB,EACpB,oBAAoB,EACpB,SAAS,EACT,SAAS,EACT,aAAa,EACb,SAAS,EACT,WAAW,EACX,eAAe,GAChB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Overlay compositing primitives for TUI apps.
3
+ *
4
+ * - `composite()` — paint overlays onto a background string (ANSI-safe)
5
+ * - `modal()` — centered dialog overlay with title, body, hint
6
+ * - `toast()` — anchored notification overlay with variant icons
7
+ */
8
+ import type { BijouContext, TokenValue } from '@flyingrobots/bijou';
9
+ export interface Overlay {
10
+ readonly content: string;
11
+ readonly row: number;
12
+ readonly col: number;
13
+ }
14
+ export interface CompositeOptions {
15
+ /** Wrap background lines in dim (ANSI 2m). */
16
+ readonly dim?: boolean;
17
+ }
18
+ export interface ModalOptions {
19
+ readonly title?: string;
20
+ readonly body: string;
21
+ readonly hint?: string;
22
+ readonly screenWidth: number;
23
+ readonly screenHeight: number;
24
+ /** Override box width (default: auto from content). */
25
+ readonly width?: number;
26
+ readonly borderToken?: TokenValue;
27
+ readonly ctx?: BijouContext;
28
+ }
29
+ export type ToastVariant = 'success' | 'error' | 'info';
30
+ export type ToastAnchor = 'top-right' | 'bottom-right' | 'bottom-left' | 'top-left';
31
+ export interface ToastOptions {
32
+ readonly message: string;
33
+ readonly variant?: ToastVariant;
34
+ readonly anchor?: ToastAnchor;
35
+ readonly screenWidth: number;
36
+ readonly screenHeight: number;
37
+ /** Rows/cols from edge. Default: 1. */
38
+ readonly margin?: number;
39
+ readonly ctx?: BijouContext;
40
+ }
41
+ export declare function composite(background: string, overlays: readonly Overlay[], options?: CompositeOptions): string;
42
+ export declare function modal(options: ModalOptions): Overlay;
43
+ export declare function toast(options: ToastOptions): Overlay;
44
+ //# sourceMappingURL=overlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../src/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAOpE,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AACxD,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,cAAc,GAAG,aAAa,GAAG,UAAU,CAAC;AAEpF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,CAAC;CAC7B;AAoCD,wBAAgB,SAAS,CACvB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAqBR;AAwBD,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CA6CpD;AAkBD,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAsDpD"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Overlay compositing primitives for TUI apps.
3
+ *
4
+ * - `composite()` — paint overlays onto a background string (ANSI-safe)
5
+ * - `modal()` — centered dialog overlay with title, body, hint
6
+ * - `toast()` — anchored notification overlay with variant icons
7
+ */
8
+ import { sliceAnsi, visibleLength } from './viewport.js';
9
+ // ---------------------------------------------------------------------------
10
+ // Border characters (same as core box.ts)
11
+ // ---------------------------------------------------------------------------
12
+ const BORDER = {
13
+ tl: '\u250c', // ┌
14
+ tr: '\u2510', // ┐
15
+ bl: '\u2514', // └
16
+ br: '\u2518', // ┘
17
+ h: '\u2500', // ─
18
+ v: '\u2502', // │
19
+ };
20
+ // ---------------------------------------------------------------------------
21
+ // spliceLine (internal)
22
+ // ---------------------------------------------------------------------------
23
+ function spliceLine(bgLine, overlayLine, col) {
24
+ const overlayVis = visibleLength(overlayLine);
25
+ const bgVis = visibleLength(bgLine);
26
+ const left = bgVis <= col
27
+ ? bgLine + ' '.repeat(col - bgVis)
28
+ : sliceAnsi(bgLine, 0, col);
29
+ const right = sliceAnsi(bgLine, col + overlayVis, bgVis);
30
+ return left + '\x1b[0m' + overlayLine + '\x1b[0m' + right;
31
+ }
32
+ // ---------------------------------------------------------------------------
33
+ // composite()
34
+ // ---------------------------------------------------------------------------
35
+ export function composite(background, overlays, options) {
36
+ const bgLines = background.split('\n');
37
+ if (options?.dim) {
38
+ for (let i = 0; i < bgLines.length; i++) {
39
+ if (visibleLength(bgLines[i]) > 0) {
40
+ bgLines[i] = '\x1b[2m' + bgLines[i] + '\x1b[0m';
41
+ }
42
+ }
43
+ }
44
+ for (const overlay of overlays) {
45
+ const oLines = overlay.content.split('\n');
46
+ for (let i = 0; i < oLines.length; i++) {
47
+ const targetRow = overlay.row + i;
48
+ if (targetRow < 0 || targetRow >= bgLines.length)
49
+ continue;
50
+ bgLines[targetRow] = spliceLine(bgLines[targetRow], oLines[i], overlay.col);
51
+ }
52
+ }
53
+ return bgLines.join('\n');
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ // renderBox (shared between modal and toast)
57
+ // ---------------------------------------------------------------------------
58
+ function renderBox(lines, borderColor) {
59
+ const innerWidth = lines.reduce((max, l) => Math.max(max, visibleLength(l)), 0);
60
+ const top = borderColor(BORDER.tl + BORDER.h.repeat(innerWidth + 2) + BORDER.tr);
61
+ const bottom = borderColor(BORDER.bl + BORDER.h.repeat(innerWidth + 2) + BORDER.br);
62
+ const body = lines.map((l) => {
63
+ const pad = innerWidth - visibleLength(l);
64
+ return borderColor(BORDER.v) + ' ' + l + ' '.repeat(pad) + ' ' + borderColor(BORDER.v);
65
+ });
66
+ return [top, ...body, bottom].join('\n');
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // modal()
70
+ // ---------------------------------------------------------------------------
71
+ export function modal(options) {
72
+ const { title, body, hint, screenWidth, screenHeight, ctx } = options;
73
+ const contentLines = [];
74
+ if (title != null) {
75
+ const titleText = ctx ? ctx.style.bold(title) : title;
76
+ contentLines.push(titleText, '');
77
+ }
78
+ contentLines.push(...body.split('\n'));
79
+ if (hint != null) {
80
+ contentLines.push('');
81
+ const hintText = ctx
82
+ ? ctx.style.styled(ctx.theme.theme.semantic.muted, hint)
83
+ : hint;
84
+ contentLines.push(hintText);
85
+ }
86
+ // If width override, constrain inner width
87
+ if (options.width != null) {
88
+ const targetInner = options.width - 4; // border + padding
89
+ // Pad short lines, but don't truncate (user controls width)
90
+ for (let i = 0; i < contentLines.length; i++) {
91
+ const vis = visibleLength(contentLines[i]);
92
+ if (vis < targetInner) {
93
+ contentLines[i] = contentLines[i] + ' '.repeat(targetInner - vis);
94
+ }
95
+ }
96
+ }
97
+ const borderColor = ctx && options.borderToken
98
+ ? (s) => ctx.style.styled(options.borderToken, s)
99
+ : (s) => s;
100
+ const boxStr = renderBox(contentLines, borderColor);
101
+ const boxLines = boxStr.split('\n');
102
+ const boxHeight = boxLines.length;
103
+ const boxWidth = visibleLength(boxLines[0]);
104
+ const row = Math.max(0, Math.floor((screenHeight - boxHeight) / 2));
105
+ const col = Math.max(0, Math.floor((screenWidth - boxWidth) / 2));
106
+ return { content: boxStr, row, col };
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // toast()
110
+ // ---------------------------------------------------------------------------
111
+ const TOAST_ICONS = {
112
+ success: '\u2714', // ✔
113
+ error: '\u2718', // ✘
114
+ info: '\u2139', // ℹ
115
+ };
116
+ const TOAST_BORDER = {
117
+ success: 'success',
118
+ error: 'error',
119
+ info: 'primary',
120
+ };
121
+ export function toast(options) {
122
+ const { message, variant = 'info', anchor = 'bottom-right', screenWidth, screenHeight, margin = 1, ctx, } = options;
123
+ const icon = TOAST_ICONS[variant];
124
+ let line = icon + ' ' + message;
125
+ if (ctx) {
126
+ line = ctx.style.styled(ctx.theme.theme.semantic[variant], line);
127
+ }
128
+ const borderKey = TOAST_BORDER[variant];
129
+ const borderColor = ctx
130
+ ? (s) => ctx.style.styled(ctx.theme.theme.border[borderKey], s)
131
+ : (s) => s;
132
+ const boxStr = renderBox([line], borderColor);
133
+ const boxLines = boxStr.split('\n');
134
+ const boxHeight = boxLines.length;
135
+ const boxWidth = visibleLength(boxLines[0]);
136
+ let row;
137
+ let col;
138
+ switch (anchor) {
139
+ case 'top-right':
140
+ row = margin;
141
+ col = screenWidth - boxWidth - margin;
142
+ break;
143
+ case 'bottom-right':
144
+ row = screenHeight - boxHeight - margin;
145
+ col = screenWidth - boxWidth - margin;
146
+ break;
147
+ case 'bottom-left':
148
+ row = screenHeight - boxHeight - margin;
149
+ col = margin;
150
+ break;
151
+ case 'top-left':
152
+ row = margin;
153
+ col = margin;
154
+ break;
155
+ }
156
+ row = Math.max(0, row);
157
+ col = Math.max(0, col);
158
+ return { content: boxStr, row, col };
159
+ }
160
+ //# sourceMappingURL=overlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay.js","sourceRoot":"","sources":["../src/overlay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Pager building block — a scrollable text viewer with status line.
3
+ *
4
+ * Wraps `viewport()` with a "Line N/M" status indicator and provides
5
+ * a convenience keymap for vim-style scroll navigation.
6
+ *
7
+ * ```ts
8
+ * // In TEA init:
9
+ * const pagerState = createPagerState({ content, width: 80, height: 20 });
10
+ *
11
+ * // In TEA view:
12
+ * const output = pager(model.pagerState);
13
+ *
14
+ * // In TEA update:
15
+ * case 'scroll-down':
16
+ * return [{ ...model, pagerState: pagerScrollBy(model.pagerState, 1) }, []];
17
+ * ```
18
+ */
19
+ import { type ScrollState } from './viewport.js';
20
+ import { type KeyMap } from './keybindings.js';
21
+ export interface PagerState {
22
+ readonly scroll: ScrollState;
23
+ readonly content: string;
24
+ readonly width: number;
25
+ readonly height: number;
26
+ }
27
+ export interface PagerOptions {
28
+ readonly content: string;
29
+ readonly width: number;
30
+ readonly height: number;
31
+ }
32
+ export interface PagerRenderOptions {
33
+ /** Show a scrollbar track on the right edge. Default: true. */
34
+ readonly showScrollbar?: boolean;
35
+ /** Show a "Line N/M" status line below the viewport. Default: true. */
36
+ readonly showStatus?: boolean;
37
+ }
38
+ /**
39
+ * Create initial pager state for the given content and dimensions.
40
+ *
41
+ * The viewport height is `height - 1` when status is shown (the default),
42
+ * reserving one line for the status indicator.
43
+ */
44
+ export declare function createPagerState(options: PagerOptions): PagerState;
45
+ /** Scroll by a relative number of lines. */
46
+ export declare function pagerScrollBy(state: PagerState, dy: number): PagerState;
47
+ /** Scroll to an absolute line. */
48
+ export declare function pagerScrollTo(state: PagerState, y: number): PagerState;
49
+ /** Scroll to the first line. */
50
+ export declare function pagerScrollToTop(state: PagerState): PagerState;
51
+ /** Scroll to the last line. */
52
+ export declare function pagerScrollToBottom(state: PagerState): PagerState;
53
+ /** Page down (one viewport height). */
54
+ export declare function pagerPageDown(state: PagerState): PagerState;
55
+ /** Page up (one viewport height). */
56
+ export declare function pagerPageUp(state: PagerState): PagerState;
57
+ /** Update content while preserving scroll position (clamped). */
58
+ export declare function pagerSetContent(state: PagerState, content: string): PagerState;
59
+ /**
60
+ * Render the pager — viewport content plus optional status line.
61
+ */
62
+ export declare function pager(state: PagerState, options?: PagerRenderOptions): string;
63
+ /**
64
+ * Create a preconfigured KeyMap for pager navigation.
65
+ *
66
+ * The caller provides their own message types for each action:
67
+ * ```ts
68
+ * const keys = pagerKeyMap({
69
+ * scrollUp: { type: 'scroll', dy: -1 },
70
+ * scrollDown: { type: 'scroll', dy: 1 },
71
+ * pageUp: { type: 'page-up' },
72
+ * pageDown: { type: 'page-down' },
73
+ * top: { type: 'top' },
74
+ * bottom: { type: 'bottom' },
75
+ * quit: { type: 'quit' },
76
+ * });
77
+ * ```
78
+ */
79
+ export declare function pagerKeyMap<Msg>(actions: {
80
+ scrollUp: Msg;
81
+ scrollDown: Msg;
82
+ pageUp: Msg;
83
+ pageDown: Msg;
84
+ top: Msg;
85
+ bottom: Msg;
86
+ quit: Msg;
87
+ }): KeyMap<Msg>;
88
+ //# sourceMappingURL=pager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pager.d.ts","sourceRoot":"","sources":["../src/pager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,KAAK,WAAW,EASjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAM7D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,uEAAuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,CASlE;AAMD,4CAA4C;AAC5C,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAEvE;AAED,kCAAkC;AAClC,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAEtE;AAED,gCAAgC;AAChC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAE9D;AAED,+BAA+B;AAC/B,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAEjE;AAED,uCAAuC;AACvC,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAE3D;AAED,qCAAqC;AACrC,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAEzD;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAS9E;AAMD;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,MAAM,CA4B7E;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE;IACxC,QAAQ,EAAE,GAAG,CAAC;IACd,UAAU,EAAE,GAAG,CAAC;IAChB,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,GAAG,CAAC;IACd,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC;CACX,GAAG,MAAM,CAAC,GAAG,CAAC,CAgBd"}
package/dist/pager.js ADDED
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Pager building block — a scrollable text viewer with status line.
3
+ *
4
+ * Wraps `viewport()` with a "Line N/M" status indicator and provides
5
+ * a convenience keymap for vim-style scroll navigation.
6
+ *
7
+ * ```ts
8
+ * // In TEA init:
9
+ * const pagerState = createPagerState({ content, width: 80, height: 20 });
10
+ *
11
+ * // In TEA view:
12
+ * const output = pager(model.pagerState);
13
+ *
14
+ * // In TEA update:
15
+ * case 'scroll-down':
16
+ * return [{ ...model, pagerState: pagerScrollBy(model.pagerState, 1) }, []];
17
+ * ```
18
+ */
19
+ import { viewport, createScrollState, scrollBy, scrollTo, scrollToTop, scrollToBottom, pageDown, pageUp, } from './viewport.js';
20
+ import { createKeyMap } from './keybindings.js';
21
+ // ---------------------------------------------------------------------------
22
+ // State creation
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Create initial pager state for the given content and dimensions.
26
+ *
27
+ * The viewport height is `height - 1` when status is shown (the default),
28
+ * reserving one line for the status indicator.
29
+ */
30
+ export function createPagerState(options) {
31
+ const { content, width, height } = options;
32
+ const viewportHeight = Math.max(1, height - 1); // reserve 1 for status
33
+ return {
34
+ scroll: createScrollState(content, viewportHeight),
35
+ content,
36
+ width,
37
+ height,
38
+ };
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // State transformers
42
+ // ---------------------------------------------------------------------------
43
+ /** Scroll by a relative number of lines. */
44
+ export function pagerScrollBy(state, dy) {
45
+ return { ...state, scroll: scrollBy(state.scroll, dy) };
46
+ }
47
+ /** Scroll to an absolute line. */
48
+ export function pagerScrollTo(state, y) {
49
+ return { ...state, scroll: scrollTo(state.scroll, y) };
50
+ }
51
+ /** Scroll to the first line. */
52
+ export function pagerScrollToTop(state) {
53
+ return { ...state, scroll: scrollToTop(state.scroll) };
54
+ }
55
+ /** Scroll to the last line. */
56
+ export function pagerScrollToBottom(state) {
57
+ return { ...state, scroll: scrollToBottom(state.scroll) };
58
+ }
59
+ /** Page down (one viewport height). */
60
+ export function pagerPageDown(state) {
61
+ return { ...state, scroll: pageDown(state.scroll) };
62
+ }
63
+ /** Page up (one viewport height). */
64
+ export function pagerPageUp(state) {
65
+ return { ...state, scroll: pageUp(state.scroll) };
66
+ }
67
+ /** Update content while preserving scroll position (clamped). */
68
+ export function pagerSetContent(state, content) {
69
+ const viewportHeight = Math.max(1, state.height - 1);
70
+ const newScroll = createScrollState(content, viewportHeight);
71
+ const clampedY = Math.min(state.scroll.y, newScroll.maxY);
72
+ return {
73
+ ...state,
74
+ content,
75
+ scroll: { ...newScroll, y: clampedY },
76
+ };
77
+ }
78
+ // ---------------------------------------------------------------------------
79
+ // Render
80
+ // ---------------------------------------------------------------------------
81
+ /**
82
+ * Render the pager — viewport content plus optional status line.
83
+ */
84
+ export function pager(state, options) {
85
+ const showScrollbar = options?.showScrollbar ?? true;
86
+ const showStatus = options?.showStatus ?? true;
87
+ const viewportHeight = showStatus
88
+ ? Math.max(1, state.height - 1)
89
+ : state.height;
90
+ // Clamp scroll to the active viewport height (which may differ from
91
+ // the height used when creating state if showStatus changed).
92
+ const maxY = Math.max(0, state.scroll.totalLines - viewportHeight);
93
+ const clampedY = Math.max(0, Math.min(state.scroll.y, maxY));
94
+ const body = viewport({
95
+ width: state.width,
96
+ height: viewportHeight,
97
+ content: state.content,
98
+ scrollY: clampedY,
99
+ showScrollbar,
100
+ });
101
+ if (!showStatus)
102
+ return body;
103
+ const currentLine = clampedY + 1;
104
+ const totalLines = state.scroll.totalLines;
105
+ const status = ` Line ${currentLine}/${totalLines}`;
106
+ return body + '\n' + status;
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // Convenience keymap
110
+ // ---------------------------------------------------------------------------
111
+ /**
112
+ * Create a preconfigured KeyMap for pager navigation.
113
+ *
114
+ * The caller provides their own message types for each action:
115
+ * ```ts
116
+ * const keys = pagerKeyMap({
117
+ * scrollUp: { type: 'scroll', dy: -1 },
118
+ * scrollDown: { type: 'scroll', dy: 1 },
119
+ * pageUp: { type: 'page-up' },
120
+ * pageDown: { type: 'page-down' },
121
+ * top: { type: 'top' },
122
+ * bottom: { type: 'bottom' },
123
+ * quit: { type: 'quit' },
124
+ * });
125
+ * ```
126
+ */
127
+ export function pagerKeyMap(actions) {
128
+ return createKeyMap()
129
+ .group('Scroll', (g) => g
130
+ .bind('k', 'Up', actions.scrollUp)
131
+ .bind('up', 'Up', actions.scrollUp)
132
+ .bind('j', 'Down', actions.scrollDown)
133
+ .bind('down', 'Down', actions.scrollDown)
134
+ .bind('u', 'Page up', actions.pageUp)
135
+ .bind('pageup', 'Page up', actions.pageUp)
136
+ .bind('d', 'Page down', actions.pageDown)
137
+ .bind('pagedown', 'Page down', actions.pageDown)
138
+ .bind('g', 'Top', actions.top)
139
+ .bind('shift+g', 'Bottom', actions.bottom))
140
+ .bind('q', 'Quit', actions.quit)
141
+ .bind('ctrl+c', 'Quit', actions.quit);
142
+ }
143
+ //# sourceMappingURL=pager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pager.js","sourceRoot":"","sources":["../src/pager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Multi-pane focus management for TUI applications.
3
+ *
4
+ * Each panel has a hotkey, label, and its own KeyMap. The group tracks
5
+ * which panel is focused, routes input to the active panel's keymap,
6
+ * and switches focus on hotkey press.
7
+ */
8
+ import type { KeyMsg } from './types.js';
9
+ import type { KeyMap } from './keybindings.js';
10
+ import type { InputStack } from './inputstack.js';
11
+ import type { BijouContext } from '@flyingrobots/bijou';
12
+ export interface PanelDef<A> {
13
+ readonly id: string;
14
+ readonly hotkey: string;
15
+ readonly label: string;
16
+ readonly keyMap: KeyMap<A>;
17
+ }
18
+ export interface PanelGroupOptions<A> {
19
+ readonly panels: readonly PanelDef<A>[];
20
+ readonly defaultFocus: string;
21
+ readonly inputStack?: InputStack<KeyMsg, A>;
22
+ }
23
+ export interface PanelGroup<A> {
24
+ readonly focused: string;
25
+ focus(id: string): void;
26
+ handle(msg: KeyMsg): A | undefined;
27
+ formatLabel(id: string, ctx?: BijouContext): string;
28
+ dispose(): void;
29
+ }
30
+ export declare function createPanelGroup<A>(options: PanelGroupOptions<A>): PanelGroup<A>;
31
+ //# sourceMappingURL=panels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMxD,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IACpD,OAAO,IAAI,IAAI,CAAC;CACjB;AAMD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAuHhF"}
package/dist/panels.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Multi-pane focus management for TUI applications.
3
+ *
4
+ * Each panel has a hotkey, label, and its own KeyMap. The group tracks
5
+ * which panel is focused, routes input to the active panel's keymap,
6
+ * and switches focus on hotkey press.
7
+ */
8
+ // ---------------------------------------------------------------------------
9
+ // Implementation
10
+ // ---------------------------------------------------------------------------
11
+ export function createPanelGroup(options) {
12
+ const panelMap = new Map();
13
+ const hotkeyMap = new Map();
14
+ for (const panel of options.panels) {
15
+ panelMap.set(panel.id, panel);
16
+ hotkeyMap.set(panel.hotkey, panel.id);
17
+ }
18
+ if (!panelMap.has(options.defaultFocus)) {
19
+ throw new Error(`createPanelGroup: defaultFocus "${options.defaultFocus}" does not match any panel id. ` +
20
+ `Available: ${[...panelMap.keys()].join(', ')}`);
21
+ }
22
+ let focusedId = options.defaultFocus;
23
+ const { inputStack } = options;
24
+ let hotkeyLayerId;
25
+ let panelLayerId;
26
+ if (inputStack) {
27
+ // Hotkey layer: passthrough, handles hotkey presses
28
+ hotkeyLayerId = inputStack.push({
29
+ handle(msg) {
30
+ if (!msg.ctrl && !msg.alt && !msg.shift) {
31
+ const targetId = hotkeyMap.get(msg.key);
32
+ if (targetId !== undefined && targetId !== focusedId) {
33
+ group.focus(targetId);
34
+ }
35
+ }
36
+ return undefined;
37
+ },
38
+ }, { passthrough: true, name: 'panel-group:hotkeys' });
39
+ // Focused panel keymap layer
40
+ const focusedPanel = panelMap.get(focusedId);
41
+ if (focusedPanel) {
42
+ panelLayerId = inputStack.push(focusedPanel.keyMap, {
43
+ passthrough: true,
44
+ name: `panel:${focusedId}`,
45
+ });
46
+ }
47
+ }
48
+ const group = {
49
+ get focused() {
50
+ return focusedId;
51
+ },
52
+ focus(id) {
53
+ if (!panelMap.has(id) || id === focusedId)
54
+ return;
55
+ focusedId = id;
56
+ if (inputStack && panelLayerId !== undefined) {
57
+ inputStack.remove(panelLayerId);
58
+ const panel = panelMap.get(id);
59
+ panelLayerId = inputStack.push(panel.keyMap, {
60
+ passthrough: true,
61
+ name: `panel:${id}`,
62
+ });
63
+ }
64
+ },
65
+ handle(msg) {
66
+ // Check hotkeys first (only plain keys, no modifiers)
67
+ if (!msg.ctrl && !msg.alt && !msg.shift) {
68
+ const targetId = hotkeyMap.get(msg.key);
69
+ if (targetId !== undefined && targetId !== focusedId) {
70
+ group.focus(targetId);
71
+ return undefined;
72
+ }
73
+ }
74
+ // Delegate to focused panel's keymap
75
+ const panel = panelMap.get(focusedId);
76
+ if (panel) {
77
+ return panel.keyMap.handle(msg);
78
+ }
79
+ return undefined;
80
+ },
81
+ formatLabel(id, ctx) {
82
+ const panel = panelMap.get(id);
83
+ if (!panel)
84
+ return '';
85
+ if (!ctx) {
86
+ return `[${panel.hotkey}] ${panel.label}`;
87
+ }
88
+ const style = ctx.style;
89
+ const semantic = ctx.theme.theme.semantic;
90
+ if (id === focusedId) {
91
+ return style.bold(style.styled(semantic.primary, panel.label));
92
+ }
93
+ return style.styled(semantic.muted, panel.label);
94
+ },
95
+ dispose() {
96
+ if (inputStack) {
97
+ if (panelLayerId !== undefined) {
98
+ inputStack.remove(panelLayerId);
99
+ panelLayerId = undefined;
100
+ }
101
+ if (hotkeyLayerId !== undefined) {
102
+ inputStack.remove(hotkeyLayerId);
103
+ hotkeyLayerId = undefined;
104
+ }
105
+ }
106
+ },
107
+ };
108
+ return group;
109
+ }
110
+ //# sourceMappingURL=panels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panels.js","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,8 @@ export interface ViewportOptions {
13
13
  readonly content: string;
14
14
  /** Vertical scroll offset (0-based line index). Default: 0. */
15
15
  readonly scrollY?: number;
16
+ /** Horizontal scroll offset (0-based column index). Default: 0. */
17
+ readonly scrollX?: number;
16
18
  /** Show a scrollbar track on the right edge. Default: true. */
17
19
  readonly showScrollbar?: boolean;
18
20
  }
@@ -21,11 +23,28 @@ export interface ScrollState {
21
23
  readonly y: number;
22
24
  /** Maximum vertical scroll offset. */
23
25
  readonly maxY: number;
26
+ /** Current horizontal scroll offset. */
27
+ readonly x: number;
28
+ /** Maximum horizontal scroll offset. */
29
+ readonly maxX: number;
24
30
  /** Total number of content lines. */
25
31
  readonly totalLines: number;
26
32
  /** Number of visible lines (viewport height). */
27
33
  readonly visibleLines: number;
28
34
  }
35
+ export declare function stripAnsi(str: string): string;
36
+ export declare function visibleLength(str: string): number;
37
+ /**
38
+ * Clip a string to a maximum visible width, preserving ANSI escapes.
39
+ * Appends a reset sequence if the string was clipped mid-style.
40
+ */
41
+ export declare function clipToWidth(str: string, maxWidth: number): string;
42
+ /**
43
+ * Extract a visible-column substring from an ANSI-styled string.
44
+ * Returns characters between visible columns [startCol, endCol).
45
+ * Preserves any active ANSI styles seen before startCol.
46
+ */
47
+ export declare function sliceAnsi(str: string, startCol: number, endCol: number): string;
29
48
  /**
30
49
  * Render a scrollable viewport into content.
31
50
  *
@@ -36,7 +55,7 @@ export declare function viewport(options: ViewportOptions): string;
36
55
  /**
37
56
  * Create initial scroll state for content within a viewport.
38
57
  */
39
- export declare function createScrollState(content: string, viewportHeight: number): ScrollState;
58
+ export declare function createScrollState(content: string, viewportHeight: number, viewportWidth?: number): ScrollState;
40
59
  /**
41
60
  * Scroll by a relative amount. Clamps to valid range.
42
61
  */
@@ -45,6 +64,14 @@ export declare function scrollBy(state: ScrollState, dy: number): ScrollState;
45
64
  * Scroll to an absolute position. Clamps to valid range.
46
65
  */
47
66
  export declare function scrollTo(state: ScrollState, y: number): ScrollState;
67
+ /**
68
+ * Scroll horizontally by a relative amount. Clamps to valid range.
69
+ */
70
+ export declare function scrollByX(state: ScrollState, dx: number): ScrollState;
71
+ /**
72
+ * Scroll horizontally to an absolute position. Clamps to valid range.
73
+ */
74
+ export declare function scrollToX(state: ScrollState, x: number): ScrollState;
48
75
  /**
49
76
  * Scroll to the top.
50
77
  */
@@ -1 +1 @@
1
- {"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,iDAAiD;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAyFD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CA2CzD;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,GACrB,WAAW,CAQb;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,CAGpE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,CAEnE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAE3D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAE9D;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAExD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAEtD"}
1
+ {"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,iDAAiD;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAQD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CA+BjE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAqD/E;AAsCD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CA+CzD;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,aAAa,CAAC,EAAE,MAAM,GACrB,WAAW,CAsBb;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,CAGpE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,CAEnE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,CAErE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,CAEpE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAE3D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAE9D;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAExD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAEtD"}
package/dist/viewport.js CHANGED
@@ -8,17 +8,17 @@
8
8
  // ANSI helpers
9
9
  // ---------------------------------------------------------------------------
10
10
  const ANSI_RE = /\x1b\[[0-9;]*m/g;
11
- function stripAnsi(str) {
11
+ export function stripAnsi(str) {
12
12
  return str.replace(ANSI_RE, '');
13
13
  }
14
- function visibleLength(str) {
14
+ export function visibleLength(str) {
15
15
  return stripAnsi(str).length;
16
16
  }
17
17
  /**
18
18
  * Clip a string to a maximum visible width, preserving ANSI escapes.
19
19
  * Appends a reset sequence if the string was clipped mid-style.
20
20
  */
21
- function clipToWidth(str, maxWidth) {
21
+ export function clipToWidth(str, maxWidth) {
22
22
  let visible = 0;
23
23
  let result = '';
24
24
  let inEscape = false;
@@ -45,6 +45,63 @@ function clipToWidth(str, maxWidth) {
45
45
  }
46
46
  return result;
47
47
  }
48
+ /**
49
+ * Extract a visible-column substring from an ANSI-styled string.
50
+ * Returns characters between visible columns [startCol, endCol).
51
+ * Preserves any active ANSI styles seen before startCol.
52
+ */
53
+ export function sliceAnsi(str, startCol, endCol) {
54
+ let visible = 0;
55
+ let inEscape = false;
56
+ let activeAnsi = '';
57
+ let result = '';
58
+ let collecting = false;
59
+ let hasStyle = false;
60
+ for (let i = 0; i < str.length; i++) {
61
+ const ch = str[i];
62
+ if (ch === '\x1b') {
63
+ inEscape = true;
64
+ if (collecting) {
65
+ result += ch;
66
+ hasStyle = true;
67
+ }
68
+ else {
69
+ activeAnsi += ch;
70
+ }
71
+ continue;
72
+ }
73
+ if (inEscape) {
74
+ if (collecting) {
75
+ result += ch;
76
+ }
77
+ else {
78
+ activeAnsi += ch;
79
+ }
80
+ if (ch === 'm')
81
+ inEscape = false;
82
+ continue;
83
+ }
84
+ if (visible >= endCol) {
85
+ if (hasStyle)
86
+ result += '\x1b[0m';
87
+ break;
88
+ }
89
+ if (visible >= startCol) {
90
+ if (!collecting) {
91
+ collecting = true;
92
+ result = activeAnsi;
93
+ if (activeAnsi.length > 0)
94
+ hasStyle = true;
95
+ }
96
+ result += ch;
97
+ }
98
+ visible++;
99
+ }
100
+ // Append reset if we reached end of string with an active style
101
+ if (collecting && hasStyle)
102
+ result += '\x1b[0m';
103
+ return result;
104
+ }
48
105
  // ---------------------------------------------------------------------------
49
106
  // Scrollbar rendering
50
107
  // ---------------------------------------------------------------------------
@@ -77,7 +134,7 @@ function renderScrollbar(viewportHeight, totalLines, scrollY) {
77
134
  * characters wide (plus optional scrollbar).
78
135
  */
79
136
  export function viewport(options) {
80
- const { width, height, content, scrollY = 0, showScrollbar = true, } = options;
137
+ const { width, height, content, scrollY = 0, scrollX = 0, showScrollbar = true, } = options;
81
138
  const allLines = content.split('\n');
82
139
  const totalLines = allLines.length;
83
140
  const maxScroll = Math.max(0, totalLines - height);
@@ -93,12 +150,16 @@ export function viewport(options) {
93
150
  }
94
151
  // Clip/pad each line to content width
95
152
  const rendered = visibleSlice.map((line) => {
96
- const vis = visibleLength(line);
97
- if (vis > contentWidth) {
98
- return clipToWidth(line, contentWidth);
153
+ let sliced;
154
+ if (scrollX > 0) {
155
+ sliced = sliceAnsi(line, scrollX, scrollX + contentWidth);
156
+ }
157
+ else {
158
+ const vis = visibleLength(line);
159
+ sliced = vis > contentWidth ? clipToWidth(line, contentWidth) : line;
99
160
  }
100
- // Right-pad with spaces to fill width
101
- return line + ' '.repeat(contentWidth - vis);
161
+ const slicedVis = visibleLength(sliced);
162
+ return slicedVis < contentWidth ? sliced + ' '.repeat(contentWidth - slicedVis) : sliced;
102
163
  });
103
164
  // Append scrollbar
104
165
  if (needsScrollbar) {
@@ -113,11 +174,24 @@ export function viewport(options) {
113
174
  /**
114
175
  * Create initial scroll state for content within a viewport.
115
176
  */
116
- export function createScrollState(content, viewportHeight) {
117
- const totalLines = content.split('\n').length;
177
+ export function createScrollState(content, viewportHeight, viewportWidth) {
178
+ const lines = content.split('\n');
179
+ const totalLines = lines.length;
180
+ let maxX = 0;
181
+ if (viewportWidth !== undefined) {
182
+ let widest = 0;
183
+ for (const line of lines) {
184
+ const w = visibleLength(line);
185
+ if (w > widest)
186
+ widest = w;
187
+ }
188
+ maxX = Math.max(0, widest - viewportWidth);
189
+ }
118
190
  return {
119
191
  y: 0,
120
192
  maxY: Math.max(0, totalLines - viewportHeight),
193
+ x: 0,
194
+ maxX,
121
195
  totalLines,
122
196
  visibleLines: viewportHeight,
123
197
  };
@@ -135,6 +209,18 @@ export function scrollBy(state, dy) {
135
209
  export function scrollTo(state, y) {
136
210
  return { ...state, y: Math.max(0, Math.min(y, state.maxY)) };
137
211
  }
212
+ /**
213
+ * Scroll horizontally by a relative amount. Clamps to valid range.
214
+ */
215
+ export function scrollByX(state, dx) {
216
+ return { ...state, x: Math.max(0, Math.min(state.x + dx, state.maxX)) };
217
+ }
218
+ /**
219
+ * Scroll horizontally to an absolute position. Clamps to valid range.
220
+ */
221
+ export function scrollToX(state, x) {
222
+ return { ...state, x: Math.max(0, Math.min(x, state.maxX)) };
223
+ }
138
224
  /**
139
225
  * Scroll to the top.
140
226
  */
@@ -1 +1 @@
1
- {"version":3,"file":"viewport.js","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"viewport.js","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flyingrobots/bijou-tui",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "TEA runtime for terminal UIs — model/update/view with keyboard input, alt screen, and layout helpers.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -24,7 +24,7 @@
24
24
  "lint": "tsc --noEmit"
25
25
  },
26
26
  "dependencies": {
27
- "@flyingrobots/bijou": "0.3.0"
27
+ "@flyingrobots/bijou": "0.4.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^22.0.0",