/* Shared atoms — top bar, footer, search field, helpers */
const Logo = () => (
L
Legal Casebase
v0.2 · prototype
);
const Topbar = ({ view, onNavigate }) => (
);
const Footer = () => (
);
const MAGNIFIER = (
);
const SearchField = ({ query, setQuery, mode, setMode, onSubmit, autoFocus, showModes = true, busy = false }) => {
return (
);
};
/* Render a snippet with [bracketed] terms (FTS5 snippet() format from
* app/retrieval.py) as . Falls back to plain preview. */
const RenderedSnippet = ({ snippet, preview }) => {
const text = snippet || preview || "";
if (!snippet) return <>{text}>;
const parts = [];
let i = 0;
const re = /\[([^\]]+)\]/g;
let m, key = 0;
while ((m = re.exec(text)) !== null) {
if (m.index > i) parts.push({text.slice(i, m.index)});
parts.push({m[1]});
i = m.index + m[0].length;
}
if (i < text.length) parts.push({text.slice(i)});
return <>{parts}>;
};
const MatchChip = ({ matched_by }) => {
const cls = matched_by === "both" ? "both" : matched_by === "fts" ? "fts" : "vector";
const label = matched_by === "both" ? "keyword + semantic"
: matched_by === "fts" ? "keyword"
: "semantic";
return (
{label}
);
};
/* Trim the long CourtListener party-name string to a readable case name.
* Real data: "Thomas Perttu, Petitioner v. Kyle Brandon Richards"
* Render: "Perttu v. Richards" (with full title shown on hover)
*/
function shortCaseName(full) {
if (!full) return "";
// Split on " v. " (CourtListener canonicalizes this)
const m = full.match(/^(.+?)\s+v\.\s+(.+)$/i);
if (!m) return full;
const [, left, right] = m;
// Strip CourtListener role suffixes like ", Petitioner" / ", et al." / ", President of..."
const cleanSide = (side) => {
return side
.replace(/,\s*(Petitioner|Respondent|Appellant|Appellee|et al\.).*$/i, "")
.replace(/,\s*(President|Secretary|Attorney General|Director|Administrator)[^,]*,?.*$/i, "")
.trim();
};
// Take the last meaningful name on each side (surname-style).
const surname = (s) => {
const cleaned = cleanSide(s);
// If it's a corporate/government party with commas, keep first phrase.
if (/(Inc\.|LLC|Corp\.|Service|Department|United States|Postal)/i.test(cleaned)) {
return cleaned.split(",")[0].trim();
}
const parts = cleaned.split(/\s+/);
return parts[parts.length - 1];
};
return `${surname(left)} v. ${surname(right)}`;
}
Object.assign(window, {
Topbar, Footer, SearchField, RenderedSnippet, MatchChip, MAGNIFIER, Logo, shortCaseName,
});