@docyrus/docyrus 0.0.8 → 0.0.11
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 +57 -9
- package/main.js.map +2 -2
- package/package.json +1 -1
- package/tui.mjs +425 -43
- package/tui.mjs.map +3 -3
package/package.json
CHANGED
package/tui.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { createCliRenderer } from "@opentui/core";
|
|
|
3
3
|
import { createRoot } from "@opentui/react";
|
|
4
4
|
|
|
5
5
|
// src/tui/opentui/DocyrusOpenTuiApp.tsx
|
|
6
|
+
import { RGBA as RGBA2 } from "@opentui/core";
|
|
6
7
|
import { useKeyboard as useKeyboard2, useRenderer } from "@opentui/react";
|
|
7
8
|
import { useCallback as useCallback2, useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
8
9
|
|
|
@@ -259,12 +260,14 @@ function useCommandInput(options) {
|
|
|
259
260
|
}
|
|
260
261
|
const keyName = keyEvent.name;
|
|
261
262
|
const hotkeysEnabled = options.hotkeysEnabled !== false;
|
|
263
|
+
const sectionTabEnabled = hotkeysEnabled && options.sectionTabEnabled !== false;
|
|
264
|
+
const inputUpHistoryEnabled = options.inputUpHistoryEnabled !== false;
|
|
262
265
|
if (keyEvent.ctrl && keyName === "l") {
|
|
263
266
|
preventDefault(keyEvent);
|
|
264
267
|
options.onClear();
|
|
265
268
|
return;
|
|
266
269
|
}
|
|
267
|
-
if (
|
|
270
|
+
if (sectionTabEnabled && keyName === "tab") {
|
|
268
271
|
preventDefault(keyEvent);
|
|
269
272
|
if (keyEvent.shift) {
|
|
270
273
|
options.onPreviousSection();
|
|
@@ -283,7 +286,7 @@ function useCommandInput(options) {
|
|
|
283
286
|
options.onNextSection();
|
|
284
287
|
return;
|
|
285
288
|
}
|
|
286
|
-
if (options.isInputFocused && keyName === "up") {
|
|
289
|
+
if (inputUpHistoryEnabled && options.isInputFocused && keyName === "up") {
|
|
287
290
|
preventDefault(keyEvent);
|
|
288
291
|
recallHistory("up");
|
|
289
292
|
return;
|
|
@@ -539,6 +542,10 @@ function serializeResultDocument(document) {
|
|
|
539
542
|
|
|
540
543
|
// src/tui/opentui/renderResult.tsx
|
|
541
544
|
import { jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
545
|
+
var RESULT_VIEW_OPTIONS = [
|
|
546
|
+
{ name: "PRETTY", description: "Markdown view", value: "pretty" },
|
|
547
|
+
{ name: "JSON", description: "Raw JSON view", value: "json" }
|
|
548
|
+
];
|
|
542
549
|
var jsonSyntaxStyle = null;
|
|
543
550
|
function getJsonSyntaxStyle() {
|
|
544
551
|
if (jsonSyntaxStyle) {
|
|
@@ -572,26 +579,227 @@ function getJsonSyntaxStyle() {
|
|
|
572
579
|
});
|
|
573
580
|
return jsonSyntaxStyle;
|
|
574
581
|
}
|
|
582
|
+
function isRecord2(value) {
|
|
583
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
584
|
+
}
|
|
585
|
+
function escapeMd(value) {
|
|
586
|
+
return value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\n/g, "<br/>");
|
|
587
|
+
}
|
|
588
|
+
function inlineValue(value) {
|
|
589
|
+
if (value === null) {
|
|
590
|
+
return "`null`";
|
|
591
|
+
}
|
|
592
|
+
if (value === void 0) {
|
|
593
|
+
return "`undefined`";
|
|
594
|
+
}
|
|
595
|
+
if (typeof value === "string") {
|
|
596
|
+
return escapeMd(value);
|
|
597
|
+
}
|
|
598
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
599
|
+
return `\`${String(value)}\``;
|
|
600
|
+
}
|
|
601
|
+
if (Array.isArray(value)) {
|
|
602
|
+
return `\`[${value.length} items]\``;
|
|
603
|
+
}
|
|
604
|
+
if (isRecord2(value)) {
|
|
605
|
+
return `\`{${Object.keys(value).length} keys}\``;
|
|
606
|
+
}
|
|
607
|
+
return escapeMd(String(value));
|
|
608
|
+
}
|
|
609
|
+
function headingForDepth(depth) {
|
|
610
|
+
const safeDepth = Math.min(6, Math.max(2, depth + 2));
|
|
611
|
+
return "#".repeat(safeDepth);
|
|
612
|
+
}
|
|
613
|
+
function renderObjectMarkdown(record, depth, sectionTitle) {
|
|
614
|
+
const entries = Object.entries(record);
|
|
615
|
+
if (entries.length === 0) {
|
|
616
|
+
return `${headingForDepth(depth)} ${sectionTitle}
|
|
617
|
+
|
|
618
|
+
_Empty object_
|
|
619
|
+
`;
|
|
620
|
+
}
|
|
621
|
+
const lines = [];
|
|
622
|
+
lines.push(`${headingForDepth(depth)} ${sectionTitle}`);
|
|
623
|
+
lines.push("");
|
|
624
|
+
lines.push("| Key | Value |");
|
|
625
|
+
lines.push("| --- | --- |");
|
|
626
|
+
const nestedSections = [];
|
|
627
|
+
for (const [key, value] of entries) {
|
|
628
|
+
const escapedKey = escapeMd(key);
|
|
629
|
+
if (Array.isArray(value) || isRecord2(value)) {
|
|
630
|
+
lines.push(`| ${escapedKey} | ${inlineValue(value)} |`);
|
|
631
|
+
nestedSections.push(renderAnyMarkdown(value, depth + 1, key));
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
lines.push(`| ${escapedKey} | ${inlineValue(value)} |`);
|
|
635
|
+
}
|
|
636
|
+
if (nestedSections.length > 0) {
|
|
637
|
+
lines.push("");
|
|
638
|
+
lines.push(...nestedSections);
|
|
639
|
+
}
|
|
640
|
+
return `${lines.join("\n")}
|
|
641
|
+
`;
|
|
642
|
+
}
|
|
643
|
+
function renderArrayMarkdown(values, depth, sectionTitle) {
|
|
644
|
+
const lines = [];
|
|
645
|
+
lines.push(`${headingForDepth(depth)} ${sectionTitle}`);
|
|
646
|
+
lines.push("");
|
|
647
|
+
if (values.length === 0) {
|
|
648
|
+
lines.push("_Empty array_");
|
|
649
|
+
return `${lines.join("\n")}
|
|
650
|
+
`;
|
|
651
|
+
}
|
|
652
|
+
const firstItem = values[0];
|
|
653
|
+
if (isRecord2(firstItem)) {
|
|
654
|
+
const headers = Object.keys(firstItem);
|
|
655
|
+
lines.push(`| ${headers.map(escapeMd).join(" | ")} |`);
|
|
656
|
+
lines.push(`| ${headers.map(() => "---").join(" | ")} |`);
|
|
657
|
+
const nestedSections2 = [];
|
|
658
|
+
values.forEach((item, index) => {
|
|
659
|
+
if (!isRecord2(item)) {
|
|
660
|
+
lines.push(`| ${escapeMd(String(item))} |`);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
const rowCells = headers.map((header) => {
|
|
664
|
+
const cellValue = item[header];
|
|
665
|
+
if (Array.isArray(cellValue) || isRecord2(cellValue)) {
|
|
666
|
+
nestedSections2.push(renderAnyMarkdown(cellValue, depth + 1, `${sectionTitle}[${index}].${header}`));
|
|
667
|
+
}
|
|
668
|
+
return inlineValue(cellValue);
|
|
669
|
+
});
|
|
670
|
+
lines.push(`| ${rowCells.join(" | ")} |`);
|
|
671
|
+
});
|
|
672
|
+
if (nestedSections2.length > 0) {
|
|
673
|
+
lines.push("");
|
|
674
|
+
lines.push(...nestedSections2);
|
|
675
|
+
}
|
|
676
|
+
return `${lines.join("\n")}
|
|
677
|
+
`;
|
|
678
|
+
}
|
|
679
|
+
lines.push("| Index | Value |");
|
|
680
|
+
lines.push("| --- | --- |");
|
|
681
|
+
const nestedSections = [];
|
|
682
|
+
values.forEach((value, index) => {
|
|
683
|
+
if (Array.isArray(value) || isRecord2(value)) {
|
|
684
|
+
lines.push(`| ${index} | ${inlineValue(value)} |`);
|
|
685
|
+
nestedSections.push(renderAnyMarkdown(value, depth + 1, `${sectionTitle}[${index}]`));
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
lines.push(`| ${index} | ${inlineValue(value)} |`);
|
|
689
|
+
});
|
|
690
|
+
if (nestedSections.length > 0) {
|
|
691
|
+
lines.push("");
|
|
692
|
+
lines.push(...nestedSections);
|
|
693
|
+
}
|
|
694
|
+
return `${lines.join("\n")}
|
|
695
|
+
`;
|
|
696
|
+
}
|
|
697
|
+
function renderAnyMarkdown(value, depth, sectionTitle) {
|
|
698
|
+
if (Array.isArray(value)) {
|
|
699
|
+
return renderArrayMarkdown(value, depth, sectionTitle);
|
|
700
|
+
}
|
|
701
|
+
if (isRecord2(value)) {
|
|
702
|
+
return renderObjectMarkdown(value, depth, sectionTitle);
|
|
703
|
+
}
|
|
704
|
+
return `${headingForDepth(depth)} ${sectionTitle}
|
|
705
|
+
|
|
706
|
+
${inlineValue(value)}
|
|
707
|
+
`;
|
|
708
|
+
}
|
|
709
|
+
function resolvePrettyPayload(document) {
|
|
710
|
+
const payload = document.payload;
|
|
711
|
+
if (!isRecord2(payload)) {
|
|
712
|
+
return payload;
|
|
713
|
+
}
|
|
714
|
+
const status = typeof payload.status === "number" ? payload.status : void 0;
|
|
715
|
+
const success = typeof payload.success === "boolean" ? payload.success : void 0;
|
|
716
|
+
if (typeof status === "number") {
|
|
717
|
+
if (status >= 400) {
|
|
718
|
+
if ("error" in payload) {
|
|
719
|
+
return payload.error;
|
|
720
|
+
}
|
|
721
|
+
if (isRecord2(payload.data) && "error" in payload.data) {
|
|
722
|
+
return payload.data.error;
|
|
723
|
+
}
|
|
724
|
+
return payload;
|
|
725
|
+
}
|
|
726
|
+
if ("data" in payload) {
|
|
727
|
+
return payload.data;
|
|
728
|
+
}
|
|
729
|
+
return payload;
|
|
730
|
+
}
|
|
731
|
+
if (success === true) {
|
|
732
|
+
if ("data" in payload) {
|
|
733
|
+
return payload.data;
|
|
734
|
+
}
|
|
735
|
+
return payload;
|
|
736
|
+
}
|
|
737
|
+
if (success === false) {
|
|
738
|
+
if ("error" in payload) {
|
|
739
|
+
return payload.error;
|
|
740
|
+
}
|
|
741
|
+
return payload;
|
|
742
|
+
}
|
|
743
|
+
if (document.ok && "data" in payload) {
|
|
744
|
+
return payload.data;
|
|
745
|
+
}
|
|
746
|
+
if (!document.ok && "error" in payload) {
|
|
747
|
+
return payload.error;
|
|
748
|
+
}
|
|
749
|
+
return payload;
|
|
750
|
+
}
|
|
751
|
+
function buildPrettyMarkdown(document) {
|
|
752
|
+
const payload = resolvePrettyPayload(document);
|
|
753
|
+
return renderAnyMarkdown(payload, 0, document.ok ? "Data" : "Error");
|
|
754
|
+
}
|
|
575
755
|
function renderResult(params) {
|
|
576
756
|
const {
|
|
577
757
|
entry,
|
|
578
|
-
isFocused
|
|
758
|
+
isFocused,
|
|
759
|
+
viewMode,
|
|
760
|
+
onViewModeChange,
|
|
761
|
+
resultScrollRef
|
|
579
762
|
} = params;
|
|
580
763
|
if (!entry) {
|
|
581
764
|
return /* @__PURE__ */ jsx("text", { fg: "yellow", children: "No command executed yet." });
|
|
582
765
|
}
|
|
583
766
|
const document = buildResultDocument(entry);
|
|
584
767
|
const json = serializeResultDocument(document);
|
|
768
|
+
const markdown = buildPrettyMarkdown(document);
|
|
769
|
+
const viewOptions = viewMode === "pretty" ? RESULT_VIEW_OPTIONS : [RESULT_VIEW_OPTIONS[1], RESULT_VIEW_OPTIONS[0]];
|
|
585
770
|
return /* @__PURE__ */ jsxs("box", { flexDirection: "column", width: "100%", height: "100%", children: [
|
|
586
|
-
/* @__PURE__ */
|
|
587
|
-
document.ok ? "
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
771
|
+
/* @__PURE__ */ jsxs("box", { paddingBottom: 1, flexDirection: "column", children: [
|
|
772
|
+
/* @__PURE__ */ jsxs("text", { fg: document.ok ? "green" : "red", children: [
|
|
773
|
+
document.ok ? "OK" : "ERR",
|
|
774
|
+
" ",
|
|
775
|
+
document.command,
|
|
776
|
+
" [",
|
|
777
|
+
document.durationMs,
|
|
778
|
+
"ms]"
|
|
779
|
+
] }),
|
|
780
|
+
/* @__PURE__ */ jsx(
|
|
781
|
+
"tab-select",
|
|
782
|
+
{
|
|
783
|
+
options: viewOptions,
|
|
784
|
+
onChange: (_index, option) => {
|
|
785
|
+
if (option?.value === "json") {
|
|
786
|
+
onViewModeChange("json");
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
onViewModeChange("pretty");
|
|
790
|
+
},
|
|
791
|
+
onSelect: (_index, option) => {
|
|
792
|
+
if (option?.value === "json") {
|
|
793
|
+
onViewModeChange("json");
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
onViewModeChange("pretty");
|
|
797
|
+
},
|
|
798
|
+
focused: false
|
|
799
|
+
}
|
|
800
|
+
)
|
|
801
|
+
] }),
|
|
802
|
+
/* @__PURE__ */ jsx("box", { flexGrow: 1, width: "100%", height: "100%", children: /* @__PURE__ */ jsx("scrollbox", { ref: resultScrollRef, focused: isFocused, scrollX: true, scrollY: true, style: { height: "100%" }, children: viewMode === "json" ? /* @__PURE__ */ jsx(
|
|
595
803
|
"code",
|
|
596
804
|
{
|
|
597
805
|
width: "100%",
|
|
@@ -601,6 +809,18 @@ function renderResult(params) {
|
|
|
601
809
|
streaming: false,
|
|
602
810
|
conceal: false
|
|
603
811
|
}
|
|
812
|
+
) : /* @__PURE__ */ jsx(
|
|
813
|
+
"markdown",
|
|
814
|
+
{
|
|
815
|
+
content: markdown,
|
|
816
|
+
syntaxStyle: getJsonSyntaxStyle(),
|
|
817
|
+
streaming: false,
|
|
818
|
+
tableOptions: {
|
|
819
|
+
widthMode: "content",
|
|
820
|
+
wrapMode: "none",
|
|
821
|
+
borders: true
|
|
822
|
+
}
|
|
823
|
+
}
|
|
604
824
|
) }) })
|
|
605
825
|
] });
|
|
606
826
|
}
|
|
@@ -642,23 +862,23 @@ var SHORTCUTS = [
|
|
|
642
862
|
var SECTION_ORDER = ["shortcuts", "datasources", "history", "messages", "help"];
|
|
643
863
|
var FOCUS_ORDER = ["input", "left", "result"];
|
|
644
864
|
var ESC_ARM_TIMEOUT_MS = 1400;
|
|
645
|
-
function
|
|
865
|
+
function isRecord3(value) {
|
|
646
866
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
647
867
|
}
|
|
648
868
|
function extractMetadata(payload) {
|
|
649
|
-
if (!
|
|
869
|
+
if (!isRecord3(payload)) {
|
|
650
870
|
return {};
|
|
651
871
|
}
|
|
652
872
|
const environmentValue = payload.environment;
|
|
653
873
|
const contextValue = payload.context;
|
|
654
|
-
const environment =
|
|
874
|
+
const environment = isRecord3(environmentValue) && typeof environmentValue.id === "string" && typeof environmentValue.name === "string" ? { id: environmentValue.id, name: environmentValue.name } : void 0;
|
|
655
875
|
if (contextValue === null) {
|
|
656
876
|
return {
|
|
657
877
|
environment,
|
|
658
878
|
context: null
|
|
659
879
|
};
|
|
660
880
|
}
|
|
661
|
-
const context =
|
|
881
|
+
const context = isRecord3(contextValue) && typeof contextValue.email === "string" && typeof contextValue.tenantDisplay === "string" ? {
|
|
662
882
|
email: contextValue.email,
|
|
663
883
|
tenantDisplay: contextValue.tenantDisplay
|
|
664
884
|
} : void 0;
|
|
@@ -706,6 +926,17 @@ function toCatalogErrorMessage(command, result) {
|
|
|
706
926
|
const message = result.error?.message || "Command failed.";
|
|
707
927
|
return `${command} failed: ${message}`;
|
|
708
928
|
}
|
|
929
|
+
function isScrollAtBottom(scrollBox) {
|
|
930
|
+
if (!scrollBox) {
|
|
931
|
+
return true;
|
|
932
|
+
}
|
|
933
|
+
const viewportHeight = scrollBox.viewport.height;
|
|
934
|
+
const maxScrollTop = Math.max(0, scrollBox.scrollHeight - viewportHeight);
|
|
935
|
+
if (maxScrollTop <= 0) {
|
|
936
|
+
return true;
|
|
937
|
+
}
|
|
938
|
+
return scrollBox.scrollTop >= maxScrollTop - 1;
|
|
939
|
+
}
|
|
709
940
|
function DocyrusOpenTuiApp(props) {
|
|
710
941
|
const renderer = useRenderer();
|
|
711
942
|
const [input, setInput] = useState2("");
|
|
@@ -722,6 +953,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
722
953
|
const [selectedHistoryIndex, setSelectedHistoryIndex] = useState2(0);
|
|
723
954
|
const [isExitConfirmOpen, setIsExitConfirmOpen] = useState2(false);
|
|
724
955
|
const [isEscArmed, setIsEscArmed] = useState2(false);
|
|
956
|
+
const [resultViewMode, setResultViewMode] = useState2("pretty");
|
|
725
957
|
const [dataSourceSearch, setDataSourceSearch] = useState2("");
|
|
726
958
|
const [dataSourcePanelFocus, setDataSourcePanelFocus] = useState2("tree");
|
|
727
959
|
const [selectedDataSourceRowIndex, setSelectedDataSourceRowIndex] = useState2(0);
|
|
@@ -732,6 +964,9 @@ function DocyrusOpenTuiApp(props) {
|
|
|
732
964
|
const [catalogError, setCatalogError] = useState2(null);
|
|
733
965
|
const [catalogLoadedAt, setCatalogLoadedAt] = useState2(null);
|
|
734
966
|
const catalogScopeRef = useRef("");
|
|
967
|
+
const resultScrollRef = useRef(null);
|
|
968
|
+
const leftScrollRef = useRef(null);
|
|
969
|
+
const commandInputRef = useRef(null);
|
|
735
970
|
const executor = useMemo(() => {
|
|
736
971
|
return createTuiProcessExecutor({
|
|
737
972
|
executionConfig: props.executionConfig
|
|
@@ -980,6 +1215,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
980
1215
|
isInputFocused: focusedPanel === "input",
|
|
981
1216
|
isModalOpen: isExitConfirmOpen,
|
|
982
1217
|
hotkeysEnabled: !isRunning && !isDataSourceSearchFocused,
|
|
1218
|
+
sectionTabEnabled: focusedPanel !== "result",
|
|
983
1219
|
setInput,
|
|
984
1220
|
onSubmit: executeLine,
|
|
985
1221
|
onClear: () => {
|
|
@@ -1024,9 +1260,44 @@ function DocyrusOpenTuiApp(props) {
|
|
|
1024
1260
|
renderer.destroy();
|
|
1025
1261
|
return;
|
|
1026
1262
|
}
|
|
1263
|
+
if (focusedPanel === "result" && keyName === "tab" && !isRunning && !isCatalogLoading) {
|
|
1264
|
+
preventDefault2(keyEvent);
|
|
1265
|
+
setResultViewMode((previous) => previous === "pretty" ? "json" : "pretty");
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1027
1268
|
if (isRunning) {
|
|
1028
1269
|
return;
|
|
1029
1270
|
}
|
|
1271
|
+
if (keyName === "down") {
|
|
1272
|
+
if (focusedPanel === "result") {
|
|
1273
|
+
if (isScrollAtBottom(resultScrollRef.current)) {
|
|
1274
|
+
preventDefault2(keyEvent);
|
|
1275
|
+
setIsEscArmed(false);
|
|
1276
|
+
setFocusedPanel("input");
|
|
1277
|
+
}
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
if (focusedPanel === "left") {
|
|
1281
|
+
let shouldSwitchToInput = false;
|
|
1282
|
+
if (activeSection === "shortcuts") {
|
|
1283
|
+
shouldSwitchToInput = shortcutOptions.length === 0 || selectedShortcutIndex >= shortcutOptions.length - 1;
|
|
1284
|
+
} else if (activeSection === "history") {
|
|
1285
|
+
shouldSwitchToInput = historyOptions.length === 0 || selectedHistoryIndex >= historyOptions.length - 1;
|
|
1286
|
+
} else if (activeSection === "datasources") {
|
|
1287
|
+
if (dataSourcePanelFocus === "tree") {
|
|
1288
|
+
shouldSwitchToInput = dataSourceOptions.length === 0 || selectedDataSourceRowIndex >= dataSourceOptions.length - 1;
|
|
1289
|
+
}
|
|
1290
|
+
} else if (activeSection === "messages" || activeSection === "help") {
|
|
1291
|
+
shouldSwitchToInput = isScrollAtBottom(leftScrollRef.current);
|
|
1292
|
+
}
|
|
1293
|
+
if (shouldSwitchToInput) {
|
|
1294
|
+
preventDefault2(keyEvent);
|
|
1295
|
+
setIsEscArmed(false);
|
|
1296
|
+
setFocusedPanel("input");
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1030
1301
|
if (isEscapeKey(keyName)) {
|
|
1031
1302
|
preventDefault2(keyEvent);
|
|
1032
1303
|
if (focusedPanel !== "input") {
|
|
@@ -1049,7 +1320,44 @@ function DocyrusOpenTuiApp(props) {
|
|
|
1049
1320
|
return;
|
|
1050
1321
|
}
|
|
1051
1322
|
if (keyName === "left" || keyName === "right") {
|
|
1052
|
-
if (focusedPanel === "input"
|
|
1323
|
+
if (focusedPanel === "input") {
|
|
1324
|
+
const cursorOffset = commandInputRef.current?.cursorOffset ?? input.length;
|
|
1325
|
+
const inputLength = input.length;
|
|
1326
|
+
const atStart = cursorOffset <= 0;
|
|
1327
|
+
const atEnd = cursorOffset >= inputLength;
|
|
1328
|
+
if (keyName === "left" && atStart) {
|
|
1329
|
+
preventDefault2(keyEvent);
|
|
1330
|
+
setIsEscArmed(false);
|
|
1331
|
+
setFocusedPanel("left");
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
if (keyName === "right" && atEnd) {
|
|
1335
|
+
preventDefault2(keyEvent);
|
|
1336
|
+
setIsEscArmed(false);
|
|
1337
|
+
setFocusedPanel("result");
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
if (focusedPanel === "result") {
|
|
1343
|
+
const resultScroll = resultScrollRef.current;
|
|
1344
|
+
if (!resultScroll) {
|
|
1345
|
+
preventDefault2(keyEvent);
|
|
1346
|
+
setIsEscArmed(false);
|
|
1347
|
+
setFocusedPanel(keyName === "left" ? "left" : "input");
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
const viewportWidth = resultScroll.viewport.width;
|
|
1351
|
+
const maxScrollLeft = Math.max(0, resultScroll.scrollWidth - viewportWidth);
|
|
1352
|
+
const hasHorizontalScroll = maxScrollLeft > 0;
|
|
1353
|
+
const isAtStart = resultScroll.scrollLeft <= 0;
|
|
1354
|
+
const isAtEnd = resultScroll.scrollLeft >= maxScrollLeft - 1;
|
|
1355
|
+
if (!hasHorizontalScroll || keyName === "left" && isAtStart || keyName === "right" && isAtEnd) {
|
|
1356
|
+
preventDefault2(keyEvent);
|
|
1357
|
+
setIsEscArmed(false);
|
|
1358
|
+
setFocusedPanel(keyName === "left" ? "left" : "input");
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1053
1361
|
return;
|
|
1054
1362
|
}
|
|
1055
1363
|
preventDefault2(keyEvent);
|
|
@@ -1302,35 +1610,105 @@ function DocyrusOpenTuiApp(props) {
|
|
|
1302
1610
|
] });
|
|
1303
1611
|
}
|
|
1304
1612
|
if (activeSection === "messages") {
|
|
1305
|
-
return /* @__PURE__ */ jsxs2(
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1613
|
+
return /* @__PURE__ */ jsxs2(
|
|
1614
|
+
"scrollbox",
|
|
1615
|
+
{
|
|
1616
|
+
ref: leftScrollRef,
|
|
1617
|
+
focused: focusedPanel === "left" && !isExitConfirmOpen && !isRunning,
|
|
1618
|
+
style: { height: "100%" },
|
|
1619
|
+
children: [
|
|
1620
|
+
systemMessages.length === 0 && /* @__PURE__ */ jsx2("text", { fg: "gray", children: "No messages yet." }),
|
|
1621
|
+
systemMessages.map((line, index) => /* @__PURE__ */ jsx2("text", { children: line }, `message-${index}`))
|
|
1622
|
+
]
|
|
1623
|
+
}
|
|
1624
|
+
);
|
|
1309
1625
|
}
|
|
1310
|
-
return /* @__PURE__ */ jsxs2(
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1626
|
+
return /* @__PURE__ */ jsxs2(
|
|
1627
|
+
"scrollbox",
|
|
1628
|
+
{
|
|
1629
|
+
ref: leftScrollRef,
|
|
1630
|
+
focused: focusedPanel === "left" && !isExitConfirmOpen && !isRunning,
|
|
1631
|
+
style: { height: "100%" },
|
|
1632
|
+
children: [
|
|
1633
|
+
/* @__PURE__ */ jsx2("text", { fg: "cyan", children: "Key Bindings" }),
|
|
1634
|
+
/* @__PURE__ */ jsx2("text", { children: "Input Left/Right: caret move, edge switches panel" }),
|
|
1635
|
+
/* @__PURE__ */ jsx2("text", { children: "Left/Right (non-input/result): switch focused panel" }),
|
|
1636
|
+
/* @__PURE__ */ jsx2("text", { children: "Up/Down in left lists: selection" }),
|
|
1637
|
+
/* @__PURE__ */ jsx2("text", { children: "Up/Down in result: vertical scroll" }),
|
|
1638
|
+
/* @__PURE__ */ jsx2("text", { children: "Left/Right in result: horizontal scroll (edge switches panel)" }),
|
|
1639
|
+
/* @__PURE__ */ jsx2("text", { children: "Down at bottom in left/result: focus command input" }),
|
|
1640
|
+
/* @__PURE__ */ jsx2("text", { children: "Enter: run command" }),
|
|
1641
|
+
/* @__PURE__ */ jsx2("text", { children: "Up/Down in input: command history" }),
|
|
1642
|
+
/* @__PURE__ */ jsx2("text", { children: "Tab in result panel: toggle PRETTY/JSON" }),
|
|
1643
|
+
/* @__PURE__ */ jsx2("text", { children: "Tab / Shift+Tab (outside result): next/prev left section" }),
|
|
1644
|
+
/* @__PURE__ */ jsx2("text", { children: "[ / ]: next/prev left section" }),
|
|
1645
|
+
/* @__PURE__ */ jsx2("text", { children: "1..6: run shortcut" }),
|
|
1646
|
+
/* @__PURE__ */ jsx2("text", { children: "/ in DataSources: focus search" }),
|
|
1647
|
+
/* @__PURE__ */ jsx2("text", { children: "Esc in DataSources search: return to tree" }),
|
|
1648
|
+
/* @__PURE__ */ jsx2("text", { children: "Space in DataSources tree: toggle app folder" }),
|
|
1649
|
+
/* @__PURE__ */ jsx2("text", { children: "Enter in DataSources tree: run ds get or toggle app" }),
|
|
1650
|
+
/* @__PURE__ */ jsx2("text", { children: "Ctrl+L: clear command and message history" }),
|
|
1651
|
+
/* @__PURE__ */ jsx2("text", { children: "Esc (other panel): focus command input" }),
|
|
1652
|
+
/* @__PURE__ */ jsx2("text", { children: "Esc then Esc: open exit confirmation" }),
|
|
1653
|
+
/* @__PURE__ */ jsx2("text", { children: "Enter (confirm): exit, Esc (confirm): cancel" }),
|
|
1654
|
+
/* @__PURE__ */ jsx2("text", { children: "Ctrl+C: force quit" })
|
|
1655
|
+
]
|
|
1656
|
+
}
|
|
1657
|
+
);
|
|
1330
1658
|
};
|
|
1331
1659
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", width: "100%", height: "100%", padding: 1, children: [
|
|
1332
1660
|
/* @__PURE__ */ jsxs2("box", { flexDirection: "row", justifyContent: "space-between", marginBottom: 1, paddingX: 1, children: [
|
|
1333
|
-
/* @__PURE__ */
|
|
1661
|
+
/* @__PURE__ */ jsxs2(
|
|
1662
|
+
"box",
|
|
1663
|
+
{
|
|
1664
|
+
style: {
|
|
1665
|
+
position: "relative",
|
|
1666
|
+
width: 50,
|
|
1667
|
+
height: 6
|
|
1668
|
+
},
|
|
1669
|
+
children: [
|
|
1670
|
+
/* @__PURE__ */ jsx2(
|
|
1671
|
+
"box",
|
|
1672
|
+
{
|
|
1673
|
+
style: {
|
|
1674
|
+
position: "absolute",
|
|
1675
|
+
left: 1,
|
|
1676
|
+
top: 0
|
|
1677
|
+
},
|
|
1678
|
+
children: /* @__PURE__ */ jsx2(
|
|
1679
|
+
"ascii-font",
|
|
1680
|
+
{
|
|
1681
|
+
text: "DOCYRUS",
|
|
1682
|
+
font: "block",
|
|
1683
|
+
color: RGBA2.fromHex("#3f4bff")
|
|
1684
|
+
}
|
|
1685
|
+
)
|
|
1686
|
+
}
|
|
1687
|
+
),
|
|
1688
|
+
/* @__PURE__ */ jsx2(
|
|
1689
|
+
"box",
|
|
1690
|
+
{
|
|
1691
|
+
style: {
|
|
1692
|
+
position: "absolute",
|
|
1693
|
+
left: 0,
|
|
1694
|
+
top: 0
|
|
1695
|
+
},
|
|
1696
|
+
children: /* @__PURE__ */ jsx2(
|
|
1697
|
+
"ascii-font",
|
|
1698
|
+
{
|
|
1699
|
+
text: "DOCYRUS",
|
|
1700
|
+
font: "block",
|
|
1701
|
+
color: [
|
|
1702
|
+
RGBA2.fromHex("#ef4444"),
|
|
1703
|
+
RGBA2.fromHex("#ef4444")
|
|
1704
|
+
]
|
|
1705
|
+
}
|
|
1706
|
+
)
|
|
1707
|
+
}
|
|
1708
|
+
)
|
|
1709
|
+
]
|
|
1710
|
+
}
|
|
1711
|
+
),
|
|
1334
1712
|
/* @__PURE__ */ jsxs2("box", { flexDirection: "column", alignItems: "flex-end", justifyContent: "center", children: [
|
|
1335
1713
|
/* @__PURE__ */ jsxs2("text", { children: [
|
|
1336
1714
|
"env: ",
|
|
@@ -1392,7 +1770,10 @@ function DocyrusOpenTuiApp(props) {
|
|
|
1392
1770
|
/* @__PURE__ */ jsx2("text", { fg: "gray", children: "Running command..." })
|
|
1393
1771
|
] }) : renderResult({
|
|
1394
1772
|
entry: activeEntry,
|
|
1395
|
-
isFocused: focusedPanel === "result" && !isExitConfirmOpen
|
|
1773
|
+
isFocused: focusedPanel === "result" && !isExitConfirmOpen,
|
|
1774
|
+
viewMode: resultViewMode,
|
|
1775
|
+
onViewModeChange: setResultViewMode,
|
|
1776
|
+
resultScrollRef
|
|
1396
1777
|
})
|
|
1397
1778
|
}
|
|
1398
1779
|
)
|
|
@@ -1410,6 +1791,7 @@ function DocyrusOpenTuiApp(props) {
|
|
|
1410
1791
|
/* @__PURE__ */ jsx2(
|
|
1411
1792
|
"input",
|
|
1412
1793
|
{
|
|
1794
|
+
ref: commandInputRef,
|
|
1413
1795
|
style: { flexGrow: 1 },
|
|
1414
1796
|
value: input,
|
|
1415
1797
|
placeholder: "Type a command...",
|