mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-24 07:37:10 +00:00
Switch to vite and preact
This commit is contained in:
16
src/app/components/Ad.tsx
Normal file
16
src/app/components/Ad.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { useEffect } from 'preact/hooks'
|
||||
|
||||
declare const ethicalads: any
|
||||
|
||||
type AdProps = {
|
||||
type: 'text' | 'image',
|
||||
id: string,
|
||||
}
|
||||
export function Ad({ type, id }: AdProps) {
|
||||
useEffect(() => {
|
||||
document.getElementById('ad-placeholder')?.remove()
|
||||
ethicalads?.load()
|
||||
}, [])
|
||||
|
||||
return <div data-ea-publisher="misode-github-io" data-ea-type={type} class="ad dark flat" id={id}></div>
|
||||
}
|
||||
15
src/app/components/Btn.tsx
Normal file
15
src/app/components/Btn.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Octicon } from '.'
|
||||
|
||||
type BtnProps = {
|
||||
icon?: keyof typeof Octicon,
|
||||
label?: string,
|
||||
active?: boolean,
|
||||
class?: string,
|
||||
onClick?: (event: MouseEvent) => unknown,
|
||||
}
|
||||
export function Btn({ icon, label, active, class: class_, onClick }: BtnProps) {
|
||||
return <div class={`btn${active ? ' active' : ''}${class_ ? ` ${class_}` : ''}`} onClick={onClick}>
|
||||
{icon && Octicon[icon]}
|
||||
{label && <span>{label}</span>}
|
||||
</div>
|
||||
}
|
||||
23
src/app/components/BtnInput.tsx
Normal file
23
src/app/components/BtnInput.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Octicon } from '.'
|
||||
|
||||
type BtnInputProps = {
|
||||
icon?: keyof typeof Octicon,
|
||||
label?: string,
|
||||
large?: boolean,
|
||||
type?: 'number' | 'text',
|
||||
value?: string,
|
||||
onChange?: (value: string) => unknown,
|
||||
}
|
||||
export function BtnInput({ icon, label, large, type, value, onChange }: BtnInputProps) {
|
||||
const onKeyUp = onChange === undefined ? () => {} : (e: any) => {
|
||||
const value = (e.target as HTMLInputElement).value
|
||||
if (type !== 'number' || (!value.endsWith('.') && !isNaN(Number(value)))) {
|
||||
onChange?.(value)
|
||||
}
|
||||
}
|
||||
return <div class={`btn btn-input ${large ? 'large-input' : ''}`} onClick={e => e.stopPropagation()}>
|
||||
{icon && Octicon[icon]}
|
||||
{label && <span>{label}</span>}
|
||||
<input type="text" value={value} onKeyUp={onKeyUp} />
|
||||
</div>
|
||||
}
|
||||
34
src/app/components/BtnMenu.tsx
Normal file
34
src/app/components/BtnMenu.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { ComponentChildren } from 'preact'
|
||||
import { useEffect, useState } from 'preact/hooks'
|
||||
import type { Octicon } from '.'
|
||||
import { Btn } from '.'
|
||||
|
||||
type BtnMenuProps = {
|
||||
icon?: keyof typeof Octicon,
|
||||
label?: string,
|
||||
relative?: boolean,
|
||||
children: ComponentChildren,
|
||||
}
|
||||
export function BtnMenu({ icon, label, relative, children }: BtnMenuProps) {
|
||||
const [active, setActive] = useState(false)
|
||||
|
||||
const hider = () => {
|
||||
setActive(false)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
document.body.addEventListener('click', hider)
|
||||
}
|
||||
return () => {
|
||||
document.body.removeEventListener('click', hider)
|
||||
}
|
||||
}, [active])
|
||||
|
||||
return <div class={`btn-menu${relative === false ? ' no-relative' : ''}`}>
|
||||
<Btn icon={icon} label={label} onClick={() => setActive(true)} />
|
||||
{active && <div class="btn-group">
|
||||
{children}
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Property } from '../state/Property';
|
||||
import { View } from '../views/View';
|
||||
import { Octicon } from './Octicon';
|
||||
|
||||
export const Dropdown = (view: View, icon: keyof typeof Octicon, entries: [string, string][], state: Property<string>, watcher?: (value: string) => void) => {
|
||||
const dropdown = view.register(el => {
|
||||
el.addEventListener('change', () => {
|
||||
state.set((el as HTMLSelectElement).value)
|
||||
})
|
||||
state.watchRun(v => (el as HTMLSelectElement).value = v, 'dropdown')
|
||||
})
|
||||
return `
|
||||
<div class="dropdown">
|
||||
<select data-id="${dropdown}">
|
||||
${entries.map(e => `
|
||||
<option value=${e[0]}>${e[1]}</option>
|
||||
`).join('')}
|
||||
</select>
|
||||
${Octicon[icon]}
|
||||
</div>`
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import { DataModel } from '@mcschema/core';
|
||||
import { App } from '../App';
|
||||
import { locale } from '../Locales';
|
||||
import { View } from '../views/View';
|
||||
import { Octicon } from './Octicon';
|
||||
import { Toggle } from './Toggle';
|
||||
import { htmlEncode } from '../Utils'
|
||||
import { Tracker } from '../Tracker';
|
||||
|
||||
export const Errors = (view: View, model: DataModel) => {
|
||||
const getContent = () => {
|
||||
if (App.jsonError.get()) {
|
||||
return `<div class="error-list">
|
||||
<div class="error">
|
||||
${htmlEncode(App.jsonError.get()!)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="toggle" style="cursor: initial;">
|
||||
${Octicon.issue_opened}
|
||||
</div>`
|
||||
}
|
||||
if (model.errors.count() === 0) return ''
|
||||
return `${App.errorsVisible.get() ? `
|
||||
<div class="error-list">
|
||||
${model.errors.getAll().map(e => `
|
||||
<div class="error">
|
||||
<span class="error-path">${e.path.toString()}</span>
|
||||
<span>-</span>
|
||||
<span class="error-message">${htmlEncode(locale(e.error, e.params))}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
${Toggle(view, [[true, 'chevron_down'], [false, 'issue_opened']], App.errorsVisible, Tracker.toggleErrors)}`
|
||||
}
|
||||
const errors = view.register(el => {
|
||||
model.addListener({
|
||||
errors() {
|
||||
view.mount(el, getContent(), false)
|
||||
}
|
||||
})
|
||||
App.jsonError.watch(() => {
|
||||
view.mount(el, getContent(), false)
|
||||
})
|
||||
App.errorsVisible.watch(() => {
|
||||
view.mount(el, getContent(), false)
|
||||
}, 'errors')
|
||||
})
|
||||
return `
|
||||
<div class="errors" data-id="${errors}">
|
||||
${getContent()}
|
||||
</div>`
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import { App } from '../App';
|
||||
import { View } from '../views/View';
|
||||
import { Dropdown } from './Dropdown';
|
||||
import { Octicon } from './Octicon';
|
||||
import { Toggle } from './Toggle';
|
||||
import { languages } from '../../config.json'
|
||||
import { Tracker } from '../Tracker';
|
||||
import { locale } from '../Locales';
|
||||
|
||||
export const Header = (view: View, title: string, homeLink = '/') => {
|
||||
const panelTogglesId = view.register(el => {
|
||||
const getPanelToggles = () => {
|
||||
const panels = [['preview', 'play'], ['tree', 'note'], ['source', 'code']]
|
||||
if (!panels.map(e => e[0]).includes(App.mobilePanel.get())) return ''
|
||||
return panels
|
||||
.filter(e => e[0] !== App.mobilePanel.get())
|
||||
.filter(e => e[0] !== 'preview' || App.preview.get() !== null)
|
||||
.map(e => `<div data-id="${view.onClick(() => App.mobilePanel.set(e[0]))}">
|
||||
${Octicon[e[1] as keyof typeof Octicon]}
|
||||
</div>`).join('')
|
||||
}
|
||||
App.mobilePanel.watchRun(() => {
|
||||
view.mount(el, getPanelToggles(), false)
|
||||
})
|
||||
App.preview.watchRun((value, oldValue) => {
|
||||
if (value === null && App.mobilePanel.get() === 'preview') {
|
||||
App.mobilePanel.set('tree')
|
||||
}
|
||||
if (value === null || oldValue === null) {
|
||||
view.mount(el, getPanelToggles(), false)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return `<header>
|
||||
<div class="header-title">
|
||||
<a data-link href="${homeLink}" class="home-link" aria-label="${locale('home')}">${Octicon.three_bars}</a>
|
||||
<h2>${title}</h2>
|
||||
</div>
|
||||
<nav>
|
||||
<div class="panel-toggles" data-id="${panelTogglesId}"></div>
|
||||
<ul>
|
||||
<li>${Dropdown(view, 'globe', languages.map(l => [l.code, l.name]), App.language, Tracker.setLanguage)}</li>
|
||||
<li>${Toggle(view, [['dark', 'sun'], ['light', 'moon']], App.theme, Tracker.setTheme)}</li>
|
||||
<li>
|
||||
<a data-link href="/settings/fields/" title="${locale('settings')}">
|
||||
${Octicon.gear}
|
||||
</a>
|
||||
</li>
|
||||
<li class="dimmed">
|
||||
<a href="https://github.com/misode/misode.github.io" target="_blank" rel="noreferrer" title="${locale('github')}">
|
||||
${Octicon.mark_github}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>`
|
||||
}
|
||||
55
src/app/components/Header.tsx
Normal file
55
src/app/components/Header.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { getCurrentUrl, Link } from 'preact-router'
|
||||
import { Btn, BtnMenu, Octicon } from '.'
|
||||
import config from '../../config.json'
|
||||
import { locale } from '../Locales'
|
||||
|
||||
const Themes: Record<string, keyof typeof Octicon> = {
|
||||
system: 'device_desktop',
|
||||
dark: 'moon',
|
||||
light: 'sun',
|
||||
}
|
||||
|
||||
type HeaderProps = {
|
||||
lang: string,
|
||||
title: string,
|
||||
theme: string,
|
||||
changeTheme: (theme: string) => unknown,
|
||||
language: string,
|
||||
changeLanguage: (language: string) => unknown,
|
||||
}
|
||||
export function Header({ lang, title, theme, changeTheme, language, changeLanguage }: HeaderProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
return <header>
|
||||
<div class="header-title">
|
||||
<Link class="home-link" href={getCurrentUrl().match(/^\/worldgen\/.+/) ? '/worldgen/' : '/'}>
|
||||
{Octicon.three_bars}
|
||||
</Link>
|
||||
<h2>{title}</h2>
|
||||
</div>
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<BtnMenu icon="globe">
|
||||
{config.languages.map(({ code, name }) =>
|
||||
<Btn label={name} active={code === language}
|
||||
onClick={() => changeLanguage(code)} />
|
||||
)}
|
||||
</BtnMenu>
|
||||
</li>
|
||||
<li>
|
||||
<BtnMenu icon={Themes[theme]}>
|
||||
{Object.entries(Themes).map(([th, icon]) =>
|
||||
<Btn icon={icon} label={loc(`theme.${th}`)} active={th === theme}
|
||||
onClick={() => changeTheme(th)} />
|
||||
)}
|
||||
</BtnMenu>
|
||||
</li>
|
||||
<li class="dimmed">
|
||||
<a href="https://github.com/misode/misode.github.io" target="_blank" rel="noreferrer" title={loc('github')}>
|
||||
{Octicon.mark_github}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
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_both: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 1.06L2.56 7h10.88l-2.22-2.22a.75.75 0 011.06-1.06l3.5 3.5a.75.75 0 010 1.06l-3.5 3.5a.75.75 0 11-1.06-1.06l2.22-2.22H2.56l2.22 2.22a.75.75 0 11-1.06 1.06l-3.5-3.5a.75.75 0 010-1.06l3.5-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>',
|
||||
dash: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2 8a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 8z"></path></svg>',
|
||||
download: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5 0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"></path></svg>',
|
||||
eye: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.679 7.932c.412-.621 1.242-1.75 2.366-2.717C5.175 4.242 6.527 3.5 8 3.5c1.473 0 2.824.742 3.955 1.715 1.124.967 1.954 2.096 2.366 2.717a.119.119 0 010 .136c-.412.621-1.242 1.75-2.366 2.717C10.825 11.758 9.473 12.5 8 12.5c-1.473 0-2.824-.742-3.955-1.715C2.92 9.818 2.09 8.69 1.679 8.068a.119.119 0 010-.136zM8 2c-1.981 0-3.67.992-4.933 2.078C1.797 5.169.88 6.423.43 7.1a1.619 1.619 0 000 1.798c.45.678 1.367 1.932 2.637 3.024C4.329 13.008 6.019 14 8 14c1.981 0 3.67-.992 4.933-2.078 1.27-1.091 2.187-2.345 2.637-3.023a1.619 1.619 0 000-1.798c-.45-.678-1.367-1.932-2.637-3.023C11.671 2.992 9.981 2 8 2zm0 8a2 2 0 100-4 2 2 0 000 4z"></path></svg>',
|
||||
eye_closed: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M.143 2.31a.75.75 0 011.047-.167l14.5 10.5a.75.75 0 11-.88 1.214l-2.248-1.628C11.346 13.19 9.792 14 8 14c-1.981 0-3.67-.992-4.933-2.078C1.797 10.832.88 9.577.43 8.9a1.618 1.618 0 010-1.797c.353-.533.995-1.42 1.868-2.305L.31 3.357A.75.75 0 01.143 2.31zm3.386 3.378a14.21 14.21 0 00-1.85 2.244.12.12 0 00-.022.068c0 .021.006.045.022.068.412.621 1.242 1.75 2.366 2.717C5.175 11.758 6.527 12.5 8 12.5c1.195 0 2.31-.488 3.29-1.191L9.063 9.695A2 2 0 016.058 7.52l-2.53-1.832zM8 3.5c-.516 0-1.017.09-1.499.251a.75.75 0 11-.473-1.423A6.23 6.23 0 018 2c1.981 0 3.67.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.619 1.619 0 010 1.798c-.11.166-.248.365-.41.587a.75.75 0 11-1.21-.887c.148-.201.272-.382.371-.53a.119.119 0 000-.137c-.412-.621-1.242-1.75-2.366-2.717C10.825 4.242 9.473 3.5 8 3.5z"></path></svg>',
|
||||
fold: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M10.896 2H8.75V.75a.75.75 0 00-1.5 0V2H5.104a.25.25 0 00-.177.427l2.896 2.896a.25.25 0 00.354 0l2.896-2.896A.25.25 0 0010.896 2zM8.75 15.25a.75.75 0 01-1.5 0V14H5.104a.25.25 0 01-.177-.427l2.896-2.896a.25.25 0 01.354 0l2.896 2.896a.25.25 0 01-.177.427H8.75v1.25zm-6.5-6.5a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM6 8a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5A.75.75 0 016 8zm2.25.75a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM12 8a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5A.75.75 0 0112 8zm2.25.75a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5z"></path></svg>',
|
||||
gear: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.429 1.525a6.593 6.593 0 011.142 0c.036.003.108.036.137.146l.289 1.105c.147.56.55.967.997 1.189.174.086.341.183.501.29.417.278.97.423 1.53.27l1.102-.303c.11-.03.175.016.195.046.219.31.41.641.573.989.014.031.022.11-.059.19l-.815.806c-.411.406-.562.957-.53 1.456a4.588 4.588 0 010 .582c-.032.499.119 1.05.53 1.456l.815.806c.08.08.073.159.059.19a6.494 6.494 0 01-.573.99c-.02.029-.086.074-.195.045l-1.103-.303c-.559-.153-1.112-.008-1.529.27-.16.107-.327.204-.5.29-.449.222-.851.628-.998 1.189l-.289 1.105c-.029.11-.101.143-.137.146a6.613 6.613 0 01-1.142 0c-.036-.003-.108-.037-.137-.146l-.289-1.105c-.147-.56-.55-.967-.997-1.189a4.502 4.502 0 01-.501-.29c-.417-.278-.97-.423-1.53-.27l-1.102.303c-.11.03-.175-.016-.195-.046a6.492 6.492 0 01-.573-.989c-.014-.031-.022-.11.059-.19l.815-.806c.411-.406.562-.957.53-1.456a4.587 4.587 0 010-.582c.032-.499-.119-1.05-.53-1.456l-.815-.806c-.08-.08-.073-.159-.059-.19a6.44 6.44 0 01.573-.99c.02-.029.086-.075.195-.045l1.103.303c.559.153 1.112.008 1.529-.27.16-.107.327-.204.5-.29.449-.222.851-.628.998-1.189l.289-1.105c.029-.11.101-.143.137-.146zM8 0c-.236 0-.47.01-.701.03-.743.065-1.29.615-1.458 1.261l-.29 1.106c-.017.066-.078.158-.211.224a5.994 5.994 0 00-.668.386c-.123.082-.233.09-.3.071L3.27 2.776c-.644-.177-1.392.02-1.82.63a7.977 7.977 0 00-.704 1.217c-.315.675-.111 1.422.363 1.891l.815.806c.05.048.098.147.088.294a6.084 6.084 0 000 .772c.01.147-.038.246-.088.294l-.815.806c-.474.469-.678 1.216-.363 1.891.2.428.436.835.704 1.218.428.609 1.176.806 1.82.63l1.103-.303c.066-.019.176-.011.299.071.213.143.436.272.668.386.133.066.194.158.212.224l.289 1.106c.169.646.715 1.196 1.458 1.26a8.094 8.094 0 001.402 0c.743-.064 1.29-.614 1.458-1.26l.29-1.106c.017-.066.078-.158.211-.224a5.98 5.98 0 00.668-.386c.123-.082.233-.09.3-.071l1.102.302c.644.177 1.392-.02 1.82-.63.268-.382.505-.789.704-1.217.315-.675.111-1.422-.364-1.891l-.814-.806c-.05-.048-.098-.147-.088-.294a6.1 6.1 0 000-.772c-.01-.147.039-.246.088-.294l.814-.806c.475-.469.679-1.216.364-1.891a7.992 7.992 0 00-.704-1.218c-.428-.609-1.176-.806-1.82-.63l-1.103.303c-.066.019-.176.011-.299-.071a5.991 5.991 0 00-.668-.386c-.133-.066-.194-.158-.212-.224L10.16 1.29C9.99.645 9.444.095 8.701.031A8.094 8.094 0 008 0zm1.5 8a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM11 8a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>',
|
||||
globe: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.543 7.25h2.733c.144-2.074.866-3.756 1.58-4.948.12-.197.237-.381.353-.552a6.506 6.506 0 00-4.666 5.5zm2.733 1.5H1.543a6.506 6.506 0 004.666 5.5 11.13 11.13 0 01-.352-.552c-.715-1.192-1.437-2.874-1.581-4.948zm1.504 0h4.44a9.637 9.637 0 01-1.363 4.177c-.306.51-.612.919-.857 1.215a9.978 9.978 0 01-.857-1.215A9.637 9.637 0 015.78 8.75zm4.44-1.5H5.78a9.637 9.637 0 011.363-4.177c.306-.51.612-.919.857-1.215.245.296.55.705.857 1.215A9.638 9.638 0 0110.22 7.25zm1.504 1.5c-.144 2.074-.866 3.756-1.58 4.948-.12.197-.237.381-.353.552a6.506 6.506 0 004.666-5.5h-2.733zm2.733-1.5h-2.733c-.144-2.074-.866-3.756-1.58-4.948a11.738 11.738 0 00-.353-.552 6.506 6.506 0 014.666 5.5zM8 0a8 8 0 100 16A8 8 0 008 0z"></path></svg>',
|
||||
history: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.643 3.143L.427 1.927A.25.25 0 000 2.104V5.75c0 .138.112.25.25.25h3.646a.25.25 0 00.177-.427L2.715 4.215a6.5 6.5 0 11-1.18 4.458.75.75 0 10-1.493.154 8.001 8.001 0 101.6-5.684zM7.75 4a.75.75 0 01.75.75v2.992l2.028.812a.75.75 0 01-.557 1.392l-2.5-1A.75.75 0 017 8.25v-3.5A.75.75 0 017.75 4z"></path></svg>',
|
||||
info: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"></path></svg>',
|
||||
issue_opened: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"></path></svg>',
|
||||
kebab_horizontal: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path></svg>',
|
||||
link: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg>',
|
||||
mark_github: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>',
|
||||
moon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M9.598 1.591a.75.75 0 01.785-.175 7 7 0 11-8.967 8.967.75.75 0 01.961-.96 5.5 5.5 0 007.046-7.046.75.75 0 01.175-.786zm1.616 1.945a7 7 0 01-7.678 7.678 5.5 5.5 0 107.678-7.678z"></path></svg>',
|
||||
note: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0114.25 14H1.75A1.75 1.75 0 010 12.25v-8.5zm1.75-.25a.25.25 0 00-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-8.5a.25.25 0 00-.25-.25H1.75zM3.5 6.25a.75.75 0 01.75-.75h7a.75.75 0 010 1.5h-7a.75.75 0 01-.75-.75zm.75 2.25a.75.75 0 000 1.5h4a.75.75 0 000-1.5h-4z"></path></svg>',
|
||||
package: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8.878.392a1.75 1.75 0 00-1.756 0l-5.25 3.045A1.75 1.75 0 001 4.951v6.098c0 .624.332 1.2.872 1.514l5.25 3.045a1.75 1.75 0 001.756 0l5.25-3.045c.54-.313.872-.89.872-1.514V4.951c0-.624-.332-1.2-.872-1.514L8.878.392zM7.875 1.69a.25.25 0 01.25 0l4.63 2.685L8 7.133 3.245 4.375l4.63-2.685zM2.5 5.677v5.372c0 .09.047.171.125.216l4.625 2.683V8.432L2.5 5.677zm6.25 8.271l4.625-2.683a.25.25 0 00.125-.216V5.677L8.75 8.432v5.516z"></path></svg>',
|
||||
play: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zM6.379 5.227A.25.25 0 006 5.442v5.117a.25.25 0 00.379.214l4.264-2.559a.25.25 0 000-.428L6.379 5.227z"></path></svg>',
|
||||
plus: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 2a.75.75 0 01.75.75v4.5h4.5a.75.75 0 010 1.5h-4.5v4.5a.75.75 0 01-1.5 0v-4.5h-4.5a.75.75 0 010-1.5h4.5v-4.5A.75.75 0 018 2z"></path></svg>',
|
||||
plus_circle: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zm.75 4.75a.75.75 0 00-1.5 0v2.5h-2.5a.75.75 0 000 1.5h2.5v2.5a.75.75 0 001.5 0v-2.5h2.5a.75.75 0 000-1.5h-2.5v-2.5z"></path></svg>',
|
||||
search: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg>',
|
||||
square: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4 5.75C4 4.784 4.784 4 5.75 4h4.5c.966 0 1.75.784 1.75 1.75v4.5A1.75 1.75 0 0110.25 12h-4.5A1.75 1.75 0 014 10.25v-4.5zm1.75-.25a.25.25 0 00-.25.25v4.5c0 .138.112.25.25.25h4.5a.25.25 0 00.25-.25v-4.5a.25.25 0 00-.25-.25h-4.5z"></path></svg>',
|
||||
square_fill: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M5.75 4A1.75 1.75 0 004 5.75v4.5c0 .966.784 1.75 1.75 1.75h4.5A1.75 1.75 0 0012 10.25v-4.5A1.75 1.75 0 0010.25 4h-4.5z"></path></svg>',
|
||||
sun: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 10.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zM8 12a4 4 0 100-8 4 4 0 000 8zM8 0a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0V.75A.75.75 0 018 0zm0 13a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 018 13zM2.343 2.343a.75.75 0 011.061 0l1.06 1.061a.75.75 0 01-1.06 1.06l-1.06-1.06a.75.75 0 010-1.06zm9.193 9.193a.75.75 0 011.06 0l1.061 1.06a.75.75 0 01-1.06 1.061l-1.061-1.06a.75.75 0 010-1.061zM16 8a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0116 8zM3 8a.75.75 0 01-.75.75H.75a.75.75 0 010-1.5h1.5A.75.75 0 013 8zm10.657-5.657a.75.75 0 010 1.061l-1.061 1.06a.75.75 0 11-1.06-1.06l1.06-1.06a.75.75 0 011.06 0zm-9.193 9.193a.75.75 0 010 1.06l-1.06 1.061a.75.75 0 11-1.061-1.06l1.06-1.061a.75.75 0 011.061 0z"></path></svg>',
|
||||
tag: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.5 7.775V2.75a.25.25 0 01.25-.25h5.025a.25.25 0 01.177.073l6.25 6.25a.25.25 0 010 .354l-5.025 5.025a.25.25 0 01-.354 0l-6.25-6.25a.25.25 0 01-.073-.177zm-1.5 0V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 010 2.474l-5.026 5.026a1.75 1.75 0 01-2.474 0l-6.25-6.25A1.75 1.75 0 011 7.775zM6 5a1 1 0 100 2 1 1 0 000-2z"></path></svg>',
|
||||
trashcan: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M6.5 1.75a.25.25 0 01.25-.25h2.5a.25.25 0 01.25.25V3h-3V1.75zm4.5 0V3h2.25a.75.75 0 010 1.5H2.75a.75.75 0 010-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75zM4.496 6.675a.75.75 0 10-1.492.15l.66 6.6A1.75 1.75 0 005.405 15h5.19c.9 0 1.652-.681 1.741-1.576l.66-6.6a.75.75 0 00-1.492-.149l-.66 6.6a.25.25 0 01-.249.225h-5.19a.25.25 0 01-.249-.225l-.66-6.6z"></path></svg>',
|
||||
three_bars: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z"></path></svg>',
|
||||
unfold: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M8.177.677l2.896 2.896a.25.25 0 01-.177.427H8.75v1.25a.75.75 0 01-1.5 0V4H5.104a.25.25 0 01-.177-.427L7.823.677a.25.25 0 01.354 0zM7.25 10.75a.75.75 0 011.5 0V12h2.146a.25.25 0 01.177.427l-2.896 2.896a.25.25 0 01-.354 0l-2.896-2.896A.25.25 0 015.104 12H7.25v-1.25zm-5-2a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM6 8a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5A.75.75 0 016 8zm2.25.75a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM12 8a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5A.75.75 0 0112 8zm2.25.75a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5z"></path></svg>',
|
||||
x: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg>',
|
||||
}
|
||||
30
src/app/components/Octicon.tsx
Normal file
30
src/app/components/Octicon.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
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_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>,
|
||||
dash: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2 8a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 8z"></path></svg>,
|
||||
device_desktop: <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.5h12.5a.25.25 0 01.25.25v7.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25v-7.5a.25.25 0 01.25-.25zM14.25 1H1.75A1.75 1.75 0 000 2.75v7.5C0 11.216.784 12 1.75 12h3.727c-.1 1.041-.52 1.872-1.292 2.757A.75.75 0 004.75 16h6.5a.75.75 0 00.565-1.243c-.772-.885-1.193-1.716-1.292-2.757h3.727A1.75 1.75 0 0016 10.25v-7.5A1.75 1.75 0 0014.25 1zM9.018 12H6.982a5.72 5.72 0 01-.765 2.5h3.566a5.72 5.72 0 01-.765-2.5z"></path></svg>,
|
||||
download: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5 0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"></path></svg>,
|
||||
eye: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.679 7.932c.412-.621 1.242-1.75 2.366-2.717C5.175 4.242 6.527 3.5 8 3.5c1.473 0 2.824.742 3.955 1.715 1.124.967 1.954 2.096 2.366 2.717a.119.119 0 010 .136c-.412.621-1.242 1.75-2.366 2.717C10.825 11.758 9.473 12.5 8 12.5c-1.473 0-2.824-.742-3.955-1.715C2.92 9.818 2.09 8.69 1.679 8.068a.119.119 0 010-.136zM8 2c-1.981 0-3.67.992-4.933 2.078C1.797 5.169.88 6.423.43 7.1a1.619 1.619 0 000 1.798c.45.678 1.367 1.932 2.637 3.024C4.329 13.008 6.019 14 8 14c1.981 0 3.67-.992 4.933-2.078 1.27-1.091 2.187-2.345 2.637-3.023a1.619 1.619 0 000-1.798c-.45-.678-1.367-1.932-2.637-3.023C11.671 2.992 9.981 2 8 2zm0 8a2 2 0 100-4 2 2 0 000 4z"></path></svg>,
|
||||
eye_closed: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M.143 2.31a.75.75 0 011.047-.167l14.5 10.5a.75.75 0 11-.88 1.214l-2.248-1.628C11.346 13.19 9.792 14 8 14c-1.981 0-3.67-.992-4.933-2.078C1.797 10.832.88 9.577.43 8.9a1.618 1.618 0 010-1.797c.353-.533.995-1.42 1.868-2.305L.31 3.357A.75.75 0 01.143 2.31zm3.386 3.378a14.21 14.21 0 00-1.85 2.244.12.12 0 00-.022.068c0 .021.006.045.022.068.412.621 1.242 1.75 2.366 2.717C5.175 11.758 6.527 12.5 8 12.5c1.195 0 2.31-.488 3.29-1.191L9.063 9.695A2 2 0 016.058 7.52l-2.53-1.832zM8 3.5c-.516 0-1.017.09-1.499.251a.75.75 0 11-.473-1.423A6.23 6.23 0 018 2c1.981 0 3.67.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.619 1.619 0 010 1.798c-.11.166-.248.365-.41.587a.75.75 0 11-1.21-.887c.148-.201.272-.382.371-.53a.119.119 0 000-.137c-.412-.621-1.242-1.75-2.366-2.717C10.825 4.242 9.473 3.5 8 3.5z"></path></svg>,
|
||||
gear: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.429 1.525a6.593 6.593 0 011.142 0c.036.003.108.036.137.146l.289 1.105c.147.56.55.967.997 1.189.174.086.341.183.501.29.417.278.97.423 1.53.27l1.102-.303c.11-.03.175.016.195.046.219.31.41.641.573.989.014.031.022.11-.059.19l-.815.806c-.411.406-.562.957-.53 1.456a4.588 4.588 0 010 .582c-.032.499.119 1.05.53 1.456l.815.806c.08.08.073.159.059.19a6.494 6.494 0 01-.573.99c-.02.029-.086.074-.195.045l-1.103-.303c-.559-.153-1.112-.008-1.529.27-.16.107-.327.204-.5.29-.449.222-.851.628-.998 1.189l-.289 1.105c-.029.11-.101.143-.137.146a6.613 6.613 0 01-1.142 0c-.036-.003-.108-.037-.137-.146l-.289-1.105c-.147-.56-.55-.967-.997-1.189a4.502 4.502 0 01-.501-.29c-.417-.278-.97-.423-1.53-.27l-1.102.303c-.11.03-.175-.016-.195-.046a6.492 6.492 0 01-.573-.989c-.014-.031-.022-.11.059-.19l.815-.806c.411-.406.562-.957.53-1.456a4.587 4.587 0 010-.582c.032-.499-.119-1.05-.53-1.456l-.815-.806c-.08-.08-.073-.159-.059-.19a6.44 6.44 0 01.573-.99c.02-.029.086-.075.195-.045l1.103.303c.559.153 1.112.008 1.529-.27.16-.107.327-.204.5-.29.449-.222.851-.628.998-1.189l.289-1.105c.029-.11.101-.143.137-.146zM8 0c-.236 0-.47.01-.701.03-.743.065-1.29.615-1.458 1.261l-.29 1.106c-.017.066-.078.158-.211.224a5.994 5.994 0 00-.668.386c-.123.082-.233.09-.3.071L3.27 2.776c-.644-.177-1.392.02-1.82.63a7.977 7.977 0 00-.704 1.217c-.315.675-.111 1.422.363 1.891l.815.806c.05.048.098.147.088.294a6.084 6.084 0 000 .772c.01.147-.038.246-.088.294l-.815.806c-.474.469-.678 1.216-.363 1.891.2.428.436.835.704 1.218.428.609 1.176.806 1.82.63l1.103-.303c.066-.019.176-.011.299.071.213.143.436.272.668.386.133.066.194.158.212.224l.289 1.106c.169.646.715 1.196 1.458 1.26a8.094 8.094 0 001.402 0c.743-.064 1.29-.614 1.458-1.26l.29-1.106c.017-.066.078-.158.211-.224a5.98 5.98 0 00.668-.386c.123-.082.233-.09.3-.071l1.102.302c.644.177 1.392-.02 1.82-.63.268-.382.505-.789.704-1.217.315-.675.111-1.422-.364-1.891l-.814-.806c-.05-.048-.098-.147-.088-.294a6.1 6.1 0 000-.772c-.01-.147.039-.246.088-.294l.814-.806c.475-.469.679-1.216.364-1.891a7.992 7.992 0 00-.704-1.218c-.428-.609-1.176-.806-1.82-.63l-1.103.303c-.066.019-.176.011-.299-.071a5.991 5.991 0 00-.668-.386c-.133-.066-.194-.158-.212-.224L10.16 1.29C9.99.645 9.444.095 8.701.031A8.094 8.094 0 008 0zm1.5 8a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM11 8a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>,
|
||||
globe: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.543 7.25h2.733c.144-2.074.866-3.756 1.58-4.948.12-.197.237-.381.353-.552a6.506 6.506 0 00-4.666 5.5zm2.733 1.5H1.543a6.506 6.506 0 004.666 5.5 11.13 11.13 0 01-.352-.552c-.715-1.192-1.437-2.874-1.581-4.948zm1.504 0h4.44a9.637 9.637 0 01-1.363 4.177c-.306.51-.612.919-.857 1.215a9.978 9.978 0 01-.857-1.215A9.637 9.637 0 015.78 8.75zm4.44-1.5H5.78a9.637 9.637 0 011.363-4.177c.306-.51.612-.919.857-1.215.245.296.55.705.857 1.215A9.638 9.638 0 0110.22 7.25zm1.504 1.5c-.144 2.074-.866 3.756-1.58 4.948-.12.197-.237.381-.353.552a6.506 6.506 0 004.666-5.5h-2.733zm2.733-1.5h-2.733c-.144-2.074-.866-3.756-1.58-4.948a11.738 11.738 0 00-.353-.552 6.506 6.506 0 014.666 5.5zM8 0a8 8 0 100 16A8 8 0 008 0z"></path></svg>,
|
||||
history: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.643 3.143L.427 1.927A.25.25 0 000 2.104V5.75c0 .138.112.25.25.25h3.646a.25.25 0 00.177-.427L2.715 4.215a6.5 6.5 0 11-1.18 4.458.75.75 0 10-1.493.154 8.001 8.001 0 101.6-5.684zM7.75 4a.75.75 0 01.75.75v2.992l2.028.812a.75.75 0 01-.557 1.392l-2.5-1A.75.75 0 017 8.25v-3.5A.75.75 0 017.75 4z"></path></svg>,
|
||||
kebab_horizontal: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path></svg>,
|
||||
link: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg>,
|
||||
mark_github: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>,
|
||||
moon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M9.598 1.591a.75.75 0 01.785-.175 7 7 0 11-8.967 8.967.75.75 0 01.961-.96 5.5 5.5 0 007.046-7.046.75.75 0 01.175-.786zm1.616 1.945a7 7 0 01-7.678 7.678 5.5 5.5 0 107.678-7.678z"></path></svg>,
|
||||
play: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zM6.379 5.227A.25.25 0 006 5.442v5.117a.25.25 0 00.379.214l4.264-2.559a.25.25 0 000-.428L6.379 5.227z"></path></svg>,
|
||||
plus: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 2a.75.75 0 01.75.75v4.5h4.5a.75.75 0 010 1.5h-4.5v4.5a.75.75 0 01-1.5 0v-4.5h-4.5a.75.75 0 010-1.5h4.5v-4.5A.75.75 0 018 2z"></path></svg>,
|
||||
search: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg>,
|
||||
sun: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 10.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zM8 12a4 4 0 100-8 4 4 0 000 8zM8 0a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0V.75A.75.75 0 018 0zm0 13a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 018 13zM2.343 2.343a.75.75 0 011.061 0l1.06 1.061a.75.75 0 01-1.06 1.06l-1.06-1.06a.75.75 0 010-1.06zm9.193 9.193a.75.75 0 011.06 0l1.061 1.06a.75.75 0 01-1.06 1.061l-1.061-1.06a.75.75 0 010-1.061zM16 8a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0116 8zM3 8a.75.75 0 01-.75.75H.75a.75.75 0 010-1.5h1.5A.75.75 0 013 8zm10.657-5.657a.75.75 0 010 1.061l-1.061 1.06a.75.75 0 11-1.06-1.06l1.06-1.06a.75.75 0 011.06 0zm-9.193 9.193a.75.75 0 010 1.06l-1.06 1.061a.75.75 0 11-1.061-1.06l1.06-1.061a.75.75 0 011.061 0z"></path></svg>,
|
||||
sync: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 2.5a5.487 5.487 0 00-4.131 1.869l1.204 1.204A.25.25 0 014.896 6H1.25A.25.25 0 011 5.75V2.104a.25.25 0 01.427-.177l1.38 1.38A7.001 7.001 0 0114.95 7.16a.75.75 0 11-1.49.178A5.501 5.501 0 008 2.5zM1.705 8.005a.75.75 0 01.834.656 5.501 5.501 0 009.592 2.97l-1.204-1.204a.25.25 0 01.177-.427h3.646a.25.25 0 01.25.25v3.646a.25.25 0 01-.427.177l-1.38-1.38A7.001 7.001 0 011.05 8.84a.75.75 0 01.656-.834z"></path></svg>,
|
||||
tag: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.5 7.775V2.75a.25.25 0 01.25-.25h5.025a.25.25 0 01.177.073l6.25 6.25a.25.25 0 010 .354l-5.025 5.025a.25.25 0 01-.354 0l-6.25-6.25a.25.25 0 01-.073-.177zm-1.5 0V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 010 2.474l-5.026 5.026a1.75 1.75 0 01-2.474 0l-6.25-6.25A1.75 1.75 0 011 7.775zM6 5a1 1 0 100 2 1 1 0 000-2z"></path></svg>,
|
||||
three_bars: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z"></path></svg>,
|
||||
unfold: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M8.177.677l2.896 2.896a.25.25 0 01-.177.427H8.75v1.25a.75.75 0 01-1.5 0V4H5.104a.25.25 0 01-.177-.427L7.823.677a.25.25 0 01.354 0zM7.25 10.75a.75.75 0 011.5 0V12h2.146a.25.25 0 01.177.427l-2.896 2.896a.25.25 0 01-.354 0l-2.896-2.896A.25.25 0 015.104 12H7.25v-1.25zm-5-2a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM6 8a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5A.75.75 0 016 8zm2.25.75a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM12 8a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5A.75.75 0 0112 8zm2.25.75a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5z"></path></svg>,
|
||||
upload: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8.53 1.22a.75.75 0 00-1.06 0L3.72 4.97a.75.75 0 001.06 1.06l2.47-2.47v6.69a.75.75 0 001.5 0V3.56l2.47 2.47a.75.75 0 101.06-1.06L8.53 1.22zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"></path></svg>,
|
||||
x_circle: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M3.404 12.596a6.5 6.5 0 119.192-9.192 6.5 6.5 0 01-9.192 9.192zM2.344 2.343a8 8 0 1011.313 11.314A8 8 0 002.343 2.343zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>,
|
||||
}
|
||||
70
src/app/components/PreviewPanel.tsx
Normal file
70
src/app/components/PreviewPanel.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { DataModel } from '@mcschema/core'
|
||||
import { Path } from '@mcschema/core'
|
||||
import type { FunctionalComponent } from 'preact'
|
||||
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/feature']
|
||||
|
||||
export const Previews: {
|
||||
id: string,
|
||||
generator: string,
|
||||
path: Path,
|
||||
predicate: (model: DataModel) => boolean,
|
||||
preview: FunctionalComponent<{
|
||||
lang: string,
|
||||
model: DataModel,
|
||||
data: any,
|
||||
version: VersionId,
|
||||
shown: boolean,
|
||||
}>,
|
||||
}[] = [
|
||||
{
|
||||
id: 'biome-noise',
|
||||
generator: 'dimension',
|
||||
path: new Path(['generator', 'biome_source']),
|
||||
predicate: model => model.get(new Path(['generator', 'type'])).endsWith('noise'),
|
||||
preview: BiomeSourcePreview,
|
||||
},
|
||||
{
|
||||
id: 'noise-settings',
|
||||
generator: 'worldgen/noise-settings',
|
||||
path: new Path(['noise']),
|
||||
predicate: () => true,
|
||||
preview: NoiseSettingsPreview,
|
||||
},
|
||||
{
|
||||
id: 'decorator',
|
||||
generator: 'worldgen/feature',
|
||||
path: new Path([]),
|
||||
predicate: () => true,
|
||||
preview: DecoratorPreview,
|
||||
},
|
||||
]
|
||||
|
||||
type PreviewProps = {
|
||||
lang: string,
|
||||
model: DataModel | null,
|
||||
version: VersionId,
|
||||
id: string,
|
||||
shown: boolean,
|
||||
}
|
||||
export function PreviewPanel({ lang, model, version, id, shown }: PreviewProps) {
|
||||
const [, setCount] = useState(0)
|
||||
|
||||
useModel(model, () => {
|
||||
setCount(count => count + 1)
|
||||
})
|
||||
|
||||
return <>
|
||||
{Previews.filter(p => p.generator === id).map(p => {
|
||||
const data = model?.get(p.path)
|
||||
if (!model || data === undefined || !p.predicate(model)) {
|
||||
return <></>
|
||||
}
|
||||
return p.preview({ lang, model: model!, data, version, shown })
|
||||
})}
|
||||
</>
|
||||
}
|
||||
62
src/app/components/SourcePanel.tsx
Normal file
62
src/app/components/SourcePanel.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { DataModel } from '@mcschema/core'
|
||||
import { ModelPath } from '@mcschema/core'
|
||||
import { useEffect, useRef } from 'preact/hooks'
|
||||
import { useModel } from '../hooks'
|
||||
import { locale } from '../Locales'
|
||||
import { transformOutput } from '../schema/transformOutput'
|
||||
|
||||
type SourcePanelProps = {
|
||||
lang: string,
|
||||
name: string,
|
||||
model: DataModel | null,
|
||||
doCopy?: number,
|
||||
doDownload?: number,
|
||||
doImport?: number,
|
||||
}
|
||||
export function SourcePanel({ lang, name, model, doCopy, doDownload, doImport }: SourcePanelProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
const source = useRef<HTMLTextAreaElement>(null)
|
||||
const download = useRef<HTMLAnchorElement>(null)
|
||||
|
||||
useModel(model, model => {
|
||||
const data = model.schema.hook(transformOutput, new ModelPath(model), model.data)
|
||||
source.current.value = JSON.stringify(data, null, 2) + '\n'
|
||||
})
|
||||
|
||||
const onImport = () => {
|
||||
try {
|
||||
const data = JSON.parse(source.current.value)
|
||||
model?.reset(data, false)
|
||||
} catch (e) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (doCopy && source.current) {
|
||||
source.current.select()
|
||||
document.execCommand('copy')
|
||||
}
|
||||
}, [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])
|
||||
|
||||
return <>
|
||||
<textarea ref={source} class="source" onChange={onImport} spellcheck={false} autocorrect="off" placeholder={loc('source_placeholder')}></textarea>
|
||||
<a ref={download} style="display: none;"></a>
|
||||
</>
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import Split from 'split.js'
|
||||
import { View } from '../views/View';
|
||||
|
||||
export const SplitGroup = (view: View, options: Split.Options, entries: string[]) => `
|
||||
<div class="split-group ${options.direction ?? 'horizontal'}" data-id=${view.register(el => {
|
||||
Split([].slice.call(el.children), { snapOffset: 0, ...options })
|
||||
})}>
|
||||
${entries.join('')}
|
||||
</div>
|
||||
`
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Property } from '../state/Property';
|
||||
import { View } from '../views/View';
|
||||
import { Octicon } from './Octicon';
|
||||
|
||||
export const Toggle = <T>(view: View, entries: [T, keyof typeof Octicon][], state: Property<T>, watcher?: (value: T) => void) => {
|
||||
const activeOcticon = () => Octicon[(entries.find(e => e[0] === state.get()) ?? entries[0])[1]]
|
||||
const toggle = view.register(el => {
|
||||
el.addEventListener('click', () => {
|
||||
const i = entries.findIndex(e => e[0] === state.get())
|
||||
state.set(entries[(i + 1) % entries.length][0])
|
||||
})
|
||||
state.watch(_ => el.innerHTML = activeOcticon(), 'toggle')
|
||||
})
|
||||
return `<div class="toggle" data-id="${toggle}">${activeOcticon()}</div>`
|
||||
}
|
||||
49
src/app/components/Tree.tsx
Normal file
49
src/app/components/Tree.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { DataModel } from '@mcschema/core'
|
||||
import { ModelPath } from '@mcschema/core'
|
||||
import { useEffect, useRef } from 'preact/hooks'
|
||||
import { useModel } from '../hooks'
|
||||
import { locale } from '../Locales'
|
||||
import { Mounter } from '../schema/Mounter'
|
||||
import { renderHtml } from '../schema/renderHtml'
|
||||
import type { VersionId } from '../Schemas'
|
||||
|
||||
type TreePanelProps = {
|
||||
lang: string,
|
||||
model: DataModel | null,
|
||||
version: VersionId,
|
||||
}
|
||||
export function Tree({ lang, model, version }: TreePanelProps) {
|
||||
const tree = useRef<HTMLDivElement>(null)
|
||||
const redraw = useRef<Function>()
|
||||
|
||||
useEffect(() => {
|
||||
redraw.current = () => {
|
||||
if (!model) return
|
||||
const mounter = new Mounter()
|
||||
const props = { loc: locale.bind(null, lang), version, mounter }
|
||||
const path = new ModelPath(model)
|
||||
const rendered = model.schema.hook(renderHtml, path, model.data, props)
|
||||
const category = model.schema.category(path)
|
||||
const type = model.schema.type(path)
|
||||
let html = rendered[2]
|
||||
if (rendered[1]) {
|
||||
html = `<div class="node ${type}-node" ${category ? `data-category="${category}"` : ''}>
|
||||
<div class="node-header">${rendered[1]}</div>
|
||||
<div class="node-body">${rendered[2]}</div>
|
||||
</div>`
|
||||
}
|
||||
tree.current.innerHTML = html
|
||||
mounter.mounted(tree.current)
|
||||
}
|
||||
})
|
||||
|
||||
useModel(model, () => {
|
||||
redraw.current()
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
redraw.current()
|
||||
}, [lang])
|
||||
|
||||
return <div ref={tree} class="tree"></div>
|
||||
}
|
||||
9
src/app/components/index.ts
Normal file
9
src/app/components/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export * from './Ad'
|
||||
export * from './Btn'
|
||||
export * from './BtnInput'
|
||||
export * from './BtnMenu'
|
||||
export * from './Header'
|
||||
export * from './Octicon'
|
||||
export * from './PreviewPanel'
|
||||
export * from './SourcePanel'
|
||||
export * from './Tree'
|
||||
@@ -1,71 +0,0 @@
|
||||
import { DataModel } from '@mcschema/core';
|
||||
import { App } from '../../App';
|
||||
import { Tracker } from '../../Tracker';
|
||||
import { View } from '../../views/View';
|
||||
import { Octicon } from '../Octicon';
|
||||
|
||||
export const PreviewPanel = (view: View, model: DataModel) => {
|
||||
const panel = view.register(el => {
|
||||
const canvas = el.querySelector('canvas')!
|
||||
const redraw = () => {
|
||||
const preview = App.preview.get()
|
||||
if (preview && preview.path && preview.path.withModel(model).get()) {
|
||||
const ctx = canvas.getContext('2d')!
|
||||
const newState = preview.path.withModel(model).get()
|
||||
preview.state = JSON.parse(JSON.stringify(newState))
|
||||
const [width, height] = preview.getSize()
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
const img = ctx.createImageData(width, height)
|
||||
preview.draw(model, img)
|
||||
ctx.putImageData(img, 0, 0)
|
||||
} else {
|
||||
App.preview.set(null)
|
||||
}
|
||||
}
|
||||
const updatePreview = () => {
|
||||
redraw()
|
||||
view.mount(el.querySelector('.panel-controls')!, `
|
||||
${App.preview.get()?.menu(view, redraw) ?? ''}
|
||||
<div class="btn" data-id="${view.onClick(() => {
|
||||
Tracker.hidePreview(); App.preview.set(null)
|
||||
})}">
|
||||
${Octicon.x}
|
||||
</div>`, false)
|
||||
}
|
||||
model.addListener({
|
||||
invalidated: redraw
|
||||
})
|
||||
App.preview.watchRun((value) => {
|
||||
if (value) {
|
||||
value.redraw = redraw
|
||||
updatePreview()
|
||||
}
|
||||
}, 'preview-panel')
|
||||
|
||||
let dragStart: number[] | undefined
|
||||
(el as HTMLCanvasElement).addEventListener('mousedown', evt => {
|
||||
dragStart = [evt.offsetX, evt.offsetY]
|
||||
})
|
||||
;(el as HTMLCanvasElement).addEventListener('mousemove', evt => {
|
||||
if (dragStart === undefined) return
|
||||
if (App.preview.get()?.onDrag) {
|
||||
const [width, height] = App.preview.get()!.getSize()
|
||||
const dx = (evt.offsetX - dragStart[0]) * width / canvas.clientWidth
|
||||
const dy = (evt.offsetY - dragStart[1]) * height / canvas.clientHeight
|
||||
if (!(dx === 0 && dy === 0)) {
|
||||
App.preview.get()?.onDrag(dx, dy)
|
||||
redraw()
|
||||
}
|
||||
}
|
||||
dragStart = [evt.offsetX, evt.offsetY]
|
||||
})
|
||||
;(el as HTMLCanvasElement).addEventListener('mouseup', evt => {
|
||||
dragStart = undefined
|
||||
})
|
||||
})
|
||||
return `<div class="panel preview-panel" data-id="${panel}">
|
||||
<div class="panel-controls"></div>
|
||||
<canvas width="512" height="256">
|
||||
</div>`
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import { DataModel, ModelPath, Path } from '@mcschema/core';
|
||||
import { Tracker } from '../../Tracker';
|
||||
import { transformOutput } from '../../hooks/transformOutput';
|
||||
import { toggleMenu, View } from '../../views/View';
|
||||
import { Octicon } from '../Octicon';
|
||||
import { App } from '../../App';
|
||||
|
||||
export const SourcePanel = (view: View, model: DataModel) => {
|
||||
const updateContent = (el: HTMLTextAreaElement) => {
|
||||
const data = model.schema.hook(transformOutput, new ModelPath(model), model.data);
|
||||
App.jsonOutput.set(JSON.stringify(data, null, 2))
|
||||
el.value = App.jsonOutput.get()
|
||||
}
|
||||
const source = view.register(el => {
|
||||
updateContent(el as HTMLTextAreaElement)
|
||||
model.addListener({
|
||||
invalidated() {
|
||||
App.jsonError.set(null)
|
||||
updateContent(el as HTMLTextAreaElement)
|
||||
}
|
||||
})
|
||||
el.addEventListener('change', () => {
|
||||
const rawSource = (el as HTMLTextAreaElement).value
|
||||
try {
|
||||
model.reset(JSON.parse(rawSource))
|
||||
App.jsonError.set(null)
|
||||
} catch (err) {
|
||||
App.jsonError.set(err.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
const copySource = (el: Element) => {
|
||||
el.closest('.panel')?.getElementsByTagName('textarea')[0].select()
|
||||
document.execCommand('copy');
|
||||
Tracker.copy()
|
||||
}
|
||||
const downloadSource = (el: Element) => {
|
||||
const fileContents = encodeURIComponent(App.jsonOutput.get() + '\n')
|
||||
const downloadAnchor = el.lastElementChild as HTMLAnchorElement
|
||||
downloadAnchor.setAttribute('href', 'data:text/json;charset=utf-8,' + fileContents)
|
||||
downloadAnchor.setAttribute("download", "data.json")
|
||||
downloadAnchor.click()
|
||||
Tracker.download()
|
||||
}
|
||||
const shareSource = (el: Element) => {
|
||||
const shareInput = el.closest('.panel-controls')?.querySelector('input')!
|
||||
const data = btoa(JSON.stringify(JSON.parse(App.jsonOutput.get())))
|
||||
const url = window.location.origin + window.location.pathname + '?q=' + data
|
||||
shareInput.value = url
|
||||
shareInput.style.display = 'inline-block'
|
||||
document.body.addEventListener('click', evt => {
|
||||
shareInput.style.display = 'none'
|
||||
}, { capture: true, once: true })
|
||||
shareInput.select()
|
||||
document.execCommand('copy');
|
||||
Tracker.share()
|
||||
}
|
||||
return `<div class="panel source-panel">
|
||||
<div class="panel-controls">
|
||||
<input style="display: none;">
|
||||
<div class="btn" data-id="${view.onClick(copySource)}">
|
||||
${Octicon.clippy}
|
||||
<span data-i18n="copy"></span>
|
||||
</div>
|
||||
<div class="panel-menu">
|
||||
<div class="btn" data-id="${view.onClick(toggleMenu)}">
|
||||
${Octicon.kebab_horizontal}
|
||||
</div>
|
||||
<div class="panel-menu-list btn-group">
|
||||
<div class="btn" data-id="${view.onClick(downloadSource)}">
|
||||
${Octicon.download}<span data-i18n="download"></span>
|
||||
<a style="diplay: none;"></a>
|
||||
</div>
|
||||
<div class="btn" data-id="${view.onClick(shareSource)}">
|
||||
${Octicon.link}<span data-i18n="share"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<textarea class="source" data-id="${source}" spellcheck="false" autocorrect="off" autocapitalize="off"></textarea>
|
||||
</div>`
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
import { DataModel, ModelPath, Path } from '@mcschema/core';
|
||||
import { App, checkVersion, Previews } from '../../App';
|
||||
import { Tracker } from '../../Tracker'
|
||||
import { toggleMenu, View } from '../../views/View';
|
||||
import { Octicon } from '../Octicon';
|
||||
import { renderHtml } from '../../hooks/renderHtml';
|
||||
import config from '../../../config.json'
|
||||
import { BiomeNoisePreview } from '../../preview/BiomeNoisePreview';
|
||||
import { fetchPreset } from '../../DataFetcher'
|
||||
|
||||
export const TreePanel = (view: View, model: DataModel) => {
|
||||
const getContent = () => {
|
||||
if (App.loaded.get()) {
|
||||
const path = new ModelPath(model)
|
||||
const rendered = model.schema.hook(renderHtml, path, model.data, view)
|
||||
const category = model.schema.category(path)
|
||||
if (rendered[1]) {
|
||||
return `<div class="node ${model.schema.type(path)}-node" ${category ? `data-category="${category}"` : ''}>
|
||||
<div class="node-header">${rendered[1]}</div>
|
||||
<div class="node-body">${rendered[2]}</div>
|
||||
</div>`
|
||||
}
|
||||
return rendered[2]
|
||||
}
|
||||
return '<div class="spinner"></div>'
|
||||
}
|
||||
const tree = view.register(el => {
|
||||
App.loaded.watchRun((value) => {
|
||||
if (!value) {
|
||||
// If loading is taking more than 100 ms, show spinner
|
||||
new Promise(r => setTimeout(r, 100)).then(() => {
|
||||
if (!App.loaded.get()) {
|
||||
view.mount(el, getContent(), false)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
view.mount(el, getContent(), false)
|
||||
}
|
||||
})
|
||||
App.treeMinimized.watch(() => {
|
||||
view.mount(el, getContent(), false)
|
||||
})
|
||||
model.addListener({
|
||||
invalidated() {
|
||||
view.mount(el, getContent(), false)
|
||||
}
|
||||
})
|
||||
;(Previews.biome_noise as BiomeNoisePreview).biomeColors.watch(() => {
|
||||
view.mount(el, getContent(), false)
|
||||
}, 'tree-panel')
|
||||
})
|
||||
const m = App.model.get()
|
||||
const registry = (m?.category ? m?.category + '/' : '') + m?.schema
|
||||
let presetList: Element
|
||||
const presetListId = view.register(el => presetList = el)
|
||||
const getPresets = (query?: string) => {
|
||||
const terms = (query ?? '').trim().split(' ')
|
||||
const results = (App.collections.get()?.get(registry) ?? [])
|
||||
.map(r => r.slice(10))
|
||||
.filter(e => terms.every(t => e.includes(t)))
|
||||
return results.map(r => `<div class="btn" data-id="${view.onClick(async () => {
|
||||
App.schemasLoaded.set(false)
|
||||
const preset = await fetchPreset(config.versions.find(v => v.id === App.version.get())!, m?.path!, r)
|
||||
model.reset(preset)
|
||||
App.schemasLoaded.set(true)
|
||||
Tracker.loadPreset(m?.path! + '/' + r)
|
||||
})}">${r}</div>`).join('')
|
||||
}
|
||||
const presetControl = view.register(el => {
|
||||
App.schemasLoaded.watch(v => {
|
||||
if (!v) return
|
||||
const enabled = (m?.path && checkVersion(App.version.get(), '1.16'))
|
||||
el.classList.toggle('disabled', !enabled || (App.collections.get()?.get(registry) ?? []).length === 0)
|
||||
if (enabled) {
|
||||
view.mount(presetList, getPresets(), false)
|
||||
}
|
||||
}, 'tree-panel')
|
||||
})
|
||||
return `<div class="panel tree-panel">
|
||||
<div class="panel-controls">
|
||||
<div class="panel-menu no-relative" data-id="${presetControl}">
|
||||
<div class="btn" data-id="${view.onClick(el => {
|
||||
toggleMenu(el)
|
||||
el.parentElement?.querySelector('input')?.select()
|
||||
})}">
|
||||
${Octicon.archive}<span data-i18n="presets"></span>
|
||||
</div>
|
||||
<div class="panel-menu-list btn-group">
|
||||
<div class="btn input large-input">
|
||||
${Octicon.search}<input data-id="${view.on('keyup', el => {
|
||||
view.mount(presetList, getPresets((el as HTMLInputElement).value), false)
|
||||
})}">
|
||||
</div>
|
||||
<div class="result-list" data-id="${presetListId}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-menu">
|
||||
<div class="btn" data-id="${view.onClick(toggleMenu)}">
|
||||
${Octicon.tag}
|
||||
<span data-id="${view.register(el => App.version.watch(v => el.textContent = v, 'tree-controls'))}">
|
||||
${App.version.get()}
|
||||
</span>
|
||||
</div>
|
||||
<div class="panel-menu-list btn-group">
|
||||
${config.versions
|
||||
.filter(v => checkVersion(v.id, App.model.get()!.minVersion ?? '1.15'))
|
||||
.reverse()
|
||||
.map(v => `
|
||||
<div class="btn" data-id="${view.onClick(() => {
|
||||
Tracker.setVersion(v.id); App.version.set(v.id)
|
||||
})}">
|
||||
${v.id}
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-menu">
|
||||
<div class="btn" data-id="${view.onClick(toggleMenu)}">
|
||||
${Octicon.kebab_horizontal}
|
||||
</div>
|
||||
<div class="panel-menu-list btn-group">
|
||||
<div class="btn" data-id="${view.onClick(() => {
|
||||
Tracker.reset(); model.reset(model.schema.default())
|
||||
})}">
|
||||
${Octicon.history}<span data-i18n="reset"></span>
|
||||
</div>
|
||||
<div class="btn" data-id="${view.register(el => {
|
||||
el.addEventListener('click', () => {
|
||||
const value = !App.treeMinimized.get()
|
||||
App.treeMinimized.set(value)
|
||||
Tracker.toggleMinimize(value)
|
||||
})
|
||||
App.treeMinimized.watchRun(value => {
|
||||
view.mount(el, `${Octicon[value ? 'unfold' : 'fold']}<span data-i18n="${value ? 'maximize' : 'minimize'}"></span>`, false)
|
||||
})
|
||||
})}"></div>
|
||||
<div class="btn" data-id="${view.onClick(() => {Tracker.undo(); model.undo()})}">
|
||||
${Octicon.arrow_left}<span data-i18n="undo"></span>
|
||||
</div>
|
||||
<div class="btn" data-id="${view.onClick(() => {Tracker.redo(); model.redo()})}">
|
||||
${Octicon.arrow_right}<span data-i18n="redo"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tree" data-id="${tree}"></div>
|
||||
</div>`
|
||||
}
|
||||
88
src/app/components/previews/BiomeSourcePreview.tsx
Normal file
88
src/app/components/previews/BiomeSourcePreview.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { DataModel } from '@mcschema/core'
|
||||
import { useEffect, useRef, useState } from 'preact/hooks'
|
||||
import { Btn } from '..'
|
||||
import { useOnDrag, useOnHover } from '../../hooks'
|
||||
import { biomeSource, getBiome } from '../../previews'
|
||||
import { hexId } from '../../Utils'
|
||||
|
||||
type BiomeSourceProps = {
|
||||
lang: string,
|
||||
model: DataModel,
|
||||
data: any,
|
||||
shown: boolean,
|
||||
}
|
||||
export const BiomeSourcePreview = ({ data, shown }: BiomeSourceProps) => {
|
||||
const [scale, setScale] = useState(2)
|
||||
const [seed, setSeed] = useState(hexId())
|
||||
const [focused, setFocused] = useState<string | undefined>(undefined)
|
||||
const type: string = data.type?.replace(/^minecraft:/, '')
|
||||
|
||||
const canvas = useRef<HTMLCanvasElement>(null)
|
||||
const offset = useRef<[number, number]>([0, 0])
|
||||
const redrawTimeout = useRef(undefined)
|
||||
const redraw = useRef<Function>()
|
||||
const refocus = useRef<Function>()
|
||||
|
||||
useEffect(() => {
|
||||
redraw.current = (res = 4) => {
|
||||
if (type !== 'multi_noise') res = 1
|
||||
const ctx = canvas.current.getContext('2d')!
|
||||
canvas.current.width = 200 / res
|
||||
canvas.current.height = 200 / res
|
||||
const img = ctx.createImageData(canvas.current.width, canvas.current.height)
|
||||
biomeSource(data, img, { biomeColors: {}, offset: offset.current, scale, seed, res })
|
||||
ctx.putImageData(img, 0, 0)
|
||||
if (res !== 1) {
|
||||
clearTimeout(redrawTimeout.current)
|
||||
redrawTimeout.current = setTimeout(() => redraw.current(1), 150) as any
|
||||
}
|
||||
}
|
||||
refocus.current = (x: number, y: number) => {
|
||||
const x2 = x * 200 / canvas.current.clientWidth
|
||||
const y2 = y * 200 / canvas.current.clientHeight
|
||||
const biome = getBiome(data, x2, y2, { biomeColors: {}, offset: offset.current, scale, seed, res: 1 })
|
||||
setFocused(biome)
|
||||
}
|
||||
})
|
||||
|
||||
useOnDrag(canvas.current, (dx, dy) => {
|
||||
const x = dx * canvas.current.width / canvas.current.clientWidth
|
||||
const y = dy * canvas.current.height / canvas.current.clientHeight
|
||||
offset.current = [offset.current[0] + x, offset.current[1] + y]
|
||||
redraw.current()
|
||||
})
|
||||
|
||||
useOnHover(canvas.current, (x, y) => {
|
||||
if (x === undefined || y === undefined) {
|
||||
setFocused(undefined)
|
||||
} else {
|
||||
refocus.current(x, y)
|
||||
}
|
||||
})
|
||||
|
||||
const state = JSON.stringify(data)
|
||||
useEffect(() => {
|
||||
if (shown) {
|
||||
redraw.current()
|
||||
}
|
||||
}, [state, scale, seed, shown])
|
||||
|
||||
const changeScale = (newScale: number) => {
|
||||
offset.current[0] *= scale / newScale
|
||||
offset.current[1] *= scale / newScale
|
||||
setScale(newScale)
|
||||
}
|
||||
|
||||
return <>
|
||||
<div class="controls">
|
||||
{focused && <Btn label={focused} class="no-pointer" />}
|
||||
{(type === 'multi_noise' || type === 'checkerboard') && <>
|
||||
<Btn icon="dash" onClick={() => changeScale(scale * 1.5)} />
|
||||
<Btn icon="plus" onClick={() => changeScale(scale / 1.5)} />
|
||||
</>}
|
||||
{type === 'multi_noise' &&
|
||||
<Btn icon="sync" onClick={() => setSeed(hexId())} />}
|
||||
</div>
|
||||
<canvas ref={canvas} width="200" height="200"></canvas>
|
||||
</>
|
||||
}
|
||||
48
src/app/components/previews/DecoratorPreview.tsx
Normal file
48
src/app/components/previews/DecoratorPreview.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { DataModel } from '@mcschema/core'
|
||||
import { useEffect, useRef, useState } from 'preact/hooks'
|
||||
import { Btn } from '..'
|
||||
import { decorator } from '../../previews'
|
||||
import type { VersionId } from '../../Schemas'
|
||||
import { hexId } from '../../Utils'
|
||||
|
||||
type DecoratorProps = {
|
||||
lang: string,
|
||||
model: DataModel,
|
||||
data: any,
|
||||
version: VersionId,
|
||||
shown: boolean,
|
||||
}
|
||||
export const DecoratorPreview = ({ data, version, shown }: DecoratorProps) => {
|
||||
const [scale, setScale] = useState(4)
|
||||
const [seed, setSeed] = useState(hexId())
|
||||
|
||||
const canvas = useRef<HTMLCanvasElement>(null)
|
||||
const redraw = useRef<Function>()
|
||||
|
||||
useEffect(() => {
|
||||
redraw.current = () => {
|
||||
const ctx = canvas.current.getContext('2d')!
|
||||
canvas.current.width = scale * 16
|
||||
canvas.current.height = scale * 16
|
||||
const img = ctx.createImageData(canvas.current.width, canvas.current.height)
|
||||
decorator(data, img, { seed, version, size: [scale * 16, 128, scale * 16] })
|
||||
ctx.putImageData(img, 0, 0)
|
||||
}
|
||||
})
|
||||
|
||||
const state = JSON.stringify(data)
|
||||
useEffect(() => {
|
||||
if (shown) {
|
||||
setTimeout(() => redraw.current())
|
||||
}
|
||||
}, [state, scale, seed, shown])
|
||||
|
||||
return <>
|
||||
<div class="controls">
|
||||
<Btn icon="dash" onClick={() => setScale(Math.min(16, scale + 1))} />
|
||||
<Btn icon="plus" onClick={() => setScale(Math.max(1, scale - 1))} />
|
||||
<Btn icon="sync" onClick={() => setSeed(hexId())} />
|
||||
</div>
|
||||
<canvas ref={canvas} width="64" height="64"></canvas>
|
||||
</>
|
||||
}
|
||||
60
src/app/components/previews/NoiseSettingsPreview.tsx
Normal file
60
src/app/components/previews/NoiseSettingsPreview.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { DataModel } from '@mcschema/core'
|
||||
import { useEffect, useRef, useState } from 'preact/hooks'
|
||||
import { Btn, BtnInput, BtnMenu } from '..'
|
||||
import { useOnDrag } from '../../hooks'
|
||||
import { locale } from '../../Locales'
|
||||
import { noiseSettings } from '../../previews'
|
||||
import { hexId } from '../../Utils'
|
||||
|
||||
type NoiseSettingsProps = {
|
||||
lang: string,
|
||||
model: DataModel,
|
||||
data: any,
|
||||
shown: boolean,
|
||||
}
|
||||
export const NoiseSettingsPreview = ({ lang, data, shown }: NoiseSettingsProps) => {
|
||||
const loc = locale.bind(null, lang)
|
||||
const [seed, setSeed] = useState(hexId())
|
||||
const [biomeDepth, setBiomeDepth] = useState(0.1)
|
||||
const [biomeScale, setBiomeScale] = useState(0.2)
|
||||
|
||||
const canvas = useRef<HTMLCanvasElement>(null)
|
||||
const offset = useRef<number>(0)
|
||||
const redraw = useRef<Function>()
|
||||
|
||||
useEffect(() => {
|
||||
redraw.current = () => {
|
||||
const ctx = canvas.current.getContext('2d')!
|
||||
const size = data.height
|
||||
canvas.current.width = size
|
||||
canvas.current.height = size
|
||||
const img = ctx.createImageData(canvas.current.width, canvas.current.height)
|
||||
noiseSettings(data, img, { biomeDepth, biomeScale, offset: offset.current, width: size, seed })
|
||||
ctx.putImageData(img, 0, 0)
|
||||
}
|
||||
})
|
||||
|
||||
useOnDrag(canvas.current, (dx) => {
|
||||
const x = dx * canvas.current.width / canvas.current.clientWidth
|
||||
offset.current = offset.current + x
|
||||
redraw.current()
|
||||
})
|
||||
|
||||
const state = JSON.stringify(data)
|
||||
useEffect(() => {
|
||||
if (shown) {
|
||||
redraw.current()
|
||||
}
|
||||
}, [state, biomeDepth, biomeScale, seed, shown])
|
||||
|
||||
return <>
|
||||
<div class="controls">
|
||||
<BtnMenu icon="gear">
|
||||
<BtnInput type="number" label={loc('preview.depth')} value={`${biomeDepth}`} onChange={v => setBiomeDepth(Number(v))} />
|
||||
<BtnInput type="number" label={loc('preview.scale')} value={`${biomeScale}`} onChange={v => setBiomeScale(Number(v))} />
|
||||
</BtnMenu>
|
||||
<Btn icon="sync" onClick={() => setSeed(hexId())} />
|
||||
</div>
|
||||
<canvas ref={canvas} width="200" height={data.height}></canvas>
|
||||
</>
|
||||
}
|
||||
3
src/app/components/previews/index.ts
Normal file
3
src/app/components/previews/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './BiomeSourcePreview'
|
||||
export * from './DecoratorPreview'
|
||||
export * from './NoiseSettingsPreview'
|
||||
Reference in New Issue
Block a user