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
13 changes: 13 additions & 0 deletions AMICODE-PATCHES.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -245,3 +245,16 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build.
reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=<free>`
if something else (e.g. the harmoniqs website dev server) holds 3000.
- Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail.

15. (mark drift fix, synced to amicode PR #99 final) — amicode: consolidated the fork's brand mark to ONE geometry, matching amicode's redesigned mark. The "kept in sync manually" cross-repo promise from patch #8 had already silently failed.

- Trigger: amicode's mark was redesigned (PR #99) without a corresponding update here — the fork still rendered the OLD "digi" pixel-accented H-robot everywhere. PR #99 went through several iterations before landing on its final geometry; this entry tracks that FINAL state (square viewBox `0 0 3600 3600`), not the intermediate "hackathon mark" (viewBox `116 287 3377 3035`) an earlier draft of this fork PR had copied — that intermediate geometry is now itself stale and was replaced here.
- Two copies of near-identical geometry lived in THIS repo (logo.tsx's `Robot` used by `Mark`/`Splash`, and spinner.tsx's `AmicoSpinner`), plus a third in favicon/amico.svg. Consolidated to a single `MARK_PATH` exported from logo.tsx.
- Geometry now mirrors amicode PR #99's two authored SVGs (amicode:`packages/extension/media/amico{,_reduced}.svg`), both square `0 0 3600 3600`:
- `MARK_PATH` = amico_reduced.svg's outer-bracket path (fill-rule evenodd screen knockout). Used by `Mark`, `Splash`, `AmicoSpinner`, and mirrored as a literal in favicon/amico.svg — every SMALL context, matching amicode's own "small → reduced" rule.
- `MarkDetailed` = amico.svg's full detailed mark (bracket path + internal circuit-pattern rects/polygons). Used ONLY by the Meet Amico home card (`w-12`/48px), large enough for the detail to resolve.
- `Mark`/`Splash`/`MarkDetailed` viewBox `0 0 64 56`/`116 287 3377 3035` → `0 0 3600 3600`; logo.css aspect-ratio `8/7` → `1/1` (the mark is square now).
- Still theme-adaptive via currentColor + var(--icon-strong-base) — this is a live webview DOM, so currentColor resolves (unlike amicode's native VS Code tab icon, which needs committed {light,dark} files; see amicode PR #99).
- NOT a re-established cross-repo sync promise — `MARK_PATH` is the single source of truth WITHIN this repo; it happens to match amicode's current geometry, kept aligned by hand when the mark changes.
- Tests: ui `bun test src/amicode` green; typecheck green ui + app.
- NOT done this round: full native `bun run script/build.ts` compile + vendored-binary swap — this patch only touches the embedded web UI. Deferred to the next amicode.N release tag, same split as patches #8/#11.
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
import { Show, type JSX } from "solid-js"
import { Logo, Mark } from "@opencode-ai/ui/logo"
import { Logo, MarkDetailed } from "@opencode-ai/ui/logo"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"

// amicode: new-session start screen, top→bottom — H-bot mark (hero), the AMICODE
// wordmark, tagline + how-it-works + starter chips, then the composer. Sizing
// mirrors the classic NewSessionView (Mark w-36 / Logo w-72); keeps the session
// tabs and centered composer. The chips are the one-tap path to the next task.
// The hero uses the DETAILED mark (144px, well above where the accents resolve).
export function NewSessionDesignView(props: { children: JSX.Element; gettingStarted?: JSX.Element }) {
return (
<div data-component="session-new-design" class="relative size-full overflow-y-auto bg-v2-background-bg-deep">
<div class="absolute inset-x-0 top-[12%] flex justify-center px-6 pb-24">
<div class={`${NEW_SESSION_CONTENT_WIDTH} flex flex-col items-center`}>
<Mark class="w-36 h-auto mb-4" />
<MarkDetailed class="w-36 h-auto mb-4" />
<Logo class="w-72 max-w-full h-auto mb-6" />
<Show when={props.gettingStarted}>
<div class="w-full flex justify-center mb-6">{props.gettingStarted}</div>
Expand Down
4 changes: 2 additions & 2 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 { Mark } from "@opencode-ai/ui/logo"
import { MarkDetailed } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"

const MAIN_WORKTREE = "main"
Expand DownExpand Up@@ -78,7 +78,7 @@ export function NewSessionView(props: NewSessionViewProps) {
<div class="flex-1 px-6 pb-30 flex items-center justify-center text-center">
<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">
<Mark class="w-28" />
<MarkDetailed class="w-28" />
{/* amicode: straight wordmark (the Logo's Racing Sans One face reads as
italic); byline keeps the brand attribution. */}
<div
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/home-cards.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createEffect, createMemo, on, type JSX, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -244,7 +244,7 @@ function MeetAmicoCard(props: { onStart: (prompt: string) => void }) {
data-slot="amicode-meet-identity"
style={{ display: "flex", gap: "12px", "align-items": "center", "margin-top": "10px" }}
>
<Mark class="w-12 h-auto shrink-0" />
<MarkDetailed class="w-12 h-auto shrink-0" />
<div style={{ "min-width": "0" }}>
<div style={{ "font-size": "18px", "font-weight": "600", color: "var(--v2-text-text-base)" }}>Amico</div>
<div style={{ "font-size": "12px", "line-height": "16px", color: "var(--v2-text-text-muted)" }}>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/onboarding-wizard.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { Mark, MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -236,7 +236,7 @@ export function AmicodeOnboardingWizard(props: {
padding: "14px 0 4px",
}}
>
<Mark class="w-14 h-auto" />
<MarkDetailed class="w-14 h-auto" />
<div>
<div style={{ "font-size": "22px", "font-weight": "700", color: "var(--v2-text-text-base)" }}>
Welcome to Amicode
Expand Down
9 changes: 7 additions & 2 deletions packages/ui/src/amicode/run-card.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,12 @@
// trading card, not UI panel. Pure string-SVG so the same markup drives both
// the in-app gallery (innerHTML) and the PNG export (SVG → Image → canvas).

// The mark geometry lives in ONE place — logo.tsx's MARK_PATH (a plain string
// const). Importing it here keeps this card from drifting into a private copy
// of the glyph, which is what this consolidation fixes. The mark is square
// (viewBox 0 0 3600 3600).
import { MARK_PATH } from "../components/logo"

export type RunCardData = {
slug: string
problem: string
Expand DownExpand Up@@ -129,14 +135,13 @@ export function renderRunCardSvg(d: RunCardData): string {
}
}

const MARK = "M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
const mono = "ui-monospace, SFMono-Regular, Menlo, monospace"
const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"

return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<rect width="${W}" height="${H}" fill="${BG}"/>
<rect x="0" y="0" width="${W}" height="6" fill="${GOLD}"/>
<g transform="translate(80,64) scale(0.55)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK}"/></g>
<g transform="translate(80,64) scale(0.011)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK_PATH}"/></g>
<text x="130" y="88" font-family="${sans}" font-size="22" font-weight="700" letter-spacing="4" fill="${TEXT}">AMICODE</text>
<text x="262" y="88" font-family="${sans}" font-size="15" letter-spacing="3" fill="${MUTED}">SOLVED PULSE</text>

Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/amicode/spinner.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { type ComponentProps } from "solid-js"
import { MARK_PATH } from "../components/logo"

// AMICODE: working/thinking spinner — the Harmoniqs H-robot silhouette as a
// small monochrome glyph (H body with the screen slit knocked out via
Expand All@@ -10,6 +11,12 @@ import { type ComponentProps } from "solid-js"
// matches the stock spinner's own animation language. prefers-reduced-motion:
// static glyph, no animation (matchMedia guard; inline animations don't
// inherit the CSS-file media-query pattern used elsewhere).
//
// Path comes from logo.tsx's MARK_PATH (Mark/Splash now render the same
// glyph) instead of its own copy — this file used to carry an independent
// literal that happened to already match Mark/Splash's silhouette variant;
// consolidated so there's one geometry in this repo, not a second one that
// could quietly drift the way Mark/Splash's OWN old copy did.

const reducedMotion = () =>
typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
Expand All@@ -22,7 +29,7 @@ export function AmicoSpinner(props: {
return (
<svg
{...props}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
data-component="amico-spinner"
classList={{
...props.classList,
Expand All@@ -32,7 +39,7 @@ export function AmicoSpinner(props: {
>
<path
fill-rule="evenodd"
d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
d={MARK_PATH}
style={
reducedMotion()
? undefined
Expand Down
28 changes: 7 additions & 21 deletions packages/ui/src/assets/favicon/amico.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/ui/src/components/logo.css
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
[data-component="logo-mark"] {
width: 16px;
aspect-ratio: 8/7;
aspect-ratio: 1/1; /* mark viewBox is square (0 0 3600 3600) */
}
102 changes: 65 additions & 37 deletions packages/ui/src/components/logo.tsx
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
import { type ComponentProps } from "solid-js"

// AMICODE branding v2: Mark/Splash render the "digi" Harmoniqs H-robot
// (canonical source also lives at amicode:packages/extension/media/amico.svg,
// kept in sync manually); Logo renders the AMICODE wordmark. Component names,
// props, and data-component hooks are kept identical to stock. The robot body
// follows currentColor; the display rect + glyphs are fixed brand colors.
// viewBox is 64:56 (8:7, not square) — see logo.css aspect-ratio.
// AMICODE branding: Mark/Splash render the Harmoniqs H-robot mark; Logo
// renders the AMICODE wordmark. Component names, props, and data-component
// hooks are kept identical to stock. The mark is square (viewBox 0 0 3600
// 3600) — see logo.css aspect-ratio 1/1.
//
// Geometry mirrors amicode PR #99's two authored SVGs (amicode:
// packages/extension/media/amico{,_reduced}.svg), both square 0 0 3600 3600:
// MARK_PATH → amico_reduced.svg — the outer bracket (fill-rule evenodd
// knocks out the screen). The SMALL-size mark: Mark, Splash,
// AmicoSpinner (../amicode/spinner.tsx, imports MARK_PATH),
// and favicon/amico.svg (mirrors it as a literal — a static
// SVG can't import a TS module; keep its <path d>
// byte-identical to this constant).
// MarkDetailed → amico.svg — the full mark WITH the internal circuit-pattern
// accents, for LARGE contexts only (the Meet Amico card).
// This matches amicode's own "small → reduced, large → detailed" split, so
// the fork and the extension render the same brand mark. MARK_PATH is the
// single source of truth for the glyph within this repo; it is kept aligned
// with amicode's geometry by hand when the mark changes (no build-time link).
export const MARK_PATH =
"M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z"

const Robot = () => (
<>
<path fill="currentColor" d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z" />
<rect x="9" y="19" width="46" height="18" fill="#0A0A0A" />
<g fill="#FFF676">
<rect x="17" y="21" width="2" height="2" />
<rect x="15" y="23" width="2" height="2" />
<rect x="13" y="25" width="2" height="2" />
<rect x="15" y="27" width="2" height="2" />
<rect x="17" y="29" width="2" height="2" />
<rect x="21" y="21" width="6" height="2" />
<rect x="21" y="23" width="2" height="6" />
<rect x="25" y="23" width="2" height="6" />
<rect x="21" y="29" width="6" height="2" />
<rect x="29" y="21" width="2" height="10" />
<rect x="33" y="21" width="2" height="10" />
<rect x="37" y="21" width="6" height="2" />
<rect x="37" y="23" width="2" height="6" />
<rect x="41" y="23" width="2" height="6" />
<rect x="37" y="29" width="6" height="2" />
<rect x="45" y="21" width="2" height="2" />
<rect x="47" y="23" width="2" height="2" />
<rect x="49" y="25" width="2" height="2" />
<rect x="47" y="27" width="2" height="2" />
<rect x="45" y="29" width="2" height="2" />
<polygon points="28,33 36,33 34,36 30,36" />
</g>
</>
)
const Robot = () => <path fill="currentColor" fill-rule="evenodd" d={MARK_PATH} />

export const Mark = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -57,7 +44,7 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
ref={props.ref}
data-component="logo-splash"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -66,6 +53,47 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
)
}

// Detailed mark (amicode PR #99's amico.svg) — the full H-robot WITH the
// internal circuit-pattern accents, used ONLY where it renders large enough to
// resolve (the Meet Amico card, ~48px). currentColor + var(--icon-strong-base),
// same convention as Mark/Splash, so it stays theme-adaptive here — unlike
// amicode's native VS Code chat-tab icon, which needs committed light/dark SVG
// files because a native tab icon has no live CSS context for currentColor.
// Geometry mirrors amicode:packages/extension/media/amico.svg (viewBox
// 0 0 3600 3600); keep in sync by hand if that mark changes.
export const MarkDetailed = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark-detailed"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
fill="currentColor"
>
<path d="M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z" />
<rect x="1778.31" y="1312.43" width="107.11" height="692.38" />
<polygon points="2769.41 1463.57 2903.01 1463.57 2903.01 1601.01 2769.39 1601.01 2769.39 1463.6 2635.79 1463.6 2635.79 1326.16 2769.41 1326.16 2769.41 1463.57" />
<polygon points="3036.63 1738.45 2903.03 1738.45 2903.03 1875.89 2769.41 1875.89 2769.41 1738.45 2903.01 1738.45 2903.01 1601.01 3036.63 1601.01 3036.63 1738.45" />
<polygon points="2903.02 1875.89 2769.43 1875.89 2769.43 2013.33 2635.81 2013.33 2635.81 1875.89 2769.4 1875.89 2769.4 1738.45 2903.02 1738.45 2903.02 1875.89" />
<rect x="2373.03" y="1451.19" width="133.62" height="423.84" transform="translate(4879.6781 3326.2281) rotate(-180)" />
<rect x="2009.75" y="1451.19" width="133.62" height="423.84" transform="translate(4153.1112 3326.2281) rotate(-180)" />
<rect x="2143.56" y="1313.76" width="229.47" height="137.44" transform="translate(4516.5887 2764.9517) rotate(-180)" />
<rect x="2143.56" y="1875.03" width="229.47" height="137.44" transform="translate(4516.5887 3887.5046) rotate(-180)" />
<rect x="1503.05" y="1446.71" width="133.62" height="423.84" transform="translate(3139.725 3317.2494) rotate(-180)" />
<rect x="1139.77" y="1446.71" width="133.62" height="423.84" transform="translate(2413.1581 3317.2494) rotate(-180)" />
<rect x="1273.58" y="1309.27" width="229.47" height="137.44" transform="translate(2776.6357 2755.9729) rotate(-180)" />
<rect x="1273.58" y="1870.54" width="229.47" height="137.44" transform="translate(2776.6357 3878.5258) rotate(-180)" />
<polygon points="888.52 1864.8 754.93 1864.8 754.93 1727.36 888.55 1727.36 888.55 1864.77 1022.15 1864.77 1022.15 2002.21 888.52 2002.21 888.52 1864.8" />
<polygon points="621.31 1589.92 754.9 1589.92 754.9 1452.48 888.52 1452.48 888.52 1589.92 754.93 1589.92 754.93 1727.36 621.31 1727.36 621.31 1589.92" />
<polygon points="754.92 1452.48 888.51 1452.48 888.51 1315.04 1022.13 1315.04 1022.13 1452.48 888.54 1452.48 888.54 1589.92 754.92 1589.92 754.92 1452.48" />
<rect x="1648.65" y="2256.8" width="349.19" height="137.44" transform="translate(3646.502 4651.0383) rotate(-180)" />
<rect x="1510.91" y="2119.73" width="138.82" height="138.82" />
<rect x="1997.85" y="2117.98" width="138.82" height="138.82" />
</svg>
)
}

export const Logo = (props: { class?: string }) => {
return (
<svg
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
13 changes: 13 additions & 0 deletions AMICODE-PATCHES.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -245,3 +245,16 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build.
reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=<free>`
if something else (e.g. the harmoniqs website dev server) holds 3000.
- Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail.

15. (mark drift fix, synced to amicode PR #99 final) — amicode: consolidated the fork's brand mark to ONE geometry, matching amicode's redesigned mark. The "kept in sync manually" cross-repo promise from patch #8 had already silently failed.

- Trigger: amicode's mark was redesigned (PR #99) without a corresponding update here — the fork still rendered the OLD "digi" pixel-accented H-robot everywhere. PR #99 went through several iterations before landing on its final geometry; this entry tracks that FINAL state (square viewBox `0 0 3600 3600`), not the intermediate "hackathon mark" (viewBox `116 287 3377 3035`) an earlier draft of this fork PR had copied — that intermediate geometry is now itself stale and was replaced here.
- Two copies of near-identical geometry lived in THIS repo (logo.tsx's `Robot` used by `Mark`/`Splash`, and spinner.tsx's `AmicoSpinner`), plus a third in favicon/amico.svg. Consolidated to a single `MARK_PATH` exported from logo.tsx.
- Geometry now mirrors amicode PR #99's two authored SVGs (amicode:`packages/extension/media/amico{,_reduced}.svg`), both square `0 0 3600 3600`:
- `MARK_PATH` = amico_reduced.svg's outer-bracket path (fill-rule evenodd screen knockout). Used by `Mark`, `Splash`, `AmicoSpinner`, and mirrored as a literal in favicon/amico.svg — every SMALL context, matching amicode's own "small → reduced" rule.
- `MarkDetailed` = amico.svg's full detailed mark (bracket path + internal circuit-pattern rects/polygons). Used ONLY by the Meet Amico home card (`w-12`/48px), large enough for the detail to resolve.
- `Mark`/`Splash`/`MarkDetailed` viewBox `0 0 64 56`/`116 287 3377 3035` → `0 0 3600 3600`; logo.css aspect-ratio `8/7` → `1/1` (the mark is square now).
- Still theme-adaptive via currentColor + var(--icon-strong-base) — this is a live webview DOM, so currentColor resolves (unlike amicode's native VS Code tab icon, which needs committed {light,dark} files; see amicode PR #99).
- NOT a re-established cross-repo sync promise — `MARK_PATH` is the single source of truth WITHIN this repo; it happens to match amicode's current geometry, kept aligned by hand when the mark changes.
- Tests: ui `bun test src/amicode` green; typecheck green ui + app.
- NOT done this round: full native `bun run script/build.ts` compile + vendored-binary swap — this patch only touches the embedded web UI. Deferred to the next amicode.N release tag, same split as patches #8/#11.
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
import { Show, type JSX } from "solid-js"
import { Logo, Mark } from "@opencode-ai/ui/logo"
import { Logo, MarkDetailed } from "@opencode-ai/ui/logo"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"

// amicode: new-session start screen, top→bottom — H-bot mark (hero), the AMICODE
// wordmark, tagline + how-it-works + starter chips, then the composer. Sizing
// mirrors the classic NewSessionView (Mark w-36 / Logo w-72); keeps the session
// tabs and centered composer. The chips are the one-tap path to the next task.
// The hero uses the DETAILED mark (144px, well above where the accents resolve).
export function NewSessionDesignView(props: { children: JSX.Element; gettingStarted?: JSX.Element }) {
return (
<div data-component="session-new-design" class="relative size-full overflow-y-auto bg-v2-background-bg-deep">
<div class="absolute inset-x-0 top-[12%] flex justify-center px-6 pb-24">
<div class={`${NEW_SESSION_CONTENT_WIDTH} flex flex-col items-center`}>
<Mark class="w-36 h-auto mb-4" />
<MarkDetailed class="w-36 h-auto mb-4" />
<Logo class="w-72 max-w-full h-auto mb-6" />
<Show when={props.gettingStarted}>
<div class="w-full flex justify-center mb-6">{props.gettingStarted}</div>
Expand Down
4 changes: 2 additions & 2 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 { Mark } from "@opencode-ai/ui/logo"
import { MarkDetailed } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"

const MAIN_WORKTREE = "main"
Expand DownExpand Up@@ -78,7 +78,7 @@ export function NewSessionView(props: NewSessionViewProps) {
<div class="flex-1 px-6 pb-30 flex items-center justify-center text-center">
<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">
<Mark class="w-28" />
<MarkDetailed class="w-28" />
{/* amicode: straight wordmark (the Logo's Racing Sans One face reads as
italic); byline keeps the brand attribution. */}
<div
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/home-cards.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createEffect, createMemo, on, type JSX, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -244,7 +244,7 @@ function MeetAmicoCard(props: { onStart: (prompt: string) => void }) {
data-slot="amicode-meet-identity"
style={{ display: "flex", gap: "12px", "align-items": "center", "margin-top": "10px" }}
>
<Mark class="w-12 h-auto shrink-0" />
<MarkDetailed class="w-12 h-auto shrink-0" />
<div style={{ "min-width": "0" }}>
<div style={{ "font-size": "18px", "font-weight": "600", color: "var(--v2-text-text-base)" }}>Amico</div>
<div style={{ "font-size": "12px", "line-height": "16px", color: "var(--v2-text-text-muted)" }}>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/onboarding-wizard.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { Mark, MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -236,7 +236,7 @@ export function AmicodeOnboardingWizard(props: {
padding: "14px 0 4px",
}}
>
<Mark class="w-14 h-auto" />
<MarkDetailed class="w-14 h-auto" />
<div>
<div style={{ "font-size": "22px", "font-weight": "700", color: "var(--v2-text-text-base)" }}>
Welcome to Amicode
Expand Down
9 changes: 7 additions & 2 deletions packages/ui/src/amicode/run-card.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,12 @@
// trading card, not UI panel. Pure string-SVG so the same markup drives both
// the in-app gallery (innerHTML) and the PNG export (SVG → Image → canvas).

// The mark geometry lives in ONE place — logo.tsx's MARK_PATH (a plain string
// const). Importing it here keeps this card from drifting into a private copy
// of the glyph, which is what this consolidation fixes. The mark is square
// (viewBox 0 0 3600 3600).
import { MARK_PATH } from "../components/logo"

export type RunCardData = {
slug: string
problem: string
Expand DownExpand Up@@ -129,14 +135,13 @@ export function renderRunCardSvg(d: RunCardData): string {
}
}

const MARK = "M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
const mono = "ui-monospace, SFMono-Regular, Menlo, monospace"
const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"

return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<rect width="${W}" height="${H}" fill="${BG}"/>
<rect x="0" y="0" width="${W}" height="6" fill="${GOLD}"/>
<g transform="translate(80,64) scale(0.55)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK}"/></g>
<g transform="translate(80,64) scale(0.011)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK_PATH}"/></g>
<text x="130" y="88" font-family="${sans}" font-size="22" font-weight="700" letter-spacing="4" fill="${TEXT}">AMICODE</text>
<text x="262" y="88" font-family="${sans}" font-size="15" letter-spacing="3" fill="${MUTED}">SOLVED PULSE</text>

Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/amicode/spinner.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { type ComponentProps } from "solid-js"
import { MARK_PATH } from "../components/logo"

// AMICODE: working/thinking spinner — the Harmoniqs H-robot silhouette as a
// small monochrome glyph (H body with the screen slit knocked out via
Expand All@@ -10,6 +11,12 @@ import { type ComponentProps } from "solid-js"
// matches the stock spinner's own animation language. prefers-reduced-motion:
// static glyph, no animation (matchMedia guard; inline animations don't
// inherit the CSS-file media-query pattern used elsewhere).
//
// Path comes from logo.tsx's MARK_PATH (Mark/Splash now render the same
// glyph) instead of its own copy — this file used to carry an independent
// literal that happened to already match Mark/Splash's silhouette variant;
// consolidated so there's one geometry in this repo, not a second one that
// could quietly drift the way Mark/Splash's OWN old copy did.

const reducedMotion = () =>
typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
Expand All@@ -22,7 +29,7 @@ export function AmicoSpinner(props: {
return (
<svg
{...props}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
data-component="amico-spinner"
classList={{
...props.classList,
Expand All@@ -32,7 +39,7 @@ export function AmicoSpinner(props: {
>
<path
fill-rule="evenodd"
d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
d={MARK_PATH}
style={
reducedMotion()
? undefined
Expand Down
28 changes: 7 additions & 21 deletions packages/ui/src/assets/favicon/amico.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/ui/src/components/logo.css
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
[data-component="logo-mark"] {
width: 16px;
aspect-ratio: 8/7;
aspect-ratio: 1/1; /* mark viewBox is square (0 0 3600 3600) */
}
102 changes: 65 additions & 37 deletions packages/ui/src/components/logo.tsx
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
import { type ComponentProps } from "solid-js"

// AMICODE branding v2: Mark/Splash render the "digi" Harmoniqs H-robot
// (canonical source also lives at amicode:packages/extension/media/amico.svg,
// kept in sync manually); Logo renders the AMICODE wordmark. Component names,
// props, and data-component hooks are kept identical to stock. The robot body
// follows currentColor; the display rect + glyphs are fixed brand colors.
// viewBox is 64:56 (8:7, not square) — see logo.css aspect-ratio.
// AMICODE branding: Mark/Splash render the Harmoniqs H-robot mark; Logo
// renders the AMICODE wordmark. Component names, props, and data-component
// hooks are kept identical to stock. The mark is square (viewBox 0 0 3600
// 3600) — see logo.css aspect-ratio 1/1.
//
// Geometry mirrors amicode PR #99's two authored SVGs (amicode:
// packages/extension/media/amico{,_reduced}.svg), both square 0 0 3600 3600:
// MARK_PATH → amico_reduced.svg — the outer bracket (fill-rule evenodd
// knocks out the screen). The SMALL-size mark: Mark, Splash,
// AmicoSpinner (../amicode/spinner.tsx, imports MARK_PATH),
// and favicon/amico.svg (mirrors it as a literal — a static
// SVG can't import a TS module; keep its <path d>
// byte-identical to this constant).
// MarkDetailed → amico.svg — the full mark WITH the internal circuit-pattern
// accents, for LARGE contexts only (the Meet Amico card).
// This matches amicode's own "small → reduced, large → detailed" split, so
// the fork and the extension render the same brand mark. MARK_PATH is the
// single source of truth for the glyph within this repo; it is kept aligned
// with amicode's geometry by hand when the mark changes (no build-time link).
export const MARK_PATH =
"M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z"

const Robot = () => (
<>
<path fill="currentColor" d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z" />
<rect x="9" y="19" width="46" height="18" fill="#0A0A0A" />
<g fill="#FFF676">
<rect x="17" y="21" width="2" height="2" />
<rect x="15" y="23" width="2" height="2" />
<rect x="13" y="25" width="2" height="2" />
<rect x="15" y="27" width="2" height="2" />
<rect x="17" y="29" width="2" height="2" />
<rect x="21" y="21" width="6" height="2" />
<rect x="21" y="23" width="2" height="6" />
<rect x="25" y="23" width="2" height="6" />
<rect x="21" y="29" width="6" height="2" />
<rect x="29" y="21" width="2" height="10" />
<rect x="33" y="21" width="2" height="10" />
<rect x="37" y="21" width="6" height="2" />
<rect x="37" y="23" width="2" height="6" />
<rect x="41" y="23" width="2" height="6" />
<rect x="37" y="29" width="6" height="2" />
<rect x="45" y="21" width="2" height="2" />
<rect x="47" y="23" width="2" height="2" />
<rect x="49" y="25" width="2" height="2" />
<rect x="47" y="27" width="2" height="2" />
<rect x="45" y="29" width="2" height="2" />
<polygon points="28,33 36,33 34,36 30,36" />
</g>
</>
)
const Robot = () => <path fill="currentColor" fill-rule="evenodd" d={MARK_PATH} />

export const Mark = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -57,7 +44,7 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
ref={props.ref}
data-component="logo-splash"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -66,6 +53,47 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
)
}

// Detailed mark (amicode PR #99's amico.svg) — the full H-robot WITH the
// internal circuit-pattern accents, used ONLY where it renders large enough to
// resolve (the Meet Amico card, ~48px). currentColor + var(--icon-strong-base),
// same convention as Mark/Splash, so it stays theme-adaptive here — unlike
// amicode's native VS Code chat-tab icon, which needs committed light/dark SVG
// files because a native tab icon has no live CSS context for currentColor.
// Geometry mirrors amicode:packages/extension/media/amico.svg (viewBox
// 0 0 3600 3600); keep in sync by hand if that mark changes.
export const MarkDetailed = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark-detailed"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
fill="currentColor"
>
<path d="M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z" />
<rect x="1778.31" y="1312.43" width="107.11" height="692.38" />
<polygon points="2769.41 1463.57 2903.01 1463.57 2903.01 1601.01 2769.39 1601.01 2769.39 1463.6 2635.79 1463.6 2635.79 1326.16 2769.41 1326.16 2769.41 1463.57" />
<polygon points="3036.63 1738.45 2903.03 1738.45 2903.03 1875.89 2769.41 1875.89 2769.41 1738.45 2903.01 1738.45 2903.01 1601.01 3036.63 1601.01 3036.63 1738.45" />
<polygon points="2903.02 1875.89 2769.43 1875.89 2769.43 2013.33 2635.81 2013.33 2635.81 1875.89 2769.4 1875.89 2769.4 1738.45 2903.02 1738.45 2903.02 1875.89" />
<rect x="2373.03" y="1451.19" width="133.62" height="423.84" transform="translate(4879.6781 3326.2281) rotate(-180)" />
<rect x="2009.75" y="1451.19" width="133.62" height="423.84" transform="translate(4153.1112 3326.2281) rotate(-180)" />
<rect x="2143.56" y="1313.76" width="229.47" height="137.44" transform="translate(4516.5887 2764.9517) rotate(-180)" />
<rect x="2143.56" y="1875.03" width="229.47" height="137.44" transform="translate(4516.5887 3887.5046) rotate(-180)" />
<rect x="1503.05" y="1446.71" width="133.62" height="423.84" transform="translate(3139.725 3317.2494) rotate(-180)" />
<rect x="1139.77" y="1446.71" width="133.62" height="423.84" transform="translate(2413.1581 3317.2494) rotate(-180)" />
<rect x="1273.58" y="1309.27" width="229.47" height="137.44" transform="translate(2776.6357 2755.9729) rotate(-180)" />
<rect x="1273.58" y="1870.54" width="229.47" height="137.44" transform="translate(2776.6357 3878.5258) rotate(-180)" />
<polygon points="888.52 1864.8 754.93 1864.8 754.93 1727.36 888.55 1727.36 888.55 1864.77 1022.15 1864.77 1022.15 2002.21 888.52 2002.21 888.52 1864.8" />
<polygon points="621.31 1589.92 754.9 1589.92 754.9 1452.48 888.52 1452.48 888.52 1589.92 754.93 1589.92 754.93 1727.36 621.31 1727.36 621.31 1589.92" />
<polygon points="754.92 1452.48 888.51 1452.48 888.51 1315.04 1022.13 1315.04 1022.13 1452.48 888.54 1452.48 888.54 1589.92 754.92 1589.92 754.92 1452.48" />
<rect x="1648.65" y="2256.8" width="349.19" height="137.44" transform="translate(3646.502 4651.0383) rotate(-180)" />
<rect x="1510.91" y="2119.73" width="138.82" height="138.82" />
<rect x="1997.85" y="2117.98" width="138.82" height="138.82" />
</svg>
)
}

export const Logo = (props: { class?: string }) => {
return (
<svg
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
13 changes: 13 additions & 0 deletions AMICODE-PATCHES.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -245,3 +245,16 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build.
reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=<free>`
if something else (e.g. the harmoniqs website dev server) holds 3000.
- Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail.

15. (mark drift fix, synced to amicode PR #99 final) — amicode: consolidated the fork's brand mark to ONE geometry, matching amicode's redesigned mark. The "kept in sync manually" cross-repo promise from patch #8 had already silently failed.

- Trigger: amicode's mark was redesigned (PR #99) without a corresponding update here — the fork still rendered the OLD "digi" pixel-accented H-robot everywhere. PR #99 went through several iterations before landing on its final geometry; this entry tracks that FINAL state (square viewBox `0 0 3600 3600`), not the intermediate "hackathon mark" (viewBox `116 287 3377 3035`) an earlier draft of this fork PR had copied — that intermediate geometry is now itself stale and was replaced here.
- Two copies of near-identical geometry lived in THIS repo (logo.tsx's `Robot` used by `Mark`/`Splash`, and spinner.tsx's `AmicoSpinner`), plus a third in favicon/amico.svg. Consolidated to a single `MARK_PATH` exported from logo.tsx.
- Geometry now mirrors amicode PR #99's two authored SVGs (amicode:`packages/extension/media/amico{,_reduced}.svg`), both square `0 0 3600 3600`:
- `MARK_PATH` = amico_reduced.svg's outer-bracket path (fill-rule evenodd screen knockout). Used by `Mark`, `Splash`, `AmicoSpinner`, and mirrored as a literal in favicon/amico.svg — every SMALL context, matching amicode's own "small → reduced" rule.
- `MarkDetailed` = amico.svg's full detailed mark (bracket path + internal circuit-pattern rects/polygons). Used ONLY by the Meet Amico home card (`w-12`/48px), large enough for the detail to resolve.
- `Mark`/`Splash`/`MarkDetailed` viewBox `0 0 64 56`/`116 287 3377 3035` → `0 0 3600 3600`; logo.css aspect-ratio `8/7` → `1/1` (the mark is square now).
- Still theme-adaptive via currentColor + var(--icon-strong-base) — this is a live webview DOM, so currentColor resolves (unlike amicode's native VS Code tab icon, which needs committed {light,dark} files; see amicode PR #99).
- NOT a re-established cross-repo sync promise — `MARK_PATH` is the single source of truth WITHIN this repo; it happens to match amicode's current geometry, kept aligned by hand when the mark changes.
- Tests: ui `bun test src/amicode` green; typecheck green ui + app.
- NOT done this round: full native `bun run script/build.ts` compile + vendored-binary swap — this patch only touches the embedded web UI. Deferred to the next amicode.N release tag, same split as patches #8/#11.
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
import { Show, type JSX } from "solid-js"
import { Logo, Mark } from "@opencode-ai/ui/logo"
import { Logo, MarkDetailed } from "@opencode-ai/ui/logo"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"

// amicode: new-session start screen, top→bottom — H-bot mark (hero), the AMICODE
// wordmark, tagline + how-it-works + starter chips, then the composer. Sizing
// mirrors the classic NewSessionView (Mark w-36 / Logo w-72); keeps the session
// tabs and centered composer. The chips are the one-tap path to the next task.
// The hero uses the DETAILED mark (144px, well above where the accents resolve).
export function NewSessionDesignView(props: { children: JSX.Element; gettingStarted?: JSX.Element }) {
return (
<div data-component="session-new-design" class="relative size-full overflow-y-auto bg-v2-background-bg-deep">
<div class="absolute inset-x-0 top-[12%] flex justify-center px-6 pb-24">
<div class={`${NEW_SESSION_CONTENT_WIDTH} flex flex-col items-center`}>
<Mark class="w-36 h-auto mb-4" />
<MarkDetailed class="w-36 h-auto mb-4" />
<Logo class="w-72 max-w-full h-auto mb-6" />
<Show when={props.gettingStarted}>
<div class="w-full flex justify-center mb-6">{props.gettingStarted}</div>
Expand Down
4 changes: 2 additions & 2 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 { Mark } from "@opencode-ai/ui/logo"
import { MarkDetailed } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"

const MAIN_WORKTREE = "main"
Expand DownExpand Up@@ -78,7 +78,7 @@ export function NewSessionView(props: NewSessionViewProps) {
<div class="flex-1 px-6 pb-30 flex items-center justify-center text-center">
<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">
<Mark class="w-28" />
<MarkDetailed class="w-28" />
{/* amicode: straight wordmark (the Logo's Racing Sans One face reads as
italic); byline keeps the brand attribution. */}
<div
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/home-cards.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createEffect, createMemo, on, type JSX, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -244,7 +244,7 @@ function MeetAmicoCard(props: { onStart: (prompt: string) => void }) {
data-slot="amicode-meet-identity"
style={{ display: "flex", gap: "12px", "align-items": "center", "margin-top": "10px" }}
>
<Mark class="w-12 h-auto shrink-0" />
<MarkDetailed class="w-12 h-auto shrink-0" />
<div style={{ "min-width": "0" }}>
<div style={{ "font-size": "18px", "font-weight": "600", color: "var(--v2-text-text-base)" }}>Amico</div>
<div style={{ "font-size": "12px", "line-height": "16px", color: "var(--v2-text-text-muted)" }}>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/onboarding-wizard.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { Mark, MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -236,7 +236,7 @@ export function AmicodeOnboardingWizard(props: {
padding: "14px 0 4px",
}}
>
<Mark class="w-14 h-auto" />
<MarkDetailed class="w-14 h-auto" />
<div>
<div style={{ "font-size": "22px", "font-weight": "700", color: "var(--v2-text-text-base)" }}>
Welcome to Amicode
Expand Down
9 changes: 7 additions & 2 deletions packages/ui/src/amicode/run-card.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,12 @@
// trading card, not UI panel. Pure string-SVG so the same markup drives both
// the in-app gallery (innerHTML) and the PNG export (SVG → Image → canvas).

// The mark geometry lives in ONE place — logo.tsx's MARK_PATH (a plain string
// const). Importing it here keeps this card from drifting into a private copy
// of the glyph, which is what this consolidation fixes. The mark is square
// (viewBox 0 0 3600 3600).
import { MARK_PATH } from "../components/logo"

export type RunCardData = {
slug: string
problem: string
Expand DownExpand Up@@ -129,14 +135,13 @@ export function renderRunCardSvg(d: RunCardData): string {
}
}

const MARK = "M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
const mono = "ui-monospace, SFMono-Regular, Menlo, monospace"
const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"

return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<rect width="${W}" height="${H}" fill="${BG}"/>
<rect x="0" y="0" width="${W}" height="6" fill="${GOLD}"/>
<g transform="translate(80,64) scale(0.55)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK}"/></g>
<g transform="translate(80,64) scale(0.011)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK_PATH}"/></g>
<text x="130" y="88" font-family="${sans}" font-size="22" font-weight="700" letter-spacing="4" fill="${TEXT}">AMICODE</text>
<text x="262" y="88" font-family="${sans}" font-size="15" letter-spacing="3" fill="${MUTED}">SOLVED PULSE</text>

Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/amicode/spinner.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { type ComponentProps } from "solid-js"
import { MARK_PATH } from "../components/logo"

// AMICODE: working/thinking spinner — the Harmoniqs H-robot silhouette as a
// small monochrome glyph (H body with the screen slit knocked out via
Expand All@@ -10,6 +11,12 @@ import { type ComponentProps } from "solid-js"
// matches the stock spinner's own animation language. prefers-reduced-motion:
// static glyph, no animation (matchMedia guard; inline animations don't
// inherit the CSS-file media-query pattern used elsewhere).
//
// Path comes from logo.tsx's MARK_PATH (Mark/Splash now render the same
// glyph) instead of its own copy — this file used to carry an independent
// literal that happened to already match Mark/Splash's silhouette variant;
// consolidated so there's one geometry in this repo, not a second one that
// could quietly drift the way Mark/Splash's OWN old copy did.

const reducedMotion = () =>
typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
Expand All@@ -22,7 +29,7 @@ export function AmicoSpinner(props: {
return (
<svg
{...props}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
data-component="amico-spinner"
classList={{
...props.classList,
Expand All@@ -32,7 +39,7 @@ export function AmicoSpinner(props: {
>
<path
fill-rule="evenodd"
d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
d={MARK_PATH}
style={
reducedMotion()
? undefined
Expand Down
28 changes: 7 additions & 21 deletions packages/ui/src/assets/favicon/amico.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/ui/src/components/logo.css
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
[data-component="logo-mark"] {
width: 16px;
aspect-ratio: 8/7;
aspect-ratio: 1/1; /* mark viewBox is square (0 0 3600 3600) */
}
102 changes: 65 additions & 37 deletions packages/ui/src/components/logo.tsx
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
import { type ComponentProps } from "solid-js"

// AMICODE branding v2: Mark/Splash render the "digi" Harmoniqs H-robot
// (canonical source also lives at amicode:packages/extension/media/amico.svg,
// kept in sync manually); Logo renders the AMICODE wordmark. Component names,
// props, and data-component hooks are kept identical to stock. The robot body
// follows currentColor; the display rect + glyphs are fixed brand colors.
// viewBox is 64:56 (8:7, not square) — see logo.css aspect-ratio.
// AMICODE branding: Mark/Splash render the Harmoniqs H-robot mark; Logo
// renders the AMICODE wordmark. Component names, props, and data-component
// hooks are kept identical to stock. The mark is square (viewBox 0 0 3600
// 3600) — see logo.css aspect-ratio 1/1.
//
// Geometry mirrors amicode PR #99's two authored SVGs (amicode:
// packages/extension/media/amico{,_reduced}.svg), both square 0 0 3600 3600:
// MARK_PATH → amico_reduced.svg — the outer bracket (fill-rule evenodd
// knocks out the screen). The SMALL-size mark: Mark, Splash,
// AmicoSpinner (../amicode/spinner.tsx, imports MARK_PATH),
// and favicon/amico.svg (mirrors it as a literal — a static
// SVG can't import a TS module; keep its <path d>
// byte-identical to this constant).
// MarkDetailed → amico.svg — the full mark WITH the internal circuit-pattern
// accents, for LARGE contexts only (the Meet Amico card).
// This matches amicode's own "small → reduced, large → detailed" split, so
// the fork and the extension render the same brand mark. MARK_PATH is the
// single source of truth for the glyph within this repo; it is kept aligned
// with amicode's geometry by hand when the mark changes (no build-time link).
export const MARK_PATH =
"M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z"

const Robot = () => (
<>
<path fill="currentColor" d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z" />
<rect x="9" y="19" width="46" height="18" fill="#0A0A0A" />
<g fill="#FFF676">
<rect x="17" y="21" width="2" height="2" />
<rect x="15" y="23" width="2" height="2" />
<rect x="13" y="25" width="2" height="2" />
<rect x="15" y="27" width="2" height="2" />
<rect x="17" y="29" width="2" height="2" />
<rect x="21" y="21" width="6" height="2" />
<rect x="21" y="23" width="2" height="6" />
<rect x="25" y="23" width="2" height="6" />
<rect x="21" y="29" width="6" height="2" />
<rect x="29" y="21" width="2" height="10" />
<rect x="33" y="21" width="2" height="10" />
<rect x="37" y="21" width="6" height="2" />
<rect x="37" y="23" width="2" height="6" />
<rect x="41" y="23" width="2" height="6" />
<rect x="37" y="29" width="6" height="2" />
<rect x="45" y="21" width="2" height="2" />
<rect x="47" y="23" width="2" height="2" />
<rect x="49" y="25" width="2" height="2" />
<rect x="47" y="27" width="2" height="2" />
<rect x="45" y="29" width="2" height="2" />
<polygon points="28,33 36,33 34,36 30,36" />
</g>
</>
)
const Robot = () => <path fill="currentColor" fill-rule="evenodd" d={MARK_PATH} />

export const Mark = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -57,7 +44,7 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
ref={props.ref}
data-component="logo-splash"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -66,6 +53,47 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
)
}

// Detailed mark (amicode PR #99's amico.svg) — the full H-robot WITH the
// internal circuit-pattern accents, used ONLY where it renders large enough to
// resolve (the Meet Amico card, ~48px). currentColor + var(--icon-strong-base),
// same convention as Mark/Splash, so it stays theme-adaptive here — unlike
// amicode's native VS Code chat-tab icon, which needs committed light/dark SVG
// files because a native tab icon has no live CSS context for currentColor.
// Geometry mirrors amicode:packages/extension/media/amico.svg (viewBox
// 0 0 3600 3600); keep in sync by hand if that mark changes.
export const MarkDetailed = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark-detailed"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
fill="currentColor"
>
<path d="M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z" />
<rect x="1778.31" y="1312.43" width="107.11" height="692.38" />
<polygon points="2769.41 1463.57 2903.01 1463.57 2903.01 1601.01 2769.39 1601.01 2769.39 1463.6 2635.79 1463.6 2635.79 1326.16 2769.41 1326.16 2769.41 1463.57" />
<polygon points="3036.63 1738.45 2903.03 1738.45 2903.03 1875.89 2769.41 1875.89 2769.41 1738.45 2903.01 1738.45 2903.01 1601.01 3036.63 1601.01 3036.63 1738.45" />
<polygon points="2903.02 1875.89 2769.43 1875.89 2769.43 2013.33 2635.81 2013.33 2635.81 1875.89 2769.4 1875.89 2769.4 1738.45 2903.02 1738.45 2903.02 1875.89" />
<rect x="2373.03" y="1451.19" width="133.62" height="423.84" transform="translate(4879.6781 3326.2281) rotate(-180)" />
<rect x="2009.75" y="1451.19" width="133.62" height="423.84" transform="translate(4153.1112 3326.2281) rotate(-180)" />
<rect x="2143.56" y="1313.76" width="229.47" height="137.44" transform="translate(4516.5887 2764.9517) rotate(-180)" />
<rect x="2143.56" y="1875.03" width="229.47" height="137.44" transform="translate(4516.5887 3887.5046) rotate(-180)" />
<rect x="1503.05" y="1446.71" width="133.62" height="423.84" transform="translate(3139.725 3317.2494) rotate(-180)" />
<rect x="1139.77" y="1446.71" width="133.62" height="423.84" transform="translate(2413.1581 3317.2494) rotate(-180)" />
<rect x="1273.58" y="1309.27" width="229.47" height="137.44" transform="translate(2776.6357 2755.9729) rotate(-180)" />
<rect x="1273.58" y="1870.54" width="229.47" height="137.44" transform="translate(2776.6357 3878.5258) rotate(-180)" />
<polygon points="888.52 1864.8 754.93 1864.8 754.93 1727.36 888.55 1727.36 888.55 1864.77 1022.15 1864.77 1022.15 2002.21 888.52 2002.21 888.52 1864.8" />
<polygon points="621.31 1589.92 754.9 1589.92 754.9 1452.48 888.52 1452.48 888.52 1589.92 754.93 1589.92 754.93 1727.36 621.31 1727.36 621.31 1589.92" />
<polygon points="754.92 1452.48 888.51 1452.48 888.51 1315.04 1022.13 1315.04 1022.13 1452.48 888.54 1452.48 888.54 1589.92 754.92 1589.92 754.92 1452.48" />
<rect x="1648.65" y="2256.8" width="349.19" height="137.44" transform="translate(3646.502 4651.0383) rotate(-180)" />
<rect x="1510.91" y="2119.73" width="138.82" height="138.82" />
<rect x="1997.85" y="2117.98" width="138.82" height="138.82" />
</svg>
)
}

export const Logo = (props: { class?: string }) => {
return (
<svg
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
13 changes: 13 additions & 0 deletions AMICODE-PATCHES.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -245,3 +245,16 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build.
reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=<free>`
if something else (e.g. the harmoniqs website dev server) holds 3000.
- Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail.

15. (mark drift fix, synced to amicode PR #99 final) — amicode: consolidated the fork's brand mark to ONE geometry, matching amicode's redesigned mark. The "kept in sync manually" cross-repo promise from patch #8 had already silently failed.

- Trigger: amicode's mark was redesigned (PR #99) without a corresponding update here — the fork still rendered the OLD "digi" pixel-accented H-robot everywhere. PR #99 went through several iterations before landing on its final geometry; this entry tracks that FINAL state (square viewBox `0 0 3600 3600`), not the intermediate "hackathon mark" (viewBox `116 287 3377 3035`) an earlier draft of this fork PR had copied — that intermediate geometry is now itself stale and was replaced here.
- Two copies of near-identical geometry lived in THIS repo (logo.tsx's `Robot` used by `Mark`/`Splash`, and spinner.tsx's `AmicoSpinner`), plus a third in favicon/amico.svg. Consolidated to a single `MARK_PATH` exported from logo.tsx.
- Geometry now mirrors amicode PR #99's two authored SVGs (amicode:`packages/extension/media/amico{,_reduced}.svg`), both square `0 0 3600 3600`:
- `MARK_PATH` = amico_reduced.svg's outer-bracket path (fill-rule evenodd screen knockout). Used by `Mark`, `Splash`, `AmicoSpinner`, and mirrored as a literal in favicon/amico.svg — every SMALL context, matching amicode's own "small → reduced" rule.
- `MarkDetailed` = amico.svg's full detailed mark (bracket path + internal circuit-pattern rects/polygons). Used ONLY by the Meet Amico home card (`w-12`/48px), large enough for the detail to resolve.
- `Mark`/`Splash`/`MarkDetailed` viewBox `0 0 64 56`/`116 287 3377 3035` → `0 0 3600 3600`; logo.css aspect-ratio `8/7` → `1/1` (the mark is square now).
- Still theme-adaptive via currentColor + var(--icon-strong-base) — this is a live webview DOM, so currentColor resolves (unlike amicode's native VS Code tab icon, which needs committed {light,dark} files; see amicode PR #99).
- NOT a re-established cross-repo sync promise — `MARK_PATH` is the single source of truth WITHIN this repo; it happens to match amicode's current geometry, kept aligned by hand when the mark changes.
- Tests: ui `bun test src/amicode` green; typecheck green ui + app.
- NOT done this round: full native `bun run script/build.ts` compile + vendored-binary swap — this patch only touches the embedded web UI. Deferred to the next amicode.N release tag, same split as patches #8/#11.
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
import { Show, type JSX } from "solid-js"
import { Logo, Mark } from "@opencode-ai/ui/logo"
import { Logo, MarkDetailed } from "@opencode-ai/ui/logo"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"

// amicode: new-session start screen, top→bottom — H-bot mark (hero), the AMICODE
// wordmark, tagline + how-it-works + starter chips, then the composer. Sizing
// mirrors the classic NewSessionView (Mark w-36 / Logo w-72); keeps the session
// tabs and centered composer. The chips are the one-tap path to the next task.
// The hero uses the DETAILED mark (144px, well above where the accents resolve).
export function NewSessionDesignView(props: { children: JSX.Element; gettingStarted?: JSX.Element }) {
return (
<div data-component="session-new-design" class="relative size-full overflow-y-auto bg-v2-background-bg-deep">
<div class="absolute inset-x-0 top-[12%] flex justify-center px-6 pb-24">
<div class={`${NEW_SESSION_CONTENT_WIDTH} flex flex-col items-center`}>
<Mark class="w-36 h-auto mb-4" />
<MarkDetailed class="w-36 h-auto mb-4" />
<Logo class="w-72 max-w-full h-auto mb-6" />
<Show when={props.gettingStarted}>
<div class="w-full flex justify-center mb-6">{props.gettingStarted}</div>
Expand Down
4 changes: 2 additions & 2 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 { Mark } from "@opencode-ai/ui/logo"
import { MarkDetailed } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"

const MAIN_WORKTREE = "main"
Expand DownExpand Up@@ -78,7 +78,7 @@ export function NewSessionView(props: NewSessionViewProps) {
<div class="flex-1 px-6 pb-30 flex items-center justify-center text-center">
<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">
<Mark class="w-28" />
<MarkDetailed class="w-28" />
{/* amicode: straight wordmark (the Logo's Racing Sans One face reads as
italic); byline keeps the brand attribution. */}
<div
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/home-cards.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createEffect, createMemo, on, type JSX, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -244,7 +244,7 @@ function MeetAmicoCard(props: { onStart: (prompt: string) => void }) {
data-slot="amicode-meet-identity"
style={{ display: "flex", gap: "12px", "align-items": "center", "margin-top": "10px" }}
>
<Mark class="w-12 h-auto shrink-0" />
<MarkDetailed class="w-12 h-auto shrink-0" />
<div style={{ "min-width": "0" }}>
<div style={{ "font-size": "18px", "font-weight": "600", color: "var(--v2-text-text-base)" }}>Amico</div>
<div style={{ "font-size": "12px", "line-height": "16px", color: "var(--v2-text-text-muted)" }}>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/onboarding-wizard.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { Mark, MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -236,7 +236,7 @@ export function AmicodeOnboardingWizard(props: {
padding: "14px 0 4px",
}}
>
<Mark class="w-14 h-auto" />
<MarkDetailed class="w-14 h-auto" />
<div>
<div style={{ "font-size": "22px", "font-weight": "700", color: "var(--v2-text-text-base)" }}>
Welcome to Amicode
Expand Down
9 changes: 7 additions & 2 deletions packages/ui/src/amicode/run-card.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,12 @@
// trading card, not UI panel. Pure string-SVG so the same markup drives both
// the in-app gallery (innerHTML) and the PNG export (SVG → Image → canvas).

// The mark geometry lives in ONE place — logo.tsx's MARK_PATH (a plain string
// const). Importing it here keeps this card from drifting into a private copy
// of the glyph, which is what this consolidation fixes. The mark is square
// (viewBox 0 0 3600 3600).
import { MARK_PATH } from "../components/logo"

export type RunCardData = {
slug: string
problem: string
Expand DownExpand Up@@ -129,14 +135,13 @@ export function renderRunCardSvg(d: RunCardData): string {
}
}

const MARK = "M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
const mono = "ui-monospace, SFMono-Regular, Menlo, monospace"
const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"

return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<rect width="${W}" height="${H}" fill="${BG}"/>
<rect x="0" y="0" width="${W}" height="6" fill="${GOLD}"/>
<g transform="translate(80,64) scale(0.55)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK}"/></g>
<g transform="translate(80,64) scale(0.011)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK_PATH}"/></g>
<text x="130" y="88" font-family="${sans}" font-size="22" font-weight="700" letter-spacing="4" fill="${TEXT}">AMICODE</text>
<text x="262" y="88" font-family="${sans}" font-size="15" letter-spacing="3" fill="${MUTED}">SOLVED PULSE</text>

Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/amicode/spinner.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { type ComponentProps } from "solid-js"
import { MARK_PATH } from "../components/logo"

// AMICODE: working/thinking spinner — the Harmoniqs H-robot silhouette as a
// small monochrome glyph (H body with the screen slit knocked out via
Expand All@@ -10,6 +11,12 @@ import { type ComponentProps } from "solid-js"
// matches the stock spinner's own animation language. prefers-reduced-motion:
// static glyph, no animation (matchMedia guard; inline animations don't
// inherit the CSS-file media-query pattern used elsewhere).
//
// Path comes from logo.tsx's MARK_PATH (Mark/Splash now render the same
// glyph) instead of its own copy — this file used to carry an independent
// literal that happened to already match Mark/Splash's silhouette variant;
// consolidated so there's one geometry in this repo, not a second one that
// could quietly drift the way Mark/Splash's OWN old copy did.

const reducedMotion = () =>
typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
Expand All@@ -22,7 +29,7 @@ export function AmicoSpinner(props: {
return (
<svg
{...props}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
data-component="amico-spinner"
classList={{
...props.classList,
Expand All@@ -32,7 +39,7 @@ export function AmicoSpinner(props: {
>
<path
fill-rule="evenodd"
d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
d={MARK_PATH}
style={
reducedMotion()
? undefined
Expand Down
28 changes: 7 additions & 21 deletions packages/ui/src/assets/favicon/amico.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/ui/src/components/logo.css
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
[data-component="logo-mark"] {
width: 16px;
aspect-ratio: 8/7;
aspect-ratio: 1/1; /* mark viewBox is square (0 0 3600 3600) */
}
102 changes: 65 additions & 37 deletions packages/ui/src/components/logo.tsx
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
import { type ComponentProps } from "solid-js"

// AMICODE branding v2: Mark/Splash render the "digi" Harmoniqs H-robot
// (canonical source also lives at amicode:packages/extension/media/amico.svg,
// kept in sync manually); Logo renders the AMICODE wordmark. Component names,
// props, and data-component hooks are kept identical to stock. The robot body
// follows currentColor; the display rect + glyphs are fixed brand colors.
// viewBox is 64:56 (8:7, not square) — see logo.css aspect-ratio.
// AMICODE branding: Mark/Splash render the Harmoniqs H-robot mark; Logo
// renders the AMICODE wordmark. Component names, props, and data-component
// hooks are kept identical to stock. The mark is square (viewBox 0 0 3600
// 3600) — see logo.css aspect-ratio 1/1.
//
// Geometry mirrors amicode PR #99's two authored SVGs (amicode:
// packages/extension/media/amico{,_reduced}.svg), both square 0 0 3600 3600:
// MARK_PATH → amico_reduced.svg — the outer bracket (fill-rule evenodd
// knocks out the screen). The SMALL-size mark: Mark, Splash,
// AmicoSpinner (../amicode/spinner.tsx, imports MARK_PATH),
// and favicon/amico.svg (mirrors it as a literal — a static
// SVG can't import a TS module; keep its <path d>
// byte-identical to this constant).
// MarkDetailed → amico.svg — the full mark WITH the internal circuit-pattern
// accents, for LARGE contexts only (the Meet Amico card).
// This matches amicode's own "small → reduced, large → detailed" split, so
// the fork and the extension render the same brand mark. MARK_PATH is the
// single source of truth for the glyph within this repo; it is kept aligned
// with amicode's geometry by hand when the mark changes (no build-time link).
export const MARK_PATH =
"M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z"

const Robot = () => (
<>
<path fill="currentColor" d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z" />
<rect x="9" y="19" width="46" height="18" fill="#0A0A0A" />
<g fill="#FFF676">
<rect x="17" y="21" width="2" height="2" />
<rect x="15" y="23" width="2" height="2" />
<rect x="13" y="25" width="2" height="2" />
<rect x="15" y="27" width="2" height="2" />
<rect x="17" y="29" width="2" height="2" />
<rect x="21" y="21" width="6" height="2" />
<rect x="21" y="23" width="2" height="6" />
<rect x="25" y="23" width="2" height="6" />
<rect x="21" y="29" width="6" height="2" />
<rect x="29" y="21" width="2" height="10" />
<rect x="33" y="21" width="2" height="10" />
<rect x="37" y="21" width="6" height="2" />
<rect x="37" y="23" width="2" height="6" />
<rect x="41" y="23" width="2" height="6" />
<rect x="37" y="29" width="6" height="2" />
<rect x="45" y="21" width="2" height="2" />
<rect x="47" y="23" width="2" height="2" />
<rect x="49" y="25" width="2" height="2" />
<rect x="47" y="27" width="2" height="2" />
<rect x="45" y="29" width="2" height="2" />
<polygon points="28,33 36,33 34,36 30,36" />
</g>
</>
)
const Robot = () => <path fill="currentColor" fill-rule="evenodd" d={MARK_PATH} />

export const Mark = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -57,7 +44,7 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
ref={props.ref}
data-component="logo-splash"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -66,6 +53,47 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
)
}

// Detailed mark (amicode PR #99's amico.svg) — the full H-robot WITH the
// internal circuit-pattern accents, used ONLY where it renders large enough to
// resolve (the Meet Amico card, ~48px). currentColor + var(--icon-strong-base),
// same convention as Mark/Splash, so it stays theme-adaptive here — unlike
// amicode's native VS Code chat-tab icon, which needs committed light/dark SVG
// files because a native tab icon has no live CSS context for currentColor.
// Geometry mirrors amicode:packages/extension/media/amico.svg (viewBox
// 0 0 3600 3600); keep in sync by hand if that mark changes.
export const MarkDetailed = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark-detailed"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
fill="currentColor"
>
<path d="M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z" />
<rect x="1778.31" y="1312.43" width="107.11" height="692.38" />
<polygon points="2769.41 1463.57 2903.01 1463.57 2903.01 1601.01 2769.39 1601.01 2769.39 1463.6 2635.79 1463.6 2635.79 1326.16 2769.41 1326.16 2769.41 1463.57" />
<polygon points="3036.63 1738.45 2903.03 1738.45 2903.03 1875.89 2769.41 1875.89 2769.41 1738.45 2903.01 1738.45 2903.01 1601.01 3036.63 1601.01 3036.63 1738.45" />
<polygon points="2903.02 1875.89 2769.43 1875.89 2769.43 2013.33 2635.81 2013.33 2635.81 1875.89 2769.4 1875.89 2769.4 1738.45 2903.02 1738.45 2903.02 1875.89" />
<rect x="2373.03" y="1451.19" width="133.62" height="423.84" transform="translate(4879.6781 3326.2281) rotate(-180)" />
<rect x="2009.75" y="1451.19" width="133.62" height="423.84" transform="translate(4153.1112 3326.2281) rotate(-180)" />
<rect x="2143.56" y="1313.76" width="229.47" height="137.44" transform="translate(4516.5887 2764.9517) rotate(-180)" />
<rect x="2143.56" y="1875.03" width="229.47" height="137.44" transform="translate(4516.5887 3887.5046) rotate(-180)" />
<rect x="1503.05" y="1446.71" width="133.62" height="423.84" transform="translate(3139.725 3317.2494) rotate(-180)" />
<rect x="1139.77" y="1446.71" width="133.62" height="423.84" transform="translate(2413.1581 3317.2494) rotate(-180)" />
<rect x="1273.58" y="1309.27" width="229.47" height="137.44" transform="translate(2776.6357 2755.9729) rotate(-180)" />
<rect x="1273.58" y="1870.54" width="229.47" height="137.44" transform="translate(2776.6357 3878.5258) rotate(-180)" />
<polygon points="888.52 1864.8 754.93 1864.8 754.93 1727.36 888.55 1727.36 888.55 1864.77 1022.15 1864.77 1022.15 2002.21 888.52 2002.21 888.52 1864.8" />
<polygon points="621.31 1589.92 754.9 1589.92 754.9 1452.48 888.52 1452.48 888.52 1589.92 754.93 1589.92 754.93 1727.36 621.31 1727.36 621.31 1589.92" />
<polygon points="754.92 1452.48 888.51 1452.48 888.51 1315.04 1022.13 1315.04 1022.13 1452.48 888.54 1452.48 888.54 1589.92 754.92 1589.92 754.92 1452.48" />
<rect x="1648.65" y="2256.8" width="349.19" height="137.44" transform="translate(3646.502 4651.0383) rotate(-180)" />
<rect x="1510.91" y="2119.73" width="138.82" height="138.82" />
<rect x="1997.85" y="2117.98" width="138.82" height="138.82" />
</svg>
)
}

export const Logo = (props: { class?: string }) => {
return (
<svg
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
13 changes: 13 additions & 0 deletions AMICODE-PATCHES.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -245,3 +245,16 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build.
reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=<free>`
if something else (e.g. the harmoniqs website dev server) holds 3000.
- Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail.

15. (mark drift fix, synced to amicode PR #99 final) — amicode: consolidated the fork's brand mark to ONE geometry, matching amicode's redesigned mark. The "kept in sync manually" cross-repo promise from patch #8 had already silently failed.

- Trigger: amicode's mark was redesigned (PR #99) without a corresponding update here — the fork still rendered the OLD "digi" pixel-accented H-robot everywhere. PR #99 went through several iterations before landing on its final geometry; this entry tracks that FINAL state (square viewBox `0 0 3600 3600`), not the intermediate "hackathon mark" (viewBox `116 287 3377 3035`) an earlier draft of this fork PR had copied — that intermediate geometry is now itself stale and was replaced here.
- Two copies of near-identical geometry lived in THIS repo (logo.tsx's `Robot` used by `Mark`/`Splash`, and spinner.tsx's `AmicoSpinner`), plus a third in favicon/amico.svg. Consolidated to a single `MARK_PATH` exported from logo.tsx.
- Geometry now mirrors amicode PR #99's two authored SVGs (amicode:`packages/extension/media/amico{,_reduced}.svg`), both square `0 0 3600 3600`:
- `MARK_PATH` = amico_reduced.svg's outer-bracket path (fill-rule evenodd screen knockout). Used by `Mark`, `Splash`, `AmicoSpinner`, and mirrored as a literal in favicon/amico.svg — every SMALL context, matching amicode's own "small → reduced" rule.
- `MarkDetailed` = amico.svg's full detailed mark (bracket path + internal circuit-pattern rects/polygons). Used ONLY by the Meet Amico home card (`w-12`/48px), large enough for the detail to resolve.
- `Mark`/`Splash`/`MarkDetailed` viewBox `0 0 64 56`/`116 287 3377 3035` → `0 0 3600 3600`; logo.css aspect-ratio `8/7` → `1/1` (the mark is square now).
- Still theme-adaptive via currentColor + var(--icon-strong-base) — this is a live webview DOM, so currentColor resolves (unlike amicode's native VS Code tab icon, which needs committed {light,dark} files; see amicode PR #99).
- NOT a re-established cross-repo sync promise — `MARK_PATH` is the single source of truth WITHIN this repo; it happens to match amicode's current geometry, kept aligned by hand when the mark changes.
- Tests: ui `bun test src/amicode` green; typecheck green ui + app.
- NOT done this round: full native `bun run script/build.ts` compile + vendored-binary swap — this patch only touches the embedded web UI. Deferred to the next amicode.N release tag, same split as patches #8/#11.
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
import { Show, type JSX } from "solid-js"
import { Logo, Mark } from "@opencode-ai/ui/logo"
import { Logo, MarkDetailed } from "@opencode-ai/ui/logo"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"

// amicode: new-session start screen, top→bottom — H-bot mark (hero), the AMICODE
// wordmark, tagline + how-it-works + starter chips, then the composer. Sizing
// mirrors the classic NewSessionView (Mark w-36 / Logo w-72); keeps the session
// tabs and centered composer. The chips are the one-tap path to the next task.
// The hero uses the DETAILED mark (144px, well above where the accents resolve).
export function NewSessionDesignView(props: { children: JSX.Element; gettingStarted?: JSX.Element }) {
return (
<div data-component="session-new-design" class="relative size-full overflow-y-auto bg-v2-background-bg-deep">
<div class="absolute inset-x-0 top-[12%] flex justify-center px-6 pb-24">
<div class={`${NEW_SESSION_CONTENT_WIDTH} flex flex-col items-center`}>
<Mark class="w-36 h-auto mb-4" />
<MarkDetailed class="w-36 h-auto mb-4" />
<Logo class="w-72 max-w-full h-auto mb-6" />
<Show when={props.gettingStarted}>
<div class="w-full flex justify-center mb-6">{props.gettingStarted}</div>
Expand Down
4 changes: 2 additions & 2 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 { Mark } from "@opencode-ai/ui/logo"
import { MarkDetailed } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"

const MAIN_WORKTREE = "main"
Expand DownExpand Up@@ -78,7 +78,7 @@ export function NewSessionView(props: NewSessionViewProps) {
<div class="flex-1 px-6 pb-30 flex items-center justify-center text-center">
<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">
<Mark class="w-28" />
<MarkDetailed class="w-28" />
{/* amicode: straight wordmark (the Logo's Racing Sans One face reads as
italic); byline keeps the brand attribution. */}
<div
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/home-cards.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createEffect, createMemo, on, type JSX, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -244,7 +244,7 @@ function MeetAmicoCard(props: { onStart: (prompt: string) => void }) {
data-slot="amicode-meet-identity"
style={{ display: "flex", gap: "12px", "align-items": "center", "margin-top": "10px" }}
>
<Mark class="w-12 h-auto shrink-0" />
<MarkDetailed class="w-12 h-auto shrink-0" />
<div style={{ "min-width": "0" }}>
<div style={{ "font-size": "18px", "font-weight": "600", color: "var(--v2-text-text-base)" }}>Amico</div>
<div style={{ "font-size": "12px", "line-height": "16px", color: "var(--v2-text-text-muted)" }}>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/onboarding-wizard.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { Mark, MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -236,7 +236,7 @@ export function AmicodeOnboardingWizard(props: {
padding: "14px 0 4px",
}}
>
<Mark class="w-14 h-auto" />
<MarkDetailed class="w-14 h-auto" />
<div>
<div style={{ "font-size": "22px", "font-weight": "700", color: "var(--v2-text-text-base)" }}>
Welcome to Amicode
Expand Down
9 changes: 7 additions & 2 deletions packages/ui/src/amicode/run-card.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,12 @@
// trading card, not UI panel. Pure string-SVG so the same markup drives both
// the in-app gallery (innerHTML) and the PNG export (SVG → Image → canvas).

// The mark geometry lives in ONE place — logo.tsx's MARK_PATH (a plain string
// const). Importing it here keeps this card from drifting into a private copy
// of the glyph, which is what this consolidation fixes. The mark is square
// (viewBox 0 0 3600 3600).
import { MARK_PATH } from "../components/logo"

export type RunCardData = {
slug: string
problem: string
Expand DownExpand Up@@ -129,14 +135,13 @@ export function renderRunCardSvg(d: RunCardData): string {
}
}

const MARK = "M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
const mono = "ui-monospace, SFMono-Regular, Menlo, monospace"
const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"

return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<rect width="${W}" height="${H}" fill="${BG}"/>
<rect x="0" y="0" width="${W}" height="6" fill="${GOLD}"/>
<g transform="translate(80,64) scale(0.55)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK}"/></g>
<g transform="translate(80,64) scale(0.011)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK_PATH}"/></g>
<text x="130" y="88" font-family="${sans}" font-size="22" font-weight="700" letter-spacing="4" fill="${TEXT}">AMICODE</text>
<text x="262" y="88" font-family="${sans}" font-size="15" letter-spacing="3" fill="${MUTED}">SOLVED PULSE</text>

Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/amicode/spinner.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { type ComponentProps } from "solid-js"
import { MARK_PATH } from "../components/logo"

// AMICODE: working/thinking spinner — the Harmoniqs H-robot silhouette as a
// small monochrome glyph (H body with the screen slit knocked out via
Expand All@@ -10,6 +11,12 @@ import { type ComponentProps } from "solid-js"
// matches the stock spinner's own animation language. prefers-reduced-motion:
// static glyph, no animation (matchMedia guard; inline animations don't
// inherit the CSS-file media-query pattern used elsewhere).
//
// Path comes from logo.tsx's MARK_PATH (Mark/Splash now render the same
// glyph) instead of its own copy — this file used to carry an independent
// literal that happened to already match Mark/Splash's silhouette variant;
// consolidated so there's one geometry in this repo, not a second one that
// could quietly drift the way Mark/Splash's OWN old copy did.

const reducedMotion = () =>
typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
Expand All@@ -22,7 +29,7 @@ export function AmicoSpinner(props: {
return (
<svg
{...props}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
data-component="amico-spinner"
classList={{
...props.classList,
Expand All@@ -32,7 +39,7 @@ export function AmicoSpinner(props: {
>
<path
fill-rule="evenodd"
d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
d={MARK_PATH}
style={
reducedMotion()
? undefined
Expand Down
28 changes: 7 additions & 21 deletions packages/ui/src/assets/favicon/amico.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/ui/src/components/logo.css
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
[data-component="logo-mark"] {
width: 16px;
aspect-ratio: 8/7;
aspect-ratio: 1/1; /* mark viewBox is square (0 0 3600 3600) */
}
102 changes: 65 additions & 37 deletions packages/ui/src/components/logo.tsx
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
import { type ComponentProps } from "solid-js"

// AMICODE branding v2: Mark/Splash render the "digi" Harmoniqs H-robot
// (canonical source also lives at amicode:packages/extension/media/amico.svg,
// kept in sync manually); Logo renders the AMICODE wordmark. Component names,
// props, and data-component hooks are kept identical to stock. The robot body
// follows currentColor; the display rect + glyphs are fixed brand colors.
// viewBox is 64:56 (8:7, not square) — see logo.css aspect-ratio.
// AMICODE branding: Mark/Splash render the Harmoniqs H-robot mark; Logo
// renders the AMICODE wordmark. Component names, props, and data-component
// hooks are kept identical to stock. The mark is square (viewBox 0 0 3600
// 3600) — see logo.css aspect-ratio 1/1.
//
// Geometry mirrors amicode PR #99's two authored SVGs (amicode:
// packages/extension/media/amico{,_reduced}.svg), both square 0 0 3600 3600:
// MARK_PATH → amico_reduced.svg — the outer bracket (fill-rule evenodd
// knocks out the screen). The SMALL-size mark: Mark, Splash,
// AmicoSpinner (../amicode/spinner.tsx, imports MARK_PATH),
// and favicon/amico.svg (mirrors it as a literal — a static
// SVG can't import a TS module; keep its <path d>
// byte-identical to this constant).
// MarkDetailed → amico.svg — the full mark WITH the internal circuit-pattern
// accents, for LARGE contexts only (the Meet Amico card).
// This matches amicode's own "small → reduced, large → detailed" split, so
// the fork and the extension render the same brand mark. MARK_PATH is the
// single source of truth for the glyph within this repo; it is kept aligned
// with amicode's geometry by hand when the mark changes (no build-time link).
export const MARK_PATH =
"M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z"

const Robot = () => (
<>
<path fill="currentColor" d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z" />
<rect x="9" y="19" width="46" height="18" fill="#0A0A0A" />
<g fill="#FFF676">
<rect x="17" y="21" width="2" height="2" />
<rect x="15" y="23" width="2" height="2" />
<rect x="13" y="25" width="2" height="2" />
<rect x="15" y="27" width="2" height="2" />
<rect x="17" y="29" width="2" height="2" />
<rect x="21" y="21" width="6" height="2" />
<rect x="21" y="23" width="2" height="6" />
<rect x="25" y="23" width="2" height="6" />
<rect x="21" y="29" width="6" height="2" />
<rect x="29" y="21" width="2" height="10" />
<rect x="33" y="21" width="2" height="10" />
<rect x="37" y="21" width="6" height="2" />
<rect x="37" y="23" width="2" height="6" />
<rect x="41" y="23" width="2" height="6" />
<rect x="37" y="29" width="6" height="2" />
<rect x="45" y="21" width="2" height="2" />
<rect x="47" y="23" width="2" height="2" />
<rect x="49" y="25" width="2" height="2" />
<rect x="47" y="27" width="2" height="2" />
<rect x="45" y="29" width="2" height="2" />
<polygon points="28,33 36,33 34,36 30,36" />
</g>
</>
)
const Robot = () => <path fill="currentColor" fill-rule="evenodd" d={MARK_PATH} />

export const Mark = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -57,7 +44,7 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
ref={props.ref}
data-component="logo-splash"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -66,6 +53,47 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
)
}

// Detailed mark (amicode PR #99's amico.svg) — the full H-robot WITH the
// internal circuit-pattern accents, used ONLY where it renders large enough to
// resolve (the Meet Amico card, ~48px). currentColor + var(--icon-strong-base),
// same convention as Mark/Splash, so it stays theme-adaptive here — unlike
// amicode's native VS Code chat-tab icon, which needs committed light/dark SVG
// files because a native tab icon has no live CSS context for currentColor.
// Geometry mirrors amicode:packages/extension/media/amico.svg (viewBox
// 0 0 3600 3600); keep in sync by hand if that mark changes.
export const MarkDetailed = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark-detailed"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
fill="currentColor"
>
<path d="M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z" />
<rect x="1778.31" y="1312.43" width="107.11" height="692.38" />
<polygon points="2769.41 1463.57 2903.01 1463.57 2903.01 1601.01 2769.39 1601.01 2769.39 1463.6 2635.79 1463.6 2635.79 1326.16 2769.41 1326.16 2769.41 1463.57" />
<polygon points="3036.63 1738.45 2903.03 1738.45 2903.03 1875.89 2769.41 1875.89 2769.41 1738.45 2903.01 1738.45 2903.01 1601.01 3036.63 1601.01 3036.63 1738.45" />
<polygon points="2903.02 1875.89 2769.43 1875.89 2769.43 2013.33 2635.81 2013.33 2635.81 1875.89 2769.4 1875.89 2769.4 1738.45 2903.02 1738.45 2903.02 1875.89" />
<rect x="2373.03" y="1451.19" width="133.62" height="423.84" transform="translate(4879.6781 3326.2281) rotate(-180)" />
<rect x="2009.75" y="1451.19" width="133.62" height="423.84" transform="translate(4153.1112 3326.2281) rotate(-180)" />
<rect x="2143.56" y="1313.76" width="229.47" height="137.44" transform="translate(4516.5887 2764.9517) rotate(-180)" />
<rect x="2143.56" y="1875.03" width="229.47" height="137.44" transform="translate(4516.5887 3887.5046) rotate(-180)" />
<rect x="1503.05" y="1446.71" width="133.62" height="423.84" transform="translate(3139.725 3317.2494) rotate(-180)" />
<rect x="1139.77" y="1446.71" width="133.62" height="423.84" transform="translate(2413.1581 3317.2494) rotate(-180)" />
<rect x="1273.58" y="1309.27" width="229.47" height="137.44" transform="translate(2776.6357 2755.9729) rotate(-180)" />
<rect x="1273.58" y="1870.54" width="229.47" height="137.44" transform="translate(2776.6357 3878.5258) rotate(-180)" />
<polygon points="888.52 1864.8 754.93 1864.8 754.93 1727.36 888.55 1727.36 888.55 1864.77 1022.15 1864.77 1022.15 2002.21 888.52 2002.21 888.52 1864.8" />
<polygon points="621.31 1589.92 754.9 1589.92 754.9 1452.48 888.52 1452.48 888.52 1589.92 754.93 1589.92 754.93 1727.36 621.31 1727.36 621.31 1589.92" />
<polygon points="754.92 1452.48 888.51 1452.48 888.51 1315.04 1022.13 1315.04 1022.13 1452.48 888.54 1452.48 888.54 1589.92 754.92 1589.92 754.92 1452.48" />
<rect x="1648.65" y="2256.8" width="349.19" height="137.44" transform="translate(3646.502 4651.0383) rotate(-180)" />
<rect x="1510.91" y="2119.73" width="138.82" height="138.82" />
<rect x="1997.85" y="2117.98" width="138.82" height="138.82" />
</svg>
)
}

export const Logo = (props: { class?: string }) => {
return (
<svg
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
13 changes: 13 additions & 0 deletions AMICODE-PATCHES.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -245,3 +245,16 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build.
reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=<free>`
if something else (e.g. the harmoniqs website dev server) holds 3000.
- Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail.

15. (mark drift fix, synced to amicode PR #99 final) — amicode: consolidated the fork's brand mark to ONE geometry, matching amicode's redesigned mark. The "kept in sync manually" cross-repo promise from patch #8 had already silently failed.

- Trigger: amicode's mark was redesigned (PR #99) without a corresponding update here — the fork still rendered the OLD "digi" pixel-accented H-robot everywhere. PR #99 went through several iterations before landing on its final geometry; this entry tracks that FINAL state (square viewBox `0 0 3600 3600`), not the intermediate "hackathon mark" (viewBox `116 287 3377 3035`) an earlier draft of this fork PR had copied — that intermediate geometry is now itself stale and was replaced here.
- Two copies of near-identical geometry lived in THIS repo (logo.tsx's `Robot` used by `Mark`/`Splash`, and spinner.tsx's `AmicoSpinner`), plus a third in favicon/amico.svg. Consolidated to a single `MARK_PATH` exported from logo.tsx.
- Geometry now mirrors amicode PR #99's two authored SVGs (amicode:`packages/extension/media/amico{,_reduced}.svg`), both square `0 0 3600 3600`:
- `MARK_PATH` = amico_reduced.svg's outer-bracket path (fill-rule evenodd screen knockout). Used by `Mark`, `Splash`, `AmicoSpinner`, and mirrored as a literal in favicon/amico.svg — every SMALL context, matching amicode's own "small → reduced" rule.
- `MarkDetailed` = amico.svg's full detailed mark (bracket path + internal circuit-pattern rects/polygons). Used ONLY by the Meet Amico home card (`w-12`/48px), large enough for the detail to resolve.
- `Mark`/`Splash`/`MarkDetailed` viewBox `0 0 64 56`/`116 287 3377 3035` → `0 0 3600 3600`; logo.css aspect-ratio `8/7` → `1/1` (the mark is square now).
- Still theme-adaptive via currentColor + var(--icon-strong-base) — this is a live webview DOM, so currentColor resolves (unlike amicode's native VS Code tab icon, which needs committed {light,dark} files; see amicode PR #99).
- NOT a re-established cross-repo sync promise — `MARK_PATH` is the single source of truth WITHIN this repo; it happens to match amicode's current geometry, kept aligned by hand when the mark changes.
- Tests: ui `bun test src/amicode` green; typecheck green ui + app.
- NOT done this round: full native `bun run script/build.ts` compile + vendored-binary swap — this patch only touches the embedded web UI. Deferred to the next amicode.N release tag, same split as patches #8/#11.
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
import { Show, type JSX } from "solid-js"
import { Logo, Mark } from "@opencode-ai/ui/logo"
import { Logo, MarkDetailed } from "@opencode-ai/ui/logo"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"

// amicode: new-session start screen, top→bottom — H-bot mark (hero), the AMICODE
// wordmark, tagline + how-it-works + starter chips, then the composer. Sizing
// mirrors the classic NewSessionView (Mark w-36 / Logo w-72); keeps the session
// tabs and centered composer. The chips are the one-tap path to the next task.
// The hero uses the DETAILED mark (144px, well above where the accents resolve).
export function NewSessionDesignView(props: { children: JSX.Element; gettingStarted?: JSX.Element }) {
return (
<div data-component="session-new-design" class="relative size-full overflow-y-auto bg-v2-background-bg-deep">
<div class="absolute inset-x-0 top-[12%] flex justify-center px-6 pb-24">
<div class={`${NEW_SESSION_CONTENT_WIDTH} flex flex-col items-center`}>
<Mark class="w-36 h-auto mb-4" />
<MarkDetailed class="w-36 h-auto mb-4" />
<Logo class="w-72 max-w-full h-auto mb-6" />
<Show when={props.gettingStarted}>
<div class="w-full flex justify-center mb-6">{props.gettingStarted}</div>
Expand Down
4 changes: 2 additions & 2 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 { Mark } from "@opencode-ai/ui/logo"
import { MarkDetailed } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"

const MAIN_WORKTREE = "main"
Expand DownExpand Up@@ -78,7 +78,7 @@ export function NewSessionView(props: NewSessionViewProps) {
<div class="flex-1 px-6 pb-30 flex items-center justify-center text-center">
<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">
<Mark class="w-28" />
<MarkDetailed class="w-28" />
{/* amicode: straight wordmark (the Logo's Racing Sans One face reads as
italic); byline keeps the brand attribution. */}
<div
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/home-cards.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createEffect, createMemo, on, type JSX, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -244,7 +244,7 @@ function MeetAmicoCard(props: { onStart: (prompt: string) => void }) {
data-slot="amicode-meet-identity"
style={{ display: "flex", gap: "12px", "align-items": "center", "margin-top": "10px" }}
>
<Mark class="w-12 h-auto shrink-0" />
<MarkDetailed class="w-12 h-auto shrink-0" />
<div style={{ "min-width": "0" }}>
<div style={{ "font-size": "18px", "font-weight": "600", color: "var(--v2-text-text-base)" }}>Amico</div>
<div style={{ "font-size": "12px", "line-height": "16px", color: "var(--v2-text-text-muted)" }}>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/onboarding-wizard.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { Mark, MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -236,7 +236,7 @@ export function AmicodeOnboardingWizard(props: {
padding: "14px 0 4px",
}}
>
<Mark class="w-14 h-auto" />
<MarkDetailed class="w-14 h-auto" />
<div>
<div style={{ "font-size": "22px", "font-weight": "700", color: "var(--v2-text-text-base)" }}>
Welcome to Amicode
Expand Down
9 changes: 7 additions & 2 deletions packages/ui/src/amicode/run-card.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,12 @@
// trading card, not UI panel. Pure string-SVG so the same markup drives both
// the in-app gallery (innerHTML) and the PNG export (SVG → Image → canvas).

// The mark geometry lives in ONE place — logo.tsx's MARK_PATH (a plain string
// const). Importing it here keeps this card from drifting into a private copy
// of the glyph, which is what this consolidation fixes. The mark is square
// (viewBox 0 0 3600 3600).
import { MARK_PATH } from "../components/logo"

export type RunCardData = {
slug: string
problem: string
Expand DownExpand Up@@ -129,14 +135,13 @@ export function renderRunCardSvg(d: RunCardData): string {
}
}

const MARK = "M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
const mono = "ui-monospace, SFMono-Regular, Menlo, monospace"
const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"

return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<rect width="${W}" height="${H}" fill="${BG}"/>
<rect x="0" y="0" width="${W}" height="6" fill="${GOLD}"/>
<g transform="translate(80,64) scale(0.55)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK}"/></g>
<g transform="translate(80,64) scale(0.011)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK_PATH}"/></g>
<text x="130" y="88" font-family="${sans}" font-size="22" font-weight="700" letter-spacing="4" fill="${TEXT}">AMICODE</text>
<text x="262" y="88" font-family="${sans}" font-size="15" letter-spacing="3" fill="${MUTED}">SOLVED PULSE</text>

Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/amicode/spinner.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { type ComponentProps } from "solid-js"
import { MARK_PATH } from "../components/logo"

// AMICODE: working/thinking spinner — the Harmoniqs H-robot silhouette as a
// small monochrome glyph (H body with the screen slit knocked out via
Expand All@@ -10,6 +11,12 @@ import { type ComponentProps } from "solid-js"
// matches the stock spinner's own animation language. prefers-reduced-motion:
// static glyph, no animation (matchMedia guard; inline animations don't
// inherit the CSS-file media-query pattern used elsewhere).
//
// Path comes from logo.tsx's MARK_PATH (Mark/Splash now render the same
// glyph) instead of its own copy — this file used to carry an independent
// literal that happened to already match Mark/Splash's silhouette variant;
// consolidated so there's one geometry in this repo, not a second one that
// could quietly drift the way Mark/Splash's OWN old copy did.

const reducedMotion = () =>
typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
Expand All@@ -22,7 +29,7 @@ export function AmicoSpinner(props: {
return (
<svg
{...props}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
data-component="amico-spinner"
classList={{
...props.classList,
Expand All@@ -32,7 +39,7 @@ export function AmicoSpinner(props: {
>
<path
fill-rule="evenodd"
d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
d={MARK_PATH}
style={
reducedMotion()
? undefined
Expand Down
28 changes: 7 additions & 21 deletions packages/ui/src/assets/favicon/amico.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/ui/src/components/logo.css
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
[data-component="logo-mark"] {
width: 16px;
aspect-ratio: 8/7;
aspect-ratio: 1/1; /* mark viewBox is square (0 0 3600 3600) */
}
102 changes: 65 additions & 37 deletions packages/ui/src/components/logo.tsx
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
import { type ComponentProps } from "solid-js"

// AMICODE branding v2: Mark/Splash render the "digi" Harmoniqs H-robot
// (canonical source also lives at amicode:packages/extension/media/amico.svg,
// kept in sync manually); Logo renders the AMICODE wordmark. Component names,
// props, and data-component hooks are kept identical to stock. The robot body
// follows currentColor; the display rect + glyphs are fixed brand colors.
// viewBox is 64:56 (8:7, not square) — see logo.css aspect-ratio.
// AMICODE branding: Mark/Splash render the Harmoniqs H-robot mark; Logo
// renders the AMICODE wordmark. Component names, props, and data-component
// hooks are kept identical to stock. The mark is square (viewBox 0 0 3600
// 3600) — see logo.css aspect-ratio 1/1.
//
// Geometry mirrors amicode PR #99's two authored SVGs (amicode:
// packages/extension/media/amico{,_reduced}.svg), both square 0 0 3600 3600:
// MARK_PATH → amico_reduced.svg — the outer bracket (fill-rule evenodd
// knocks out the screen). The SMALL-size mark: Mark, Splash,
// AmicoSpinner (../amicode/spinner.tsx, imports MARK_PATH),
// and favicon/amico.svg (mirrors it as a literal — a static
// SVG can't import a TS module; keep its <path d>
// byte-identical to this constant).
// MarkDetailed → amico.svg — the full mark WITH the internal circuit-pattern
// accents, for LARGE contexts only (the Meet Amico card).
// This matches amicode's own "small → reduced, large → detailed" split, so
// the fork and the extension render the same brand mark. MARK_PATH is the
// single source of truth for the glyph within this repo; it is kept aligned
// with amicode's geometry by hand when the mark changes (no build-time link).
export const MARK_PATH =
"M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z"

const Robot = () => (
<>
<path fill="currentColor" d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z" />
<rect x="9" y="19" width="46" height="18" fill="#0A0A0A" />
<g fill="#FFF676">
<rect x="17" y="21" width="2" height="2" />
<rect x="15" y="23" width="2" height="2" />
<rect x="13" y="25" width="2" height="2" />
<rect x="15" y="27" width="2" height="2" />
<rect x="17" y="29" width="2" height="2" />
<rect x="21" y="21" width="6" height="2" />
<rect x="21" y="23" width="2" height="6" />
<rect x="25" y="23" width="2" height="6" />
<rect x="21" y="29" width="6" height="2" />
<rect x="29" y="21" width="2" height="10" />
<rect x="33" y="21" width="2" height="10" />
<rect x="37" y="21" width="6" height="2" />
<rect x="37" y="23" width="2" height="6" />
<rect x="41" y="23" width="2" height="6" />
<rect x="37" y="29" width="6" height="2" />
<rect x="45" y="21" width="2" height="2" />
<rect x="47" y="23" width="2" height="2" />
<rect x="49" y="25" width="2" height="2" />
<rect x="47" y="27" width="2" height="2" />
<rect x="45" y="29" width="2" height="2" />
<polygon points="28,33 36,33 34,36 30,36" />
</g>
</>
)
const Robot = () => <path fill="currentColor" fill-rule="evenodd" d={MARK_PATH} />

export const Mark = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -57,7 +44,7 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
ref={props.ref}
data-component="logo-splash"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -66,6 +53,47 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
)
}

// Detailed mark (amicode PR #99's amico.svg) — the full H-robot WITH the
// internal circuit-pattern accents, used ONLY where it renders large enough to
// resolve (the Meet Amico card, ~48px). currentColor + var(--icon-strong-base),
// same convention as Mark/Splash, so it stays theme-adaptive here — unlike
// amicode's native VS Code chat-tab icon, which needs committed light/dark SVG
// files because a native tab icon has no live CSS context for currentColor.
// Geometry mirrors amicode:packages/extension/media/amico.svg (viewBox
// 0 0 3600 3600); keep in sync by hand if that mark changes.
export const MarkDetailed = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark-detailed"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
fill="currentColor"
>
<path d="M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z" />
<rect x="1778.31" y="1312.43" width="107.11" height="692.38" />
<polygon points="2769.41 1463.57 2903.01 1463.57 2903.01 1601.01 2769.39 1601.01 2769.39 1463.6 2635.79 1463.6 2635.79 1326.16 2769.41 1326.16 2769.41 1463.57" />
<polygon points="3036.63 1738.45 2903.03 1738.45 2903.03 1875.89 2769.41 1875.89 2769.41 1738.45 2903.01 1738.45 2903.01 1601.01 3036.63 1601.01 3036.63 1738.45" />
<polygon points="2903.02 1875.89 2769.43 1875.89 2769.43 2013.33 2635.81 2013.33 2635.81 1875.89 2769.4 1875.89 2769.4 1738.45 2903.02 1738.45 2903.02 1875.89" />
<rect x="2373.03" y="1451.19" width="133.62" height="423.84" transform="translate(4879.6781 3326.2281) rotate(-180)" />
<rect x="2009.75" y="1451.19" width="133.62" height="423.84" transform="translate(4153.1112 3326.2281) rotate(-180)" />
<rect x="2143.56" y="1313.76" width="229.47" height="137.44" transform="translate(4516.5887 2764.9517) rotate(-180)" />
<rect x="2143.56" y="1875.03" width="229.47" height="137.44" transform="translate(4516.5887 3887.5046) rotate(-180)" />
<rect x="1503.05" y="1446.71" width="133.62" height="423.84" transform="translate(3139.725 3317.2494) rotate(-180)" />
<rect x="1139.77" y="1446.71" width="133.62" height="423.84" transform="translate(2413.1581 3317.2494) rotate(-180)" />
<rect x="1273.58" y="1309.27" width="229.47" height="137.44" transform="translate(2776.6357 2755.9729) rotate(-180)" />
<rect x="1273.58" y="1870.54" width="229.47" height="137.44" transform="translate(2776.6357 3878.5258) rotate(-180)" />
<polygon points="888.52 1864.8 754.93 1864.8 754.93 1727.36 888.55 1727.36 888.55 1864.77 1022.15 1864.77 1022.15 2002.21 888.52 2002.21 888.52 1864.8" />
<polygon points="621.31 1589.92 754.9 1589.92 754.9 1452.48 888.52 1452.48 888.52 1589.92 754.93 1589.92 754.93 1727.36 621.31 1727.36 621.31 1589.92" />
<polygon points="754.92 1452.48 888.51 1452.48 888.51 1315.04 1022.13 1315.04 1022.13 1452.48 888.54 1452.48 888.54 1589.92 754.92 1589.92 754.92 1452.48" />
<rect x="1648.65" y="2256.8" width="349.19" height="137.44" transform="translate(3646.502 4651.0383) rotate(-180)" />
<rect x="1510.91" y="2119.73" width="138.82" height="138.82" />
<rect x="1997.85" y="2117.98" width="138.82" height="138.82" />
</svg>
)
}

export const Logo = (props: { class?: string }) => {
return (
<svg
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
13 changes: 13 additions & 0 deletions AMICODE-PATCHES.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -245,3 +245,16 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build.
reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=<free>`
if something else (e.g. the harmoniqs website dev server) holds 3000.
- Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail.

15. (mark drift fix, synced to amicode PR #99 final) — amicode: consolidated the fork's brand mark to ONE geometry, matching amicode's redesigned mark. The "kept in sync manually" cross-repo promise from patch #8 had already silently failed.

- Trigger: amicode's mark was redesigned (PR #99) without a corresponding update here — the fork still rendered the OLD "digi" pixel-accented H-robot everywhere. PR #99 went through several iterations before landing on its final geometry; this entry tracks that FINAL state (square viewBox `0 0 3600 3600`), not the intermediate "hackathon mark" (viewBox `116 287 3377 3035`) an earlier draft of this fork PR had copied — that intermediate geometry is now itself stale and was replaced here.
- Two copies of near-identical geometry lived in THIS repo (logo.tsx's `Robot` used by `Mark`/`Splash`, and spinner.tsx's `AmicoSpinner`), plus a third in favicon/amico.svg. Consolidated to a single `MARK_PATH` exported from logo.tsx.
- Geometry now mirrors amicode PR #99's two authored SVGs (amicode:`packages/extension/media/amico{,_reduced}.svg`), both square `0 0 3600 3600`:
- `MARK_PATH` = amico_reduced.svg's outer-bracket path (fill-rule evenodd screen knockout). Used by `Mark`, `Splash`, `AmicoSpinner`, and mirrored as a literal in favicon/amico.svg — every SMALL context, matching amicode's own "small → reduced" rule.
- `MarkDetailed` = amico.svg's full detailed mark (bracket path + internal circuit-pattern rects/polygons). Used ONLY by the Meet Amico home card (`w-12`/48px), large enough for the detail to resolve.
- `Mark`/`Splash`/`MarkDetailed` viewBox `0 0 64 56`/`116 287 3377 3035` → `0 0 3600 3600`; logo.css aspect-ratio `8/7` → `1/1` (the mark is square now).
- Still theme-adaptive via currentColor + var(--icon-strong-base) — this is a live webview DOM, so currentColor resolves (unlike amicode's native VS Code tab icon, which needs committed {light,dark} files; see amicode PR #99).
- NOT a re-established cross-repo sync promise — `MARK_PATH` is the single source of truth WITHIN this repo; it happens to match amicode's current geometry, kept aligned by hand when the mark changes.
- Tests: ui `bun test src/amicode` green; typecheck green ui + app.
- NOT done this round: full native `bun run script/build.ts` compile + vendored-binary swap — this patch only touches the embedded web UI. Deferred to the next amicode.N release tag, same split as patches #8/#11.
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
import { Show, type JSX } from "solid-js"
import { Logo, Mark } from "@opencode-ai/ui/logo"
import { Logo, MarkDetailed } from "@opencode-ai/ui/logo"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"

// amicode: new-session start screen, top→bottom — H-bot mark (hero), the AMICODE
// wordmark, tagline + how-it-works + starter chips, then the composer. Sizing
// mirrors the classic NewSessionView (Mark w-36 / Logo w-72); keeps the session
// tabs and centered composer. The chips are the one-tap path to the next task.
// The hero uses the DETAILED mark (144px, well above where the accents resolve).
export function NewSessionDesignView(props: { children: JSX.Element; gettingStarted?: JSX.Element }) {
return (
<div data-component="session-new-design" class="relative size-full overflow-y-auto bg-v2-background-bg-deep">
<div class="absolute inset-x-0 top-[12%] flex justify-center px-6 pb-24">
<div class={`${NEW_SESSION_CONTENT_WIDTH} flex flex-col items-center`}>
<Mark class="w-36 h-auto mb-4" />
<MarkDetailed class="w-36 h-auto mb-4" />
<Logo class="w-72 max-w-full h-auto mb-6" />
<Show when={props.gettingStarted}>
<div class="w-full flex justify-center mb-6">{props.gettingStarted}</div>
Expand Down
4 changes: 2 additions & 2 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 { Mark } from "@opencode-ai/ui/logo"
import { MarkDetailed } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"

const MAIN_WORKTREE = "main"
Expand DownExpand Up@@ -78,7 +78,7 @@ export function NewSessionView(props: NewSessionViewProps) {
<div class="flex-1 px-6 pb-30 flex items-center justify-center text-center">
<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">
<Mark class="w-28" />
<MarkDetailed class="w-28" />
{/* amicode: straight wordmark (the Logo's Racing Sans One face reads as
italic); byline keeps the brand attribution. */}
<div
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/home-cards.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createEffect, createMemo, on, type JSX, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -244,7 +244,7 @@ function MeetAmicoCard(props: { onStart: (prompt: string) => void }) {
data-slot="amicode-meet-identity"
style={{ display: "flex", gap: "12px", "align-items": "center", "margin-top": "10px" }}
>
<Mark class="w-12 h-auto shrink-0" />
<MarkDetailed class="w-12 h-auto shrink-0" />
<div style={{ "min-width": "0" }}>
<div style={{ "font-size": "18px", "font-weight": "600", color: "var(--v2-text-text-base)" }}>Amico</div>
<div style={{ "font-size": "12px", "line-height": "16px", color: "var(--v2-text-text-muted)" }}>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/onboarding-wizard.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { Mark, MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -236,7 +236,7 @@ export function AmicodeOnboardingWizard(props: {
padding: "14px 0 4px",
}}
>
<Mark class="w-14 h-auto" />
<MarkDetailed class="w-14 h-auto" />
<div>
<div style={{ "font-size": "22px", "font-weight": "700", color: "var(--v2-text-text-base)" }}>
Welcome to Amicode
Expand Down
9 changes: 7 additions & 2 deletions packages/ui/src/amicode/run-card.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,12 @@
// trading card, not UI panel. Pure string-SVG so the same markup drives both
// the in-app gallery (innerHTML) and the PNG export (SVG → Image → canvas).

// The mark geometry lives in ONE place — logo.tsx's MARK_PATH (a plain string
// const). Importing it here keeps this card from drifting into a private copy
// of the glyph, which is what this consolidation fixes. The mark is square
// (viewBox 0 0 3600 3600).
import { MARK_PATH } from "../components/logo"

export type RunCardData = {
slug: string
problem: string
Expand DownExpand Up@@ -129,14 +135,13 @@ export function renderRunCardSvg(d: RunCardData): string {
}
}

const MARK = "M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
const mono = "ui-monospace, SFMono-Regular, Menlo, monospace"
const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"

return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<rect width="${W}" height="${H}" fill="${BG}"/>
<rect x="0" y="0" width="${W}" height="6" fill="${GOLD}"/>
<g transform="translate(80,64) scale(0.55)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK}"/></g>
<g transform="translate(80,64) scale(0.011)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK_PATH}"/></g>
<text x="130" y="88" font-family="${sans}" font-size="22" font-weight="700" letter-spacing="4" fill="${TEXT}">AMICODE</text>
<text x="262" y="88" font-family="${sans}" font-size="15" letter-spacing="3" fill="${MUTED}">SOLVED PULSE</text>

Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/amicode/spinner.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { type ComponentProps } from "solid-js"
import { MARK_PATH } from "../components/logo"

// AMICODE: working/thinking spinner — the Harmoniqs H-robot silhouette as a
// small monochrome glyph (H body with the screen slit knocked out via
Expand All@@ -10,6 +11,12 @@ import { type ComponentProps } from "solid-js"
// matches the stock spinner's own animation language. prefers-reduced-motion:
// static glyph, no animation (matchMedia guard; inline animations don't
// inherit the CSS-file media-query pattern used elsewhere).
//
// Path comes from logo.tsx's MARK_PATH (Mark/Splash now render the same
// glyph) instead of its own copy — this file used to carry an independent
// literal that happened to already match Mark/Splash's silhouette variant;
// consolidated so there's one geometry in this repo, not a second one that
// could quietly drift the way Mark/Splash's OWN old copy did.

const reducedMotion = () =>
typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
Expand All@@ -22,7 +29,7 @@ export function AmicoSpinner(props: {
return (
<svg
{...props}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
data-component="amico-spinner"
classList={{
...props.classList,
Expand All@@ -32,7 +39,7 @@ export function AmicoSpinner(props: {
>
<path
fill-rule="evenodd"
d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
d={MARK_PATH}
style={
reducedMotion()
? undefined
Expand Down
28 changes: 7 additions & 21 deletions packages/ui/src/assets/favicon/amico.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/ui/src/components/logo.css
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
[data-component="logo-mark"] {
width: 16px;
aspect-ratio: 8/7;
aspect-ratio: 1/1; /* mark viewBox is square (0 0 3600 3600) */
}
102 changes: 65 additions & 37 deletions packages/ui/src/components/logo.tsx
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
import { type ComponentProps } from "solid-js"

// AMICODE branding v2: Mark/Splash render the "digi" Harmoniqs H-robot
// (canonical source also lives at amicode:packages/extension/media/amico.svg,
// kept in sync manually); Logo renders the AMICODE wordmark. Component names,
// props, and data-component hooks are kept identical to stock. The robot body
// follows currentColor; the display rect + glyphs are fixed brand colors.
// viewBox is 64:56 (8:7, not square) — see logo.css aspect-ratio.
// AMICODE branding: Mark/Splash render the Harmoniqs H-robot mark; Logo
// renders the AMICODE wordmark. Component names, props, and data-component
// hooks are kept identical to stock. The mark is square (viewBox 0 0 3600
// 3600) — see logo.css aspect-ratio 1/1.
//
// Geometry mirrors amicode PR #99's two authored SVGs (amicode:
// packages/extension/media/amico{,_reduced}.svg), both square 0 0 3600 3600:
// MARK_PATH → amico_reduced.svg — the outer bracket (fill-rule evenodd
// knocks out the screen). The SMALL-size mark: Mark, Splash,
// AmicoSpinner (../amicode/spinner.tsx, imports MARK_PATH),
// and favicon/amico.svg (mirrors it as a literal — a static
// SVG can't import a TS module; keep its <path d>
// byte-identical to this constant).
// MarkDetailed → amico.svg — the full mark WITH the internal circuit-pattern
// accents, for LARGE contexts only (the Meet Amico card).
// This matches amicode's own "small → reduced, large → detailed" split, so
// the fork and the extension render the same brand mark. MARK_PATH is the
// single source of truth for the glyph within this repo; it is kept aligned
// with amicode's geometry by hand when the mark changes (no build-time link).
export const MARK_PATH =
"M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z"

const Robot = () => (
<>
<path fill="currentColor" d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z" />
<rect x="9" y="19" width="46" height="18" fill="#0A0A0A" />
<g fill="#FFF676">
<rect x="17" y="21" width="2" height="2" />
<rect x="15" y="23" width="2" height="2" />
<rect x="13" y="25" width="2" height="2" />
<rect x="15" y="27" width="2" height="2" />
<rect x="17" y="29" width="2" height="2" />
<rect x="21" y="21" width="6" height="2" />
<rect x="21" y="23" width="2" height="6" />
<rect x="25" y="23" width="2" height="6" />
<rect x="21" y="29" width="6" height="2" />
<rect x="29" y="21" width="2" height="10" />
<rect x="33" y="21" width="2" height="10" />
<rect x="37" y="21" width="6" height="2" />
<rect x="37" y="23" width="2" height="6" />
<rect x="41" y="23" width="2" height="6" />
<rect x="37" y="29" width="6" height="2" />
<rect x="45" y="21" width="2" height="2" />
<rect x="47" y="23" width="2" height="2" />
<rect x="49" y="25" width="2" height="2" />
<rect x="47" y="27" width="2" height="2" />
<rect x="45" y="29" width="2" height="2" />
<polygon points="28,33 36,33 34,36 30,36" />
</g>
</>
)
const Robot = () => <path fill="currentColor" fill-rule="evenodd" d={MARK_PATH} />

export const Mark = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -57,7 +44,7 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
ref={props.ref}
data-component="logo-splash"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -66,6 +53,47 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
)
}

// Detailed mark (amicode PR #99's amico.svg) — the full H-robot WITH the
// internal circuit-pattern accents, used ONLY where it renders large enough to
// resolve (the Meet Amico card, ~48px). currentColor + var(--icon-strong-base),
// same convention as Mark/Splash, so it stays theme-adaptive here — unlike
// amicode's native VS Code chat-tab icon, which needs committed light/dark SVG
// files because a native tab icon has no live CSS context for currentColor.
// Geometry mirrors amicode:packages/extension/media/amico.svg (viewBox
// 0 0 3600 3600); keep in sync by hand if that mark changes.
export const MarkDetailed = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark-detailed"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
fill="currentColor"
>
<path d="M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z" />
<rect x="1778.31" y="1312.43" width="107.11" height="692.38" />
<polygon points="2769.41 1463.57 2903.01 1463.57 2903.01 1601.01 2769.39 1601.01 2769.39 1463.6 2635.79 1463.6 2635.79 1326.16 2769.41 1326.16 2769.41 1463.57" />
<polygon points="3036.63 1738.45 2903.03 1738.45 2903.03 1875.89 2769.41 1875.89 2769.41 1738.45 2903.01 1738.45 2903.01 1601.01 3036.63 1601.01 3036.63 1738.45" />
<polygon points="2903.02 1875.89 2769.43 1875.89 2769.43 2013.33 2635.81 2013.33 2635.81 1875.89 2769.4 1875.89 2769.4 1738.45 2903.02 1738.45 2903.02 1875.89" />
<rect x="2373.03" y="1451.19" width="133.62" height="423.84" transform="translate(4879.6781 3326.2281) rotate(-180)" />
<rect x="2009.75" y="1451.19" width="133.62" height="423.84" transform="translate(4153.1112 3326.2281) rotate(-180)" />
<rect x="2143.56" y="1313.76" width="229.47" height="137.44" transform="translate(4516.5887 2764.9517) rotate(-180)" />
<rect x="2143.56" y="1875.03" width="229.47" height="137.44" transform="translate(4516.5887 3887.5046) rotate(-180)" />
<rect x="1503.05" y="1446.71" width="133.62" height="423.84" transform="translate(3139.725 3317.2494) rotate(-180)" />
<rect x="1139.77" y="1446.71" width="133.62" height="423.84" transform="translate(2413.1581 3317.2494) rotate(-180)" />
<rect x="1273.58" y="1309.27" width="229.47" height="137.44" transform="translate(2776.6357 2755.9729) rotate(-180)" />
<rect x="1273.58" y="1870.54" width="229.47" height="137.44" transform="translate(2776.6357 3878.5258) rotate(-180)" />
<polygon points="888.52 1864.8 754.93 1864.8 754.93 1727.36 888.55 1727.36 888.55 1864.77 1022.15 1864.77 1022.15 2002.21 888.52 2002.21 888.52 1864.8" />
<polygon points="621.31 1589.92 754.9 1589.92 754.9 1452.48 888.52 1452.48 888.52 1589.92 754.93 1589.92 754.93 1727.36 621.31 1727.36 621.31 1589.92" />
<polygon points="754.92 1452.48 888.51 1452.48 888.51 1315.04 1022.13 1315.04 1022.13 1452.48 888.54 1452.48 888.54 1589.92 754.92 1589.92 754.92 1452.48" />
<rect x="1648.65" y="2256.8" width="349.19" height="137.44" transform="translate(3646.502 4651.0383) rotate(-180)" />
<rect x="1510.91" y="2119.73" width="138.82" height="138.82" />
<rect x="1997.85" y="2117.98" width="138.82" height="138.82" />
</svg>
)
}

export const Logo = (props: { class?: string }) => {
return (
<svg
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
13 changes: 13 additions & 0 deletions AMICODE-PATCHES.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -245,3 +245,16 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build.
reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=<free>`
if something else (e.g. the harmoniqs website dev server) holds 3000.
- Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail.

15. (mark drift fix, synced to amicode PR #99 final) — amicode: consolidated the fork's brand mark to ONE geometry, matching amicode's redesigned mark. The "kept in sync manually" cross-repo promise from patch #8 had already silently failed.

- Trigger: amicode's mark was redesigned (PR #99) without a corresponding update here — the fork still rendered the OLD "digi" pixel-accented H-robot everywhere. PR #99 went through several iterations before landing on its final geometry; this entry tracks that FINAL state (square viewBox `0 0 3600 3600`), not the intermediate "hackathon mark" (viewBox `116 287 3377 3035`) an earlier draft of this fork PR had copied — that intermediate geometry is now itself stale and was replaced here.
- Two copies of near-identical geometry lived in THIS repo (logo.tsx's `Robot` used by `Mark`/`Splash`, and spinner.tsx's `AmicoSpinner`), plus a third in favicon/amico.svg. Consolidated to a single `MARK_PATH` exported from logo.tsx.
- Geometry now mirrors amicode PR #99's two authored SVGs (amicode:`packages/extension/media/amico{,_reduced}.svg`), both square `0 0 3600 3600`:
- `MARK_PATH` = amico_reduced.svg's outer-bracket path (fill-rule evenodd screen knockout). Used by `Mark`, `Splash`, `AmicoSpinner`, and mirrored as a literal in favicon/amico.svg — every SMALL context, matching amicode's own "small → reduced" rule.
- `MarkDetailed` = amico.svg's full detailed mark (bracket path + internal circuit-pattern rects/polygons). Used ONLY by the Meet Amico home card (`w-12`/48px), large enough for the detail to resolve.
- `Mark`/`Splash`/`MarkDetailed` viewBox `0 0 64 56`/`116 287 3377 3035` → `0 0 3600 3600`; logo.css aspect-ratio `8/7` → `1/1` (the mark is square now).
- Still theme-adaptive via currentColor + var(--icon-strong-base) — this is a live webview DOM, so currentColor resolves (unlike amicode's native VS Code tab icon, which needs committed {light,dark} files; see amicode PR #99).
- NOT a re-established cross-repo sync promise — `MARK_PATH` is the single source of truth WITHIN this repo; it happens to match amicode's current geometry, kept aligned by hand when the mark changes.
- Tests: ui `bun test src/amicode` green; typecheck green ui + app.
- NOT done this round: full native `bun run script/build.ts` compile + vendored-binary swap — this patch only touches the embedded web UI. Deferred to the next amicode.N release tag, same split as patches #8/#11.
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
import { Show, type JSX } from "solid-js"
import { Logo, Mark } from "@opencode-ai/ui/logo"
import { Logo, MarkDetailed } from "@opencode-ai/ui/logo"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"

// amicode: new-session start screen, top→bottom — H-bot mark (hero), the AMICODE
// wordmark, tagline + how-it-works + starter chips, then the composer. Sizing
// mirrors the classic NewSessionView (Mark w-36 / Logo w-72); keeps the session
// tabs and centered composer. The chips are the one-tap path to the next task.
// The hero uses the DETAILED mark (144px, well above where the accents resolve).
export function NewSessionDesignView(props: { children: JSX.Element; gettingStarted?: JSX.Element }) {
return (
<div data-component="session-new-design" class="relative size-full overflow-y-auto bg-v2-background-bg-deep">
<div class="absolute inset-x-0 top-[12%] flex justify-center px-6 pb-24">
<div class={`${NEW_SESSION_CONTENT_WIDTH} flex flex-col items-center`}>
<Mark class="w-36 h-auto mb-4" />
<MarkDetailed class="w-36 h-auto mb-4" />
<Logo class="w-72 max-w-full h-auto mb-6" />
<Show when={props.gettingStarted}>
<div class="w-full flex justify-center mb-6">{props.gettingStarted}</div>
Expand Down
4 changes: 2 additions & 2 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 { Mark } from "@opencode-ai/ui/logo"
import { MarkDetailed } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"

const MAIN_WORKTREE = "main"
Expand DownExpand Up@@ -78,7 +78,7 @@ export function NewSessionView(props: NewSessionViewProps) {
<div class="flex-1 px-6 pb-30 flex items-center justify-center text-center">
<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">
<Mark class="w-28" />
<MarkDetailed class="w-28" />
{/* amicode: straight wordmark (the Logo's Racing Sans One face reads as
italic); byline keeps the brand attribution. */}
<div
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/home-cards.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createEffect, createMemo, on, type JSX, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -244,7 +244,7 @@ function MeetAmicoCard(props: { onStart: (prompt: string) => void }) {
data-slot="amicode-meet-identity"
style={{ display: "flex", gap: "12px", "align-items": "center", "margin-top": "10px" }}
>
<Mark class="w-12 h-auto shrink-0" />
<MarkDetailed class="w-12 h-auto shrink-0" />
<div style={{ "min-width": "0" }}>
<div style={{ "font-size": "18px", "font-weight": "600", color: "var(--v2-text-text-base)" }}>Amico</div>
<div style={{ "font-size": "12px", "line-height": "16px", color: "var(--v2-text-text-muted)" }}>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/amicode/onboarding-wizard.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { For, Show, createSignal, onCleanup } from "solid-js"
import { Mark } from "../components/logo"
import { Mark, MarkDetailed } from "../components/logo"
import {
institutionLogoUrl,
suggestInstitutions,
Expand DownExpand Up@@ -236,7 +236,7 @@ export function AmicodeOnboardingWizard(props: {
padding: "14px 0 4px",
}}
>
<Mark class="w-14 h-auto" />
<MarkDetailed class="w-14 h-auto" />
<div>
<div style={{ "font-size": "22px", "font-weight": "700", color: "var(--v2-text-text-base)" }}>
Welcome to Amicode
Expand Down
9 changes: 7 additions & 2 deletions packages/ui/src/amicode/run-card.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,12 @@
// trading card, not UI panel. Pure string-SVG so the same markup drives both
// the in-app gallery (innerHTML) and the PNG export (SVG → Image → canvas).

// The mark geometry lives in ONE place — logo.tsx's MARK_PATH (a plain string
// const). Importing it here keeps this card from drifting into a private copy
// of the glyph, which is what this consolidation fixes. The mark is square
// (viewBox 0 0 3600 3600).
import { MARK_PATH } from "../components/logo"

export type RunCardData = {
slug: string
problem: string
Expand DownExpand Up@@ -129,14 +135,13 @@ export function renderRunCardSvg(d: RunCardData): string {
}
}

const MARK = "M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
const mono = "ui-monospace, SFMono-Regular, Menlo, monospace"
const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"

return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<rect width="${W}" height="${H}" fill="${BG}"/>
<rect x="0" y="0" width="${W}" height="6" fill="${GOLD}"/>
<g transform="translate(80,64) scale(0.55)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK}"/></g>
<g transform="translate(80,64) scale(0.011)" fill="${GOLD}"><path fill-rule="evenodd" d="${MARK_PATH}"/></g>
<text x="130" y="88" font-family="${sans}" font-size="22" font-weight="700" letter-spacing="4" fill="${TEXT}">AMICODE</text>
<text x="262" y="88" font-family="${sans}" font-size="15" letter-spacing="3" fill="${MUTED}">SOLVED PULSE</text>

Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/amicode/spinner.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { type ComponentProps } from "solid-js"
import { MARK_PATH } from "../components/logo"

// AMICODE: working/thinking spinner — the Harmoniqs H-robot silhouette as a
// small monochrome glyph (H body with the screen slit knocked out via
Expand All@@ -10,6 +11,12 @@ import { type ComponentProps } from "solid-js"
// matches the stock spinner's own animation language. prefers-reduced-motion:
// static glyph, no animation (matchMedia guard; inline animations don't
// inherit the CSS-file media-query pattern used elsewhere).
//
// Path comes from logo.tsx's MARK_PATH (Mark/Splash now render the same
// glyph) instead of its own copy — this file used to carry an independent
// literal that happened to already match Mark/Splash's silhouette variant;
// consolidated so there's one geometry in this repo, not a second one that
// could quietly drift the way Mark/Splash's OWN old copy did.

const reducedMotion = () =>
typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
Expand All@@ -22,7 +29,7 @@ export function AmicoSpinner(props: {
return (
<svg
{...props}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
data-component="amico-spinner"
classList={{
...props.classList,
Expand All@@ -32,7 +39,7 @@ export function AmicoSpinner(props: {
>
<path
fill-rule="evenodd"
d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z M9 19h46v18H9Z"
d={MARK_PATH}
style={
reducedMotion()
? undefined
Expand Down
28 changes: 7 additions & 21 deletions packages/ui/src/assets/favicon/amico.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/ui/src/components/logo.css
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
[data-component="logo-mark"] {
width: 16px;
aspect-ratio: 8/7;
aspect-ratio: 1/1; /* mark viewBox is square (0 0 3600 3600) */
}
102 changes: 65 additions & 37 deletions packages/ui/src/components/logo.tsx
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
import { type ComponentProps } from "solid-js"

// AMICODE branding v2: Mark/Splash render the "digi" Harmoniqs H-robot
// (canonical source also lives at amicode:packages/extension/media/amico.svg,
// kept in sync manually); Logo renders the AMICODE wordmark. Component names,
// props, and data-component hooks are kept identical to stock. The robot body
// follows currentColor; the display rect + glyphs are fixed brand colors.
// viewBox is 64:56 (8:7, not square) — see logo.css aspect-ratio.
// AMICODE branding: Mark/Splash render the Harmoniqs H-robot mark; Logo
// renders the AMICODE wordmark. Component names, props, and data-component
// hooks are kept identical to stock. The mark is square (viewBox 0 0 3600
// 3600) — see logo.css aspect-ratio 1/1.
//
// Geometry mirrors amicode PR #99's two authored SVGs (amicode:
// packages/extension/media/amico{,_reduced}.svg), both square 0 0 3600 3600:
// MARK_PATH → amico_reduced.svg — the outer bracket (fill-rule evenodd
// knocks out the screen). The SMALL-size mark: Mark, Splash,
// AmicoSpinner (../amicode/spinner.tsx, imports MARK_PATH),
// and favicon/amico.svg (mirrors it as a literal — a static
// SVG can't import a TS module; keep its <path d>
// byte-identical to this constant).
// MarkDetailed → amico.svg — the full mark WITH the internal circuit-pattern
// accents, for LARGE contexts only (the Meet Amico card).
// This matches amicode's own "small → reduced, large → detailed" split, so
// the fork and the extension render the same brand mark. MARK_PATH is the
// single source of truth for the glyph within this repo; it is kept aligned
// with amicode's geometry by hand when the mark changes (no build-time link).
export const MARK_PATH =
"M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z"

const Robot = () => (
<>
<path fill="currentColor" d="M2 2h16v14h28V2h16v52H46V40H18v14H2Z" />
<rect x="9" y="19" width="46" height="18" fill="#0A0A0A" />
<g fill="#FFF676">
<rect x="17" y="21" width="2" height="2" />
<rect x="15" y="23" width="2" height="2" />
<rect x="13" y="25" width="2" height="2" />
<rect x="15" y="27" width="2" height="2" />
<rect x="17" y="29" width="2" height="2" />
<rect x="21" y="21" width="6" height="2" />
<rect x="21" y="23" width="2" height="6" />
<rect x="25" y="23" width="2" height="6" />
<rect x="21" y="29" width="6" height="2" />
<rect x="29" y="21" width="2" height="10" />
<rect x="33" y="21" width="2" height="10" />
<rect x="37" y="21" width="6" height="2" />
<rect x="37" y="23" width="2" height="6" />
<rect x="41" y="23" width="2" height="6" />
<rect x="37" y="29" width="6" height="2" />
<rect x="45" y="21" width="2" height="2" />
<rect x="47" y="23" width="2" height="2" />
<rect x="49" y="25" width="2" height="2" />
<rect x="47" y="27" width="2" height="2" />
<rect x="45" y="29" width="2" height="2" />
<polygon points="28,33 36,33 34,36 30,36" />
</g>
</>
)
const Robot = () => <path fill="currentColor" fill-rule="evenodd" d={MARK_PATH} />

export const Mark = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -57,7 +44,7 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
ref={props.ref}
data-component="logo-splash"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 64 56"
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
>
Expand All@@ -66,6 +53,47 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => {
)
}

// Detailed mark (amicode PR #99's amico.svg) — the full H-robot WITH the
// internal circuit-pattern accents, used ONLY where it renders large enough to
// resolve (the Meet Amico card, ~48px). currentColor + var(--icon-strong-base),
// same convention as Mark/Splash, so it stays theme-adaptive here — unlike
// amicode's native VS Code chat-tab icon, which needs committed light/dark SVG
// files because a native tab icon has no live CSS context for currentColor.
// Geometry mirrors amicode:packages/extension/media/amico.svg (viewBox
// 0 0 3600 3600); keep in sync by hand if that mark changes.
export const MarkDetailed = (props: { class?: string }) => {
return (
<svg
data-component="logo-mark-detailed"
classList={{ [props.class ?? ""]: !!props.class }}
viewBox="0 0 3600 3600"
xmlns="http://www.w3.org/2000/svg"
style={{ color: "var(--icon-strong-base)" }}
fill="currentColor"
>
<path d="M2279.19,374.09v622.56h-958.38V374.09H202.07v2851.83h1118.74v-520.15h958.38v520.15h1118.74V374.09h-1118.74ZM3165.55,2523.71H478.91v-1338.38h2686.65v1338.38Z" />
<rect x="1778.31" y="1312.43" width="107.11" height="692.38" />
<polygon points="2769.41 1463.57 2903.01 1463.57 2903.01 1601.01 2769.39 1601.01 2769.39 1463.6 2635.79 1463.6 2635.79 1326.16 2769.41 1326.16 2769.41 1463.57" />
<polygon points="3036.63 1738.45 2903.03 1738.45 2903.03 1875.89 2769.41 1875.89 2769.41 1738.45 2903.01 1738.45 2903.01 1601.01 3036.63 1601.01 3036.63 1738.45" />
<polygon points="2903.02 1875.89 2769.43 1875.89 2769.43 2013.33 2635.81 2013.33 2635.81 1875.89 2769.4 1875.89 2769.4 1738.45 2903.02 1738.45 2903.02 1875.89" />
<rect x="2373.03" y="1451.19" width="133.62" height="423.84" transform="translate(4879.6781 3326.2281) rotate(-180)" />
<rect x="2009.75" y="1451.19" width="133.62" height="423.84" transform="translate(4153.1112 3326.2281) rotate(-180)" />
<rect x="2143.56" y="1313.76" width="229.47" height="137.44" transform="translate(4516.5887 2764.9517) rotate(-180)" />
<rect x="2143.56" y="1875.03" width="229.47" height="137.44" transform="translate(4516.5887 3887.5046) rotate(-180)" />
<rect x="1503.05" y="1446.71" width="133.62" height="423.84" transform="translate(3139.725 3317.2494) rotate(-180)" />
<rect x="1139.77" y="1446.71" width="133.62" height="423.84" transform="translate(2413.1581 3317.2494) rotate(-180)" />
<rect x="1273.58" y="1309.27" width="229.47" height="137.44" transform="translate(2776.6357 2755.9729) rotate(-180)" />
<rect x="1273.58" y="1870.54" width="229.47" height="137.44" transform="translate(2776.6357 3878.5258) rotate(-180)" />
<polygon points="888.52 1864.8 754.93 1864.8 754.93 1727.36 888.55 1727.36 888.55 1864.77 1022.15 1864.77 1022.15 2002.21 888.52 2002.21 888.52 1864.8" />
<polygon points="621.31 1589.92 754.9 1589.92 754.9 1452.48 888.52 1452.48 888.52 1589.92 754.93 1589.92 754.93 1727.36 621.31 1727.36 621.31 1589.92" />
<polygon points="754.92 1452.48 888.51 1452.48 888.51 1315.04 1022.13 1315.04 1022.13 1452.48 888.54 1452.48 888.54 1589.92 754.92 1589.92 754.92 1452.48" />
<rect x="1648.65" y="2256.8" width="349.19" height="137.44" transform="translate(3646.502 4651.0383) rotate(-180)" />
<rect x="1510.91" y="2119.73" width="138.82" height="138.82" />
<rect x="1997.85" y="2117.98" width="138.82" height="138.82" />
</svg>
)
}

export const Logo = (props: { class?: string }) => {
return (
<svg
Expand Down
Loading