@sherif-fanous/pi-presets-plus 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/package.json +74 -0
- package/src/activation/active-state.ts +25 -0
- package/src/activation/apply.ts +236 -0
- package/src/activation/baseline.ts +32 -0
- package/src/activation/clear.ts +434 -0
- package/src/activation/dirty.ts +69 -0
- package/src/activation/drift-handlers.ts +71 -0
- package/src/activation/drift.ts +77 -0
- package/src/activation/same-set.ts +32 -0
- package/src/activation/state-matches.ts +29 -0
- package/src/activation/thinking.ts +54 -0
- package/src/commands/presets/clear.ts +18 -0
- package/src/commands/presets/index.ts +9 -0
- package/src/commands/presets/notify.ts +22 -0
- package/src/commands/presets/reload.ts +28 -0
- package/src/commands/presets/router.ts +139 -0
- package/src/commands/presets/status.ts +262 -0
- package/src/flag.ts +88 -0
- package/src/hotkey-conflicts.ts +136 -0
- package/src/hotkey-reload-baseline.ts +112 -0
- package/src/hotkeys.ts +104 -0
- package/src/index.ts +171 -0
- package/src/messages.ts +34 -0
- package/src/store/api.ts +262 -0
- package/src/store/load.ts +175 -0
- package/src/store/merge.ts +69 -0
- package/src/store/paths.ts +38 -0
- package/src/store/save.ts +75 -0
- package/src/store/validate.ts +195 -0
- package/src/types.ts +169 -0
- package/src/ui/confirm.ts +126 -0
- package/src/ui/editor.ts +1617 -0
- package/src/ui/filter.ts +79 -0
- package/src/ui/frame.ts +109 -0
- package/src/ui/hotkey-input.ts +242 -0
- package/src/ui/info-dialog.ts +118 -0
- package/src/ui/labels.ts +51 -0
- package/src/ui/picker-state.ts +151 -0
- package/src/ui/picker.ts +982 -0
- package/src/ui/reload-prompt.ts +59 -0
- package/src/ui/status.ts +55 -0
- package/src/ui/widgets.ts +274 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure state transitions for the preset picker.
|
|
3
|
+
*
|
|
4
|
+
* Owns focus, scope, selection, and scroll invariants for the picker; it
|
|
5
|
+
* does NOT own terminal input decoding, rendering, activation side
|
|
6
|
+
* effects, or pi-tui component wiring.
|
|
7
|
+
*/
|
|
8
|
+
import type { LoadedPreset } from "../types.js";
|
|
9
|
+
import { applyScopeFilter, rankPresets, type ScopeFilter } from "./filter.js";
|
|
10
|
+
|
|
11
|
+
export interface PickerState {
|
|
12
|
+
readonly focusMode: PickerFocusMode;
|
|
13
|
+
readonly scopeFilter: ScopeFilter;
|
|
14
|
+
readonly selectedIndex: number;
|
|
15
|
+
readonly scrollOffset: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type PickerFocusMode = "list" | "filter";
|
|
19
|
+
|
|
20
|
+
const SCOPE_ORDER: readonly ScopeFilter[] = ["all", "user", "project"];
|
|
21
|
+
|
|
22
|
+
export function cycleScope(
|
|
23
|
+
state: PickerState,
|
|
24
|
+
allPresets: readonly LoadedPreset[],
|
|
25
|
+
query: string,
|
|
26
|
+
direction: -1 | 1,
|
|
27
|
+
pageSize: number,
|
|
28
|
+
): PickerState {
|
|
29
|
+
const currentIndex = SCOPE_ORDER.indexOf(state.scopeFilter);
|
|
30
|
+
const nextIndex =
|
|
31
|
+
(currentIndex + direction + SCOPE_ORDER.length) % SCOPE_ORDER.length;
|
|
32
|
+
const nextScope = SCOPE_ORDER[nextIndex];
|
|
33
|
+
|
|
34
|
+
if (!nextScope) return state;
|
|
35
|
+
|
|
36
|
+
const previousSelection = selectedPresetKey(state, allPresets, query);
|
|
37
|
+
|
|
38
|
+
return preserveSelectionOrFirst(
|
|
39
|
+
{ ...state, scopeFilter: nextScope },
|
|
40
|
+
allPresets,
|
|
41
|
+
query,
|
|
42
|
+
previousSelection,
|
|
43
|
+
pageSize,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function initialPickerState(): PickerState {
|
|
48
|
+
return {
|
|
49
|
+
focusMode: "list",
|
|
50
|
+
scopeFilter: "all",
|
|
51
|
+
scrollOffset: 0,
|
|
52
|
+
selectedIndex: 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function moveSelection(
|
|
57
|
+
state: PickerState,
|
|
58
|
+
allPresets: readonly LoadedPreset[],
|
|
59
|
+
query: string,
|
|
60
|
+
delta: number,
|
|
61
|
+
pageSize: number,
|
|
62
|
+
options: { wrap: boolean } = { wrap: true },
|
|
63
|
+
): PickerState {
|
|
64
|
+
const visibleCount = visiblePresets(state, allPresets, query).length;
|
|
65
|
+
|
|
66
|
+
if (visibleCount === 0) return state;
|
|
67
|
+
|
|
68
|
+
const nextIndex = state.selectedIndex + delta;
|
|
69
|
+
const selectedIndex = options.wrap
|
|
70
|
+
? ((nextIndex % visibleCount) + visibleCount) % visibleCount
|
|
71
|
+
: Math.max(0, Math.min(nextIndex, visibleCount - 1));
|
|
72
|
+
|
|
73
|
+
return ensureSelectionVisible({ ...state, selectedIndex }, pageSize);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function preserveSelectionOrFirst(
|
|
77
|
+
state: PickerState,
|
|
78
|
+
allPresets: readonly LoadedPreset[],
|
|
79
|
+
query: string,
|
|
80
|
+
previousSelection: string | undefined,
|
|
81
|
+
pageSize: number,
|
|
82
|
+
): PickerState {
|
|
83
|
+
const visible = visiblePresets(state, allPresets, query);
|
|
84
|
+
|
|
85
|
+
if (visible.length === 0) {
|
|
86
|
+
return { ...state, scrollOffset: 0, selectedIndex: 0 };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const nextIndex = previousSelection
|
|
90
|
+
? visible.findIndex((preset) => presetKey(preset) === previousSelection)
|
|
91
|
+
: -1;
|
|
92
|
+
const selectedIndex = nextIndex >= 0 ? nextIndex : 0;
|
|
93
|
+
|
|
94
|
+
return ensureSelectionVisible({ ...state, selectedIndex }, pageSize);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function selectedPreset(
|
|
98
|
+
state: PickerState,
|
|
99
|
+
allPresets: readonly LoadedPreset[],
|
|
100
|
+
query: string,
|
|
101
|
+
): LoadedPreset | undefined {
|
|
102
|
+
return visiblePresets(state, allPresets, query)[state.selectedIndex];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function selectedPresetKey(
|
|
106
|
+
state: PickerState,
|
|
107
|
+
allPresets: readonly LoadedPreset[],
|
|
108
|
+
query: string,
|
|
109
|
+
): string | undefined {
|
|
110
|
+
const preset = selectedPreset(state, allPresets, query);
|
|
111
|
+
|
|
112
|
+
return preset ? presetKey(preset) : undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function setFocusMode(
|
|
116
|
+
state: PickerState,
|
|
117
|
+
focusMode: PickerFocusMode,
|
|
118
|
+
): PickerState {
|
|
119
|
+
return { ...state, focusMode };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function visiblePresets(
|
|
123
|
+
state: PickerState,
|
|
124
|
+
allPresets: readonly LoadedPreset[],
|
|
125
|
+
query: string,
|
|
126
|
+
): readonly LoadedPreset[] {
|
|
127
|
+
return rankPresets(applyScopeFilter(allPresets, state.scopeFilter), query);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function ensureSelectionVisible(
|
|
131
|
+
state: PickerState,
|
|
132
|
+
pageSize: number,
|
|
133
|
+
): PickerState {
|
|
134
|
+
let scrollOffset = state.scrollOffset;
|
|
135
|
+
|
|
136
|
+
if (state.selectedIndex < scrollOffset) {
|
|
137
|
+
scrollOffset = state.selectedIndex;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const lastVisibleIndex = scrollOffset + pageSize - 1;
|
|
141
|
+
|
|
142
|
+
if (state.selectedIndex > lastVisibleIndex) {
|
|
143
|
+
scrollOffset = state.selectedIndex - pageSize + 1;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { ...state, scrollOffset };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function presetKey(loadedPreset: LoadedPreset): string {
|
|
150
|
+
return `${loadedPreset.scope}:${loadedPreset.name}`;
|
|
151
|
+
}
|