Skip to content

Commit

Permalink
Add shareable playground links (#134)
Browse files Browse the repository at this point in the history
Co-authored-by: Luca Schneider <[email protected]>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 21, 2024
1 parent ebefecc commit 07324da
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 262 deletions.
3 changes: 2 additions & 1 deletion packages/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"@babel/core": "7.23.2",
"@babel/plugin-syntax-typescript": "7.22.5",
"@babel/standalone": "^7.24.4",
"monaco-editor": "^0.46.0",
"lz-string": "^1.5.0",
"monaco-editor": "^0.50.0",
"next-yak": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
7 changes: 5 additions & 2 deletions packages/playground/src/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import { YakEditor } from "./editor/YakEditor";
import { setupMonaco } from "./monaco";
import { useYakCompiler } from "./useYakCompiler/useYakCompiler";
import { readStateFromURL } from "./editor/shareableState";

const defaultInputValue = `import { styled } from "next-yak";
Expand All @@ -16,7 +17,9 @@ export const Button = styled.button\`
\`;`;

export function Playground() {
const [code, setCode] = useState(defaultInputValue);
const [code, setCode] = useState(
() => readStateFromURL() ?? defaultInputValue
);
const result = useHighlighter(code);
return (
<>
Expand All @@ -32,7 +35,7 @@ export function Playground() {
}}
>
<YakEditor
initialValue={defaultInputValue}
initialValue={code}
onChange={(value) => {
setCode(value);
}}
Expand Down
12 changes: 11 additions & 1 deletion packages/playground/src/editor/YakEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as monaco from "monaco-editor";
import { useEffect, useRef } from "react";
import { useVocsTheme } from "../useVocsTheme";
import { updateWindowLocation } from "./shareableState";

export const YakEditor = ({
initialValue,
Expand All @@ -13,7 +14,8 @@ export const YakEditor = ({
const theme = useVocsTheme();
useEffect(() => {
if (monaco.editor.getModels().length > 0) {
return;
// remove active editors when switching from one link to another
monaco.editor.getModels().forEach((model) => model.dispose());
}
const model = monaco.editor.createModel(
initialValue,
Expand Down Expand Up @@ -41,6 +43,14 @@ export const YakEditor = ({
editor.onDidChangeModelContent(() => {
onChange(editor.getModel()?.getValue() ?? "");
});

window.addEventListener(
"focusout",
() => {
updateWindowLocation(editor.getModel()?.getValue() ?? "");
},
{ passive: true }
);
}, []);

useEffect(() => {
Expand Down
22 changes: 22 additions & 0 deletions packages/playground/src/editor/shareableState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
compressToEncodedURIComponent,
decompressFromEncodedURIComponent,
} from "lz-string";

export function updateWindowLocation(code: string) {
const result = compressToEncodedURIComponent(code);
window.history.replaceState({}, "", `?code=${result}`);
}

export function saveToClipboard() {
window.navigator.clipboard.writeText(location.href.toString());
// todo: show a toast
}

export const readStateFromURL = () => {
const params = new URLSearchParams(location.search);
const code = params.get("code");
if (code) {
return decompressFromEncodedURIComponent(code);
}
};
30 changes: 28 additions & 2 deletions packages/playground/src/monaco.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker"
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
import { getStoredTheme } from "./getStoredTheme";
import { saveToClipboard, updateWindowLocation } from "./editor/shareableState";

let highlighterPromise: ReturnType<typeof getHighlighterCore>;

Expand Down Expand Up @@ -91,7 +92,7 @@ export const setupMonaco = async () => {

monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
jsx: monaco.languages.typescript.JsxEmit.Preserve,
jsxImportSource: 'next-yak',
jsxImportSource: "next-yak",
esModuleInterop: true,
paths: {
react: ["/node_modules/@types/react"],
Expand All @@ -101,7 +102,32 @@ export const setupMonaco = async () => {
// Register the themes from Shiki, and provide syntax highlighting for Monaco.
shikiToMonaco(highlighter, monaco);
// monaco is loaded after the theme is set, so we need to set it again
monaco.editor.setTheme( getStoredTheme() === "dark" ? "vitesse-dark" : "vitesse-light");
monaco.editor.setTheme(
getStoredTheme() === "dark" ? "vitesse-dark" : "vitesse-light"
);

monaco.editor.registerCommand("copy-clipboard", () => {
updateWindowLocation(monaco.editor.getModels()[0]?.getValue() ?? "");
saveToClipboard();
});
monaco.editor.addKeybindingRule({
command: "copy-clipboard",
when: undefined,
keybinding: monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS,
});
monaco.editor.addEditorAction({
id: "copy-clipboard",
label: "Save to clipboard",
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS],

contextMenuGroupId: "run",
contextMenuOrder: 1.5,

run: () => {
updateWindowLocation(monaco.editor.getModels()[0]?.getValue() ?? "");
saveToClipboard();
},
});

return highlighter;
};
Loading

0 comments on commit 07324da

Please sign in to comment.