mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-24 23:56:51 +00:00
Use ace editor and remove output limit
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
import { DataModel } from '@mcschema/core'
|
||||
import brace from 'brace'
|
||||
import 'brace/mode/json'
|
||||
import 'brace/mode/yaml'
|
||||
import json from 'comment-json'
|
||||
import yaml from 'js-yaml'
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
||||
@@ -10,8 +13,6 @@ import type { BlockStateRegistry } from '../../services'
|
||||
import { Store } from '../../Store'
|
||||
import { message } from '../../Utils'
|
||||
|
||||
const OUTPUT_CHARS_LIMIT = 10000
|
||||
|
||||
const INDENT: Record<string, number | string | undefined> = {
|
||||
'2_spaces': 2,
|
||||
'4_spaces': 4,
|
||||
@@ -50,9 +51,11 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
||||
const { locale } = useLocale()
|
||||
const [indent, setIndent] = useState(Store.getIndent())
|
||||
const [format, setFormat] = useState(Store.getFormat())
|
||||
const source = useRef<HTMLTextAreaElement>(null)
|
||||
const download = useRef<HTMLAnchorElement>(null)
|
||||
const retransform = useRef<Function>()
|
||||
const onImport = useRef<(e: any) => any>()
|
||||
|
||||
const editor = useRef<brace.Editor>()
|
||||
|
||||
const getSerializedOutput = useCallback((model: DataModel, blockStates: BlockStateRegistry) => {
|
||||
const data = getOutput(model, blockStates)
|
||||
@@ -64,18 +67,38 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
||||
if (!model || !blockStates) return
|
||||
try {
|
||||
const output = getSerializedOutput(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
|
||||
}
|
||||
editor.current.getSession().setValue(output)
|
||||
} catch (e) {
|
||||
onError(`Error getting JSON output: ${message(e)}`)
|
||||
console.error(e)
|
||||
source.current.value = ''
|
||||
editor.current.setValue('')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onImport.current = () => {
|
||||
const value = editor.current.getValue()
|
||||
if (value.length === 0) return
|
||||
try {
|
||||
const data = FORMATS[format].parse(value)
|
||||
model?.reset(DataModel.wrapLists(data), false)
|
||||
} catch (e) {
|
||||
onError(`Error importing: ${message(e)}`)
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}, [model, blockStates, indent, format])
|
||||
|
||||
useEffect(() => {
|
||||
editor.current = brace.edit('editor')
|
||||
editor.current.setOptions({
|
||||
fontSize: 14,
|
||||
showFoldWidgets: false,
|
||||
highlightSelectedWord: false,
|
||||
})
|
||||
editor.current.$blockScrolling = Infinity
|
||||
editor.current.on('blur', e => onImport.current(e))
|
||||
editor.current.getSession().setMode('ace/mode/json')
|
||||
}, [])
|
||||
|
||||
useModel(model, () => {
|
||||
retransform.current()
|
||||
@@ -85,20 +108,12 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
||||
}, [model])
|
||||
|
||||
useEffect(() => {
|
||||
const softTabs = indent === 'tabs' ? false : INDENT[indent]
|
||||
editor.current.setOption('useSoftTabs', softTabs)
|
||||
editor.current.getSession().setMode(`ace/mode/${format}`)
|
||||
retransform.current()
|
||||
}, [indent, format])
|
||||
|
||||
const onImport = () => {
|
||||
if (source.current.value.length === 0) return
|
||||
try {
|
||||
const data = FORMATS[format].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(getSerializedOutput(model, blockStates)).then(() => {
|
||||
@@ -117,9 +132,9 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
||||
}, [doDownload])
|
||||
|
||||
useEffect(() => {
|
||||
if (doImport && source.current) {
|
||||
source.current.value = ''
|
||||
source.current.select()
|
||||
if (doImport && editor.current) {
|
||||
editor.current.setValue('')
|
||||
editor.current.selectAll()
|
||||
}
|
||||
}, [doImport])
|
||||
|
||||
@@ -134,7 +149,7 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
||||
}
|
||||
|
||||
return <>
|
||||
<div class="controls">
|
||||
<div class="controls source-controls">
|
||||
<BtnMenu icon="gear" tooltip={locale('output_settings')} data-cy="source-controls">
|
||||
{Object.entries(INDENT).map(([key]) =>
|
||||
<Btn label={locale(`indentation.${key}`)} active={indent === key}
|
||||
@@ -146,7 +161,7 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm
|
||||
onClick={() => changeFormat(key)} />)}
|
||||
</BtnMenu>
|
||||
</div>
|
||||
<textarea ref={source} class="source" onBlur={onImport} spellcheck={false} autocorrect="off" placeholder={locale('source_placeholder', format.toUpperCase())} data-cy="import-area"></textarea>
|
||||
<pre id="editor" class="source"></pre>
|
||||
<a ref={download} style="display: none;"></a>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -20,12 +20,16 @@
|
||||
--nav-hover: #b4b3b0;
|
||||
--nav-faded: #4d4c4c;
|
||||
--nav-faded-hover: #6e6e6e;
|
||||
--selection: #6786dd99;
|
||||
--selection: #445a9599;
|
||||
--errors-background: #62190f;
|
||||
--errors-text: #ffffffcc;
|
||||
--invalid-text: #fd7951;
|
||||
--text-saturation: 60%;
|
||||
--text-lightness: 45%;
|
||||
--editor-variable: #9CDCFE;
|
||||
--editor-string: #CE9178;
|
||||
--editor-constant: #569CD6;
|
||||
--editor-number: #B5CEA8;
|
||||
}
|
||||
|
||||
:root[data-theme=light] {
|
||||
@@ -56,6 +60,10 @@
|
||||
--invalid-text: #a32600;
|
||||
--text-saturation: 100%;
|
||||
--text-lightness: 30%;
|
||||
--editor-variable: #0451A5;
|
||||
--editor-string: #A31515;
|
||||
--editor-constant: #0000FF;
|
||||
--editor-number: #098658;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
@@ -307,6 +315,7 @@ main > .controls {
|
||||
|
||||
.source-controls {
|
||||
justify-content: flex-end;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.tree {
|
||||
@@ -1249,6 +1258,49 @@ hr {
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
|
||||
.ace_editor,
|
||||
.ace_gutter,
|
||||
.ace_gutter .ace_layer,
|
||||
.ace_content {
|
||||
color: var(--text-2) !important;
|
||||
background-color: var(--background-2) !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ace_cursor {
|
||||
color: var(--text-1) !important;
|
||||
}
|
||||
|
||||
.ace_gutter-active-line {
|
||||
background-color: var(--background-3) !important;
|
||||
}
|
||||
|
||||
.ace_tag,
|
||||
.ace_variable {
|
||||
color: var(--editor-variable) !important;
|
||||
}
|
||||
|
||||
.ace_string {
|
||||
color: var(--editor-string) !important;
|
||||
}
|
||||
|
||||
.ace_constant {
|
||||
color: var(--editor-constant) !important;
|
||||
}
|
||||
|
||||
.ace_numeric {
|
||||
color: var(--editor-number) !important;
|
||||
}
|
||||
|
||||
.ace_markup,
|
||||
.ace_keyword {
|
||||
color: unset !important;
|
||||
}
|
||||
|
||||
.ace_marker-layer .ace_selection {
|
||||
background-color: var(--selection) !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 720px) {
|
||||
.sound-search-group {
|
||||
margin-bottom: 8px;
|
||||
|
||||
Reference in New Issue
Block a user