Skip to content

Commit 798f797

Browse files
committed
Improve editing UX and save chunks
1 parent 9970bbf commit 798f797

9 files changed

+211
-77
lines changed

editor/res/editor.css

+44-18
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,26 @@ html, body {
1616
}
1717

1818
.nbt-content {
19-
padding-top: 30px;
19+
padding: 30px 0;
2020
}
2121

2222
.nbt-tag {
2323
height: 22px;
2424
cursor: default;
2525
user-select: none;
26+
overflow: hidden;
27+
display: flex;
28+
align-items: center;
2629
}
2730

2831
.nbt-tag:hover {
2932
background: #252526;
3033
}
3134

35+
.nbt-tag.selected {
36+
background-color: #3a3321;
37+
}
38+
3239
.nbt-tag:not(.collapse) {
3340
position: relative;
3441
padding-left: 13px;
@@ -37,22 +44,21 @@ html, body {
3744
}
3845

3946
div:last-child > .nbt-tag {
40-
border-color: transparent;
47+
border: none;
4148
}
4249

4350
.nbt-tag:not(.collapse)::before {
4451
content: '';
4552
position: absolute;
4653
width: 9px;
47-
height: 8px;
54+
height: 10px;
4855
top: 0;
49-
left: -1px;
56+
left: 0;
5057
border-bottom: 1px #777 solid;
5158
}
5259

5360
div:last-child > .nbt-tag:not(.collapse)::before {
5461
border-left: 1px #777 solid;
55-
width: 10px;
5662
}
5763

5864
.nbt-body {
@@ -69,6 +75,11 @@ div:last-child > .nbt-body {
6975
font-style: italic;
7076
}
7177

78+
.nbt-key {
79+
margin-right: 5px;
80+
line-height: 1;
81+
}
82+
7283
.nbt-value {
7384
font-family: var(--monospace);
7485
font-size: 14px;
@@ -78,10 +89,13 @@ input.nbt-value {
7889
background-color: #3c3c3c;
7990
border: none;
8091
color: var(--vscode-editor-foreground);
92+
width: 250px;
8193
}
8294

8395
span.nbt-value {
84-
margin-left: 2px;
96+
text-overflow: ellipsis;
97+
overflow: hidden;
98+
margin: 0 2px;
8599
}
86100

87101
.center {
@@ -108,6 +122,7 @@ span.nbt-value {
108122
font-family: monospace;
109123
font-size: 12px;
110124
font-weight: bold;
125+
margin-right: 5px;
111126
}
112127

113128
.nbt-icon {
@@ -116,7 +131,8 @@ span.nbt-value {
116131
background-size: 64px;
117132
width: 16px;
118133
height: 16px;
119-
margin-bottom: -3px;
134+
min-width: 16px;
135+
margin-right: 5px;
120136
}
121137

122138
.nbt-icon.byte-icon { background-position: 0 0; }
@@ -162,10 +178,6 @@ span.nbt-value {
162178
display: flex;
163179
}
164180

165-
.panel-menu > *:not(:last-child) {
166-
margin-right: 5px;
167-
}
168-
169181
.menu-spacer {
170182
margin: 0 10px;
171183
}
@@ -184,6 +196,10 @@ span.nbt-value {
184196
color: #d8b24a;
185197
}
186198

199+
.btn.disabled {
200+
display: none;
201+
}
202+
187203
.spinner {
188204
position: absolute;
189205
top: 80px;
@@ -234,11 +250,25 @@ span.nbt-value {
234250
}
235251

236252
.side-panel {
237-
background-color: #252526;
253+
background-color: #252526be;
238254
position: absolute;
239-
right: 20px;
240-
top: 5px;
255+
left: 20px;
256+
top: 37px;
241257
padding: 4px 8px;
258+
max-width: 400px;
259+
overflow: hidden;
260+
}
261+
262+
.side-panel .nbt-content {
263+
padding: 10px 0 0;
264+
}
265+
266+
.side-panel .nbt-tag:hover {
267+
background: #333333;
268+
}
269+
270+
.side-panel .nbt-tag.selected {
271+
background-color: #423a27;
242272
}
243273

244274
.block-name {
@@ -268,10 +298,6 @@ span.nbt-value {
268298
margin-bottom: 2px;
269299
}
270300

271-
.side-panel .nbt-content {
272-
padding-top: 10px;
273-
}
274-
275301
.structure-size input {
276302
width: 50px;
277303
background: #3c3c3c;

editor/src/Editor.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ import { SnbtEditor } from "./SnbtEditor";
44
import { StructureEditor } from "./StructureEditor";
55
import { TreeEditor } from "./TreeEditor";
66

7-
export type VsCode = {
7+
export type VSCode = {
88
postMessage(message: EditorMessage): void
99
}
1010

11-
declare function acquireVsCodeApi(): VsCode
11+
declare function acquireVsCodeApi(): VSCode
1212
const vscode = acquireVsCodeApi();
1313

1414
const root = document.querySelector('.nbt-editor')
1515

1616
const LOCALES = {
1717
'copy': 'Copy',
18+
'editTag': 'Edit Tag',
1819
'grid': 'Show Grid',
1920
'panel.structure': '3D',
2021
'panel.region': 'Region',
@@ -44,6 +45,8 @@ export interface EditorPanel {
4445
menu?(): Element[]
4546
}
4647

48+
export type EditHandler = (edit: NbtEdit) => void
49+
4750
class Editor {
4851
private panels: {
4952
[key: string]: {
@@ -53,18 +56,19 @@ class Editor {
5356
}
5457
} = {
5558
'structure': {
56-
editor: lazy(() => new StructureEditor(root, vscode)),
59+
editor: lazy(() => new StructureEditor(root, vscode, this.makeEdit)),
5760
options: ['structure', 'tree', 'snbt']
5861
},
5962
'region': {
60-
editor: lazy(() => new RegionEditor(root, vscode))
63+
editor: lazy(() => new RegionEditor(root, vscode, this.makeEdit)),
64+
options: ['region']
6165
},
6266
'tree': {
63-
editor: lazy(() => new TreeEditor(root, vscode)),
67+
editor: lazy(() => new TreeEditor(root, vscode, this.makeEdit)),
6468
options: ['tree', 'snbt']
6569
},
6670
'snbt': {
67-
editor: lazy(() => new SnbtEditor(root, vscode))
71+
editor: lazy(() => new SnbtEditor(root, vscode, this.makeEdit))
6872
}
6973
}
7074

@@ -145,6 +149,10 @@ class Editor {
145149
}
146150
}
147151

152+
private makeEdit(edit: NbtEdit) {
153+
vscode.postMessage({ type: 'edit', body: edit })
154+
}
155+
148156
private applyEdit(edit: NbtEdit) {
149157
for (let i = 0; i < edit.ops.length; i += 1) {
150158
const op = edit.ops[i];

editor/src/NbtPath.ts

+50-15
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,65 @@
1+
import { NamedNbtTag } from "@webmc/nbt";
2+
13
export class NbtPath {
24
private arr: (string | number)[]
35

4-
constructor(arr: (string | number)[] = []) {
5-
this.arr = arr;
6+
constructor(arr: (string | number)[] = []) {
7+
this.arr = arr;
8+
}
9+
10+
public pop(count = 1) {
11+
return new NbtPath(this.arr.slice(0, -count))
612
}
713

8-
public pop(count = 1) {
9-
return new NbtPath(this.arr.slice(0, -count))
14+
public shift(count = 1) {
15+
return new NbtPath(this.arr.slice(count))
1016
}
1117

1218
public push(...el: (string | number)[]) {
13-
return new NbtPath([...this.arr, ...el])
19+
return new NbtPath([...this.arr, ...el])
20+
}
21+
22+
public head() {
23+
return this.arr[0]
1424
}
1525

16-
public last() {
17-
return this.arr[this.arr.length - 1]
26+
public last() {
27+
return this.arr[this.arr.length - 1]
1828
}
1929

20-
public length() {
21-
return this.arr.length
30+
public length() {
31+
return this.arr.length
2232
}
2333

24-
public toString() {
25-
return this.arr
26-
.map(e => (typeof e === 'string') ? `.${e}` : `[${e}]`)
27-
.join('')
28-
.replace(/^\./, '')
29-
}
34+
public toString() {
35+
return this.arr
36+
.map(e => (typeof e === 'string') ? `.${e}` : `[${e}]`)
37+
.join('')
38+
.replace(/^\./, '')
39+
}
40+
41+
public getFrom(data: NamedNbtTag) {
42+
console.log(this.arr, data)
43+
let value = data.value as any
44+
let type = 'compound'
45+
46+
for (const el of this.arr) {
47+
if (type === 'compound' && typeof el === 'string') {
48+
type = value[el].type
49+
value = value[el].value
50+
} else if (type === 'list' && typeof el === 'number') {
51+
type = value.type
52+
value = value.value[el]
53+
} else if (type.endsWith('Array') && typeof el === 'number') {
54+
type = type.slice(-5)
55+
value = value[el]
56+
} else {
57+
throw new Error(`Invalid path ${this.toString()}`)
58+
}
59+
if (value === undefined) {
60+
throw new Error(`Invalid path ${this.toString()}`)
61+
}
62+
}
63+
return { type, value }
64+
}
3065
}

editor/src/RegionEditor.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { NbtChunk } from "@webmc/nbt";
22
import { NbtFile, ViewMessage } from "../../src/types";
3-
import { VsCode } from "./Editor";
3+
import { EditHandler, VSCode } from "./Editor";
44
import { NbtPath } from "./NbtPath";
55
import { TreeEditor } from "./TreeEditor";
66

77
export class RegionEditor extends TreeEditor {
88
private chunks: Partial<NbtChunk>[]
99

10-
constructor(root: Element, vscode: VsCode) {
11-
super(root, vscode)
10+
constructor(root: Element, vscode: VSCode, editHandler: EditHandler) {
11+
super(root, vscode, editHandler)
1212
}
1313

1414
redraw() {
@@ -41,8 +41,13 @@ export class RegionEditor extends TreeEditor {
4141

4242
private drawChunk(path: NbtPath, chunk: Partial<NbtChunk>) {
4343
const expanded = chunk.nbt && this.isExpanded(path);
44-
return `<div class="nbt-tag collapse" ${this.on('click', el => this.clickChunk(path, chunk, el))}>
45-
${this.drawCollapse(path)}
44+
return `<div class="nbt-tag collapse" ${this.onLoad(el => {
45+
el.addEventListener('click', () => this.select({
46+
path, type: 'compound', data: () => path.shift().getFrom(this.chunks[path.head()].nbt), el
47+
}))
48+
el.addEventListener('dblclick', () => this.clickChunk(path, chunk, el))
49+
})}>
50+
${this.drawCollapse(path, 'compound', (el) => this.clickChunk(path, chunk, el.parentElement))}
4651
${this.drawIcon('chunk')}
4752
<span class="nbt-key">Chunk [${chunk.x}, ${chunk.z}]</span>
4853
</div>

editor/src/SnbtEditor.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { NbtFile } from "../../src/types";
2-
import { EditorPanel, locale, VsCode } from "./Editor";
2+
import { EditHandler, EditorPanel, locale, VSCode } from "./Editor";
33
import { Snbt } from "./Snbt";
44

55
export class SnbtEditor implements EditorPanel {
66
private snbt: string
77

8-
constructor(private root: Element, private vscode: VsCode) {
8+
constructor(private root: Element, private vscode: VSCode, private editHandler: EditHandler) {
99
this.snbt = ''
1010
}
1111

@@ -15,7 +15,7 @@ export class SnbtEditor implements EditorPanel {
1515
const textarea = document.createElement('textarea')
1616
textarea.classList.add('snbt-editor')
1717
textarea.textContent = this.snbt
18-
textarea.rows = (this.snbt.match(/\n/g)?.length ?? 0) + 2
18+
textarea.rows = (this.snbt.match(/\n/g)?.length ?? 0) + 1
1919
textarea.readOnly = true
2020
content.append(textarea)
2121
this.root.append(content)

0 commit comments

Comments
 (0)