feat(schema, vibe): schema explorer - #451
Conversation
8239f8f to
271134a
Compare
e98a1d8 to
0f9d32d
Compare
|
/merge |
📝 WalkthroughWalkthroughThis PR adds an experimental Svelte Schema Explorer, a reusable schema-viewer package, YAML-to-WebAssembly parsing, example schemas, comprehensive tests, workspace configuration, documentation, and GitHub Pages deployment. ChangesSchema Explorer workspace and YAML parsing
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (11)
experimental/vibe/ui/schema-viewer/tests/components.test.ts-221-243 (1)
221-243: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winHarden the assertions that use possibly
undefinedvalues.
fsm.textContent?.trim()andstate.dataset.statecan beundefined.toMatchObjectthen comparesundefinedagainst a missing orundefinedproperty, so the assertion can pass without validating the payload. Capture the values first and assert that they are defined.♻️ Proposed hardening
const fsm = graph.querySelector<HTMLButtonElement>( '[data-quent-role="timeline-fsm-select"]', )!; + const fsmName = fsm.textContent?.trim(); + expect(fsmName).toBeTruthy(); fsm.click(); expect(onSelect.mock.calls.at(-1)?.[0].detail).toMatchObject({ kind: 'entity', - entity: { name: fsm.textContent?.trim() }, + entity: { name: fsmName }, }); const state = graph.querySelector<HTMLButtonElement>( '[data-quent-role="timeline-fsm-state"]', )!; + const stateName = state.dataset.state; + expect(stateName).toBeTruthy(); state.dispatchEvent(new PointerEvent('pointerenter', { bubbles: true })); expect(onHover.mock.calls.at(-1)?.[0].detail).toMatchObject({ kind: 'fsm-state', - state: state.dataset.state, + state: stateName, });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/tests/components.test.ts` around lines 221 - 243, Harden the assertions in the timeline FSM test by capturing the trimmed `fsm.textContent` and `state.dataset.state` values, asserting each is defined, then using those validated values in the `onSelect` and `onHover`/`onSelect` payload expectations. Remove optional chaining from the expected values so missing data cannot make `toMatchObject` pass.experimental/vibe/ui/schema-viewer/tests/xyflow.test.ts-226-239 (1)
226-239: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winStrengthen the label assertions; they can pass without checking anything.
Two problems exist here:
- Line 232 pairs
flow.edges[index]withlayout.references[index]. The adapter filters references, so the two arrays are not guaranteed to be index-aligned.edge.data?.labelX === position?.xreturnstruewhen both sides areundefined. If the adapter stops emittinglabelX, this test still passes.Add a length assertion and require the values to be defined. The same empty-array risk applies to lines 258-276, where
.every()runs without a prior length assertion onflow.edges.♻️ Proposed hardening
+ expect(flow.edges.length).toBe(layout.references.length); + expect(flow.edges.length).toBeGreaterThan(0); expect( layout.references.every( (reference) => reference.labelPosition !== null, ), ).toBe(true); expect( flow.edges.every((edge, index) => { const position = layout.references[index]?.labelPosition; return ( + position !== undefined && + position !== null && + Number.isFinite(edge.data?.labelX) && edge.data?.labelX === position?.x && edge.data?.labelY === position?.y ); }), ).toBe(true);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/tests/xyflow.test.ts` around lines 226 - 239, Strengthen the label assertions in the xyflow test by asserting the relevant edge and reference collections have the expected nonzero/matching lengths before using every. In the flow.edges validation, match each edge to its corresponding layout reference through the adapter’s stable identity rather than array indexes, and require labelX, labelY, and the reference coordinates to be defined before comparing them. Apply the same flow.edges length assertion to the checks covering lines 258–276.experimental/vibe/ui/schema-explorer/scripts/build-yaml-wasm.sh-7-18 (1)
7-18: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winPin the
wasm-bindgenCLI to version0.2.126.The script accepts any
wasm-bindgenexecutable fromPATH. Invoke the CLI from a project-managed installation that matches the crate and lockfile version0.2.126.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-explorer/scripts/build-yaml-wasm.sh` around lines 7 - 18, Update the wasm-bindgen invocation in the build script to use the project-managed CLI pinned to version 0.2.126, rather than resolving an arbitrary executable from PATH. Preserve the existing arguments, output directory, and generated name.experimental/vibe/ui/schema-explorer/src/yaml-schema.ts-10-15 (1)
10-15: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winReset the failed initialization promise.
If
initialize()rejects once,initializationremains rejected. Every later YAML edit then fails before parsing, even if the WASM resource becomes available. Clear the cached promise when initialization fails.Proposed fix
let initialization: Promise<unknown> | null = null; export async function parseYamlSchema(source: string): Promise<Schema> { - initialization ??= initialize(); + initialization ??= initialize().catch((error) => { + initialization = null; + throw error; + }); await initialization; return JSON.parse(parse_schema_json(source)) as Schema; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-explorer/src/yaml-schema.ts` around lines 10 - 15, Update parseYamlSchema’s cached initialization flow so a rejected initialize() promise clears initialization before the rejection propagates. Preserve successful promise reuse and parsing behavior, while allowing a later invocation to retry initialization.experimental/vibe/ui/schema-explorer/package.json-9-9 (1)
9-9: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse the package-local Vite configuration.
Change
--config ../vite.config.tsto--config ./vite.config.ts. Thesrcworkspace exists, and--tsconfig ../tsconfig.jsonresolves to the package-localtsconfig.json.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-explorer/package.json` at line 9, Update the package.json check script to pass the package-local Vite configuration via ./vite.config.ts instead of ../vite.config.ts, while preserving the existing workspace and TypeScript configuration arguments.experimental/vibe/ui/schema-explorer/scripts/verify-yaml-wasm.mjs-1-3 (1)
1-3: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winComplete the contribution prerequisites before merge. PR
#451has no linked approved Quent maintainer issue. Add aSigned-off-by:trailer to all four commits. Wait for the pending CodeRabbit check. The PR title and all other required checks pass.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-explorer/scripts/verify-yaml-wasm.mjs` around lines 1 - 3, Complete the contribution prerequisites for PR `#451` by linking an approved Quent maintainer issue, adding a Signed-off-by trailer to each of the four commits, and waiting for the pending CodeRabbit check to pass before merging.Source: Coding guidelines
experimental/vibe/ui/schema-viewer/package.json-25-25 (1)
25-25: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winRemove the unsupported
--configoption.
svelte-check4.7.4 does not support--config, so thecheckscript fails beforetsc --noEmit. Keep--tsconfig ../tsconfig.json; with--workspace ./src, it resolves to the package's existingtsconfig.json.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/package.json` at line 25, Update the package check script to remove the unsupported --config argument from the svelte-check invocation while preserving --workspace ./src, --tsconfig ../tsconfig.json, and the subsequent tsc --noEmit command.experimental/vibe/ui/schema-viewer/src/styles.css-318-318 (1)
318-318: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winFix the stylelint error. Use lowercase
currentcolor.Stylelint reports
value-keyword-caseon this line. The lint job fails, so the pull request cannot merge.🛠️ Proposed fix
- border: 1px solid currentColor; + border: 1px solid currentcolor;As per coding guidelines: "All CI checks must pass before a pull request is merged."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/styles.css` at line 318, Update the border declaration to use the lowercase CSS keyword currentcolor instead of currentColor, resolving the value-keyword-case stylelint error while preserving the existing border styling.Sources: Coding guidelines, Linters/SAST tools
experimental/vibe/ui/schema-viewer/src/components/ResourceTimeline.svelte-216-226 (1)
216-226: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winThe
aria-labelon line 218 is ignored.The element is a
divwith norole. ARIA does not exposearia-labelon a generic container, so assistive technology announces nothing for the capacity chart.Add
role="img"to make the label effective. Mark the bar elements as decorative.🛠️ Proposed fix
<div class="quent-resource-timeline__bins" + role="img" aria-label={`${capacity.name} illustrative utilization`} > {`#each` capacity.bins as bin (bin.id)} <i + aria-hidden="true" data-quent-role="timeline-capacity-bin" style={`height:${bin.height}px`} ></i> {/each} </div>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/ResourceTimeline.svelte` around lines 216 - 226, Update the capacity chart container in the ResourceTimeline markup to add role="img" alongside its existing aria-label, then mark each timeline capacity bin element identified by data-quent-role="timeline-capacity-bin" as decorative with aria-hidden="true".experimental/vibe/ui/schema-viewer/src/components/details/RecordsSection.svelte-42-55 (1)
42-55: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winSelection state is visual only in the details sections. Both components mark the selected item with CSS classes and provide no accessible state. Screen reader users cannot tell which item is selected. Compute the
selectionMatchesresult once per item and bind it toaria-pressed.
experimental/vibe/ui/schema-viewer/src/components/details/RecordsSection.svelte#L42-L55: add{@constisSelected = selectionMatches(selection, recordSelection)}, setaria-pressed={isSelected}on the record button, and reuseisSelectedinclassNames.experimental/vibe/ui/schema-viewer/src/components/details/ResourcesSection.svelte#L128-L140: add the sameisSelectedconstant forroleSelection, setaria-pressed={isSelected}on the resource-record button, and apply the equivalent change to the resource title button at Lines 62-68.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/details/RecordsSection.svelte` around lines 42 - 55, Make selection state accessible in RecordsSection.svelte and ResourcesSection.svelte by computing each item’s selectionMatches result once as isSelected, adding aria-pressed={isSelected}, and reusing isSelected for the selected CSS classes. Apply this to the record button in RecordsSection.svelte#L42-L55, the resource-record button in ResourcesSection.svelte#L128-L140, and the resource title button in ResourcesSection.svelte#L62-L68.experimental/vibe/ui/schema-viewer/src/components/EntityGraph.svelte-100-132 (1)
100-132: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick winDefer inactive view computations.
ResourceTimelinedoes not receiveactiveand always callsbuildResourceTimeline.GraphViewshadows itsactiveprop with a local lifecycle flag, so it startslayoutEntityGraphwhile hidden. Use the prop to guard both computations.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/EntityGraph.svelte` around lines 100 - 132, Update the EntityGraph view rendering to pass the active state into ResourceTimeline and ensure both ResourceTimeline’s buildResourceTimeline and GraphView’s layoutEntityGraph execute only when their active prop is true. Remove or rename GraphView’s local active shadowing flag so the component prop controls the computation and hidden views defer work.
🧹 Nitpick comments (16)
experimental/vibe/ui/schema-explorer/src/SelectionBreadcrumbs.svelte (1)
38-91: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd an exhaustiveness guard to
buildBreadcrumbs.The switch covers the seven current
SchemaSelectionkinds and has nodefaultcase. The function then relies on the union staying exhaustive. If a new kind is added toSchemaSelectioninschema-viewer/src/lib/types.ts, this function returnsundefined. Line 99 then iteratesundefinedand the breadcrumb bar throws at runtime.Add a
nevercheck so a new kind produces a compile error instead of a runtime failure.♻️ Proposed exhaustiveness guard
case 'resource-record': return [ { label: pathKey(value.resource), kind: 'Resource', selection: { kind: 'resource', resource: value.resource, }, }, { label: pathKey(value.record), kind: 'Record' }, ]; + default: { + const unhandled: never = value; + throw new Error( + `Unhandled selection kind: ${(unhandled as SchemaSelection).kind}`, + ); + } } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-explorer/src/SelectionBreadcrumbs.svelte` around lines 38 - 91, Update buildBreadcrumbs so its switch over SchemaSelection handles the unreachable fallthrough with a never-based exhaustiveness check. Ensure every current selection kind still returns its breadcrumbs, while any future kind causes a compile-time error rather than allowing the function to return undefined.experimental/vibe/ui/schema-viewer/src/lib/layout.ts (1)
510-526: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winClear the cached engine promise when initialization fails.
Line 511 caches the promise in
engine. If the dynamic import or the worker construction fails, the rejected promise stays cached. Every laterlayoutEntityGraphandlayoutFsmTopologycall then rejects with the same error, and the view cannot recover without a page reload.♻️ Proposed change
function layoutEngine(): Promise<ELK> { engine ??= typeof Worker === 'undefined' ? import('elkjs/lib/elk.bundled.js').then( ({ default: ElkConstructor }) => new ElkConstructor({ algorithms: ['layered'], }), ) : import('elkjs/lib/elk-api.js').then( ({ default: ElkConstructor }) => new ElkConstructor({ algorithms: ['layered'], workerFactory: () => new ElkWorker(), }) as ELK, ); + engine.catch(() => { + engine = undefined; + }); return engine; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/lib/layout.ts` around lines 510 - 526, Update layoutEngine to clear the cached engine promise when dynamic import or ELK/worker construction fails, so subsequent layoutEntityGraph and layoutFsmTopology calls retry initialization instead of reusing a rejected promise.experimental/vibe/ui/schema-viewer/src/lib/types.ts (1)
69-90: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDerive
ResolvedEntityGraphConfigfromEntityGraphConfig.Every key is restated manually. A new optional key in
EntityGraphConfigwill not appear here, and the compiler will not report the omission.Required<EntityGraphConfig>keeps both types in sync.♻️ Proposed refactor
-export interface ResolvedEntityGraphConfig { - direction: NonNullable<EntityGraphConfig['direction']>; - edgeRouting: NonNullable<EntityGraphConfig['edgeRouting']>; - density: NonNullable<EntityGraphConfig['density']>; - layeringStrategy: NonNullable<EntityGraphConfig['layeringStrategy']>; - nodePlacementStrategy: NonNullable< - EntityGraphConfig['nodePlacementStrategy'] - >; - hierarchicalGreedySwitch: boolean; - layoutThoroughness: number; - highDegreeNodeTreatment: boolean; - groupNamespaces: boolean; - references: NonNullable<EntityGraphConfig['references']>; - referenceLabels: NonNullable<EntityGraphConfig['referenceLabels']>; - showNodeMetadata: boolean; - showViewSwitcher: boolean; - fitPadding: number; - minZoom: number; - maxZoom: number; - nodeWidth: number; - nodeHeight: number; -} +export type ResolvedEntityGraphConfig = Required<EntityGraphConfig>;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/lib/types.ts` around lines 69 - 90, Update ResolvedEntityGraphConfig to derive its keys from EntityGraphConfig using Required<EntityGraphConfig>, preserving the existing resolved non-nullable property behavior while ensuring newly added configuration keys are checked and included automatically.experimental/vibe/ui/schema-viewer/src/styles.css (1)
31-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWrap these rules in
:where()for consistent specificity.The file wraps almost every selector in
:where()to give zero specificity. That lets a consumer override the styles through theclasseshooks declared inEntityGraphClassesandSchemaDetailsClassesinsrc/lib/types.ts.These rules are not wrapped:
.quent-schema-nameand.quent-schema-name--titleon lines 31-46..quent-schema-details__fsm-flow-node-*on lines 761-822.They carry class specificity (0,1,0). A consumer class passed through
classesalso carries (0,1,0), so source order decides the winner, not the consumer. The override contract is unreliable for these elements.Wrap them in
:where()to match the rest of the file.♻️ Proposed refactor
-.quent-schema-name { +:where(.quent-schema-name) { padding: 0;-.quent-schema-name--title { +:where(.quent-schema-name--title) { color: inherit; }-.quent-schema-details__fsm-flow-node-wrapper { +:where(.quent-schema-details__fsm-flow-node-wrapper) { display: grid;Apply the same change to the remaining
.quent-schema-details__fsm-flow-node-*rules on lines 772-822.Note:
--selected,--entry, and--exitmodifiers must still win over the base rule. Keep their source order after the base rule, because:where()removes the specificity tiebreak.Also applies to: 761-822
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/styles.css` around lines 31 - 46, Wrap the `.quent-schema-name` and `.quent-schema-name--title` selectors in `:where()`, and apply the same zero-specificity wrapping to all `.quent-schema-details__fsm-flow-node-*` rules. Preserve the existing source order so the `--selected`, `--entry`, and `--exit` modifiers remain after and override the base flow-node rule.experimental/vibe/ui/schema-viewer/src/components/details/DataTypeDisplay.svelte (2)
24-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated
classNameshelper in two detail components. Both files declare the same three-line helper with the same signature and the same body. The shared root cause is a missing class-name utility in the package. Other detail components will copy it again.
experimental/vibe/ui/schema-viewer/src/components/details/DataTypeDisplay.svelte#L24-L26: remove the local helper and importclassNamesfrom a shared module, for examplesrc/lib/classNames.ts.experimental/vibe/ui/schema-viewer/src/components/details/EntityEventsSection.svelte#L31-L33: remove the local helper and import the same sharedclassNames.The library already uses an inline
[...].filter(Boolean).join(' ')pattern insrc/lib/xyflow.tsand insrc/components/ResourceTimeline.svelte. Move that pattern into the shared module too, so one implementation covers every call site.♻️ Proposed shared module
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 export function classNames( ...values: Array<string | undefined | null | false> ): string { return values.filter(Boolean).join(' '); }Then in each component:
- function classNames(...values: Array<string | undefined | false>): string { - return values.filter(Boolean).join(' '); - } + import { classNames } from '../../lib/classNames';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/details/DataTypeDisplay.svelte` around lines 24 - 26, Create a shared classNames utility in experimental/vibe/ui/schema-viewer/src/lib/classNames.ts using the existing filter-and-join behavior, then remove the local classNames helper from DataTypeDisplay.svelte at lines 24-26 and EntityEventsSection.svelte at lines 31-33 and import the shared function in both components.
58-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse a distinct
DataTypePart.kindfor each reference label.
dataTypePartscurrently emitsreference-labelfor both labels, so extend the discriminant for target and data labels. Selectdata-quent-rolefrompart.kind, not from the display text inpart.value.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/details/DataTypeDisplay.svelte` around lines 58 - 60, Update the DataTypePart discriminant used by dataTypeParts so target and data reference labels receive distinct kind values instead of sharing reference-label. In DataTypeDisplay, derive data-quent-role from part.kind rather than comparing part.value, preserving the existing target and data role mappings.experimental/vibe/ui/schema-viewer/src/components/GraphView.svelte (1)
95-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRename the local cancellation flag. It shadows the
activeprop.Line 95 declares
let active = true;inside the effect. The component already has anactiveprop, destructured at line 58 and passed toFitViewOnLayouton line 241. Inside the effect body the prop is unreachable.Rename the local variable to
cancelledorcurrent. This prevents a future edit from reading the wrongactive.♻️ Proposed refactor
- let active = true; + let cancelled = false;.then((result) => { - if (!active) { + if (cancelled) { return; }.catch((error: unknown) => { - if (!active) { + if (cancelled) { return; }return () => { - active = false; + cancelled = true; };Also applies to: 150-152
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/GraphView.svelte` at line 95, Rename the effect-local `active` cancellation flag and its references around lines 150–152 to `cancelled` or `current`, while leaving the component `active` prop and its `FitViewOnLayout` usage unchanged.experimental/vibe/ui/schema-viewer/src/components/details/RecordsSection.svelte (1)
24-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the duplicated
classNameshelper.
classNamesis defined identically in this file,ResourcesSection.svelte, andFsmTransitionAttributes.svelte. Move it to a shared module undersrc/liband import it. This removes the duplication and keeps one definition to maintain.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/details/RecordsSection.svelte` around lines 24 - 26, Extract the duplicated classNames helper from RecordsSection.svelte into a shared module under src/lib, then import and use that shared function in RecordsSection.svelte, ResourcesSection.svelte, and FsmTransitionAttributes.svelte. Remove the local definitions while preserving the existing filtering and space-joining behavior.experimental/vibe/ui/schema-viewer/src/lib/schema.ts (2)
365-403: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueNarrow the
rolesparameter type.
collectResourceConsumersandCollectResourceConsumerInputtyperolesasMap<string, unknown>, but the code only callsroles.has(recordKey). Pass aReadonlySet<string>of record keys, or the concrete role type frombuildResources. This keeps the contract explicit and prevents accidental misuse of the erased value type.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/lib/schema.ts` around lines 365 - 403, Narrow the roles contract in collectResourceConsumers and CollectResourceConsumerInput from Map<string, unknown> to ReadonlySet<string>, since consumers only require has(recordKey). Update the corresponding caller, such as buildResources, to pass the record-key set while preserving all existing membership checks.
89-101: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valuePrecompute reference counts to avoid a quadratic scan.
references.filter(...)runs once per entity. The cost is O(entities × references). Build a count map once, then read it per node.♻️ Proposed refactor
+ const referenceCounts = new Map<string, number>(); + for (const reference of references) { + const key = pathKey(reference.source); + referenceCounts.set(key, (referenceCounts.get(key) ?? 0) + 1); + } + return { nodes: schema.entities.map(([path, entity]) => { const resource = parseResource(entity.annotations); return { id: pathKey(path), path, eventCount: Object.keys(entity.events).length, - referenceCount: references.filter( - (reference) => pathKey(reference.source) === pathKey(path), - ).length, + referenceCount: referenceCounts.get(pathKey(path)) ?? 0, fsm: parseFsm(entity) !== null, resource: resource?.kind === 'definition', }; }),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/lib/schema.ts` around lines 89 - 101, Update the schema node-building flow around the `nodes` mapping to precompute reference counts in a map keyed by `pathKey(reference.source)` before iterating entities. Replace the per-entity `references.filter(...)` scan with a map lookup, defaulting to zero when no references exist, while preserving the existing node fields and values..github/workflows/schema-explorer-pages.yml (1)
34-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDerive the base path from
configure-pagesinstead of hardcoding/quent/.The build runs before
Set up Pages, andSCHEMA_EXPLORER_BASEis hardcoded to/quent/. If the repository is renamed or forked, every asset URL in the deployed site breaks.actions/configure-pagesoutputsbase_path. Move that step before the build and read the output.♻️ Proposed change
+ - name: Set up Pages + id: pages + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6 - name: Install dependencies run: pixi run --frozen pnpm --dir experimental/vibe/ui install --frozen-lockfile - name: Build run: pixi run --frozen pnpm --dir experimental/vibe/ui build env: - SCHEMA_EXPLORER_BASE: /quent/ - - name: Set up Pages - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6 + SCHEMA_EXPLORER_BASE: ${{ steps.pages.outputs.base_path }}/🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/schema-explorer-pages.yml around lines 34 - 39, Move the “Set up Pages” step using configure-pages before “Build”, then replace the hardcoded SCHEMA_EXPLORER_BASE value with the step’s base_path output via its step id. Ensure the configure-pages step exposes a unique id and the build consumes that output.Source: Path instructions
experimental/vibe/ui/schema-viewer/src/components/details/ResourcesSection.svelte (1)
26-34: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse the shared record type instead of indexing
usages.
resourceGroupstypes theboundsrecords withResourceDefinition['usages']. This couples the bounds group to the usages field.ResourceRecordis exported from./lib/types. Use it directly so a future divergence betweenusagesandboundsproduces a type error instead of a silent mismatch.♻️ Proposed change
import type { ResourceDefinition, + ResourceRecord, SchemaDetailsClasses, SchemaSelection, } from '../../lib/types'; @@ function resourceGroups(resource: ResourceDefinition): Array<{ role: 'usage' | 'bounds'; - records: ResourceDefinition['usages']; + records: ResourceRecord[]; }> {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/details/ResourcesSection.svelte` around lines 26 - 34, Update the records type in resourceGroups to use the exported ResourceRecord type from ./lib/types instead of ResourceDefinition['usages'], while preserving the existing usage and bounds group behavior.experimental/vibe/ui/schema-viewer/src/components/FitViewOnLayout.svelte (1)
34-70: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winTrack fit options synchronously and include them in the fit guard.
Read the options before
tick. Store the options used by the last successful fit. Otherwise, an option change still returns becausefittedVersion === nextVersion. ReadfittedVersionwithuntrackto avoid the extra effect pass.useViewportInitializedis available in@xyflow/svelte1.6.2.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/FitViewOnLayout.svelte` around lines 34 - 70, Update the effect in FitViewOnLayout to synchronously capture the current fit options before tick, and include those options in the already-fitted guard so option changes trigger a new fit even when version is unchanged. Store the options associated with the last successful fit, read fittedVersion via untrack, and use the available useViewportInitialized API where applicable.experimental/vibe/ui/schema-viewer/src/svelte.d.ts (1)
4-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the wildcard
*.svelteshim. The package already runssvelte-check, which generates component types. This shim makes TypeScript resolve Svelte imports asComponent<Record<string, unknown>>and bypasses prop checks for components such asEntityNodeComponentand the details components. Iftscstill needs a shim, ensure it does not override generated declarations.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/svelte.d.ts` around lines 4 - 9, Remove the wildcard *.svelte module declaration from svelte.d.ts so generated declarations from svelte-check provide component types and prop validation for EntityNodeComponent and the details components; only retain a non-overriding fallback if tsc requires one.experimental/vibe/ui/schema-viewer/src/components/details/FsmTopologyGraph.svelte (1)
72-76: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winLog the layout error before you discard it.
The
catchblock setsfailedand drops the error object. The user sees "FSM layout failed." with no cause. ELK layout failures are hard to reproduce, and the schema explorer is a debugging tool.Keep the flag and record the error.
♻️ Proposed change
- .catch(() => { + .catch((error: unknown) => { if (active) { + console.error('FSM topology layout failed', error); failed = true; } });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/components/details/FsmTopologyGraph.svelte` around lines 72 - 76, Update the layout promise catch block in FsmTopologyGraph to accept the caught error and log it before setting failed, preserving the active guard and existing failure state behavior.experimental/vibe/ui/schema-viewer/src/lib/xyflow.ts (1)
349-365: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the unreachable fallback branch.
pathsis non-empty at line 349 because of the guard at line 335. Every entry inpathsholds at least two points, sopoints.slice(1)yields at least one segment.segmentsis therefore never empty, andArray.prototype.reducewithout an initial value never throws here.longestSegmentis always a truthy object, so lines 362-365 never execute.Drop the condition and the second return to remove the dead code.
♻️ Proposed simplification
const longestSegment = segments.reduce((longest, segment) => segment.length > longest.length ? segment : longest ); - if (longestSegment) { - return { - path: paths.map((value) => value.path).join(' '), - label: { - x: (longestSegment.start.x + longestSegment.end.x) / 2, - y: (longestSegment.start.y + longestSegment.end.y) / 2, - }, - }; - } - return { path: paths.map((value) => value.path).join(' '), - label: paths.at(-1)!.points.at(-1)!, + label: { + x: (longestSegment.start.x + longestSegment.end.x) / 2, + y: (longestSegment.start.y + longestSegment.end.y) / 2, + }, }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@experimental/vibe/ui/schema-viewer/src/lib/xyflow.ts` around lines 349 - 365, In the path-label calculation around the segments reduce, remove the always-true longestSegment condition and delete the fallback return using paths.at. Return the computed path and midpoint label directly after determining longestSegment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@experimental/vibe/ui/package.json`:
- Around line 15-17: Update the schema:ci script in package.json to run cargo
fmt --check and the required locked workspace Clippy command with all targets
and features and warnings denied, alongside the existing Rust tests. Keep the
existing JavaScript checks and build steps unchanged.
In `@experimental/vibe/ui/pnpm-workspace.yaml`:
- Line 1: Remove the no-review declaration comment from pnpm-workspace.yaml
after reviewing and validating the submitted changes; leave the workspace
configuration otherwise unchanged.
In
`@experimental/vibe/ui/schema-viewer/src/components/details/FsmTopologyGraph.svelte`:
- Around line 94-133: Add keyboard selection handling in FsmFlowNode.svelte for
focusable FSM nodes, activating on Enter and Space and invoking the existing
onSelect callback with the node’s FSM state. Wire the handler through the node
component’s available keyboard event path rather than relying on unsupported
SvelteFlow onnodekeydown.
In `@experimental/vibe/ui/schema-viewer/src/components/GraphView.svelte`:
- Around line 92-106: Update the layout $effect in GraphView.svelte to invoke
onLayoutStart through Svelte’s untrack, preventing callback identity changes
from retriggering the expensive layout. Keep the existing callback payload and
layout initialization behavior unchanged.
In `@experimental/vibe/ui/schema-viewer/src/components/RecordDetails.svelte`:
- Around line 35-43: Extract the duplicated selection event and empty-state UI
across all three details components. In
experimental/vibe/ui/schema-viewer/src/components/RecordDetails.svelte:35-43,
ResourceDetails.svelte:35-43, and FsmDetails.svelte:37-45, add and use the
shared emitSchemaSelection(host, detail) helper from lib/events.ts instead of
each local emitSelection, and replace each identical “No schema selected.” block
with a shared empty-state component.
In `@experimental/vibe/ui/schema-viewer/src/components/ResourceTimeline.svelte`:
- Around line 77-122: Extract the duplicated entity-cell markup into a Svelte 5
snippet accepting row and tree-mark glyph parameters, preserving the existing
namespace rendering, badge logic, class lists, and data-quent-role attributes.
Replace both entity-cell copies in the timeline with calls to the snippet,
passing the respective row and glyph values: ● for the first and ○ for the
second.
In `@experimental/vibe/ui/schema-viewer/src/lib/config.ts`:
- Around line 38-52: Update the configuration merge in the config resolver to
remove properties whose values are undefined before spreading config over
DEFAULT_ENTITY_GRAPH_CONFIG. Preserve explicitly provided enum and boolean
values while ensuring fields such as direction and edgeRouting always retain
their defaults when omitted or explicitly undefined.
In `@experimental/vibe/ui/schema-viewer/src/lib/viewRegistry.ts`:
- Around line 8-17: Replace the positional entries in ENTITY_GRAPH_VIEW_REGISTRY
with an id-keyed mapping from EntityGraphView identifiers to their components,
then derive the registry by iterating ENTITY_GRAPH_VIEWS so every declared view
is represented. Preserve the GraphView and ResourceTimeline bindings by matching
their ids rather than array indexes, and update consumers if necessary to
accommodate the resulting derived type instead of the current tuple-literal
type.
In `@experimental/vibe/ui/schema-viewer/vite.config.ts`:
- Around line 35-43: Remove the duplicated isSchemaViewerElement function from
experimental/vibe/ui/schema-viewer/vite.config.ts lines 35-43 and
experimental/vibe/ui/schema-viewer/vitest.config.ts lines 30-38, then import and
use the shared helper in both configurations so they retain the same
custom-element component contract.
- Around line 9-12: Replace URL.pathname with fileURLToPath for the cacheDir URL
in experimental/vibe/ui/schema-viewer/vite.config.ts (lines 9-12), adding the
node:url import. Apply the same conversion and import in
experimental/vibe/ui/schema-viewer/vitest.config.ts (lines 8-11) for the
schema-viewer-test cache directory.
- Around line 23-32: Update the library build configuration in vite.config.ts to
externalize both svelte and `@xyflow/svelte`, including all their subpath imports,
through the Rollup external configuration. Keep the existing library entry,
format, file naming, and CSS settings unchanged.
In `@experimental/vibe/ui/yaml-wasm/src/lib.rs`:
- Line 7: Replace the fixed NAMESPACE_SEPARATOR scheme used by the parsing and
rewriting logic with a per-parse marker guaranteed not to occur in source, or
limit encoding/decoding to structured namespace fields. Update the relevant
parse flow and line-56 replacement to use that marker consistently, and add a
regression test covering a scalar containing the current marker while preserving
the scalar unchanged.
---
Other comments:
In `@experimental/vibe/ui/schema-explorer/package.json`:
- Line 9: Update the package.json check script to pass the package-local Vite
configuration via ./vite.config.ts instead of ../vite.config.ts, while
preserving the existing workspace and TypeScript configuration arguments.
In `@experimental/vibe/ui/schema-explorer/scripts/build-yaml-wasm.sh`:
- Around line 7-18: Update the wasm-bindgen invocation in the build script to
use the project-managed CLI pinned to version 0.2.126, rather than resolving an
arbitrary executable from PATH. Preserve the existing arguments, output
directory, and generated name.
In `@experimental/vibe/ui/schema-explorer/scripts/verify-yaml-wasm.mjs`:
- Around line 1-3: Complete the contribution prerequisites for PR `#451` by
linking an approved Quent maintainer issue, adding a Signed-off-by trailer to
each of the four commits, and waiting for the pending CodeRabbit check to pass
before merging.
In `@experimental/vibe/ui/schema-explorer/src/yaml-schema.ts`:
- Around line 10-15: Update parseYamlSchema’s cached initialization flow so a
rejected initialize() promise clears initialization before the rejection
propagates. Preserve successful promise reuse and parsing behavior, while
allowing a later invocation to retry initialization.
In `@experimental/vibe/ui/schema-viewer/package.json`:
- Line 25: Update the package check script to remove the unsupported --config
argument from the svelte-check invocation while preserving --workspace ./src,
--tsconfig ../tsconfig.json, and the subsequent tsc --noEmit command.
In
`@experimental/vibe/ui/schema-viewer/src/components/details/RecordsSection.svelte`:
- Around line 42-55: Make selection state accessible in RecordsSection.svelte
and ResourcesSection.svelte by computing each item’s selectionMatches result
once as isSelected, adding aria-pressed={isSelected}, and reusing isSelected for
the selected CSS classes. Apply this to the record button in
RecordsSection.svelte#L42-L55, the resource-record button in
ResourcesSection.svelte#L128-L140, and the resource title button in
ResourcesSection.svelte#L62-L68.
In `@experimental/vibe/ui/schema-viewer/src/components/EntityGraph.svelte`:
- Around line 100-132: Update the EntityGraph view rendering to pass the active
state into ResourceTimeline and ensure both ResourceTimeline’s
buildResourceTimeline and GraphView’s layoutEntityGraph execute only when their
active prop is true. Remove or rename GraphView’s local active shadowing flag so
the component prop controls the computation and hidden views defer work.
In `@experimental/vibe/ui/schema-viewer/src/components/ResourceTimeline.svelte`:
- Around line 216-226: Update the capacity chart container in the
ResourceTimeline markup to add role="img" alongside its existing aria-label,
then mark each timeline capacity bin element identified by
data-quent-role="timeline-capacity-bin" as decorative with aria-hidden="true".
In `@experimental/vibe/ui/schema-viewer/src/styles.css`:
- Line 318: Update the border declaration to use the lowercase CSS keyword
currentcolor instead of currentColor, resolving the value-keyword-case stylelint
error while preserving the existing border styling.
In `@experimental/vibe/ui/schema-viewer/tests/components.test.ts`:
- Around line 221-243: Harden the assertions in the timeline FSM test by
capturing the trimmed `fsm.textContent` and `state.dataset.state` values,
asserting each is defined, then using those validated values in the `onSelect`
and `onHover`/`onSelect` payload expectations. Remove optional chaining from the
expected values so missing data cannot make `toMatchObject` pass.
In `@experimental/vibe/ui/schema-viewer/tests/xyflow.test.ts`:
- Around line 226-239: Strengthen the label assertions in the xyflow test by
asserting the relevant edge and reference collections have the expected
nonzero/matching lengths before using every. In the flow.edges validation, match
each edge to its corresponding layout reference through the adapter’s stable
identity rather than array indexes, and require labelX, labelY, and the
reference coordinates to be defined before comparing them. Apply the same
flow.edges length assertion to the checks covering lines 258–276.
---
Nitpick comments:
In @.github/workflows/schema-explorer-pages.yml:
- Around line 34-39: Move the “Set up Pages” step using configure-pages before
“Build”, then replace the hardcoded SCHEMA_EXPLORER_BASE value with the step’s
base_path output via its step id. Ensure the configure-pages step exposes a
unique id and the build consumes that output.
In `@experimental/vibe/ui/schema-explorer/src/SelectionBreadcrumbs.svelte`:
- Around line 38-91: Update buildBreadcrumbs so its switch over SchemaSelection
handles the unreachable fallthrough with a never-based exhaustiveness check.
Ensure every current selection kind still returns its breadcrumbs, while any
future kind causes a compile-time error rather than allowing the function to
return undefined.
In
`@experimental/vibe/ui/schema-viewer/src/components/details/DataTypeDisplay.svelte`:
- Around line 24-26: Create a shared classNames utility in
experimental/vibe/ui/schema-viewer/src/lib/classNames.ts using the existing
filter-and-join behavior, then remove the local classNames helper from
DataTypeDisplay.svelte at lines 24-26 and EntityEventsSection.svelte at lines
31-33 and import the shared function in both components.
- Around line 58-60: Update the DataTypePart discriminant used by dataTypeParts
so target and data reference labels receive distinct kind values instead of
sharing reference-label. In DataTypeDisplay, derive data-quent-role from
part.kind rather than comparing part.value, preserving the existing target and
data role mappings.
In
`@experimental/vibe/ui/schema-viewer/src/components/details/FsmTopologyGraph.svelte`:
- Around line 72-76: Update the layout promise catch block in FsmTopologyGraph
to accept the caught error and log it before setting failed, preserving the
active guard and existing failure state behavior.
In
`@experimental/vibe/ui/schema-viewer/src/components/details/RecordsSection.svelte`:
- Around line 24-26: Extract the duplicated classNames helper from
RecordsSection.svelte into a shared module under src/lib, then import and use
that shared function in RecordsSection.svelte, ResourcesSection.svelte, and
FsmTransitionAttributes.svelte. Remove the local definitions while preserving
the existing filtering and space-joining behavior.
In
`@experimental/vibe/ui/schema-viewer/src/components/details/ResourcesSection.svelte`:
- Around line 26-34: Update the records type in resourceGroups to use the
exported ResourceRecord type from ./lib/types instead of
ResourceDefinition['usages'], while preserving the existing usage and bounds
group behavior.
In `@experimental/vibe/ui/schema-viewer/src/components/FitViewOnLayout.svelte`:
- Around line 34-70: Update the effect in FitViewOnLayout to synchronously
capture the current fit options before tick, and include those options in the
already-fitted guard so option changes trigger a new fit even when version is
unchanged. Store the options associated with the last successful fit, read
fittedVersion via untrack, and use the available useViewportInitialized API
where applicable.
In `@experimental/vibe/ui/schema-viewer/src/components/GraphView.svelte`:
- Line 95: Rename the effect-local `active` cancellation flag and its references
around lines 150–152 to `cancelled` or `current`, while leaving the component
`active` prop and its `FitViewOnLayout` usage unchanged.
In `@experimental/vibe/ui/schema-viewer/src/lib/layout.ts`:
- Around line 510-526: Update layoutEngine to clear the cached engine promise
when dynamic import or ELK/worker construction fails, so subsequent
layoutEntityGraph and layoutFsmTopology calls retry initialization instead of
reusing a rejected promise.
In `@experimental/vibe/ui/schema-viewer/src/lib/schema.ts`:
- Around line 365-403: Narrow the roles contract in collectResourceConsumers and
CollectResourceConsumerInput from Map<string, unknown> to ReadonlySet<string>,
since consumers only require has(recordKey). Update the corresponding caller,
such as buildResources, to pass the record-key set while preserving all existing
membership checks.
- Around line 89-101: Update the schema node-building flow around the `nodes`
mapping to precompute reference counts in a map keyed by
`pathKey(reference.source)` before iterating entities. Replace the per-entity
`references.filter(...)` scan with a map lookup, defaulting to zero when no
references exist, while preserving the existing node fields and values.
In `@experimental/vibe/ui/schema-viewer/src/lib/types.ts`:
- Around line 69-90: Update ResolvedEntityGraphConfig to derive its keys from
EntityGraphConfig using Required<EntityGraphConfig>, preserving the existing
resolved non-nullable property behavior while ensuring newly added configuration
keys are checked and included automatically.
In `@experimental/vibe/ui/schema-viewer/src/lib/xyflow.ts`:
- Around line 349-365: In the path-label calculation around the segments reduce,
remove the always-true longestSegment condition and delete the fallback return
using paths.at. Return the computed path and midpoint label directly after
determining longestSegment.
In `@experimental/vibe/ui/schema-viewer/src/styles.css`:
- Around line 31-46: Wrap the `.quent-schema-name` and
`.quent-schema-name--title` selectors in `:where()`, and apply the same
zero-specificity wrapping to all `.quent-schema-details__fsm-flow-node-*` rules.
Preserve the existing source order so the `--selected`, `--entry`, and `--exit`
modifiers remain after and override the base flow-node rule.
In `@experimental/vibe/ui/schema-viewer/src/svelte.d.ts`:
- Around line 4-9: Remove the wildcard *.svelte module declaration from
svelte.d.ts so generated declarations from svelte-check provide component types
and prop validation for EntityNodeComponent and the details components; only
retain a non-overriding fallback if tsc requires one.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: 1c47c791-4265-44dc-bc19-70703d86e4f4
⛔ Files ignored due to path filters (3)
experimental/vibe/ui/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlexperimental/vibe/ui/schema-explorer/wasm/quent_yaml_bg.wasmis excluded by!**/*.wasmexperimental/vibe/ui/yaml-wasm/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (84)
.github/workflows/schema-explorer-pages.ymlexperimental/README.mdexperimental/vibe/README.mdexperimental/vibe/ui/.gitignoreexperimental/vibe/ui/README.mdexperimental/vibe/ui/eslint.config.jsexperimental/vibe/ui/package.jsonexperimental/vibe/ui/pnpm-workspace.yamlexperimental/vibe/ui/schema-explorer/README.mdexperimental/vibe/ui/schema-explorer/index.htmlexperimental/vibe/ui/schema-explorer/package.jsonexperimental/vibe/ui/schema-explorer/scripts/build-yaml-wasm.shexperimental/vibe/ui/schema-explorer/scripts/verify-yaml-wasm.mjsexperimental/vibe/ui/schema-explorer/src/App.svelteexperimental/vibe/ui/schema-explorer/src/EntityNode.svelteexperimental/vibe/ui/schema-explorer/src/GraphConfigBar.svelteexperimental/vibe/ui/schema-explorer/src/SelectionBreadcrumbs.svelteexperimental/vibe/ui/schema-explorer/src/YamlEditor.svelteexperimental/vibe/ui/schema-explorer/src/main.tsexperimental/vibe/ui/schema-explorer/src/models/dynamo-inference.yamlexperimental/vibe/ui/schema-explorer/src/models/hello.yamlexperimental/vibe/ui/schema-explorer/src/models/simple.yamlexperimental/vibe/ui/schema-explorer/src/models/simulator.yamlexperimental/vibe/ui/schema-explorer/src/models/sirius.yamlexperimental/vibe/ui/schema-explorer/src/style.cssexperimental/vibe/ui/schema-explorer/src/yaml-models.tsexperimental/vibe/ui/schema-explorer/src/yaml-schema.tsexperimental/vibe/ui/schema-explorer/svelte.config.jsexperimental/vibe/ui/schema-explorer/tsconfig.jsonexperimental/vibe/ui/schema-explorer/vite.config.tsexperimental/vibe/ui/schema-explorer/wasm/quent_yaml.d.tsexperimental/vibe/ui/schema-explorer/wasm/quent_yaml.jsexperimental/vibe/ui/schema-explorer/wasm/quent_yaml_bg.wasm.d.tsexperimental/vibe/ui/schema-viewer/README.mdexperimental/vibe/ui/schema-viewer/package.jsonexperimental/vibe/ui/schema-viewer/src/components/ElkFlowEdge.svelteexperimental/vibe/ui/schema-viewer/src/components/EntityEvents.svelteexperimental/vibe/ui/schema-viewer/src/components/EntityFlowNode.svelteexperimental/vibe/ui/schema-viewer/src/components/EntityGraph.svelteexperimental/vibe/ui/schema-viewer/src/components/FitViewOnLayout.svelteexperimental/vibe/ui/schema-viewer/src/components/FsmDetails.svelteexperimental/vibe/ui/schema-viewer/src/components/FsmFlowNode.svelteexperimental/vibe/ui/schema-viewer/src/components/GraphView.svelteexperimental/vibe/ui/schema-viewer/src/components/GraphViewportController.svelteexperimental/vibe/ui/schema-viewer/src/components/NamespaceFlowNode.svelteexperimental/vibe/ui/schema-viewer/src/components/RecordDetails.svelteexperimental/vibe/ui/schema-viewer/src/components/ResourceDetails.svelteexperimental/vibe/ui/schema-viewer/src/components/ResourceTimeline.svelteexperimental/vibe/ui/schema-viewer/src/components/details/DataTypeDisplay.svelteexperimental/vibe/ui/schema-viewer/src/components/details/EntityEventsSection.svelteexperimental/vibe/ui/schema-viewer/src/components/details/FsmSection.svelteexperimental/vibe/ui/schema-viewer/src/components/details/FsmTopologyGraph.svelteexperimental/vibe/ui/schema-viewer/src/components/details/FsmTransitionAttributes.svelteexperimental/vibe/ui/schema-viewer/src/components/details/RecordsSection.svelteexperimental/vibe/ui/schema-viewer/src/components/details/ResourcesSection.svelteexperimental/vibe/ui/schema-viewer/src/index.tsexperimental/vibe/ui/schema-viewer/src/lib/config.tsexperimental/vibe/ui/schema-viewer/src/lib/constants.tsexperimental/vibe/ui/schema-viewer/src/lib/layout.tsexperimental/vibe/ui/schema-viewer/src/lib/resourceTimeline.tsexperimental/vibe/ui/schema-viewer/src/lib/schema.tsexperimental/vibe/ui/schema-viewer/src/lib/selection.tsexperimental/vibe/ui/schema-viewer/src/lib/types.tsexperimental/vibe/ui/schema-viewer/src/lib/viewRegistry.tsexperimental/vibe/ui/schema-viewer/src/lib/xyflow.tsexperimental/vibe/ui/schema-viewer/src/public.tsexperimental/vibe/ui/schema-viewer/src/register.tsexperimental/vibe/ui/schema-viewer/src/styles.cssexperimental/vibe/ui/schema-viewer/src/svelte.d.tsexperimental/vibe/ui/schema-viewer/svelte.config.jsexperimental/vibe/ui/schema-viewer/tests/components.test.tsexperimental/vibe/ui/schema-viewer/tests/fixtures/TestEntityNode.svelteexperimental/vibe/ui/schema-viewer/tests/fixtures/sample-schema.tsexperimental/vibe/ui/schema-viewer/tests/resourceTimeline.test.tsexperimental/vibe/ui/schema-viewer/tests/schema.test.tsexperimental/vibe/ui/schema-viewer/tests/setup.tsexperimental/vibe/ui/schema-viewer/tests/xyflow.test.tsexperimental/vibe/ui/schema-viewer/tsconfig.build.jsonexperimental/vibe/ui/schema-viewer/tsconfig.jsonexperimental/vibe/ui/schema-viewer/vite.config.tsexperimental/vibe/ui/schema-viewer/vitest.config.tsexperimental/vibe/ui/tsconfig.base.jsonexperimental/vibe/ui/yaml-wasm/Cargo.tomlexperimental/vibe/ui/yaml-wasm/src/lib.rs
| "test": "cargo test --manifest-path yaml-wasm/Cargo.toml && pnpm --filter @quent/schema-viewer test && pnpm --filter @quent-experimental/schema-explorer wasm:test", | ||
| "build": "pnpm --filter @quent/schema-viewer build && pnpm --filter @quent-experimental/schema-explorer build", | ||
| "schema:ci": "pnpm lint && pnpm check && pnpm test && pnpm build", |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Run the required Rust checks from schema:ci.
schema:ci runs cargo test only. It does not run cargo fmt --check or the required locked Clippy command with warnings denied. Add these checks to the CI path before this workspace can merge Rust changes.
As per coding guidelines, “Rust changes must be formatted with cargo fmt and linted with cargo clippy --workspace --all-targets --all-features --locked -- -D warnings; warnings are not permitted.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@experimental/vibe/ui/package.json` around lines 15 - 17, Update the schema:ci
script in package.json to run cargo fmt --check and the required locked
workspace Clippy command with all targets and features and warnings denied,
alongside the existing Rust tests. Keep the existing JavaScript checks and build
steps unchanged.
Source: Coding guidelines
| @@ -0,0 +1,22 @@ | |||
| # Generated entirely by coding agents from prompts without human review or refinement. | |||
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Remove the no-review declaration.
This declaration states that no human reviewed or refined the submitted changes. Review and validate the changes before submission, then remove this statement.
As per coding guidelines, “Contributors must understand and be able to explain every submitted change, including AI-assisted changes.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@experimental/vibe/ui/pnpm-workspace.yaml` at line 1, Remove the no-review
declaration comment from pnpm-workspace.yaml after reviewing and validating the
submitted changes; leave the workspace configuration otherwise unchanged.
Source: Coding guidelines
| function selectState( | ||
| { node }: Parameters<NodeEventWithPointer<MouseEvent | TouchEvent, FsmFlowNode>>[0], | ||
| ): void { | ||
| if (!node.data.entry && !node.data.exit) { | ||
| onSelect({ | ||
| kind: 'fsm-state', | ||
| entity: entity.path, | ||
| state: node.data.state, | ||
| }); | ||
| } | ||
| } | ||
| </script> | ||
|
|
||
| <div | ||
| class="quent-schema-details__fsm-graph" | ||
| data-quent-role="fsm-graph" | ||
| style:height={layout | ||
| ? `${Math.min(Math.max(layout.height, 240), 480)}px` | ||
| : '12rem'} | ||
| > | ||
| {#if failed} | ||
| <p class="quent-schema-details__muted">FSM layout failed.</p> | ||
| {:else if !layout} | ||
| <p class="quent-schema-details__muted">Laying out FSM topology.</p> | ||
| {:else} | ||
| <SvelteFlow | ||
| bind:nodes | ||
| bind:edges | ||
| {nodeTypes} | ||
| {edgeTypes} | ||
| fitView | ||
| fitViewOptions={{ padding: 0.12, minZoom: 0.5, maxZoom: 1.5 }} | ||
| minZoom={0.5} | ||
| maxZoom={1.5} | ||
| {...READ_ONLY_FLOW_CONFIG} | ||
| onnodeclick={selectState} | ||
| aria-label={`FSM topology for ${pathKey(entity.path)}`} | ||
| colorMode="light" | ||
| proOptions={{ hideAttribution: true }} | ||
| /> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check the SvelteFlow node keyboard event surface and existing keyboard handling.
set -euo pipefail
echo "=== existing keyboard handlers in the viewer ==="
rg -n -C4 'onnodekeydown|onkeydown|KeyboardEvent' experimental/vibe/ui/schema-viewer/src || true
echo "=== SvelteFlow node event prop types ==="
fd -t f -p 'node_modules/@xyflow/svelte' -e d.ts 2>/dev/null | head -20
rg -n 'onnodekeydown|NodeEventWithPointer|NodeTargetEventWith' \
--iglob '*.d.ts' node_modules/@xyflow 2>/dev/null | head -20 \
|| echo "package types not present in sandbox"Repository: rapidsai/quent
Length of output: 279
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== FsmTopologyGraph.svelte ==="
sed -n '1,180p' experimental/vibe/ui/schema-viewer/src/components/details/FsmTopologyGraph.svelte
echo "=== xyflow adapter ==="
sed -n '1,240p' experimental/vibe/ui/schema-viewer/src/lib/xyflow.ts
echo "=== SvelteFlow dependency declarations ==="
rg -n -C3 '`@xyflow/svelte`|svelte-flow|xyflow' \
experimental/vibe/ui/schema-viewer package.json pnpm-lock.yaml yarn.lock package-lock.json 2>/dev/null || true
echo "=== node component event and focus handling ==="
rg -n -C4 'focusable|tabindex|onkeydown|keydown|NodeEventWithPointer|nodeclick' \
experimental/vibe/ui/schema-viewer/srcRepository: rapidsai/quent
Length of output: 42150
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== FSM adapter node construction ==="
sed -n '225,325p' experimental/vibe/ui/schema-viewer/src/lib/xyflow.ts
echo "=== FSM node component ==="
sed -n '1,180p' experimental/vibe/ui/schema-viewer/src/components/FsmFlowNode.svelte
echo "=== dependency versions and lockfile catalog ==="
sed -n '1,80p' experimental/vibe/ui/schema-viewer/package.json
rg -n -C3 '`@xyflow/svelte`|xyflow' pnpm-workspace.yaml pnpm-lock.yaml experimental/vibe/ui/schema-viewer/package.json 2>/dev/null | head -100
echo "=== tests covering FSM node accessibility or selection ==="
rg -n -C4 'fsm-state|fsm-flow-node|focusable|tabindex|keyboard|Enter|Space|nodeclick' \
experimental/vibe/ui/schema-viewer/tests experimental/vibe/ui/schema-viewer/srcRepository: rapidsai/quent
Length of output: 6254
🌐 Web query:
@xyflow/svelte SvelteFlow onnodekeydown NodeEventWithPointer focusable node keyboard events
💡 Result:
In @xyflow/svelte, there is no public-facing onNodeKeyDown event prop exposed on the SvelteFlow component API [1][2][3]. Keyboard interaction handling is primarily managed internally by the library's NodeWrapper component [4]. Keyboard events for nodes are processed as follows: 1. Built-in Keyboard Support: The library internally handles specific keyboard interactions for focusable nodes, such as moving selected nodes with arrow keys and managing selection states with keys like 'Backspace' or 'Escape' [4]. These behaviors can be controlled via props like deleteKey and disableKeyboardA11y [1][4]. 2. Custom Keyboard Event Handling: If you need to implement custom keyboard logic for a node, the recommended approach is to handle the keydown event directly within your custom node component [5]. Since custom nodes are standard Svelte components, you can add an event listener to your node's container or any specific element inside it:
nodesFocusable is set to true (default), nodes can receive focus, which enables the internal keyboard movement and selection features [4][6]. 4. Canvas-level Events: If you are looking to capture keyboard events on the SvelteFlow canvas itself (when no node is selected), you can set tabIndex={0} on the SvelteFlow container element [8]. This allows the container to receive focus and capture keydown events that aren't otherwise intercepted by nodes or internal components [8]. In summary, rather than using an onNodeKeyDown prop, you should handle custom keyboard interactions within the scope of your custom node components or, for global canvas events, ensure the SvelteFlow container is focusable via tabIndex [4][8].
Citations:
- 1: https://svelteflow.dev/api-reference/svelte-flow
- 2: https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte
- 3: https://cdn.jsdelivr.net/npm/@xyflow/svelte@1.5.0/dist/lib/container/SvelteFlow/types.d.ts
- 4: https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte
- 5: https://svelteflow.dev/api-reference
- 6: https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/container/SvelteFlow/Wrapper.svelte
- 7: feat(NodeWrapper): focus nodes in the viewport on tab xyflow/xyflow#5308
- 8: How to handle keydown events on canvas (when node not selected)? xyflow/xyflow#4692
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== tracked lock/workspace manifests ==="
git ls-files '*lock*' '*workspace*.yaml' '*package.json' | head -80
echo "=== xyflow catalog/version declarations ==="
for f in $(git ls-files '*lock*' '*workspace*.yaml' '*package.json'); do
if rg -q '`@xyflow/svelte`|xyflow' "$f"; then
echo "--- $f"
rg -n -C3 '`@xyflow/svelte`|xyflow' "$f"
fi
done
echo "=== repository tests for FSM selection ==="
rg -n -C6 'FsmTopologyGraph|fsm-state|data-quent-role.*fsm-state|onSelect' \
experimental/vibe/ui/schema-viewer/tests experimental/vibe/ui/schema-viewer/src/components/details \
|| trueRepository: rapidsai/quent
Length of output: 47974
🌐 Web query:
site:github.com/xyflow/xyflow packages/svelte NodeWrapper.svelte onkeydown Enter Space node focusable
💡 Result:
In @xyflow/svelte, the NodeWrapper component manages node focusability and accessibility through the focusable property [1][2]. When a node is marked as focusable, it is rendered with tabIndex={0} and given an appropriate ARIA role (defaulting to "group") to support keyboard navigation [1][2]. The component handles keyboard interactions within NodeWrapper.svelte via an onkeydown handler [1][2]. While the internal implementation focuses on selection and movement—such as using specific keys for node selection or arrow keys for moving nodes [1]—it does not explicitly implement native HTML button-like "Enter" or "Space" activation by default for custom node types unless specifically configured by the developer [1]. If you need to trigger actions on "Enter" or "Space" for a focusable node, you can utilize the domAttributes prop [3][2]. Since recent updates allow passing custom attributes to the node's DOM element via domAttributes [2], you can attach your own event handlers: <div onkeydown={(event) => { if (event.key === 'Enter' || event.key === ' ') { // Your custom activation logic } }} tabindex="0" role="button" >... Key technical details include: - Focusability: Controlled by the focusable boolean in the node definition [3][2]. - Accessibility: The NodeWrapper applies tabIndex={0} and roles to ensure screen readers and keyboard users can interact with nodes [1][2]. - Customization: Use domAttributes to add specific event listeners or additional ARIA attributes to the root node element [3][2]. If you are experiencing issues where keyboard interaction is completely missing, ensure your node is defined with focusable: true and check that disableKeyboardA11y is not set to true in your SvelteFlow configuration [1][4].
Citations:
- 1: https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte
- 2: xyflow/xyflow@09458f5
- 3: https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/types/nodes.ts
- 4: https://github.com/xyflow/xyflow/blob/main/packages/svelte/src/lib/container/SvelteFlow/Wrapper.svelte
Handle keyboard selection in FsmFlowNode.svelte.
@xyflow/svelte does not expose onnodekeydown. Focusable FSM nodes need a keydown handler that activates selection for Enter and Space, then connects that action to onSelect.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@experimental/vibe/ui/schema-viewer/src/components/details/FsmTopologyGraph.svelte`
around lines 94 - 133, Add keyboard selection handling in FsmFlowNode.svelte for
focusable FSM nodes, activating on Enter and Space and invoking the existing
onSelect callback with the node’s FSM state. Wire the handler through the node
component’s available keyboard event path rather than relying on unsupported
SvelteFlow onnodekeydown.
| $effect(() => { | ||
| const currentModel = model; | ||
| const currentConfig = config; | ||
| let active = true; | ||
| const referenceCount = currentModel.references.filter( | ||
| (reference) => | ||
| reference.target && | ||
| referenceMatchesFilter(reference, currentConfig.references), | ||
| ).length; | ||
| const started = performance.now(); | ||
| layoutStatus = 'loading'; | ||
| onLayoutStart({ | ||
| nodeCount: currentModel.nodes.length, | ||
| referenceCount, | ||
| }); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find every call site that passes onLayoutStart to GraphView.
set -euo pipefail
rg -nP -C6 'onLayoutStart' experimental/vibe/ui --glob '*.svelte' --glob '*.ts'Repository: rapidsai/quent
Length of output: 5705
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- GraphView effect and props ---'
sed -n '1,180p' experimental/vibe/ui/schema-viewer/src/components/GraphView.svelte
printf '%s\n' '--- EntityGraph call site and surrounding script ---'
sed -n '1,180p' experimental/vibe/ui/schema-viewer/src/components/EntityGraph.svelte
printf '%s\n' '--- GraphView usages ---'
rg -n -P -C8 '<GraphView|GraphView' experimental/vibe/ui/schema-viewer --glob '*.svelte' --glob '*.ts'
printf '%s\n' '--- Svelte versions and configuration ---'
rg -n -i -C3 '"svelte"|"`@sveltejs`' experimental/vibe/ui --glob 'package.json' --glob 'pnpm-lock.yaml' --glob 'package-lock.json' --glob 'yarn.lock' --glob '*.config.*' --glob 'svelte.config.*'Repository: rapidsai/quent
Length of output: 25975
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- Custom-element consumers and event handlers ---'
rg -n -P -C8 'quent-entity-graph|quent-layout-start|quent-layout-complete|quent-layout-error' . \
--glob '*.svelte' --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.html' --glob '*.md'
printf '%s\n' '--- All assignments and reads of GraphView callback props ---'
rg -n -P -C4 'onLayout(Start|Complete|Error)|\$effect|untrack' experimental/vibe/ui/schema-viewer \
--glob '*.svelte' --glob '*.ts'
printf '%s\n' '--- Relevant package metadata ---'
sed -n '1,90p' experimental/vibe/ui/schema-viewer/package.json
rg -n -i -C4 'svelte:' experimental/vibe/ui --glob 'package.json' --glob 'pnpm-workspace.yaml' --glob 'catalog*' --glob 'pnpm-lock.yaml'Repository: rapidsai/quent
Length of output: 50372
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
entity_graph = Path("experimental/vibe/ui/schema-viewer/src/components/EntityGraph.svelte").read_text()
graph_view = Path("experimental/vibe/ui/schema-viewer/src/components/GraphView.svelte").read_text()
app = Path("experimental/vibe/ui/schema-explorer/src/App.svelte").read_text()
assert re.search(
r"onLayoutStart=\{\(detail: EntityGraphLayoutStart\) =>\s*"
r"emit<EntityGraphLayoutStart>\('quent-layout-start', detail\)\}",
entity_graph,
)
assert re.search(
r"\$effect\(\(\) => \{.*?onLayoutStart\(\{",
graph_view,
re.S,
)
assert "untrack" not in graph_view
for name in ("handleSelection", "handleHover", "handleViewChange"):
match = re.search(
rf"function {name}\b.*?(?=\n\s*function |\n\s*let |\n</script>)",
app,
re.S,
)
if match:
print(f"--- {name} ---")
print(match.group(0).strip())
print("--- source invariant ---")
print("EntityGraph passes an inline onLayoutStart callback: yes")
print("GraphView reads onLayoutStart in the synchronous layout effect: yes")
print("GraphView currently uses untrack: no")
PY
node - <<'JS'
const first = (detail) => detail;
const second = (detail) => detail;
console.log('inline callback identity changes across parent updates:', first !== second);
JSRepository: rapidsai/quent
Length of output: 807
Read onLayoutStart through untrack. EntityGraph.svelte creates a new inline callback on each update. Because the layout effect reads onLayoutStart synchronously, unrelated updates can restart the expensive ELK layout. Wrap the callback invocation in untrack.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@experimental/vibe/ui/schema-viewer/src/components/GraphView.svelte` around
lines 92 - 106, Update the layout $effect in GraphView.svelte to invoke
onLayoutStart through Svelte’s untrack, preventing callback identity changes
from retriggering the expensive layout. Keep the existing callback payload and
layout initialization behavior unchanged.
| function emitSelection(detail: SchemaSelection): void { | ||
| $host().dispatchEvent( | ||
| new CustomEvent<SchemaSelection>('quent-select', { | ||
| detail, | ||
| bubbles: true, | ||
| composed: true, | ||
| }), | ||
| ); | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Extract the duplicated quent-select dispatch and empty-state markup. The three details custom elements repeat an identical emitSelection helper and an identical "No schema selected." block. The shared root cause is a missing helper module and a missing shared empty-state component. A change to the event contract or the empty-state markup must currently be applied in three places.
experimental/vibe/ui/schema-viewer/src/components/RecordDetails.svelte#L35-L43: replace the localemitSelectionwith a sharedemitSchemaSelection(host, detail)helper, and replace the empty-state block at Lines 47-50 with the shared component.experimental/vibe/ui/schema-viewer/src/components/ResourceDetails.svelte#L35-L43: use the same shared helper and the same shared empty-state component.experimental/vibe/ui/schema-viewer/src/components/FsmDetails.svelte#L37-L45: use the same shared helper and the same shared empty-state component.
♻️ Proposed shared helper
Add experimental/vibe/ui/schema-viewer/src/lib/events.ts:
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import type { SchemaSelection } from './types';
export function emitSchemaSelection(
host: HTMLElement,
detail: SchemaSelection,
): void {
host.dispatchEvent(
new CustomEvent<SchemaSelection>('quent-select', {
detail,
bubbles: true,
composed: true,
}),
);
}Then in each component:
+ import { emitSchemaSelection } from '../lib/events';
+
- function emitSelection(detail: SchemaSelection): void {
- $host().dispatchEvent(
- new CustomEvent<SchemaSelection>('quent-select', {
- detail,
- bubbles: true,
- composed: true,
- }),
- );
- }
+ const emitSelection = (detail: SchemaSelection): void =>
+ emitSchemaSelection($host(), detail);📍 Affects 3 files
experimental/vibe/ui/schema-viewer/src/components/RecordDetails.svelte#L35-L43(this comment)experimental/vibe/ui/schema-viewer/src/components/ResourceDetails.svelte#L35-L43experimental/vibe/ui/schema-viewer/src/components/FsmDetails.svelte#L37-L45
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@experimental/vibe/ui/schema-viewer/src/components/RecordDetails.svelte`
around lines 35 - 43, Extract the duplicated selection event and empty-state UI
across all three details components. In
experimental/vibe/ui/schema-viewer/src/components/RecordDetails.svelte:35-43,
ResourceDetails.svelte:35-43, and FsmDetails.svelte:37-45, add and use the
shared emitSchemaSelection(host, detail) helper from lib/events.ts instead of
each local emitSelection, and replace each identical “No schema selected.” block
with a shared empty-state component.
| export const ENTITY_GRAPH_VIEW_REGISTRY = [ | ||
| { | ||
| ...ENTITY_GRAPH_VIEWS[0], | ||
| component: GraphView, | ||
| }, | ||
| { | ||
| ...ENTITY_GRAPH_VIEWS[1], | ||
| component: ResourceTimeline, | ||
| }, | ||
| ] as const; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Key the registry by view id, not by array position.
The registry binds components to ENTITY_GRAPH_VIEWS[0] and ENTITY_GRAPH_VIEWS[1]. This creates two silent failure modes against the ENTITY_GRAPH_VIEWS contract in src/lib/types.ts lines 107-110:
- If the array order changes,
GraphViewrenders forresource-timelineandResourceTimelinerenders forgraph. The code still compiles. - If a third view is appended, the registry omits it. The code still compiles.
Map the components by EntityGraphView id and derive the registry from the array. The compiler then requires an entry for every view id.
♻️ Proposed refactor
import GraphView from '../components/GraphView.svelte';
import ResourceTimeline from '../components/ResourceTimeline.svelte';
-import { ENTITY_GRAPH_VIEWS } from './types';
+import { ENTITY_GRAPH_VIEWS } from './types';
+import type { EntityGraphView } from './types';
-export const ENTITY_GRAPH_VIEW_REGISTRY = [
- {
- ...ENTITY_GRAPH_VIEWS[0],
- component: GraphView,
- },
- {
- ...ENTITY_GRAPH_VIEWS[1],
- component: ResourceTimeline,
- },
-] as const;
+const ENTITY_GRAPH_VIEW_COMPONENTS: Record<
+ EntityGraphView,
+ typeof GraphView | typeof ResourceTimeline
+> = {
+ graph: GraphView,
+ 'resource-timeline': ResourceTimeline,
+};
+
+export const ENTITY_GRAPH_VIEW_REGISTRY = ENTITY_GRAPH_VIEWS.map((view) => ({
+ ...view,
+ component: ENTITY_GRAPH_VIEW_COMPONENTS[view.id],
+}));Note: consumers that rely on the tuple literal type of the current as const export need a check after this change.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const ENTITY_GRAPH_VIEW_REGISTRY = [ | |
| { | |
| ...ENTITY_GRAPH_VIEWS[0], | |
| component: GraphView, | |
| }, | |
| { | |
| ...ENTITY_GRAPH_VIEWS[1], | |
| component: ResourceTimeline, | |
| }, | |
| ] as const; | |
| import GraphView from '../components/GraphView.svelte'; | |
| import ResourceTimeline from '../components/ResourceTimeline.svelte'; | |
| import { ENTITY_GRAPH_VIEWS } from './types'; | |
| import type { EntityGraphView } from './types'; | |
| const ENTITY_GRAPH_VIEW_COMPONENTS: Record< | |
| EntityGraphView, | |
| typeof GraphView | typeof ResourceTimeline | |
| > = { | |
| graph: GraphView, | |
| 'resource-timeline': ResourceTimeline, | |
| }; | |
| export const ENTITY_GRAPH_VIEW_REGISTRY = ENTITY_GRAPH_VIEWS.map((view) => ({ | |
| ...view, | |
| component: ENTITY_GRAPH_VIEW_COMPONENTS[view.id], | |
| })); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@experimental/vibe/ui/schema-viewer/src/lib/viewRegistry.ts` around lines 8 -
17, Replace the positional entries in ENTITY_GRAPH_VIEW_REGISTRY with an
id-keyed mapping from EntityGraphView identifiers to their components, then
derive the registry by iterating ENTITY_GRAPH_VIEWS so every declared view is
represented. Preserve the GraphView and ResourceTimeline bindings by matching
their ids rather than array indexes, and update consumers if necessary to
accommodate the resulting derived type instead of the current tuple-literal
type.
| cacheDir: new URL( | ||
| '../node_modules/.vite/schema-viewer', | ||
| import.meta.url, | ||
| ).pathname, |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
URL.pathname is used as a filesystem path in both Vite configuration files. URL.pathname keeps percent-encoding and returns a leading-slash drive path on Windows, so the resolved cacheDir is invalid on Windows and wrong for any path that contains a space. Use fileURLToPath from node:url in both files.
experimental/vibe/ui/schema-viewer/vite.config.ts#L9-L12: wrap thenew URL('../node_modules/.vite/schema-viewer', import.meta.url)expression infileURLToPathand add thenode:urlimport.experimental/vibe/ui/schema-viewer/vitest.config.ts#L8-L11: wrap thenew URL('../node_modules/.vite/schema-viewer-test', import.meta.url)expression infileURLToPathand add thenode:urlimport.
📍 Affects 2 files
experimental/vibe/ui/schema-viewer/vite.config.ts#L9-L12(this comment)experimental/vibe/ui/schema-viewer/vitest.config.ts#L8-L11
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@experimental/vibe/ui/schema-viewer/vite.config.ts` around lines 9 - 12,
Replace URL.pathname with fileURLToPath for the cacheDir URL in
experimental/vibe/ui/schema-viewer/vite.config.ts (lines 9-12), adding the
node:url import. Apply the same conversion and import in
experimental/vibe/ui/schema-viewer/vitest.config.ts (lines 8-11) for the
schema-viewer-test cache directory.
| build: { | ||
| lib: { | ||
| entry: { | ||
| index: 'src/index.ts', | ||
| }, | ||
| formats: ['es'], | ||
| fileName: (_format, entryName) => `${entryName}.js`, | ||
| cssFileName: 'schema-viewer', | ||
| }, | ||
| }, |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect the schema-viewer manifest for peerDependencies and runtime deps.
set -euo pipefail
fd -t f 'package.json' experimental/vibe/ui --exec sh -c 'echo "=== $1 ==="; cat "$1"' _ {}Repository: rapidsai/quent
Length of output: 3859
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import json
from pathlib import Path
manifest = Path("experimental/vibe/ui/schema-viewer/package.json")
config = Path("experimental/vibe/ui/schema-viewer/vite.config.ts")
pkg = json.loads(manifest.read_text())
print("manifest dependencies:")
for name in ("svelte", "`@xyflow/svelte`"):
locations = [
section for section in ("dependencies", "optionalDependencies", "peerDependencies", "devDependencies")
if name in pkg.get(section, {})
]
print(f" {name}: {locations}")
text = config.read_text()
print("rollup external configuration:")
for line_no, line in enumerate(text.splitlines(), 1):
if "external" in line or "rollupOptions" in line:
print(f" {line_no}: {line}")
PY
echo "=== schema-viewer config ==="
cat -n experimental/vibe/ui/schema-viewer/vite.config.ts
echo "=== `@xyflow/svelte` references ==="
rg -n --glob '*.{ts,svelte,json}' '`@xyflow/svelte`|svelte' experimental/vibe/ui/schema-viewer/src experimental/vibe/ui/schema-viewer/package.jsonRepository: rapidsai/quent
Length of output: 7837
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== lockfile entries for `@xyflow/svelte` ==="
rg -n -A18 -B3 '(`@xyflow/svelte`|`@xyflow`%2fsvelte)' pnpm-lock.yaml experimental/vibe/ui 2>/dev/null || true
echo "=== dependency declarations across workspace manifests ==="
rg -n -A4 -B2 '"`@xyflow/svelte`"|"svelte"' --glob 'package.json' --glob 'pnpm-lock.yaml' .
echo "=== source imports from `@xyflow/svelte` ==="
rg -l --glob '*.{ts,svelte}' "from '`@xyflow/svelte`'|from \"`@xyflow/svelte`\"|`@xyflow/svelte/dist/style.css`" experimental/vibe/ui/schema-viewer/srcRepository: rapidsai/quent
Length of output: 34638
🌐 Web query:
Vite library mode automatic externalization peerDependencies build.rollupOptions.external @xyflow/svelte peerDependencies svelte
💡 Result:
Vite does not currently provide a native, automatic feature to externalize peerDependencies in library mode [1][2]. By design, Vite bundles dependencies by default to ensure the library is self-contained [3][4]. To prevent your library from bundling its peer dependencies (such as svelte when working with @xyflow/svelte), you must explicitly configure them in your vite.config.ts [3][5]. To avoid bundling peer dependencies like svelte, update your build configuration to include them in rollupOptions.external [6][5]. Common Implementation Pattern A common and recommended approach is to programmatically extract dependencies from your package.json, which ensures that your configuration stays in sync with your manifest [6][5]: import { defineConfig } from 'vite'; import pkg from './package.json'; export default defineConfig({ build: { lib: { entry: 'src/index.ts', formats: ['es', 'cjs'], }, rollupOptions: { // Externalize all packages listed in peerDependencies external: [...Object.keys(pkg.peerDependencies || {}), ], }, }, }); Important Considerations 1. Sub-path Matching: Simply listing a package name (e.g., 'svelte') in external will not automatically externalize its sub-paths (e.g., 'svelte/internal'). If your library imports sub-paths, you should use a regular expression or a function in the external array to match them [6]: external: [/^svelte($|/)/] 2. Why Manual Configuration is Required: While there have been community proposals to automate this (e.g., inferring from package.json) [1][2], Vite maintainers have historically prioritized keeping core library mode configurations explicit to support diverse use cases and avoid unexpected breaking changes in how dependencies are resolved [1][2]. 3. Context for @xyflow/svelte: When building libraries that depend on framework-specific packages like @xyflow/svelte or svelte, keeping them external is critical [6][5]. If they are bundled, consumers may encounter errors due to multiple versions of the framework being initialized or conflicts with the framework's internal runtime [6][7][4]. Always ensure that any package listed as a peerDependency in your package.json is also accounted for in your build.rollupOptions.external config [6].
Citations:
- 1: Infer library mode configurations from package.json vitejs/vite#12405
- 2: Automatically use `package.json`'s dependencies for `rollupOptions.external` when in `lib` mode vitejs/vite#10582
- 3: Why should we bundle dependencies when using lib mode? vitejs/vite#18744
- 4: https://cmdcolin.github.io/posts/2025-02-23-vitelibrarymode/
- 5: https://stackoverflow.com/questions/70433387/library-mode-in-vitejs-how-should-i-manage-external-dependencies
- 6: https://frontend-build-tooling.com/vite-configuration-ecosystem/vite-library-mode-and-package-bundling/
- 7: Chore/bump svelte peer dep xyflow/xyflow#4758
Externalize svelte and @xyflow/svelte in the library build. svelte is a peer dependency, but Vite does not add peer dependencies to Rollup externalization automatically. @xyflow/svelte is a regular dependency. Add subpath-aware external rules for both packages to prevent duplicate Svelte runtimes in consumers.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@experimental/vibe/ui/schema-viewer/vite.config.ts` around lines 23 - 32,
Update the library build configuration in vite.config.ts to externalize both
svelte and `@xyflow/svelte`, including all their subpath imports, through the
Rollup external configuration. Keep the existing library entry, format, file
naming, and CSS settings unchanged.
| function isSchemaViewerElement(filename: string): boolean { | ||
| return [ | ||
| '/EntityGraph.svelte', | ||
| '/EntityEvents.svelte', | ||
| '/FsmDetails.svelte', | ||
| '/RecordDetails.svelte', | ||
| '/ResourceDetails.svelte', | ||
| ].some((suffix) => filename.endsWith(suffix)); | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
isSchemaViewerElement is duplicated across the build and test configuration. Both copies declare the same contract: which Svelte components compile as custom elements. If the two lists diverge, the tests exercise a different compilation mode than the shipped bundle, and no error is reported.
experimental/vibe/ui/schema-viewer/vite.config.ts#L35-L43: remove the local function and import the shared helper.experimental/vibe/ui/schema-viewer/vitest.config.ts#L30-L38: remove the local function and import the same shared helper.
📍 Affects 2 files
experimental/vibe/ui/schema-viewer/vite.config.ts#L35-L43(this comment)experimental/vibe/ui/schema-viewer/vitest.config.ts#L30-L38
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@experimental/vibe/ui/schema-viewer/vite.config.ts` around lines 35 - 43,
Remove the duplicated isSchemaViewerElement function from
experimental/vibe/ui/schema-viewer/vite.config.ts lines 35-43 and
experimental/vibe/ui/schema-viewer/vitest.config.ts lines 30-38, then import and
use the shared helper in both configurations so they retain the same
custom-element component contract.
| use serde_json::Value; | ||
| use wasm_bindgen::prelude::*; | ||
|
|
||
| const NAMESPACE_SEPARATOR: &str = "QuentNamespaceSeparator"; |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Prevent collisions with the namespace marker.
QuentNamespaceSeparator is valid YAML scalar content. If an annotation or other scalar contains this literal, line 56 rewrites it to :: even though line 20 did not encode it. The parser then returns altered schema data.
Generate a marker that is absent from source for each parse, or encode and decode only structured namespace fields. Add a regression test for a scalar that contains the current marker.
Proposed direction
-fn parse_schema_value(source: &str) -> Result<Value, String> {
- let encoded = source.replace("::", NAMESPACE_SEPARATOR);
+fn parse_schema_value(source: &str) -> Result<Value, String> {
+ let mut marker = NAMESPACE_SEPARATOR.to_owned();
+ while source.contains(&marker) {
+ marker.push('_');
+ }
+ let encoded = source.replace("::", &marker);
let parsed = quent_yaml::parse_from_str(encoded, Some("editor.yaml"))
.map_err(|error| error.to_string())?;
let mut schema = serde_json::to_value(parsed.schema).map_err(|error| error.to_string())?;
- restore_namespaces(&mut schema);
+ restore_namespaces(&mut schema, &marker);Also applies to: 20-20, 55-56
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@experimental/vibe/ui/yaml-wasm/src/lib.rs` at line 7, Replace the fixed
NAMESPACE_SEPARATOR scheme used by the parsing and rewriting logic with a
per-parse marker guaranteed not to occur in source, or limit encoding/decoding
to structured namespace fields. Update the relevant parse flow and line-56
replacement to use that marker consistently, and add a regression test covering
a scalar containing the current marker while preserving the scalar unchanged.
# Description Externalize Svelte from the schema-viewer bundle so the viewer and custom node components share one runtime in production. This fixes the null `nodes` crash on GitHub Pages. Also suppress the unnecessary `/favicon.ico` request. ## Related Issues Follow-up to #451. ## Testing - Full experimental schema CI - Pages-equivalent headless browser test: HTTP 200, graph rendered, no console or page errors - License and whitespace checks _Written by Codex._ Authors: - Johan Peltenburg (https://github.com/johanpel) Approvers: - Pradeep Garigipati (https://github.com/9prady9) URL: #546
Adds a little playground for YAML DSL to capture schemas and visualizes the entity tree plus shows you what a resource timeline would look like approximately.
Screen.Recording.2026-08-07.at.12.26.56.mov
This is 100% vibe coded, so I'm proposing to open up two directories under the repo:
experimental/for experimental stuff, might have some dead code due to building stuff up bottom-up, and stuff might not be checked by CI for breaking changes, place for folks to iterate on stuff quicklyexperimental/vibesame as above but fully vibe coded, use at your own risk