Skip to content

Repository files navigation

Margin

Margin is a live, annotation-led writing editor. It scores every sentence after a 300 ms typing pause and aligns the results beside the current text. It never rewrites or suggests replacement text; accepting a trim removes only the selected sentence.

Live annotation

This animated preview and the full video are committed in this repository. It shows the working app with Jev connected and a sentence being appended in real time. Select the preview to open the full-size MP4.

Animated preview of Margin annotating a live edit

Heat map and trim mode

This animated preview and the full video are committed in this repository. It shows confidence-opacity heat mapping and trim decisions. Accepting a cut removes that sentence from the essay. Select the preview to open the full-size MP4.

Animated preview of Margin heat map and trim mode

What it does

  • Splits plain text with Intl.Segmenter.
  • Scores role, cut_safety, redundancy, support, and clarity with a 0–1 confidence for every verdict.
  • Follows the device's light or dark appearance preference.
  • Reconciles unchanged sentences with an LCS diff, then re-scores only changed sentences and their immediate neighbors.
  • Cancels stale requests when typing continues.
  • Colors the heat map by cut safety and uses cut-safety confidence as opacity.
  • Builds a trim plan from cuttable sentences, ranked by ascending confidence-weighted importance.
  • Applies accepted sentence cuts and records keep decisions without changing unselected text.
  • Exports aligned Markdown and a JSON score file.
  • Drops malformed provider results instead of displaying an unvalidated verdict.

Stack

  • TypeScript, React, and Vite
  • TypeSafe AI's official @typesafe-ai/sdk
  • Zod validation
  • Intl.Segmenter
  • Vitest and Playwright

There is no auth, database, or persistent editor state.

Setup

Requires Node.js 20 or newer.

npm install
cp .env.example .env

Set one provider in .env.

Jev

SCORING_PROVIDER=jev
TYPESAFE_API_KEY=your_key
TYPESAFE_DEFAULT_MODEL=jev-latest

Create a key from the TypeSafe dashboard. Then run:

npm run dev

Open the local URL printed by Vite. The API key is read only by the Vite server middleware and is never included in the browser bundle.

Structured-output LLM fallback

LLMProvider expects an OpenAI-compatible Chat Completions endpoint that supports strict json_schema response formats.

SCORING_PROVIDER=llm
LLM_API_KEY=your_key
LLM_BASE_URL=https://api.openai.com/v1
LLM_MODEL=your_structured_output_model

OPENAI_API_KEY and OPENAI_BASE_URL are accepted as fallbacks. LLM_MODEL is required; Margin does not guess a model.

Verified Jev research

Verified on September 24, 2026 against TypeSafe AI's official documentation and your configured account:

  • Introduction: Jev evaluates typed questions against state and returns structured decisions rather than generated prose.
  • JavaScript/TypeScript SDK: official package @typesafe-ai/sdk, Node.js 20+, using TYPESAFE_API_KEY.
  • API reference: authenticated POST https://api.typesafe.ai/v1/systemone with state, model, and a map of typed questions.
  • Models: jev-latest currently resolves to jev-1.13.0; GET /v1/models is account-scoped.
  • Choice primitive: Choice answers include a selected label, probabilities, and confidence from 0–1.

Authenticated GET /v1/models returned HTTP 200 for the supplied key and listed jev-latest and jev-preview. The app therefore uses the documented SDK and endpoint rather than a guessed integration.

Privacy boundary

The editor and annotation history remain in browser memory. The local Vite API receives the current document's sentence strings, their zero-based indices, and the target sentences needed for scoring. The current document is sent to the selected scoring provider for analysis—no editor storage, replacement text, export history, or unrelated application state.

Only changed sentences and immediate neighbors are re-scored. Unchanged scores are reconciled locally. The full current sentence array is used as model context because redundancy and support can depend on earlier sentences.

Architecture

  • src/core/schema.ts defines the shared typed result schema and per-result sanitizer.
  • src/core/provider.ts defines the single ScoringProvider.score(sentences, context) interface.
  • server/providers/jev.ts maps each field to a typed Jev Choice question and validates every answer.
  • server/providers/llm.ts requests strict JSON matching the shared schema and validates each returned result independently.
  • server/api.ts is the same-origin Vite API boundary; API keys remain server-side.
  • src/lib/sentences.ts uses Intl.Segmenter, LCS reconciliation, and neighbor expansion.
  • src/hooks/useIncrementalScoring.ts owns the 300 ms debounce, stale-request cancellation, and score replacement.
  • src/lib/trim.ts computes the transparent cut plan; accepted decisions are applied through the normal text update path.

Jev does not expose an Noul confidence value, so support uses a two-option Choice (n/a or unsupported_claim) rather than a Noul. Sentence 0's redundancy is deterministically none with confidence 1.0 because no earlier sentence exists.

Evaluation

evals/samples.json contains five original short essays and 25 human-authored cut/keep labels. A model prediction is cut only when cut_safety is cuttable; malformed or missing results count as disagreements.

set -a
source .env
set +a
npm run eval -- jev,llm

Run one provider with npm run eval -- jev or npm run eval -- llm.

Live results on September 24, 2026:

Provider Configuration Agreement Correct Dropped
Jev jev-latest 80.0% 20/25 0
LLM gpt-4.1-mini, strict JSON schema 92.0% 23/25 0

These labels are small, author-created smoke evaluations, not an independent benchmark.

Verification

npm run lint
npm run typecheck
npm test
npm run build

With npm run dev running in another terminal:

npm run smoke
npm run demo:record

The smoke test covers live scoring, heat mapping, trim decisions, Markdown/JSON downloads, mobile overflow, and browser console errors. demo:record regenerates the two WebM walkthroughs.

Assumptions and limits

  • Sentence segmentation uses the en locale because Jev's strongest documented language support is English.
  • Trim mode uses a transparent greedy plan in ascending cut-safety confidence, not a globally optimal word-count subset.
  • Jev Choice supports at most 255 options, so redundancy compares against the 254 nearest earlier sentences.
  • The fallback assumes strict JSON-schema support from the configured OpenAI-compatible endpoint.
  • First-sentence redundancy is logically none with confidence 1.0; it is not a model verdict.
  • npm run dev and npm run preview provide the local API middleware. A production host must preserve that server boundary or implement an equivalent one.

About

Live annotation-only writing editor with Jev and structured-output LLM scoring

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages