Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
8a6a2f6
canvas resource DAG
royendo Jan 27, 2026
769ef52
Update ResourceGraphOverlay.svelte
royendo Jan 27, 2026
1058a08
overwrite source as model
royendo Jan 27, 2026
98b6a7b
Merge branch 'main' into feat/dag-canvas
royendo Jan 28, 2026
94b8fcc
https://www.loom.com/share/f21f8e222af24cc78f843d87d0611d0e
royendo Jan 28, 2026
4e8954b
bring back source, rename to source model. change logic for model pag…
royendo Jan 28, 2026
d3c6ef3
code review fixes/ prettier
royendo Jan 28, 2026
548bede
code qual
royendo Jan 28, 2026
92b53a6
code qual
royendo Jan 28, 2026
600ce9f
cherry pick dag-canvas into branch
royendo Jan 28, 2026
0a04220
Node Badges!
royendo Feb 5, 2026
8bb2889
prettier + functionality
royendo Feb 6, 2026
f9178b2
Merge branch 'main' into feat/dag-changes
royendo Feb 6, 2026
582356d
variant tooltip
royendo Feb 6, 2026
b3ffeeb
SIDEBARRR
royendo Feb 6, 2026
e264299
Merge branch 'main' into feat/dag-changes
royendo Feb 6, 2026
f6a2676
Update GraphInspector.svelte
royendo Feb 6, 2026
b98548e
styling and button fixes
royendo Feb 6, 2026
8c0ebaf
connector fix
royendo Feb 6, 2026
5158421
search and filter
royendo Feb 6, 2026
87d1a97
Update +page.svelte
royendo Feb 7, 2026
53ed800
design
royendo Feb 7, 2026
7f495a1
local PR change review
royendo Feb 9, 2026
1b4db0b
Merge branch 'main' into feat/dag-changes
royendo Feb 9, 2026
4c1cedd
code qual
royendo Feb 9, 2026
2b0b36c
web code qual
royendo Feb 9, 2026
cf5dc60
Update connector-icon-mapping.ts
royendo Feb 9, 2026
eda7c17
fix
royendo Feb 9, 2026
898fc6a
new node page,
royendo Feb 13, 2026
0473b86
Merge branch 'main' into feat/dag-changes
royendo Feb 13, 2026
125ba76
describe node
royendo Feb 13, 2026
20bca57
hide lineage button when on lineage view
royendo Feb 13, 2026
9e61404
prettier
royendo Feb 13, 2026
e32ef95
local PR reivew
royendo Feb 13, 2026
121c8c5
round2
royendo Feb 13, 2026
5475e43
round3
royendo Feb 13, 2026
641eef8
clean up
royendo Feb 13, 2026
2acdb79
Merge branch 'main' into feat/dag-changes
royendo Feb 17, 2026
2d29e12
similar UI as rill cloud resources
royendo Feb 17, 2026
d8f174f
nit behavioral changes, dont show ..., rmeove lineage
royendo Feb 17, 2026
807d10e
remove connector from DAG, it makes it very confusing
royendo Feb 17, 2026
1a71317
code qual
royendo Feb 17, 2026
2ab88ab
must + should fixe
royendo Feb 17, 2026
e8cd30e
nit, dont default to metrics for views, add OK
royendo Feb 18, 2026
790fa14
PR review fixes
royendo Feb 18, 2026
061b728
Merge branch 'main' into feat/dag-changes
royendo Feb 18, 2026
d84d8d9
more contexual nodes, better formatting of describe
royendo Feb 18, 2026
c46b13a
remove --preview, just --preview-locked is enough
royendo Feb 18, 2026
a8a5eee
Pr reiview fixes
royendo Feb 18, 2026
57f8e74
fix overlay CSS
royendo Feb 18, 2026
5f11941
prettier / code qual
royendo Feb 18, 2026
c1ff8ed
Update ResourceGraphOverlay.svelte
royendo Feb 18, 2026
b338ac7
Fixes 1-8
royendo Feb 19, 2026
dfbb115
web code qual
royendo Feb 19, 2026
361f563
missed code qual
royendo Feb 19, 2026
79aae23
nit describe fix
royendo Feb 19, 2026
cd45ed8
olap connector node
royendo Feb 20, 2026
285db57
filter and dropdown fixes
royendo Feb 20, 2026
29a40a7
rmeove node anchors, add some connector info
royendo Feb 20, 2026
69f342e
fix duck --> model, only top level
royendo Feb 20, 2026
44a1683
node fixes, upgrades
royendo Feb 20, 2026
1c3c16f
additional hover states
royendo Feb 20, 2026
cdd0b28
Resource, Web code qual, prettier, fix redirect to Graph
royendo Feb 20, 2026
5439bd3
first pass
royendo Feb 27, 2026
b0dad6e
Merge branch 'main' into feat/dag-changes
royendo Feb 27, 2026
235ca3b
simplify UI, clean up
royendo Mar 2, 2026
4c35236
PR review fixes
royendo Mar 2, 2026
c711a5c
CSS changes, simplify UI
royendo Mar 2, 2026
d6b56d1
disconnect filter from DAG, only dropdown
royendo Mar 2, 2026
d476219
web code
royendo Mar 2, 2026
259a2fe
prettier, code qual
royendo Mar 2, 2026
3f678de
IA and UX review
royendo Mar 2, 2026
375a7b1
prettier
royendo Mar 2, 2026
0ff5eee
Update ResourceNode.svelte
royendo Mar 2, 2026
564dabf
more reviewers
royendo Mar 2, 2026
a474a64
remove warn icon from error/warn state
royendo Mar 2, 2026
0af9211
UX review fixes
royendo Mar 3, 2026
aa67f67
clean up
royendo Mar 3, 2026
212bd67
DAG tool bar consistnecy
royendo Mar 5, 2026
d2268e9
unify resources and DAG pages options, search filters, refresh
royendo Mar 5, 2026
17f16b8
wrap trees, materialized
royendo Mar 5, 2026
a3e5eae
remove connector from all views expect explicit
royendo Mar 5, 2026
749bf8b
see other connectors, change css for loading
royendo Mar 5, 2026
75fe70e
prettier
royendo Mar 5, 2026
1fdaaa4
ux/code local passes
royendo Mar 5, 2026
76878e4
Merge branch 'main' into feat/dag-changes
royendo Mar 6, 2026
c390982
fix checks
royendo Mar 6, 2026
c954cd3
Update ConnectorMenuItems.svelte
royendo Mar 6, 2026
be15053
web code qual
royendo Mar 6, 2026
291e139
min node width, set max view of graph to width of container, dynamic …
royendo Mar 6, 2026
7cb3873
review fixes
royendo Mar 6, 2026
ff44104
Make graph navigation configurable via Svelte context
royendo Mar 13, 2026
b84500e
Hide "Go to Resource" when no openFile handler is provided
royendo Mar 13, 2026
219617d
Remove horizontal padding from graph toolbar bar
royendo Mar 13, 2026
160e485
Remove file path hover tooltip from graph nodes
royendo Mar 13, 2026
52f763d
in rill cloud ;)
royendo Mar 13, 2026
74ee987
fix default view
royendo Mar 14, 2026
99091e3
prettier
royendo Mar 14, 2026
c94cfe8
Merge branch 'main' into feat/dag-changes
royendo Mar 14, 2026
c148220
code wual
royendo Mar 16, 2026
af9e33e
first pass
royendo Mar 24, 2026
3625cce
node name show full; move DAG next to AI first class citizen
royendo Mar 24, 2026
0a11276
only icon and additional legend
royendo Mar 24, 2026
2c09d2b
close on scroll;
royendo Mar 24, 2026
5287633
Merge remote-tracking branch 'origin/main' into feat/dag-changes
royendo Mar 24, 2026
35590df
migrate to svelte
royendo Mar 24, 2026
606c34b
revert to orignal right click modal
royendo Mar 24, 2026
95244bf
close modal on drag event
royendo Mar 24, 2026
07b62a3
fix url in modal
royendo Mar 24, 2026
e81ad39
fix status filter;
royendo Mar 24, 2026
66ee7c1
web fix
royendo Mar 24, 2026
1eaa45f
code qual
royendo Mar 24, 2026
d578811
qa fixes; refresh not updating UI; remove reconcile/error modal and r…
royendo Mar 25, 2026
4faa3a9
Update ResourceGraph.svelte
royendo Mar 25, 2026
81951c7
Merge branch 'main' into feat/dag-changes
royendo Mar 25, 2026
1a5a9a9
Merge branch 'main' into feat/dag-changes
royendo Mar 25, 2026
b9401c9
nit for siloed nodes clean up view; show isolated node toggle
royendo Mar 25, 2026
d3c3d94
prettier
royendo Mar 25, 2026
8de5b13
Update ContentContainer.svelte
royendo Mar 25, 2026
f879cab
Update ContentContainer.svelte
royendo Mar 25, 2026
ae6df13
Di Feedback
royendo Mar 26, 2026
a923e0b
fix
royendo Mar 26, 2026
acfa23f
Update GraphCanvas.svelte
royendo Mar 26, 2026
523e6f2
dag final fixes
royendo Mar 26, 2026
e6808b4
local code review
royendo Mar 26, 2026
52af5b0
round 2
royendo Mar 26, 2026
ee79a0f
prettier
royendo Mar 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion runtime/parser/parse_canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,24 @@ func (p *Parser) parseCanvas(node *Node) error {
}
}

// Track canvas
// Collect MetricsView refs from inline components for direct Canvas -> MetricsView links in the DAG.
// This enables the graph to show Canvas -> MetricsView edges and ensures MetricsView changes
// trigger canvas reconciliation. Only inline components are checked here; external component
// MetricsView dependencies cascade through the Component layer (Canvas -> Component -> MetricsView).
existingRefs := make(map[ResourceName]bool, len(node.Refs))
for _, ref := range node.Refs {
existingRefs[ref] = true
}
for _, def := range inlineComponentDefs {
for _, ref := range def.refs {
if ref.Kind == ResourceKindMetricsView && !existingRefs[ref] {
node.Refs = append(node.Refs, ref)
existingRefs[ref] = true
}
}
}

// Track canvas (now with MetricsView refs included)
r, err := p.insertResource(ResourceKindCanvas, node.Name, node.Paths, node.Refs...)
if err != nil {
return err
Expand Down
18 changes: 9 additions & 9 deletions runtime/parser/parse_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,26 +248,26 @@ func (p *Parser) parseStem(paths []string, ymlPath, yml, sqlPath, sql string) (*
v, ok := v.(string)
if !ok {
err = fmt.Errorf("invalid type %T for property 'type'", v)
break
}
res.Kind, err = ParseResourceKind(v)
if err != nil {
break
} else {
res.Kind, err = ParseResourceKind(v)
}
case "name":
v, ok := v.(string)
if !ok {
err = fmt.Errorf("invalid type %T for property 'name'", v)
break
} else {
res.Name = v
}
res.Name = v
case "connector":
v, ok := v.(string)
if !ok {
err = fmt.Errorf("invalid type %T for property 'connector'", v)
break
} else {
res.Connector = v
}
res.Connector = v
}
if err != nil {
break
}
}
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions runtime/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,12 +646,13 @@ func (p *Parser) parsePaths(ctx context.Context, paths []string) error {
// NOTE 2: Using a map since the two-way check (necessary for reparses) may match the same resource twice.
modelsWithNameErrs := make(map[ResourceName]string)
for _, r := range p.insertedResources {
if r.Name.Kind == ResourceKindSource {
switch r.Name.Kind {
case ResourceKindSource:
n := ResourceName{Kind: ResourceKindModel, Name: r.Name.Name}.Normalized()
if _, ok := p.Resources[n]; ok {
modelsWithNameErrs[n] = r.Name.Name
}
} else if r.Name.Kind == ResourceKindModel {
case ResourceKindModel:
n := ResourceName{Kind: ResourceKindSource, Name: r.Name.Name}.Normalized()
if r2, ok := p.Resources[n]; ok {
modelsWithNameErrs[r.Name.Normalized()] = r2.Name.Name
Expand Down
40 changes: 33 additions & 7 deletions runtime/reconcilers/canvas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,19 @@ rows:
require.NotNil(t, mv1.GetMetricsView().State.ValidSpec)
c1 = testruntime.GetResource(t, rt, id, runtime.ResourceKindCanvas, "c1")
require.NotNil(t, c1.GetCanvas().State.ValidSpec)
require.Len(t, c1.Meta.Refs, 2)
for _, componentName := range c1.Meta.Refs {
r := testruntime.GetResource(t, rt, id, runtime.ResourceKindComponent, componentName.Name)
require.Len(t, c1.Meta.Refs, 3) // 2 components + 1 MetricsView (mv1)
var foundMvRef bool
for _, ref := range c1.Meta.Refs {
if ref.Kind == runtime.ResourceKindMetricsView {
require.Equal(t, "mv1", ref.Name)
foundMvRef = true
continue
}
require.Equal(t, runtime.ResourceKindComponent, ref.Kind)
r := testruntime.GetResource(t, rt, id, runtime.ResourceKindComponent, ref.Name)
require.NotNil(t, r.GetComponent().State.ValidSpec)
}
require.True(t, foundMvRef, "expected a MetricsView ref for mv1")

// Fix everything
testruntime.PutFiles(t, rt, id, map[string]string{"m1.sql": `SELECT 'foo' as foo, 1 as x`})
Expand Down Expand Up @@ -92,10 +100,16 @@ rows:
c1 = testruntime.GetResource(t, rt, id, runtime.ResourceKindCanvas, "c1")
require.NotNil(t, c1.GetCanvas().State.ValidSpec)
require.NotEmpty(t, c1.Meta.ReconcileError)
require.Len(t, c1.Meta.Refs, 2)
require.Len(t, c1.Meta.Refs, 4) // 2 components + 2 MetricsView (doesnt_exist + mv1)
var valid, invalid int
for _, componentName := range c1.Meta.Refs {
r := testruntime.GetResource(t, rt, id, runtime.ResourceKindComponent, componentName.Name)
mvRefNames := make(map[string]bool)
for _, ref := range c1.Meta.Refs {
if ref.Kind == runtime.ResourceKindMetricsView {
mvRefNames[ref.Name] = true
continue
}
require.Equal(t, runtime.ResourceKindComponent, ref.Kind)
r := testruntime.GetResource(t, rt, id, runtime.ResourceKindComponent, ref.Name)
if r.GetComponent().State.ValidSpec == nil {
invalid++
} else {
Expand All @@ -104,6 +118,8 @@ rows:
}
require.Equal(t, 1, valid)
require.Equal(t, 1, invalid)
require.True(t, mvRefNames["mv1"], "expected MetricsView ref for mv1")
require.True(t, mvRefNames["doesnt_exist"], "expected MetricsView ref for doesnt_exist")
}

func TestCanvasValidateMetricsViewTimeConsistency(t *testing.T) {
Expand Down Expand Up @@ -280,7 +296,17 @@ rows:
c1 := testruntime.GetResource(t, rt, id, runtime.ResourceKindCanvas, "c1")
require.NotNil(t, c1.GetCanvas().State.DataRefreshedOn)

comp1 := testruntime.GetResource(t, rt, id, runtime.ResourceKindComponent, c1.Meta.Refs[0].Name)
// Find the component ref (canvas refs now include MetricsView refs too)
var compRefName string
for _, ref := range c1.Meta.Refs {
if ref.Kind == runtime.ResourceKindComponent {
compRefName = ref.Name
break
}
}
require.NotEmpty(t, compRefName)

comp1 := testruntime.GetResource(t, rt, id, runtime.ResourceKindComponent, compRefName)
require.NotNil(t, comp1.GetComponent().State.DataRefreshedOn)

mv1 := testruntime.GetResource(t, rt, id, runtime.ResourceKindMetricsView, "mv1")
Expand Down
10 changes: 9 additions & 1 deletion web-admin/src/components/layout/ContentContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
export let title = "";
export let maxWidth = 800;
export let showTitle = true;
export let flush = false;
</script>

<main
class="bg-surface-base size-full pt-8 pb-16 lg:pt-12 flex flex-col items-center px-8 sm:px-16 lg:px-32 2xl:px-40 overflow-y-auto"
class="bg-surface-base size-full pt-8 pb-16 lg:pt-12 flex flex-col items-center overflow-y-auto"
class:padded={!flush}
style="scrollbar-gutter: stable;"
>
<section class="w-full flex flex-col gap-y-3" style:max-width="{maxWidth}px">
Expand All @@ -21,3 +23,9 @@
<slot />
</section>
</main>

<style lang="postcss">
.padded {
@apply px-8 sm:px-16 lg:px-32 2xl:px-40;
}
</style>
7 changes: 5 additions & 2 deletions web-admin/src/components/nav/LeftNav.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@
label: string;
route: string;
hasPermission?: boolean;
matchPrefix?: boolean;
}[];
</script>

<div class="nav-items" style:min-width={minWidth}>
<!-- if hasPermission is not provided, it will be undefined -->
{#each navItems as { label, route, hasPermission = true } (route)}
{#each navItems as { label, route, hasPermission = true, matchPrefix = false } (route)}
{#if hasPermission}
<LeftNavItem
{label}
link={`${basePage}${route}`}
selected={$page.route.id === `${baseRoute}${route}`}
selected={matchPrefix
? ($page.route.id?.startsWith(`${baseRoute}${route}`) ?? false)
: $page.route.id === `${baseRoute}${route}`}
/>
{/if}
{/each}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
]);
filterSync.init($page.url);

export let showHeader = true;

let isConfirmDialogOpen = false;
let filterDropdownOpen = false;
let statusDropdownOpen = false;
Expand Down Expand Up @@ -73,9 +75,10 @@

type StatusFilter = { label: string; value: string };
const statusFilters: StatusFilter[] = [
{ label: "Error", value: "error" },
{ label: "Warn", value: "warn" },
{ label: "OK", value: "ok" },
{ label: "Pending", value: "pending" },
{ label: "Warning", value: "warning" },
{ label: "Errored", value: "errored" },
];

// Resource types available for filtering (excluding internal types)
Expand Down Expand Up @@ -157,7 +160,9 @@
</script>

<section class="flex flex-col gap-y-4">
<h2 class="text-lg font-medium">Resources</h2>
{#if showHeader}
<h2 class="text-lg font-medium">Resources</h2>
{/if}

<!-- Search, Filter, and Action Controls -->
<div class="flex flex-row items-center gap-x-4 min-h-9">
Expand Down

This file was deleted.

33 changes: 20 additions & 13 deletions web-admin/src/features/projects/status/resource-table/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,32 @@ function makeResource(
}

describe("getResourceStatus", () => {
it("returns 'error' when resource has reconcileError", () => {
it("returns 'errored' when resource has reconcileError", () => {
const r = makeResource(ResourceKind.Model, "m1", {
reconcileError: "something broke",
});
expect(getResourceStatus(r)).toBe("error");
expect(getResourceStatus(r)).toBe("errored");
});

it("returns 'warn' for PENDING status", () => {
it("returns 'warning' for test-only failures", () => {
const r = makeResource(ResourceKind.Model, "m1", {
reconcileError: "tests failed: some test",
});
expect(getResourceStatus(r)).toBe("warning");
});

it("returns 'pending' for PENDING status", () => {
const r = makeResource(ResourceKind.Model, "m1", {
reconcileStatus: V1ReconcileStatus.RECONCILE_STATUS_PENDING,
});
expect(getResourceStatus(r)).toBe("warn");
expect(getResourceStatus(r)).toBe("pending");
});

it("returns 'warn' for RUNNING status", () => {
it("returns 'pending' for RUNNING status", () => {
const r = makeResource(ResourceKind.Model, "m1", {
reconcileStatus: V1ReconcileStatus.RECONCILE_STATUS_RUNNING,
});
expect(getResourceStatus(r)).toBe("warn");
expect(getResourceStatus(r)).toBe("pending");
});

it("returns 'ok' for IDLE status", () => {
Expand All @@ -61,12 +68,12 @@ describe("getResourceStatus", () => {
expect(getResourceStatus(r)).toBe("ok");
});

it("error takes priority over warn status", () => {
it("error takes priority over pending status", () => {
const r = makeResource(ResourceKind.Model, "m1", {
reconcileError: "error msg",
reconcileStatus: V1ReconcileStatus.RECONCILE_STATUS_RUNNING,
});
expect(getResourceStatus(r)).toBe("error");
expect(getResourceStatus(r)).toBe("errored");
});
});

Expand Down Expand Up @@ -115,14 +122,14 @@ describe("filterResources", () => {
expect(result[0].meta?.name?.name).toBe("my_model");
});

it("filters by status 'error'", () => {
const result = filterResources(resources, [], "", ["error"]);
it("filters by status 'errored'", () => {
const result = filterResources(resources, [], "", ["errored"]);
expect(result).toHaveLength(1);
expect(result[0].meta?.name?.name).toBe("errored_model");
});

it("filters by status 'warn'", () => {
const result = filterResources(resources, [], "", ["warn"]);
it("filters by status 'pending'", () => {
const result = filterResources(resources, [], "", ["pending"]);
expect(result).toHaveLength(1);
expect(result[0].meta?.name?.name).toBe("my_metrics");
});
Expand All @@ -134,7 +141,7 @@ describe("filterResources", () => {

it("combines kind + status + search filters", () => {
const result = filterResources(resources, [ResourceKind.Model], "errored", [
"error",
"errored",
]);
expect(result).toHaveLength(1);
expect(result[0].meta?.name?.name).toBe("errored_model");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import LeftNav from "@rilldata/web-admin/components/nav/LeftNav.svelte";

$: basePage = `/${$page.params.organization}/${$page.params.project}/-/status`;

const navItems = [
{
label: "Overview",
Expand All @@ -17,6 +16,7 @@
label: "Resources",
route: "/resources",
hasPermission: true,
matchPrefix: true,
},
{
label: "Tables",
Expand Down
Loading
Loading