feat(docs): visualize KTX ingestion with ReactFlow diagram (#133)

* feat(docs): visualize KTX ingestion with ReactFlow diagram

Reframe the introduction around the two user-facing ingestion outputs (wiki
and executable semantic layer) and replace the static product-mechanics card
flow with a ReactFlow diagram: sources fan into a sequential ingest pipeline,
which forks into wiki and semantic-layer outputs connected by a bidirectional
"references" edge. Drop the .ktx/raw-sources internal-implementation rows from
the intro table and update the content test to guard the new copy.

* Improve KTX docs introduction

* feat(docs): animate ingestion flow with running dots

Replace static smoothstep edges in the introduction page's ingestion
diagram with a custom animated edge that runs glowing cyan dots along
each path, conveying the source → stage → output flow. Dot duration
scales with path length and is hidden under prefers-reduced-motion.

* feat(docs): route ingestion atoms through full source→output journey

Replace per-edge dots with full-journey particles: each atom is born at
a source, threads the entire stage chain, and lands at either the wiki
or semantic layer. Particles are tinted by their source's accent so
the origin is legible. Each source produces exactly 2 atoms (8 total)
to guarantee every input is visibly active, while the destination and
begin offsets are randomized per page load. Particles populate on
client mount to avoid hydration mismatch, and are hidden under
prefers-reduced-motion.
This commit is contained in:
Andrey Avtomonov 2026-05-18 17:41:37 +02:00 committed by GitHub
parent 611f830fe0
commit 7d156d9a06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 870 additions and 480 deletions

View file

@ -23,7 +23,7 @@ test("docs introduction frames the concept before showing product mechanics", as
const heroIndex = introduction.indexOf("Make analytics context");
const whyIndex = introduction.indexOf("## Why KTX");
const createsIndex = introduction.indexOf("## What KTX creates");
const worksIndex = introduction.indexOf("## How KTX works");
const mechanicsIndex = introduction.indexOf("<ProductMechanics />");
const useCaseIndex = introduction.indexOf("## Use it for");
const heroSource = introduction.slice(0, mechanicsIndex);
@ -34,12 +34,12 @@ test("docs introduction frames the concept before showing product mechanics", as
"problem framing should appear after the hero",
);
assert.ok(
createsIndex > whyIndex,
"artifact summary should appear after problem framing",
worksIndex > whyIndex,
"mechanics bridge should appear after problem framing",
);
assert.ok(
mechanicsIndex > createsIndex,
"mechanics component should appear after the artifact summary",
mechanicsIndex > worksIndex,
"mechanics component should appear after the mechanics bridge",
);
assert.ok(
mechanicsIndex < useCaseIndex,
@ -49,49 +49,47 @@ test("docs introduction frames the concept before showing product mechanics", as
assert.doesNotMatch(heroSource, /The Context Layer/);
assert.doesNotMatch(heroSource, /Building Context/);
assert.doesNotMatch(heroSource, /flex flex-wrap gap-3/);
assert.doesNotMatch(introduction, /raw-sources/);
assert.doesNotMatch(introduction, /\.ktx/);
});
test("product mechanics component covers source-specific context and SQL expansion", async () => {
test("product mechanics component explains ingestion outputs", async () => {
const component = await readDocsFile("components/product-mechanics.tsx");
for (const expectedText of [
"How KTX works",
"Build context from source evidence",
"Run agent requests through the model",
"Ingestion",
"Runtime",
"wiki/",
"semantic-layer/",
"raw-sources/",
".ktx/",
"sl_refs",
"Database structure",
"BI and usage evidence",
"Semantic modeling",
"Company documentation",
"Notion pages",
"Sources",
"KTX transforms evidence",
"KTX builds the model",
"Outputs KTX writes",
"Postgres",
"How ingestion works",
"Ingestion flow",
"From scattered source systems to agent-ready context",
"wiki/*.md",
"semantic-layer/*.yaml",
"Wiki",
"Semantic layer",
"Databases",
"BI tools",
"Modeling code",
"Docs and notes",
"Source adapters",
"Context builder",
"Reconciliation",
"Validation",
"PostgreSQL",
"Snowflake",
"BigQuery",
"and many others",
"Metabase",
"Looker",
"dbt",
"MetricFlow",
"LookML",
"extract evidence",
"reconcile entities",
"validate references",
"semantic query plan",
"dialect SQL",
"bounded rows",
"provenance",
"measure: orders.total_revenue",
"dimension: customers.segment",
"select",
"Notion",
"Any text",
"compile into SQL",
'"use client"',
"@xyflow/react",
"<ReactFlow",
"getSmoothStepPath",
"animateMotion",
"mechanics-particle",
"buildParticlePath",
]) {
assert.ok(
component.includes(expectedText),
@ -99,7 +97,27 @@ test("product mechanics component covers source-specific context and SQL expansi
);
}
assert.match(
component,
/nodesDraggable=\{false\}/,
"ReactFlow canvas should disable node dragging",
);
assert.match(
component,
/panOnDrag=\{false\}/,
"ReactFlow canvas should disable panning",
);
assert.match(
component,
/zoomOnScroll=\{false\}/,
"ReactFlow canvas should disable scroll zoom",
);
assert.doesNotMatch(component, /raw-sources/);
assert.doesNotMatch(component, /\.ktx/);
assert.doesNotMatch(component, /Product mechanics/);
assert.doesNotMatch(component, /How KTX works/);
assert.doesNotMatch(component, /Runtime/);
assert.doesNotMatch(component, /A semantic compiler for analytics agents/);
assert.doesNotMatch(component, /KTX does more than retrieve Markdown/);
assert.doesNotMatch(component, /Plain Markdown \+ RAG/);
@ -109,12 +127,9 @@ test("product mechanics component covers source-specific context and SQL expansi
assert.doesNotMatch(component, /KTX works in two moments/);
assert.doesNotMatch(component, /name: "Metabase and query history"/);
assert.doesNotMatch(component, /name: "dbt, MetricFlow, LookML"/);
assert.doesNotMatch(component, /query history/);
assert.doesNotMatch(component, /analyst notes/);
assert.doesNotMatch(component, /ClickHouse/);
assert.doesNotMatch(component, /MySQL/);
assert.doesNotMatch(component, /SQL Server/);
assert.doesNotMatch(component, /SQLite/);
assert.doesNotMatch(
component,
/\/ktx\/brand\/(?:postgresql|snowflake|bigquery|clickhouse|mysql|sqlserver|sqlite|metabase|dbt|looker|notion)\.svg/,