diff options
| author | Thomas Grothe <grothe.tr@gmail.com> | 2026-04-30 00:53:13 -0400 |
|---|---|---|
| committer | Thomas Grothe <grothe.tr@gmail.com> | 2026-04-30 00:53:13 -0400 |
| commit | 73d75835e18a33c7f6c1b09bbcef93b16a7a9bfa (patch) | |
| tree | e079c6c45416333e29cf900831c07619a87d5c39 /web-timeplot/src/app/create-app.js | |
| parent | a1c95e72bea26f554eb05916d6fc584927367159 (diff) | |
redo timeplot web
Diffstat (limited to 'web-timeplot/src/app/create-app.js')
| -rw-r--r-- | web-timeplot/src/app/create-app.js | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/web-timeplot/src/app/create-app.js b/web-timeplot/src/app/create-app.js new file mode 100644 index 0000000..daf3559 --- /dev/null +++ b/web-timeplot/src/app/create-app.js @@ -0,0 +1,152 @@ +import { EventBus } from '../core/event-bus.js'; +import { Store, createInitialState } from '../core/store.js'; +import { TimeController } from '../core/time-controller.js'; +import { PlotBuffer } from '../plot/plot-buffer.js'; +import { TimeplotView } from '../plot/timeplot-view.js'; +import { SourceRegistry } from '../data/source-registry.js'; +import { PanelManager } from '../ui/panel-manager.js'; + +function clamp(value, min, max) { + return Math.min(max, Math.max(min, value)); +} + +export async function createApp(root) { + const bus = new EventBus(); + const store = new Store(createInitialState()); + const timeController = new TimeController(store); + const buffer = new PlotBuffer(store.getState().plot.maxPoints); + let sourceRegistry; + + const actions = { + togglePause: () => timeController.togglePause(), + setSpeed: (speed) => timeController.setSpeed(speed), + resetScene: () => { + timeController.reset(); + buffer.clear(); + sourceRegistry.reset(); + }, + togglePanel: (panelId) => { + store.setState((state) => ({ + ...state, + panels: { + ...state.panels, + [panelId]: { + ...state.panels[panelId], + visible: !state.panels[panelId].visible, + }, + }, + })); + }, + updateSource: (field, value) => { + store.setState((state) => ({ + ...state, + source: { + ...state.source, + [field]: value, + }, + })); + sourceRegistry.syncFromState(); + }, + updatePlot: (field, value) => { + store.setState((state) => ({ + ...state, + plot: { + ...state.plot, + [field]: value, + }, + })); + + if (field === 'maxPoints') { + buffer.maxPoints = clamp(value, 200, 4000); + } + }, + }; + + const panelManager = new PanelManager({ root, store, actions }); + const elements = panelManager.mount(); + + const plotView = new TimeplotView({ + host: elements.canvasHost, + onHover: (hoverState) => { + store.setState((state) => ({ + ...state, + plot: { + ...state.plot, + hoveredPoint: hoverState?.point ?? null, + tooltip: hoverState + ? { + visible: true, + x: hoverState.x, + y: hoverState.y, + point: hoverState.point, + } + : { + ...state.plot.tooltip, + visible: false, + point: null, + }, + }, + })); + }, + }); + + const renderer = await plotView.init(); + store.patch({ + app: { + ...store.getState().app, + renderer, + }, + }); + + sourceRegistry = new SourceRegistry(store, bus); + + bus.on('data:point', (point) => { + buffer.addPoint(point); + }); + + const keyHandler = (event) => { + if (event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement) { + return; + } + + if (event.code === 'Space') { + event.preventDefault(); + actions.togglePause(); + return; + } + + if (event.key === '[') { + actions.setSpeed(store.getState().time.speed - 0.1); + return; + } + + if (event.key === ']') { + actions.setSpeed(store.getState().time.speed + 0.1); + return; + } + + if (event.key.toLowerCase() === 'g') { + actions.updatePlot('showGrid', !store.getState().plot.showGrid); + } + }; + + window.addEventListener('keydown', keyHandler); + + plotView.app.ticker.add(() => { + timeController.tick(); + sourceRegistry.syncFromState(); + sourceRegistry.update(store.getState().time.plotTimeMs); + + const state = store.getState(); + const visiblePoints = buffer.getVisiblePoints(state.time.plotTimeMs, state.plot.windowDurationMs); + plotView.render(state, visiblePoints); + panelManager.sync(state, visiblePoints.length); + }); + + return { + destroy() { + window.removeEventListener('keydown', keyHandler); + plotView.destroy(); + }, + }; +} |
