Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495import { adopt, define, sheet } from "@nibling/web/internals";import "@nibling/web/tree";import type { TreeActivateEvent, TreeElement } from "@nibling/web/tree";import { treeFromPaths } from "@nibling/web/tree/model";import type { Files } from "../files.ts";import { stacks, type PanelId } from "../shell/layout.ts";import type { Unsubscribe, Workbench } from "../shell/workbench.ts";
const explorerSheet = sheet(` :host { display: flex; container: nib-explorer / size; min-width: 0; min-height: 0; } ::slotted(nib-tree) { flex: 1; min-width: 0; }`);
const hidden = (segment: string) => segment.startsWith(".");
/** The explorer: a `<nib-tree>` over `files` whose selection follows the editor the workbench shows. */export class ExplorerElement extends HTMLElement { readonly #tree: TreeElement = document.createElement("nib-tree"); #workbench: Workbench | undefined; #listing = "";
constructor() { super(); const root = this.attachShadow({ mode: "open", delegatesFocus: true }); adopt(root, explorerSheet); root.append(document.createElement("slot")); this.#tree.label = "Files"; this.#tree.actions = (node) => { if (node.children || !node.id.endsWith(".html")) return; const run = document.createElement("button"); run.textContent = "▶"; run.ariaLabel = `Preview ${node.id}`; run.addEventListener("click", () => this.#workbench?.open({ kind: "preview", input: { path: node.id } })); return [run]; }; this.#tree.addEventListener("activate", (event) => { this.#workbench?.open({ kind: "editor", input: { path: (event as TreeActivateEvent).node.id } }); }); }
connectedCallback(): void { if (!this.#tree.isConnected) this.append(this.#tree); }
override focus(options?: FocusOptions): void { this.#tree.focus(options); }
connect(files: Files, workbench: Workbench): Unsubscribe { this.#workbench = workbench; const sync = () => { const paths = files.list(); // Files reports every write; the tree only changes when the set of paths does. const next = paths.join("\0"); if (next === this.#listing) return; this.#listing = next; this.#tree.nodes = treeFromPaths(paths, { hidden }); }; const follow = () => { const path = shownEditor(workbench); if (path === undefined || path === this.#tree.selected) return; this.#tree.selected = path; this.#tree.reveal(path); }; sync(); follow(); const unwatch = files.watch(sync); const unsubscribe = workbench.subscribe(follow); return () => { unwatch(); unsubscribe(); }; }}
/** The editor in the focused stack, or else in the first stack showing one. */function shownEditor(wb: Workbench): string | undefined { const { layout } = wb; const editorIn = (id: PanelId | null) => { const panel = id ? layout.panels[id] : undefined; return panel?.kind === "editor" ? panel.input.path : undefined; }; const all = stacks(layout.root); const focused = all.find((stack) => stack.id === layout.focus); return editorIn(focused?.active ?? null) ?? all.map((stack) => editorIn(stack.active)).find(Boolean);}
declare global { interface HTMLElementTagNameMap { "nib-explorer": ExplorerElement; }}
define("nib-explorer", ExplorerElement);