pi-rtk-optimizer 0.7.1 → 0.8.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/CHANGELOG.md +14 -0
- package/README.md +6 -3
- package/package.json +67 -67
- package/src/additional-coverage-test.ts +1 -1
- package/src/command-completions.ts +49 -49
- package/src/command-rewriter-test.ts +1 -1
- package/src/command-rewriter.ts +46 -46
- package/src/config-modal-test.ts +2 -2
- package/src/config-modal.ts +78 -9
- package/src/constants.ts +1 -1
- package/src/index-test.ts +2 -2
- package/src/index.ts +1 -1
- package/src/output-compactor-test.ts +208 -3
- package/src/output-compactor.ts +294 -1
- package/src/rtk-executable-resolver.ts +97 -97
- package/src/rtk-rewrite-provider.ts +126 -126
- package/src/types-shims.d.ts +4 -2
- package/src/zellij-modal.ts +137 -30
package/src/zellij-modal.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Vendored from ../zellij-modal/index.ts to keep pi-rtk-optimizer standalone.
|
|
2
2
|
// Keep this module in sync when upstream zellij-modal primitives change.
|
|
3
|
-
import { getSettingsListTheme, type ExtensionAPI, type Theme } from "@
|
|
3
|
+
import { getSettingsListTheme, type ExtensionAPI, type Theme } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import {
|
|
5
5
|
Box,
|
|
6
6
|
Container,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
truncateToWidth,
|
|
11
11
|
visibleWidth,
|
|
12
12
|
type SettingItem,
|
|
13
|
-
} from "@
|
|
13
|
+
} from "@earendil-works/pi-tui";
|
|
14
14
|
|
|
15
15
|
const ANSI_RESET = "\x1b[0m";
|
|
16
16
|
|
|
@@ -782,13 +782,22 @@ export class ZellijModal implements ZellijModalComponent {
|
|
|
782
782
|
/**
|
|
783
783
|
* Options for the pre-built settings modal content renderer.
|
|
784
784
|
*/
|
|
785
|
+
export interface SettingsTab {
|
|
786
|
+
label: string;
|
|
787
|
+
settings: SettingItem[];
|
|
788
|
+
}
|
|
789
|
+
|
|
785
790
|
export interface SettingsModalOptions {
|
|
786
791
|
/** Modal heading. */
|
|
787
792
|
title: string;
|
|
788
793
|
/** Optional descriptive subtitle shown above settings. */
|
|
789
794
|
description?: string;
|
|
790
|
-
/** Settings list items. */
|
|
791
|
-
settings
|
|
795
|
+
/** Settings list items (used when tabs are not provided). */
|
|
796
|
+
settings?: SettingItem[];
|
|
797
|
+
/** Optional tabs for grouped settings. */
|
|
798
|
+
tabs?: SettingsTab[];
|
|
799
|
+
/** Initial active tab index. */
|
|
800
|
+
activeTabIndex?: number;
|
|
792
801
|
/** Called when a setting value changes. */
|
|
793
802
|
onChange: (id: string, value: string) => void;
|
|
794
803
|
/** Called when modal should close. */
|
|
@@ -808,6 +817,9 @@ export class ZellijSettingsModal implements ZellijModalContentRenderer {
|
|
|
808
817
|
private settingsList: SettingsList;
|
|
809
818
|
private options: SettingsModalOptions;
|
|
810
819
|
private theme: Theme;
|
|
820
|
+
private showTabs: boolean;
|
|
821
|
+
private activeTabIndex: number;
|
|
822
|
+
private tabLists: SettingsList[];
|
|
811
823
|
|
|
812
824
|
constructor(options: SettingsModalOptions, theme: Theme) {
|
|
813
825
|
if (!options.title || !options.title.trim()) {
|
|
@@ -816,37 +828,34 @@ export class ZellijSettingsModal implements ZellijModalContentRenderer {
|
|
|
816
828
|
|
|
817
829
|
this.options = options;
|
|
818
830
|
this.theme = theme;
|
|
831
|
+
this.showTabs = options.tabs !== undefined && options.tabs.length > 0;
|
|
832
|
+
this.tabLists = this.showTabs ? options.tabs!.map((tab) => this.createSettingsList(tab.settings, () => this.options.onClose())) : [];
|
|
833
|
+
this.activeTabIndex = this.normalizeActiveTabIndex(options.activeTabIndex ?? 0);
|
|
819
834
|
this.container = new Container();
|
|
820
835
|
this.contentBox = new Box(0, 0);
|
|
821
836
|
|
|
822
|
-
|
|
837
|
+
if (this.showTabs) {
|
|
838
|
+
this.settingsList = this.tabLists[this.activeTabIndex] ?? this.tabLists[0]!;
|
|
839
|
+
} else {
|
|
840
|
+
this.contentBox.addChild(new Text(this.theme.fg("accent", this.theme.bold(options.title)), 0, 0));
|
|
841
|
+
|
|
842
|
+
if (options.description) {
|
|
843
|
+
this.contentBox.addChild(new Spacer(1));
|
|
844
|
+
this.contentBox.addChild(new Text(this.theme.fg("muted", options.description), 0, 0));
|
|
845
|
+
}
|
|
823
846
|
|
|
824
|
-
if (options.description) {
|
|
825
847
|
this.contentBox.addChild(new Spacer(1));
|
|
826
|
-
|
|
827
|
-
|
|
848
|
+
const fallbackSettings = options.settings ?? [];
|
|
849
|
+
this.settingsList = this.createSettingsList(fallbackSettings, () => this.options.onClose());
|
|
850
|
+
this.contentBox.addChild(this.settingsList);
|
|
828
851
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
getSettingsListTheme(),
|
|
834
|
-
(id: string, value: string) => {
|
|
835
|
-
this.options.onChange(id, value);
|
|
836
|
-
},
|
|
837
|
-
() => {
|
|
838
|
-
this.options.onClose();
|
|
839
|
-
},
|
|
840
|
-
{ enableSearch: options.enableSearch ?? true },
|
|
841
|
-
);
|
|
842
|
-
this.contentBox.addChild(this.settingsList);
|
|
852
|
+
if (options.helpText) {
|
|
853
|
+
this.contentBox.addChild(new Spacer(1));
|
|
854
|
+
this.contentBox.addChild(new Text(this.theme.fg("dim", options.helpText), 0, 0));
|
|
855
|
+
}
|
|
843
856
|
|
|
844
|
-
|
|
845
|
-
this.contentBox.addChild(new Spacer(1));
|
|
846
|
-
this.contentBox.addChild(new Text(this.theme.fg("dim", options.helpText), 0, 0));
|
|
857
|
+
this.container.addChild(this.contentBox);
|
|
847
858
|
}
|
|
848
|
-
|
|
849
|
-
this.container.addChild(this.contentBox);
|
|
850
859
|
}
|
|
851
860
|
|
|
852
861
|
/**
|
|
@@ -855,18 +864,100 @@ export class ZellijSettingsModal implements ZellijModalContentRenderer {
|
|
|
855
864
|
render(width: number): string[] {
|
|
856
865
|
const safeWidth = Math.max(1, width);
|
|
857
866
|
try {
|
|
858
|
-
|
|
867
|
+
if (!this.showTabs) {
|
|
868
|
+
return this.container.render(safeWidth);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const lines: string[] = [];
|
|
872
|
+
|
|
873
|
+
// Title
|
|
874
|
+
lines.push(this.theme.fg("accent", this.theme.bold(this.options.title)));
|
|
875
|
+
lines.push("");
|
|
876
|
+
|
|
877
|
+
// Tab bar
|
|
878
|
+
lines.push(this.renderTabBar(safeWidth));
|
|
879
|
+
lines.push("");
|
|
880
|
+
|
|
881
|
+
// Active settings list for the selected tab.
|
|
882
|
+
const activeList = this.tabLists[this.activeTabIndex] ?? this.tabLists[0];
|
|
883
|
+
if (activeList) {
|
|
884
|
+
const listRender = activeList.render(safeWidth);
|
|
885
|
+
lines.push(...listRender);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Separator + help text
|
|
889
|
+
if (this.options.helpText) {
|
|
890
|
+
lines.push(this.theme.fg("border", "─".repeat(Math.max(0, safeWidth))));
|
|
891
|
+
lines.push(this.theme.fg("dim", this.options.helpText));
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return lines;
|
|
859
895
|
} catch (error) {
|
|
860
896
|
const message = error instanceof Error ? error.message : String(error);
|
|
861
897
|
return [this.theme.fg("error", truncateToWidth(`Settings render error: ${message}`, safeWidth, "…"))];
|
|
862
898
|
}
|
|
863
899
|
}
|
|
864
900
|
|
|
901
|
+
private createSettingsList(settings: SettingItem[], onCancel: () => void): SettingsList {
|
|
902
|
+
return new SettingsList(
|
|
903
|
+
settings,
|
|
904
|
+
Math.min(Math.max(settings.length + 2, 6), 18),
|
|
905
|
+
getSettingsListTheme(),
|
|
906
|
+
(id: string, value: string) => {
|
|
907
|
+
this.options.onChange(id, value);
|
|
908
|
+
},
|
|
909
|
+
onCancel,
|
|
910
|
+
{ enableSearch: this.options.enableSearch ?? true },
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
private normalizeActiveTabIndex(index: number): number {
|
|
915
|
+
if (!this.showTabs || this.tabLists.length === 0) {
|
|
916
|
+
return 0;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const normalized = Number.isFinite(index) ? Math.floor(index) : 0;
|
|
920
|
+
return ((normalized % this.tabLists.length) + this.tabLists.length) % this.tabLists.length;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
private renderTabBar(width: number): string {
|
|
924
|
+
if (!this.showTabs || !this.options.tabs || this.options.tabs.length === 0) {
|
|
925
|
+
return "";
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
const parts: string[] = [];
|
|
929
|
+
for (let i = 0; i < this.options.tabs.length; i++) {
|
|
930
|
+
const label = this.options.tabs[i].label;
|
|
931
|
+
if (i === this.activeTabIndex) {
|
|
932
|
+
parts.push(this.theme.fg("accent", `[ ${label} ]`));
|
|
933
|
+
} else {
|
|
934
|
+
parts.push(this.theme.fg("muted", ` ${label} `));
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
return truncateToWidth(parts.join(""), width, "…");
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
private switchTab(direction: number): void {
|
|
942
|
+
if (!this.showTabs || this.tabLists.length === 0) {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
this.activeTabIndex = this.normalizeActiveTabIndex(this.activeTabIndex + direction);
|
|
946
|
+
this.settingsList = this.tabLists[this.activeTabIndex] ?? this.tabLists[0]!;
|
|
947
|
+
}
|
|
948
|
+
|
|
865
949
|
/**
|
|
866
950
|
* Invalidate internal caches.
|
|
867
951
|
*/
|
|
868
952
|
invalidate(): void {
|
|
869
|
-
this.
|
|
953
|
+
if (!this.showTabs) {
|
|
954
|
+
this.container.invalidate();
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
for (const list of this.tabLists) {
|
|
959
|
+
list.invalidate();
|
|
960
|
+
}
|
|
870
961
|
}
|
|
871
962
|
|
|
872
963
|
/**
|
|
@@ -876,6 +967,15 @@ export class ZellijSettingsModal implements ZellijModalContentRenderer {
|
|
|
876
967
|
if (isEnterActivationInput(data)) {
|
|
877
968
|
return;
|
|
878
969
|
}
|
|
970
|
+
if (this.showTabs && data === "\x1b[D") {
|
|
971
|
+
this.switchTab(-1);
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
if (this.showTabs && data === "\x1b[C") {
|
|
975
|
+
this.switchTab(1);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
|
|
879
979
|
this.settingsList.handleInput(data);
|
|
880
980
|
}
|
|
881
981
|
|
|
@@ -883,7 +983,14 @@ export class ZellijSettingsModal implements ZellijModalContentRenderer {
|
|
|
883
983
|
* Programmatically update one setting value in the list.
|
|
884
984
|
*/
|
|
885
985
|
updateValue(id: string, value: string): void {
|
|
886
|
-
this.
|
|
986
|
+
if (!this.showTabs) {
|
|
987
|
+
this.settingsList.updateValue(id, value);
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
for (const list of this.tabLists) {
|
|
992
|
+
list.updateValue(id, value);
|
|
993
|
+
}
|
|
887
994
|
}
|
|
888
995
|
}
|
|
889
996
|
|