Skip to content

Commit

Permalink
Merge branch 'master' into feature-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
spaaaacccee committed Dec 10, 2023
2 parents 924970d + 988201c commit 587e76d
Show file tree
Hide file tree
Showing 17 changed files with 262 additions and 133 deletions.
83 changes: 23 additions & 60 deletions client/src/components/app-bar/Playback.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
import {
SkipNextOutlined as ForwardIcon,
ChevronLeftOutlined as PreviousIcon,
ChevronRightOutlined as NextIcon,
SkipNextOutlined as SkipIcon,
PauseOutlined as PauseIcon,
PlayArrowOutlined as PlayIcon,
SkipPreviousOutlined as PreviousIcon,
StopOutlined as StopIcon,
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 { noop, range, trimEnd } from "lodash";
import { ReactNode, useCallback, useEffect } from "react";
import { noop } from "lodash";
import { useEffect } from "react";
import { UploadedTrace } from "slices/UIState";
import { Layer } from "slices/layers";
import { useSettings } from "slices/settings";

function cancellable<T = void>(f: () => Promise<T>, g: (result: T) => void) {
let cancelled = false;
requestAnimationFrame(async () => {
const result = await f();
if (!cancelled) g(result);
});
return () => {
cancelled = true;
};
}
export type PlaybackLayerData = {
step?: number;
playback?: "playing" | "paused";
Expand All @@ -37,44 +25,17 @@ export function PlaybackService({
children,
value,
}: EditorSetterProps<Layer<PlaybackLayerData>>) {
const { step, tick, end, playing, pause } = usePlaybackState(value?.key);
const { step, end, playing, pause, stepWithBreakpointCheck } =
usePlaybackState(value?.key);

const notify = useSnackbar();
const [{ playbackRate = 1 }] = useSettings();
const shouldBreak = useBreakpoints(value?.key);

const renderLabel = useCallback(
(label: ReactNode, offset: number) => (
<Label primary={label} secondary={`Step ${step + offset}`} />
),
[step]
);

useEffect(() => {
if (playing) {
let cancel = noop;
const r = setInterval(() => {
if (step < end) {
cancel = cancellable(
async () => {
for (const i of range(playbackRate)) {
const r = shouldBreak(step + i);
if (r.result || r.error) return { ...r, offset: i };
}
return { result: "", offset: 0, error: undefined };
},
({ result, offset, error }) => {
if (!error) {
if (result) {
notify(`Breakpoint hit: ${result}`, `${offset}`);
pause(offset);
} else tick(playbackRate);
} else {
notify(`${trimEnd(error, ".")}`, `${offset}`);
pause();
}
}
);
cancel = stepWithBreakpointCheck(playbackRate);
} else {
pause();
}
Expand All @@ -84,17 +45,8 @@ export function PlaybackService({
clearInterval(r);
};
}
}, [
renderLabel,
playing,
end,
step,
pause,
tick,
notify,
shouldBreak,
playbackRate,
]);
}, [stepWithBreakpointCheck, playing, end, step, pause, playbackRate]);

return <>{children}</>;
}

Expand All @@ -115,6 +67,9 @@ export function Playback({
stepBackward,
stepForward,
stop,
stepWithBreakpointCheck,
step,
end,
} = usePlaybackState(layer?.key);
return (
<>
Expand Down Expand Up @@ -142,10 +97,18 @@ export function Playback({
/>
<Button
label="step-forward"
icon={<ForwardIcon />}
icon={<NextIcon />}
onClick={stepForward}
disabled={!canStepForward}
/>
<Button
label="step-to-next-breakpoint"
icon={<SkipIcon />}
onClick={() => {
stepWithBreakpointCheck(end - step, 1);
}}
disabled={!canStepForward}
/>
<Button
label="stop"
icon={<StopIcon />}
Expand Down
14 changes: 6 additions & 8 deletions client/src/components/breakpoint-editor/BreakpointListEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Box } from "@mui/material";
import { ListEditor } from "components/generic/ListEditor";
import { Breakpoint, DebugLayerData } from "hooks/useBreakpoints";
import { flatMap as flat, get, keys, map, set, uniq } from "lodash";
import { chain as _, flatMap as flat, get, keys, map, set, uniq } from "lodash";
import { produce } from "produce";
import { useLayer } from "slices/layers";
import { BreakpointEditor } from "./BreakpointEditor";
Expand Down Expand Up @@ -30,13 +30,11 @@ export function BreakpointListEditor({
);
}

const properties = uniq([
...intrinsicProperties,
...flat(paths, (p) =>
//TODO:
flat([], (v) => map(keys(get(v, p)), (k) => `${p}.${k}`))
),
]);
const properties = _(layer?.source?.trace?.content?.events)
.flatMap(keys)
.uniq()
.filter((p) => p !== "type")
.value();

return (
<Box sx={{ overflow: "auto hidden", width: "100%" }}>
Expand Down
5 changes: 2 additions & 3 deletions client/src/components/inspector/EventInspector.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import {
Box,
Divider,
Expand All @@ -14,11 +15,9 @@ import {
import { getColorHex } from "components/renderer/colors";
import { pick } from "lodash";
import { TraceEvent } from "protocol/Trace";
import { useCss } from "react-use";
import { EventLabel } from "./EventLabel";
import { PropertyList } from "./PropertyList";
import IconButton from "@mui/material/IconButton";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import { useCss } from "react-use";

type EventInspectorProps = {
event?: TraceEvent;
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/inspector/TraceRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ export function TraceRenderer({
) : (
<>
<Box ref={ref}>
{layers.map((l) => (
<RenderLayer key={l.key} layer={l} />
{layers.map((l, i) => (
<RenderLayer index={i} key={l.key} layer={l} />
))}
</Box>
</>
Expand Down
43 changes: 39 additions & 4 deletions client/src/components/layer-editor/LayerEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,35 @@ import {
import { Layer } from "slices/layers";
import { inferLayerName, layerHandlers } from "../../layers/Layer";

const compositeOperations = [
"color",
"color-burn",
"color-dodge",
"copy",
"darken",
"destination-atop",
"destination-in",
"destination-out",
"destination-over",
"difference",
"exclusion",
"hard-light",
"hue",
"lighten",
"lighter",
"luminosity",
"multiply",
"overlay",
"saturation",
"screen",
"soft-light",
"source-atop",
"source-in",
"source-out",
"source-over",
"xor",
];

type LayerEditorProps = {
value: Layer;
onValueChange?: (v: Layer) => void;
Expand Down Expand Up @@ -126,21 +155,27 @@ function Component(
"Transparency",
<FeaturePicker
label="Transparency"
items={["25", "50", "75", "100"].map((c) => ({
items={["0", "25", "50", "75"].map((c) => ({
id: c,
name: `${c}%`,
}))}
value="100"
value={draft.transparency ?? "0"}
showArrow
onChange={(e) =>
setDraft?.(produce(draft, (d) => set(d, "transparency", e)))
}
/>
)}
{renderOption(
"Display Mode",
<FeaturePicker
label="Display Mode"
value="normal"
items={options(["normal", "difference"])}
value={draft.displayMode ?? "source-over"}
items={options(compositeOperations)}
showArrow
onChange={(e) =>
setDraft?.(produce(draft, (d) => set(d, "displayMode", e)))
}
/>
)}
{renderHeading("Source Options")}
Expand Down
9 changes: 4 additions & 5 deletions client/src/hooks/useBreakpoints.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { useMemo } from "react";
import { UploadedTrace } from "slices/UIState";
import { useLayer } from "slices/layers";


export type Comparator = {
key: string;
apply: (value: number, reference: number) => boolean;
Expand All @@ -30,7 +29,8 @@ export type DebugLayerData = {
};
export function useBreakpoints(key?: string) {
const { layer } = useLayer<DebugLayerData>(key);
const {monotonicF, monotonicG,breakpoints ,code,trace} = layer?.source??{}
const { monotonicF, monotonicG, breakpoints, code, trace } =
layer?.source ?? {};
// TODO:
return useMemo(() => {
const memo = keyBy(trace?.content?.events, "id");
Expand All @@ -53,7 +53,7 @@ export function useBreakpoints(key?: string) {
type,
property = "",
reference = 0,
} of breakpoints??[]) {
} of breakpoints ?? []) {
const isType = !type || type === event.type;
const match = condition?.apply?.(get(event, property), reference);
if (active && isType && match) {
Expand All @@ -69,7 +69,7 @@ export function useBreakpoints(key?: string) {
call(code ?? "", "shouldBreak", [
step,
event,
trace?.content?.events?? [],
trace?.content?.events ?? [],
])
) {
return { result: "Script editor" };
Expand All @@ -81,5 +81,4 @@ export function useBreakpoints(key?: string) {
return { result: "" };
});
}, [code, trace?.content, breakpoints, monotonicF, monotonicG]);

}
56 changes: 49 additions & 7 deletions client/src/hooks/usePlaybackState.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import { PlaybackLayerData } from "components/app-bar/Playback";
import { clamp, min, set } from "lodash";
import { useSnackbar } from "components/generic/Snackbar";
import { clamp, min, range, set, trimEnd } from "lodash";
import { produce } from "produce";
import { useMemo } from "react";
import { useLayer } from "slices/layers";
import { useBreakpoints } from "./useBreakpoints";

function cancellable<T = void>(f: () => Promise<T>, g: (result: T) => void) {
let cancelled = false;
requestAnimationFrame(async () => {
const result = await f();
if (!cancelled) g(result);
});
return () => {
cancelled = true;
};
}

export function usePlaybackState(key?: string) {
const { layer, setLayer } = useLayer<PlaybackLayerData>(key);
const notify = useSnackbar();
const shouldBreak = useBreakpoints(key);

const { playback, playbackTo, step: _step = 0 } = layer?.source ?? {};

Expand All @@ -32,23 +47,50 @@ export function usePlaybackState(key?: string) {
canStepBackward: ready && !playing && step > 0,
};

const pause = (n = 0) => {
// notify("Playback paused");
setPlaybackState({ playback: "paused", step: stepBy(n) });
};

const tick = (n = 1) =>
setPlaybackState({ playback: "playing", step: stepBy(n) });

const stepWithBreakpointCheck = (count: number, offset: number = 0) =>
cancellable(
async () => {
for (const i of range(offset, count)) {
const r = shouldBreak(step + i);
if (r.result || r.error) return { ...r, offset: i };
}
return { result: "", offset: 0, error: undefined };
},
({ result, offset, error }) => {
if (!error) {
if (result) {
notify(`Breakpoint hit: ${result}`, `Step ${offset}`);
pause(offset);
} else tick(count);
} else {
notify(`${trimEnd(error, ".")}`, `Step ${offset}`);
pause();
}
}
);

const stepBy = (n: number) => clamp(step + n, start, end);

const callbacks = {
play: () => {
// notify("Playback started");
setPlaybackState({ playback: "playing", step: stepBy(1) });
},
pause: (n = 0) => {
// notify("Playback paused");
setPlaybackState({ playback: "paused", step: stepBy(n) });
},
pause,
stepTo: (n = 0) => setPlaybackState({ step: n }),
stop: () => setPlaybackState({ step: start, playback: "paused" }),
stepForward: () => setPlaybackState({ step: stepBy(1) }),
stepBackward: () => setPlaybackState({ step: stepBy(-1) }),
tick: (n = 1) =>
setPlaybackState({ playback: "playing", step: stepBy(n) }),
tick,
stepWithBreakpointCheck,
};

return {
Expand Down
Loading

0 comments on commit 587e76d

Please sign in to comment.