@zkmake/three-cameras 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"file":"mount-DBjcu7if.js","names":[],"sources":["../src/ui/styles.ts","../src/ui/panel.ts","../src/ui/mount.ts"],"sourcesContent":["/** The panel's stylesheet, injected once. Palettes by `data-theme` on `.tcm`. */\nexport const CAMERA_PANEL_STYLES = /* css */ `\n/* Inside a dev-panel card the tokens come from three-meter's (\\`--perf-*\\`), so the panels match;\n on its own (in a host's tab) the same palette is the fallback. */\n.tcm {\n --tcm-bg: var(--perf-bg, rgba(22, 24, 29, 0.92));\n --tcm-fg: var(--perf-fg, #e6e8eb);\n --tcm-dim: var(--perf-muted, #8b909a);\n --tcm-line: var(--perf-border, rgba(255, 255, 255, 0.12));\n --tcm-field: var(--perf-row, rgba(255, 255, 255, 0.04));\n --tcm-hover: var(--perf-row, rgba(255, 255, 255, 0.04));\n --tcm-accent: var(--perf-accent, #60a5fa);\n --tcm-accent-soft: rgba(96, 165, 250, 0.14);\n --tcm-live: #34d399;\n box-sizing: border-box;\n color: var(--tcm-fg);\n font: 11px/1.35 ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n}\n.tcm[data-theme=\"light\"] {\n --tcm-bg: rgba(250, 250, 252, 0.94);\n --tcm-fg: #1a1c21;\n --tcm-dim: #6b7079;\n --tcm-line: rgba(0, 0, 0, 0.12);\n --tcm-field: rgba(0, 0, 0, 0.05);\n --tcm-hover: rgba(0, 0, 0, 0.05);\n --tcm-accent: #2563eb;\n --tcm-accent-soft: rgba(37, 99, 235, 0.1);\n --tcm-live: #047857;\n}\n.tcm *, .tcm *::before, .tcm *::after { box-sizing: inherit; }\n.tcm button { font: inherit; color: inherit; }\n\n.tcm-panel { display: flex; flex-direction: column; gap: 6px; min-height: 0; padding: 8px; }\n/* In the dev-panel card: a fixed width (the frame's cap raised to fit), the list scrolling\n inside the card's height. */\n.perf-hud.tcm-frame { max-width: min(23rem, calc(100vw - 48px)); }\n.perf-hud__card > .tcm-panel { width: 22rem; max-width: 100%; flex: 1; }\n/* Compact: the brand row alone, with the count. */\n.perf-hud.tcm-compact .tcm-panel { display: none; }\n.perf-hud.tcm-compact .perf-hud__brand { padding-bottom: 6px; }\n\n.tcm-toolbar { display: flex; gap: 6px; }\n.tcm-search {\n flex: 1;\n min-width: 0;\n border: 1px solid var(--tcm-line);\n border-radius: 4px;\n background: var(--tcm-field);\n padding: 4px 6px;\n color: inherit;\n font: inherit;\n}\n.tcm-search::placeholder { color: var(--tcm-dim); }\n.tcm-search:focus { outline: 1px solid var(--tcm-accent); outline-offset: 0; }\n\n.tcm-icon {\n display: inline-grid;\n place-items: center;\n width: 26px;\n height: 26px;\n flex: none;\n padding: 0;\n border: 1px solid transparent;\n border-radius: 5px;\n background: transparent;\n color: var(--tcm-dim);\n cursor: pointer;\n}\n.tcm-icon:hover:not(:disabled) { background: var(--tcm-hover); color: var(--tcm-fg); }\n.tcm-icon:focus-visible { outline: 1px solid var(--tcm-accent); }\n.tcm-icon:disabled { visibility: hidden; }\n.tcm-icon[aria-pressed=\"true\"] { color: var(--tcm-accent); background: var(--tcm-accent-soft); }\n.tcm svg {\n width: 16px;\n height: 16px;\n fill: none;\n stroke: currentColor;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n\n.tcm-list { margin: 0; padding: 0; list-style: none; overflow-y: auto; min-height: 0; }\n.tcm-row { border-radius: 5px; border: 1px solid transparent; }\n.tcm-row.is-hidden { display: none; }\n.tcm-row.is-open { border-color: var(--tcm-line); background: var(--tcm-field); }\n.tcm-head {\n display: grid;\n grid-template-columns: 32px 1fr auto;\n align-items: center;\n gap: 8px;\n padding: 4px;\n border-radius: 5px;\n}\n.tcm-row:not(.is-open) .tcm-head:hover { background: var(--tcm-hover); }\n.tcm-kind {\n display: grid;\n place-items: center;\n width: 32px;\n height: 32px;\n border: 1px solid var(--tcm-line);\n border-radius: 5px;\n color: var(--tcm-dim);\n}\n.tcm-row.is-live .tcm-kind { color: var(--tcm-live); border-color: color-mix(in srgb, var(--tcm-live) 45%, transparent); }\n.tcm-text {\n min-width: 0;\n padding: 0;\n border: 0;\n background: none;\n text-align: left;\n cursor: pointer;\n}\n.tcm-name-line { display: flex; align-items: baseline; gap: 6px; min-width: 0; }\n.tcm-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }\n.tcm-badge { flex: none; color: var(--tcm-dim); }\n.tcm-badge:empty { display: none; }\n.tcm-badge.is-live, .tcm-badge.is-viewing { display: inline-flex; align-items: center; gap: 4px; }\n.tcm-badge.is-live { color: var(--tcm-live); }\n.tcm-badge.is-viewing { color: var(--tcm-accent); }\n.tcm-badge.is-live::before {\n content: \"\";\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentColor;\n animation: tcm-pulse 1.6s ease-in-out infinite;\n}\n@keyframes tcm-pulse { 50% { opacity: 0.25; } }\n@media (prefers-reduced-motion: reduce) { .tcm-badge.is-live::before { animation: none; } }\n.tcm-row.is-viewing .tcm-kind { color: var(--tcm-accent); border-color: color-mix(in srgb, var(--tcm-accent) 50%, transparent); }\n.tcm-meta { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--tcm-dim); }\n.tcm-actions { display: flex; gap: 2px; }\n.tcm-chevron svg { transition: rotate 120ms; }\n.tcm-row.is-open .tcm-chevron svg { rotate: 180deg; }\n\n.tcm-details {\n display: none;\n gap: 8px;\n padding: 2px 8px 8px;\n}\n.tcm-row.is-open .tcm-details { display: grid; }\n.tcm-group { display: grid; gap: 3px; }\n.tcm-term { color: var(--tcm-dim); }\n.tcm-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px; }\n.tcm-fields--lens { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n.tcm-num {\n display: flex;\n align-items: center;\n min-width: 0;\n border: 1px solid var(--tcm-line);\n border-radius: 4px;\n background: var(--tcm-field);\n}\n.tcm-num:focus-within { border-color: var(--tcm-accent); }\n.tcm-scrub {\n flex: none;\n padding: 0 5px;\n color: var(--tcm-dim);\n cursor: ew-resize;\n user-select: none;\n touch-action: none;\n}\n.tcm-scrub:hover { color: var(--tcm-fg); }\n.tcm-num input {\n width: 100%;\n min-width: 0;\n padding: 3px 5px 3px 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n font-variant-numeric: tabular-nums;\n text-align: right;\n -moz-appearance: textfield;\n appearance: textfield;\n}\n.tcm-num input::-webkit-inner-spin-button, .tcm-num input::-webkit-outer-spin-button { appearance: none; margin: 0; }\n.tcm-num input:focus { outline: none; }\n.tcm-readout { display: grid; grid-template-columns: auto 1fr; gap: 2px 10px; margin: 0; }\n.tcm-readout dt { color: var(--tcm-dim); }\n.tcm-readout dd { margin: 0; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }\n.tcm-buttons { display: flex; gap: 6px; }\n.tcm-text-button {\n border: 1px solid var(--tcm-line);\n border-radius: 4px;\n background: var(--tcm-field);\n padding: 3px 8px;\n cursor: pointer;\n white-space: nowrap;\n}\n.tcm-text-button:hover { border-color: var(--tcm-accent); }\n.tcm-text-button:focus-visible { outline: 1px solid var(--tcm-accent); }\n.tcm-views { display: flex; flex-wrap: wrap; gap: 4px; margin: 0; padding: 0; list-style: none; }\n.tcm-views:empty { display: none; }\n.tcm-view { display: flex; align-items: center; border: 1px solid var(--tcm-line); border-radius: 4px; }\n.tcm-view .tcm-icon { width: 20px; height: 20px; }\n.tcm-view .tcm-icon svg { width: 12px; height: 12px; }\n.tcm-view-go {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 4px 2px 6px;\n border: 0;\n background: none;\n cursor: pointer;\n}\n.tcm-view-go svg { width: 12px; height: 12px; }\n.tcm-view-go:hover { color: var(--tcm-accent); }\n.tcm-banner {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 4px 4px 4px 8px;\n border-radius: 5px;\n background: var(--tcm-accent-soft);\n color: var(--tcm-accent);\n}\n.tcm-banner[hidden] { display: none; }\n.tcm-status { color: var(--tcm-dim); padding: 0 4px; }\n.tcm-status:empty { display: none; }\n.tcm-empty { color: var(--tcm-dim); padding: 6px 4px; }\n`;\n\nconst STYLE_ID = \"zkmake-three-cameras-styles\";\n\n/** Add the stylesheet to `document.head` once. */\nexport const injectStyles = () => {\n if (typeof document === \"undefined\" || document.getElementById(STYLE_ID)) {\n return;\n }\n\n const style = document.createElement(\"style\");\n\n style.id = STYLE_ID;\n style.textContent = CAMERA_PANEL_STYLES;\n document.head.append(style);\n};\n","/**\n * The camera list: one row per camera with its kind, name, whether it's drawing right now and how\n * often, and its projection at a glance. Pick a row and it opens into the camera's controls: its\n * position, rotation and lens as fields you can type in or scrub (drag a field's label), copy as\n * code, and saved views to fly back to. Per row: look through it, and draw its frustum.\n */\nimport type { CameraKind } from \"../core/discover.ts\";\nimport type { CameraDetails, CameraEntry, CameraLab, CameraPatch } from \"../core/lab.ts\";\nimport { capturePose, poseToCode } from \"../core/pose.ts\";\nimport { injectStyles } from \"./styles.ts\";\n\nexport type CameraPanel = {\n element: HTMLElement;\n /** Paint the panel when it's not in a dev-panel card, `dark` or `light`. Default dark. */\n setTheme(theme: \"dark\" | \"light\"): void;\n /** Re-read the scene now (the panel also does a few times a second while shown). */\n refresh(): void;\n /** Pause or resume the live updates, e.g. while a host tab is hidden. */\n setActive(active: boolean): void;\n dispose(): void;\n};\n\ntype FieldKey = \"px\" | \"py\" | \"pz\" | \"rx\" | \"ry\" | \"rz\" | \"fov\" | \"near\" | \"far\" | \"zoom\";\n\ntype Controls = {\n fields: Map<FieldKey, HTMLInputElement>;\n readout: HTMLElement;\n views: HTMLUListElement;\n copy: HTMLButtonElement;\n /** What the saved-view list was drawn from, to redraw only when it changes. */\n viewsDrawn: string;\n};\n\ntype Row = {\n li: HTMLLIElement;\n name: HTMLElement;\n badge: HTMLElement;\n meta: HTMLElement;\n look: HTMLButtonElement;\n helper: HTMLButtonElement;\n details: HTMLElement;\n controls: Controls | null;\n open: boolean;\n};\n\n/** Live badges and open rows update this often, ms. */\nconst TICK_MS = 250;\n\n/** Per field: the step a typed arrow key takes and what a pixel of scrubbing adds. */\nconst FIELDS: Record<FieldKey, { label: string; step: number; perPixel: number; min?: number }> = {\n px: { label: \"x\", step: 0.01, perPixel: 0.02 },\n py: { label: \"y\", step: 0.01, perPixel: 0.02 },\n pz: { label: \"z\", step: 0.01, perPixel: 0.02 },\n rx: { label: \"x\", step: 0.1, perPixel: 0.5 },\n ry: { label: \"y\", step: 0.1, perPixel: 0.5 },\n rz: { label: \"z\", step: 0.1, perPixel: 0.5 },\n fov: { label: \"fov\", step: 0.1, perPixel: 0.2, min: 1 },\n near: { label: \"near\", step: 0.01, perPixel: 0.005, min: 0.0001 },\n far: { label: \"far\", step: 1, perPixel: 0.5, min: 0.001 },\n zoom: { label: \"zoom\", step: 0.01, perPixel: 0.005, min: 0.01 },\n};\n\n/** Line icons on a 24 grid, stroked in the text colour (styles.ts). */\nconst KIND_ICONS: Record<CameraKind, string> = {\n perspective: '<rect x=\"2\" y=\"7\" width=\"12\" height=\"10\" rx=\"2\"/><path d=\"m14 10.5 7-4v11l-7-4\"/>',\n orthographic: '<rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\"/><path d=\"M3 12h18\"/>',\n array:\n '<rect x=\"3\" y=\"3\" width=\"8\" height=\"8\" rx=\"1.5\"/><rect x=\"13\" y=\"3\" width=\"8\" height=\"8\" rx=\"1.5\"/><rect x=\"3\" y=\"13\" width=\"8\" height=\"8\" rx=\"1.5\"/><rect x=\"13\" y=\"13\" width=\"8\" height=\"8\" rx=\"1.5\"/>',\n cube: '<path d=\"M12 3 20 7.5v9L12 21l-8-4.5v-9Z\"/><path d=\"m4 7.5 8 4.5 8-4.5M12 12v9\"/>',\n other: '<circle cx=\"12\" cy=\"12\" r=\"8\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>',\n};\n\nconst ICONS = {\n look: '<path d=\"M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7Z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>',\n frustum: '<path d=\"M3 12 20 5v14Z\"/><path d=\"M20 5v14\"/><circle cx=\"3\" cy=\"12\" r=\"1\"/>',\n chevron: '<path d=\"m6 9 6 6 6-6\"/>',\n refresh: '<path d=\"M21 12a9 9 0 1 1-3-6.7L21 8\"/><path d=\"M21 3v5h-5\"/>',\n go: '<path d=\"M5 12h14\"/><path d=\"m13 6 6 6-6 6\"/>',\n remove: '<path d=\"M6 6l12 12M18 6 6 18\"/>',\n};\n\nconst svg = (paths: string) => `<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\">${paths}</svg>`;\n\nconst icon = (name: keyof typeof ICONS, label: string, extra = \"\") =>\n `<button type=\"button\" class=\"tcm-icon ${extra}\" data-action=\"${name}\" title=\"${label}\" aria-label=\"${label}\">${svg(ICONS[name])}</button>`;\n\nconst trim = (value: number, digits = 2) => {\n const fixed = value.toFixed(digits);\n\n return fixed.includes(\".\") ? fixed.replace(/\\.?0+$/, \"\") : fixed;\n};\n\nconst range = (near: number, far: number) => `${trim(near)}–${trim(far)}`;\n\nconst triple = (values: readonly number[], digits = 2) =>\n values.map((value) => trim(value, digits)).join(\" \");\n\nconst escape = (text: string) => text.replace(/[&<>\"]/g, (char) => `&#${char.charCodeAt(0)};`);\n\n/** A camera's name as a JS identifier, for copied code: `orbit view` → `orbitView`. */\nconst identifier = (name: string) => {\n const words = name.split(/[^A-Za-z0-9]+/).filter(Boolean);\n const joined = words\n .map((word, index) =>\n index === 0 ? word.toLowerCase() : word[0]!.toUpperCase() + word.slice(1),\n )\n .join(\"\");\n\n return /^[A-Za-z_]/.test(joined) ? joined : `camera${joined}`;\n};\n\n/** The row's second line: how it projects, at a glance. */\nconst describe = (entry: CameraEntry, details: CameraDetails | null) => {\n const projection = details?.projection;\n const parts: string[] = [];\n\n if (entry.kind === \"cube\") {\n parts.push(\"cube · 6 faces\");\n } else if (projection?.fov !== undefined) {\n parts.push(`fov ${trim(projection.fov, 1)}°`, range(projection.near, projection.far));\n\n if (projection.aspect !== undefined) {\n parts.push(trim(projection.aspect));\n }\n } else if (projection) {\n parts.push(\"ortho\", `zoom ${trim(projection.zoom)}`, range(projection.near, projection.far));\n } else {\n parts.push(entry.kind);\n }\n\n if (!entry.inScene) {\n parts.push(\"not in scene\");\n }\n\n return parts.join(\" · \");\n};\n\n/** Facts that aren't edited here, under the fields. */\nconst readoutOf = (entry: CameraEntry, details: CameraDetails) => {\n const { projection } = details;\n const rows: [string, string][] = [[\"world\", triple(details.position)]];\n\n if (projection?.aspect !== undefined) {\n rows.push([\"aspect\", trim(projection.aspect, 3)]);\n }\n\n if (projection?.left !== undefined) {\n rows.push([\n \"frustum\",\n `l ${trim(projection.left)} r ${trim(projection.right!)} t ${trim(projection.top!)} b ${trim(projection.bottom!)}`,\n ]);\n }\n\n rows.push([\"drawing\", entry.live ? `${entry.fps} fps` : \"no\"]);\n rows.push([\n \"in\",\n entry.inScene ? entry.path.join(\" › \") || \"scene\" : \"the renderer only (not in the scene)\",\n ]);\n\n return rows.map(([term, value]) => `<dt>${term}</dt><dd>${escape(value)}</dd>`).join(\"\");\n};\n\nconst numberField = (key: FieldKey) => {\n const { label, step, min } = FIELDS[key];\n\n return `<label class=\"tcm-num\"><span class=\"tcm-scrub\" data-scrub=\"${key}\" title=\"Drag to change: Shift for fine, Alt for coarse\">${label}</span><input type=\"number\" data-field=\"${key}\" step=\"${step}\"${min === undefined ? \"\" : ` min=\"${min}\"`} inputmode=\"decimal\" /></label>`;\n};\n\n/** The values a field shows, from the camera now. */\nconst valuesOf = (details: CameraDetails): Partial<Record<FieldKey, number>> => {\n const [px, py, pz] = details.local.position;\n const [rx, ry, rz] = details.local.rotation;\n const projection = details.projection;\n\n return {\n px,\n py,\n pz,\n rx,\n ry,\n rz,\n ...(projection\n ? { near: projection.near, far: projection.far, zoom: projection.zoom, fov: projection.fov }\n : {}),\n };\n};\n\nconst formatField = (key: FieldKey, value: number) =>\n trim(value, key.startsWith(\"r\") || key === \"fov\" ? 2 : 4);\n\nexport const createCameraPanel = (lab: CameraLab): CameraPanel => {\n injectStyles();\n\n const element = document.createElement(\"div\");\n\n element.className = \"tcm tcm-panel\";\n element.innerHTML = `\n <div class=\"tcm-toolbar\">\n <input type=\"search\" class=\"tcm-search\" placeholder=\"Filter by name…\" autocomplete=\"off\" spellcheck=\"false\" aria-label=\"Filter cameras\" />\n ${icon(\"refresh\", \"Look for new cameras\")}\n </div>\n <div class=\"tcm-banner\" hidden><span></span><button type=\"button\" class=\"tcm-text-button\" data-action=\"look-back\">Back</button></div>\n <ul class=\"tcm-list\"></ul>\n <div class=\"tcm-empty\" hidden>No cameras yet. The one the renderer draws with shows up on its first frame.</div>\n <div class=\"tcm-status\" role=\"status\"></div>\n `;\n\n const search = element.querySelector<HTMLInputElement>(\".tcm-search\")!;\n const list = element.querySelector<HTMLUListElement>(\".tcm-list\")!;\n const empty = element.querySelector<HTMLElement>(\".tcm-empty\")!;\n const status = element.querySelector<HTMLElement>(\".tcm-status\")!;\n const banner = element.querySelector<HTMLElement>(\".tcm-banner\")!;\n const bannerText = banner.querySelector(\"span\")!;\n const rows = new Map<string, Row>();\n let timer: ReturnType<typeof setInterval> | null = null;\n /** The field being scrubbed: the tick leaves it alone. */\n let scrubbing: HTMLInputElement | null = null;\n\n const say = (message: string) => {\n status.textContent = message;\n };\n\n const attempt = (work: () => void) => {\n try {\n say(\"\");\n work();\n } catch (error) {\n say((error as Error).message.replace(/^three-cameras: /, \"\"));\n }\n };\n\n /** Read one field (or its x/y/z group) into a patch and set it. */\n const apply = (id: string, controls: Controls, key: FieldKey) => {\n const read = (field: FieldKey) => Number(controls.fields.get(field)?.value);\n const patch: CameraPatch = {};\n\n if (key.startsWith(\"p\")) {\n const values = [read(\"px\"), read(\"py\"), read(\"pz\")] as const;\n\n if (values.every(Number.isFinite)) {\n patch.position = values;\n }\n } else if (key.startsWith(\"r\")) {\n const values = [read(\"rx\"), read(\"ry\"), read(\"rz\")] as const;\n\n if (values.every(Number.isFinite)) {\n patch.rotation = values;\n }\n } else {\n const value = read(key);\n const min = FIELDS[key].min;\n\n if (Number.isFinite(value) && (min === undefined || value >= min)) {\n patch[key as \"fov\" | \"near\" | \"far\" | \"zoom\"] = value;\n }\n }\n\n attempt(() => lab.set(id, patch));\n };\n\n const buildControls = (row: Row, entry: CameraEntry): Controls => {\n const lens =\n entry.kind === \"perspective\"\n ? ([\"fov\", \"near\", \"far\", \"zoom\"] as const)\n : entry.kind === \"orthographic\"\n ? ([\"zoom\", \"near\", \"far\"] as const)\n : [];\n\n row.details.innerHTML = `\n <div class=\"tcm-group\"><span class=\"tcm-term\">position</span><div class=\"tcm-fields\">${numberField(\"px\")}${numberField(\"py\")}${numberField(\"pz\")}</div></div>\n <div class=\"tcm-group\"><span class=\"tcm-term\">rotation °</span><div class=\"tcm-fields\">${numberField(\"rx\")}${numberField(\"ry\")}${numberField(\"rz\")}</div></div>\n ${lens.length > 0 ? `<div class=\"tcm-group\"><span class=\"tcm-term\">lens</span><div class=\"tcm-fields tcm-fields--lens\">${lens.map(numberField).join(\"\")}</div></div>` : \"\"}\n <dl class=\"tcm-readout\"></dl>\n ${\n entry.projects\n ? `<div class=\"tcm-buttons\">\n <button type=\"button\" class=\"tcm-text-button\" data-action=\"copy\" title=\"Copy three.js that sets this camera up as it is now\">Copy as code</button>\n <button type=\"button\" class=\"tcm-text-button\" data-action=\"save\" title=\"Save where it is and how it projects, to fly back to\">Save view</button>\n </div>\n <ul class=\"tcm-views\" aria-label=\"Saved views\"></ul>`\n : \"\"\n }\n `;\n\n const fields = new Map<FieldKey, HTMLInputElement>();\n\n for (const input of row.details.querySelectorAll<HTMLInputElement>(\"input[data-field]\")) {\n fields.set(input.dataset.field as FieldKey, input);\n }\n\n const controls: Controls = {\n fields,\n readout: row.details.querySelector(\".tcm-readout\")!,\n views: row.details.querySelector(\".tcm-views\") ?? document.createElement(\"ul\"),\n copy: row.details.querySelector('[data-action=\"copy\"]') ?? document.createElement(\"button\"),\n viewsDrawn: \"\",\n };\n const id = entry.id;\n\n for (const [key, input] of fields) {\n input.addEventListener(\"input\", () => apply(id, controls, key));\n // Put the camera's real value back once the edit is done, e.g. after an invalid entry.\n input.addEventListener(\"blur\", () => {\n const details = lab.details(id);\n const value = details ? valuesOf(details)[key] : undefined;\n\n if (value !== undefined) {\n input.value = formatField(key, value);\n }\n });\n }\n\n for (const handle of row.details.querySelectorAll<HTMLElement>(\"[data-scrub]\")) {\n const key = handle.dataset.scrub as FieldKey;\n const input = fields.get(key)!;\n\n handle.addEventListener(\"pointerdown\", (event) => {\n event.preventDefault();\n handle.setPointerCapture(event.pointerId);\n scrubbing = input;\n\n const startX = event.clientX;\n const startValue = Number(input.value) || 0;\n const { perPixel, min } = FIELDS[key];\n\n const onMove = (move: PointerEvent) => {\n const scale = move.shiftKey ? 0.1 : move.altKey ? 10 : 1;\n let value = startValue + (move.clientX - startX) * perPixel * scale;\n\n if (min !== undefined) {\n value = Math.max(min, value);\n }\n\n input.value = formatField(key, value);\n apply(id, controls, key);\n };\n const onUp = () => {\n scrubbing = null;\n handle.removeEventListener(\"pointermove\", onMove);\n handle.removeEventListener(\"pointerup\", onUp);\n handle.removeEventListener(\"pointercancel\", onUp);\n };\n\n handle.addEventListener(\"pointermove\", onMove);\n handle.addEventListener(\"pointerup\", onUp);\n handle.addEventListener(\"pointercancel\", onUp);\n });\n }\n\n return controls;\n };\n\n const drawViews = (row: Row, entry: CameraEntry) => {\n const controls = row.controls!;\n const views = lab.views(entry.id);\n const drawn = views.map((view) => `${view.id}:${view.name}`).join(\"|\");\n\n if (drawn === controls.viewsDrawn) {\n return;\n }\n\n controls.viewsDrawn = drawn;\n controls.views.innerHTML = views\n .map(\n (view) => `<li class=\"tcm-view\" data-view=\"${escape(view.id)}\">\n <button type=\"button\" class=\"tcm-view-go\" data-action=\"go\" title=\"Fly back to ${escape(view.name)}\">${svg(ICONS.go)}<span>${escape(view.name)}</span></button>\n ${icon(\"remove\", `Forget ${escape(view.name)}`)}\n </li>`,\n )\n .join(\"\");\n };\n\n const makeRow = (entry: CameraEntry): Row => {\n const li = document.createElement(\"li\");\n const id = entry.id;\n\n li.className = \"tcm-row\";\n li.innerHTML = `\n <div class=\"tcm-head\">\n <span class=\"tcm-kind\" title=\"${entry.kind} camera\">${svg(KIND_ICONS[entry.kind])}</span>\n <button type=\"button\" class=\"tcm-text\" data-action=\"open\" aria-expanded=\"false\" title=\"Pick to edit\">\n <span class=\"tcm-name-line\"><span class=\"tcm-name\"></span><span class=\"tcm-badge\"></span></span>\n <span class=\"tcm-meta\"></span>\n </button>\n <div class=\"tcm-actions\">\n ${icon(\"look\", \"Look through it\")}\n ${icon(\"frustum\", \"Show its frustum in the scene\")}\n ${icon(\"chevron\", \"Edit\", \"tcm-chevron\")}\n </div>\n </div>\n <div class=\"tcm-details\"></div>\n `;\n\n const row: Row = {\n li,\n name: li.querySelector(\".tcm-name\")!,\n badge: li.querySelector(\".tcm-badge\")!,\n meta: li.querySelector(\".tcm-meta\")!,\n look: li.querySelector<HTMLButtonElement>('[data-action=\"look\"]')!,\n helper: li.querySelector<HTMLButtonElement>('[data-action=\"frustum\"]')!,\n details: li.querySelector(\".tcm-details\")!,\n controls: null,\n open: false,\n };\n\n row.name.textContent = id;\n row.name.title = id;\n li.addEventListener(\"click\", (event) => {\n const target = (event.target as HTMLElement).closest<HTMLElement>(\"[data-action]\");\n const action = target?.dataset.action;\n const current = lab.entry(id);\n\n if (!current || !action) {\n return;\n }\n\n if (action === \"open\" || action === \"chevron\") {\n row.open = !row.open;\n\n if (row.open && !row.controls) {\n row.controls = buildControls(row, current);\n }\n\n update(row, current);\n } else if (action === \"look\") {\n attempt(() => lab.lookThrough(current.viewing ? null : id));\n } else if (action === \"frustum\") {\n attempt(() => lab.setHelper(id, !current.helper));\n } else if (action === \"copy\") {\n void copyCode(row, current);\n } else if (action === \"save\") {\n attempt(() => lab.saveView(id));\n } else if (action === \"go\" || action === \"remove\") {\n const viewId = target.closest<HTMLElement>(\"[data-view]\")?.dataset.view;\n\n if (viewId) {\n attempt(() => (action === \"go\" ? lab.goToView(id, viewId) : lab.deleteView(id, viewId)));\n }\n }\n });\n\n return row;\n };\n\n const copyCode = async (row: Row, entry: CameraEntry) => {\n const code = poseToCode(capturePose(entry.camera as never), identifier(entry.id));\n const button = row.controls!.copy;\n\n try {\n await navigator.clipboard.writeText(code);\n button.textContent = \"Copied\";\n } catch {\n say(\"The clipboard is blocked here. The code is logged to the console instead.\");\n // oxlint-disable-next-line no-console -- the fallback when the clipboard is blocked\n console.log(code);\n }\n\n setTimeout(() => {\n button.textContent = \"Copy as code\";\n }, 1200);\n };\n\n const update = (row: Row, entry: CameraEntry) => {\n const open = row.open;\n const details = lab.details(entry.id);\n\n row.li.classList.toggle(\"is-live\", entry.live);\n row.li.classList.toggle(\"is-open\", open);\n row.li.classList.toggle(\"is-viewing\", entry.viewing);\n row.badge.className = `tcm-badge${entry.viewing ? \" is-viewing\" : entry.live ? \" is-live\" : \"\"}`;\n row.badge.textContent = entry.viewing\n ? \"viewing\"\n : entry.standingIn\n ? \"replaced\"\n : entry.live\n ? `live · ${entry.fps} fps`\n : \"\";\n row.meta.textContent = describe(entry, details);\n row.meta.title = row.meta.textContent;\n row.look.disabled = !entry.projects || !lab.watching;\n row.look.setAttribute(\"aria-pressed\", String(entry.viewing));\n row.look.title = entry.viewing ? \"Stop looking through it\" : \"Look through it\";\n row.look.setAttribute(\"aria-label\", row.look.title);\n row.helper.disabled = !entry.canHelp;\n row.helper.setAttribute(\"aria-pressed\", String(entry.helper));\n row.helper.title = entry.helper ? \"Hide its frustum\" : \"Show its frustum in the scene\";\n row.helper.setAttribute(\"aria-label\", row.helper.title);\n\n for (const toggle of row.li.querySelectorAll('[data-action=\"open\"], [data-action=\"chevron\"]')) {\n toggle.setAttribute(\"aria-expanded\", String(open));\n }\n\n if (!open || !details || !row.controls) {\n return;\n }\n\n const values = valuesOf(details);\n\n for (const [key, input] of row.controls.fields) {\n const value = values[key];\n\n // Leave a field alone while it's being typed in or scrubbed.\n if (value !== undefined && input !== document.activeElement && input !== scrubbing) {\n input.value = formatField(key, value);\n }\n }\n\n row.controls.readout.innerHTML = readoutOf(entry, details);\n drawViews(row, entry);\n };\n\n const filter = () => {\n const query = search.value.trim().toLowerCase();\n\n for (const [id, row] of rows) {\n row.li.classList.toggle(\"is-hidden\", query.length > 0 && !id.toLowerCase().includes(query));\n }\n };\n\n /** Rows in, rows out, then every row's live state. */\n const render = () => {\n const entries = lab.entries();\n const ids = new Set(entries.map((entry) => entry.id));\n\n for (const [id, row] of rows) {\n if (!ids.has(id)) {\n row.li.remove();\n rows.delete(id);\n }\n }\n\n for (const entry of entries) {\n let row = rows.get(entry.id);\n\n if (!row) {\n row = makeRow(entry);\n rows.set(entry.id, row);\n list.append(row.li);\n }\n\n update(row, entry);\n }\n\n const viewing = entries.find((entry) => entry.viewing);\n const standIn = entries.find((entry) => entry.standingIn);\n\n banner.hidden = !viewing;\n bannerText.textContent = viewing\n ? `Looking through ${viewing.id}${standIn ? ` in place of ${standIn.id}` : \"\"}`\n : \"\";\n empty.hidden = entries.length > 0;\n filter();\n element.dataset.count = String(entries.length);\n element.dispatchEvent(new CustomEvent(\"tcm-count\", { detail: entries.length }));\n };\n\n element.querySelector('[data-action=\"refresh\"]')!.addEventListener(\"click\", () => lab.refresh());\n banner\n .querySelector(\"button\")!\n .addEventListener(\"click\", () => attempt(() => lab.lookThrough(null)));\n search.addEventListener(\"input\", filter);\n\n const unsubscribe = lab.subscribe(render);\n\n const setActive = (active: boolean) => {\n if (active && !timer) {\n lab.refresh();\n render();\n timer = setInterval(() => {\n lab.refresh();\n render();\n }, TICK_MS);\n } else if (!active && timer) {\n clearInterval(timer);\n timer = null;\n }\n };\n\n render();\n setActive(true);\n\n return {\n element,\n setTheme: (theme) => {\n // In a dev-panel card the frame's palette (`--perf-*`) applies; elsewhere the panel's own.\n if (element.closest(\".perf-hud__card\")) {\n delete element.dataset.theme;\n } else {\n element.dataset.theme = theme;\n }\n },\n refresh: () => lab.refresh(),\n setActive,\n dispose: () => {\n setActive(false);\n unsubscribe();\n element.remove();\n },\n };\n};\n","/**\n * The panel in the zkmake dev-panel frame (three-meter's `mountDevPanel`), so it docks, wakes,\n * dims and toggles exactly like the three-meter HUD: drag grip, expand/compact and dim-on-leave\n * discs beside a card with the brand label on top. One call for a vanilla three app.\n */\nimport {\n type DefaultPlacement,\n HudTheme,\n mountDevPanel,\n type ThemeMode,\n} from \"@zkmake/three-meter/ui\";\n\nimport { CameraLab, type CameraLabOptions, type SavedView, type ViewStore } from \"../core/lab.ts\";\nimport { createCameraPanel } from \"./panel.ts\";\n\nexport type MountCameraPanelOptions = CameraLabOptions & {\n /** localStorage key prefix for the dock, compact state and saved views. `null` keeps them for this page only. Default `three-cameras`. */\n storageKey?: string | null;\n /** Where it docks on a first visit. Default the right edge, top. */\n defaultPlacement?: DefaultPlacement;\n /** `dark`, `light` or `system` (default). */\n theme?: ThemeMode;\n /** Start compact: the brand row and the count. The choice is remembered. */\n compact?: boolean;\n /** Where to mount. Default `document.body`. */\n container?: HTMLElement;\n};\n\nexport type CameraPanelHandle = {\n lab: CameraLab;\n element: HTMLElement;\n setCompact(compact: boolean): void;\n /** `dark`, `light` or `system`: the panel follows it. */\n setTheme(mode: ThemeMode): void;\n dispose(): void;\n};\n\nconst readFlag = (key: string) => {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n};\n\nconst writeFlag = (key: string, value: string) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n // Private mode: the choice lasts this page.\n }\n};\n\n/** Saved views in localStorage, as JSON under one key. */\nconst localViews = (key: string): ViewStore => ({\n load: () => {\n try {\n const parsed: unknown = JSON.parse(localStorage.getItem(key) ?? \"{}\");\n\n return parsed && typeof parsed === \"object\" ? (parsed as Record<string, SavedView[]>) : {};\n } catch {\n return {};\n }\n },\n save: (views) => {\n try {\n localStorage.setItem(key, JSON.stringify(views));\n } catch {\n // Private mode or full: the views last this page.\n }\n },\n});\n\nexport const mountCameraPanel = (options: MountCameraPanelOptions): CameraPanelHandle => {\n const key = options.storageKey === null ? null : (options.storageKey ?? \"three-cameras\");\n const store = options.store ?? (key ? localViews(`${key}:views`) : undefined);\n const lab = new CameraLab({ ...options, ...(store ? { store } : {}) });\n const panel = createCameraPanel(lab);\n const theme = new HudTheme(options.theme ?? \"system\");\n const compactKey = key ? `${key}:compact` : null;\n\n const setCompact = (compact: boolean) => {\n frame.element.classList.toggle(\"tcm-compact\", compact);\n panel.setActive(!compact);\n\n if (compactKey) {\n writeFlag(compactKey, compact ? \"1\" : \"0\");\n }\n\n frame.refresh();\n };\n\n const frame = mountDevPanel({\n brand: \"three-cameras\",\n content: panel.element,\n defaultPlacement: options.defaultPlacement ?? { edge: \"right\", align: \"start\" },\n label: \"Cameras\",\n onToggle: () => setCompact(!frame.element.classList.contains(\"tcm-compact\")),\n storageKey: key ? `${key}:panel` : null,\n theme,\n toggleLabel: \"Show or hide the camera list\",\n ...(options.container ? { parent: options.container } : {}),\n });\n\n frame.element.classList.add(\"tcm-frame\");\n\n const paintTheme = () => panel.setTheme(theme.resolved);\n const unsubscribeTheme = theme.subscribe(paintTheme);\n const showCount = (count: number) => {\n frame.detail.textContent = `${count} camera${count === 1 ? \"\" : \"s\"}`;\n };\n const onCount = (event: Event) => showCount((event as CustomEvent<number>).detail);\n\n paintTheme();\n panel.element.addEventListener(\"tcm-count\", onCount);\n showCount(Number(panel.element.dataset.count ?? 0));\n\n const stored = compactKey ? readFlag(compactKey) : null;\n\n setCompact(stored === null ? options.compact === true : stored === \"1\");\n\n return {\n lab,\n element: frame.element,\n setCompact,\n setTheme: (mode) => theme.setMode(mode),\n dispose: () => {\n unsubscribeTheme();\n theme.dispose();\n panel.element.removeEventListener(\"tcm-count\", onCount);\n panel.dispose();\n frame.dispose();\n lab.dispose();\n },\n };\n};\n"],"mappings":"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"}