mirror of
https://github.com/xmanrui/OpenClaw-bot-review.git
synced 2026-07-31 04:06:49 +00:00
- Add layout editor with floor/wall paint, erase, furniture place/move/rotate/delete tools - Add editor state management (undo/redo, selection, drag, ghost preview) - Add EditorToolbar and EditActionBar UI components - Add layout persistence API (GET/POST to ~/.openclaw/pixel-office/layout.json) - Add notification sound system (Web Audio API ascending chime on agent completion) - Add sub-agent visualization via session JSONL parsing - Fix agent activity API to read from agents.list config and correct session paths - Add agent name labels above character sprites in canvas renderer - Add i18n translations for editor features (zh/en)
73 lines
2.0 KiB
TypeScript
73 lines
2.0 KiB
TypeScript
import type { SpriteData } from '../types'
|
|
|
|
const zoomCaches = new Map<number, WeakMap<SpriteData, HTMLCanvasElement>>()
|
|
|
|
const outlineCache = new WeakMap<SpriteData, SpriteData>()
|
|
|
|
/** Generate a 1px white outline SpriteData (2px larger in each dimension) */
|
|
export function getOutlineSprite(sprite: SpriteData): SpriteData {
|
|
const cached = outlineCache.get(sprite)
|
|
if (cached) return cached
|
|
|
|
const rows = sprite.length
|
|
const cols = sprite[0].length
|
|
const outline: string[][] = []
|
|
for (let r = 0; r < rows + 2; r++) {
|
|
outline.push(new Array<string>(cols + 2).fill(''))
|
|
}
|
|
|
|
for (let r = 0; r < rows; r++) {
|
|
for (let c = 0; c < cols; c++) {
|
|
if (sprite[r][c] === '') continue
|
|
const er = r + 1
|
|
const ec = c + 1
|
|
if (outline[er - 1][ec] === '') outline[er - 1][ec] = '#FFFFFF'
|
|
if (outline[er + 1][ec] === '') outline[er + 1][ec] = '#FFFFFF'
|
|
if (outline[er][ec - 1] === '') outline[er][ec - 1] = '#FFFFFF'
|
|
if (outline[er][ec + 1] === '') outline[er][ec + 1] = '#FFFFFF'
|
|
}
|
|
}
|
|
|
|
for (let r = 0; r < rows; r++) {
|
|
for (let c = 0; c < cols; c++) {
|
|
if (sprite[r][c] !== '') {
|
|
outline[r + 1][c + 1] = ''
|
|
}
|
|
}
|
|
}
|
|
|
|
outlineCache.set(sprite, outline)
|
|
return outline
|
|
}
|
|
|
|
export function getCachedSprite(sprite: SpriteData, zoom: number): HTMLCanvasElement {
|
|
let cache = zoomCaches.get(zoom)
|
|
if (!cache) {
|
|
cache = new WeakMap()
|
|
zoomCaches.set(zoom, cache)
|
|
}
|
|
|
|
const cached = cache.get(sprite)
|
|
if (cached) return cached
|
|
|
|
const rows = sprite.length
|
|
const cols = sprite[0].length
|
|
const canvas = document.createElement('canvas')
|
|
canvas.width = cols * zoom
|
|
canvas.height = rows * zoom
|
|
const ctx = canvas.getContext('2d')!
|
|
ctx.imageSmoothingEnabled = false
|
|
|
|
for (let r = 0; r < rows; r++) {
|
|
for (let c = 0; c < cols; c++) {
|
|
const color = sprite[r][c]
|
|
if (color === '') continue
|
|
ctx.fillStyle = color
|
|
ctx.fillRect(c * zoom, r * zoom, zoom, zoom)
|
|
}
|
|
}
|
|
|
|
cache.set(sprite, canvas)
|
|
return canvas
|
|
}
|