Tokens
Claude, Cursor 등 AI 코딩 도우미 토큰 사용량을 추적·시각화·경쟁
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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://…