mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-24 23:56:51 +00:00
Add sounds explorer tool
This commit is contained in:
41
src/app/components/generator/PreviewPanel.tsx
Normal file
41
src/app/components/generator/PreviewPanel.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { DataModel } from '@mcschema/core'
|
||||
import { Path } from '@mcschema/core'
|
||||
import { useState } from 'preact/hooks'
|
||||
import { useModel } from '../../hooks'
|
||||
import type { VersionId } from '../../Schemas'
|
||||
import { BiomeSourcePreview, DecoratorPreview, NoiseSettingsPreview } from '../previews'
|
||||
|
||||
export const HasPreview = ['dimension', 'worldgen/noise_settings', 'worldgen/configured_feature']
|
||||
|
||||
type PreviewPanelProps = {
|
||||
lang: string,
|
||||
model: DataModel | null,
|
||||
version: VersionId,
|
||||
id: string,
|
||||
shown: boolean,
|
||||
onError: (message: string) => unknown,
|
||||
}
|
||||
export function PreviewPanel({ lang, model, version, id, shown }: PreviewPanelProps) {
|
||||
const [, setCount] = useState(0)
|
||||
|
||||
useModel(model, () => {
|
||||
setCount(count => count + 1)
|
||||
})
|
||||
|
||||
if (id === 'dimension' && model?.get(new Path(['generator', 'type']))?.endsWith('noise')) {
|
||||
const data = model.get(new Path(['generator', 'biome_source']))
|
||||
if (data) return <BiomeSourcePreview {...{ lang, model, version, shown, data }} />
|
||||
}
|
||||
|
||||
if (id === 'worldgen/noise_settings' && model) {
|
||||
const data = model.get(new Path([]))
|
||||
if (data) return <NoiseSettingsPreview {...{ lang, model, version, shown, data }} />
|
||||
}
|
||||
|
||||
if (id === 'worldgen/configured_feature' && model) {
|
||||
const data = model.get(new Path([]))
|
||||
if (data) return <DecoratorPreview {...{ lang, model, version, shown, data }} />
|
||||
}
|
||||
|
||||
return <></>
|
||||
}
|
||||
122
src/app/components/generator/SourcePanel.tsx
Normal file
122
src/app/components/generator/SourcePanel.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import { DataModel, ModelPath } from '@mcschema/core'
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
||||
import { Btn, BtnMenu } from '..'
|
||||
import { useModel } from '../../hooks'
|
||||
import { locale } from '../../Locales'
|
||||
import { transformOutput } from '../../schema/transformOutput'
|
||||
import type { BlockStateRegistry } from '../../Schemas'
|
||||
import { Store } from '../../Store'
|
||||
import { message } from '../../Utils'
|
||||
|
||||
const OUTPUT_CHARS_LIMIT = 10000
|
||||
|
||||
const INDENT: Record<string, number | string> = {
|
||||
'2_spaces': 2,
|
||||
'4_spaces': 4,
|
||||
tabs: '\t',
|
||||
}
|
||||
|
||||
type SourcePanelProps = {
|
||||
lang: string,
|
||||
name: string,
|
||||
model: DataModel | null,
|
||||
blockStates: BlockStateRegistry | null,
|
||||
doCopy?: number,
|
||||
doDownload?: number,
|
||||
doImport?: number,
|
||||
copySuccess: () => unknown,
|
||||
onError: (message: string) => unknown,
|
||||
}
|
||||
export function SourcePanel({ lang, name, model, blockStates, doCopy, doDownload, doImport, copySuccess, onError }: SourcePanelProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
const [indent, setIndent] = useState(Store.getIndent())
|
||||
const source = useRef<HTMLTextAreaElement>(null)
|
||||
const download = useRef<HTMLAnchorElement>(null)
|
||||
const retransform = useRef<Function>()
|
||||
|
||||
const getOutput = useCallback((model: DataModel, blockStates: BlockStateRegistry) => {
|
||||
const data = model.schema.hook(transformOutput, new ModelPath(model), model.data, { blockStates })
|
||||
return JSON.stringify(data, null, INDENT[indent]) + '\n'
|
||||
}, [indent])
|
||||
|
||||
useEffect(() => {
|
||||
retransform.current = () => {
|
||||
if (!model || !blockStates) return
|
||||
try {
|
||||
const output = getOutput(model, blockStates)
|
||||
if (output.length >= OUTPUT_CHARS_LIMIT) {
|
||||
source.current.value = output.slice(0, OUTPUT_CHARS_LIMIT) + `\n\nOutput is too large to display (+${OUTPUT_CHARS_LIMIT} chars)\nExport to view complete output\n\n`
|
||||
} else {
|
||||
source.current.value = output
|
||||
}
|
||||
} catch (e) {
|
||||
onError(`Error getting JSON output: ${message(e)}`)
|
||||
console.error(e)
|
||||
source.current.value = ''
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
useModel(model, () => {
|
||||
retransform.current()
|
||||
})
|
||||
useEffect(() => {
|
||||
if (model) retransform.current()
|
||||
}, [model])
|
||||
|
||||
useEffect(() => {
|
||||
retransform.current()
|
||||
}, [indent])
|
||||
|
||||
const onImport = () => {
|
||||
try {
|
||||
const data = JSON.parse(source.current.value)
|
||||
model?.reset(DataModel.wrapLists(data), false)
|
||||
} catch (e) {
|
||||
onError(`Error importing: ${message(e)}`)
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (doCopy && model && blockStates) {
|
||||
navigator.clipboard.writeText(getOutput(model, blockStates)).then(() => {
|
||||
copySuccess()
|
||||
})
|
||||
}
|
||||
}, [doCopy])
|
||||
|
||||
useEffect(() => {
|
||||
if (doDownload && source.current && download.current) {
|
||||
const content = encodeURIComponent(source.current.value)
|
||||
download.current.setAttribute('href', `data:text/json;charset=utf-8,${content}`)
|
||||
download.current.setAttribute('download', `${name}.json`)
|
||||
download.current.click()
|
||||
}
|
||||
}, [doDownload])
|
||||
|
||||
useEffect(() => {
|
||||
if (doImport && source.current) {
|
||||
source.current.value = ''
|
||||
source.current.select()
|
||||
}
|
||||
}, [doImport])
|
||||
|
||||
const changeIndent = (value: string) => {
|
||||
Store.setIndent(value)
|
||||
setIndent(value)
|
||||
}
|
||||
|
||||
return <>
|
||||
<div class="controls">
|
||||
<BtnMenu icon="gear" tooltip={loc('output_settings')}>
|
||||
{Object.entries(INDENT).map(([key]) =>
|
||||
<Btn label={loc(`indentation.${key}`)} active={indent === key}
|
||||
onClick={() => changeIndent(key)}/>
|
||||
)}
|
||||
</BtnMenu>
|
||||
</div>
|
||||
<textarea ref={source} class="source" onBlur={onImport} spellcheck={false} autocorrect="off" placeholder={loc('source_placeholder')}></textarea>
|
||||
<a ref={download} style="display: none;"></a>
|
||||
</>
|
||||
}
|
||||
31
src/app/components/generator/Tree.tsx
Normal file
31
src/app/components/generator/Tree.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { DataModel } from '@mcschema/core'
|
||||
import { useErrorBoundary, useState } from 'preact/hooks'
|
||||
import { useModel } from '../../hooks'
|
||||
import { FullNode } from '../../schema/renderHtml'
|
||||
import type { BlockStateRegistry, VersionId } from '../../Schemas'
|
||||
|
||||
type TreePanelProps = {
|
||||
lang: string,
|
||||
version: VersionId,
|
||||
model: DataModel | null,
|
||||
blockStates: BlockStateRegistry | null,
|
||||
onError: (message: string) => unknown,
|
||||
}
|
||||
export function Tree({ lang, model, blockStates, onError }: TreePanelProps) {
|
||||
if (!model || !blockStates || lang === 'none') return <></>
|
||||
|
||||
const [error] = useErrorBoundary(e => {
|
||||
onError(`Error rendering the tree: ${e.message}`)
|
||||
console.error(e)
|
||||
})
|
||||
if (error) return <></>
|
||||
|
||||
const [, setState] = useState(0)
|
||||
useModel(model, () => {
|
||||
setState(state => state + 1)
|
||||
})
|
||||
|
||||
return <div class="tree">
|
||||
<FullNode {...{model, lang, blockStates}}/>
|
||||
</div>
|
||||
}
|
||||
3
src/app/components/generator/index.ts
Normal file
3
src/app/components/generator/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './PreviewPanel'
|
||||
export * from './SourcePanel'
|
||||
export * from './Tree'
|
||||
Reference in New Issue
Block a user