Skip to content

feat(frontend): interface editor - tabs, inFlow graph canvas, inChat panel - #79

Open
maan-iitd2 wants to merge 1 commit into
blackrock:mainfrom
maan-iitd2:pr/frontend-editor
Open

feat(frontend): interface editor - tabs, inFlow graph canvas, inChat panel#79
maan-iitd2 wants to merge 1 commit into
blackrock:mainfrom
maan-iitd2:pr/frontend-editor

Conversation

@maan-iitd2

@maan-iitd2 maan-iitd2 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the interface editor — the part of InGen Studio where a config is actually authored. Three
ways to edit the same model: structured tabs, a visual graph canvas (inFlow), and a
conversational panel (inChat).

This is part 4 of the frontend series. All new files under
frontend/src/components/editor/
— nothing existing is modified.

Area What it does
editor/tabs/ Sources, Columns, Output and Validations tabs — the structured editing surface.
editor/graph/ inFlow canvas: sources, pre-processing steps and output as a directed graph, with a sidebar node creator.
editor/chat/ inChat panel — describe a change in words, see it applied to the model.

Screenshots

Source loader — pick a source type, configure it:

Source loader

inFlow graph canvas — the pipeline as a directed graph:

inFlow graph canvas

Columns tab — mapping source columns to output columns, with per-column formatters:

Columns editor

inChat panel — the same model edited conversationally; note the YAML panel on the right updating from the same edits:

inChat panel

Images are hosted on a pr-assets branch of the fork, so they add nothing to this diff.

⚠️ Please read before reviewing

This builds on #76 and the two preceding frontend PRs, and CI will fail here until they merge.
These components import @/models/*, @/serializers/*, @/services/* and @/state/*. Expected —
not a defect in this branch.

The diff is conflict-free and independent: no file here is touched by any other open PR.

On inChat scope: the panel in this PR is UI only, and it works standalone in mock mode. The
backend endpoint it calls in http mode is a deliberate follow-up PR, held back so that the
backend wrapper (#75) can be reviewed on its own first. Nothing in this PR requires that endpoint to
exist — no new Python dependency is introduced here.

Design notes for reviewers

  • All three surfaces edit one model. Tabs, graph and chat are views over the same config object,
    not parallel implementations — edits made in any one immediately appear in the others and in the
    YAML panel. This is the single most important thing to sanity-check in review.
  • The graph is derived, never authoritative. Node positions are layout only; the config model
    stays the source of truth, so a graph bug can't corrupt a config.
  • If this PR feels too large to review in one pass, it splits cleanly along
    editor/tabs/ vs editor/graph/ — happy to break it in two on request.

Testing

Verified on a local integration branch (main + #76 + the two preceding frontend PRs + this PR):

  • npm run build — full next build compiles clean, all 6 routes generated
  • npm run test — 57/57 pass
  • npm run dev in mock mode — browser smoke pass: start screen → create pipeline → editor with
    source loader and live YAML panel → run console, 0 console errors
  • Re-verified alongside the Python side: pytest test/ and pytest backend/tests/ (42 passed), no
    new failures; a real python -m ingen CLI run and a real HTTP run through the backend both
    completed with all five stages ok

Quality gates

All green on the integration branch:

Gate Result
npm run lint 0 errors, 0 warnings
npm run test 57 / 57
npm run build compiles, all 6 routes generated
pytest test/ no new failures vs. baseline
pytest backend/tests/ 42 passed

🤖 Generated with Claude Code

…panel

Signed-off-by: maan-iitd2 <maan.iitd.ac.in@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Writer-shape errors, inconsistent graph mutations, unstable editor state, and chat races can corrupt or misrepresent configurations.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds InGen Studio’s interface editor with structured forms, a model-derived graph canvas, and conversational editing.

Changes:

  • Adds source, column, processing, validation, and output editors.
  • Adds inFlow graph visualization and editing controls.
  • Adds interface management and inChat sessions.
File summaries
File Description
tabs/ValidationsTab.jsx Edits column validations.
tabs/SourcesTab.jsx Manages interface sources.
tabs/PostProcessingTab.jsx Configures pivot processing.
tabs/OutputTab.jsx Configures output writers.
tabs/ColumnsTab.jsx Maps and formats columns.
InterfaceManager.jsx Manages interfaces.
InterfaceEditor.jsx Selects graph or chat view.
graph/useGraphLayout.js Computes graph layout.
graph/nodes/ValidationsNode.jsx Renders validation stage.
graph/nodes/SourceNode.jsx Renders source nodes.
graph/nodes/PreprocessNode.jsx Renders transform nodes.
graph/nodes/PostprocessNode.jsx Renders post-processing stage.
graph/nodes/OutputNode.jsx Renders output stage.
graph/nodes/NodeShared.jsx Provides shared node UI.
graph/nodes/ColumnsNode.jsx Renders column stage.
graph/InterfaceGraphEditor.jsx Implements inFlow canvas.
graph/GraphToolbar.jsx Adds graph controls.
graph/graphConstants.js Defines graph contracts and styling.
graph/ContextMenu.jsx Adds graph context actions.
chat/InterfaceChatEditor.jsx Implements conversational editing.
Review details

Suppressed comments (2)

frontend/src/components/editor/graph/InterfaceGraphEditor.jsx:441

  • “Disconnect all edges” only filters the local ReactFlow edge array; it does not clear the source fields represented by feed edges or change the base-source chain. The YAML is unchanged and the edges reappear on the next model update. Persist each supported disconnection through the config model, or do not offer this action for derived edges/nodes.
  const ctxDisconnect = ctxMenu?.nodeId
    ? () => {
        setEdges((eds) => eds.filter((e) => e.source !== ctxMenu.nodeId && e.target !== ctxMenu.nodeId));
        closeCtx();

frontend/src/components/editor/chat/InterfaceChatEditor.jsx:214

  • The text input and Send button are disabled while a request is pending, but suggestion chips remain active and handleSendMessage does not reject calls while isTyping. Clicking another chip starts overlapping requests; the first completion clears the single boolean spinner while another request is still running, and replies can appear out of order. Disable chips while typing (and guard the handler) or track requests individually.
          {chips.map((c) => (
            <button key={c} className="prompt-chip" onClick={() => handleSendMessage(c)}>
              {c === 'explain' ? <><Sparkles size={11} /> explain</> : `+ ${c}`}
  • Files reviewed: 20/20 changed files
  • Comments generated: 12
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +153 to +155
const isConversation = (ops?.length ?? 0) === 0;
const text = modelReply && (changed || isConversation) ? modelReply : reply;
setMessages((prev) => [...prev, { id: `msg-reply-${Date.now()}`, sender: 'assistant', text }]);
Comment on lines +420 to +423
if (ctxMenu.nodeId?.startsWith(SRC)) {
const srcId = ctxMenu.nodeId.slice(SRC.length);
updateModel((m) => removeSource(m, srcId));
apply((it) => ({ ...it, sources: (it.sources ?? []).filter((s) => s !== srcId) }));
Comment on lines +67 to +70
for (const [name, it] of Object.entries(next.interfacesByName)) {
const filtered = (it.sources ?? []).filter((s) => s !== srcId);
if (filtered.length !== (it.sources ?? []).length) {
next = { ...next, interfacesByName: { ...next.interfacesByName, [name]: { ...it, sources: filtered } } };
Comment on lines +50 to +56
try {
updateModel((m) => renameInterface(m, editingName, next));
// Navigate to the renamed interface if it was active.
if (editingName === activeIface) {
router.replace(`/configs/${configId}/interfaces/${encodeURIComponent(next)}`);
}
} catch { /* duplicate name — ignore */ }
Comment on lines +187 to +191
const flow = (id, source, target) => ({
id, source, target,
type: 'smoothstep',
sourceHandle: 'out', targetHandle: 'in',
animated: true, style: FLOW_STYLE,
Comment on lines +565 to +567
selectionOnDrag
multiSelectionKeyCode="Shift"
deleteKeyCode={['Backspace', 'Delete']}
Comment on lines +252 to +254
// Keyed on the column identity, not the index: ColumnCard holds local UI state and
// an index key would carry that state to a different row when columns are reordered.
key={`${col.src_col_name ?? ''}->${col.dest_col_name ?? ''}`}
Comment on lines +12 to +16
const props = output.props && !Array.isArray(output.props) ? output.props : {};

const apply = (fn) => updateInterface(interfaceName, fn);
// Reset props on type change so fields from the previous writer don't leak into the new one's YAML.
const setType = (type) => apply((it) => setField(it, 'output', type ? { type, props: {} } : {}));
Comment on lines +619 to +620
<button className="grapheditor__drawer-close" onClick={() => setSelectedNodeId(null)}>
<X size={16} />
Comment on lines +52 to +55
<select className="field__input" value={safeTarget} onChange={(e) => setTarget(Number(e.target.value))}>
{columns.map((c, i) => <option key={`${colLabel(c)}:${i}`} value={i}>{colLabel(c)}</option>)}
</select>
<select className="field__input field__input--wide" value={expectation} onChange={(e) => setExpectation(e.target.value)}>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants