From 857b388500da7e08cc0ceaefdd29518dfc08479c Mon Sep 17 00:00:00 2001 From: Coder Girl <316228223+CillaSam@users.noreply.github.com> Date: Wed, 21 Oct 2026 16:43:11 +0100 Subject: [PATCH] Add enhanced copy options and workflow management features - Add reset to defaults for active workflow - Add command history with one-click restore - Add copy with explanatory comments option - Add copy as reusable shell script option - Update tests and documentation - Add keyboard accessibility for all new features --- .vscode/settings.json | 2 + README.md | 6 ++ src/App.tsx | 128 +++++++++++++++++++++++++++++-- src/styles.css | 10 +++ src/testkit.test.ts | 171 ++++++++++++++++++++++++++++++++++++++++++ src/testkit.ts | 114 ++++++++++++++++++++++++++++ 6 files changed, 426 insertions(+), 5 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/README.md b/README.md index cbac473..9eca605 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,12 @@ The interface does not execute contracts in the browser. Generated commands run - **Required vs. Optional Setting Distinction**: Visual badges (`Required` / `Optional`) and `aria-required` attributes in the CLI workbench distinguishing mandatory parameters from optional configuration flags. - **Audit workflow support**: An expandable, keyboard-accessible preflight checklist, strict-mode control, and advanced multiline command editor help users prepare and reproduce source audits. - **Limits command reference**: A standalone, keyboard-accessible walkthrough builds the Sororail batch-payout contract and runs `soroban-testkit limits` against its `execute_equal` entry point, alongside argument defaults and offline/read-only behavior. +- **Enhanced Copy Options**: Multiple ways to copy commands: + - **Copy with Comments** (#144): Export commands with inline explanatory comments for documentation and learning. + - **Copy as Script** (#145): Generate complete, executable bash scripts with variables and error handling. +- **Workflow Management**: + - **Reset to Defaults** (#141): Quickly reset the active workflow to its default configuration. + - **Command History** (#143): Restore previously configured commands with one click (maintains last 10 commands). ## Development diff --git a/src/App.tsx b/src/App.tsx index f686df5..a0ea510 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,8 @@ import { type KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } import { buildCommand, formatCommandLine, + formatCommandWithComments, + formatCommandAsScript, modules, moduleCategories, type CommandName, @@ -235,6 +237,10 @@ export default function App() { const [shell, setShell] = useState<"bash" | "zsh" | "fish" | "sh">("bash"); const [copied, setCopied] = useState(false); const [copiedStep, setCopiedStep] = useState(null); + const [copiedWithComments, setCopiedWithComments] = useState(false); + const [copiedAsScript, setCopiedAsScript] = useState(false); + // Issue #143: Command history for restore functionality + const [commandHistory, setCommandHistory] = useState([]); // Copy buttons change their label, which screen readers often skip; this // polite live region announces the result explicitly (#108). const [announcement, setAnnouncement] = useState(""); @@ -261,14 +267,9 @@ export default function App() { const [auditGuideOpen, setAuditGuideOpen] = useState(false); const [checkedPrerequisites, setCheckedPrerequisites] = useState>({}); const comparePanelRef = useRef(null); - const [showBackToTop, setShowBackToTop] = useState(false); - const [categoryFilter, setCategoryFilter] = useState(null); - const [selectedModule, setSelectedModule] = useState(null); // #117 — mobile nav toggle const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const mobileMenuRef = useRef(null); - // #118 — module table of contents - const [tocOpen, setTocOpen] = useState(false); // #116 — accessibility statement modal const [a11yOpen, setA11yOpen] = useState(false); const [activeSection, setActiveSection] = useActiveSection(navSectionIds); @@ -400,6 +401,56 @@ export default function App() { setMultilineEditor(false); }; + // Issue #141: Reset to defaults for active workflow only + const resetToDefaults = () => { + if (!window.confirm(`Reset ${command} workflow to its default values?`)) return; + + if (command === "coverage") { + setPath("."); + setFormat("html"); + setOpen(true); + setCoverageOrder(defaultCoverageOrder); + } else if (command === "limits") { + setPath(""); + setFn("batch_payout"); + setRamp("recipients"); + } else if (command === "audit") { + setPath(""); + setStrict(true); + } + setShell("bash"); + setMultilineEditor(false); + }; + + // Issue #143: Save current configuration to history + const saveToHistory = () => { + setCommandHistory((prev) => { + const newHistory = [{ ...options }, ...prev]; + // Keep only last 10 commands + return newHistory.slice(0, 10); + }); + }; + + // Issue #143: Restore previous command from history + const restorePrevious = () => { + if (commandHistory.length === 0) return; + + const previous = commandHistory[0]; + setCommand(previous.command); + setPath(previous.path); + setFormat(previous.format); + setOpen(previous.open); + setFn(previous.fn); + setRamp(previous.ramp); + setStrict(previous.strict); + if (previous.shell) setShell(previous.shell); + if (previous.optionalArgOrder) setCoverageOrder(previous.optionalArgOrder); + + // Remove from history after restoring + setCommandHistory((prev) => prev.slice(1)); + setAnnouncement("Previous command restored"); + }; + const applyAdvancedCommand = () => { const tokens = advancedCommand.match(/"[^"]+"|'[^']+'|[^\s]+/g) ?? []; if (tokens.length === 0) return; @@ -439,6 +490,7 @@ export default function App() { const copy = async () => { await copyToClipboard(generated); + saveToHistory(); // Save to history when copying setCopied(true); setAnnouncement("Command copied to clipboard"); window.setTimeout(() => { @@ -447,6 +499,32 @@ export default function App() { }, 1600); }; + // Issue #144: Copy command with explanatory comments + const copyWithComments = async () => { + const commented = formatCommandWithComments(options); + await copyToClipboard(commented); + saveToHistory(); // Save to history when copying + setCopiedWithComments(true); + setAnnouncement("Command with comments copied to clipboard"); + window.setTimeout(() => { + setCopiedWithComments(false); + setAnnouncement(""); + }, 1600); + }; + + // Issue #145: Copy command as reusable shell script + const copyAsScript = async () => { + const script = formatCommandAsScript(options); + await copyToClipboard(script); + saveToHistory(); // Save to history when copying + setCopiedAsScript(true); + setAnnouncement("Shell script copied to clipboard"); + window.setTimeout(() => { + setCopiedAsScript(false); + setAnnouncement(""); + }, 1600); + }; + const copyStep = async (index: number, stepCommand: string) => { await copyToClipboard(stepCommand); setCopiedStep(index); @@ -883,6 +961,26 @@ export default function App() { + {/* Issue #141: Reset to defaults for active workflow */} + + {/* Issue #143: Restore previous command */} +
@@ -1117,6 +1215,26 @@ export default function App() { + {/* Issue #144 & #145: Additional copy options */} +
+ + +
+