add localisation and theming for playground
This commit is contained in:
parent
1a5dcd24bc
commit
a02af4f90c
182
astro.config.mjs
182
astro.config.mjs
|
@ -1,78 +1,114 @@
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from "astro/config";
|
||||||
import starlight from '@astrojs/starlight';
|
import starlight from "@astrojs/starlight";
|
||||||
import react from "@astrojs/react";
|
import react from "@astrojs/react";
|
||||||
import starlightLinksValidator from "starlight-links-validator";
|
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
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [react(), starlight({
|
integrations: [
|
||||||
title: 'ShulkerScript',
|
react(),
|
||||||
logo: {
|
starlight({
|
||||||
src: './src/assets/logo.webp',
|
title: "ShulkerScript",
|
||||||
alt: 'ShulkerScript Logo'
|
logo: {
|
||||||
},
|
src: "./src/assets/logo.webp",
|
||||||
favicon: '/favicon.ico',
|
alt: "ShulkerScript Logo",
|
||||||
description: 'A simple and powerful scripting language for Minecraft datapacks.',
|
},
|
||||||
social: {
|
favicon: "/favicon.ico",
|
||||||
github: 'https://github.com/moritz-hoelting/shulkerscript-cli'
|
description:
|
||||||
},
|
"A simple and powerful scripting language for Minecraft datapacks.",
|
||||||
tableOfContents: {
|
social: {
|
||||||
minHeadingLevel: 1,
|
github: "https://github.com/moritz-hoelting/shulkerscript-cli",
|
||||||
maxHeadingLevel: 3
|
},
|
||||||
},
|
tableOfContents: {
|
||||||
defaultLocale: 'root',
|
minHeadingLevel: 1,
|
||||||
locales: {
|
maxHeadingLevel: 3,
|
||||||
root: {
|
},
|
||||||
label: 'English',
|
defaultLocale: "root",
|
||||||
lang: 'en'
|
locales: {
|
||||||
},
|
root: {
|
||||||
de: {
|
label: "English",
|
||||||
label: 'Deutsch',
|
lang: "en",
|
||||||
lang: 'de'
|
},
|
||||||
}
|
de: {
|
||||||
},
|
label: "Deutsch",
|
||||||
editLink: {
|
lang: "de",
|
||||||
baseUrl: 'https://github.com/moritz-hoelting/shulkerscript-webpage/edit/main'
|
},
|
||||||
},
|
},
|
||||||
customCss: ['./src/styles/style.css'],
|
editLink: {
|
||||||
plugins: [starlightLinksValidator({
|
baseUrl:
|
||||||
errorOnFallbackPages: false
|
"https://github.com/moritz-hoelting/shulkerscript-webpage/edit/main",
|
||||||
})],
|
},
|
||||||
expressiveCode: {
|
customCss: ["./src/styles/style.css"],
|
||||||
shiki: shikiConfig
|
plugins: [
|
||||||
},
|
starlightUtils({
|
||||||
components: {
|
navLinks: {
|
||||||
PageTitle: './src/components/override/PageTitle.astro',
|
leading: {
|
||||||
ContentPanel: './src/components/override/ContentPanel.astro',
|
useSidebarLabelled: "leadingNavLinks",
|
||||||
},
|
},
|
||||||
sidebar: [{
|
},
|
||||||
label: 'Guides',
|
}),
|
||||||
autogenerate: {
|
starlightLinksValidator({
|
||||||
directory: 'guides'
|
errorOnFallbackPages: false,
|
||||||
},
|
}),
|
||||||
translations: {
|
],
|
||||||
de: 'Anleitungen'
|
expressiveCode: {
|
||||||
}
|
shiki: shikiConfig,
|
||||||
}, {
|
},
|
||||||
label: 'Roadmap',
|
components: {
|
||||||
link: '/roadmap',
|
PageTitle: "./src/components/override/PageTitle.astro",
|
||||||
translations: {
|
ContentPanel: "./src/components/override/ContentPanel.astro",
|
||||||
de: 'Zukunftspläne'
|
},
|
||||||
}
|
sidebar: [
|
||||||
}, {
|
{
|
||||||
label: 'Reference',
|
label: "leadingNavLinks",
|
||||||
autogenerate: {
|
items: navLinks,
|
||||||
directory: 'reference'
|
},
|
||||||
},
|
{
|
||||||
collapsed: true,
|
label: "Guides",
|
||||||
translations: {
|
autogenerate: {
|
||||||
de: 'Referenz'
|
directory: "guides",
|
||||||
},
|
},
|
||||||
badge: {
|
translations: {
|
||||||
text: 'WIP',
|
de: "Anleitungen",
|
||||||
variant: 'caution'
|
},
|
||||||
}
|
},
|
||||||
}]
|
{
|
||||||
})]
|
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",
|
"@astrojs/starlight": "^0.24.2",
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
|
"@lorenzo_lewis/starlight-utils": "^0.1.1",
|
||||||
"@monaco-editor/react": "^4.6.0",
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@mui/icons-material": "^5.15.20",
|
"@mui/icons-material": "^5.15.20",
|
||||||
"@mui/material": "^5.15.20",
|
"@mui/material": "^5.15.20",
|
||||||
|
|
|
@ -20,6 +20,9 @@ dependencies:
|
||||||
'@emotion/styled':
|
'@emotion/styled':
|
||||||
specifier: ^11.11.5
|
specifier: ^11.11.5
|
||||||
version: 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.3)(react@18.3.1)
|
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':
|
'@monaco-editor/react':
|
||||||
specifier: ^4.6.0
|
specifier: ^4.6.0
|
||||||
version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1)(react@18.3.1)
|
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
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
dev: false
|
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:
|
/@mdx-js/mdx@3.0.1:
|
||||||
resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==}
|
resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2161,6 +2177,13 @@ packages:
|
||||||
resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==}
|
resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==}
|
||||||
dev: false
|
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:
|
/astring@1.8.6:
|
||||||
resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
|
resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -2175,6 +2198,20 @@ packages:
|
||||||
rehype-expressive-code: 0.35.3
|
rehype-expressive-code: 0.35.3
|
||||||
dev: false
|
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):
|
/astro@4.10.2(sass@1.77.6)(typescript@5.4.5):
|
||||||
resolution: {integrity: sha512-SBdkoOanPsxKlKVU4uu/XG0G7NYAFoqmfBtq9SPMJ34B7Hr1MxVdEugERs8IwYN6UaxdDVcqA++9PvH6Onq2cg==}
|
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'}
|
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'}
|
engines: {node: '>=8'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/pathe@1.1.2:
|
||||||
|
resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/periscopic@3.1.0:
|
/periscopic@3.1.0:
|
||||||
resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
|
resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4639,6 +4680,17 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
picomatch: 2.3.1
|
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:
|
/regenerator-runtime@0.14.1:
|
||||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -5079,6 +5131,11 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/source-map@0.6.1:
|
||||||
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/source-map@0.7.4:
|
/source-map@0.7.4:
|
||||||
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
|
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
@ -5279,6 +5336,10 @@ packages:
|
||||||
b4a: 1.6.6
|
b4a: 1.6.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/tiny-invariant@1.3.3:
|
||||||
|
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tm-themes@1.4.3:
|
/tm-themes@1.4.3:
|
||||||
resolution: {integrity: sha512-nsUGwktLaWFMyKw2e/hNyDmcTIO+ue6Q2Mvb8L7WQkKSCflOm2TjGSspcJw6q7hi4QxQQNTuGICyvQN1UqtunQ==}
|
resolution: {integrity: sha512-nsUGwktLaWFMyKw2e/hNyDmcTIO+ue6Q2Mvb8L7WQkKSCflOm2TjGSspcJw6q7hi4QxQQNTuGICyvQN1UqtunQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -5319,7 +5380,6 @@ packages:
|
||||||
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
|
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
|
||||||
|
|
||||||
/tunnel-agent@0.6.0:
|
/tunnel-agent@0.6.0:
|
||||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
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 { useMonaco, type Monaco } from "@monaco-editor/react";
|
||||||
import { useImmer, type Updater } from "use-immer";
|
import { useImmer, type Updater } from "use-immer";
|
||||||
|
|
||||||
|
@ -14,16 +14,7 @@ import initWasm, {
|
||||||
compile,
|
compile,
|
||||||
compileZip,
|
compileZip,
|
||||||
} from "@wasm/webcompiler/pkg/webcompiler";
|
} from "@wasm/webcompiler/pkg/webcompiler";
|
||||||
|
import type { Directory, File, PlaygroundLang } from "@utils/playground";
|
||||||
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>>;
|
|
||||||
|
|
||||||
const FILE_STORAGE_KEY = "playground-files";
|
const FILE_STORAGE_KEY = "playground-files";
|
||||||
const DEFAULT_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) => {
|
initWasm().catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
|
@ -55,32 +46,40 @@ export default function Playground() {
|
||||||
getStorageOrDefault(FILE_STORAGE_KEY, DEFAULT_FILES) as Directory
|
getStorageOrDefault(FILE_STORAGE_KEY, DEFAULT_FILES) as Directory
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [theme, setTheme] = useState<"light" | "dark">("dark");
|
||||||
const [fileName, setFileName] = useState("src/main.shu");
|
const [fileName, setFileName] = useState("src/main.shu");
|
||||||
const file = getFile(rootDir, fileName);
|
const file = getFile(rootDir, fileName);
|
||||||
|
|
||||||
const onBuild = () => {
|
const onBuild = () => {
|
||||||
if (monaco) {
|
if (monaco) {
|
||||||
const dist = JSON.parse(
|
const compiled = compile(getFiles(monaco));
|
||||||
JSON.stringify(compile(getFiles(monaco)), jsonReplacer)
|
if (compiled) {
|
||||||
);
|
const dist = JSON.parse(JSON.stringify(compiled, jsonReplacer));
|
||||||
const withRoot = {
|
const withRoot = {
|
||||||
dirs: {
|
dirs: {
|
||||||
dist: dist,
|
dist: dist,
|
||||||
},
|
},
|
||||||
} as Directory;
|
} as Directory;
|
||||||
loadFiles(monaco, updateRootDir, withRoot);
|
loadFiles(monaco, updateRootDir, withRoot);
|
||||||
|
} else {
|
||||||
|
alert("Compilation failed");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error("monaco has not loaded");
|
console.error("monaco has not loaded");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onZip = () => {
|
const onZip = () => {
|
||||||
if (monaco) {
|
if (monaco) {
|
||||||
const data =
|
const zipped = compileZip(getFiles(monaco));
|
||||||
"data:application/zip;base64," + compileZip(getFiles(monaco));
|
if (zipped) {
|
||||||
const a = document.createElement("a");
|
const data = "data:application/zip;base64," + zipped;
|
||||||
a.href = data;
|
const a = document.createElement("a");
|
||||||
a.download = "shulkerscript-pack.zip";
|
a.href = data;
|
||||||
a.click();
|
a.download = "shulkerscript-pack.zip";
|
||||||
|
a.click();
|
||||||
|
} else {
|
||||||
|
alert("Compilation failed");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error("monaco has not loaded");
|
console.error("monaco has not loaded");
|
||||||
}
|
}
|
||||||
|
@ -125,6 +124,37 @@ export default function Playground() {
|
||||||
}
|
}
|
||||||
}, [monaco]);
|
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<main
|
<main
|
||||||
|
@ -136,6 +166,7 @@ export default function Playground() {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Header
|
<Header
|
||||||
|
lang={lang.header}
|
||||||
onSave={onSave}
|
onSave={onSave}
|
||||||
onReset={onReset}
|
onReset={onReset}
|
||||||
onBuild={onBuild}
|
onBuild={onBuild}
|
||||||
|
@ -146,8 +177,13 @@ export default function Playground() {
|
||||||
root={rootDir}
|
root={rootDir}
|
||||||
fileName={fileName}
|
fileName={fileName}
|
||||||
setSelectedFileName={setFileName}
|
setSelectedFileName={setFileName}
|
||||||
|
lang={lang.explorer}
|
||||||
|
/>
|
||||||
|
<Editor
|
||||||
|
fileName={fileName}
|
||||||
|
file={file ?? undefined}
|
||||||
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
<Editor fileName={fileName} file={file ?? undefined} />
|
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
---
|
---
|
||||||
import type { Props } from "@astrojs/starlight/props";
|
import type { Props } from "@astrojs/starlight/props";
|
||||||
import Default from "@astrojs/starlight/components/ContentPanel.astro";
|
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 type { Props } from "@astrojs/starlight/props";
|
||||||
import Default from "@astrojs/starlight/components/PageTitle.astro";
|
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 MonacoEditor, { useMonaco } from "@monaco-editor/react";
|
||||||
import { getHighlighter, type Highlighter } from "shiki";
|
import { getHighlighter, type Highlighter } from "shiki";
|
||||||
import { shikiToMonaco } from "@shikijs/monaco";
|
import { shikiToMonaco } from "@shikijs/monaco";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import darkPlus from "tm-themes/themes/dark-plus.json";
|
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 { shulkerscriptGrammar } from "@utils/shulkerscript-grammar";
|
||||||
import { mcfunctionGrammar } from "@utils/mcfunction-grammar";
|
import { mcfunctionGrammar } from "@utils/mcfunction-grammar";
|
||||||
|
|
||||||
export default function Editor({
|
export default function Editor({
|
||||||
|
theme,
|
||||||
fileName,
|
fileName,
|
||||||
file,
|
file,
|
||||||
}: {
|
}: {
|
||||||
|
theme: "light" | "dark";
|
||||||
fileName: string;
|
fileName: string;
|
||||||
file?: File;
|
file?: File;
|
||||||
}) {
|
}) {
|
||||||
|
@ -22,9 +25,12 @@ export default function Editor({
|
||||||
if (monaco) {
|
if (monaco) {
|
||||||
if (highlighter == null) {
|
if (highlighter == null) {
|
||||||
getHighlighter({
|
getHighlighter({
|
||||||
themes: [darkPlus as any],
|
themes: [darkPlus as any, lightPlus],
|
||||||
langs: ["toml", shulkerscriptGrammar, mcfunctionGrammar],
|
langs: ["toml", shulkerscriptGrammar, mcfunctionGrammar],
|
||||||
}).then((highlighter) => {
|
}).then((highlighter) => {
|
||||||
|
highlighter.setTheme(
|
||||||
|
theme === "dark" ? "dark-plus" : "light-plus"
|
||||||
|
);
|
||||||
setHighlighter(highlighter);
|
setHighlighter(highlighter);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -43,14 +49,13 @@ export default function Editor({
|
||||||
}, [highlighter]);
|
}, [highlighter]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="editor">
|
<MonacoEditor
|
||||||
<MonacoEditor
|
height="70vh"
|
||||||
height="70vh"
|
theme={theme === "dark" ? "dark-plus" : "light-plus"}
|
||||||
theme="vs-dark"
|
path={fileName}
|
||||||
path={fileName}
|
defaultLanguage={file?.language}
|
||||||
defaultLanguage={file?.language}
|
defaultValue={file?.content}
|
||||||
defaultValue={file?.content}
|
wrapperProps={{ className: "editor" }}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
GoChevronDown as ChevDown,
|
GoChevronDown as ChevDown,
|
||||||
|
@ -6,11 +6,13 @@ import {
|
||||||
} from "react-icons/go";
|
} from "react-icons/go";
|
||||||
|
|
||||||
export default function FileView({
|
export default function FileView({
|
||||||
|
lang,
|
||||||
root,
|
root,
|
||||||
fileName,
|
fileName,
|
||||||
setSelectedFileName,
|
setSelectedFileName,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
|
lang: PlaygroundExplorerLang;
|
||||||
root: Directory;
|
root: Directory;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
setSelectedFileName: SetState<string>;
|
setSelectedFileName: SetState<string>;
|
||||||
|
@ -18,7 +20,7 @@ export default function FileView({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<h3>Explorer</h3>
|
<h3>{lang.title}</h3>
|
||||||
<div className="entries">
|
<div className="entries">
|
||||||
{Object.entries(root.dirs ?? {}).map(([name, dir]) => {
|
{Object.entries(root.dirs ?? {}).map(([name, dir]) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
|
import type { PlaygroundHeaderLang } from "@utils/playground";
|
||||||
import DropdownButton from "./DropdownButton";
|
import DropdownButton from "./DropdownButton";
|
||||||
|
|
||||||
export default function Header({
|
export default function Header({
|
||||||
|
lang,
|
||||||
onSave,
|
onSave,
|
||||||
onReset,
|
onReset,
|
||||||
onBuild,
|
onBuild,
|
||||||
onZip,
|
onZip,
|
||||||
}: {
|
}: {
|
||||||
|
lang: PlaygroundHeaderLang;
|
||||||
onSave: () => void;
|
onSave: () => void;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
onBuild: () => void;
|
onBuild: () => void;
|
||||||
|
@ -19,17 +22,17 @@ export default function Header({
|
||||||
marginBottom: "0.5cm",
|
marginBottom: "0.5cm",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<h1 id="_top">Playground</h1>
|
<h1 id="_top">{lang.title}</h1>
|
||||||
<div className="buttons" style={{ height: "100%" }}>
|
<div className="buttons" style={{ height: "100%" }}>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
style={{ height: "100%", marginRight: "0.5cm"}}
|
style={{ height: "100%", marginRight: "0.5cm"}}
|
||||||
visible={[["Save", onSave]]}
|
visible={[[lang.buttons.save, onSave]]}
|
||||||
options={[["Reset", onReset]]}
|
options={[[lang.buttons.reset, onReset]]}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
style={{ height: "100%" }}
|
style={{ height: "100%" }}
|
||||||
visible={[["Build", onBuild]]}
|
visible={[[lang.buttons.build, onBuild]]}
|
||||||
options={[["Download zip", onZip]]}
|
options={[[lang.buttons.zip, onZip]]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</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 PlaygroundComponent from "@components/Playground";
|
||||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
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" }}>
|
<StarlightPage frontmatter={{ title: "Playground", template: "splash" }}>
|
||||||
<PlaygroundComponent client:only="react" />
|
<PlaygroundComponent client:only="react" {lang} />
|
||||||
</StarlightPage>
|
</StarlightPage>
|
||||||
|
|
||||||
|
<style is:global>
|
||||||
|
.pagination-links {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -13,6 +13,8 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-right: 0.5cm;
|
margin-right: 0.5cm;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
.entries {
|
.entries {
|
||||||
button {
|
button {
|
||||||
|
@ -22,15 +24,15 @@
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #444444;
|
||||||
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
color: var(--sl-color-text);
|
color: var(--sl-color-text);
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #444444;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,3 +40,15 @@
|
||||||
grid-area: editor;
|
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() {
|
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 {
|
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.
|
/// Returns a base64 encoded zip file containing the compiled datapack.
|
||||||
#[wasm_bindgen(js_name = compileZip)]
|
#[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 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 buffer = Cursor::new(Vec::new());
|
||||||
let mut writer = ZipWriter::new(&mut buffer);
|
let mut writer = ZipWriter::new(&mut buffer);
|
||||||
|
@ -70,7 +70,7 @@ pub fn compile_zip(root_dir: JsValue) -> String {
|
||||||
|
|
||||||
writer.finish().unwrap();
|
writer.finish().unwrap();
|
||||||
|
|
||||||
BASE64_STANDARD.encode(buffer.into_inner())
|
Some(BASE64_STANDARD.encode(buffer.into_inner()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _compile(root_dir: &VFolder) -> Result<VFolder> {
|
fn _compile(root_dir: &VFolder) -> Result<VFolder> {
|
||||||
|
|
Loading…
Reference in New Issue