From a10e449e771fae44600d72b3b79e59129cec23b3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 24 Sep 2026 04:10:47 +0000 Subject: [PATCH 1/2] style: drop the gradient, panel blur, glow, fade-in and web fonts from the viewer The page loaded Syne, Manrope and Recursive from Google Fonts and drew a radial gradient behind the dashboard, a 12px backdrop blur on every panel, a staggered fade-in on load and a glow on the confidence dots. It now uses the system sans and monospace stacks, flat panels in the existing --bg-panel colour, and keeps only the hover transitions. The emoji favicon is replaced by an inline SVG triangle that follows the colour scheme. Layout, colour tokens and spacing are unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EpGaiBuSk5nCSV2hKyD6PN --- index.html | 5 +--- src/styles/main.css | 70 +++++---------------------------------------- 2 files changed, 8 insertions(+), 67 deletions(-) diff --git a/index.html b/index.html index 36108d9..f45fd2a 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,7 @@ Refract — Observation UI - - - - +
diff --git a/src/styles/main.css b/src/styles/main.css index 9053955..b870686 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -29,9 +29,10 @@ --cyan: #22d3ee; --radius: 10px; --radius-sm: 6px; - --font: "Manrope", system-ui, sans-serif; - --font-heading: "Syne", system-ui, sans-serif; - --mono: "Recursive", monospace; + --font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + --font-heading: var(--font); + --mono: + ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; } html { @@ -40,7 +41,6 @@ html { body { font-family: var(--font); - font-weight: 350; background: var(--bg-deep); color: var(--text); line-height: 1.5; @@ -48,29 +48,10 @@ body { min-height: 100vh; } -/* ── Ambient Background ── */ -body::before { - content: ""; - position: fixed; - top: 0; - right: 0; - width: 50%; - height: 50%; - background: radial-gradient( - ellipse at top right, - rgba(124, 138, 255, 0.05), - transparent 60% - ); - pointer-events: none; - z-index: 0; -} - #app { max-width: 1440px; margin: 0 auto; padding: 1.25rem 1.5rem; - position: relative; - z-index: 1; } /* ── Header ── */ @@ -85,7 +66,7 @@ body::before { #app-header h1 { font-size: 1.4rem; - font-weight: 650; + font-weight: 600; color: var(--accent); letter-spacing: -0.02em; } @@ -128,46 +109,17 @@ body::before { /* ── Panels ── */ .panel { background: var(--bg-panel); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem 1.25rem; overflow: auto; - transition: - border-color 0.2s ease, - box-shadow 0.2s ease; - animation: panelFadeIn 0.35s ease-out both; + transition: border-color 0.2s ease; } .panel:hover { border-color: var(--border-strong); } -.panel:nth-child(1) { - animation-delay: 0ms; -} -.panel:nth-child(2) { - animation-delay: 60ms; -} -.panel:nth-child(3) { - animation-delay: 120ms; -} -.panel:nth-child(4) { - animation-delay: 180ms; -} - -@keyframes panelFadeIn { - from { - opacity: 0; - transform: translateY(6px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - .panel-title { font-size: 0.7rem; font-weight: 700; @@ -357,7 +309,6 @@ select { color: var(--text-dim); margin-top: 0.15rem; font-family: var(--mono); - font-variation-settings: "MONO" 1; } /* ── Diff Viewer ── */ @@ -384,7 +335,6 @@ select { .diff-lines { font-family: var(--mono); - font-variation-settings: "MONO" 1; font-size: 0.78rem; line-height: 1.6; padding: 0.5rem 0.6rem; @@ -476,14 +426,11 @@ select { background: var(--accent); transform: translate(-50%, -50%); cursor: pointer; - transition: - transform 0.2s ease, - box-shadow 0.2s ease; + transition: transform 0.2s ease; } .certainty-dot:hover { transform: translate(-50%, -50%) scale(1.5); - box-shadow: 0 0 12px rgba(124, 138, 255, 0.4); } .certainty-dot.high { @@ -518,7 +465,6 @@ select { padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--border); font-family: var(--mono); - font-variation-settings: "MONO" 1; font-size: 0.76rem; } @@ -559,7 +505,6 @@ select { .card-diff { font-family: var(--mono); - font-variation-settings: "MONO" 1; font-size: 0.78rem; margin-top: 0.4rem; padding: 0.4rem; @@ -628,7 +573,6 @@ select { /* ── Schema Viewer ── */ .schema-viewer { font-family: var(--mono); - font-variation-settings: "MONO" 1; font-size: 0.76rem; line-height: 1.6; white-space: pre-wrap; From 6b0dffbd8c35ea55c7cdf737777031f3e46eec7d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 24 Sep 2026 04:11:17 +0000 Subject: [PATCH 2/2] fix: name the viewer's panels for what they plot and correct the README against the components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - index.html: the subtitle "Observation Engine — Timeline Viewer" and the title "Refract — Observation UI" now say what the page is: an event viewer for Refract JSONL exports. - Panel titles are sentence case. "Certainty Over Time" plots modelInterpretation.confidence for model_interpretation events, which the engine's own certaintyProfile field is easily confused with, so it is "Model confidence". "Edit Velocity" counts every event per day, so it is "Events per day". "Diff Viewer" and "Event Schema Inspector" drop the filler word. Timeline event types are sentence case too, which also fixes the Title Case fallback summary ("Sentence First Seen in Lead"). - README: the header GIF was a generated animation of labelled circles ("events", "diffs", "breakdown") showing a citation graph the app does not have; it is removed. The citation table is not sortable, the wording panel shows sentence_* events rather than claim_reworded/ strengthened/softened (types the importer rejects), the timeline keeps file order rather than sorting, and "works fully offline after first load" had no service worker behind it; each line now says what the code does. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EpGaiBuSk5nCSV2hKyD6PN --- README.md | 20 ++++++++------------ index.html | 4 ++-- src/citations/CitationChurnChart.ts | 2 +- src/citations/SourceChangeTable.ts | 2 +- src/disputes/EditVelocityChart.ts | 2 +- src/disputes/RevertClusterDisplay.ts | 2 +- src/disputes/TalkPageTimeline.ts | 2 +- src/export/SchemaViewer.ts | 2 +- src/language/CertaintyTimeline.ts | 2 +- src/language/WordingDiffCard.ts | 2 +- src/timeline/DiffView.ts | 2 +- src/timeline/TimelineView.ts | 5 +++-- 12 files changed, 22 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 7d14778..56a9854 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,8 @@ # Refract UI -

- refract-ui demo animation -

- Standalone visualization for the [Refract](https://github.com/refract-org/refract) observation engine. -Load any JSONL file of Refract observation events and explore what changed — timelines, diffs, citation graphs, disputes, and event-type breakdowns. No backend needed. +Load a JSONL file of Refract events to see them as a timeline with word-level diffs, citation and revert charts, talk page activity, and a list of the fields the events carry. Everything runs in the browser; there is no backend. ## Quick start @@ -27,17 +23,17 @@ Then drag `bitcoin-events.jsonl` onto the Refract UI upload zone. ## What you'll see -**Timeline** — Every event in chronological order. Click any event to see the word-level diff of what changed. Filter by event type to narrow the view. +**Timeline** — Every event, in the order the file lists them. Click an event to see the word-level diff of what changed. Filter by event type to narrow the view. -**Citations** — Bar chart showing citation additions, removals, and replacements per revision, plus a sortable table of every citation change. +**Citations** — Bar chart of citation additions, removals, and replacements per revision, plus a table of every citation change. -**Certainty** — Confidence scores over time for model-interpreted events. +**Model confidence** — The confidence score of each event on the `model_interpretation` layer. -**Disputes** — Revert clusters, edit velocity per day, and talk page activity. +**Disputes** — Revert clusters, events per day (with reverts), and talk page activity. -**Language Changes** — Card grid of wording events (claims reworded, strengthened, softened, removed). +**Wording changes** — One card per sentence event (first seen, modified, removed, reintroduced), with the before and after text. -**Schema** — Every JSON key in the loaded data, with types and presence percentage. +**Event schema** — Every top-level field in the loaded events, with its types and the share of events that have it. **Export** — Download filtered data as JSON, JSONL, or CSV. @@ -46,7 +42,7 @@ Then drag `bitcoin-events.jsonl` onto the Refract UI upload zone. - Vite + vanilla TypeScript - No framework dependencies - DOM-based rendering with CSS custom properties -- Works fully offline after first load +- Loads nothing from other origins; an uploaded file is read in the browser and never sent anywhere --- diff --git a/index.html b/index.html index f45fd2a..8d3a91e 100644 --- a/index.html +++ b/index.html @@ -3,14 +3,14 @@ - Refract — Observation UI + Refract event viewer

Refract

-

Observation Engine — Timeline Viewer

+

Event viewer for Refract JSONL exports

diff --git a/src/citations/CitationChurnChart.ts b/src/citations/CitationChurnChart.ts index ed16b89..b093e25 100644 --- a/src/citations/CitationChurnChart.ts +++ b/src/citations/CitationChurnChart.ts @@ -26,7 +26,7 @@ export class CitationChurnChart { const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Citation Churn"; + title.textContent = "Citation churn"; this.container.appendChild(title); const churnData = this.computeChurn(); diff --git a/src/citations/SourceChangeTable.ts b/src/citations/SourceChangeTable.ts index f4b1e88..ee530dd 100644 --- a/src/citations/SourceChangeTable.ts +++ b/src/citations/SourceChangeTable.ts @@ -26,7 +26,7 @@ export class SourceChangeTable { const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Source Changes"; + title.textContent = "Source changes"; this.container.appendChild(title); const changes = this.computeChanges(); diff --git a/src/disputes/EditVelocityChart.ts b/src/disputes/EditVelocityChart.ts index a2a773e..1f954bb 100644 --- a/src/disputes/EditVelocityChart.ts +++ b/src/disputes/EditVelocityChart.ts @@ -25,7 +25,7 @@ export class EditVelocityChart { const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Edit Velocity"; + title.textContent = "Events per day"; this.container.appendChild(title); const buckets = this.computeBuckets(); diff --git a/src/disputes/RevertClusterDisplay.ts b/src/disputes/RevertClusterDisplay.ts index 09cdcdb..5a1ba64 100644 --- a/src/disputes/RevertClusterDisplay.ts +++ b/src/disputes/RevertClusterDisplay.ts @@ -26,7 +26,7 @@ export class RevertClusterDisplay { const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Revert Clusters"; + title.textContent = "Revert clusters"; this.container.appendChild(title); const clusters = this.computeClusters(); diff --git a/src/disputes/TalkPageTimeline.ts b/src/disputes/TalkPageTimeline.ts index 61b08a3..a6c569c 100644 --- a/src/disputes/TalkPageTimeline.ts +++ b/src/disputes/TalkPageTimeline.ts @@ -25,7 +25,7 @@ export class TalkPageTimeline { const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Talk Page Activity"; + title.textContent = "Talk page activity"; this.container.appendChild(title); const activities = this.computeActivities(); diff --git a/src/export/SchemaViewer.ts b/src/export/SchemaViewer.ts index bd5811e..4be8847 100644 --- a/src/export/SchemaViewer.ts +++ b/src/export/SchemaViewer.ts @@ -17,7 +17,7 @@ export class SchemaViewer { const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Event Schema Inspector"; + title.textContent = "Event schema"; this.container.appendChild(title); if (this.events.length === 0) { diff --git a/src/language/CertaintyTimeline.ts b/src/language/CertaintyTimeline.ts index 75de00b..dfbc9e0 100644 --- a/src/language/CertaintyTimeline.ts +++ b/src/language/CertaintyTimeline.ts @@ -25,7 +25,7 @@ export class CertaintyTimeline { const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Certainty Over Time"; + title.textContent = "Model confidence"; this.container.appendChild(title); const points = this.computePoints(); diff --git a/src/language/WordingDiffCard.ts b/src/language/WordingDiffCard.ts index c1159cb..29a5d70 100644 --- a/src/language/WordingDiffCard.ts +++ b/src/language/WordingDiffCard.ts @@ -28,7 +28,7 @@ export class WordingDiffCard { const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Wording Changes"; + title.textContent = "Wording changes"; this.container.appendChild(title); const changes = this.computeChanges(); diff --git a/src/timeline/DiffView.ts b/src/timeline/DiffView.ts index 4c67a88..63918be 100644 --- a/src/timeline/DiffView.ts +++ b/src/timeline/DiffView.ts @@ -101,7 +101,7 @@ export class DiffView { this.container.innerHTML = ""; const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Diff Viewer"; + title.textContent = "Diff"; this.container.appendChild(title); const hint = document.createElement("div"); diff --git a/src/timeline/TimelineView.ts b/src/timeline/TimelineView.ts index 2517ac7..bbbdc82 100644 --- a/src/timeline/TimelineView.ts +++ b/src/timeline/TimelineView.ts @@ -38,7 +38,7 @@ export class TimelineView { const title = document.createElement("div"); title.className = "panel-title"; - title.textContent = "Revision Timeline"; + title.textContent = "Revision timeline"; this.container.appendChild(title); const count = document.createElement("div"); @@ -99,7 +99,8 @@ export class TimelineView { } function formatEventType(type: EventType): string { - return type.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); + const words = type.replace(/_/g, " "); + return words.charAt(0).toUpperCase() + words.slice(1); } function summarizeEvent(event: EvidenceEvent): string {