@three-flatland/devtools 0.1.0-alpha.3 → 1.0.0-alpha.6

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.
Files changed (172) hide show
  1. package/README.md +1 -1
  2. package/dist/buffers-modal.d.ts +16 -52
  3. package/dist/buffers-modal.d.ts.map +1 -0
  4. package/dist/buffers-modal.js +459 -464
  5. package/dist/buffers-modal.js.map +1 -1
  6. package/dist/buffers-view.d.ts +22 -34
  7. package/dist/buffers-view.d.ts.map +1 -0
  8. package/dist/buffers-view.js +252 -240
  9. package/dist/buffers-view.js.map +1 -1
  10. package/dist/create-pane.d.ts +38 -37
  11. package/dist/create-pane.d.ts.map +1 -0
  12. package/dist/create-pane.js +197 -166
  13. package/dist/create-pane.js.map +1 -1
  14. package/dist/dashboard/app.tsx +2 -0
  15. package/dist/dashboard/export.ts +1 -3
  16. package/dist/dashboard/frame-cursor.ts +81 -0
  17. package/dist/dashboard/hooks.ts +10 -2
  18. package/dist/dashboard/index.html +61 -0
  19. package/dist/dashboard/panels/batches.tsx +5 -18
  20. package/dist/dashboard/panels/buffers.tsx +52 -26
  21. package/dist/dashboard/panels/env.tsx +5 -6
  22. package/dist/dashboard/panels/protocol-log.tsx +146 -58
  23. package/dist/dashboard/panels/registry.tsx +55 -69
  24. package/dist/dashboard/panels/scrubber.tsx +100 -0
  25. package/dist/dashboard/panels/stats.tsx +60 -32
  26. package/dist/dashboard/protocol-store.ts +25 -8
  27. package/dist/dashboard/vendor/hooks.module.d.ts +146 -0
  28. package/dist/dashboard/vendor/jsx-runtime.d.ts +55 -0
  29. package/dist/dashboard/vendor/jsx.d.ts +3123 -0
  30. package/dist/dashboard/vendor/preact.module.d.ts +397 -0
  31. package/dist/devtools-client.d.ts +312 -300
  32. package/dist/devtools-client.d.ts.map +1 -0
  33. package/dist/devtools-client.js +866 -838
  34. package/dist/devtools-client.js.map +1 -1
  35. package/dist/index.d.ts +6 -8
  36. package/dist/index.js +5 -12
  37. package/dist/perf-trace.d.ts +6 -4
  38. package/dist/perf-trace.d.ts.map +1 -0
  39. package/dist/perf-trace.js +80 -66
  40. package/dist/perf-trace.js.map +1 -1
  41. package/dist/plugins.d.ts +6 -6
  42. package/dist/plugins.d.ts.map +1 -0
  43. package/dist/plugins.js +5 -5
  44. package/dist/plugins.js.map +1 -1
  45. package/dist/provider-switcher.d.ts +8 -15
  46. package/dist/provider-switcher.d.ts.map +1 -0
  47. package/dist/provider-switcher.js +68 -68
  48. package/dist/provider-switcher.js.map +1 -1
  49. package/dist/react/devtools-provider.d.ts +11 -10
  50. package/dist/react/devtools-provider.d.ts.map +1 -0
  51. package/dist/react/devtools-provider.js +60 -32
  52. package/dist/react/devtools-provider.js.map +1 -1
  53. package/dist/react/use-fps-graph.d.ts +7 -7
  54. package/dist/react/use-fps-graph.d.ts.map +1 -0
  55. package/dist/react/use-fps-graph.js +27 -21
  56. package/dist/react/use-fps-graph.js.map +1 -1
  57. package/dist/react/use-pane-button.d.ts +4 -4
  58. package/dist/react/use-pane-button.d.ts.map +1 -0
  59. package/dist/react/use-pane-button.js +35 -30
  60. package/dist/react/use-pane-button.js.map +1 -1
  61. package/dist/react/use-pane-folder.d.ts +5 -4
  62. package/dist/react/use-pane-folder.d.ts.map +1 -0
  63. package/dist/react/use-pane-folder.js +33 -20
  64. package/dist/react/use-pane-folder.js.map +1 -1
  65. package/dist/react/use-pane-input.d.ts +31 -30
  66. package/dist/react/use-pane-input.d.ts.map +1 -0
  67. package/dist/react/use-pane-input.js +46 -42
  68. package/dist/react/use-pane-input.js.map +1 -1
  69. package/dist/react/use-pane-radio-grid.d.ts +20 -20
  70. package/dist/react/use-pane-radio-grid.d.ts.map +1 -0
  71. package/dist/react/use-pane-radio-grid.js +72 -50
  72. package/dist/react/use-pane-radio-grid.js.map +1 -1
  73. package/dist/react/use-pane.d.ts +4 -4
  74. package/dist/react/use-pane.d.ts.map +1 -0
  75. package/dist/react/use-pane.js +52 -17
  76. package/dist/react/use-pane.js.map +1 -1
  77. package/dist/react.d.ts +10 -12
  78. package/dist/react.js +2 -12
  79. package/dist/registry-view.d.ts +8 -21
  80. package/dist/registry-view.d.ts.map +1 -0
  81. package/dist/registry-view.js +357 -338
  82. package/dist/registry-view.js.map +1 -1
  83. package/dist/relay.d.ts +16 -0
  84. package/dist/relay.d.ts.map +1 -0
  85. package/dist/relay.js +189 -0
  86. package/dist/relay.js.map +1 -0
  87. package/dist/remote.d.ts +10 -0
  88. package/dist/remote.d.ts.map +1 -0
  89. package/dist/remote.js +28 -0
  90. package/dist/remote.js.map +1 -0
  91. package/dist/stats-graph.d.ts +23 -31
  92. package/dist/stats-graph.d.ts.map +1 -0
  93. package/dist/stats-graph.js +323 -264
  94. package/dist/stats-graph.js.map +1 -1
  95. package/dist/stats-row.d.ts +8 -21
  96. package/dist/stats-row.d.ts.map +1 -0
  97. package/dist/stats-row.js +99 -101
  98. package/dist/stats-row.js.map +1 -1
  99. package/dist/theme.d.ts +3 -1
  100. package/dist/theme.d.ts.map +1 -0
  101. package/dist/theme.js +43 -54
  102. package/dist/theme.js.map +1 -1
  103. package/dist/vite-plugin.d.ts +7 -6
  104. package/dist/vite-plugin.d.ts.map +1 -0
  105. package/dist/vite-plugin.js +87 -78
  106. package/dist/vite-plugin.js.map +1 -1
  107. package/package.json +33 -29
  108. package/dist/buffers-modal.cjs +0 -491
  109. package/dist/buffers-modal.cjs.map +0 -1
  110. package/dist/buffers-modal.d.cts +0 -62
  111. package/dist/buffers-view.cjs +0 -269
  112. package/dist/buffers-view.cjs.map +0 -1
  113. package/dist/buffers-view.d.cts +0 -39
  114. package/dist/create-pane.cjs +0 -203
  115. package/dist/create-pane.cjs.map +0 -1
  116. package/dist/create-pane.d.cts +0 -66
  117. package/dist/devtools-client.cjs +0 -870
  118. package/dist/devtools-client.cjs.map +0 -1
  119. package/dist/devtools-client.d.cts +0 -362
  120. package/dist/index.cjs +0 -42
  121. package/dist/index.cjs.map +0 -1
  122. package/dist/index.d.cts +0 -8
  123. package/dist/index.js.map +0 -1
  124. package/dist/perf-trace.cjs +0 -103
  125. package/dist/perf-trace.cjs.map +0 -1
  126. package/dist/perf-trace.d.cts +0 -27
  127. package/dist/plugins.cjs +0 -44
  128. package/dist/plugins.cjs.map +0 -1
  129. package/dist/plugins.d.cts +0 -7
  130. package/dist/provider-switcher.cjs +0 -95
  131. package/dist/provider-switcher.cjs.map +0 -1
  132. package/dist/provider-switcher.d.cts +0 -18
  133. package/dist/react/devtools-provider.cjs +0 -59
  134. package/dist/react/devtools-provider.cjs.map +0 -1
  135. package/dist/react/devtools-provider.d.cts +0 -39
  136. package/dist/react/use-fps-graph.cjs +0 -49
  137. package/dist/react/use-fps-graph.cjs.map +0 -1
  138. package/dist/react/use-fps-graph.d.cts +0 -15
  139. package/dist/react/use-pane-button.cjs +0 -58
  140. package/dist/react/use-pane-button.cjs.map +0 -1
  141. package/dist/react/use-pane-button.d.cts +0 -10
  142. package/dist/react/use-pane-folder.cjs +0 -48
  143. package/dist/react/use-pane-folder.cjs.map +0 -1
  144. package/dist/react/use-pane-folder.d.cts +0 -15
  145. package/dist/react/use-pane-input.cjs +0 -70
  146. package/dist/react/use-pane-input.cjs.map +0 -1
  147. package/dist/react/use-pane-input.d.cts +0 -44
  148. package/dist/react/use-pane-radio-grid.cjs +0 -78
  149. package/dist/react/use-pane-radio-grid.cjs.map +0 -1
  150. package/dist/react/use-pane-radio-grid.d.cts +0 -35
  151. package/dist/react/use-pane.cjs +0 -45
  152. package/dist/react/use-pane.cjs.map +0 -1
  153. package/dist/react/use-pane.d.cts +0 -40
  154. package/dist/react.cjs +0 -50
  155. package/dist/react.cjs.map +0 -1
  156. package/dist/react.d.cts +0 -12
  157. package/dist/react.js.map +0 -1
  158. package/dist/registry-view.cjs +0 -371
  159. package/dist/registry-view.cjs.map +0 -1
  160. package/dist/registry-view.d.cts +0 -24
  161. package/dist/stats-graph.cjs +0 -297
  162. package/dist/stats-graph.cjs.map +0 -1
  163. package/dist/stats-graph.d.cts +0 -40
  164. package/dist/stats-row.cjs +0 -153
  165. package/dist/stats-row.cjs.map +0 -1
  166. package/dist/stats-row.d.cts +0 -24
  167. package/dist/theme.cjs +0 -260
  168. package/dist/theme.cjs.map +0 -1
  169. package/dist/theme.d.cts +0 -10
  170. package/dist/vite-plugin.cjs +0 -113
  171. package/dist/vite-plugin.cjs.map +0 -1
  172. package/dist/vite-plugin.d.cts +0 -9
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-switcher.d.ts","names":[],"sources":["../src/provider-switcher.ts"],"mappings":";;;UAWiB;WACN,SAAS;EAClB;;iBAGc,oBAAoB,QAAQ,OAAO,WAAW,QAAQ,iBAAiB"}
@@ -1,71 +1,71 @@
1
+ //#region src/provider-switcher.ts
1
2
  function addProviderSwitcher(parent, client) {
2
- const blade = parent.addBlade({ view: "separator" });
3
- const bladeEl = blade.element;
4
- bladeEl.innerHTML = "";
5
- bladeEl.className = "tp-cntv";
6
- bladeEl.style.cssText = "display:none";
7
- const row = document.createElement("div");
8
- row.style.cssText = [
9
- "display:grid",
10
- "grid-template-columns:auto 1fr auto",
11
- "align-items:center",
12
- "gap:6px",
13
- "height:calc(var(--cnt-usz, 20px) * 1.1)",
14
- "padding:0 6px",
15
- "font-size:12px",
16
- "line-height:1",
17
- "color:var(--tp-label-foreground-color)",
18
- "font-variant-numeric:tabular-nums",
19
- "user-select:none",
20
- "-webkit-user-select:none"
21
- ].join(";");
22
- const arrowStyle = "cursor:pointer;padding:0 4px;opacity:0.8;font-family:ui-monospace,monospace;";
23
- const prevBtn = document.createElement("span");
24
- prevBtn.textContent = "\u25C0";
25
- prevBtn.style.cssText = arrowStyle;
26
- prevBtn.setAttribute("role", "button");
27
- prevBtn.setAttribute("aria-label", "Previous provider");
28
- const label = document.createElement("span");
29
- label.style.cssText = "text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
30
- const nextBtn = document.createElement("span");
31
- nextBtn.textContent = "\u25B6";
32
- nextBtn.style.cssText = arrowStyle;
33
- nextBtn.setAttribute("role", "button");
34
- nextBtn.setAttribute("aria-label", "Next provider");
35
- row.appendChild(prevBtn);
36
- row.appendChild(label);
37
- row.appendChild(nextBtn);
38
- bladeEl.appendChild(row);
39
- function step(delta) {
40
- const providers = client.state.providers;
41
- if (providers.length < 2) return;
42
- const selected = client.state.selectedProviderId;
43
- const idx = Math.max(0, providers.findIndex((p) => p.id === selected));
44
- const next = providers[(idx + delta + providers.length) % providers.length];
45
- client.selectProvider(next.id);
46
- }
47
- prevBtn.addEventListener("click", () => step(-1));
48
- nextBtn.addEventListener("click", () => step(1));
49
- const unsubscribe = client.addListener((s) => {
50
- bladeEl.style.display = s.providers.length >= 2 ? "" : "none";
51
- const current = s.providers.find((p) => p.id === s.selectedProviderId);
52
- if (current) {
53
- label.textContent = `${current.kind}:${current.name}`;
54
- label.title = `${current.kind}:${current.name} (${current.id.slice(0, 8)})`;
55
- } else {
56
- label.textContent = "\u2014";
57
- label.title = "";
58
- }
59
- });
60
- return {
61
- element: bladeEl,
62
- dispose() {
63
- unsubscribe();
64
- blade.dispose();
65
- }
66
- };
3
+ const blade = parent.addBlade({ view: "separator" });
4
+ const bladeEl = blade.element;
5
+ bladeEl.innerHTML = "";
6
+ bladeEl.className = "tp-cntv";
7
+ bladeEl.style.cssText = "display:none";
8
+ const row = document.createElement("div");
9
+ row.style.cssText = [
10
+ "display:grid",
11
+ "grid-template-columns:auto 1fr auto",
12
+ "align-items:center",
13
+ "gap:6px",
14
+ "height:calc(var(--cnt-usz, 20px) * 1.1)",
15
+ "padding:0 6px",
16
+ "font-size:12px",
17
+ "line-height:1",
18
+ "color:var(--tp-label-foreground-color)",
19
+ "font-variant-numeric:tabular-nums",
20
+ "user-select:none",
21
+ "-webkit-user-select:none"
22
+ ].join(";");
23
+ const arrowStyle = "cursor:pointer;padding:0 4px;opacity:0.8;font-family:ui-monospace,monospace;";
24
+ const prevBtn = document.createElement("span");
25
+ prevBtn.textContent = "";
26
+ prevBtn.style.cssText = arrowStyle;
27
+ prevBtn.setAttribute("role", "button");
28
+ prevBtn.setAttribute("aria-label", "Previous provider");
29
+ const label = document.createElement("span");
30
+ label.style.cssText = "text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
31
+ const nextBtn = document.createElement("span");
32
+ nextBtn.textContent = "";
33
+ nextBtn.style.cssText = arrowStyle;
34
+ nextBtn.setAttribute("role", "button");
35
+ nextBtn.setAttribute("aria-label", "Next provider");
36
+ row.appendChild(prevBtn);
37
+ row.appendChild(label);
38
+ row.appendChild(nextBtn);
39
+ bladeEl.appendChild(row);
40
+ function step(delta) {
41
+ const providers = client.state.providers;
42
+ if (providers.length < 2) return;
43
+ const selected = client.state.selectedProviderId;
44
+ const next = providers[(Math.max(0, providers.findIndex((p) => p.id === selected)) + delta + providers.length) % providers.length];
45
+ client.selectProvider(next.id);
46
+ }
47
+ prevBtn.addEventListener("click", () => step(-1));
48
+ nextBtn.addEventListener("click", () => step(1));
49
+ const unsubscribe = client.addListener((s) => {
50
+ bladeEl.style.display = s.providers.length >= 2 ? "" : "none";
51
+ const current = s.providers.find((p) => p.id === s.selectedProviderId);
52
+ if (current) {
53
+ label.textContent = `${current.kind}:${current.name}`;
54
+ label.title = `${current.kind}:${current.name} (${current.id.slice(0, 8)})`;
55
+ } else {
56
+ label.textContent = "—";
57
+ label.title = "";
58
+ }
59
+ });
60
+ return {
61
+ element: bladeEl,
62
+ dispose() {
63
+ unsubscribe();
64
+ blade.dispose();
65
+ }
66
+ };
67
67
  }
68
- export {
69
- addProviderSwitcher
70
- };
68
+ //#endregion
69
+ export { addProviderSwitcher };
70
+
71
71
  //# sourceMappingURL=provider-switcher.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider-switcher.ts"],"sourcesContent":["/**\n * Provider switcher — a single-row blade with ◀ / ▶ arrows + the\n * selected provider's name. Hidden while only one provider is known\n * (the common case); shown automatically whenever two or more appear\n * on the discovery bus.\n */\n\nimport type { FolderApi, Pane } from 'tweakpane'\n\nimport type { DevtoolsClient } from './devtools-client.js'\n\nexport interface ProviderSwitcherHandle {\n readonly element: HTMLElement\n dispose(): void\n}\n\nexport function addProviderSwitcher(\n parent: Pane | FolderApi,\n client: DevtoolsClient,\n): ProviderSwitcherHandle {\n // Separator-blade shim so the rack ordering stays correct; we swap\n // its innards for our own layout (same pattern as stats-graph / row).\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 = 'display:none' // Hidden until >= 2 providers\n\n const row = document.createElement('div')\n row.style.cssText = [\n 'display:grid',\n 'grid-template-columns:auto 1fr auto',\n 'align-items:center',\n 'gap:6px',\n 'height:calc(var(--cnt-usz, 20px) * 1.1)',\n 'padding:0 6px',\n 'font-size:12px',\n 'line-height:1',\n 'color:var(--tp-label-foreground-color)',\n 'font-variant-numeric:tabular-nums',\n 'user-select:none',\n '-webkit-user-select:none',\n ].join(';')\n\n const arrowStyle =\n 'cursor:pointer;padding:0 4px;opacity:0.8;font-family:ui-monospace,monospace;'\n const prevBtn = document.createElement('span')\n prevBtn.textContent = '◀'\n prevBtn.style.cssText = arrowStyle\n prevBtn.setAttribute('role', 'button')\n prevBtn.setAttribute('aria-label', 'Previous provider')\n\n const label = document.createElement('span')\n label.style.cssText = 'text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'\n\n const nextBtn = document.createElement('span')\n nextBtn.textContent = '▶'\n nextBtn.style.cssText = arrowStyle\n nextBtn.setAttribute('role', 'button')\n nextBtn.setAttribute('aria-label', 'Next provider')\n\n row.appendChild(prevBtn)\n row.appendChild(label)\n row.appendChild(nextBtn)\n bladeEl.appendChild(row)\n\n function step(delta: number): void {\n const providers = client.state.providers\n if (providers.length < 2) return\n const selected = client.state.selectedProviderId\n const idx = Math.max(0, providers.findIndex((p) => p.id === selected))\n const next = providers[(idx + delta + providers.length) % providers.length]!\n client.selectProvider(next.id)\n }\n\n prevBtn.addEventListener('click', () => step(-1))\n nextBtn.addEventListener('click', () => step(1))\n\n const unsubscribe = client.addListener((s) => {\n // Visibility: only when multi-provider.\n bladeEl.style.display = s.providers.length >= 2 ? '' : 'none'\n const current = s.providers.find((p) => p.id === s.selectedProviderId)\n if (current) {\n // Prefix with kind so the user can tell flatland's system provider\n // apart from a user provider with the same display name.\n label.textContent = `${current.kind}:${current.name}`\n label.title = `${current.kind}:${current.name} (${current.id.slice(0, 8)})`\n } else {\n label.textContent = '—'\n label.title = ''\n }\n })\n\n return {\n element: bladeEl,\n dispose() {\n unsubscribe()\n blade.dispose()\n },\n }\n}\n"],"mappings":"AAgBO,SAAS,oBACd,QACA,QACwB;AAGxB,QAAM,QAAQ,OAAO,SAAS,EAAE,MAAM,YAAY,CAAC;AAInD,QAAM,UAAU,MAAM;AACtB,UAAQ,YAAY;AACpB,UAAQ,YAAY;AACpB,UAAQ,MAAM,UAAU;AAExB,QAAM,MAAM,SAAS,cAAc,KAAK;AACxC,MAAI,MAAM,UAAU;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,GAAG;AAEV,QAAM,aACJ;AACF,QAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,UAAQ,cAAc;AACtB,UAAQ,MAAM,UAAU;AACxB,UAAQ,aAAa,QAAQ,QAAQ;AACrC,UAAQ,aAAa,cAAc,mBAAmB;AAEtD,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,MAAM,UAAU;AAEtB,QAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,UAAQ,cAAc;AACtB,UAAQ,MAAM,UAAU;AACxB,UAAQ,aAAa,QAAQ,QAAQ;AACrC,UAAQ,aAAa,cAAc,eAAe;AAElD,MAAI,YAAY,OAAO;AACvB,MAAI,YAAY,KAAK;AACrB,MAAI,YAAY,OAAO;AACvB,UAAQ,YAAY,GAAG;AAEvB,WAAS,KAAK,OAAqB;AACjC,UAAM,YAAY,OAAO,MAAM;AAC/B,QAAI,UAAU,SAAS,EAAG;AAC1B,UAAM,WAAW,OAAO,MAAM;AAC9B,UAAM,MAAM,KAAK,IAAI,GAAG,UAAU,UAAU,CAAC,MAAM,EAAE,OAAO,QAAQ,CAAC;AACrE,UAAM,OAAO,WAAW,MAAM,QAAQ,UAAU,UAAU,UAAU,MAAM;AAC1E,WAAO,eAAe,KAAK,EAAE;AAAA,EAC/B;AAEA,UAAQ,iBAAiB,SAAS,MAAM,KAAK,EAAE,CAAC;AAChD,UAAQ,iBAAiB,SAAS,MAAM,KAAK,CAAC,CAAC;AAE/C,QAAM,cAAc,OAAO,YAAY,CAAC,MAAM;AAE5C,YAAQ,MAAM,UAAU,EAAE,UAAU,UAAU,IAAI,KAAK;AACvD,UAAM,UAAU,EAAE,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,kBAAkB;AACrE,QAAI,SAAS;AAGX,YAAM,cAAc,GAAG,QAAQ,IAAI,IAAI,QAAQ,IAAI;AACnD,YAAM,QAAQ,GAAG,QAAQ,IAAI,IAAI,QAAQ,IAAI,KAAK,QAAQ,GAAG,MAAM,GAAG,CAAC,CAAC;AAAA,IAC1E,OAAO;AACL,YAAM,cAAc;AACpB,YAAM,QAAQ;AAAA,IAChB;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AACR,kBAAY;AACZ,YAAM,QAAQ;AAAA,IAChB;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"file":"provider-switcher.js","names":[],"sources":["../src/provider-switcher.ts"],"sourcesContent":["/**\n * Provider switcher — a single-row blade with ◀ / ▶ arrows + the\n * selected provider's name. Hidden while only one provider is known\n * (the common case); shown automatically whenever two or more appear\n * on the discovery bus.\n */\n\nimport type { FolderApi, Pane } from 'tweakpane'\n\nimport type { DevtoolsClient } from './devtools-client.js'\n\nexport interface ProviderSwitcherHandle {\n readonly element: HTMLElement\n dispose(): void\n}\n\nexport function addProviderSwitcher(parent: Pane | FolderApi, client: DevtoolsClient): ProviderSwitcherHandle {\n // Separator-blade shim so the rack ordering stays correct; we swap\n // its innards for our own layout (same pattern as stats-graph / row).\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 = 'display:none' // Hidden until >= 2 providers\n\n const row = document.createElement('div')\n row.style.cssText = [\n 'display:grid',\n 'grid-template-columns:auto 1fr auto',\n 'align-items:center',\n 'gap:6px',\n 'height:calc(var(--cnt-usz, 20px) * 1.1)',\n 'padding:0 6px',\n 'font-size:12px',\n 'line-height:1',\n 'color:var(--tp-label-foreground-color)',\n 'font-variant-numeric:tabular-nums',\n 'user-select:none',\n '-webkit-user-select:none',\n ].join(';')\n\n const arrowStyle = 'cursor:pointer;padding:0 4px;opacity:0.8;font-family:ui-monospace,monospace;'\n const prevBtn = document.createElement('span')\n prevBtn.textContent = '◀'\n prevBtn.style.cssText = arrowStyle\n prevBtn.setAttribute('role', 'button')\n prevBtn.setAttribute('aria-label', 'Previous provider')\n\n const label = document.createElement('span')\n label.style.cssText = 'text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'\n\n const nextBtn = document.createElement('span')\n nextBtn.textContent = '▶'\n nextBtn.style.cssText = arrowStyle\n nextBtn.setAttribute('role', 'button')\n nextBtn.setAttribute('aria-label', 'Next provider')\n\n row.appendChild(prevBtn)\n row.appendChild(label)\n row.appendChild(nextBtn)\n bladeEl.appendChild(row)\n\n function step(delta: number): void {\n const providers = client.state.providers\n if (providers.length < 2) return\n const selected = client.state.selectedProviderId\n const idx = Math.max(\n 0,\n providers.findIndex((p) => p.id === selected)\n )\n const next = providers[(idx + delta + providers.length) % providers.length]!\n client.selectProvider(next.id)\n }\n\n prevBtn.addEventListener('click', () => step(-1))\n nextBtn.addEventListener('click', () => step(1))\n\n const unsubscribe = client.addListener((s) => {\n // Visibility: only when multi-provider.\n bladeEl.style.display = s.providers.length >= 2 ? '' : 'none'\n const current = s.providers.find((p) => p.id === s.selectedProviderId)\n if (current) {\n // Prefix with kind so the user can tell flatland's system provider\n // apart from a user provider with the same display name.\n label.textContent = `${current.kind}:${current.name}`\n label.title = `${current.kind}:${current.name} (${current.id.slice(0, 8)})`\n } else {\n label.textContent = '—'\n label.title = ''\n }\n })\n\n return {\n element: bladeEl,\n dispose() {\n unsubscribe()\n blade.dispose()\n },\n }\n}\n"],"mappings":";AAgBA,SAAgB,oBAAoB,QAA0B,QAAgD;CAG5G,MAAM,QAAQ,OAAO,SAAS,EAAE,MAAM,YAAY,CAAC;CAInD,MAAM,UAAU,MAAM;CACtB,QAAQ,YAAY;CACpB,QAAQ,YAAY;CACpB,QAAQ,MAAM,UAAU;CAExB,MAAM,MAAM,SAAS,cAAc,KAAK;CACxC,IAAI,MAAM,UAAU;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,GAAG;CAEV,MAAM,aAAa;CACnB,MAAM,UAAU,SAAS,cAAc,MAAM;CAC7C,QAAQ,cAAc;CACtB,QAAQ,MAAM,UAAU;CACxB,QAAQ,aAAa,QAAQ,QAAQ;CACrC,QAAQ,aAAa,cAAc,mBAAmB;CAEtD,MAAM,QAAQ,SAAS,cAAc,MAAM;CAC3C,MAAM,MAAM,UAAU;CAEtB,MAAM,UAAU,SAAS,cAAc,MAAM;CAC7C,QAAQ,cAAc;CACtB,QAAQ,MAAM,UAAU;CACxB,QAAQ,aAAa,QAAQ,QAAQ;CACrC,QAAQ,aAAa,cAAc,eAAe;CAElD,IAAI,YAAY,OAAO;CACvB,IAAI,YAAY,KAAK;CACrB,IAAI,YAAY,OAAO;CACvB,QAAQ,YAAY,GAAG;CAEvB,SAAS,KAAK,OAAqB;EACjC,MAAM,YAAY,OAAO,MAAM;EAC/B,IAAI,UAAU,SAAS,GAAG;EAC1B,MAAM,WAAW,OAAO,MAAM;EAK9B,MAAM,OAAO,WAJD,KAAK,IACf,GACA,UAAU,WAAW,MAAM,EAAE,OAAO,QAAQ,CAEpB,IAAI,QAAQ,UAAU,UAAU,UAAU;EACpE,OAAO,eAAe,KAAK,EAAE;CAC/B;CAEA,QAAQ,iBAAiB,eAAe,KAAK,EAAE,CAAC;CAChD,QAAQ,iBAAiB,eAAe,KAAK,CAAC,CAAC;CAE/C,MAAM,cAAc,OAAO,aAAa,MAAM;EAE5C,QAAQ,MAAM,UAAU,EAAE,UAAU,UAAU,IAAI,KAAK;EACvD,MAAM,UAAU,EAAE,UAAU,MAAM,MAAM,EAAE,OAAO,EAAE,kBAAkB;EACrE,IAAI,SAAS;GAGX,MAAM,cAAc,GAAG,QAAQ,KAAK,GAAG,QAAQ;GAC/C,MAAM,QAAQ,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK,IAAI,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE;EAC3E,OAAO;GACL,MAAM,cAAc;GACpB,MAAM,QAAQ;EAChB;CACF,CAAC;CAED,OAAO;EACL,SAAS;EACT,UAAU;GACR,YAAY;GACZ,MAAM,QAAQ;EAChB;CACF;AACF"}
@@ -1,12 +1,12 @@
1
- import { ReactElement } from 'react';
2
-
1
+ import { ReactElement } from "react";
2
+ //#region src/react/devtools-provider.d.ts
3
3
  interface DevtoolsProviderProps {
4
- /** Display name shown in the consumer's provider switcher. */
5
- name?: string;
6
- /** Optional explicit provider id (defaults to a fresh uuid). */
7
- id?: string;
8
- /** Override the discovery channel name (advanced). */
9
- discoveryChannelName?: string;
4
+ /** Display name shown in the consumer's provider switcher. */
5
+ name?: string;
6
+ /** Optional explicit provider id (defaults to a fresh uuid). */
7
+ id?: string;
8
+ /** Override the discovery channel name (advanced). */
9
+ discoveryChannelName?: string;
10
10
  }
11
11
  /**
12
12
  * Mount inside `<Canvas>` to publish stats / registry / buffer feeds.
@@ -35,5 +35,6 @@ interface DevtoolsProviderProps {
35
35
  * registered, and R3F's normal auto-render path is untouched.
36
36
  */
37
37
  declare function DevtoolsProvider(props: DevtoolsProviderProps): ReactElement | null;
38
-
39
- export { DevtoolsProvider, type DevtoolsProviderProps };
38
+ //#endregion
39
+ export { DevtoolsProvider, DevtoolsProviderProps };
40
+ //# sourceMappingURL=devtools-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools-provider.d.ts","names":[],"sources":["../../src/react/devtools-provider.tsx"],"mappings":";;UAOiB;;EAEf;;EAEA;;EAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6Bc,iBAAiB,OAAO,wBAAwB"}
@@ -1,38 +1,66 @@
1
- import { jsx } from "react/jsx-runtime";
1
+ import { createDevtoolsProvider, isDevtoolsActive } from "three-flatland";
2
2
  import { useEffect, useRef } from "react";
3
3
  import { useFrame } from "@react-three/fiber";
4
- import {
5
- createDevtoolsProvider,
6
- isDevtoolsActive
7
- } from "three-flatland";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/react/devtools-provider.tsx
6
+ /**
7
+ * Mount inside `<Canvas>` to publish stats / registry / buffer feeds.
8
+ * Drop it next to your scene — no children, no wrapping:
9
+ *
10
+ * ```tsx
11
+ * <Canvas>
12
+ * <DevtoolsProvider name="basic-sprite" />
13
+ * <Scene />
14
+ * </Canvas>
15
+ * ```
16
+ *
17
+ * Passive sampler — does NOT take over rendering. Registers a
18
+ * default-phase `useFrame` and snaps `endFrame → beginFrame` once per
19
+ * tick, leaving R3F's auto-render (and any explicit render useFrame
20
+ * like Flatland's `flatland.render(gl)`) completely alone. The
21
+ * `cpuMs` value reported is the inter-tick interval — equivalent to
22
+ * frame time at 60fps, slightly looser than Flatland's internal
23
+ * tight-bracket measurement when stressed (Flatland's number isolates
24
+ * just its `render()` work; ours covers the whole rAF interval).
25
+ *
26
+ * **Safe to leave in production.** The outer component short-circuits
27
+ * when the devtools build gate is false (build-time, terser folds the
28
+ * branch away) or when `isDevtoolsActive()` is false (runtime toggle). In
29
+ * those cases the inner component never mounts, no `useFrame` is
30
+ * registered, and R3F's normal auto-render path is untouched.
31
+ */
8
32
  function DevtoolsProvider(props) {
9
- if (process.env.NODE_ENV === "production" && process.env.FL_DEVTOOLS !== "true") return null;
10
- if (!isDevtoolsActive()) return null;
11
- return /* @__PURE__ */ jsx(DevtoolsProviderActive, { ...props });
33
+ if (process.env.NODE_ENV === "production" && process.env.FL_DEVTOOLS !== "true") return null;
34
+ if (!isDevtoolsActive()) return null;
35
+ return /* @__PURE__ */ jsx(DevtoolsProviderActive, { ...props });
12
36
  }
13
- function DevtoolsProviderActive({
14
- name,
15
- id,
16
- discoveryChannelName
17
- }) {
18
- const handleRef = useRef(null);
19
- useFrame((state) => {
20
- const h = handleRef.current;
21
- if (!h || h.disposed) return;
22
- h.endFrame(state.gl);
23
- h.beginFrame(performance.now(), state.gl);
24
- });
25
- useEffect(() => {
26
- const handle = createDevtoolsProvider({ name, id, discoveryChannelName });
27
- handleRef.current = handle;
28
- return () => {
29
- handle.dispose();
30
- if (handleRef.current === handle) handleRef.current = null;
31
- };
32
- }, [name, id, discoveryChannelName]);
33
- return null;
37
+ function DevtoolsProviderActive({ name, id, discoveryChannelName }) {
38
+ const handleRef = useRef(null);
39
+ useFrame((state) => {
40
+ const h = handleRef.current;
41
+ if (!h || h.disposed) return;
42
+ h.endFrame(state.gl);
43
+ h.beginFrame(performance.now(), state.gl);
44
+ });
45
+ useEffect(() => {
46
+ const handle = createDevtoolsProvider({
47
+ name,
48
+ id,
49
+ discoveryChannelName
50
+ });
51
+ handleRef.current = handle;
52
+ return () => {
53
+ handle.dispose();
54
+ if (handleRef.current === handle) handleRef.current = null;
55
+ };
56
+ }, [
57
+ name,
58
+ id,
59
+ discoveryChannelName
60
+ ]);
61
+ return null;
34
62
  }
35
- export {
36
- DevtoolsProvider
37
- };
63
+ //#endregion
64
+ export { DevtoolsProvider };
65
+
38
66
  //# sourceMappingURL=devtools-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/devtools-provider.tsx"],"sourcesContent":["import { useEffect, useRef, type ReactElement } from 'react'\nimport { useFrame } from '@react-three/fiber'\nimport {\n createDevtoolsProvider,\n isDevtoolsActive,\n type DevtoolsProviderHandle,\n} from 'three-flatland'\n\n// Types the build-time `process.env` reads without requiring @types/node (shadows the global where present; erased at compile).\ndeclare const process: { env: { NODE_ENV?: string; FL_DEVTOOLS?: string } }\n\nexport interface DevtoolsProviderProps {\n /** Display name shown in the consumer's provider switcher. */\n name?: string\n /** Optional explicit provider id (defaults to a fresh uuid). */\n id?: string\n /** Override the discovery channel name (advanced). */\n discoveryChannelName?: string\n}\n\n/**\n * Mount inside `<Canvas>` to publish stats / registry / buffer feeds.\n * Drop it next to your scene — no children, no wrapping:\n *\n * ```tsx\n * <Canvas>\n * <DevtoolsProvider name=\"basic-sprite\" />\n * <Scene />\n * </Canvas>\n * ```\n *\n * Passive sampler — does NOT take over rendering. Registers a\n * default-phase `useFrame` and snaps `endFrame → beginFrame` once per\n * tick, leaving R3F's auto-render (and any explicit render useFrame\n * like Flatland's `flatland.render(gl)`) completely alone. The\n * `cpuMs` value reported is the inter-tick interval — equivalent to\n * frame time at 60fps, slightly looser than Flatland's internal\n * tight-bracket measurement when stressed (Flatland's number isolates\n * just its `render()` work; ours covers the whole rAF interval).\n *\n * **Safe to leave in production.** The outer component short-circuits\n * when the devtools build gate is false (build-time, terser folds the\n * branch away) or when `isDevtoolsActive()` is false (runtime toggle). In\n * those cases the inner component never mounts, no `useFrame` is\n * registered, and R3F's normal auto-render path is untouched.\n */\nexport function DevtoolsProvider(\n props: DevtoolsProviderProps,\n): ReactElement | null {\n if (process.env.NODE_ENV === 'production' && process.env.FL_DEVTOOLS !== 'true') return null\n if (!isDevtoolsActive()) return null\n return <DevtoolsProviderActive {...props} />\n}\n\nfunction DevtoolsProviderActive({\n name,\n id,\n discoveryChannelName,\n}: DevtoolsProviderProps): null {\n // Provider creation lives in `useEffect` only — never in render. R3F's\n // concurrent-rendering + StrictMode double-render can throw away renders\n // before commit, and any side effect we performed in render (like\n // opening BroadcastChannels) would leak as an orphan. Keeping the only\n // construction site inside the committed effect guarantees one live\n // provider per mounted component instance.\n const handleRef = useRef<DevtoolsProviderHandle | null>(null)\n\n useFrame((state) => {\n const h = handleRef.current\n if (!h || h.disposed) return\n // Default-phase `useFrame` runs every rAF tick BEFORE R3F's\n // auto-render (or whatever render useFrame the example registered).\n // We close the previous tick's window and open the next one, so\n // `cpuMs` measures the full inter-tick interval. We deliberately\n // do NOT use `phase: 'render'` — that would steal R3F's render\n // slot and prevent users from driving their own render path\n // (Flatland's `flatland.render(gl)`, custom passes, etc.).\n h.endFrame(state.gl as never)\n h.beginFrame(performance.now(), state.gl as never)\n })\n\n useEffect(() => {\n const handle = createDevtoolsProvider({ name, id, discoveryChannelName })\n handleRef.current = handle\n return () => {\n handle.dispose()\n if (handleRef.current === handle) handleRef.current = null\n }\n }, [name, id, discoveryChannelName])\n\n return null\n}\n"],"mappings":"AAmDS;AAnDT,SAAS,WAAW,cAAiC;AACrD,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAwCA,SAAS,iBACd,OACqB;AACrB,MAAI,QAAQ,IAAI,aAAa,gBAAgB,QAAQ,IAAI,gBAAgB,OAAQ,QAAO;AACxF,MAAI,CAAC,iBAAiB,EAAG,QAAO;AAChC,SAAO,oBAAC,0BAAwB,GAAG,OAAO;AAC5C;AAEA,SAAS,uBAAuB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AACF,GAAgC;AAO9B,QAAM,YAAY,OAAsC,IAAI;AAE5D,WAAS,CAAC,UAAU;AAClB,UAAM,IAAI,UAAU;AACpB,QAAI,CAAC,KAAK,EAAE,SAAU;AAQtB,MAAE,SAAS,MAAM,EAAW;AAC5B,MAAE,WAAW,YAAY,IAAI,GAAG,MAAM,EAAW;AAAA,EACnD,CAAC;AAED,YAAU,MAAM;AACd,UAAM,SAAS,uBAAuB,EAAE,MAAM,IAAI,qBAAqB,CAAC;AACxE,cAAU,UAAU;AACpB,WAAO,MAAM;AACX,aAAO,QAAQ;AACf,UAAI,UAAU,YAAY,OAAQ,WAAU,UAAU;AAAA,IACxD;AAAA,EACF,GAAG,CAAC,MAAM,IAAI,oBAAoB,CAAC;AAEnC,SAAO;AACT;","names":[]}
1
+ {"version":3,"file":"devtools-provider.js","names":[],"sources":["../../src/react/devtools-provider.tsx"],"sourcesContent":["import { useEffect, useRef, type ReactElement } from 'react'\nimport { useFrame } from '@react-three/fiber'\nimport { createDevtoolsProvider, isDevtoolsActive, type DevtoolsProviderHandle } from 'three-flatland'\n\n// Types the build-time `process.env` reads without requiring @types/node (shadows the global where present; erased at compile).\ndeclare const process: { env: { NODE_ENV?: string; FL_DEVTOOLS?: string } }\n\nexport interface DevtoolsProviderProps {\n /** Display name shown in the consumer's provider switcher. */\n name?: string\n /** Optional explicit provider id (defaults to a fresh uuid). */\n id?: string\n /** Override the discovery channel name (advanced). */\n discoveryChannelName?: string\n}\n\n/**\n * Mount inside `<Canvas>` to publish stats / registry / buffer feeds.\n * Drop it next to your scene — no children, no wrapping:\n *\n * ```tsx\n * <Canvas>\n * <DevtoolsProvider name=\"basic-sprite\" />\n * <Scene />\n * </Canvas>\n * ```\n *\n * Passive sampler — does NOT take over rendering. Registers a\n * default-phase `useFrame` and snaps `endFrame → beginFrame` once per\n * tick, leaving R3F's auto-render (and any explicit render useFrame\n * like Flatland's `flatland.render(gl)`) completely alone. The\n * `cpuMs` value reported is the inter-tick interval — equivalent to\n * frame time at 60fps, slightly looser than Flatland's internal\n * tight-bracket measurement when stressed (Flatland's number isolates\n * just its `render()` work; ours covers the whole rAF interval).\n *\n * **Safe to leave in production.** The outer component short-circuits\n * when the devtools build gate is false (build-time, terser folds the\n * branch away) or when `isDevtoolsActive()` is false (runtime toggle). In\n * those cases the inner component never mounts, no `useFrame` is\n * registered, and R3F's normal auto-render path is untouched.\n */\nexport function DevtoolsProvider(props: DevtoolsProviderProps): ReactElement | null {\n if (process.env.NODE_ENV === 'production' && process.env.FL_DEVTOOLS !== 'true') return null\n if (!isDevtoolsActive()) return null\n return <DevtoolsProviderActive {...props} />\n}\n\nfunction DevtoolsProviderActive({ name, id, discoveryChannelName }: DevtoolsProviderProps): null {\n // Provider creation lives in `useEffect` only — never in render. R3F's\n // concurrent-rendering + StrictMode double-render can throw away renders\n // before commit, and any side effect we performed in render (like\n // opening BroadcastChannels) would leak as an orphan. Keeping the only\n // construction site inside the committed effect guarantees one live\n // provider per mounted component instance.\n const handleRef = useRef<DevtoolsProviderHandle | null>(null)\n\n useFrame((state) => {\n const h = handleRef.current\n if (!h || h.disposed) return\n // Default-phase `useFrame` runs every rAF tick BEFORE R3F's\n // auto-render (or whatever render useFrame the example registered).\n // We close the previous tick's window and open the next one, so\n // `cpuMs` measures the full inter-tick interval. We deliberately\n // do NOT use `phase: 'render'` — that would steal R3F's render\n // slot and prevent users from driving their own render path\n // (Flatland's `flatland.render(gl)`, custom passes, etc.).\n h.endFrame(state.gl as never)\n h.beginFrame(performance.now(), state.gl as never)\n })\n\n useEffect(() => {\n const handle = createDevtoolsProvider({ name, id, discoveryChannelName })\n handleRef.current = handle\n return () => {\n handle.dispose()\n if (handleRef.current === handle) handleRef.current = null\n }\n }, [name, id, discoveryChannelName])\n\n return null\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,iBAAiB,OAAmD;CAClF,IAAI,QAAQ,IAAI,aAAa,gBAAgB,QAAQ,IAAI,gBAAgB,QAAQ,OAAO;CACxF,IAAI,CAAC,iBAAiB,GAAG,OAAO;CAChC,OAAO,oBAAC,wBAAD,EAAwB,GAAI,MAAQ,CAAA;AAC7C;AAEA,SAAS,uBAAuB,EAAE,MAAM,IAAI,wBAAqD;CAO/F,MAAM,YAAY,OAAsC,IAAI;CAE5D,UAAU,UAAU;EAClB,MAAM,IAAI,UAAU;EACpB,IAAI,CAAC,KAAK,EAAE,UAAU;EAQtB,EAAE,SAAS,MAAM,EAAW;EAC5B,EAAE,WAAW,YAAY,IAAI,GAAG,MAAM,EAAW;CACnD,CAAC;CAED,gBAAgB;EACd,MAAM,SAAS,uBAAuB;GAAE;GAAM;GAAI;EAAqB,CAAC;EACxE,UAAU,UAAU;EACpB,aAAa;GACX,OAAO,QAAQ;GACf,IAAI,UAAU,YAAY,QAAQ,UAAU,UAAU;EACxD;CACF,GAAG;EAAC;EAAM;EAAI;CAAoB,CAAC;CAEnC,OAAO;AACT"}
@@ -1,9 +1,8 @@
1
- import { PaneParent } from './use-pane-input.js';
2
- import 'tweakpane';
3
-
1
+ import { PaneParent } from "./use-pane-input.js";
2
+ //#region src/react/use-fps-graph.d.ts
4
3
  interface FpsGraphHandle {
5
- begin: () => void;
6
- end: () => void;
4
+ begin: () => void;
5
+ end: () => void;
7
6
  }
8
7
  /**
9
8
  * Add an FPS graph blade. Returns { begin, end } for use inside useFrame.
@@ -11,5 +10,6 @@ interface FpsGraphHandle {
11
10
  * and use paneBundle.fpsGraph directly.
12
11
  */
13
12
  declare function useFpsGraph(parent: PaneParent | null): FpsGraphHandle;
14
-
15
- export { type FpsGraphHandle, useFpsGraph };
13
+ //#endregion
14
+ export { FpsGraphHandle, useFpsGraph };
15
+ //# sourceMappingURL=use-fps-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-fps-graph.d.ts","names":[],"sources":["../../src/react/use-fps-graph.ts"],"mappings":";;UAIiB;EACf;EACA;;;;;;;iBAQc,YAAY,QAAQ,oBAAoB"}
@@ -1,25 +1,31 @@
1
1
  import { useEffect, useRef } from "react";
2
+ //#region src/react/use-fps-graph.ts
3
+ /**
4
+ * Add an FPS graph blade. Returns { begin, end } for use inside useFrame.
5
+ * If the pane was created with `fps: true` (default), you can skip this hook
6
+ * and use paneBundle.fpsGraph directly.
7
+ */
2
8
  function useFpsGraph(parent) {
3
- const graphRef = useRef(null);
4
- useEffect(() => {
5
- if (!parent) return;
6
- const graph = parent.addBlade({
7
- view: "fpsgraph",
8
- label: "fps",
9
- rows: 2
10
- });
11
- graphRef.current = graph;
12
- return () => {
13
- graph.dispose();
14
- graphRef.current = null;
15
- };
16
- }, [parent]);
17
- return {
18
- begin: () => graphRef.current?.begin(),
19
- end: () => graphRef.current?.end()
20
- };
9
+ const graphRef = useRef(null);
10
+ useEffect(() => {
11
+ if (!parent) return;
12
+ const graph = parent.addBlade({
13
+ view: "fpsgraph",
14
+ label: "fps",
15
+ rows: 2
16
+ });
17
+ graphRef.current = graph;
18
+ return () => {
19
+ graph.dispose();
20
+ graphRef.current = null;
21
+ };
22
+ }, [parent]);
23
+ return {
24
+ begin: () => graphRef.current?.begin(),
25
+ end: () => graphRef.current?.end()
26
+ };
21
27
  }
22
- export {
23
- useFpsGraph
24
- };
28
+ //#endregion
29
+ export { useFpsGraph };
30
+
25
31
  //# sourceMappingURL=use-fps-graph.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/use-fps-graph.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport type { FpsGraphBladeApi } from '@tweakpane/plugin-essentials'\nimport type { PaneParent } from './use-pane-input.js'\n\nexport interface FpsGraphHandle {\n begin: () => void\n end: () => void\n}\n\n/**\n * Add an FPS graph blade. Returns { begin, end } for use inside useFrame.\n * If the pane was created with `fps: true` (default), you can skip this hook\n * and use paneBundle.fpsGraph directly.\n */\nexport function useFpsGraph(parent: PaneParent | null): FpsGraphHandle {\n const graphRef = useRef<FpsGraphBladeApi | null>(null)\n\n useEffect(() => {\n if (!parent) return\n\n const graph = parent.addBlade({\n view: 'fpsgraph',\n label: 'fps',\n rows: 2,\n }) as FpsGraphBladeApi\n\n graphRef.current = graph\n\n return () => {\n graph.dispose()\n graphRef.current = null\n }\n }, [parent])\n\n return {\n begin: () => graphRef.current?.begin(),\n end: () => graphRef.current?.end(),\n }\n}\n"],"mappings":"AAAA,SAAS,WAAW,cAAc;AAc3B,SAAS,YAAY,QAA2C;AACrE,QAAM,WAAW,OAAgC,IAAI;AAErD,YAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AAEb,UAAM,QAAQ,OAAO,SAAS;AAAA,MAC5B,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,IACR,CAAC;AAED,aAAS,UAAU;AAEnB,WAAO,MAAM;AACX,YAAM,QAAQ;AACd,eAAS,UAAU;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AAAA,IACL,OAAO,MAAM,SAAS,SAAS,MAAM;AAAA,IACrC,KAAK,MAAM,SAAS,SAAS,IAAI;AAAA,EACnC;AACF;","names":[]}
1
+ {"version":3,"file":"use-fps-graph.js","names":[],"sources":["../../src/react/use-fps-graph.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport type { FpsGraphBladeApi } from '@tweakpane/plugin-essentials'\nimport type { PaneParent } from './use-pane-input.js'\n\nexport interface FpsGraphHandle {\n begin: () => void\n end: () => void\n}\n\n/**\n * Add an FPS graph blade. Returns { begin, end } for use inside useFrame.\n * If the pane was created with `fps: true` (default), you can skip this hook\n * and use paneBundle.fpsGraph directly.\n */\nexport function useFpsGraph(parent: PaneParent | null): FpsGraphHandle {\n const graphRef = useRef<FpsGraphBladeApi | null>(null)\n\n useEffect(() => {\n if (!parent) return\n\n const graph = parent.addBlade({\n view: 'fpsgraph',\n label: 'fps',\n rows: 2,\n }) as FpsGraphBladeApi\n\n graphRef.current = graph\n\n return () => {\n graph.dispose()\n graphRef.current = null\n }\n }, [parent])\n\n return {\n begin: () => graphRef.current?.begin(),\n end: () => graphRef.current?.end(),\n }\n}\n"],"mappings":";;;;;;;AAcA,SAAgB,YAAY,QAA2C;CACrE,MAAM,WAAW,OAAgC,IAAI;CAErD,gBAAgB;EACd,IAAI,CAAC,QAAQ;EAEb,MAAM,QAAQ,OAAO,SAAS;GAC5B,MAAM;GACN,OAAO;GACP,MAAM;EACR,CAAC;EAED,SAAS,UAAU;EAEnB,aAAa;GACX,MAAM,QAAQ;GACd,SAAS,UAAU;EACrB;CACF,GAAG,CAAC,MAAM,CAAC;CAEX,OAAO;EACL,aAAa,SAAS,SAAS,MAAM;EACrC,WAAW,SAAS,SAAS,IAAI;CACnC;AACF"}
@@ -1,10 +1,10 @@
1
- import { PaneParent } from './use-pane-input.js';
2
- import 'tweakpane';
3
-
1
+ import { PaneParent } from "./use-pane-input.js";
2
+ //#region src/react/use-pane-button.d.ts
4
3
  /**
5
4
  * Add a button to a Tweakpane parent. Uses a ref for the callback to avoid stale closures.
6
5
  * Uses deferred disposal to survive React strict mode.
7
6
  */
8
7
  declare function usePaneButton(parent: PaneParent | null, title: string, onClick: () => void): void;
9
-
8
+ //#endregion
10
9
  export { usePaneButton };
10
+ //# sourceMappingURL=use-pane-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-pane-button.d.ts","names":[],"sources":["../../src/react/use-pane-button.ts"],"mappings":";;;;;;iBAOgB,cAAc,QAAQ,mBAAmB,eAAe"}
@@ -1,34 +1,39 @@
1
1
  import { useEffect, useRef } from "react";
2
+ //#region src/react/use-pane-button.ts
3
+ /**
4
+ * Add a button to a Tweakpane parent. Uses a ref for the callback to avoid stale closures.
5
+ * Uses deferred disposal to survive React strict mode.
6
+ */
2
7
  function usePaneButton(parent, title, onClick) {
3
- const callbackRef = useRef(onClick);
4
- callbackRef.current = onClick;
5
- const buttonRef = useRef(null);
6
- const mountedRef = useRef(false);
7
- if (parent && buttonRef.current === null) {
8
- const button = parent.addButton({ title });
9
- button.on("click", () => callbackRef.current());
10
- buttonRef.current = button;
11
- }
12
- useEffect(() => {
13
- mountedRef.current = true;
14
- if (parent && buttonRef.current === null) {
15
- const button = parent.addButton({ title });
16
- button.on("click", () => callbackRef.current());
17
- buttonRef.current = button;
18
- }
19
- return () => {
20
- const button = buttonRef.current;
21
- setTimeout(() => {
22
- if (!mountedRef.current && button) {
23
- button.dispose();
24
- if (buttonRef.current === button) buttonRef.current = null;
25
- }
26
- }, 0);
27
- mountedRef.current = false;
28
- };
29
- }, [parent]);
8
+ const callbackRef = useRef(onClick);
9
+ callbackRef.current = onClick;
10
+ const buttonRef = useRef(null);
11
+ const mountedRef = useRef(false);
12
+ if (parent && buttonRef.current === null) {
13
+ const button = parent.addButton({ title });
14
+ button.on("click", () => callbackRef.current());
15
+ buttonRef.current = button;
16
+ }
17
+ useEffect(() => {
18
+ mountedRef.current = true;
19
+ if (parent && buttonRef.current === null) {
20
+ const button = parent.addButton({ title });
21
+ button.on("click", () => callbackRef.current());
22
+ buttonRef.current = button;
23
+ }
24
+ return () => {
25
+ const button = buttonRef.current;
26
+ setTimeout(() => {
27
+ if (!mountedRef.current && button) {
28
+ button.dispose();
29
+ if (buttonRef.current === button) buttonRef.current = null;
30
+ }
31
+ }, 0);
32
+ mountedRef.current = false;
33
+ };
34
+ }, [parent]);
30
35
  }
31
- export {
32
- usePaneButton
33
- };
36
+ //#endregion
37
+ export { usePaneButton };
38
+
34
39
  //# sourceMappingURL=use-pane-button.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/use-pane-button.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport type { PaneParent } from './use-pane-input.js'\n\n/**\n * Add a button to a Tweakpane parent. Uses a ref for the callback to avoid stale closures.\n * Uses deferred disposal to survive React strict mode.\n */\nexport function usePaneButton(\n parent: PaneParent | null,\n title: string,\n onClick: () => void,\n): void {\n const callbackRef = useRef(onClick)\n callbackRef.current = onClick\n const buttonRef = useRef<{ dispose(): void } | null>(null)\n const mountedRef = useRef(false)\n\n if (parent && buttonRef.current === null) {\n const button = parent.addButton({ title })\n button.on('click', () => callbackRef.current())\n buttonRef.current = button\n }\n\n useEffect(() => {\n mountedRef.current = true\n\n if (parent && buttonRef.current === null) {\n const button = parent.addButton({ title })\n button.on('click', () => callbackRef.current())\n buttonRef.current = button\n }\n\n return () => {\n const button = buttonRef.current\n setTimeout(() => {\n if (!mountedRef.current && button) {\n button.dispose()\n if (buttonRef.current === button) buttonRef.current = null\n }\n }, 0)\n mountedRef.current = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [parent])\n}\n"],"mappings":"AAAA,SAAS,WAAW,cAAc;AAO3B,SAAS,cACd,QACA,OACA,SACM;AACN,QAAM,cAAc,OAAO,OAAO;AAClC,cAAY,UAAU;AACtB,QAAM,YAAY,OAAmC,IAAI;AACzD,QAAM,aAAa,OAAO,KAAK;AAE/B,MAAI,UAAU,UAAU,YAAY,MAAM;AACxC,UAAM,SAAS,OAAO,UAAU,EAAE,MAAM,CAAC;AACzC,WAAO,GAAG,SAAS,MAAM,YAAY,QAAQ,CAAC;AAC9C,cAAU,UAAU;AAAA,EACtB;AAEA,YAAU,MAAM;AACd,eAAW,UAAU;AAErB,QAAI,UAAU,UAAU,YAAY,MAAM;AACxC,YAAM,SAAS,OAAO,UAAU,EAAE,MAAM,CAAC;AACzC,aAAO,GAAG,SAAS,MAAM,YAAY,QAAQ,CAAC;AAC9C,gBAAU,UAAU;AAAA,IACtB;AAEA,WAAO,MAAM;AACX,YAAM,SAAS,UAAU;AACzB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,WAAW,QAAQ;AACjC,iBAAO,QAAQ;AACf,cAAI,UAAU,YAAY,OAAQ,WAAU,UAAU;AAAA,QACxD;AAAA,MACF,GAAG,CAAC;AACJ,iBAAW,UAAU;AAAA,IACvB;AAAA,EAEF,GAAG,CAAC,MAAM,CAAC;AACb;","names":[]}
1
+ {"version":3,"file":"use-pane-button.js","names":[],"sources":["../../src/react/use-pane-button.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport type { PaneParent } from './use-pane-input.js'\n\n/**\n * Add a button to a Tweakpane parent. Uses a ref for the callback to avoid stale closures.\n * Uses deferred disposal to survive React strict mode.\n */\nexport function usePaneButton(parent: PaneParent | null, title: string, onClick: () => void): void {\n const callbackRef = useRef(onClick)\n callbackRef.current = onClick\n const buttonRef = useRef<{ dispose(): void } | null>(null)\n const mountedRef = useRef(false)\n\n if (parent && buttonRef.current === null) {\n const button = parent.addButton({ title })\n button.on('click', () => callbackRef.current())\n buttonRef.current = button\n }\n\n useEffect(() => {\n mountedRef.current = true\n\n if (parent && buttonRef.current === null) {\n const button = parent.addButton({ title })\n button.on('click', () => callbackRef.current())\n buttonRef.current = button\n }\n\n return () => {\n const button = buttonRef.current\n setTimeout(() => {\n if (!mountedRef.current && button) {\n button.dispose()\n if (buttonRef.current === button) buttonRef.current = null\n }\n }, 0)\n mountedRef.current = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [parent])\n}\n"],"mappings":";;;;;;AAOA,SAAgB,cAAc,QAA2B,OAAe,SAA2B;CACjG,MAAM,cAAc,OAAO,OAAO;CAClC,YAAY,UAAU;CACtB,MAAM,YAAY,OAAmC,IAAI;CACzD,MAAM,aAAa,OAAO,KAAK;CAE/B,IAAI,UAAU,UAAU,YAAY,MAAM;EACxC,MAAM,SAAS,OAAO,UAAU,EAAE,MAAM,CAAC;EACzC,OAAO,GAAG,eAAe,YAAY,QAAQ,CAAC;EAC9C,UAAU,UAAU;CACtB;CAEA,gBAAgB;EACd,WAAW,UAAU;EAErB,IAAI,UAAU,UAAU,YAAY,MAAM;GACxC,MAAM,SAAS,OAAO,UAAU,EAAE,MAAM,CAAC;GACzC,OAAO,GAAG,eAAe,YAAY,QAAQ,CAAC;GAC9C,UAAU,UAAU;EACtB;EAEA,aAAa;GACX,MAAM,SAAS,UAAU;GACzB,iBAAiB;IACf,IAAI,CAAC,WAAW,WAAW,QAAQ;KACjC,OAAO,QAAQ;KACf,IAAI,UAAU,YAAY,QAAQ,UAAU,UAAU;IACxD;GACF,GAAG,CAAC;GACJ,WAAW,UAAU;EACvB;CAEF,GAAG,CAAC,MAAM,CAAC;AACb"}
@@ -1,6 +1,6 @@
1
- import { FolderParams, FolderApi } from 'tweakpane';
2
- import { PaneParent } from './use-pane-input.js';
3
-
1
+ import { PaneParent } from "./use-pane-input.js";
2
+ import { FolderApi, FolderParams } from "tweakpane";
3
+ //#region src/react/use-pane-folder.d.ts
4
4
  /**
5
5
  * Create a Tweakpane folder underneath `parent`. Disposes on unmount and
6
6
  * recreates when `parent` (or `title`) changes — e.g., when `usePane`
@@ -11,5 +11,6 @@ import { PaneParent } from './use-pane-input.js';
11
11
  * synchronously before paint, so consumers don't see a flicker.
12
12
  */
13
13
  declare function usePaneFolder(parent: PaneParent | null, title: string, options?: Partial<FolderParams>): FolderApi | null;
14
-
14
+ //#endregion
15
15
  export { usePaneFolder };
16
+ //# sourceMappingURL=use-pane-folder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-pane-folder.d.ts","names":[],"sources":["../../src/react/use-pane-folder.ts"],"mappings":";;;;;;;;;;;;iBAagB,cACd,QAAQ,mBACR,eACA,UAAS,QAAQ,gBAChB"}
@@ -1,24 +1,37 @@
1
1
  import { useEffectEvent, useLayoutEffect, useState } from "react";
2
+ //#region src/react/use-pane-folder.ts
3
+ /**
4
+ * Create a Tweakpane folder underneath `parent`. Disposes on unmount and
5
+ * recreates when `parent` (or `title`) changes — e.g., when `usePane`
6
+ * tears down and rebuilds its bundle on a StrictMode remount.
7
+ *
8
+ * Returns `null` on the first render before the layout effect commits,
9
+ * then the FolderApi on the next render. The two renders happen
10
+ * synchronously before paint, so consumers don't see a flicker.
11
+ */
2
12
  function usePaneFolder(parent, title, options = {}) {
3
- const [folder, setFolder] = useState(null);
4
- const getOptions = useEffectEvent(() => options);
5
- useLayoutEffect(() => {
6
- if (!parent) {
7
- setFolder(null);
8
- return;
9
- }
10
- const f = parent.addFolder({ expanded: false, ...getOptions(), title });
11
- setFolder(f);
12
- return () => {
13
- try {
14
- f.dispose();
15
- } catch {
16
- }
17
- };
18
- }, [parent, title]);
19
- return folder;
13
+ const [folder, setFolder] = useState(null);
14
+ const getOptions = useEffectEvent(() => options);
15
+ useLayoutEffect(() => {
16
+ if (!parent) {
17
+ setFolder(null);
18
+ return;
19
+ }
20
+ const f = parent.addFolder({
21
+ expanded: false,
22
+ ...getOptions(),
23
+ title
24
+ });
25
+ setFolder(f);
26
+ return () => {
27
+ try {
28
+ f.dispose();
29
+ } catch {}
30
+ };
31
+ }, [parent, title]);
32
+ return folder;
20
33
  }
21
- export {
22
- usePaneFolder
23
- };
34
+ //#endregion
35
+ export { usePaneFolder };
36
+
24
37
  //# sourceMappingURL=use-pane-folder.js.map