pre-claude 0.0.1-beta.3 → 0.0.1-beta.4
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/dist/cli.js +382 -149
- package/dist/cli.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { defineCommand as defineCommand3, runMain } from "citty";
|
|
|
5
5
|
|
|
6
6
|
// package.json
|
|
7
7
|
var name = "pre-claude";
|
|
8
|
-
var version = "0.0.1-beta.
|
|
8
|
+
var version = "0.0.1-beta.4";
|
|
9
9
|
var description = "\u{1F40D} TUI for building structured prompts for Claude";
|
|
10
10
|
|
|
11
11
|
// src/cli/commands/init.ts
|
|
@@ -237,11 +237,12 @@ var safeParseConfig = (data) => {
|
|
|
237
237
|
};
|
|
238
238
|
|
|
239
239
|
// src/tui/App.tsx
|
|
240
|
-
import { Text as Text16, useApp } from "ink";
|
|
240
|
+
import { Text as Text16, useApp as useApp2 } from "ink";
|
|
241
241
|
import { useCallback as useCallback6, useState as useState8 } from "react";
|
|
242
242
|
|
|
243
243
|
// src/tui/views/Preview/Preview.tsx
|
|
244
|
-
import {
|
|
244
|
+
import { spawn } from "node:child_process";
|
|
245
|
+
import { Box as Box5, Text as Text4, useApp, useStdout as useStdout2 } from "ink";
|
|
245
246
|
import { useCallback, useMemo, useState } from "react";
|
|
246
247
|
|
|
247
248
|
// src/tui/features/document/services.ts
|
|
@@ -352,26 +353,6 @@ var HELP_COLOR = "blue";
|
|
|
352
353
|
|
|
353
354
|
// src/tui/features/generation/services.ts
|
|
354
355
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
355
|
-
async function* generateDesignDocStream({
|
|
356
|
-
scenario,
|
|
357
|
-
formData,
|
|
358
|
-
aiContext
|
|
359
|
-
}) {
|
|
360
|
-
const prompt = scenario.prompt({ formData, aiContext });
|
|
361
|
-
for await (const msg of query({
|
|
362
|
-
prompt,
|
|
363
|
-
options: {
|
|
364
|
-
includePartialMessages: true
|
|
365
|
-
}
|
|
366
|
-
})) {
|
|
367
|
-
if (msg.type === "stream_event") {
|
|
368
|
-
const event = msg.event;
|
|
369
|
-
if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta.text != null) {
|
|
370
|
-
yield { type: "text_delta", text: event.delta.text };
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
356
|
var generatePreview = async ({
|
|
376
357
|
scenario,
|
|
377
358
|
formData,
|
|
@@ -382,15 +363,26 @@ var generatePreview = async ({
|
|
|
382
363
|
}) => {
|
|
383
364
|
try {
|
|
384
365
|
let content = "";
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
366
|
+
let sessionId = null;
|
|
367
|
+
const prompt = scenario.prompt({ formData, aiContext });
|
|
368
|
+
for await (const msg of query({
|
|
369
|
+
prompt,
|
|
370
|
+
options: {
|
|
371
|
+
includePartialMessages: true
|
|
372
|
+
}
|
|
389
373
|
})) {
|
|
390
|
-
|
|
391
|
-
|
|
374
|
+
if (msg.type === "stream_event") {
|
|
375
|
+
const event = msg.event;
|
|
376
|
+
if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta.text != null) {
|
|
377
|
+
content += event.delta.text;
|
|
378
|
+
onChunk(event.delta.text);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if ("session_id" in msg && msg.session_id && !sessionId) {
|
|
382
|
+
sessionId = msg.session_id;
|
|
383
|
+
}
|
|
392
384
|
}
|
|
393
|
-
onComplete(content);
|
|
385
|
+
onComplete({ content, sessionId });
|
|
394
386
|
} catch (error) {
|
|
395
387
|
onError(error instanceof Error ? error : new Error(String(error)));
|
|
396
388
|
}
|
|
@@ -414,6 +406,7 @@ var useControl = (options = {}) => {
|
|
|
414
406
|
onPrev,
|
|
415
407
|
onRegenerate,
|
|
416
408
|
onInfo,
|
|
409
|
+
onContinue,
|
|
417
410
|
onChar,
|
|
418
411
|
isActive = true,
|
|
419
412
|
viKeysEnabled = true
|
|
@@ -485,6 +478,9 @@ var useControl = (options = {}) => {
|
|
|
485
478
|
case "i":
|
|
486
479
|
onInfo?.();
|
|
487
480
|
return;
|
|
481
|
+
case "c":
|
|
482
|
+
onContinue?.();
|
|
483
|
+
return;
|
|
488
484
|
}
|
|
489
485
|
onChar?.(input);
|
|
490
486
|
},
|
|
@@ -522,28 +518,19 @@ var ControlBar = ({ items }) => {
|
|
|
522
518
|
// src/tui/components/Header/Header.tsx
|
|
523
519
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
524
520
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
525
|
-
var ASCII_LOGO = ` _ _
|
|
526
|
-
_ __ _ __ ___ ___| | __ _ _ _ __| | ___
|
|
527
|
-
| '_ \\| '__/ _ \\_____/ __| |/ _\` | | | |/ _\` |/ _ \\
|
|
528
|
-
| |_) | | | __/_____| (__| | (_| | |_| | (_| | __/
|
|
529
|
-
| .__/|_| \\___| \\___|_|\\__,_|\\__,_|\\__,_|\\___|
|
|
530
|
-
|_|`;
|
|
531
521
|
var Header = ({
|
|
532
522
|
description: description2 = "Structured Prompt Builder for Claude"
|
|
533
523
|
}) => {
|
|
534
524
|
return /* @__PURE__ */ jsxs(
|
|
535
525
|
Box2,
|
|
536
526
|
{
|
|
537
|
-
flexDirection: "column",
|
|
538
527
|
borderStyle: "double",
|
|
539
528
|
borderColor: ACCENT_COLOR,
|
|
540
529
|
paddingX: 1,
|
|
530
|
+
justifyContent: "space-between",
|
|
541
531
|
children: [
|
|
542
|
-
/* @__PURE__ */ jsx2(Text2, { color: ACCENT_COLOR, children:
|
|
543
|
-
/* @__PURE__ */ jsx2(
|
|
544
|
-
"\u{1F40D} ",
|
|
545
|
-
description2
|
|
546
|
-
] }) })
|
|
532
|
+
/* @__PURE__ */ jsx2(Text2, { color: ACCENT_COLOR, bold: true, children: "\u{1F40D} pre-claude" }),
|
|
533
|
+
/* @__PURE__ */ jsx2(Text2, { color: ACCENT_COLOR, children: description2 })
|
|
547
534
|
]
|
|
548
535
|
}
|
|
549
536
|
);
|
|
@@ -589,6 +576,16 @@ var StatusBar = ({ message, type = "info" }) => {
|
|
|
589
576
|
] }) : /* @__PURE__ */ jsx3(Text3, { color, children: message }) });
|
|
590
577
|
};
|
|
591
578
|
|
|
579
|
+
// src/tui/hooks/useTerminalHeight.ts
|
|
580
|
+
import { useStdout } from "ink";
|
|
581
|
+
var useTerminalHeight = () => {
|
|
582
|
+
const { stdout } = useStdout();
|
|
583
|
+
const rows = stdout?.rows ?? 24;
|
|
584
|
+
const fixedLayoutHeight = 7;
|
|
585
|
+
const availableHeight = Math.max(10, rows - fixedLayoutHeight);
|
|
586
|
+
return { rows, availableHeight };
|
|
587
|
+
};
|
|
588
|
+
|
|
592
589
|
// src/tui/layouts/CommonLayout/CommonLayout.tsx
|
|
593
590
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
594
591
|
var CommonLayout = ({
|
|
@@ -596,9 +593,10 @@ var CommonLayout = ({
|
|
|
596
593
|
status,
|
|
597
594
|
controls = []
|
|
598
595
|
}) => {
|
|
599
|
-
|
|
596
|
+
const { rows } = useTerminalHeight();
|
|
597
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", padding: 1, height: rows, children: [
|
|
600
598
|
/* @__PURE__ */ jsx4(Header, {}),
|
|
601
|
-
children,
|
|
599
|
+
/* @__PURE__ */ jsx4(Box4, { flexDirection: "column", flexGrow: 1, children }),
|
|
602
600
|
controls.length > 0 && /* @__PURE__ */ jsx4(ControlBar, { items: controls }),
|
|
603
601
|
status?.message && /* @__PURE__ */ jsx4(StatusBar, { message: status.message, type: status.type })
|
|
604
602
|
] });
|
|
@@ -630,22 +628,30 @@ var Preview = ({
|
|
|
630
628
|
const [error, setError] = useState(null);
|
|
631
629
|
const [isSaving, setIsSaving] = useState(false);
|
|
632
630
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
631
|
+
const [dataPreviewScrollOffset, setDataPreviewScrollOffset] = useState(0);
|
|
633
632
|
const [savedFilename, setSavedFilename] = useState(null);
|
|
634
633
|
const [showDataPreview, setShowDataPreview] = useState(false);
|
|
634
|
+
const [showContinueDialog, setShowContinueDialog] = useState(false);
|
|
635
|
+
const { stdout } = useStdout2();
|
|
636
|
+
const [sessionId, setSessionId] = useState(null);
|
|
637
|
+
const { exit } = useApp();
|
|
635
638
|
const isEditing = editingFilename != null;
|
|
636
639
|
const canSave = previewContent !== "" && !isGenerating;
|
|
637
640
|
const lines = previewContent.split("\n");
|
|
638
|
-
const
|
|
641
|
+
const fixedHeight = 8;
|
|
642
|
+
const maxVisible = Math.max(10, (stdout?.rows ?? 24) - fixedHeight);
|
|
639
643
|
const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);
|
|
644
|
+
const canContinue = sessionId != null && !isGenerating;
|
|
640
645
|
const controls = useMemo(
|
|
641
646
|
() => [
|
|
642
647
|
{ key: "\u2191\u2193/jk", action: "scroll" },
|
|
643
648
|
{ key: "r", action: "regenerate" },
|
|
644
649
|
...canSave ? [{ key: "s", action: "save" }] : [],
|
|
650
|
+
...canContinue ? [{ key: "c", action: "continue in Claude" }] : [],
|
|
645
651
|
{ key: "i", action: "info" },
|
|
646
652
|
{ key: "q/Esc", action: "back" }
|
|
647
653
|
],
|
|
648
|
-
[canSave]
|
|
654
|
+
[canSave, canContinue]
|
|
649
655
|
);
|
|
650
656
|
const status = useMemo(() => {
|
|
651
657
|
if (error) {
|
|
@@ -666,6 +672,7 @@ var Preview = ({
|
|
|
666
672
|
setIsGenerating(true);
|
|
667
673
|
setError(null);
|
|
668
674
|
setPreviewContent("");
|
|
675
|
+
setSessionId(null);
|
|
669
676
|
let accumulatedContent = "";
|
|
670
677
|
try {
|
|
671
678
|
await generatePreview({
|
|
@@ -676,8 +683,9 @@ var Preview = ({
|
|
|
676
683
|
accumulatedContent += chunk;
|
|
677
684
|
setPreviewContent(accumulatedContent);
|
|
678
685
|
},
|
|
679
|
-
onComplete: (
|
|
680
|
-
setPreviewContent(content);
|
|
686
|
+
onComplete: (result) => {
|
|
687
|
+
setPreviewContent(result.content);
|
|
688
|
+
setSessionId(result.sessionId);
|
|
681
689
|
setIsGenerating(false);
|
|
682
690
|
},
|
|
683
691
|
onError: (err) => {
|
|
@@ -719,33 +727,128 @@ var Preview = ({
|
|
|
719
727
|
editingFilename,
|
|
720
728
|
isSaving
|
|
721
729
|
]);
|
|
730
|
+
const formDataLines = useMemo(
|
|
731
|
+
() => JSON.stringify(formValues, null, 2).split("\n"),
|
|
732
|
+
[formValues]
|
|
733
|
+
);
|
|
734
|
+
const aiContextLines = useMemo(
|
|
735
|
+
() => JSON.stringify(aiContext, null, 2).split("\n"),
|
|
736
|
+
[aiContext]
|
|
737
|
+
);
|
|
738
|
+
const maxDataLines = Math.max(formDataLines.length, aiContextLines.length);
|
|
739
|
+
const handleContinueInClaude = useCallback(
|
|
740
|
+
async (shouldSave) => {
|
|
741
|
+
if (!sessionId) return;
|
|
742
|
+
if (shouldSave && canSave) {
|
|
743
|
+
setIsSaving(true);
|
|
744
|
+
setError(null);
|
|
745
|
+
try {
|
|
746
|
+
await saveDocument({
|
|
747
|
+
config,
|
|
748
|
+
scenario,
|
|
749
|
+
formData: formValues,
|
|
750
|
+
aiContext,
|
|
751
|
+
content: previewContent,
|
|
752
|
+
existingFilename: editingFilename
|
|
753
|
+
});
|
|
754
|
+
} catch (err) {
|
|
755
|
+
setError(err instanceof Error ? err.message : "Failed to save");
|
|
756
|
+
setIsSaving(false);
|
|
757
|
+
setShowContinueDialog(false);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
setIsSaving(false);
|
|
761
|
+
}
|
|
762
|
+
exit();
|
|
763
|
+
setTimeout(() => {
|
|
764
|
+
const child = spawn("claude", ["--resume", sessionId], {
|
|
765
|
+
stdio: "inherit",
|
|
766
|
+
shell: true
|
|
767
|
+
});
|
|
768
|
+
child.on("close", (code) => {
|
|
769
|
+
process.exit(code ?? 0);
|
|
770
|
+
});
|
|
771
|
+
}, 100);
|
|
772
|
+
},
|
|
773
|
+
[
|
|
774
|
+
sessionId,
|
|
775
|
+
exit,
|
|
776
|
+
canSave,
|
|
777
|
+
config,
|
|
778
|
+
scenario,
|
|
779
|
+
formValues,
|
|
780
|
+
aiContext,
|
|
781
|
+
previewContent,
|
|
782
|
+
editingFilename
|
|
783
|
+
]
|
|
784
|
+
);
|
|
722
785
|
useControl({
|
|
723
786
|
onEscape: () => {
|
|
724
|
-
if (
|
|
787
|
+
if (showContinueDialog) {
|
|
788
|
+
setShowContinueDialog(false);
|
|
789
|
+
} else if (showDataPreview) {
|
|
725
790
|
setShowDataPreview(false);
|
|
726
791
|
} else {
|
|
727
792
|
onBack();
|
|
728
793
|
}
|
|
729
794
|
},
|
|
730
|
-
onQuit: onBack,
|
|
795
|
+
onQuit: showContinueDialog ? void 0 : onBack,
|
|
731
796
|
onSave: () => {
|
|
732
|
-
if (!isGenerating && !isSaving) {
|
|
797
|
+
if (!isGenerating && !isSaving && !showContinueDialog) {
|
|
733
798
|
handleSave();
|
|
734
799
|
}
|
|
735
800
|
},
|
|
736
801
|
onRegenerate: () => {
|
|
737
|
-
if (!isGenerating) {
|
|
802
|
+
if (!isGenerating && !showContinueDialog) {
|
|
738
803
|
handleGenerate();
|
|
739
804
|
}
|
|
740
805
|
},
|
|
741
806
|
onUp: () => {
|
|
742
|
-
|
|
807
|
+
if (showContinueDialog) return;
|
|
808
|
+
if (showDataPreview) {
|
|
809
|
+
setDataPreviewScrollOffset((prev) => Math.max(0, prev - 1));
|
|
810
|
+
} else {
|
|
811
|
+
setScrollOffset((prev) => Math.max(0, prev - 1));
|
|
812
|
+
}
|
|
743
813
|
},
|
|
744
814
|
onDown: () => {
|
|
745
|
-
|
|
815
|
+
if (showContinueDialog) return;
|
|
816
|
+
if (showDataPreview) {
|
|
817
|
+
const maxDataVisible = Math.max(5, maxVisible - 2);
|
|
818
|
+
setDataPreviewScrollOffset(
|
|
819
|
+
(prev) => Math.min(prev + 1, Math.max(0, maxDataLines - maxDataVisible))
|
|
820
|
+
);
|
|
821
|
+
} else {
|
|
822
|
+
setScrollOffset(
|
|
823
|
+
(prev) => Math.min(prev + 1, Math.max(0, lines.length - maxVisible))
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
setScrollOffset(
|
|
827
|
+
(prev) => Math.min(prev + 1, Math.max(0, lines.length - maxVisible))
|
|
828
|
+
);
|
|
746
829
|
},
|
|
747
830
|
onInfo: () => {
|
|
748
|
-
|
|
831
|
+
if (showContinueDialog) return;
|
|
832
|
+
setShowDataPreview((prev) => {
|
|
833
|
+
if (!prev) {
|
|
834
|
+
setDataPreviewScrollOffset(0);
|
|
835
|
+
}
|
|
836
|
+
return !prev;
|
|
837
|
+
});
|
|
838
|
+
},
|
|
839
|
+
onContinue: () => {
|
|
840
|
+
if (canContinue && !showContinueDialog) {
|
|
841
|
+
setShowContinueDialog(true);
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
onChar: (char) => {
|
|
845
|
+
if (showContinueDialog) {
|
|
846
|
+
if (char === "y" || char === "Y") {
|
|
847
|
+
handleContinueInClaude(true);
|
|
848
|
+
} else if (char === "n" || char === "N") {
|
|
849
|
+
handleContinueInClaude(false);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
749
852
|
}
|
|
750
853
|
});
|
|
751
854
|
useMount(() => {
|
|
@@ -754,10 +857,19 @@ var Preview = ({
|
|
|
754
857
|
}
|
|
755
858
|
});
|
|
756
859
|
if (showDataPreview) {
|
|
757
|
-
const
|
|
758
|
-
const
|
|
860
|
+
const maxDataVisible = Math.max(5, maxVisible - 2);
|
|
861
|
+
const visibleFormDataLines = formDataLines.slice(
|
|
862
|
+
dataPreviewScrollOffset,
|
|
863
|
+
dataPreviewScrollOffset + maxDataVisible
|
|
864
|
+
);
|
|
865
|
+
const visibleAiContextLines = aiContextLines.slice(
|
|
866
|
+
dataPreviewScrollOffset,
|
|
867
|
+
dataPreviewScrollOffset + maxDataVisible
|
|
868
|
+
);
|
|
869
|
+
const hasMoreDataAbove = dataPreviewScrollOffset > 0;
|
|
870
|
+
const hasMoreDataBelow = dataPreviewScrollOffset + maxDataVisible < maxDataLines;
|
|
759
871
|
return /* @__PURE__ */ jsxs4(ScenarioLayout, { controls, status, children: [
|
|
760
|
-
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "row", gap: 1, children: [
|
|
872
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "row", gap: 1, flexGrow: 1, children: [
|
|
761
873
|
/* @__PURE__ */ jsxs4(
|
|
762
874
|
Box5,
|
|
763
875
|
{
|
|
@@ -773,7 +885,11 @@ var Preview = ({
|
|
|
773
885
|
"formData",
|
|
774
886
|
" "
|
|
775
887
|
] }) }),
|
|
776
|
-
|
|
888
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", overflowY: "hidden", children: [
|
|
889
|
+
hasMoreDataAbove && /* @__PURE__ */ jsx6(Text4, { dimColor: true, children: "\u2191 more" }),
|
|
890
|
+
visibleFormDataLines.map((line, index) => /* @__PURE__ */ jsx6(Text4, { children: line || " " }, dataPreviewScrollOffset + index)),
|
|
891
|
+
hasMoreDataBelow && /* @__PURE__ */ jsx6(Text4, { dimColor: true, children: "\u2193 more" })
|
|
892
|
+
] })
|
|
777
893
|
]
|
|
778
894
|
}
|
|
779
895
|
),
|
|
@@ -792,12 +908,19 @@ var Preview = ({
|
|
|
792
908
|
"aiContext",
|
|
793
909
|
" "
|
|
794
910
|
] }) }),
|
|
795
|
-
|
|
911
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", overflowY: "hidden", children: [
|
|
912
|
+
hasMoreDataAbove && /* @__PURE__ */ jsx6(Text4, { dimColor: true, children: "\u2191 more" }),
|
|
913
|
+
visibleAiContextLines.map((line, index) => /* @__PURE__ */ jsx6(Text4, { children: line || " " }, dataPreviewScrollOffset + index)),
|
|
914
|
+
hasMoreDataBelow && /* @__PURE__ */ jsx6(Text4, { dimColor: true, children: "\u2193 more" })
|
|
915
|
+
] })
|
|
796
916
|
]
|
|
797
917
|
}
|
|
798
918
|
)
|
|
799
919
|
] }),
|
|
800
|
-
/* @__PURE__ */ jsx6(Box5, { paddingX: 1, children: /* @__PURE__ */
|
|
920
|
+
/* @__PURE__ */ jsx6(Box5, { paddingX: 1, children: /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
921
|
+
"Press i or Esc to close",
|
|
922
|
+
maxDataLines > maxDataVisible && ` | Lines ${dataPreviewScrollOffset + 1}-${Math.min(dataPreviewScrollOffset + maxDataVisible, maxDataLines)} of ${maxDataLines}`
|
|
923
|
+
] }) })
|
|
801
924
|
] });
|
|
802
925
|
}
|
|
803
926
|
return /* @__PURE__ */ jsxs4(ScenarioLayout, { controls, status, children: [
|
|
@@ -828,7 +951,39 @@ var Preview = ({
|
|
|
828
951
|
" of",
|
|
829
952
|
" ",
|
|
830
953
|
lines.length
|
|
831
|
-
] }) })
|
|
954
|
+
] }) }),
|
|
955
|
+
showContinueDialog && /* @__PURE__ */ jsxs4(
|
|
956
|
+
Box5,
|
|
957
|
+
{
|
|
958
|
+
flexDirection: "column",
|
|
959
|
+
borderStyle: "round",
|
|
960
|
+
borderColor: "yellow",
|
|
961
|
+
paddingX: 1,
|
|
962
|
+
paddingY: 0,
|
|
963
|
+
marginTop: 1,
|
|
964
|
+
children: [
|
|
965
|
+
/* @__PURE__ */ jsx6(Box5, { marginTop: -1, marginLeft: 1, children: /* @__PURE__ */ jsxs4(Text4, { backgroundColor: "black", color: "yellow", bold: true, children: [
|
|
966
|
+
" ",
|
|
967
|
+
"Continue in Claude",
|
|
968
|
+
" "
|
|
969
|
+
] }) }),
|
|
970
|
+
/* @__PURE__ */ jsx6(Text4, { children: "Save document before continuing?" }),
|
|
971
|
+
/* @__PURE__ */ jsx6(Box5, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
972
|
+
/* @__PURE__ */ jsx6(Text4, { color: "green", bold: true, children: "[y]" }),
|
|
973
|
+
" ",
|
|
974
|
+
"Save and continue",
|
|
975
|
+
" ",
|
|
976
|
+
/* @__PURE__ */ jsx6(Text4, { color: "blue", bold: true, children: "[n]" }),
|
|
977
|
+
" ",
|
|
978
|
+
"Continue without saving",
|
|
979
|
+
" ",
|
|
980
|
+
/* @__PURE__ */ jsx6(Text4, { dimColor: true, bold: true, children: "[Esc]" }),
|
|
981
|
+
" ",
|
|
982
|
+
"Cancel"
|
|
983
|
+
] }) })
|
|
984
|
+
]
|
|
985
|
+
}
|
|
986
|
+
)
|
|
832
987
|
] });
|
|
833
988
|
};
|
|
834
989
|
|
|
@@ -1760,6 +1915,19 @@ var FieldEditor = ({
|
|
|
1760
1915
|
// src/tui/views/ScenarioForm/-internal/FieldSelector/FieldSelector.tsx
|
|
1761
1916
|
import { Box as Box11, Text as Text12 } from "ink";
|
|
1762
1917
|
import { useState as useState5 } from "react";
|
|
1918
|
+
|
|
1919
|
+
// src/tui/utils/scroll.ts
|
|
1920
|
+
var adjustScrollOffset = (newIndex, currentOffset, maxVisible) => {
|
|
1921
|
+
if (newIndex < currentOffset) {
|
|
1922
|
+
return newIndex;
|
|
1923
|
+
}
|
|
1924
|
+
if (newIndex >= currentOffset + maxVisible) {
|
|
1925
|
+
return newIndex - maxVisible + 1;
|
|
1926
|
+
}
|
|
1927
|
+
return currentOffset;
|
|
1928
|
+
};
|
|
1929
|
+
|
|
1930
|
+
// src/tui/views/ScenarioForm/-internal/FieldSelector/FieldSelector.tsx
|
|
1763
1931
|
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1764
1932
|
var FieldSelector = ({
|
|
1765
1933
|
flatItems,
|
|
@@ -1767,6 +1935,7 @@ var FieldSelector = ({
|
|
|
1767
1935
|
isFocused,
|
|
1768
1936
|
isFirstStep,
|
|
1769
1937
|
isLastStep,
|
|
1938
|
+
maxHeight,
|
|
1770
1939
|
onFocusUp,
|
|
1771
1940
|
onFocusToForm,
|
|
1772
1941
|
onAddItem,
|
|
@@ -1777,18 +1946,34 @@ var FieldSelector = ({
|
|
|
1777
1946
|
onBack
|
|
1778
1947
|
}) => {
|
|
1779
1948
|
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
1949
|
+
const [scrollOffset, setScrollOffset] = useState5(0);
|
|
1780
1950
|
const validIndex = Math.min(selectedIndex, Math.max(0, flatItems.length - 1));
|
|
1781
1951
|
const currentItem = flatItems[validIndex];
|
|
1952
|
+
const maxVisibleItems = Math.max(3, maxHeight - 3);
|
|
1953
|
+
const visibleItems = flatItems.slice(
|
|
1954
|
+
scrollOffset,
|
|
1955
|
+
scrollOffset + maxVisibleItems
|
|
1956
|
+
);
|
|
1957
|
+
const hasMoreAbove = scrollOffset > 0;
|
|
1958
|
+
const hasMoreBelow = scrollOffset + maxVisibleItems < flatItems.length;
|
|
1782
1959
|
useControl({
|
|
1783
1960
|
onUp: () => {
|
|
1784
1961
|
if (selectedIndex === 0) {
|
|
1785
1962
|
onFocusUp();
|
|
1786
1963
|
return;
|
|
1787
1964
|
}
|
|
1788
|
-
|
|
1965
|
+
const newIndex = selectedIndex - 1;
|
|
1966
|
+
setSelectedIndex(newIndex);
|
|
1967
|
+
setScrollOffset(
|
|
1968
|
+
(prev) => adjustScrollOffset(newIndex, prev, maxVisibleItems)
|
|
1969
|
+
);
|
|
1789
1970
|
},
|
|
1790
1971
|
onDown: () => {
|
|
1791
|
-
|
|
1972
|
+
const newIndex = selectedIndex < flatItems.length - 1 ? selectedIndex + 1 : 0;
|
|
1973
|
+
setSelectedIndex(newIndex);
|
|
1974
|
+
setScrollOffset(
|
|
1975
|
+
(prev) => adjustScrollOffset(newIndex, prev, maxVisibleItems)
|
|
1976
|
+
);
|
|
1792
1977
|
},
|
|
1793
1978
|
onEnter: () => {
|
|
1794
1979
|
if (!currentItem) return;
|
|
@@ -1840,58 +2025,63 @@ var FieldSelector = ({
|
|
|
1840
2025
|
]
|
|
1841
2026
|
}
|
|
1842
2027
|
) }),
|
|
1843
|
-
/* @__PURE__ */
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
{
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
2028
|
+
/* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", overflowY: "hidden", children: [
|
|
2029
|
+
hasMoreAbove && /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "\u2191 more" }),
|
|
2030
|
+
visibleItems.map((item, visibleIndex) => {
|
|
2031
|
+
const actualIndex = scrollOffset + visibleIndex;
|
|
2032
|
+
const isSelected = actualIndex === validIndex;
|
|
2033
|
+
const valueDisplay = getValueDisplay(item, stepValues);
|
|
2034
|
+
const typeIndicator = getFieldTypeIndicator(item);
|
|
2035
|
+
const treePrefix = item.treePrefix || "";
|
|
2036
|
+
if (item.type === "repeatable-add") {
|
|
2037
|
+
return /* @__PURE__ */ jsxs9(Box11, { children: [
|
|
2038
|
+
/* @__PURE__ */ jsx14(Text12, { color: "gray", children: treePrefix }),
|
|
2039
|
+
/* @__PURE__ */ jsx14(
|
|
2040
|
+
Text12,
|
|
2041
|
+
{
|
|
2042
|
+
color: isSelected && isFocused ? "black" : "green",
|
|
2043
|
+
backgroundColor: isSelected && isFocused ? ACCENT_COLOR : void 0,
|
|
2044
|
+
children: item.label
|
|
2045
|
+
}
|
|
2046
|
+
)
|
|
2047
|
+
] }, `add-${item.path}`);
|
|
2048
|
+
}
|
|
2049
|
+
if (item.type === "repeatable-header") {
|
|
2050
|
+
return /* @__PURE__ */ jsxs9(Box11, { children: [
|
|
2051
|
+
/* @__PURE__ */ jsx14(Text12, { color: "gray", children: treePrefix }),
|
|
2052
|
+
/* @__PURE__ */ jsx14(
|
|
2053
|
+
Text12,
|
|
2054
|
+
{
|
|
2055
|
+
color: isSelected && isFocused ? "black" : ACCENT_COLOR,
|
|
2056
|
+
backgroundColor: isSelected && isFocused ? ACCENT_COLOR : void 0,
|
|
2057
|
+
bold: true,
|
|
2058
|
+
children: item.label
|
|
2059
|
+
}
|
|
2060
|
+
)
|
|
2061
|
+
] }, `header-${item.path}`);
|
|
2062
|
+
}
|
|
1862
2063
|
return /* @__PURE__ */ jsxs9(Box11, { children: [
|
|
1863
2064
|
/* @__PURE__ */ jsx14(Text12, { color: "gray", children: treePrefix }),
|
|
1864
|
-
/* @__PURE__ */
|
|
2065
|
+
/* @__PURE__ */ jsxs9(
|
|
1865
2066
|
Text12,
|
|
1866
2067
|
{
|
|
1867
|
-
color: isSelected && isFocused ? "black" :
|
|
2068
|
+
color: isSelected && isFocused ? "black" : void 0,
|
|
1868
2069
|
backgroundColor: isSelected && isFocused ? ACCENT_COLOR : void 0,
|
|
1869
|
-
|
|
1870
|
-
|
|
2070
|
+
children: [
|
|
2071
|
+
typeIndicator,
|
|
2072
|
+
" ",
|
|
2073
|
+
item.label
|
|
2074
|
+
]
|
|
1871
2075
|
}
|
|
1872
|
-
)
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
backgroundColor: isSelected && isFocused ? ACCENT_COLOR : void 0,
|
|
1882
|
-
children: [
|
|
1883
|
-
typeIndicator,
|
|
1884
|
-
" ",
|
|
1885
|
-
item.label
|
|
1886
|
-
]
|
|
1887
|
-
}
|
|
1888
|
-
),
|
|
1889
|
-
valueDisplay && /* @__PURE__ */ jsxs9(Text12, { color: "gray", children: [
|
|
1890
|
-
" : ",
|
|
1891
|
-
valueDisplay
|
|
1892
|
-
] })
|
|
1893
|
-
] }, item.path);
|
|
1894
|
-
}) })
|
|
2076
|
+
),
|
|
2077
|
+
valueDisplay && /* @__PURE__ */ jsxs9(Text12, { color: "gray", children: [
|
|
2078
|
+
" : ",
|
|
2079
|
+
valueDisplay
|
|
2080
|
+
] })
|
|
2081
|
+
] }, item.path);
|
|
2082
|
+
}),
|
|
2083
|
+
hasMoreBelow && /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "\u2193 more" })
|
|
2084
|
+
] })
|
|
1895
2085
|
]
|
|
1896
2086
|
}
|
|
1897
2087
|
);
|
|
@@ -1982,6 +2172,8 @@ var ScenarioForm = ({
|
|
|
1982
2172
|
const [focusPanel, setFocusPanel] = useState6("steps");
|
|
1983
2173
|
const [currentItem, setCurrentItem] = useState6();
|
|
1984
2174
|
const [validationError, setValidationError] = useState6(null);
|
|
2175
|
+
const { availableHeight } = useTerminalHeight();
|
|
2176
|
+
const fieldSelectorHeight = Math.max(8, availableHeight - 3);
|
|
1985
2177
|
const currentStep = scenario.steps[stepIndex];
|
|
1986
2178
|
if (currentStep == null) {
|
|
1987
2179
|
return /* @__PURE__ */ jsx16(Text14, { color: "red", children: "Error: Step not found" });
|
|
@@ -2216,7 +2408,7 @@ var ScenarioForm = ({
|
|
|
2216
2408
|
onBack
|
|
2217
2409
|
}
|
|
2218
2410
|
),
|
|
2219
|
-
/* @__PURE__ */ jsxs11(Box13, {
|
|
2411
|
+
/* @__PURE__ */ jsxs11(Box13, { flexGrow: 1, children: [
|
|
2220
2412
|
/* @__PURE__ */ jsx16(
|
|
2221
2413
|
FieldSelector,
|
|
2222
2414
|
{
|
|
@@ -2225,6 +2417,7 @@ var ScenarioForm = ({
|
|
|
2225
2417
|
isFocused: focusPanel === "list",
|
|
2226
2418
|
isFirstStep,
|
|
2227
2419
|
isLastStep,
|
|
2420
|
+
maxHeight: fieldSelectorHeight,
|
|
2228
2421
|
onFocusUp: () => setFocusPanel("steps"),
|
|
2229
2422
|
onFocusToForm: handleFocusToForm,
|
|
2230
2423
|
onAddItem: handleAddItem,
|
|
@@ -2253,7 +2446,7 @@ var ScenarioForm = ({
|
|
|
2253
2446
|
// src/tui/views/SelectScenario/SelectScenario.tsx
|
|
2254
2447
|
import { Box as Box14, Text as Text15 } from "ink";
|
|
2255
2448
|
import { useCallback as useCallback5, useMemo as useMemo4, useState as useState7 } from "react";
|
|
2256
|
-
import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2449
|
+
import { Fragment, jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2257
2450
|
var SelectScenario = ({
|
|
2258
2451
|
scenarios,
|
|
2259
2452
|
onSelectNew,
|
|
@@ -2265,6 +2458,10 @@ var SelectScenario = ({
|
|
|
2265
2458
|
const [focusPanel, setFocusPanel] = useState7("scenarios");
|
|
2266
2459
|
const [documents, setDocuments] = useState7([]);
|
|
2267
2460
|
const [isLoading, setIsLoading] = useState7(false);
|
|
2461
|
+
const [scenarioScrollOffset, setScenarioScrollOffset] = useState7(0);
|
|
2462
|
+
const [actionScrollOffset, setActionScrollOffset] = useState7(0);
|
|
2463
|
+
const { availableHeight } = useTerminalHeight();
|
|
2464
|
+
const maxVisibleItems = Math.max(3, availableHeight - 3);
|
|
2268
2465
|
const currentScenario = scenarios[scenarioIndex];
|
|
2269
2466
|
const actionItems = useMemo4(
|
|
2270
2467
|
() => [
|
|
@@ -2273,6 +2470,14 @@ var SelectScenario = ({
|
|
|
2273
2470
|
],
|
|
2274
2471
|
[documents]
|
|
2275
2472
|
);
|
|
2473
|
+
const visibleScenarios = scenarios.slice(
|
|
2474
|
+
scenarioScrollOffset,
|
|
2475
|
+
scenarioScrollOffset + maxVisibleItems
|
|
2476
|
+
);
|
|
2477
|
+
const visibleActionItems = actionItems.slice(
|
|
2478
|
+
actionScrollOffset,
|
|
2479
|
+
actionScrollOffset + maxVisibleItems
|
|
2480
|
+
);
|
|
2276
2481
|
const controls = useMemo4(() => {
|
|
2277
2482
|
if (focusPanel === "scenarios") {
|
|
2278
2483
|
return [
|
|
@@ -2308,11 +2513,17 @@ var SelectScenario = ({
|
|
|
2308
2513
|
onUp: () => {
|
|
2309
2514
|
const newIndex = scenarioIndex > 0 ? scenarioIndex - 1 : scenarios.length - 1;
|
|
2310
2515
|
setScenarioIndex(newIndex);
|
|
2516
|
+
setScenarioScrollOffset(
|
|
2517
|
+
(prev) => adjustScrollOffset(newIndex, prev, maxVisibleItems)
|
|
2518
|
+
);
|
|
2311
2519
|
loadDocuments(newIndex);
|
|
2312
2520
|
},
|
|
2313
2521
|
onDown: () => {
|
|
2314
2522
|
const newIndex = scenarioIndex < scenarios.length - 1 ? scenarioIndex + 1 : 0;
|
|
2315
2523
|
setScenarioIndex(newIndex);
|
|
2524
|
+
setScenarioScrollOffset(
|
|
2525
|
+
(prev) => adjustScrollOffset(newIndex, prev, maxVisibleItems)
|
|
2526
|
+
);
|
|
2316
2527
|
loadDocuments(newIndex);
|
|
2317
2528
|
},
|
|
2318
2529
|
onRight: () => {
|
|
@@ -2326,10 +2537,18 @@ var SelectScenario = ({
|
|
|
2326
2537
|
});
|
|
2327
2538
|
useControl({
|
|
2328
2539
|
onUp: () => {
|
|
2329
|
-
|
|
2540
|
+
const newIndex = actionIndex > 0 ? actionIndex - 1 : actionItems.length - 1;
|
|
2541
|
+
setActionIndex(newIndex);
|
|
2542
|
+
setActionScrollOffset(
|
|
2543
|
+
(prev) => adjustScrollOffset(newIndex, prev, maxVisibleItems)
|
|
2544
|
+
);
|
|
2330
2545
|
},
|
|
2331
2546
|
onDown: () => {
|
|
2332
|
-
|
|
2547
|
+
const newIndex = actionIndex < actionItems.length - 1 ? actionIndex + 1 : 0;
|
|
2548
|
+
setActionIndex(newIndex);
|
|
2549
|
+
setActionScrollOffset(
|
|
2550
|
+
(prev) => adjustScrollOffset(newIndex, prev, maxVisibleItems)
|
|
2551
|
+
);
|
|
2333
2552
|
},
|
|
2334
2553
|
onEnter: () => {
|
|
2335
2554
|
const item = actionItems[actionIndex];
|
|
@@ -2353,7 +2572,11 @@ var SelectScenario = ({
|
|
|
2353
2572
|
useMount(() => {
|
|
2354
2573
|
loadDocuments(0);
|
|
2355
2574
|
});
|
|
2356
|
-
|
|
2575
|
+
const hasMoreScenariosAbove = scenarioScrollOffset > 0;
|
|
2576
|
+
const hasMoreScenariosBelow = scenarioScrollOffset + maxVisibleItems < scenarios.length;
|
|
2577
|
+
const hasMoreActionsAbove = actionScrollOffset > 0;
|
|
2578
|
+
const hasMoreActionsBelow = actionScrollOffset + maxVisibleItems < actionItems.length;
|
|
2579
|
+
return /* @__PURE__ */ jsx17(CommonLayout, { controls, children: /* @__PURE__ */ jsxs12(Box14, { flexGrow: 1, children: [
|
|
2357
2580
|
/* @__PURE__ */ jsxs12(
|
|
2358
2581
|
Box14,
|
|
2359
2582
|
{
|
|
@@ -2376,22 +2599,27 @@ var SelectScenario = ({
|
|
|
2376
2599
|
]
|
|
2377
2600
|
}
|
|
2378
2601
|
) }),
|
|
2379
|
-
/* @__PURE__ */
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
"
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2602
|
+
/* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", overflowY: "hidden", children: [
|
|
2603
|
+
hasMoreScenariosAbove && /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "\u2191 more" }),
|
|
2604
|
+
visibleScenarios.map((scenario, visibleIndex) => {
|
|
2605
|
+
const actualIndex = scenarioScrollOffset + visibleIndex;
|
|
2606
|
+
const isSelected = actualIndex === scenarioIndex;
|
|
2607
|
+
const isFocused = focusPanel === "scenarios";
|
|
2608
|
+
return /* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsxs12(
|
|
2609
|
+
Text15,
|
|
2610
|
+
{
|
|
2611
|
+
color: isSelected && isFocused ? "black" : void 0,
|
|
2612
|
+
backgroundColor: isSelected ? isFocused ? ACCENT_COLOR : "gray" : void 0,
|
|
2613
|
+
children: [
|
|
2614
|
+
" ",
|
|
2615
|
+
scenario.name,
|
|
2616
|
+
" "
|
|
2617
|
+
]
|
|
2618
|
+
}
|
|
2619
|
+
) }, scenario.id);
|
|
2620
|
+
}),
|
|
2621
|
+
hasMoreScenariosBelow && /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "\u2193 more" })
|
|
2622
|
+
] })
|
|
2395
2623
|
]
|
|
2396
2624
|
}
|
|
2397
2625
|
),
|
|
@@ -2418,23 +2646,28 @@ var SelectScenario = ({
|
|
|
2418
2646
|
]
|
|
2419
2647
|
}
|
|
2420
2648
|
) }),
|
|
2421
|
-
/* @__PURE__ */ jsx17(Box14, { flexDirection: "column", children: isLoading ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Loading..." }) :
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
" ",
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2649
|
+
/* @__PURE__ */ jsx17(Box14, { flexDirection: "column", overflowY: "hidden", children: isLoading ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Loading..." }) : /* @__PURE__ */ jsxs12(Fragment, { children: [
|
|
2650
|
+
hasMoreActionsAbove && /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "\u2191 more" }),
|
|
2651
|
+
visibleActionItems.map((item, visibleIndex) => {
|
|
2652
|
+
const actualIndex = actionScrollOffset + visibleIndex;
|
|
2653
|
+
const isSelected = actualIndex === actionIndex;
|
|
2654
|
+
const isFocused = focusPanel === "actions";
|
|
2655
|
+
const label = item.type === "new" ? item.label : item.doc.filename;
|
|
2656
|
+
return /* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsxs12(
|
|
2657
|
+
Text15,
|
|
2658
|
+
{
|
|
2659
|
+
color: isSelected && isFocused ? "black" : item.type === "new" ? "green" : void 0,
|
|
2660
|
+
backgroundColor: isSelected && isFocused ? ACCENT_COLOR : void 0,
|
|
2661
|
+
children: [
|
|
2662
|
+
" ",
|
|
2663
|
+
label,
|
|
2664
|
+
" "
|
|
2665
|
+
]
|
|
2666
|
+
}
|
|
2667
|
+
) }, item.type === "new" ? "new" : item.doc.filename);
|
|
2668
|
+
}),
|
|
2669
|
+
hasMoreActionsBelow && /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "\u2193 more" })
|
|
2670
|
+
] }) })
|
|
2438
2671
|
]
|
|
2439
2672
|
}
|
|
2440
2673
|
)
|
|
@@ -2444,7 +2677,7 @@ var SelectScenario = ({
|
|
|
2444
2677
|
// src/tui/App.tsx
|
|
2445
2678
|
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
2446
2679
|
var App = ({ config, initialScenarioId }) => {
|
|
2447
|
-
const { exit } =
|
|
2680
|
+
const { exit } = useApp2();
|
|
2448
2681
|
const [appState, setAppState] = useState8(() => {
|
|
2449
2682
|
if (initialScenarioId != null) {
|
|
2450
2683
|
const scenario2 = config.scenarios.find((s) => s.id === initialScenarioId);
|