Start on editor UI; introduce some tools; implement adjust and force floors
|
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B |
|
Before Width: | Height: | Size: 319 B After Width: | Height: | Size: 319 B |
BIN
icons/compat-notcc2.png
Normal file
|
After Width: | Height: | Size: 716 B |
BIN
icons/help.png
Normal file
|
After Width: | Height: | Size: 266 B |
BIN
icons/tool-adjust.png
Normal file
|
After Width: | Height: | Size: 299 B |
BIN
icons/tool-bg-selected.png
Normal file
|
After Width: | Height: | Size: 115 B |
BIN
icons/tool-bg-unselected.png
Normal file
|
After Width: | Height: | Size: 158 B |
BIN
icons/tool-box.png
Normal file
|
After Width: | Height: | Size: 155 B |
BIN
icons/tool-connect.png
Normal file
|
After Width: | Height: | Size: 276 B |
BIN
icons/tool-fill.png
Normal file
|
After Width: | Height: | Size: 207 B |
BIN
icons/tool-force-floors.png
Normal file
|
After Width: | Height: | Size: 226 B |
BIN
icons/tool-line.png
Normal file
|
After Width: | Height: | Size: 154 B |
BIN
icons/tool-pencil.png
Normal file
|
After Width: | Height: | Size: 184 B |
BIN
icons/tool-wire.png
Normal file
|
After Width: | Height: | Size: 260 B |
24
index.html
@ -106,6 +106,30 @@
|
|||||||
</header>
|
</header>
|
||||||
<div class="level"><!-- level canvas and any overlays go here --></div>
|
<div class="level"><!-- level canvas and any overlays go here --></div>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
|
<!--
|
||||||
|
<p style>
|
||||||
|
Tip: Right click to color drop.<br>
|
||||||
|
Tip: Ctrl-click with terrain to replace only the current tile's terrain, rather than overwriting the whole tile.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Layer: [all/auto] [terrain] [item] [actor] [overlay]</p>
|
||||||
|
|
||||||
|
<p>Actor direction: [north] [south] [east] [west]</p>
|
||||||
|
|
||||||
|
<p>[ ] Show connections</p>
|
||||||
|
<p>[ ] Toggle green objects</p>
|
||||||
|
<p>[ ] Show monster pathing</p>
|
||||||
|
<p>[ ] Show circuits???</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
Metadata:
|
||||||
|
xxx / yyy chips required
|
||||||
|
Time limit: [____]
|
||||||
|
Title: [__________]
|
||||||
|
Author: [__________]
|
||||||
|
map size
|
||||||
|
</pre>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
<div class="palette"></div>
|
<div class="palette"></div>
|
||||||
<!-- TODO:
|
<!-- TODO:
|
||||||
|
|||||||
297
js/main.js
@ -1,6 +1,6 @@
|
|||||||
// TODO bugs and quirks i'm aware of:
|
// TODO bugs and quirks i'm aware of:
|
||||||
// - steam: if a player character starts on a force floor they won't be able to make any voluntary movements until they are no longer on a force floor
|
// - steam: if a player character starts on a force floor they won't be able to make any voluntary movements until they are no longer on a force floor
|
||||||
import { TICS_PER_SECOND } from './defs.js';
|
import { DIRECTIONS, TICS_PER_SECOND } from './defs.js';
|
||||||
import * as c2m from './format-c2m.js';
|
import * as c2m from './format-c2m.js';
|
||||||
import * as dat from './format-dat.js';
|
import * as dat from './format-dat.js';
|
||||||
import * as format_util from './format-util.js';
|
import * as format_util from './format-util.js';
|
||||||
@ -639,6 +639,119 @@ class Player extends PrimaryView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const EDITOR_TOOLS = [{
|
||||||
|
mode: 'pencil',
|
||||||
|
icon: 'icons/tool-pencil.png',
|
||||||
|
name: "Pencil",
|
||||||
|
desc: "Draw individual tiles",
|
||||||
|
}, {
|
||||||
|
mode: 'line',
|
||||||
|
icon: 'icons/tool-line.png',
|
||||||
|
name: "Line",
|
||||||
|
desc: "Draw straight lines",
|
||||||
|
}, {
|
||||||
|
mode: 'box',
|
||||||
|
icon: 'icons/tool-box.png',
|
||||||
|
name: "Box",
|
||||||
|
desc: "Fill a rectangular area with tiles",
|
||||||
|
}, {
|
||||||
|
mode: 'fill',
|
||||||
|
icon: 'icons/tool-fill.png',
|
||||||
|
name: "Fill",
|
||||||
|
desc: "Flood-fill an area with tiles",
|
||||||
|
}, {
|
||||||
|
mode: 'force-floors',
|
||||||
|
icon: 'icons/tool-force-floors.png',
|
||||||
|
name: "Force floors",
|
||||||
|
desc: "Draw force floors in the direction you draw",
|
||||||
|
}, {
|
||||||
|
mode: 'adjust',
|
||||||
|
icon: 'icons/tool-adjust.png',
|
||||||
|
name: "Adjust",
|
||||||
|
desc: "Toggle blocks and rotate actors",
|
||||||
|
}, {
|
||||||
|
mode: 'connect',
|
||||||
|
icon: 'icons/tool-connect.png',
|
||||||
|
name: "Connect",
|
||||||
|
desc: "Set up CC1 clone and trap connections",
|
||||||
|
}, {
|
||||||
|
mode: 'wire',
|
||||||
|
icon: 'icons/tool-wire.png',
|
||||||
|
name: "Wire",
|
||||||
|
desc: "Draw CC2 wiring",
|
||||||
|
// TODO text tool; thin walls tool; map generator?; subtools for select tool (copy, paste, crop)
|
||||||
|
}];
|
||||||
|
// Tiles the "adjust" tool will turn into each other
|
||||||
|
const EDITOR_ADJUST_TOGGLES = {
|
||||||
|
floor: 'wall',
|
||||||
|
wall: 'floor',
|
||||||
|
floor_custom_green: 'wall_custom_green',
|
||||||
|
floor_custom_pink: 'wall_custom_pink',
|
||||||
|
floor_custom_yellow: 'wall_custom_yellow',
|
||||||
|
floor_custom_blue: 'wall_custom_blue',
|
||||||
|
wall_custom_green: 'floor_custom_green',
|
||||||
|
wall_custom_pink: 'floor_custom_pink',
|
||||||
|
wall_custom_yellow: 'floor_custom_yellow',
|
||||||
|
wall_custom_blue: 'floor_custom_blue',
|
||||||
|
fake_floor: 'fake_wall',
|
||||||
|
fake_wall: 'fake_floor',
|
||||||
|
wall_invisible: 'wall_appearing',
|
||||||
|
wall_appearing: 'wall_invisible',
|
||||||
|
green_floor: 'green_wall',
|
||||||
|
green_wall: 'green_floor',
|
||||||
|
green_bomb: 'green_chip',
|
||||||
|
green_chip: 'green_bomb',
|
||||||
|
purple_floor: 'purple_wall',
|
||||||
|
purple_wall: 'purple_floor',
|
||||||
|
thief_keys: 'thief_tools',
|
||||||
|
thief_tools: 'thief_keys',
|
||||||
|
};
|
||||||
|
// TODO this MUST use a cc2 tileset!
|
||||||
|
const EDITOR_PALETTE = [{
|
||||||
|
title: "Our hero",
|
||||||
|
tiles: ['player'],
|
||||||
|
}, {
|
||||||
|
title: "Terrain",
|
||||||
|
tiles: [
|
||||||
|
'floor', 'wall', 'hint', 'socket', 'exit',
|
||||||
|
'popwall',
|
||||||
|
'fake_floor', 'fake_wall',
|
||||||
|
'gravel',
|
||||||
|
'dirt',
|
||||||
|
'water', 'turtle', 'fire',
|
||||||
|
'ice', 'ice_nw', 'ice_ne', 'ice_sw', 'ice_se',
|
||||||
|
'force_floor_n', 'force_floor_s', 'force_floor_w', 'force_floor_e', 'force_floor_all',
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
title: "Items",
|
||||||
|
tiles: [
|
||||||
|
'chip', 'chip_extra',
|
||||||
|
'key_blue', 'key_red', 'key_yellow', 'key_green',
|
||||||
|
'flippers', 'fire_boots', 'cleats', 'suction_boots',
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
title: "Creatures",
|
||||||
|
tiles: [
|
||||||
|
'tank_blue',
|
||||||
|
'ball',
|
||||||
|
'fireball',
|
||||||
|
'glider',
|
||||||
|
'bug',
|
||||||
|
'paramecium',
|
||||||
|
'walker',
|
||||||
|
'teeth',
|
||||||
|
'blob',
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
title: "Mechanisms",
|
||||||
|
tiles: [
|
||||||
|
'bomb',
|
||||||
|
'dirt_block',
|
||||||
|
'button_red',
|
||||||
|
'cloner',
|
||||||
|
'trap',
|
||||||
|
],
|
||||||
|
}];
|
||||||
class Editor extends PrimaryView {
|
class Editor extends PrimaryView {
|
||||||
constructor(conductor) {
|
constructor(conductor) {
|
||||||
super(conductor, document.body.querySelector('main#editor'));
|
super(conductor, document.body.querySelector('main#editor'));
|
||||||
@ -655,12 +768,38 @@ class Editor extends PrimaryView {
|
|||||||
let [x, y] = this.renderer.cell_coords_from_event(ev);
|
let [x, y] = this.renderer.cell_coords_from_event(ev);
|
||||||
this.mouse_cell = [x, y];
|
this.mouse_cell = [x, y];
|
||||||
|
|
||||||
this.place_in_cell(x, y, this.palette_selection);
|
if (this.current_tool === 'pencil') {
|
||||||
|
this.place_in_cell(x, y, this.palette_selection);
|
||||||
|
}
|
||||||
|
else if (this.current_tool === 'force-floors') {
|
||||||
|
// Begin by placing an all-way force floor under the mouse
|
||||||
|
this.place_in_cell(x, y, 'force_floor_all');
|
||||||
|
}
|
||||||
|
else if (this.current_tool === 'adjust') {
|
||||||
|
let cell = this.stored_level.cells[y][x];
|
||||||
|
for (let tile of cell) {
|
||||||
|
// Toggle tiles that go in obvious pairs
|
||||||
|
let other = EDITOR_ADJUST_TOGGLES[tile.name];
|
||||||
|
if (other) {
|
||||||
|
tile.name = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate actors
|
||||||
|
if (TILE_TYPES[tile.name].is_actor) {
|
||||||
|
tile.direction = DIRECTIONS[tile.direction].right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
this.renderer.draw();
|
this.renderer.draw();
|
||||||
});
|
});
|
||||||
this.renderer.canvas.addEventListener('mousemove', ev => {
|
this.renderer.canvas.addEventListener('mousemove', ev => {
|
||||||
if (this.mouse_mode === null)
|
if (this.mouse_mode === null)
|
||||||
return;
|
return;
|
||||||
|
// TODO check for the specific button we're holding
|
||||||
|
if (ev.buttons === 0) {
|
||||||
|
this.mouse_mode = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.mouse_mode === 'draw') {
|
if (this.mouse_mode === 'draw') {
|
||||||
// FIXME also fill in a trail between previous cell and here, mousemove is not fired continuously
|
// FIXME also fill in a trail between previous cell and here, mousemove is not fired continuously
|
||||||
@ -669,8 +808,64 @@ class Editor extends PrimaryView {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO do a pixel-perfect draw too
|
// TODO do a pixel-perfect draw too
|
||||||
for (let [cx, cy] of walk_grid(this.mouse_cell[0], this.mouse_cell[1], x, y)) {
|
if (this.current_tool === 'pencil') {
|
||||||
this.place_in_cell(cx, cy, this.palette_selection);
|
for (let [cx, cy] of walk_grid(this.mouse_cell[0], this.mouse_cell[1], x, y)) {
|
||||||
|
this.place_in_cell(cx, cy, this.palette_selection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this.current_tool === 'force-floors') {
|
||||||
|
// Walk the mouse movement and change each we touch to match the direction we
|
||||||
|
// crossed the border
|
||||||
|
let i = 0;
|
||||||
|
let prevx, prevy;
|
||||||
|
for (let [cx, cy] of walk_grid(this.mouse_cell[0], this.mouse_cell[1], x, y)) {
|
||||||
|
i++;
|
||||||
|
// The very first cell is the one the mouse was already in, and we don't
|
||||||
|
// have a movement direction yet, so leave that alone
|
||||||
|
if (i === 1) {
|
||||||
|
prevx = cx;
|
||||||
|
prevy = cy;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let name;
|
||||||
|
let cell = this.stored_level.cells[cy][cx];
|
||||||
|
if (cell[0].name.startsWith('force_floor_')) {
|
||||||
|
// Drawing a loop with force floors creates ice
|
||||||
|
name = 'ice';
|
||||||
|
}
|
||||||
|
else if (cx === prevx) {
|
||||||
|
if (cy > prevy) {
|
||||||
|
name = 'force_floor_s';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = 'force_floor_n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (cx > prevx) {
|
||||||
|
name = 'force_floor_e';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = 'force_floor_w';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.place_in_cell(cx, cy, name);
|
||||||
|
|
||||||
|
// The second cell tells us the direction to use for the first, assuming it
|
||||||
|
// had an RFF marking it
|
||||||
|
if (i === 2) {
|
||||||
|
let prevcell = this.stored_level.cells[prevy][prevx];
|
||||||
|
if (prevcell[0].name === 'force_floor_all') {
|
||||||
|
prevcell[0].name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prevx = cx;
|
||||||
|
prevy = cy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this.current_tool === 'adjust') {
|
||||||
|
// Adjust tool doesn't support dragging
|
||||||
|
// TODO should it
|
||||||
}
|
}
|
||||||
this.renderer.draw();
|
this.renderer.draw();
|
||||||
|
|
||||||
@ -685,39 +880,52 @@ class Editor extends PrimaryView {
|
|||||||
this.mouse_mode = null;
|
this.mouse_mode = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Toolbox
|
||||||
|
let toolbox = mk('div.icon-button-set')
|
||||||
|
this.root.querySelector('.controls').append(toolbox);
|
||||||
|
this.tool_button_els = {};
|
||||||
|
for (let tooldef of EDITOR_TOOLS) {
|
||||||
|
let button = mk(
|
||||||
|
'button', {
|
||||||
|
type: 'button',
|
||||||
|
'data-tool': tooldef.mode,
|
||||||
|
},
|
||||||
|
mk('img', {
|
||||||
|
src: tooldef.icon,
|
||||||
|
alt: tooldef.name,
|
||||||
|
title: `${tooldef.name}: ${tooldef.desc}`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.tool_button_els[tooldef.mode] = button;
|
||||||
|
toolbox.append(button);
|
||||||
|
}
|
||||||
|
this.current_tool = 'pencil';
|
||||||
|
this.tool_button_els['pencil'].classList.add('-selected');
|
||||||
|
toolbox.addEventListener('click', ev => {
|
||||||
|
let button = ev.target.closest('.icon-button-set button');
|
||||||
|
if (! button)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.select_tool(button.getAttribute('data-tool'));
|
||||||
|
});
|
||||||
|
|
||||||
// Tile palette
|
// Tile palette
|
||||||
let palette_el = this.root.querySelector('.palette');
|
let palette_el = this.root.querySelector('.palette');
|
||||||
this.palette = {}; // name => element
|
this.palette = {}; // name => element
|
||||||
for (let name of [
|
for (let sectiondef of EDITOR_PALETTE) {
|
||||||
// Terrain
|
let section_el = mk('section');
|
||||||
'floor', 'wall', 'hint', 'socket', 'exit',
|
palette_el.append(mk('h2', sectiondef.title), section_el);
|
||||||
'popwall',
|
for (let name of sectiondef.tiles) {
|
||||||
'fake_floor', 'fake_wall',
|
let entry = mk('canvas.palette-entry', {
|
||||||
'water', 'fire', 'ice', 'force_floor_all',
|
width: this.conductor.tileset.size_x,
|
||||||
// TODO ice curves
|
height: this.conductor.tileset.size_y,
|
||||||
// Items
|
'data-tile-name': name,
|
||||||
'chip', // 'chip_extra', -- XXX doesn't exist in TW tileset!
|
});
|
||||||
'key_blue', 'key_red', 'key_yellow', 'key_green',
|
let ctx = entry.getContext('2d');
|
||||||
// Creatures
|
this.conductor.tileset.draw_type(name, null, null, ctx, 0, 0);
|
||||||
'ball', 'player',
|
this.palette[name] = entry;
|
||||||
'dirt_block',
|
section_el.append(entry);
|
||||||
'clone_block',
|
}
|
||||||
'cloner',
|
|
||||||
'bomb',
|
|
||||||
'button_red',
|
|
||||||
'tank_blue',
|
|
||||||
'turtle',
|
|
||||||
])
|
|
||||||
{
|
|
||||||
let entry = mk('canvas.palette-entry', {
|
|
||||||
width: this.conductor.tileset.size_x,
|
|
||||||
height: this.conductor.tileset.size_y,
|
|
||||||
'data-tile-name': name,
|
|
||||||
});
|
|
||||||
let ctx = entry.getContext('2d');
|
|
||||||
this.conductor.tileset.draw_type(name, null, null, ctx, 0, 0);
|
|
||||||
this.palette[name] = entry;
|
|
||||||
palette_el.append(entry);
|
|
||||||
}
|
}
|
||||||
palette_el.addEventListener('click', ev => {
|
palette_el.addEventListener('click', ev => {
|
||||||
let entry = ev.target.closest('canvas.palette-entry');
|
let entry = ev.target.closest('canvas.palette-entry');
|
||||||
@ -759,6 +967,17 @@ class Editor extends PrimaryView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select_tool(tool) {
|
||||||
|
if (tool === this.current_tool)
|
||||||
|
return;
|
||||||
|
if (! this.tool_button_els[tool])
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.tool_button_els[this.current_tool].classList.remove('-selected');
|
||||||
|
this.current_tool = tool;
|
||||||
|
this.tool_button_els[this.current_tool].classList.add('-selected');
|
||||||
|
}
|
||||||
|
|
||||||
select_palette(name) {
|
select_palette(name) {
|
||||||
if (name === this.palette_selection)
|
if (name === this.palette_selection)
|
||||||
return;
|
return;
|
||||||
@ -770,6 +989,12 @@ class Editor extends PrimaryView {
|
|||||||
if (this.palette_selection) {
|
if (this.palette_selection) {
|
||||||
this.palette[this.palette_selection].classList.add('--selected');
|
this.palette[this.palette_selection].classList.add('--selected');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some tools obviously don't work with a palette selection, in which case changing tiles
|
||||||
|
// should default you back to the pencil
|
||||||
|
if (this.current_tool === 'adjust') {
|
||||||
|
this.select_tool('pencil');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
place_in_cell(x, y, name) {
|
place_in_cell(x, y, name) {
|
||||||
@ -1012,11 +1237,11 @@ class OptionsOverlay extends DialogOverlay {
|
|||||||
label.append(mk('input', {type: 'checkbox', name: optdef.key}));
|
label.append(mk('input', {type: 'checkbox', name: optdef.key}));
|
||||||
if (optdef.impls) {
|
if (optdef.impls) {
|
||||||
for (let impl of optdef.impls) {
|
for (let impl of optdef.impls) {
|
||||||
label.append(mk('img.compat-icon', {src: `compat-${impl}.png`}));
|
label.append(mk('img.compat-icon', {src: `icons/compat-${impl}.png`}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
label.append(mk('span.option-label', optdef.label));
|
label.append(mk('span.option-label', optdef.label));
|
||||||
let help_icon = mk('img.-help', {src: 'help.png'});
|
let help_icon = mk('img.-help', {src: 'icons/help.png'});
|
||||||
label.append(help_icon);
|
label.append(help_icon);
|
||||||
let help_text = mk('p.option-help', optdef.note);
|
let help_text = mk('p.option-help', optdef.note);
|
||||||
li.append(label);
|
li.append(label);
|
||||||
|
|||||||
46
style.css
@ -469,7 +469,7 @@ dl.score-chart .-sum {
|
|||||||
.inventory img {
|
.inventory img {
|
||||||
width: calc(var(--tile-width) * var(--scale));
|
width: calc(var(--tile-width) * var(--scale));
|
||||||
}
|
}
|
||||||
.controls {
|
#player .controls {
|
||||||
grid-area: controls;
|
grid-area: controls;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@ -528,8 +528,11 @@ main.--has-demo .demo-controls {
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid:
|
grid:
|
||||||
"level palette"
|
"controls level"
|
||||||
|
"palette level"
|
||||||
|
/ minmax(20%, 1fr) auto
|
||||||
;
|
;
|
||||||
|
gap: 1em;
|
||||||
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
@ -541,9 +544,48 @@ main.--has-demo .demo-controls {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#editor .controls {
|
||||||
|
grid-area: controls;
|
||||||
|
}
|
||||||
|
#editor .controls p img {
|
||||||
|
background: url(icons/tool-bg-unselected.png);
|
||||||
|
}
|
||||||
|
.icon-button-set {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.icon-button-set button {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1;
|
||||||
|
background: url(icons/tool-bg-unselected.png) no-repeat;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.icon-button-set button.-selected {
|
||||||
|
background-image: url(icons/tool-bg-selected.png);
|
||||||
|
}
|
||||||
|
.icon-button-set button img {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
#editor .palette {
|
#editor .palette {
|
||||||
grid-area: palette;
|
grid-area: palette;
|
||||||
}
|
}
|
||||||
|
#editor .palette h2 {
|
||||||
|
font-size: 1em;
|
||||||
|
margin-top: 1em;
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
color: #909090;
|
||||||
|
}
|
||||||
|
#editor .palette h2:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
#editor .palette section {
|
||||||
|
display: grid;
|
||||||
|
grid: auto-flow 32px / repeat(auto-fit, 32px);
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
.palette-entry {
|
.palette-entry {
|
||||||
margin: 0.25em;
|
margin: 0.25em;
|
||||||
}
|
}
|
||||||
|
|||||||