Settings: add Fast Mode toggle for codex/claude + tidy account row alignment (#46)

Fast mode controls
- Codex: writes [features].fast_mode = true|false in ~/.codex/config.toml.
  This matches the in-TUI /fast toggle ("toggle Fast mode to enable
  fastest inference with increased plan usage").
- Claude: writes fastMode: bool to ~/.claude/settings.json. Mirrors the
  /fast slash command preference (effective on opus-4-6).

UX polish
- Account rows switch from flex-wrap to a fixed grid (tag · email · plan ·
  expiry · action) so emails of varying lengths don't cause ragged
  baselines between rows.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Eyejoker
2026-04-27 22:06:56 +09:00
committed by GitHub
parent 5883e57d73
commit 7047caa608
5 changed files with 290 additions and 5 deletions

View File

@@ -24,6 +24,7 @@ import {
type DashboardTaskAction,
type DashboardOverview,
type DashboardTask,
type FastModeSnapshot,
type ModelConfigSnapshot,
type ModelRoleConfig,
type UpdateScheduledTaskInput,
@@ -33,11 +34,13 @@ import {
deleteAccount,
fetchAccounts,
fetchDashboardData,
fetchFastMode,
fetchModelConfig,
runInboxAction,
runServiceAction,
runScheduledTaskAction,
sendRoomMessage,
updateFastMode,
updateModels,
updateScheduledTask,
} from './api';
@@ -1098,6 +1101,8 @@ function SettingsPanel({
<ModelSettings onRestartStack={onRestartStack} />
<FastModeSettings />
<AccountSettings onRestartStack={onRestartStack} />
</div>
);
@@ -1234,6 +1239,90 @@ function ModelSettings({ onRestartStack }: { onRestartStack: () => void }) {
);
}
function FastModeSettings() {
const [state, setState] = useState<FastModeSnapshot | null>(null);
const [busy, setBusy] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
fetchFastMode()
.then((s) => {
if (cancelled) return;
setState(s);
setError(null);
})
.catch((err) => {
if (cancelled) return;
setError(err instanceof Error ? err.message : String(err));
});
return () => {
cancelled = true;
};
}, []);
async function toggle(provider: keyof FastModeSnapshot) {
if (!state) return;
const next = !state[provider];
const optimistic = { ...state, [provider]: next };
setState(optimistic);
setBusy(true);
setError(null);
try {
const fresh = await updateFastMode({ [provider]: next });
setState(fresh);
} catch (err) {
setState(state);
setError(err instanceof Error ? err.message : String(err));
} finally {
setBusy(false);
}
}
return (
<section className="settings-section">
<h3> </h3>
{error ? <p className="settings-error">{error}</p> : null}
{!state ? (
<p className="settings-hint"> </p>
) : (
<>
<label className="settings-toggle-row">
<span className="settings-toggle-label">
<span className="settings-toggle-title">Codex (GPT)</span>
<small className="settings-hint">
~/.codex/config.toml [features].fast_mode
.
</small>
</span>
<input
checked={state.codex}
disabled={busy}
onChange={() => void toggle('codex')}
type="checkbox"
/>
</label>
<label className="settings-toggle-row">
<span className="settings-toggle-label">
<span className="settings-toggle-title">Claude</span>
<small className="settings-hint">
~/.claude/settings.json fastMode /fast
. opus-4-6 .
</small>
</span>
<input
checked={state.claude}
disabled={busy}
onChange={() => void toggle('claude')}
type="checkbox"
/>
</label>
</>
)}
</section>
);
}
function formatExpiry(
iso: string | null,
): { label: string; cls: string } | null {