diff --git a/adapter-warthog-wasm/src/core/templates.ts b/adapter-warthog-wasm/src/core/templates.ts index 939250ad..60ddf189 100644 --- a/adapter-warthog-wasm/src/core/templates.ts +++ b/adapter-warthog-wasm/src/core/templates.ts @@ -10,9 +10,9 @@ export const gridTemplate: Partial = { $: "rect", width: 1, height: 1, - x: "{{ctx.x}}", - y: "{{ctx.y}}", - fill: "{{ctx.color[ctx.type]}}", + x: "{{$.event.x}}", + y: "{{$.event.y}}", + fill: "{{$.color[$.event.type]}}", }, ], }, @@ -26,7 +26,7 @@ export const gridTemplate: Partial = { }, }, path: { - pivot: { x: "{{ctx.x + 0.5}}", y: "{{ctx.y + 0.5}}" }, + pivot: { x: "{{$.event.x + 0.5}}", y: "{{$.event.y + 0.5}}" }, scale: 0.3, }, }, @@ -39,10 +39,10 @@ export const xyTemplate: Partial = { node: [ { $: "circle", - fill: "{{ctx.color[ctx.type]}}", + fill: "{{$.color[$.event.type]}}", radius: 120, - x: "{{ctx.x}}", - y: "{{ctx.y}}", + x: "{{$.event.x}}", + y: "{{$.event.y}}", }, ], line: [ @@ -50,15 +50,15 @@ export const xyTemplate: Partial = { $: "path", points: [ { - x: "{{ctx.parent ? ctx.parent.x: ctx.x}}", - y: "{{ctx.parent ? ctx.parent.y: ctx.y}}", + x: "{{$.parent ? $.parent.x: $.event.x}}", + y: "{{$.parent ? $.parent.y: $.event.y}}", }, { - x: "{{ctx.x}}", - y: "{{ctx.y}}", + x: "{{$.event.x}}", + y: "{{$.event.y}}", }, ], - fill: "{{ctx.color[ctx.type]}}", + fill: "{{$.color[$.event.type]}}", lineWidth: 90, }, ], @@ -78,7 +78,7 @@ export const xyTemplate: Partial = { }, }, path: { - pivot: { x: "{{ctx.x}}", y: "{{ctx.y}}" }, + pivot: { x: "{{$.event.x}}", y: "{{$.event.y}}" }, scale: 120, }, }, diff --git a/adapter-warthog-websocket/src/core/templates.ts b/adapter-warthog-websocket/src/core/templates.ts index 939250ad..60ddf189 100644 --- a/adapter-warthog-websocket/src/core/templates.ts +++ b/adapter-warthog-websocket/src/core/templates.ts @@ -10,9 +10,9 @@ export const gridTemplate: Partial = { $: "rect", width: 1, height: 1, - x: "{{ctx.x}}", - y: "{{ctx.y}}", - fill: "{{ctx.color[ctx.type]}}", + x: "{{$.event.x}}", + y: "{{$.event.y}}", + fill: "{{$.color[$.event.type]}}", }, ], }, @@ -26,7 +26,7 @@ export const gridTemplate: Partial = { }, }, path: { - pivot: { x: "{{ctx.x + 0.5}}", y: "{{ctx.y + 0.5}}" }, + pivot: { x: "{{$.event.x + 0.5}}", y: "{{$.event.y + 0.5}}" }, scale: 0.3, }, }, @@ -39,10 +39,10 @@ export const xyTemplate: Partial = { node: [ { $: "circle", - fill: "{{ctx.color[ctx.type]}}", + fill: "{{$.color[$.event.type]}}", radius: 120, - x: "{{ctx.x}}", - y: "{{ctx.y}}", + x: "{{$.event.x}}", + y: "{{$.event.y}}", }, ], line: [ @@ -50,15 +50,15 @@ export const xyTemplate: Partial = { $: "path", points: [ { - x: "{{ctx.parent ? ctx.parent.x: ctx.x}}", - y: "{{ctx.parent ? ctx.parent.y: ctx.y}}", + x: "{{$.parent ? $.parent.x: $.event.x}}", + y: "{{$.parent ? $.parent.y: $.event.y}}", }, { - x: "{{ctx.x}}", - y: "{{ctx.y}}", + x: "{{$.event.x}}", + y: "{{$.event.y}}", }, ], - fill: "{{ctx.color[ctx.type]}}", + fill: "{{$.color[$.event.type]}}", lineWidth: 90, }, ], @@ -78,7 +78,7 @@ export const xyTemplate: Partial = { }, }, path: { - pivot: { x: "{{ctx.x}}", y: "{{ctx.y}}" }, + pivot: { x: "{{$.event.x}}", y: "{{$.event.y}}" }, scale: 120, }, }, diff --git a/client/main.js b/client/main.js index 0e28247b..1ace9a83 100644 --- a/client/main.js +++ b/client/main.js @@ -56,6 +56,7 @@ server.listen(0, () => { show: false, titleBarStyle: "hidden", titleBarOverlay: { + height: 32, color: "#00000000", symbolColor: "#00000000", }, diff --git a/client/src/components/Editor.tsx b/client/src/components/Editor.tsx index d4748a7c..f60f42f8 100644 --- a/client/src/components/Editor.tsx +++ b/client/src/components/Editor.tsx @@ -1,3 +1,5 @@ +import { ReactNode } from "react"; + export type EditorProps = { value?: T; onChange?: (key: T) => void; @@ -5,4 +7,5 @@ export type EditorProps = { export type EditorSetterProps = { value?: T; onChange?: (key: (value: T) => T) => void; + children?: ReactNode; }; diff --git a/client/src/components/app-bar/Input.tsx b/client/src/components/app-bar/Input.tsx index ecac9e28..40c1be96 100644 --- a/client/src/components/app-bar/Input.tsx +++ b/client/src/components/app-bar/Input.tsx @@ -9,12 +9,14 @@ import { EditorProps } from "../Editor"; import { FeaturePicker } from "./FeaturePicker"; import { FeaturePickerButton } from "./FeaturePickerButton"; import { custom as customMap, uploadMap, uploadTrace } from "./upload"; +import { LARGE_FILE_B, formatByte, useBusyState } from "slices/busy"; export const mapDefaults = { start: undefined, end: undefined }; export function MapPicker({ onChange, value }: EditorProps) { const notify = useSnackbar(); - const usingLoadingState = useLoadingState("specimen"); + const usingLoadingState = useLoadingState("map"); + const usingBusyState = useBusyState("map"); const [connections] = useConnections(); const [{ maps, formats }] = useFeatures(); return ( @@ -37,8 +39,17 @@ export function MapPicker({ onChange, value }: EditorProps) { const f = await uploadMap(formats); if (f) { usingLoadingState(async () => { - notify("Reading map..."); - onChange?.(await f()); + notify("Opening map..."); + const output = + f.file.size > LARGE_FILE_B + ? await usingBusyState( + f.read, + `Opening map (${formatByte(f.file.size)})` + ) + : await f.read(); + if (output) { + onChange?.(output); + } }); } } catch (e) { @@ -57,6 +68,7 @@ export function MapPicker({ onChange, value }: EditorProps) { export function TracePicker({ onChange, value }: EditorProps) { const notify = useSnackbar(); const usingLoadingState = useLoadingState("specimen"); + const usingBusyState = useBusyState("specimen"); return ( } @@ -65,10 +77,16 @@ export function TracePicker({ onChange, value }: EditorProps) { const f = await uploadTrace(); if (f) usingLoadingState(async () => { - notify("Reading trace..."); - const g = await f(); - if (g) { - onChange?.(g); + notify("Opening trace..."); + const output = + f.file.size > LARGE_FILE_B + ? await usingBusyState( + f.read, + `Opening trace (${formatByte(f.file.size)})` + ) + : await f.read(); + if (output) { + onChange?.(output); } }); } catch (e) { @@ -80,87 +98,3 @@ export function TracePicker({ onChange, value }: EditorProps) { ); } - -// export function Input() { -// const notify = useSnackbar(); -// const [connections] = useConnections(); -// const [{ algorithms, maps, formats }] = useFeatures(); -// const [{ algorithm, map, parameters }, setUIState] = useUIState(); - -// return ( -// <> -// } -// label="Map" -// value={map?.id} -// items={[ -// customTrace(parameters), -// customMap(map), -// ...maps.map((c) => ({ -// ...c, -// description: find(connections, { url: c.source })?.name, -// })), -// ]} -// onChange={async (v) => { -// switch (v) { -// case customMap().id: -// try { -// const f = await uploadMap(formats); -// if (f) { -// setUIState({ -// ...mapDefaults, -// map: f, -// algorithm: algorithm ?? "identity", -// parameters: {}, -// }); -// notify("Solution was cleared because the map changed."); -// } -// } catch (e) { -// notify(`${e}`); -// } -// break; -// case customTrace().id: -// try { -// const f2 = await uploadTrace(); -// if (f2) { -// setUIState({ -// parameters: f2, -// algorithm: "identity", -// start: 0, -// end: 0, -// map: { -// format: f2.format, -// content: map?.format === f2.format ? map?.content : " ", -// id: "internal/upload", -// }, -// }); -// } -// } catch (e) { -// notify(`${e}`); -// } -// break; -// default: -// setUIState({ -// ...mapDefaults, -// map: find(maps, { id: v }), -// parameters: {}, -// }); -// notify("Solution was cleared because the map changed."); -// break; -// } -// }} -// /> -// -// } -// label="Algorithm" -// value={algorithm} -// items={algorithms.map((c) => ({ -// ...c, -// description: find(connections, { url: c.source })?.name, -// }))} -// onChange={async (v) => setUIState({ algorithm: v, parameters: {} })} -// /> -// -// ); -// } diff --git a/client/src/components/app-bar/Playback.tsx b/client/src/components/app-bar/Playback.tsx index 05f36719..2b996bf6 100644 --- a/client/src/components/app-bar/Playback.tsx +++ b/client/src/components/app-bar/Playback.tsx @@ -1,13 +1,3 @@ -import { range, trimEnd } from "lodash"; -import { useRaf } from "react-use"; -import { ReactNode, useCallback, useEffect } from "react"; -import { IconButtonWithTooltip as Button } from "components/generic/IconButtonWithTooltip"; -import { Label } from "components/generic/Label"; -import { useSnackbar } from "components/generic/Snackbar"; -import { useBreakpoints } from "hooks/useBreakpoints"; -import { usePlaybackState } from "hooks/usePlaybackState"; -import { useSettings } from "slices/settings"; -import { UploadedTrace } from "slices/UIState"; import { SkipNextOutlined as ForwardIcon, PauseOutlined as PauseIcon, @@ -15,7 +5,18 @@ import { SkipPreviousOutlined as PreviousIcon, StopOutlined as StopIcon, } from "@mui/icons-material"; +import { EditorSetterProps } from "components/Editor"; +import { IconButtonWithTooltip as Button } from "components/generic/IconButtonWithTooltip"; +import { Label } from "components/generic/Label"; +import { useSnackbar } from "components/generic/Snackbar"; +import { useBreakpoints } from "hooks/useBreakpoints"; +import { usePlaybackState } from "hooks/usePlaybackState"; +import { range, trimEnd } from "lodash"; +import { ReactNode, useCallback, useEffect } from "react"; +import { useRaf } from "react-use"; +import { UploadedTrace } from "slices/UIState"; import { Layer } from "slices/layers"; +import { useSettings } from "slices/settings"; function cancellable(f: () => Promise, g: (result: T) => void) { let cancelled = false; @@ -27,28 +28,17 @@ function cancellable(f: () => Promise, g: (result: T) => void) { cancelled = true; }; } +export type PlaybackLayerData = { + step?: number; + playback?: "playing" | "paused"; + playbackTo?: number; +}; -export function Playback({ - layer, -}: { - layer?: Layer<{ trace?: UploadedTrace }>; -}) { - const { - step, - tick, - end, - playing, - canPause, - canPlay, - canStepBackward, - canStepForward, - canStop, - pause, - play, - stepBackward, - stepForward, - stop, - } = usePlaybackState(layer?.key); +export function PlaybackService({ + children, + value, +}: EditorSetterProps>) { + const { step, tick, end, playing, pause } = usePlaybackState(value?.key); useRaf(); const notify = useSnackbar(); @@ -98,6 +88,28 @@ export function Playback({ shouldBreak, playbackRate, ]); + return <>{children}; +} + +export function Playback({ + layer, +}: { + layer?: Layer<{ trace?: UploadedTrace }>; +}) { + const { + playing, + canPause, + canPlay, + canStepBackward, + canStepForward, + canStop, + pause, + play, + stepBackward, + stepForward, + stop, + } = usePlaybackState(layer?.key); + useRaf(); return ( <>