diff --git a/src/app/App.tsx b/src/app/App.tsx
new file mode 100644
index 00000000..12f90202
--- /dev/null
+++ b/src/app/App.tsx
@@ -0,0 +1,28 @@
+import type { RouterOnChangeArgs } from 'preact-router'
+import { Router } from 'preact-router'
+import '../styles/global.css'
+import '../styles/nodes.css'
+import { Analytics } from './Analytics'
+import { Header } from './components'
+import { Category, Changelog, Generator, Home, Project, Sounds } from './pages'
+import { cleanUrl } from './Utils'
+
+export function App() {
+ const changeRoute = (e: RouterOnChangeArgs) => {
+ // Needs a timeout to ensure the title is set correctly
+ setTimeout(() => Analytics.pageview(cleanUrl(e.url)))
+ }
+
+ return <>
+
+
+
+
+
+
+
+
+
+
+ >
+}
diff --git a/src/app/Locales.ts b/src/app/Locales.ts
deleted file mode 100644
index 2aee9f80..00000000
--- a/src/app/Locales.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import config from '../config.json'
-import English from '../locales/en.json'
-
-export type Localize = (key: string, ...params: string[]) => string
-
-interface Locale {
- [key: string]: string
-}
-
-export const Locales: {
- [key: string]: Locale,
-} = {
- fallback: English,
-}
-
-function resolveLocaleParams(value: string, params?: string[]): string {
- return value.replace(/%\d+%/g, match => {
- const index = parseInt(match.slice(1, -1))
- return params?.[index] !== undefined ? params[index] : match
- })
-}
-
-export function locale(language: string, key: string, ...params: string[]): string {
- const value: string | undefined = Locales[language]?.[key]
- ?? Locales.en?.[key] ?? Locales.fallback[key] ?? key
- return resolveLocaleParams(value, params)
-}
-
-export async function loadLocale(language: string) {
- const langConfig = config.languages.find(lang => lang.code === language)
- if (!langConfig) return
- const data = await import(`../locales/${language}.json`)
- const schema = langConfig.schemas !== false
- && await import(`../../node_modules/@mcschema/locales/src/${language}.json`)
- Locales[language] = { ...data.default, ...schema.default }
-}
diff --git a/src/app/Main.tsx b/src/app/Main.tsx
index c7070ac8..d1478fee 100644
--- a/src/app/Main.tsx
+++ b/src/app/Main.tsx
@@ -1,93 +1,21 @@
import { render } from 'preact'
-import type { RouterOnChangeArgs } from 'preact-router'
-import { getCurrentUrl, Router } from 'preact-router'
-import { useCallback, useEffect, useState } from 'preact/hooks'
-import config from '../config.json'
import '../styles/global.css'
import '../styles/nodes.css'
-import { Analytics } from './Analytics'
-import { Header } from './components'
-import { loadLocale, locale, Locales } from './Locales'
-import { Category, Changelog, Generator, Home, Sounds } from './pages'
-import type { VersionId } from './services'
-import { VersionIds } from './services'
-import { Store } from './Store'
-import { cleanUrl, getSearchParams, setSeachParams } from './Utils'
-
-const VERSIONS_IN_TITLE = 3
+import { App } from './App'
+import { LocaleProvider, ProjectProvider, ThemeProvider, TitleProvider, VersionProvider } from './contexts'
function Main() {
- const [lang, setLanguage] = useState('none')
- const changeLanguage = async (language: string) => {
- if (!Locales[language]) {
- await loadLocale(language)
- }
- Analytics.setLanguage(language)
- Store.setLanguage(language)
- setLanguage(language)
- }
- useEffect(() => {
- (async () => {
- const target = Store.getLanguage()
- await Promise.all([
- loadLocale('en'),
- ...(target !== 'en' ? [loadLocale(target)] : []),
- ])
- setLanguage(target)
- })()
- }, [])
-
- const [theme, setTheme] = useState(Store.getTheme())
- const changeTheme = (theme: string) => {
- Analytics.setTheme(theme)
- Store.setTheme(theme)
- setTheme(theme)
- }
- useEffect(() => {
- document.documentElement.setAttribute('data-theme', theme)
- }, [theme])
-
- const searchParams = getSearchParams(getCurrentUrl())
- const targetVersion = searchParams.get('version')
- const [version, setVersion] = useState(Store.getVersion())
- const changeVersion = useCallback((version: VersionId) => {
- if (getSearchParams(getCurrentUrl()).has('version')) {
- setSeachParams({ version })
- }
- Analytics.setVersion(version)
- Store.setVersion(version)
- setVersion(version)
- }, [targetVersion])
- useEffect(() => {
- if (VersionIds.includes(targetVersion as VersionId) && version !== targetVersion) {
- setVersion(targetVersion as VersionId)
- }
- }, [version, targetVersion])
-
- const [title, setTitle] = useState(locale(lang, 'title.home'))
- const changeTitle = (title: string, versions?: VersionId[]) => {
- versions ??= config.versions.map(v => v.id as VersionId)
- const titleVersions = versions.slice(versions.length - VERSIONS_IN_TITLE)
- document.title = `${title} Minecraft ${titleVersions.join(', ')}`
- setTitle(title)
- }
-
- const changeRoute = (e: RouterOnChangeArgs) => {
- // Needs a timeout to ensure the title is set correctly
- setTimeout(() => Analytics.pageview(cleanUrl(e.url)))
- }
-
- return <>
-
-
-
-
-
-
-
-
-
- >
+ return
+
+
+
+
+
+
+
+
+
+
}
render(, document.body)
diff --git a/src/app/Store.ts b/src/app/Store.ts
index 4a79ebb4..b56cf877 100644
--- a/src/app/Store.ts
+++ b/src/app/Store.ts
@@ -1,3 +1,5 @@
+import type { Project } from './contexts'
+import { DRAFT_PROJECT } from './contexts'
import type { VersionId } from './services'
import { VersionIds } from './services'
@@ -8,6 +10,7 @@ export namespace Store {
export const ID_INDENT = 'indentation'
export const ID_FORMAT = 'output_format'
export const ID_SOUNDS_VERSION = 'minecraft_sounds_version'
+ export const ID_PROJECTS = 'misode_projects'
export function getLanguage() {
return localStorage.getItem(ID_LANGUAGE) ?? 'en'
@@ -37,6 +40,14 @@ export namespace Store {
return localStorage.getItem(ID_SOUNDS_VERSION) ?? 'latest'
}
+ export function getProjects(): Project[] {
+ const projects = localStorage.getItem(ID_PROJECTS)
+ if (projects) {
+ return JSON.parse(projects) as Project[]
+ }
+ return [DRAFT_PROJECT]
+ }
+
export function setLanguage(language: string | undefined) {
if (language) localStorage.setItem(ID_LANGUAGE, language)
}
@@ -60,4 +71,8 @@ export namespace Store {
export function setSoundsVersion(version: string | undefined) {
if (version) localStorage.setItem(ID_SOUNDS_VERSION, version)
}
+
+ export function setProjects(projects: Project[] | undefined) {
+ if (projects) localStorage.setItem(ID_PROJECTS, JSON.stringify(projects))
+ }
}
diff --git a/src/app/components/BtnMenu.tsx b/src/app/components/BtnMenu.tsx
index 44c58349..c2294954 100644
--- a/src/app/components/BtnMenu.tsx
+++ b/src/app/components/BtnMenu.tsx
@@ -8,14 +8,15 @@ interface BtnMenuProps extends JSX.HTMLAttributes {
label?: string,
relative?: boolean,
tooltip?: string,
+ tooltipLoc?: 'se' | 'sw' | 'nw',
children: ComponentChildren,
}
export function BtnMenu(props: BtnMenuProps) {
- const { icon, label, relative, tooltip, children } = props
+ const { icon, label, relative, tooltip, tooltipLoc, children } = props
const [active, setActive] = useFocus()
return