UI design-consistency pass: token scales, sentence case, sans-first, rounded everywhere (v0.1.68) (#103)

* ui: add type/weight/radius token scales, drop dead CSS rules

* ui: drop all uppercase transforms and tracking, sentence-case status strings

* ui: reserve mono for code, paths, ids and terminal output

* ui: primary content uses --text; grays reserved for true secondary text

* ui: normalize font weights onto the --fw-* scale

* ui: normalize radii and type sizes onto token scales, round callouts and inputs

* ui: pin badges to sans in mono contexts, un-mono instance durations; rebuild dist

* ui: sentence-case remaining status strings, fix stale ModelPicker comment

* ui: promote composer reasoning pill to --text

* ui: shared Wordmark component — logo mark + all-black OpenResearch text

* ui: match home wordmark weight to the landing page (800 -> 700)

* chore: bump version to 0.1.68
This commit is contained in:
Daniel Kim
2026-07-20 14:59:21 -07:00
committed by GitHub
parent 0a42fb8b28
commit 2dd67bc2ca
16 changed files with 612 additions and 640 deletions
Generated
+1 -1
View File
@@ -928,7 +928,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "openresearch-cli"
version = "0.1.67"
version = "0.1.68"
dependencies = [
"anyhow",
"async-trait",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "openresearch-cli"
version = "0.1.67"
version = "0.1.68"
edition = "2021"
description = "OpenResearch CLI (orx) — Rust port"
repository = "https://github.com/alphaXiv/openresearch-cli"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -9,8 +9,8 @@
html { background: #ffffff; }
@media (prefers-color-scheme: dark) { html { background: #0e0c0c; } }
</style>
<script type="module" crossorigin src="/assets/index-DNALzhHF.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B1p7OPJI.css">
<script type="module" crossorigin src="/assets/index-DtLrlkjR.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-FZ8l5lIQ.css">
</head>
<body>
<div id="root"></div>
+2 -1
View File
@@ -12,6 +12,7 @@ import {
X,
} from "lucide-react";
import { useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
import { Wordmark } from "./Wordmark";
import {
chatAttachmentUrl,
createChatSession,
@@ -1481,7 +1482,7 @@ export function ChatPanel({
{!threadMounted ? (
<div className="chat-empty">
<h2>
Open<span>Research</span>
<Wordmark />
</h2>
<p>
Ask the agent to explore your codebase, create and run your baseline experiment, and
+5 -1
View File
@@ -28,9 +28,13 @@ export function LogTerminal({ runId }: { runId: string }) {
convertEol: true,
disableStdin: true,
fontSize: 12,
fontFamily: 'ui-monospace, "SF Mono", Menlo, Consolas, monospace',
// xterm needs a resolved font string, so read --mono off the root element.
fontFamily:
getComputedStyle(document.documentElement).getPropertyValue("--mono").trim() ||
"ui-monospace, Menlo, Consolas, monospace",
scrollback: 20000,
theme: {
// Matches --term-bg in styles.css (terminal stays dark in both themes).
background: "#1a1a1a",
foreground: "#e6e1e0",
cursor: "#1a1a1a",
+1 -1
View File
@@ -220,7 +220,7 @@ export function OptionPicker({
/** The harness's default id — pinned at top of the menu and used when
* `value` is null. */
defaultId?: string | null;
/** Uppercase group header (e.g. "Mode"). */
/** Group header (e.g. "Mode"). */
header?: string;
align?: "left" | "right";
/** `pill` = boxed (permission mode); `bare` = text-only (reasoning). */
+11 -10
View File
@@ -1,4 +1,5 @@
import { ArrowLeft, ArrowRight, Check, Copy, RefreshCw } from "lucide-react";
import { Wordmark } from "./Wordmark";
import { useEffect, useState } from "react";
import {
getDataDir,
@@ -53,7 +54,7 @@ export function Onboarding({ onDone }: { onDone: () => void }) {
{step === 0 ? (
<>
<div className="onb-eyebrow">
Open<span>Research</span> · step 1 of 4
<Wordmark /> · Step 1 of 4
</div>
<h2 className="onb-title">Your coding agents</h2>
<p className="onb-sub">
@@ -82,7 +83,7 @@ export function Onboarding({ onDone }: { onDone: () => void }) {
) : step === 1 ? (
<>
<div className="onb-eyebrow">
Open<span>Research</span> · step 2 of 4
<Wordmark /> · Step 2 of 4
</div>
<h2 className="onb-title">Git &amp; GitHub</h2>
<p className="onb-sub">
@@ -109,7 +110,7 @@ export function Onboarding({ onDone }: { onDone: () => void }) {
) : step === 2 ? (
<>
<div className="onb-eyebrow">
Open<span>Research</span> · step 3 of 4
<Wordmark /> · Step 3 of 4
</div>
<h2 className="onb-title">Where orx keeps your data</h2>
<p className="onb-sub">
@@ -133,7 +134,7 @@ export function Onboarding({ onDone }: { onDone: () => void }) {
) : (
<>
<div className="onb-eyebrow">
Open<span>Research</span> · step 4 of 4
<Wordmark /> · Step 4 of 4
</div>
<h2 className="onb-title">Usage analytics</h2>
<p className="onb-sub">
@@ -161,9 +162,9 @@ export function Onboarding({ onDone }: { onDone: () => void }) {
}
function agentBadge(h: Harness): { cls: string; label: string } {
if (h.agentReady) return { cls: "st-done", label: "connected" };
if (h.installed) return { cls: "st-starting", label: "not signed in" };
return { cls: "st-idle", label: "not detected" };
if (h.agentReady) return { cls: "st-done", label: "Connected" };
if (h.installed) return { cls: "st-starting", label: "Not signed in" };
return { cls: "st-idle", label: "Not detected" };
}
function AgentCard({ h }: { h: Harness }) {
@@ -260,10 +261,10 @@ function GitCard({
{git.githubTokenSource === "env"
? "token from GITHUB_TOKEN"
: git.githubTokenSource === "stored"
? "token saved in orx"
? "Token saved in orx"
: git.githubTokenSource === "gh"
? "signed in via gh CLI"
: "not connected"}
? "Signed in via gh CLI"
: "Not connected"}
</span>
<span className={`status-badge ${git.githubTokenSource ? "st-done" : "st-starting"}`}>
{git.githubTokenSource ? <Check size={12} strokeWidth={3} /> : <span className="dot" />}
+2 -1
View File
@@ -1,4 +1,5 @@
import { Plus, Trash2 } from "lucide-react";
import { Wordmark } from "./Wordmark";
import { useState } from "react";
import { deleteProject, timeAgo, type Project } from "../api";
import { NewProjectForm } from "./NewProjectForm";
@@ -38,7 +39,7 @@ export function ProjectsHome({
<div className="home">
<div className="home-inner">
<div className="home-brand">
Open<span>Research</span>
<Wordmark />
</div>
<div className="home-head">
<h2>Projects</h2>
+38 -38
View File
@@ -195,12 +195,12 @@ function HarnessesTab() {
// --- compute (kubernetes) -------------------------------------------------------
function K8sHealthBadge({ s }: { s: K8sSettings }) {
if (!s.configured) return <span className="badge">not configured</span>;
if (!s.configured) return <span className="badge">Not configured</span>;
const p = s.preflight;
if (!p.kubectlFound) return <span className="badge err">kubectl not found</span>;
if (!p.reachable) return <span className="badge err">cluster unreachable</span>;
if (!p.canCreateJobs) return <span className="badge err">no job-create permission</span>;
return <span className="badge ok">connected</span>;
if (!p.reachable) return <span className="badge err">Cluster unreachable</span>;
if (!p.canCreateJobs) return <span className="badge err">No job-create permission</span>;
return <span className="badge ok">Connected</span>;
}
function K8sSection() {
@@ -332,12 +332,12 @@ const MODAL_TOKEN_LABELS: Record<ModalTokenSource, string> = {
};
function ModalBadge({ s }: { s: ModalSettings }) {
if (s.ready) return <span className="badge ok">connected</span>;
if (!s.tokenConfigured && !s.modalImportable) return <span className="badge">not set up</span>;
if (s.ready) return <span className="badge ok">Connected</span>;
if (!s.tokenConfigured && !s.modalImportable) return <span className="badge">Not set up</span>;
if (!s.modalImportable)
return <span className="badge err">{s.envProvisioned ? "env broken" : "env not built"}</span>;
if (!s.tokenConfigured) return <span className="badge err">no token</span>;
return <span className="badge">unknown</span>;
return <span className="badge err">{s.envProvisioned ? "Env broken" : "Env not built"}</span>;
if (!s.tokenConfigured) return <span className="badge err">No token</span>;
return <span className="badge">Unknown</span>;
}
function ModalSection() {
@@ -388,14 +388,14 @@ function ModalSection() {
<span className="k">Environment</span>
<span className="v">
{s.modalImportable
? "ready"
? "Ready"
: s.envProvisioned
? "provisioned (modal import failing)"
: "not built yet"}
? "Provisioned (modal import failing)"
: "Not built yet"}
</span>
<span className="k">Token</span>
<span className="v">
{s.tokenSource ? MODAL_TOKEN_LABELS[s.tokenSource] : "not configured"}
{s.tokenSource ? MODAL_TOKEN_LABELS[s.tokenSource] : "Not configured"}
</span>
</div>
{!s.tokenConfigured && (
@@ -430,11 +430,11 @@ function HostTestCell({ test }: { test: HostTest | undefined }) {
if (test === undefined) return <span className="muted">never tested</span>;
if (test === "testing") return <span className="spinner" />;
const badge = !test.reachable ? (
<span className="badge err" title={test.error ?? undefined}>unreachable</span>
<span className="badge err" title={test.error ?? undefined}>Unreachable</span>
) : !test.gitFound ? (
<span className="badge err">no git</span>
<span className="badge err">No git</span>
) : (
<span className="badge ok">ready</span>
<span className="badge ok">Ready</span>
);
return (
<>
@@ -537,12 +537,12 @@ function SlurmTestBadge({ test }: { test: "testing" | SlurmPreflight | null }) {
if (!test.reachable)
return (
<span className="badge err" title={test.error ?? undefined}>
unreachable
Unreachable
</span>
);
if (!test.slurmFound) return <span className="badge err">no slurm CLI</span>;
if (!test.gitFound) return <span className="badge err">no git</span>;
return <span className="badge ok">ready</span>;
if (!test.slurmFound) return <span className="badge err">No Slurm CLI</span>;
if (!test.gitFound) return <span className="badge err">No git</span>;
return <span className="badge ok">Ready</span>;
}
function SlurmSection() {
@@ -818,18 +818,18 @@ function OpenResearchSection() {
<div className="kv">
<span className="k">Status</span>
<span className="v">
<span className="badge ok">signed in</span>
<span className="badge ok">Signed in</span>
</span>
<span className="k">Orgs</span>
<span className="v">{s.orgs.length > 0 ? s.orgs.join(", ") : "—"}</span>
<span className="k">SSH key</span>
<span className="v">
{s.sshKeyRegistered === true ? (
<span className="badge ok">registered</span>
<span className="badge ok">Registered</span>
) : s.sshKeyRegistered === false ? (
<span className="badge err">none registered</span>
<span className="badge err">None registered</span>
) : (
<span className="badge">unknown</span>
<span className="badge">Unknown</span>
)}
</span>
</div>
@@ -882,10 +882,10 @@ const FLAVOR_SUGGESTIONS: Partial<Record<ComputeTargetId, string[]>> = {
};
function TargetStatusBadge({ t, isDefault }: { t: ComputeTargetSummary; isDefault: boolean }) {
if (t.id === "local") return <span className="badge ok">ready</span>;
if (!t.configured && isDefault) return <span className="badge warn">not configured</span>;
if (!t.configured) return <span className="badge">not set up</span>;
return <span className="badge ok">configured</span>;
if (t.id === "local") return <span className="badge ok">Ready</span>;
if (!t.configured && isDefault) return <span className="badge warn">Not configured</span>;
if (!t.configured) return <span className="badge">Not set up</span>;
return <span className="badge ok">Configured</span>;
}
/** The default row's inline flavor editor (flavored backends only). */
@@ -1168,18 +1168,18 @@ const SOURCE_LABELS: Record<HfTokenSource, string> = {
};
function HfStatusBadge({ settings }: { settings: HfSettings }) {
if (!settings.configured) return <span className="badge">not configured</span>;
if (!settings.valid) return <span className="badge err">invalid token</span>;
return <span className="badge ok">connected</span>;
if (!settings.configured) return <span className="badge">Not configured</span>;
if (!settings.valid) return <span className="badge err">Invalid token</span>;
return <span className="badge ok">Connected</span>;
}
/** Jobs-permission detail only — configured/valid state is HfStatusBadge's job. */
function HfJobsBadge({ settings }: { settings: HfSettings }) {
if (!settings.configured || !settings.valid) return null;
if (settings.jobsWrite === true) return <span className="badge ok">jobs: write OK</span>;
if (settings.jobsWrite === true) return <span className="badge ok">Jobs: write OK</span>;
if (settings.jobsWrite === false)
return <span className="badge err">no job.write permission</span>;
return <span className="badge">jobs permission unknown</span>;
return <span className="badge err">No job.write permission</span>;
return <span className="badge">Jobs permission unknown</span>;
}
function HfSection() {
@@ -1247,7 +1247,7 @@ function HfSection() {
<span className="v">{settings.maskedToken ?? "—"}</span>
<span className="k">Source</span>
<span className="v">
{settings.source ? SOURCE_LABELS[settings.source] : "not configured"}
{settings.source ? SOURCE_LABELS[settings.source] : "Not configured"}
</span>
<span className="k">Jobs</span>
<span className="v">
@@ -1371,7 +1371,7 @@ function EnvRow({
{entry ? (
<>
{entry.maskedValue}
{entry.inProcessEnv && <span className="badge">overridden by env</span>}
{entry.inProcessEnv && <span className="badge">Overridden by env</span>}
</>
) : (
<input
@@ -1860,7 +1860,7 @@ function StorageTab() {
<div className="settings-card-head">
<h3>Data directory</h3>
<div className="spacer" style={{ flex: 1 }} />
<span className="badge">{settings.isDefault ? "default" : "custom"}</span>
<span className="badge">{settings.isDefault ? "Default" : "Custom"}</span>
</div>
<div className="kv">
<span className="k">Current</span>
@@ -2016,7 +2016,7 @@ function InstancesTable({ instances, emptyLabel }: { instances: Instance[]; empt
<StatusBadge status={inst.status} />
</td>
<td>{timeAgo(inst.createdAt)}</td>
<td className="mono">{runtimeLabel(inst)}</td>
<td>{runtimeLabel(inst)}</td>
</tr>
);
})}
+6 -2
View File
@@ -1,4 +1,4 @@
// Mirror of openresearch.sh StatusBadge: mono uppercase label + colored dot,
// Mirror of openresearch.sh StatusBadge: sentence-case label + colored dot,
// live statuses pulse. STATUS_STYLES is the single source of truth for status
// coloring across the table, graph and drawer.
@@ -21,12 +21,16 @@ export function statusStyle(status: string): StatusStyle {
return STATUS_STYLES[status] ?? STATUS_STYLES.idle;
}
function sentenceCase(s: string): string {
return s.charAt(0).toUpperCase() + s.slice(1);
}
export function StatusBadge({ status, label }: { status: string; label?: string }) {
const style = statusStyle(status);
return (
<span className={`status-badge ${style.className}${style.live ? " live" : ""}`}>
<span className="dot" />
{label ?? status}
{label ?? sentenceCase(status)}
</span>
);
}
+2 -2
View File
@@ -75,7 +75,7 @@ const ExpNode = memo(function ExpNode({ data }: NodeProps<ExpFlowNode>) {
const { exp, latestRun, runs, isBaseline, githubOwner, githubRepo, onOpenView, onOpenCodeBranch } = data;
const status = latestRun?.status;
const live = status === "running" || status === "starting";
const kind = isBaseline ? "BASELINE" : live ? "RUNNING" : "EXPERIMENT";
const kind = isBaseline ? "Baseline" : live ? "Running" : "Experiment";
const squares = runs.slice(-MAX_SQUARES);
return (
<div className={`exp-node ${live ? "live" : ""}`}>
@@ -91,7 +91,7 @@ const ExpNode = memo(function ExpNode({ data }: NodeProps<ExpFlowNode>) {
<div className="node-title">{exp.title || exp.description}</div>
)}
<div className="node-meta">
<span>RUNS</span>
<span>Runs</span>
{squares.length > 0 ? (
<span className="run-squares">
{squares.map((run) => (
+16
View File
@@ -0,0 +1,16 @@
// OpenResearch wordmark: logo mark + name, sized by the parent's font-size.
// Single source for the brand lockup (home, chat empty state, onboarding).
export function Wordmark() {
return (
<span className="wordmark">
<svg viewBox="0 0 100 100" aria-hidden="true">
<rect width="100" height="100" rx="8" fill="#9a2036" />
<path
d="M15.375 16.782v63.843a4 4 0 0 0 4 4h63.843c3.564 0 5.348-4.309 2.829-6.828L22.203 13.953c-2.52-2.52-6.828-.735-6.828 2.829"
fill="#fff"
/>
</svg>
OpenResearch
</span>
);
}
+386 -441
View File
File diff suppressed because it is too large Load Diff