fix(ui): unify Memory and Evidence reading typography
This commit is contained in:
@@ -29,7 +29,7 @@ function CopyText({ text, label, showText = true }: { text: string; label: strin
|
||||
const { t } = useI18n();
|
||||
const [notice, setNotice] = useState("");
|
||||
return <div className="flex min-w-0 flex-wrap items-start gap-2">
|
||||
{showText && <code className="min-w-0 flex-1 [overflow-wrap:anywhere] text-xs select-text">{text}</code>}
|
||||
{showText && <code className="min-w-0 flex-1 [overflow-wrap:anywhere] text-sm select-text">{text}</code>}
|
||||
<Button size="icon" variant="ghost" title={t("Copy {label}", { label })} aria-label={t("Copy {label}", { label })} onClick={async () => {
|
||||
try { await navigator.clipboard.writeText(text); setNotice("Copied"); }
|
||||
catch { setNotice("Select the text and copy it manually."); }
|
||||
@@ -131,15 +131,15 @@ function UnitDetail({ item, location }: { item: EvidenceUnit; location: Evidence
|
||||
const { t } = useI18n();
|
||||
const source = item.provenance.kind === "manual" ? item.provenance.original : item.provenance;
|
||||
const file = location.workspace ? `${location.workspace.replace(/\\/g, "/")}/evidence/${item.file}` : null;
|
||||
return <article className="min-w-0 w-full space-y-5 pb-8 [overflow-wrap:anywhere]">
|
||||
<header><p className="text-xs text-muted-foreground">{t(states[item.status])} · {item.purposes.map(purpose => t(purpose.replaceAll("_", " "))).join(", ")}</p><h2 className="mt-1 font-sans text-xl font-semibold">{item.title}</h2></header>
|
||||
return <article className="thot-knowledge-reader space-y-6 pb-8">
|
||||
<header><p className="thot-knowledge-meta text-muted-foreground">{t(states[item.status])} · {item.purposes.map(purpose => t(purpose.replaceAll("_", " "))).join(", ")}</p><h2 className="thot-knowledge-title">{item.title}</h2></header>
|
||||
{file && <CopyText text={file} label={t("Evidence file path")} />}
|
||||
{item.status !== "active" && <p className="text-sm text-muted-foreground">{["invalid", "removed"].includes(item.status) ? t("The file is invalid or removed. Its last active content is shown here.") : t("This is the file version awaiting consolidation.")} {t("The core continues to use its last active version.")}</p>}
|
||||
{Object.entries(item.applies_to).some(([, values]) => values.length > 0) && <section><h3 className="font-medium">{t("Scope")}</h3>{Object.entries(item.applies_to).map(([name, values]) => values.length > 0 && <p className="mt-2 break-words text-sm" key={name}><span className="capitalize">{t(name)}</span>: {values.join(", ")}</p>)}</section>}
|
||||
{Object.entries(item.payload).map(([field, value]) => <section key={field}><h3 className="mb-2 text-sm font-semibold capitalize">{t(field.replaceAll("_", " "))}</h3>{typeof value === "string" ? field === "sql" ? <pre className="overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] rounded-md bg-muted p-3 text-sm">{value}</pre> : <MarkdownView source={value} readable /> : Array.isArray(value) ? <ul className="list-inside list-disc space-y-2 text-sm">{value.map(v => <li key={v}>{v}</li>)}</ul> : <dl className="space-y-2 text-sm">{Object.entries(value).map(([key, text]) => <div key={key}><dt className="font-mono font-medium">{key || t("(empty value)")}</dt><dd><MarkdownView source={text} readable /></dd></div>)}</dl>}</section>)}
|
||||
<section className="border-t pt-4"><h3 className="font-medium">{t("Provenance")}</h3><p className="mt-2 text-sm">{item.provenance.kind === "manual" ? t("Manual declaration by {author}.", { author: item.provenance.declared_by ?? "" }) : t("Derived from a source document.")}</p>
|
||||
{source?.source_file && <><p className="mt-2 [overflow-wrap:anywhere] text-xs">{item.provenance.kind === "manual" ? t("Original document (lineage): ") : t("Source: ")}{source.source_file}</p>{source.supporting_excerpts?.map((text, i) => <div key={i} className="mt-4"><MarkdownView source={text} readable /></div>)}</>}
|
||||
{Object.entries(item.applies_to).some(([, values]) => values.length > 0) && <section><h3 className="thot-knowledge-heading">{t("Scope")}</h3>{Object.entries(item.applies_to).map(([name, values]) => values.length > 0 && <p className="mt-3" key={name}><span className="capitalize">{t(name)}</span>: {values.join(", ")}</p>)}</section>}
|
||||
{Object.entries(item.payload).map(([field, value]) => <section key={field}><h3 className="thot-knowledge-heading capitalize">{t(field.replaceAll("_", " "))}</h3>{typeof value === "string" ? field === "sql" ? <pre className="overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] rounded-md bg-muted p-3">{value}</pre> : <MarkdownView source={value} readable /> : Array.isArray(value) ? <ul className="list-outside list-disc space-y-2 pl-5">{value.map(v => <li key={v}>{v}</li>)}</ul> : <dl className="space-y-4">{Object.entries(value).map(([key, text]) => <div key={key}><dt className="mb-2 font-semibold">{key ? <code>{key}</code> : t("(empty value)")}</dt><dd><MarkdownView source={text} readable /></dd></div>)}</dl>}</section>)}
|
||||
<section className="border-t pt-6"><h3 className="thot-knowledge-heading">{t("Provenance")}</h3><p>{item.provenance.kind === "manual" ? t("Manual declaration by {author}.", { author: item.provenance.declared_by ?? "" }) : t("Derived from a source document.")}</p>
|
||||
{source?.source_file && <><p className="thot-knowledge-meta mt-3">{item.provenance.kind === "manual" ? t("Original document (lineage): ") : t("Source: ")}<code>{source.source_file}</code></p>{source.supporting_excerpts?.map((text, i) => <div key={i} className="mt-5"><MarkdownView source={text} readable /></div>)}</>}
|
||||
</section>
|
||||
{item.review_items.length > 0 && <section><h3 className="font-medium">{t("Review required")}</h3>{item.review_items.map((review, i) => <p key={i} className="mt-2 text-sm">{t(review.message)}</p>)}</section>}
|
||||
{item.review_items.length > 0 && <section><h3 className="thot-knowledge-heading">{t("Review required")}</h3>{item.review_items.map((review, i) => <p key={i} className="mt-3">{t(review.message)}</p>)}</section>}
|
||||
</article>;
|
||||
}
|
||||
|
||||
@@ -180,21 +180,21 @@ function MemoryWorkspace({ workspace, onDirtyChange, onBusyChange }: {
|
||||
</section>
|
||||
<section aria-label={t("Memory detail")} className="thot-administration-detail">
|
||||
{!draft && !selected && <p className="py-8 text-sm text-muted-foreground">{t("Select a card to inspect its content, scope and provenance.")}</p>}
|
||||
{selected && !draft && <div className="min-w-0 w-full space-y-5 [overflow-wrap:anywhere]">
|
||||
{selected && !draft && <div className="thot-knowledge-reader space-y-6">
|
||||
{formattingExample && <p role="note" className="rounded-md border bg-muted p-3 text-sm">{t("Simulated cards for visual review only. Not saved, indexed or sent to the model.")}</p>}
|
||||
<div><p className="text-xs text-muted-foreground">{t(families[selected.family])}</p><h2 className="mt-1 break-words font-sans text-xl font-semibold">{selected.subject}</h2></div>
|
||||
<dl className="space-y-4 text-sm">
|
||||
<div><p className="thot-knowledge-meta text-muted-foreground">{t(families[selected.family])}</p><h2 className="thot-knowledge-title">{selected.subject}</h2></div>
|
||||
<dl className="space-y-6">
|
||||
{(["detail", "scope", "rationale", "question", "sql"] as const).filter(k => selected[k]).map(key => <div key={key}>
|
||||
<dt className="mb-1 font-semibold capitalize">{key === "detail" ? t("Content") : key === "sql" ? t("Approved SQL") : t(key)}</dt>
|
||||
<dd className="min-w-0 w-full">{key === "sql" ? <pre className="whitespace-pre-wrap [overflow-wrap:anywhere] rounded-md bg-muted p-3 font-mono text-sm">{selected[key]}</pre> : <MarkdownView source={selected[key]} readable />}</dd></div>)}
|
||||
<div><dt className="font-semibold">{t("Provenance")}</dt><dd>{formattingExample ? t("Formatting example, not workspace knowledge.") : selected.origin === "manual" ? t("Created manually") : t("Workflow session {session}", { session: selected.session_id ?? "" })}</dd></div>
|
||||
{selected.decision_seq !== null && <div><dt className="font-semibold">{t("Source decision")}</dt><dd>{selected.decision_seq}</dd></div>}
|
||||
<div><dt className="font-semibold">{t("Card ID")}</dt><dd className="break-all font-mono text-xs">{selected.id}</dd></div>
|
||||
<div><dt className="font-semibold">{t("Updated")}</dt><dd>{new Date(selected.updated_at).toLocaleString(getLocale())}</dd></div>
|
||||
<div><dt className="font-semibold">{t("Created")}</dt><dd>{new Date(selected.created_at).toLocaleString(getLocale())}</dd></div>
|
||||
{selected.concepts.length > 0 && <div><dt className="font-semibold">{t("Concepts")}</dt><dd>{selected.concepts.join(", ")}</dd></div>}
|
||||
{selected.dependencies.length > 0 && <div><dt className="font-semibold">{t("Schema dependencies")}</dt><dd><ul>{selected.dependencies.map((d, i) => <li key={i}>{[d.database, d.schema_name, d.table, d.column].filter(Boolean).join(" / ")}</li>)}</ul></dd></div>}
|
||||
{selected.links.length > 0 && <div><dt className="font-semibold">{t("Linked cards")}</dt><dd><ul>{selected.links.map(link => <li key={link.target_id} className="my-2"><button className="text-left text-primary underline" onClick={() => void open(link.target_id)}>{link.meaning}</button></li>)}</ul></dd></div>}
|
||||
<dt className="thot-knowledge-heading capitalize">{key === "detail" ? t("Content") : key === "sql" ? t("Approved SQL") : t(key)}</dt>
|
||||
<dd className="min-w-0 w-full">{key === "sql" ? <pre className="whitespace-pre-wrap [overflow-wrap:anywhere] rounded-md bg-muted p-3">{selected[key]}</pre> : <MarkdownView source={selected[key]} readable />}</dd></div>)}
|
||||
<div className="border-t pt-6"><dt className="thot-knowledge-heading">{t("Provenance")}</dt><dd>{formattingExample ? t("Formatting example, not workspace knowledge.") : selected.origin === "manual" ? t("Created manually") : t("Workflow session {session}", { session: selected.session_id ?? "" })}</dd></div>
|
||||
{selected.decision_seq !== null && <div className="thot-knowledge-meta"><dt>{t("Source decision")}</dt><dd>{selected.decision_seq}</dd></div>}
|
||||
<div className="thot-knowledge-meta"><dt>{t("Card ID")}</dt><dd><code>{selected.id}</code></dd></div>
|
||||
<div className="thot-knowledge-meta"><dt>{t("Updated")}</dt><dd>{new Date(selected.updated_at).toLocaleString(getLocale())}</dd></div>
|
||||
<div className="thot-knowledge-meta"><dt>{t("Created")}</dt><dd>{new Date(selected.created_at).toLocaleString(getLocale())}</dd></div>
|
||||
{selected.concepts.length > 0 && <div><dt className="thot-knowledge-heading">{t("Concepts")}</dt><dd>{selected.concepts.join(", ")}</dd></div>}
|
||||
{selected.dependencies.length > 0 && <div><dt className="thot-knowledge-heading">{t("Schema dependencies")}</dt><dd><ul className="list-outside list-disc space-y-2 pl-5">{selected.dependencies.map((d, i) => <li key={i}><code>{[d.database, d.schema_name, d.table, d.column].filter(Boolean).join(" / ")}</code></li>)}</ul></dd></div>}
|
||||
{selected.links.length > 0 && <div><dt className="thot-knowledge-heading">{t("Linked cards")}</dt><dd><ul>{selected.links.map(link => <li key={link.target_id} className="my-2"><button className="text-left text-primary underline" onClick={() => void open(link.target_id)}>{link.meaning}</button></li>)}</ul></dd></div>}
|
||||
</dl>
|
||||
{!formattingExample && <div className="flex gap-2"><Button disabled={busy} onClick={() => setDraft(memoryInput(selected))}>{t("Edit card")}</Button>
|
||||
<Button variant="outline" disabled={busy} onClick={() => setDeleteConfirm(true)}>{t("Delete card")}</Button></div>}
|
||||
|
||||
Reference in New Issue
Block a user