SOURCE / PINNED RELEASE
Made of little things.
Black Site Map Editor
- Release
- 32a57733b7e9…
- Author-recorded commit
- 3f54890f542f…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/black-site-map-editor
Archive hash verified: 7a4b1abbac7db5b0…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import { html } from 'lit';
import { live } from 'lit/directives/live.js';
import { editor, guard } from '../editor-controller';
import { objectControls } from '../editor-controls';
import { selectionValue, type BlackSiteMap, type Layer } from '../model';
import { listedLayers, toolLabel } from '../tools';
import { keyHint } from './key-hint';
import { LightElement } from './light-element';
export class BsInspector extends LightElement {
static properties = { layer: { state: true } };
declare layer: Layer;
constructor() {
super();
this.layer = listedLayers[0][0] as Layer;
this.watch((s) => [
s.map,
s.selection,
s.busy,
s.dirty,
s.base,
s.validation,
s.shortcutsRev,
]);
}
#objects() {
const m = editor.map;
const layer = this.layer;
const items =
layer === 'start' || layer === 'exfil' ? [m[layer]] : m[layer as keyof BlackSiteMap];
return Array.isArray(items) ? items : [];
}
render() {
const { selection, busy, dirty, base, validation } = editor.state;
const value = selection ? selectionValue(editor.map, selection) : undefined;
const fixed = !selection || ['start', 'exfil'].includes(selection.layer);
const controls = () => this.querySelector<HTMLElement>('#object-controls')!;
const moveBy = (axis: string) =>
Number(this.querySelector<HTMLInputElement>(`#move-${axis}`)!.value);
return html`<div class="section">
<h3>SELECTED OBJECT</h3>
<div id="selection-label" class="selection-label">
${
selection
? `${toolLabel(selection.layer)} #${selection.index + 1}${selection.buildingIndex !== undefined ? ` · building #${selection.buildingIndex + 1}` : ''}`
: 'Nothing selected'
}
</div>
<div id="object-controls">${objectControls(editor.map, selection)}</div>
<textarea
id="object-json"
hidden
.value=${live(value ? JSON.stringify(value, null, 2) : '')}
></textarea>
<div class="buttons">
<button
id="apply-object"
?disabled=${!selection || busy}
@click=${guard(() => editor.applyObject(controls()))}
>
Apply changes
</button>
<button
id="delete-object"
class="danger"
?disabled=${fixed || busy}
${keyHint('selection.delete')}
@click=${guard(() => editor.deleteSelected())}
>
Delete
</button>
</div>
<div class="row">
<label>Move across<input id="move-x" type="number" value="0" /></label>
<label>Move down<input id="move-y" type="number" value="0" /></label>
</div>
<button
id="move-object"
?disabled=${!selection || busy}
@click=${guard(() => editor.moveSelectionBy(moveBy('x'), moveBy('y')))}
>
Move selected
</button>
</div>
<div class="section">
<h3>MAP CHECK</h3>
<div id="validation" class="validation">${validation}</div>
<div id="draft-info" class="draft-info">
${
dirty
? 'Workspace has unsaved changes.'
: base
? 'Loaded published revision.'
: 'Local workspace.'
}
</div>
</div>
<details>
<summary>Objects & layers</summary>
<select
id="object-layer"
aria-label="Object layer"
@change=${(e: Event) =>
(this.layer = (e.target as HTMLSelectElement).value as Layer)}
>
${listedLayers.map(([key, label]) => html`<option value=${key} ?selected=${key === this.layer}>${label}</option>`)}
</select>
<div id="object-list" class="object-list">
${this.#objects().map(
(_value, index) =>
html`<button
@click=${() => editor.selectObject({ layer: this.layer, index })}
>
${toolLabel(this.layer)} ${index + 1}
</button>`,
)}
</div>
</details>`;
}
}
customElements.define('bs-inspector', BsInspector);
