@docyrus/docyrus 0.0.5 → 0.0.7
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/main.js +1 -1
- package/main.js.map +1 -1
- package/package.json +1 -1
- package/tui.mjs +493 -63
- package/tui.mjs.map +4 -4
package/tui.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { createRoot } from "@opentui/react";
|
|
|
4
4
|
|
|
5
5
|
// src/tui/opentui/DocyrusOpenTuiApp.tsx
|
|
6
6
|
import { useKeyboard as useKeyboard2, useRenderer } from "@opentui/react";
|
|
7
|
-
import { useCallback as useCallback2, useEffect, useMemo, useState as useState2 } from "react";
|
|
7
|
+
import { useCallback as useCallback2, useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
8
8
|
|
|
9
9
|
// src/services/tuiProcessExecutor.ts
|
|
10
10
|
import { spawnSync } from "node:child_process";
|
|
@@ -202,21 +202,6 @@ function preventDefault(event) {
|
|
|
202
202
|
event.preventDefault();
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
|
-
function resolveSectionJumpKey(keyName, shift) {
|
|
206
|
-
if (keyName === "s") {
|
|
207
|
-
return "shortcuts";
|
|
208
|
-
}
|
|
209
|
-
if (keyName === "h") {
|
|
210
|
-
return "history";
|
|
211
|
-
}
|
|
212
|
-
if (keyName === "m") {
|
|
213
|
-
return "messages";
|
|
214
|
-
}
|
|
215
|
-
if (keyName === "?" || keyName === "/" && shift) {
|
|
216
|
-
return "help";
|
|
217
|
-
}
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
205
|
function useCommandInput(options) {
|
|
221
206
|
const [history, setHistory] = useState([]);
|
|
222
207
|
const [historyCursor, setHistoryCursor] = useState(null);
|
|
@@ -273,12 +258,13 @@ function useCommandInput(options) {
|
|
|
273
258
|
return;
|
|
274
259
|
}
|
|
275
260
|
const keyName = keyEvent.name;
|
|
261
|
+
const hotkeysEnabled = options.hotkeysEnabled !== false;
|
|
276
262
|
if (keyEvent.ctrl && keyName === "l") {
|
|
277
263
|
preventDefault(keyEvent);
|
|
278
264
|
options.onClear();
|
|
279
265
|
return;
|
|
280
266
|
}
|
|
281
|
-
if (keyName === "tab") {
|
|
267
|
+
if (hotkeysEnabled && keyName === "tab") {
|
|
282
268
|
preventDefault(keyEvent);
|
|
283
269
|
if (keyEvent.shift) {
|
|
284
270
|
options.onPreviousSection();
|
|
@@ -287,12 +273,12 @@ function useCommandInput(options) {
|
|
|
287
273
|
options.onNextSection();
|
|
288
274
|
return;
|
|
289
275
|
}
|
|
290
|
-
if (keyName === "[") {
|
|
276
|
+
if (hotkeysEnabled && keyName === "[") {
|
|
291
277
|
preventDefault(keyEvent);
|
|
292
278
|
options.onPreviousSection();
|
|
293
279
|
return;
|
|
294
280
|
}
|
|
295
|
-
if (keyName === "]") {
|
|
281
|
+
if (hotkeysEnabled && keyName === "]") {
|
|
296
282
|
preventDefault(keyEvent);
|
|
297
283
|
options.onNextSection();
|
|
298
284
|
return;
|
|
@@ -307,18 +293,10 @@ function useCommandInput(options) {
|
|
|
307
293
|
recallHistory("down");
|
|
308
294
|
return;
|
|
309
295
|
}
|
|
310
|
-
if (options.input.length === 0) {
|
|
311
|
-
const jumpSection = resolveSectionJumpKey(keyName, Boolean(keyEvent.shift));
|
|
312
|
-
if (jumpSection) {
|
|
313
|
-
preventDefault(keyEvent);
|
|
314
|
-
options.onJumpSection(jumpSection);
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
296
|
if (options.busy) {
|
|
319
297
|
return;
|
|
320
298
|
}
|
|
321
|
-
if (options.input.length === 0 && /^[1-6]$/.test(keyName)) {
|
|
299
|
+
if (hotkeysEnabled && options.input.length === 0 && /^[1-6]$/.test(keyName)) {
|
|
322
300
|
preventDefault(keyEvent);
|
|
323
301
|
options.onShortcut(Number(keyName));
|
|
324
302
|
}
|
|
@@ -328,6 +306,188 @@ function useCommandInput(options) {
|
|
|
328
306
|
};
|
|
329
307
|
}
|
|
330
308
|
|
|
309
|
+
// src/tui/opentui/dataSourceCatalog.ts
|
|
310
|
+
function isRecord(value) {
|
|
311
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
312
|
+
}
|
|
313
|
+
function toNonEmptyString(value) {
|
|
314
|
+
if (typeof value !== "string") {
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
const trimmed = value.trim();
|
|
318
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
319
|
+
}
|
|
320
|
+
function toStringValue(value, fallback) {
|
|
321
|
+
return toNonEmptyString(value) || fallback;
|
|
322
|
+
}
|
|
323
|
+
function getArrayCandidates(payload) {
|
|
324
|
+
if (Array.isArray(payload)) {
|
|
325
|
+
return payload;
|
|
326
|
+
}
|
|
327
|
+
if (!isRecord(payload)) {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
const directData = payload.data;
|
|
331
|
+
if (Array.isArray(directData)) {
|
|
332
|
+
return directData;
|
|
333
|
+
}
|
|
334
|
+
if (isRecord(directData) && Array.isArray(directData.data)) {
|
|
335
|
+
return directData.data;
|
|
336
|
+
}
|
|
337
|
+
if (Array.isArray(payload.items)) {
|
|
338
|
+
return payload.items;
|
|
339
|
+
}
|
|
340
|
+
if (Array.isArray(payload.dataSources)) {
|
|
341
|
+
return payload.dataSources;
|
|
342
|
+
}
|
|
343
|
+
return [];
|
|
344
|
+
}
|
|
345
|
+
function compareByName(a, b) {
|
|
346
|
+
return a.localeCompare(b, void 0, { sensitivity: "base" });
|
|
347
|
+
}
|
|
348
|
+
function getDataSourceDisplayName(dataSource) {
|
|
349
|
+
return dataSource.title || dataSource.name || dataSource.slug;
|
|
350
|
+
}
|
|
351
|
+
function extractAppsFromPayload(payload) {
|
|
352
|
+
const result = [];
|
|
353
|
+
const seen = /* @__PURE__ */ new Set();
|
|
354
|
+
for (const candidate of getArrayCandidates(payload)) {
|
|
355
|
+
if (!isRecord(candidate)) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
const id = toNonEmptyString(candidate.id);
|
|
359
|
+
const slug = toNonEmptyString(candidate.slug);
|
|
360
|
+
if (!id || !slug) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const name = toStringValue(candidate.name, slug);
|
|
364
|
+
const dedupeKey = `${id}::${slug}`;
|
|
365
|
+
if (seen.has(dedupeKey)) {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
seen.add(dedupeKey);
|
|
369
|
+
result.push({ id, slug, name });
|
|
370
|
+
}
|
|
371
|
+
result.sort((a, b) => compareByName(a.name, b.name));
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
function extractDataSourcesFromPayload(payload) {
|
|
375
|
+
const result = [];
|
|
376
|
+
const seen = /* @__PURE__ */ new Set();
|
|
377
|
+
for (const candidate of getArrayCandidates(payload)) {
|
|
378
|
+
if (!isRecord(candidate)) {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
const id = toNonEmptyString(candidate.id);
|
|
382
|
+
const slug = toNonEmptyString(candidate.slug);
|
|
383
|
+
if (!id || !slug) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const title = toNonEmptyString(candidate.title);
|
|
387
|
+
const name = toStringValue(candidate.name, slug);
|
|
388
|
+
const appSlug = toNonEmptyString(candidate.app_slug);
|
|
389
|
+
const tenantAppId = toNonEmptyString(candidate.tenant_app_id);
|
|
390
|
+
const dedupeKey = `${id}::${slug}`;
|
|
391
|
+
if (seen.has(dedupeKey)) {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
seen.add(dedupeKey);
|
|
395
|
+
result.push({
|
|
396
|
+
id,
|
|
397
|
+
name,
|
|
398
|
+
title,
|
|
399
|
+
slug,
|
|
400
|
+
app_slug: appSlug,
|
|
401
|
+
tenant_app_id: tenantAppId
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
result.sort((a, b) => compareByName(getDataSourceDisplayName(a), getDataSourceDisplayName(b)));
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
407
|
+
function groupDataSourcesByApp(params) {
|
|
408
|
+
const { apps, dataSources } = params;
|
|
409
|
+
const appById = /* @__PURE__ */ new Map();
|
|
410
|
+
const appBySlug = /* @__PURE__ */ new Map();
|
|
411
|
+
const groups = /* @__PURE__ */ new Map();
|
|
412
|
+
for (const app of apps) {
|
|
413
|
+
appById.set(app.id, app);
|
|
414
|
+
appBySlug.set(app.slug, app);
|
|
415
|
+
}
|
|
416
|
+
for (const dataSource of dataSources) {
|
|
417
|
+
const byId = dataSource.tenant_app_id ? appById.get(dataSource.tenant_app_id) : void 0;
|
|
418
|
+
const bySlug = dataSource.app_slug ? appBySlug.get(dataSource.app_slug) : void 0;
|
|
419
|
+
const matchedApp = byId || bySlug;
|
|
420
|
+
const appKey = matchedApp ? matchedApp.id : dataSource.app_slug ? `slug:${dataSource.app_slug}` : "unknown";
|
|
421
|
+
const group = groups.get(appKey) || {
|
|
422
|
+
appKey,
|
|
423
|
+
appId: matchedApp?.id || dataSource.tenant_app_id,
|
|
424
|
+
appName: matchedApp?.name || dataSource.app_slug || "Unknown App",
|
|
425
|
+
appSlug: matchedApp?.slug || dataSource.app_slug,
|
|
426
|
+
dataSources: []
|
|
427
|
+
};
|
|
428
|
+
group.dataSources.push(dataSource);
|
|
429
|
+
groups.set(appKey, group);
|
|
430
|
+
}
|
|
431
|
+
const list = Array.from(groups.values());
|
|
432
|
+
for (const group of list) {
|
|
433
|
+
group.dataSources.sort((a, b) => compareByName(getDataSourceDisplayName(a), getDataSourceDisplayName(b)));
|
|
434
|
+
}
|
|
435
|
+
list.sort((a, b) => compareByName(a.appName, b.appName));
|
|
436
|
+
return list;
|
|
437
|
+
}
|
|
438
|
+
function filterDataSources(groups, query) {
|
|
439
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
440
|
+
if (normalizedQuery.length === 0) {
|
|
441
|
+
return groups;
|
|
442
|
+
}
|
|
443
|
+
return groups.map((group) => {
|
|
444
|
+
const filteredDataSources = group.dataSources.filter((dataSource) => {
|
|
445
|
+
const haystack = [
|
|
446
|
+
dataSource.title,
|
|
447
|
+
dataSource.name,
|
|
448
|
+
dataSource.slug
|
|
449
|
+
].filter((value) => Boolean(value)).join(" ").toLowerCase();
|
|
450
|
+
return haystack.includes(normalizedQuery);
|
|
451
|
+
});
|
|
452
|
+
return {
|
|
453
|
+
...group,
|
|
454
|
+
dataSources: filteredDataSources
|
|
455
|
+
};
|
|
456
|
+
}).filter((group) => group.dataSources.length > 0);
|
|
457
|
+
}
|
|
458
|
+
function flattenTreeRows(params) {
|
|
459
|
+
const rows = [];
|
|
460
|
+
const alwaysExpanded = params.query.trim().length > 0;
|
|
461
|
+
for (const group of params.groups) {
|
|
462
|
+
const expanded = alwaysExpanded || params.expandedAppKeys.has(group.appKey);
|
|
463
|
+
rows.push({
|
|
464
|
+
id: `app:${group.appKey}`,
|
|
465
|
+
kind: "app",
|
|
466
|
+
appKey: group.appKey,
|
|
467
|
+
appName: group.appName,
|
|
468
|
+
appSlug: group.appSlug,
|
|
469
|
+
expanded
|
|
470
|
+
});
|
|
471
|
+
if (!expanded) {
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
for (const dataSource of group.dataSources) {
|
|
475
|
+
const dataSourceName = getDataSourceDisplayName(dataSource);
|
|
476
|
+
rows.push({
|
|
477
|
+
id: `ds:${group.appKey}:${dataSource.id}`,
|
|
478
|
+
kind: "datasource",
|
|
479
|
+
appKey: group.appKey,
|
|
480
|
+
appName: group.appName,
|
|
481
|
+
appSlug: group.appSlug || dataSource.app_slug,
|
|
482
|
+
dataSourceId: dataSource.id,
|
|
483
|
+
dataSourceName,
|
|
484
|
+
dataSourceSlug: dataSource.slug
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return rows;
|
|
489
|
+
}
|
|
490
|
+
|
|
331
491
|
// src/tui/opentui/renderResult.tsx
|
|
332
492
|
import { RGBA, SyntaxStyle } from "@opentui/core";
|
|
333
493
|
|
|
@@ -479,26 +639,26 @@ var SHORTCUTS = [
|
|
|
479
639
|
label: "insert: ds list <appSlug> <dataSourceSlug>"
|
|
480
640
|
}
|
|
481
641
|
];
|
|
482
|
-
var SECTION_ORDER = ["shortcuts", "history", "messages", "help"];
|
|
642
|
+
var SECTION_ORDER = ["shortcuts", "datasources", "history", "messages", "help"];
|
|
483
643
|
var FOCUS_ORDER = ["input", "left", "result"];
|
|
484
644
|
var ESC_ARM_TIMEOUT_MS = 1400;
|
|
485
|
-
function
|
|
645
|
+
function isRecord2(value) {
|
|
486
646
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
487
647
|
}
|
|
488
648
|
function extractMetadata(payload) {
|
|
489
|
-
if (!
|
|
649
|
+
if (!isRecord2(payload)) {
|
|
490
650
|
return {};
|
|
491
651
|
}
|
|
492
652
|
const environmentValue = payload.environment;
|
|
493
653
|
const contextValue = payload.context;
|
|
494
|
-
const environment =
|
|
654
|
+
const environment = isRecord2(environmentValue) && typeof environmentValue.id === "string" && typeof environmentValue.name === "string" ? { id: environmentValue.id, name: environmentValue.name } : void 0;
|
|
495
655
|
if (contextValue === null) {
|
|
496
656
|
return {
|
|
497
657
|
environment,
|
|
498
658
|
context: null
|
|
499
659
|
};
|
|
500
660
|
}
|
|
501
|
-
const context =
|
|
661
|
+
const context = isRecord2(contextValue) && typeof contextValue.email === "string" && typeof contextValue.tenantDisplay === "string" ? {
|
|
502
662
|
email: contextValue.email,
|
|
503
663
|
tenantDisplay: contextValue.tenantDisplay
|
|
504
664
|
} : void 0;
|
|
@@ -535,6 +695,17 @@ function isEscapeKey(keyName) {
|
|
|
535
695
|
function isEnterKey(keyName) {
|
|
536
696
|
return keyName === "enter" || keyName === "return";
|
|
537
697
|
}
|
|
698
|
+
function isSlashKey(keyName) {
|
|
699
|
+
return keyName === "/" || keyName === "slash";
|
|
700
|
+
}
|
|
701
|
+
function toCatalogErrorMessage(command, result) {
|
|
702
|
+
const details = result.error?.details || result.error?.message || "Unknown error.";
|
|
703
|
+
if (details.includes("Architect.Read.All") || details.includes("Architect.ReadWrite.All")) {
|
|
704
|
+
return `${command} failed: missing required scope Architect.Read.All or Architect.ReadWrite.All.`;
|
|
705
|
+
}
|
|
706
|
+
const message = result.error?.message || "Command failed.";
|
|
707
|
+
return `${command} failed: ${message}`;
|
|
708
|
+
}
|
|
538
709
|
function DocyrusOpenTuiApp(props) {
|
|
539
710
|
const renderer = useRenderer();
|
|
540
711
|
const [input, setInput] = useState2("");
|
|
@@ -551,11 +722,36 @@ function DocyrusOpenTuiApp(props) {
|
|
|
551
722
|
const [selectedHistoryIndex, setSelectedHistoryIndex] = useState2(0);
|
|
552
723
|
const [isExitConfirmOpen, setIsExitConfirmOpen] = useState2(false);
|
|
553
724
|
const [isEscArmed, setIsEscArmed] = useState2(false);
|
|
725
|
+
const [dataSourceSearch, setDataSourceSearch] = useState2("");
|
|
726
|
+
const [dataSourcePanelFocus, setDataSourcePanelFocus] = useState2("tree");
|
|
727
|
+
const [selectedDataSourceRowIndex, setSelectedDataSourceRowIndex] = useState2(0);
|
|
728
|
+
const [expandedAppKeys, setExpandedAppKeys] = useState2([]);
|
|
729
|
+
const [appsCatalog, setAppsCatalog] = useState2([]);
|
|
730
|
+
const [dataSourcesCatalog, setDataSourcesCatalog] = useState2([]);
|
|
731
|
+
const [isCatalogLoading, setIsCatalogLoading] = useState2(false);
|
|
732
|
+
const [catalogError, setCatalogError] = useState2(null);
|
|
733
|
+
const [catalogLoadedAt, setCatalogLoadedAt] = useState2(null);
|
|
734
|
+
const catalogScopeRef = useRef("");
|
|
554
735
|
const executor = useMemo(() => {
|
|
555
736
|
return createTuiProcessExecutor({
|
|
556
737
|
executionConfig: props.executionConfig
|
|
557
738
|
});
|
|
558
739
|
}, [props.executionConfig]);
|
|
740
|
+
const applyMetadataFromPayload = useCallback2((payload) => {
|
|
741
|
+
const metadata = extractMetadata(payload);
|
|
742
|
+
if (metadata.environment) {
|
|
743
|
+
setEnvironment(metadata.environment);
|
|
744
|
+
}
|
|
745
|
+
if (metadata.context !== void 0) {
|
|
746
|
+
setContext(metadata.context);
|
|
747
|
+
}
|
|
748
|
+
}, []);
|
|
749
|
+
const appendSystemMessages = useCallback2((messages) => {
|
|
750
|
+
if (messages.length === 0) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
setSystemMessages((previous) => [...previous, ...messages]);
|
|
754
|
+
}, []);
|
|
559
755
|
const applyCommandResult = useCallback2((result, command) => {
|
|
560
756
|
const entry = {
|
|
561
757
|
id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
@@ -567,18 +763,12 @@ function DocyrusOpenTuiApp(props) {
|
|
|
567
763
|
setActiveEntryId(entry.id);
|
|
568
764
|
setSelectedHistoryIndex(0);
|
|
569
765
|
if (result.messages && result.messages.length > 0) {
|
|
570
|
-
|
|
766
|
+
appendSystemMessages(result.messages);
|
|
571
767
|
}
|
|
572
768
|
if (result.ok && result.data !== void 0) {
|
|
573
|
-
|
|
574
|
-
if (metadata.environment) {
|
|
575
|
-
setEnvironment(metadata.environment);
|
|
576
|
-
}
|
|
577
|
-
if (metadata.context !== void 0) {
|
|
578
|
-
setContext(metadata.context);
|
|
579
|
-
}
|
|
769
|
+
applyMetadataFromPayload(result.data);
|
|
580
770
|
}
|
|
581
|
-
}, []);
|
|
771
|
+
}, [appendSystemMessages, applyMetadataFromPayload]);
|
|
582
772
|
const executeArgs = useCallback2(async (args, commandText) => {
|
|
583
773
|
setIsRunning(true);
|
|
584
774
|
try {
|
|
@@ -588,6 +778,13 @@ function DocyrusOpenTuiApp(props) {
|
|
|
588
778
|
setIsRunning(false);
|
|
589
779
|
}
|
|
590
780
|
}, [applyCommandResult, executor]);
|
|
781
|
+
const runInternalArgs = useCallback2(async (args) => {
|
|
782
|
+
const result = await executor.runCliCommand(args);
|
|
783
|
+
if (result.ok && result.data !== void 0) {
|
|
784
|
+
applyMetadataFromPayload(result.data);
|
|
785
|
+
}
|
|
786
|
+
return result;
|
|
787
|
+
}, [applyMetadataFromPayload, executor]);
|
|
591
788
|
const executeLine = useCallback2((line) => {
|
|
592
789
|
let args;
|
|
593
790
|
try {
|
|
@@ -615,7 +812,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
615
812
|
}, [applyCommandResult, executeArgs]);
|
|
616
813
|
const applyShortcut = useCallback2((shortcutId) => {
|
|
617
814
|
const action = SHORTCUTS.find((shortcut) => shortcut.id === shortcutId);
|
|
618
|
-
if (!action || isRunning) {
|
|
815
|
+
if (!action || isRunning || isCatalogLoading) {
|
|
619
816
|
return;
|
|
620
817
|
}
|
|
621
818
|
if (action.template) {
|
|
@@ -626,7 +823,107 @@ function DocyrusOpenTuiApp(props) {
|
|
|
626
823
|
if (action.run) {
|
|
627
824
|
void executeArgs(parseCommandLine(action.run), action.run);
|
|
628
825
|
}
|
|
629
|
-
}, [executeArgs, isRunning]);
|
|
826
|
+
}, [executeArgs, isCatalogLoading, isRunning]);
|
|
827
|
+
const groupedDataSourceItems = useMemo(() => {
|
|
828
|
+
return groupDataSourcesByApp({
|
|
829
|
+
apps: appsCatalog,
|
|
830
|
+
dataSources: dataSourcesCatalog
|
|
831
|
+
});
|
|
832
|
+
}, [appsCatalog, dataSourcesCatalog]);
|
|
833
|
+
const filteredDataSourceGroups = useMemo(() => {
|
|
834
|
+
return filterDataSources(groupedDataSourceItems, dataSourceSearch);
|
|
835
|
+
}, [dataSourceSearch, groupedDataSourceItems]);
|
|
836
|
+
const dataSourceCountByAppKey = useMemo(() => {
|
|
837
|
+
const result = /* @__PURE__ */ new Map();
|
|
838
|
+
for (const group of filteredDataSourceGroups) {
|
|
839
|
+
result.set(group.appKey, group.dataSources.length);
|
|
840
|
+
}
|
|
841
|
+
return result;
|
|
842
|
+
}, [filteredDataSourceGroups]);
|
|
843
|
+
const expandedAppKeySet = useMemo(() => {
|
|
844
|
+
return new Set(expandedAppKeys);
|
|
845
|
+
}, [expandedAppKeys]);
|
|
846
|
+
const dataSourceRows = useMemo(() => {
|
|
847
|
+
return flattenTreeRows({
|
|
848
|
+
groups: filteredDataSourceGroups,
|
|
849
|
+
expandedAppKeys: expandedAppKeySet,
|
|
850
|
+
query: dataSourceSearch
|
|
851
|
+
});
|
|
852
|
+
}, [dataSourceSearch, expandedAppKeySet, filteredDataSourceGroups]);
|
|
853
|
+
const selectedDataSourceRow = useMemo(() => {
|
|
854
|
+
if (dataSourceRows.length === 0) {
|
|
855
|
+
return null;
|
|
856
|
+
}
|
|
857
|
+
return dataSourceRows[selectedDataSourceRowIndex] || dataSourceRows[0] || null;
|
|
858
|
+
}, [dataSourceRows, selectedDataSourceRowIndex]);
|
|
859
|
+
const toggleAppExpansion = useCallback2((appKey) => {
|
|
860
|
+
setExpandedAppKeys((previous) => {
|
|
861
|
+
if (previous.includes(appKey)) {
|
|
862
|
+
return previous.filter((key) => key !== appKey);
|
|
863
|
+
}
|
|
864
|
+
return [...previous, appKey];
|
|
865
|
+
});
|
|
866
|
+
}, []);
|
|
867
|
+
const loadDataSourceCatalog = useCallback2(async () => {
|
|
868
|
+
if (isCatalogLoading) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
setIsCatalogLoading(true);
|
|
872
|
+
setCatalogError(null);
|
|
873
|
+
try {
|
|
874
|
+
const nextMessages = [];
|
|
875
|
+
const appsResult = await runInternalArgs(["apps", "list"]);
|
|
876
|
+
const dataSourcesResult = await runInternalArgs(["curl", "/dev/data-sources"]);
|
|
877
|
+
const nextApps = appsResult.ok && appsResult.data !== void 0 ? extractAppsFromPayload(appsResult.data) : [];
|
|
878
|
+
const nextDataSources = dataSourcesResult.ok && dataSourcesResult.data !== void 0 ? extractDataSourcesFromPayload(dataSourcesResult.data) : [];
|
|
879
|
+
if (!appsResult.ok) {
|
|
880
|
+
nextMessages.push(toCatalogErrorMessage("apps list", appsResult));
|
|
881
|
+
}
|
|
882
|
+
if (!dataSourcesResult.ok) {
|
|
883
|
+
nextMessages.push(toCatalogErrorMessage("curl /dev/data-sources", dataSourcesResult));
|
|
884
|
+
}
|
|
885
|
+
setAppsCatalog(nextApps);
|
|
886
|
+
setDataSourcesCatalog(nextDataSources);
|
|
887
|
+
setCatalogLoadedAt(Date.now());
|
|
888
|
+
const nextGroups = groupDataSourcesByApp({
|
|
889
|
+
apps: nextApps,
|
|
890
|
+
dataSources: nextDataSources
|
|
891
|
+
});
|
|
892
|
+
setExpandedAppKeys((previous) => {
|
|
893
|
+
if (previous.length > 0) {
|
|
894
|
+
const allowed = new Set(nextGroups.map((group) => group.appKey));
|
|
895
|
+
return previous.filter((key) => allowed.has(key));
|
|
896
|
+
}
|
|
897
|
+
return nextGroups.map((group) => group.appKey);
|
|
898
|
+
});
|
|
899
|
+
if (nextMessages.length > 0) {
|
|
900
|
+
appendSystemMessages(nextMessages);
|
|
901
|
+
setCatalogError(nextMessages[0] || "Failed to load catalog.");
|
|
902
|
+
} else {
|
|
903
|
+
setCatalogError(null);
|
|
904
|
+
}
|
|
905
|
+
} finally {
|
|
906
|
+
setIsCatalogLoading(false);
|
|
907
|
+
}
|
|
908
|
+
}, [appendSystemMessages, isCatalogLoading, runInternalArgs]);
|
|
909
|
+
const runSelectedDataSourceRow = useCallback2(async (row) => {
|
|
910
|
+
if (!row || isRunning || isCatalogLoading) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
if (row.kind === "app") {
|
|
914
|
+
toggleAppExpansion(row.appKey);
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
if (!row.appSlug || !row.dataSourceSlug) {
|
|
918
|
+
appendSystemMessages([
|
|
919
|
+
"Cannot execute ds get because selected row is missing appSlug or dataSourceSlug."
|
|
920
|
+
]);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
const command = `ds get ${row.appSlug} ${row.dataSourceSlug}`;
|
|
924
|
+
setFocusedPanel("result");
|
|
925
|
+
await executeArgs(["ds", "get", row.appSlug, row.dataSourceSlug], command);
|
|
926
|
+
}, [appendSystemMessages, executeArgs, isCatalogLoading, isRunning, toggleAppExpansion]);
|
|
630
927
|
const setSectionByIndex = useCallback2((index) => {
|
|
631
928
|
const safeIndex = index < 0 ? SECTION_ORDER.length - 1 : index >= SECTION_ORDER.length ? 0 : index;
|
|
632
929
|
const section = SECTION_ORDER[safeIndex];
|
|
@@ -676,11 +973,13 @@ function DocyrusOpenTuiApp(props) {
|
|
|
676
973
|
return [...previous, message];
|
|
677
974
|
});
|
|
678
975
|
}, []);
|
|
976
|
+
const isDataSourceSearchFocused = activeSection === "datasources" && focusedPanel === "left" && dataSourcePanelFocus === "search" && !isRunning && !isExitConfirmOpen;
|
|
679
977
|
const { submitInput } = useCommandInput({
|
|
680
|
-
busy: isRunning,
|
|
978
|
+
busy: isRunning || isCatalogLoading,
|
|
681
979
|
input,
|
|
682
980
|
isInputFocused: focusedPanel === "input",
|
|
683
981
|
isModalOpen: isExitConfirmOpen,
|
|
982
|
+
hotkeysEnabled: !isRunning && !isDataSourceSearchFocused,
|
|
684
983
|
setInput,
|
|
685
984
|
onSubmit: executeLine,
|
|
686
985
|
onClear: () => {
|
|
@@ -691,11 +990,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
691
990
|
},
|
|
692
991
|
onShortcut: applyShortcut,
|
|
693
992
|
onNextSection: nextSection,
|
|
694
|
-
onPreviousSection: previousSection
|
|
695
|
-
onJumpSection: (section) => {
|
|
696
|
-
setActiveSection(section);
|
|
697
|
-
setFocusedPanel("left");
|
|
698
|
-
}
|
|
993
|
+
onPreviousSection: previousSection
|
|
699
994
|
});
|
|
700
995
|
useKeyboard2((keyEvent) => {
|
|
701
996
|
const keyName = keyEvent.name;
|
|
@@ -715,10 +1010,23 @@ function DocyrusOpenTuiApp(props) {
|
|
|
715
1010
|
}
|
|
716
1011
|
return;
|
|
717
1012
|
}
|
|
1013
|
+
if (activeSection === "datasources" && focusedPanel === "left" && dataSourcePanelFocus === "search" && isEscapeKey(keyName)) {
|
|
1014
|
+
preventDefault2(keyEvent);
|
|
1015
|
+
setDataSourcePanelFocus("tree");
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
if (activeSection === "datasources" && focusedPanel === "left" && dataSourcePanelFocus === "tree" && isSlashKey(keyName)) {
|
|
1019
|
+
preventDefault2(keyEvent);
|
|
1020
|
+
setDataSourcePanelFocus("search");
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
718
1023
|
if (keyEvent.ctrl && keyName === "c") {
|
|
719
1024
|
renderer.destroy();
|
|
720
1025
|
return;
|
|
721
1026
|
}
|
|
1027
|
+
if (isRunning) {
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
722
1030
|
if (isEscapeKey(keyName)) {
|
|
723
1031
|
preventDefault2(keyEvent);
|
|
724
1032
|
if (focusedPanel !== "input") {
|
|
@@ -733,6 +1041,13 @@ function DocyrusOpenTuiApp(props) {
|
|
|
733
1041
|
openExitConfirmation();
|
|
734
1042
|
return;
|
|
735
1043
|
}
|
|
1044
|
+
if (activeSection === "datasources" && focusedPanel === "left" && dataSourcePanelFocus === "tree" && keyName === "space") {
|
|
1045
|
+
preventDefault2(keyEvent);
|
|
1046
|
+
if (selectedDataSourceRow?.kind === "app") {
|
|
1047
|
+
toggleAppExpansion(selectedDataSourceRow.appKey);
|
|
1048
|
+
}
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
736
1051
|
if (keyName === "left" || keyName === "right") {
|
|
737
1052
|
if (focusedPanel === "input" && input.length > 0) {
|
|
738
1053
|
return;
|
|
@@ -746,7 +1061,12 @@ function DocyrusOpenTuiApp(props) {
|
|
|
746
1061
|
}
|
|
747
1062
|
return;
|
|
748
1063
|
}
|
|
749
|
-
if (keyName === "r" && activeSection === "
|
|
1064
|
+
if (keyName === "r" && activeSection === "datasources" && focusedPanel === "left" && dataSourcePanelFocus === "tree" && !isCatalogLoading) {
|
|
1065
|
+
preventDefault2(keyEvent);
|
|
1066
|
+
void loadDataSourceCatalog();
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
if (keyName === "r" && activeSection === "history" && focusedPanel === "left" && input.length === 0 && !isRunning && !isCatalogLoading) {
|
|
750
1070
|
const entry = entries[selectedHistoryIndex];
|
|
751
1071
|
if (entry) {
|
|
752
1072
|
void runHistoryEntry(entry);
|
|
@@ -768,7 +1088,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
768
1088
|
};
|
|
769
1089
|
}, [isEscArmed]);
|
|
770
1090
|
useEffect(() => {
|
|
771
|
-
if (!isRunning) {
|
|
1091
|
+
if (!isRunning && !isCatalogLoading) {
|
|
772
1092
|
return;
|
|
773
1093
|
}
|
|
774
1094
|
const timer = setInterval(() => {
|
|
@@ -777,7 +1097,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
777
1097
|
return () => {
|
|
778
1098
|
clearInterval(timer);
|
|
779
1099
|
};
|
|
780
|
-
}, [isRunning]);
|
|
1100
|
+
}, [isCatalogLoading, isRunning]);
|
|
781
1101
|
useEffect(() => {
|
|
782
1102
|
if (selectedHistoryIndex < entries.length) {
|
|
783
1103
|
return;
|
|
@@ -785,6 +1105,43 @@ function DocyrusOpenTuiApp(props) {
|
|
|
785
1105
|
const nextIndex = Math.max(entries.length - 1, 0);
|
|
786
1106
|
setSelectedHistoryIndex(nextIndex);
|
|
787
1107
|
}, [entries.length, selectedHistoryIndex]);
|
|
1108
|
+
useEffect(() => {
|
|
1109
|
+
if (selectedDataSourceRowIndex < dataSourceRows.length) {
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
const nextIndex = Math.max(dataSourceRows.length - 1, 0);
|
|
1113
|
+
setSelectedDataSourceRowIndex(nextIndex);
|
|
1114
|
+
}, [dataSourceRows.length, selectedDataSourceRowIndex]);
|
|
1115
|
+
useEffect(() => {
|
|
1116
|
+
if (activeSection !== "datasources" && dataSourcePanelFocus !== "tree") {
|
|
1117
|
+
setDataSourcePanelFocus("tree");
|
|
1118
|
+
}
|
|
1119
|
+
}, [activeSection, dataSourcePanelFocus]);
|
|
1120
|
+
useEffect(() => {
|
|
1121
|
+
const scopeKey = `${environment?.id || ""}::${context?.tenantDisplay || ""}`;
|
|
1122
|
+
if (catalogScopeRef.current.length === 0) {
|
|
1123
|
+
catalogScopeRef.current = scopeKey;
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
if (catalogScopeRef.current === scopeKey) {
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
catalogScopeRef.current = scopeKey;
|
|
1130
|
+
if (catalogLoadedAt !== null) {
|
|
1131
|
+
setCatalogLoadedAt(null);
|
|
1132
|
+
setAppsCatalog([]);
|
|
1133
|
+
setDataSourcesCatalog([]);
|
|
1134
|
+
setExpandedAppKeys([]);
|
|
1135
|
+
setSelectedDataSourceRowIndex(0);
|
|
1136
|
+
setCatalogError(null);
|
|
1137
|
+
}
|
|
1138
|
+
}, [catalogLoadedAt, context?.tenantDisplay, environment?.id]);
|
|
1139
|
+
useEffect(() => {
|
|
1140
|
+
if (activeSection !== "datasources" || catalogLoadedAt !== null || isCatalogLoading) {
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
void loadDataSourceCatalog();
|
|
1144
|
+
}, [activeSection, catalogLoadedAt, isCatalogLoading, loadDataSourceCatalog]);
|
|
788
1145
|
const spinner = useMemo(() => {
|
|
789
1146
|
return ["|", "/", "-", "\\"][spinnerFrame] || "|";
|
|
790
1147
|
}, [spinnerFrame]);
|
|
@@ -803,8 +1160,8 @@ function DocyrusOpenTuiApp(props) {
|
|
|
803
1160
|
...SECTION_ORDER.filter((section) => section !== activeSection)
|
|
804
1161
|
];
|
|
805
1162
|
return ordered.map((section) => ({
|
|
806
|
-
name: section === "shortcuts" ? "Shortcuts" : section === "history" ? "History" : section === "messages" ? "Messages" : "Help",
|
|
807
|
-
description: section === "shortcuts" ? "Quick actions" : section === "history" ? "Past runs" : section === "messages" ? "System output" : "Key map",
|
|
1163
|
+
name: section === "shortcuts" ? "Shortcuts" : section === "datasources" ? "DataSources" : section === "history" ? "History" : section === "messages" ? "Messages" : "Help",
|
|
1164
|
+
description: section === "shortcuts" ? "Quick actions" : section === "datasources" ? "App tree" : section === "history" ? "Past runs" : section === "messages" ? "System output" : "Key map",
|
|
808
1165
|
value: section
|
|
809
1166
|
}));
|
|
810
1167
|
}, [activeSection]);
|
|
@@ -822,12 +1179,30 @@ function DocyrusOpenTuiApp(props) {
|
|
|
822
1179
|
value: entry.id
|
|
823
1180
|
}));
|
|
824
1181
|
}, [entries]);
|
|
1182
|
+
const dataSourceOptions = useMemo(() => {
|
|
1183
|
+
return dataSourceRows.map((row) => {
|
|
1184
|
+
if (row.kind === "app") {
|
|
1185
|
+
const itemCount = dataSourceCountByAppKey.get(row.appKey) || 0;
|
|
1186
|
+
return {
|
|
1187
|
+
name: `${row.expanded ? "[-]" : "[+]"} ${row.appName}${row.appSlug ? ` (${row.appSlug})` : ""}`,
|
|
1188
|
+
description: `${itemCount} data source${itemCount === 1 ? "" : "s"}`,
|
|
1189
|
+
value: row.id
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
const dataSourceLabel = row.dataSourceName || row.dataSourceSlug || row.dataSourceId || "Unknown";
|
|
1193
|
+
return {
|
|
1194
|
+
name: ` - ${dataSourceLabel}${row.dataSourceSlug ? ` (${row.dataSourceSlug})` : ""}`,
|
|
1195
|
+
description: row.appSlug ? `app: ${row.appSlug}` : "missing app slug",
|
|
1196
|
+
value: row.id
|
|
1197
|
+
};
|
|
1198
|
+
});
|
|
1199
|
+
}, [dataSourceCountByAppKey, dataSourceRows]);
|
|
825
1200
|
const renderLeftSection = () => {
|
|
826
1201
|
if (activeSection === "shortcuts") {
|
|
827
1202
|
return /* @__PURE__ */ jsx2(
|
|
828
1203
|
"select",
|
|
829
1204
|
{
|
|
830
|
-
focused: focusedPanel === "left" && !isExitConfirmOpen,
|
|
1205
|
+
focused: focusedPanel === "left" && !isExitConfirmOpen && !isRunning,
|
|
831
1206
|
options: shortcutOptions,
|
|
832
1207
|
selectedIndex: selectedShortcutIndex,
|
|
833
1208
|
onChange: (index) => {
|
|
@@ -847,6 +1222,53 @@ function DocyrusOpenTuiApp(props) {
|
|
|
847
1222
|
}
|
|
848
1223
|
);
|
|
849
1224
|
}
|
|
1225
|
+
if (activeSection === "datasources") {
|
|
1226
|
+
const treeFocused = focusedPanel === "left" && dataSourcePanelFocus === "tree" && !isRunning && !isExitConfirmOpen;
|
|
1227
|
+
const searchFocused = focusedPanel === "left" && dataSourcePanelFocus === "search" && !isRunning && !isExitConfirmOpen;
|
|
1228
|
+
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", flexGrow: 1, children: [
|
|
1229
|
+
/* @__PURE__ */ jsx2("box", { border: true, borderStyle: "single", borderColor: searchFocused ? "#7aa2f7" : "#414868", paddingX: 1, children: /* @__PURE__ */ jsx2(
|
|
1230
|
+
"input",
|
|
1231
|
+
{
|
|
1232
|
+
value: dataSourceSearch,
|
|
1233
|
+
placeholder: "Search data sources...",
|
|
1234
|
+
onChange: (value) => {
|
|
1235
|
+
setDataSourceSearch(value);
|
|
1236
|
+
setSelectedDataSourceRowIndex(0);
|
|
1237
|
+
},
|
|
1238
|
+
onSubmit: () => {
|
|
1239
|
+
setDataSourcePanelFocus("tree");
|
|
1240
|
+
},
|
|
1241
|
+
focused: searchFocused,
|
|
1242
|
+
style: {
|
|
1243
|
+
flexGrow: 1
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
) }),
|
|
1247
|
+
/* @__PURE__ */ jsx2("box", { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx2("text", { fg: "gray", children: "/ to search, Esc to tree, Space toggle app, Enter run ds get, r refresh" }) }),
|
|
1248
|
+
isCatalogLoading && /* @__PURE__ */ jsx2("text", { fg: "cyan", children: "Loading DataSources catalog..." }),
|
|
1249
|
+
catalogError && /* @__PURE__ */ jsx2("text", { fg: "red", children: catalogError }),
|
|
1250
|
+
dataSourceOptions.length === 0 ? /* @__PURE__ */ jsx2("text", { fg: "gray", children: dataSourceSearch.trim().length > 0 ? `No data sources match "${dataSourceSearch}".` : "No data sources available for this tenant." }) : /* @__PURE__ */ jsx2(
|
|
1251
|
+
"select",
|
|
1252
|
+
{
|
|
1253
|
+
focused: treeFocused,
|
|
1254
|
+
options: dataSourceOptions,
|
|
1255
|
+
selectedIndex: selectedDataSourceRowIndex,
|
|
1256
|
+
onChange: (index) => {
|
|
1257
|
+
setSelectedDataSourceRowIndex(index);
|
|
1258
|
+
},
|
|
1259
|
+
onSelect: (index) => {
|
|
1260
|
+
setSelectedDataSourceRowIndex(index);
|
|
1261
|
+
const row = dataSourceRows[index] || null;
|
|
1262
|
+
void runSelectedDataSourceRow(row);
|
|
1263
|
+
},
|
|
1264
|
+
showScrollIndicator: true,
|
|
1265
|
+
style: {
|
|
1266
|
+
flexGrow: 1
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
)
|
|
1270
|
+
] });
|
|
1271
|
+
}
|
|
850
1272
|
if (activeSection === "history") {
|
|
851
1273
|
if (entries.length === 0) {
|
|
852
1274
|
return /* @__PURE__ */ jsx2("text", { fg: "gray", children: "No command history yet." });
|
|
@@ -856,7 +1278,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
856
1278
|
/* @__PURE__ */ jsx2(
|
|
857
1279
|
"select",
|
|
858
1280
|
{
|
|
859
|
-
focused: focusedPanel === "left" && !isExitConfirmOpen,
|
|
1281
|
+
focused: focusedPanel === "left" && !isExitConfirmOpen && !isRunning,
|
|
860
1282
|
options: historyOptions,
|
|
861
1283
|
selectedIndex: selectedHistoryIndex,
|
|
862
1284
|
onChange: (index, option) => {
|
|
@@ -880,12 +1302,12 @@ function DocyrusOpenTuiApp(props) {
|
|
|
880
1302
|
] });
|
|
881
1303
|
}
|
|
882
1304
|
if (activeSection === "messages") {
|
|
883
|
-
return /* @__PURE__ */ jsxs2("scrollbox", { focused: focusedPanel === "left" && !isExitConfirmOpen, style: { height: "100%" }, children: [
|
|
1305
|
+
return /* @__PURE__ */ jsxs2("scrollbox", { focused: focusedPanel === "left" && !isExitConfirmOpen && !isRunning, style: { height: "100%" }, children: [
|
|
884
1306
|
systemMessages.length === 0 && /* @__PURE__ */ jsx2("text", { fg: "gray", children: "No messages yet." }),
|
|
885
1307
|
systemMessages.map((line, index) => /* @__PURE__ */ jsx2("text", { children: line }, `message-${index}`))
|
|
886
1308
|
] });
|
|
887
1309
|
}
|
|
888
|
-
return /* @__PURE__ */ jsxs2("scrollbox", { focused: focusedPanel === "left" && !isExitConfirmOpen, style: { height: "100%" }, children: [
|
|
1310
|
+
return /* @__PURE__ */ jsxs2("scrollbox", { focused: focusedPanel === "left" && !isExitConfirmOpen && !isRunning, style: { height: "100%" }, children: [
|
|
889
1311
|
/* @__PURE__ */ jsx2("text", { fg: "cyan", children: "Key Bindings" }),
|
|
890
1312
|
/* @__PURE__ */ jsx2("text", { children: "Left/Right: switch focused panel" }),
|
|
891
1313
|
/* @__PURE__ */ jsx2("text", { children: "Up/Down in left lists: selection" }),
|
|
@@ -894,8 +1316,11 @@ function DocyrusOpenTuiApp(props) {
|
|
|
894
1316
|
/* @__PURE__ */ jsx2("text", { children: "Up/Down in input: command history" }),
|
|
895
1317
|
/* @__PURE__ */ jsx2("text", { children: "Tab / Shift+Tab: next/prev left section" }),
|
|
896
1318
|
/* @__PURE__ */ jsx2("text", { children: "[ / ]: next/prev left section" }),
|
|
897
|
-
/* @__PURE__ */ jsx2("text", { children: "s/h/m/?: jump to section" }),
|
|
898
1319
|
/* @__PURE__ */ jsx2("text", { children: "1..6: run shortcut" }),
|
|
1320
|
+
/* @__PURE__ */ jsx2("text", { children: "/ in DataSources: focus search" }),
|
|
1321
|
+
/* @__PURE__ */ jsx2("text", { children: "Esc in DataSources search: return to tree" }),
|
|
1322
|
+
/* @__PURE__ */ jsx2("text", { children: "Space in DataSources tree: toggle app folder" }),
|
|
1323
|
+
/* @__PURE__ */ jsx2("text", { children: "Enter in DataSources tree: run ds get or toggle app" }),
|
|
899
1324
|
/* @__PURE__ */ jsx2("text", { children: "Ctrl+L: clear command and message history" }),
|
|
900
1325
|
/* @__PURE__ */ jsx2("text", { children: "Esc (other panel): focus command input" }),
|
|
901
1326
|
/* @__PURE__ */ jsx2("text", { children: "Esc then Esc: open exit confirmation" }),
|
|
@@ -913,7 +1338,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
913
1338
|
] }),
|
|
914
1339
|
/* @__PURE__ */ jsxs2("text", { children: [
|
|
915
1340
|
"status: ",
|
|
916
|
-
isRunning ? `${spinner} running` : "idle"
|
|
1341
|
+
isRunning ? `${spinner} running` : isCatalogLoading ? `${spinner} loading-catalog` : "idle"
|
|
917
1342
|
] }),
|
|
918
1343
|
/* @__PURE__ */ jsxs2("text", { children: [
|
|
919
1344
|
"account: ",
|
|
@@ -940,10 +1365,12 @@ function DocyrusOpenTuiApp(props) {
|
|
|
940
1365
|
/* @__PURE__ */ jsx2(
|
|
941
1366
|
"tab-select",
|
|
942
1367
|
{
|
|
1368
|
+
focused: focusedPanel === "left" && !isExitConfirmOpen && !isRunning,
|
|
943
1369
|
options: sectionOptions,
|
|
944
1370
|
onChange: (_index, option) => {
|
|
945
1371
|
if (typeof option?.value === "string") {
|
|
946
1372
|
setActiveSection(option.value);
|
|
1373
|
+
setDataSourcePanelFocus("tree");
|
|
947
1374
|
}
|
|
948
1375
|
}
|
|
949
1376
|
}
|
|
@@ -960,7 +1387,10 @@ function DocyrusOpenTuiApp(props) {
|
|
|
960
1387
|
borderColor: focusedPanel === "result" ? "#9ece6a" : "#3b4261",
|
|
961
1388
|
padding: 1,
|
|
962
1389
|
flexGrow: 1,
|
|
963
|
-
children:
|
|
1390
|
+
children: isRunning ? /* @__PURE__ */ jsxs2("box", { width: "100%", height: "100%", alignItems: "center", justifyContent: "center", flexDirection: "column", children: [
|
|
1391
|
+
/* @__PURE__ */ jsx2("text", { fg: "cyan", children: spinner }),
|
|
1392
|
+
/* @__PURE__ */ jsx2("text", { fg: "gray", children: "Running command..." })
|
|
1393
|
+
] }) : renderResult({
|
|
964
1394
|
entry: activeEntry,
|
|
965
1395
|
isFocused: focusedPanel === "result" && !isExitConfirmOpen
|
|
966
1396
|
})
|
|
@@ -989,7 +1419,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
989
1419
|
onSubmit: (value) => {
|
|
990
1420
|
submitInput(typeof value === "string" ? value : void 0);
|
|
991
1421
|
},
|
|
992
|
-
focused: !isRunning && focusedPanel === "input" && !isExitConfirmOpen
|
|
1422
|
+
focused: !isRunning && !isCatalogLoading && focusedPanel === "input" && !isExitConfirmOpen
|
|
993
1423
|
}
|
|
994
1424
|
)
|
|
995
1425
|
]
|