Tokens
Track, visualize, and compete on AI coding assistant token usage across Claude Code, Cursor, OpenCode, Codex, Gemini, Kimi, and Qwen. The Kardashev Scale for AI Devs.
翻譯準備中
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Find interesting data, chart it readably, and share it — in the browser at loom.ibm.io or as a macOS app (DuckDB, Vega-Lite, WebGPU).
翻譯準備中
正在載入完整 README。
檢視完整 README ↗Find interesting data, turn it into a readable chart, and share it — in your browser, on your phone, or as a macOS app.
▶ Try it: loom.ibm.io — no install. Live earthquakes, flights, Wikipedia edits, crypto, and weather are one click away, or load your own CSVs.
The desktop app (Tauri + DuckDB) adds folder mounting, millions of rows, SQL over CSV/Parquet, and WebGPU scatter. Charts are Vega-Lite specs drawn on Canvas / WebGPU; export PNG, SVG, or a link that reopens the exact chart.
Contributors & AI agents: See DOCS.md for architecture and AGENTS.md for a concise repo map and conventions. Platform support: docs/CAPABILITY_MATRIX.md. Verification gates: docs/DEPLOYMENT_GATES.md.
#chart=…) reopens the exact chart — dataset, chart type, encodings, headline, look, and framing; live feeds, demo files, and open-data CSVs reopen on load, local files wait for the recipient to open a file with the same name.-3 hours / yesterday), filters, group by (nested), and metrics (count, count distinct, sum, avg, min, max, p5 … p99.9; min / max / avg also work on time columns as first / last seen). Results update instantly.hour(ts), lower(country), CASE WHEN … END, regexp_extract(…)) and filter / group / aggregate on it. Evaluated in the browser; the same text goes into the SQL preview.#dive=…); links to live feeds and demo files reopen the dataset on load. SQL shows the equivalent DuckDB query (open it in Query on desktop). Open as cube sends the filtered rows to the Data cube.0 7 * * 1-5). Confirm over several checks to skip one-off blips, set reminder cooldowns, and set quiet hours.loom_active. Schema browser (Tables + Columns) and click-to-insert. Validation (parentheses, SELECT/WITH) before run.globals.css.wiki_stream); 60 live and poll feeds in six groups — Earth & climate (USGS quakes, NASA natural events, GDACS disaster alerts, weather and air quality for 12 cities, UK grid carbon, global temperature since 1880, ocean buoys, VIIRS active fires, USGS river gauges), Cities & transport (live aircraft, Citi Bike docks, bike share in Chicago / DC / SF, Boston MBTA vehicles, NYC 311, US airport delays, NYC restaurant inspections and parking tickets, Chicago crime reports), Space (ISS, upcoming launches, SpaceX history, NOAA space weather, aurora forecast, asteroid flybys, Starlink constellation, fireballs, visible satellites), Web & markets (Hacker News, Lobsters, Wikipedia most-read, Steam top games, crypto, Bitcoin blocks, 90 days of FX, internet outages, live BGP routing, Bluesky pulse, GitHub activity, new arXiv papers, trending books, Open Trivia, Gen‑1 Pokémon, internet radio), United States (NWS alerts, US national debt, every state year by year and the US economy month by month via USAFacts, FEMA, FDA and vehicle recalls, lobbying filings, federal agency budgets, Powerball draws), (COVID, countries, World Bank, Nobel laureates, Met Museum highlights, Open Food Facts) — each opens with charts picked for it and has Query-view SQL against virtual sources. The Data & sources browser has a shortlist and a full . load whole public CSVs: OWID CO₂, life expectancy, GDP, renewables, and population; the Keeling curve; every confirmed exoplanet; 35k power plants; US cities; Gapminder; every M7+ earthquake since 1900; Nobel laureates; FiveThirtyEight's candy ranking. searches every Socrata portal (NYC, Chicago, SF, Seattle, Austin, states, CDC…) by keyword, portal, and sort, and Explore pulls up to ~50k rows of the table straight into Chart. lists every weekly community dataset since 2018 (newest first, filterable by topic, file, or year) — tap a CSV/TSV file to chart it.All captured from loom.ibm.io — each chart opens from a shareable #chart= / #dive= link.
| Live earthquakes on a map | Dive: Wikipedia edits by wiki |
|---|---|
| Explorer | Query |
|---|---|
How these were captured (and how to refresh them): docs/screenshots/README.md.
rustup)nvm)cargo install tauri-climake setup # install npm + cargo deps
make spool # generate sample data in .loom-data
make spin # launch Loom (Tauri + Next.js + optional Ollama)
Then Choose folder → pick .loom-data (or any folder with CSV/Parquet), select a file, and switch to Chart to see suggestions.
Run make (or make help) to list all commands. Every target uses a weaving metaphor.
| Command | Description |
|---|---|
make spin | Full dev: Tauri + Next.js hot reload; starts Ollama in background if available |
make thread | Frontend-only dev (no Rust) — good for UI work and web-only testing |
npx wrangler dev --port 8787 + LOOM_DEV_API=http://localhost:8787 npm run dev | Web dev with the Worker’s /api/* + /s/* (live feeds, catalog search, share links) proxied locally |
make warp | Rust backend type-check only (cargo check) |
make setup | First-time: install npm deps and fetch Rust deps |
| Command | Description |
|---|---|
make weave | Production build → .app bundle |
make weave-web | Static web export only (output in ./out) |
make weave-rust | Rust release binary only |
| Command | Description |
|---|---|
make spool | Generate sample datasets (10K–1M rows) in .loom-data |
make spool-small | Small test set (1K rows) |
make spool-mega | Stress test (5M rows, ~500MB) |
| Command | Description |
|---|---|
make shuttle | Build and run web UI in Docker |
make shuttle-build | Build Docker image only |
make shuttle-down | Stop containers |
make shuttle-shell | Shell into running container |
| Command | Description |
|---|---|
make loft | Build static export and deploy to Cloudflare Workers |
make loft-mcp | Deploy Loom MCP / ChatGPT plugin Worker |
make loft-dry | Same build, dry-run deploy (no upload) |
make thread-mcp | Local MCP Worker on :8787 |
Hosted at loom.ibm.io — the browser web UI. Live feeds (USGS, Wikipedia, NWS, weather, flights, crypto, Hacker News…), catalog discovery (Data.gov / UK), and published story links run through the Cloudflare Worker (workers/catalog-proxy.ts); CSV Load is in-memory and size-capped. Full DuckDB folder workflows need the desktop app.
npx wrangler login # once
make loft # → https://loom.ibm.io/
make loft-mcp # → https://loom-mcp.mhsenkow.workers.dev/mcp
Remote MCP (same pattern as wordcount): chart profiling + recommendations for ChatGPT Developer Mode, Claude connectors, Cursor via mcp-remote.
https://loom-mcp.mhsenkow.workers.dev/mcpmcp/README.md · submission checklist: mcp/SUBMISSION.mdmake loft-mcp
Custom domain (e.g. loom.ibm.io): Cloudflare dashboard → Workers & Pages → loom-storyteller → Domains → add the hostname (or set routes in wrangler.jsonc).
Live streams (web): Connect works in the browser — poll sources via /api/source/*, Wikipedia via EventSource. Buffers are in-memory (not DuckDB); use Explore after Connect.
Feedback notes: Bottom-left note FAB → GitHub issue. One-time: create a fine-grained PAT with Issues: Read and write on mhsenkow/Loom_story_teller, then:
npx wrangler secret put GITHUB_TOKEN
Without the secret, notes still open the GitHub “new issue” form as a fallback.
| Command | Description |
|---|---|
make test | Run unit tests (Vitest) |
make check | Run all checks (TypeScript + Rust) |
make check-ts | TypeScript type-check + lint |
make check-rust | Rust check + clippy |
make fmt | Format code (Prettier + rustfmt) |
| Command | Description |
|---|---|
make unspool | Remove generated .loom-data |
make unravel | Deep clean (node_modules, .next, out, targets, data) |
make tidy | Light clean (build caches only) |
All of these are also exposed as npm scripts: npm run spin, npm run weave, npm run spool, etc.
To use Suggest with AI in the Chart view:
ollama serve
ollama pull llama3.2
.env.local or shell):
NEXT_PUBLIC_OLLAMA_URL — default http://localhost:11434NEXT_PUBLIC_OLLAMA_MODEL — default: first available or llama3.2OLLAMA_ORIGINS="*" ollama serve
Or set OLLAMA_ORIGINS to your dev URL (e.g. http://localhost:1420).Hover Why? on a suggestion to see the reason (heuristic or AI).
┌──────────────────────────────────────────────────────────────────┐
│ Tauri Shell (Rust) │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────────────┐ │
│ │ tauri-fs │ │ DuckDB │ │ reqwest (Data.gov, │ │
│ │ (folder I/O) │ │ (analytics) │ │ save CSV to folder) │ │
│ └──────┬───────┘ └──────┬───────┘ └───────────┬─────────────┘ │
│ │ IPC (invoke) │ │ │
├─────────┼─────────────────┼────────────────────────┼──────────────┤
│ Frontend (Next.js + TypeScript) │
│ ┌──────┴───────┐ ┌──────┴───────┐ ┌─────────────┴───────────┐ │
│ │ Zustand │ │ Vega-Lite │ │ WebGPU / Canvas 2D │ │
│ │ (state) │ │ (spec gen) │ │ (scatter + bar/line/…) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
Loom_story_teller/
├── src-tauri/ # Rust backend
│ ├── Cargo.toml # DuckDB, Tauri, reqwest, etc.
│ ├── tauri.conf.json # Window, plugins, CSP
│ ├── capabilities/ # Tauri v2 permissions
│ └── src/
│ ├── lib.rs # Plugin init, command registration, DB + stream + sources state
│ ├── main.rs # Binary entry
│ ├── db.rs # DuckDB: scan, query, column stats
│ ├── stream.rs # Wikipedia SSE → wiki_stream
│ ├── sources.rs # Poll sources → DuckDB tables (schema from sourceRegistry.ts)
│ └── commands.rs # All #[tauri::command] handlers (see tauri.ts)
│
├── src/ # Next.js frontend
│ ├── app/
│ │ ├── layout.tsx # Root layout, fonts, globals
│ │ └── page.tsx # Three-panel layout (Sidebar | Main | DetailPanel)
│ ├── components/
│ │ ├── Sidebar.tsx # Files, Data & sources, live stream + poll source cards
│ │ ├── TopBar.tsx # View tabs (Explorer / Chart / Query)
│ │ ├── DetailPanel.tsx # Right panel: Stats, Chart (encoding + Visual), Export, Smart
│ │ ├── ChartView.tsx # Chart canvas, suggestions, Smart overlays, title edit, export
│ │ ├── ChartCard.tsx # Thumbnail + “Try” for suggestions
│ │ ├── ExplorerView.tsx # Full-width data table
│ │ ├── QueryView.tsx # SQL editor + results
│ │ └── PreviewFooter.tsx # Collapsible preview + Schema (drag tokens)
│ ├── lib/
│ │ ├── store.ts # Zustand state
│ │ ├── tauri.ts # Typed IPC bridge (invoke wrappers)
│ │ ├── vega.ts # Vega-Lite spec builders
│ │ ├── webgpu.ts # WebGPU pipeline (scatter)
│ │ ├── dive.ts # Dive engine: filters, group by, metrics/percentiles, buckets, compare, SQL, links
│ │ ├── diveSource.ts # Dive row loading (DuckDB sample on desktop, JS buffers on web)
│ │ ├── diveExpr.ts # Dive derived columns: DuckDB-style expression parser + evaluator
│ │ ├── dataCube.ts # Data cube binning, aggregation, camera, Canvas renderer, hover pick
│ │ ├── webgpuCube.ts # WebGPU instanced-voxel renderer for the data cube (cube.wgsl)
│ │ ├── recommendations.ts # Heuristics + recommendStreamStory / recommendSourceStory + SQL snippets
│ │ ├── ollama.ts # Ollama API for AI suggestions
│ │ ├── mock-data.ts # Browser fallbacks when not in Tauri
│ │ ├── format.ts # Number/byte formatting
│ │ ├── chartPalettes.ts # Chart color palettes
│ │ └── smartAnalytics.ts # Anomaly, forecast, trend, reference lines, clustering
│ ├── shaders/
│ │ └── scatter.wgsl # Compute + vertex + fragment
│ └── styles/
│ └── globals.css # Design tokens, theme
│
├── scripts/
│ └── generate_data.py # Sample data (scatter, sales, timeseries)
├── docs/
│ └── screenshots/ # Screenshots for README
├── Makefile # Command Loom (run `make` for help)
├── Dockerfile # Web UI container
├── docker-compose.yml
├── mcp/ # Remote MCP + ChatGPT plugin Worker
│ ├── src/index.ts # Tools: profile / recommend / build_chart
│ └── SUBMISSION.md # OpenAI plugin directory checklist
├── wrangler.jsonc # Cloudflare Workers static web UI
├── next.config.mjs # Static export for Tauri
├── tailwind.config.ts # Token-linked theme
└── package.json
See DOCS.md for a deeper codebase map and conventions.
Token-based in src/styles/globals.css, catalog in src/lib/lookSystem.ts (aligned with ibm.io wordcount / portfolio).
Themes (data-theme): light · dark · contrast · paper · glass · frost · brutal · loom · tank · nes
Chrome (data-ui): braun · monocle · bauhaus · noyes · ikea · military · terminal · nyt
Type: Libre Baskerville, Lora, IBM Plex, Inter, Geist, JetBrains Mono, Fira Code · faces auto/web/local
--loom-* aliases map to semantic colors so Tailwind (bg-loom-surface, …) keeps working. Chart → Visual presets replace look bundles (not merge). Colorblind chart series is a separate a11y toggle.
| Token | Purpose |
|---|---|
--loom-bg / --color-bg | Page background |
--loom-surface | Cards, panels |
--loom-elevated | Hover, inputs |
--loom-border | Borders |
--loom-text / --loom-muted | Text |
--loom-accent | Accent |
--chart-1 … --chart-8 | Series colors (per theme) |
--shadow-1 / --shadow-2 | Theme-aware shadows |
Component classes: .loom-panel, .loom-card, .loom-btn-primary, .loom-btn-ghost, .loom-input, .loom-badge.
Frontend calls go through src/lib/tauri.ts; do not use raw invoke().
| Command | Args | Returns |
|---|---|---|
scan_folder | { folderPath: string } | FileEntry[] |
query_file | { filePath, sql, limit? } | QueryResult |
get_column_stats | { filePath: string } | ColumnInfo[] |
get_sample_rows | { filePath, limit? } | QueryResult |
inspect_file | { filePath, limit? } | InspectResult (stats + sample) |
save_csv_to_folder | { folder_path, url, filename } | string (saved path) |
fetch_data_gov_recent_csv, fetch_uk_data_recent_csv | { rows?, query?, sort? } | DataGovDataset[] (CKAN CSV search) |
stream_* | (see tauri.ts) | Wikipedia SSE ingest → wiki_stream |
source_* | kind: usgs | meteo | nws | world_bank | Poll-based APIs → per-source tables |
Full signatures live in src/lib/tauri.ts and Rust in src-tauri/src/commands.rs.
WebGPU is used for scatter only when the mark is circle, there is no outline/jitter/glow, emoji, category-shape, data-driven glow/outline/opacity encoding, or Smart overlay (anomaly, trend, forecast, reference lines, clustering). When any of those are active, scatter uses Canvas 2D so overlays and encodings render correctly.
CPU: Float32Array (x, y, category, size_norm)
→ upload to GPU storage buffer
→ compute_positions (workgroups) × size_scale
→ screen-space coords + color
→ vertex_main (instanced quads)
→ fragment_main (circle + soft edge)
→ framebuffer
Shaders: src/shaders/scatter.wgsl. Palette aligns with --chart-* in CSS. Size scale (0.5–2×) multiplies size-encoded point radius in the shader.
Module not found: Can't resolve 'canvas' from vega-canvas. This is an optional native dependency used by Vega in Node; the browser build works without it. You can ignore the warning or add canvas as an optional dependency if you run Vega in Node.next.config.mjs uses output: "export". No server-side API routes at runtime.Plot backgrounds use --chart-bg, mirrored by THEME_UI.bg for Canvas and exports and guarded by theme inheritance tests. This separates plotting surfaces from workspace colors (especially NES). Dialogs and sharing sheets inherit the selected corner geometry.
Automatic chart palettes resolve scale before accessibility: categorical uses Okabe–Ito and magnitude uses Cividis when colorblind defaults are enabled. Explicit palette choices are preserved; the Visual panel shows the resolved palette and theme-aware swatches. Device-font mode labels named faces as fallbacks.
Theme mark materials apply to Canvas bars and pies (including previews): flat, glaze, satin, ink, etched. Visual → Composition can override or restore inheritance. The fill path and hit geometry stay unchanged; density ramps remain flat. SVG material finishes remain flat approximations. Grids and title weights inherit theme defaults. Data cube wall/grid colors derive from theme ink; toolbar and Discover share a theme cycle with distinct icons.
Firefly plots keep observations on fixed X/Y axes. Size encodes steady glow intensity; without Size, an encoded time field supplies recency. Without either, every observation has equal weight. Missing timestamps never become row-order recency. Colored cores and source-over halos remain readable on light and dark themes. Axis and grid styling applies to the live chart and Canvas captures. No perpetual twinkle runs, so static data does not imply change and idle charts spend no animation frames.
Chart → Visual → Color keeps Auto and the existing presets, with independent search and category/gradient browsing. Customize up to 16 swatches or gradient stops using native color wells and hex entry; fit the number of colors to a field’s distinct values, reverse the scale, or restore the preset. Swatches follow each chart’s category order (not named category assignments); extra groups repeat the palette. Counts describe the loaded field, before chart-specific Top N or grouping. Encoding → Group into chunks a crowded color or shape into ranges (numbers) or the largest categories plus Other, on every chart that uses those channels. Custom colors are included in Undo/Redo, saved looks, share links, PNG and SVG; SVG remains an approximation for chart types already marked as such. Axis lines, labels and mark outlines also accept custom colors.
[ / ], or by clicking a voxel (other layers ghost; Esc for all). ▦ Table docks a linked pivot table (rows × columns for the slice, or rolled up across depth, with row / column / grand totals) — hover a cell to light its voxels.stream://…