UNPKG

aura-ai

Version:

AI-powered marketing strategist CLI tool for developers

446 lines (445 loc) 17 kB
#!/usr/bin/env -S node --no-warnings import { jsxs, jsx, Fragment } from "react/jsx-runtime"; import { useState, useRef, useEffect } from "react"; import { useInput, Box, Text, Spacer } from "ink"; import "@inkjs/ui"; import TextInput from "ink-text-input"; import SelectInput from "ink-select-input"; import fs from "fs/promises"; import path, { dirname } from "path"; import { fileURLToPath } from "url"; const CompositeInput = ({ options = [], onSubmit, placeholder = "Type your answer...", initialValue = "", onBack = null, isReview = false, onPrevious = null, showTypeYourAnswer = true, directSubmitValues = [] }) => { const [mode, setMode] = useState(options.length > 0 ? "select" : "text"); const [inputValue, setInputValue] = useState(""); const [exitPrompt, setExitPrompt] = useState(false); const lastCtrlC = useRef(0); useInput((input, key) => { if (key.ctrl && input === "c") { const now = Date.now(); if (now - lastCtrlC.current < 2e3) { if (onBack) onBack(); } else { setExitPrompt(true); lastCtrlC.current = now; setTimeout(() => { setExitPrompt(false); }, 2e3); } } }); useEffect(() => { setMode(options.length > 0 ? "select" : "text"); setInputValue(initialValue); setExitPrompt(false); }, [options, initialValue]); const selectItems = options.length > 0 ? [ ...options.map((opt) => ({ label: opt.label, value: opt.value })), ...showTypeYourAnswer ? [ { label: "Type your answer...", value: "__custom_input__" } ] : [] ] : []; const handleSelect = (item) => { if (item.value === "__custom_input__") { setMode("text"); setInputValue(""); } else if (directSubmitValues.includes(item.value)) { onSubmit(item.value); } else { setMode("text"); setInputValue(item.label); } }; const handleTextSubmit = (value) => { if (value.trim()) { if (value.toLowerCase() === "/back" && !isReview && onPrevious) { onPrevious(); return; } onSubmit(value); } }; return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: mode === "select" && options.length > 0 ? /* @__PURE__ */ jsx( Box, { paddingX: 1, paddingY: 0, borderColor: "whiteBright", borderStyle: "round", width: "100%", children: /* @__PURE__ */ jsx(SelectInput, { items: selectItems, onSelect: handleSelect }) } ) : /* @__PURE__ */ jsxs( Box, { paddingX: 1, paddingY: 0, borderColor: "whiteBright", borderStyle: "round", width: "100%", children: [ /* @__PURE__ */ jsx(Text, { color: "blue", children: "> " }), /* @__PURE__ */ jsx( TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleTextSubmit, placeholder, focus: true } ) ] } ) }), /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, fontSize: 12, children: mode === "select" ? /* @__PURE__ */ jsx(Fragment, { children: "Use ↑↓ to navigate, Enter to select" }) : isReview ? /* @__PURE__ */ jsx(Fragment, { children: "Use /1, /2, etc. to jump to specific questions" }) : /* @__PURE__ */ jsx(Fragment, { children: "Use /back to go to previous question" }) }) }), exitPrompt && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "Press Ctrl+C again to return to main menu" }) }) ] }); }; const EmojiSpinner = ({ label = "", emojis = ["💬", "💭"], interval = 500 }) => { const [currentEmojiIndex, setCurrentEmojiIndex] = useState(0); useEffect(() => { const timer = setInterval(() => { setCurrentEmojiIndex((prev) => (prev + 1) % emojis.length); }, interval); return () => clearInterval(timer); }, [emojis, interval]); return /* @__PURE__ */ jsxs(Box, { gap: 1, children: [ /* @__PURE__ */ jsx(Text, { children: emojis[currentEmojiIndex] }), label && /* @__PURE__ */ jsx(Text, { children: label }) ] }); }; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const InitPage = ({ onBack }) => { const [questions, setQuestions] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); const [answers, setAnswers] = useState([]); const [showConfirmSubmit, setShowConfirmSubmit] = useState(false); const [showModifyInput, setShowModifyInput] = useState(false); const [modifyInputValue, setModifyInputValue] = useState(""); const [editingQuestionIndex, setEditingQuestionIndex] = useState(-1); const [isProcessing, setIsProcessing] = useState(false); useEffect(() => { const loadQuestions = async () => { try { const possiblePaths = [ path.join(__dirname, "..", "src", "init", "generated", "questions.json"), // From dist to src/init path.join(path.dirname(__dirname), "src", "init", "generated", "questions.json"), // Alternative dist path path.resolve("./src/init/generated/questions.json"), // Absolute from project root "/Users/leo/Documents/GitHub/rina/src/init/generated/questions.json" // Fallback absolute path ]; let data = null; for (const jsonPath of possiblePaths) { try { data = await fs.readFile(jsonPath, "utf-8"); break; } catch (err) { } } if (!data) { throw new Error("Questions file not found in any expected location"); } const parsed = JSON.parse(data); setQuestions(parsed); setLoading(false); } catch (err) { setError(`Failed to load questions: ${err.message}`); setLoading(false); } }; loadQuestions(); }, []); const handlePrevious = () => { if (currentQuestionIndex > 0) { setCurrentQuestionIndex((prev) => prev - 1); setEditingQuestionIndex(-1); } }; const handleCompositeSubmit = (answer) => { if (answer.startsWith("/")) { const questionNum = parseInt(answer.substring(1)); if (questionNum >= 1 && questionNum <= questions.totalQuestions) { setCurrentQuestionIndex(questionNum - 1); setEditingQuestionIndex(questionNum - 1); return; } } setIsProcessing(true); const wasReAnswering = answers.some( (ans) => ans.question === questions.questions[currentQuestionIndex].question ); setTimeout(() => { setAnswers((prev) => { const newAnswers = [...prev]; const existingIndex = newAnswers.findIndex( (ans) => ans.question === questions.questions[currentQuestionIndex].question ); if (existingIndex >= 0) { newAnswers[existingIndex] = { question: questions.questions[currentQuestionIndex].question, answer }; } else { newAnswers.push({ question: questions.questions[currentQuestionIndex].question, answer }); } return newAnswers; }); setEditingQuestionIndex(-1); setIsProcessing(false); if (wasReAnswering) { const answeredQuestions = new Set(answers.map((ans) => ans.question)); answeredQuestions.add(questions.questions[currentQuestionIndex].question); for (let i = 0; i < questions.questions.length; i++) { if (!answeredQuestions.has(questions.questions[i].question)) { setCurrentQuestionIndex(i); return; } } setShowConfirmSubmit(true); } else { if (currentQuestionIndex < questions.questions.length - 1) { setCurrentQuestionIndex((prev) => prev + 1); } else { setShowConfirmSubmit(true); } } }, 2e3); }; const handleConfirmSubmit = (confirmed) => { { if (onBack) onBack(); } }; const handleModifyInput = (value) => { if (value.trim()) { if (value.startsWith("/")) { const questionNum2 = parseInt(value.substring(1)); if (questionNum2 >= 1 && questionNum2 <= questions.totalQuestions) { setCurrentQuestionIndex(questionNum2 - 1); setEditingQuestionIndex(questionNum2 - 1); setShowModifyInput(false); return; } } const questionNum = parseInt(value); if (questionNum >= 1 && questionNum <= questions.totalQuestions) { setCurrentQuestionIndex(questionNum - 1); setEditingQuestionIndex(questionNum - 1); setShowModifyInput(false); } } }; if (loading) { return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "Loading questions..." }) }); } if (error) { return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: error }) }); } if (showConfirmSubmit) { return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, children: [ /* @__PURE__ */ jsx(Box, { marginBottom: 2, children: /* @__PURE__ */ jsx(Text, { color: "green", bold: true, children: "🎉 All questions completed!" }) }), /* @__PURE__ */ jsxs( Box, { paddingX: 2, paddingY: 1, borderColor: "white", borderStyle: "round", marginBottom: 2, flexDirection: "column", children: [ /* @__PURE__ */ jsx(Text, { color: "yellow", children: "Your answers:" }), answers.map((ans, idx) => { const questionIndex = questions.questions.findIndex((q) => q.question === ans.question); const isEditing = editingQuestionIndex === questionIndex; return /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [ /* @__PURE__ */ jsxs(Text, { color: "white", children: [ "Q", idx + 1, ": ", ans.question ] }), /* @__PURE__ */ jsxs(Text, { color: "gray", children: [ "A", idx + 1, ": ", isEditing ? "[editing]" : ans.answer ] }) ] }, idx); }) ] } ), isProcessing && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(EmojiSpinner, { label: "Aura is thinking..." }) }), /* @__PURE__ */ jsxs(Box, { columnGap: 1, marginBottom: 2, children: [ /* @__PURE__ */ jsx(Text, { children: "🌀" }), /* @__PURE__ */ jsx(Text, { bold: true, children: "Ready to submit?" }) ] }), /* @__PURE__ */ jsx( CompositeInput, { options: [ { label: "Yes,submit for deep analysis", value: "submit" }, { label: "No, modify answers...", value: "modify" } ], onSubmit: (value) => { setIsProcessing(true); setTimeout(() => { if (value === "submit") { handleConfirmSubmit(); } else if (value === "modify") { setShowConfirmSubmit(false); setShowModifyInput(true); setModifyInputValue(""); } setIsProcessing(false); }, 2e3); }, onBack, isReview: true, showTypeYourAnswer: false, directSubmitValues: ["submit", "modify"] } ) ] }); } if (showModifyInput) { return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, children: [ answers.length > 0 && /* @__PURE__ */ jsxs(Box, { marginBottom: 2, flexDirection: "column", children: [ /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "Your answers:" }), answers.map((ans, idx) => { const questionIndex = questions.questions.findIndex((q) => q.question === ans.question); const isEditing = editingQuestionIndex === questionIndex; return /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [ /* @__PURE__ */ jsxs(Text, { color: "white", children: [ "Q", idx + 1, ": ", ans.question ] }), /* @__PURE__ */ jsxs(Text, { color: "gray", children: [ "A", idx + 1, ": ", isEditing ? "[editing]" : ans.answer ] }) ] }, idx); }) ] }), /* @__PURE__ */ jsxs(Box, { columnGap: 1, marginBottom: 2, children: [ /* @__PURE__ */ jsx(Text, { children: "🌀" }), /* @__PURE__ */ jsx(Text, { bold: true, children: "Which question do you want to modify?" }) ] }), /* @__PURE__ */ jsx( CompositeInput, { options: [], onSubmit: handleModifyInput, placeholder: "/1, /2, etc. to jump to specific questions", initialValue: modifyInputValue, onBack, isReview: true, showTypeYourAnswer: false } ) ] }); } if (questions && questions.questions[currentQuestionIndex]) { const currentQuestion = questions.questions[currentQuestionIndex]; return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, children: [ /* @__PURE__ */ jsxs(Box, { marginBottom: 1, flexDirection: "row", children: [ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { bold: true, children: [ "📝 Question ", currentQuestionIndex + 1, " of ", questions.totalQuestions ] }) }), /* @__PURE__ */ jsx(Spacer, {}), /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Box, { marginBottom: 1, marginRight: 2, children: (() => { { const completed = currentQuestionIndex + 1; const remaining = questions.totalQuestions - completed; return /* @__PURE__ */ jsxs(Text, { children: [ /* @__PURE__ */ jsx(Text, { color: "white", children: "🐤".repeat(completed) }), /* @__PURE__ */ jsx(Text, { color: "white", children: "🥚".repeat(remaining) }) ] }); } })() }) }) ] }), answers.length > 0 && answers.length === questions.totalQuestions && /* @__PURE__ */ jsx(Box, { marginBottom: 2, flexDirection: "column", width: "100%", children: answers.map((ans, idx) => { const questionIndex = questions.questions.findIndex((q) => q.question === ans.question); const isEditing = editingQuestionIndex === questionIndex; return /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [ /* @__PURE__ */ jsxs(Text, { color: "white", children: [ "Q", idx + 1, ": ", ans.question ] }), /* @__PURE__ */ jsxs(Text, { color: "gray", children: [ "A", idx + 1, ": ", isEditing ? "[editing]" : ans.answer ] }) ] }, idx); }) }), /* @__PURE__ */ jsxs(Box, { columnGap: 1, marginBottom: 2, children: [ /* @__PURE__ */ jsx(Text, { children: "🌀" }), /* @__PURE__ */ jsxs(Box, { flexDirection: "column", rowGap: 1, children: [ /* @__PURE__ */ jsx(Text, { bold: true, children: currentQuestion.question }), currentQuestion.explainer && /* @__PURE__ */ jsx(Text, { marginLeft: 2, dimColor: true, fontSize: 12, children: currentQuestion.explainer }) ] }) ] }), isProcessing && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(EmojiSpinner, { label: "Processing your answer..." }) }), /* @__PURE__ */ jsx( CompositeInput, { options: currentQuestion.options || [], onSubmit: handleCompositeSubmit, placeholder: "Type your answer...", initialValue: editingQuestionIndex === currentQuestionIndex ? answers.find((ans) => ans.question === currentQuestion.question)?.answer || "" : "", onBack, onPrevious: handlePrevious, isReview: answers.length === questions.totalQuestions } ) ] }); } return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: "No questions available" }) }); }; export { EmojiSpinner as E, InitPage as I }; //# sourceMappingURL=page-CHKhZAAX.js.map