@fractaal/pi-tui 0.85.3 → 0.86.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -19
- package/dist/alt-screen-search.d.ts +46 -0
- package/dist/alt-screen-search.d.ts.map +1 -0
- package/dist/alt-screen-search.js +282 -0
- package/dist/alt-screen-search.js.map +1 -0
- package/dist/autocomplete.d.ts +2 -1
- package/dist/autocomplete.d.ts.map +1 -1
- package/dist/autocomplete.js +85 -24
- package/dist/autocomplete.js.map +1 -1
- package/dist/colors.d.ts +73 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +273 -0
- package/dist/colors.js.map +1 -0
- package/dist/components/alt-screen-flash.d.ts +13 -0
- package/dist/components/alt-screen-flash.d.ts.map +1 -0
- package/dist/components/alt-screen-flash.js +37 -0
- package/dist/components/alt-screen-flash.js.map +1 -0
- package/dist/components/box.d.ts +3 -1
- package/dist/components/box.d.ts.map +1 -1
- package/dist/components/box.js +38 -8
- package/dist/components/box.js.map +1 -1
- package/dist/components/cancellable-loader.d.ts.map +1 -1
- package/dist/components/cancellable-loader.js.map +1 -1
- package/dist/components/editor.d.ts +6 -1
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +118 -24
- package/dist/components/editor.js.map +1 -1
- package/dist/components/h-stack.d.ts +7 -0
- package/dist/components/h-stack.d.ts.map +1 -0
- package/dist/components/h-stack.js +43 -0
- package/dist/components/h-stack.js.map +1 -0
- package/dist/components/image.d.ts +12 -0
- package/dist/components/image.d.ts.map +1 -1
- package/dist/components/image.js +36 -3
- package/dist/components/image.js.map +1 -1
- package/dist/components/input.d.ts +12 -1
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +44 -5
- package/dist/components/input.js.map +1 -1
- package/dist/components/loader.d.ts +2 -0
- package/dist/components/loader.d.ts.map +1 -1
- package/dist/components/loader.js +10 -3
- package/dist/components/loader.js.map +1 -1
- package/dist/components/markdown.d.ts +5 -0
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +150 -9
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/mouse-region.d.ts +12 -0
- package/dist/components/mouse-region.d.ts.map +1 -0
- package/dist/components/mouse-region.js +21 -0
- package/dist/components/mouse-region.js.map +1 -0
- package/dist/components/scroll-view.d.ts +59 -0
- package/dist/components/scroll-view.d.ts.map +1 -0
- package/dist/components/scroll-view.js +195 -0
- package/dist/components/scroll-view.js.map +1 -0
- package/dist/components/select-list.d.ts +4 -1
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +49 -2
- package/dist/components/select-list.js.map +1 -1
- package/dist/components/settings-list.d.ts +13 -3
- package/dist/components/settings-list.d.ts.map +1 -1
- package/dist/components/settings-list.js +82 -15
- package/dist/components/settings-list.js.map +1 -1
- package/dist/components/spacer.d.ts.map +1 -1
- package/dist/components/spacer.js.map +1 -1
- package/dist/components/stack.d.ts +32 -0
- package/dist/components/stack.d.ts.map +1 -0
- package/dist/components/stack.js +111 -0
- package/dist/components/stack.js.map +1 -0
- package/dist/components/text.d.ts.map +1 -1
- package/dist/components/text.js +7 -5
- package/dist/components/text.js.map +1 -1
- package/dist/components/truncated-text.d.ts.map +1 -1
- package/dist/components/truncated-text.js.map +1 -1
- package/dist/components/v-stack.d.ts +8 -0
- package/dist/components/v-stack.d.ts.map +1 -0
- package/dist/components/v-stack.js +26 -0
- package/dist/components/v-stack.js.map +1 -0
- package/dist/editor-component.d.ts.map +1 -1
- package/dist/fuzzy.d.ts.map +1 -1
- package/dist/fuzzy.js +23 -22
- package/dist/fuzzy.js.map +1 -1
- package/dist/index.d.ts +18 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -6
- package/dist/index.js.map +1 -1
- package/dist/keybindings.d.ts +82 -2
- package/dist/keybindings.d.ts.map +1 -1
- package/dist/keybindings.js +61 -2
- package/dist/keybindings.js.map +1 -1
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js.map +1 -1
- package/dist/kill-ring.d.ts.map +1 -1
- package/dist/kill-ring.js.map +1 -1
- package/dist/latex.d.ts +10 -0
- package/dist/latex.d.ts.map +1 -0
- package/dist/latex.js +1378 -0
- package/dist/latex.js.map +1 -0
- package/dist/layout-node.d.ts +40 -0
- package/dist/layout-node.d.ts.map +1 -0
- package/dist/layout-node.js +6 -0
- package/dist/layout-node.js.map +1 -0
- package/dist/layout.d.ts +42 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +344 -0
- package/dist/layout.js.map +1 -0
- package/dist/native-modifiers.d.ts +2 -1
- package/dist/native-modifiers.d.ts.map +1 -1
- package/dist/native-modifiers.js +3 -43
- package/dist/native-modifiers.js.map +1 -1
- package/dist/native-module-path.d.ts +7 -0
- package/dist/native-module-path.d.ts.map +1 -0
- package/dist/native-module-path.js +19 -0
- package/dist/native-module-path.js.map +1 -0
- package/dist/native-platform.d.ts +20 -0
- package/dist/native-platform.d.ts.map +1 -0
- package/dist/native-platform.js +42 -0
- package/dist/native-platform.js.map +1 -0
- package/dist/oklab.d.ts +43 -0
- package/dist/oklab.d.ts.map +1 -0
- package/dist/oklab.js +210 -0
- package/dist/oklab.js.map +1 -0
- package/dist/stdin-buffer.d.ts +8 -2
- package/dist/stdin-buffer.d.ts.map +1 -1
- package/dist/stdin-buffer.js +7 -2
- package/dist/stdin-buffer.js.map +1 -1
- package/dist/terminal-colors.d.ts +19 -2
- package/dist/terminal-colors.d.ts.map +1 -1
- package/dist/terminal-colors.js +13 -8
- package/dist/terminal-colors.js.map +1 -1
- package/dist/terminal-image.d.ts +29 -1
- package/dist/terminal-image.d.ts.map +1 -1
- package/dist/terminal-image.js +223 -18
- package/dist/terminal-image.js.map +1 -1
- package/dist/terminal.d.ts +16 -0
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +62 -46
- package/dist/terminal.js.map +1 -1
- package/dist/tui-alt-screen.d.ts +166 -0
- package/dist/tui-alt-screen.d.ts.map +1 -0
- package/dist/tui-alt-screen.js +1546 -0
- package/dist/tui-alt-screen.js.map +1 -0
- package/dist/tui-main-screen.d.ts +39 -0
- package/dist/tui-main-screen.d.ts.map +1 -0
- package/dist/tui-main-screen.js +594 -0
- package/dist/tui-main-screen.js.map +1 -0
- package/dist/tui.d.ts +178 -60
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +303 -641
- package/dist/tui.js.map +1 -1
- package/dist/undo-stack.d.ts.map +1 -1
- package/dist/undo-stack.js.map +1 -1
- package/dist/utils.d.ts +22 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +245 -63
- package/dist/utils.js.map +1 -1
- package/dist/wheel-scroll.d.ts +23 -0
- package/dist/wheel-scroll.d.ts.map +1 -0
- package/dist/wheel-scroll.js +73 -0
- package/dist/wheel-scroll.js.map +1 -0
- package/dist/word-navigation.d.ts.map +1 -1
- package/dist/word-navigation.js.map +1 -1
- package/native/clipboard.h +208 -0
- package/native/darwin/README.md +22 -0
- package/native/darwin/build.sh +69 -0
- package/native/darwin/prebuilds/darwin-arm64/{darwin-modifiers.node → darwin-platform.node} +0 -0
- package/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/native/darwin/src/darwin-platform.m +132 -0
- package/native/linux/README.md +23 -0
- package/native/linux/build.sh +65 -0
- package/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
- package/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
- package/native/linux/src/clipboard-worker.h +70 -0
- package/native/linux/src/linux-platform-x11.c +274 -0
- package/native/napi.h +91 -0
- package/native/win32/README.md +31 -0
- package/native/win32/build.mjs +232 -0
- package/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
- package/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
- package/native/win32/src/win32-platform.c +241 -0
- package/package.json +19 -4
- package/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
- package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-view.js","sourceRoot":"","sources":["../../src/components/scroll-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { LAYOUT_NODE, type ScrollLayoutNode } from \"../layout-node.ts\";\nimport { type Component, Container } from \"../tui.ts\";\n\nexport type ScrollViewScrollbar = \"hidden\" | \"auto\" | \"always\";\n\nexport interface ScrollViewOptions {\n\taxis?: \"vertical\";\n\tfollow?: \"none\" | \"end\";\n\tprimary?: boolean;\n\toverscroll?: \"chain\" | \"contain\";\n\tscrollbar?: ScrollViewScrollbar;\n\tscrollbarTrackStyle?: (text: string) => string;\n\tscrollbarThumbStyle?: (text: string) => string;\n\tscrollbarHideDelayMs?: number;\n}\n\nexport interface ScrollViewScrollToOptions {\n\t/** Keep follow-end disabled even when the target is the current content end. */\n\tdisableFollow?: boolean;\n}\n\nexport class ScrollView extends Container {\n\tprivate readonly child: Component;\n\treadonly followEnd: boolean;\n\treadonly primary: boolean;\n\treadonly overscroll: \"chain\" | \"contain\";\n\treadonly scrollbarTrackStyle: (text: string) => string;\n\treadonly scrollbarThumbStyle: (text: string) => string;\n\tprivate currentScrollbar: ScrollViewScrollbar;\n\tprivate readonly scrollbarHideDelayMs: number;\n\tprivate currentScrollTop = 0;\n\tprivate contentHeight = 0;\n\tprivate currentViewportHeight = 0;\n\tprivate followingEnd: boolean;\n\tprivate followSuppressedAtEnd = false;\n\tprivate requestRenderCallback: (() => void) | undefined;\n\tprivate transientScrollbarVisible = false;\n\tprivate scrollbarActive = false;\n\tprivate scrollbarHideTimer: NodeJS.Timeout | undefined;\n\n\tconstructor(component: Component, options: ScrollViewOptions = {}) {\n\t\tsuper();\n\t\tif (options.axis !== undefined && options.axis !== \"vertical\") {\n\t\t\tthrow new Error(`Unsupported ScrollView axis: ${options.axis}`);\n\t\t}\n\t\tthis.child = component;\n\t\tthis.children.push(component);\n\t\tthis.followEnd = (options.follow ?? \"none\") === \"end\";\n\t\tthis.followingEnd = this.followEnd;\n\t\tthis.primary = options.primary ?? false;\n\t\tthis.overscroll = options.overscroll ?? \"chain\";\n\t\tthis.currentScrollbar = options.scrollbar ?? \"hidden\";\n\t\tthis.scrollbarTrackStyle = options.scrollbarTrackStyle ?? ((text) => `\\x1b[90m${text}\\x1b[39m`);\n\t\tthis.scrollbarThumbStyle = options.scrollbarThumbStyle ?? ((text) => `\\x1b[37m${text}\\x1b[39m`);\n\t\tthis.scrollbarHideDelayMs = Math.max(0, Math.floor(options.scrollbarHideDelayMs ?? 1000));\n\t}\n\n\tget scrollTop(): number {\n\t\treturn this.currentScrollTop;\n\t}\n\n\tget isFollowingEnd(): boolean {\n\t\treturn this.followingEnd;\n\t}\n\n\tget viewportHeight(): number {\n\t\treturn this.currentViewportHeight;\n\t}\n\n\tget scrollbar(): ScrollViewScrollbar {\n\t\treturn this.currentScrollbar;\n\t}\n\n\tget isScrollbarVisible(): boolean {\n\t\tif (this.scrollbar === \"always\") return this.currentViewportHeight > 0;\n\t\treturn (\n\t\t\tthis.scrollbar === \"auto\" && this.contentHeight > this.currentViewportHeight && this.transientScrollbarVisible\n\t\t);\n\t}\n\n\tget isScrollbarActive(): boolean {\n\t\treturn this.scrollbarActive;\n\t}\n\n\tsetScrollbar(scrollbar: ScrollViewScrollbar): void {\n\t\tif (scrollbar === this.currentScrollbar) return;\n\t\tthis.currentScrollbar = scrollbar;\n\t\tif (scrollbar !== \"auto\") this.hideTransientScrollbar();\n\t\telse if (this.scrollbarActive) this.markScrollbarActivity();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tgetContentWidth(width: number): number {\n\t\treturn this.scrollbar === \"always\" && width > 1 ? width - 1 : width;\n\t}\n\n\tprivate markScrollbarActivity(): void {\n\t\tif (this.scrollbar !== \"auto\" || this.contentHeight <= this.currentViewportHeight) return;\n\t\tthis.transientScrollbarVisible = true;\n\t\tif (this.scrollbarHideTimer) {\n\t\t\tclearTimeout(this.scrollbarHideTimer);\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t}\n\t\tif (this.scrollbarActive) return;\n\t\tthis.scrollbarHideTimer = setTimeout(() => {\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t\tthis.transientScrollbarVisible = false;\n\t\t\tthis.requestRenderCallback?.();\n\t\t}, this.scrollbarHideDelayMs);\n\t\tthis.scrollbarHideTimer.unref();\n\t}\n\n\tprivate hideTransientScrollbar(): void {\n\t\tthis.transientScrollbarVisible = false;\n\t\tif (!this.scrollbarHideTimer) return;\n\t\tclearTimeout(this.scrollbarHideTimer);\n\t\tthis.scrollbarHideTimer = undefined;\n\t}\n\n\tsetScrollbarActive(active: boolean): void {\n\t\tif (active === this.scrollbarActive) return;\n\t\tthis.scrollbarActive = active;\n\t\tthis.markScrollbarActivity();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tscrollTo(scrollTop: number, options: ScrollViewScrollToOptions = {}): void {\n\t\tconst requested = Number.isFinite(scrollTop) ? Math.trunc(scrollTop) : this.currentScrollTop;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, requested));\n\t\tconst nextFollowSuppressedAtEnd = options.disableFollow === true && next === maxScrollTop;\n\t\tconst nextFollowingEnd = !nextFollowSuppressedAtEnd && this.followEnd && next === maxScrollTop;\n\t\tif (\n\t\t\tnext === this.currentScrollTop &&\n\t\t\tnextFollowingEnd === this.followingEnd &&\n\t\t\tnextFollowSuppressedAtEnd === this.followSuppressedAtEnd\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tconst moved = next !== this.currentScrollTop;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = nextFollowingEnd;\n\t\tthis.followSuppressedAtEnd = nextFollowSuppressedAtEnd;\n\t\tif (moved) this.markScrollbarActivity();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tscrollBy(lines: number): number {\n\t\tconst requested = Number.isFinite(lines) ? Math.trunc(lines) : 0;\n\t\tif (requested === 0) return 0;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst start = this.followingEnd ? maxScrollTop : this.currentScrollTop;\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, start + requested));\n\t\tconst moved = next - start;\n\t\tconst wasFollowingEnd = this.followingEnd;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd && next === maxScrollTop;\n\t\tthis.followSuppressedAtEnd = false;\n\t\tif (moved !== 0) this.markScrollbarActivity();\n\t\tif (moved !== 0 || this.followingEnd !== wasFollowingEnd) this.requestRenderCallback?.();\n\t\treturn requested - moved;\n\t}\n\n\tscrollToStart(): void {\n\t\tconst changed =\n\t\t\tthis.currentScrollTop !== 0 ||\n\t\t\tthis.followingEnd !== (this.followEnd && this.contentHeight <= this.currentViewportHeight);\n\t\tthis.currentScrollTop = 0;\n\t\tthis.followingEnd = this.followEnd && this.contentHeight <= this.currentViewportHeight;\n\t\tthis.followSuppressedAtEnd = false;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.requestRenderCallback?.();\n\t\t}\n\t}\n\n\tscrollToEnd(): void {\n\t\tconst next = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst changed = this.currentScrollTop !== next || this.followingEnd !== this.followEnd;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd;\n\t\tthis.followSuppressedAtEnd = false;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.requestRenderCallback?.();\n\t\t}\n\t}\n\n\tupdateLayout(contentHeight: number, viewportHeight: number, requestRender: () => void): void {\n\t\tthis.contentHeight = Math.max(0, Math.floor(contentHeight));\n\t\tthis.currentViewportHeight = Math.max(0, Math.floor(viewportHeight));\n\t\tthis.requestRenderCallback = requestRender;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tif (this.followingEnd) this.currentScrollTop = maxScrollTop;\n\t\telse this.currentScrollTop = Math.max(0, Math.min(this.currentScrollTop, maxScrollTop));\n\t\tif (this.currentScrollTop < maxScrollTop) this.followSuppressedAtEnd = false;\n\t\tif (this.followEnd && this.currentScrollTop === maxScrollTop && !this.followSuppressedAtEnd) {\n\t\t\tthis.followingEnd = true;\n\t\t}\n\t\tif (this.contentHeight <= this.currentViewportHeight) this.hideTransientScrollbar();\n\t}\n\n\toverride addChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView has exactly one child\");\n\t}\n\n\toverride removeChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView child cannot be removed\");\n\t}\n\n\toverride clear(): void {\n\t\tthrow new Error(\"ScrollView child cannot be cleared\");\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst contentWidth = this.getContentWidth(width);\n\t\tconst lines = this.child.render(contentWidth);\n\t\treturn contentWidth === width ? lines : lines.map((line) => `${line} `);\n\t}\n\n\t[LAYOUT_NODE](): ScrollLayoutNode {\n\t\treturn { type: \"scroll\", component: this.child, state: this };\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Component } from "../tui.ts";
|
|
1
|
+
import type { Component, TuiMouseEvent, TuiMouseEventResult } from "../tui.ts";
|
|
2
2
|
export interface SelectItem {
|
|
3
3
|
value: string;
|
|
4
4
|
label: string;
|
|
@@ -27,6 +27,7 @@ export declare class SelectList implements Component {
|
|
|
27
27
|
private items;
|
|
28
28
|
private filteredItems;
|
|
29
29
|
private selectedIndex;
|
|
30
|
+
private mousePressedIndex;
|
|
30
31
|
private maxVisible;
|
|
31
32
|
private theme;
|
|
32
33
|
private layout;
|
|
@@ -38,7 +39,9 @@ export declare class SelectList implements Component {
|
|
|
38
39
|
setSelectedIndex(index: number): void;
|
|
39
40
|
invalidate(): void;
|
|
40
41
|
render(width: number): string[];
|
|
42
|
+
handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined;
|
|
41
43
|
handleInput(keyData: string): void;
|
|
44
|
+
private getVisibleRange;
|
|
42
45
|
private renderItem;
|
|
43
46
|
private getPrimaryColumnWidth;
|
|
44
47
|
private getPrimaryColumnBounds;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-list.d.ts","sourceRoot":"","sources":["../../src/components/select-list.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"select-list.d.ts","sourceRoot":"","sources":["../../src/components/select-list.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAU/E,MAAM,WAAW,UAAU;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC/B,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,gCAAgC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACvC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,gCAAgC,KAAK,MAAM,CAAC;CACxE;AAED,qBAAa,UAAW,YAAW,SAAS;IAC3C,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,MAAM,CAA0B;IAEjC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IAEtD,YAAY,KAAK,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,GAAE,uBAA4B,EAMhH;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAI9B;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEpC;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgC9B;IAED,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,mBAAmB,GAAG,SAAS,CAkCjE;IAED,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAyBjC;IAED,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,UAAU;IAuClB,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,qBAAqB;IAO7B,eAAe,IAAI,UAAU,GAAG,IAAI,CAGnC;CACD"}
|
|
@@ -9,6 +9,7 @@ export class SelectList {
|
|
|
9
9
|
items = [];
|
|
10
10
|
filteredItems = [];
|
|
11
11
|
selectedIndex = 0;
|
|
12
|
+
mousePressedIndex;
|
|
12
13
|
maxVisible = 5;
|
|
13
14
|
theme;
|
|
14
15
|
layout;
|
|
@@ -42,8 +43,7 @@ export class SelectList {
|
|
|
42
43
|
}
|
|
43
44
|
const primaryColumnWidth = this.getPrimaryColumnWidth();
|
|
44
45
|
// Calculate visible range with scrolling
|
|
45
|
-
const startIndex
|
|
46
|
-
const endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);
|
|
46
|
+
const { startIndex, endIndex } = this.getVisibleRange();
|
|
47
47
|
// Render visible items
|
|
48
48
|
for (let i = startIndex; i < endIndex; i++) {
|
|
49
49
|
const item = this.filteredItems[i];
|
|
@@ -61,6 +61,46 @@ export class SelectList {
|
|
|
61
61
|
}
|
|
62
62
|
return lines;
|
|
63
63
|
}
|
|
64
|
+
handleMouse(event) {
|
|
65
|
+
if (this.filteredItems.length === 0)
|
|
66
|
+
return undefined;
|
|
67
|
+
if (event.type === "wheel" && event.wheelDelta) {
|
|
68
|
+
const delta = event.wheelDelta < 0 ? -1 : 1;
|
|
69
|
+
const previousIndex = this.selectedIndex;
|
|
70
|
+
this.selectedIndex = Math.max(0, Math.min(this.filteredItems.length - 1, this.selectedIndex + delta));
|
|
71
|
+
if (this.selectedIndex !== previousIndex)
|
|
72
|
+
this.notifySelectionChange();
|
|
73
|
+
return { handled: true, render: this.selectedIndex !== previousIndex };
|
|
74
|
+
}
|
|
75
|
+
// Hover must not change selection: the visible range is centered on it.
|
|
76
|
+
if (event.button !== "left" || (event.type !== "press" && event.type !== "click"))
|
|
77
|
+
return undefined;
|
|
78
|
+
const { startIndex, endIndex } = this.getVisibleRange();
|
|
79
|
+
const itemIndex = startIndex + event.y;
|
|
80
|
+
if (itemIndex < startIndex || itemIndex >= endIndex)
|
|
81
|
+
return undefined;
|
|
82
|
+
if (event.type === "press") {
|
|
83
|
+
this.mousePressedIndex = itemIndex;
|
|
84
|
+
if (this.selectedIndex !== itemIndex) {
|
|
85
|
+
this.selectedIndex = itemIndex;
|
|
86
|
+
this.notifySelectionChange();
|
|
87
|
+
}
|
|
88
|
+
return { handled: true, focus: true };
|
|
89
|
+
}
|
|
90
|
+
if (event.type === "click") {
|
|
91
|
+
const clickedIndex = this.mousePressedIndex ?? itemIndex;
|
|
92
|
+
this.mousePressedIndex = undefined;
|
|
93
|
+
const changed = this.selectedIndex !== clickedIndex;
|
|
94
|
+
this.selectedIndex = clickedIndex;
|
|
95
|
+
if (changed)
|
|
96
|
+
this.notifySelectionChange();
|
|
97
|
+
const selectedItem = this.filteredItems[this.selectedIndex];
|
|
98
|
+
if (selectedItem)
|
|
99
|
+
this.onSelect?.(selectedItem);
|
|
100
|
+
return { handled: true };
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
64
104
|
handleInput(keyData) {
|
|
65
105
|
const kb = getKeybindings();
|
|
66
106
|
// Up arrow - wrap to bottom when at top
|
|
@@ -87,6 +127,13 @@ export class SelectList {
|
|
|
87
127
|
}
|
|
88
128
|
}
|
|
89
129
|
}
|
|
130
|
+
getVisibleRange() {
|
|
131
|
+
const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible));
|
|
132
|
+
return {
|
|
133
|
+
startIndex,
|
|
134
|
+
endIndex: Math.min(startIndex + this.maxVisible, this.filteredItems.length),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
90
137
|
renderItem(item, isSelected, width, descriptionSingleLine, primaryColumnWidth) {
|
|
91
138
|
const prefix = isSelected ? "→ " : " ";
|
|
92
139
|
const prefixWidth = visibleWidth(prefix);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-list.js","sourceRoot":"","sources":["../../src/components/select-list.ts"],"names":[],"mappings":"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","sourcesContent":["import { getKeybindings } from \"../keybindings.ts\";\nimport type { Component } from \"../tui.ts\";\nimport { truncateToWidth, visibleWidth } from \"../utils.ts\";\n\nconst DEFAULT_PRIMARY_COLUMN_WIDTH = 32;\nconst PRIMARY_COLUMN_GAP = 2;\nconst MIN_DESCRIPTION_WIDTH = 10;\n\nconst normalizeToSingleLine = (text: string): string => text.replace(/[\\r\\n]+/g, \" \").trim();\nconst clamp = (value: number, min: number, max: number): number => Math.max(min, Math.min(value, max));\n\nexport interface SelectItem {\n\tvalue: string;\n\tlabel: string;\n\tdescription?: string;\n}\n\nexport interface SelectListTheme {\n\tselectedPrefix: (text: string) => string;\n\tselectedText: (text: string) => string;\n\tdescription: (text: string) => string;\n\tscrollInfo: (text: string) => string;\n\tnoMatch: (text: string) => string;\n}\n\nexport interface SelectListTruncatePrimaryContext {\n\ttext: string;\n\tmaxWidth: number;\n\tcolumnWidth: number;\n\titem: SelectItem;\n\tisSelected: boolean;\n}\n\nexport interface SelectListLayoutOptions {\n\tminPrimaryColumnWidth?: number;\n\tmaxPrimaryColumnWidth?: number;\n\ttruncatePrimary?: (context: SelectListTruncatePrimaryContext) => string;\n}\n\nexport class SelectList implements Component {\n\tprivate items: SelectItem[] = [];\n\tprivate filteredItems: SelectItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate maxVisible: number = 5;\n\tprivate theme: SelectListTheme;\n\tprivate layout: SelectListLayoutOptions;\n\n\tpublic onSelect?: (item: SelectItem) => void;\n\tpublic onCancel?: () => void;\n\tpublic onSelectionChange?: (item: SelectItem) => void;\n\n\tconstructor(items: SelectItem[], maxVisible: number, theme: SelectListTheme, layout: SelectListLayoutOptions = {}) {\n\t\tthis.items = items;\n\t\tthis.filteredItems = items;\n\t\tthis.maxVisible = maxVisible;\n\t\tthis.theme = theme;\n\t\tthis.layout = layout;\n\t}\n\n\tsetFilter(filter: string): void {\n\t\tthis.filteredItems = this.items.filter((item) => item.value.toLowerCase().startsWith(filter.toLowerCase()));\n\t\t// Reset selection when filter changes\n\t\tthis.selectedIndex = 0;\n\t}\n\n\tsetSelectedIndex(index: number): void {\n\t\tthis.selectedIndex = Math.max(0, Math.min(index, this.filteredItems.length - 1));\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\t// If no items match filter, show message\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tlines.push(this.theme.noMatch(\" No matching commands\"));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst primaryColumnWidth = this.getPrimaryColumnWidth();\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\n\t\t// Render visible items\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst descriptionSingleLine = item.description ? normalizeToSingleLine(item.description) : undefined;\n\t\t\tlines.push(this.renderItem(item, isSelected, width, descriptionSingleLine, primaryColumnWidth));\n\t\t}\n\n\t\t// Add scroll indicators if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${this.filteredItems.length})`;\n\t\t\t// Truncate if too long for terminal\n\t\t\tlines.push(this.theme.scrollInfo(truncateToWidth(scrollText, width - 2, \"\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.notifySelectionChange();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.notifySelectionChange();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\t\tif (selectedItem && this.onSelect) {\n\t\t\t\tthis.onSelect(selectedItem);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate renderItem(\n\t\titem: SelectItem,\n\t\tisSelected: boolean,\n\t\twidth: number,\n\t\tdescriptionSingleLine: string | undefined,\n\t\tprimaryColumnWidth: number,\n\t): string {\n\t\tconst prefix = isSelected ? \"→ \" : \" \";\n\t\tconst prefixWidth = visibleWidth(prefix);\n\n\t\tif (descriptionSingleLine && width > 40) {\n\t\t\tconst effectivePrimaryColumnWidth = Math.max(1, Math.min(primaryColumnWidth, width - prefixWidth - 4));\n\t\t\tconst maxPrimaryWidth = Math.max(1, effectivePrimaryColumnWidth - PRIMARY_COLUMN_GAP);\n\t\t\tconst truncatedValue = this.truncatePrimary(item, isSelected, maxPrimaryWidth, effectivePrimaryColumnWidth);\n\t\t\tconst truncatedValueWidth = visibleWidth(truncatedValue);\n\t\t\tconst spacing = \" \".repeat(Math.max(1, effectivePrimaryColumnWidth - truncatedValueWidth));\n\t\t\tconst descriptionStart = prefixWidth + truncatedValueWidth + spacing.length;\n\t\t\tconst remainingWidth = width - descriptionStart - 2; // -2 for safety\n\n\t\t\tif (remainingWidth > MIN_DESCRIPTION_WIDTH) {\n\t\t\t\tconst truncatedDesc = truncateToWidth(descriptionSingleLine, remainingWidth, \"\");\n\t\t\t\tif (isSelected) {\n\t\t\t\t\treturn this.theme.selectedText(`${prefix}${truncatedValue}${spacing}${truncatedDesc}`);\n\t\t\t\t}\n\n\t\t\t\tconst descText = this.theme.description(spacing + truncatedDesc);\n\t\t\t\treturn prefix + truncatedValue + descText;\n\t\t\t}\n\t\t}\n\n\t\tconst maxWidth = width - prefixWidth - 2;\n\t\tconst truncatedValue = this.truncatePrimary(item, isSelected, maxWidth, maxWidth);\n\t\tif (isSelected) {\n\t\t\treturn this.theme.selectedText(`${prefix}${truncatedValue}`);\n\t\t}\n\n\t\treturn prefix + truncatedValue;\n\t}\n\n\tprivate getPrimaryColumnWidth(): number {\n\t\tconst { min, max } = this.getPrimaryColumnBounds();\n\t\tconst widestPrimary = this.filteredItems.reduce((widest, item) => {\n\t\t\treturn Math.max(widest, visibleWidth(this.getDisplayValue(item)) + PRIMARY_COLUMN_GAP);\n\t\t}, 0);\n\n\t\treturn clamp(widestPrimary, min, max);\n\t}\n\n\tprivate getPrimaryColumnBounds(): { min: number; max: number } {\n\t\tconst rawMin =\n\t\t\tthis.layout.minPrimaryColumnWidth ?? this.layout.maxPrimaryColumnWidth ?? DEFAULT_PRIMARY_COLUMN_WIDTH;\n\t\tconst rawMax =\n\t\t\tthis.layout.maxPrimaryColumnWidth ?? this.layout.minPrimaryColumnWidth ?? DEFAULT_PRIMARY_COLUMN_WIDTH;\n\n\t\treturn {\n\t\t\tmin: Math.max(1, Math.min(rawMin, rawMax)),\n\t\t\tmax: Math.max(1, Math.max(rawMin, rawMax)),\n\t\t};\n\t}\n\n\tprivate truncatePrimary(item: SelectItem, isSelected: boolean, maxWidth: number, columnWidth: number): string {\n\t\tconst displayValue = this.getDisplayValue(item);\n\t\tconst truncatedValue = this.layout.truncatePrimary\n\t\t\t? this.layout.truncatePrimary({\n\t\t\t\t\ttext: displayValue,\n\t\t\t\t\tmaxWidth,\n\t\t\t\t\tcolumnWidth,\n\t\t\t\t\titem,\n\t\t\t\t\tisSelected,\n\t\t\t\t})\n\t\t\t: truncateToWidth(displayValue, maxWidth, \"\");\n\n\t\treturn truncateToWidth(truncatedValue, maxWidth, \"\");\n\t}\n\n\tprivate getDisplayValue(item: SelectItem): string {\n\t\treturn item.label || item.value;\n\t}\n\n\tprivate notifySelectionChange(): void {\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem && this.onSelectionChange) {\n\t\t\tthis.onSelectionChange(selectedItem);\n\t\t}\n\t}\n\n\tgetSelectedItem(): SelectItem | null {\n\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\treturn item || null;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"select-list.js","sourceRoot":"","sources":["../../src/components/select-list.ts"],"names":[],"mappings":"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","sourcesContent":["import { getKeybindings } from \"../keybindings.ts\";\nimport type { Component, TuiMouseEvent, TuiMouseEventResult } from \"../tui.ts\";\nimport { truncateToWidth, visibleWidth } from \"../utils.ts\";\n\nconst DEFAULT_PRIMARY_COLUMN_WIDTH = 32;\nconst PRIMARY_COLUMN_GAP = 2;\nconst MIN_DESCRIPTION_WIDTH = 10;\n\nconst normalizeToSingleLine = (text: string): string => text.replace(/[\\r\\n]+/g, \" \").trim();\nconst clamp = (value: number, min: number, max: number): number => Math.max(min, Math.min(value, max));\n\nexport interface SelectItem {\n\tvalue: string;\n\tlabel: string;\n\tdescription?: string;\n}\n\nexport interface SelectListTheme {\n\tselectedPrefix: (text: string) => string;\n\tselectedText: (text: string) => string;\n\tdescription: (text: string) => string;\n\tscrollInfo: (text: string) => string;\n\tnoMatch: (text: string) => string;\n}\n\nexport interface SelectListTruncatePrimaryContext {\n\ttext: string;\n\tmaxWidth: number;\n\tcolumnWidth: number;\n\titem: SelectItem;\n\tisSelected: boolean;\n}\n\nexport interface SelectListLayoutOptions {\n\tminPrimaryColumnWidth?: number;\n\tmaxPrimaryColumnWidth?: number;\n\ttruncatePrimary?: (context: SelectListTruncatePrimaryContext) => string;\n}\n\nexport class SelectList implements Component {\n\tprivate items: SelectItem[] = [];\n\tprivate filteredItems: SelectItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate mousePressedIndex: number | undefined;\n\tprivate maxVisible: number = 5;\n\tprivate theme: SelectListTheme;\n\tprivate layout: SelectListLayoutOptions;\n\n\tpublic onSelect?: (item: SelectItem) => void;\n\tpublic onCancel?: () => void;\n\tpublic onSelectionChange?: (item: SelectItem) => void;\n\n\tconstructor(items: SelectItem[], maxVisible: number, theme: SelectListTheme, layout: SelectListLayoutOptions = {}) {\n\t\tthis.items = items;\n\t\tthis.filteredItems = items;\n\t\tthis.maxVisible = maxVisible;\n\t\tthis.theme = theme;\n\t\tthis.layout = layout;\n\t}\n\n\tsetFilter(filter: string): void {\n\t\tthis.filteredItems = this.items.filter((item) => item.value.toLowerCase().startsWith(filter.toLowerCase()));\n\t\t// Reset selection when filter changes\n\t\tthis.selectedIndex = 0;\n\t}\n\n\tsetSelectedIndex(index: number): void {\n\t\tthis.selectedIndex = Math.max(0, Math.min(index, this.filteredItems.length - 1));\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\t// If no items match filter, show message\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tlines.push(this.theme.noMatch(\" No matching commands\"));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst primaryColumnWidth = this.getPrimaryColumnWidth();\n\n\t\t// Calculate visible range with scrolling\n\t\tconst { startIndex, endIndex } = this.getVisibleRange();\n\n\t\t// Render visible items\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst descriptionSingleLine = item.description ? normalizeToSingleLine(item.description) : undefined;\n\t\t\tlines.push(this.renderItem(item, isSelected, width, descriptionSingleLine, primaryColumnWidth));\n\t\t}\n\n\t\t// Add scroll indicators if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${this.filteredItems.length})`;\n\t\t\t// Truncate if too long for terminal\n\t\t\tlines.push(this.theme.scrollInfo(truncateToWidth(scrollText, width - 2, \"\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined {\n\t\tif (this.filteredItems.length === 0) return undefined;\n\t\tif (event.type === \"wheel\" && event.wheelDelta) {\n\t\t\tconst delta = event.wheelDelta < 0 ? -1 : 1;\n\t\t\tconst previousIndex = this.selectedIndex;\n\t\t\tthis.selectedIndex = Math.max(0, Math.min(this.filteredItems.length - 1, this.selectedIndex + delta));\n\t\t\tif (this.selectedIndex !== previousIndex) this.notifySelectionChange();\n\t\t\treturn { handled: true, render: this.selectedIndex !== previousIndex };\n\t\t}\n\t\t// Hover must not change selection: the visible range is centered on it.\n\t\tif (event.button !== \"left\" || (event.type !== \"press\" && event.type !== \"click\")) return undefined;\n\t\tconst { startIndex, endIndex } = this.getVisibleRange();\n\t\tconst itemIndex = startIndex + event.y;\n\t\tif (itemIndex < startIndex || itemIndex >= endIndex) return undefined;\n\n\t\tif (event.type === \"press\") {\n\t\t\tthis.mousePressedIndex = itemIndex;\n\t\t\tif (this.selectedIndex !== itemIndex) {\n\t\t\t\tthis.selectedIndex = itemIndex;\n\t\t\t\tthis.notifySelectionChange();\n\t\t\t}\n\t\t\treturn { handled: true, focus: true };\n\t\t}\n\t\tif (event.type === \"click\") {\n\t\t\tconst clickedIndex = this.mousePressedIndex ?? itemIndex;\n\t\t\tthis.mousePressedIndex = undefined;\n\t\t\tconst changed = this.selectedIndex !== clickedIndex;\n\t\t\tthis.selectedIndex = clickedIndex;\n\t\t\tif (changed) this.notifySelectionChange();\n\t\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\t\tif (selectedItem) this.onSelect?.(selectedItem);\n\t\t\treturn { handled: true };\n\t\t}\n\t\treturn undefined;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.notifySelectionChange();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.notifySelectionChange();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\t\tif (selectedItem && this.onSelect) {\n\t\t\t\tthis.onSelect(selectedItem);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getVisibleRange(): { startIndex: number; endIndex: number } {\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\treturn {\n\t\t\tstartIndex,\n\t\t\tendIndex: Math.min(startIndex + this.maxVisible, this.filteredItems.length),\n\t\t};\n\t}\n\n\tprivate renderItem(\n\t\titem: SelectItem,\n\t\tisSelected: boolean,\n\t\twidth: number,\n\t\tdescriptionSingleLine: string | undefined,\n\t\tprimaryColumnWidth: number,\n\t): string {\n\t\tconst prefix = isSelected ? \"→ \" : \" \";\n\t\tconst prefixWidth = visibleWidth(prefix);\n\n\t\tif (descriptionSingleLine && width > 40) {\n\t\t\tconst effectivePrimaryColumnWidth = Math.max(1, Math.min(primaryColumnWidth, width - prefixWidth - 4));\n\t\t\tconst maxPrimaryWidth = Math.max(1, effectivePrimaryColumnWidth - PRIMARY_COLUMN_GAP);\n\t\t\tconst truncatedValue = this.truncatePrimary(item, isSelected, maxPrimaryWidth, effectivePrimaryColumnWidth);\n\t\t\tconst truncatedValueWidth = visibleWidth(truncatedValue);\n\t\t\tconst spacing = \" \".repeat(Math.max(1, effectivePrimaryColumnWidth - truncatedValueWidth));\n\t\t\tconst descriptionStart = prefixWidth + truncatedValueWidth + spacing.length;\n\t\t\tconst remainingWidth = width - descriptionStart - 2; // -2 for safety\n\n\t\t\tif (remainingWidth > MIN_DESCRIPTION_WIDTH) {\n\t\t\t\tconst truncatedDesc = truncateToWidth(descriptionSingleLine, remainingWidth, \"\");\n\t\t\t\tif (isSelected) {\n\t\t\t\t\treturn this.theme.selectedText(`${prefix}${truncatedValue}${spacing}${truncatedDesc}`);\n\t\t\t\t}\n\n\t\t\t\tconst descText = this.theme.description(spacing + truncatedDesc);\n\t\t\t\treturn prefix + truncatedValue + descText;\n\t\t\t}\n\t\t}\n\n\t\tconst maxWidth = width - prefixWidth - 2;\n\t\tconst truncatedValue = this.truncatePrimary(item, isSelected, maxWidth, maxWidth);\n\t\tif (isSelected) {\n\t\t\treturn this.theme.selectedText(`${prefix}${truncatedValue}`);\n\t\t}\n\n\t\treturn prefix + truncatedValue;\n\t}\n\n\tprivate getPrimaryColumnWidth(): number {\n\t\tconst { min, max } = this.getPrimaryColumnBounds();\n\t\tconst widestPrimary = this.filteredItems.reduce((widest, item) => {\n\t\t\treturn Math.max(widest, visibleWidth(this.getDisplayValue(item)) + PRIMARY_COLUMN_GAP);\n\t\t}, 0);\n\n\t\treturn clamp(widestPrimary, min, max);\n\t}\n\n\tprivate getPrimaryColumnBounds(): { min: number; max: number } {\n\t\tconst rawMin =\n\t\t\tthis.layout.minPrimaryColumnWidth ?? this.layout.maxPrimaryColumnWidth ?? DEFAULT_PRIMARY_COLUMN_WIDTH;\n\t\tconst rawMax =\n\t\t\tthis.layout.maxPrimaryColumnWidth ?? this.layout.minPrimaryColumnWidth ?? DEFAULT_PRIMARY_COLUMN_WIDTH;\n\n\t\treturn {\n\t\t\tmin: Math.max(1, Math.min(rawMin, rawMax)),\n\t\t\tmax: Math.max(1, Math.max(rawMin, rawMax)),\n\t\t};\n\t}\n\n\tprivate truncatePrimary(item: SelectItem, isSelected: boolean, maxWidth: number, columnWidth: number): string {\n\t\tconst displayValue = this.getDisplayValue(item);\n\t\tconst truncatedValue = this.layout.truncatePrimary\n\t\t\t? this.layout.truncatePrimary({\n\t\t\t\t\ttext: displayValue,\n\t\t\t\t\tmaxWidth,\n\t\t\t\t\tcolumnWidth,\n\t\t\t\t\titem,\n\t\t\t\t\tisSelected,\n\t\t\t\t})\n\t\t\t: truncateToWidth(displayValue, maxWidth, \"\");\n\n\t\treturn truncateToWidth(truncatedValue, maxWidth, \"\");\n\t}\n\n\tprivate getDisplayValue(item: SelectItem): string {\n\t\treturn item.label || item.value;\n\t}\n\n\tprivate notifySelectionChange(): void {\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem && this.onSelectionChange) {\n\t\t\tthis.onSelectionChange(selectedItem);\n\t\t}\n\t}\n\n\tgetSelectedItem(): SelectItem | null {\n\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\treturn item || null;\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Component } from "../tui.ts";
|
|
1
|
+
import type { Component, TuiMouseEvent, TuiMouseEventResult } from "../tui.ts";
|
|
2
2
|
export interface SettingItem {
|
|
3
3
|
/** Unique identifier for this setting */
|
|
4
4
|
id: string;
|
|
@@ -10,8 +10,11 @@ export interface SettingItem {
|
|
|
10
10
|
currentValue: string;
|
|
11
11
|
/** If provided, Enter/Space cycles through these values */
|
|
12
12
|
values?: string[];
|
|
13
|
-
/** If provided, Enter opens this submenu. Receives current value and done callback.
|
|
14
|
-
|
|
13
|
+
/** If provided, Enter opens this submenu. Receives current value and done callback.
|
|
14
|
+
* done() accepts an optional selectedValue and an optional navigateTo id to move the cursor after close. */
|
|
15
|
+
submenu?: (currentValue: string, done: (selectedValue?: string, options?: {
|
|
16
|
+
navigateTo?: string;
|
|
17
|
+
}) => void) => Component;
|
|
15
18
|
}
|
|
16
19
|
export interface SettingsListTheme {
|
|
17
20
|
label: (text: string, selected: boolean) => string;
|
|
@@ -28,6 +31,7 @@ export declare class SettingsList implements Component {
|
|
|
28
31
|
private filteredItems;
|
|
29
32
|
private theme;
|
|
30
33
|
private selectedIndex;
|
|
34
|
+
private mousePressedIndex;
|
|
31
35
|
private maxVisible;
|
|
32
36
|
private onChange;
|
|
33
37
|
private onCancel;
|
|
@@ -35,13 +39,19 @@ export declare class SettingsList implements Component {
|
|
|
35
39
|
private searchEnabled;
|
|
36
40
|
private submenuComponent;
|
|
37
41
|
private submenuItemIndex;
|
|
42
|
+
private navigateAfterClose;
|
|
38
43
|
constructor(items: SettingItem[], maxVisible: number, theme: SettingsListTheme, onChange: (id: string, newValue: string) => void, onCancel: () => void, options?: SettingsListOptions);
|
|
39
44
|
/** Update an item's currentValue */
|
|
40
45
|
updateValue(id: string, newValue: string): void;
|
|
46
|
+
/** Move selection to the item with the given id (no-op if not found). */
|
|
47
|
+
selectItem(id: string): void;
|
|
41
48
|
invalidate(): void;
|
|
42
49
|
render(width: number): string[];
|
|
43
50
|
private renderMainList;
|
|
51
|
+
handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined;
|
|
44
52
|
handleInput(data: string): void;
|
|
53
|
+
private getDisplayItems;
|
|
54
|
+
private getVisibleRange;
|
|
45
55
|
private activateItem;
|
|
46
56
|
private closeSubmenu;
|
|
47
57
|
private applyFilter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-list.d.ts","sourceRoot":"","sources":["../../src/components/settings-list.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAI3C,MAAM,WAAW,WAAW;IAC3B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,uFAAuF;IACvF,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,aAAa,CAAC,EAAE,MAAM,KAAK,IAAI,KAAK,SAAS,CAAC;CACtF;AAED,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;IACnD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;IACnD,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IACnC,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qBAAa,YAAa,YAAW,SAAS;IAC7C,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAyC;IACzD,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,WAAW,CAAC,CAAQ;IAC5B,OAAO,CAAC,aAAa,CAAU;IAG/B,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,gBAAgB,CAAuB;IAE/C,YACC,KAAK,EAAE,WAAW,EAAE,EACpB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,iBAAiB,EACxB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,EAChD,QAAQ,EAAE,MAAM,IAAI,EACpB,OAAO,GAAE,mBAAwB,EAYjC;IAED,oCAAoC;IACpC,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAK9C;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAO9B;IAED,OAAO,CAAC,cAAc;IA8EtB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA6B9B;IAED,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,WAAW;CAanB","sourcesContent":["import { fuzzyFilter } from \"../fuzzy.ts\";\nimport { getKeybindings } from \"../keybindings.ts\";\nimport type { Component } from \"../tui.ts\";\nimport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"../utils.ts\";\nimport { Input } from \"./input.ts\";\n\nexport interface SettingItem {\n\t/** Unique identifier for this setting */\n\tid: string;\n\t/** Display label (left side) */\n\tlabel: string;\n\t/** Optional description shown when selected */\n\tdescription?: string;\n\t/** Current value to display (right side) */\n\tcurrentValue: string;\n\t/** If provided, Enter/Space cycles through these values */\n\tvalues?: string[];\n\t/** If provided, Enter opens this submenu. Receives current value and done callback. */\n\tsubmenu?: (currentValue: string, done: (selectedValue?: string) => void) => Component;\n}\n\nexport interface SettingsListTheme {\n\tlabel: (text: string, selected: boolean) => string;\n\tvalue: (text: string, selected: boolean) => string;\n\tdescription: (text: string) => string;\n\tcursor: string;\n\thint: (text: string) => string;\n}\n\nexport interface SettingsListOptions {\n\tenableSearch?: boolean;\n}\n\nexport class SettingsList implements Component {\n\tprivate items: SettingItem[];\n\tprivate filteredItems: SettingItem[];\n\tprivate theme: SettingsListTheme;\n\tprivate selectedIndex = 0;\n\tprivate maxVisible: number;\n\tprivate onChange: (id: string, newValue: string) => void;\n\tprivate onCancel: () => void;\n\tprivate searchInput?: Input;\n\tprivate searchEnabled: boolean;\n\n\t// Submenu state\n\tprivate submenuComponent: Component | null = null;\n\tprivate submenuItemIndex: number | null = null;\n\n\tconstructor(\n\t\titems: SettingItem[],\n\t\tmaxVisible: number,\n\t\ttheme: SettingsListTheme,\n\t\tonChange: (id: string, newValue: string) => void,\n\t\tonCancel: () => void,\n\t\toptions: SettingsListOptions = {},\n\t) {\n\t\tthis.items = items;\n\t\tthis.filteredItems = items;\n\t\tthis.maxVisible = maxVisible;\n\t\tthis.theme = theme;\n\t\tthis.onChange = onChange;\n\t\tthis.onCancel = onCancel;\n\t\tthis.searchEnabled = options.enableSearch ?? false;\n\t\tif (this.searchEnabled) {\n\t\t\tthis.searchInput = new Input();\n\t\t}\n\t}\n\n\t/** Update an item's currentValue */\n\tupdateValue(id: string, newValue: string): void {\n\t\tconst item = this.items.find((i) => i.id === id);\n\t\tif (item) {\n\t\t\titem.currentValue = newValue;\n\t\t}\n\t}\n\n\tinvalidate(): void {\n\t\tthis.submenuComponent?.invalidate?.();\n\t}\n\n\trender(width: number): string[] {\n\t\t// If submenu is active, render it instead\n\t\tif (this.submenuComponent) {\n\t\t\treturn this.submenuComponent.render(width);\n\t\t}\n\n\t\treturn this.renderMainList(width);\n\t}\n\n\tprivate renderMainList(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.searchEnabled && this.searchInput) {\n\t\t\tlines.push(...this.searchInput.render(width));\n\t\t\tlines.push(\"\");\n\t\t}\n\n\t\tif (this.items.length === 0) {\n\t\t\tlines.push(this.theme.hint(\" No settings available\"));\n\t\t\tif (this.searchEnabled) {\n\t\t\t\tthis.addHintLine(lines, width);\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst displayItems = this.searchEnabled ? this.filteredItems : this.items;\n\t\tif (displayItems.length === 0) {\n\t\t\tlines.push(truncateToWidth(this.theme.hint(\" No matching settings\"), width));\n\t\t\tthis.addHintLine(lines, width);\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), displayItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, displayItems.length);\n\n\t\t// Calculate max label width for alignment\n\t\tconst maxLabelWidth = Math.min(30, Math.max(...this.items.map((item) => visibleWidth(item.label))));\n\n\t\t// Render visible items\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = displayItems[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? this.theme.cursor : \" \";\n\t\t\tconst prefixWidth = visibleWidth(prefix);\n\n\t\t\t// Pad label to align values\n\t\t\tconst labelPadded = item.label + \" \".repeat(Math.max(0, maxLabelWidth - visibleWidth(item.label)));\n\t\t\tconst labelText = this.theme.label(labelPadded, isSelected);\n\n\t\t\t// Calculate space for value\n\t\t\tconst separator = \" \";\n\t\t\tconst usedWidth = prefixWidth + maxLabelWidth + visibleWidth(separator);\n\t\t\tconst valueMaxWidth = width - usedWidth - 2;\n\n\t\t\tconst valueText = this.theme.value(truncateToWidth(item.currentValue, valueMaxWidth, \"\"), isSelected);\n\n\t\t\tlines.push(truncateToWidth(prefix + labelText + separator + valueText, width));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < displayItems.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${displayItems.length})`;\n\t\t\tlines.push(this.theme.hint(truncateToWidth(scrollText, width - 2, \"\")));\n\t\t}\n\n\t\t// Add description for selected item\n\t\tconst selectedItem = displayItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tlines.push(\"\");\n\t\t\tconst wrappedDesc = wrapTextWithAnsi(selectedItem.description, width - 4);\n\t\t\tfor (const line of wrappedDesc) {\n\t\t\t\tlines.push(this.theme.description(` ${line}`));\n\t\t\t}\n\t\t}\n\n\t\t// Add hint\n\t\tthis.addHintLine(lines, width);\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\t// If submenu is active, delegate all input to it\n\t\t// The submenu's onCancel (triggered by escape) will call done() which closes it\n\t\tif (this.submenuComponent) {\n\t\t\tthis.submenuComponent.handleInput?.(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Main list input handling\n\t\tconst kb = getKeybindings();\n\t\tconst displayItems = this.searchEnabled ? this.filteredItems : this.items;\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? displayItems.length - 1 : this.selectedIndex - 1;\n\t\t} else if (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === displayItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t} else if (kb.matches(data, \"tui.select.confirm\") || data === \" \") {\n\t\t\tthis.activateItem();\n\t\t} else if (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancel();\n\t\t} else if (this.searchEnabled && this.searchInput) {\n\t\t\tconst sanitized = data.replace(/ /g, \"\");\n\t\t\tif (!sanitized) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.searchInput.handleInput(sanitized);\n\t\t\tthis.applyFilter(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate activateItem(): void {\n\t\tconst item = this.searchEnabled ? this.filteredItems[this.selectedIndex] : this.items[this.selectedIndex];\n\t\tif (!item) return;\n\n\t\tif (item.submenu) {\n\t\t\t// Open submenu, passing current value so it can pre-select correctly\n\t\t\tthis.submenuItemIndex = this.selectedIndex;\n\t\t\tthis.submenuComponent = item.submenu(item.currentValue, (selectedValue?: string) => {\n\t\t\t\tif (selectedValue !== undefined) {\n\t\t\t\t\titem.currentValue = selectedValue;\n\t\t\t\t\tthis.onChange(item.id, selectedValue);\n\t\t\t\t}\n\t\t\t\tthis.closeSubmenu();\n\t\t\t});\n\t\t} else if (item.values && item.values.length > 0) {\n\t\t\t// Cycle through values\n\t\t\tconst currentIndex = item.values.indexOf(item.currentValue);\n\t\t\tconst nextIndex = (currentIndex + 1) % item.values.length;\n\t\t\tconst newValue = item.values[nextIndex];\n\t\t\titem.currentValue = newValue;\n\t\t\tthis.onChange(item.id, newValue);\n\t\t}\n\t}\n\n\tprivate closeSubmenu(): void {\n\t\tthis.submenuComponent = null;\n\t\t// Restore selection to the item that opened the submenu\n\t\tif (this.submenuItemIndex !== null) {\n\t\t\tthis.selectedIndex = this.submenuItemIndex;\n\t\t\tthis.submenuItemIndex = null;\n\t\t}\n\t}\n\n\tprivate applyFilter(query: string): void {\n\t\tthis.filteredItems = fuzzyFilter(this.items, query, (item) => item.label);\n\t\tthis.selectedIndex = 0;\n\t}\n\n\tprivate addHintLine(lines: string[], width: number): void {\n\t\tlines.push(\"\");\n\t\tlines.push(\n\t\t\ttruncateToWidth(\n\t\t\t\tthis.theme.hint(\n\t\t\t\t\tthis.searchEnabled\n\t\t\t\t\t\t? \" Type to search · Enter/Space to change · Esc to cancel\"\n\t\t\t\t\t\t: \" Enter/Space to change · Esc to cancel\",\n\t\t\t\t),\n\t\t\t\twidth,\n\t\t\t),\n\t\t);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"settings-list.d.ts","sourceRoot":"","sources":["../../src/components/settings-list.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAI/E,MAAM,WAAW,WAAW;IAC3B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;iHAC6G;IAC7G,OAAO,CAAC,EAAE,CACT,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,KACrE,SAAS,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;IACnD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;IACnD,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IACnC,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qBAAa,YAAa,YAAW,SAAS;IAC7C,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAyC;IACzD,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,WAAW,CAAC,CAAQ;IAC5B,OAAO,CAAC,aAAa,CAAU;IAG/B,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,kBAAkB,CAAuB;IAEjD,YACC,KAAK,EAAE,WAAW,EAAE,EACpB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,iBAAiB,EACxB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,EAChD,QAAQ,EAAE,MAAM,IAAI,EACpB,OAAO,GAAE,mBAAwB,EAYjC;IAED,oCAAoC;IACpC,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAK9C;IAED,yEAAyE;IACzE,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAM3B;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAO9B;IAED,OAAO,CAAC,cAAc;IA0EtB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,mBAAmB,GAAG,SAAS,CAyCjE;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA4B9B;IAED,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,YAAY;IA8BpB,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,WAAW;CAanB"}
|
|
@@ -7,6 +7,7 @@ export class SettingsList {
|
|
|
7
7
|
filteredItems;
|
|
8
8
|
theme;
|
|
9
9
|
selectedIndex = 0;
|
|
10
|
+
mousePressedIndex;
|
|
10
11
|
maxVisible;
|
|
11
12
|
onChange;
|
|
12
13
|
onCancel;
|
|
@@ -15,6 +16,7 @@ export class SettingsList {
|
|
|
15
16
|
// Submenu state
|
|
16
17
|
submenuComponent = null;
|
|
17
18
|
submenuItemIndex = null;
|
|
19
|
+
navigateAfterClose = null;
|
|
18
20
|
constructor(items, maxVisible, theme, onChange, onCancel, options = {}) {
|
|
19
21
|
this.items = items;
|
|
20
22
|
this.filteredItems = items;
|
|
@@ -34,6 +36,14 @@ export class SettingsList {
|
|
|
34
36
|
item.currentValue = newValue;
|
|
35
37
|
}
|
|
36
38
|
}
|
|
39
|
+
/** Move selection to the item with the given id (no-op if not found). */
|
|
40
|
+
selectItem(id) {
|
|
41
|
+
const items = this.searchEnabled ? this.filteredItems : this.items;
|
|
42
|
+
const index = items.findIndex((i) => i.id === id);
|
|
43
|
+
if (index !== -1) {
|
|
44
|
+
this.selectedIndex = index;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
37
47
|
invalidate() {
|
|
38
48
|
this.submenuComponent?.invalidate?.();
|
|
39
49
|
}
|
|
@@ -57,17 +67,16 @@ export class SettingsList {
|
|
|
57
67
|
}
|
|
58
68
|
return lines;
|
|
59
69
|
}
|
|
60
|
-
const displayItems = this.
|
|
70
|
+
const displayItems = this.getDisplayItems();
|
|
61
71
|
if (displayItems.length === 0) {
|
|
62
72
|
lines.push(truncateToWidth(this.theme.hint(" No matching settings"), width));
|
|
63
73
|
this.addHintLine(lines, width);
|
|
64
74
|
return lines;
|
|
65
75
|
}
|
|
66
76
|
// Calculate visible range with scrolling
|
|
67
|
-
const startIndex =
|
|
68
|
-
const endIndex = Math.min(startIndex + this.maxVisible, displayItems.length);
|
|
77
|
+
const { startIndex, endIndex } = this.getVisibleRange(displayItems);
|
|
69
78
|
// Calculate max label width for alignment
|
|
70
|
-
const maxLabelWidth = Math.min(
|
|
79
|
+
const maxLabelWidth = Math.min(36, Math.max(...this.items.map((item) => visibleWidth(item.label))));
|
|
71
80
|
// Render visible items
|
|
72
81
|
for (let i = startIndex; i < endIndex; i++) {
|
|
73
82
|
const item = displayItems[i];
|
|
@@ -104,6 +113,49 @@ export class SettingsList {
|
|
|
104
113
|
this.addHintLine(lines, width);
|
|
105
114
|
return lines;
|
|
106
115
|
}
|
|
116
|
+
handleMouse(event) {
|
|
117
|
+
if (this.submenuComponent) {
|
|
118
|
+
const result = this.submenuComponent.handleMouse?.(event);
|
|
119
|
+
return result ? { ...result, focus: true } : undefined;
|
|
120
|
+
}
|
|
121
|
+
if (this.searchEnabled && this.searchInput) {
|
|
122
|
+
if (event.y === 0) {
|
|
123
|
+
const result = this.searchInput.handleMouse?.(event);
|
|
124
|
+
return result ? { ...result, focus: true } : undefined;
|
|
125
|
+
}
|
|
126
|
+
if (event.y === 1)
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const displayItems = this.getDisplayItems();
|
|
130
|
+
if (displayItems.length === 0)
|
|
131
|
+
return undefined;
|
|
132
|
+
if (event.type === "wheel" && event.wheelDelta) {
|
|
133
|
+
const delta = event.wheelDelta < 0 ? -1 : 1;
|
|
134
|
+
const previousIndex = this.selectedIndex;
|
|
135
|
+
this.selectedIndex = Math.max(0, Math.min(displayItems.length - 1, this.selectedIndex + delta));
|
|
136
|
+
return { handled: true, render: this.selectedIndex !== previousIndex };
|
|
137
|
+
}
|
|
138
|
+
// Hover must not change selection: the visible range is centered on it.
|
|
139
|
+
if (event.button !== "left" || (event.type !== "press" && event.type !== "click"))
|
|
140
|
+
return undefined;
|
|
141
|
+
const rowOffset = this.searchEnabled ? 2 : 0;
|
|
142
|
+
const { startIndex, endIndex } = this.getVisibleRange(displayItems);
|
|
143
|
+
const itemIndex = startIndex + event.y - rowOffset;
|
|
144
|
+
if (itemIndex < startIndex || itemIndex >= endIndex)
|
|
145
|
+
return undefined;
|
|
146
|
+
if (event.type === "press") {
|
|
147
|
+
this.mousePressedIndex = itemIndex;
|
|
148
|
+
this.selectedIndex = itemIndex;
|
|
149
|
+
return { handled: true, focus: true };
|
|
150
|
+
}
|
|
151
|
+
if (event.type === "click") {
|
|
152
|
+
this.selectedIndex = this.mousePressedIndex ?? itemIndex;
|
|
153
|
+
this.mousePressedIndex = undefined;
|
|
154
|
+
this.activateItem();
|
|
155
|
+
return { handled: true };
|
|
156
|
+
}
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
107
159
|
handleInput(data) {
|
|
108
160
|
// If submenu is active, delegate all input to it
|
|
109
161
|
// The submenu's onCancel (triggered by escape) will call done() which closes it
|
|
@@ -113,7 +165,7 @@ export class SettingsList {
|
|
|
113
165
|
}
|
|
114
166
|
// Main list input handling
|
|
115
167
|
const kb = getKeybindings();
|
|
116
|
-
const displayItems = this.
|
|
168
|
+
const displayItems = this.getDisplayItems();
|
|
117
169
|
if (kb.matches(data, "tui.select.up")) {
|
|
118
170
|
if (displayItems.length === 0)
|
|
119
171
|
return;
|
|
@@ -124,33 +176,40 @@ export class SettingsList {
|
|
|
124
176
|
return;
|
|
125
177
|
this.selectedIndex = this.selectedIndex === displayItems.length - 1 ? 0 : this.selectedIndex + 1;
|
|
126
178
|
}
|
|
127
|
-
else if (kb.matches(data, "tui.select.confirm") ||
|
|
179
|
+
else if (kb.matches(data, "tui.select.confirm") ||
|
|
180
|
+
(data === " " && (!this.searchEnabled || this.searchInput?.getValue().length === 0))) {
|
|
128
181
|
this.activateItem();
|
|
129
182
|
}
|
|
130
183
|
else if (kb.matches(data, "tui.select.cancel")) {
|
|
131
184
|
this.onCancel();
|
|
132
185
|
}
|
|
133
186
|
else if (this.searchEnabled && this.searchInput) {
|
|
134
|
-
|
|
135
|
-
if (!sanitized) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
this.searchInput.handleInput(sanitized);
|
|
187
|
+
this.searchInput.handleInput(data);
|
|
139
188
|
this.applyFilter(this.searchInput.getValue());
|
|
140
189
|
}
|
|
141
190
|
}
|
|
191
|
+
getDisplayItems() {
|
|
192
|
+
return this.searchEnabled ? this.filteredItems : this.items;
|
|
193
|
+
}
|
|
194
|
+
getVisibleRange(displayItems) {
|
|
195
|
+
const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisible / 2), displayItems.length - this.maxVisible));
|
|
196
|
+
return { startIndex, endIndex: Math.min(startIndex + this.maxVisible, displayItems.length) };
|
|
197
|
+
}
|
|
142
198
|
activateItem() {
|
|
143
|
-
const item = this.
|
|
199
|
+
const item = this.getDisplayItems()[this.selectedIndex];
|
|
144
200
|
if (!item)
|
|
145
201
|
return;
|
|
146
202
|
if (item.submenu) {
|
|
147
203
|
// Open submenu, passing current value so it can pre-select correctly
|
|
148
204
|
this.submenuItemIndex = this.selectedIndex;
|
|
149
|
-
this.submenuComponent = item.submenu(item.currentValue, (selectedValue) => {
|
|
205
|
+
this.submenuComponent = item.submenu(item.currentValue, (selectedValue, options) => {
|
|
150
206
|
if (selectedValue !== undefined) {
|
|
151
207
|
item.currentValue = selectedValue;
|
|
152
208
|
this.onChange(item.id, selectedValue);
|
|
153
209
|
}
|
|
210
|
+
if (options?.navigateTo) {
|
|
211
|
+
this.navigateAfterClose = options.navigateTo;
|
|
212
|
+
}
|
|
154
213
|
this.closeSubmenu();
|
|
155
214
|
});
|
|
156
215
|
}
|
|
@@ -165,8 +224,16 @@ export class SettingsList {
|
|
|
165
224
|
}
|
|
166
225
|
closeSubmenu() {
|
|
167
226
|
this.submenuComponent = null;
|
|
168
|
-
|
|
169
|
-
|
|
227
|
+
if (this.navigateAfterClose !== null) {
|
|
228
|
+
const id = this.navigateAfterClose;
|
|
229
|
+
this.navigateAfterClose = null;
|
|
230
|
+
this.submenuItemIndex = null;
|
|
231
|
+
this.selectItem(id);
|
|
232
|
+
// Open the target item's submenu automatically
|
|
233
|
+
this.activateItem();
|
|
234
|
+
}
|
|
235
|
+
else if (this.submenuItemIndex !== null) {
|
|
236
|
+
// Restore selection to the item that opened the submenu
|
|
170
237
|
this.selectedIndex = this.submenuItemIndex;
|
|
171
238
|
this.submenuItemIndex = null;
|
|
172
239
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-list.js","sourceRoot":"","sources":["../../src/components/settings-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AA6BnC,MAAM,OAAO,YAAY;IAChB,KAAK,CAAgB;IACrB,aAAa,CAAgB;IAC7B,KAAK,CAAoB;IACzB,aAAa,GAAG,CAAC,CAAC;IAClB,UAAU,CAAS;IACnB,QAAQ,CAAyC;IACjD,QAAQ,CAAa;IACrB,WAAW,CAAS;IACpB,aAAa,CAAU;IAE/B,gBAAgB;IACR,gBAAgB,GAAqB,IAAI,CAAC;IAC1C,gBAAgB,GAAkB,IAAI,CAAC;IAE/C,YACC,KAAoB,EACpB,UAAkB,EAClB,KAAwB,EACxB,QAAgD,EAChD,QAAoB,EACpB,OAAO,GAAwB,EAAE,EAChC;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,YAAY,IAAI,KAAK,CAAC;QACnD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,IAAI,CAAC,WAAW,GAAG,IAAI,KAAK,EAAE,CAAC;QAChC,CAAC;IAAA,CACD;IAED,oCAAoC;IACpC,WAAW,CAAC,EAAU,EAAE,QAAgB,EAAQ;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACjD,IAAI,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;QAC9B,CAAC;IAAA,CACD;IAED,UAAU,GAAS;QAClB,IAAI,CAAC,gBAAgB,EAAE,UAAU,EAAE,EAAE,CAAC;IAAA,CACtC;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,0CAA0C;QAC1C,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAAA,CAClC;IAEO,cAAc,CAAC,KAAa,EAAY;QAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC;YACvD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACxB,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAChC,CAAC;YACD,OAAO,KAAK,CAAC;QACd,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;QAC1E,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;YAC9E,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC/B,OAAO,KAAK,CAAC;QACd,CAAC;QAED,yCAAyC;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAC1B,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,CACrG,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;QAE7E,0CAA0C;QAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAEpG,uBAAuB;QACvB,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YAC7B,IAAI,CAAC,IAAI;gBAAE,SAAS;YAEpB,MAAM,UAAU,GAAG,CAAC,KAAK,IAAI,CAAC,aAAa,CAAC;YAC5C,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;YACrD,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAEzC,4BAA4B;YAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACnG,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;YAE5D,4BAA4B;YAC5B,MAAM,SAAS,GAAG,IAAI,CAAC;YACvB,MAAM,SAAS,GAAG,WAAW,GAAG,aAAa,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YACxE,MAAM,aAAa,GAAG,KAAK,GAAG,SAAS,GAAG,CAAC,CAAC;YAE5C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,EAAE,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;YAEtG,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;QAChF,CAAC;QAED,iCAAiC;QACjC,IAAI,UAAU,GAAG,CAAC,IAAI,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;YACtD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,aAAa,GAAG,CAAC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;YAC1E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACzE,CAAC;QAED,oCAAoC;QACpC,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACtD,IAAI,YAAY,EAAE,WAAW,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,MAAM,WAAW,GAAG,gBAAgB,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YAC1E,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;gBAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;YACjD,CAAC;QACF,CAAC;QAED,WAAW;QACX,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAE/B,OAAO,KAAK,CAAC;IAAA,CACb;IAED,WAAW,CAAC,IAAY,EAAQ;QAC/B,iDAAiD;QACjD,gFAAgF;QAChF,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC;YAC1C,OAAO;QACR,CAAC;QAED,2BAA2B;QAC3B,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;QAC1E,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC;YACvC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YACtC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;QAClG,CAAC;aAAM,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,CAAC;YAChD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YACtC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;QAClG,CAAC;aAAM,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,oBAAoB,CAAC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACnE,IAAI,CAAC,YAAY,EAAE,CAAC;QACrB,CAAC;aAAM,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,mBAAmB,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjB,CAAC;aAAM,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACzC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAChB,OAAO;YACR,CAAC;YACD,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YACxC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/C,CAAC;IAAA,CACD;IAEO,YAAY,GAAS;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC1G,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,qEAAqE;YACrE,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,aAAa,CAAC;YAC3C,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,aAAsB,EAAE,EAAE,CAAC;gBACnF,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;oBACjC,IAAI,CAAC,YAAY,GAAG,aAAa,CAAC;oBAClC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;gBACvC,CAAC;gBACD,IAAI,CAAC,YAAY,EAAE,CAAC;YAAA,CACpB,CAAC,CAAC;QACJ,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,uBAAuB;YACvB,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC5D,MAAM,SAAS,GAAG,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;YAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACxC,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;YAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAClC,CAAC;IAAA,CACD;IAEO,YAAY,GAAS;QAC5B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,wDAAwD;QACxD,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;YACpC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC;YAC3C,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC9B,CAAC;IAAA,CACD;IAEO,WAAW,CAAC,KAAa,EAAQ;QACxC,IAAI,CAAC,aAAa,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1E,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;IAAA,CACvB;IAEO,WAAW,CAAC,KAAe,EAAE,KAAa,EAAQ;QACzD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CACT,eAAe,CACd,IAAI,CAAC,KAAK,CAAC,IAAI,CACd,IAAI,CAAC,aAAa;YACjB,CAAC,CAAC,4DAA0D;YAC5D,CAAC,CAAC,0CAAyC,CAC5C,EACD,KAAK,CACL,CACD,CAAC;IAAA,CACF;CACD","sourcesContent":["import { fuzzyFilter } from \"../fuzzy.ts\";\nimport { getKeybindings } from \"../keybindings.ts\";\nimport type { Component } from \"../tui.ts\";\nimport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"../utils.ts\";\nimport { Input } from \"./input.ts\";\n\nexport interface SettingItem {\n\t/** Unique identifier for this setting */\n\tid: string;\n\t/** Display label (left side) */\n\tlabel: string;\n\t/** Optional description shown when selected */\n\tdescription?: string;\n\t/** Current value to display (right side) */\n\tcurrentValue: string;\n\t/** If provided, Enter/Space cycles through these values */\n\tvalues?: string[];\n\t/** If provided, Enter opens this submenu. Receives current value and done callback. */\n\tsubmenu?: (currentValue: string, done: (selectedValue?: string) => void) => Component;\n}\n\nexport interface SettingsListTheme {\n\tlabel: (text: string, selected: boolean) => string;\n\tvalue: (text: string, selected: boolean) => string;\n\tdescription: (text: string) => string;\n\tcursor: string;\n\thint: (text: string) => string;\n}\n\nexport interface SettingsListOptions {\n\tenableSearch?: boolean;\n}\n\nexport class SettingsList implements Component {\n\tprivate items: SettingItem[];\n\tprivate filteredItems: SettingItem[];\n\tprivate theme: SettingsListTheme;\n\tprivate selectedIndex = 0;\n\tprivate maxVisible: number;\n\tprivate onChange: (id: string, newValue: string) => void;\n\tprivate onCancel: () => void;\n\tprivate searchInput?: Input;\n\tprivate searchEnabled: boolean;\n\n\t// Submenu state\n\tprivate submenuComponent: Component | null = null;\n\tprivate submenuItemIndex: number | null = null;\n\n\tconstructor(\n\t\titems: SettingItem[],\n\t\tmaxVisible: number,\n\t\ttheme: SettingsListTheme,\n\t\tonChange: (id: string, newValue: string) => void,\n\t\tonCancel: () => void,\n\t\toptions: SettingsListOptions = {},\n\t) {\n\t\tthis.items = items;\n\t\tthis.filteredItems = items;\n\t\tthis.maxVisible = maxVisible;\n\t\tthis.theme = theme;\n\t\tthis.onChange = onChange;\n\t\tthis.onCancel = onCancel;\n\t\tthis.searchEnabled = options.enableSearch ?? false;\n\t\tif (this.searchEnabled) {\n\t\t\tthis.searchInput = new Input();\n\t\t}\n\t}\n\n\t/** Update an item's currentValue */\n\tupdateValue(id: string, newValue: string): void {\n\t\tconst item = this.items.find((i) => i.id === id);\n\t\tif (item) {\n\t\t\titem.currentValue = newValue;\n\t\t}\n\t}\n\n\tinvalidate(): void {\n\t\tthis.submenuComponent?.invalidate?.();\n\t}\n\n\trender(width: number): string[] {\n\t\t// If submenu is active, render it instead\n\t\tif (this.submenuComponent) {\n\t\t\treturn this.submenuComponent.render(width);\n\t\t}\n\n\t\treturn this.renderMainList(width);\n\t}\n\n\tprivate renderMainList(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.searchEnabled && this.searchInput) {\n\t\t\tlines.push(...this.searchInput.render(width));\n\t\t\tlines.push(\"\");\n\t\t}\n\n\t\tif (this.items.length === 0) {\n\t\t\tlines.push(this.theme.hint(\" No settings available\"));\n\t\t\tif (this.searchEnabled) {\n\t\t\t\tthis.addHintLine(lines, width);\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst displayItems = this.searchEnabled ? this.filteredItems : this.items;\n\t\tif (displayItems.length === 0) {\n\t\t\tlines.push(truncateToWidth(this.theme.hint(\" No matching settings\"), width));\n\t\t\tthis.addHintLine(lines, width);\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), displayItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, displayItems.length);\n\n\t\t// Calculate max label width for alignment\n\t\tconst maxLabelWidth = Math.min(30, Math.max(...this.items.map((item) => visibleWidth(item.label))));\n\n\t\t// Render visible items\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = displayItems[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? this.theme.cursor : \" \";\n\t\t\tconst prefixWidth = visibleWidth(prefix);\n\n\t\t\t// Pad label to align values\n\t\t\tconst labelPadded = item.label + \" \".repeat(Math.max(0, maxLabelWidth - visibleWidth(item.label)));\n\t\t\tconst labelText = this.theme.label(labelPadded, isSelected);\n\n\t\t\t// Calculate space for value\n\t\t\tconst separator = \" \";\n\t\t\tconst usedWidth = prefixWidth + maxLabelWidth + visibleWidth(separator);\n\t\t\tconst valueMaxWidth = width - usedWidth - 2;\n\n\t\t\tconst valueText = this.theme.value(truncateToWidth(item.currentValue, valueMaxWidth, \"\"), isSelected);\n\n\t\t\tlines.push(truncateToWidth(prefix + labelText + separator + valueText, width));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < displayItems.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${displayItems.length})`;\n\t\t\tlines.push(this.theme.hint(truncateToWidth(scrollText, width - 2, \"\")));\n\t\t}\n\n\t\t// Add description for selected item\n\t\tconst selectedItem = displayItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tlines.push(\"\");\n\t\t\tconst wrappedDesc = wrapTextWithAnsi(selectedItem.description, width - 4);\n\t\t\tfor (const line of wrappedDesc) {\n\t\t\t\tlines.push(this.theme.description(` ${line}`));\n\t\t\t}\n\t\t}\n\n\t\t// Add hint\n\t\tthis.addHintLine(lines, width);\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\t// If submenu is active, delegate all input to it\n\t\t// The submenu's onCancel (triggered by escape) will call done() which closes it\n\t\tif (this.submenuComponent) {\n\t\t\tthis.submenuComponent.handleInput?.(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Main list input handling\n\t\tconst kb = getKeybindings();\n\t\tconst displayItems = this.searchEnabled ? this.filteredItems : this.items;\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? displayItems.length - 1 : this.selectedIndex - 1;\n\t\t} else if (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === displayItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t} else if (kb.matches(data, \"tui.select.confirm\") || data === \" \") {\n\t\t\tthis.activateItem();\n\t\t} else if (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancel();\n\t\t} else if (this.searchEnabled && this.searchInput) {\n\t\t\tconst sanitized = data.replace(/ /g, \"\");\n\t\t\tif (!sanitized) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.searchInput.handleInput(sanitized);\n\t\t\tthis.applyFilter(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate activateItem(): void {\n\t\tconst item = this.searchEnabled ? this.filteredItems[this.selectedIndex] : this.items[this.selectedIndex];\n\t\tif (!item) return;\n\n\t\tif (item.submenu) {\n\t\t\t// Open submenu, passing current value so it can pre-select correctly\n\t\t\tthis.submenuItemIndex = this.selectedIndex;\n\t\t\tthis.submenuComponent = item.submenu(item.currentValue, (selectedValue?: string) => {\n\t\t\t\tif (selectedValue !== undefined) {\n\t\t\t\t\titem.currentValue = selectedValue;\n\t\t\t\t\tthis.onChange(item.id, selectedValue);\n\t\t\t\t}\n\t\t\t\tthis.closeSubmenu();\n\t\t\t});\n\t\t} else if (item.values && item.values.length > 0) {\n\t\t\t// Cycle through values\n\t\t\tconst currentIndex = item.values.indexOf(item.currentValue);\n\t\t\tconst nextIndex = (currentIndex + 1) % item.values.length;\n\t\t\tconst newValue = item.values[nextIndex];\n\t\t\titem.currentValue = newValue;\n\t\t\tthis.onChange(item.id, newValue);\n\t\t}\n\t}\n\n\tprivate closeSubmenu(): void {\n\t\tthis.submenuComponent = null;\n\t\t// Restore selection to the item that opened the submenu\n\t\tif (this.submenuItemIndex !== null) {\n\t\t\tthis.selectedIndex = this.submenuItemIndex;\n\t\t\tthis.submenuItemIndex = null;\n\t\t}\n\t}\n\n\tprivate applyFilter(query: string): void {\n\t\tthis.filteredItems = fuzzyFilter(this.items, query, (item) => item.label);\n\t\tthis.selectedIndex = 0;\n\t}\n\n\tprivate addHintLine(lines: string[], width: number): void {\n\t\tlines.push(\"\");\n\t\tlines.push(\n\t\t\ttruncateToWidth(\n\t\t\t\tthis.theme.hint(\n\t\t\t\t\tthis.searchEnabled\n\t\t\t\t\t\t? \" Type to search · Enter/Space to change · Esc to cancel\"\n\t\t\t\t\t\t: \" Enter/Space to change · Esc to cancel\",\n\t\t\t\t),\n\t\t\t\twidth,\n\t\t\t),\n\t\t);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"settings-list.js","sourceRoot":"","sources":["../../src/components/settings-list.ts"],"names":[],"mappings":"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","sourcesContent":["import { fuzzyFilter } from \"../fuzzy.ts\";\nimport { getKeybindings } from \"../keybindings.ts\";\nimport type { Component, TuiMouseEvent, TuiMouseEventResult } from \"../tui.ts\";\nimport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"../utils.ts\";\nimport { Input } from \"./input.ts\";\n\nexport interface SettingItem {\n\t/** Unique identifier for this setting */\n\tid: string;\n\t/** Display label (left side) */\n\tlabel: string;\n\t/** Optional description shown when selected */\n\tdescription?: string;\n\t/** Current value to display (right side) */\n\tcurrentValue: string;\n\t/** If provided, Enter/Space cycles through these values */\n\tvalues?: string[];\n\t/** If provided, Enter opens this submenu. Receives current value and done callback.\n\t * done() accepts an optional selectedValue and an optional navigateTo id to move the cursor after close. */\n\tsubmenu?: (\n\t\tcurrentValue: string,\n\t\tdone: (selectedValue?: string, options?: { navigateTo?: string }) => void,\n\t) => Component;\n}\n\nexport interface SettingsListTheme {\n\tlabel: (text: string, selected: boolean) => string;\n\tvalue: (text: string, selected: boolean) => string;\n\tdescription: (text: string) => string;\n\tcursor: string;\n\thint: (text: string) => string;\n}\n\nexport interface SettingsListOptions {\n\tenableSearch?: boolean;\n}\n\nexport class SettingsList implements Component {\n\tprivate items: SettingItem[];\n\tprivate filteredItems: SettingItem[];\n\tprivate theme: SettingsListTheme;\n\tprivate selectedIndex = 0;\n\tprivate mousePressedIndex: number | undefined;\n\tprivate maxVisible: number;\n\tprivate onChange: (id: string, newValue: string) => void;\n\tprivate onCancel: () => void;\n\tprivate searchInput?: Input;\n\tprivate searchEnabled: boolean;\n\n\t// Submenu state\n\tprivate submenuComponent: Component | null = null;\n\tprivate submenuItemIndex: number | null = null;\n\tprivate navigateAfterClose: string | null = null;\n\n\tconstructor(\n\t\titems: SettingItem[],\n\t\tmaxVisible: number,\n\t\ttheme: SettingsListTheme,\n\t\tonChange: (id: string, newValue: string) => void,\n\t\tonCancel: () => void,\n\t\toptions: SettingsListOptions = {},\n\t) {\n\t\tthis.items = items;\n\t\tthis.filteredItems = items;\n\t\tthis.maxVisible = maxVisible;\n\t\tthis.theme = theme;\n\t\tthis.onChange = onChange;\n\t\tthis.onCancel = onCancel;\n\t\tthis.searchEnabled = options.enableSearch ?? false;\n\t\tif (this.searchEnabled) {\n\t\t\tthis.searchInput = new Input();\n\t\t}\n\t}\n\n\t/** Update an item's currentValue */\n\tupdateValue(id: string, newValue: string): void {\n\t\tconst item = this.items.find((i) => i.id === id);\n\t\tif (item) {\n\t\t\titem.currentValue = newValue;\n\t\t}\n\t}\n\n\t/** Move selection to the item with the given id (no-op if not found). */\n\tselectItem(id: string): void {\n\t\tconst items = this.searchEnabled ? this.filteredItems : this.items;\n\t\tconst index = items.findIndex((i) => i.id === id);\n\t\tif (index !== -1) {\n\t\t\tthis.selectedIndex = index;\n\t\t}\n\t}\n\n\tinvalidate(): void {\n\t\tthis.submenuComponent?.invalidate?.();\n\t}\n\n\trender(width: number): string[] {\n\t\t// If submenu is active, render it instead\n\t\tif (this.submenuComponent) {\n\t\t\treturn this.submenuComponent.render(width);\n\t\t}\n\n\t\treturn this.renderMainList(width);\n\t}\n\n\tprivate renderMainList(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.searchEnabled && this.searchInput) {\n\t\t\tlines.push(...this.searchInput.render(width));\n\t\t\tlines.push(\"\");\n\t\t}\n\n\t\tif (this.items.length === 0) {\n\t\t\tlines.push(this.theme.hint(\" No settings available\"));\n\t\t\tif (this.searchEnabled) {\n\t\t\t\tthis.addHintLine(lines, width);\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst displayItems = this.getDisplayItems();\n\t\tif (displayItems.length === 0) {\n\t\t\tlines.push(truncateToWidth(this.theme.hint(\" No matching settings\"), width));\n\t\t\tthis.addHintLine(lines, width);\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst { startIndex, endIndex } = this.getVisibleRange(displayItems);\n\n\t\t// Calculate max label width for alignment\n\t\tconst maxLabelWidth = Math.min(36, Math.max(...this.items.map((item) => visibleWidth(item.label))));\n\n\t\t// Render visible items\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = displayItems[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? this.theme.cursor : \" \";\n\t\t\tconst prefixWidth = visibleWidth(prefix);\n\n\t\t\t// Pad label to align values\n\t\t\tconst labelPadded = item.label + \" \".repeat(Math.max(0, maxLabelWidth - visibleWidth(item.label)));\n\t\t\tconst labelText = this.theme.label(labelPadded, isSelected);\n\n\t\t\t// Calculate space for value\n\t\t\tconst separator = \" \";\n\t\t\tconst usedWidth = prefixWidth + maxLabelWidth + visibleWidth(separator);\n\t\t\tconst valueMaxWidth = width - usedWidth - 2;\n\n\t\t\tconst valueText = this.theme.value(truncateToWidth(item.currentValue, valueMaxWidth, \"\"), isSelected);\n\n\t\t\tlines.push(truncateToWidth(prefix + labelText + separator + valueText, width));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < displayItems.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${displayItems.length})`;\n\t\t\tlines.push(this.theme.hint(truncateToWidth(scrollText, width - 2, \"\")));\n\t\t}\n\n\t\t// Add description for selected item\n\t\tconst selectedItem = displayItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tlines.push(\"\");\n\t\t\tconst wrappedDesc = wrapTextWithAnsi(selectedItem.description, width - 4);\n\t\t\tfor (const line of wrappedDesc) {\n\t\t\t\tlines.push(this.theme.description(` ${line}`));\n\t\t\t}\n\t\t}\n\n\t\t// Add hint\n\t\tthis.addHintLine(lines, width);\n\n\t\treturn lines;\n\t}\n\n\thandleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined {\n\t\tif (this.submenuComponent) {\n\t\t\tconst result = this.submenuComponent.handleMouse?.(event);\n\t\t\treturn result ? { ...result, focus: true } : undefined;\n\t\t}\n\n\t\tif (this.searchEnabled && this.searchInput) {\n\t\t\tif (event.y === 0) {\n\t\t\t\tconst result = this.searchInput.handleMouse?.(event);\n\t\t\t\treturn result ? { ...result, focus: true } : undefined;\n\t\t\t}\n\t\t\tif (event.y === 1) return undefined;\n\t\t}\n\n\t\tconst displayItems = this.getDisplayItems();\n\t\tif (displayItems.length === 0) return undefined;\n\t\tif (event.type === \"wheel\" && event.wheelDelta) {\n\t\t\tconst delta = event.wheelDelta < 0 ? -1 : 1;\n\t\t\tconst previousIndex = this.selectedIndex;\n\t\t\tthis.selectedIndex = Math.max(0, Math.min(displayItems.length - 1, this.selectedIndex + delta));\n\t\t\treturn { handled: true, render: this.selectedIndex !== previousIndex };\n\t\t}\n\t\t// Hover must not change selection: the visible range is centered on it.\n\t\tif (event.button !== \"left\" || (event.type !== \"press\" && event.type !== \"click\")) return undefined;\n\n\t\tconst rowOffset = this.searchEnabled ? 2 : 0;\n\t\tconst { startIndex, endIndex } = this.getVisibleRange(displayItems);\n\t\tconst itemIndex = startIndex + event.y - rowOffset;\n\t\tif (itemIndex < startIndex || itemIndex >= endIndex) return undefined;\n\t\tif (event.type === \"press\") {\n\t\t\tthis.mousePressedIndex = itemIndex;\n\t\t\tthis.selectedIndex = itemIndex;\n\t\t\treturn { handled: true, focus: true };\n\t\t}\n\t\tif (event.type === \"click\") {\n\t\t\tthis.selectedIndex = this.mousePressedIndex ?? itemIndex;\n\t\t\tthis.mousePressedIndex = undefined;\n\t\t\tthis.activateItem();\n\t\t\treturn { handled: true };\n\t\t}\n\t\treturn undefined;\n\t}\n\n\thandleInput(data: string): void {\n\t\t// If submenu is active, delegate all input to it\n\t\t// The submenu's onCancel (triggered by escape) will call done() which closes it\n\t\tif (this.submenuComponent) {\n\t\t\tthis.submenuComponent.handleInput?.(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Main list input handling\n\t\tconst kb = getKeybindings();\n\t\tconst displayItems = this.getDisplayItems();\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? displayItems.length - 1 : this.selectedIndex - 1;\n\t\t} else if (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === displayItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t} else if (\n\t\t\tkb.matches(data, \"tui.select.confirm\") ||\n\t\t\t(data === \" \" && (!this.searchEnabled || this.searchInput?.getValue().length === 0))\n\t\t) {\n\t\t\tthis.activateItem();\n\t\t} else if (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancel();\n\t\t} else if (this.searchEnabled && this.searchInput) {\n\t\t\tthis.searchInput.handleInput(data);\n\t\t\tthis.applyFilter(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate getDisplayItems(): SettingItem[] {\n\t\treturn this.searchEnabled ? this.filteredItems : this.items;\n\t}\n\n\tprivate getVisibleRange(displayItems: readonly SettingItem[]): { startIndex: number; endIndex: number } {\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), displayItems.length - this.maxVisible),\n\t\t);\n\t\treturn { startIndex, endIndex: Math.min(startIndex + this.maxVisible, displayItems.length) };\n\t}\n\n\tprivate activateItem(): void {\n\t\tconst item = this.getDisplayItems()[this.selectedIndex];\n\t\tif (!item) return;\n\n\t\tif (item.submenu) {\n\t\t\t// Open submenu, passing current value so it can pre-select correctly\n\t\t\tthis.submenuItemIndex = this.selectedIndex;\n\t\t\tthis.submenuComponent = item.submenu(\n\t\t\t\titem.currentValue,\n\t\t\t\t(selectedValue?: string, options?: { navigateTo?: string }) => {\n\t\t\t\t\tif (selectedValue !== undefined) {\n\t\t\t\t\t\titem.currentValue = selectedValue;\n\t\t\t\t\t\tthis.onChange(item.id, selectedValue);\n\t\t\t\t\t}\n\t\t\t\t\tif (options?.navigateTo) {\n\t\t\t\t\t\tthis.navigateAfterClose = options.navigateTo;\n\t\t\t\t\t}\n\t\t\t\t\tthis.closeSubmenu();\n\t\t\t\t},\n\t\t\t);\n\t\t} else if (item.values && item.values.length > 0) {\n\t\t\t// Cycle through values\n\t\t\tconst currentIndex = item.values.indexOf(item.currentValue);\n\t\t\tconst nextIndex = (currentIndex + 1) % item.values.length;\n\t\t\tconst newValue = item.values[nextIndex];\n\t\t\titem.currentValue = newValue;\n\t\t\tthis.onChange(item.id, newValue);\n\t\t}\n\t}\n\n\tprivate closeSubmenu(): void {\n\t\tthis.submenuComponent = null;\n\t\tif (this.navigateAfterClose !== null) {\n\t\t\tconst id = this.navigateAfterClose;\n\t\t\tthis.navigateAfterClose = null;\n\t\t\tthis.submenuItemIndex = null;\n\t\t\tthis.selectItem(id);\n\t\t\t// Open the target item's submenu automatically\n\t\t\tthis.activateItem();\n\t\t} else if (this.submenuItemIndex !== null) {\n\t\t\t// Restore selection to the item that opened the submenu\n\t\t\tthis.selectedIndex = this.submenuItemIndex;\n\t\t\tthis.submenuItemIndex = null;\n\t\t}\n\t}\n\n\tprivate applyFilter(query: string): void {\n\t\tthis.filteredItems = fuzzyFilter(this.items, query, (item) => item.label);\n\t\tthis.selectedIndex = 0;\n\t}\n\n\tprivate addHintLine(lines: string[], width: number): void {\n\t\tlines.push(\"\");\n\t\tlines.push(\n\t\t\ttruncateToWidth(\n\t\t\t\tthis.theme.hint(\n\t\t\t\t\tthis.searchEnabled\n\t\t\t\t\t\t? \" Type to search · Enter/Space to change · Esc to cancel\"\n\t\t\t\t\t\t: \" Enter/Space to change · Esc to cancel\",\n\t\t\t\t),\n\t\t\t\twidth,\n\t\t\t),\n\t\t);\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spacer.d.ts","sourceRoot":"","sources":["../../src/components/spacer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C;;GAEG;AACH,qBAAa,MAAO,YAAW,SAAS;IACvC,OAAO,CAAC,KAAK,CAAS;IAEtB,YAAY,KAAK,GAAE,MAAU,EAE5B;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE5B;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAM/B;CACD"
|
|
1
|
+
{"version":3,"file":"spacer.d.ts","sourceRoot":"","sources":["../../src/components/spacer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C;;GAEG;AACH,qBAAa,MAAO,YAAW,SAAS;IACvC,OAAO,CAAC,KAAK,CAAS;IAEtB,YAAY,KAAK,GAAE,MAAU,EAE5B;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE5B;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAM/B;CACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spacer.js","sourceRoot":"","sources":["../../src/components/spacer.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,MAAM;IACV,KAAK,CAAS;IAEtB,YAAY,KAAK,GAAW,CAAC
|
|
1
|
+
{"version":3,"file":"spacer.js","sourceRoot":"","sources":["../../src/components/spacer.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,MAAM;IACV,KAAK,CAAS;IAEtB,YAAY,KAAK,GAAW,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,QAAQ,CAAC,KAAa;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,UAAU;QACT,0CAA0C;IAC3C,CAAC;IAED,MAAM,CAAC,MAAc;QACpB,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;CACD","sourcesContent":["import type { Component } from \"../tui.ts\";\n\n/**\n * Spacer component that renders empty lines\n */\nexport class Spacer implements Component {\n\tprivate lines: number;\n\n\tconstructor(lines: number = 1) {\n\t\tthis.lines = lines;\n\t}\n\n\tsetLines(lines: number): void {\n\t\tthis.lines = lines;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(_width: number): string[] {\n\t\tconst result: string[] = [];\n\t\tfor (let i = 0; i < this.lines; i++) {\n\t\t\tresult.push(\"\");\n\t\t}\n\t\treturn result;\n\t}\n}\n"]}
|