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
17 changes: 4 additions & 13 deletions packages/app/src/components/session/session-new-view.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ import { amicodeGet } from "@/utils/amicode-fetch"
import { parseProblemsResponse } from "@opencode-ai/ui/amicode-problem-switcher"
import { Icon } from "@opencode-ai/ui/icon"
import { AmicodeGettingStarted } from "@opencode-ai/ui/amicode-getting-started"
import { MarkDetailed } from "@opencode-ai/ui/logo"
import { Logo, MarkDetailed } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"

const MAIN_WORKTREE = "main"
Expand DownExpand Up@@ -79,18 +79,9 @@ export function NewSessionView(props: NewSessionViewProps) {
<div class="w-full max-w-200 flex flex-col items-center text-center gap-4">
<div class="flex flex-col items-center gap-6">
<MarkDetailed class="w-28" />
{/* amicode: straight wordmark (the Logo's Racing Sans One face reads as
italic); byline keeps the brand attribution. */}
<div
style={{
"font-size": "34px",
"font-weight": "700",
"letter-spacing": "0.1em",
color: "var(--v2-text-text-base)",
}}
>
AMICODE
</div>
{/* amicode: brand wordmark in the brand typeface (Racing Sans One — restored per review);
MarkDetailed is the redesign's detailed mark (kept from trunk) */}
<Logo class="w-56 max-w-full" />
</div>
{/* amicode: getting-started block (tagline + how-it-works + starter chips) */}
<AmicodeGettingStarted
Expand Down
5 changes: 5 additions & 0 deletions packages/app/src/pages/home.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,7 @@ import { type ServerHealth } from "@/utils/server-health"
import { amicodeGet, amicodePost } from "@/utils/amicode-fetch"
import { AmicodeRunGallery } from "@opencode-ai/ui/amicode-run-gallery"
import { AmicodeOnboardingWizard, shouldShowWizard } from "@opencode-ai/ui/amicode-onboarding-wizard"
import { AmicodeSolverToggle } from "@opencode-ai/ui/amicode-solver-toggle"
import { parseRunCardsResponse } from "@opencode-ai/ui/amicode-run-card"
import { AmicodeHomeCards, parseProfileResponse, type HomeLiveRun } from "@opencode-ai/ui/amicode-home-cards"
import { parseRunSeriesResponse } from "@opencode-ai/ui/amicode-run-window"
Expand DownExpand Up@@ -696,6 +697,10 @@ function HomeDesign() {
</div>
{/* Cards: full width across, sized to content so they never scroll */}
<div class="relative z-[1] flex-none pt-1">
{/* solver mode (show-only v1, spec-20260709-093000): right-aligned above the cards */}
<div style={{ display: "flex", "justify-content": "flex-end", "margin-bottom": "8px" }}>
<AmicodeSolverToggle />
</div>
<AmicodeHomeCards
profile={profileView()}
onStart={startWithPrompt}
Expand Down
30 changes: 30 additions & 0 deletions packages/ui/src/amicode/solver-toggle.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
import { describe, expect, test } from "bun:test"
import { loadSolverMode, saveSolverMode } from "./solver-toggle"

const mem = () => {
const m = new Map<string, string>()
return { getItem: (k: string) => m.get(k) ?? null, setItem: (k: string, v: string) => void m.set(k, v) }
}

describe("solver mode persistence", () => {
test("defaults to piccolo; hp round-trips; garbage → piccolo", () => {
const s = mem()
expect(loadSolverMode(s)).toBe("piccolo")
saveSolverMode("hp", s)
expect(loadSolverMode(s)).toBe("hp")
s.setItem("amicode-solver-mode", "nonsense")
expect(loadSolverMode(s)).toBe("piccolo")
})
test("storage failures fail soft", () => {
const broken = {
getItem: () => {
throw new Error("nope")
},
setItem: () => {
throw new Error("nope")
},
}
expect(loadSolverMode(broken)).toBe("piccolo")
expect(() => saveSolverMode("hp", broken)).not.toThrow()
})
})
102 changes: 102 additions & 0 deletions packages/ui/src/amicode/solver-toggle.tsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
import { createSignal } from "solid-js"

// AMICODE: solver mode toggle — SHOW-ONLY v1 (spec-20260709-093000). Presents
// the future High-Performance path (Piccolissimo splines + Altissimo GPU
// solver on Harmoniqs cloud) as a selectable mode so demos can tell the story
// today. It deliberately changes NOTHING about solves: the future wiring is
// the scores schema's `executor: "cloud-altissimo" | local` seam, and the
// existing `issimo` entitlement tier becomes the subscription gate.

export type SolverMode = "piccolo" | "hp"
const KEY = "amicode-solver-mode"

export function loadSolverMode(storage: Pick<Storage, "getItem"> = localStorage): SolverMode {
try {
return storage.getItem(KEY) === "hp" ? "hp" : "piccolo"
} catch {
return "piccolo"
}
}

export function saveSolverMode(mode: SolverMode, storage: Pick<Storage, "setItem"> = localStorage): void {
try {
storage.setItem(KEY, mode)
} catch {
/* storage unavailable — selection just won't persist */
}
}

export function AmicodeSolverToggle() {
const [mode, setMode] = createSignal<SolverMode>(loadSolverMode())
const pick = (m: SolverMode) => {
setMode(m)
saveSolverMode(m)
}
const seg = (active: boolean): Record<string, string> => ({
display: "inline-flex",
"align-items": "center",
gap: "6px",
padding: "5px 12px",
"font-size": "12px",
"font-weight": active ? "650" : "450",
border: "none",
cursor: "pointer",
background: active ? "color-mix(in srgb, var(--v2-icon-icon-accent) 14%, transparent)" : "transparent",
color: active ? "var(--v2-text-text-base)" : "var(--v2-text-text-muted)",
})
return (
<div
data-component="amicode-solver-toggle"
title="Preview — GPU cloud solver rolling out"
style={{ display: "flex", "align-items": "center", gap: "10px" }}
>
<span
style={{
"font-size": "10px",
"font-weight": "700",
"letter-spacing": "0.08em",
"text-transform": "uppercase",
color: "var(--v2-text-text-faint)",
}}
>
Solver
</span>
<div
style={{
display: "inline-flex",
border: mode() === "hp" ? "1px solid var(--v2-icon-icon-accent)" : "1px solid var(--v2-border-border-base)",
"border-radius": "8px",
overflow: "hidden",
transition: "border-color 0.15s ease",
}}
>
<button
type="button"
data-slot="amicode-solver-piccolo"
style={seg(mode() === "piccolo")}
onClick={() => pick("piccolo")}
>
Piccolo
</button>
<button type="button" data-slot="amicode-solver-hp" style={seg(mode() === "hp")} onClick={() => pick("hp")}>
<span aria-hidden="true">⚡</span>
Piccolissimo + Altissimo
<span
style={{
"font-size": "9px",
"font-weight": "750",
"letter-spacing": "0.06em",
padding: "1px 5px",
"border-radius": "4px",
background: "var(--v2-icon-icon-accent)",
color: "var(--v2-background-bg-base, #000)",
}}
>
PRO
</span>
</button>
</div>
<span style={{ "font-size": "11px", color: "var(--v2-text-text-faint)" }}>High Performance Solver</span>
</div>
)
}
2 changes: 2 additions & 0 deletions packages/ui/src/components/amicode-solver-toggle.tsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
// AMICODE: re-export shim (wildcard export path) — logic in ../amicode/solver-toggle.tsx.
export { AmicodeSolverToggle, loadSolverMode, saveSolverMode, type SolverMode } from "../amicode/solver-toggle"
5 changes: 2 additions & 3 deletions packages/ui/src/components/logo.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,9 +109,8 @@ export const Logo = (props: { class?: string }) => {
dominant-baseline="central"
textLength="230"
lengthAdjust="spacingAndGlyphs"
font-family="var(--font-family-sans, ui-sans-serif, system-ui, -apple-system, sans-serif)"
font-weight="750"
letter-spacing="4"
font-family="'Racing Sans One', var(--font-family-mono, ui-monospace, monospace)"
font-weight="400"
font-size="36"
fill="var(--icon-base)"
>
Expand Down
Loading