commit 16547480221abf17ef2b8494b17eeebbccc29ba0
parent a71b0fd8cbb3cd5bfea6a10791d5ecaf785beccf
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Tue, 22 Sep 2026 16:09:25 -0400
search: a leaf section is labelled by the builder's own scope table
SearchResults filed every hit section under a four-way ternary written
before the description and tags scopes existed, so hits from either were
labelled "Transcripts" — the one place a reader could be sure they did
not come from. QueryLeafView's SCOPE_LABELS is now exported and used
there, which also makes the result list say "Keywords" for the yt-dlp
keyword scope, the same word the builder's own scope picker shows, and
leaves "Tags" meaning the curated vocabulary everywhere.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
3 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/common/components/QueryLeafView.tsx b/common/components/QueryLeafView.tsx
@@ -43,7 +43,11 @@ type Props = {
// (lib/curatedTags.ts) — the chip row in the filter panel. The code token, the
// URL and the MCP `scopes` enum stay `"tags"`: renaming those would break every
// saved query and share link to fix a word on a screen.
-const SCOPE_LABELS: Record<LayerScope, string> = {
+// Exported because the result list labels its per-leaf sections with the same
+// words (SearchResults.tsx): a reader picks "Keywords" in the builder and has
+// to find "Keywords" over the hits it produced, so there is one table, not two
+// that drift.
+export const SCOPE_LABELS: Record<LayerScope, string> = {
transcripts: "Transcripts",
chat: "Live chat",
posts: "Posts",
diff --git a/common/components/SearchResults.tsx b/common/components/SearchResults.tsx
@@ -33,6 +33,7 @@ import { formatTimestamp } from "../lib/vtt";
import { vodExpiry } from "../lib/vodExpiry";
import { Button } from "./ui/button";
import { LayerSwatch } from "./LayerSwatch";
+import { SCOPE_LABELS } from "./QueryLeafView";
import { ChartShapeControls } from "./charts/ChartShapeControls";
import { SearchChartPanel } from "./charts/SearchChartPanel";
import {
@@ -697,14 +698,12 @@ const ResultCard = memo(function ResultCard({
data-leaf-section={leafId}
>
<LayerSwatch leafId={leafId} size="xs" />
+ {/* The builder's table, not a second copy of it: this chain
+ predated the description and tags scopes and called both
+ of them "Transcripts", which is where the hits did NOT come
+ from. */}
<span className="text-[10px] uppercase tracking-wide text-muted-foreground">
- {leafInfo.scope === "metadata"
- ? "Title / channel"
- : leafInfo.scope === "chat"
- ? "Live chat"
- : leafInfo.scope === "posts"
- ? "Posts"
- : "Transcripts"}
+ {SCOPE_LABELS[leafInfo.scope]}
</span>
<span className="font-mono text-xs text-muted-foreground truncate">
{leafInfo.query}
diff --git a/export/e2e/query-tree.spec.ts b/export/e2e/query-tree.spec.ts
@@ -208,6 +208,42 @@ test.describe("composite search — query tree", () => {
await expectResultSlugs(page, [CHAT_LARGE_SLUG]);
});
+ test("a leaf section is labelled with its own scope", async ({ page }) => {
+ // The section bar over a card's hits used to be a four-way ternary that
+ // predated the description and tags scopes, so hits from either were
+ // filed under "Transcripts" — the one place they demonstrably did not
+ // come from. It reads the builder's own table now, which is also why
+ // `tags` says "Keywords": the curated vocabulary is what "Tags" means.
+ const tree: SGroup = {
+ k: "g",
+ o: "OR",
+ c: [
+ { k: "l", q: "zebra", s: "description" },
+ { k: "l", q: "gaming", s: "tags" },
+ ],
+ };
+ await page.goto(`/?qt=${qt(tree)}`);
+ await expectResultSlugs(page, [TRANSCRIPT_ONLY_SLUG, CHAT_LARGE_SLUG]);
+
+ await expect(
+ page.locator(
+ `[data-result-slug="${TRANSCRIPT_ONLY_SLUG}"] [data-leaf-section]`,
+ ),
+ ).toContainText("Description");
+ await expect(
+ page.locator(
+ `[data-result-slug="${CHAT_LARGE_SLUG}"] [data-leaf-section]`,
+ ),
+ ).toContainText("Keywords");
+ // Neither says Transcripts, which is what the old chain said for both.
+ // textContent, not innerText: the bar is CSS-uppercased.
+ const sections = page.locator("[data-leaf-section]");
+ await expect(sections).toHaveCount(2);
+ expect((await sections.allTextContents()).join(" ")).not.toContain(
+ "Transcripts",
+ );
+ });
+
test("legacy URL: ?q=alpha auto-migrates to a single-leaf transcripts query", async ({
page,
}) => {