Skip to content

Commit 3340e79

Browse files
hyperpolymathclaude
andcommitted
feat(frontend): wire real canvas, URL routing, fix import flow, clean warnings
- Replace CanvasPlaceholder with real PipelineCanvas (SVG node-graph with bezier connections, minimap, context menus, drag-drop, zoom/pan) - Remove 146 lines of dead CanvasPlaceholder code - Wire AppRouter for URL-based navigation: tabs sync with browser URLs, back/forward navigation works, initial route read from URL - Add PipelineView route (/pipeline) to AppRouter + navigation items - Fix Import flow: TriggerImportDesign now dispatches ImportDesignSuccess back through TEA instead of fire-and-forget Console.log - Add 404 NotFound page for unmatched routes - Fix all 20 deprecation warnings (Js.nullable, fst/snd, sliceToEnd, unused variables) — zero warnings remaining - Add PipelineView case to AppIntegrated.res route switch Clean build: 50 modules, 0 errors, 0 warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 712c945 commit 3340e79

15 files changed

Lines changed: 147 additions & 358 deletions

frontend/src/App.res

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,8 @@ open Update
88
// Direct JS binding for Array.join (avoids deprecated Js.Array2.joinWith)
99
@send external joinWith: (array<string>, string) => string = "join"
1010

11-
type page =
12-
| NetworkView // Cisco-style topology (TopologyView.res)
13-
| StackView // Paragon-style vertical (View.res)
14-
| PipelineView // Assembly pipeline designer (node-graph editor)
15-
| LagoGreyView // Lago Grey image designer
16-
| PortConfigView // Port configuration with ephemeral pinholes
17-
| SecurityView // Security inspector with attack surface analysis
18-
| GapAnalysisView // Gap analysis with automated remediation
19-
| SimulationView // Packet animation simulation
20-
| SettingsView // Settings and preferences
11+
// Page type delegated to AppRouter for URL synchronisation
12+
type page = AppRouter.route
2113

2214
type appState = {
2315
currentPage: page,
@@ -27,7 +19,7 @@ type appState = {
2719
}
2820

2921
let initialAppState = {
30-
currentPage: NetworkView,
22+
currentPage: AppRouter.getCurrentRoute(),
3123
model: initialModel,
3224
pipelineDesigner: PipelineModel.initialState(),
3325
isDark: true,
@@ -246,14 +238,31 @@ let make = () => {
246238
})
247239
}
248240

241+
| TriggerImportDesign => {
242+
// Side effect: open file picker, dispatch result back through TEA
243+
Import.triggerImport(
244+
importedModel => dispatch(ImportDesignSuccess(importedModel)),
245+
error => dispatch(ImportDesignError(error)),
246+
)
247+
}
248+
249249
| _ => ()
250250
}
251251
}
252252

253253
let switchPage = page => {
254+
AppRouter.navigateTo(page)
254255
setState(prev => {...prev, currentPage: page})
255256
}
256257

258+
// Listen for browser back/forward navigation
259+
React.useEffect0(() => {
260+
AppRouter.onRouteChange(route => {
261+
setState(prev => {...prev, currentPage: route})
262+
})
263+
None
264+
})
265+
257266
<ErrorBoundary>
258267
<div className="app">
259268
<nav className="nav-tabs">
@@ -359,6 +368,21 @@ let make = () => {
359368
})
360369
}
361370
/>
371+
| NotFound =>
372+
<div className="page" style={Sx.make(~textAlign="center", ~paddingTop="4rem", ())}>
373+
<h1 style={Sx.make(~fontSize="3rem", ~color="#64b5f6", ~marginBottom="1rem", ())}>
374+
{"404"->React.string}
375+
</h1>
376+
<p style={Sx.make(~color="#8892a6", ~marginBottom="2rem", ())}>
377+
{"Page not found"->React.string}
378+
</p>
379+
<button
380+
className="action-btn"
381+
onClick={_ => switchPage(NetworkView)}
382+
>
383+
{"Go to Network View"->React.string}
384+
</button>
385+
</div>
362386
}}
363387
</div>
364388

frontend/src/App.res.js

Lines changed: 61 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/AppIntegrated.res

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ let make = () => {
278278
| NetworkView =>
279279
TopologyView.view(state.model, state.isDark, stackMsg => dispatch(StackMsg(stackMsg)))
280280
| StackView => StackView.view(state.model)
281+
| PipelineView =>
282+
<PipelineDesigner
283+
state={PipelineModel.initialState()}
284+
dispatch={_pMsg => ()}
285+
/>
281286
| LagoGreyView => <LagoGreyImageDesigner />
282287
| PortConfigView =>
283288
<PortConfigPanel

frontend/src/AppIntegrated.res.js

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/AppRouter.res

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
type route =
66
| NetworkView
77
| StackView
8+
| PipelineView
89
| LagoGreyView
910
| PortConfigView
1011
| SecurityView
@@ -18,6 +19,7 @@ let routeToPath = (route: route): string => {
1819
switch route {
1920
| NetworkView => "/"
2021
| StackView => "/stack"
22+
| PipelineView => "/pipeline"
2123
| LagoGreyView => "/lago-grey"
2224
| PortConfigView => "/ports"
2325
| SecurityView => "/security"
@@ -33,6 +35,7 @@ let pathToRoute = (path: string): route => {
3335
switch path {
3436
| "/" => NetworkView
3537
| "/stack" => StackView
38+
| "/pipeline" => PipelineView
3639
| "/lago-grey" => LagoGreyView
3740
| "/ports" => PortConfigView
3841
| "/security" => SecurityView
@@ -101,6 +104,12 @@ let navigationItems: array<routeMeta> = [
101104
icon: "📦",
102105
description: "Vertical stack view",
103106
},
107+
{
108+
route: PipelineView,
109+
label: "Pipeline",
110+
icon: "🔧",
111+
description: "Assembly pipeline designer",
112+
},
104113
{
105114
route: LagoGreyView,
106115
label: "Lago Grey",

frontend/src/AppRouter.res.js

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/PipelineCanvas.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,14 +657,14 @@ let make = (
657657
~dispatch: PipelineModel.pipelineMsg => unit,
658658
): React.element => {
659659
// Local state for canvas dimensions (measured from container)
660-
let containerRef: React.ref<Js.nullable<Dom.element>> = React.useRef(Js.Nullable.null)
660+
let containerRef: React.ref<Nullable.t<Dom.element>> = React.useRef(Nullable.null)
661661
let (canvasSize, setCanvasSize) = React.useState(() => (1200.0, 800.0))
662662
let (contextMenu, setContextMenu) = React.useState(() => None)
663663

664664
// Measure canvas container on mount and window resize
665665
React.useEffect0(() => {
666666
let measure = () => {
667-
switch Js.Nullable.toOption(containerRef.current) {
667+
switch Nullable.toOption(containerRef.current) {
668668
| Some(el) => {
669669
let rect = getBoundingClientRect(el)
670670
setCanvasSize(_ => (rect.width, rect.height))

0 commit comments

Comments
 (0)