diff --git a/crates/haku-wasm/src/lib.rs b/crates/haku-wasm/src/lib.rs
index b45e065..416092f 100644
--- a/crates/haku-wasm/src/lib.rs
+++ b/crates/haku-wasm/src/lib.rs
@@ -279,11 +279,24 @@ extern "C" fn haku_status_string(code: StatusCode) -> *const i8 {
.as_ptr()
}
+#[derive(Debug)]
+struct CompileStats {
+ ast_size: usize,
+}
+
+#[derive(Debug)]
+struct RunnableBrush {
+ chunk_id: ChunkId,
+ closure_spec: ClosureSpec,
+
+ compile_stats: CompileStats,
+}
+
#[derive(Debug, Default)]
enum BrushState {
#[default]
Default,
- Ready(ChunkId, ClosureSpec),
+ Ready(RunnableBrush),
}
#[derive(Debug, Default)]
@@ -431,7 +444,13 @@ unsafe extern "C" fn haku_compile_brush(
Ok(chunk_id) => chunk_id,
Err(_) => return StatusCode::TooManyChunks,
};
- brush.state = BrushState::Ready(chunk_id, closure_spec);
+ brush.state = BrushState::Ready(RunnableBrush {
+ chunk_id,
+ closure_spec,
+ compile_stats: CompileStats {
+ ast_size: ast.len(),
+ },
+ });
info!("brush compiled into {chunk_id:?}");
@@ -470,7 +489,7 @@ unsafe extern "C" fn haku_begin_brush(instance: *mut Instance, brush: *const Bru
let instance = &mut *instance;
let brush = &*brush;
- let BrushState::Ready(chunk_id, closure_spec) = brush.state else {
+ let BrushState::Ready(runnable) = &brush.state else {
panic!("brush is not compiled and ready to be used");
};
@@ -479,10 +498,10 @@ unsafe extern "C" fn haku_begin_brush(instance: *mut Instance, brush: *const Bru
instance.reset_exception();
instance.trampoline = None;
- let Ok(closure_id) = instance
- .vm
- .create_ref(Ref::Closure(Closure::chunk(chunk_id, closure_spec)))
- else {
+ let Ok(closure_id) = instance.vm.create_ref(Ref::Closure(Closure::chunk(
+ runnable.chunk_id,
+ runnable.closure_spec,
+ ))) else {
return StatusCode::OutOfRefSlots;
};
@@ -574,3 +593,26 @@ unsafe extern "C" fn haku_cont_dotter(
)
})
}
+
+#[unsafe(no_mangle)]
+unsafe extern "C" fn haku_stat_ast_size(brush: *const Brush) -> usize {
+ match &(*brush).state {
+ BrushState::Default => 0,
+ BrushState::Ready(runnable) => runnable.compile_stats.ast_size,
+ }
+}
+
+#[unsafe(no_mangle)]
+unsafe extern "C" fn haku_stat_num_refs(instance: *const Instance) -> usize {
+ (*instance).vm.num_refs()
+}
+
+#[unsafe(no_mangle)]
+unsafe extern "C" fn haku_stat_remaining_fuel(instance: *const Instance) -> usize {
+ (*instance).vm.remaining_fuel()
+}
+
+#[unsafe(no_mangle)]
+unsafe extern "C" fn haku_stat_remaining_memory(instance: *const Instance) -> usize {
+ (*instance).vm.remaining_memory()
+}
diff --git a/crates/haku/src/vm.rs b/crates/haku/src/vm.rs
index ce9df0e..59aa97b 100644
--- a/crates/haku/src/vm.rs
+++ b/crates/haku/src/vm.rs
@@ -73,10 +73,18 @@ impl Vm {
}
}
+ pub fn num_refs(&self) -> usize {
+ self.refs.len()
+ }
+
pub fn remaining_fuel(&self) -> usize {
self.fuel
}
+ pub fn remaining_memory(&self) -> usize {
+ self.memory
+ }
+
pub fn set_fuel(&mut self, fuel: usize) {
self.fuel = fuel;
}
diff --git a/scripts/mkicon.fish b/scripts/mkicon.fish
index 5fbca61..406f1b4 100755
--- a/scripts/mkicon.fish
+++ b/scripts/mkicon.fish
@@ -1,7 +1,13 @@
#!/usr/bin/env fish
-set filename $argv[1]
-set icon_name (basename $filename .svg)
-set icon_base64 (svgcleaner --stdout $filename 2>/dev/null | base64 -w0)
+function mkicon
+ set -l filename $argv[1]
+ set -l icon_name (basename $filename .svg)
+ set -l icon_base64 (svgcleaner --stdout $filename 2>/dev/null | base64 -w0)
-printf "--icon-%s: url('data:image/svg+xml;base64,%s');" "$icon_name" "$icon_base64"
+ printf "--icon-%s: url('data:image/svg+xml;base64,%s');\n" "$icon_name" "$icon_base64"
+end
+
+for arg in $argv
+ mkicon $arg
+end
diff --git a/static/base.css b/static/base.css
index 933649d..5118f27 100644
--- a/static/base.css
+++ b/static/base.css
@@ -166,12 +166,20 @@ pre:has(code) {
/* Icons */
:root {
- --icon-rkgk-grayscale: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNhKSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiPjxwYXRoIGQ9Im0xMiAxNy00LjU1Mjc5LTkuMTA1NTdjLS42NjQ5LTEuMzI5ODEuMzAyMDktMi44OTQ0MyAxLjc4ODg2LTIuODk0NDNoOC43NjM5MyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxnIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+PHBhdGggZD0ibTUuNSAxMi0yLjUgNSIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW9wYWNpdHk9Ii41Ii8+PHBhdGggZD0ibTMgNWgxIi8+PC9nPjwvZz48L3N2Zz4=");
+ --icon-brackets: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNyA0aC0zdjhoM20yLThoM3Y4aC0zIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==");
+ --icon-droplet: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNSAxMGMwLTEuNjM0NzIgMS4wMDIxMi00LjI3MTU2IDIuMTg3MjYtNi41NDUzNy4zNDg5My0uNjY5NDQgMS4yNzY1NS0uNjY5NDQgMS42MjU0OCAwIDEuMTg1MTQgMi4yNzM4MSAyLjE4NzI2IDQuOTEwNjUgMi4xODcyNiA2LjU0NTM3IDAgMi0xLjUgMy0zIDNzLTMtMS0zLTN6IiBmaWxsPSIjMDAwIi8+PC9zdmc+");
--icon-external-link: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBzdHJva2U9IiMwMDAiPjxwYXRoIGQ9Im0xMC41IDl2Mi41aC02di02aDIuNSIvPjxwYXRoIGQ9Im03IDkgNC41LTQuNW0wIDBoLTMuNW0zLjUgMHYzLjUiLz48L2c+PC9zdmc+");
+ --icon-memory: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iLjEiPjxwYXRoIGQ9Im01LjA1IDMuMDVoNS45djkuOWgtNS45eiIvPjxwYXRoIGQ9Im0zLjA1IDQuMDVoMS45djEuOWgtMS45eiIvPjxwYXRoIGQ9Im0xMS4wNSA0LjA1aDEuOXYxLjloLTEuOXoiLz48cGF0aCBkPSJtMy4wNSA3LjA1aDEuOXYxLjloLTEuOXoiLz48cGF0aCBkPSJtMTEuMDUgNy4wNWgxLjl2MS45aC0xLjl6Ii8+PHBhdGggZD0ibTMuMDUgMTAuMDVoMS45djEuOWgtMS45eiIvPjxwYXRoIGQ9Im0xMS4wNSAxMC4wNWgxLjl2MS45aC0xLjl6Ii8+PC9nPjwvc3ZnPg==");
+ --icon-object: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0xMiA2YzAgMS4xMDQ1Ny0uODk1NCAyLTIgMnYyYzIuMjA5MSAwIDQtMS43OTA4NiA0LTRzLTEuNzkwOS00LTQtNGMtMi4yMDkxNCAwLTQgMS43OTA4Ni00IDRoMmMwLTEuMTA0NTcuODk1NDMtMiAyLTIgMS4xMDQ2IDAgMiAuODk1NDMgMiAyeiIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIi8+PHBhdGggZD0ibTMgN2g2djZoLTZ6IiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==");
+ --icon-rkgk-grayscale: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNhKSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiPjxwYXRoIGQ9Im0xMiAxNy00LjU1Mjc5LTkuMTA1NTdjLS42NjQ5LTEuMzI5ODEuMzAyMDktMi44OTQ0MyAxLjc4ODg2LTIuODk0NDNoOC43NjM5MyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxnIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+PHBhdGggZD0ibTUuNSAxMi0yLjUgNSIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW9wYWNpdHk9Ii41Ii8+PHBhdGggZD0ibTMgNWgxIi8+PC9nPjwvZz48L3N2Zz4=");
+
+ --icon-brackets-white: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNyA0aC0zdjhoM20yLThoM3Y4aC0zIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==");
+ --icon-droplet-white: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNSAxMGMwLTEuNjM0NzIgMS4wMDIxMi00LjI3MTU2IDIuMTg3MjYtNi41NDUzNy4zNDg5My0uNjY5NDQgMS4yNzY1NS0uNjY5NDQgMS42MjU0OCAwIDEuMTg1MTQgMi4yNzM4MSAyLjE4NzI2IDQuOTEwNjUgMi4xODcyNiA2LjU0NTM3IDAgMi0xLjUgMy0zIDNzLTMtMS0zLTN6IiBmaWxsPSIjZmZmIi8+PC9zdmc+");
+ --icon-object-white: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0xMiA2YzAgMS4xMDQ1Ny0uODk1NCAyLTIgMnYyYzIuMjA5MSAwIDQtMS43OTA4NiA0LTRzLTEuNzkwOS00LTQtNGMtMi4yMDkxNCAwLTQgMS43OTA4Ni00IDRoMmMwLTEuMTA0NTcuODk1NDMtMiAyLTIgMS4xMDQ2IDAgMiAuODk1NDMgMiAyeiIgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIi8+PHBhdGggZD0ibTMgN2g2djZoLTZ6IiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==");
+ --icon-memory-white: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjZmZmIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iLjEiPjxwYXRoIGQ9Im01LjA1IDMuMDVoNS45djkuOWgtNS45eiIvPjxwYXRoIGQ9Im0zLjA1IDQuMDVoMS45djEuOWgtMS45eiIvPjxwYXRoIGQ9Im0xMS4wNSA0LjA1aDEuOXYxLjloLTEuOXoiLz48cGF0aCBkPSJtMy4wNSA3LjA1aDEuOXYxLjloLTEuOXoiLz48cGF0aCBkPSJtMTEuMDUgNy4wNWgxLjl2MS45aC0xLjl6Ii8+PHBhdGggZD0ibTMuMDUgMTAuMDVoMS45djEuOWgtMS45eiIvPjxwYXRoIGQ9Im0xMS4wNSAxMC4wNWgxLjl2MS45aC0xLjl6Ii8+PC9nPjwvc3ZnPg==");
}
.icon {
- display: inline-block;
vertical-align: middle;
width: 16px;
height: 16px;
@@ -179,10 +187,35 @@ pre:has(code) {
background-repeat: no-repeat;
background-position: 50% 50%;
- &.icon-rkgk-grayscale {
- background-image: var(--icon-rkgk-grayscale);
+ &.icon-brackets {
+ background-image: var(--icon-brackets);
+ }
+ &.icon-droplet {
+ background-image: var(--icon-droplet);
}
&.icon-external-link {
background-image: var(--icon-external-link);
}
+ &.icon-memory {
+ background-image: var(--icon-memory);
+ }
+ &.icon-object {
+ background-image: var(--icon-object);
+ }
+ &.icon-rkgk-grayscale {
+ background-image: var(--icon-rkgk-grayscale);
+ }
+
+ &.icon-brackets-white {
+ background-image: var(--icon-brackets-white);
+ }
+ &.icon-droplet-white {
+ background-image: var(--icon-droplet-white);
+ }
+ &.icon-memory-white {
+ background-image: var(--icon-memory-white);
+ }
+ &.icon-object-white {
+ background-image: var(--icon-object-white);
+ }
}
diff --git a/static/brush-cost.js b/static/brush-cost.js
new file mode 100644
index 0000000..31e2263
--- /dev/null
+++ b/static/brush-cost.js
@@ -0,0 +1,81 @@
+class Gauge extends HTMLElement {
+ constructor(iconName, label) {
+ super();
+
+ this.iconName = iconName;
+ this.label = label;
+ }
+
+ connectedCallback() {
+ this.role = "progressbar";
+ this.classList.add("icon", `icon-${this.iconName}`);
+
+ this.full = this.appendChild(document.createElement("div"));
+ this.full.classList.add("full", "icon", `icon-${this.iconName}-white`);
+ }
+
+ setValue(value, valueMax) {
+ let clampedNormalized = Math.max(0, Math.min(1, value / valueMax));
+ this.style.setProperty("--progress", `${clampedNormalized * 100}%`);
+ this.title = `${this.label}: ${value} / ${valueMax} (${Math.ceil((value / valueMax) * 100)}%)`;
+ }
+}
+
+customElements.define("rkgk-brush-cost-gauge", Gauge);
+
+export class BrushCostGauges extends HTMLElement {
+ constructor() {
+ super();
+ }
+
+ connectedCallback() {
+ this.codeSizeGauge = this.appendChild(
+ createGauge({
+ className: "code-size",
+ iconName: "brackets",
+ label: "Code size",
+ }),
+ );
+ this.fuelGauge = this.appendChild(
+ createGauge({
+ className: "fuel",
+ iconName: "droplet",
+ label: "Fuel",
+ }),
+ );
+ this.objectsGauge = this.appendChild(
+ createGauge({
+ className: "objects",
+ iconName: "object",
+ label: "Objects",
+ }),
+ );
+ this.memoryGauge = this.appendChild(
+ createGauge({
+ className: "memory",
+ iconName: "memory",
+ label: "Bulk memory",
+ }),
+ );
+
+ this.codeSizeGauge.setValue(0);
+ this.fuelGauge.setValue(0);
+ this.objectsGauge.setValue(0);
+ this.memoryGauge.setValue(0);
+ }
+
+ update(stats) {
+ this.codeSizeGauge.setValue(stats.astSize, stats.astSizeMax);
+ this.fuelGauge.setValue(stats.fuel, stats.fuelMax);
+ this.objectsGauge.setValue(stats.numRefs, stats.numRefsMax);
+ this.memoryGauge.setValue(stats.memory, stats.memoryMax);
+ }
+}
+
+customElements.define("rkgk-brush-cost-gauges", BrushCostGauges);
+
+function createGauge({ className, iconName, label }) {
+ let gauge = new Gauge(iconName, label);
+ gauge.classList.add(className);
+ return gauge;
+}
diff --git a/static/haku.js b/static/haku.js
index efede63..2a734df 100644
--- a/static/haku.js
+++ b/static/haku.js
@@ -257,4 +257,24 @@ export class Haku {
return { status: "ok" };
}
+
+ get astSize() {
+ if (this.#pBrush != 0) {
+ return w.haku_stat_ast_size(this.#pBrush);
+ } else {
+ return 0;
+ }
+ }
+
+ get numRefs() {
+ return w.haku_stat_num_refs(this.#pInstance);
+ }
+
+ get remainingFuel() {
+ return w.haku_stat_remaining_fuel(this.#pInstance);
+ }
+
+ get remainingMemory() {
+ return w.haku_stat_remaining_memory(this.#pInstance);
+ }
}
diff --git a/static/icon/brackets-white.svg b/static/icon/brackets-white.svg
new file mode 100644
index 0000000..06e166d
--- /dev/null
+++ b/static/icon/brackets-white.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/icon/brackets.svg b/static/icon/brackets.svg
new file mode 100644
index 0000000..7404df9
--- /dev/null
+++ b/static/icon/brackets.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/icon/droplet-white.svg b/static/icon/droplet-white.svg
new file mode 100644
index 0000000..ea06ed0
--- /dev/null
+++ b/static/icon/droplet-white.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/icon/droplet.svg b/static/icon/droplet.svg
new file mode 100644
index 0000000..e17734d
--- /dev/null
+++ b/static/icon/droplet.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/icon/memory-white.svg b/static/icon/memory-white.svg
new file mode 100644
index 0000000..202e9e3
--- /dev/null
+++ b/static/icon/memory-white.svg
@@ -0,0 +1,9 @@
+
diff --git a/static/icon/memory.svg b/static/icon/memory.svg
new file mode 100644
index 0000000..2e84fe3
--- /dev/null
+++ b/static/icon/memory.svg
@@ -0,0 +1,9 @@
+
diff --git a/static/icon/object-white.svg b/static/icon/object-white.svg
new file mode 100644
index 0000000..e270309
--- /dev/null
+++ b/static/icon/object-white.svg
@@ -0,0 +1,4 @@
+
diff --git a/static/icon/object.svg b/static/icon/object.svg
new file mode 100644
index 0000000..9054acb
--- /dev/null
+++ b/static/icon/object.svg
@@ -0,0 +1,4 @@
+
diff --git a/static/icon/rkgk design.zip b/static/icon/rkgk design.zip
new file mode 100644
index 0000000..1921ee5
Binary files /dev/null and b/static/icon/rkgk design.zip differ
diff --git a/static/index.css b/static/index.css
index 1b67091..b13f26b 100644
--- a/static/index.css
+++ b/static/index.css
@@ -15,7 +15,7 @@ main {
height: 100%;
position: relative;
- &>.fullscreen {
+ & > .fullscreen {
width: 100%;
height: 100%;
max-width: 100%;
@@ -25,23 +25,26 @@ main {
top: 0;
}
- &>.panels {
+ & > .panels {
--right-width: 384px; /* Overridden by JavaScript */
-
+
box-sizing: border-box;
padding: 16px;
display: grid;
- grid-template-columns: [left] 1fr [right-resize] auto [right] minmax(0, var(--right-width));
+ grid-template-columns: [left] 1fr [right-resize] auto [right] minmax(
+ 0,
+ var(--right-width)
+ );
/* Pass all events through. Children may receive events as normal. */
pointer-events: none;
- &>* {
+ & > * {
pointer-events: all;
}
- &>.right {
+ & > .right {
grid-column: right / right;
min-height: 0;
@@ -53,29 +56,41 @@ main {
pointer-events: none;
- &>* {
+ & > * {
min-width: 0;
min-height: 0;
}
- &>rkgk-resize-handle {
+ & > rkgk-resize-handle {
pointer-events: auto;
}
- &>.docked>rkgk-brush-editor {
+ & > .docked > rkgk-brush-editor {
max-height: 100%;
pointer-events: auto;
}
- &>.floating>rkgk-brush-preview {
- width: 128px;
- height: 128px;
- pointer-events: auto;
+ & > .floating {
+ display: flex;
+ flex-direction: column;
+
+ gap: 12px;
+
+ & > rkgk-brush-preview {
+ width: 128px;
+ height: 128px;
+ pointer-events: auto;
+ }
+
+ & > rkgk-brush-cost-gauges {
+ width: 100%;
+ pointer-events: auto;
+ }
}
}
}
- &>rkgk-canvas-renderer {
+ & > rkgk-canvas-renderer {
width: 100%;
height: 100%;
position: absolute;
@@ -83,7 +98,7 @@ main {
top: 0;
}
- &>rkgk-reticle-renderer {
+ & > rkgk-reticle-renderer {
width: 100%;
height: 100%;
position: absolute;
@@ -92,7 +107,7 @@ main {
overflow: hidden;
}
- &>#js-loading {
+ & > #js-loading {
background-color: var(--color-panel-background);
display: flex;
@@ -111,7 +126,7 @@ rkgk-resize-handle {
cursor: col-resize;
- &>.visual {
+ & > .visual {
width: 2px;
height: 100%;
background-color: var(--color-brand-blue);
@@ -119,7 +134,8 @@ rkgk-resize-handle {
opacity: 0%;
}
- &:hover>.visual, &.dragging>.visual {
+ &:hover > .visual,
+ &.dragging > .visual {
opacity: 100%;
}
}
@@ -130,7 +146,7 @@ rkgk-resize-handle {
rkgk-canvas-renderer {
display: block;
- &>canvas {
+ & > canvas {
display: block;
}
}
@@ -142,7 +158,7 @@ rkgk-reticle-renderer {
pointer-events: none;
- &>.reticles {
+ & > .reticles {
position: relative;
}
}
@@ -153,15 +169,15 @@ rkgk-reticle-cursor {
position: absolute;
display: block;
- &>.container {
- &>.arrow {
+ & > .container {
+ & > .arrow {
width: 24px;
height: 24px;
background-color: var(--color);
clip-path: path("M 0,0 L 13,13 L 6,13 L 0,19 Z");
}
- &>.nickname {
+ & > .nickname {
position: absolute;
top: 20px;
left: 8px;
@@ -188,7 +204,7 @@ rkgk-code-editor {
overflow: auto;
- &>.layer {
+ & > .layer {
position: absolute;
left: 0;
top: 0;
@@ -203,7 +219,7 @@ rkgk-code-editor {
display: flex;
flex-direction: column;
- &>.line {
+ & > .line {
flex-shrink: 0;
white-space: pre-wrap;
@@ -211,14 +227,14 @@ rkgk-code-editor {
}
}
- &>.layer-gutter {
+ & > .layer-gutter {
user-select: none;
counter-reset: line;
color: transparent;
- &>.line {
+ & > .line {
display: flex;
flex-direction: row;
@@ -241,27 +257,27 @@ rkgk-code-editor {
}
}
- &>.layer:not(.layer-gutter) {
+ & > .layer:not(.layer-gutter) {
margin-left: var(--gutter-width);
width: calc(100% - var(--gutter-width));
}
- &>.layer-error-squiggles {
+ & > .layer-error-squiggles {
color: transparent;
- &>.line {
- &>.squiggle {
+ & > .line {
+ & > .squiggle {
text-decoration: underline wavy black;
text-decoration-skip-ink: none;
}
- &>.squiggle-error {
+ & > .squiggle-error {
text-decoration-color: var(--color-error);
}
}
}
- &>textarea {
+ & > textarea {
display: block;
width: calc(100% - var(--gutter-width));
margin: 0;
@@ -294,8 +310,8 @@ rkgk-brush-editor.rkgk-panel {
gap: 4px;
position: relative;
-
- &>.text-area {
+
+ & > .text-area {
display: block;
width: 100%;
margin: 0;
@@ -306,18 +322,19 @@ rkgk-brush-editor.rkgk-panel {
box-sizing: border-box;
}
- &>.errors:empty, &>.error-header:empty {
+ & > .errors:empty,
+ & > .error-header:empty {
display: none;
}
- &>.error-header {
+ & > .error-header {
margin: 0;
margin-top: 0.5em;
font-size: 1rem;
color: var(--color-error);
}
- &>.errors {
+ & > .errors {
margin: 0;
color: var(--color-error);
white-space: pre-wrap;
@@ -334,13 +351,19 @@ rkgk-brush-preview {
display: block;
position: relative;
- background:
- repeating-conic-gradient(var(--checkerboard-light) 0% 25%, var(--checkerboard-dark) 0% 50%)
+ background: repeating-conic-gradient(
+ var(--checkerboard-light) 0% 25%,
+ var(--checkerboard-dark) 0% 50%
+ )
50% 50% / var(--checkerboard-size) var(--checkerboard-size);
border-radius: 4px;
+ & > canvas {
+ border-radius: 4px;
+ }
+
&.error {
- &>canvas {
+ & > canvas {
display: none;
}
@@ -354,10 +377,53 @@ rkgk-brush-preview {
}
}
+/* Brush cost gauges */
+
+rkgk-brush-cost-gauges,
+rkgk-brush-cost-gauges.rkgk-panel {
+ --gauge-size: 20px;
+
+ height: var(--gauge-size);
+ border-radius: 4px;
+
+ display: flex;
+ flex-direction: row;
+
+ overflow: clip; /* clip corners */
+
+ & > rkgk-brush-cost-gauge {
+ display: block;
+ height: var(--gauge-size);
+ flex-grow: 1;
+
+ & > .full {
+ width: 100%;
+ height: 100%;
+
+ clip-path: xywh(0 0 var(--progress) 100%);
+
+ background-color: var(--gauge-color);
+ }
+
+ &.code-size {
+ --gauge-color: var(--color-brand-blue);
+ }
+ &.fuel {
+ --gauge-color: #f44096;
+ }
+ &.objects {
+ --gauge-color: #fd9916;
+ }
+ &.memory {
+ --gauge-color: #5aca40;
+ }
+ }
+}
+
/* Welcome screen */
rkgk-welcome {
- &>dialog {
+ & > dialog {
h3 {
margin: 0.5rem 0;
font-size: 2rem;
@@ -369,7 +435,8 @@ rkgk-welcome {
/* Connection status dialogs */
rkgk-connection-status {
- &>dialog[name='logging-in-dialog'][open], &>dialog[name='disconnected-dialog'][open] {
+ & > dialog[name="logging-in-dialog"][open],
+ & > dialog[name="disconnected-dialog"][open] {
border: none;
outline: none;
background: none;
@@ -379,8 +446,8 @@ rkgk-connection-status {
align-items: center;
}
- &>dialog[name='error-dialog'][open] {
- & textarea[name='error-text'] {
+ & > dialog[name="error-dialog"][open] {
+ & textarea[name="error-text"] {
box-sizing: border-box;
width: 100%;
resize: none;
@@ -394,7 +461,7 @@ rkgk-connection-status {
.menu-bar {
--border-radius: 4px;
-
+
display: flex;
align-items: center;
box-sizing: border-box;
@@ -403,9 +470,9 @@ rkgk-connection-status {
height: 24px;
border-radius: var(--border-radius);
- &>a {
+ & > a {
display: block;
-
+
color: var(--color-text);
padding: 4px 8px;
text-decoration: none;
@@ -432,11 +499,10 @@ rkgk-connection-status {
}
}
- &>hr {
+ & > hr {
height: 100%;
margin: 0;
border: none;
border-right: 1px solid var(--color-panel-border);
}
}
-
diff --git a/static/index.js b/static/index.js
index 5434f61..58ac40e 100644
--- a/static/index.js
+++ b/static/index.js
@@ -18,6 +18,7 @@ let canvasRenderer = main.querySelector("rkgk-canvas-renderer");
let reticleRenderer = main.querySelector("rkgk-reticle-renderer");
let brushEditor = main.querySelector("rkgk-brush-editor");
let brushPreview = main.querySelector("rkgk-brush-preview");
+let brushCostGauges = main.querySelector("rkgk-brush-cost-gauges");
let welcome = main.querySelector("rkgk-welcome");
let connectionStatus = main.querySelector("rkgk-connection-status");
@@ -251,12 +252,15 @@ function readUrl(urlString) {
let compileResult = currentUser.setBrush(brushEditor.code);
brushEditor.renderHakuResult("Compilation", compileResult);
+ brushCostGauges.update(currentUser.getStats(session.wallInfo));
+
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",
diff --git a/static/online-users.js b/static/online-users.js
index 16f39a0..9424412 100644
--- a/static/online-users.js
+++ b/static/online-users.js
@@ -89,6 +89,19 @@ export class User {
return false;
}
}
+
+ getStats(wallInfo) {
+ return {
+ astSize: this.haku.astSize,
+ astSizeMax: wallInfo.hakuLimits.ast_capacity,
+ numRefs: this.haku.numRefs,
+ numRefsMax: wallInfo.hakuLimits.ref_capacity,
+ fuel: wallInfo.hakuLimits.fuel - this.haku.remainingFuel,
+ fuelMax: wallInfo.hakuLimits.fuel,
+ memory: wallInfo.hakuLimits.memory - this.haku.remainingMemory,
+ memoryMax: wallInfo.hakuLimits.memory,
+ };
+ }
}
export class OnlineUsers extends EventTarget {
diff --git a/template/index.hbs.html b/template/index.hbs.html
index 4aeff19..f9d846a 100644
--- a/template/index.hbs.html
+++ b/template/index.hbs.html
@@ -21,6 +21,7 @@