vccs
Vue 3용 차트 컴포넌트 모음, Recharts에서 포팅
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Interactive visual analytics, from data to direction.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Interactive visual analytics, from data to direction.
ALUR is a browser-based workspace for inspecting, exploring and understanding data through coordinated tables, charts, maps and visual workflows. Load data, compare distributions, filter and select across linked views, calculate fields, build reproducible processing pipelines and ask the analysis copilot for help.README 전문 보기 ↗Interactive visual analytics, from data to direction.
ALUR is a browser-based workspace for inspecting, exploring and understanding data through coordinated tables, charts, maps and visual workflows. Load data, compare distributions, filter and select across linked views, calculate fields, build reproducible processing pipelines and ask the analysis copilot for help.
Maps are one analytical view, not the product boundary: ALUR uses MapLibre when geography helps answer the question, while the interactive table, charts and DuckDB workflow remain equally important. Everything runs locally in the browser. The copilot is bring-your-own-key — add an OpenRouter API key in Settings; it is stored only in your browser's localStorage.
| Layer | Technology |
|---|---|
| Analytical engine | DuckDB-Wasm (in-browser SQL + spatial, plus h3 fetched on first use) |
| Linked views | TanStack Table + custom SVG charts + MapLibre GL JS |
| Workflow editor | XYFlow (React Flow) |
| State management | Zustand |
| AI assistant | LLM-powered chat with tool calling |
| UI framework | React 18 + Tailwind CSS |
| Build tool | Vite |
Everything runs client-side. No backend, no server, no installation.
npm install
npm run dev # Start dev server on localhost:5173
npm run build # Production build
npm run preview # Preview production build
npm run test # Run the test suite
Netlify is the sole production deployment at alur-app.netlify.app. It builds the site with npm run build and publishes dist; the included netlify.toml records those settings in the repository.
Coordinated visual-analytics shell: the main canvas provides geographic context when available, while charts, layers and the copilot live in a collapsible left panel and the workflow, table and SQL views share a resizable bottom drawer.
┌──────────────────────────────────────────────────────────┐
│ Header: logo · Add data · New project · Settings (BYOK) │
├──┬───────────────┬───────────────────────────────────────┤
│R │ Left panel │ │
│a │ (collapsible) │ MapLibre GL (full-bleed) │
│i │ · Layers + │ │
│l │ Style │ Legend overlay Selection overlay │
│ │ · Charts │ │
│ │ · Copilot │ │
├──┴───────────────┴───────────────────────────────────────┤
│ Bottom drawer (collapsed ▲ / resizable / maximizable) │
│ Workflow (node canvas + palette) · Table · SQL │
└──────────────────────────────────────────────────────────┘
│ │ │
└───────────────┼────────────────────┘
│
┌──────┴──────┐
│ Zustand │ Single store for all state
│ Store │
└──────┬──────┘
│
┌──────┴──────┐
│ DuckDB-Wasm│ Spatial queries, filtering,
│ │ profiling, dot generation
└─────────────┘
GROUP BY, or geometry dissolve), Allocate (spend a budget or capacity down a ranked list), Join (spatial predicate or attribute key), Operation (a named, reusable group of steps you saved), Visualisation (style recipe), Output (map preview or file export)Summarise computes count, count distinct, sum, average, median, min and max over any number of group keys. Sum, average and median cast to a number; min and max do not, so they still work on dates and text. Merging each group's geometry is optional, and keeps the summary mappable.
Allocate answers "who gets served before the money runs out". It accumulates a column in priority order and either flags each row within/over the limit, drops the rows past it, or gives the row straddling it a partial share. Partitioning gives each group its own limit rather than sharing one.
Score combines several numeric columns into one weighted ranking. Each column is normalised across the whole result — min–max, z-score or percentile rank — before weighting, so columns on different scales combine safely, and weights are shares of their total so they need not sum to 1. Alongside the score and its rank it emits one column per criterion recording what that criterion contributed, and those contributions sum exactly to the score.
ST_GeneratePoints creates random dots within polygons, proportional to a numeric fieldAll styles are compiled to native MapLibre expressions — no hand-written JSON.
The Score panel in the left rail is where weights become arguable rather than declared. Moving a weight re-ranks the list underneath it against live data, each row carries a stacked bar of its criterion contributions, and a sensitivity strip reports how far the ranking moves when each weight is nudged — so a criterion that barely disturbs the top of the list is visibly not worth arguing over. Any model built there can be handed to the workflow as a Score node, which is what makes the result reproducible and feeds the rest of the pipeline.
A WHERE clause answers what survived; it cannot answer why a particular row did not, because the statement that produces the answer destroys the evidence. The Filter node's named conditions mode takes a list of labelled conditions instead of one anonymous clause, and writes on every row which ones it failed — alur_excluded, alur_excluded_by and alur_excluded_count. Conditions are hard (can remove a row) or soft (only marks it), so near-misses stay visible instead of being lost. Set the outcome to keep them, marked and nothing is removed at all.
The reasons are plain text, so they group in a Summarise node, colour a map through the category renderer and export to CSV without anything downstream needing to know they are special.
Inside the node, a constraint funnel measures each condition against the real upstream rows and reports both what it removes on its own and what it removes that the earlier conditions had not already. The gap is the useful part: a condition that removes thousands alone but nothing in sequence does not bind, which no surviving row count would ever tell you.
A workflow that reads as fifteen anonymous Attribute nodes is one nobody can follow a month later. Select a run of steps and save them as an operation with a name and fill-in-the-blank values: it joins the node palette and reads as one step on the canvas.
Blanks are found rather than declared — write {{amount}} or {{column}} anywhere in a step's configuration and the save dialog offers them to be labelled and typed. Values are a number, a column, or one of a list, and each is validated before any SQL is built; free text is deliberately not offered, because a value is interpolated into the query.
ALUR ships no domain vocabulary of its own. Retrofit(−20% on Gcons2023) is something you author out of generic nodes, and it travels in your project file for whoever you share it with.
Project files record the workflow, styling, cohorts and report — but not the data, which stays on your machine. ALUR keeps a copy of every file you load in the browser's private storage, so reopening a project or recovering a crashed session re-attaches its data without asking you to find each file again. Where a file is not held — a different machine, or storage the browser has reclaimed — the existing relink prompt asks for exactly that file and checks it matches before accepting it.
Nothing leaves your device. Settings shows how much is cached and clears it, and the cache stays inside a fixed budget by dropping least-recently-used files.
_alur_feature_id assigned on layer creation, used for cross-component linkingData Input Filter Analysis Output
┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────┐
│ wards │ ──► │ need >= 10│ ──► │ ST_Buffer │ ──► │ Map │
│ .parquet │ │ │ │ (500m) │ │ Preview │
└──────────┘ └──────────┘ └──────────────┘ └──────────┘
This compiles to a single DuckDB query:
WITH step1 AS (SELECT * FROM "wards"),
step2 AS (SELECT * FROM step1 WHERE need >= 10),
step3 AS (SELECT *, ST_Buffer("geometry", 500) AS geom_buffered FROM step2)
SELECT * FROM step3 LIMIT 5000;
src/
├── store/
│ └── useStore.ts # Zustand store (nodes, edges, mapLayers, visualAnalytics, chat)
├── types/
│ ├── visualisation.ts # LayerVisualisation union, LegendSpec
│ └── visualAnalytics.ts # VisualFilter, LayerFeatureSelection, summaries
├── utils/
│ ├── workflowEngine.ts # Node graph → chained-CTE SQL compiler
│ ├── mapStyleCompiler.ts # Visualisation → MapLibre expressions
│ ├── classification.ts # Profiling, classification, vis builders
│ ├── scoreModel.ts # Weighted multi-criteria score → SQL
│ ├── aggregationSql.ts # Group-by measures, running-total allocation, top-N
│ ├── filterPredicates.ts # Named conditions → exclusion columns and funnel SQL
│ ├── workflowFragments.ts # Saved operations: parameters, validation, expansion
│ ├── variantLineage.ts # Scenario tree, per-branch parameter diffs, assumptions
│ ├── visualFilterSql.ts # VisualFilter → DuckDB WHERE clauses
│ ├── visualisationResolver.ts # Workflow config → LayerVisualisation
│ ├── scenarioComparison.ts # Variant results → ComparisonSpec
│ ├── storyDiff.ts # Claim matching between two stories
│ ├── fieldCalculator.ts # Table-view expression parser
│ └── toolDefinitions.ts # LLM tool schemas
├── services/
│ ├── duckdb.ts # DuckDB-Wasm init + query interface
│ ├── visualAnalyticsService.ts # Filtered rows, profiles, summaries, temporal range
│ ├── comparisonService.ts # Comparison alignment and denominators
│ ├── scoreService.ts # Live scoring, ranking and weight sensitivity
│ ├── filterFunnelService.ts # What each condition in a filter actually removes
│ ├── sourceCache.ts # Loaded files kept in OPFS so projects reopen with their data
│ ├── layerMaterialization.ts # Workflow result → layer or dataset
│ ├── workflowRun.ts # Registers a run's output across the store
│ ├── projectService.ts # Project manifest save / load / migrate
│ └── storyService.ts # Story export, parse and disclosure
├── components/
│ ├── Visualisation/ # Style editor, cohorts, filters, KPIs, selection
│ ├── Compare/ # Compare workspace + shared evidence views
│ ├── Explain/ # Report workspace, story viewer, diff, export
│ ├── Variants/ # Scenario variants panel
│ ├── Charts/ · Map/ · Flow/ # Chart panel, MapLibre view, workflow nodes
│ ├── shell/ # AppShell, rail, panel, drawer, command palette
│ ├── Chat.tsx # LLM agent with tool execution (BYOK)
│ └── DataTable.tsx # Attribute table with profiling
└── App.tsx # DuckDB init + AppShell mount
379 tests across 46 files, covering the visualisation pipeline, workflow SQL generation, store actions, comparison and story services, and a full workflow smoke test. Some of the more load-bearing ones:
| Test file | Focus |
|---|---|
visualisationIntegration.test.ts | End-to-end pipeline: vis kinds, resolver, branching, interaction state, feature IDs, clustering, cleanup |
workflowEngine.test.ts | SQL generation, joins, visualisation propagation, terminal-node attribution |
scoreModel.test.ts | Weighted score compilation: weights, direction, normalisation, missing values |
filterPredicates.test.ts | Named conditions: NULL handling, hard vs soft, exclusion columns, funnel counts |
workflowFragments.test.ts | Saved operations: parameter validation, expansion, rewiring, graph immutability |
variantLineage.test.ts | Scenario tree, per-branch diffs, orphans and cycles, assumption capture |
comparisonService.test.ts | Comparison alignment, denominators and warnings |
useStore.test.ts | Layer state, layout preferences, scenario variants |
storyDiff.test.ts | Claim matching and divergence reasons between two stories |
sampleWorkflowSmoke.test.tsx | Full workflow sequence + UI mounting + chat tools |
npm test # Run once
npm run test:watch # Watch mode
npm run test:e2e # Playwright
docs/improvement-plan.md is the current plan. Composite scoring, the numeric GROUP BY and allocation nodes, filter provenance, named reusable operations, equal-area hexbins, resumable projects and scenario lineage have shipped; wider copilot coverage has not.
Still deferred, and not in that plan: