Conversation
Extract inline canvas custom charts into reusable component resources: - Components declare typed params (metrics_view, measure, dimension, time_dimension, scalars) validated at parse and reconcile time - Canvas items reference components via 'component:' + 'params:' bindings; param-bound metrics views flow through transitive access security - ResolveComponent merges defaults, validates bindings, resolves templates, and injects scalar params as native Vega-Lite params - Component workspace with live preview, test bindings, used-by panel, and breaking-change warnings - 'Save as custom viz' extraction from inline charts and 'Detach copy' inverse - Vega-Lite examples gallery (vendored snapshot with static SVG thumbnails) with AI-first import: spec-in-prompt generation, placeholder file, and a self-healing validation loop with static structure/consistency checks - Generated components include customizable order_by and limit params with density-based defaults; order_by follows re-bound fields in the editor - Dev-agent instructions for authoring components (metrics_sql dialect rules, encoding/param type matching, row-local calculate preservation for diverging charts) Gated behind the customComponents frontend flag and custom_components runtime flag.
- Format touched files with prettier - Fix svelte-check errors: Extract string keys in AllKeys so ComponentRefSpec's index signature doesn't widen keys to string | number; narrow the index signature values to scalars (matching the parser's scalar-only bindings); type initializeRow over AddableItem; pass the required editor binding in ComponentWorkspace; remove unused imports and cast union field accesses - Add customComponents to the expected maps in Test_ResolveFeatureFlags
- Add the component_extract_title message key (en/es) - Bind the required viewVL prop on the preview's inline VegaLiteRenderer - Cast metrics_view access in FiltersMapper to string | null
- ResolveCanvas: skip templated metrics_view renderer properties instead of looking them up as literal resource names (param-bound metrics views are collected from the canvas items) - Frontend: give canvas items referencing an external component their positional instance id as component.id, so two references to the same component get distinct DOM ids and consistent selection/cleanup; make the drag-move reselect path instance-aware and use getElementById for ids containing '::' - Transitive access: scan metrics_sql lists (multi-query custom charts) for metrics view refs, not just single query strings - Parser: components declaring a metrics_view param with a default now get a ref to the default metrics view for DAG ordering and invalidation
# Conflicts: # proto/gen/rill/runtime/v1/queries.pb.go # proto/gen/rill/runtime/v1/resources.pb.go # runtime/parser/parser_test.go
…ct time_dimension picker - Renderer validators now run structural checks (required keys, types, enums) on parameterized components with templated properties; only field-membership checks against the metrics view are skipped for unresolvable values. - The time_dimension param picker now only offers time fields (the primary time dimension and time-typed dimensions) and supports search, instead of listing every dimension and returning no results for any search term.
|
Existing canvases that reference a non-chart component file will break. The chart recompiles on every resize frame. The
Duplication. The
Minor: |
nishantmonu51
left a comment
There was a problem hiding this comment.
This PR contains #9685 in full (its head a3c94ec869 is an ancestor of fe70a09aee) and then deletes 9685's "Save as custom viz" extraction (custom-viz/extract/, SaveAsComponentDialog.svelte) and its PNG thumbnails, replacing the Vega gallery with the Flint catalog. The two cannot both merge as they stand: either 9685 lands first and this rebases onto it, or 9685 is closed in favour of this one. Which way do you want to sequence them?
Separately, the branch is 50 commits behind main with content conflicts in 11 files; most are proto regen, but runtime/canvas/component.go needs real work, since main has since gained validateMap (#8931) and an ephemeralNames parameter on validateOptionalMeasureField (#9855) that the rendererValidator receiver refactor has to absorb.
| item?: V1CanvasItem, | ||
| allowUnvalidatedSpec = true, | ||
| ): CanvasComponentType { | ||
| if (item && !item.definedInCanvas && item.component) { |
There was a problem hiding this comment.
Any item with component: <name> and !definedInCanvas now routes to component_ref, and CanvasComponentRef.svelte:96 renders every renderer other than custom_chart as Components with renderer "..." cannot be referenced on a canvas yet. On main, createComponent keys off the referenced component's renderer, so a canvas referencing a standalone kpi, markdown, table or image component file (documented in docs/docs/reference/project-files/component.md) renders today and breaks after this merges, and the reroute is not behind customComponents. Even for custom_chart, normalizeMetricsSQL at CanvasComponentRef.svelte:86 keeps only the first entry of a metrics_sql list, so a standalone multi-query chart whose vega_spec reads query2 loses that dataset, where main's CustomChartRenderer takes metricsSQL: string[]; AddComponentDropdown.svelte:64 then lists every non-inline component regardless of renderer, so the add menu offers items that render as errors. Either route non-custom_chart renderers and array metrics_sql to their existing classes, or gate the reroute on the flag.
| meta: { defaultValue: param.default ?? false }, | ||
| }; | ||
| case "string": | ||
| case "number": |
There was a problem hiding this comment.
A number param with options maps to type: "select", ParamMapper.svelte:250 renders that with Select, whose onChange is typed (value: string) => void, and the string is passed straight to component.updateProperty and persisted to the item's params:. With - name: limit, type: number, options: [10, 25, 50], picking 25 writes limit: "25", and validateParamValueType (runtime/canvas/params.go:270) fails with value for param "limit" must be a number, got 25, so the whole canvas goes invalid from a click in its own inspector. The workspace ParamValueInput already coerces with Number(next); the canvas path needs the same.
| ); | ||
| if (binding) return `{{ .fields.${binding.param}.display_name }}`; | ||
| } | ||
| return substitute(value, replacements); |
There was a problem hiding this comment.
substitute is applied to every string that is not under a title/text key, and the buildReplacements needles are word-bounded field names, so any Vega-Lite enum or mark value equal to a bound field name is rewritten. The catalog's Histogram binds only x: measure and the compiler supplies the y channel as a count aggregate; bind that param to a measure named count (the name this PR's own fixtures use in runtime/server/canvases_test.go) and eject, and "aggregate": "count" becomes "aggregate": "{{ .params.x_axis }}". That resolves back to count for the original binding but to the field name for any dashboard that binds the param differently, and Vega-Lite rejects the spec, so the ejected component is not reusable, which is the property ejection exists to preserve. The same applies to a dimension named bar/line/point against mark.type, or sum/mean/descending against aggregate/op/sort; restrict substitution to field-valued keys and datum[...] references in expression strings.
| $: ejected = parseVegaSpec(vegaSpec); | ||
|
|
||
| // Flint reads the data to make layout decisions, so compilation waits for the rows. | ||
| $: compiled = |
There was a problem hiding this comment.
compiled is reactive on chartWidth/chartHeight, which are bound with bind:clientWidth/bind:clientHeight at line 199, so each frame of a drag-resize produces a new spec object with the full data.values inlined by the compiler. VegaLiteRenderer keeps options identity stable precisely because, as its comment at VegaLiteRenderer.svelte:100 states, svelte-vega re-embeds from options whenever the spec changes, so a fresh spec per frame defeats #9859 for these charts. Debounce the size passed to the compiler, or only recompile once the size has settled.
Uses flint for creating a gallery of custom viz with an option to eject to a vega lite spec for more granular control.
Checklist: