feat: Electrobun Svelte+WGPU prototype (Dawn GPU confirmed on Linux)

- Svelte 5 frontend with Catppuccin Mocha theme, 2 project cards
- Electrobun v1.16.0 with bundleWGPU: true (Dawn on Linux x64)
- WebKitGTK webview + WGPU surface coexistence confirmed
- CPU: 6.5% idle (CSS animation + WebKitGTK overhead)
- Port 9760 for dev server (project convention)
This commit is contained in:
Hibryda 2026-03-20 01:25:41 +01:00
parent 1f20fc460e
commit cfc135ffaf
29 changed files with 1106 additions and 1020 deletions

69
ui-electrobun/README.md Normal file
View file

@ -0,0 +1,69 @@
# Svelte Electrobun Template
A fast Electrobun desktop app template with Svelte 5 and Vite for hot module replacement (HMR).
## Getting Started
```bash
# Install dependencies
bun install
# Development without HMR (uses bundled assets)
bun run dev
# Development with HMR (recommended)
bun run dev:hmr
# Build for production
bun run build
# Build for production release
bun run build:prod
```
## How HMR Works
When you run `bun run dev:hmr`:
1. **Vite dev server** starts on `http://localhost:5173` with HMR enabled
2. **Electrobun** starts and detects the running Vite server
3. The app loads from the Vite dev server instead of bundled assets
4. Changes to Svelte components update instantly without full page reload
When you run `bun run dev` (without HMR):
1. Electrobun starts and loads from `views://mainview/index.html`
2. You need to rebuild (`bun run build`) to see changes
## Project Structure
```
├── src/
│ ├── bun/
│ │ └── index.ts # Main process (Electrobun/Bun)
│ └── mainview/
│ ├── App.svelte # Svelte app component
│ ├── main.ts # Svelte entry point
│ ├── index.html # HTML template
│ └── app.css # Global styles
├── electrobun.config.ts # Electrobun configuration
├── vite.config.ts # Vite configuration
├── svelte.config.js # Svelte configuration
└── package.json
```
## Svelte 5 Features
This template uses Svelte 5 with the new runes syntax:
- `$state()` - reactive state
- `$derived()` - computed values
- `$effect()` - side effects
## Customizing
- **Svelte components**: Edit files in `src/mainview/`
- **Global styles**: Edit `src/mainview/app.css`
- **Vite settings**: Edit `vite.config.ts`
- **Window settings**: Edit `src/bun/index.ts`
- **App metadata**: Edit `electrobun.config.ts`

View file

@ -5,9 +5,9 @@
<key>CFBundleExecutable</key>
<string>launcher</string>
<key>CFBundleIdentifier</key>
<string>wgpu.electrobun.dev</string>
<string>dev.agor.orchestrator</string>
<key>CFBundleName</key>
<string>wgpu-dev</string>
<string>AgentOrchestrator-dev</string>
<key>CFBundleVersion</key>
<string>0.0.1</string>
<key>CFBundlePackageType</key>

View file

@ -3206,7 +3206,7 @@ function toCString(jsString, addNullTerminator = true) {
const buff = Buffer.from(jsString + appendWith, "utf8");
return ptr(buff);
}
var menuDataRegistry, menuDataCounter = 0, ELECTROBUN_DELIMITER = "|EB|", native, ffi, WGPUBridge, windowCloseCallback, windowMoveCallback, windowResizeCallback, windowFocusCallback, windowBlurCallback, windowKeyCallback, getMimeType, getHTMLForWebviewSync, urlOpenCallback, appReopenCallback, quitRequestedCallback, globalShortcutHandlers, globalShortcutCallback, Screen, sessionCache, webviewDecideNavigation, webviewEventHandler = (id, eventName, detail) => {
var menuDataRegistry, menuDataCounter = 0, ELECTROBUN_DELIMITER = "|EB|", native, ffi, WGPUBridge, windowCloseCallback, windowMoveCallback, windowResizeCallback, windowFocusCallback, windowBlurCallback, windowKeyCallback, getMimeType, getHTMLForWebviewSync, urlOpenCallback, appReopenCallback, quitRequestedCallback, globalShortcutHandlers, globalShortcutCallback, sessionCache, webviewDecideNavigation, webviewEventHandler = (id, eventName, detail) => {
const webview = BrowserView.getById(id);
if (!webview) {
console.error("[webviewEventHandler] No webview found for id:", id);
@ -4563,60 +4563,6 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
threadsafe: true
});
native.symbols.setGlobalShortcutCallback(globalShortcutCallback);
Screen = {
getPrimaryDisplay: () => {
const jsonStr = native.symbols.getPrimaryDisplay();
if (!jsonStr) {
return {
id: 0,
bounds: { x: 0, y: 0, width: 0, height: 0 },
workArea: { x: 0, y: 0, width: 0, height: 0 },
scaleFactor: 1,
isPrimary: true
};
}
try {
return JSON.parse(jsonStr.toString());
} catch {
return {
id: 0,
bounds: { x: 0, y: 0, width: 0, height: 0 },
workArea: { x: 0, y: 0, width: 0, height: 0 },
scaleFactor: 1,
isPrimary: true
};
}
},
getAllDisplays: () => {
const jsonStr = native.symbols.getAllDisplays();
if (!jsonStr) {
return [];
}
try {
return JSON.parse(jsonStr.toString());
} catch {
return [];
}
},
getCursorScreenPoint: () => {
const jsonStr = native.symbols.getCursorScreenPoint();
if (!jsonStr) {
return { x: 0, y: 0 };
}
try {
return JSON.parse(jsonStr.toString());
} catch {
return { x: 0, y: 0 };
}
},
getMouseButtons: () => {
try {
return native.symbols.getMouseButtons();
} catch {
return 0n;
}
}
};
sessionCache = new Map;
webviewDecideNavigation = new JSCallback((_webviewId, _url) => {
return true;
@ -231800,438 +231746,30 @@ init_BuildConfig();
await init_native();
// src/bun/index.ts
import { CString as CString3, ptr as ptr3, toArrayBuffer as toArrayBuffer3 } from "bun:ffi";
var WGPUNative2 = webGPU_default.native;
var WGPU_STRLEN2 = 0xffffffffffffffffn;
var WGPU_DEPTH_SLICE_UNDEFINED2 = 4294967295;
var WGPUTextureUsage_RenderAttachment2 = 0x0000000000000010n;
var WGPUBufferUsage_Vertex = 0x0000000000000020n;
var WGPUBufferUsage_CopyDst = 0x0000000000000008n;
var WGPUVertexFormat_Float322 = 28;
var WGPUVertexFormat_Float32x22 = 29;
var WGPUVertexFormat_Float32x42 = 31;
var WGPUVertexStepMode_Vertex2 = 1;
var WGPUPrimitiveTopology_TriangleList2 = 4;
var WGPUFrontFace_CCW2 = 1;
var WGPUCullMode_None2 = 1;
var WGPUPresentMode_Fifo2 = 1;
var KEEPALIVE = [];
function writePtr2(view, offset, value) {
view.setBigUint64(offset, BigInt(value ?? 0), true);
var DEV_SERVER_PORT = 9760;
var DEV_SERVER_URL = `http://localhost:${DEV_SERVER_PORT}`;
async function getMainViewUrl() {
const channel = await Updater.localInfo.channel();
if (channel === "dev") {
try {
await fetch(DEV_SERVER_URL, { method: "HEAD" });
console.log(`HMR enabled: Using Vite dev server at ${DEV_SERVER_URL}`);
return DEV_SERVER_URL;
} catch {
console.log("Vite dev server not running. Run 'bun run dev:hmr' for HMR support.");
}
}
return "views://mainview/index.html";
}
function writeU322(view, offset, value) {
view.setUint32(offset, value >>> 0, true);
}
function writeU642(view, offset, value) {
view.setBigUint64(offset, value, true);
}
function makeSurfaceConfiguration2(devicePtr, width, height, format) {
const buffer2 = new ArrayBuffer(64);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writePtr2(view, 8, devicePtr);
writeU322(view, 16, format);
writeU322(view, 20, 0);
writeU642(view, 24, WGPUTextureUsage_RenderAttachment2);
writeU322(view, 32, width);
writeU322(view, 36, height);
writeU642(view, 40, 0n);
writePtr2(view, 48, 0);
writeU322(view, 56, 1);
writeU322(view, 60, WGPUPresentMode_Fifo2);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeShaderSourceWGSL2(codePtr) {
const buffer2 = new ArrayBuffer(32);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writeU322(view, 8, 2);
writeU322(view, 12, 0);
writePtr2(view, 16, codePtr);
writeU642(view, 24, WGPU_STRLEN2);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeShaderModuleDescriptor2(nextInChainPtr) {
const buffer2 = new ArrayBuffer(24);
const view = new DataView(buffer2);
writePtr2(view, 0, nextInChainPtr);
writePtr2(view, 8, 0);
writeU642(view, 16, 0n);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeVertexAttribute2(offset, shaderLocation, format) {
const buffer2 = new ArrayBuffer(32);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writeU322(view, 8, format);
writeU322(view, 12, 0);
writeU642(view, 16, BigInt(offset));
writeU322(view, 24, shaderLocation);
writeU322(view, 28, 0);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeVertexBufferLayout2(attributePtr, attributeCount, stride) {
const buffer2 = new ArrayBuffer(40);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writeU322(view, 8, WGPUVertexStepMode_Vertex2);
writeU322(view, 12, 0);
writeU642(view, 16, BigInt(stride));
writeU642(view, 24, BigInt(attributeCount));
writePtr2(view, 32, attributePtr);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeColorTargetState2(format) {
const buffer2 = new ArrayBuffer(32);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writeU322(view, 8, format);
writeU322(view, 12, 0);
writePtr2(view, 16, 0);
writeU642(view, 24, 0x0fn);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeVertexState2(modulePtr, entryPointPtr, bufferLayoutPtr) {
const buffer2 = new ArrayBuffer(64);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writePtr2(view, 8, modulePtr);
writePtr2(view, 16, entryPointPtr);
writeU642(view, 24, WGPU_STRLEN2);
writeU642(view, 32, 0n);
writePtr2(view, 40, 0);
writeU642(view, 48, 1n);
writePtr2(view, 56, bufferLayoutPtr);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeFragmentState2(modulePtr, entryPointPtr, targetPtr) {
const buffer2 = new ArrayBuffer(64);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writePtr2(view, 8, modulePtr);
writePtr2(view, 16, entryPointPtr);
writeU642(view, 24, WGPU_STRLEN2);
writeU642(view, 32, 0n);
writePtr2(view, 40, 0);
writeU642(view, 48, 1n);
writePtr2(view, 56, targetPtr);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makePrimitiveState2() {
const buffer2 = new ArrayBuffer(32);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writeU322(view, 8, WGPUPrimitiveTopology_TriangleList2);
writeU322(view, 12, 0);
writeU322(view, 16, WGPUFrontFace_CCW2);
writeU322(view, 20, WGPUCullMode_None2);
writeU322(view, 24, 0);
writeU322(view, 28, 0);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeMultisampleState2() {
const buffer2 = new ArrayBuffer(24);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writeU322(view, 8, 1);
writeU322(view, 12, 4294967295);
writeU322(view, 16, 0);
writeU322(view, 20, 0);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeRenderPipelineDescriptor2(vertexStatePtr, primitiveStatePtr, multisampleStatePtr, fragmentStatePtr) {
const buffer2 = new ArrayBuffer(168);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writePtr2(view, 8, 0);
writeU642(view, 16, 0n);
writePtr2(view, 24, 0);
new Uint8Array(buffer2, 32, 64).set(new Uint8Array(vertexStatePtr.buffer));
new Uint8Array(buffer2, 96, 32).set(new Uint8Array(primitiveStatePtr.buffer));
writePtr2(view, 128, 0);
new Uint8Array(buffer2, 136, 24).set(new Uint8Array(multisampleStatePtr.buffer));
writePtr2(view, 160, fragmentStatePtr.ptr);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeBufferDescriptor2(size) {
const buffer2 = new ArrayBuffer(48);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writePtr2(view, 8, 0);
writeU642(view, 16, 0n);
writeU642(view, 24, WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst);
writeU642(view, 32, BigInt(size));
writeU322(view, 40, 0);
writeU322(view, 44, 0);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeCommandEncoderDescriptor2() {
const buffer2 = new ArrayBuffer(24);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writePtr2(view, 8, 0);
writeU642(view, 16, 0n);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeSurfaceTexture2() {
const buffer2 = new ArrayBuffer(24);
return { buffer: buffer2, view: new DataView(buffer2), ptr: ptr3(buffer2) };
}
function makeRenderPassColorAttachment2(viewPtr, clear) {
const buffer2 = new ArrayBuffer(72);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writePtr2(view, 8, viewPtr);
writeU322(view, 16, WGPU_DEPTH_SLICE_UNDEFINED2);
writeU322(view, 20, 0);
writePtr2(view, 24, 0);
writeU322(view, 32, 2);
writeU322(view, 36, 1);
view.setFloat64(40, clear.r, true);
view.setFloat64(48, clear.g, true);
view.setFloat64(56, clear.b, true);
view.setFloat64(64, clear.a, true);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeRenderPassDescriptor2(colorAttachmentPtr) {
const buffer2 = new ArrayBuffer(64);
const view = new DataView(buffer2);
writePtr2(view, 0, 0);
writePtr2(view, 8, 0);
writeU642(view, 16, 0n);
writeU642(view, 24, 1n);
writePtr2(view, 32, colorAttachmentPtr);
writePtr2(view, 40, 0);
writePtr2(view, 48, 0);
writePtr2(view, 56, 0);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
function makeCommandBufferArray(cmdPtr) {
const buffer2 = new BigUint64Array([BigInt(cmdPtr)]);
return { buffer: buffer2, ptr: ptr3(buffer2) };
}
var size = 640;
var display = Screen.getPrimaryDisplay();
var workArea = display.workArea;
var x = workArea.x + Math.floor((workArea.width - size) / 2);
var y = workArea.y + Math.floor((workArea.height - size) / 2);
var win = new GpuWindow({
title: "WGPU Shader",
frame: { width: size, height: size, x, y },
titleBarStyle: "default",
transparent: false
var url = await getMainViewUrl();
var mainWindow = new BrowserWindow({
title: "Agent Orchestrator \u2014 Electrobun",
url,
frame: {
width: 1400,
height: 900,
x: 100,
y: 100
}
});
if (!WGPUNative2.available) {
throw new Error("WGPU not available for wgpu");
}
var instance = WGPUNative2.symbols.wgpuCreateInstance(0);
var surface = WGPUBridge.createSurfaceForView(instance, win.wgpuView.ptr);
var adapterDevice = new BigUint64Array(2);
WGPUBridge.createAdapterDeviceMainThread(instance, surface, ptr3(adapterDevice));
var adapter = Number(adapterDevice[0]);
var device = Number(adapterDevice[1]);
if (!adapter || !device) {
throw new Error("Failed to get WGPU adapter/device");
}
var queue = WGPUNative2.symbols.wgpuDeviceGetQueue(device);
var capsBuffer = new ArrayBuffer(64);
var capsView = new DataView(capsBuffer);
WGPUNative2.symbols.wgpuSurfaceGetCapabilities(surface, adapter, ptr3(capsBuffer));
var formatCount = Number(capsView.getBigUint64(16, true));
var formatPtr = Number(capsView.getBigUint64(24, true));
var surfaceFormat = 23;
if (formatCount && formatPtr) {
const formats = new Uint32Array(toArrayBuffer3(formatPtr, 0, formatCount * 4));
if (formats.length)
surfaceFormat = formats[0];
}
var surfaceConfig = makeSurfaceConfiguration2(device, size, size, surfaceFormat);
WGPUBridge.surfaceConfigure(surface, surfaceConfig.ptr);
var shaderText = `
struct VSOut {
@builtin(position) position : vec4<f32>,
@location(0) uv : vec2<f32>,
@location(1) time : f32,
@location(2) resolution : vec2<f32>,
@location(3) mouse : vec4<f32>,
};
@vertex
fn vs_main(
@location(0) position: vec2<f32>,
@location(1) time: f32,
@location(2) resolution: vec2<f32>,
@location(3) mouse: vec4<f32>
) -> VSOut {
var out: VSOut;
out.position = vec4<f32>(position, 0.0, 1.0);
out.uv = position;
out.time = time;
out.resolution = resolution;
out.mouse = mouse;
return out;
}
@fragment
fn fs_main(
@location(0) uv: vec2<f32>,
@location(1) time: f32,
@location(2) resolution: vec2<f32>,
@location(3) mouse: vec4<f32>
) -> @location(0) vec4<f32> {
let fragCoord = (uv * 0.5 + vec2<f32>(0.5)) * resolution;
let m = (mouse.xy / max(resolution, vec2<f32>(1.0))) * 2.0 - vec2<f32>(1.0);
let loopMax: i32 = select(32, 64, mouse.z > 0.5);
var o = vec4<f32>(0.0);
var i: f32 = 0.0;
var d: f32 = 0.0;
var c: f32 = 0.0;
var s: f32 = 0.0;
var q = vec3<f32>(0.0);
var p = vec3<f32>(0.0);
let r = vec3<f32>(resolution, 0.0);
var dir = normalize(vec3<f32>((fragCoord + fragCoord - r.xy) / r.y, 1.0));
dir.x = dir.x + m.x * 0.35;
dir.y = dir.y + -m.y * 0.35;
for (var iter: i32 = 0; iter < loopMax; iter = iter + 1) {
i = f32(iter + 1);
p = dir * d;
p.z = p.z + time * 4.0;
q = p;
s = 0.0;
c = 20.0;
loop {
if (c <= 0.2) { break; }
let m = mat2x2<f32>(
vec2<f32>(cos(c / 30.0 + 0.0), cos(c / 30.0 + 33.0)),
vec2<f32>(cos(c / 30.0 + 11.0), cos(c / 30.0 + 0.0))
);
let xz = m * vec2<f32>(p.x, p.z);
p.x = xz.x;
p.z = xz.y;
p = abs(fract(p / c) * c - vec3<f32>(c * 0.5)) - vec3<f32>(c * 0.2);
s = max(
9.0 + 3.0 * sin(q.z * 0.05) - abs(q.x),
max(s, min(p.x, min(p.y, p.z)))
);
p = q;
c = c * 0.5;
}
let sinp = sin(p * 12.0);
let dotv = dot(sinp, vec3<f32>(0.1, 0.1, 0.1));
s = min(s, p.y + 8.0 + dotv);
d = d + s;
let add = i / max(s, 0.001);
o = o + vec4<f32>(add, add, add, add);
}
let denom = max(d, 0.000001);
o = tanh(o / denom / 30000.0);
return vec4<f32>(o.xyz, 1.0);
}
`;
var shaderBytes = new TextEncoder().encode(shaderText + "\x00");
var shaderBuf = new Uint8Array(shaderBytes);
KEEPALIVE.push(shaderBuf);
var shaderPtr = ptr3(shaderBuf);
var shaderSource = makeShaderSourceWGSL2(shaderPtr);
var shaderDesc = makeShaderModuleDescriptor2(shaderSource.ptr);
var shaderModule = WGPUNative2.symbols.wgpuDeviceCreateShaderModule(device, shaderDesc.ptr);
var entryPoint = new CString3("vs_main");
var fragEntryPoint = new CString3("fs_main");
KEEPALIVE.push(entryPoint, fragEntryPoint);
var posAttr = makeVertexAttribute2(0, 0, WGPUVertexFormat_Float32x22);
var timeAttr = makeVertexAttribute2(8, 1, WGPUVertexFormat_Float322);
var resAttr = makeVertexAttribute2(12, 2, WGPUVertexFormat_Float32x22);
var mouseAttr = makeVertexAttribute2(20, 3, WGPUVertexFormat_Float32x42);
var attrBuf = new ArrayBuffer(32 * 4);
new Uint8Array(attrBuf, 0, 32).set(new Uint8Array(posAttr.buffer));
new Uint8Array(attrBuf, 32, 32).set(new Uint8Array(timeAttr.buffer));
new Uint8Array(attrBuf, 64, 32).set(new Uint8Array(resAttr.buffer));
new Uint8Array(attrBuf, 96, 32).set(new Uint8Array(mouseAttr.buffer));
var attrPtr = ptr3(attrBuf);
KEEPALIVE.push(attrBuf);
var vertexLayout = makeVertexBufferLayout2(attrPtr, 4, 36);
var vertexState = makeVertexState2(shaderModule, entryPoint.ptr, vertexLayout.ptr);
var colorTarget = makeColorTargetState2(surfaceFormat);
var fragmentState = makeFragmentState2(shaderModule, fragEntryPoint.ptr, colorTarget.ptr);
var primitiveState = makePrimitiveState2();
var multisampleState = makeMultisampleState2();
var pipelineDesc = makeRenderPipelineDescriptor2(vertexState, primitiveState, multisampleState, fragmentState);
var pipeline = WGPUNative2.symbols.wgpuDeviceCreateRenderPipeline(device, pipelineDesc.ptr);
var vertexCount = 3;
var bufferDesc = makeBufferDescriptor2(vertexCount * 9 * 4);
var vertexBuffer = WGPUNative2.symbols.wgpuDeviceCreateBuffer(device, bufferDesc.ptr);
var encoderDesc = makeCommandEncoderDescriptor2();
var lastLeftDown = false;
var qualityBoost = false;
var clickX = 0;
var clickY = 0;
function renderFrame() {
const sizeNow = win.getSize();
const t = performance.now() * 0.001;
const positions = [-1, -1, 3, -1, -1, 3];
const frame2 = win.getFrame();
const cursor = Screen.getCursorScreenPoint();
const rawX = cursor.x - frame2.x;
const rawY = cursor.y - frame2.y;
const mx = Math.max(0, Math.min(frame2.width, rawX));
const my = Math.max(0, Math.min(frame2.height, rawY));
const buttons = Screen.getMouseButtons();
const leftDown = (buttons & 1n) === 1n;
if (leftDown && !lastLeftDown) {
qualityBoost = !qualityBoost;
clickX = mx;
clickY = my;
}
lastLeftDown = leftDown;
const packed = new Float32Array(vertexCount * 9);
for (let i = 0;i < vertexCount; i += 1) {
const idx = i * 9;
packed[idx] = positions[i * 2];
packed[idx + 1] = positions[i * 2 + 1];
packed[idx + 2] = t;
packed[idx + 3] = sizeNow.width;
packed[idx + 4] = sizeNow.height;
packed[idx + 5] = mx;
packed[idx + 6] = my;
packed[idx + 7] = qualityBoost ? clickX : 0;
packed[idx + 8] = qualityBoost ? clickY : 0;
}
WGPUNative2.symbols.wgpuQueueWriteBuffer(queue, vertexBuffer, 0, ptr3(packed), packed.byteLength);
WGPUNative2.symbols.wgpuInstanceProcessEvents(instance);
const surfaceTexture = makeSurfaceTexture2();
WGPUBridge.surfaceGetCurrentTexture(surface, surfaceTexture.ptr);
const status = surfaceTexture.view.getUint32(16, true);
if (status !== 1 && status !== 2)
return;
const texPtr = Number(surfaceTexture.view.getBigUint64(8, true));
if (!texPtr)
return;
const textureView = WGPUNative2.symbols.wgpuTextureCreateView(texPtr, 0);
if (!textureView)
return;
const colorAttachment = makeRenderPassColorAttachment2(textureView, {
r: 0.05,
g: 0.05,
b: 0.1,
a: 1
});
const renderPassDesc = makeRenderPassDescriptor2(colorAttachment.ptr);
const encoder = WGPUNative2.symbols.wgpuDeviceCreateCommandEncoder(device, encoderDesc.ptr);
const pass2 = WGPUNative2.symbols.wgpuCommandEncoderBeginRenderPass(encoder, renderPassDesc.ptr);
WGPUNative2.symbols.wgpuRenderPassEncoderSetPipeline(pass2, pipeline);
WGPUNative2.symbols.wgpuRenderPassEncoderSetVertexBuffer(pass2, 0, vertexBuffer, 0, packed.byteLength);
WGPUNative2.symbols.wgpuRenderPassEncoderDraw(pass2, vertexCount, 1, 0, 0);
WGPUNative2.symbols.wgpuRenderPassEncoderEnd(pass2);
const commandBuffer = WGPUNative2.symbols.wgpuCommandEncoderFinish(encoder, 0);
const commandArray = makeCommandBufferArray(commandBuffer);
WGPUNative2.symbols.wgpuQueueSubmit(queue, 1, commandArray.ptr);
WGPUBridge.surfacePresent(surface);
WGPUNative2.symbols.wgpuTextureViewRelease(textureView);
WGPUNative2.symbols.wgpuTextureRelease(texPtr);
WGPUNative2.symbols.wgpuCommandBufferRelease(commandBuffer);
WGPUNative2.symbols.wgpuCommandEncoderRelease(encoder);
}
setInterval(renderFrame, 16);
console.log("Agent Orchestrator (Electrobun) started!");

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Svelte App</title>
<script type="module" crossorigin src="/assets/index-D-cE0ySN.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DpJ88YKe.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

View file

@ -0,0 +1 @@
{"version":"0.0.1","hash":"dev","channel":"dev","baseUrl":"","name":"AgentOrchestrator-dev","identifier":"dev.agor.orchestrator"}

View file

@ -1 +0,0 @@
{"version":"0.0.1","hash":"dev","channel":"dev","baseUrl":"","name":"wgpu-dev","identifier":"wgpu.electrobun.dev"}

View file

@ -3,34 +3,180 @@
"configVersion": 1,
"workspaces": {
"": {
"name": "electrobun-wgpu",
"name": "electrobun-svelte",
"dependencies": {
"electrobun": "latest",
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.1",
"@types/bun": "latest",
"concurrently": "^9.1.0",
"svelte": "^5.14.1",
"typescript": "^5.7.2",
"vite": "^6.0.3",
},
},
},
"packages": {
"@babylonjs/core": ["@babylonjs/core@7.54.3", "", {}, "sha512-P5ncXVd8GEUJLhwloP9V0oVwQYIrvZztguVeLlvd5Rx+9aQnenKjpV8auJ6SRsUlAmNZU4pFTKzwF6o2EUfhAw=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@malept/cross-spawn-promise": ["@malept/cross-spawn-promise@1.1.1", "", { "dependencies": { "cross-spawn": "^7.0.1" } }, "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.1", "", { "os": "android", "cpu": "arm" }, "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA=="],
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g=="],
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw=="],
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw=="],
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w=="],
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg=="],
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.1", "", { "os": "none", "cpu": "arm64" }, "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA=="],
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw=="],
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.8", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA=="],
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.1.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ=="],
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="],
"@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="],
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
"@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="],
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
"ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
"basic-ftp": ["basic-ftp@5.2.0", "", {}, "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw=="],
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
"bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"concurrently": ["concurrently@9.2.1", "", { "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", "shell-quote": "1.8.3", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", "concurrently": "dist/bin/concurrently.js" } }, "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
@ -40,20 +186,42 @@
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
"degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="],
"devalue": ["devalue@5.6.1", "", {}, "sha512-jDwizj+IlEZBunHcOuuFVBnIMPAEHvTsJj0BcIp94xYguLRVBcXO853px/MyIJvbVzWdsGvrRweIUWJw8hBP7A=="],
"electrobun": ["electrobun@1.16.0", "", { "dependencies": { "@babylonjs/core": "^7.45.0", "@types/bun": "^1.3.8", "png-to-ico": "^2.1.8", "proxy-agent": "^6.5.0", "rcedit": "^4.0.1", "three": "^0.165.0" }, "bin": { "electrobun": "bin/electrobun.cjs" } }, "sha512-KO/GQO6vpWACJXizqD8F551xtRAgg83Dcfzmjo+6qqCseobttu9x+HL40n+CBnYTe+kBvFXzwso2ZrPuec78zg=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="],
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
"esrap": ["esrap@2.2.1", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-GiYWG34AN/4CUyaWAgunGt0Rxvr1PTMlGC0vvEov/uOQYWne2bpN03Um+k8jT+q3op33mKouP2zeJ6OlM+qeUg=="],
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
"get-uri": ["get-uri@6.0.5", "", { "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" } }, "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
@ -62,16 +230,28 @@
"is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
"is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
"lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="],
"pac-proxy-agent": ["pac-proxy-agent@7.2.0", "", { "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", "socks-proxy-agent": "^8.0.5" } }, "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA=="],
@ -80,20 +260,34 @@
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"png-to-ico": ["png-to-ico@2.1.8", "", { "dependencies": { "@types/node": "^17.0.36", "minimist": "^1.2.6", "pngjs": "^6.0.0" }, "bin": { "png-to-ico": "bin/cli.js" } }, "sha512-Nf+IIn/cZ/DIZVdGveJp86NG5uNib1ZXMiDd/8x32HCTeKSvgpyg6D/6tUBn1QO/zybzoMK0/mc3QRgAyXdv9w=="],
"pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"proxy-agent": ["proxy-agent@6.5.0", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", "socks-proxy-agent": "^8.0.5" } }, "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A=="],
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"rcedit": ["rcedit@4.0.1", "", { "dependencies": { "cross-spawn-windows-exe": "^1.1.0" } }, "sha512-bZdaQi34krFWhrDn+O53ccBDw0MkAT2Vhu75SqhtvhQu4OPyFM4RoVheyYiVQYdjhUi6EJMVWQ0tR6bCIYVkUg=="],
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
"rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="],
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
"socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],
@ -102,16 +296,50 @@
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
"svelte": ["svelte@5.46.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.5.0", "esm-env": "^1.2.1", "esrap": "^2.2.1", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-ynjfCHD3nP2el70kN5Pmg37sSi0EjOm9FgHYQdC4giWG/hzO3AatzXXJJgP305uIhGQxSufJLuYWtkY8uK/8RA=="],
"three": ["three@0.165.0", "", {}, "sha512-cc96IlVYGydeceu0e5xq70H8/yoVT/tXBxV/W8A/U6uOq7DXc4/s1Mkmnu6SqoYGhSRWWYFOhVwvq6V0VtbplA=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="],
"chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"electrobun/@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
"png-to-ico/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="],
"electrobun/@types/bun/bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
}
}

View file

@ -2,14 +2,19 @@ import type { ElectrobunConfig } from "electrobun";
export default {
app: {
name: "wgpu",
identifier: "wgpu.electrobun.dev",
name: "Agent Orchestrator",
identifier: "dev.agor.orchestrator",
version: "0.0.1",
},
build: {
bun: {
entrypoint: "src/bun/index.ts",
},
copy: {
"dist/index.html": "views/mainview/index.html",
"dist/assets": "views/mainview/assets",
},
watchIgnore: ["dist/**"],
mac: {
bundleCEF: false,
bundleWGPU: true,

24
ui-electrobun/llms.txt Normal file
View file

@ -0,0 +1,24 @@
# Electrobun Project
This is an Electrobun desktop application.
IMPORTANT: Electrobun is NOT Electron. Do not use Electron APIs or patterns.
## Documentation
Full API reference: https://blackboard.sh/electrobun/llms.txt
Getting started: https://blackboard.sh/electrobun/docs/
## Quick Reference
Import patterns:
- Main process (Bun): `import { BrowserWindow } from "electrobun/bun"`
- Browser context: `import { Electroview } from "electrobun/view"`
Use `views://` URLs to load bundled assets (e.g., `url: "views://mainview/index.html"`).
Views must be configured in `electrobun.config.ts` to be built and copied into the bundle.
## About
Electrobun is built by Blackboard (https://blackboard.sh), an innovation lab building
tools and funding teams that define the next generation of technology.

View file

@ -1,18 +1,24 @@
{
"name": "electrobun-wgpu",
"name": "electrobun-svelte",
"version": "1.0.0",
"type": "module",
"description": "A minimal WebGPU shader demo",
"description": "Electrobun app with Svelte and Vite HMR",
"scripts": {
"start": "electrobun dev",
"start": "vite build && electrobun dev",
"dev": "electrobun dev --watch",
"build:canary": "electrobun build --env=canary"
"dev:hmr": "concurrently \"bun run hmr\" \"bun run start\"",
"hmr": "vite --port 9760",
"build:canary": "vite build && electrobun build --env=canary"
},
"dependencies": {
"electrobun": "latest"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.1",
"@types/bun": "latest",
"typescript": "^5.7.2"
"concurrently": "^9.1.0",
"svelte": "^5.14.1",
"typescript": "^5.7.2",
"vite": "^6.0.3"
}
}

View file

@ -1,526 +1,37 @@
import { GpuWindow, Screen, WGPU, WGPUBridge } from "electrobun/bun";
import { CString, ptr, toArrayBuffer } from "bun:ffi";
import { BrowserWindow, Updater } from "electrobun/bun";
const WGPUNative = WGPU.native;
const WGPU_STRLEN = 0xffffffffffffffffn;
const WGPU_DEPTH_SLICE_UNDEFINED = 0xffffffff;
const WGPUTextureUsage_RenderAttachment = 0x0000000000000010n;
const WGPUBufferUsage_Vertex = 0x0000000000000020n;
const WGPUBufferUsage_CopyDst = 0x0000000000000008n;
const WGPUVertexFormat_Float32 = 0x0000001c;
const WGPUVertexFormat_Float32x2 = 0x0000001d;
const WGPUVertexFormat_Float32x4 = 0x0000001f;
const WGPUVertexStepMode_Vertex = 0x00000001;
const WGPUPrimitiveTopology_TriangleList = 0x00000004;
const WGPUFrontFace_CCW = 0x00000001;
const WGPUCullMode_None = 0x00000001;
const WGPUPresentMode_Fifo = 0x00000001;
const DEV_SERVER_PORT = 9760; // Project convention: 9700+ range
const DEV_SERVER_URL = `http://localhost:${DEV_SERVER_PORT}`;
const KEEPALIVE: any[] = [];
function writePtr(view: DataView, offset: number, value: number | bigint | null) {
view.setBigUint64(offset, BigInt(value ?? 0), true);
// Check if Vite dev server is running for HMR
async function getMainViewUrl(): Promise<string> {
const channel = await Updater.localInfo.channel();
if (channel === "dev") {
try {
await fetch(DEV_SERVER_URL, { method: "HEAD" });
console.log(`HMR enabled: Using Vite dev server at ${DEV_SERVER_URL}`);
return DEV_SERVER_URL;
} catch {
console.log(
"Vite dev server not running. Run 'bun run dev:hmr' for HMR support.",
);
}
}
return "views://mainview/index.html";
}
function writeU32(view: DataView, offset: number, value: number) {
view.setUint32(offset, value >>> 0, true);
}
// Create the main application window
const url = await getMainViewUrl();
function writeU64(view: DataView, offset: number, value: bigint) {
view.setBigUint64(offset, value, true);
}
function makeSurfaceConfiguration(
devicePtr: number,
width: number,
height: number,
format: number,
) {
const buffer = new ArrayBuffer(64);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writePtr(view, 8, devicePtr);
writeU32(view, 16, format);
writeU32(view, 20, 0);
writeU64(view, 24, WGPUTextureUsage_RenderAttachment);
writeU32(view, 32, width);
writeU32(view, 36, height);
writeU64(view, 40, 0n);
writePtr(view, 48, 0);
writeU32(view, 56, 1);
writeU32(view, 60, WGPUPresentMode_Fifo);
return { buffer, ptr: ptr(buffer) };
}
function makeShaderSourceWGSL(codePtr: number) {
const buffer = new ArrayBuffer(32);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writeU32(view, 8, 0x00000002);
writeU32(view, 12, 0);
writePtr(view, 16, codePtr);
writeU64(view, 24, WGPU_STRLEN);
return { buffer, ptr: ptr(buffer) };
}
function makeShaderModuleDescriptor(nextInChainPtr: number) {
const buffer = new ArrayBuffer(24);
const view = new DataView(buffer);
writePtr(view, 0, nextInChainPtr);
writePtr(view, 8, 0);
writeU64(view, 16, 0n);
return { buffer, ptr: ptr(buffer) };
}
function makeVertexAttribute(offset: number, shaderLocation: number, format: number) {
const buffer = new ArrayBuffer(32);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writeU32(view, 8, format);
writeU32(view, 12, 0);
writeU64(view, 16, BigInt(offset));
writeU32(view, 24, shaderLocation);
writeU32(view, 28, 0);
return { buffer, ptr: ptr(buffer) };
}
function makeVertexBufferLayout(
attributePtr: number,
attributeCount: number,
stride: number,
) {
const buffer = new ArrayBuffer(40);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writeU32(view, 8, WGPUVertexStepMode_Vertex);
writeU32(view, 12, 0);
writeU64(view, 16, BigInt(stride));
writeU64(view, 24, BigInt(attributeCount));
writePtr(view, 32, attributePtr);
return { buffer, ptr: ptr(buffer) };
}
function makeColorTargetState(format: number) {
const buffer = new ArrayBuffer(32);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writeU32(view, 8, format);
writeU32(view, 12, 0);
writePtr(view, 16, 0);
writeU64(view, 24, 0x0fn);
return { buffer, ptr: ptr(buffer) };
}
function makeVertexState(
modulePtr: number,
entryPointPtr: number,
bufferLayoutPtr: number,
) {
const buffer = new ArrayBuffer(64);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writePtr(view, 8, modulePtr);
writePtr(view, 16, entryPointPtr);
writeU64(view, 24, WGPU_STRLEN);
writeU64(view, 32, 0n);
writePtr(view, 40, 0);
writeU64(view, 48, 1n);
writePtr(view, 56, bufferLayoutPtr);
return { buffer, ptr: ptr(buffer) };
}
function makeFragmentState(
modulePtr: number,
entryPointPtr: number,
targetPtr: number,
) {
const buffer = new ArrayBuffer(64);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writePtr(view, 8, modulePtr);
writePtr(view, 16, entryPointPtr);
writeU64(view, 24, WGPU_STRLEN);
writeU64(view, 32, 0n);
writePtr(view, 40, 0);
writeU64(view, 48, 1n);
writePtr(view, 56, targetPtr);
return { buffer, ptr: ptr(buffer) };
}
function makePrimitiveState() {
const buffer = new ArrayBuffer(32);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writeU32(view, 8, WGPUPrimitiveTopology_TriangleList);
writeU32(view, 12, 0);
writeU32(view, 16, WGPUFrontFace_CCW);
writeU32(view, 20, WGPUCullMode_None);
writeU32(view, 24, 0);
writeU32(view, 28, 0);
return { buffer, ptr: ptr(buffer) };
}
function makeMultisampleState() {
const buffer = new ArrayBuffer(24);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writeU32(view, 8, 1);
writeU32(view, 12, 0xffffffff);
writeU32(view, 16, 0);
writeU32(view, 20, 0);
return { buffer, ptr: ptr(buffer) };
}
function makeRenderPipelineDescriptor(
vertexStatePtr: number,
primitiveStatePtr: number,
multisampleStatePtr: number,
fragmentStatePtr: number,
) {
const buffer = new ArrayBuffer(168);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writePtr(view, 8, 0);
writeU64(view, 16, 0n);
writePtr(view, 24, 0);
new Uint8Array(buffer, 32, 64).set(new Uint8Array(vertexStatePtr.buffer));
new Uint8Array(buffer, 96, 32).set(new Uint8Array(primitiveStatePtr.buffer));
writePtr(view, 128, 0);
new Uint8Array(buffer, 136, 24).set(new Uint8Array(multisampleStatePtr.buffer));
writePtr(view, 160, fragmentStatePtr.ptr as unknown as number);
return { buffer, ptr: ptr(buffer) };
}
function makeBufferDescriptor(size: number) {
const buffer = new ArrayBuffer(48);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writePtr(view, 8, 0);
writeU64(view, 16, 0n);
writeU64(view, 24, WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst);
writeU64(view, 32, BigInt(size));
writeU32(view, 40, 0);
writeU32(view, 44, 0);
return { buffer, ptr: ptr(buffer) };
}
function makeCommandEncoderDescriptor() {
const buffer = new ArrayBuffer(24);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writePtr(view, 8, 0);
writeU64(view, 16, 0n);
return { buffer, ptr: ptr(buffer) };
}
function makeSurfaceTexture() {
const buffer = new ArrayBuffer(24);
return { buffer, view: new DataView(buffer), ptr: ptr(buffer) };
}
function makeRenderPassColorAttachment(viewPtr: number, clear: { r: number; g: number; b: number; a: number }) {
const buffer = new ArrayBuffer(72);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writePtr(view, 8, viewPtr);
writeU32(view, 16, WGPU_DEPTH_SLICE_UNDEFINED);
writeU32(view, 20, 0);
writePtr(view, 24, 0);
writeU32(view, 32, 2);
writeU32(view, 36, 1);
view.setFloat64(40, clear.r, true);
view.setFloat64(48, clear.g, true);
view.setFloat64(56, clear.b, true);
view.setFloat64(64, clear.a, true);
return { buffer, ptr: ptr(buffer) };
}
function makeRenderPassDescriptor(colorAttachmentPtr: number) {
const buffer = new ArrayBuffer(64);
const view = new DataView(buffer);
writePtr(view, 0, 0);
writePtr(view, 8, 0);
writeU64(view, 16, 0n);
writeU64(view, 24, 1n);
writePtr(view, 32, colorAttachmentPtr);
writePtr(view, 40, 0);
writePtr(view, 48, 0);
writePtr(view, 56, 0);
return { buffer, ptr: ptr(buffer) };
}
function makeCommandBufferArray(cmdPtr: number) {
const buffer = new BigUint64Array([BigInt(cmdPtr)]);
return { buffer, ptr: ptr(buffer) };
}
const size = 640;
const display = Screen.getPrimaryDisplay();
const workArea = display.workArea;
const x = workArea.x + Math.floor((workArea.width - size) / 2);
const y = workArea.y + Math.floor((workArea.height - size) / 2);
const win = new GpuWindow({
title: "WGPU Shader",
frame: { width: size, height: size, x, y },
titleBarStyle: "default",
transparent: false,
const mainWindow = new BrowserWindow({
title: "Agent Orchestrator — Electrobun",
url,
frame: {
width: 1400,
height: 900,
x: 100,
y: 100,
},
});
if (!WGPUNative.available) {
throw new Error("WGPU not available for wgpu");
}
const instance = WGPUNative.symbols.wgpuCreateInstance(0);
const surface = WGPUBridge.createSurfaceForView(
instance as number,
win.wgpuView.ptr as number,
);
const adapterDevice = new BigUint64Array(2);
WGPUBridge.createAdapterDeviceMainThread(
instance as number,
surface as number,
ptr(adapterDevice),
);
const adapter = Number(adapterDevice[0]);
const device = Number(adapterDevice[1]);
if (!adapter || !device) {
throw new Error("Failed to get WGPU adapter/device");
}
const queue = WGPUNative.symbols.wgpuDeviceGetQueue(device);
// Query the surface capabilities to get a supported texture format
const capsBuffer = new ArrayBuffer(64);
const capsView = new DataView(capsBuffer);
WGPUNative.symbols.wgpuSurfaceGetCapabilities(
surface,
adapter,
ptr(capsBuffer),
);
const formatCount = Number(capsView.getBigUint64(16, true));
const formatPtr = Number(capsView.getBigUint64(24, true));
let surfaceFormat = 0x00000017; // BGRA8Unorm fallback
if (formatCount && formatPtr) {
const formats = new Uint32Array(toArrayBuffer(formatPtr, 0, formatCount * 4));
if (formats.length) surfaceFormat = formats[0]!;
}
const surfaceConfig = makeSurfaceConfiguration(
device,
size,
size,
surfaceFormat,
);
WGPUBridge.surfaceConfigure(surface as number, surfaceConfig.ptr as number);
const shaderText = `
struct VSOut {
@builtin(position) position : vec4<f32>,
@location(0) uv : vec2<f32>,
@location(1) time : f32,
@location(2) resolution : vec2<f32>,
@location(3) mouse : vec4<f32>,
};
@vertex
fn vs_main(
@location(0) position: vec2<f32>,
@location(1) time: f32,
@location(2) resolution: vec2<f32>,
@location(3) mouse: vec4<f32>
) -> VSOut {
var out: VSOut;
out.position = vec4<f32>(position, 0.0, 1.0);
out.uv = position;
out.time = time;
out.resolution = resolution;
out.mouse = mouse;
return out;
}
@fragment
fn fs_main(
@location(0) uv: vec2<f32>,
@location(1) time: f32,
@location(2) resolution: vec2<f32>,
@location(3) mouse: vec4<f32>
) -> @location(0) vec4<f32> {
let fragCoord = (uv * 0.5 + vec2<f32>(0.5)) * resolution;
let m = (mouse.xy / max(resolution, vec2<f32>(1.0))) * 2.0 - vec2<f32>(1.0);
let loopMax: i32 = select(32, 64, mouse.z > 0.5);
var o = vec4<f32>(0.0);
var i: f32 = 0.0;
var d: f32 = 0.0;
var c: f32 = 0.0;
var s: f32 = 0.0;
var q = vec3<f32>(0.0);
var p = vec3<f32>(0.0);
let r = vec3<f32>(resolution, 0.0);
var dir = normalize(vec3<f32>((fragCoord + fragCoord - r.xy) / r.y, 1.0));
dir.x = dir.x + m.x * 0.35;
dir.y = dir.y + -m.y * 0.35;
for (var iter: i32 = 0; iter < loopMax; iter = iter + 1) {
i = f32(iter + 1);
p = dir * d;
p.z = p.z + time * 4.0;
q = p;
s = 0.0;
c = 20.0;
loop {
if (c <= 0.2) { break; }
let m = mat2x2<f32>(
vec2<f32>(cos(c / 30.0 + 0.0), cos(c / 30.0 + 33.0)),
vec2<f32>(cos(c / 30.0 + 11.0), cos(c / 30.0 + 0.0))
);
let xz = m * vec2<f32>(p.x, p.z);
p.x = xz.x;
p.z = xz.y;
p = abs(fract(p / c) * c - vec3<f32>(c * 0.5)) - vec3<f32>(c * 0.2);
s = max(
9.0 + 3.0 * sin(q.z * 0.05) - abs(q.x),
max(s, min(p.x, min(p.y, p.z)))
);
p = q;
c = c * 0.5;
}
let sinp = sin(p * 12.0);
let dotv = dot(sinp, vec3<f32>(0.1, 0.1, 0.1));
s = min(s, p.y + 8.0 + dotv);
d = d + s;
let add = i / max(s, 0.001);
o = o + vec4<f32>(add, add, add, add);
}
let denom = max(d, 0.000001);
o = tanh(o / denom / 30000.0);
return vec4<f32>(o.xyz, 1.0);
}
`;
const shaderBytes = new TextEncoder().encode(shaderText + "\0");
const shaderBuf = new Uint8Array(shaderBytes);
KEEPALIVE.push(shaderBuf);
const shaderPtr = ptr(shaderBuf);
const shaderSource = makeShaderSourceWGSL(shaderPtr);
const shaderDesc = makeShaderModuleDescriptor(shaderSource.ptr as number);
const shaderModule = WGPUNative.symbols.wgpuDeviceCreateShaderModule(device, shaderDesc.ptr as number);
const entryPoint = new CString("vs_main");
const fragEntryPoint = new CString("fs_main");
KEEPALIVE.push(entryPoint, fragEntryPoint);
const posAttr = makeVertexAttribute(0, 0, WGPUVertexFormat_Float32x2);
const timeAttr = makeVertexAttribute(8, 1, WGPUVertexFormat_Float32);
const resAttr = makeVertexAttribute(12, 2, WGPUVertexFormat_Float32x2);
const mouseAttr = makeVertexAttribute(20, 3, WGPUVertexFormat_Float32x4);
const attrBuf = new ArrayBuffer(32 * 4);
new Uint8Array(attrBuf, 0, 32).set(new Uint8Array(posAttr.buffer));
new Uint8Array(attrBuf, 32, 32).set(new Uint8Array(timeAttr.buffer));
new Uint8Array(attrBuf, 64, 32).set(new Uint8Array(resAttr.buffer));
new Uint8Array(attrBuf, 96, 32).set(new Uint8Array(mouseAttr.buffer));
const attrPtr = ptr(attrBuf);
KEEPALIVE.push(attrBuf);
const vertexLayout = makeVertexBufferLayout(attrPtr as number, 4, 36);
const vertexState = makeVertexState(shaderModule, entryPoint.ptr, vertexLayout.ptr as number);
const colorTarget = makeColorTargetState(surfaceFormat);
const fragmentState = makeFragmentState(shaderModule, fragEntryPoint.ptr, colorTarget.ptr as number);
const primitiveState = makePrimitiveState();
const multisampleState = makeMultisampleState();
const pipelineDesc = makeRenderPipelineDescriptor(
vertexState,
primitiveState,
multisampleState,
fragmentState,
);
const pipeline = WGPUNative.symbols.wgpuDeviceCreateRenderPipeline(device, pipelineDesc.ptr as number);
const vertexCount = 3;
const bufferDesc = makeBufferDescriptor(vertexCount * 9 * 4);
const vertexBuffer = WGPUNative.symbols.wgpuDeviceCreateBuffer(device, bufferDesc.ptr as number);
const encoderDesc = makeCommandEncoderDescriptor();
let lastLeftDown = false;
let qualityBoost = false;
let clickX = 0;
let clickY = 0;
function renderFrame() {
const sizeNow = win.getSize();
const t = performance.now() * 0.001;
const positions = [-1, -1, 3, -1, -1, 3];
const frame = win.getFrame();
const cursor = Screen.getCursorScreenPoint();
const rawX = cursor.x - frame.x;
const rawY = cursor.y - frame.y;
const mx = Math.max(0, Math.min(frame.width, rawX));
const my = Math.max(0, Math.min(frame.height, rawY));
const buttons = Screen.getMouseButtons();
const leftDown = (buttons & 1n) === 1n;
if (leftDown && !lastLeftDown) {
qualityBoost = !qualityBoost;
clickX = mx;
clickY = my;
}
lastLeftDown = leftDown;
const packed = new Float32Array(vertexCount * 9);
for (let i = 0; i < vertexCount; i += 1) {
const idx = i * 9;
packed[idx] = positions[i * 2]!;
packed[idx + 1] = positions[i * 2 + 1]!;
packed[idx + 2] = t;
packed[idx + 3] = sizeNow.width;
packed[idx + 4] = sizeNow.height;
packed[idx + 5] = mx;
packed[idx + 6] = my;
packed[idx + 7] = qualityBoost ? clickX : 0;
packed[idx + 8] = qualityBoost ? clickY : 0;
}
WGPUNative.symbols.wgpuQueueWriteBuffer(
queue,
vertexBuffer,
0,
ptr(packed),
packed.byteLength,
);
WGPUNative.symbols.wgpuInstanceProcessEvents(instance);
const surfaceTexture = makeSurfaceTexture();
WGPUBridge.surfaceGetCurrentTexture(surface as number, surfaceTexture.ptr as number);
const status = surfaceTexture.view.getUint32(16, true);
if (status !== 1 && status !== 2) return;
const texPtr = Number(surfaceTexture.view.getBigUint64(8, true));
if (!texPtr) return;
const textureView = WGPUNative.symbols.wgpuTextureCreateView(texPtr, 0);
if (!textureView) return;
const colorAttachment = makeRenderPassColorAttachment(textureView, {
r: 0.05,
g: 0.05,
b: 0.1,
a: 1.0,
});
const renderPassDesc = makeRenderPassDescriptor(colorAttachment.ptr as number);
const encoder = WGPUNative.symbols.wgpuDeviceCreateCommandEncoder(device, encoderDesc.ptr as number);
const pass = WGPUNative.symbols.wgpuCommandEncoderBeginRenderPass(encoder, renderPassDesc.ptr as number);
WGPUNative.symbols.wgpuRenderPassEncoderSetPipeline(pass, pipeline);
WGPUNative.symbols.wgpuRenderPassEncoderSetVertexBuffer(pass, 0, vertexBuffer, 0, packed.byteLength);
WGPUNative.symbols.wgpuRenderPassEncoderDraw(pass, vertexCount, 1, 0, 0);
WGPUNative.symbols.wgpuRenderPassEncoderEnd(pass);
const commandBuffer = WGPUNative.symbols.wgpuCommandEncoderFinish(encoder, 0);
const commandArray = makeCommandBufferArray(commandBuffer);
WGPUNative.symbols.wgpuQueueSubmit(queue, 1, commandArray.ptr as number);
WGPUBridge.surfacePresent(surface as number);
WGPUNative.symbols.wgpuTextureViewRelease(textureView);
WGPUNative.symbols.wgpuTextureRelease(texPtr);
WGPUNative.symbols.wgpuCommandBufferRelease(commandBuffer);
WGPUNative.symbols.wgpuCommandEncoderRelease(encoder);
}
setInterval(renderFrame, 16);
console.log("Agent Orchestrator (Electrobun) started!");

View file

@ -0,0 +1,239 @@
<script lang="ts">
// ── Types ────────────────────────────────────────────────────
type AgentStatus = 'running' | 'idle' | 'stalled';
type MsgRole = 'user' | 'assistant' | 'tool-call' | 'tool-result';
type TabId = 'model' | 'docs' | 'files';
interface AgentMessage {
id: number;
role: MsgRole;
content: string;
}
interface Project {
id: string;
name: string;
cwd: string;
accent: string;
status: AgentStatus;
costUsd: number;
tokens: number;
messages: AgentMessage[];
}
// ── Demo data ────────────────────────────────────────────────
const PROJECTS: Project[] = [
{
id: 'p1',
name: 'agent-orchestrator',
cwd: '~/code/ai/agent-orchestrator',
accent: 'var(--ctp-mauve)',
status: 'running',
costUsd: 0.034,
tokens: 18420,
messages: [
{ id: 1, role: 'user', content: 'Add a wake scheduler for Manager agents that wakes them when review queue depth > 3.' },
{ id: 2, role: 'assistant', content: 'Reading existing wake-scheduler.svelte.ts to understand the 3-strategy pattern...' },
{ id: 3, role: 'tool-call', content: 'Read("src/lib/stores/wake-scheduler.svelte.ts")' },
{ id: 4, role: 'tool-result', content: '// 312 lines\nexport type WakeStrategy = "persistent" | "on-demand" | "smart";\n...' },
{ id: 5, role: 'assistant', content: 'Found the WakeSignal enum. I\'ll add ReviewBacklog(0.6) and hook into bttask polling. Writing wake-scorer.ts update now.' },
{ id: 6, role: 'tool-call', content: 'Edit("src/lib/utils/wake-scorer.ts", old="// signals", new="// signals\n ReviewBacklog = 0.6,")' },
{ id: 7, role: 'assistant', content: 'Done. ReviewBacklog signal added at priority 0.6 (between TaskQueuePressure 0.7 and PeriodicFloor 0.1). Run `npm test` to verify 24 scorer tests still pass.' },
],
},
{
id: 'p2',
name: 'quanta-discord-bot',
cwd: '~/code/quanta/discord-bot',
accent: 'var(--ctp-sapphire)',
status: 'idle',
costUsd: 0.011,
tokens: 6830,
messages: [
{ id: 1, role: 'user', content: 'Why is the QRAG MCP server returning 504s on large semantic search queries?' },
{ id: 2, role: 'assistant', content: 'Checking the MCP HTTP handler timeout config and Qdrant query path...' },
{ id: 3, role: 'tool-call', content: 'Read("src/mcp/server.ts", offset=120, limit=40)' },
{ id: 4, role: 'tool-result', content: 'const QUERY_TIMEOUT_MS = 5000; // default\n...' },
{ id: 5, role: 'assistant', content: 'Found it. The MCP handler has a hardcoded 5s timeout but Qdrant HNSW search on 500k vectors takes 69s at ef=256. Two options:\n1. Raise timeout to 15s (quick fix)\n2. Lower ef to 128 for faster approximate search (~3s, slight recall drop)\n\nRecommend option 2 for latency-sensitive bot responses.' },
{ id: 6, role: 'user', content: 'Go with option 2.' },
{ id: 7, role: 'assistant', content: 'Updated `ef_searching: 128` in qdrant-config.ts and raised timeout to 8s as safety margin. Restarted service on port 9320.' },
],
},
];
// ── Reactive state ───────────────────────────────────────────
let activeTab = $state<Record<string, TabId>>({ p1: 'model', p2: 'model' });
let settingsOpen = $state(false);
// ── Derived status bar aggregates ───────────────────────────
let runningCount = $derived(PROJECTS.filter(p => p.status === 'running').length);
let idleCount = $derived(PROJECTS.filter(p => p.status === 'idle').length);
let totalCost = $derived(PROJECTS.reduce((s, p) => s + p.costUsd, 0));
let totalTokens = $derived(PROJECTS.reduce((s, p) => s + p.tokens, 0));
function setTab(projectId: string, tab: TabId) {
activeTab = { ...activeTab, [projectId]: tab };
}
function fmtTokens(n: number): string {
return n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
}
function fmtCost(n: number): string {
return `$${n.toFixed(3)}`;
}
</script>
<div class="app-shell">
<!-- ── Sidebar icon rail ──────────────────────────────────── -->
<aside class="sidebar" role="navigation" aria-label="Primary navigation">
<div class="sidebar-spacer"></div>
<button
class="sidebar-icon"
class:active={settingsOpen}
onclick={() => settingsOpen = !settingsOpen}
aria-label="Settings"
title="Settings"
>
<!-- gear icon -->
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
</button>
</aside>
<!-- ── Main workspace ───────────────────────────────────────── -->
<main class="workspace">
<div class="project-grid">
{#each PROJECTS as project (project.id)}
<article
class="project-card"
style="--accent: {project.accent}"
aria-label="Project: {project.name}"
>
<!-- Project header -->
<header class="project-header">
<!-- Status dot — wgpu surface placeholder -->
<div class="status-dot-wrap" aria-label="Status: {project.status}">
<!--
Future: replace inner div with <electrobun-wgpu id="wgpu-surface-{project.id}">
for GPU-rendered animation. CSS pulse is the WebView fallback.
-->
<div class="status-dot {project.status}" role="img" aria-label="{project.status}"></div>
</div>
<span class="project-name">{project.name}</span>
<span class="project-cwd" title={project.cwd}>{project.cwd}</span>
</header>
<!-- Tab bar -->
<div class="tab-bar" role="tablist" aria-label="{project.name} tabs">
{#each (['model', 'docs', 'files'] as TabId[]) as tab}
<button
class="tab-btn"
class:active={activeTab[project.id] === tab}
role="tab"
aria-selected={activeTab[project.id] === tab}
aria-controls="tabpanel-{project.id}-{tab}"
onclick={() => setTab(project.id, tab)}
>
{tab.charAt(0).toUpperCase() + tab.slice(1)}
</button>
{/each}
</div>
<!-- Tab content -->
<div class="tab-content">
<!-- Model tab: agent messages -->
<div
id="tabpanel-{project.id}-model"
class="tab-pane"
class:active={activeTab[project.id] === 'model'}
role="tabpanel"
aria-label="Model"
>
{#each project.messages as msg (msg.id)}
<div class="msg">
<span class="msg-role {msg.role.split('-')[0]}">{msg.role}</span>
<div
class="msg-body"
class:tool-call={msg.role === 'tool-call'}
class:tool-result={msg.role === 'tool-result'}
>{msg.content}</div>
</div>
{/each}
</div>
<!-- Docs tab placeholder -->
<div
id="tabpanel-{project.id}-docs"
class="tab-pane"
class:active={activeTab[project.id] === 'docs'}
role="tabpanel"
aria-label="Docs"
>
<div class="placeholder-pane">No markdown files open</div>
</div>
<!-- Files tab placeholder -->
<div
id="tabpanel-{project.id}-files"
class="tab-pane"
class:active={activeTab[project.id] === 'files'}
role="tabpanel"
aria-label="Files"
>
<div class="placeholder-pane">File browser — coming soon</div>
</div>
</div>
</article>
{/each}
</div>
</main>
</div>
<!-- ── Status bar ─────────────────────────────────────────────── -->
<footer class="status-bar" role="status" aria-live="polite" aria-label="System status">
{#if runningCount > 0}
<span class="status-segment">
<span class="status-dot-sm green" aria-hidden="true"></span>
<span class="status-value">{runningCount}</span>
<span>running</span>
</span>
{/if}
{#if idleCount > 0}
<span class="status-segment">
<span class="status-dot-sm gray" aria-hidden="true"></span>
<span class="status-value">{idleCount}</span>
<span>idle</span>
</span>
{/if}
<span class="status-bar-spacer"></span>
<span class="status-segment" title="Total tokens used">
<span>tokens</span>
<span class="status-value">{fmtTokens(totalTokens)}</span>
</span>
<span class="status-segment" title="Total session cost">
<span>cost</span>
<span class="status-value">{fmtCost(totalCost)}</span>
</span>
</footer>
<style>
/* Component-scoped overrides only — base styles live in app.css */
:global(body) {
overflow: hidden;
}
:global(#app) {
display: flex;
flex-direction: column;
height: 100vh;
}
.app-shell {
flex: 1;
min-height: 0;
}
</style>

View file

@ -0,0 +1,400 @@
/* Catppuccin Mocha palette — same --ctp-* vars as Tauri app */
:root {
--ctp-rosewater: #f5e0dc;
--ctp-flamingo: #f2cdcd;
--ctp-pink: #f5c2e7;
--ctp-mauve: #cba6f7;
--ctp-red: #f38ba8;
--ctp-maroon: #eba0ac;
--ctp-peach: #fab387;
--ctp-yellow: #f9e2af;
--ctp-green: #a6e3a1;
--ctp-teal: #94e2d5;
--ctp-sky: #89dceb;
--ctp-sapphire: #74c7ec;
--ctp-blue: #89b4fa;
--ctp-lavender: #b4befe;
--ctp-text: #cdd6f4;
--ctp-subtext1: #bac2de;
--ctp-subtext0: #a6adc8;
--ctp-overlay2: #9399b2;
--ctp-overlay1: #7f849c;
--ctp-overlay0: #6c7086;
--ctp-surface2: #585b70;
--ctp-surface1: #45475a;
--ctp-surface0: #313244;
--ctp-base: #1e1e2e;
--ctp-mantle: #181825;
--ctp-crust: #11111b;
/* Typography */
--ui-font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
--ui-font-size: 0.875rem;
--term-font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace;
--term-font-size: 0.8125rem;
/* Layout */
--sidebar-width: 2.75rem;
--status-bar-height: 1.75rem;
--tab-bar-height: 2rem;
--header-height: 2.5rem;
}
*, *::before, *::after {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: var(--ctp-base);
color: var(--ctp-text);
font-family: var(--ui-font-family);
font-size: var(--ui-font-size);
-webkit-font-smoothing: antialiased;
}
#app {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* ── App shell ─────────────────────────────────────────────── */
.app-shell {
display: flex;
flex: 1;
min-height: 0;
overflow: hidden;
}
/* ── Sidebar / icon rail ───────────────────────────────────── */
.sidebar {
width: var(--sidebar-width);
flex-shrink: 0;
background: var(--ctp-mantle);
border-right: 1px solid var(--ctp-surface0);
display: flex;
flex-direction: column;
align-items: center;
padding: 0.5rem 0;
gap: 0.25rem;
}
.sidebar-icon {
width: 2rem;
height: 2rem;
border-radius: 0.375rem;
border: none;
background: transparent;
color: var(--ctp-overlay1);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s, color 0.15s;
padding: 0;
}
.sidebar-icon:hover {
background: var(--ctp-surface0);
color: var(--ctp-text);
}
.sidebar-icon.active {
background: var(--ctp-surface1);
color: var(--ctp-mauve);
}
.sidebar-icon svg {
width: 1rem;
height: 1rem;
}
.sidebar-spacer {
flex: 1;
}
/* ── Project grid (main workspace) ────────────────────────── */
.workspace {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.project-grid {
flex: 1;
min-height: 0;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
padding: 0.5rem;
background: var(--ctp-crust);
}
/* ── Project card ──────────────────────────────────────────── */
.project-card {
background: var(--ctp-base);
border: 1px solid var(--ctp-surface0);
border-radius: 0.5rem;
display: flex;
flex-direction: column;
overflow: hidden;
min-height: 0;
}
/* Accent stripe on left edge */
.project-card::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--accent, var(--ctp-mauve));
border-radius: 0.5rem 0 0 0.5rem;
}
.project-card {
position: relative;
}
/* ── Project header ────────────────────────────────────────── */
.project-header {
height: var(--header-height);
background: var(--ctp-mantle);
border-bottom: 1px solid var(--ctp-surface0);
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0 0.625rem 0 0.875rem;
flex-shrink: 0;
}
.status-dot-wrap {
flex-shrink: 0;
width: 0.625rem;
height: 0.625rem;
position: relative;
}
/* wgpu placeholder — same dimensions as the dot, GPU surface goes here */
#wgpu-surface,
.wgpu-surface {
width: 100%;
height: 100%;
border-radius: 50%;
background: var(--dot-color, var(--ctp-overlay0));
}
/* CSS fallback pulsing dot (compositor-driven, ~0% CPU) */
.status-dot {
width: 100%;
height: 100%;
border-radius: 50%;
background: var(--dot-color, var(--ctp-overlay0));
}
.status-dot.running {
--dot-color: var(--ctp-green);
animation: pulse-dot 2s ease-in-out infinite;
}
.status-dot.idle {
--dot-color: var(--ctp-overlay1);
}
.status-dot.stalled {
--dot-color: var(--ctp-peach);
animation: pulse-dot 1.4s ease-in-out infinite;
}
@keyframes pulse-dot {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.55; transform: scale(0.85); }
}
.project-name {
font-weight: 600;
color: var(--ctp-text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
min-width: 0;
}
.project-cwd {
font-size: 0.75rem;
color: var(--ctp-subtext0);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
direction: rtl;
max-width: 10rem;
flex-shrink: 0;
}
/* ── Tab bar ───────────────────────────────────────────────── */
.tab-bar {
height: var(--tab-bar-height);
background: var(--ctp-mantle);
border-bottom: 1px solid var(--ctp-surface0);
display: flex;
align-items: stretch;
flex-shrink: 0;
padding: 0 0.25rem;
gap: 0.125rem;
}
.tab-btn {
padding: 0 0.75rem;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
color: var(--ctp-subtext0);
font-family: var(--ui-font-family);
font-size: 0.8125rem;
cursor: pointer;
white-space: nowrap;
transition: color 0.12s, border-color 0.12s;
margin-bottom: -1px;
}
.tab-btn:hover {
color: var(--ctp-text);
}
.tab-btn.active {
color: var(--ctp-text);
border-bottom-color: var(--accent, var(--ctp-mauve));
}
/* ── Tab content area ──────────────────────────────────────── */
.tab-content {
flex: 1;
min-height: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
.tab-pane {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 0.5rem 0.625rem;
display: none;
}
.tab-pane.active {
display: flex;
flex-direction: column;
gap: 0.375rem;
}
/* scrollbar */
.tab-pane::-webkit-scrollbar { width: 0.375rem; }
.tab-pane::-webkit-scrollbar-track { background: transparent; }
.tab-pane::-webkit-scrollbar-thumb { background: var(--ctp-surface1); border-radius: 0.25rem; }
/* ── Agent messages ────────────────────────────────────────── */
.msg {
display: flex;
flex-direction: column;
gap: 0.125rem;
}
.msg-role {
font-size: 0.6875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--ctp-overlay1);
}
.msg-role.user { color: var(--ctp-blue); }
.msg-role.assistant { color: var(--ctp-mauve); }
.msg-role.tool { color: var(--ctp-peach); }
.msg-body {
background: var(--ctp-surface0);
border-radius: 0.3125rem;
padding: 0.375rem 0.5rem;
font-size: 0.8125rem;
line-height: 1.5;
color: var(--ctp-text);
white-space: pre-wrap;
word-break: break-word;
}
.msg-body.tool-call {
background: color-mix(in srgb, var(--ctp-peach) 8%, var(--ctp-surface0));
border-left: 2px solid var(--ctp-peach);
font-family: var(--term-font-family);
font-size: 0.75rem;
}
.msg-body.tool-result {
background: color-mix(in srgb, var(--ctp-teal) 6%, var(--ctp-surface0));
border-left: 2px solid var(--ctp-teal);
font-family: var(--term-font-family);
font-size: 0.75rem;
color: var(--ctp-subtext1);
}
/* ── Docs / Files placeholder ──────────────────────────────── */
.placeholder-pane {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: var(--ctp-overlay0);
font-size: 0.8125rem;
font-style: italic;
}
/* ── Status bar ────────────────────────────────────────────── */
.status-bar {
height: var(--status-bar-height);
background: var(--ctp-crust);
border-top: 1px solid var(--ctp-surface0);
display: flex;
align-items: center;
gap: 1rem;
padding: 0 0.75rem;
flex-shrink: 0;
font-size: 0.75rem;
color: var(--ctp-subtext0);
}
.status-segment {
display: flex;
align-items: center;
gap: 0.3rem;
}
.status-dot-sm {
width: 0.4375rem;
height: 0.4375rem;
border-radius: 50%;
flex-shrink: 0;
}
.status-dot-sm.green { background: var(--ctp-green); }
.status-dot-sm.gray { background: var(--ctp-overlay0); }
.status-dot-sm.orange { background: var(--ctp-peach); }
.status-bar-spacer { flex: 1; }
.status-value {
color: var(--ctp-text);
font-weight: 500;
}

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Svelte App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/main.ts"></script>
</body>
</html>

View file

@ -0,0 +1,9 @@
import "./app.css";
import App from "./App.svelte";
import { mount } from "svelte";
const app = mount(App, {
target: document.getElementById("app")!,
});
export default app;

View file

@ -0,0 +1,5 @@
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
export default {
preprocess: vitePreprocess(),
};

View file

@ -5,6 +5,7 @@
"module": "ESNext",
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,

View file

@ -0,0 +1,15 @@
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
export default defineConfig({
plugins: [svelte()],
root: "src/mainview",
build: {
outDir: "../../dist",
emptyOutDir: true,
},
server: {
port: 9760,
strictPort: true,
},
});