toolcraft-openapi 0.0.17 → 0.0.19

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.
Files changed (88) hide show
  1. package/dist/bin/generate.js +7 -0
  2. package/dist/define-client.js +2 -2
  3. package/dist/generate.js +2 -2
  4. package/dist/http.d.ts +21 -2
  5. package/dist/http.js +147 -22
  6. package/dist/index.d.ts +1 -1
  7. package/dist/lock.d.ts +1 -1
  8. package/dist/lock.js +109 -5
  9. package/dist/mock/fetch.js +1 -1
  10. package/dist/network-error.d.ts +2 -0
  11. package/dist/network-error.js +83 -0
  12. package/dist/spec-source.js +103 -3
  13. package/node_modules/@poe-code/design-system/dist/acp/components.js +15 -13
  14. package/node_modules/@poe-code/design-system/dist/components/color.d.ts +31 -0
  15. package/node_modules/@poe-code/design-system/dist/components/color.js +101 -0
  16. package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.d.ts +1 -0
  17. package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.js +1 -1
  18. package/node_modules/@poe-code/design-system/dist/components/index.d.ts +4 -0
  19. package/node_modules/@poe-code/design-system/dist/components/index.js +2 -0
  20. package/node_modules/@poe-code/design-system/dist/components/logger.js +2 -2
  21. package/node_modules/@poe-code/design-system/dist/components/symbols.js +3 -3
  22. package/node_modules/@poe-code/design-system/dist/components/table.js +191 -40
  23. package/node_modules/@poe-code/design-system/dist/components/template.d.ts +6 -0
  24. package/node_modules/@poe-code/design-system/dist/components/template.js +271 -0
  25. package/node_modules/@poe-code/design-system/dist/components/text.d.ts +1 -0
  26. package/node_modules/@poe-code/design-system/dist/components/text.js +11 -3
  27. package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +20 -13
  28. package/node_modules/@poe-code/design-system/dist/dashboard/keymap.d.ts +5 -0
  29. package/node_modules/@poe-code/design-system/dist/dashboard/keymap.js +146 -12
  30. package/node_modules/@poe-code/design-system/dist/dashboard/terminal.js +31 -0
  31. package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
  32. package/node_modules/@poe-code/design-system/dist/explorer/actions.d.ts +16 -0
  33. package/node_modules/@poe-code/design-system/dist/explorer/actions.js +39 -0
  34. package/node_modules/@poe-code/design-system/dist/explorer/demo.d.ts +13 -0
  35. package/node_modules/@poe-code/design-system/dist/explorer/demo.js +297 -0
  36. package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +61 -0
  37. package/node_modules/@poe-code/design-system/dist/explorer/events.js +1 -0
  38. package/node_modules/@poe-code/design-system/dist/explorer/filter.d.ts +10 -0
  39. package/node_modules/@poe-code/design-system/dist/explorer/filter.js +95 -0
  40. package/node_modules/@poe-code/design-system/dist/explorer/index.d.ts +8 -0
  41. package/node_modules/@poe-code/design-system/dist/explorer/index.js +8 -0
  42. package/node_modules/@poe-code/design-system/dist/explorer/jobs.d.ts +7 -0
  43. package/node_modules/@poe-code/design-system/dist/explorer/jobs.js +59 -0
  44. package/node_modules/@poe-code/design-system/dist/explorer/keymap.d.ts +21 -0
  45. package/node_modules/@poe-code/design-system/dist/explorer/keymap.js +363 -0
  46. package/node_modules/@poe-code/design-system/dist/explorer/layout.d.ts +20 -0
  47. package/node_modules/@poe-code/design-system/dist/explorer/layout.js +73 -0
  48. package/node_modules/@poe-code/design-system/dist/explorer/reducer.d.ts +9 -0
  49. package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +704 -0
  50. package/node_modules/@poe-code/design-system/dist/explorer/render/detail.d.ts +4 -0
  51. package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +96 -0
  52. package/node_modules/@poe-code/design-system/dist/explorer/render/footer.d.ts +4 -0
  53. package/node_modules/@poe-code/design-system/dist/explorer/render/footer.js +49 -0
  54. package/node_modules/@poe-code/design-system/dist/explorer/render/header.d.ts +4 -0
  55. package/node_modules/@poe-code/design-system/dist/explorer/render/header.js +56 -0
  56. package/node_modules/@poe-code/design-system/dist/explorer/render/index.d.ts +8 -0
  57. package/node_modules/@poe-code/design-system/dist/explorer/render/index.js +61 -0
  58. package/node_modules/@poe-code/design-system/dist/explorer/render/list.d.ts +4 -0
  59. package/node_modules/@poe-code/design-system/dist/explorer/render/list.js +106 -0
  60. package/node_modules/@poe-code/design-system/dist/explorer/render/modal.d.ts +3 -0
  61. package/node_modules/@poe-code/design-system/dist/explorer/render/modal.js +91 -0
  62. package/node_modules/@poe-code/design-system/dist/explorer/render/test-fixtures.d.ts +8 -0
  63. package/node_modules/@poe-code/design-system/dist/explorer/render/test-fixtures.js +156 -0
  64. package/node_modules/@poe-code/design-system/dist/explorer/runtime.d.ts +2 -0
  65. package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +282 -0
  66. package/node_modules/@poe-code/design-system/dist/explorer/runtime.test-helpers.d.ts +50 -0
  67. package/node_modules/@poe-code/design-system/dist/explorer/runtime.test-helpers.js +101 -0
  68. package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +130 -0
  69. package/node_modules/@poe-code/design-system/dist/explorer/state.js +87 -0
  70. package/node_modules/@poe-code/design-system/dist/explorer/theme.d.ts +27 -0
  71. package/node_modules/@poe-code/design-system/dist/explorer/theme.js +97 -0
  72. package/node_modules/@poe-code/design-system/dist/index.d.ts +7 -0
  73. package/node_modules/@poe-code/design-system/dist/index.js +5 -0
  74. package/node_modules/@poe-code/design-system/dist/internal/color-support.d.ts +9 -0
  75. package/node_modules/@poe-code/design-system/dist/internal/color-support.js +12 -0
  76. package/node_modules/@poe-code/design-system/dist/prompts/index.js +2 -2
  77. package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.js +2 -2
  78. package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -2
  79. package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +4 -4
  80. package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +5 -5
  81. package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.js +2 -2
  82. package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.js +3 -3
  83. package/node_modules/@poe-code/design-system/dist/static/menu.js +5 -5
  84. package/node_modules/@poe-code/design-system/dist/static/spinner.js +8 -8
  85. package/node_modules/@poe-code/design-system/dist/tokens/colors.js +29 -29
  86. package/node_modules/@poe-code/design-system/dist/tokens/typography.js +6 -6
  87. package/node_modules/@poe-code/design-system/package.json +6 -3
  88. package/package.json +2 -4
@@ -0,0 +1,704 @@
1
+ import { buildActionContext, resolveAction } from "./actions.js";
2
+ import { filterRows } from "./filter.js";
3
+ import { REGION_ALL, REGION_DETAIL, REGION_FOOTER, REGION_HEADER, REGION_LIST, REGION_MODAL, resolveExplorerLayoutMode } from "./state.js";
4
+ const NO_EFFECTS = [];
5
+ const DEFAULT_ACTION_HANDLES = {
6
+ refresh: async () => undefined,
7
+ suspendAnd: async (fn) => fn(),
8
+ toast: () => undefined,
9
+ confirm: async () => false,
10
+ exit: () => undefined
11
+ };
12
+ export function step(state, event, runtimeHandles = DEFAULT_ACTION_HANDLES) {
13
+ switch (event.type) {
14
+ case "key":
15
+ return stepKey(state, event.key, runtimeHandles);
16
+ case "resize":
17
+ return resize(state, event.cols, event.rows);
18
+ case "rowsLoaded":
19
+ return rowsLoaded(state, event.rows);
20
+ case "detailLoading":
21
+ return detailLoading(state, event.rowId, event.token);
22
+ case "detailLoaded":
23
+ return detailLoaded(state, event.rowId, event.token, event.items);
24
+ case "detailError":
25
+ return detailError(state, event.rowId, event.token, event.error);
26
+ case "actionResolved":
27
+ return actionResolved(state, event.actionId);
28
+ case "toastExpired":
29
+ return expireToast(state);
30
+ case "suspendResumed":
31
+ return suspendResumed(state, event.emit, runtimeHandles);
32
+ case "modalDismissed":
33
+ return modalDismissed(state, event.result, runtimeHandles);
34
+ }
35
+ }
36
+ function stepKey(state, key, runtimeHandles) {
37
+ const target = state.bindings.resolve(key);
38
+ if (state.modal !== null) {
39
+ return stepModalKey(state, key, target, runtimeHandles);
40
+ }
41
+ if (state.filterFocused) {
42
+ return stepFilterKey(state, key, target, runtimeHandles);
43
+ }
44
+ if (target?.type === "action") {
45
+ const action = resolveAction(state, key);
46
+ return action === null ? mark(state, 0) : dispatchAction(state, action, false, runtimeHandles);
47
+ }
48
+ if (target?.type === "builtin") {
49
+ switch (target.id) {
50
+ case "quit":
51
+ return { state: markDirty(state, 0), effects: [{ type: "exit", result: null }] };
52
+ case "filter":
53
+ return focusFilter(state);
54
+ case "help":
55
+ return setModal(state, { kind: "help" });
56
+ case "palette":
57
+ return setModal(state, { kind: "palette", query: "", cursor: 0 });
58
+ case "cursorUp":
59
+ return moveCursor(state, -1);
60
+ case "cursorDown":
61
+ return moveCursor(state, 1);
62
+ case "top":
63
+ return setCursor(state, 0);
64
+ case "bottom":
65
+ return setCursor(state, state.filtered.length - 1);
66
+ case "pageUp":
67
+ return moveCursor(state, -pageSize(state));
68
+ case "pageDown":
69
+ return moveCursor(state, pageSize(state));
70
+ case "focusNext":
71
+ return focusNext(state);
72
+ case "escape":
73
+ return escape(state, runtimeHandles);
74
+ case "confirm":
75
+ return confirmKey(state, runtimeHandles);
76
+ case "toggleSelect":
77
+ return toggleSelect(state);
78
+ case "selectAll":
79
+ return selectAll(state);
80
+ case "clearSelection":
81
+ return clearSelection(state);
82
+ case "detailScrollDown":
83
+ return detailScroll(state, 1);
84
+ case "detailScrollUp":
85
+ return detailScroll(state, -1);
86
+ case "extendSelectionUp":
87
+ return extendSelection(state, -1);
88
+ case "extendSelectionDown":
89
+ return extendSelection(state, 1);
90
+ case "reorderUp":
91
+ return reorder(state, -1);
92
+ case "reorderDown":
93
+ return reorder(state, 1);
94
+ }
95
+ }
96
+ if (isBackspace(key)) {
97
+ return updateFilter(state, state.filter.slice(0, -1));
98
+ }
99
+ if (isPrintable(key)) {
100
+ return updateFilter(state, `${state.filter}${key.ch}`);
101
+ }
102
+ return mark(state, 0);
103
+ }
104
+ function stepFilterKey(state, key, target, runtimeHandles) {
105
+ if (target?.type === "builtin" && target.id === "escape") {
106
+ return escape(state, runtimeHandles);
107
+ }
108
+ if (target?.type === "builtin" && target.id === "confirm") {
109
+ return {
110
+ state: { ...state, filterFocused: false, dirty: REGION_HEADER | REGION_FOOTER },
111
+ effects: NO_EFFECTS
112
+ };
113
+ }
114
+ if (isBackspace(key)) {
115
+ return updateFilter(state, state.filter.slice(0, -1));
116
+ }
117
+ if (isPrintable(key)) {
118
+ return updateFilter(state, `${state.filter}${key.ch}`);
119
+ }
120
+ return mark(state, 0);
121
+ }
122
+ function stepModalKey(state, key, target, runtimeHandles) {
123
+ if (state.modal?.kind === "confirm") {
124
+ if (target?.type === "builtin" && target.id === "escape") {
125
+ return modalDismissed(state, false, runtimeHandles);
126
+ }
127
+ if (target?.type === "builtin" && target.id === "confirm") {
128
+ return modalDismissed(state, true, runtimeHandles);
129
+ }
130
+ if (isConfirmNo(key)) {
131
+ return modalDismissed(state, false, runtimeHandles);
132
+ }
133
+ if (isConfirmYes(key)) {
134
+ return modalDismissed(state, true, runtimeHandles);
135
+ }
136
+ return mark(state, 0);
137
+ }
138
+ if (state.modal?.kind === "help") {
139
+ if (target?.type === "builtin" && (target.id === "escape" || target.id === "help")) {
140
+ return modalDismissed(state, false, runtimeHandles);
141
+ }
142
+ return mark(state, 0);
143
+ }
144
+ if (state.modal?.kind === "palette") {
145
+ if (target?.type === "builtin") {
146
+ switch (target.id) {
147
+ case "escape":
148
+ return modalDismissed(state, false, runtimeHandles);
149
+ case "confirm":
150
+ return dispatchPaletteAction(state, runtimeHandles);
151
+ case "cursorUp":
152
+ return movePaletteCursor(state, -1);
153
+ case "cursorDown":
154
+ return movePaletteCursor(state, 1);
155
+ }
156
+ }
157
+ return paletteInput(state, key);
158
+ }
159
+ return mark(state, 0);
160
+ }
161
+ function resize(state, cols, rows) {
162
+ const size = { cols: normalizeSize(cols), rows: normalizeSize(rows) };
163
+ const layout = resolveExplorerLayoutMode(size.cols);
164
+ if (state.size.cols === size.cols && state.size.rows === size.rows && state.layout === layout) {
165
+ return mark(state, 0);
166
+ }
167
+ return {
168
+ state: { ...state, size, layout, dirty: REGION_ALL },
169
+ effects: NO_EFFECTS
170
+ };
171
+ }
172
+ function rowsLoaded(state, rows) {
173
+ const matches = filterRows(state.filter, rows);
174
+ const filtered = matches.map((match) => match.index);
175
+ const matchPositions = createMatchPositions(matches);
176
+ const cursor = clamp(state.cursor, 0, Math.max(0, filtered.length - 1));
177
+ const next = {
178
+ ...state,
179
+ rows,
180
+ filtered,
181
+ matchPositions,
182
+ cursor,
183
+ selected: pruneSelection(state.selected, rows),
184
+ detail: resetDetailForCursor(state, rows, filtered, cursor),
185
+ modal: modalStillValid(state.modal, rows),
186
+ actionState: recomputeActionState({ ...state, rows, filtered, matchPositions, cursor }),
187
+ dirty: REGION_HEADER | REGION_LIST | REGION_DETAIL | REGION_FOOTER | REGION_MODAL
188
+ };
189
+ const effect = detailEffect(next);
190
+ return { state: next, effects: effect === undefined ? NO_EFFECTS : [effect] };
191
+ }
192
+ function detailLoading(state, rowId, token) {
193
+ if (state.detail.rowId !== rowId || state.detail.token !== token) {
194
+ return mark(state, 0);
195
+ }
196
+ if (state.detail.loading) {
197
+ return mark(state, 0);
198
+ }
199
+ return {
200
+ state: { ...state, detail: { ...state.detail, loading: true }, dirty: REGION_DETAIL },
201
+ effects: NO_EFFECTS
202
+ };
203
+ }
204
+ function detailLoaded(state, rowId, token, items) {
205
+ if (state.detail.rowId !== rowId || state.detail.token !== token) {
206
+ return mark(state, 0);
207
+ }
208
+ const detail = {
209
+ ...state.detail,
210
+ items,
211
+ cursor: clamp(state.detail.cursor, 0, Math.max(0, items.length - 1)),
212
+ scroll: 0,
213
+ loading: false
214
+ };
215
+ return {
216
+ state: {
217
+ ...state,
218
+ detail,
219
+ actionState: recomputeActionState({ ...state, detail }),
220
+ dirty: REGION_DETAIL | REGION_FOOTER
221
+ },
222
+ effects: NO_EFFECTS
223
+ };
224
+ }
225
+ function detailError(state, rowId, token, error) {
226
+ if (state.detail.rowId !== rowId || state.detail.token !== token) {
227
+ return mark(state, 0);
228
+ }
229
+ return detailLoaded(state, rowId, token, [
230
+ {
231
+ id: `${rowId}:error`,
232
+ title: "Error",
233
+ badge: { text: "error", tone: "error" },
234
+ render: () => error.message
235
+ }
236
+ ]);
237
+ }
238
+ function actionResolved(state, actionId) {
239
+ const current = state.actionState.get(actionId);
240
+ if (current === undefined || current.running !== true) {
241
+ return mark(state, 0);
242
+ }
243
+ const actionState = new Map(state.actionState);
244
+ actionState.set(actionId, { ...current, running: false });
245
+ return { state: { ...state, actionState, dirty: REGION_FOOTER }, effects: NO_EFFECTS };
246
+ }
247
+ function expireToast(state) {
248
+ if (state.toast === null) {
249
+ return mark(state, 0);
250
+ }
251
+ return { state: { ...state, toast: null, dirty: REGION_FOOTER }, effects: NO_EFFECTS };
252
+ }
253
+ function suspendResumed(state, emit, runtimeHandles) {
254
+ const next = step(state, emit, runtimeHandles);
255
+ return {
256
+ state: { ...next.state, dirty: next.state.dirty | REGION_ALL },
257
+ effects: next.effects
258
+ };
259
+ }
260
+ function modalDismissed(state, result, runtimeHandles) {
261
+ const modal = state.modal;
262
+ const closed = { ...state, modal: null, dirty: REGION_MODAL | REGION_FOOTER };
263
+ if (modal?.kind === "confirm") {
264
+ modal.resolver(result === true);
265
+ }
266
+ if (modal?.kind !== "confirm" || result !== true) {
267
+ return { state: closed, effects: NO_EFFECTS };
268
+ }
269
+ return dispatchAction(closed, modal.action, true, runtimeHandles, modal.rows);
270
+ }
271
+ function moveCursor(state, delta) {
272
+ if (state.focused === "detail" && hasDetailCursor(state)) {
273
+ return moveDetailCursor(state, delta);
274
+ }
275
+ return setCursor(state, state.cursor + delta);
276
+ }
277
+ function moveDetailCursor(state, delta) {
278
+ const max = Math.max(0, (state.detail.items?.length ?? 0) - 1);
279
+ const cursor = clamp(state.detail.cursor + delta, 0, max);
280
+ if (cursor === state.detail.cursor) {
281
+ return mark(state, 0);
282
+ }
283
+ const detail = { ...state.detail, cursor };
284
+ return {
285
+ state: {
286
+ ...state,
287
+ detail,
288
+ actionState: recomputeActionState({ ...state, detail }),
289
+ dirty: REGION_DETAIL | REGION_FOOTER
290
+ },
291
+ effects: NO_EFFECTS
292
+ };
293
+ }
294
+ function hasDetailCursor(state) {
295
+ const items = state.detail.items ?? [];
296
+ return items.some((item) => item.title !== undefined);
297
+ }
298
+ function setCursor(state, cursor) {
299
+ const nextCursor = clamp(cursor, 0, Math.max(0, state.filtered.length - 1));
300
+ if (nextCursor === state.cursor) {
301
+ return mark(state, 0);
302
+ }
303
+ const detail = resetDetailForCursor(state, state.rows, state.filtered, nextCursor);
304
+ const next = {
305
+ ...state,
306
+ cursor: nextCursor,
307
+ detail,
308
+ actionState: recomputeActionState({ ...state, cursor: nextCursor, detail }),
309
+ dirty: REGION_LIST | REGION_DETAIL | REGION_FOOTER
310
+ };
311
+ const effect = detailEffect(next);
312
+ return { state: next, effects: effect === undefined ? NO_EFFECTS : [effect] };
313
+ }
314
+ function updateFilter(state, filter) {
315
+ if (filter === state.filter) {
316
+ return mark(state, 0);
317
+ }
318
+ const matches = filterRows(filter, state.rows);
319
+ const filtered = matches.map((match) => match.index);
320
+ const matchPositions = createMatchPositions(matches);
321
+ const cursor = clamp(0, 0, Math.max(0, filtered.length - 1));
322
+ const detail = resetDetailForCursor({ ...state, filter }, state.rows, filtered, cursor);
323
+ const next = {
324
+ ...state,
325
+ filter,
326
+ filterFocused: filter === "" ? false : state.filterFocused,
327
+ filtered,
328
+ matchPositions,
329
+ cursor,
330
+ detail,
331
+ actionState: recomputeActionState({ ...state, filter, filtered, matchPositions, cursor, detail }),
332
+ dirty: REGION_HEADER | REGION_LIST | REGION_DETAIL | REGION_FOOTER
333
+ };
334
+ const effect = detailEffect(next);
335
+ return { state: next, effects: effect === undefined ? NO_EFFECTS : [effect] };
336
+ }
337
+ function focusFilter(state) {
338
+ if (state.filterFocused) {
339
+ return mark(state, 0);
340
+ }
341
+ return {
342
+ state: { ...state, filterFocused: true, dirty: REGION_HEADER | REGION_FOOTER },
343
+ effects: NO_EFFECTS
344
+ };
345
+ }
346
+ function focusNext(state) {
347
+ const focused = state.focused === "list" ? "detail" : "list";
348
+ return {
349
+ state: {
350
+ ...state,
351
+ focused,
352
+ actionState: recomputeActionState({ ...state, focused }),
353
+ dirty: REGION_LIST | REGION_DETAIL | REGION_FOOTER
354
+ },
355
+ effects: NO_EFFECTS
356
+ };
357
+ }
358
+ function escape(state, runtimeHandles) {
359
+ if (state.filterFocused || state.filter.length > 0) {
360
+ const cleared = updateFilter({ ...state, filterFocused: false }, "");
361
+ return {
362
+ state: { ...cleared.state, filterFocused: false, dirty: cleared.state.dirty | REGION_HEADER | REGION_FOOTER },
363
+ effects: cleared.effects
364
+ };
365
+ }
366
+ if (state.selected.size > 0) {
367
+ return clearSelection(state);
368
+ }
369
+ if (state.modal !== null) {
370
+ return modalDismissed(state, false, runtimeHandles);
371
+ }
372
+ return { state: markDirty(state, 0), effects: [{ type: "exit", result: null }] };
373
+ }
374
+ function confirmKey(state, runtimeHandles) {
375
+ if (state.modal?.kind === "confirm") {
376
+ return modalDismissed(state, true, runtimeHandles);
377
+ }
378
+ return dispatchPrimary(state, runtimeHandles);
379
+ }
380
+ function toggleSelect(state) {
381
+ const row = currentRow(state);
382
+ if (row === undefined) {
383
+ return mark(state, 0);
384
+ }
385
+ const selected = new Set(state.selected);
386
+ if (selected.has(row.id)) {
387
+ selected.delete(row.id);
388
+ }
389
+ else {
390
+ selected.add(row.id);
391
+ }
392
+ return selectionChanged(state, selected);
393
+ }
394
+ function selectAll(state) {
395
+ const selected = new Set(state.selected);
396
+ for (const index of state.filtered) {
397
+ const row = state.rows[index];
398
+ if (row !== undefined) {
399
+ selected.add(row.id);
400
+ }
401
+ }
402
+ return selectionChanged(state, selected);
403
+ }
404
+ function clearSelection(state) {
405
+ if (state.selected.size === 0) {
406
+ return mark(state, 0);
407
+ }
408
+ return selectionChanged(state, new Set());
409
+ }
410
+ function selectionChanged(state, selected) {
411
+ if (setsEqual(state.selected, selected)) {
412
+ return mark(state, 0);
413
+ }
414
+ const next = {
415
+ ...state,
416
+ selected,
417
+ actionState: recomputeActionState({ ...state, selected }),
418
+ dirty: REGION_LIST | REGION_FOOTER
419
+ };
420
+ return { state: next, effects: NO_EFFECTS };
421
+ }
422
+ function detailScroll(state, delta) {
423
+ if (state.focused !== "detail") {
424
+ return mark(state, 0);
425
+ }
426
+ const scroll = Math.max(0, state.detail.scroll + delta);
427
+ if (scroll === state.detail.scroll) {
428
+ return mark(state, 0);
429
+ }
430
+ return {
431
+ state: { ...state, detail: { ...state.detail, scroll }, dirty: REGION_DETAIL },
432
+ effects: NO_EFFECTS
433
+ };
434
+ }
435
+ function extendSelection(state, delta) {
436
+ const moved = moveCursor(state, delta);
437
+ const row = currentRow(moved.state);
438
+ if (row === undefined) {
439
+ return moved;
440
+ }
441
+ const selected = new Set(moved.state.selected);
442
+ selected.add(row.id);
443
+ return {
444
+ state: {
445
+ ...moved.state,
446
+ selected,
447
+ actionState: recomputeActionState({ ...moved.state, selected }),
448
+ dirty: moved.state.dirty
449
+ },
450
+ effects: moved.effects
451
+ };
452
+ }
453
+ function reorder(state, delta) {
454
+ if (state.filter !== "" || state.focused !== "list" || state.modal !== null) {
455
+ return mark(state, 0);
456
+ }
457
+ const rowIndex = state.filtered[state.cursor];
458
+ if (rowIndex === undefined) {
459
+ return mark(state, 0);
460
+ }
461
+ const targetIndex = rowIndex + delta;
462
+ if (targetIndex < 0 || targetIndex >= state.rows.length) {
463
+ return mark(state, 0);
464
+ }
465
+ const rows = [...state.rows];
466
+ const current = rows[rowIndex];
467
+ const target = rows[targetIndex];
468
+ if (current === undefined || target === undefined) {
469
+ return mark(state, 0);
470
+ }
471
+ rows[rowIndex] = target;
472
+ rows[targetIndex] = current;
473
+ const filtered = rows.map((_, index) => index);
474
+ const matchPositions = new Map();
475
+ const cursor = targetIndex;
476
+ const next = {
477
+ ...state,
478
+ rows,
479
+ filtered,
480
+ matchPositions,
481
+ cursor,
482
+ actionState: recomputeActionState({ ...state, rows, filtered, matchPositions, cursor }),
483
+ dirty: REGION_LIST | REGION_FOOTER
484
+ };
485
+ return { state: next, effects: [{ type: "persistOrder", orderedIds: rows.map((row) => row.id) }] };
486
+ }
487
+ function paletteInput(state, key) {
488
+ if (state.modal?.kind !== "palette") {
489
+ return mark(state, 0);
490
+ }
491
+ if (isBackspace(key)) {
492
+ return setPaletteQuery(state, state.modal.query.slice(0, -1));
493
+ }
494
+ if (isPrintable(key)) {
495
+ return setPaletteQuery(state, `${state.modal.query}${key.ch}`);
496
+ }
497
+ return mark(state, 0);
498
+ }
499
+ function setPaletteQuery(state, query) {
500
+ if (state.modal?.kind !== "palette") {
501
+ return mark(state, 0);
502
+ }
503
+ const entries = paletteEntries({ ...state, modal: { ...state.modal, query } });
504
+ return setModal(state, {
505
+ ...state.modal,
506
+ query,
507
+ cursor: clamp(state.modal.cursor, 0, Math.max(0, entries.length - 1))
508
+ });
509
+ }
510
+ function movePaletteCursor(state, delta) {
511
+ if (state.modal?.kind !== "palette") {
512
+ return mark(state, 0);
513
+ }
514
+ const max = Math.max(0, paletteEntries(state).length - 1);
515
+ const cursor = clamp(state.modal.cursor + delta, 0, max);
516
+ if (cursor === state.modal.cursor) {
517
+ return mark(state, 0);
518
+ }
519
+ return setModal(state, { ...state.modal, cursor });
520
+ }
521
+ function dispatchPaletteAction(state, runtimeHandles) {
522
+ if (state.modal?.kind !== "palette") {
523
+ return mark(state, 0);
524
+ }
525
+ const entry = paletteEntries(state)[state.modal.cursor];
526
+ if (entry === undefined) {
527
+ return mark(state, 0);
528
+ }
529
+ return dispatchActionById({ ...state, modal: null, dirty: REGION_MODAL | REGION_FOOTER }, entry.id, false, runtimeHandles);
530
+ }
531
+ function dispatchPrimary(state, runtimeHandles) {
532
+ for (const [id, entry] of state.actionState.entries()) {
533
+ if (entry.action?.primary === true) {
534
+ return dispatchActionById(state, id, false, runtimeHandles);
535
+ }
536
+ }
537
+ return mark(state, 0);
538
+ }
539
+ function dispatchActionById(state, actionId, confirmed, runtimeHandles) {
540
+ const entry = state.actionState.get(actionId);
541
+ if (entry?.available !== true || entry.running === true || entry.action === undefined) {
542
+ return mark(state, 0);
543
+ }
544
+ return dispatchAction(state, entry.action, confirmed, runtimeHandles);
545
+ }
546
+ function dispatchAction(state, action, confirmed, runtimeHandles, modalRows) {
547
+ const rows = modalRows ?? selectedRows(state);
548
+ if (rows.length === 0) {
549
+ return mark(state, 0);
550
+ }
551
+ if (action.destructive === true && !confirmed) {
552
+ return {
553
+ state: {
554
+ ...state,
555
+ modal: { kind: "confirm", action, rows, resolver: () => undefined },
556
+ dirty: REGION_MODAL | REGION_FOOTER
557
+ },
558
+ effects: NO_EFFECTS
559
+ };
560
+ }
561
+ const actionState = new Map(state.actionState);
562
+ const current = actionState.get(action.id);
563
+ if (current !== undefined) {
564
+ actionState.set(action.id, { ...current, running: true });
565
+ }
566
+ const next = { ...state, actionState, dirty: state.dirty | REGION_FOOTER };
567
+ return {
568
+ state: next,
569
+ effects: [
570
+ {
571
+ type: "suspend",
572
+ fn: async () => action.handler(buildActionContext(next, action, current?.source ?? actionSource(next, action), runtimeHandles, rows)),
573
+ resumeWith: () => ({ type: "actionResolved", actionId: action.id })
574
+ }
575
+ ]
576
+ };
577
+ }
578
+ function recomputeActionState(view) {
579
+ const next = new Map();
580
+ for (const [id, entry] of view.actionState.entries()) {
581
+ const { action } = entry;
582
+ if (action === undefined) {
583
+ next.set(id, entry);
584
+ continue;
585
+ }
586
+ const ctx = buildActionContext(view, action, entry.source ?? actionSource(view, action), DEFAULT_ACTION_HANDLES);
587
+ const available = action.predicate === undefined ? entry.available : action.predicate(ctx);
588
+ const label = typeof action.label === "function" ? action.label() : action.label;
589
+ next.set(id, { ...entry, available, label });
590
+ }
591
+ return next;
592
+ }
593
+ function resetDetailForCursor(state, rows, filtered, cursor) {
594
+ const row = rows[filtered[cursor] ?? -1];
595
+ if (row === undefined) {
596
+ return {
597
+ rowId: null,
598
+ items: null,
599
+ cursor: 0,
600
+ scroll: 0,
601
+ token: state.detail.token + 1,
602
+ loading: false
603
+ };
604
+ }
605
+ return {
606
+ rowId: row.id,
607
+ items: null,
608
+ cursor: 0,
609
+ scroll: 0,
610
+ token: state.detail.token + 1,
611
+ loading: false
612
+ };
613
+ }
614
+ function detailEffect(state) {
615
+ if (state.detail.rowId === null) {
616
+ return undefined;
617
+ }
618
+ return { type: "renderDetail", rowId: state.detail.rowId, token: state.detail.token };
619
+ }
620
+ function currentRow(state) {
621
+ return state.rows[state.filtered[state.cursor] ?? -1];
622
+ }
623
+ function actionSource(state, action) {
624
+ return state.actionState.get(action.id)?.source ?? "row";
625
+ }
626
+ function selectedRows(state) {
627
+ if (state.selected.size === 0) {
628
+ const row = currentRow(state);
629
+ return row === undefined ? [] : [row];
630
+ }
631
+ return state.rows.filter((row) => state.selected.has(row.id));
632
+ }
633
+ function paletteEntries(state) {
634
+ const query = state.modal?.kind === "palette" ? state.modal.query.toLocaleLowerCase() : "";
635
+ const entries = [];
636
+ for (const [id, entry] of state.actionState.entries()) {
637
+ if (entry.available !== true || entry.running === true || entry.action === undefined) {
638
+ continue;
639
+ }
640
+ if (query !== "" && !entry.label.toLocaleLowerCase().includes(query)) {
641
+ continue;
642
+ }
643
+ entries.push({ id, label: entry.label });
644
+ }
645
+ return entries;
646
+ }
647
+ function setModal(state, modal) {
648
+ return { state: { ...state, modal, dirty: REGION_MODAL | REGION_FOOTER }, effects: NO_EFFECTS };
649
+ }
650
+ function mark(state, dirty) {
651
+ return { state: markDirty(state, dirty), effects: NO_EFFECTS };
652
+ }
653
+ function markDirty(state, dirty) {
654
+ return state.dirty === dirty ? state : { ...state, dirty };
655
+ }
656
+ function pageSize(state) {
657
+ return Math.max(1, Math.floor(state.size.rows / 2));
658
+ }
659
+ function pruneSelection(selected, rows) {
660
+ const ids = new Set(rows.map((row) => row.id));
661
+ return new Set([...selected].filter((id) => ids.has(id)));
662
+ }
663
+ function modalStillValid(modal, rows) {
664
+ if (modal?.kind !== "confirm") {
665
+ return modal;
666
+ }
667
+ const ids = new Set(rows.map((row) => row.id));
668
+ return modal.rows.every((row) => ids.has(row.id)) ? modal : null;
669
+ }
670
+ function normalizeSize(value) {
671
+ if (!Number.isFinite(value)) {
672
+ return 0;
673
+ }
674
+ return Math.max(0, Math.floor(value));
675
+ }
676
+ function clamp(value, min, max) {
677
+ return Math.min(max, Math.max(min, value));
678
+ }
679
+ function createMatchPositions(matches) {
680
+ return new Map(matches.map((match) => [match.index, match.positions]));
681
+ }
682
+ function setsEqual(left, right) {
683
+ if (left.size !== right.size) {
684
+ return false;
685
+ }
686
+ for (const value of left) {
687
+ if (!right.has(value)) {
688
+ return false;
689
+ }
690
+ }
691
+ return true;
692
+ }
693
+ function isPrintable(key) {
694
+ return key.ch !== undefined && !key.ctrl && !key.meta;
695
+ }
696
+ function isBackspace(key) {
697
+ return key.name === "backspace" || key.name === "delete";
698
+ }
699
+ function isConfirmYes(key) {
700
+ return key.ch === "y" || key.ch === "Y";
701
+ }
702
+ function isConfirmNo(key) {
703
+ return key.ch === "n" || key.ch === "N";
704
+ }
@@ -0,0 +1,4 @@
1
+ import { ScreenBuffer } from "../../dashboard/buffer.js";
2
+ import type { ExplorerLayout } from "../layout.js";
3
+ import type { ExplorerState } from "../state.js";
4
+ export declare function renderDetail(state: ExplorerState, screen: ScreenBuffer, layout: ExplorerLayout): void;