implement brush cost gauges
they're a little ugly at the moment, and can be a little useless for most simple brushes, but whatever we'll make them better later
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										81
									
								
								static/brush-cost.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								static/icon/brackets-white.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M7 4H4V12H7M9 4H12V12H9" stroke="white" stroke-width="2"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 171 B  | 
							
								
								
									
										3
									
								
								static/icon/brackets.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M7 4H4V12H7M9 4H12V12H9" stroke="black" stroke-width="2"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 171 B  | 
							
								
								
									
										3
									
								
								static/icon/droplet-white.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M5 10C5 8.36528 6.00212 5.72844 7.18726 3.45463C7.53619 2.78519 8.46381 2.78519 8.81274 3.45463C9.99788 5.72844 11 8.36528 11 10C11 12 9.5 13 8 13C6.5 13 5 12 5 10Z" fill="white"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 293 B  | 
							
								
								
									
										3
									
								
								static/icon/droplet.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M5 10C5 8.36528 6.00212 5.72844 7.18726 3.45463C7.53619 2.78519 8.46381 2.78519 8.81274 3.45463C9.99788 5.72844 11 8.36528 11 10C11 12 9.5 13 8 13C6.5 13 5 12 5 10Z" fill="black"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 293 B  | 
							
								
								
									
										9
									
								
								static/icon/memory-white.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<rect x="5.05" y="3.05" width="5.9" height="9.9" fill="white" stroke="white" stroke-width="0.1"/>
 | 
			
		||||
<rect x="3.05" y="4.05" width="1.9" height="1.9" fill="white" stroke="white" stroke-width="0.1"/>
 | 
			
		||||
<rect x="11.05" y="4.05" width="1.9" height="1.9" fill="white" stroke="white" stroke-width="0.1"/>
 | 
			
		||||
<rect x="3.05" y="7.05" width="1.9" height="1.9" fill="white" stroke="white" stroke-width="0.1"/>
 | 
			
		||||
<rect x="11.05" y="7.05" width="1.9" height="1.9" fill="white" stroke="white" stroke-width="0.1"/>
 | 
			
		||||
<rect x="3.05" y="10.05" width="1.9" height="1.9" fill="white" stroke="white" stroke-width="0.1"/>
 | 
			
		||||
<rect x="11.05" y="10.05" width="1.9" height="1.9" fill="white" stroke="white" stroke-width="0.1"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 794 B  | 
							
								
								
									
										9
									
								
								static/icon/memory.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<rect x="5.05" y="3.05" width="5.9" height="9.9" fill="black" stroke="black" stroke-width="0.1"/>
 | 
			
		||||
<rect x="3.05" y="4.05" width="1.9" height="1.9" fill="black" stroke="black" stroke-width="0.1"/>
 | 
			
		||||
<rect x="11.05" y="4.05" width="1.9" height="1.9" fill="black" stroke="black" stroke-width="0.1"/>
 | 
			
		||||
<rect x="3.05" y="7.05" width="1.9" height="1.9" fill="black" stroke="black" stroke-width="0.1"/>
 | 
			
		||||
<rect x="11.05" y="7.05" width="1.9" height="1.9" fill="black" stroke="black" stroke-width="0.1"/>
 | 
			
		||||
<rect x="3.05" y="10.05" width="1.9" height="1.9" fill="black" stroke="black" stroke-width="0.1"/>
 | 
			
		||||
<rect x="11.05" y="10.05" width="1.9" height="1.9" fill="black" stroke="black" stroke-width="0.1"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 794 B  | 
							
								
								
									
										4
									
								
								static/icon/object-white.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 6C12 7.10457 11.1046 8 10 8V10C12.2091 10 14 8.20914 14 6C14 3.79086 12.2091 2 10 2C7.79086 2 6 3.79086 6 6H8C8 4.89543 8.89543 4 10 4C11.1046 4 12 4.89543 12 6Z" fill="white"/>
 | 
			
		||||
<rect x="3" y="7" width="6" height="6" stroke="white" stroke-width="2"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 407 B  | 
							
								
								
									
										4
									
								
								static/icon/object.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 6C12 7.10457 11.1046 8 10 8V10C12.2091 10 14 8.20914 14 6C14 3.79086 12.2091 2 10 2C7.79086 2 6 3.79086 6 6H8C8 4.89543 8.89543 4 10 4C11.1046 4 12 4.89543 12 6Z" fill="black"/>
 | 
			
		||||
<rect x="3" y="7" width="6" height="6" stroke="black" stroke-width="2"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 407 B  | 
							
								
								
									
										
											BIN
										
									
								
								static/icon/rkgk design.zip
									
										
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										166
									
								
								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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,7 @@
 | 
			
		|||
        <script type="module">
 | 
			
		||||
            import "rkgk/live-reload.js";
 | 
			
		||||
 | 
			
		||||
            import "rkgk/brush-cost.js";
 | 
			
		||||
            import "rkgk/brush-editor.js";
 | 
			
		||||
            import "rkgk/brush-preview.js";
 | 
			
		||||
            import "rkgk/canvas-renderer.js";
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +65,7 @@
 | 
			
		|||
                <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
 | 
			
		||||
                        data-direction="vertical"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||