@craf-te/canvas-buffer-viewer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,307 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class k{constructor(t=10){this._lastCapture=0,this._interval=1e3/t}setFps(t){this._interval=1e3/t}shouldCapture(t){return t-this._lastCapture<this._interval?!1:(this._lastCapture=t,!0)}}const I=`
2
+ :host {
3
+ position: fixed;
4
+ pointer-events: none;
5
+ z-index: 99999;
6
+ font-family: monospace;
7
+ font-size: 12px;
8
+ color: #fff;
9
+ /* initial placeholder to not break while drag happens on the panel itself */
10
+ inset: 0;
11
+ }
12
+
13
+ .fbv-panel {
14
+ position: absolute;
15
+ pointer-events: auto;
16
+ background: rgba(0, 0, 0, 0.75);
17
+ border: 1px solid rgba(255, 255, 255, 0.25);
18
+ border-radius: 4px;
19
+ overflow: visible;
20
+ display: flex;
21
+ flex-direction: column;
22
+ min-width: 200px;
23
+ min-height: 120px;
24
+ transition: width 0.3s ease, height 0.3s ease, top 0.3s ease, left 0.3s ease;
25
+ }
26
+
27
+ /* Bottom position: header at bottom, content opens upward */
28
+ :host([bottom]) .fbv-panel {
29
+ flex-direction: column-reverse;
30
+ }
31
+
32
+ :host([bottom]) .fbv-header {
33
+ border-radius: 0 0 4px 4px;
34
+ }
35
+
36
+ :host([bottom]) .fbv-grid {
37
+ border-radius: 4px 4px 0 0;
38
+ }
39
+
40
+ /* Invisible hit area for edge dragging */
41
+ .fbv-panel::before {
42
+ content: '';
43
+ position: absolute;
44
+ inset: -8px;
45
+ cursor: grab;
46
+ pointer-events: auto;
47
+ z-index: -1;
48
+ }
49
+
50
+ .fbv-panel:active::before {
51
+ cursor: grabbing;
52
+ }
53
+
54
+ /* Clip inner content to panel bounds */
55
+ .fbv-panel .fbv-grid {
56
+ overflow: hidden;
57
+ }
58
+
59
+ /* Disable transition when dragging/resizing for immediate feedback */
60
+ .fbv-panel.dragging, .fbv-panel.resizing {
61
+ transition: none;
62
+ }
63
+
64
+ /* Maximize state overrides */
65
+ :host([maximized]) .fbv-panel {
66
+ top: 0 !important;
67
+ left: 0 !important;
68
+ width: 100vw !important;
69
+ height: 100vh !important;
70
+ border-radius: 0;
71
+ border: none;
72
+ }
73
+
74
+ :host([minimized]) .fbv-panel {
75
+ min-height: auto;
76
+ height: auto !important;
77
+ }
78
+
79
+ :host([minimized]) .fbv-grid,
80
+ :host([minimized]) .fbv-resize {
81
+ display: none;
82
+ }
83
+
84
+ .fbv-header {
85
+ display: flex;
86
+ align-items: center;
87
+ padding: 2px 6px;
88
+ background: rgba(255, 255, 255, 0.1);
89
+ cursor: grab;
90
+ user-select: none;
91
+ font-size: 11px;
92
+ line-height: 18px;
93
+ }
94
+
95
+ .fbv-header:active {
96
+ cursor: grabbing;
97
+ }
98
+
99
+ .fbv-header-title {
100
+ flex: 1;
101
+ white-space: nowrap;
102
+ overflow: hidden;
103
+ text-overflow: ellipsis;
104
+ }
105
+
106
+ .fbv-header-btn {
107
+ flex-shrink: 0;
108
+ width: 18px;
109
+ height: 18px;
110
+ margin-left: 2px;
111
+ padding: 0;
112
+ border: none;
113
+ background: none;
114
+ color: rgba(255, 255, 255, 0.6);
115
+ font-size: 14px;
116
+ line-height: 18px;
117
+ text-align: center;
118
+ cursor: pointer;
119
+ border-radius: 2px;
120
+ }
121
+
122
+ .fbv-header-btn:hover {
123
+ background: rgba(255, 255, 255, 0.15);
124
+ color: #fff;
125
+ }
126
+
127
+ .fbv-header-btn.back-btn {
128
+ margin-right: 6px;
129
+ font-size: 16px;
130
+ }
131
+
132
+ .fbv-grid {
133
+ flex: 1;
134
+ display: grid;
135
+ gap: 4px;
136
+ padding: 4px;
137
+ overflow: hidden;
138
+ }
139
+
140
+ .fbv-grid.single-view {
141
+ display: block;
142
+ padding: 0;
143
+ }
144
+
145
+ .fbv-grid.single-view fbv-thumbnail {
146
+ display: none;
147
+ }
148
+
149
+ .fbv-grid.single-view fbv-thumbnail.selected {
150
+ display: flex;
151
+ height: 100%;
152
+ width: 100%;
153
+ }
154
+
155
+ /* Hover effect for thumbnails in list view */
156
+ .fbv-grid:not(.single-view) fbv-thumbnail:hover {
157
+ outline: 2px solid rgba(100, 180, 255, 0.7);
158
+ outline-offset: -2px;
159
+ }
160
+
161
+ .fbv-resize {
162
+ position: absolute;
163
+ width: 16px;
164
+ height: 16px;
165
+ }
166
+
167
+ .fbv-resize-tl {
168
+ top: -4px;
169
+ left: -4px;
170
+ cursor: nwse-resize;
171
+ }
172
+
173
+ .fbv-resize-tr {
174
+ top: -4px;
175
+ right: -4px;
176
+ cursor: nesw-resize;
177
+ }
178
+
179
+ .fbv-resize-bl {
180
+ bottom: -4px;
181
+ left: -4px;
182
+ cursor: nesw-resize;
183
+ }
184
+
185
+ .fbv-resize-br {
186
+ bottom: -4px;
187
+ right: -4px;
188
+ cursor: nwse-resize;
189
+ }
190
+ `,T=`
191
+ :host {
192
+ display: flex;
193
+ flex-direction: column;
194
+ overflow: hidden;
195
+ min-height: 0;
196
+ font-family: monospace;
197
+ color: #fff;
198
+ cursor: pointer;
199
+ }
200
+
201
+ .fbv-buffer-header {
202
+ display: flex;
203
+ align-items: center;
204
+ background: rgba(0, 0, 0, 0.4);
205
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
206
+ }
207
+
208
+ .fbv-buffer-label {
209
+ padding: 2px 4px;
210
+ font-size: 10px;
211
+ line-height: 14px;
212
+ opacity: 0.8;
213
+ white-space: nowrap;
214
+ flex-shrink: 0;
215
+ }
216
+
217
+ .fbv-buffer-note {
218
+ flex-grow: 1;
219
+ margin-left: 4px;
220
+ overflow: hidden;
221
+ white-space: nowrap;
222
+ font-size: 10px;
223
+ color: #bbb;
224
+ position: relative;
225
+ border-left: 1px solid rgba(255, 255, 255, 0.2);
226
+ padding-left: 4px;
227
+ cursor: help;
228
+ }
229
+
230
+ .fbv-buffer-note-text {
231
+ display: inline-block;
232
+ padding-right: 100%;
233
+ animation: fbv-marquee 10s linear infinite;
234
+ }
235
+
236
+ .fbv-buffer-note:hover .fbv-buffer-note-text {
237
+ animation-play-state: paused;
238
+ }
239
+
240
+ .fbv-buffer-note::after {
241
+ content: attr(title);
242
+ position: absolute;
243
+ left: 4px;
244
+ top: 100%;
245
+ margin-top: 2px;
246
+ background: rgba(0, 0, 0, 0.9);
247
+ color: #fff;
248
+ padding: 4px 6px;
249
+ border-radius: 2px;
250
+ border: 1px solid rgba(255, 255, 255, 0.3);
251
+ font-size: 10px;
252
+ white-space: normal;
253
+ word-wrap: break-word;
254
+ max-width: 200px;
255
+ z-index: 100000;
256
+ opacity: 0;
257
+ pointer-events: none;
258
+ transition: opacity 0.2s;
259
+ display: none;
260
+ }
261
+
262
+ .fbv-buffer-note:hover::after {
263
+ display: block;
264
+ opacity: 1;
265
+ }
266
+
267
+ @keyframes fbv-marquee {
268
+ 0% { transform: translateX(0); }
269
+ 100% { transform: translateX(-100%); }
270
+ }
271
+
272
+ canvas {
273
+ flex: 1;
274
+ width: 100%;
275
+ min-height: 0;
276
+ display: block;
277
+ image-rendering: pixelated;
278
+ object-fit: contain;
279
+ }
280
+ `;class C extends HTMLElement{constructor(){super(),this._label="",this._lastWidth=0,this._lastHeight=0,this._lastNote=void 0,this.attachShadow({mode:"open"})}connectedCallback(){this._render()}set label(t){this._label=t,this._labelEl&&this._updateLabelText()}get label(){return this._label}get lastWidth(){return this._lastWidth}get lastHeight(){return this._lastHeight}_render(){this.shadowRoot&&(this.shadowRoot.innerHTML=`
281
+ <style>${T}</style>
282
+ <div class="fbv-buffer-header">
283
+ <div class="fbv-buffer-label"></div>
284
+ <div class="fbv-buffer-note" style="display: none;">
285
+ <span class="fbv-buffer-note-text"></span>
286
+ </div>
287
+ </div>
288
+ <canvas></canvas>
289
+ `,this._labelEl=this.shadowRoot.querySelector(".fbv-buffer-label"),this._noteEl=this.shadowRoot.querySelector(".fbv-buffer-note"),this._noteTextEl=this.shadowRoot.querySelector(".fbv-buffer-note-text"),this.canvas=this.shadowRoot.querySelector("canvas"),this.ctx=this.canvas.getContext("2d"),this._updateLabelText())}_updateLabelText(){this._lastWidth>0&&this._lastHeight>0?this._labelEl.textContent=`${this._label} (${this._lastWidth}x${this._lastHeight})`:this._labelEl.textContent=this._label}updateImage(t,e,i,s=!0,n){if(!this.canvas)return;(this.canvas.width!==e||this.canvas.height!==i)&&(this.canvas.width=e,this.canvas.height=i),(this._lastWidth!==e||this._lastHeight!==i)&&(this._lastWidth=e,this._lastHeight=i,this._updateLabelText()),this._lastNote!==n&&(this._lastNote=n,n?(this._noteEl.style.display="block",this._noteTextEl.textContent=n,this._noteEl.title=n,this._noteTextEl.style.animation="none",this._noteTextEl.offsetWidth,this._noteTextEl.style.animation=""):(this._noteEl.style.display="none",this._noteTextEl.textContent="",this._noteEl.title=""));const o=this.ctx.createImageData(e,i);if(s)for(let r=0;r<i;r++){const d=(i-1-r)*e*4,l=r*e*4;o.data.set(t.subarray(d,d+e*4),l)}else o.data.set(t);this.ctx.putImageData(o,0,0)}dispose(){this.remove()}}customElements.get("fbv-thumbnail")||customElements.define("fbv-thumbnail",C);const M="fbv-state",f=420,u=240,v=12,B=4,g=4,A=22;function H(a,t,e){if(e<=0)return{cols:1,rows:1,cellArea:0};if(e===1){const o=a-g*2,r=t-g*2;return{cols:1,rows:1,cellArea:o*r}}const i=a-g*2,s=t-g*2;let n={cols:1,rows:e,cellArea:0};for(let o=1;o<=e;o++){const r=Math.ceil(e/o),d=(i-B*(o-1))/o,l=(s-B*(r-1))/r;if(d<60||l<40)continue;const h=d*l;h>n.cellArea&&(n={cols:o,rows:r,cellArea:h})}return n}class $ extends HTMLElement{constructor(){super(),this._corner="top-right",this._initialPosSet=!1,this._preMaxState=null,this._selectedLabel=null,this._resizeObserver=null,this.attachShadow({mode:"open"}),this._state=this._loadState(),this._boundEscHandler=t=>{t.key==="Escape"&&(this._selectedLabel!==null?this.deselectItem():this._state.maximized&&this._toggleMaximize(!1))}}connectedCallback(){this._render(),this._applyBottomAttribute(),this._applyState(),document.addEventListener("keydown",this._boundEscHandler),this._initResizeObserver()}disconnectedCallback(){var t;document.removeEventListener("keydown",this._boundEscHandler),(t=this._resizeObserver)==null||t.disconnect(),this._resizeObserver=null}set corner(t){this._corner=t,this._applyBottomAttribute(),!this._initialPosSet&&this._state.top===null&&this._state.left===null&&this._applyDefaultCornerPos()}_applyBottomAttribute(){this._corner.indexOf("bottom")>=0?this.setAttribute("bottom",""):this.removeAttribute("bottom")}_render(){if(!this.shadowRoot)return;this.shadowRoot.innerHTML=`
290
+ <style>${I}</style>
291
+ <div class="fbv-panel">
292
+ <div class="fbv-header">
293
+ <button class="fbv-header-btn back-btn" title="リストに戻る" style="display:none;">←</button>
294
+ <span class="fbv-header-title">Framebuffer Viewer</span>
295
+ <button class="fbv-header-btn reset-btn" title="Reset Position">↺</button>
296
+ <button class="fbv-header-btn minimize-btn" title="Minimize">−</button>
297
+ <button class="fbv-header-btn restore-btn" title="Restore" style="display:none;">□</button>
298
+ <button class="fbv-header-btn maximize-btn" title="Maximize">▣</button>
299
+ <button class="fbv-header-btn unmaximize-btn" title="Unmaximize" style="display:none;">▨</button>
300
+ </div>
301
+ <div class="fbv-grid"></div>
302
+ <div class="fbv-resize fbv-resize-tl"></div>
303
+ <div class="fbv-resize fbv-resize-tr"></div>
304
+ <div class="fbv-resize fbv-resize-bl"></div>
305
+ <div class="fbv-resize fbv-resize-br"></div>
306
+ </div>
307
+ `,this._panelEl=this.shadowRoot.querySelector(".fbv-panel"),this._gridEl=this.shadowRoot.querySelector(".fbv-grid"),this._minimizeBtn=this.shadowRoot.querySelector(".minimize-btn"),this._restoreBtn=this.shadowRoot.querySelector(".restore-btn"),this._maximizeBtn=this.shadowRoot.querySelector(".maximize-btn"),this._unmaximizeBtn=this.shadowRoot.querySelector(".unmaximize-btn"),this._backBtn=this.shadowRoot.querySelector(".back-btn");const t=this.shadowRoot.querySelector(".fbv-header"),e=this.shadowRoot.querySelector(".reset-btn");this._initDrag(t),this._initResize(),this._minimizeBtn.addEventListener("click",i=>{i.stopPropagation(),this._toggleMinimize(!0)}),this._restoreBtn.addEventListener("click",i=>{i.stopPropagation(),this._toggleMinimize(!1)}),this._maximizeBtn.addEventListener("click",i=>{i.stopPropagation(),this._toggleMaximize(!0)}),this._unmaximizeBtn.addEventListener("click",i=>{i.stopPropagation(),this._toggleMaximize(!1)}),e.addEventListener("click",i=>{i.stopPropagation(),this._resetState()}),this._backBtn.addEventListener("click",i=>{i.stopPropagation(),this.deselectItem()}),this._gridEl.addEventListener("click",i=>{const s=i.target.closest("fbv-thumbnail");if(!s)return;const n=s.dataset.label;n&&(this._selectedLabel===n?this.deselectItem():this.selectItem(n))})}addThumbnail(t){const e=document.createElement("fbv-thumbnail");return e.label=t,e.dataset.label=t,this._gridEl.appendChild(e),this._updateGridColumns(),this._selectedLabel===t&&this.selectItem(t),e}removeThumbnail(t){const e=this._gridEl.querySelector(`fbv-thumbnail[data-label="${t}"]`);e&&(this._selectedLabel===t&&this.deselectItem(),e.remove(),this._updateGridColumns())}get items(){return this._gridEl?Array.from(this._gridEl.querySelectorAll("fbv-thumbnail")):[]}getItem(t){return this.items.find(e=>e.label===t)}dispose(){this.remove()}selectItem(t){const e=this.getItem(t);e&&(this.items.forEach(i=>i.classList.remove("selected")),this._selectedLabel=t,e.classList.add("selected"),this._gridEl.classList.add("single-view"),this._backBtn.style.display="",this._state.selectedLabel=t,this._saveState())}deselectItem(){this._selectedLabel=null,this.items.forEach(t=>t.classList.remove("selected")),this._gridEl.classList.remove("single-view"),this._backBtn.style.display="none",this._state.selectedLabel=null,this._saveState()}_updateGridColumns(){if(!this._gridEl||!this._panelEl)return;const t=this._panelEl.clientWidth,e=this._panelEl.clientHeight-A,i=H(t,e,this.items.length);this._gridEl.style.gridTemplateColumns=`repeat(${i.cols}, 1fr)`,this._gridEl.style.gridTemplateRows=`repeat(${i.rows}, 1fr)`}_initResizeObserver(){this._panelEl&&(this._resizeObserver=new ResizeObserver(()=>{this._updateGridColumns()}),this._resizeObserver.observe(this._panelEl))}_loadState(){try{const t=sessionStorage.getItem(M);if(t){const e=JSON.parse(t);return"selectedLabel"in e||(e.selectedLabel=null),e}}catch{}return{width:f,height:u,top:null,left:null,minimized:!1,maximized:!1,selectedLabel:null}}_saveState(){try{sessionStorage.setItem(M,JSON.stringify(this._state))}catch{}}_applyState(){this._state.width&&this._state.height&&(this._panelEl.style.width=`${this._state.width}px`,this._panelEl.style.height=`${this._state.height}px`),this._state.top!==null&&this._state.left!==null?(this._panelEl.style.top=`${this._state.top}px`,this._panelEl.style.left=`${this._state.left}px`,this._initialPosSet=!0):this._applyDefaultCornerPos(),this._toggleMinimize(this._state.minimized,!1),this._toggleMaximize(this._state.maximized,!1),this._selectedLabel=this._state.selectedLabel}_resetState(){this._state={width:f,height:u,top:null,left:null,minimized:!1,maximized:!1,selectedLabel:null},this._initialPosSet=!1,this._preMaxState=null,this.deselectItem(),this._applyDefaultCornerPos(),this._applyState(),this._saveState()}_applyDefaultCornerPos(){if(!this._panelEl)return;this._panelEl.style.width=`${f}px`,this._panelEl.style.height=`${u}px`,this._panelEl.style.right="",this._panelEl.style.bottom="";const t=this._corner.indexOf("top")>=0,e=this._corner.indexOf("left")>=0,i=t?v:window.innerHeight-u-v,s=e?v:window.innerWidth-f-v;this._panelEl.style.top=`${i}px`,this._panelEl.style.left=`${s}px`,this._state.top=i,this._state.left=s,this._state.width=f,this._state.height=u}_toggleMinimize(t,e=!0){this._state.minimized=t,t?(this.setAttribute("minimized",""),this._minimizeBtn.style.display="none",this._restoreBtn.style.display="",this._state.maximized&&this._toggleMaximize(!1,!1)):(this.removeAttribute("minimized"),this._minimizeBtn.style.display="",this._restoreBtn.style.display="none"),e&&this._saveState()}_toggleMaximize(t,e=!0){this._state.maximized=t,t?(this.hasAttribute("maximized")||(this._preMaxState={width:this._state.width,height:this._state.height,top:this._state.top,left:this._state.left}),this.setAttribute("maximized",""),this._maximizeBtn.style.display="none",this._unmaximizeBtn.style.display="",this._state.minimized&&this._toggleMinimize(!1,!1)):(this.removeAttribute("maximized"),this._maximizeBtn.style.display="",this._unmaximizeBtn.style.display="none",this._preMaxState&&(this._state.width=this._preMaxState.width,this._state.height=this._preMaxState.height,this._state.top=this._preMaxState.top,this._state.left=this._preMaxState.left,this._panelEl.style.width=`${this._state.width}px`,this._panelEl.style.height=`${this._state.height}px`,this._panelEl.style.top=`${this._state.top}px`,this._panelEl.style.left=`${this._state.left}px`)),e&&this._saveState()}_initDrag(t){let e=0,i=0,s=0,n=0;const o=l=>{let h=s+(l.clientX-e),b=n+(l.clientY-i);this._panelEl.style.left=`${h}px`,this._panelEl.style.top=`${b}px`},r=()=>{this._panelEl.classList.remove("dragging"),document.removeEventListener("mousemove",o),document.removeEventListener("mouseup",r);const l=this._panelEl.getBoundingClientRect();this._state.left=l.left,this._state.top=l.top,this._saveState()},d=l=>{if(l.target.tagName.toLowerCase()==="button"||this._state.maximized)return;l.preventDefault(),this._panelEl.classList.add("dragging");const h=this._panelEl.getBoundingClientRect();e=l.clientX,i=l.clientY,s=h.left,n=h.top,document.addEventListener("mousemove",o),document.addEventListener("mouseup",r)};t.addEventListener("mousedown",d),this._panelEl.addEventListener("mousedown",l=>{const h=l.target;h.closest(".fbv-grid")||h.closest(".fbv-resize")||h.closest(".fbv-header")||h.closest("fbv-thumbnail")||d(l)})}_initResize(){["tl","tr","bl","br"].forEach(e=>{var s;const i=(s=this.shadowRoot)==null?void 0:s.querySelector(`.fbv-resize-${e}`);i&&i.addEventListener("mousedown",n=>{if(this._state.maximized)return;n.preventDefault(),n.stopPropagation(),this._panelEl.classList.add("resizing");const o=n.clientX,r=n.clientY,d=this._panelEl.getBoundingClientRect(),l=d.width,h=d.height,b=d.top,y=d.left,w=p=>{const z=p.clientX-o,L=p.clientY-r;let _=l,m=h,S=b,R=y;e==="tl"||e==="bl"?(_=Math.max(200,l-z),R=y+(l-_)):_=Math.max(200,l+z),e==="tl"||e==="tr"?(m=Math.max(120,h-L),S=b+(h-m)):m=Math.max(120,h+L),this._panelEl.style.width=`${_}px`,this._panelEl.style.height=`${m}px`,this._panelEl.style.top=`${S}px`,this._panelEl.style.left=`${R}px`},E=()=>{this._panelEl.classList.remove("resizing"),document.removeEventListener("mousemove",w),document.removeEventListener("mouseup",E);const p=this._panelEl.getBoundingClientRect();this._state.width=p.width,this._state.height=p.height,this._state.top=p.top,this._state.left=p.left,this._saveState()};document.addEventListener("mousemove",w),document.addEventListener("mouseup",E)})})}}customElements.get("fbv-panel")||customElements.define("fbv-panel",$);class P{constructor(t="top-right"){this._panel=null,this._corner=t}mount(){this._panel||(this._panel=document.createElement("fbv-panel"),this._panel.corner=this._corner,document.body.appendChild(this._panel))}unmount(){this._panel&&(this._panel.remove(),this._panel=null)}addItem(t){if(!this._panel)throw new Error("[BufferViewer] Not mounted yet.");const e=this._panel.getItem(t);return e||this._panel.addThumbnail(t)}removeItem(t){this._panel&&this._panel.removeThumbnail(t)}getItem(t){var e;return(e=this._panel)==null?void 0:e.getItem(t)}get items(){return this._panel?this._panel.items.values():[].values()}dispose(){this.unmount()}}const c=class c{constructor(t={}){this._slots=new Map,this._disposed=!1,this._fps=t.fps??10,this._overlay=new P(t.corner??"top-right"),this._active=!1,t.active!==!1&&(this.active=!0)}static getInstance(t){return c._instance||(c._instance=new c(t)),c._instance}get active(){return this._active}set active(t){this._disposed||(this._active=t,t?this._overlay.mount():this._overlay.unmount())}toggle(){this.active=!this._active}setFps(t){this._fps=t;for(const e of this._slots.values())e.scheduler.setFps(t)}capture(t,e,i){if(!this._active||this._disposed)return;const s=this._getOrCreateSlot(t),n=performance.now();if(!s.scheduler.shouldCapture(n))return;const o=e(),r=o.width??s.panel.lastWidth,d=o.height??s.panel.lastHeight;if(!r||!d)throw new Error(`[BufferViewer] "${t}": width/height required on first capture`);s.panel.updateImage(o.data,r,d,o.flipY??!0,i)}removeBuffer(t){this._slots.has(t)&&(this._overlay.removeItem(t),this._slots.delete(t))}dispose(){this._disposed=!0,this._active=!1,this._slots.clear(),this._overlay.dispose(),c._instance=null}_getOrCreateSlot(t){let e=this._slots.get(t);return e||(e={scheduler:new k(this._fps),panel:this._overlay.addItem(t)},this._slots.set(t,e)),e}};c._instance=null;let x=c;function D(a,t){const e=t.width,i=t.height,s=new Uint8Array(e*i*4);return a.readRenderTargetPixels(t,0,0,e,i,s),{data:s,width:e,height:i}}function O(a,t){const e=a.getParameter(a.FRAMEBUFFER_BINDING);t!==void 0&&a.bindFramebuffer(a.FRAMEBUFFER,t);const i=a.drawingBufferWidth,s=a.drawingBufferHeight,n=new Uint8Array(i*s*4);return a.readPixels(0,0,i,s,a.RGBA,a.UNSIGNED_BYTE,n),t!==void 0&&a.bindFramebuffer(a.FRAMEBUFFER,e),{data:n,width:i,height:s}}function q(a){let t;a instanceof HTMLCanvasElement||a instanceof OffscreenCanvas?t=a.getContext("2d"):t=a;const e=t.canvas.width,i=t.canvas.height,s=t.getImageData(0,0,e,i);return{data:new Uint8Array(s.data.buffer),width:e,height:i,flipY:!1}}exports.BufferViewer=x;exports.readCanvas=q;exports.readPixels=O;exports.readRenderTarget=D;
@@ -0,0 +1,65 @@
1
+ import { WebGLRenderer } from 'three';
2
+ import { WebGLRenderTarget } from 'three';
3
+
4
+ export declare class BufferViewer {
5
+ private static _instance;
6
+ private _overlay;
7
+ private _slots;
8
+ private _active;
9
+ private _disposed;
10
+ private _fps;
11
+ private constructor();
12
+ static getInstance(options?: ViewerOptions): BufferViewer;
13
+ get active(): boolean;
14
+ set active(value: boolean);
15
+ toggle(): void;
16
+ setFps(fps: number): void;
17
+ /**
18
+ * Capture and display pixel data under the given label.
19
+ * The getData callback is only invoked when the FPS throttle allows.
20
+ * It should return { data, width, height } with RGBA pixel data.
21
+ *
22
+ * Call this at any point in your render pipeline.
23
+ * The panel is auto-created on first use for each label.
24
+ */
25
+ capture(label: string, getData: () => CaptureData, note?: string): void;
26
+ removeBuffer(label: string): void;
27
+ dispose(): void;
28
+ private _getOrCreateSlot;
29
+ }
30
+
31
+ export declare interface CaptureData {
32
+ data: Uint8Array;
33
+ width?: number;
34
+ height?: number;
35
+ /** Set to false for top-to-bottom data (e.g. Canvas2D). Defaults to true (WebGL bottom-to-top). */
36
+ flipY?: boolean;
37
+ }
38
+
39
+ export declare type Corner = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
40
+
41
+ /**
42
+ * Read pixels from a Canvas2D context or canvas element.
43
+ * Returns RGBA Uint8Array compatible with the viewer.
44
+ */
45
+ export declare function readCanvas(source: HTMLCanvasElement | OffscreenCanvas | CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D): CaptureData;
46
+
47
+ /**
48
+ * Read pixels from the current framebuffer (or screen) of a WebGL context.
49
+ * If no framebuffer is bound, reads from the default (screen) framebuffer.
50
+ */
51
+ export declare function readPixels(gl: WebGLRenderingContext | WebGL2RenderingContext, framebuffer?: WebGLFramebuffer | null): CaptureData;
52
+
53
+ /**
54
+ * Read the color attachment of a Three.js WebGLRenderTarget
55
+ * and return it as CaptureData for the viewer.
56
+ */
57
+ export declare function readRenderTarget(renderer: WebGLRenderer, renderTarget: WebGLRenderTarget): CaptureData;
58
+
59
+ export declare interface ViewerOptions {
60
+ active?: boolean;
61
+ fps?: number;
62
+ corner?: Corner;
63
+ }
64
+
65
+ export { }