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
7 changes: 4 additions & 3 deletions src/components/app/TopicPill.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { resolveChapterTag } from "$lib/far/glossary";
import { resolveChapterTag, topicPillLabel } from "$lib/far/glossary";
import TermDefinitionPopover from "./TermDefinitionPopover.svelte";

let {
Expand All @@ -9,19 +9,20 @@
} = $props();

let term = $derived(resolveChapterTag(tag));
let label = $derived(topicPillLabel(tag));
</script>

{#if term}
<TermDefinitionPopover
{term}
triggerClass="rounded-md bg-muted px-1.5 py-0.5 text-[0.65rem] text-muted-foreground outline-none transition-colors hover:bg-muted/80 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring sm:text-xs"
>
{tag}
{label}
</TermDefinitionPopover>
{:else}
<span
class="rounded-md bg-muted px-1.5 py-0.5 text-[0.65rem] text-muted-foreground sm:text-xs"
>
{tag}
{label}
</span>
{/if}
24 changes: 22 additions & 2 deletions src/lib/far/glossary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ export const CORE_GLOSSARY_TERMS: GlossaryTerm[] = [
},
{
id: "sources-sought",
term: "Sources sought",
aliases: ["sources-sought", "Sources Sought", "sources sought notice"],
term: "Sources Sought",
aliases: ["sources-sought", "Sources sought", "sources sought notice"],
definition:
"A market-research notice that gathers capability evidence and often informs set-aside and acquisition strategy. It is not a solicitation for binding offers.",
chapterId: "market-signal-to-solicitation",
Expand Down Expand Up @@ -503,6 +503,26 @@ export function resolveChapterTag(tag: string): GlossaryTerm | undefined {
return byTagKey.get(normalizeKey(tag));
}

/** Fallback when a tag has no glossary entry: `market-research` → `Market Research`. */
function formatKebabAsTitle(tag: string): string {
const parts = tag
.trim()
.split(/[-_]+/)
.filter(Boolean);
if (parts.length <= 1) {
return tag.trim();
}
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
}

/**
* Display label for a shelf topic pill.
* Prefers the curated glossary `term`; otherwise formats the kebab-case key.
*/
export function topicPillLabel(tag: string): string {
return resolveChapterTag(tag)?.term ?? formatKebabAsTitle(tag);
}

/** All match strings for a term (canonical + aliases), longest first. */
export function matchStringsForTerm(term: GlossaryTerm): string[] {
return [term.term, ...(term.aliases ?? [])].sort((a, b) => b.length - a.length);
Expand Down