@three-flatland/devtools 0.1.0-alpha.3
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.
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/dist/buffers-modal.cjs +491 -0
- package/dist/buffers-modal.cjs.map +1 -0
- package/dist/buffers-modal.d.cts +62 -0
- package/dist/buffers-modal.d.ts +62 -0
- package/dist/buffers-modal.js +467 -0
- package/dist/buffers-modal.js.map +1 -0
- package/dist/buffers-view.cjs +269 -0
- package/dist/buffers-view.cjs.map +1 -0
- package/dist/buffers-view.d.cts +39 -0
- package/dist/buffers-view.d.ts +39 -0
- package/dist/buffers-view.js +245 -0
- package/dist/buffers-view.js.map +1 -0
- package/dist/create-pane.cjs +203 -0
- package/dist/create-pane.cjs.map +1 -0
- package/dist/create-pane.d.cts +66 -0
- package/dist/create-pane.d.ts +66 -0
- package/dist/create-pane.js +178 -0
- package/dist/create-pane.js.map +1 -0
- package/dist/dashboard/app.tsx +42 -0
- package/dist/dashboard/client.ts +21 -0
- package/dist/dashboard/export.ts +84 -0
- package/dist/dashboard/hooks.ts +75 -0
- package/dist/dashboard/index.html +1149 -0
- package/dist/dashboard/index.tsx +18 -0
- package/dist/dashboard/panels/batches.tsx +282 -0
- package/dist/dashboard/panels/buffers.tsx +425 -0
- package/dist/dashboard/panels/env.tsx +85 -0
- package/dist/dashboard/panels/header-stats.tsx +30 -0
- package/dist/dashboard/panels/producer-select.tsx +81 -0
- package/dist/dashboard/panels/protocol-log.tsx +743 -0
- package/dist/dashboard/panels/registry.tsx +642 -0
- package/dist/dashboard/panels/stats.tsx +431 -0
- package/dist/dashboard/protocol-store.ts +312 -0
- package/dist/dashboard/tsconfig.json +19 -0
- package/dist/dashboard/vendor/hooks.module.js +1 -0
- package/dist/dashboard/vendor/jsx-runtime.js +22 -0
- package/dist/dashboard/vendor/preact.module.js +12 -0
- package/dist/devtools-client.cjs +870 -0
- package/dist/devtools-client.cjs.map +1 -0
- package/dist/devtools-client.d.cts +362 -0
- package/dist/devtools-client.d.ts +362 -0
- package/dist/devtools-client.js +852 -0
- package/dist/devtools-client.js.map +1 -0
- package/dist/index.cjs +42 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/perf-trace.cjs +103 -0
- package/dist/perf-trace.cjs.map +1 -0
- package/dist/perf-trace.d.cts +27 -0
- package/dist/perf-trace.d.ts +27 -0
- package/dist/perf-trace.js +79 -0
- package/dist/perf-trace.js.map +1 -0
- package/dist/plugins.cjs +44 -0
- package/dist/plugins.cjs.map +1 -0
- package/dist/plugins.d.cts +7 -0
- package/dist/plugins.d.ts +7 -0
- package/dist/plugins.js +9 -0
- package/dist/plugins.js.map +1 -0
- package/dist/provider-switcher.cjs +95 -0
- package/dist/provider-switcher.cjs.map +1 -0
- package/dist/provider-switcher.d.cts +18 -0
- package/dist/provider-switcher.d.ts +18 -0
- package/dist/provider-switcher.js +71 -0
- package/dist/provider-switcher.js.map +1 -0
- package/dist/react/devtools-provider.cjs +59 -0
- package/dist/react/devtools-provider.cjs.map +1 -0
- package/dist/react/devtools-provider.d.cts +39 -0
- package/dist/react/devtools-provider.d.ts +39 -0
- package/dist/react/devtools-provider.js +38 -0
- package/dist/react/devtools-provider.js.map +1 -0
- package/dist/react/use-fps-graph.cjs +49 -0
- package/dist/react/use-fps-graph.cjs.map +1 -0
- package/dist/react/use-fps-graph.d.cts +15 -0
- package/dist/react/use-fps-graph.d.ts +15 -0
- package/dist/react/use-fps-graph.js +25 -0
- package/dist/react/use-fps-graph.js.map +1 -0
- package/dist/react/use-pane-button.cjs +58 -0
- package/dist/react/use-pane-button.cjs.map +1 -0
- package/dist/react/use-pane-button.d.cts +10 -0
- package/dist/react/use-pane-button.d.ts +10 -0
- package/dist/react/use-pane-button.js +34 -0
- package/dist/react/use-pane-button.js.map +1 -0
- package/dist/react/use-pane-folder.cjs +48 -0
- package/dist/react/use-pane-folder.cjs.map +1 -0
- package/dist/react/use-pane-folder.d.cts +15 -0
- package/dist/react/use-pane-folder.d.ts +15 -0
- package/dist/react/use-pane-folder.js +24 -0
- package/dist/react/use-pane-folder.js.map +1 -0
- package/dist/react/use-pane-input.cjs +70 -0
- package/dist/react/use-pane-input.cjs.map +1 -0
- package/dist/react/use-pane-input.d.cts +44 -0
- package/dist/react/use-pane-input.d.ts +44 -0
- package/dist/react/use-pane-input.js +46 -0
- package/dist/react/use-pane-input.js.map +1 -0
- package/dist/react/use-pane-radio-grid.cjs +78 -0
- package/dist/react/use-pane-radio-grid.cjs.map +1 -0
- package/dist/react/use-pane-radio-grid.d.cts +35 -0
- package/dist/react/use-pane-radio-grid.d.ts +35 -0
- package/dist/react/use-pane-radio-grid.js +54 -0
- package/dist/react/use-pane-radio-grid.js.map +1 -0
- package/dist/react/use-pane.cjs +45 -0
- package/dist/react/use-pane.cjs.map +1 -0
- package/dist/react/use-pane.d.cts +40 -0
- package/dist/react/use-pane.d.ts +40 -0
- package/dist/react/use-pane.js +21 -0
- package/dist/react/use-pane.js.map +1 -0
- package/dist/react.cjs +50 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +12 -0
- package/dist/react.d.ts +12 -0
- package/dist/react.js +19 -0
- package/dist/react.js.map +1 -0
- package/dist/registry-view.cjs +371 -0
- package/dist/registry-view.cjs.map +1 -0
- package/dist/registry-view.d.cts +24 -0
- package/dist/registry-view.d.ts +24 -0
- package/dist/registry-view.js +347 -0
- package/dist/registry-view.js.map +1 -0
- package/dist/stats-graph.cjs +297 -0
- package/dist/stats-graph.cjs.map +1 -0
- package/dist/stats-graph.d.cts +40 -0
- package/dist/stats-graph.d.ts +40 -0
- package/dist/stats-graph.js +273 -0
- package/dist/stats-graph.js.map +1 -0
- package/dist/stats-row.cjs +153 -0
- package/dist/stats-row.cjs.map +1 -0
- package/dist/stats-row.d.cts +24 -0
- package/dist/stats-row.d.ts +24 -0
- package/dist/stats-row.js +129 -0
- package/dist/stats-row.js.map +1 -0
- package/dist/theme.cjs +260 -0
- package/dist/theme.cjs.map +1 -0
- package/dist/theme.d.cts +10 -0
- package/dist/theme.d.ts +10 -0
- package/dist/theme.js +235 -0
- package/dist/theme.js.map +1 -0
- package/dist/vite-plugin.cjs +113 -0
- package/dist/vite-plugin.cjs.map +1 -0
- package/dist/vite-plugin.d.cts +9 -0
- package/dist/vite-plugin.d.ts +9 -0
- package/dist/vite-plugin.js +88 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +124 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const ICON_ATTRS = 'viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" style="display:block;width:calc(1em - 1px);height:calc(1em - 1px)"';
|
|
2
|
+
const ICONS = {
|
|
3
|
+
// Pencil — diagonal parallelogram body with a ferrule band near the eraser
|
|
4
|
+
draws: `<svg ${ICON_ATTRS}><path d="M1 13 L3 13 L13 3 L11 1 L1 11 Z"/><path d="M9 3 L11 5"/></svg>`,
|
|
5
|
+
// Isoceles triangle stretched to fill the 12×12 box
|
|
6
|
+
tris: `<svg ${ICON_ATTRS}><path d="M7 1 L13 13 L1 13 Z"/></svg>`,
|
|
7
|
+
// Square (aggregated lines + points)
|
|
8
|
+
prims: `<svg ${ICON_ATTRS}><path d="M1 1 L13 1 L13 13 L1 13 Z"/></svg>`,
|
|
9
|
+
// Regular-ish pentagon (dodecahedron silhouette), y-scaled to fill the box
|
|
10
|
+
geoms: `<svg ${ICON_ATTRS}><path d="M7 1 L13 5.6 L10.7 13 L3.3 13 L1 5.6 Z"/></svg>`,
|
|
11
|
+
// 3×3 dot grid (filled — swap stroke→fill). Centers spread so outer dot
|
|
12
|
+
// edges touch the same (1,1)–(13,13) box as the other icons.
|
|
13
|
+
textures: `<svg viewBox="0 0 14 14" fill="currentColor" style="display:block;width:calc(1em - 1px);height:calc(1em - 1px)"><circle cx="2.1" cy="2.1" r="1.1"/><circle cx="7" cy="2.1" r="1.1"/><circle cx="11.9" cy="2.1" r="1.1"/><circle cx="2.1" cy="7" r="1.1"/><circle cx="7" cy="7" r="1.1"/><circle cx="11.9" cy="7" r="1.1"/><circle cx="2.1" cy="11.9" r="1.1"/><circle cx="7" cy="11.9" r="1.1"/><circle cx="11.9" cy="11.9" r="1.1"/></svg>`
|
|
14
|
+
};
|
|
15
|
+
const TOOLTIPS = {
|
|
16
|
+
draws: "Draw Calls",
|
|
17
|
+
tris: "Triangles",
|
|
18
|
+
prims: "Primitives (lines + points)",
|
|
19
|
+
geoms: "Geometries",
|
|
20
|
+
textures: "Textures"
|
|
21
|
+
};
|
|
22
|
+
const COMPACT_UNITS = ["K", "M", "B", "T"];
|
|
23
|
+
const NBSP = "\xA0";
|
|
24
|
+
function pad4(s) {
|
|
25
|
+
return s.length >= 4 ? s : s + NBSP.repeat(4 - s.length);
|
|
26
|
+
}
|
|
27
|
+
function formatCompact(n) {
|
|
28
|
+
if (!Number.isFinite(n) || n < 0) return pad4("0");
|
|
29
|
+
if (n < 1e3) return pad4(String(Math.round(n)));
|
|
30
|
+
let unit = 0;
|
|
31
|
+
let val = n / 1e3;
|
|
32
|
+
while (val >= 1e3 && unit < COMPACT_UNITS.length - 1) {
|
|
33
|
+
val /= 1e3;
|
|
34
|
+
unit++;
|
|
35
|
+
}
|
|
36
|
+
if (val < 9.95) return pad4(val.toFixed(1) + COMPACT_UNITS[unit]);
|
|
37
|
+
const rounded = Math.round(val);
|
|
38
|
+
if (rounded >= 1e3 && unit < COMPACT_UNITS.length - 1) {
|
|
39
|
+
return pad4("1.0" + COMPACT_UNITS[unit + 1]);
|
|
40
|
+
}
|
|
41
|
+
return pad4(String(rounded) + COMPACT_UNITS[unit]);
|
|
42
|
+
}
|
|
43
|
+
const STYLE_MARKER = "data-tp-flatland-statsrow";
|
|
44
|
+
const CELL_CLASS = "tp-flatland-statsrow-cell";
|
|
45
|
+
function injectStyles() {
|
|
46
|
+
if (document.querySelector(`style[${STYLE_MARKER}]`)) return;
|
|
47
|
+
const style = document.createElement("style");
|
|
48
|
+
style.setAttribute(STYLE_MARKER, "");
|
|
49
|
+
style.textContent = `
|
|
50
|
+
.${CELL_CLASS} { position: relative; }
|
|
51
|
+
.${CELL_CLASS} + .${CELL_CLASS}::before {
|
|
52
|
+
content: '';
|
|
53
|
+
position: absolute;
|
|
54
|
+
left: 0;
|
|
55
|
+
top: 3px;
|
|
56
|
+
bottom: 3px;
|
|
57
|
+
width: 1px;
|
|
58
|
+
background: var(--tp-groove-foreground-color, rgba(240, 237, 216, 0.12));
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
document.head.appendChild(style);
|
|
63
|
+
}
|
|
64
|
+
function addStatsRow(parent, client) {
|
|
65
|
+
injectStyles();
|
|
66
|
+
const blade = parent.addBlade({ view: "separator" });
|
|
67
|
+
const bladeEl = blade.element;
|
|
68
|
+
bladeEl.innerHTML = "";
|
|
69
|
+
bladeEl.className = "tp-cntv";
|
|
70
|
+
bladeEl.style.cssText = "";
|
|
71
|
+
const row = document.createElement("div");
|
|
72
|
+
row.style.cssText = [
|
|
73
|
+
// Five equal columns spanning the full blade width. Dividers between
|
|
74
|
+
// cells are drawn by a `::before` pseudo-element on every cell after
|
|
75
|
+
// the first (see injectStyles). Row padding is intentionally tight
|
|
76
|
+
// (4px) because each column has only ~50px to fit a 4-char value plus
|
|
77
|
+
// an icon — shaving 2px per side here gives the triangles cell enough
|
|
78
|
+
// headroom for "999K" / "1.2M" without clipping.
|
|
79
|
+
"display:grid",
|
|
80
|
+
"grid-template-columns:repeat(5,1fr)",
|
|
81
|
+
"align-items:center",
|
|
82
|
+
"height:calc(var(--cnt-usz, 20px) * 1.1)",
|
|
83
|
+
"padding:0 4px",
|
|
84
|
+
"font-size:12px",
|
|
85
|
+
"line-height:1",
|
|
86
|
+
"font-family:var(--tp-base-font-family, ui-monospace, monospace)",
|
|
87
|
+
"font-variant-numeric:tabular-nums",
|
|
88
|
+
"color:var(--tp-monitor-foreground-color)"
|
|
89
|
+
].join(";");
|
|
90
|
+
const keys = ["draws", "textures", "geoms", "prims", "tris"];
|
|
91
|
+
const valueEls = {};
|
|
92
|
+
for (const key of keys) {
|
|
93
|
+
const cell = document.createElement("div");
|
|
94
|
+
cell.className = CELL_CLASS;
|
|
95
|
+
cell.title = TOOLTIPS[key];
|
|
96
|
+
cell.style.cssText = "display:flex;align-items:center;gap:3px;padding-left:6px;overflow:hidden;white-space:nowrap;user-select:none;-webkit-user-select:none;-moz-user-select:none";
|
|
97
|
+
const iconWrap = document.createElement("span");
|
|
98
|
+
iconWrap.title = TOOLTIPS[key];
|
|
99
|
+
iconWrap.style.cssText = "display:inline-flex;flex-shrink:0;color:var(--tp-label-foreground-color);align-self:flex-end;transform:translateY(-1px)";
|
|
100
|
+
iconWrap.innerHTML = ICONS[key];
|
|
101
|
+
cell.appendChild(iconWrap);
|
|
102
|
+
const valueEl = document.createElement("span");
|
|
103
|
+
valueEl.title = TOOLTIPS[key];
|
|
104
|
+
valueEl.textContent = `0${NBSP}${NBSP}${NBSP}`;
|
|
105
|
+
valueEl.style.cssText = "min-width:4ch;text-align:left;flex-shrink:0";
|
|
106
|
+
cell.appendChild(valueEl);
|
|
107
|
+
row.appendChild(cell);
|
|
108
|
+
valueEls[key] = valueEl;
|
|
109
|
+
}
|
|
110
|
+
bladeEl.appendChild(row);
|
|
111
|
+
const unsubscribe = client.addListener((s) => {
|
|
112
|
+
valueEls.draws.textContent = formatCompact(s.drawCalls ?? 0);
|
|
113
|
+
valueEls.tris.textContent = formatCompact(s.triangles ?? 0);
|
|
114
|
+
valueEls.prims.textContent = formatCompact(s.primitives ?? 0);
|
|
115
|
+
valueEls.geoms.textContent = formatCompact(s.geometries ?? 0);
|
|
116
|
+
valueEls.textures.textContent = formatCompact(s.textures ?? 0);
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
element: bladeEl,
|
|
120
|
+
dispose() {
|
|
121
|
+
unsubscribe();
|
|
122
|
+
blade.dispose();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export {
|
|
127
|
+
addStatsRow
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=stats-row.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stats-row.ts"],"sourcesContent":["/**\n * Compact single-row renderer stats blade.\n *\n * Five equal-width cells separated by dim vertical dividers:\n * `✎ 3 | △ 10K | ◇ 42 | ⌂ 12 | ▦ 3`\n *\n * Each cell shows an outline icon on the left and a left-aligned,\n * compact-formatted number on the right. A native `title=` tooltip on each\n * cell exposes the full field name. Injected via the same \"replace a\n * separator blade's content\" trick that `stats-graph.ts` uses, so the blade\n * still gets a proper slot in the blade rack.\n */\n\nimport type { FolderApi, Pane } from 'tweakpane'\nimport type { DevtoolsClient } from './devtools-client.js'\n\n/** Internal shape of the cells we render — matches the icon + tooltip tables below. */\ninterface StatsRowValues {\n draws: number\n tris: number\n /** Points + lines aggregated — not currently on the bus (reserved for future). */\n prims: number\n geoms: number\n textures: number\n}\n\nexport interface StatsRowHandle {\n readonly element: HTMLElement\n dispose(): void\n}\n\n// ── Icons ────────────────────────────────────────────────────────────────\n// Outline glyphs, stroked with currentColor so they inherit theme vars.\n// Sized to `calc(1em - 1px)` (→ 11px at the row's 12px font-size), which\n// lines up with the digit cap-height without looking too tall. Every path\n// fills a `(1,1)–(13,13)` box inside the 14×14 viewBox so all icons share\n// the same visual footprint.\n\nconst ICON_ATTRS =\n 'viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"display:block;width:calc(1em - 1px);height:calc(1em - 1px)\"'\n\nconst ICONS: Record<keyof Required<StatsRowValues>, string> = {\n // Pencil — diagonal parallelogram body with a ferrule band near the eraser\n draws: `<svg ${ICON_ATTRS}><path d=\"M1 13 L3 13 L13 3 L11 1 L1 11 Z\"/><path d=\"M9 3 L11 5\"/></svg>`,\n // Isoceles triangle stretched to fill the 12×12 box\n tris: `<svg ${ICON_ATTRS}><path d=\"M7 1 L13 13 L1 13 Z\"/></svg>`,\n // Square (aggregated lines + points)\n prims: `<svg ${ICON_ATTRS}><path d=\"M1 1 L13 1 L13 13 L1 13 Z\"/></svg>`,\n // Regular-ish pentagon (dodecahedron silhouette), y-scaled to fill the box\n geoms: `<svg ${ICON_ATTRS}><path d=\"M7 1 L13 5.6 L10.7 13 L3.3 13 L1 5.6 Z\"/></svg>`,\n // 3×3 dot grid (filled — swap stroke→fill). Centers spread so outer dot\n // edges touch the same (1,1)–(13,13) box as the other icons.\n textures: `<svg viewBox=\"0 0 14 14\" fill=\"currentColor\" style=\"display:block;width:calc(1em - 1px);height:calc(1em - 1px)\"><circle cx=\"2.1\" cy=\"2.1\" r=\"1.1\"/><circle cx=\"7\" cy=\"2.1\" r=\"1.1\"/><circle cx=\"11.9\" cy=\"2.1\" r=\"1.1\"/><circle cx=\"2.1\" cy=\"7\" r=\"1.1\"/><circle cx=\"7\" cy=\"7\" r=\"1.1\"/><circle cx=\"11.9\" cy=\"7\" r=\"1.1\"/><circle cx=\"2.1\" cy=\"11.9\" r=\"1.1\"/><circle cx=\"7\" cy=\"11.9\" r=\"1.1\"/><circle cx=\"11.9\" cy=\"11.9\" r=\"1.1\"/></svg>`,\n}\n\nconst TOOLTIPS: Record<keyof Required<StatsRowValues>, string> = {\n draws: 'Draw Calls',\n tris: 'Triangles',\n prims: 'Primitives (lines + points)',\n geoms: 'Geometries',\n textures: 'Textures',\n}\n\n// ── Compact number formatting ────────────────────────────────────────────\n// Always produces exactly 4 characters (short values are right-padded with\n// non-breaking spaces so every cell renders a stable 4-char slot). Auto-\n// scales through K / M / B / T suffixes so triangle counts (the widest-\n// ranging stat — can hit hundreds of millions) stay legible without\n// widening the row.\n\nconst COMPACT_UNITS = ['K', 'M', 'B', 'T']\nconst NBSP = '\\u00A0'\n\nfunction pad4(s: string): string {\n return s.length >= 4 ? s : s + NBSP.repeat(4 - s.length)\n}\n\nfunction formatCompact(n: number): string {\n if (!Number.isFinite(n) || n < 0) return pad4('0')\n if (n < 1000) return pad4(String(Math.round(n)))\n\n let unit = 0\n let val = n / 1000\n while (val >= 1000 && unit < COMPACT_UNITS.length - 1) {\n val /= 1000\n unit++\n }\n\n // val ∈ [1, 1000). Under 10 shows one decimal (\"1.2K\" / \"9.9K\"); 10+\n // rounds to integer (\"10K\" / \"999K\"). If the integer would overflow\n // into the next magnitude (e.g. 999.5 → 1000), promote one unit as\n // \"1.0X\" so the output never grows beyond 4 characters.\n if (val < 9.95) return pad4(val.toFixed(1) + COMPACT_UNITS[unit])\n const rounded = Math.round(val)\n if (rounded >= 1000 && unit < COMPACT_UNITS.length - 1) {\n return pad4('1.0' + COMPACT_UNITS[unit + 1])\n }\n return pad4(String(rounded) + COMPACT_UNITS[unit])\n}\n\n// ── One-time <style> injection for divider pseudo-elements ───────────────\n// Pseudo-elements can't be set via inline style attributes, so we inject a\n// single stylesheet the first time `addStatsRow` runs. Uses a `data-*`\n// marker so repeat calls are idempotent.\n\nconst STYLE_MARKER = 'data-tp-flatland-statsrow'\nconst CELL_CLASS = 'tp-flatland-statsrow-cell'\n\nfunction injectStyles(): void {\n if (document.querySelector(`style[${STYLE_MARKER}]`)) return\n const style = document.createElement('style')\n style.setAttribute(STYLE_MARKER, '')\n style.textContent = `\n .${CELL_CLASS} { position: relative; }\n .${CELL_CLASS} + .${CELL_CLASS}::before {\n content: '';\n position: absolute;\n left: 0;\n top: 3px;\n bottom: 3px;\n width: 1px;\n background: var(--tp-groove-foreground-color, rgba(240, 237, 216, 0.12));\n pointer-events: none;\n }\n `\n document.head.appendChild(style)\n}\n\n// ── Blade construction ──────────────────────────────────────────────────\n\nexport function addStatsRow(parent: Pane | FolderApi, client: DevtoolsClient): StatsRowHandle {\n injectStyles()\n\n // Use a separator blade as a slot so ordering plays nicely with the rack,\n // then nuke its content and drop in our own layout.\n const blade = parent.addBlade({ view: 'separator' }) as unknown as {\n element: HTMLElement\n dispose(): void\n }\n const bladeEl = blade.element\n bladeEl.innerHTML = ''\n bladeEl.className = 'tp-cntv'\n bladeEl.style.cssText = ''\n\n const row = document.createElement('div')\n row.style.cssText = [\n // Five equal columns spanning the full blade width. Dividers between\n // cells are drawn by a `::before` pseudo-element on every cell after\n // the first (see injectStyles). Row padding is intentionally tight\n // (4px) because each column has only ~50px to fit a 4-char value plus\n // an icon — shaving 2px per side here gives the triangles cell enough\n // headroom for \"999K\" / \"1.2M\" without clipping.\n 'display:grid',\n 'grid-template-columns:repeat(5,1fr)',\n 'align-items:center',\n 'height:calc(var(--cnt-usz, 20px) * 1.1)',\n 'padding:0 4px',\n 'font-size:12px',\n 'line-height:1',\n 'font-family:var(--tp-base-font-family, ui-monospace, monospace)',\n 'font-variant-numeric:tabular-nums',\n 'color:var(--tp-monitor-foreground-color)',\n ].join(';')\n\n const keys = ['draws', 'textures', 'geoms', 'prims', 'tris'] as const\n const valueEls = {} as Record<(typeof keys)[number], HTMLSpanElement>\n\n for (const key of keys) {\n const cell = document.createElement('div')\n cell.className = CELL_CLASS\n cell.title = TOOLTIPS[key]\n // Left-aligned icon + number. `padding-left: 6px` leaves breathing room\n // between the divider line and the icon; `gap: 3px` keeps the icon tight\n // against the number. Both are tuned to fit a 4-char value in the\n // narrowest cell (~49px) without clipping. `user-select: none` (plus\n // vendor prefixes for Safari/Firefox) so stats aren't accidentally\n // selectable or copyable — they're a readout, not content.\n cell.style.cssText =\n 'display:flex;align-items:center;gap:3px;padding-left:6px;overflow:hidden;white-space:nowrap;user-select:none;-webkit-user-select:none;-moz-user-select:none'\n\n const iconWrap = document.createElement('span')\n // Tooltip also attached here so hovering the icon (not just the number)\n // surfaces the field name. Bottom-aligned + shifted up 1px pins the\n // icon bottom to the digit baseline.\n iconWrap.title = TOOLTIPS[key]\n iconWrap.style.cssText =\n 'display:inline-flex;flex-shrink:0;color:var(--tp-label-foreground-color);align-self:flex-end;transform:translateY(-1px)'\n iconWrap.innerHTML = ICONS[key]\n cell.appendChild(iconWrap)\n\n // Value slot reserves 4 chars of width — `formatCompact` always emits\n // exactly 4 chars (padded with NBSP) so the icon stays planted. Title\n // mirrored here so the tooltip shows over the number too.\n const valueEl = document.createElement('span')\n valueEl.title = TOOLTIPS[key]\n valueEl.textContent = `0${NBSP}${NBSP}${NBSP}`\n valueEl.style.cssText = 'min-width:4ch;text-align:left;flex-shrink:0'\n cell.appendChild(valueEl)\n\n row.appendChild(cell)\n valueEls[key] = valueEl\n }\n\n bladeEl.appendChild(row)\n\n // Bus-driven: the client holds the full live state. On every update\n // we just render the current values — setting textContent to the\n // same string is a no-op, so diffing locally is unnecessary.\n const unsubscribe = client.addListener((s) => {\n valueEls.draws.textContent = formatCompact(s.drawCalls ?? 0)\n valueEls.tris.textContent = formatCompact(s.triangles ?? 0)\n valueEls.prims.textContent = formatCompact(s.primitives ?? 0)\n valueEls.geoms.textContent = formatCompact(s.geometries ?? 0)\n valueEls.textures.textContent = formatCompact(s.textures ?? 0)\n })\n\n return {\n element: bladeEl,\n dispose() {\n unsubscribe()\n blade.dispose()\n },\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/theme.cjs
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var theme_exports = {};
|
|
20
|
+
__export(theme_exports, {
|
|
21
|
+
FLATLAND_THEME: () => FLATLAND_THEME,
|
|
22
|
+
applyTheme: () => applyTheme
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(theme_exports);
|
|
25
|
+
const FLATLAND_THEME = {
|
|
26
|
+
// Backgrounds — retro darks
|
|
27
|
+
"--tp-base-background-color": "#00021c",
|
|
28
|
+
// retro-midnight
|
|
29
|
+
"--tp-container-background-color": "rgba(28, 40, 77, 0.4)",
|
|
30
|
+
// retro-deep-purple @ 40%
|
|
31
|
+
"--tp-input-background-color": "rgba(0, 2, 28, 0.6)",
|
|
32
|
+
// retro-black @ 60%
|
|
33
|
+
"--tp-monitor-background-color": "rgba(0, 2, 28, 0.4)",
|
|
34
|
+
// retro-black @ 40%
|
|
35
|
+
// Text — retro whites and greys
|
|
36
|
+
"--tp-container-foreground-color": "#f0edd8",
|
|
37
|
+
// retro-white
|
|
38
|
+
"--tp-button-foreground-color": "#f0edd8",
|
|
39
|
+
// retro-white
|
|
40
|
+
"--tp-input-foreground-color": "#c8c5b4",
|
|
41
|
+
// muted retro-white
|
|
42
|
+
"--tp-label-foreground-color": "rgba(240, 237, 216, 0.55)",
|
|
43
|
+
// retro-white @ 55%
|
|
44
|
+
"--tp-monitor-foreground-color": "#c8c5b4",
|
|
45
|
+
// muted retro-white
|
|
46
|
+
// Buttons — retro-deep-purple
|
|
47
|
+
"--tp-button-background-color": "#1c284d",
|
|
48
|
+
// retro-deep-purple
|
|
49
|
+
// Accent — slider grooves & separators
|
|
50
|
+
"--tp-groove-foreground-color": "rgba(240, 237, 216, 0.12)",
|
|
51
|
+
// muted separator/groove
|
|
52
|
+
// Typography
|
|
53
|
+
"--tp-base-font-family": "monospace"
|
|
54
|
+
};
|
|
55
|
+
const ACCENT_CSS = `
|
|
56
|
+
/* Slider thumb */
|
|
57
|
+
.tp-sldv_k::after { background-color: #d94c87 !important; }
|
|
58
|
+
.tp-sldv_t:hover .tp-sldv_k::after { background-color: #e0609a !important; }
|
|
59
|
+
.tp-sldv_t:active .tp-sldv_k::after { background-color: #a6216e !important; }
|
|
60
|
+
/* Slider track (filled portion) */
|
|
61
|
+
.tp-sldv_k::before { background-color: rgba(240, 237, 216, 0.25) !important; }
|
|
62
|
+
/* Number spinner drag handle */
|
|
63
|
+
.tp-txtv_k::before { background-color: #d94c87 !important; }
|
|
64
|
+
/* Select/dropdown */
|
|
65
|
+
.tp-lstv_s { background-color: rgba(28, 40, 77, 0.6) !important; color: rgba(240, 237, 216, 0.5) !important; }
|
|
66
|
+
.tp-lstv_s:hover { background-color: rgba(28, 40, 77, 0.9) !important; color: #f0edd8 !important; }
|
|
67
|
+
.tp-lstv_s:focus { background-color: rgba(28, 40, 77, 0.9) !important; color: #f0edd8 !important; }
|
|
68
|
+
.tp-lstv_s:active { background-color: #343473 !important; color: #d94c87 !important; }
|
|
69
|
+
.tp-lstv_m { color: rgba(240, 237, 216, 0.5) !important; }
|
|
70
|
+
/* Buttons */
|
|
71
|
+
.tp-btnv_b { background-color: rgba(28, 40, 77, 0.6) !important; color: rgba(240, 237, 216, 0.5) !important; }
|
|
72
|
+
.tp-btnv_b:hover { background-color: rgba(28, 40, 77, 0.9) !important; color: #f0edd8 !important; }
|
|
73
|
+
.tp-btnv_b:active { background-color: #343473 !important; color: #d94c87 !important; }
|
|
74
|
+
/* Radio grid buttons */
|
|
75
|
+
.tp-radv_b { background-color: rgba(28, 40, 77, 0.6) !important; color: rgba(240, 237, 216, 0.5) !important; }
|
|
76
|
+
.tp-radv_i:hover + .tp-radv_b { background-color: rgba(28, 40, 77, 0.9) !important; color: #f0edd8 !important; }
|
|
77
|
+
.tp-radv_i:checked + .tp-radv_b { background-color: #1c284d !important; color: #d94c87 !important; }
|
|
78
|
+
.tp-radv_i:active + .tp-radv_b { background-color: #343473 !important; color: #f0edd8 !important; }
|
|
79
|
+
/* Checkbox \u2014 default background blends with the container, so bump to
|
|
80
|
+
the same surface as the select/button controls for a visible hit
|
|
81
|
+
target, and accent the check stroke on :checked. Markup:
|
|
82
|
+
<input.tp-ckbv_i> + <div.tp-ckbv_w><svg><path/></svg></div>
|
|
83
|
+
|
|
84
|
+
Tweakpane's hidden input ships at browser-default size (~13\xD713) at
|
|
85
|
+
top-left of the label, relying on <label>\u2192<input> click forwarding
|
|
86
|
+
to toggle from clicks on the visible box. That forwarding is flaky
|
|
87
|
+
under some pointer-events + z-index combinations (reports: clicks
|
|
88
|
+
sometimes register, sometimes don't). Stretching the input to cover
|
|
89
|
+
the entire visible box puts the hit target directly on the pointer,
|
|
90
|
+
no label forwarding required. */
|
|
91
|
+
.tp-ckbv_i {
|
|
92
|
+
/* Absolute positioning + zero opacity is Tweakpane's default; we
|
|
93
|
+
just add explicit size + cursor so the click area matches the
|
|
94
|
+
visible affordance exactly. */
|
|
95
|
+
width: var(--cnt-usz) !important;
|
|
96
|
+
height: var(--cnt-usz) !important;
|
|
97
|
+
margin: 0 !important;
|
|
98
|
+
cursor: pointer !important;
|
|
99
|
+
}
|
|
100
|
+
.tp-ckbv_w { background-color: rgba(28, 40, 77, 0.6) !important; }
|
|
101
|
+
.tp-ckbv_i:hover + .tp-ckbv_w { background-color: rgba(28, 40, 77, 0.9) !important; }
|
|
102
|
+
.tp-ckbv_i:focus + .tp-ckbv_w { background-color: rgba(28, 40, 77, 0.9) !important; }
|
|
103
|
+
.tp-ckbv_i:active + .tp-ckbv_w { background-color: #343473 !important; }
|
|
104
|
+
.tp-ckbv_i:checked + .tp-ckbv_w svg path { stroke: #d94c87 !important; }
|
|
105
|
+
|
|
106
|
+
/* Dimmable pane \u2014 slightly translucent when idle, opaque on hover or when pinned.
|
|
107
|
+
Hover-promotion is gated on devices that actually have a pointer (desktop),
|
|
108
|
+
so on touch devices the pin toggle is the only way to flip opacity. */
|
|
109
|
+
.tp-flatland-dimmable { opacity: 0.8; transition: opacity 750ms ease; }
|
|
110
|
+
.tp-flatland-dimmable.tp-flatland-pinned { opacity: 1; }
|
|
111
|
+
@media (hover: hover) {
|
|
112
|
+
.tp-flatland-dimmable:hover { opacity: 1; }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Pin in the pane header (left side, mirrors the collapse caret on the right) */
|
|
116
|
+
.tp-flatland-pin {
|
|
117
|
+
position: absolute;
|
|
118
|
+
left: var(--cnt-hp, 4px);
|
|
119
|
+
top: 0;
|
|
120
|
+
bottom: 0;
|
|
121
|
+
margin: auto;
|
|
122
|
+
width: var(--cnt-usz, 20px);
|
|
123
|
+
height: var(--cnt-usz, 20px);
|
|
124
|
+
display: flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
justify-content: center;
|
|
127
|
+
color: rgba(240, 237, 216, 0.45);
|
|
128
|
+
cursor: pointer;
|
|
129
|
+
border-radius: 2px;
|
|
130
|
+
transition: color 150ms, background-color 150ms;
|
|
131
|
+
}
|
|
132
|
+
.tp-flatland-pin:hover { color: #d94c87; background: transparent; }
|
|
133
|
+
.tp-flatland-pin svg { display: block; }
|
|
134
|
+
.tp-flatland-pin svg circle { fill: none; stroke: currentColor; stroke-width: 1.6; }
|
|
135
|
+
.tp-flatland-pinned .tp-flatland-pin svg circle { fill: currentColor; }
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* Three-mode cycle toggle in the header (replaces Tweakpane's built-in
|
|
139
|
+
* fold caret). We hide the native caret via the .tp-rotv_m marker
|
|
140
|
+
* class and render our own button that cycles full - minimal -
|
|
141
|
+
* collapsed - full. The data-mode attribute drives icon styling.
|
|
142
|
+
*/
|
|
143
|
+
.tp-rotv_m { display: none !important; }
|
|
144
|
+
.tp-flatland-mode {
|
|
145
|
+
position: absolute;
|
|
146
|
+
right: var(--cnt-hp, 4px);
|
|
147
|
+
top: 0;
|
|
148
|
+
bottom: 0;
|
|
149
|
+
margin: auto;
|
|
150
|
+
min-width: var(--cnt-usz, 20px);
|
|
151
|
+
height: var(--cnt-usz, 20px);
|
|
152
|
+
padding: 0 4px;
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
justify-content: center;
|
|
156
|
+
/* Match the original tweakpane fold caret and folder-header glyphs \u2014
|
|
157
|
+
* the muted foreground token pane headers use throughout. */
|
|
158
|
+
color: var(--tp-label-foreground-color);
|
|
159
|
+
cursor: pointer;
|
|
160
|
+
font-size: 11px;
|
|
161
|
+
line-height: 1;
|
|
162
|
+
user-select: none;
|
|
163
|
+
}
|
|
164
|
+
/*
|
|
165
|
+
* Glyph exactly replicates tweakpane's native fold marker \u2014 a 6\xD76
|
|
166
|
+
* square with a horizontal linear-gradient that draws two vertical
|
|
167
|
+
* stripes (screw-head look). Rotating gives the three states:
|
|
168
|
+
* full \u2192 0\xB0 (vertical stripes, ||) \u2014 matches tweakpane's expanded
|
|
169
|
+
* minimal \u2192 45\xB0 (diagonal, like a flat-head screw)
|
|
170
|
+
* collapsed \u2192 90\xB0 (horizontal stripes, =) \u2014 matches tweakpane's collapsed
|
|
171
|
+
*/
|
|
172
|
+
.tp-flatland-mode-glyph {
|
|
173
|
+
display: block;
|
|
174
|
+
width: 6px;
|
|
175
|
+
height: 6px;
|
|
176
|
+
border-radius: 2px;
|
|
177
|
+
background: linear-gradient(
|
|
178
|
+
to left,
|
|
179
|
+
var(--cnt-fg, currentColor),
|
|
180
|
+
var(--cnt-fg, currentColor) 2px,
|
|
181
|
+
transparent 2px,
|
|
182
|
+
transparent 4px,
|
|
183
|
+
var(--cnt-fg, currentColor) 4px
|
|
184
|
+
);
|
|
185
|
+
opacity: 0.5;
|
|
186
|
+
/* 0.2s ease-in-out \u2014 the exact transition tweakpane applies to its
|
|
187
|
+
* own fold marker. */
|
|
188
|
+
transition: transform 0.2s ease-in-out;
|
|
189
|
+
}
|
|
190
|
+
.tp-flatland-mode[data-mode="full"] .tp-flatland-mode-glyph { transform: rotate(0deg); }
|
|
191
|
+
.tp-flatland-mode[data-mode="minimal"] .tp-flatland-mode-glyph { transform: rotate(45deg); }
|
|
192
|
+
.tp-flatland-mode[data-mode="collapsed"] .tp-flatland-mode-glyph { transform: rotate(90deg); }
|
|
193
|
+
|
|
194
|
+
/*
|
|
195
|
+
* Minimal mode: keep only blades tagged with .tp-flatland-minimal-keep
|
|
196
|
+
* (stats graph + stats row). Non-keep blades animate max-height to 0
|
|
197
|
+
* and fade out so the whole pane visibly shrinks, matching tweakpane
|
|
198
|
+
* native fold feel. Values and bindings stay intact \u2014 DOM is just
|
|
199
|
+
* clipped.
|
|
200
|
+
*
|
|
201
|
+
* max-height uses a generous ceiling instead of a measured value so we
|
|
202
|
+
* can stay declarative; the transition duration matches the native
|
|
203
|
+
* pane fold for continuity.
|
|
204
|
+
*/
|
|
205
|
+
/*
|
|
206
|
+
* Animation timings + easings mirror tweakpane's own fold exactly:
|
|
207
|
+
* .tp-rotv_c / .tp-fldv_c use
|
|
208
|
+
* height .2s ease-in-out, opacity .2s linear, padding .2s ease-in-out
|
|
209
|
+
* and on expand, opacity has a 0.2s delay so height finishes first
|
|
210
|
+
* then opacity fades in. The collapse direction has no delay \u2014 both
|
|
211
|
+
* animate together.
|
|
212
|
+
*
|
|
213
|
+
* We use max-height in place of height because content height isn't
|
|
214
|
+
* known at author time; with a generous ceiling the visual is
|
|
215
|
+
* indistinguishable from native height tweens.
|
|
216
|
+
*/
|
|
217
|
+
.tp-rotv_c > *:not(.tp-flatland-minimal-keep) {
|
|
218
|
+
max-height: 1200px;
|
|
219
|
+
opacity: 1;
|
|
220
|
+
overflow: hidden;
|
|
221
|
+
transition:
|
|
222
|
+
max-height 0.2s ease-in-out,
|
|
223
|
+
opacity 0.2s linear 0.2s,
|
|
224
|
+
margin 0.2s ease-in-out,
|
|
225
|
+
padding 0.2s ease-in-out;
|
|
226
|
+
}
|
|
227
|
+
.tp-flatland-minimal .tp-rotv_c > *:not(.tp-flatland-minimal-keep) {
|
|
228
|
+
max-height: 0;
|
|
229
|
+
opacity: 0;
|
|
230
|
+
margin-top: 0 !important;
|
|
231
|
+
margin-bottom: 0 !important;
|
|
232
|
+
padding-top: 0 !important;
|
|
233
|
+
padding-bottom: 0 !important;
|
|
234
|
+
pointer-events: none;
|
|
235
|
+
/* On collapse: no delay on opacity \u2014 both animate together,
|
|
236
|
+
* matching tweakpane's non-expanded transition shape. */
|
|
237
|
+
transition:
|
|
238
|
+
max-height 0.2s ease-in-out,
|
|
239
|
+
opacity 0.2s linear,
|
|
240
|
+
margin 0.2s ease-in-out,
|
|
241
|
+
padding 0.2s ease-in-out;
|
|
242
|
+
}
|
|
243
|
+
`;
|
|
244
|
+
function applyTheme(element, theme = FLATLAND_THEME) {
|
|
245
|
+
for (const [key, value] of Object.entries(theme)) {
|
|
246
|
+
element.style.setProperty(key, value);
|
|
247
|
+
}
|
|
248
|
+
if (!element.querySelector("style[data-flatland]")) {
|
|
249
|
+
const style = document.createElement("style");
|
|
250
|
+
style.setAttribute("data-flatland", "");
|
|
251
|
+
style.textContent = ACCENT_CSS;
|
|
252
|
+
element.appendChild(style);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
256
|
+
0 && (module.exports = {
|
|
257
|
+
FLATLAND_THEME,
|
|
258
|
+
applyTheme
|
|
259
|
+
});
|
|
260
|
+
//# sourceMappingURL=theme.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/theme.ts"],"sourcesContent":["/**\n * Flatland theme — based on the Jehkoba32 retro palette.\n *\n * Uses project dark/neutral colors for backgrounds, retro-white/grey for text,\n * and accent color only on control thumbs and slider grooves.\n */\nexport const FLATLAND_THEME: Record<string, string> = {\n // Backgrounds — retro darks\n '--tp-base-background-color': '#00021c', // retro-midnight\n '--tp-container-background-color': 'rgba(28, 40, 77, 0.4)', // retro-deep-purple @ 40%\n '--tp-input-background-color': 'rgba(0, 2, 28, 0.6)', // retro-black @ 60%\n '--tp-monitor-background-color': 'rgba(0, 2, 28, 0.4)', // retro-black @ 40%\n\n // Text — retro whites and greys\n '--tp-container-foreground-color': '#f0edd8', // retro-white\n '--tp-button-foreground-color': '#f0edd8', // retro-white\n '--tp-input-foreground-color': '#c8c5b4', // muted retro-white\n '--tp-label-foreground-color': 'rgba(240, 237, 216, 0.55)', // retro-white @ 55%\n '--tp-monitor-foreground-color': '#c8c5b4', // muted retro-white\n\n // Buttons — retro-deep-purple\n '--tp-button-background-color': '#1c284d', // retro-deep-purple\n\n // Accent — slider grooves & separators\n '--tp-groove-foreground-color': 'rgba(240, 237, 216, 0.12)', // muted separator/groove\n\n // Typography\n '--tp-base-font-family': 'monospace',\n}\n\n/** Accent CSS — colorizes slider thumb and number spinner drag handle */\nconst ACCENT_CSS = `\n/* Slider thumb */\n.tp-sldv_k::after { background-color: #d94c87 !important; }\n.tp-sldv_t:hover .tp-sldv_k::after { background-color: #e0609a !important; }\n.tp-sldv_t:active .tp-sldv_k::after { background-color: #a6216e !important; }\n/* Slider track (filled portion) */\n.tp-sldv_k::before { background-color: rgba(240, 237, 216, 0.25) !important; }\n/* Number spinner drag handle */\n.tp-txtv_k::before { background-color: #d94c87 !important; }\n/* Select/dropdown */\n.tp-lstv_s { background-color: rgba(28, 40, 77, 0.6) !important; color: rgba(240, 237, 216, 0.5) !important; }\n.tp-lstv_s:hover { background-color: rgba(28, 40, 77, 0.9) !important; color: #f0edd8 !important; }\n.tp-lstv_s:focus { background-color: rgba(28, 40, 77, 0.9) !important; color: #f0edd8 !important; }\n.tp-lstv_s:active { background-color: #343473 !important; color: #d94c87 !important; }\n.tp-lstv_m { color: rgba(240, 237, 216, 0.5) !important; }\n/* Buttons */\n.tp-btnv_b { background-color: rgba(28, 40, 77, 0.6) !important; color: rgba(240, 237, 216, 0.5) !important; }\n.tp-btnv_b:hover { background-color: rgba(28, 40, 77, 0.9) !important; color: #f0edd8 !important; }\n.tp-btnv_b:active { background-color: #343473 !important; color: #d94c87 !important; }\n/* Radio grid buttons */\n.tp-radv_b { background-color: rgba(28, 40, 77, 0.6) !important; color: rgba(240, 237, 216, 0.5) !important; }\n.tp-radv_i:hover + .tp-radv_b { background-color: rgba(28, 40, 77, 0.9) !important; color: #f0edd8 !important; }\n.tp-radv_i:checked + .tp-radv_b { background-color: #1c284d !important; color: #d94c87 !important; }\n.tp-radv_i:active + .tp-radv_b { background-color: #343473 !important; color: #f0edd8 !important; }\n/* Checkbox — default background blends with the container, so bump to\n the same surface as the select/button controls for a visible hit\n target, and accent the check stroke on :checked. Markup:\n <input.tp-ckbv_i> + <div.tp-ckbv_w><svg><path/></svg></div>\n\n Tweakpane's hidden input ships at browser-default size (~13×13) at\n top-left of the label, relying on <label>→<input> click forwarding\n to toggle from clicks on the visible box. That forwarding is flaky\n under some pointer-events + z-index combinations (reports: clicks\n sometimes register, sometimes don't). Stretching the input to cover\n the entire visible box puts the hit target directly on the pointer,\n no label forwarding required. */\n.tp-ckbv_i {\n /* Absolute positioning + zero opacity is Tweakpane's default; we\n just add explicit size + cursor so the click area matches the\n visible affordance exactly. */\n width: var(--cnt-usz) !important;\n height: var(--cnt-usz) !important;\n margin: 0 !important;\n cursor: pointer !important;\n}\n.tp-ckbv_w { background-color: rgba(28, 40, 77, 0.6) !important; }\n.tp-ckbv_i:hover + .tp-ckbv_w { background-color: rgba(28, 40, 77, 0.9) !important; }\n.tp-ckbv_i:focus + .tp-ckbv_w { background-color: rgba(28, 40, 77, 0.9) !important; }\n.tp-ckbv_i:active + .tp-ckbv_w { background-color: #343473 !important; }\n.tp-ckbv_i:checked + .tp-ckbv_w svg path { stroke: #d94c87 !important; }\n\n/* Dimmable pane — slightly translucent when idle, opaque on hover or when pinned.\n Hover-promotion is gated on devices that actually have a pointer (desktop),\n so on touch devices the pin toggle is the only way to flip opacity. */\n.tp-flatland-dimmable { opacity: 0.8; transition: opacity 750ms ease; }\n.tp-flatland-dimmable.tp-flatland-pinned { opacity: 1; }\n@media (hover: hover) {\n .tp-flatland-dimmable:hover { opacity: 1; }\n}\n\n/* Pin in the pane header (left side, mirrors the collapse caret on the right) */\n.tp-flatland-pin {\n position: absolute;\n left: var(--cnt-hp, 4px);\n top: 0;\n bottom: 0;\n margin: auto;\n width: var(--cnt-usz, 20px);\n height: var(--cnt-usz, 20px);\n display: flex;\n align-items: center;\n justify-content: center;\n color: rgba(240, 237, 216, 0.45);\n cursor: pointer;\n border-radius: 2px;\n transition: color 150ms, background-color 150ms;\n}\n.tp-flatland-pin:hover { color: #d94c87; background: transparent; }\n.tp-flatland-pin svg { display: block; }\n.tp-flatland-pin svg circle { fill: none; stroke: currentColor; stroke-width: 1.6; }\n.tp-flatland-pinned .tp-flatland-pin svg circle { fill: currentColor; }\n\n/*\n * Three-mode cycle toggle in the header (replaces Tweakpane's built-in\n * fold caret). We hide the native caret via the .tp-rotv_m marker\n * class and render our own button that cycles full - minimal -\n * collapsed - full. The data-mode attribute drives icon styling.\n */\n.tp-rotv_m { display: none !important; }\n.tp-flatland-mode {\n position: absolute;\n right: var(--cnt-hp, 4px);\n top: 0;\n bottom: 0;\n margin: auto;\n min-width: var(--cnt-usz, 20px);\n height: var(--cnt-usz, 20px);\n padding: 0 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n /* Match the original tweakpane fold caret and folder-header glyphs —\n * the muted foreground token pane headers use throughout. */\n color: var(--tp-label-foreground-color);\n cursor: pointer;\n font-size: 11px;\n line-height: 1;\n user-select: none;\n}\n/*\n * Glyph exactly replicates tweakpane's native fold marker — a 6×6\n * square with a horizontal linear-gradient that draws two vertical\n * stripes (screw-head look). Rotating gives the three states:\n * full → 0° (vertical stripes, ||) — matches tweakpane's expanded\n * minimal → 45° (diagonal, like a flat-head screw)\n * collapsed → 90° (horizontal stripes, =) — matches tweakpane's collapsed\n */\n.tp-flatland-mode-glyph {\n display: block;\n width: 6px;\n height: 6px;\n border-radius: 2px;\n background: linear-gradient(\n to left,\n var(--cnt-fg, currentColor),\n var(--cnt-fg, currentColor) 2px,\n transparent 2px,\n transparent 4px,\n var(--cnt-fg, currentColor) 4px\n );\n opacity: 0.5;\n /* 0.2s ease-in-out — the exact transition tweakpane applies to its\n * own fold marker. */\n transition: transform 0.2s ease-in-out;\n}\n.tp-flatland-mode[data-mode=\"full\"] .tp-flatland-mode-glyph { transform: rotate(0deg); }\n.tp-flatland-mode[data-mode=\"minimal\"] .tp-flatland-mode-glyph { transform: rotate(45deg); }\n.tp-flatland-mode[data-mode=\"collapsed\"] .tp-flatland-mode-glyph { transform: rotate(90deg); }\n\n/*\n * Minimal mode: keep only blades tagged with .tp-flatland-minimal-keep\n * (stats graph + stats row). Non-keep blades animate max-height to 0\n * and fade out so the whole pane visibly shrinks, matching tweakpane\n * native fold feel. Values and bindings stay intact — DOM is just\n * clipped.\n *\n * max-height uses a generous ceiling instead of a measured value so we\n * can stay declarative; the transition duration matches the native\n * pane fold for continuity.\n */\n/*\n * Animation timings + easings mirror tweakpane's own fold exactly:\n * .tp-rotv_c / .tp-fldv_c use\n * height .2s ease-in-out, opacity .2s linear, padding .2s ease-in-out\n * and on expand, opacity has a 0.2s delay so height finishes first\n * then opacity fades in. The collapse direction has no delay — both\n * animate together.\n *\n * We use max-height in place of height because content height isn't\n * known at author time; with a generous ceiling the visual is\n * indistinguishable from native height tweens.\n */\n.tp-rotv_c > *:not(.tp-flatland-minimal-keep) {\n max-height: 1200px;\n opacity: 1;\n overflow: hidden;\n transition:\n max-height 0.2s ease-in-out,\n opacity 0.2s linear 0.2s,\n margin 0.2s ease-in-out,\n padding 0.2s ease-in-out;\n}\n.tp-flatland-minimal .tp-rotv_c > *:not(.tp-flatland-minimal-keep) {\n max-height: 0;\n opacity: 0;\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n pointer-events: none;\n /* On collapse: no delay on opacity — both animate together,\n * matching tweakpane's non-expanded transition shape. */\n transition:\n max-height 0.2s ease-in-out,\n opacity 0.2s linear,\n margin 0.2s ease-in-out,\n padding 0.2s ease-in-out;\n}\n`\n\nexport function applyTheme(\n element: HTMLElement,\n theme: Record<string, string> = FLATLAND_THEME,\n): void {\n for (const [key, value] of Object.entries(theme)) {\n element.style.setProperty(key, value)\n }\n\n // Inject accent styles once\n if (!element.querySelector('style[data-flatland]')) {\n const style = document.createElement('style')\n style.setAttribute('data-flatland', '')\n style.textContent = ACCENT_CSS\n element.appendChild(style)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,MAAM,iBAAyC;AAAA;AAAA,EAEpD,8BAA8B;AAAA;AAAA,EAC9B,mCAAmC;AAAA;AAAA,EACnC,+BAA+B;AAAA;AAAA,EAC/B,iCAAiC;AAAA;AAAA;AAAA,EAGjC,mCAAmC;AAAA;AAAA,EACnC,gCAAgC;AAAA;AAAA,EAChC,+BAA+B;AAAA;AAAA,EAC/B,+BAA+B;AAAA;AAAA,EAC/B,iCAAiC;AAAA;AAAA;AAAA,EAGjC,gCAAgC;AAAA;AAAA;AAAA,EAGhC,gCAAgC;AAAA;AAAA;AAAA,EAGhC,yBAAyB;AAC3B;AAGA,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8LZ,SAAS,WACd,SACA,QAAgC,gBAC1B;AACN,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,YAAQ,MAAM,YAAY,KAAK,KAAK;AAAA,EACtC;AAGA,MAAI,CAAC,QAAQ,cAAc,sBAAsB,GAAG;AAClD,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,aAAa,iBAAiB,EAAE;AACtC,UAAM,cAAc;AACpB,YAAQ,YAAY,KAAK;AAAA,EAC3B;AACF;","names":[]}
|
package/dist/theme.d.cts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatland theme — based on the Jehkoba32 retro palette.
|
|
3
|
+
*
|
|
4
|
+
* Uses project dark/neutral colors for backgrounds, retro-white/grey for text,
|
|
5
|
+
* and accent color only on control thumbs and slider grooves.
|
|
6
|
+
*/
|
|
7
|
+
declare const FLATLAND_THEME: Record<string, string>;
|
|
8
|
+
declare function applyTheme(element: HTMLElement, theme?: Record<string, string>): void;
|
|
9
|
+
|
|
10
|
+
export { FLATLAND_THEME, applyTheme };
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatland theme — based on the Jehkoba32 retro palette.
|
|
3
|
+
*
|
|
4
|
+
* Uses project dark/neutral colors for backgrounds, retro-white/grey for text,
|
|
5
|
+
* and accent color only on control thumbs and slider grooves.
|
|
6
|
+
*/
|
|
7
|
+
declare const FLATLAND_THEME: Record<string, string>;
|
|
8
|
+
declare function applyTheme(element: HTMLElement, theme?: Record<string, string>): void;
|
|
9
|
+
|
|
10
|
+
export { FLATLAND_THEME, applyTheme };
|