Editor: CodeLens to scaffold Vega-Lite data transforms

Add a discoverable '+ Add transform' / '+ filter/aggregate/...' CodeLens in the
spec editor that scaffolds field-typed transform[] steps, with a type-to-filter
completion on the same catalog as an accelerator. New pure core
(spec-data-transforms): step-slot and view-site classification, the field-typed
catalog, and comma affixing. Deliberately the one home the schema leaves bare —
inline channel transforms and the transform key stay with the JSON schema.

Rename the composition toolbar menu Transform -> Compose so it reads distinctly
from a data transform. Deferred (recorded at their sites): extract a shared
cursor-lens skeleton across the two installers; a shared completion replace-range
helper (now 3 sites); params scaffolding as the sibling feature.
This commit is contained in:
2026-07-01 15:43:48 +03:00
parent da6a675982
commit 87b1b98c12
6 changed files with 851 additions and 21 deletions
+25 -13
View File
@@ -50,6 +50,10 @@ import {
configureSpecExpressionHints,
installExpressionMarkers,
} from '../services/spec-expression-hints';
import {
configureSpecTransformScaffold,
installSpecTransformScaffoldCodeLens,
} from '../services/spec-transform-scaffold';
import { runExtract } from '../services/extract-action';
import { useAppStore } from '../stores/AppStore';
import { confirm } from '../stores/ConfirmStore';
@@ -173,6 +177,8 @@ configureSpecTransformCodeActions();
configureSpecDatasetHints();
// Register the expression completion/signature-help/hover providers once (docs/architecture/08).
configureSpecExpressionHints();
// Register the data-transform step-scaffold completion once (docs/architecture/08).
configureSpecTransformScaffold();
/** The two spec↔config operations, surfaced as an overflow menu (council:
* Carbon menu-buttons — overflow for additional options under space
@@ -198,11 +204,12 @@ const CONFIG_ACTIONS = [
type ConfigActionId = (typeof CONFIG_ACTIONS)[number]['value'];
/** Structural transforms, surfaced as a sibling menu to Config (the discoverable
/** Composition restructures, surfaced as a sibling menu to Config (the discoverable
* home; the lightbulb and F1 palette are the accelerators — see
* services/spec-transform-actions). They act on the selection, else the whole
* spec. */
const TRANSFORM_ACTIONS = [
* spec. Named "Compose" so it reads distinctly from a Vega-Lite data `transform`
* (the pipeline scaffolded by the editor completion — services/spec-transform-scaffold). */
const COMPOSE_ACTIONS = [
{ value: 'layer', label: 'Wrap in layer', detail: 'Overlay marks on shared scales' },
{ value: 'hconcat', label: 'Wrap in horizontal concat', detail: 'Place views side by side' },
{ value: 'vconcat', label: 'Wrap in vertical concat', detail: 'Stack views top to bottom' },
@@ -215,7 +222,7 @@ const TRANSFORM_ACTIONS = [
},
] as const;
type TransformActionId = (typeof TRANSFORM_ACTIONS)[number]['value'];
type ComposeActionId = (typeof COMPOSE_ACTIONS)[number]['value'];
function EditorToolbar({
editorRef,
@@ -280,7 +287,7 @@ function EditorToolbar({
else runExtractConfigToTheme(editor);
};
const handleTransformAction = (action: TransformActionId) => {
const handleComposeAction = (action: ComposeActionId) => {
const editor = editorRef.current;
if (!editor) return;
if (action === 'simplify') runUnwrap(editor);
@@ -344,13 +351,13 @@ function EditorToolbar({
</Button>
)}
<SelectControl
id="editor-transform-actions"
label="Spec transform actions"
heading="Transform"
options={TRANSFORM_ACTIONS}
onSelect={handleTransformAction}
triggerContent="Transform"
triggerTitle="Structural transforms — wrap the focused view in a composition, or simplify one"
id="editor-compose-actions"
label="Spec composition actions"
heading="Compose"
options={COMPOSE_ACTIONS}
onSelect={handleComposeAction}
triggerContent="Compose"
triggerTitle="Composition restructures — wrap the focused view in a layer/concat/facet/repeat, or simplify one"
disabled={activeId === null || editorView === 'published'}
/>
<SelectControl
@@ -447,7 +454,7 @@ export function SpecEditor() {
const configActionsSub = installSpecConfigActions(editor);
// Structural-transform actions in the F1 palette (the lightbulb is registered
// once, globally, above; the toolbar Transform menu is the home). Per editor,
// once, globally, above; the toolbar Compose menu is the home). Per editor,
// disposed below like the config actions.
const transformActionsSub = installSpecTransformActions(editor);
@@ -455,6 +462,10 @@ export function SpecEditor() {
// editor, because its commands need this editor's handle to apply the edit.
const codeLensSub = installSpecTransformCodeLens(editor);
// Cursor-aware data-transform scaffold CodeLens ( Add transform / filter …)
// — per editor for the same reason: its commands drive this editor's snippet edit.
const scaffoldLensSub = installSpecTransformScaffoldCodeLens(editor);
// Validate Vega expressions in the draft and squiggle the invalid ones — per
// editor, because it writes markers to this model (docs/architecture/08).
// Debounced internally; recomputes on edit and on a draft↔published toggle.
@@ -471,6 +482,7 @@ export function SpecEditor() {
configActionsSub.dispose();
transformActionsSub.dispose();
codeLensSub.dispose();
scaffoldLensSub.dispose();
exprMarkersSub.dispose();
editor.dispose();
editorRef.current = null;