Spaces:
Running
design(ui): Awwwards-quality typography, SVG icons, lit-edge diagram tabs
Browse filesLanding hero:
- Compass 64β88px β scales with viewport without looking lost
- Headline 34β48px Anthropic Serif β makes it a moment, not a label
- "any" wrapped in gradient em β warm sienna accent highlights the key promise
- Onboarding step p: var(--muted)βvar(--text-2) β body text on a learning tool
must be readable, not decorative
Diagram mode tabs:
- Unicode β⬑⫠replaced with inline SVG TabIcon component β infinitely crisp at
every DPI; unicode glyphs rasterise at screen resolution and look fuzzy at 2Γ
- "Lit edge" ::before gradient top-line on active card β the same depth signal
used by Linear, Vercel, Raycast; materialises light catching a raised edge
- Padding 8px 14px β 11px 18px, min-width 130β150px β feature cards not form controls
- Icon color transitions on hover/active β guides the eye to the selection
- diagram-type-desc: var(--muted)βvar(--text-2) β readable context
Chat suggest state:
- h2: 22β28px β repo selected is an invitation, needs presence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ui/src/App.jsx +2 -2
- ui/src/components/DiagramView.jsx +43 -5
- ui/src/index.css +64 -21
|
@@ -728,7 +728,7 @@ export default function App() {
|
|
| 728 |
// No repo selected yet (landing), or All repos selected but nothing indexed
|
| 729 |
<div className="onboarding-steps">
|
| 730 |
<div className="onboarding-header">
|
| 731 |
-
<svg width="
|
| 732 |
{/* N β pulses bright */}
|
| 733 |
<path className="compass-north" d="M12 2 L14.5 7 L12 12 L9.5 7 Z" fill="var(--accent)"/>
|
| 734 |
{/* S β delayed fade */}
|
|
@@ -740,7 +740,7 @@ export default function App() {
|
|
| 740 |
{/* Center */}
|
| 741 |
<circle cx="12" cy="12" r="1.4" fill="var(--accent)"/>
|
| 742 |
</svg>
|
| 743 |
-
<div className="onboarding-headline">Map any codebase</div>
|
| 744 |
<div className="onboarding-sub">Index any public repo and ask questions about the code β architecture, data flow, classes, functions, and more.</div>
|
| 745 |
</div>
|
| 746 |
<div className="onboarding-step active">
|
|
|
|
| 728 |
// No repo selected yet (landing), or All repos selected but nothing indexed
|
| 729 |
<div className="onboarding-steps">
|
| 730 |
<div className="onboarding-header">
|
| 731 |
+
<svg width="88" height="88" viewBox="0 0 24 24" fill="none" style={{ marginBottom: 20 }}>
|
| 732 |
{/* N β pulses bright */}
|
| 733 |
<path className="compass-north" d="M12 2 L14.5 7 L12 12 L9.5 7 Z" fill="var(--accent)"/>
|
| 734 |
{/* S β delayed fade */}
|
|
|
|
| 740 |
{/* Center */}
|
| 741 |
<circle cx="12" cy="12" r="1.4" fill="var(--accent)"/>
|
| 742 |
</svg>
|
| 743 |
+
<div className="onboarding-headline">Map <em>any</em> codebase</div>
|
| 744 |
<div className="onboarding-sub">Index any public repo and ask questions about the code β architecture, data flow, classes, functions, and more.</div>
|
| 745 |
</div>
|
| 746 |
<div className="onboarding-step active">
|
|
@@ -22,7 +22,7 @@ import NodeDetailPanel from "./NodeDetailPanel";
|
|
| 22 |
|
| 23 |
// ββ Diagram tab definitions βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 24 |
const EXPLORE_TAB = {
|
| 25 |
-
id: "explore", label: "Explore", desc: "Guided concept tour",
|
| 26 |
};
|
| 27 |
|
| 28 |
// Only AST-verified diagrams β Sequence and Data Flow were removed because
|
|
@@ -33,16 +33,54 @@ const DIAGRAM_TABS = [
|
|
| 33 |
id: "architecture",
|
| 34 |
label: "Architecture",
|
| 35 |
desc: "Components & connections",
|
| 36 |
-
icon: "⬑",
|
| 37 |
},
|
| 38 |
{
|
| 39 |
id: "class",
|
| 40 |
label: "Class Hierarchy",
|
| 41 |
desc: "Classes & relationships",
|
| 42 |
-
icon: "β«",
|
| 43 |
},
|
| 44 |
];
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
const ALL_TABS = [EXPLORE_TAB, ...DIAGRAM_TABS];
|
| 47 |
|
| 48 |
export default function DiagramView({ repo, onAskAbout, focusFiles }) {
|
|
@@ -246,7 +284,7 @@ export default function DiagramView({ repo, onAskAbout, focusFiles }) {
|
|
| 246 |
: "rgba(212,132,90,0.25)",
|
| 247 |
}}
|
| 248 |
>
|
| 249 |
-
<span className="diagram-type-icon" style={{ color: "var(--accent-soft)" }}>
|
| 250 |
<span className="diagram-type-label" style={{ color: diagramType === "explore" ? "var(--accent)" : "var(--accent-soft)" }}>Explore</span>
|
| 251 |
<span className="diagram-type-desc">Guided concept tour</span>
|
| 252 |
</button>
|
|
@@ -265,7 +303,7 @@ export default function DiagramView({ repo, onAskAbout, focusFiles }) {
|
|
| 265 |
className={`diagram-type-btn${diagramType === t.id ? " active" : ""}`}
|
| 266 |
onClick={() => setType(t.id)}
|
| 267 |
>
|
| 268 |
-
<span className="diagram-type-icon">{t.
|
| 269 |
<span className="diagram-type-label">{t.label}</span>
|
| 270 |
<span className="diagram-type-desc">{t.desc}</span>
|
| 271 |
</button>
|
|
|
|
| 22 |
|
| 23 |
// ββ Diagram tab definitions βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 24 |
const EXPLORE_TAB = {
|
| 25 |
+
id: "explore", label: "Explore", desc: "Guided concept tour",
|
| 26 |
};
|
| 27 |
|
| 28 |
// Only AST-verified diagrams β Sequence and Data Flow were removed because
|
|
|
|
| 33 |
id: "architecture",
|
| 34 |
label: "Architecture",
|
| 35 |
desc: "Components & connections",
|
|
|
|
| 36 |
},
|
| 37 |
{
|
| 38 |
id: "class",
|
| 39 |
label: "Class Hierarchy",
|
| 40 |
desc: "Classes & relationships",
|
|
|
|
| 41 |
},
|
| 42 |
];
|
| 43 |
|
| 44 |
+
// ββ Tab icons (SVG) βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 45 |
+
// Inline SVGs render crisp at every DPI β unicode glyphs (β ⬑ β«) are
|
| 46 |
+
// rasterised at screen resolution and look blurry on high-DPI displays.
|
| 47 |
+
function TabIcon({ id }) {
|
| 48 |
+
if (id === "explore") return (
|
| 49 |
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
| 50 |
+
{/* Compass rose β N point pulses in the landing animation */}
|
| 51 |
+
<path d="M12 2L14.5 7.5L12 12L9.5 7.5Z" fill="currentColor"/>
|
| 52 |
+
<path d="M12 22L13.5 16.5L12 12L10.5 16.5Z" fill="currentColor" opacity="0.45"/>
|
| 53 |
+
<path d="M22 12L16.5 10.5L12 12L16.5 13.5Z" fill="currentColor" opacity="0.45"/>
|
| 54 |
+
<path d="M2 12L7.5 10.5L12 12L7.5 13.5Z" fill="currentColor" opacity="0.45"/>
|
| 55 |
+
<circle cx="12" cy="12" r="1.5" fill="currentColor"/>
|
| 56 |
+
</svg>
|
| 57 |
+
);
|
| 58 |
+
if (id === "architecture") return (
|
| 59 |
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
|
| 60 |
+
stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" aria-hidden="true">
|
| 61 |
+
{/* Three nodes connected by edges β represents a dependency/import graph */}
|
| 62 |
+
<circle cx="5" cy="12" r="2.5"/>
|
| 63 |
+
<circle cx="19" cy="6.5" r="2.5"/>
|
| 64 |
+
<circle cx="19" cy="17.5" r="2.5"/>
|
| 65 |
+
<line x1="7.4" y1="11" x2="16.6" y2="7.5"/>
|
| 66 |
+
<line x1="7.4" y1="13" x2="16.6" y2="16.5"/>
|
| 67 |
+
</svg>
|
| 68 |
+
);
|
| 69 |
+
if (id === "class") return (
|
| 70 |
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
|
| 71 |
+
stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
| 72 |
+
{/* Parent node + two children β represents inheritance / class tree */}
|
| 73 |
+
<rect x="8.5" y="2" width="7" height="5" rx="1.5"/>
|
| 74 |
+
<rect x="1" y="17" width="7" height="5" rx="1.5"/>
|
| 75 |
+
<rect x="16" y="17" width="7" height="5" rx="1.5"/>
|
| 76 |
+
<line x1="12" y1="7" x2="12" y2="12"/>
|
| 77 |
+
<line x1="12" y1="12" x2="4.5" y2="17"/>
|
| 78 |
+
<line x1="12" y1="12" x2="19.5" y2="17"/>
|
| 79 |
+
</svg>
|
| 80 |
+
);
|
| 81 |
+
return null;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
const ALL_TABS = [EXPLORE_TAB, ...DIAGRAM_TABS];
|
| 85 |
|
| 86 |
export default function DiagramView({ repo, onAskAbout, focusFiles }) {
|
|
|
|
| 284 |
: "rgba(212,132,90,0.25)",
|
| 285 |
}}
|
| 286 |
>
|
| 287 |
+
<span className="diagram-type-icon" style={{ color: "var(--accent-soft)" }}><TabIcon id="explore" /></span>
|
| 288 |
<span className="diagram-type-label" style={{ color: diagramType === "explore" ? "var(--accent)" : "var(--accent-soft)" }}>Explore</span>
|
| 289 |
<span className="diagram-type-desc">Guided concept tour</span>
|
| 290 |
</button>
|
|
|
|
| 303 |
className={`diagram-type-btn${diagramType === t.id ? " active" : ""}`}
|
| 304 |
onClick={() => setType(t.id)}
|
| 305 |
>
|
| 306 |
+
<span className="diagram-type-icon"><TabIcon id={t.id} /></span>
|
| 307 |
<span className="diagram-type-label">{t.label}</span>
|
| 308 |
<span className="diagram-type-desc">{t.desc}</span>
|
| 309 |
</button>
|
|
@@ -1319,15 +1319,26 @@ textarea:focus-visible {
|
|
| 1319 |
}
|
| 1320 |
|
| 1321 |
.onboarding-headline {
|
| 1322 |
-
|
|
|
|
| 1323 |
font-weight: 400;
|
| 1324 |
font-family: "Anthropic Serif", Georgia, serif;
|
| 1325 |
-
letter-spacing: -0.
|
| 1326 |
-
line-height: 1.
|
| 1327 |
-
margin-bottom:
|
| 1328 |
color: var(--text);
|
| 1329 |
}
|
| 1330 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1331 |
.onboarding-sub {
|
| 1332 |
font-size: 15px;
|
| 1333 |
color: var(--text-2);
|
|
@@ -1422,7 +1433,8 @@ textarea:focus-visible {
|
|
| 1422 |
}
|
| 1423 |
.onboarding-step p {
|
| 1424 |
font-size: 13px;
|
| 1425 |
-
|
|
|
|
| 1426 |
margin: 3px 0;
|
| 1427 |
line-height: 1.55;
|
| 1428 |
}
|
|
@@ -1441,7 +1453,8 @@ textarea:focus-visible {
|
|
| 1441 |
/* Suggest state */
|
| 1442 |
.suggest-state { max-width: 540px; width: 100%; text-align: center; }
|
| 1443 |
.suggest-state h2 {
|
| 1444 |
-
|
|
|
|
| 1445 |
font-weight: 400;
|
| 1446 |
font-family: "Anthropic Serif", Georgia, serif;
|
| 1447 |
letter-spacing: -0.02em;
|
|
@@ -2121,50 +2134,80 @@ textarea:focus-visible {
|
|
| 2121 |
.diagram-type-bar {
|
| 2122 |
display: flex;
|
| 2123 |
gap: 8px;
|
| 2124 |
-
padding:
|
| 2125 |
border-bottom: 1px solid var(--border-subtle);
|
| 2126 |
flex-shrink: 0;
|
| 2127 |
-
background: rgba(20, 18, 14, 0.
|
| 2128 |
}
|
| 2129 |
|
| 2130 |
.diagram-type-btn {
|
| 2131 |
display: flex;
|
| 2132 |
flex-direction: column;
|
| 2133 |
align-items: flex-start;
|
| 2134 |
-
gap:
|
| 2135 |
-
padding
|
| 2136 |
-
|
|
|
|
| 2137 |
border: 1px solid var(--border);
|
| 2138 |
background: transparent;
|
| 2139 |
cursor: pointer;
|
| 2140 |
-
|
| 2141 |
-
|
|
|
|
|
|
|
|
|
|
| 2142 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2143 |
.diagram-type-btn:hover {
|
| 2144 |
background: rgba(237,228,206,0.04);
|
| 2145 |
border-color: var(--accent-border);
|
| 2146 |
}
|
| 2147 |
.diagram-type-btn.active {
|
| 2148 |
-
background: rgba(212,132,90,0.
|
| 2149 |
-
border-color: rgba(212,132,90,0.
|
|
|
|
| 2150 |
}
|
|
|
|
| 2151 |
|
| 2152 |
.diagram-type-icon {
|
| 2153 |
-
|
| 2154 |
-
|
| 2155 |
-
|
|
|
|
|
|
|
|
|
|
| 2156 |
}
|
|
|
|
|
|
|
|
|
|
| 2157 |
.diagram-type-label {
|
| 2158 |
-
font-size:
|
| 2159 |
font-weight: 600;
|
| 2160 |
color: var(--text);
|
| 2161 |
font-family: var(--mono);
|
|
|
|
| 2162 |
}
|
| 2163 |
.diagram-type-btn.active .diagram-type-label { color: var(--accent); }
|
| 2164 |
|
|
|
|
| 2165 |
.diagram-type-desc {
|
| 2166 |
-
font-size:
|
| 2167 |
-
color: var(--
|
|
|
|
| 2168 |
}
|
| 2169 |
|
| 2170 |
/* ββ Canvas ββ */
|
|
|
|
| 1319 |
}
|
| 1320 |
|
| 1321 |
.onboarding-headline {
|
| 1322 |
+
/* 48px makes this a moment, not a label β the first thing that lands */
|
| 1323 |
+
font-size: 48px;
|
| 1324 |
font-weight: 400;
|
| 1325 |
font-family: "Anthropic Serif", Georgia, serif;
|
| 1326 |
+
letter-spacing: -0.03em;
|
| 1327 |
+
line-height: 1.05;
|
| 1328 |
+
margin-bottom: 14px;
|
| 1329 |
color: var(--text);
|
| 1330 |
}
|
| 1331 |
|
| 1332 |
+
/* "any" in "Map any codebase" β gradient accent highlights the key idea:
|
| 1333 |
+
that Cartographer works on every repo, not just pre-selected ones */
|
| 1334 |
+
.onboarding-headline em {
|
| 1335 |
+
font-style: normal;
|
| 1336 |
+
background: linear-gradient(110deg, var(--accent-soft) 0%, var(--accent) 65%);
|
| 1337 |
+
-webkit-background-clip: text;
|
| 1338 |
+
-webkit-text-fill-color: transparent;
|
| 1339 |
+
background-clip: text;
|
| 1340 |
+
}
|
| 1341 |
+
|
| 1342 |
.onboarding-sub {
|
| 1343 |
font-size: 15px;
|
| 1344 |
color: var(--text-2);
|
|
|
|
| 1433 |
}
|
| 1434 |
.onboarding-step p {
|
| 1435 |
font-size: 13px;
|
| 1436 |
+
/* text-2 not muted β this is the body of a learning step, not decoration */
|
| 1437 |
+
color: var(--text-2);
|
| 1438 |
margin: 3px 0;
|
| 1439 |
line-height: 1.55;
|
| 1440 |
}
|
|
|
|
| 1453 |
/* Suggest state */
|
| 1454 |
.suggest-state { max-width: 540px; width: 100%; text-align: center; }
|
| 1455 |
.suggest-state h2 {
|
| 1456 |
+
/* 28px gives this moment presence β you've chosen a repo, this is the invitation */
|
| 1457 |
+
font-size: 28px;
|
| 1458 |
font-weight: 400;
|
| 1459 |
font-family: "Anthropic Serif", Georgia, serif;
|
| 1460 |
letter-spacing: -0.02em;
|
|
|
|
| 2134 |
.diagram-type-bar {
|
| 2135 |
display: flex;
|
| 2136 |
gap: 8px;
|
| 2137 |
+
padding: 14px 24px;
|
| 2138 |
border-bottom: 1px solid var(--border-subtle);
|
| 2139 |
flex-shrink: 0;
|
| 2140 |
+
background: rgba(20, 18, 14, 0.70);
|
| 2141 |
}
|
| 2142 |
|
| 2143 |
.diagram-type-btn {
|
| 2144 |
display: flex;
|
| 2145 |
flex-direction: column;
|
| 2146 |
align-items: flex-start;
|
| 2147 |
+
gap: 3px;
|
| 2148 |
+
/* More generous padding β these are feature entry points, not list items */
|
| 2149 |
+
padding: 11px 18px;
|
| 2150 |
+
border-radius: var(--radius-md);
|
| 2151 |
border: 1px solid var(--border);
|
| 2152 |
background: transparent;
|
| 2153 |
cursor: pointer;
|
| 2154 |
+
/* position: relative anchors the ::before gradient top-edge line */
|
| 2155 |
+
position: relative;
|
| 2156 |
+
overflow: hidden;
|
| 2157 |
+
transition: background var(--transition), border-color var(--transition), box-shadow var(--transition);
|
| 2158 |
+
min-width: 150px;
|
| 2159 |
}
|
| 2160 |
+
|
| 2161 |
+
/* "Lit edge" technique β a gradient line materialises at the top of the
|
| 2162 |
+
active card, mimicking light catching a raised edge. Seen on Linear,
|
| 2163 |
+
Vercel, Raycast. Invisible by default; only the active card shows it. */
|
| 2164 |
+
.diagram-type-btn::before {
|
| 2165 |
+
content: '';
|
| 2166 |
+
position: absolute;
|
| 2167 |
+
top: 0; left: 12px; right: 12px;
|
| 2168 |
+
height: 2px;
|
| 2169 |
+
border-radius: 0 0 2px 2px;
|
| 2170 |
+
background: linear-gradient(90deg, transparent, var(--accent), var(--accent-soft), transparent);
|
| 2171 |
+
opacity: 0;
|
| 2172 |
+
transition: opacity var(--transition);
|
| 2173 |
+
}
|
| 2174 |
+
|
| 2175 |
.diagram-type-btn:hover {
|
| 2176 |
background: rgba(237,228,206,0.04);
|
| 2177 |
border-color: var(--accent-border);
|
| 2178 |
}
|
| 2179 |
.diagram-type-btn.active {
|
| 2180 |
+
background: rgba(212,132,90,0.12);
|
| 2181 |
+
border-color: rgba(212,132,90,0.45);
|
| 2182 |
+
box-shadow: 0 4px 20px rgba(212,132,90,0.08), inset 0 0 0 0 transparent;
|
| 2183 |
}
|
| 2184 |
+
.diagram-type-btn.active::before { opacity: 1; }
|
| 2185 |
|
| 2186 |
.diagram-type-icon {
|
| 2187 |
+
/* Block display so the SVG sits on its own line above the label */
|
| 2188 |
+
display: flex;
|
| 2189 |
+
align-items: center;
|
| 2190 |
+
margin-bottom: 4px;
|
| 2191 |
+
color: var(--muted);
|
| 2192 |
+
transition: color var(--transition);
|
| 2193 |
}
|
| 2194 |
+
.diagram-type-btn.active .diagram-type-icon,
|
| 2195 |
+
.diagram-type-btn:hover .diagram-type-icon { color: var(--accent-soft); }
|
| 2196 |
+
|
| 2197 |
.diagram-type-label {
|
| 2198 |
+
font-size: 13px;
|
| 2199 |
font-weight: 600;
|
| 2200 |
color: var(--text);
|
| 2201 |
font-family: var(--mono);
|
| 2202 |
+
letter-spacing: -0.01em;
|
| 2203 |
}
|
| 2204 |
.diagram-type-btn.active .diagram-type-label { color: var(--accent); }
|
| 2205 |
|
| 2206 |
+
/* text-2 not muted β the description is the context, not decoration */
|
| 2207 |
.diagram-type-desc {
|
| 2208 |
+
font-size: 11px;
|
| 2209 |
+
color: var(--text-2);
|
| 2210 |
+
letter-spacing: -0.01em;
|
| 2211 |
}
|
| 2212 |
|
| 2213 |
/* ββ Canvas ββ */
|