add localisation and theming for playground
This commit is contained in:
parent
1a5dcd24bc
commit
a02af4f90c
180
astro.config.mjs
180
astro.config.mjs
|
@ -1,78 +1,114 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import starlight from '@astrojs/starlight';
|
||||
import { defineConfig } from "astro/config";
|
||||
import starlight from "@astrojs/starlight";
|
||||
import react from "@astrojs/react";
|
||||
import starlightLinksValidator from "starlight-links-validator";
|
||||
import shikiConfig from './src/utils/shiki';
|
||||
import starlightUtils from "@lorenzo_lewis/starlight-utils";
|
||||
import shikiConfig from "./src/utils/shiki";
|
||||
|
||||
const playgroundSidebarEntry = {
|
||||
label: "Playground",
|
||||
link: "/playground",
|
||||
translations: { de: "Spielplatz" },
|
||||
};
|
||||
|
||||
const navLinks = [playgroundSidebarEntry];
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [react(), starlight({
|
||||
title: 'ShulkerScript',
|
||||
logo: {
|
||||
src: './src/assets/logo.webp',
|
||||
alt: 'ShulkerScript Logo'
|
||||
},
|
||||
favicon: '/favicon.ico',
|
||||
description: 'A simple and powerful scripting language for Minecraft datapacks.',
|
||||
social: {
|
||||
github: 'https://github.com/moritz-hoelting/shulkerscript-cli'
|
||||
},
|
||||
tableOfContents: {
|
||||
minHeadingLevel: 1,
|
||||
maxHeadingLevel: 3
|
||||
},
|
||||
defaultLocale: 'root',
|
||||
locales: {
|
||||
root: {
|
||||
label: 'English',
|
||||
lang: 'en'
|
||||
},
|
||||
de: {
|
||||
label: 'Deutsch',
|
||||
lang: 'de'
|
||||
}
|
||||
},
|
||||
editLink: {
|
||||
baseUrl: 'https://github.com/moritz-hoelting/shulkerscript-webpage/edit/main'
|
||||
},
|
||||
customCss: ['./src/styles/style.css'],
|
||||
plugins: [starlightLinksValidator({
|
||||
errorOnFallbackPages: false
|
||||
})],
|
||||
expressiveCode: {
|
||||
shiki: shikiConfig
|
||||
},
|
||||
components: {
|
||||
PageTitle: './src/components/override/PageTitle.astro',
|
||||
ContentPanel: './src/components/override/ContentPanel.astro',
|
||||
},
|
||||
sidebar: [{
|
||||
label: 'Guides',
|
||||
autogenerate: {
|
||||
directory: 'guides'
|
||||
},
|
||||
translations: {
|
||||
de: 'Anleitungen'
|
||||
}
|
||||
}, {
|
||||
label: 'Roadmap',
|
||||
link: '/roadmap',
|
||||
translations: {
|
||||
de: 'Zukunftspläne'
|
||||
}
|
||||
}, {
|
||||
label: 'Reference',
|
||||
autogenerate: {
|
||||
directory: 'reference'
|
||||
},
|
||||
collapsed: true,
|
||||
translations: {
|
||||
de: 'Referenz'
|
||||
},
|
||||
badge: {
|
||||
text: 'WIP',
|
||||
variant: 'caution'
|
||||
}
|
||||
}]
|
||||
})]
|
||||
integrations: [
|
||||
react(),
|
||||
starlight({
|
||||
title: "ShulkerScript",
|
||||
logo: {
|
||||
src: "./src/assets/logo.webp",
|
||||
alt: "ShulkerScript Logo",
|
||||
},
|
||||
favicon: "/favicon.ico",
|
||||
description:
|
||||
"A simple and powerful scripting language for Minecraft datapacks.",
|
||||
social: {
|
||||
github: "https://github.com/moritz-hoelting/shulkerscript-cli",
|
||||
},
|
||||
tableOfContents: {
|
||||
minHeadingLevel: 1,
|
||||
maxHeadingLevel: 3,
|
||||
},
|
||||
defaultLocale: "root",
|
||||
locales: {
|
||||
root: {
|
||||
label: "English",
|
||||
lang: "en",
|
||||
},
|
||||
de: {
|
||||
label: "Deutsch",
|
||||
lang: "de",
|
||||
},
|
||||
},
|
||||
editLink: {
|
||||
baseUrl:
|
||||
"https://github.com/moritz-hoelting/shulkerscript-webpage/edit/main",
|
||||
},
|
||||
customCss: ["./src/styles/style.css"],
|
||||
plugins: [
|
||||
starlightUtils({
|
||||
navLinks: {
|
||||
leading: {
|
||||
useSidebarLabelled: "leadingNavLinks",
|
||||
},
|
||||
},
|
||||
}),
|
||||
starlightLinksValidator({
|
||||
errorOnFallbackPages: false,
|
||||
}),
|
||||
],
|
||||
expressiveCode: {
|
||||
shiki: shikiConfig,
|
||||
},
|
||||
components: {
|
||||
PageTitle: "./src/components/override/PageTitle.astro",
|
||||
ContentPanel: "./src/components/override/ContentPanel.astro",
|
||||
},
|
||||
sidebar: [
|
||||
{
|
||||
label: "leadingNavLinks",
|
||||
items: navLinks,
|
||||
},
|
||||
{
|
||||
label: "Guides",
|
||||
autogenerate: {
|
||||
directory: "guides",
|
||||
},
|
||||
translations: {
|
||||
de: "Anleitungen",
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "More",
|
||||
translations: {
|
||||
de: "Mehr",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
label: "Roadmap",
|
||||
link: "/roadmap",
|
||||
translations: {
|
||||
de: "Zukunftspläne",
|
||||
},
|
||||
},
|
||||
playgroundSidebarEntry
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Reference",
|
||||
autogenerate: {
|
||||
directory: "reference",
|
||||
},
|
||||
collapsed: true,
|
||||
translations: {
|
||||
de: "Referenz",
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
|
@ -15,6 +15,7 @@
|
|||
"@astrojs/starlight": "^0.24.2",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@lorenzo_lewis/starlight-utils": "^0.1.1",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@mui/icons-material": "^5.15.20",
|
||||
"@mui/material": "^5.15.20",
|
||||
|
|
|
@ -20,6 +20,9 @@ dependencies:
|
|||
'@emotion/styled':
|
||||
specifier: ^11.11.5
|
||||
version: 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.3)(react@18.3.1)
|
||||
'@lorenzo_lewis/starlight-utils':
|
||||
specifier: ^0.1.1
|
||||
version: 0.1.1(@astrojs/starlight@0.24.2)(astro@4.10.2)
|
||||
'@monaco-editor/react':
|
||||
specifier: ^4.6.0
|
||||
version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1)(react@18.3.1)
|
||||
|
@ -1410,6 +1413,19 @@ packages:
|
|||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
dev: false
|
||||
|
||||
/@lorenzo_lewis/starlight-utils@0.1.1(@astrojs/starlight@0.24.2)(astro@4.10.2):
|
||||
resolution: {integrity: sha512-WBbZ9tnLxRsiiNVBzyNrANbl098/wMt7gVT09XCJtHZiiOlYQFBBYywgk/vAwzgicUe3vb27MBFc6jDOqvmu5w==}
|
||||
peerDependencies:
|
||||
'@astrojs/starlight': '>=0.16.0'
|
||||
astro: '>=4.0.0'
|
||||
dependencies:
|
||||
'@astrojs/starlight': 0.24.2(astro@4.10.2)
|
||||
astro: 4.10.2(sass@1.77.6)(typescript@5.4.5)
|
||||
astro-integration-kit: 0.13.3(astro@4.10.2)
|
||||
transitivePeerDependencies:
|
||||
- '@astrojs/db'
|
||||
dev: false
|
||||
|
||||
/@mdx-js/mdx@3.0.1:
|
||||
resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==}
|
||||
dependencies:
|
||||
|
@ -2161,6 +2177,13 @@ packages:
|
|||
resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==}
|
||||
dev: false
|
||||
|
||||
/ast-types@0.16.1:
|
||||
resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
|
||||
engines: {node: '>=4'}
|
||||
dependencies:
|
||||
tslib: 2.6.3
|
||||
dev: false
|
||||
|
||||
/astring@1.8.6:
|
||||
resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
|
||||
hasBin: true
|
||||
|
@ -2175,6 +2198,20 @@ packages:
|
|||
rehype-expressive-code: 0.35.3
|
||||
dev: false
|
||||
|
||||
/astro-integration-kit@0.13.3(astro@4.10.2):
|
||||
resolution: {integrity: sha512-hUEQMnZ7z+7ySPCX6mXnIr0BFZU1+49eQQBg4aHjKGz1o2oZ5tvuB9Tlyj/orRH9ubd+Gkd0SSoldz0BTNe4Rg==}
|
||||
peerDependencies:
|
||||
'@astrojs/db': ^0.9 || ^0.10 || ^0.11
|
||||
astro: ^4.4.1
|
||||
peerDependenciesMeta:
|
||||
'@astrojs/db':
|
||||
optional: true
|
||||
dependencies:
|
||||
astro: 4.10.2(sass@1.77.6)(typescript@5.4.5)
|
||||
pathe: 1.1.2
|
||||
recast: 0.23.9
|
||||
dev: false
|
||||
|
||||
/astro@4.10.2(sass@1.77.6)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-SBdkoOanPsxKlKVU4uu/XG0G7NYAFoqmfBtq9SPMJ34B7Hr1MxVdEugERs8IwYN6UaxdDVcqA++9PvH6Onq2cg==}
|
||||
engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'}
|
||||
|
@ -4437,6 +4474,10 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/pathe@1.1.2:
|
||||
resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
|
||||
dev: false
|
||||
|
||||
/periscopic@3.1.0:
|
||||
resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
|
||||
dependencies:
|
||||
|
@ -4639,6 +4680,17 @@ packages:
|
|||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
|
||||
/recast@0.23.9:
|
||||
resolution: {integrity: sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==}
|
||||
engines: {node: '>= 4'}
|
||||
dependencies:
|
||||
ast-types: 0.16.1
|
||||
esprima: 4.0.1
|
||||
source-map: 0.6.1
|
||||
tiny-invariant: 1.3.3
|
||||
tslib: 2.6.3
|
||||
dev: false
|
||||
|
||||
/regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
dev: false
|
||||
|
@ -5079,6 +5131,11 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/source-map@0.6.1:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/source-map@0.7.4:
|
||||
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -5279,6 +5336,10 @@ packages:
|
|||
b4a: 1.6.6
|
||||
dev: false
|
||||
|
||||
/tiny-invariant@1.3.3:
|
||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||
dev: false
|
||||
|
||||
/tm-themes@1.4.3:
|
||||
resolution: {integrity: sha512-nsUGwktLaWFMyKw2e/hNyDmcTIO+ue6Q2Mvb8L7WQkKSCflOm2TjGSspcJw6q7hi4QxQQNTuGICyvQN1UqtunQ==}
|
||||
dev: false
|
||||
|
@ -5319,7 +5380,6 @@ packages:
|
|||
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/tunnel-agent@0.6.0:
|
||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useMonaco, type Monaco } from "@monaco-editor/react";
|
||||
import { useImmer, type Updater } from "use-immer";
|
||||
|
||||
|
@ -14,16 +14,7 @@ import initWasm, {
|
|||
compile,
|
||||
compileZip,
|
||||
} from "@wasm/webcompiler/pkg/webcompiler";
|
||||
|
||||
export type File = {
|
||||
language?: string;
|
||||
content: string;
|
||||
};
|
||||
export type Directory = {
|
||||
dirs?: { [key: string]: Directory };
|
||||
files?: { [key: string]: File };
|
||||
};
|
||||
export type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
|
||||
import type { Directory, File, PlaygroundLang } from "@utils/playground";
|
||||
|
||||
const FILE_STORAGE_KEY = "playground-files";
|
||||
const DEFAULT_FILES = {
|
||||
|
@ -46,7 +37,7 @@ const DEFAULT_FILES = {
|
|||
},
|
||||
};
|
||||
|
||||
export default function Playground() {
|
||||
export default function Playground({ lang }: { lang: PlaygroundLang }) {
|
||||
initWasm().catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
|
@ -55,32 +46,40 @@ export default function Playground() {
|
|||
getStorageOrDefault(FILE_STORAGE_KEY, DEFAULT_FILES) as Directory
|
||||
);
|
||||
|
||||
const [theme, setTheme] = useState<"light" | "dark">("dark");
|
||||
const [fileName, setFileName] = useState("src/main.shu");
|
||||
const file = getFile(rootDir, fileName);
|
||||
|
||||
const onBuild = () => {
|
||||
if (monaco) {
|
||||
const dist = JSON.parse(
|
||||
JSON.stringify(compile(getFiles(monaco)), jsonReplacer)
|
||||
);
|
||||
const withRoot = {
|
||||
dirs: {
|
||||
dist: dist,
|
||||
},
|
||||
} as Directory;
|
||||
loadFiles(monaco, updateRootDir, withRoot);
|
||||
const compiled = compile(getFiles(monaco));
|
||||
if (compiled) {
|
||||
const dist = JSON.parse(JSON.stringify(compiled, jsonReplacer));
|
||||
const withRoot = {
|
||||
dirs: {
|
||||
dist: dist,
|
||||
},
|
||||
} as Directory;
|
||||
loadFiles(monaco, updateRootDir, withRoot);
|
||||
} else {
|
||||
alert("Compilation failed");
|
||||
}
|
||||
} else {
|
||||
console.error("monaco has not loaded");
|
||||
}
|
||||
};
|
||||
const onZip = () => {
|
||||
if (monaco) {
|
||||
const data =
|
||||
"data:application/zip;base64," + compileZip(getFiles(monaco));
|
||||
const a = document.createElement("a");
|
||||
a.href = data;
|
||||
a.download = "shulkerscript-pack.zip";
|
||||
a.click();
|
||||
const zipped = compileZip(getFiles(monaco));
|
||||
if (zipped) {
|
||||
const data = "data:application/zip;base64," + zipped;
|
||||
const a = document.createElement("a");
|
||||
a.href = data;
|
||||
a.download = "shulkerscript-pack.zip";
|
||||
a.click();
|
||||
} else {
|
||||
alert("Compilation failed");
|
||||
}
|
||||
} else {
|
||||
console.error("monaco has not loaded");
|
||||
}
|
||||
|
@ -125,6 +124,37 @@ export default function Playground() {
|
|||
}
|
||||
}, [monaco]);
|
||||
|
||||
useEffect(() => {
|
||||
if (monaco) {
|
||||
let isReadOnly = fileName.startsWith("dist/");
|
||||
monaco.editor.getEditors().forEach((e) =>
|
||||
e.updateOptions({
|
||||
readOnly: isReadOnly,
|
||||
readOnlyMessage: {
|
||||
value: "Generated files are read-only",
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [fileName]);
|
||||
|
||||
useEffect(() => {
|
||||
const root = document.querySelector(":root") as HTMLElement;
|
||||
if (root) {
|
||||
function reactToThemeChange() {
|
||||
const selectedTheme = root.getAttribute("data-theme");
|
||||
if (selectedTheme !== theme && selectedTheme !== null) {
|
||||
setTheme(selectedTheme as "light" | "dark");
|
||||
}
|
||||
}
|
||||
reactToThemeChange();
|
||||
|
||||
root.onchange = () => {
|
||||
reactToThemeChange();
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main
|
||||
|
@ -136,6 +166,7 @@ export default function Playground() {
|
|||
}}
|
||||
>
|
||||
<Header
|
||||
lang={lang.header}
|
||||
onSave={onSave}
|
||||
onReset={onReset}
|
||||
onBuild={onBuild}
|
||||
|
@ -146,8 +177,13 @@ export default function Playground() {
|
|||
root={rootDir}
|
||||
fileName={fileName}
|
||||
setSelectedFileName={setFileName}
|
||||
lang={lang.explorer}
|
||||
/>
|
||||
<Editor
|
||||
fileName={fileName}
|
||||
file={file ?? undefined}
|
||||
theme={theme}
|
||||
/>
|
||||
<Editor fileName={fileName} file={file ?? undefined} />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
---
|
||||
import type { Props } from "@astrojs/starlight/props";
|
||||
import Default from "@astrojs/starlight/components/ContentPanel.astro";
|
||||
import { isPlaygroundPage } from '@utils/playground';
|
||||
|
||||
const isPlayground = Astro.props.slug === 'playground';
|
||||
const isPlayground = isPlaygroundPage(Astro.props.slug, Astro.currentLocale);
|
||||
---
|
||||
|
||||
|
||||
{isPlayground ? <slot /> : <Default {...Astro.props}><slot /></Default>}
|
||||
{
|
||||
isPlayground ? (
|
||||
<slot />
|
||||
) : (
|
||||
<Default {...Astro.props}>
|
||||
<slot />
|
||||
</Default>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
import type { Props } from "@astrojs/starlight/props";
|
||||
import Default from "@astrojs/starlight/components/PageTitle.astro";
|
||||
import { isPlaygroundPage } from '@utils/playground';
|
||||
|
||||
const isPlayground = Astro.props.slug === 'playground';
|
||||
const isPlayground = isPlaygroundPage(Astro.props.slug, Astro.currentLocale);
|
||||
---
|
||||
|
||||
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import type { File } from "@components/Playground";
|
||||
import type { File } from "@utils/playground";
|
||||
import MonacoEditor, { useMonaco } from "@monaco-editor/react";
|
||||
import { getHighlighter, type Highlighter } from "shiki";
|
||||
import { shikiToMonaco } from "@shikijs/monaco";
|
||||
import { useEffect, useState } from "react";
|
||||
import darkPlus from "tm-themes/themes/dark-plus.json";
|
||||
import lightPlus from "tm-themes/themes/light-plus.json";
|
||||
|
||||
import { shulkerscriptGrammar } from "@utils/shulkerscript-grammar";
|
||||
import { mcfunctionGrammar } from "@utils/mcfunction-grammar";
|
||||
|
||||
export default function Editor({
|
||||
theme,
|
||||
fileName,
|
||||
file,
|
||||
}: {
|
||||
theme: "light" | "dark";
|
||||
fileName: string;
|
||||
file?: File;
|
||||
}) {
|
||||
|
@ -22,9 +25,12 @@ export default function Editor({
|
|||
if (monaco) {
|
||||
if (highlighter == null) {
|
||||
getHighlighter({
|
||||
themes: [darkPlus as any],
|
||||
themes: [darkPlus as any, lightPlus],
|
||||
langs: ["toml", shulkerscriptGrammar, mcfunctionGrammar],
|
||||
}).then((highlighter) => {
|
||||
highlighter.setTheme(
|
||||
theme === "dark" ? "dark-plus" : "light-plus"
|
||||
);
|
||||
setHighlighter(highlighter);
|
||||
});
|
||||
} else {
|
||||
|
@ -43,14 +49,13 @@ export default function Editor({
|
|||
}, [highlighter]);
|
||||
|
||||
return (
|
||||
<div className="editor">
|
||||
<MonacoEditor
|
||||
height="70vh"
|
||||
theme="vs-dark"
|
||||
path={fileName}
|
||||
defaultLanguage={file?.language}
|
||||
defaultValue={file?.content}
|
||||
/>
|
||||
</div>
|
||||
<MonacoEditor
|
||||
height="70vh"
|
||||
theme={theme === "dark" ? "dark-plus" : "light-plus"}
|
||||
path={fileName}
|
||||
defaultLanguage={file?.language}
|
||||
defaultValue={file?.content}
|
||||
wrapperProps={{ className: "editor" }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Directory, SetState } from "@components/Playground";
|
||||
import type { Directory, PlaygroundExplorerLang, SetState } from "@utils/playground";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
GoChevronDown as ChevDown,
|
||||
|
@ -6,11 +6,13 @@ import {
|
|||
} from "react-icons/go";
|
||||
|
||||
export default function FileView({
|
||||
lang,
|
||||
root,
|
||||
fileName,
|
||||
setSelectedFileName,
|
||||
className,
|
||||
}: {
|
||||
lang: PlaygroundExplorerLang;
|
||||
root: Directory;
|
||||
fileName: string;
|
||||
setSelectedFileName: SetState<string>;
|
||||
|
@ -18,7 +20,7 @@ export default function FileView({
|
|||
}) {
|
||||
return (
|
||||
<div className={className}>
|
||||
<h3>Explorer</h3>
|
||||
<h3>{lang.title}</h3>
|
||||
<div className="entries">
|
||||
{Object.entries(root.dirs ?? {}).map(([name, dir]) => {
|
||||
return (
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import type { PlaygroundHeaderLang } from "@utils/playground";
|
||||
import DropdownButton from "./DropdownButton";
|
||||
|
||||
export default function Header({
|
||||
lang,
|
||||
onSave,
|
||||
onReset,
|
||||
onBuild,
|
||||
onZip,
|
||||
}: {
|
||||
lang: PlaygroundHeaderLang;
|
||||
onSave: () => void;
|
||||
onReset: () => void;
|
||||
onBuild: () => void;
|
||||
|
@ -19,17 +22,17 @@ export default function Header({
|
|||
marginBottom: "0.5cm",
|
||||
}}
|
||||
>
|
||||
<h1 id="_top">Playground</h1>
|
||||
<h1 id="_top">{lang.title}</h1>
|
||||
<div className="buttons" style={{ height: "100%" }}>
|
||||
<DropdownButton
|
||||
style={{ height: "100%", marginRight: "0.5cm"}}
|
||||
visible={[["Save", onSave]]}
|
||||
options={[["Reset", onReset]]}
|
||||
visible={[[lang.buttons.save, onSave]]}
|
||||
options={[[lang.buttons.reset, onReset]]}
|
||||
/>
|
||||
<DropdownButton
|
||||
style={{ height: "100%" }}
|
||||
visible={[["Build", onBuild]]}
|
||||
options={[["Download zip", onZip]]}
|
||||
visible={[[lang.buttons.build, onBuild]]}
|
||||
options={[[lang.buttons.zip, onZip]]}
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
import PlaygroundComponent from "@components/Playground";
|
||||
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
|
||||
import type { PlaygroundLang } from "@utils/playground";
|
||||
|
||||
const lang: PlaygroundLang = {
|
||||
header: {
|
||||
title: "Spielplatz",
|
||||
buttons: {
|
||||
build: "Bauen",
|
||||
zip: "Als Zip herunterladen",
|
||||
save: "Speichern",
|
||||
reset: "Zurücksetzen",
|
||||
},
|
||||
},
|
||||
explorer: {
|
||||
title: "Dateien",
|
||||
},
|
||||
};
|
||||
---
|
||||
|
||||
<StarlightPage frontmatter={{ title: "Playground", template: "splash" }}>
|
||||
<PlaygroundComponent client:only="react" {lang} />
|
||||
</StarlightPage>
|
||||
|
||||
<style is:global>
|
||||
.pagination-links {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
|
@ -1,8 +1,30 @@
|
|||
---
|
||||
import PlaygroundComponent from '@components/Playground';
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
import PlaygroundComponent from "@components/Playground";
|
||||
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
|
||||
import type { PlaygroundLang } from "@utils/playground";
|
||||
|
||||
const lang: PlaygroundLang = {
|
||||
header: {
|
||||
title: "Playground",
|
||||
buttons: {
|
||||
build: "Build",
|
||||
zip: "Download zip",
|
||||
save: "Save",
|
||||
reset: "Reset",
|
||||
},
|
||||
},
|
||||
explorer: {
|
||||
title: "Explorer",
|
||||
},
|
||||
};
|
||||
---
|
||||
|
||||
<StarlightPage frontmatter={{ title: 'Playground', template: "splash" }}>
|
||||
<PlaygroundComponent client:only="react" />
|
||||
<StarlightPage frontmatter={{ title: "Playground", template: "splash" }}>
|
||||
<PlaygroundComponent client:only="react" {lang} />
|
||||
</StarlightPage>
|
||||
|
||||
<style is:global>
|
||||
.pagination-links {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
|
@ -13,6 +13,8 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: 0.5cm;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
.entries {
|
||||
button {
|
||||
|
@ -22,15 +24,15 @@
|
|||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #444444;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
color: var(--sl-color-text);
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #444444;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,3 +40,15 @@
|
|||
grid-area: editor;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme='light'] {
|
||||
.playground > .file-view .entries button {
|
||||
&:hover {
|
||||
background-color: var(--sl-color-gray-5);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: var(--sl-color-gray-6);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
export type PlaygroundLang = {
|
||||
header: PlaygroundHeaderLang;
|
||||
explorer: PlaygroundExplorerLang;
|
||||
};
|
||||
|
||||
export type PlaygroundHeaderLang = {
|
||||
title: string;
|
||||
buttons: {
|
||||
save: string;
|
||||
reset: string;
|
||||
build: string;
|
||||
zip: string;
|
||||
};
|
||||
};
|
||||
export type PlaygroundExplorerLang = {
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type File = {
|
||||
language?: string;
|
||||
content: string;
|
||||
};
|
||||
export type Directory = {
|
||||
dirs?: { [key: string]: Directory };
|
||||
files?: { [key: string]: File };
|
||||
};
|
||||
export type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
|
||||
|
||||
export function isPlaygroundPage(slug: string, lang?: string): boolean {
|
||||
return (
|
||||
slug === (!lang || lang == "en" ? "playground" : `${lang}/playground`)
|
||||
);
|
||||
}
|
|
@ -62,7 +62,10 @@ impl From<VFolder> for Directory {
|
|||
}
|
||||
|
||||
for (name, item) in value.get_files() {
|
||||
files.insert(name.to_string(), item.clone().into());
|
||||
files.insert(
|
||||
name.to_string(),
|
||||
File::from(item.clone()).correct_lang(name),
|
||||
);
|
||||
}
|
||||
|
||||
Self {
|
||||
|
@ -84,3 +87,16 @@ impl From<VFile> for File {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn correct_lang(self, name: &str) -> Self {
|
||||
let language = match name.split('.').last() {
|
||||
Some("shu") => Some("shulkerscript".to_string()),
|
||||
Some("mcfunction") => Some("mcfunction".to_string()),
|
||||
Some("json" | "mcmeta") => Some("json".to_string()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Self { language, ..self }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,10 +42,10 @@ pub fn compile(root_dir: JsValue) -> JsValue {
|
|||
|
||||
/// Returns a base64 encoded zip file containing the compiled datapack.
|
||||
#[wasm_bindgen(js_name = compileZip)]
|
||||
pub fn compile_zip(root_dir: JsValue) -> String {
|
||||
pub fn compile_zip(root_dir: JsValue) -> Option<String> {
|
||||
let root_dir = VFolder::from(serde_wasm_bindgen::from_value::<Directory>(root_dir).unwrap());
|
||||
|
||||
let datapack = _compile(&root_dir).unwrap();
|
||||
let datapack = _compile(&root_dir).ok()?;
|
||||
|
||||
let mut buffer = Cursor::new(Vec::new());
|
||||
let mut writer = ZipWriter::new(&mut buffer);
|
||||
|
@ -70,7 +70,7 @@ pub fn compile_zip(root_dir: JsValue) -> String {
|
|||
|
||||
writer.finish().unwrap();
|
||||
|
||||
BASE64_STANDARD.encode(buffer.into_inner())
|
||||
Some(BASE64_STANDARD.encode(buffer.into_inner()))
|
||||
}
|
||||
|
||||
fn _compile(root_dir: &VFolder) -> Result<VFolder> {
|
||||
|
|
Loading…
Reference in New Issue