@pokit/tabs-core 0.0.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/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/constants/help-content.d.ts +19 -0
- package/dist/constants/help-content.d.ts.map +1 -0
- package/dist/constants/help-content.js +38 -0
- package/dist/constants/index.d.ts +9 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +7 -0
- package/dist/constants/keyboard.d.ts +29 -0
- package/dist/constants/keyboard.d.ts.map +1 -0
- package/dist/constants/keyboard.js +34 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/use-keyboard-handler.d.ts +114 -0
- package/dist/hooks/use-keyboard-handler.d.ts.map +1 -0
- package/dist/hooks/use-keyboard-handler.js +224 -0
- package/dist/hooks/use-tabs-state.d.ts +47 -0
- package/dist/hooks/use-tabs-state.d.ts.map +1 -0
- package/dist/hooks/use-tabs-state.js +92 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/process-manager.d.ts +78 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +171 -0
- package/dist/ring-buffer.d.ts +143 -0
- package/dist/ring-buffer.d.ts.map +1 -0
- package/dist/ring-buffer.js +270 -0
- package/dist/state-reducer.d.ts +25 -0
- package/dist/state-reducer.d.ts.map +1 -0
- package/dist/state-reducer.js +175 -0
- package/dist/types.d.ts +77 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/package.json +64 -0
- package/src/index.ts +87 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ring Buffer Implementation
|
|
3
|
+
*
|
|
4
|
+
* A fixed-capacity circular buffer with O(1) push operations.
|
|
5
|
+
* When full, oldest elements are automatically overwritten.
|
|
6
|
+
*
|
|
7
|
+
* Key features:
|
|
8
|
+
* - O(1) push operations (no array shifting)
|
|
9
|
+
* - Configurable capacity with buffer pressure warnings
|
|
10
|
+
* - Track number of dropped items
|
|
11
|
+
* - Dropped line indicator support
|
|
12
|
+
*/
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// RingBuffer Class
|
|
15
|
+
// =============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* A circular buffer that overwrites oldest items when full.
|
|
18
|
+
* All operations are O(1) except toArray() which is O(n).
|
|
19
|
+
*/
|
|
20
|
+
export class RingBuffer {
|
|
21
|
+
buffer;
|
|
22
|
+
head = 0; // Points to next write position
|
|
23
|
+
_length = 0;
|
|
24
|
+
capacity;
|
|
25
|
+
maxLineLength;
|
|
26
|
+
warnAtPercentage;
|
|
27
|
+
onPressure;
|
|
28
|
+
_droppedCount = 0;
|
|
29
|
+
warningEmitted = false;
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.capacity = options.capacity;
|
|
32
|
+
this.maxLineLength = options.maxLineLength;
|
|
33
|
+
this.warnAtPercentage = options.warnAtPercentage ?? 80;
|
|
34
|
+
this.onPressure = options.onPressure;
|
|
35
|
+
this.buffer = new Array(this.capacity);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Number of items currently in the buffer
|
|
39
|
+
*/
|
|
40
|
+
get length() {
|
|
41
|
+
return this._length;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Number of items that have been dropped due to capacity limits
|
|
45
|
+
*/
|
|
46
|
+
get droppedCount() {
|
|
47
|
+
return this._droppedCount;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Current buffer usage as a percentage (0-100)
|
|
51
|
+
*/
|
|
52
|
+
get usagePercentage() {
|
|
53
|
+
return (this._length / this.capacity) * 100;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Whether the buffer is at capacity
|
|
57
|
+
*/
|
|
58
|
+
get isFull() {
|
|
59
|
+
return this._length === this.capacity;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Push an item into the buffer.
|
|
63
|
+
* If full, the oldest item is overwritten and droppedCount is incremented.
|
|
64
|
+
*
|
|
65
|
+
* @param item - The item to add
|
|
66
|
+
* @returns true if an item was dropped, false otherwise
|
|
67
|
+
*/
|
|
68
|
+
push(item) {
|
|
69
|
+
let dropped = false;
|
|
70
|
+
// Truncate string items if maxLineLength is set
|
|
71
|
+
let processedItem = item;
|
|
72
|
+
if (this.maxLineLength && typeof item === 'string') {
|
|
73
|
+
processedItem =
|
|
74
|
+
item.length > this.maxLineLength
|
|
75
|
+
? (item.slice(0, this.maxLineLength) + '…')
|
|
76
|
+
: item;
|
|
77
|
+
}
|
|
78
|
+
// Check if we're about to overwrite
|
|
79
|
+
if (this._length === this.capacity) {
|
|
80
|
+
this._droppedCount++;
|
|
81
|
+
dropped = true;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
this._length++;
|
|
85
|
+
}
|
|
86
|
+
// Write to current head position
|
|
87
|
+
this.buffer[this.head] = processedItem;
|
|
88
|
+
this.head = (this.head + 1) % this.capacity;
|
|
89
|
+
// Check for buffer pressure warning
|
|
90
|
+
this.checkPressure();
|
|
91
|
+
return dropped;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Push multiple items into the buffer.
|
|
95
|
+
*
|
|
96
|
+
* @param items - Array of items to add
|
|
97
|
+
* @returns Number of items that were dropped
|
|
98
|
+
*/
|
|
99
|
+
pushMany(items) {
|
|
100
|
+
let droppedThisCall = 0;
|
|
101
|
+
for (const item of items) {
|
|
102
|
+
if (this.push(item)) {
|
|
103
|
+
droppedThisCall++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return droppedThisCall;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Clear all items from the buffer and reset dropped count.
|
|
110
|
+
*/
|
|
111
|
+
clear() {
|
|
112
|
+
this.buffer = new Array(this.capacity);
|
|
113
|
+
this.head = 0;
|
|
114
|
+
this._length = 0;
|
|
115
|
+
this._droppedCount = 0;
|
|
116
|
+
this.warningEmitted = false;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Convert buffer contents to an array in correct order (oldest to newest).
|
|
120
|
+
* This is O(n) where n is the number of items in the buffer.
|
|
121
|
+
*/
|
|
122
|
+
toArray() {
|
|
123
|
+
if (this._length === 0)
|
|
124
|
+
return [];
|
|
125
|
+
const result = new Array(this._length);
|
|
126
|
+
if (this._length < this.capacity) {
|
|
127
|
+
// Buffer not full yet, items are from 0 to head-1
|
|
128
|
+
for (let i = 0; i < this._length; i++) {
|
|
129
|
+
result[i] = this.buffer[i];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Buffer is full, read from head (oldest) to head-1 (newest)
|
|
134
|
+
for (let i = 0; i < this._length; i++) {
|
|
135
|
+
const index = (this.head + i) % this.capacity;
|
|
136
|
+
result[i] = this.buffer[index];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get the item at the specified index (0 = oldest).
|
|
143
|
+
*/
|
|
144
|
+
get(index) {
|
|
145
|
+
if (index < 0 || index >= this._length)
|
|
146
|
+
return undefined;
|
|
147
|
+
if (this._length < this.capacity) {
|
|
148
|
+
return this.buffer[index];
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const actualIndex = (this.head + index) % this.capacity;
|
|
152
|
+
return this.buffer[actualIndex];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get a slice of items from the buffer.
|
|
157
|
+
*
|
|
158
|
+
* @param start - Start index (inclusive)
|
|
159
|
+
* @param end - End index (exclusive), defaults to length
|
|
160
|
+
*/
|
|
161
|
+
slice(start, end) {
|
|
162
|
+
const actualEnd = end ?? this._length;
|
|
163
|
+
const normalizedStart = Math.max(0, start);
|
|
164
|
+
const normalizedEnd = Math.min(this._length, actualEnd);
|
|
165
|
+
if (normalizedStart >= normalizedEnd)
|
|
166
|
+
return [];
|
|
167
|
+
const result = [];
|
|
168
|
+
for (let i = normalizedStart; i < normalizedEnd; i++) {
|
|
169
|
+
const item = this.get(i);
|
|
170
|
+
if (item !== undefined) {
|
|
171
|
+
result.push(item);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Reset the warning flag so it can be emitted again.
|
|
178
|
+
* Useful after displaying a warning to the user.
|
|
179
|
+
*/
|
|
180
|
+
resetWarning() {
|
|
181
|
+
this.warningEmitted = false;
|
|
182
|
+
}
|
|
183
|
+
// ===========================================================================
|
|
184
|
+
// Private Methods
|
|
185
|
+
// ===========================================================================
|
|
186
|
+
checkPressure() {
|
|
187
|
+
if (this.warningEmitted || !this.onPressure)
|
|
188
|
+
return;
|
|
189
|
+
const usage = this.usagePercentage;
|
|
190
|
+
if (usage >= this.warnAtPercentage) {
|
|
191
|
+
this.warningEmitted = true;
|
|
192
|
+
this.onPressure(usage);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const DEFAULT_MAX_LINES = 10_000;
|
|
197
|
+
const DEFAULT_MAX_LINE_LENGTH = 5_000;
|
|
198
|
+
const DEFAULT_WARN_PERCENTAGE = 80;
|
|
199
|
+
/**
|
|
200
|
+
* Specialized output buffer for tab process output.
|
|
201
|
+
* Wraps RingBuffer with dropped line indicator support.
|
|
202
|
+
*/
|
|
203
|
+
export class OutputBuffer {
|
|
204
|
+
buffer;
|
|
205
|
+
tabId;
|
|
206
|
+
lastReportedDropped = 0;
|
|
207
|
+
constructor(options = {}) {
|
|
208
|
+
this.tabId = options.tabId ?? 'unknown';
|
|
209
|
+
this.buffer = new RingBuffer({
|
|
210
|
+
capacity: options.maxLines ?? DEFAULT_MAX_LINES,
|
|
211
|
+
maxLineLength: options.maxLineLength ?? DEFAULT_MAX_LINE_LENGTH,
|
|
212
|
+
warnAtPercentage: options.warnAtPercentage ?? DEFAULT_WARN_PERCENTAGE,
|
|
213
|
+
onPressure: options.onPressure
|
|
214
|
+
? (usage) => options.onPressure(this.tabId, usage)
|
|
215
|
+
: undefined,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
get length() {
|
|
219
|
+
return this.buffer.length;
|
|
220
|
+
}
|
|
221
|
+
get droppedCount() {
|
|
222
|
+
return this.buffer.droppedCount;
|
|
223
|
+
}
|
|
224
|
+
get usagePercentage() {
|
|
225
|
+
return this.buffer.usagePercentage;
|
|
226
|
+
}
|
|
227
|
+
get isFull() {
|
|
228
|
+
return this.buffer.isFull;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Push lines into the buffer.
|
|
232
|
+
*/
|
|
233
|
+
push(...lines) {
|
|
234
|
+
this.buffer.pushMany(lines);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Clear the buffer.
|
|
238
|
+
*/
|
|
239
|
+
clear() {
|
|
240
|
+
this.buffer.clear();
|
|
241
|
+
this.lastReportedDropped = 0;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get all lines as an array with dropped line indicator if applicable.
|
|
245
|
+
* The dropped indicator is inserted at the beginning if lines were dropped.
|
|
246
|
+
*/
|
|
247
|
+
toArray() {
|
|
248
|
+
const lines = this.buffer.toArray();
|
|
249
|
+
const dropped = this.buffer.droppedCount;
|
|
250
|
+
if (dropped > 0 && dropped !== this.lastReportedDropped) {
|
|
251
|
+
this.lastReportedDropped = dropped;
|
|
252
|
+
}
|
|
253
|
+
if (dropped > 0) {
|
|
254
|
+
return [`... (${dropped.toLocaleString()} lines dropped) ...`, ...lines];
|
|
255
|
+
}
|
|
256
|
+
return lines;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Get lines without the dropped indicator.
|
|
260
|
+
*/
|
|
261
|
+
toArrayRaw() {
|
|
262
|
+
return this.buffer.toArray();
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Reset the warning flag.
|
|
266
|
+
*/
|
|
267
|
+
resetWarning() {
|
|
268
|
+
this.buffer.resetWarning();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Reducer for Event-Driven CLI
|
|
3
|
+
*
|
|
4
|
+
* Builds a state tree from CLI events using a reducer pattern.
|
|
5
|
+
* Framework-agnostic - used by both Ink and OpenTUI adapters.
|
|
6
|
+
*/
|
|
7
|
+
import type { CLIEvent, GroupId } from '@pokit/core';
|
|
8
|
+
import type { EventDrivenState, ActivityNode, GroupNode } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Create initial state
|
|
11
|
+
*/
|
|
12
|
+
export declare function createInitialState(): EventDrivenState;
|
|
13
|
+
/**
|
|
14
|
+
* Reducer function for CLI events
|
|
15
|
+
*/
|
|
16
|
+
export declare function reducer(state: EventDrivenState, event: CLIEvent): EventDrivenState;
|
|
17
|
+
/**
|
|
18
|
+
* Get activities that belong to a tabs group
|
|
19
|
+
*/
|
|
20
|
+
export declare function getTabsGroupActivities(state: EventDrivenState, groupId: GroupId): ActivityNode[];
|
|
21
|
+
/**
|
|
22
|
+
* Find the first tabs group in the state
|
|
23
|
+
*/
|
|
24
|
+
export declare function findTabsGroup(state: EventDrivenState): GroupNode | undefined;
|
|
25
|
+
//# sourceMappingURL=state-reducer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-reducer.d.ts","sourceRoot":"","sources":["../src/state-reducer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAc,OAAO,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5E;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,gBAAgB,CASrD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,GAAG,gBAAgB,CA0JlF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY,EAAE,CAOhG;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,SAAS,GAAG,SAAS,CAO5E"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Reducer for Event-Driven CLI
|
|
3
|
+
*
|
|
4
|
+
* Builds a state tree from CLI events using a reducer pattern.
|
|
5
|
+
* Framework-agnostic - used by both Ink and OpenTUI adapters.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Create initial state
|
|
9
|
+
*/
|
|
10
|
+
export function createInitialState() {
|
|
11
|
+
return {
|
|
12
|
+
appName: undefined,
|
|
13
|
+
version: undefined,
|
|
14
|
+
exitCode: undefined,
|
|
15
|
+
activities: new Map(),
|
|
16
|
+
groups: new Map(),
|
|
17
|
+
rootChildren: [],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reducer function for CLI events
|
|
22
|
+
*/
|
|
23
|
+
export function reducer(state, event) {
|
|
24
|
+
switch (event.type) {
|
|
25
|
+
case 'root:start':
|
|
26
|
+
return {
|
|
27
|
+
...state,
|
|
28
|
+
appName: event.appName,
|
|
29
|
+
version: event.version,
|
|
30
|
+
};
|
|
31
|
+
case 'root:end':
|
|
32
|
+
return {
|
|
33
|
+
...state,
|
|
34
|
+
exitCode: event.exitCode,
|
|
35
|
+
};
|
|
36
|
+
case 'group:start': {
|
|
37
|
+
const newGroup = {
|
|
38
|
+
type: 'group',
|
|
39
|
+
id: event.id,
|
|
40
|
+
parentId: event.parentId,
|
|
41
|
+
label: event.label,
|
|
42
|
+
layout: event.layout,
|
|
43
|
+
children: [],
|
|
44
|
+
};
|
|
45
|
+
const newGroups = new Map(state.groups);
|
|
46
|
+
newGroups.set(event.id, newGroup);
|
|
47
|
+
if (event.parentId) {
|
|
48
|
+
const parentGroup = state.groups.get(event.parentId);
|
|
49
|
+
if (parentGroup) {
|
|
50
|
+
const updatedParent = {
|
|
51
|
+
...parentGroup,
|
|
52
|
+
children: [...parentGroup.children, event.id],
|
|
53
|
+
};
|
|
54
|
+
newGroups.set(event.parentId, updatedParent);
|
|
55
|
+
}
|
|
56
|
+
return { ...state, groups: newGroups };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
...state,
|
|
60
|
+
groups: newGroups,
|
|
61
|
+
rootChildren: [...state.rootChildren, event.id],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
case 'group:end':
|
|
65
|
+
return state;
|
|
66
|
+
case 'activity:start': {
|
|
67
|
+
const newActivity = {
|
|
68
|
+
type: 'activity',
|
|
69
|
+
id: event.id,
|
|
70
|
+
parentId: event.parentId,
|
|
71
|
+
label: event.label,
|
|
72
|
+
status: 'running',
|
|
73
|
+
meta: event.meta,
|
|
74
|
+
logs: [],
|
|
75
|
+
};
|
|
76
|
+
const newActivities = new Map(state.activities);
|
|
77
|
+
newActivities.set(event.id, newActivity);
|
|
78
|
+
if (event.parentId) {
|
|
79
|
+
const parentGroup = state.groups.get(event.parentId);
|
|
80
|
+
if (parentGroup) {
|
|
81
|
+
const newGroups = new Map(state.groups);
|
|
82
|
+
const updatedParent = {
|
|
83
|
+
...parentGroup,
|
|
84
|
+
children: [...parentGroup.children, event.id],
|
|
85
|
+
};
|
|
86
|
+
newGroups.set(event.parentId, updatedParent);
|
|
87
|
+
return { ...state, activities: newActivities, groups: newGroups };
|
|
88
|
+
}
|
|
89
|
+
return { ...state, activities: newActivities };
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
...state,
|
|
93
|
+
activities: newActivities,
|
|
94
|
+
rootChildren: [...state.rootChildren, event.id],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
case 'activity:update': {
|
|
98
|
+
const activity = state.activities.get(event.id);
|
|
99
|
+
if (!activity)
|
|
100
|
+
return state;
|
|
101
|
+
const updatedActivity = {
|
|
102
|
+
...activity,
|
|
103
|
+
progress: event.payload.progress ?? activity.progress,
|
|
104
|
+
message: event.payload.message ?? activity.message,
|
|
105
|
+
};
|
|
106
|
+
const newActivities = new Map(state.activities);
|
|
107
|
+
newActivities.set(event.id, updatedActivity);
|
|
108
|
+
return { ...state, activities: newActivities };
|
|
109
|
+
}
|
|
110
|
+
case 'activity:success': {
|
|
111
|
+
const activity = state.activities.get(event.id);
|
|
112
|
+
if (!activity)
|
|
113
|
+
return state;
|
|
114
|
+
const updatedActivity = {
|
|
115
|
+
...activity,
|
|
116
|
+
status: 'success',
|
|
117
|
+
};
|
|
118
|
+
const newActivities = new Map(state.activities);
|
|
119
|
+
newActivities.set(event.id, updatedActivity);
|
|
120
|
+
return { ...state, activities: newActivities };
|
|
121
|
+
}
|
|
122
|
+
case 'activity:failure': {
|
|
123
|
+
const activity = state.activities.get(event.id);
|
|
124
|
+
if (!activity)
|
|
125
|
+
return state;
|
|
126
|
+
const errorMessage = event.error instanceof Error ? event.error.message : String(event.error);
|
|
127
|
+
const updatedActivity = {
|
|
128
|
+
...activity,
|
|
129
|
+
status: 'failure',
|
|
130
|
+
message: errorMessage,
|
|
131
|
+
};
|
|
132
|
+
const newActivities = new Map(state.activities);
|
|
133
|
+
newActivities.set(event.id, updatedActivity);
|
|
134
|
+
return { ...state, activities: newActivities };
|
|
135
|
+
}
|
|
136
|
+
case 'log': {
|
|
137
|
+
if (!event.activityId)
|
|
138
|
+
return state;
|
|
139
|
+
const activity = state.activities.get(event.activityId);
|
|
140
|
+
if (!activity)
|
|
141
|
+
return state;
|
|
142
|
+
const updatedActivity = {
|
|
143
|
+
...activity,
|
|
144
|
+
logs: [...activity.logs, { level: event.level, message: event.message }],
|
|
145
|
+
};
|
|
146
|
+
const newActivities = new Map(state.activities);
|
|
147
|
+
newActivities.set(event.activityId, updatedActivity);
|
|
148
|
+
return { ...state, activities: newActivities };
|
|
149
|
+
}
|
|
150
|
+
default:
|
|
151
|
+
return state;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get activities that belong to a tabs group
|
|
156
|
+
*/
|
|
157
|
+
export function getTabsGroupActivities(state, groupId) {
|
|
158
|
+
const group = state.groups.get(groupId);
|
|
159
|
+
if (!group || group.layout !== 'tabs')
|
|
160
|
+
return [];
|
|
161
|
+
return group.children
|
|
162
|
+
.map((childId) => state.activities.get(childId))
|
|
163
|
+
.filter((activity) => activity !== undefined);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Find the first tabs group in the state
|
|
167
|
+
*/
|
|
168
|
+
export function findTabsGroup(state) {
|
|
169
|
+
for (const group of state.groups.values()) {
|
|
170
|
+
if (group.layout === 'tabs') {
|
|
171
|
+
return group;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Types for CLI Tabs
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic types used by both Ink and OpenTUI adapters.
|
|
5
|
+
*/
|
|
6
|
+
import type { ActivityId, GroupId, GroupLayout } from '@pokit/core';
|
|
7
|
+
export type TabStatus = 'running' | 'done' | 'error' | 'stopped';
|
|
8
|
+
export type TabProcess = {
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
|
+
exec: string;
|
|
12
|
+
output: string[];
|
|
13
|
+
status: TabStatus;
|
|
14
|
+
exitCode?: number;
|
|
15
|
+
};
|
|
16
|
+
export declare const MAX_OUTPUT_LINES = 10000;
|
|
17
|
+
export declare const MAX_LINE_LENGTH = 5000;
|
|
18
|
+
export declare const BUFFER_WARNING_THRESHOLD = 80;
|
|
19
|
+
/**
|
|
20
|
+
* Status indicator configuration for tabs.
|
|
21
|
+
*
|
|
22
|
+
* Color scheme:
|
|
23
|
+
* - running: green (active process)
|
|
24
|
+
* - done: cyan (completed/inactive)
|
|
25
|
+
* - error: red (failed)
|
|
26
|
+
* - stopped: yellow (manually stopped)
|
|
27
|
+
*/
|
|
28
|
+
export type StatusIndicator = {
|
|
29
|
+
color: string;
|
|
30
|
+
colorBright: string;
|
|
31
|
+
icon: string;
|
|
32
|
+
};
|
|
33
|
+
export declare const STATUS_INDICATORS: Record<TabStatus, StatusIndicator>;
|
|
34
|
+
export declare function getStatusIndicator(status: TabStatus, bright?: boolean): {
|
|
35
|
+
color: string;
|
|
36
|
+
icon: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Activity node in the state tree
|
|
40
|
+
*/
|
|
41
|
+
export type ActivityNode = {
|
|
42
|
+
type: 'activity';
|
|
43
|
+
id: ActivityId;
|
|
44
|
+
parentId?: GroupId | ActivityId;
|
|
45
|
+
label: string;
|
|
46
|
+
status: 'running' | 'success' | 'failure';
|
|
47
|
+
progress?: number;
|
|
48
|
+
message?: string;
|
|
49
|
+
meta?: Record<string, unknown>;
|
|
50
|
+
logs: Array<{
|
|
51
|
+
level: string;
|
|
52
|
+
message: string;
|
|
53
|
+
}>;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Group node in the state tree
|
|
57
|
+
*/
|
|
58
|
+
export type GroupNode = {
|
|
59
|
+
type: 'group';
|
|
60
|
+
id: GroupId;
|
|
61
|
+
parentId?: GroupId;
|
|
62
|
+
label: string;
|
|
63
|
+
layout: GroupLayout;
|
|
64
|
+
children: Array<ActivityId | GroupId>;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Root state for the event-driven CLI
|
|
68
|
+
*/
|
|
69
|
+
export type EventDrivenState = {
|
|
70
|
+
appName?: string;
|
|
71
|
+
version?: string;
|
|
72
|
+
exitCode?: number;
|
|
73
|
+
activities: Map<ActivityId, ActivityNode>;
|
|
74
|
+
groups: Map<GroupId, GroupNode>;
|
|
75
|
+
rootChildren: Array<ActivityId | GroupId>;
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAMpE,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAEjE,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAS,CAAC;AACvC,eAAO,MAAM,eAAe,OAAQ,CAAC;AACrC,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAM3C;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,eAAe,CAKhE,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,GAAE,OAAe,GACtB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAMjC;AAMD;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,UAAU,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,KAAK,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC;CACvC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC1C,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAChC,YAAY,EAAE,KAAK,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC;CAC3C,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Types for CLI Tabs
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic types used by both Ink and OpenTUI adapters.
|
|
5
|
+
*/
|
|
6
|
+
export const MAX_OUTPUT_LINES = 10_000;
|
|
7
|
+
export const MAX_LINE_LENGTH = 5_000;
|
|
8
|
+
export const BUFFER_WARNING_THRESHOLD = 80; // Percentage
|
|
9
|
+
export const STATUS_INDICATORS = {
|
|
10
|
+
running: { color: '#00AA00', colorBright: '#00FF00', icon: '●' },
|
|
11
|
+
done: { color: '#00AAAA', colorBright: '#00FFFF', icon: '○' },
|
|
12
|
+
error: { color: '#AA0000', colorBright: '#FF0000', icon: '✗' },
|
|
13
|
+
stopped: { color: '#AAAA00', colorBright: '#FFFF00', icon: '■' },
|
|
14
|
+
};
|
|
15
|
+
export function getStatusIndicator(status, bright = false) {
|
|
16
|
+
const indicator = STATUS_INDICATORS[status];
|
|
17
|
+
return {
|
|
18
|
+
color: bright ? indicator.colorBright : indicator.color,
|
|
19
|
+
icon: indicator.icon,
|
|
20
|
+
};
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pokit/tabs-core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Core tab management utilities for pok CLI applications",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"command-line",
|
|
8
|
+
"typescript",
|
|
9
|
+
"bun",
|
|
10
|
+
"terminal",
|
|
11
|
+
"pok",
|
|
12
|
+
"pokjs",
|
|
13
|
+
"tabs",
|
|
14
|
+
"tab-management",
|
|
15
|
+
"process-manager"
|
|
16
|
+
],
|
|
17
|
+
"type": "module",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/notation-dev/openpok.git",
|
|
22
|
+
"directory": "packages/tabs-core"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/notation-dev/openpok#readme",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/notation-dev/openpok/issues"
|
|
27
|
+
},
|
|
28
|
+
"main": "./src/index.ts",
|
|
29
|
+
"module": "./src/index.ts",
|
|
30
|
+
"types": "./src/index.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"bun": "./src/index.ts",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"import": "./dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
48
|
+
"@pokit/core": "0.0.1"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"react": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/bun": "latest",
|
|
57
|
+
"@types/react": "^19.2.0",
|
|
58
|
+
"react": "^19.2.0",
|
|
59
|
+
"@pokit/core": "0.0.1"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"bun": ">=1.0.0"
|
|
63
|
+
}
|
|
64
|
+
}
|