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
4 changes: 0 additions & 4 deletions .eslintrc.json

This file was deleted.

6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
/coverage

# next.js
/.next/
.next/
/out/

# mission worktrees — sibling checkouts, not app source
.claude/
.grok/

# production
/build

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ Truncation is purely client-side — `lib/contextWindow.js` decides what leaves

## Stack

- Next.js 13.5.11 pages router, React 18, hand-written CSS in `styles/`. `styles/globals.css` is now just the `@import` entry that `pages/_app.js` loads; the sheet lives in `tokens.css`, `base.css`, `glass.css`, `shell.css`, `cost.css`, `surfaces.css`, `transcript.css`, `composer.css`, `panels.css`, and the import order in the entry file *is* the old source order — several equal-specificity rules depend on it, so the files are cut, never reshuffled. `tokens.css` holds the whole palette: type scale (`--fs-1`…`--fs-5`), radii (`--r-1`…`--r-4`, `--r-pill`), and semantic colours (`--ink*`, `--muted*`, `--line*`, `--surface*`, `--ground`, `--blue`/`--accent*`, `--violet*`, `--warn-*`, `--danger-*`, `--shadow-*`), plus a `prefers-color-scheme: dark` block that redefines those tokens and nothing else
- `geist` — Geist Sans and Geist Mono, self-hosted via `next/font/local`; every token count, price, and JSON block is set in the mono face so numbers line up column to column. `next.config.js` needs `transpilePackages: ['geist']`: without it the pages-router build's “collecting page data” step evaluates the package's `next/font/local` import through Node's own ESM resolver, which has no exports map for it on this Next version. `pages/_app.js` puts the font variable classes on `document.body` after mount, so the server markup stays plain and the first paint falls back to the system stack declared in `styles/base.css`
- Next.js 16.3.2 pages router, React 18, hand-written CSS in `styles/`. `styles/globals.css` is now just the `@import` entry that `pages/_app.js` loads; the sheet lives in `tokens.css`, `base.css`, `glass.css`, `shell.css`, `cost.css`, `surfaces.css`, `transcript.css`, `composer.css`, `panels.css`, and the import order in the entry file *is* the old source order — several equal-specificity rules depend on it, so the files are cut, never reshuffled. `tokens.css` holds the whole palette: type scale (`--fs-1`…`--fs-5`), radii (`--r-1`…`--r-4`, `--r-pill`), and semantic colours (`--ink*`, `--muted*`, `--line*`, `--surface*`, `--ground`, `--blue`/`--accent*`, `--violet*`, `--warn-*`, `--danger-*`, `--shadow-*`), plus a `prefers-color-scheme: dark` block that redefines those tokens and nothing else
- `geist` — Geist Sans and Geist Mono, self-hosted via `next/font/local`; every token count, price, and JSON block is set in the mono face so numbers line up column to column. Under Turbopack (Next 16's default `next build` / `next dev`) `transpilePackages: ['geist']` is not needed. The webpack “collecting page data” path (`next build --webpack`) still resolves geist's `next/font/local` through Node's ESM loader — put the transpile line back if that fallback is ever used. `pages/_app.js` puts the font variable classes on `document.body` after mount, so the server markup stays plain and the first paint falls back to the system stack declared in `styles/base.css`
- `gpt-tokenizer` for the composer’s `o200k_base` count, dynamically imported on first use so its ~1 MB table never enters the initial bundle
- One serverless route: `POST /api/chat` (`maxDuration` 60), answering with JSON or an NDJSON stream. Not Edge, and not the Vercel AI Gateway — those paths drop logprobs.
- OpenAI Chat Completions with `logprobs`, `top_logprobs: 5`, and `n: 3`
Expand Down Expand Up @@ -114,7 +114,7 @@ node --test "lib/*.test.js"

The unit tests cover the pure modules in `lib/` — re-softmax, tokenizer chunking, completion statistics, rates, sampling clamps, context truncation, storage pruning, the weather fetch (with a stubbed `fetch`), the tool schema, the two-round usage totals and their one-line summary, the fraction-of-a-cent and million-chat formatters, the cutoff-relevance test, and the model-facts table — and make no network calls. The three files in `scripts/` are the opposite: manual gates that hit the live API, so run them by hand and never in CI.

`next`, `@next/env`, and `eslint-config-next` are pinned to the exact `13.5.11`, and an `overrides` entry in `package.json` (`"minimatch@9": "^9.0.7"`) lifts the copy of `minimatch` that `@typescript-eslint` brings in past the 9.x ReDoS advisories, which are first patched in `9.0.7`. `npm audit` still reports two high findings — `next` itself and the `postcss` that comes in with it — and the only fix npm offers for either is a Next major, which would take the app off the 13.5 pages router it is built on. Those two are left in place on purpose: do not run `npm audit fix --force` here.
`next` and `eslint-config-next` are pinned to `16.3.2`. `react` / `react-dom` stay at `18.2.0` — Pages Router uses the React in `package.json`, and 16.3.2 still peers `react@^18.2`. Lint is `eslint .` with a flat `eslint.config.mjs` (`next lint` was removed in 16). Node 20.9+ is the floor (`engines` in `package.json`); CI uses 24. `npm audit` is clean; `npm audit fix --force` would still yank `next` / `eslint-config-next` off those exact pins.

## Internals

Expand Down
28 changes: 28 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { defineConfig, globalIgnores } from 'eslint/config'
import nextVitals from 'eslint-config-next/core-web-vitals'

const eslintConfig = defineConfig([
...nextVitals,
{
// Existing effects in these files trip the Next 16 react-hooks rule.
// New files stay covered.
files: [
'components/ChatInterface.js',
'components/SamplingPanel.js',
'components/TokenProbabilities.js',
],
rules: {
'react-hooks/set-state-in-effect': 'off',
Comment thread
rdtiv marked this conversation as resolved.
},
},
globalIgnores([
Comment thread
rdtiv marked this conversation as resolved.
'**/.next/**',
'**/out/**',
'**/build/**',
'**/next-env.d.ts',
'.claude/**',
'.grok/**',
]),
])

export default eslintConfig
8 changes: 1 addition & 7 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
Comment thread
rdtiv marked this conversation as resolved.
swcMinify: true,
// geist ships next/font/local imports from node_modules; without this the
// pages-router build's "collecting page data" step evaluates that file
// through Node's own ESM resolver (which has no exports map for
// next/font/local on this Next version) instead of Next's font loader.
transpilePackages: ['geist'],
}

module.exports = nextConfig
module.exports = nextConfig
Loading
Loading