SPB Git

spb/search-box Public

Agentic web research engine — hypotheses, verbatim evidence, contradictions, sourced answers streamed live. Claude Opus 5 + Firecrawl + PostgreSQL.

TypeScript 76.9% CSS 18.7% SQL 2.1% JavaScript 1.8% Shell 0.5%

README: screenshots, logo, metric badges; screenshot script

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 1 h ago (Aug 12, 2026) parent e8ed0e1

Showing 7 changed files with +88 and −2

modified README.md +21 −0
@@ -29,6 +29,27 @@ to a real backend event.
29 29
30 30 ---
31 31
32 +## Screenshots
33 +
34 +*A completed research session — the question blooms into objectives and claims, every node driven by a real backend event:*
35 +
36 +<img src="docs/screenshots/session-live.png" alt="Live research session: bloom graph, stats bar, timeline and streamed answer" width="100%">
37 +
38 +<table>
39 + <tr>
40 + <td width="62%" valign="top">
41 + <img src="docs/screenshots/home.png" alt="Home page — question composer" width="100%">
42 + <p align="center"><sub>Home — ask a question that deserves real research</sub></p>
43 + </td>
44 + <td width="23%" valign="top">
45 + <img src="docs/screenshots/mobile.png" alt="Mobile view — streamed answer" width="100%">
46 + <p align="center"><sub>Mobile-first, one-handed</sub></p>
47 + </td>
48 + </tr>
49 +</table>
50 +
51 +---
52 +
32 53 ## Validated on a real research problem
33 54
34 55 Benchmark question: *"Can transformer KV cache be compressed by an order of magnitude
added docs/screenshots/home.png +0 −0

Binary file not shown.

added docs/screenshots/mobile.png +0 −0

Binary file not shown.

added docs/screenshots/session-live.png +0 −0

Binary file not shown.

modified package.json +3 −2
@@ -13,8 +13,9 @@
13 13 "typecheck": "for p in shared events db firecrawl anthropic research agent; do npx tsc --noEmit -p packages/$p/tsconfig.json || exit 1; done"
14 14 },
15 15 "devDependencies": {
16 + "@types/node": "^22.10.2",
17 + "playwright": "^1.62.1",
16 18 "tsx": "^4.19.2",
17 "typescript": "^5.7.2",
18 "@types/node": "^22.10.2"
19 + "typescript": "^5.7.2"
19 20 }
20 21 }
modified pnpm-lock.yaml +29 −0
@@ -11,6 +11,9 @@ importers:
11 11 '@types/node':
12 12 specifier: ^22.10.2
13 13 version: 22.20.1
14 + playwright:
15 + specifier: ^1.62.1
16 + version: 1.62.1
14 17 tsx:
15 18 specifier: ^4.19.2
16 19 version: 4.23.12
@@ -572,6 +575,11 @@ packages:
572 575 fast-sha256@1.3.0:
573 576 resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==}
574 577
578 + fsevents@2.3.2:
579 + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
580 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
581 + os: [darwin]
582 +
575 583 fsevents@2.3.3:
576 584 resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
577 585 engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -649,6 +657,16 @@ packages:
649 657 picocolors@1.1.1:
650 658 resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
651 659
660 + playwright-core@1.62.1:
661 + resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==}
662 + engines: {node: '>=20'}
663 + hasBin: true
664 +
665 + playwright@1.62.1:
666 + resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==}
667 + engines: {node: '>=20'}
668 + hasBin: true
669 +
652 670 postcss@8.4.31:
653 671 resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
654 672 engines: {node: ^10 || ^12 || >=14}
@@ -1028,6 +1046,9 @@ snapshots:
1028 1046
1029 1047 fast-sha256@1.3.0: {}
1030 1048
1049 + fsevents@2.3.2:
1050 + optional: true
1051 +
1031 1052 fsevents@2.3.3:
1032 1053 optional: true
1033 1054
@@ -1100,6 +1121,14 @@ snapshots:
1100 1121
1101 1122 picocolors@1.1.1: {}
1102 1123
1124 + playwright-core@1.62.1: {}
1125 +
1126 + playwright@1.62.1:
1127 + dependencies:
1128 + playwright-core: 1.62.1
1129 + optionalDependencies:
1130 + fsevents: 2.3.2
1131 +
1103 1132 postcss@8.4.31:
1104 1133 dependencies:
1105 1134 nanoid: 3.3.18
added scripts/screenshots.mjs +35 −0
@@ -0,0 +1,35 @@
1 +/**
2 + * Search-box.ai
3 + * Author: Simon-Pierre Boucher
4 + * Contact: contact@spboucher.ai
5 + * File: scripts/screenshots.mjs
6 + * Description: Captures README screenshots (home, session desktop, session mobile) via Playwright.
7 + *
8 + * Usage: node scripts/screenshots.mjs [baseUrl] [sessionId]
9 + */
10 +
11 +import { chromium } from "playwright";
12 +
13 +const base = process.argv[2] ?? "http://localhost:3000";
14 +const sessionId = process.argv[3] ?? "ses_4OqHdb-YRqwM";
15 +
16 +const browser = await chromium.launch();
17 +
18 +async function shot(url, viewport, out, { settle = 9000, toTop = true } = {}) {
19 + const page = await browser.newPage({ viewport });
20 + await page.goto(url, { waitUntil: "networkidle" }).catch(() => {});
21 + await page.waitForTimeout(settle);
22 + if (toTop) {
23 + await page.evaluate(() => window.scrollTo(0, 0));
24 + await page.waitForTimeout(600);
25 + }
26 + await page.screenshot({ path: out });
27 + await page.close();
28 + console.log(`captured ${out}`);
29 +}
30 +
31 +await shot(`${base}/`, { width: 1440, height: 860 }, "docs/screenshots/home.png", { settle: 3000 });
32 +await shot(`${base}/r/${sessionId}`, { width: 1440, height: 900 }, "docs/screenshots/session-live.png");
33 +await shot(`${base}/r/${sessionId}`, { width: 390, height: 844 }, "docs/screenshots/mobile.png");
34 +
35 +await browser.close();
36