mirror of
https://github.com/misode/misode.github.io.git
synced 2026-05-05 23:11:47 +00:00
Add generator quick switcher
This commit is contained in:
+1
-1
@@ -62,7 +62,7 @@ function Main() {
|
||||
}
|
||||
|
||||
return <>
|
||||
<Header {...{lang, title, theme, language: lang, changeLanguage, changeTheme}} />
|
||||
<Header {...{lang, title, version, theme, language: lang, changeLanguage, changeTheme}} />
|
||||
<Router onChange={changeRoute}>
|
||||
<Home path="/" {...{lang, changeTitle}} />
|
||||
<FieldSettings path="/settings/fields" {...{lang, changeTitle}} />
|
||||
|
||||
+2
-2
@@ -11,10 +11,10 @@ export type VersionId = typeof VersionIds[number]
|
||||
|
||||
export type BlockStateRegistry = {
|
||||
[block: string]: {
|
||||
properties: {
|
||||
properties?: {
|
||||
[key: string]: string[],
|
||||
},
|
||||
default: {
|
||||
default?: {
|
||||
[key: string]: string,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { getCurrentUrl, Link } from 'preact-router'
|
||||
import { getCurrentUrl, Link, route } from 'preact-router'
|
||||
import { Btn, BtnMenu, Icons, Octicon } from '.'
|
||||
import config from '../../config.json'
|
||||
import { locale } from '../Locales'
|
||||
import type { VersionId } from '../Schemas'
|
||||
import { checkVersion } from '../Schemas'
|
||||
import { cleanUrl, getGenerator } from '../Utils'
|
||||
|
||||
const Themes: Record<string, keyof typeof Octicon> = {
|
||||
@@ -13,21 +15,27 @@ const Themes: Record<string, keyof typeof Octicon> = {
|
||||
type HeaderProps = {
|
||||
lang: string,
|
||||
title: string,
|
||||
version: VersionId,
|
||||
theme: string,
|
||||
changeTheme: (theme: string) => unknown,
|
||||
language: string,
|
||||
changeLanguage: (language: string) => unknown,
|
||||
}
|
||||
export function Header({ lang, title, theme, changeTheme, language, changeLanguage }: HeaderProps) {
|
||||
export function Header({ lang, title, version, theme, changeTheme, language, changeLanguage }: HeaderProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
const category = getGenerator(getCurrentUrl())?.category
|
||||
const gen = getGenerator(getCurrentUrl())
|
||||
|
||||
return <header>
|
||||
<div class="header-title">
|
||||
<Link class="home-link" href={typeof category === 'string' ? cleanUrl(category) : '/'}>
|
||||
{Icons.home}
|
||||
</Link>
|
||||
<div class="title">
|
||||
<Link class="home-link" href="/">{Icons.home}</Link>
|
||||
<h2>{title}</h2>
|
||||
{gen && <BtnMenu icon="chevron_down">
|
||||
{config.generators
|
||||
.filter(g => g.category === gen?.category && checkVersion(version, g.minVersion))
|
||||
.map(g =>
|
||||
<Btn label={loc(g.id)} active={g.id === gen.id} onClick={() => route(cleanUrl(g.url))} />
|
||||
)}
|
||||
</BtnMenu>}
|
||||
</div>
|
||||
<nav>
|
||||
<ul>
|
||||
|
||||
@@ -2,6 +2,7 @@ export const Octicon = {
|
||||
archive: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.75 2.5a.25.25 0 00-.25.25v1.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-1.5a.25.25 0 00-.25-.25H1.75zM0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v1.5A1.75 1.75 0 0114.25 6H1.75A1.75 1.75 0 010 4.25v-1.5zM1.75 7a.75.75 0 01.75.75v5.5c0 .138.112.25.25.25h10.5a.25.25 0 00.25-.25v-5.5a.75.75 0 111.5 0v5.5A1.75 1.75 0 0113.25 15H2.75A1.75 1.75 0 011 13.25v-5.5A.75.75 0 011.75 7zm4.5 1a.75.75 0 000 1.5h3.5a.75.75 0 100-1.5h-3.5z"></path></svg>,
|
||||
arrow_left: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.78 12.53a.75.75 0 01-1.06 0L2.47 8.28a.75.75 0 010-1.06l4.25-4.25a.75.75 0 011.06 1.06L4.81 7h7.44a.75.75 0 010 1.5H4.81l2.97 2.97a.75.75 0 010 1.06z"></path></svg>,
|
||||
arrow_right: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8.22 2.97a.75.75 0 011.06 0l4.25 4.25a.75.75 0 010 1.06l-4.25 4.25a.75.75 0 01-1.06-1.06l2.97-2.97H3.75a.75.75 0 010-1.5h7.44L8.22 4.03a.75.75 0 010-1.06z"></path></svg>,
|
||||
chevron_down: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M12.78 6.22a.75.75 0 010 1.06l-4.25 4.25a.75.75 0 01-1.06 0L3.22 7.28a.75.75 0 011.06-1.06L8 9.94l3.72-3.72a.75.75 0 011.06 0z"></path></svg>,
|
||||
chevron_right: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M6.22 3.22a.75.75 0 011.06 0l4.25 4.25a.75.75 0 010 1.06l-4.25 4.25a.75.75 0 01-1.06-1.06L9.94 8 6.22 4.28a.75.75 0 010-1.06z"></path></svg>,
|
||||
clippy: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M5.75 1a.75.75 0 00-.75.75v3c0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75v-3a.75.75 0 00-.75-.75h-4.5zm.75 3V2.5h3V4h-3zm-2.874-.467a.75.75 0 00-.752-1.298A1.75 1.75 0 002 3.75v9.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 13.25v-9.5a1.75 1.75 0 00-.874-1.515.75.75 0 10-.752 1.298.25.25 0 01.126.217v9.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-9.5a.25.25 0 01.126-.217z"></path></svg>,
|
||||
code: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.72 3.22a.75.75 0 011.06 1.06L2.06 8l3.72 3.72a.75.75 0 11-1.06 1.06L.47 8.53a.75.75 0 010-1.06l4.25-4.25zm6.56 0a.75.75 0 10-1.06 1.06L13.94 8l-3.72 3.72a.75.75 0 101.06 1.06l4.25-4.25a.75.75 0 000-1.06l-4.25-4.25z"></path></svg>,
|
||||
|
||||
+15
-16
@@ -26,22 +26,21 @@ export function Generator({ lang, changeTitle, version, onChangeVersion }: Gener
|
||||
const dismissError = (error: string) => {
|
||||
setErrors(errors.filter(e => e !== error))
|
||||
}
|
||||
const [errorBoundary] = useErrorBoundary()
|
||||
const [errorBoundary, errorRetry] = useErrorBoundary()
|
||||
if (errorBoundary) {
|
||||
return <main><ErrorPanel error={`Something went wrong rendering the generator: ${errorBoundary.message}`}/></main>
|
||||
return <main><ErrorPanel error={`Something went wrong rendering the generator: ${errorBoundary.message}`} onDismiss={errorRetry} /></main>
|
||||
}
|
||||
|
||||
const generator = getGenerator(getCurrentUrl())
|
||||
if (!generator) {
|
||||
return <main><ErrorPanel error="Cannot find generator" /></main>
|
||||
const gen = getGenerator(getCurrentUrl())
|
||||
if (!gen) {
|
||||
return <main><ErrorPanel error={`Cannot find generator "${getCurrentUrl()}"`} /></main>
|
||||
}
|
||||
|
||||
const minVersion = generator.minVersion ?? '1.15'
|
||||
const allowedVersions = config.versions
|
||||
.filter(v => checkVersion(v.id, minVersion))
|
||||
.filter(v => checkVersion(v.id, gen.minVersion))
|
||||
.map(v => v.id as VersionId)
|
||||
|
||||
changeTitle(loc('title.generator', loc(generator.id)), allowedVersions)
|
||||
changeTitle(loc('title.generator', loc(gen.id)), allowedVersions)
|
||||
|
||||
const [model, setModel] = useState<DataModel | null>(null)
|
||||
const [blockStates, setBlockStates] = useState<BlockStateRegistry | null>(null)
|
||||
@@ -49,10 +48,10 @@ export function Generator({ lang, changeTitle, version, onChangeVersion }: Gener
|
||||
setModel(null)
|
||||
getBlockStates(version)
|
||||
.then(b => setBlockStates(b))
|
||||
getModel(version, generator.id)
|
||||
getModel(version, gen.id)
|
||||
.then(m => setModel(m))
|
||||
.catch(e => { console.error(e); addError(e.message) })
|
||||
}, [version, generator.id])
|
||||
}, [version, gen.id])
|
||||
|
||||
const reset = () => {
|
||||
Analytics.generatorEvent('reset')
|
||||
@@ -91,7 +90,7 @@ export function Generator({ lang, changeTitle, version, onChangeVersion }: Gener
|
||||
getCollections(version)
|
||||
.then(collections => {
|
||||
const terms = (presetFilter ?? '').trim().split(' ')
|
||||
const presets = collections.get(generator.id)
|
||||
const presets = collections.get(gen.id)
|
||||
.map(p => p.slice(10))
|
||||
.filter(p => terms.every(t => p.includes(t)))
|
||||
if (presets) {
|
||||
@@ -99,11 +98,11 @@ export function Generator({ lang, changeTitle, version, onChangeVersion }: Gener
|
||||
}
|
||||
})
|
||||
.catch(e => { console.error(e); addError(e.message) })
|
||||
}, [version, generator.id, presetFilter])
|
||||
}, [version, gen.id, presetFilter])
|
||||
|
||||
const loadPreset = (id: string) => {
|
||||
Analytics.generatorEvent('load-preset', id)
|
||||
fetchPreset(version, generator.path ?? generator.id, id).then(preset => {
|
||||
fetchPreset(version, gen.path ?? gen.id, id).then(preset => {
|
||||
model?.reset(preset, false)
|
||||
})
|
||||
}
|
||||
@@ -136,7 +135,7 @@ export function Generator({ lang, changeTitle, version, onChangeVersion }: Gener
|
||||
|
||||
const [previewShown, setPreviewShown] = useState(false)
|
||||
|
||||
const hasPreview = HasPreview.includes(generator.id)
|
||||
const hasPreview = HasPreview.includes(gen.id)
|
||||
let actionsShown = 1
|
||||
if (hasPreview) actionsShown += 1
|
||||
if (sourceShown) actionsShown += 2
|
||||
@@ -187,10 +186,10 @@ export function Generator({ lang, changeTitle, version, onChangeVersion }: Gener
|
||||
</div>
|
||||
</div>
|
||||
<div class={`popup-preview${previewShown ? ' shown' : ''}`}>
|
||||
<PreviewPanel {...{lang, model, version, id: generator.id}} shown={previewShown} onError={addError} />
|
||||
<PreviewPanel {...{lang, model, version, id: gen.id}} shown={previewShown} onError={addError} />
|
||||
</div>
|
||||
<div class={`popup-source${sourceShown ? ' shown' : ''}`}>
|
||||
<SourcePanel {...{lang, model, blockStates, doCopy, doDownload, doImport}} name={generator.schema ?? 'data'} onError={addError} />
|
||||
<SourcePanel {...{lang, model, blockStates, doCopy, doDownload, doImport}} name={gen.schema ?? 'data'} onError={addError} />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export type TreeProps = {
|
||||
const selectRegistries = ['loot_table.type', 'loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'carver.type', 'feature.type', 'decorator.type', 'feature.tree.minimum_size.type', 'block_state_provider.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'int_provider.type', 'float_provider.type', 'height_provider.type', 'structure_feature.type', 'surface_builder.type', 'processor.processor_type', 'rule_test.predicate_type', 'pos_rule_test.predicate_type', 'template_element.element_type', 'block_placer.type']
|
||||
const hiddenFields = ['number_provider.type', 'score_provider.type', 'nbt_provider.type', 'int_provider.type', 'float_provider.type', 'height_provider.type']
|
||||
const flattenedFields = ['feature.config', 'decorator.config', 'int_provider.value', 'float_provider.value', 'block_state_provider.simple_state_provider.state', 'block_state_provider.rotated_block_provider.state', 'block_state_provider.weighted_state_provider.entries.entry.data', 'rule_test.block_state', 'structure_feature.config', 'surface_builder.config', 'template_pool.elements.entry.element']
|
||||
const inlineFields = ['loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'feature.type', 'decorator.type', 'block_state_provider.type', 'feature.tree.minimum_size.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'block_placer.type', 'rule_test.predicate_type', 'processor.processor_type', 'template_element.element_type']
|
||||
const inlineFields = ['loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'feature.type', 'decorator.type', 'block_state_provider.type', 'feature.tree.minimum_size.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'block_placer.type', 'rule_test.predicate_type', 'processor.processor_type', 'template_element.element_type', 'nbt_operation.op', 'number_provider.value', 'score_provider.name', 'score_provider.target', 'nbt_provider.source', 'nbt_provider.target']
|
||||
|
||||
/**
|
||||
* Secondary model used to remember the keys of a map
|
||||
@@ -132,9 +132,9 @@ export const renderHtml: Hook<[any, TreeProps], [string, string, string]> = {
|
||||
const suffix = keyRendered[1] + `<button class="add" data-id="${onAdd}" aria-label="${props.loc('button.add')}">${Octicon.plus_circle}</button>`
|
||||
if (blockState && path.last() === 'Properties') {
|
||||
if (typeof value !== 'object') value = {}
|
||||
const properties = Object.entries(blockState.properties)
|
||||
const properties = Object.entries(blockState.properties ?? {})
|
||||
.map(([key, values]) => [key, StringNode(null!, { enum: values })])
|
||||
Object.entries(blockState.properties).forEach(([key, values]) => {
|
||||
Object.entries(blockState.properties ?? {}).forEach(([key, values]) => {
|
||||
if (typeof value[key] !== 'string') {
|
||||
path.model.errors.add(path.push(key), 'error.expected_string')
|
||||
} else if (!values.includes(value[key])) {
|
||||
@@ -151,9 +151,9 @@ export const renderHtml: Hook<[any, TreeProps], [string, string, string]> = {
|
||||
const childPath = path.modelPush(key)
|
||||
const category = children.category(childPath)
|
||||
const childrenSchema = blockState
|
||||
? StringNode(null!, { enum: blockState.properties[key] ?? [] })
|
||||
? StringNode(null!, { enum: blockState.properties?.[key] ?? [] })
|
||||
: children
|
||||
if (blockState?.properties[key] && !blockState.properties[key].includes(value[key])) {
|
||||
if (blockState?.properties?.[key] && !blockState.properties?.[key].includes(value[key])) {
|
||||
path.model.errors.add(childPath, 'error.invalid_enum_option', value[key])
|
||||
}
|
||||
const [cPrefix, cSuffix, cBody] = childrenSchema.hook(this, childPath, value[key], props)
|
||||
|
||||
@@ -27,7 +27,7 @@ export const transformOutput: Hook<[any, OutputProps], any> = {
|
||||
const res: any = {}
|
||||
Object.keys(value).forEach(f => {
|
||||
if (blockState) {
|
||||
if (!Object.keys(blockState.properties).includes(f)) return
|
||||
if (!Object.keys(blockState.properties ?? {}).includes(f)) return
|
||||
}
|
||||
res[f] = children.hook(this, path.push(f), value[f], props)
|
||||
})
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
"preview": "Visualisieren",
|
||||
"reset": "Zurücksetzen",
|
||||
"share": "Teilen",
|
||||
"title.generator": "%0%_Generator",
|
||||
"title.generator": "%0%-Generator",
|
||||
"title.home": "Datenpaketgeneratoren",
|
||||
"worldgen/biome": "Biom",
|
||||
"worldgen/configured_carver": "Borer",
|
||||
|
||||
+15
-6
@@ -99,12 +99,12 @@ body[data-panel="settings"] header {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-title h2 {
|
||||
.title h2 {
|
||||
color: var(--nav);
|
||||
}
|
||||
|
||||
@@ -170,14 +170,23 @@ nav li svg {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
nav li .btn-menu > .btn {
|
||||
.title .btn-menu {
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.title .btn-menu > .btn svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
header .btn-menu > .btn {
|
||||
background: none !important;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
fill: var(--nav);
|
||||
}
|
||||
|
||||
nav li .btn-menu > .btn:hover {
|
||||
header .btn-menu > .btn:hover {
|
||||
fill: var(--nav-hover);
|
||||
}
|
||||
|
||||
@@ -683,8 +692,8 @@ hr {
|
||||
|
||||
/* SMALL */
|
||||
@media screen and (max-width: 580px) {
|
||||
.header-title h2 {
|
||||
font-size: 22px;
|
||||
.title h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
body nav li {
|
||||
|
||||
Reference in New Issue
Block a user