Spaces:
Running
Running
| <script lang="ts"> | |
| import { onMount } from "svelte"; | |
| import { Splitpanes, Pane } from "svelte-splitpanes"; | |
| import { uiStore } from "../../stores/ui"; | |
| import CodeEditor from "../editor/CodeEditor.svelte"; | |
| import GameCanvas from "../game/GameCanvas.svelte"; | |
| import ConsolePanel from "../console/ConsolePanel.svelte"; | |
| import ChatPanel from "../chat/ChatPanel.svelte"; | |
| import gsap from "gsap"; | |
| $: viewMode = $uiStore.viewMode; | |
| let userMainPaneSizes = { editor: 0.4, preview: 0.6 }; | |
| let userEditorPaneSizes = { code: 0.5, chat: 0.5 }; | |
| let userPreviewPaneSizes = { game: 0.75, console: 0.25 }; | |
| onMount(() => { | |
| gsap.fromTo( | |
| ".editor-pane", | |
| { opacity: 0, x: -20 }, | |
| { opacity: 1, x: 0, duration: 0.6, delay: 0.1, ease: "power3.out" }, | |
| ); | |
| gsap.fromTo( | |
| ".preview-pane", | |
| { opacity: 0, x: 20 }, | |
| { opacity: 1, x: 0, duration: 0.6, delay: 0.2, ease: "power3.out" }, | |
| ); | |
| const resetCursor = () => { | |
| document.body.style.cursor = ""; | |
| }; | |
| document.addEventListener("mouseup", resetCursor); | |
| return () => { | |
| document.removeEventListener("mouseup", resetCursor); | |
| }; | |
| }); | |
| $: if (viewMode && viewMode !== 'about') { | |
| handleViewModeAnimation(viewMode); | |
| } | |
| function handleViewModeAnimation(mode: "code" | "preview") { | |
| const mainPanes = document | |
| .querySelector(".main-splitpanes") | |
| ?.querySelectorAll(":scope > .splitpanes__pane"); | |
| if (!mainPanes || mainPanes.length < 2) return; | |
| const editorPaneEl = mainPanes[0] as HTMLElement; | |
| const previewPaneEl = mainPanes[1] as HTMLElement; | |
| const mainSplitter = document.querySelector( | |
| ".main-splitpanes > .splitpanes__splitter", | |
| ) as HTMLElement; | |
| if (!editorPaneEl || !previewPaneEl) return; | |
| const editorSplitpanes = editorPaneEl.querySelectorAll(".splitpanes__pane"); | |
| const codePane = editorSplitpanes[0] as HTMLElement; | |
| const chatPane = editorSplitpanes[1] as HTMLElement; | |
| const previewSplitpanes = previewPaneEl.querySelectorAll(".splitpanes__pane"); | |
| const gamePane = previewSplitpanes[0] as HTMLElement; | |
| const consolePane = previewSplitpanes[1] as HTMLElement; | |
| const consoleSplitter = previewPaneEl.querySelector(".splitpanes__splitter") as HTMLElement; | |
| if (mode === "preview") { | |
| const editorWidth = editorPaneEl.offsetWidth; | |
| const previewWidth = previewPaneEl.offsetWidth; | |
| const totalWidth = editorWidth + previewWidth; | |
| if (totalWidth > 0) { | |
| userMainPaneSizes.editor = editorWidth / totalWidth; | |
| userMainPaneSizes.preview = previewWidth / totalWidth; | |
| } | |
| if (codePane && chatPane) { | |
| const codeHeight = codePane.offsetHeight; | |
| const chatHeight = chatPane.offsetHeight; | |
| const totalEditorHeight = codeHeight + chatHeight; | |
| if (totalEditorHeight > 0) { | |
| userEditorPaneSizes.code = codeHeight / totalEditorHeight; | |
| userEditorPaneSizes.chat = chatHeight / totalEditorHeight; | |
| } | |
| } | |
| if (gamePane && consolePane) { | |
| const gameHeight = gamePane.offsetHeight; | |
| const consoleHeight = consolePane.offsetHeight; | |
| const totalHeight = gameHeight + consoleHeight; | |
| if (totalHeight > 0) { | |
| userPreviewPaneSizes.game = gameHeight / totalHeight; | |
| userPreviewPaneSizes.console = consoleHeight / totalHeight; | |
| } | |
| } | |
| gsap.set(editorPaneEl, { | |
| flexGrow: userMainPaneSizes.editor, | |
| flexBasis: `${userMainPaneSizes.editor * 100}%`, | |
| }); | |
| gsap.set(previewPaneEl, { | |
| flexGrow: userMainPaneSizes.preview, | |
| flexBasis: `${userMainPaneSizes.preview * 100}%`, | |
| }); | |
| if (codePane && chatPane) { | |
| gsap.set(codePane, { | |
| flexGrow: userEditorPaneSizes.code, | |
| flexBasis: `${userEditorPaneSizes.code * 100}%`, | |
| }); | |
| gsap.set(chatPane, { | |
| flexGrow: userEditorPaneSizes.chat, | |
| flexBasis: `${userEditorPaneSizes.chat * 100}%`, | |
| }); | |
| } | |
| if (gamePane && consolePane) { | |
| gsap.set(gamePane, { | |
| flexGrow: userPreviewPaneSizes.game, | |
| flexBasis: `${userPreviewPaneSizes.game * 100}%`, | |
| }); | |
| gsap.set(consolePane, { | |
| flexGrow: userPreviewPaneSizes.console, | |
| flexBasis: `${userPreviewPaneSizes.console * 100}%`, | |
| }); | |
| } | |
| } | |
| if (mode === "preview") { | |
| gsap.timeline({ ease: "power2.out" }) | |
| .to(editorPaneEl, { | |
| opacity: 0, | |
| duration: 0.15, | |
| }) | |
| .to( | |
| consolePane, | |
| { | |
| opacity: 0, | |
| duration: 0.15, | |
| }, | |
| "-=0.15", | |
| ) | |
| .to( | |
| editorPaneEl, | |
| { | |
| flexGrow: 0, | |
| flexBasis: "0%", | |
| duration: 0.2, | |
| ease: "power2.inOut", | |
| onComplete: () => { | |
| editorPaneEl.style.display = "none"; | |
| if (mainSplitter) mainSplitter.style.display = "none"; | |
| }, | |
| }, | |
| "-=0.1", | |
| ) | |
| .to( | |
| consolePane, | |
| { | |
| flexGrow: 0, | |
| flexBasis: "0%", | |
| duration: 0.2, | |
| ease: "power2.inOut", | |
| onComplete: () => { | |
| consolePane.style.display = "none"; | |
| if (consoleSplitter) consoleSplitter.style.display = "none"; | |
| }, | |
| }, | |
| "-=0.2", | |
| ) | |
| .to( | |
| gamePane, | |
| { | |
| flexGrow: 1, | |
| flexBasis: "100%", | |
| duration: 0.2, | |
| ease: "power2.inOut", | |
| }, | |
| "-=0.2", | |
| ) | |
| .to( | |
| previewPaneEl, | |
| { | |
| flexGrow: 1, | |
| flexBasis: "100%", | |
| duration: 0.2, | |
| ease: "power2.inOut", | |
| }, | |
| "-=0.2", | |
| ); | |
| } else { | |
| editorPaneEl.style.display = ""; | |
| if (mainSplitter) mainSplitter.style.display = ""; | |
| consolePane.style.display = ""; | |
| if (consoleSplitter) consoleSplitter.style.display = ""; | |
| gsap.timeline({ ease: "power2.out" }) | |
| .set(editorPaneEl, { | |
| opacity: 0, | |
| flexGrow: 0, | |
| flexBasis: "0%", | |
| }) | |
| .set(consolePane, { | |
| opacity: 0, | |
| flexGrow: 0, | |
| flexBasis: "0%", | |
| }) | |
| .to([editorPaneEl, previewPaneEl], { | |
| flexGrow: (i) => | |
| i === 0 ? userMainPaneSizes.editor : userMainPaneSizes.preview, | |
| flexBasis: (i) => | |
| i === 0 | |
| ? `${userMainPaneSizes.editor * 100}%` | |
| : `${userMainPaneSizes.preview * 100}%`, | |
| duration: 0.2, | |
| ease: "power2.inOut", | |
| }) | |
| .to( | |
| [codePane, chatPane], | |
| { | |
| flexGrow: (i) => | |
| i === 0 ? userEditorPaneSizes.code : userEditorPaneSizes.chat, | |
| flexBasis: (i) => | |
| i === 0 | |
| ? `${userEditorPaneSizes.code * 100}%` | |
| : `${userEditorPaneSizes.chat * 100}%`, | |
| duration: 0.2, | |
| ease: "power2.inOut", | |
| }, | |
| "-=0.2", | |
| ) | |
| .to( | |
| [gamePane, consolePane], | |
| { | |
| flexGrow: (i) => | |
| i === 0 ? userPreviewPaneSizes.game : userPreviewPaneSizes.console, | |
| flexBasis: (i) => | |
| i === 0 | |
| ? `${userPreviewPaneSizes.game * 100}%` | |
| : `${userPreviewPaneSizes.console * 100}%`, | |
| duration: 0.2, | |
| ease: "power2.inOut", | |
| }, | |
| "-=0.2", | |
| ) | |
| .to( | |
| consolePane, | |
| { | |
| opacity: 1, | |
| duration: 0.15, | |
| }, | |
| "-=0.15", | |
| ) | |
| .to( | |
| editorPaneEl, | |
| { | |
| opacity: 1, | |
| duration: 0.15, | |
| onComplete: () => { | |
| window.dispatchEvent(new Event("resize")); | |
| }, | |
| }, | |
| "-=0.15", | |
| ); | |
| } | |
| } | |
| </script> | |
| <div class="editor-layout"> | |
| <Splitpanes class="main-splitpanes" theme="modern-theme"> | |
| <Pane minSize={20} size={40} class="editor-pane"> | |
| <div class="editor-panel"> | |
| <Splitpanes horizontal class="editor-splitpanes" theme="modern-theme"> | |
| <Pane minSize={30} size={userEditorPaneSizes.code * 100} class="code-pane"> | |
| <CodeEditor /> | |
| </Pane> | |
| <Pane minSize={15} size={userEditorPaneSizes.chat * 100} class="chat-pane"> | |
| <ChatPanel /> | |
| </Pane> | |
| </Splitpanes> | |
| </div> | |
| </Pane> | |
| <Pane minSize={25} size={60} class="preview-pane"> | |
| <div class="preview-panel"> | |
| <Splitpanes horizontal class="preview-splitpanes" theme="modern-theme"> | |
| <Pane minSize={30} class="game-pane"> | |
| <GameCanvas /> | |
| </Pane> | |
| <Pane minSize={15} size={25} class="console-pane"> | |
| <ConsolePanel /> | |
| </Pane> | |
| </Splitpanes> | |
| </div> | |
| </Pane> | |
| </Splitpanes> | |
| </div> | |
| <style> | |
| .editor-layout { | |
| flex: 1; | |
| display: flex; | |
| min-height: 0; | |
| background: rgba(139, 115, 85, 0.02); | |
| } | |
| :global(.main-splitpanes) { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| :global(.editor-splitpanes) { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| :global(.preview-splitpanes) { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| :global(.editor-pane) { | |
| display: flex; | |
| overflow: hidden; | |
| } | |
| :global(.preview-pane) { | |
| display: flex; | |
| overflow: hidden; | |
| } | |
| :global(.code-pane) { | |
| display: flex; | |
| overflow: hidden; | |
| } | |
| :global(.game-pane) { | |
| display: flex; | |
| overflow: hidden; | |
| } | |
| :global(.console-pane) { | |
| display: flex; | |
| overflow: hidden; | |
| } | |
| :global(.chat-pane) { | |
| display: flex; | |
| overflow: hidden; | |
| } | |
| .editor-panel { | |
| width: 100%; | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| background: rgba(11, 10, 9, 0.3); | |
| min-width: 0; | |
| overflow: hidden; | |
| } | |
| .preview-panel { | |
| width: 100%; | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| background: rgba(11, 10, 9, 0.3); | |
| min-width: 0; | |
| overflow: hidden; | |
| } | |
| :global(.splitpanes.modern-theme .splitpanes__splitter) { | |
| background: rgba(139, 115, 85, 0.06); | |
| position: relative; | |
| transition: background 0.2s; | |
| } | |
| :global(.splitpanes.modern-theme .splitpanes__splitter:hover) { | |
| background: rgba(139, 115, 85, 0.12); | |
| } | |
| :global(.splitpanes.modern-theme .splitpanes__splitter:before) { | |
| content: ""; | |
| position: absolute; | |
| z-index: 1; | |
| transition: opacity 0.2s; | |
| background: rgba(124, 152, 133, 0.1); | |
| opacity: 0; | |
| } | |
| :global(.splitpanes.modern-theme .splitpanes__splitter:hover:before) { | |
| opacity: 1; | |
| } | |
| :global(.splitpanes--vertical.modern-theme > .splitpanes__splitter) { | |
| width: 1px; | |
| cursor: col-resize; | |
| } | |
| :global(.splitpanes--vertical.modern-theme > .splitpanes__splitter:before) { | |
| left: -3px; | |
| right: -3px; | |
| height: 100%; | |
| cursor: col-resize; | |
| } | |
| :global(.splitpanes--horizontal.modern-theme > .splitpanes__splitter) { | |
| height: 1px; | |
| cursor: row-resize; | |
| } | |
| :global(.splitpanes--horizontal.modern-theme > .splitpanes__splitter:before) { | |
| top: -3px; | |
| bottom: -3px; | |
| width: 100%; | |
| cursor: row-resize; | |
| } | |
| :global(body.splitpanes--dragging) { | |
| cursor: inherit; | |
| } | |
| :global(body:not(.splitpanes--dragging) .splitpanes__splitter:not(:hover)) { | |
| cursor: default; | |
| } | |
| </style> | |