style(frontend): look&feel v2 refinements — 70% gate modal, structured cards, mono labels
- ArtifactGateWidget: gate modal 90%→70% of viewport, larger heading, more padding. - ArtifactView StructuredValue: depth-aware hierarchy (top-level hairline dividers, warm bg-muted card surfaces + shadow for object-array items). - index.css: .thot-label micro-labels switched to the mono register (SF Mono) with tightened tracking, distinguishing labels/meta from sans body prose. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
committed by
Marco Pancotti
co-authored by
Claude Opus 4.8
parent
9fe1c9321b
commit
e9b2934ef7
@@ -77,8 +77,10 @@ function humanizeKey(k: string): string {
|
||||
|
||||
/** Renders any artifact payload as readable text — NEVER raw JSON. Objects become
|
||||
* labelled sections, arrays become bullet lists, primitives become paragraphs;
|
||||
* strings render as markdown. Nested structures recurse. */
|
||||
function StructuredValue({ value }: { value: unknown }): ReactElement {
|
||||
* strings render as markdown. Nested structures recurse. `depth` drives the
|
||||
* visual hierarchy: top-level sections are split by hairline rules, nested fields
|
||||
* sit tighter, and object-arrays become grouped cards. */
|
||||
function StructuredValue({ value, depth = 0 }: { value: unknown; depth?: number }): ReactElement {
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return <p className="text-sm text-muted-foreground">—</p>;
|
||||
}
|
||||
@@ -98,21 +100,32 @@ function StructuredValue({ value }: { value: unknown }): ReactElement {
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
{value.map((item, i) => (
|
||||
<div key={i} className="rounded-lg border border-border/60 p-3">
|
||||
<StructuredValue value={item} />
|
||||
<div
|
||||
key={i}
|
||||
className="rounded-xl border border-border/70 bg-muted/50 p-4 shadow-xs"
|
||||
>
|
||||
<StructuredValue value={item} depth={depth + 1} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const entries = Object.entries(value as Record<string, unknown>);
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{Object.entries(value as Record<string, unknown>).map(([k, v]) => (
|
||||
<section key={k}>
|
||||
<h4 className="thot-label mb-1.5">
|
||||
{humanizeKey(k)}
|
||||
</h4>
|
||||
<StructuredValue value={v} />
|
||||
<div className="flex flex-col">
|
||||
{entries.map(([k, v], i) => (
|
||||
<section
|
||||
key={k}
|
||||
className={
|
||||
i === 0
|
||||
? undefined
|
||||
: depth === 0
|
||||
? "mt-5 border-t border-border/50 pt-5"
|
||||
: "mt-3"
|
||||
}
|
||||
>
|
||||
<h4 className="thot-label mb-1.5">{humanizeKey(k)}</h4>
|
||||
<StructuredValue value={v} depth={depth + 1} />
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user