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
1 change: 1 addition & 0 deletions apps/docs-site/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dist
/docs/mcp-clients.html
/docs/support.html
/docs/terms.html
/docs/accessibility.html
/docs/refund-policy.html
/docs/dmca.html
/docs/dsa-contact.html
Expand Down
3 changes: 2 additions & 1 deletion apps/docs-site/auth/cli.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<main class="auth-shell"><div id="auth"></div></main>
<a class="skip-link" href="#main">Skip to content</a>
<main id="main" class="auth-shell"><div id="auth"></div></main>
<script type="module" src="/auth/src/cli.js"></script>
</body>
</html>
3 changes: 2 additions & 1 deletion apps/docs-site/auth/reset.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<main class="auth-shell"><div id="auth"></div></main>
<a class="skip-link" href="#main">Skip to content</a>
<main id="main" class="auth-shell"><div id="auth"></div></main>
<script type="module" src="/auth/src/reset.js"></script>
</body>
</html>
3 changes: 2 additions & 1 deletion apps/docs-site/auth/signin.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<main class="auth-shell"><div id="auth"></div></main>
<a class="skip-link" href="#main">Skip to content</a>
<main id="main" class="auth-shell"><div id="auth"></div></main>
<script type="module">
import { mountAccountPage } from "/auth/src/account.js";
void mountAccountPage("signin");
Expand Down
3 changes: 2 additions & 1 deletion apps/docs-site/auth/signup.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<main class="auth-shell"><div id="auth"></div></main>
<a class="skip-link" href="#main">Skip to content</a>
<main id="main" class="auth-shell"><div id="auth"></div></main>
<script type="module">
import { mountAccountPage } from "/auth/src/account.js";
void mountAccountPage("signup");
Expand Down
32 changes: 21 additions & 11 deletions apps/docs-site/auth/src/auth-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ export async function mountAuthForm(container, { mode = "signin", onSession }) {
container.innerHTML = `
<div class="auth-card">
<a class="auth-brand" href="/">Crosscode</a>
<div class="auth-tabs">
<button type="button" data-mode="signin" class="${isSignup ? "" : "active"}">Sign in</button>
<button type="button" data-mode="signup" class="${isSignup ? "active" : ""}">Sign up</button>
<div class="auth-tabs" role="group" aria-label="Account">
<button type="button" data-mode="signin" aria-pressed="${isSignup ? "false" : "true"}" class="${isSignup ? "" : "active"}">Sign in</button>
<button type="button" data-mode="signup" aria-pressed="${isSignup ? "true" : "false"}" class="${isSignup ? "active" : ""}">Sign up</button>
</div>
<h1>${isSignup ? "Create your account" : "Welcome back"}</h1>
<p class="auth-subtitle">
Expand All @@ -63,16 +63,21 @@ export async function mountAuthForm(container, { mode = "signin", onSession }) {
<form id="auth-form" class="stack">
<label>
Email
<input type="email" name="email" required autocomplete="email" />
<input type="email" name="email" required autocomplete="email" aria-describedby="auth-error" />
</label>
<label>
Password
<input type="password" name="password" required minlength="6" autocomplete="${isSignup ? "new-password" : "current-password"}" />
<input type="password" name="password" required minlength="6" autocomplete="${isSignup ? "new-password" : "current-password"}" aria-describedby="auth-error" />
</label>
${legalNoticeHtml(isSignup, legal)}
<button type="submit">${isSignup ? "Sign up" : "Sign in"}</button>
<p id="auth-status" class="muted" hidden></p>
<p id="auth-error" class="error" hidden></p>
<!-- Both stay in the DOM and empty rather than toggling the hidden attribute.
A live region a screen reader cannot see when the text arrives announces
nothing, and hidden takes the element out of the accessibility tree
entirely, so the old pattern wrote the message and then hid the only thing
that could have read it out. -->
<p id="auth-status" class="muted" role="status"></p>
<p id="auth-error" class="error" role="alert"></p>
</form>
<p class="auth-alt"><a href="/auth/reset.html">Forgot your password?</a></p>
<div class="auth-divider"><span>or</span></div>
Expand Down Expand Up @@ -124,9 +129,12 @@ export async function mountAuthForm(container, { mode = "signin", onSession }) {
void handleSubmit();
});

const inputs = [...form.querySelectorAll("input")];

async function handleSubmit() {
errorEl.hidden = true;
statusEl.hidden = true;
errorEl.textContent = "";
statusEl.textContent = "";
for (const input of inputs) input.removeAttribute("aria-invalid");
const formData = new FormData(form);
const email = String(formData.get("email") ?? "");
const password = String(formData.get("password") ?? "");
Expand All @@ -146,7 +154,6 @@ export async function mountAuthForm(container, { mode = "signin", onSession }) {
// Email confirmation is required before a session is issued; nothing more
// to do here client-side until the user confirms and signs in.
statusEl.textContent = "Check your email to confirm your account, then sign in.";
statusEl.hidden = false;
return;
}
// Recorded before the workspace is created, so the first thing this account does
Expand All @@ -162,7 +169,10 @@ export async function mountAuthForm(container, { mode = "signin", onSession }) {
}
} catch (error) {
errorEl.textContent = error instanceof Error ? error.message : `${isSignup ? "Sign-up" : "Sign-in"} failed`;
errorEl.hidden = false;
// The service cannot say which field was wrong -- "invalid login credentials" is
// deliberately one answer for both -- so both are marked, and both point at the
// one message via aria-describedby.
for (const input of inputs) input.setAttribute("aria-invalid", "true");
} finally {
submitButton.disabled = false;
}
Expand Down
16 changes: 8 additions & 8 deletions apps/docs-site/auth/src/reset.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ function renderRequestForm() {
<input type="email" name="email" required autocomplete="email" />
</label>
<button type="submit">Send reset link</button>
<p id="auth-status" class="muted" hidden></p>
<p id="auth-error" class="error" hidden></p>
<p id="auth-status" class="muted" role="status"></p>
<p id="auth-error" class="error" role="alert"></p>
</form>
<p class="auth-alt"><a href="/auth/signin.html">Back to sign in</a></p>
</div>
Expand All @@ -52,8 +52,8 @@ function renderUpdateForm() {
<input type="password" name="password" required minlength="6" autocomplete="new-password" />
</label>
<button type="submit">Update password</button>
<p id="auth-status" class="muted" hidden></p>
<p id="auth-error" class="error" hidden></p>
<p id="auth-status" class="muted" role="status"></p>
<p id="auth-error" class="error" role="alert"></p>
</form>
<p class="auth-alt"><a href="/auth/signin.html">Back to sign in</a></p>
</div>
Expand All @@ -75,15 +75,15 @@ function wire(submit) {
form.addEventListener("submit", (event) => {
event.preventDefault();
void (async () => {
errorEl.hidden = true;
statusEl.hidden = true;
// Emptied rather than hidden: `hidden` drops the live region out of the
// accessibility tree, so a message written into a hidden one is announced to nobody.
errorEl.textContent = "";
statusEl.textContent = "";
submitButton.disabled = true;
try {
statusEl.textContent = await submit(new FormData(form));
statusEl.hidden = false;
} catch (error) {
errorEl.textContent = error instanceof Error ? error.message : "Request failed";
errorEl.hidden = false;
} finally {
submitButton.disabled = false;
}
Expand Down
3 changes: 2 additions & 1 deletion apps/docs-site/device.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<main class="auth-shell"><div id="device" class="auth-card"></div></main>
<a class="skip-link" href="#main">Skip to content</a>
<main id="main" class="auth-shell"><div id="device" class="auth-card"></div></main>
<script type="module">
import { mountDevicePage } from "/src/device.js";
void mountDevicePage(document.querySelector("#device"));
Expand Down
20 changes: 11 additions & 9 deletions apps/docs-site/docs/cli.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>

<header class="site-header">
<nav>
<nav aria-label="Primary">
<a class="brand" href="/">Crosscode</a>
<a href="/docs/index.html">Docs</a>
<a href="/docs/install.html">Install &amp; quickstart</a>
Expand All @@ -28,7 +30,7 @@
</nav>
</header>

<main class="docs-layout wide">
<main id="main" class="docs-layout wide">
<nav class="docs-sidebar" aria-label="Docs navigation">
<div>
<div class="docs-sidebar-label">Start here</div>
Expand Down Expand Up @@ -99,7 +101,7 @@ <h2>crosscode start</h2>
attaches the checkout to a project, starts the daemon, and installs the MCP server,
the skill, and the pre-edit hooks for your coding agent.
</p>
<pre><code>crosscode start
<pre tabindex="0"><code>crosscode start
crosscode start --no-browser # print the sign-in URL instead of opening a browser</code></pre>
<p>
Use <code>--no-browser</code> on a remote shell or in CI. Restart your agent afterwards
Expand All @@ -108,7 +110,7 @@ <h2>crosscode start</h2>

<h2>crosscode invite</h2>
<p>Prints a link that invites a teammate to this project. The link is the whole output.</p>
<pre><code>crosscode invite
<pre tabindex="0"><code>crosscode invite
# https://www.getcrosscode.dev/join/7f3a9c2e</code></pre>
<p>
The join page checks the invitee actually has access to the repository before it hands
Expand All @@ -120,13 +122,13 @@ <h2>crosscode join &lt;code&gt;</h2>
Redeems an invite code in this checkout, then sets it up exactly as <code>start</code>
would. The code is a positional argument in the form printed by the join page.
</p>
<pre><code>crosscode join CC-7F3A-9C2E
<pre tabindex="0"><code>crosscode join CC-7F3A-9C2E
crosscode join CC-7F3A-9C2E --no-browser</code></pre>

<h2>crosscode status</h2>
<p>Shows what this checkout's daemon is doing.</p>
<pre><code>crosscode status --json</code></pre>
<pre><code>{
<pre tabindex="0"><code>crosscode status --json</code></pre>
<pre tabindex="0"><code>{
"value": {
"branch": "main",
"connected": true,
Expand All @@ -147,15 +149,15 @@ <h2>crosscode stop</h2>
Stops the daemon for this checkout. Syncing stops; nothing is uninstalled and nothing
in your repository changes.
</p>
<pre><code>crosscode stop</code></pre>
<pre tabindex="0"><code>crosscode stop</code></pre>

<h2>Output envelope</h2>
<p>
With <code>--json</code>, success is wrapped in <code>value</code> so it is symmetric
with the failure shape, and an agent can branch on which key is present. Progress goes
to stderr, so stdout stays a single parseable object.
</p>
<pre><code>{"value": {"branch": "main", "connected": true, ...}}
<pre tabindex="0"><code>{"value": {"branch": "main", "connected": true, ...}}
{"error": {"code": "NOT_A_GIT_REPOSITORY", "message": "Crosscode runs inside a Git repository", "hint": "cd into your project first."}}</code></pre>
<p>
Without <code>--json</code> the value is printed bare, because the envelope is only
Expand Down
7 changes: 5 additions & 2 deletions apps/docs-site/docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>

<header class="site-header">
<nav>
<nav aria-label="Primary">
<a class="brand" href="/">Crosscode</a>
<a href="/docs/index.html">Docs</a>
<a href="/docs/install.html">Install &amp; quickstart</a>
Expand All @@ -28,7 +30,7 @@
</nav>
</header>

<main class="docs-layout wide">
<main id="main" class="docs-layout wide">
<nav class="docs-sidebar" aria-label="Docs navigation">
<div>
<div class="docs-sidebar-label">Start here</div>
Expand Down Expand Up @@ -77,6 +79,7 @@ <h1>Documentation</h1>
UI, because there isn't one.
</p>

<h2 class="visually-hidden">Documentation pages</h2>
<div class="card-grid">
<div class="card">
<h3><a href="/docs/architecture.html">Architecture</a></h3>
Expand Down
18 changes: 10 additions & 8 deletions apps/docs-site/docs/install.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>

<header class="site-header">
<nav>
<nav aria-label="Primary">
<a class="brand" href="/">Crosscode</a>
<a href="/docs/index.html">Docs</a>
<a href="/docs/install.html">Install &amp; quickstart</a>
Expand All @@ -28,7 +30,7 @@
</nav>
</header>

<main class="docs-layout wide">
<main id="main" class="docs-layout wide">
<nav class="docs-sidebar" aria-label="Docs navigation">
<div>
<div class="docs-sidebar-label">Start here</div>
Expand Down Expand Up @@ -93,7 +95,7 @@ <h2>Prerequisites</h2>
</ul>

<h2>Install and start</h2>
<pre><code>npm install -g crosscode-cli
<pre tabindex="0"><code>npm install -g crosscode-cli
cd your-repo
crosscode start</code></pre>

Expand All @@ -116,12 +118,12 @@ <h2>Install and start</h2>
</p>

<h2>Bring in a teammate</h2>
<pre><code>crosscode invite</code></pre>
<pre tabindex="0"><code>crosscode invite</code></pre>
<p>
That prints a link. They open it, sign in with GitHub, and the page checks they
actually have access to the repository before giving them two lines to paste:
</p>
<pre><code>git clone git@github.com:acme/app.git &amp;&amp; cd app
<pre tabindex="0"><code>git clone git@github.com:acme/app.git &amp;&amp; cd app
crosscode join CC-7F3A-9C2E</code></pre>
<p>
<code>join</code> redeems the code and then sets the checkout up exactly as
Expand All @@ -135,7 +137,7 @@ <h2>Day to day</h2>
and a conflict goes to your coding agent rather than to you. The two remaining commands
exist for when you want to look:
</p>
<pre><code>crosscode status # branch, connected, paused, who else is on this branch
<pre tabindex="0"><code>crosscode status # branch, connected, paused, who else is on this branch
crosscode stop # stop syncing this checkout</code></pre>

<h2>What syncs</h2>
Expand All @@ -149,7 +151,7 @@ <h2>What syncs</h2>
Your commits, branches, index, stash, and remotes are never touched, and nothing here
pushes. To undo anything Crosscode wrote:
</p>
<pre><code>git checkout refs/crosscode/shadow -- &lt;path&gt;</code></pre>
<pre tabindex="0"><code>git checkout refs/crosscode/shadow -- &lt;path&gt;</code></pre>

<h2>Hand the setup to your agent</h2>
<p>
Expand All @@ -163,7 +165,7 @@ <h2>Hand the setup to your agent</h2>

<h2>Running from a clone</h2>
<p>For working on Crosscode itself, or for running the CLI before it is on npm:</p>
<pre><code>pnpm install
<pre tabindex="0"><code>pnpm install
pnpm build # typecheck + bundle
pnpm test # vitest
pnpm crosscode -- status --json</code></pre>
Expand Down
6 changes: 4 additions & 2 deletions apps/docs-site/docs/limitations.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>

<header class="site-header">
<nav>
<nav aria-label="Primary">
<a class="brand" href="/">Crosscode</a>
<a href="/docs/index.html">Docs</a>
<a href="/docs/install.html">Install &amp; quickstart</a>
Expand All @@ -28,7 +30,7 @@
</nav>
</header>

<main class="docs-layout wide">
<main id="main" class="docs-layout wide">
<nav class="docs-sidebar" aria-label="Docs navigation">
<div>
<div class="docs-sidebar-label">Start here</div>
Expand Down
Loading
Loading