Skip to content

Commit 01f26e3

Browse files
leyder-benclaude
andauthored
Phase 4: Web Interface (#14)
* feat(web): scaffold Vite/React/TS project with Tailwind v4 tokens and self-hosted Geist * feat(web): add API types and fetch client with the 5xx-never-verbatim error contract * feat(web): add TanStack Query hooks for query answers and index status * feat(web): add ConfidenceBadge and ErrorCard with icon-locked confidence-state discriminator * fix(web): use real lucide-react auto-generated classes in ConfidenceBadge/ErrorCard tests Remove hardcoded className props from icon elements that lucide-react merges onto its own auto-generated class, which silently defeated the icon-identity tests. Update the test selectors to match the actual classes generated by the installed lucide-react 1.25.0 (lucide-circle-check, lucide-triangle-alert, lucide-circle-x). * feat(web): add IndexStatusBadge with the not-indexed/error/healthy discriminator * feat(web): add QueryInput with Enter-to-submit and global '/' focus shortcut * feat(web): add CopyButton, copies the speakable answer only Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(web): add inline-expanding sources and supporting-points disclosures * feat(web): add AnswerCard, LoadingState, and the AnswerPanel status switch * feat(web): wire AppShell, TopBar, and QueryClientProvider into the real App * ci(web): add unit/integration test job; add Playwright E2E scaffold for manual runs * style(web): fix pre-existing Prettier drift so the new CI format check passes AnswerPanel.test.tsx (Task 9) had a line exceeding printWidth that an earlier `npm run format` run didn't catch; also add test-results/, playwright-report/, blob-report/ to .prettierignore so Prettier stops scanning Playwright's generated output (mirrors the existing dist/ entry — Prettier doesn't consult .gitignore, only .prettierignore). Whitespace-only; no assertion or logic changes. * fix(web): personal_examples is an object array, not string[] — matches real backend contract The frontend types and SupportingPointsDisclosure assumed personal_examples: string[], but the backend (apps/api/app/generation/schema.py) returns PersonalExample objects ({project, example, source_chunk_ids}). A real response with a populated personal_examples array would crash the whole app with no error boundary to catch it. Also fixes QuerySource.heading (string -> string | null) to match the backend's actual optional field, guarding SourceItem against a dangling separator. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 77f5ff8 commit 01f26e3

49 files changed

Lines changed: 6933 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,26 @@ jobs:
4040
run: ruff check .
4141
- name: Run tests
4242
run: pytest -v
43+
44+
web:
45+
runs-on: ubuntu-latest
46+
defaults:
47+
run:
48+
working-directory: apps/web
49+
steps:
50+
- uses: actions/checkout@v4
51+
- uses: actions/setup-node@v4
52+
with:
53+
node-version: "22"
54+
cache: "npm"
55+
cache-dependency-path: apps/web/package-lock.json
56+
- name: Install dependencies
57+
run: npm ci
58+
- name: Check formatting
59+
run: npm run format
60+
- name: Lint
61+
run: npm run lint
62+
- name: Typecheck
63+
run: npm run typecheck
64+
- name: Run tests
65+
run: npm test

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ node_modules/
1717
dist/
1818
.vite/
1919

20+
# Playwright E2E (generated, not source)
21+
test-results/
22+
playwright-report/
23+
blob-report/
24+
2025
# Docker
2126
*.pid
2227

apps/web/.prettierignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
dist
2+
node_modules
3+
test-results
4+
playwright-report
5+
blob-report

apps/web/.prettierrc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"semi": true,
3+
"singleQuote": false,
4+
"trailingComma": "es5",
5+
"printWidth": 100
6+
}

apps/web/eslint.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import js from "@eslint/js";
2+
import reactHooks from "eslint-plugin-react-hooks";
3+
import tseslint from "typescript-eslint";
4+
5+
export default tseslint.config(
6+
{ ignores: ["dist"] },
7+
{
8+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
9+
files: ["**/*.{ts,tsx}"],
10+
plugins: { "react-hooks": reactHooks },
11+
rules: { ...reactHooks.configs.recommended.rules },
12+
}
13+
);

apps/web/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>vault-interview-copilot</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/src/main.tsx"></script>
11+
</body>
12+
</html>

0 commit comments

Comments
 (0)