From 1fdadee59cde6889262f5fcfff100f34a85b2f4e Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Wed, 26 Jul 2023 07:55:59 +0200 Subject: [PATCH] playground: Persist source and panel (#6071) --- playground/src/Editor/Editor.tsx | 61 +++++++++++++++++----- playground/src/Editor/SecondaryPanel.tsx | 7 ++- playground/src/Editor/SecondarySideBar.tsx | 16 +++--- playground/src/Editor/settings.ts | 29 ++++++++-- playground/src/index.css | 16 +++--- 5 files changed, 97 insertions(+), 32 deletions(-) diff --git a/playground/src/Editor/Editor.tsx b/playground/src/Editor/Editor.tsx index 60c91e626f..7b3e12dd90 100644 --- a/playground/src/Editor/Editor.tsx +++ b/playground/src/Editor/Editor.tsx @@ -10,7 +10,7 @@ import init, { Diagnostic, Workspace } from "../pkg"; import { ErrorMessage } from "./ErrorMessage"; import Header from "./Header"; import { useTheme } from "./theme"; -import { persist, restore, stringify } from "./settings"; +import { persist, persistLocal, restore, stringify } from "./settings"; import SettingsEditor from "./SettingsEditor"; import SourceEditor from "./SourceEditor"; import { Panel, PanelGroup } from "react-resizable-panels"; @@ -50,17 +50,45 @@ export default function Editor() { }); const [tab, setTab] = useState("Source"); - const [theme, setTheme] = useTheme(); const [secondaryTool, setSecondaryTool] = useState( - null, + () => { + const secondaryValue = new URLSearchParams(location.search).get( + "secondary", + ); + if (secondaryValue == null) { + return null; + } else { + return parseSecondaryTool(secondaryValue); + } + }, ); + const [theme, setTheme] = useTheme(); const initialized = ruffVersion != null; + // Ideally this would be retrieved right from the URl... but routing without a proper + // router is hard (there's no location changed event) and pulling in a router + // feels overkill. + const handleSecondaryToolSelected = (tool: SecondaryTool | null) => { + if (tool === secondaryTool) { + tool = null; + } + + const url = new URL(location.href); + + if (tool == null) { + url.searchParams.delete("secondary"); + } else { + url.searchParams.set("secondary", tool); + } + + history.replaceState(null, "", url); + + setSecondaryTool(tool); + }; + useEffect(() => { init().then(() => { - setRuffVersion(Workspace.version()); - const [settingsSource, pythonSource] = restore() ?? [ stringify(Workspace.defaultSettings()), DEFAULT_PYTHON_SOURCE, @@ -71,6 +99,7 @@ export default function Editor() { revision: 0, settingsSource, }); + setRuffVersion(Workspace.version()); }); }, []); @@ -141,6 +170,12 @@ export default function Editor() { } }, [initialized, deferredSource, secondaryTool]); + useEffect(() => { + if (initialized) { + persistLocal(source); + } + }, [initialized, source]); + const handleShare = useMemo(() => { if (!initialized) { return undefined; @@ -215,13 +250,7 @@ export default function Editor() { )} { - if (secondaryTool === tool) { - setSecondaryTool(null); - } else { - setSecondaryTool(tool); - } - }} + onSelected={handleSecondaryToolSelected} /> ) : null} @@ -241,3 +270,11 @@ export default function Editor() { ); } + +function parseSecondaryTool(tool: string): SecondaryTool | null { + if (Object.hasOwn(SecondaryTool, tool)) { + return tool as any; + } + + return null; +} diff --git a/playground/src/Editor/SecondaryPanel.tsx b/playground/src/Editor/SecondaryPanel.tsx index c4d2b11d3d..f7b0b7649f 100644 --- a/playground/src/Editor/SecondaryPanel.tsx +++ b/playground/src/Editor/SecondaryPanel.tsx @@ -1,7 +1,12 @@ import Editor from "@monaco-editor/react"; import { Theme } from "./theme"; -export type SecondaryTool = "Format" | "AST" | "Tokens" | "FIR"; +export enum SecondaryTool { + "Format" = "Format", + "AST" = "AST", + "Tokens" = "Tokens", + "FIR" = "FIR", +} export type SecondaryPanelResult = | null diff --git a/playground/src/Editor/SecondarySideBar.tsx b/playground/src/Editor/SecondarySideBar.tsx index 1abe6db6a3..44177c3ea6 100644 --- a/playground/src/Editor/SecondarySideBar.tsx +++ b/playground/src/Editor/SecondarySideBar.tsx @@ -15,32 +15,32 @@ export default function SecondarySideBar({ onSelected("Format")} + selected={selected === SecondaryTool.Format} + onClick={() => onSelected(SecondaryTool.Format)} > onSelected("AST")} + selected={selected === SecondaryTool.AST} + onClick={() => onSelected(SecondaryTool.AST)} > onSelected("Tokens")} + selected={selected === SecondaryTool.Tokens} + onClick={() => onSelected(SecondaryTool.Tokens)} > onSelected("FIR")} + selected={selected === SecondaryTool.FIR} + onClick={() => onSelected(SecondaryTool.FIR)} > FIR diff --git a/playground/src/Editor/settings.ts b/playground/src/Editor/settings.ts index e6d0d67fa7..34e447eee3 100644 --- a/playground/src/Editor/settings.ts +++ b/playground/src/Editor/settings.ts @@ -39,10 +39,33 @@ export function restore(): [string, string] | null { window.location.hash.slice(1), ); - if (value != null) { + if (value == null) { + return restoreLocal(); + } else { const [settingsSource, pythonSource] = value.split("$$$"); return [settingsSource.replaceAll("$$$$$$", "$$$"), pythonSource]; - } else { - return null; } } + +function restoreLocal(): [string, string] | null { + const source = localStorage.getItem("source"); + + if (source == null) { + return null; + } else { + return JSON.parse(source); + } +} + +export function persistLocal({ + settingsSource, + pythonSource, +}: { + settingsSource: string; + pythonSource: string; +}) { + localStorage.setItem( + "source", + JSON.stringify([settingsSource, pythonSource]), + ); +} diff --git a/playground/src/index.css b/playground/src/index.css index 5bf338e90a..5c9d5245d2 100644 --- a/playground/src/index.css +++ b/playground/src/index.css @@ -29,8 +29,8 @@ html, @font-face { font-family: "Alliance Text"; src: - url("../public/fonts/Alliance-TextRegular.woff2") format("woff2"), - url("../public/fonts/Alliance-TextRegular.woff") format("woff"); + url("../fonts/Alliance-TextRegular.woff2") format("woff2"), + url("../fonts/Alliance-TextRegular.woff") format("woff"); font-weight: normal; font-style: normal; font-display: block; @@ -39,8 +39,8 @@ html, @font-face { font-family: "Alliance Text"; src: - url("../public/fonts/Alliance-TextMedium.woff2") format("woff2"), - url("../public/fonts/Alliance-TextMedium.woff") format("woff"); + url("../fonts/Alliance-TextMedium.woff2") format("woff2"), + url("../fonts/Alliance-TextMedium.woff") format("woff"); font-weight: 500; font-style: normal; font-display: block; @@ -49,8 +49,8 @@ html, @font-face { font-family: "Alliance Platt"; src: - url("../public/fonts/Alliance-PlattMedium.woff2") format("woff2"), - url("../public/fonts/Alliance-PlattMedium.woff") format("woff"); + url("../fonts/Alliance-PlattMedium.woff2") format("woff2"), + url("../fonts/Alliance-PlattMedium.woff") format("woff"); font-weight: 500; font-style: normal; font-display: block; @@ -59,8 +59,8 @@ html, @font-face { font-family: "Alliance Platt"; src: - url("../public/fonts/Alliance-PlattRegular.woff2") format("woff2"), - url("../public/fonts/Alliance-PlattRegular.woff") format("woff"); + url("../fonts/Alliance-PlattRegular.woff2") format("woff2"), + url("../fonts/Alliance-PlattRegular.woff") format("woff"); font-weight: normal; font-style: normal; font-display: block;