Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ The interface does not execute contracts in the browser. Generated commands run
- **Tablet Two-Column Grid**: Compact two-column module layout for tablet widths (601px-900px) for better space utilization.
- **Landscape Phone Optimization**: Condensed command form layout for landscape phone orientations (541px-900px width, max 600px height).
- **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 reference explains the ready-to-run limits command, its `--contract`, `--fn`, and `--ramp` arguments, defaults, and offline/read-only behavior.

## Development

Expand Down
5 changes: 3 additions & 2 deletions docs/accessibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ browser and screen reader pair per platform.
| 5 | Module numbers (`01`–`07`) were read as text before every module name. | Fixed: `aria-hidden`. | `src/App.tsx` |
| 6 | The `footer` sits inside `main`, so it is not exposed as a `contentinfo` landmark. | Open. Moving it out of `main` changes the wide-screen layout, so it needs a visual check. | `src/App.tsx` |
| 7 | Collapsed guide toggles set `aria-controls` to panels that are not rendered until opened. | Open. Minor; the attribute points at a missing id while collapsed. | `src/App.tsx` |
| 8 | Scrollable `pre` blocks are not keyboard-focusable in every browser, so long lines may be unreachable by keyboard on some browsers. | Open. | `src/styles.css` |
| 9 | The stored visual baselines in `tests/visual/__screenshots__` predate the current layout and do not match a fresh render. | Open. Baselines should be regenerated on the CI platform, not locally. | `tests/visual/` |
| 8 | The limits workflow needed a discoverable command reference. | Fixed: added a focusable reference section with semantic headings, lists, code examples, and a keyboard-accessible navigation link. | `src/App.tsx`, `src/styles.css` |
| 9 | Scrollable `pre` blocks are not keyboard-focusable in every browser, so long lines may be unreachable by keyboard on some browsers. | Open. | `src/styles.css` |
| 10 | The stored visual baselines in `tests/visual/__screenshots__` predate the current layout and do not match a fresh render. | Open. Baselines should be regenerated on the CI platform, not locally. | `tests/visual/` |
23 changes: 22 additions & 1 deletion src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,27 @@ describe("Soroban workflow documentation and prerequisites", () => {
});
});

describe("Limits command reference (#92)", () => {
it("documents the ready-to-run command, arguments, defaults, and offline behavior", () => {
render(<App />);

expect(screen.getByRole("heading", { name: /resource limits, at a glance/i })).toBeInTheDocument();
expect(screen.getByLabelText("Resource limits command example")).toHaveTextContent("soroban-testkit limits");
expect(screen.getByText("--contract")).toBeInTheDocument();
expect(screen.getByText("--fn")).toBeInTheDocument();
expect(screen.getByText("--ramp")).toBeInTheDocument();
expect(screen.getByText(/runs offline/i)).toBeInTheDocument();
});

it("exposes the reference as a focusable navigation target", () => {
render(<App />);

const link = screen.getByRole("link", { name: "Limits reference" });
expect(link).toHaveAttribute("href", "#limits-reference");
expect(document.getElementById("limits-reference")).toHaveAttribute("tabindex", "-1");
});
});

describe("Audit workflow prerequisites checklist (#91)", () => {
it("lets a user check off audit prerequisites, each with its own accessible name", () => {
render(<App />);
Expand Down Expand Up @@ -479,7 +500,7 @@ describe("Screen reader semantics (#108)", () => {
Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true });
render(<App />);

fireEvent.click(screen.getByRole("button", { name: "Copy", exact: true }));
fireEvent.click(screen.getByRole("button", { name: /^Copy$/ }));

const announcement = await screen.findByText("Command copied to clipboard");
expect(announcement).toHaveAttribute("aria-live", "polite");
Expand Down
47 changes: 47 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ import {
modules,
type CommandName,
type OptionalArgKey,
localFileGuides,
} from "./testkit";
import { copyToClipboard } from "./clipboard";
import { CORE_REPO_URL, INSTALL_GUIDE_URL, INTERFACE_REPO_URL } from "./links";
import { useActiveSection } from "./useActiveSection";
import "./styles.css";

const navSections = [
{ id: "install", label: "Install" },
{ id: "modules", label: "Modules" },
{ id: "commands", label: "Commands" },
{ id: "limits-reference", label: "Limits reference" },
] as const;
const navSectionIds = navSections.map((section) => section.id);

Expand Down Expand Up @@ -255,6 +258,7 @@ export default function App() {
const [activeSection, setActiveSection] = useActiveSection(navSectionIds);

useEffect(() => {
document.documentElement.style.scrollPaddingTop = "78px";
const handleKeyDown = (event: globalThis.KeyboardEvent) => {
if (!event.ctrlKey && !event.metaKey) return;

Expand All @@ -271,6 +275,9 @@ export default function App() {
case "4":
document.getElementById("commands")?.scrollIntoView({ behavior: "smooth" });
break;
case "5":
document.getElementById("limits-reference")?.scrollIntoView({ behavior: "smooth" });
break;
}
};
document.addEventListener("keydown", handleKeyDown);
Expand Down Expand Up @@ -1288,6 +1295,46 @@ export default function App() {
</div>
)}
</article>

<section
className="command-reference"
id="limits-reference"
tabIndex={-1}
aria-labelledby="limits-reference-heading"
>
<div className="reference-header">
<p className="eyebrow">Command reference</p>
<h2 id="limits-reference-heading">Resource limits, at a glance.</h2>
<p>
Use <code>soroban-testkit limits</code> to ramp one contract input until the local WASM
execution reaches its practical resource ceiling. The command is read-only and runs offline.
</p>
</div>
<div className="reference-grid">
<article className="reference-card">
<h3>Ready-to-run command</h3>
<pre aria-label="Resource limits command example"><code>{"soroban-testkit limits \\\n --contract target/wasm32v1-none/release/contract.wasm \\\n --fn batch_payout \\\n --ramp recipients"}</code></pre>
<p>Replace the function and ramp names with exported contract inputs from your ABI.</p>
</article>
<article className="reference-card">
<h3>Arguments</h3>
<dl className="reference-arguments">
<div><dt><code>--contract</code></dt><dd>Compiled WASM artifact. Defaults to <code>{localFileGuides.limits.defaultPath}</code>.</dd></div>
<div><dt><code>--fn</code></dt><dd>Exported function to probe. Defaults to <code>batch_payout</code>.</dd></div>
<div><dt><code>--ramp</code></dt><dd>Input parameter to increase. Defaults to <code>recipients</code>.</dd></div>
</dl>
</article>
<article className="reference-card">
<h3>Before you run it</h3>
<ol>
<li>Build the contract for <code>wasm32v1-none</code> in release mode.</li>
<li>Confirm the function is exported and the ramp input is accepted.</li>
<li>Read the instruction and memory ceilings printed to stdout.</li>
</ol>
<p className="reference-note">Reads: local WASM and export metadata. Writes: none.</p>
</article>
</div>
</section>
</section>

<footer>
Expand Down
32 changes: 31 additions & 1 deletion src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
html { scroll-behavior: smooth; scroll-padding-top: 78px; }
body { margin: 0; min-width: 320px; background: radial-gradient(circle at 75% 8%, var(--color-canvas-gradient) 0, transparent 28rem), var(--color-canvas); }
a { color: inherit; text-decoration: none; }
button, input, select { font: inherit; }
Expand Down Expand Up @@ -209,6 +209,35 @@ h2 { margin: var(--space-md) 0; font-size: clamp(2rem, 4vw, 4rem); letter-spacin
.module-empty-state p { margin: 0 0 var(--space-xl); color: var(--color-text-secondary); font-size: .9rem; line-height: 1.6; }
.module-empty-state button { display: inline-block; }

/* Limits command reference */
.command-reference {
margin-top: var(--space-section);
padding: var(--space-section) var(--page-padding);
border-top: 1px solid var(--color-border-subtle);
background: var(--color-surface);
}
.reference-header { max-width: 720px; margin-bottom: 38px; }
.reference-header h2 { margin-bottom: var(--space-xl); }
.reference-header > p:last-child { color: var(--color-text-body); line-height: 1.7; }
.reference-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-xl); }
.reference-card {
padding: var(--space-3xl);
border: 1px solid var(--color-border-default);
border-radius: var(--radius-lg);
background: var(--color-surface-card);
}
.reference-card h3 { margin: 0 0 var(--space-xl); color: var(--color-accent); font-size: 1rem; }
.reference-card p, .reference-card li, .reference-card dd { color: var(--color-text-body); line-height: 1.65; font-size: .86rem; }
.reference-card pre { margin: 0 0 var(--space-xl); padding: var(--space-lg); overflow-x: auto; background: var(--color-surface-output); border-radius: var(--radius-md); color: var(--color-accent); font: .75rem/1.8 var(--font-mono); }
.reference-card ol { margin: 0; padding-left: 1.25rem; }
.reference-arguments { margin: 0; }
.reference-arguments > div { padding: var(--space-md) 0; border-bottom: 1px solid var(--color-border-subtle); }
.reference-arguments > div:first-child { padding-top: 0; }
.reference-arguments > div:last-child { border-bottom: 0; }
.reference-arguments dt { color: var(--color-text-code); font-family: var(--font-mono); font-size: .8rem; }
.reference-arguments dd { margin: var(--space-xs) 0 0; }
.reference-note { margin-bottom: 0; color: var(--color-text-muted) !important; font-family: var(--font-mono); font-size: .75rem !important; }

/* Commands / CLI Workbench */
.commands { display: grid; grid-template-columns: .8fr 1.2fr; gap: 8vw; background: var(--color-surface); }
.command-copy > p:not(.eyebrow) { color: var(--color-text-body); line-height: 1.7; }
Expand Down Expand Up @@ -279,6 +308,7 @@ footer { display: flex; justify-content: space-between; padding: 34px var(--page
@media (max-width: 900px) {
.hero, .commands { grid-template-columns: 1fr; }
.hero { padding-top: 70px; }
.reference-grid { grid-template-columns: 1fr; }
}

/* Tablet-specific spacing for section headers (#186) */
Expand Down