This commit is contained in:
k
2026-09-21 19:14:55 +02:00
parent 2a34d16b52
commit 5903b77d95
16 changed files with 958 additions and 125 deletions
+14 -3
View File
@@ -99,7 +99,7 @@ only ever sees reviewed text blocks, never raw files.
## Drivers`src/drivers/`: `LLMDriver` interface (`listModels`, `chat`, `chatStream`),
`DriverRegistry` with global priority
`ollama → mistral → glm → puppeteer-openai (stub) → deepseek → anthropic → openai`.
`ollama → mistral → glm → puppeteer-openai (POC) → deepseek → anthropic → openai`.
Models are addressed as `"driver:model"` (split on the first `:`).
- `OllamaDriver`: model discovery via `GET {OLLAMA_HOST}/api/tags`, chat via
@@ -113,5 +113,16 @@ Models are addressed as `"driver:model"` (split on the first `:`).
(SSE) implementation; concrete drivers only set base URL, key, and fallback
model list. A driver is enabled only when its `*_ENABLED` flag is set **and**
its key is non-empty; failing drivers are skipped (not fatal) in listings.
- `PuppeteerOpenAIDriver`: intentional stub, always disabled (headless scraping
of chatgpt.com is fragile and against ToS).
- `PuppeteerOpenAIDriver` (+ `browser.ts` `ChatGPTBrowserEngine`): ChatGPT via
the web UI — puppeteer-core driving Firefox (WebDriver BiDi) with a
**throwaway profile** (mkdtemp under /tmp, deleted on close). Anonymous
session works without login; if the composer is missing the engine waits up
to `PUPPETEER_LOGIN_TIMEOUT_S` for a manual login in the window. Flow:
accept the cookie banner, focus the composer (`#mobile-composer-prompt`,
multi-line via Shift+Enter), submit with `[data-composer-submit]`, stream
by polling `[data-assistant-stream-block]` text, and treat a newly
**visible** `[data-copy-message]` button as end-of-response (toolbars exist
hidden in the DOM during streaming — visibility is the real signal).
Navigation uses domcontentloaded + retries (chatgpt.com stalls sometimes).
POC — ToS-risky, off unless `PUPPETEER_ENABLED=true`. Smoke test:
`bun scripts/poc-chatgpt.ts` (headed, `--probe` for a state report).
+19 -5
View File
@@ -7,9 +7,18 @@ directory is a build artifact, never hand-edit, not committed).
## Routing
No router library — `App.tsx` matches `window.location.pathname` once (module
level, constant for the page lifetime): `/share/:publicId` renders the public
read-only `SharePage` (no auth), everything else renders the authenticated app.
No router library — a tiny history wrapper (`lib/route.ts`: `navigate()`,
`useRoute()`, trailing-slash normalization) drives pathname routing:
- `/login` — sign-in card (username + access key + TOTP step). Renders
instantly, **never shows the boot splash**.
- `/register` — full-page self-registration (`RegisterPage.tsx`; key shown
once, then hands username+key to `/login` via `sessionStorage`). No splash.
- `/chat` — the platform itself (auth required; boots behind the splash).
- `/` — redirects to `/chat` when logged in, `/login` otherwise (placeholder
for a future landing page).
- Public routes (unchanged): `/share/:publicId`, `/reset/:token`,
`/review/:id`, `/admin`.
## State & data flow
@@ -20,7 +29,8 @@ read-only `SharePage` (no auth), everything else renders the authenticated app.
`{ error }` parsing, `ApiError` with status, auto logout+reload on 401
(except on `/share/*`). `chatStream()` parses SSE `token`/`done`/`error`.
- Boot shows `LoadingScreen` for at least 1.5 s (`MIN_SPLASH_MS` in `App.tsx`)
so the bloom animation always plays, even when the session restores instantly.
so the bloom animation always plays, even when the session restores
instantly — **only on `/` and `/chat`**. `/login` and `/register` skip it.
- Sending: ensure a server conversation exists (created lazily on first send),
optimistic user message, stream tokens into `streaming`, then append the full
assistant message and refresh the list (server auto-titles new chats).
@@ -33,7 +43,11 @@ read-only `SharePage` (no auth), everything else renders the authenticated app.
**`Picker`** — the framework-styled dropdown (pill button + popover, optional
option groups, lucide `ChevronDown`/`Check`, Escape/outside-click to close).
**Never use a native `<select>`**; use `Picker` for model and theme choices.
- `Login.tsx` — username + access-key card.
- `Login.tsx` — username + access-key card (`/login`); the Register button
navigates to `/register`, and a fresh registration prefills the form.
- `RegisterPage.tsx` — full-page registration (`/register`) reusing the login
card style; shows the new key once with a copy button, then continues to
`/login`.
- `Sidebar.tsx` — collapsible conversation list with per-item context menu
(right-click or `···`: Rename / Set topic / Share publicly / Archive /
Delete), user chip (opens settings), Accounts button (admin), logout.