Compare commits

..

No commits in common. "0ddc42c00f35253c5a7aa8c878cf0602d1b5c521" and "a40480a4642b64c0c3c995cb42dde8f3ff6ea539" have entirely different histories.

11 changed files with 186 additions and 287 deletions

View file

@ -18,9 +18,6 @@
--font-monospace: "Iosevka Hyperlegible", monospace; --font-monospace: "Iosevka Hyperlegible", monospace;
--line-height: 1.5; --line-height: 1.5;
--line-height-em: 1.5em; --line-height-em: 1.5em;
--z-resize-handle: 50;
--z-modal: 100;
} }
/* Reset */ /* Reset */
@ -95,10 +92,6 @@ button.icon {
input { input {
border: none; border: none;
border-bottom: 1px solid var(--color-panel-border); border-bottom: 1px solid var(--color-panel-border);
&:hover:not(:focus) {
background-color: var(--color-shaded-background);
}
} }
*:focus-visible { *:focus-visible {
@ -108,23 +101,14 @@ input {
/* Modal dialogs */ /* Modal dialogs */
dialog {
&:not([open]) {
/* Default to dialogs being invisible.
Otherwise dialogs placed on the page via HTML display on top of everything. */
display: none;
}
&::backdrop {
background-color: var(--dialog-backdrop);
backdrop-filter: blur(8px);
}
}
dialog:not([open]) { dialog:not([open]) {
/* Weird this doesn't seem to work by default. */
display: none;
} }
dialog::backdrop { dialog::backdrop {
background-color: var(--dialog-backdrop);
backdrop-filter: blur(8px);
} }
/* Details */ /* Details */
@ -233,6 +217,8 @@ abbr {
.icon { .icon {
vertical-align: middle; vertical-align: middle;
width: 16px;
height: 16px;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: 50% 50%; background-position: 50% 50%;

View file

@ -132,6 +132,8 @@ export class BrushBox extends HTMLElement {
connectedCallback() { connectedCallback() {
this.saveData.attachToElement(this); this.saveData.attachToElement(this);
this.classList.add("rkgk-panel");
this.brushes = []; this.brushes = [];
this.brushesContainer = this.appendChild(document.createElement("div")); this.brushesContainer = this.appendChild(document.createElement("div"));

View file

@ -1,9 +1,6 @@
import { CodeEditor, Selection } from "rkgk/code-editor.js"; import { CodeEditor, Selection } from "rkgk/code-editor.js";
import { SaveData } from "rkgk/framework.js"; import { SaveData } from "rkgk/framework.js";
import { builtInPresets } from "rkgk/brush-box.js"; import { builtInPresets } from "rkgk/brush-box.js";
import { ResizeHandle } from "rkgk/resize-handle.js";
import { BrushPreview } from "rkgk/brush-preview.js";
import { BrushCostGauges } from "rkgk/brush-cost.js";
export class BrushEditor extends HTMLElement { export class BrushEditor extends HTMLElement {
saveData = new SaveData("brushEditor"); saveData = new SaveData("brushEditor");
@ -13,14 +10,13 @@ export class BrushEditor extends HTMLElement {
} }
connectedCallback() { connectedCallback() {
this.classList.add("rkgk-panel");
this.saveData.attachToElement(this); this.saveData.attachToElement(this);
const defaultBrush = builtInPresets[0]; const defaultBrush = builtInPresets[0];
this.editorContainer = this.appendChild(document.createElement("div")); this.nameEditor = this.appendChild(document.createElement("input"));
this.editorContainer.classList.add("editor");
this.nameEditor = this.editorContainer.appendChild(document.createElement("input"));
this.nameEditor.value = this.saveData.get("name", defaultBrush.name); this.nameEditor.value = this.saveData.get("name", defaultBrush.name);
this.nameEditor.classList.add("name"); this.nameEditor.classList.add("name");
this.nameEditor.addEventListener("input", () => { this.nameEditor.addEventListener("input", () => {
@ -35,7 +31,7 @@ export class BrushEditor extends HTMLElement {
); );
}); });
this.codeEditor = this.editorContainer.appendChild( this.codeEditor = this.appendChild(
new CodeEditor([ new CodeEditor([
{ {
className: "layer-syntax", className: "layer-syntax",
@ -63,28 +59,11 @@ export class BrushEditor extends HTMLElement {
); );
}); });
this.output = document.createElement("output"); this.errorHeader = this.appendChild(document.createElement("h1"));
this.errorHeader.classList.add("error-header");
this.appendChild( this.errorArea = this.appendChild(document.createElement("pre"));
new ResizeHandle({ this.errorArea.classList.add("errors");
direction: "horizontal",
inverse: true,
targetElement: this,
targetProperty: "--brush-preview-height",
initSize: 192,
minSize: 64,
}),
).classList.add("always-visible");
this.appendChild(this.output);
this.ok = this.output.appendChild(document.createElement("div"));
this.ok.classList.add("ok");
this.brushPreview = this.ok.appendChild(new BrushPreview());
this.brushCostGauges = this.ok.appendChild(new BrushCostGauges());
this.errors = this.output.appendChild(document.createElement("pre"));
this.errors.classList.add("errors");
// NOTE(localStorage): Migration from old storage key. // NOTE(localStorage): Migration from old storage key.
localStorage.removeItem("rkgk.brushEditor.code"); localStorage.removeItem("rkgk.brushEditor.code");
@ -108,19 +87,11 @@ export class BrushEditor extends HTMLElement {
} }
resetErrors() { resetErrors() {
this.output.dataset.state = "ok"; this.errorHeader.textContent = "";
this.errors.textContent = ""; this.errorArea.textContent = "";
} }
async updatePreview(haku, { getStats }) { renderHakuResult(phase, result) {
let previewResult = await this.brushPreview.renderBrush(haku);
this.brushCostGauges.update(getStats());
if (previewResult.status == "error") {
this.renderHakuResult(previewResult.result);
}
}
renderHakuResult(result) {
this.resetErrors(); this.resetErrors();
this.errorSquiggles = null; this.errorSquiggles = null;
@ -131,7 +102,7 @@ export class BrushEditor extends HTMLElement {
return; return;
} }
this.output.dataset.state = "error"; this.errorHeader.textContent = `${phase} failed`;
if (result.errorKind == "diagnostics") { if (result.errorKind == "diagnostics") {
this.codeEditor.rebuildLineMap(); this.codeEditor.rebuildLineMap();
@ -141,13 +112,13 @@ export class BrushEditor extends HTMLElement {
); );
this.codeEditor.renderLayer("layer-error-squiggles"); this.codeEditor.renderLayer("layer-error-squiggles");
this.errors.textContent = result.diagnostics this.errorArea.textContent = result.diagnostics
.map( .map(
(diagnostic) => `${diagnostic.start}..${diagnostic.end}: ${diagnostic.message}`, (diagnostic) => `${diagnostic.start}..${diagnostic.end}: ${diagnostic.message}`,
) )
.join("\n"); .join("\n");
} else if (result.errorKind == "plain") { } else if (result.errorKind == "plain") {
this.errors.textContent = result.message; this.errorHeader.textContent = result.message;
} else if (result.errorKind == "exception") { } else if (result.errorKind == "exception") {
let renderer = new ErrorException(result); let renderer = new ErrorException(result);
let squiggles = renderer.prepareSquiggles(); let squiggles = renderer.prepareSquiggles();
@ -173,11 +144,11 @@ export class BrushEditor extends HTMLElement {
this.codeEditor.setSelection(new Selection(span.start, span.end)); this.codeEditor.setSelection(new Selection(span.start, span.end));
}); });
this.errors.replaceChildren(); this.errorArea.replaceChildren();
this.errors.appendChild(renderer); this.errorArea.appendChild(renderer);
} else { } else {
console.warn(`unknown error kind: ${result.errorKind}`); console.warn(`unknown error kind: ${result.errorKind}`);
this.errors.textContent = "(unknown error kind)"; this.errorHeader.textContent = "(unknown error kind)";
} }
} }

View file

@ -13,24 +13,16 @@ export class BrushPreview extends HTMLElement {
this.ctx = this.canvas.getContext("2d"); this.ctx = this.canvas.getContext("2d");
this.#resizeCanvas(); this.#resizeCanvas();
if (this.width == null || this.height == null) {
new ResizeObserver(() => this.#resizeCanvas()).observe(this);
}
} }
#resizeCanvas() { #resizeCanvas() {
this.canvas.width = this.width ?? this.clientWidth; this.canvas.width = this.width ?? this.clientWidth;
this.canvas.height = this.height ?? this.clientHeight; this.canvas.height = this.height ?? this.clientHeight;
// This can happen if the element's `display: none`.
if (this.canvas.width == 0 || this.canvas.height == 0) return;
if (this.pixmap != null) { if (this.pixmap != null) {
this.pixmap.destroy(); this.pixmap.destroy();
} }
this.pixmap = new Pixmap(this.canvas.width, this.canvas.height); this.pixmap = new Pixmap(this.canvas.width, this.canvas.height);
this.dispatchEvent(new Event(".pixmapLost"));
} }
async #renderBrushInner(haku) { async #renderBrushInner(haku) {
@ -39,9 +31,9 @@ export class BrushPreview extends HTMLElement {
runDotter: async () => { runDotter: async () => {
return { return {
fromX: this.canvas.width / 2, fromX: this.canvas.width / 2,
fromY: this.canvas.height / 2, fromY: this.canvas.width / 2,
toX: this.canvas.width / 2, toX: this.canvas.width / 2,
toY: this.canvas.height / 2, toY: this.canvas.width / 2,
num: 0, num: 0,
}; };
}, },

View file

@ -451,10 +451,10 @@ class CanvasRenderer extends HTMLElement {
async #cursorReportingBehaviour() { async #cursorReportingBehaviour() {
while (true) { while (true) {
let event = await listen([window, "mousemove"]); let event = await listen([this, "mousemove"]);
let [x, y] = this.viewport.toViewportSpace( let [x, y] = this.viewport.toViewportSpace(
event.clientX - this.clientLeft, event.clientX - this.clientLeft,
event.clientY - this.clientTop, event.offsetY - this.clientTop,
this.getWindowSize(), this.getWindowSize(),
); );
this.dispatchEvent(Object.assign(new Event(".cursor"), { x, y })); this.dispatchEvent(Object.assign(new Event(".cursor"), { x, y }));
@ -493,7 +493,10 @@ class InteractEvent extends Event {
continueAsDotter() { continueAsDotter() {
(async () => { (async () => {
let event = await listen([window, "mousemove"], [window, "mouseup"]); let event = await listen(
[this.canvasRenderer, "mousemove"],
[this.canvasRenderer, "mouseup"],
);
if (event.type == "mousemove") { if (event.type == "mousemove") {
let [mouseX, mouseY] = this.canvasRenderer.viewport.toViewportSpace( let [mouseX, mouseY] = this.canvasRenderer.viewport.toViewportSpace(

View file

@ -44,9 +44,6 @@ export class CodeEditor extends HTMLElement {
this.undoHistory = []; this.undoHistory = [];
this.undoHistoryTop = 0; this.undoHistoryTop = 0;
this.textArea.addEventListener("input", () => {
this.#codeChanged();
});
this.#textAreaAutoSizingBehaviour(); this.#textAreaAutoSizingBehaviour();
this.#keyShortcutBehaviours(); this.#keyShortcutBehaviours();
@ -79,6 +76,9 @@ export class CodeEditor extends HTMLElement {
// Resizing the text area // Resizing the text area
#textAreaAutoSizingBehaviour() { #textAreaAutoSizingBehaviour() {
this.textArea.addEventListener("input", () => {
this.#codeChanged();
});
this.#resizeTextArea(); this.#resizeTextArea();
document.fonts.addEventListener("loadingdone", () => this.#resizeTextArea()); document.fonts.addEventListener("loadingdone", () => this.#resizeTextArea());
new ResizeObserver(() => this.#resizeTextArea()).observe(this.textArea); new ResizeObserver(() => this.#resizeTextArea()).observe(this.textArea);

View file

@ -65,7 +65,7 @@ export class SaveData {
} }
attachToElement(element) { attachToElement(element) {
this.elementId = element.id; this.elementId = element.dataset.storageId;
} }
getRaw(key) { getRaw(key) {

View file

@ -44,6 +44,9 @@ main {
& > .panels { & > .panels {
--right-width: 384px; /* Overridden by JavaScript */ --right-width: 384px; /* Overridden by JavaScript */
box-sizing: border-box;
padding: 16px;
display: grid; display: grid;
grid-template-columns: [left] 1fr [right-resize] auto [right] minmax( grid-template-columns: [left] 1fr [right-resize] auto [right] minmax(
0, 0,
@ -60,9 +63,9 @@ main {
& > .left { & > .left {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 16px;
pointer-events: none; pointer-events: none;
& > * { & > * {
pointer-events: auto; pointer-events: auto;
} }
@ -77,8 +80,8 @@ main {
min-height: 0; min-height: 0;
display: grid; display: grid;
grid-template-rows: 100%; grid-template-rows: minmax(0, min-content);
grid-template-columns: [resize] min-content [docked] auto; grid-template-columns: [floating] max-content [resize] min-content [docked] auto;
padding-left: 16px; padding-left: 16px;
@ -89,14 +92,29 @@ main {
min-height: 0; min-height: 0;
} }
& > rkgk-resize-handle {
pointer-events: auto;
}
& > .docked {
display: flex;
flex-direction: column;
& > * {
pointer-events: auto;
}
}
& > .docked > rkgk-brush-editor {
max-height: 100%;
}
& > .floating { & > .floating {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
padding: 16px;
& > rkgk-brush-preview { & > rkgk-brush-preview {
width: 128px; width: 128px;
height: 128px; height: 128px;
@ -108,35 +126,6 @@ main {
pointer-events: auto; pointer-events: auto;
} }
} }
& > rkgk-resize-handle {
pointer-events: auto;
}
& > .docked {
display: flex;
flex-direction: column;
height: 100%;
max-height: 100%;
background-color: var(--color-panel-background);
box-shadow: 0 0 0 1px var(--color-panel-border);
pointer-events: auto;
& > * {
flex-shrink: 0;
}
& > rkgk-brush-editor {
flex-grow: 1;
flex-shrink: 1;
}
& > .menu-bar {
border-bottom: 1px solid var(--color-panel-border);
}
}
} }
} }
@ -159,7 +148,6 @@ main {
& > #js-loading { & > #js-loading {
background-color: var(--color-panel-background); background-color: var(--color-panel-background);
z-index: var(--z-modal);
display: flex; display: flex;
align-items: center; align-items: center;
@ -170,54 +158,47 @@ main {
/* Resize handle */ /* Resize handle */
rkgk-resize-handle { rkgk-resize-handle {
--width: 8px; --width: 16px;
--line: none; --line-width: 2px;
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-shrink: 0; flex-shrink: 0;
z-index: var(--z-resize-handle);
&.always-visible {
--line: 1px solid var(--color-panel-border);
}
&[data-direction="horizontal"] { &[data-direction="horizontal"] {
width: 100%; width: 100%;
height: var(--width); height: var(--width);
margin: calc(var(--width) / -2) 0;
flex-direction: column; flex-direction: column;
cursor: row-resize; cursor: row-resize;
& > .visual { & > .visual {
width: 100%; width: 100%;
height: 0; height: var(--line-width);
border-bottom: var(--line);
} }
} }
&[data-direction="vertical"] { &[data-direction="vertical"] {
width: var(--width); width: var(--width);
height: 100%; height: 100%;
margin: 0 calc(var(--width) / -2);
flex-direction: row; flex-direction: row;
cursor: col-resize; cursor: col-resize;
& > .visual { & > .visual {
width: 0; width: var(--line-width);
height: 100%; height: 100%;
border-left: var(--line);
} }
} }
& > .visual {
background-color: var(--color-brand-blue);
opacity: 0%;
}
&:hover > .visual, &:hover > .visual,
&.dragging > .visual { &.dragging > .visual {
--line: 2px solid var(--color-brand-blue); opacity: 100%;
} }
} }
@ -280,15 +261,12 @@ rkgk-reticle-cursor {
/* Brush box */ /* Brush box */
rkgk-brush-box { rkgk-brush-box.rkgk-panel {
--button-size: 56px; --button-size: 56px;
height: var(--height); height: var(--height);
padding: 12px; padding: 12px;
overflow-x: hidden;
overflow-y: auto;
& > .brushes { & > .brushes {
display: grid; display: grid;
grid-template-columns: repeat( grid-template-columns: repeat(
@ -297,6 +275,8 @@ rkgk-brush-box {
); );
max-height: 100%; max-height: 100%;
overflow-x: hidden;
overflow-y: auto;
& > button { & > button {
padding: 0; padding: 0;
@ -357,20 +337,20 @@ rkgk-brush-box {
/* Code editor */ /* Code editor */
rkgk-code-editor { rkgk-code-editor {
--gutter-width: 3.5em; --gutter-width: 2.75em;
--vertical-padding: 12px;
display: block; display: block;
position: relative; position: relative;
width: 100%;
padding: var(--vertical-padding) 0;
overflow: auto; overflow: auto;
& > .layer { & > .layer {
position: absolute; position: absolute;
left: 0; left: 0;
top: var(--vertical-padding); top: 0;
width: 100%; width: 100%;
height: 100%;
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
@ -488,60 +468,47 @@ rkgk-code-editor {
&:has(textarea:focus) { &:has(textarea:focus) {
outline: 1px solid var(--color-brand-blue); outline: 1px solid var(--color-brand-blue);
outline-offset: -1px; outline-offset: 4px;
} }
} }
/* Brush editor */ /* Brush editor */
rkgk-brush-editor { rkgk-brush-editor.rkgk-panel {
padding: 12px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px;
min-height: 0;
position: relative; position: relative;
& > .editor {
display: flex;
flex-direction: column;
height: calc(100% - var(--brush-preview-height));
& > .name { & > .name {
margin: 12px; margin-bottom: 4px;
margin-bottom: 0;
font-weight: bold; font-weight: bold;
} }
& > rkgk-code-editor { & > .text-area {
height: 100%; display: block;
flex-shrink: 1; width: 100%;
} margin: 0;
resize: none;
white-space: pre-wrap;
border: none;
overflow: hidden;
box-sizing: border-box;
} }
& > output { & > .errors:empty,
height: 64px; & > .error-header:empty {
flex-grow: 1;
user-select: text;
&[data-state="ok"] > .errors {
display: none;
}
&[data-state="error"] > .ok {
display: none; display: none;
} }
& > .ok { & > .error-header {
display: flex; margin: 0;
flex-direction: row; margin-top: 0.5em;
font-size: 1rem;
height: 100%; color: var(--color-error);
& > rkgk-brush-preview {
flex-grow: 1;
}
} }
& > .errors { & > .errors {
@ -549,10 +516,11 @@ rkgk-brush-editor {
color: var(--color-error); color: var(--color-error);
white-space: pre-wrap; white-space: pre-wrap;
user-select: text;
max-height: 20em; max-height: 20em;
overflow-y: auto; overflow-y: auto;
} }
}
} }
rkgk-brush-editor-error-exception { rkgk-brush-editor-error-exception {
@ -626,6 +594,7 @@ rkgk-brush-preview {
var(--checkerboard-dark) 0% 50% var(--checkerboard-dark) 0% 50%
) )
50% 50% / var(--checkerboard-size) var(--checkerboard-size); 50% 50% / var(--checkerboard-size) var(--checkerboard-size);
border-radius: 4px;
& > canvas { & > canvas {
display: block; display: block;
@ -656,20 +625,26 @@ rkgk-brush-preview {
/* Brush cost gauges */ /* Brush cost gauges */
rkgk-brush-cost-gauges { rkgk-brush-cost-gauges,
rkgk-brush-cost-gauges.rkgk-panel {
--gauge-size: 20px; --gauge-size: 20px;
height: var(--gauge-size);
border-radius: 4px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
overflow: clip; /* clip corners */
&.hidden { &.hidden {
display: none; display: none;
} }
& > rkgk-brush-cost-gauge { & > rkgk-brush-cost-gauge {
display: block; display: block;
width: var(--gauge-size); height: var(--gauge-size);
height: 100%; flex-grow: 1;
&.hidden { &.hidden {
display: none; display: none;
@ -679,7 +654,7 @@ rkgk-brush-cost-gauges {
width: 100%; width: 100%;
height: 100%; height: 100%;
clip-path: inset(calc(100% - var(--progress)) 0 0 0); clip-path: xywh(0 0 var(--progress) 100%);
background-color: var(--gauge-color); background-color: var(--gauge-color);
} }
@ -697,10 +672,6 @@ rkgk-brush-cost-gauges {
--gauge-color: #5aca40; --gauge-color: #5aca40;
} }
} }
& .icon {
background-position: 50% calc(100% - 4px);
}
} }
/* Zoom indicator */ /* Zoom indicator */
@ -818,38 +789,21 @@ rkgk-context-menu.rkgk-panel {
/* Menu bar */ /* Menu bar */
.menu-bar { .menu-bar {
--border-radius: 4px;
display: flex; display: flex;
align-items: center; align-items: center;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: fit-content;
height: 28px; height: 24px;
border-radius: var(--border-radius);
margin: 0; & > a {
padding: 0;
list-style: none;
& > li {
display: flex;
flex-direction: row;
align-items: center;
height: 100%;
}
& > li.icon {
display: block; display: block;
width: 28px;
height: 28px;
}
& > li > a {
display: flex;
flex-direction: row;
align-items: center;
color: var(--color-text); color: var(--color-text);
height: 100%; padding: 4px 8px;
padding: 0 8px;
text-decoration: none; text-decoration: none;
line-height: 1; line-height: 1;
@ -862,6 +816,16 @@ rkgk-context-menu.rkgk-panel {
width: 24px; width: 24px;
height: 24px; height: 24px;
} }
&:first-child {
border-top-left-radius: var(--border-radius);
border-bottom-left-radius: var(--border-radius);
}
&:last-child {
border-top-right-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
} }
& > hr { & > hr {

View file

@ -18,6 +18,8 @@ let canvasRenderer = main.querySelector("rkgk-canvas-renderer");
let reticleRenderer = main.querySelector("rkgk-reticle-renderer"); let reticleRenderer = main.querySelector("rkgk-reticle-renderer");
let brushBox = main.querySelector("rkgk-brush-box"); let brushBox = main.querySelector("rkgk-brush-box");
let brushEditor = main.querySelector("rkgk-brush-editor"); let brushEditor = main.querySelector("rkgk-brush-editor");
let brushPreview = main.querySelector("rkgk-brush-preview");
let brushCostGauges = main.querySelector("rkgk-brush-cost-gauges");
let zoomIndicator = main.querySelector("rkgk-zoom-indicator"); let zoomIndicator = main.querySelector("rkgk-zoom-indicator");
let welcome = main.querySelector("rkgk-welcome"); let welcome = main.querySelector("rkgk-welcome");
let connectionStatus = main.querySelector("rkgk-connection-status"); let connectionStatus = main.querySelector("rkgk-connection-status");
@ -251,7 +253,7 @@ function readUrl(urlString) {
let result = await currentUser.haku.evalBrush( let result = await currentUser.haku.evalBrush(
selfController(interactionQueue, wall, event), selfController(interactionQueue, wall, event),
); );
brushEditor.renderHakuResult(result); brushEditor.renderHakuResult(result.phase == "eval" ? "Evaluation" : "Rendering", result);
}); });
canvasRenderer.addEventListener(".viewportUpdate", () => reticleRenderer.render()); canvasRenderer.addEventListener(".viewportUpdate", () => reticleRenderer.render());
@ -261,19 +263,26 @@ function readUrl(urlString) {
// Brush editor // Brush editor
function updateBrushPreview() {
brushEditor.updatePreview(currentUser.haku, {
getStats: () => currentUser.getStats(session.wallInfo),
});
}
function compileBrush() { function compileBrush() {
let compileResult = currentUser.setBrush(brushEditor.code); let compileResult = currentUser.setBrush(brushEditor.code);
brushEditor.renderHakuResult(compileResult); brushEditor.renderHakuResult("Compilation", compileResult);
if (compileResult.status == "ok") { brushCostGauges.update(currentUser.getStats(session.wallInfo));
updateBrushPreview();
if (compileResult.status != "ok") {
brushPreview.setErrorFlag();
return;
} }
brushPreview.renderBrush(currentUser.haku).then((previewResult) => {
brushCostGauges.update(currentUser.getStats(session.wallInfo));
if (previewResult.status == "error") {
brushEditor.renderHakuResult(
previewResult.phase == "eval" ? "Evaluation" : "Rendering",
previewResult.result,
);
}
});
} }
compileBrush(); compileBrush();
@ -285,8 +294,6 @@ function readUrl(urlString) {
}); });
}); });
brushEditor.brushPreview.addEventListener(".pixmapLost", updateBrushPreview);
// Brush box // Brush box
function updateBrushBoxDirtyState() { function updateBrushBoxDirtyState() {

View file

@ -3,31 +3,13 @@ import { listen, SaveData } from "rkgk/framework.js";
export class ResizeHandle extends HTMLElement { export class ResizeHandle extends HTMLElement {
saveData = new SaveData("resizeHandle"); saveData = new SaveData("resizeHandle");
constructor(props) {
super();
this.props = props;
}
connectedCallback() { connectedCallback() {
let props = this.props ?? this.dataset; this.direction = this.getAttribute("data-direction");
this.targetId = this.getAttribute("data-target");
this.direction = this.dataset.direction = props.direction;
this.targetProperty = props.targetProperty;
this.initSize = parseInt(props.initSize);
this.minSize = parseInt(props.minSize);
this.inverse = props.inverse != null;
if (props.targetElement != null) {
// In case you want to construct the resize handle programatically:
// pass in the target element via targetElement.
// Don't forget to set its id.
this.target = props.targetElement;
this.targetId = this.target.id;
} else {
// Else use data-target.
this.targetId = props.target;
this.target = document.getElementById(this.targetId); this.target = document.getElementById(this.targetId);
} this.targetProperty = this.getAttribute("data-target-property");
this.initSize = parseInt(this.getAttribute("data-init-size"));
this.minSize = parseInt(this.getAttribute("data-min-size"));
this.saveData.elementId = this.targetId; this.saveData.elementId = this.targetId;
@ -76,10 +58,8 @@ export class ResizeHandle extends HTMLElement {
if (event.type == "mousemove") { if (event.type == "mousemove") {
let delta = let delta =
this.direction == "vertical" this.direction == "vertical"
? event.clientX - mouseDown.clientX ? mouseDown.clientX - event.clientX
: event.clientY - mouseDown.clientY; : event.clientY - mouseDown.clientY;
if (this.inverse) delta = -delta;
this.#setSize(startingSize + delta); this.#setSize(startingSize + delta);
this.#updateTargetProperty(); this.#updateTargetProperty();
} else if (event.type == "mouseup") { } else if (event.type == "mouseup") {

View file

@ -7,6 +7,8 @@
<title>rakugaki</title> <title>rakugaki</title>
<link rel="stylesheet" href="{{ static 'base.css' }}"> <link rel="stylesheet" href="{{ static 'base.css' }}">
<link rel="stylesheet" href="{{ static 'fonts.css' }}">
<link rel="stylesheet" href="{{ static 'index.css' }}">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@ -25,20 +27,13 @@
import "rkgk/brush-editor.js"; import "rkgk/brush-editor.js";
import "rkgk/brush-preview.js"; import "rkgk/brush-preview.js";
import "rkgk/canvas-renderer.js"; import "rkgk/canvas-renderer.js";
import "rkgk/code-editor.js";
import "rkgk/connection-status.js"; import "rkgk/connection-status.js";
import "rkgk/context-menu.js";
import "rkgk/framework.js"; import "rkgk/framework.js";
import "rkgk/haku.js";
import "rkgk/online-users.js";
import "rkgk/painter.js";
import "rkgk/random.js";
import "rkgk/resize-handle.js"; import "rkgk/resize-handle.js";
import "rkgk/reticle-renderer.js"; import "rkgk/reticle-renderer.js";
import "rkgk/session.js"; import "rkgk/session.js";
import "rkgk/throbber.js"; import "rkgk/throbber.js";
import "rkgk/viewport.js"; import "rkgk/viewport.js";
import "rkgk/wall.js";
import "rkgk/welcome.js"; import "rkgk/welcome.js";
import "rkgk/zoom-indicator.js"; import "rkgk/zoom-indicator.js";
@ -60,45 +55,44 @@
</head> </head>
<body> <body>
<!-- We shouldn't consider these stylesheets render-blocking.
About the only render-blocking thing on the page is the throbber. -->
<link rel="stylesheet" href="{{ static 'fonts.css' }}">
<link rel="stylesheet" href="{{ static 'index.css' }}">
<main> <main>
<rkgk-canvas-renderer class="fullscreen"></rkgk-canvas-renderer> <rkgk-canvas-renderer class="fullscreen"></rkgk-canvas-renderer>
<rkgk-reticle-renderer class="fullscreen"></rkgk-reticle-renderer> <rkgk-reticle-renderer class="fullscreen"></rkgk-reticle-renderer>
<div class="panels fullscreen" id="panels-overlay"> <div class="panels fullscreen" id="panels-overlay">
<div class="left"> <div class="left">
<div class="rkgk-panel menu-bar">
<a class="icon icon-rkgk-grayscale" title="I know this menu bar is really ugly. Sorry about that."></a>
<hr>
<a href="/docs/rkgk.html">Manual</a>
</div>
<rkgk-zoom-indicator class="rkgk-panel"></rkgk-zoom-indicator> <rkgk-zoom-indicator class="rkgk-panel"></rkgk-zoom-indicator>
</div> </div>
<div class="right"> <div class="right">
<div class="floating">
<rkgk-brush-preview></rkgk-brush-preview>
<rkgk-brush-cost-gauges class="rkgk-panel"></rkgk-brush-cost-gauges>
</div>
<rkgk-resize-handle <rkgk-resize-handle
data-direction="vertical" data-direction="vertical"
data-inverse
data-target="panels-overlay" data-target="panels-overlay"
data-target-property="--right-width" data-target-property="--right-width"
data-init-size="528" data-init-size="528"
data-min-size="256"></rkgk-resize-handle> data-min-size="384"></rkgk-resize-handle>
<div class="docked"> <div class="docked">
<menu class="menu-bar"> <rkgk-brush-box
<li class="icon icon-rkgk-grayscale"></li> id="brush-box"
<hr> data-storage-id="brush-box"></rkgk-brush-box>
<li><a href="/docs/rkgk.html">Manual</a></li>
</menu>
<rkgk-brush-box id="brush-box"></rkgk-brush-box>
<rkgk-resize-handle <rkgk-resize-handle
class="always-visible"
data-direction="horizontal" data-direction="horizontal"
data-target="brush-box" data-target="brush-box"
data-target-property="--height" data-target-property="--height"
data-init-size="168" data-init-size="168"
data-min-size="96"></rkgk-resize-handle> data-min-size="96"></rkgk-resize-handle>
<rkgk-brush-editor id="brush-editor"></rkgk-brush-editor> <rkgk-brush-editor
data-storage-id="brush-editor"></rkgk-brush-editor>
</div> </div>
</div> </div>
</div> </div>