Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

Agent setup page: the Generate button lines up with the Label input, and the page fits a phone

- The Label's hint goes below the input and button, so the button aligns with the input
  instead of the input plus its hint, and drops below the input where both don't fit.
- Tokens are a wrapping list instead of a table, so Renew, Revoke and regenerate and
  Revoke stay on screen on a phone.
- The merge setting's explanation is a small hint under the checkbox, and the boxes'
  closing paragraphs leave no extra space at the bottom.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JocnWrggUDcArR2s8cXGZp
Claude authored signed1 parent 1d10bcfcommit 919fdf4250c015a4f2ca589827bacb98e5d4083fBrowse files
1 file changed+35−44
modifiedapps/diffinery/src/web/agent-setup.tsx+35−44View file
@@ -100,31 +100,29 @@
100100
101101 const TokenRow: FC<{ c: AppContext; base: string; t: AgentSetupToken }> = ({ base, t, c }) => {
102102 const soon = Date.parse(t.expiresAt) - Date.now() < 14 * 86400_000;
103+ // A list row rather than a table row: on a phone the buttons wrap below the token's details.
103104 return (
104- <tr>
105- <td>
106- <b>{t.name}</b>
107- <div class="small muted mono">{t.prefix}…</div>
108- </td>
109- <td class={soon ? "warn" : ""}>
110- {date(t.expiresAt)}
111- <div class="small muted">created {date(t.createdAt)}</div>
112- </td>
113- <td class="small">{t.lastUsedAt ? <Time at={t.lastUsedAt} /> : <span class="muted">never</span>}</td>
114- <td style="text-align:right;white-space:nowrap">
105+ <li style="flex-wrap:wrap;align-items:center">
106+ <div class="grow" style="min-width:200px">
107+ <b>{t.name}</b> <span class="small muted mono">{t.prefix}…</span>
108+ <div class="small muted">
109+ <span class={soon ? "warn" : ""}>Expires {date(t.expiresAt)}</span> · created {date(t.createdAt)} · {t.lastUsedAt ? <>last used <Time at={t.lastUsedAt} /></> : "never used"}
110+ </div>
111+ </div>
112+ <div class="row" style="gap:6px">
115113 <Form action={`${base}/tokens/${t.id}/renew`} t={csrfToken(c)} class="inline">
116114 <button class="btn sm" title={`Keep this token working for ${AGENT_SETUP_DAYS} days from today; nothing to paste again`}>
117115 Renew
118116 </button>
119- </Form>{" "}
117+ </Form>
120118 <Form action={`${base}/tokens/${t.id}/regenerate`} t={csrfToken(c)} class="inline" confirm="Revoke this token and make a new one? Sessions using it stop working until you paste the new one.">
121119 <button class="btn sm">Revoke and regenerate</button>
122- </Form>{" "}
120+ </Form>
123121 <Form action={`${base}/tokens/${t.id}/revoke`} t={csrfToken(c)} class="inline" confirm="Revoke this token? Environments using it stop working.">
124122 <button class="btn sm danger">Revoke</button>
125123 </Form>
126- </td>
127- </tr>
124+ </div>
125+ </li>
128126 );
129127 };
130128
@@ -176,37 +174,30 @@
176174 </p>
177175 )}
178176 {tokens.length ? (
179- <table class="tbl mb">
180- <thead>
181- <tr>
182- <th>Token</th>
183- <th>Expires</th>
184- <th>Last used</th>
185- <th />
186- </tr>
187- </thead>
188- <tbody>
189- {tokens.map((k) => (
190- <TokenRow c={c} base={base} t={k} />
191- ))}
192- </tbody>
193- </table>
177+ <ul class="list mb" style="border:1px solid var(--border2);border-radius:7px">
178+ {tokens.map((k) => (
179+ <TokenRow c={c} base={base} t={k} />
180+ ))}
181+ </ul>
194182 ) : null}
195- <Form action={`${base}/generate`} t={t} class="row" style="align-items:flex-end">
196- <div class="field grow m0">
197- <label for="tname">Label</label>
198- <input id="tname" name="name" type="text" maxlength={100} value={defaultTokenName()} />
199- <div class="hint">Shown in the token list and in audit logs, so you can tell environments apart.</div>
183+ {/* The button lines up with the input, and drops below it where both don't fit. */}
184+ <Form action={`${base}/generate`} t={t} class="field m0">
185+ <label for="tname">Label</label>
186+ <div class="row">
187+ <input id="tname" name="name" type="text" maxlength={100} value={defaultTokenName()} style="flex:1 1 240px;width:auto;min-width:0" />
188+ <button class="btn primary">{tokens.length ? "Generate another token" : "Generate agent token"}</button>
200189 </div>
201- <button class="btn primary">{tokens.length ? "Generate another token" : "Generate agent token"}</button>
190+ <div class="hint">Shown in the token list and in audit logs, so you can tell environments apart.</div>
202191 </Form>
203192 {active ? (
204- <Form action={`${base}/merge`} t={t} class="row mt" style="align-items:center">
205- <KCheckbox name="mayMerge" checked={agentMayMerge(active)} label="Can merge pull requests" />
206- <button class="btn sm">Save</button>
207- <span class="hint" style="margin:0">
193+ <Form action={`${base}/merge`} t={t} class="mt">
194+ <div class="row">
195+ <KCheckbox name="mayMerge" checked={agentMayMerge(active)} label="Can merge pull requests" />
196+ <button class="btn sm">Save</button>
197+ </div>
198+ <p class="small muted" style="margin:6px 0 0">
208199 It merges, or turns on auto-merge, only once a person approved the pull request's latest commit and every rule of the branch is met. A change to a workflow file always needs a person to merge.
209- </span>
200+ </p>
210201 </Form>
211202 ) : null}
212203 </div>
@@ -218,7 +209,7 @@
218209 </div>
219210 <div class="body">
220211 <Paste value={host} />
221- <p class="small muted mt">
212+ <p class="small muted mt" style="margin-bottom:0">
222213 The domain the environment must reach (the container registry is on it too). In Claude Code, set <b>Network access</b> to <b>Custom</b>, add it under <b>Allowed domains</b> and keep <b>Also include default list of common package managers</b> ticked; or choose <b>Full</b>. The default <b>Trusted</b> level
223214 doesn't include it.
224215 </p>
@@ -231,7 +222,7 @@
231222 </div>
232223 <div class="body">
233224 <Paste value={script} />
234- <p class="small muted mt">
225+ <p class="small muted mt" style="margin-bottom:0">
235226 Installs the <code>diffinery</code> CLI on PATH, makes git and gh use the token from the environment (it never writes the token to disk or prints it), clones {slug} unless it's there (into <code>/home/user/{repo.name}</code> in Claude Code; set <code>DIFFINERY_NO_CLONE=1</code> to skip), then checks that
236227 everything works and reports to the Check below. Safe to run every session, and quick once the CLI is installed. <a href={`/${slug}/agent-setup.sh`}>Read the script</a>.
237228 </p>
@@ -244,7 +235,7 @@
244235 </div>
245236 <div class="body">
246237 <Paste value={env} copy={!!created} id="agent-env" />
247- <p class="small muted mt">
238+ <p class="small muted mt" style="margin-bottom:0">
248239 {created
249240 ? "Paste these lines as they are. The token is in two of them: the CLI and git read DIFFINERY_TOKEN, gh reads GH_ENTERPRISE_TOKEN, and GH_HOST points gh at Diffinery."
250241 : tokens.length