Skip to content

feat(frontend): adapters, services, state and forms engine - #77

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

feat(frontend): adapters, services, state and forms engine#77
maan-iitd2 wants to merge 1 commit into
blackrock:mainfrom
maan-iitd2:pr/frontend-services-state

Conversation

@maan-iitd2

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

Copy link
Copy Markdown
Contributor

Summary

Adds the service layer that sits between InGen Studio's UI and its data: pluggable adapters, the
services that wrap them, React context providers for shared state, two small hooks, and a
schema-driven forms engine.

This is part 2 of the frontend series. It is all new files under frontend/src/ — nothing
existing is modified.

Area What it does
src/adapters/ Two interchangeable backends behind one interface — mock* (localStorage, no server needed) and http* (talks to the FastAPI wrapper). Selected by NEXT_PUBLIC_ADAPTER_MODE.
src/services/ Thin domain wrappers over the adapters — configs, runs, validation, history, catalog, files.
src/state/ React context providers for config, run, catalog, chat session, graph selection and view mode, plus useModelHistory (undo/redo).
src/hooks/ useDocTitle, useWorkspaceShortcuts.
src/forms/ SchemaForm + field components — renders the source/column editors from schema definitions rather than hand-written forms.
src/lib/columnStore.js Column metadata cache shared across tabs.

⚠️ Please read before reviewing

This builds on #76 and CI will fail here until #76 merges. These modules import
@/models/* and @/serializers/*, which #76 introduces. That is expected — not a defect in this
branch.

The diff itself is conflict-free and independent: this PR touches no file that #76 or any other
open PR touches, so it can be merged in any order once #76 lands.

src/lib/fuzzy.js and src/utils/id.js are absent by design — they are already in #76, where
they were needed for that PR's tests to run. This PR does not re-ship them.

Design notes for reviewers

  • The adapter split is the load-bearing decision. Every service depends only on the adapter
    interface, so the whole UI runs with zero backend in mock mode. That is what makes the app
    demo-able and testable without standing up Python.
  • Contexts are deliberately granular (six small providers rather than one store) to keep
    re-renders localised. If you'd prefer a single reducer, say so — it's a contained change.
  • useModelHistory keeps bounded undo/redo stacks with a max size, so long editing sessions don't
    grow memory without limit.

Testing

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

  • npm run test — 57/57 pass, including this PR's state/useModelHistory.test.js
  • npm run build — full next build compiles clean, all 6 routes generated
  • npm run dev in mock mode — browser smoke pass over start screen → editor → run console, 0
    console errors
  • Backend untouched by this PR, but re-verified alongside it: pytest test/ and
    pytest backend/tests/ (42 passed) show no new failures

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

What it looks like

The schema-driven forms engine in action — this source editor is not hand-written JSX. It is
generated from a field-descriptor array in src/forms/schemas/, which is what keeps source,
pre-processor, formatter and output forms from being four copies of the same code:

Schema-driven source form

The state and adapter layers in this PR are what keep the YAML panel on the right in sync on every
edit.

Shown running as part of the complete Studio on a local integration branch, since this PR alone
does not render an app.

🤖 Generated with Claude Code

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

Several generated configuration shapes are incompatible with backend contracts, alongside adapter and shortcut correctness issues.

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

Pull request overview

Adds the frontend data, state, hook, and schema-driven form foundations for InGen Studio.

Changes:

  • Adds mock/HTTP adapters and domain services.
  • Adds shared React contexts, history, and workspace hooks.
  • Adds reusable schema-based form fields and backend configuration schemas.
File summaries
File Description
frontend/src/state/ViewModeContext.jsx Shares editor view mode.
frontend/src/state/useModelHistory.test.js Tests history transitions.
frontend/src/state/useModelHistory.js Implements bounded undo/redo.
frontend/src/state/RunContext.jsx Manages run lifecycle.
frontend/src/state/GraphSelectionContext.jsx Shares graph selection.
frontend/src/state/ConfigContext.jsx Manages editing and autosave.
frontend/src/state/ChatSessionContext.jsx Coordinates chat sessions.
frontend/src/state/CatalogContext.jsx Provides catalog data.
frontend/src/services/validationService.js Defines validation contract.
frontend/src/services/runService.js Defines run contract.
frontend/src/services/index.js Locates configured services.
frontend/src/services/historyService.js Defines history contract.
frontend/src/services/fileService.js Adds file/source requests.
frontend/src/services/configService.js Defines config contract.
frontend/src/services/chatHistoryService.js Persists chat sessions.
frontend/src/services/catalogService.js Defines catalog contract.
frontend/src/lib/columnStore.js Caches source columns.
frontend/src/hooks/useWorkspaceShortcuts.js Adds workspace shortcuts.
frontend/src/hooks/useSourceActions.js Centralizes source actions.
frontend/src/hooks/useDocTitle.js Updates document titles.
frontend/src/forms/schemas/validationSchemas.js Defines validation metadata.
frontend/src/forms/schemas/sourceSchemas.js Defines source forms.
frontend/src/forms/schemas/preProcessorSchemas.js Defines preprocessor forms.
frontend/src/forms/schemas/outputSchemas.js Defines output forms.
frontend/src/forms/schemas/formatterSchemas.js Defines formatter forms.
frontend/src/forms/SchemaForm.jsx Renders descriptor-driven forms.
frontend/src/forms/fields/Fields.jsx Provides form primitives.
frontend/src/adapters/mockValidationAdapter.js Simulates validation results.
frontend/src/adapters/mockRunAdapter.js Simulates run execution.
frontend/src/adapters/mockHistoryAdapter.js Stores local run history.
frontend/src/adapters/mockConfigAdapter.js Stores local configurations.
frontend/src/adapters/mockChatAdapter.js Provides mock chat fallback.
frontend/src/adapters/mockCatalogAdapter.js Supplies static catalog data.
frontend/src/adapters/index.js Selects adapter implementations.
frontend/src/adapters/httpValidationAdapter.js Provides HTTP validation preview.
frontend/src/adapters/httpRunAdapter.js Executes runs through FastAPI.
frontend/src/adapters/httpHistoryAdapter.js Reads backend run history.
frontend/src/adapters/httpConfigAdapter.js Adds backend config validation.
frontend/src/adapters/httpClient.js Wraps JSON HTTP requests.
frontend/src/adapters/httpChatAdapter.js Connects to backend chat.
Review details

Suppressed comments (1)

frontend/src/services/fileService.js:20

  • Column discovery also always calls FastAPI, so selecting mock mode does not provide the stated zero-backend workflow for configured sources. Put this operation behind the adapter/service seam or expose it as unsupported and prevent the mock UI from invoking it.
  const res = await fetch(`${API}/api/sources/columns`, {
  • Files reviewed: 40/40 changed files
  • Comments generated: 10
  • Review effort level: Balanced

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

column,
expectation: v.type,
severity: v.severity ?? 'warning',
status: 'passed', // preview only — real status comes from a run
Comment on lines +35 to +39
json_writer: {
label: 'JSON writer',
schema: [
{ key: 'destination', label: 'Destination', kind: 'select', options: ['file', 'api'], help: 'Where to write the JSON output' },
{ key: 'destination_props', label: 'Destination props', kind: 'group', fields: [
Comment on lines +48 to +52
splitted_file: {
label: 'Splitted file',
schema: [
{ key: 'path', label: 'Output directory', kind: 'text', placeholder: 'out/splits/', help: 'Files are written as <directory>/<split_col_value>.<ext>' },
{ key: 'split_col', label: 'Split column', kind: 'text', help: 'Each unique value in this column produces a separate file' },
Comment on lines +27 to +29
async clear() {
// No-op: no delete endpoint in scope; history is backend-owned in HTTP mode.
}
let interfaceAborted = false;
for (const stage of order) {
if (isCancelled?.()) { cancelled = true; break; }
if (interfaceAborted) { stages.push({ interface: name, stage, status: 'skipped', durationMs: 0 }); continue; }
<select
className="field__input"
value={value ?? ''}
onChange={(e) => onChange(e.target.value === '' ? undefined : e.target.value)}
{ key: 'data_node', label: 'data_node', kind: 'tags' },
{ key: 'data_key', label: 'data_key', kind: 'tags' },
{ key: 'success_criteria', label: 'Success criteria', kind: 'text' },
{ key: 'criteria_option', label: 'Criteria option', kind: 'json', rows: 2, visibleIf: (v) => !!v.success_criteria },
} else if (e.key === 'z' && !e.shiftKey) {
e.preventDefault();
onUndo?.();
} else if (e.key === 'y' || (e.key === 'z' && e.shiftKey)) {
export async function uploadFile(file) {
const body = new FormData();
body.append('file', file);
const res = await fetch(`${API}/api/files/upload`, { method: 'POST', body });
Comment on lines +15 to +17
export function pushSnapshot(past, model, max = MAX_HISTORY) {
return [...past.slice(-(max - 1)), model];
}
Signed-off-by: maan-iitd2 <maan.iitd.ac.in@gmail.com>
@Jatin-8898
Jatin-8898 force-pushed the pr/frontend-services-state branch from 62d5640 to 91935b1 Compare September 9, 2026 18:52
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