mirror of
https://github.com/alphaXiv/OpenResearch.git
synced 2026-11-11 09:59:34 +08:00
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:
Generated
+1
-1
@@ -928,7 +928,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "openresearch-cli"
|
||||
version = "0.1.67"
|
||||
version = "0.1.68"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
Vendored
-32
File diff suppressed because one or more lines are too long
+107
-107
File diff suppressed because one or more lines are too long
Vendored
+32
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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). */
|
||||
|
||||
@@ -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 & 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" />}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user