0)if(G0?!0:V||j.isRowDirty(G)||Y.has(G)||q.has(G))&&(U.add(G),G>0&&U.add(G-1),G0)if(G0?G-Math.floor(P):G;N=j.getLine(F)}else N=j.getLine(G);N&&this.renderLine(N,G,L.cols)}if(P===0&&K.visible&&this.cursorVisible){let G=K.style??this.cursorStyle;this.renderCursor(K.x,K.y,G)}Z&&J>0&&this.renderScrollbar(P,W,L.rows,J),this.lastCursorPosition={x:K.x,y:K.y},j.clearDirty()}renderLine(j,V,$){let Z=V*this.metrics.height;this.ctx.fillStyle=this.theme.background,this.ctx.fillRect(0,Z,$*this.metrics.width,this.metrics.height);for(let J=0;J0&&((J=this.currentBuffer)!=null&&J.getGraphemeString)?T=this.currentBuffer.getGraphemeString($,V):T=String.fromCodePoint(j.codepoint||32);let v=j.codepoint||32;if(this.renderBlockChar(v,O,P,X)||this.renderPowerlineGlyph(v,O,P,X)||this.ctx.fillText(T,Y,q),j.flags&_.FAINT&&(this.ctx.globalAlpha=1),j.flags&_.UNDERLINE){let U=P+this.metrics.baseline+2;this.ctx.strokeStyle=this.ctx.fillStyle,this.ctx.lineWidth=1,this.ctx.beginPath(),this.ctx.moveTo(O,U),this.ctx.lineTo(O+X,U),this.ctx.stroke()}if(j.flags&_.STRIKETHROUGH){let U=P+this.metrics.height/2;this.ctx.strokeStyle=this.ctx.fillStyle,this.ctx.lineWidth=1,this.ctx.beginPath(),this.ctx.moveTo(O,U),this.ctx.lineTo(O+X,U),this.ctx.stroke()}if(j.hyperlink_id>0&&j.hyperlink_id===this.hoveredHyperlinkId){let U=P+this.metrics.baseline+2;this.ctx.strokeStyle="#4A90E2",this.ctx.lineWidth=1,this.ctx.beginPath(),this.ctx.moveTo(O,U),this.ctx.lineTo(O+X,U),this.ctx.stroke()}if(this.hoveredLinkRange){let U=this.hoveredLinkRange;if($===U.startY&&V>=U.startX&&($U.startY&&$U.startY||V>=U.startX)){let G=P+this.metrics.baseline+2;this.ctx.strokeStyle="#4A90E2",this.ctx.lineWidth=1,this.ctx.beginPath(),this.ctx.moveTo(O,G),this.ctx.lineTo(O+X,G),this.ctx.stroke()}}}renderBlockChar(j,V,$,Z){let J=this.metrics.height;switch(j){case 9600:return this.ctx.fillRect(V,$,Z,J/2),!0;case 9601:return this.ctx.fillRect(V,$+J*7/8,Z,J/8),!0;case 9602:return this.ctx.fillRect(V,$+J*3/4,Z,J/4),!0;case 9603:return this.ctx.fillRect(V,$+J*5/8,Z,J*3/8),!0;case 9604:return this.ctx.fillRect(V,$+J/2,Z,J/2),!0;case 9605:return this.ctx.fillRect(V,$+J*3/8,Z,J*5/8),!0;case 9606:return this.ctx.fillRect(V,$+J/4,Z,J*3/4),!0;case 9607:return this.ctx.fillRect(V,$+J/8,Z,J*7/8),!0;case 9608:return this.ctx.fillRect(V,$,Z,J),!0;case 9609:return this.ctx.fillRect(V,$,Z*7/8,J),!0;case 9610:return this.ctx.fillRect(V,$,Z*3/4,J),!0;case 9611:return this.ctx.fillRect(V,$,Z*5/8,J),!0;case 9612:return this.ctx.fillRect(V,$,Z/2,J),!0;case 9613:return this.ctx.fillRect(V,$,Z*3/8,J),!0;case 9614:return this.ctx.fillRect(V,$,Z/4,J),!0;case 9615:return this.ctx.fillRect(V,$,Z/8,J),!0;case 9616:return this.ctx.fillRect(V+Z/2,$,Z/2,J),!0;case 9620:return this.ctx.fillRect(V,$,Z,J/8),!0;case 9621:return this.ctx.fillRect(V+Z*7/8,$,Z/8,J),!0;default:return!1}}renderPowerlineGlyph(j,V,$,Z){let J=this.metrics.height,O=this.ctx;switch(j){case 57520:return O.beginPath(),O.moveTo(V,$),O.lineTo(V+Z,$+J/2),O.lineTo(V,$+J),O.closePath(),O.fill(),!0;case 57521:return O.beginPath(),O.moveTo(V,$),O.lineTo(V+Z,$+J/2),O.lineTo(V,$+J),O.strokeStyle=O.fillStyle,O.lineWidth=1,O.stroke(),!0;case 57522:return O.beginPath(),O.moveTo(V+Z,$),O.lineTo(V,$+J/2),O.lineTo(V+Z,$+J),O.closePath(),O.fill(),!0;case 57523:return O.beginPath(),O.moveTo(V+Z,$),O.lineTo(V,$+J/2),O.lineTo(V+Z,$+J),O.strokeStyle=O.fillStyle,O.lineWidth=1,O.stroke(),!0;case 57524:return O.beginPath(),O.moveTo(V,$),O.ellipse(V,$+J/2,Z,J/2,0,-Math.PI/2,Math.PI/2,!1),O.closePath(),O.fill(),!0;case 57525:return O.beginPath(),O.moveTo(V,$),O.ellipse(V,$+J/2,Z,J/2,0,-Math.PI/2,Math.PI/2,!1),O.strokeStyle=O.fillStyle,O.lineWidth=1,O.stroke(),!0;case 57526:return O.beginPath(),O.moveTo(V+Z,$),O.ellipse(V+Z,$+J/2,Z,J/2,0,-Math.PI/2,Math.PI/2,!0),O.closePath(),O.fill(),!0;case 57527:return O.beginPath(),O.moveTo(V+Z,$),O.ellipse(V+Z,$+J/2,Z,J/2,0,-Math.PI/2,Math.PI/2,!0),O.strokeStyle=O.fillStyle,O.lineWidth=1,O.stroke(),!0;default:return!1}}renderCursor(j,V,$){var Z;let J=j*this.metrics.width,O=V*this.metrics.height,P=$??this.cursorStyle;switch(this.ctx.fillStyle=this.theme.cursor,P){case"block":this.ctx.fillRect(J,O,this.metrics.width,this.metrics.height);let X=(Z=this.currentBuffer)==null?void 0:Z.getLine(V);X!=null&&X[j]&&this.renderCellText(X[j],j,V,this.theme.cursorAccent);break;case"underline":let K=Math.max(2,Math.floor(this.metrics.height*0.15));this.ctx.fillRect(J,O+this.metrics.height-K,this.metrics.width,K);break;case"bar":let L=Math.max(2,Math.floor(this.metrics.width*0.15));this.ctx.fillRect(J,O,L,this.metrics.height);break}}startCursorBlink(){this.cursorBlinkInterval=window.setInterval(()=>{this.cursorVisible=!this.cursorVisible},530)}stopCursorBlink(){this.cursorBlinkInterval!==void 0&&(clearInterval(this.cursorBlinkInterval),this.cursorBlinkInterval=void 0),this.cursorVisible=!0}setTheme(j){this.theme={...c,...j},this.palette=[this.theme.black,this.theme.red,this.theme.green,this.theme.yellow,this.theme.blue,this.theme.magenta,this.theme.cyan,this.theme.white,this.theme.brightBlack,this.theme.brightRed,this.theme.brightGreen,this.theme.brightYellow,this.theme.brightBlue,this.theme.brightMagenta,this.theme.brightCyan,this.theme.brightWhite]}setFontSize(j){this.fontSize=j,this.metrics=this.measureFont()}setFontFamily(j){this.fontFamily=j,this.metrics=this.measureFont()}setCursorStyle(j){this.cursorStyle=j}setCursorBlink(j){j&&!this.cursorBlink?(this.cursorBlink=!0,this.startCursorBlink()):!j&&this.cursorBlink&&(this.cursorBlink=!1,this.stopCursorBlink())}renderScrollbar(j,V,$,Z=1){let J=this.ctx,O=this.canvas.height/this.devicePixelRatio,P=this.canvas.width/this.devicePixelRatio,K=P-8-4,W=O-8;if(J.fillStyle=this.theme.background,J.fillRect(K-2,0,14,O),Z<=0||V===0)return;let z=V+$,H=Math.max(20,$/z*W),Y=j/V,q=4+(W-H)*(1-Y);J.fillStyle=`rgba(128, 128, 128, ${0.1*Z})`,J.fillRect(K,4,8,W);let T=j>0?0.5:0.3;J.fillStyle=`rgba(128, 128, 128, ${T*Z})`,J.fillRect(K,q,8,H)}getMetrics(){return{...this.metrics}}getCanvas(){return this.canvas}setSelectionManager(j){this.selectionManager=j}isInSelection(j,V){let $=this.currentSelectionCoords;if(!$)return!1;let{startCol:Z,startRow:J,endCol:O,endRow:P}=$;return J===P?V===J&&j>=Z&&j<=O:V===J?j>=Z:V===P?j<=O:V>J&&VJ||$===J&&V>Z)&&([V,Z]=[Z,V],[$,J]=[J,$]);let O=this.wasmTerm.getScrollbackLength(),P="";for(let X=$;X<=J;X++){let K=null;if(X0)if(X=0?H=H.substring(0,L):H="",P+=H,X=J.cols;)P-=J.cols,O++;O=Math.min(O,J.rows-1);let X=this.getViewportY();this.selectionStart={col:V,absoluteRow:X+$},this.selectionEnd={col:P,absoluteRow:X+O},this.requestRender(),this.selectionChangedEmitter.fire()}selectLines(V,$){let Z=this.wasmTerm.getDimensions();V=Math.max(0,Math.min(V,Z.rows-1)),$=Math.max(0,Math.min($,Z.rows-1)),V>$&&([V,$]=[$,V]);let J=this.getViewportY();this.selectionStart={col:0,absoluteRow:J+V},this.selectionEnd={col:Z.cols-1,absoluteRow:J+$},this.requestRender(),this.selectionChangedEmitter.fire()}getSelectionPosition(){let V=this.normalizeSelection();if(V)return{start:{x:V.startCol,y:V.startRow},end:{x:V.endCol,y:V.endRow}}}deselect(){this.clearSelection(),this.selectionChangedEmitter.fire()}focus(){let V=this.renderer.getCanvas();V.parentElement&&V.parentElement.focus()}getSelectionCoords(){return this.normalizeSelection()}getDirtySelectionRows(){return this.dirtySelectionRows}clearDirtySelectionRows(){this.dirtySelectionRows.clear()}get onSelectionChange(){return this.selectionChangedEmitter.event}dispose(){this.selectionChangedEmitter.dispose(),this.stopAutoScroll(),this.boundMouseUpHandler&&(document.removeEventListener("mouseup",this.boundMouseUpHandler),this.boundMouseUpHandler=null),this.boundDocumentMouseMoveHandler&&(document.removeEventListener("mousemove",this.boundDocumentMouseMoveHandler),this.boundDocumentMouseMoveHandler=null),this.boundContextMenuHandler&&(this.renderer.getCanvas().removeEventListener("contextmenu",this.boundContextMenuHandler),this.boundContextMenuHandler=null),this.boundClickHandler&&(document.removeEventListener("click",this.boundClickHandler),this.boundClickHandler=null)}hasSGRMouseMode(){let V=this.terminal.wasmTerm;return V?V.getMode(1006,!1):!1}shouldReportMotion(V){let $=this.terminal.wasmTerm;return $?!!($.getMode(1003,!1)||$.getMode(1002,!1)&&V):!1}mapButton(V){return V>=0&&V<=2?V:null}encodeModifiers(V){let $=0;return V.shiftKey&&($+=4),(V.altKey||V.metaKey)&&($+=8),V.ctrlKey&&($+=16),$}sendMouseEvent(V,$,Z,J,O=0,P=!1){var X;if(!this.hasSGRMouseMode())return;let K=V+1,L=$+1,W=Z;P&&(W+=32),W+=O;let z=`\x1B[<${W};${K};${L}${J?"m":"M"}`;(X=this.terminal.dataEmitter)==null||X.fire(z)}sendScrollEvent(V,$,Z,J=0){let O=Z?64:65;this.sendMouseEvent(V,$,O,!1,J,!1)}attachEventListeners(){let V=this.renderer.getCanvas();V.addEventListener("mousedown",($)=>{V.parentElement&&V.parentElement.focus();let Z=this.pixelToCell($.offsetX,$.offsetY);if(this.terminal.hasMouseTracking()){let J=this.mapButton($.button);if(J===null)return;this.mouseButtonsPressed.add(J);let O=this.encodeModifiers($);this.sendMouseEvent(Z.col,Z.row,J,!1,O),$.preventDefault();return}if($.button===0){this.hasSelection()&&this.clearSelection();let J=this.viewportRowToAbsolute(Z.row);this.selectionStart={col:Z.col,absoluteRow:J},this.selectionEnd={col:Z.col,absoluteRow:J},this.isSelecting=!0}}),V.addEventListener("mousemove",($)=>{if(this.terminal.hasMouseTracking()){let Z=this.mouseButtonsPressed.size>0;if(this.shouldReportMotion(Z)){let J=this.pixelToCell($.offsetX,$.offsetY),O=this.encodeModifiers($),P=Z?[...this.mouseButtonsPressed][0]:3;this.sendMouseEvent(J.col,J.row,P,!1,O,!0);return}if(Z)return}if(this.isSelecting){this.markCurrentSelectionDirty();let Z=this.pixelToCell($.offsetX,$.offsetY),J=this.viewportRowToAbsolute(Z.row);this.selectionEnd={col:Z.col,absoluteRow:J},this.requestRender(),this.updateAutoScroll($.offsetY,V.clientHeight)}}),V.addEventListener("mouseleave",($)=>{if(this.isSelecting){let Z=V.getBoundingClientRect();$.clientYZ.bottom&&this.startAutoScroll(1)}}),V.addEventListener("mouseenter",()=>{this.isSelecting&&this.stopAutoScroll()}),this.boundDocumentMouseMoveHandler=($)=>{if(this.isSelecting){let Z=V.getBoundingClientRect(),J=Math.max(Z.left,Math.min($.clientX,Z.right)),O=Math.max(Z.top,Math.min($.clientY,Z.bottom)),P=J-Z.left,X=O-Z.top;if(($.clientXZ.right||$.clientYZ.bottom)&&($.clientYZ.bottom?this.startAutoScroll(1):this.stopAutoScroll(),this.autoScrollDirection===0)){this.markCurrentSelectionDirty();let K=this.pixelToCell(P,X),L=this.viewportRowToAbsolute(K.row);this.selectionEnd={col:K.col,absoluteRow:L},this.requestRender()}}},document.addEventListener("mousemove",this.boundDocumentMouseMoveHandler),document.addEventListener("mousedown",($)=>{this.mouseDownTarget=$.target}),this.boundMouseUpHandler=($)=>{let Z=this.mapButton($.button);if(Z!==null&&this.mouseButtonsPressed.has(Z)&&this.terminal.hasMouseTracking()){let J=V.getBoundingClientRect(),O=Math.max(0,Math.min($.clientX-J.left,J.width)),P=Math.max(0,Math.min($.clientY-J.top,J.height)),X=this.pixelToCell(O,P),K=this.encodeModifiers($);this.sendMouseEvent(X.col,X.row,Z,!0,K),this.mouseButtonsPressed.delete(Z);return}if(Z!==null&&this.mouseButtonsPressed.delete(Z),this.isSelecting){if(this.isSelecting=!1,this.stopAutoScroll(),this.selectionStart&&this.selectionEnd&&this.selectionStart.col===this.selectionEnd.col&&this.selectionStart.absoluteRow===this.selectionEnd.absoluteRow){this.selectionStart=null,this.selectionEnd=null;return}let J=this.getSelection();J&&(this.copyToClipboard(J),this.selectionChangedEmitter.fire())}},document.addEventListener("mouseup",this.boundMouseUpHandler),V.addEventListener("click",($)=>{if($.detail===2){let Z=this.pixelToCell($.offsetX,$.offsetY),J=this.getWordAtCell(Z.col,Z.row);if(J){let O=this.viewportRowToAbsolute(Z.row);this.selectionStart={col:J.startCol,absoluteRow:O},this.selectionEnd={col:J.endCol,absoluteRow:O},this.requestRender();let P=this.getSelection();P&&(this.copyToClipboard(P),this.selectionChangedEmitter.fire())}}else if($.detail>=3){let Z=this.pixelToCell($.offsetX,$.offsetY),J=this.viewportRowToAbsolute(Z.row),O=this.wasmTerm.getScrollbackLength(),P=null;if(J=0;K--)if(P[K]&&P[K].codepoint!==0&&P[K].codepoint!==32){X=K;break}}if(X>=0){this.selectionStart={col:0,absoluteRow:J},this.selectionEnd={col:X,absoluteRow:J},this.requestRender();let K=this.getSelection();K&&(this.copyToClipboard(K),this.selectionChangedEmitter.fire())}}}),this.boundContextMenuHandler=($)=>{if(this.terminal.hasMouseTracking()){$.preventDefault();return}if(this.renderer.getCanvas().getBoundingClientRect(),this.textarea.style.position="fixed",this.textarea.style.left=`${$.clientX}px`,this.textarea.style.top=`${$.clientY}px`,this.textarea.style.width="1px",this.textarea.style.height="1px",this.textarea.style.zIndex="1000",this.textarea.style.opacity="0",this.textarea.style.pointerEvents="auto",this.hasSelection()){let Z=this.getSelection();this.textarea.value=Z,this.textarea.select(),this.textarea.setSelectionRange(0,Z.length)}else this.textarea.value="";this.textarea.focus(),setTimeout(()=>{let Z=()=>{this.textarea.style.pointerEvents="none",this.textarea.style.zIndex="-10",this.textarea.style.width="0",this.textarea.style.height="0",this.textarea.style.left="0",this.textarea.style.top="0",this.textarea.value="",document.removeEventListener("click",Z),document.removeEventListener("contextmenu",Z),this.textarea.removeEventListener("blur",Z)};document.addEventListener("click",Z,{once:!0}),document.addEventListener("contextmenu",Z,{once:!0}),this.textarea.addEventListener("blur",Z,{once:!0})},10)},V.addEventListener("contextmenu",this.boundContextMenuHandler),this.boundClickHandler=($)=>{if(this.isSelecting||this.mouseDownTarget&&V.contains(this.mouseDownTarget))return;let Z=$.target;V.contains(Z)||this.hasSelection()&&this.clearSelection()},document.addEventListener("click",this.boundClickHandler)}markCurrentSelectionDirty(){let V=this.normalizeSelection();if(V)for(let $=V.startRow;$<=V.endRow;$++)this.dirtySelectionRows.add($)}updateAutoScroll(V,$){let Z=j.AUTO_SCROLL_EDGE_SIZE;V$-Z?this.startAutoScroll(1):this.stopAutoScroll()}startAutoScroll(V){this.autoScrollInterval!==null&&this.autoScrollDirection===V||(this.stopAutoScroll(),this.autoScrollDirection=V,this.autoScrollInterval=setInterval(()=>{if(!this.isSelecting){this.stopAutoScroll();return}let $=j.AUTO_SCROLL_SPEED*this.autoScrollDirection;if(this.terminal.scrollLines($),this.selectionEnd){let Z=this.wasmTerm.getDimensions();if(this.autoScrollDirection<0){let J=this.viewportRowToAbsolute(0);Jthis.selectionEnd.absoluteRow&&(this.selectionEnd={col:Z.cols-1,absoluteRow:J})}}this.requestRender()},j.AUTO_SCROLL_INTERVAL))}stopAutoScroll(){this.autoScrollInterval!==null&&(clearInterval(this.autoScrollInterval),this.autoScrollInterval=null),this.autoScrollDirection=0}pixelToCell(V,$){let Z=this.renderer.getMetrics(),J=Math.floor(V/Z.width),O=Math.floor($/Z.height);return{col:Math.max(0,Math.min(J,this.terminal.cols-1)),row:Math.max(0,Math.min(O,this.terminal.rows-1))}}normalizeSelection(){if(!this.selectionStart||!this.selectionEnd)return null;let{col:V,absoluteRow:$}=this.selectionStart,{col:Z,absoluteRow:J}=this.selectionEnd;($>J||$===J&&V>Z)&&([V,Z]=[Z,V],[$,J]=[J,$]);let O=this.absoluteRowToViewport($),P=this.absoluteRowToViewport(J),X=this.wasmTerm.getDimensions(),K=X.rows-1;return P<0||O>K?null:(O<0&&(O=0,V=0),P>K&&(P=K,Z=X.cols-1),{startCol:V,startRow:O,endCol:Z,endRow:P})}getWordAtCell(V,$){let Z=this.wasmTerm.getLine($);if(!Z)return null;let J=(X)=>{if(!X||X.codepoint===0)return!1;let K=String.fromCodePoint(X.codepoint);return/[\w\-./~:@+]/.test(K)};if(!J(Z[V]))return null;let O=V;for(;O>0&&J(Z[O-1]);)O--;let P=V;for(;P{console.warn("ClipboardItem write failed, trying writeText:",J),this.copyWithWriteText(V)});return}catch{}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(V).catch(($)=>{console.warn("Clipboard writeText failed, trying execCommand:",$),this.copyWithExecCommand(V)});return}this.copyWithExecCommand(V)}}copyWithWriteText(V){navigator.clipboard&&navigator.clipboard.writeText?navigator.clipboard.writeText(V).catch(($)=>{console.warn("Clipboard writeText failed, trying execCommand:",$),this.copyWithExecCommand(V)}):this.copyWithExecCommand(V)}copyWithExecCommand(V){let $=document.activeElement;try{let Z=this.textarea;Z.value=V,Z.style.position="fixed",Z.style.left="-9999px",Z.style.top="0",Z.style.width="1px",Z.style.height="1px",Z.style.opacity="0",Z.focus(),Z.select(),Z.setSelectionRange(0,V.length);let J=document.execCommand("copy");$&&$.focus(),J||console.warn("execCommand copy failed")}catch(Z){console.warn("execCommand copy threw:",Z),$&&$.focus()}}requestRender(){}};m.AUTO_SCROLL_EDGE_SIZE=30;m.AUTO_SCROLL_SPEED=3;m.AUTO_SCROLL_INTERVAL=50;var T0=m;class O0{constructor(j){this.terminal=j}getLine(j){var V;let $=this.terminal.getSnapshotCells();return $&&j>=0&&j<$.length?$[j]:((V=this.terminal.wasmTerm)==null?void 0:V.getLine(j))??null}getCursor(){var j;let V=this.terminal.getSnapshotCursor();return V?{...V,visible:!0}:((j=this.terminal.wasmTerm)==null?void 0:j.getCursor())??{x:0,y:0,visible:!0}}getDimensions(){return{cols:this.terminal.cols,rows:this.terminal.rows}}isRowDirty(j){var V;return this.terminal.isSnapshotDirty()?!0:((V=this.terminal.wasmTerm)==null?void 0:V.isRowDirty(j))??!1}needsFullRedraw(){if(this.terminal.isSnapshotDirty())return!0;let j=this.terminal.wasmTerm;return j!=null&&j.needsFullRedraw?j.needsFullRedraw():!1}clearDirty(){var j;this.terminal.clearSnapshotDirty(),(j=this.terminal.wasmTerm)==null||j.clearDirty()}getGraphemeString(j,V){let $=this.terminal.getSnapshotCells();if($&&j>=0&&j<$.length){let J=$[j][V];return J?String.fromCodePoint(J.codepoint||32):" "}let Z=this.terminal.wasmTerm;return Z!=null&&Z.getGraphemeString?Z.getGraphemeString(j,V):" "}}class d{constructor(j={}){this.unicode={get activeVersion(){return"15.1"}},this.dataEmitter=new b,this.resizeEmitter=new b,this.bellEmitter=new b,this.selectionChangeEmitter=new b,this.keyEmitter=new b,this.titleChangeEmitter=new b,this.scrollEmitter=new b,this.renderEmitter=new b,this.cursorMoveEmitter=new b,this.onData=this.dataEmitter.event,this.onResize=this.resizeEmitter.event,this.onBell=this.bellEmitter.event,this.onSelectionChange=this.selectionChangeEmitter.event,this.onKey=this.keyEmitter.event,this.onTitleChange=this.titleChangeEmitter.event,this.onScroll=this.scrollEmitter.event,this.onRender=this.renderEmitter.event,this.onCursorMove=this.cursorMoveEmitter.event,this.isOpen=!1,this.isDisposed=!1,this._isResizing=!1,this._writeQueue=[],this.addons=[],this.currentTitle="",this.viewportY=0,this.targetViewportY=0,this.lastCursorY=0,this.isDraggingScrollbar=!1,this.scrollbarDragStart=null,this.scrollbarDragStartViewportY=0,this.scrollbarVisible=!1,this.scrollbarOpacity=0,this.SCROLLBAR_HIDE_DELAY_MS=1500,this.SCROLLBAR_FADE_DURATION_MS=200,this.snapshotCells=null,this.snapshotCursor=null,this.snapshotDirty=!1,this.animateScroll=()=>{if(!this.wasmTerm||this.scrollAnimationStartTime===void 0)return;let $=this.options.smoothScrollDuration??100,Z=this.targetViewportY-this.viewportY;if(Math.abs(Z)<0.01){this.viewportY=this.targetViewportY,this.scrollEmitter.fire(Math.floor(this.viewportY)),this.getScrollbackLength()>0&&this.showScrollbar(),this.scrollAnimationFrame=void 0,this.scrollAnimationStartTime=void 0,this.scrollAnimationStartY=void 0;return}let J=1-(1/($/1000*60))**2;this.viewportY+=Z*J;let O=Math.floor(this.viewportY);this.scrollEmitter.fire(O),this.getScrollbackLength()>0&&this.showScrollbar(),this.scrollAnimationFrame=requestAnimationFrame(this.animateScroll)},this.handleMouseMove=($)=>{if(!(!this.canvas||!this.renderer||!this.wasmTerm)){if(this.isDraggingScrollbar){this.processScrollbarDrag($);return}if(this.linkDetector){if(this.mouseMoveThrottleTimeout){this.pendingMouseMove=$;return}this.processMouseMove($),this.mouseMoveThrottleTimeout=window.setTimeout(()=>{if(this.mouseMoveThrottleTimeout=void 0,this.pendingMouseMove){let Z=this.pendingMouseMove;this.pendingMouseMove=void 0,this.processMouseMove(Z)}},16)}}},this.handleMouseLeave=()=>{var $,Z;this.renderer&&this.wasmTerm&&((this.renderer.hoveredHyperlinkId||0)>0&&this.renderer.setHoveredHyperlinkId(0),this.renderer.setHoveredLinkRange(null)),this.currentHoveredLink&&((Z=($=this.currentHoveredLink).hover)==null||Z.call($,!1),this.currentHoveredLink=void 0,this.element&&(this.element.style.cursor="text"))},this.handleClick=async($)=>{if(!this.canvas||!this.renderer||!this.linkDetector||!this.wasmTerm)return;let Z=this.canvas.getBoundingClientRect(),J=Math.floor(($.clientX-Z.left)/this.renderer.charWidth),O=Math.floor(($.clientY-Z.top)/this.renderer.charHeight),P=this.wasmTerm.getScrollbackLength(),X,K=this.getViewportY(),L=Math.max(0,Math.floor(K));if(L>0)if(O{var Z,J,O,P;if($.preventDefault(),$.stopPropagation(),this.customWheelEventHandler&&this.customWheelEventHandler($))return;let X=((Z=this.wasmTerm)==null?void 0:Z.getMode(1006,!1))??!1;if(this.hasMouseTracking()&&X&&this.canvas&&this.renderer){let K=this.canvas.getBoundingClientRect(),L=$.clientX-K.left,W=$.clientY-K.top,z=this.renderer.getMetrics(),H=Math.max(0,Math.min(Math.floor(L/z.width),this.cols-1)),Y=Math.max(0,Math.min(Math.floor(W/z.height),this.rows-1)),q=0;$.shiftKey&&(q+=4),($.altKey||$.metaKey)&&(q+=8),$.ctrlKey&&(q+=16);let T=Math.min(Math.abs(Math.round($.deltaY/33)),5),v=($.deltaY<0?64:65)+q;for(let U=0;U0?"down":"up",L=Math.min(Math.abs(Math.round($.deltaY/33)),5);for(let W=0;W{if(!this.canvas||!this.renderer||!this.wasmTerm)return;let Z=this.wasmTerm.getScrollbackLength();if(Z===0)return;let J=this.canvas.getBoundingClientRect(),O=$.clientX-J.left,P=$.clientY-J.top,X=J.width,K=J.height,L=8,W=X-L-4,z=4;if(O>=W&&O<=W+L){$.preventDefault(),$.stopPropagation(),$.stopImmediatePropagation();let H=K-z*2,Y=this.rows,q=Z+Y,T=Math.max(20,Y/q*H),v=this.viewportY/Z,U=z+(H-T)*(1-v);if(P>=U&&P<=U+T)this.isDraggingScrollbar=!0,this.scrollbarDragStart=P,this.scrollbarDragStartViewportY=this.viewportY,this.canvas&&(this.canvas.style.userSelect="none",this.canvas.style.webkitUserSelect="none");else{let G=1-(P-z)/H,N=Math.round(G*Z);this.scrollToLine(Math.max(0,Math.min(Z,N)))}}},this.handleMouseUp=()=>{this.isDraggingScrollbar&&(this.isDraggingScrollbar=!1,this.scrollbarDragStart=null,this.canvas&&(this.canvas.style.userSelect="",this.canvas.style.webkitUserSelect=""),this.scrollbarVisible&&this.getScrollbackLength()>0&&this.showScrollbar())},this.ghostty=j.ghostty??b0(),this.linkClickHandler=j.onLinkClick;let V={cols:j.cols??80,rows:j.rows??24,cursorBlink:j.cursorBlink??!1,cursorStyle:j.cursorStyle??"block",theme:j.theme??{},scrollback:j.scrollback??1e4,fontSize:j.fontSize??15,fontFamily:j.fontFamily??"monospace",allowTransparency:j.allowTransparency??!1,convertEol:j.convertEol??!1,disableStdin:j.disableStdin??!1,smoothScrollDuration:j.smoothScrollDuration??100};this.options=new Proxy(V,{set:($,Z,J)=>{let O=$[Z];return $[Z]=J,this.isOpen&&this.handleOptionChange(Z,J,O),!0}}),this.cols=this.options.cols,this.rows=this.options.rows,this.buffer=new A(this),this.snapshotBuffer=new O0(this)}handleOptionChange(j,V,$){if(V!==$)switch(j){case"disableStdin":break;case"cursorBlink":case"cursorStyle":this.renderer&&(this.renderer.setCursorStyle(this.options.cursorStyle),this.renderer.setCursorBlink(this.options.cursorBlink));break;case"theme":this.renderer&&(this.renderer.setTheme(this.options.theme),this.wasmTerm&&this.renderer.render(this.wasmTerm,!0,this.viewportY,this));break;case"fontSize":this.renderer&&(this.renderer.setFontSize(this.options.fontSize),this.handleFontChange());break;case"fontFamily":this.renderer&&(this.renderer.setFontFamily(this.options.fontFamily),this.handleFontChange());break;case"cols":case"rows":this.resize(this.options.cols,this.options.rows);break}}handleFontChange(){if(!this.renderer||!this.wasmTerm||!this.canvas)return;this.selectionManager&&this.selectionManager.clearSelection(),this.renderer.resize(this.cols,this.rows);let j=this.renderer.getMetrics();this.canvas.width=j.width*this.cols,this.canvas.height=j.height*this.rows,this.canvas.style.width=`${j.width*this.cols}px`,this.canvas.style.height=`${j.height*this.rows}px`,this.renderer.render(this.wasmTerm,!0,this.viewportY,this)}parseColorToHex(j){if(!j)return 0;if(j.startsWith("#")){let $=j.slice(1);$.length===3&&($=$[0]+$[0]+$[1]+$[1]+$[2]+$[2]);let Z=Number.parseInt($,16);return Number.isNaN(Z)?0:Z}let V=j.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);if(V){let $=Number.parseInt(V[1],10),Z=Number.parseInt(V[2],10),J=Number.parseInt(V[3],10);return $<<16|Z<<8|J}return 0}buildWasmConfig(){let j=this.options.theme,V=this.options.scrollback;if(!j&&V===1e4)return;let $={...c,...j},Z=[this.parseColorToHex($.black),this.parseColorToHex($.red),this.parseColorToHex($.green),this.parseColorToHex($.yellow),this.parseColorToHex($.blue),this.parseColorToHex($.magenta),this.parseColorToHex($.cyan),this.parseColorToHex($.white),this.parseColorToHex($.brightBlack),this.parseColorToHex($.brightRed),this.parseColorToHex($.brightGreen),this.parseColorToHex($.brightYellow),this.parseColorToHex($.brightBlue),this.parseColorToHex($.brightMagenta),this.parseColorToHex($.brightCyan),this.parseColorToHex($.brightWhite)];return{scrollbackLimit:V,fgColor:this.parseColorToHex($.foreground),bgColor:this.parseColorToHex($.background),cursorColor:this.parseColorToHex($.cursor),palette:Z}}open(j){if(this.isOpen)throw Error("Terminal is already open");if(this.isDisposed)throw Error("Terminal has been disposed");this.element=j,this.isOpen=!0;try{j.setAttribute("tabindex","-1"),j.setAttribute("role","textbox"),j.setAttribute("aria-label","Terminal input"),j.setAttribute("aria-multiline","true");let V=this.buildWasmConfig();this.wasmTerm=this.ghostty.createTerminal(this.cols,this.rows,V),this.canvas=document.createElement("canvas"),this.canvas.style.display="block",j.appendChild(this.canvas),this.textarea=document.createElement("textarea"),this.textarea.setAttribute("autocorrect","off"),this.textarea.setAttribute("autocapitalize","off"),this.textarea.setAttribute("spellcheck","false"),this.textarea.setAttribute("tabindex","0"),this.textarea.setAttribute("aria-label","Terminal input"),this.textarea.style.position="absolute",this.textarea.style.left="0",this.textarea.style.top="0",this.textarea.style.width="1px",this.textarea.style.height="1px",this.textarea.style.padding="0",this.textarea.style.border="none",this.textarea.style.margin="0",this.textarea.style.opacity="0",this.textarea.style.clipPath="inset(50%)",this.textarea.style.overflow="hidden",this.textarea.style.whiteSpace="nowrap",this.textarea.style.resize="none",j.appendChild(this.textarea),this.compositionPreview=document.createElement("div"),this.compositionPreview.style.position="absolute",this.compositionPreview.style.top="4px",this.compositionPreview.style.right="4px",this.compositionPreview.style.padding="2px 8px",this.compositionPreview.style.backgroundColor="rgba(0, 0, 0, 0.7)",this.compositionPreview.style.color="#ffcc00",this.compositionPreview.style.fontFamily="monospace",this.compositionPreview.style.fontSize="12px",this.compositionPreview.style.borderRadius="3px",this.compositionPreview.style.display="none",this.compositionPreview.style.zIndex="1000",j.appendChild(this.compositionPreview),this.textarea.addEventListener("compositionupdate",(X)=>{X.data&&(this.compositionPreview.textContent=`조합중: ${X.data}`,this.compositionPreview.style.display="block")}),this.textarea.addEventListener("compositionend",()=>{this.compositionPreview.style.display="none"});let $=this.textarea;this.canvas.addEventListener("mousedown",(X)=>{X.preventDefault(),$.focus()}),this.canvas.addEventListener("touchend",(X)=>{X.preventDefault(),$.focus()}),j.addEventListener("mousedown",(X)=>{X.target===j&&(X.preventDefault(),$.focus())}),j.addEventListener("focus",()=>{$.focus()}),this.renderer=new J0(this.canvas,{fontSize:this.options.fontSize,fontFamily:this.options.fontFamily,cursorStyle:this.options.cursorStyle,cursorBlink:this.options.cursorBlink,theme:this.options.theme}),this.renderer.resize(this.cols,this.rows);let Z=this.canvas,J=this.renderer,O=this.wasmTerm,P={hasMouseTracking:()=>(O==null?void 0:O.hasMouseTracking())??!1,hasSgrMouseMode:()=>(O==null?void 0:O.getMode(1006,!1))??!0,getCellDimensions:()=>({width:J.charWidth,height:J.charHeight}),getCanvasOffset:()=>{let X=Z.getBoundingClientRect();return{left:X.left,top:X.top}}};this.inputHandler=new q0(this.ghostty,j,(X)=>{this.options.disableStdin||this.dataEmitter.fire(X)},()=>{this.bellEmitter.fire()},(X)=>{this.keyEmitter.fire(X)},this.customKeyEventHandler,(X)=>{var K;return((K=this.wasmTerm)==null?void 0:K.getMode(X,!1))??!1},()=>this.copySelection(),this.textarea,P),this.selectionManager=new T0(this,this.renderer,this.wasmTerm,this.textarea),this.renderer.setSelectionManager(this.selectionManager),this.selectionManager.onSelectionChange(()=>{this.selectionChangeEmitter.fire()}),this.linkDetector=new $0(this),this.linkDetector.registerProvider(new Z0(this)),this.linkDetector.registerProvider(new U0(this)),j.addEventListener("mousedown",this.handleMouseDown,{capture:!0}),j.addEventListener("mousemove",this.handleMouseMove),j.addEventListener("mouseleave",this.handleMouseLeave),j.addEventListener("click",this.handleClick),document.addEventListener("mouseup",this.handleMouseUp),j.addEventListener("wheel",this.handleWheel,{passive:!1,capture:!0}),this.isOpen=!0,this.renderer.render(this.snapshotBuffer,!0,this.viewportY,this,this.scrollbarOpacity),this.startRenderLoop(),this.focus()}catch(V){throw this.isOpen=!1,this.cleanupComponents(),Error(`Failed to open terminal: ${V}`)}}write(j,V){if(this.assertOpen(),this.options.convertEol&&typeof j=="string"&&(j=j.replace(/\n/g,`\r
`)),this._isResizing){let $=j instanceof Uint8Array?new Uint8Array(j):j;this._writeQueue.push({data:$,callback:V});return}this.writeInternal(j,V)}writeInternal(j,V){var $;this.wasmTerm.write(j),this.processTerminalResponses(),typeof j=="string"&&j.includes("\x07")?this.bellEmitter.fire():j instanceof Uint8Array&&j.includes(7)&&this.bellEmitter.fire(),($=this.linkDetector)==null||$.invalidateCache(),this.viewportY!==0&&this.scrollToBottom(),typeof j=="string"&&j.includes("\x1B]")&&this.checkForTitleChange(j),V&&requestAnimationFrame(V)}writeln(j,V){if(typeof j=="string")this.write(j+`\r
-`,V);else{let $=new Uint8Array(j.length+2);$.set(j),$[j.length]=13,$[j.length+1]=10,this.write($,V)}}paste(j){this.assertOpen(),!this.options.disableStdin&&(this.wasmTerm.hasBracketedPaste()?this.dataEmitter.fire("\x1B[200~"+j+"\x1B[201~"):this.dataEmitter.fire(j))}input(j,V=!1){this.assertOpen(),!this.options.disableStdin&&(V?this.dataEmitter.fire(j):this.write(j))}resize(j,V){if(this.assertOpen(),j===this.cols&&V===this.rows)return;this._resizeFlushFrameId&&(cancelAnimationFrame(this._resizeFlushFrameId),this._resizeFlushFrameId=void 0),this._isResizing=!0;let $=this.animationFrameId!==void 0;this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=void 0);try{this.wasmTerm.resize(j,V),this.cols=j,this.rows=V,this.renderer.resize(j,V);let Z=this.renderer.getMetrics();this.canvas.width=Z.width*j,this.canvas.height=Z.height*V,this.canvas.style.width=`${Z.width*j}px`,this.canvas.style.height=`${Z.height*V}px`,this.resizeEmitter.fire({cols:j,rows:V}),this.renderer.render(this.snapshotBuffer,!0,this.viewportY,this)}catch(Z){console.error("[ghostty-web] Resize error:",Z)}$&&this.startRenderLoop(),this._resizeFlushFrameId=requestAnimationFrame(()=>{this._resizeFlushFrameId=void 0,this._isResizing=!1,this.flushWriteQueue()})}flushWriteQueue(){if(this.isDisposed||!this.isOpen){this._writeQueue=[];return}let j=this._writeQueue;this._writeQueue=[];for(let{data:V,callback:$}of j)this.writeInternal(V,$)}clear(){this.assertOpen(),this.wasmTerm.write("\x1B[2J\x1B[H")}reset(){this.assertOpen(),this.wasmTerm&&this.wasmTerm.free();let j=this.buildWasmConfig();this.wasmTerm=this.ghostty.createTerminal(this.cols,this.rows,j),this.renderer.clear(),this.currentTitle=""}focus(){if(this.isOpen){let j=this.textarea||this.element;j&&(j.focus(),setTimeout(()=>{j==null||j.focus()},0))}}blur(){this.isOpen&&this.element&&this.element.blur()}loadAddon(j){j.activate(this),this.addons.push(j)}getSelection(){var j;return((j=this.selectionManager)==null?void 0:j.getSelection())||""}hasSelection(){var j;return((j=this.selectionManager)==null?void 0:j.hasSelection())||!1}clearSelection(){var j;(j=this.selectionManager)==null||j.clearSelection()}copySelection(){var j;return((j=this.selectionManager)==null?void 0:j.copySelection())||!1}selectAll(){var j;(j=this.selectionManager)==null||j.selectAll()}select(j,V,$){var Z;(Z=this.selectionManager)==null||Z.select(j,V,$)}selectLines(j,V){var $;($=this.selectionManager)==null||$.selectLines(j,V)}getViewportY(){return this.viewportY}getSelectionPosition(){var j;return(j=this.selectionManager)==null?void 0:j.getSelectionPosition()}attachCustomKeyEventHandler(j){this.customKeyEventHandler=j,this.inputHandler&&this.inputHandler.setCustomKeyEventHandler(j)}attachCustomWheelEventHandler(j){this.customWheelEventHandler=j}loadFonts(){this.renderer&&(this.renderer.remeasureFont(),this.handleFontChange())}registerLinkProvider(j){if(!this.linkDetector)throw Error("Terminal must be opened before registering link providers");this.linkDetector.registerProvider(j)}scrollLines(j){if(!this.wasmTerm)throw Error("Terminal not open");let V=this.getScrollbackLength(),$=Math.max(0,Math.min(V,this.viewportY-j));$!==this.viewportY&&(this.viewportY=$,this.scrollEmitter.fire(this.viewportY),V>0&&this.showScrollbar())}scrollPages(j){this.scrollLines(j*this.rows)}scrollToTop(){let j=this.getScrollbackLength();j>0&&this.viewportY!==j&&(this.viewportY=j,this.scrollEmitter.fire(this.viewportY),this.showScrollbar())}scrollToBottom(){this.viewportY!==0&&(this.viewportY=0,this.scrollEmitter.fire(this.viewportY),this.getScrollbackLength()>0&&this.showScrollbar())}scrollToLine(j){let V=this.getScrollbackLength(),$=Math.max(0,Math.min(V,j));$!==this.viewportY&&(this.viewportY=$,this.scrollEmitter.fire(this.viewportY),V>0&&this.showScrollbar())}smoothScrollTo(j){if(!this.wasmTerm)return;let V=this.getScrollbackLength(),$=Math.max(0,Math.min(V,j));if((this.options.smoothScrollDuration??100)===0){this.viewportY=$,this.targetViewportY=$,this.scrollEmitter.fire(Math.floor(this.viewportY)),V>0&&this.showScrollbar();return}this.targetViewportY=$,!this.scrollAnimationFrame&&(this.scrollAnimationStartTime=Date.now(),this.scrollAnimationStartY=this.viewportY,this.animateScroll())}dispose(){if(!this.isDisposed){this.isDisposed=!0,this.isOpen=!1,this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=void 0),this.scrollAnimationFrame&&(cancelAnimationFrame(this.scrollAnimationFrame),this.scrollAnimationFrame=void 0),this._resizeFlushFrameId&&(cancelAnimationFrame(this._resizeFlushFrameId),this._resizeFlushFrameId=void 0),this._writeQueue=[],this._isResizing=!1,this.mouseMoveThrottleTimeout&&(clearTimeout(this.mouseMoveThrottleTimeout),this.mouseMoveThrottleTimeout=void 0),this.pendingMouseMove=void 0;for(let j of this.addons)j.dispose();this.addons=[],this.cleanupComponents(),this.dataEmitter.dispose(),this.resizeEmitter.dispose(),this.bellEmitter.dispose(),this.selectionChangeEmitter.dispose(),this.keyEmitter.dispose(),this.titleChangeEmitter.dispose(),this.scrollEmitter.dispose(),this.renderEmitter.dispose(),this.cursorMoveEmitter.dispose()}}startRenderLoop(){let j=()=>{if(!this.isDisposed&&this.isOpen){try{this.renderer.render(this.snapshotBuffer,!1,this.viewportY,this,this.scrollbarOpacity);let V=this.wasmTerm.getCursor();V.y!==this.lastCursorY&&(this.lastCursorY=V.y,this.cursorMoveEmitter.fire())}catch(V){console.error("[ghostty-web] render loop error (recovering):",V)}this.animationFrameId=requestAnimationFrame(j)}};j()}getScrollbackLine(j){return this.wasmTerm?this.wasmTerm.getScrollbackLine(j):null}getScrollbackLength(){return this.wasmTerm?this.wasmTerm.getScrollbackLength():0}cleanupComponents(){this.selectionManager&&(this.selectionManager.dispose(),this.selectionManager=void 0),this.inputHandler&&(this.inputHandler.dispose(),this.inputHandler=void 0),this.renderer&&(this.renderer.dispose(),this.renderer=void 0),this.canvas&&this.canvas.parentNode&&(this.canvas.parentNode.removeChild(this.canvas),this.canvas=void 0),this.textarea&&this.textarea.parentNode&&(this.textarea.parentNode.removeChild(this.textarea),this.textarea=void 0),this.compositionPreview&&this.compositionPreview.parentNode&&(this.compositionPreview.parentNode.removeChild(this.compositionPreview),this.compositionPreview=void 0),this.element&&(this.element.removeEventListener("wheel",this.handleWheel),this.element.removeEventListener("mousedown",this.handleMouseDown,{capture:!0}),this.element.removeEventListener("mousemove",this.handleMouseMove),this.element.removeEventListener("mouseleave",this.handleMouseLeave),this.element.removeEventListener("click",this.handleClick),this.element.removeAttribute("role"),this.element.removeAttribute("aria-label"),this.element.removeAttribute("aria-multiline")),this.isOpen&&typeof document<"u"&&document.removeEventListener("mouseup",this.handleMouseUp),this.scrollbarHideTimeout&&(window.clearTimeout(this.scrollbarHideTimeout),this.scrollbarHideTimeout=void 0),this.linkDetector&&(this.linkDetector.dispose(),this.linkDetector=void 0),this.wasmTerm&&(this.wasmTerm.free(),this.wasmTerm=void 0),this.ghostty=void 0,this.element=void 0,this.textarea=void 0}assertOpen(){if(this.isDisposed)throw Error("Terminal has been disposed");if(!this.isOpen)throw Error("Terminal must be opened before use. Call terminal.open(parent) first.")}processMouseMove(j){if(!this.canvas||!this.renderer||!this.linkDetector||!this.wasmTerm)return;let V=this.canvas.getBoundingClientRect(),$=Math.floor((j.clientX-V.left)/this.renderer.charWidth),Z=Math.floor((j.clientY-V.top)/this.renderer.charHeight),J=0,O=null,X=this.getViewportY(),P=Math.max(0,Math.floor(X));if(P>0){let H=this.wasmTerm.getScrollbackLength();if(Z=0&&$0)if(Z{var q,_,b,U;if(H!==this.currentHoveredLink&&((_=(q=this.currentHoveredLink)==null?void 0:q.hover)==null||_.call(q,!1),this.currentHoveredLink=H,(b=H==null?void 0:H.hover)==null||b.call(H,!0),this.element&&(this.element.style.cursor=H?"pointer":"text"),this.renderer))if(H){let G=((U=this.wasmTerm)==null?void 0:U.getScrollbackLength())||0,T=this.getViewportY(),x=Math.max(0,Math.floor(T)),s=H.range.start.y-G+x,a=H.range.end.y-G+x;s=0?this.renderer.setHoveredLinkRange({startX:H.range.start.x,startY:Math.max(0,s),endX:H.range.end.x,endY:Math.min(this.rows-1,a)}):this.renderer.setHoveredLinkRange(null)}else this.renderer.setHoveredLinkRange(null)}).catch((H)=>{console.warn("Link detection error:",H)})}processScrollbarDrag(j){if(!this.canvas||!this.renderer||!this.wasmTerm||this.scrollbarDragStart===null)return;let V=this.wasmTerm.getScrollbackLength();if(V===0)return;let $=this.canvas.getBoundingClientRect(),Z=j.clientY-$.top-this.scrollbarDragStart,J=$.height-8,O=this.rows,X=V+O,P=Math.max(20,O/X*J),K=-Z/(J-P),L=Math.round(K*V),z=this.scrollbarDragStartViewportY+L;this.scrollToLine(Math.max(0,Math.min(V,z)))}showScrollbar(){this.scrollbarHideTimeout&&(window.clearTimeout(this.scrollbarHideTimeout),this.scrollbarHideTimeout=void 0),this.scrollbarVisible?this.scrollbarOpacity=1:(this.scrollbarVisible=!0,this.scrollbarOpacity=0,this.fadeInScrollbar()),this.isDraggingScrollbar||(this.scrollbarHideTimeout=window.setTimeout(()=>{this.hideScrollbar()},this.SCROLLBAR_HIDE_DELAY_MS))}hideScrollbar(){this.scrollbarHideTimeout&&(window.clearTimeout(this.scrollbarHideTimeout),this.scrollbarHideTimeout=void 0),this.scrollbarVisible&&this.fadeOutScrollbar()}fadeInScrollbar(){let j=Date.now(),V=()=>{let $=Date.now()-j,Z=Math.min($/this.SCROLLBAR_FADE_DURATION_MS,1);this.scrollbarOpacity=Z,this.renderer&&this.wasmTerm&&this.renderer.render(this.wasmTerm,!1,this.viewportY,this,this.scrollbarOpacity),Z<1&&requestAnimationFrame(V)};V()}fadeOutScrollbar(){let j=Date.now(),V=this.scrollbarOpacity,$=()=>{let Z=Date.now()-j,J=Math.min(Z/this.SCROLLBAR_FADE_DURATION_MS,1);this.scrollbarOpacity=V*(1-J),this.renderer&&this.wasmTerm&&this.renderer.render(this.wasmTerm,!1,this.viewportY,this,this.scrollbarOpacity),J<1?requestAnimationFrame($):(this.scrollbarVisible=!1,this.scrollbarOpacity=0,this.renderer&&this.wasmTerm&&this.renderer.render(this.wasmTerm,!1,this.viewportY,this,0))};$()}processTerminalResponses(){if(this.wasmTerm)for(;;){let j=this.wasmTerm.readResponse();if(j===null)break;this.dataEmitter.fire(j)}}checkForTitleChange(j){let V=/\x1b\]([012]);([^\x07\x1b]*?)(?:\x07|\x1b\\)/g,$=null;for(;($=V.exec(j))!==null;){let Z=$[1],J=$[2];(Z==="0"||Z==="2")&&J!==this.currentTitle&&(this.currentTitle=J,this.titleChangeEmitter.fire(J))}}getMode(j,V=!1){return this.assertOpen(),this.wasmTerm.getMode(j,V)}hasBracketedPaste(){return this.assertOpen(),this.wasmTerm.hasBracketedPaste()}hasFocusEvents(){return this.assertOpen(),this.wasmTerm.hasFocusEvents()}hasMouseTracking(){return this.assertOpen(),this.wasmTerm.hasMouseTracking()}setSnapshot(j,V){let $=[];for(let Z=0;Z{this._isResizing=!1},50)}}}proposeDimensions(){var j;if(!((j=this._terminal)!=null&&j.element))return;let V=this._terminal.renderer;if(!V||typeof V.getMetrics!="function")return;let $=V.getMetrics();if(!$||$.width===0||$.height===0)return;let Z=this._terminal.element;if(typeof Z.clientWidth>"u")return;let J=window.getComputedStyle(Z),O=Number.parseInt(J.getPropertyValue("padding-top"))||0,X=Number.parseInt(J.getPropertyValue("padding-bottom"))||0,P=Number.parseInt(J.getPropertyValue("padding-left"))||0,K=Number.parseInt(J.getPropertyValue("padding-right"))||0,L=Z.clientWidth,z=Z.clientHeight;if(L===0||z===0)return;let W=L-P-K-x0,Y=z-O-X,H=Math.max(N0,Math.floor(W/$.width)),q=Math.max(b0,Math.floor(Y/$.height));return{cols:H,rows:q}}observeResize(){var j;(j=this._terminal)!=null&&j.element&&(this._resizeObserver||(this._resizeObserver=new ResizeObserver((V)=>{this._isResizing||!V[0]||(this._resizeDebounceTimer&&clearTimeout(this._resizeDebounceTimer),this._resizeDebounceTimer=setTimeout(()=>{this.fit()},F0))}),this._resizeObserver.observe(this._terminal.element)))}}var t=null;function v0(){if(!t)throw Error(`ghostty-web not initialized. Call init() before creating Terminal instances.
+`,V);else{let $=new Uint8Array(j.length+2);$.set(j),$[j.length]=13,$[j.length+1]=10,this.write($,V)}}paste(j){this.assertOpen(),!this.options.disableStdin&&(this.wasmTerm.hasBracketedPaste()?this.dataEmitter.fire("\x1B[200~"+j+"\x1B[201~"):this.dataEmitter.fire(j))}input(j,V=!1){this.assertOpen(),!this.options.disableStdin&&(V?this.dataEmitter.fire(j):this.write(j))}resize(j,V){if(this.assertOpen(),j===this.cols&&V===this.rows)return;this._resizeFlushFrameId&&(cancelAnimationFrame(this._resizeFlushFrameId),this._resizeFlushFrameId=void 0),this._isResizing=!0;let $=this.animationFrameId!==void 0;this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=void 0);try{this.wasmTerm.resize(j,V),this.cols=j,this.rows=V,this.renderer.resize(j,V);let Z=this.renderer.getMetrics();this.canvas.width=Z.width*j,this.canvas.height=Z.height*V,this.canvas.style.width=`${Z.width*j}px`,this.canvas.style.height=`${Z.height*V}px`,this.resizeEmitter.fire({cols:j,rows:V}),this.renderer.render(this.snapshotBuffer,!0,this.viewportY,this)}catch(Z){console.error("[ghostty-web] Resize error:",Z)}$&&this.startRenderLoop(),this._resizeFlushFrameId=requestAnimationFrame(()=>{this._resizeFlushFrameId=void 0,this._isResizing=!1,this.flushWriteQueue()})}flushWriteQueue(){if(this.isDisposed||!this.isOpen){this._writeQueue=[];return}let j=this._writeQueue;this._writeQueue=[];for(let{data:V,callback:$}of j)this.writeInternal(V,$)}clear(){this.assertOpen(),this.wasmTerm.write("\x1B[2J\x1B[H")}reset(){this.assertOpen(),this.wasmTerm&&this.wasmTerm.free();let j=this.buildWasmConfig();this.wasmTerm=this.ghostty.createTerminal(this.cols,this.rows,j),this.renderer.clear(),this.currentTitle=""}focus(){if(this.isOpen){let j=this.textarea||this.element;j&&(j.focus(),setTimeout(()=>{j==null||j.focus()},0))}}blur(){this.isOpen&&this.element&&this.element.blur()}loadAddon(j){j.activate(this),this.addons.push(j)}getSelection(){var j;return((j=this.selectionManager)==null?void 0:j.getSelection())||""}hasSelection(){var j;return((j=this.selectionManager)==null?void 0:j.hasSelection())||!1}clearSelection(){var j;(j=this.selectionManager)==null||j.clearSelection()}copySelection(){var j;return((j=this.selectionManager)==null?void 0:j.copySelection())||!1}selectAll(){var j;(j=this.selectionManager)==null||j.selectAll()}select(j,V,$){var Z;(Z=this.selectionManager)==null||Z.select(j,V,$)}selectLines(j,V){var $;($=this.selectionManager)==null||$.selectLines(j,V)}getViewportY(){return this.viewportY}getSelectionPosition(){var j;return(j=this.selectionManager)==null?void 0:j.getSelectionPosition()}attachCustomKeyEventHandler(j){this.customKeyEventHandler=j,this.inputHandler&&this.inputHandler.setCustomKeyEventHandler(j)}attachCustomWheelEventHandler(j){this.customWheelEventHandler=j}loadFonts(){this.renderer&&(this.renderer.remeasureFont(),this.handleFontChange())}registerLinkProvider(j){if(!this.linkDetector)throw Error("Terminal must be opened before registering link providers");this.linkDetector.registerProvider(j)}scrollLines(j){if(!this.wasmTerm)throw Error("Terminal not open");let V=this.getScrollbackLength(),$=Math.max(0,Math.min(V,this.viewportY-j));$!==this.viewportY&&(this.viewportY=$,this.scrollEmitter.fire(this.viewportY),V>0&&this.showScrollbar())}scrollPages(j){this.scrollLines(j*this.rows)}scrollToTop(){let j=this.getScrollbackLength();j>0&&this.viewportY!==j&&(this.viewportY=j,this.scrollEmitter.fire(this.viewportY),this.showScrollbar())}scrollToBottom(){this.viewportY!==0&&(this.viewportY=0,this.scrollEmitter.fire(this.viewportY),this.getScrollbackLength()>0&&this.showScrollbar())}scrollToLine(j){let V=this.getScrollbackLength(),$=Math.max(0,Math.min(V,j));$!==this.viewportY&&(this.viewportY=$,this.scrollEmitter.fire(this.viewportY),V>0&&this.showScrollbar())}smoothScrollTo(j){if(!this.wasmTerm)return;let V=this.getScrollbackLength(),$=Math.max(0,Math.min(V,j));if((this.options.smoothScrollDuration??100)===0){this.viewportY=$,this.targetViewportY=$,this.scrollEmitter.fire(Math.floor(this.viewportY)),V>0&&this.showScrollbar();return}this.targetViewportY=$,!this.scrollAnimationFrame&&(this.scrollAnimationStartTime=Date.now(),this.scrollAnimationStartY=this.viewportY,this.animateScroll())}dispose(){if(!this.isDisposed){this.isDisposed=!0,this.isOpen=!1,this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=void 0),this.scrollAnimationFrame&&(cancelAnimationFrame(this.scrollAnimationFrame),this.scrollAnimationFrame=void 0),this._resizeFlushFrameId&&(cancelAnimationFrame(this._resizeFlushFrameId),this._resizeFlushFrameId=void 0),this._writeQueue=[],this._isResizing=!1,this.mouseMoveThrottleTimeout&&(clearTimeout(this.mouseMoveThrottleTimeout),this.mouseMoveThrottleTimeout=void 0),this.pendingMouseMove=void 0;for(let j of this.addons)j.dispose();this.addons=[],this.cleanupComponents(),this.dataEmitter.dispose(),this.resizeEmitter.dispose(),this.bellEmitter.dispose(),this.selectionChangeEmitter.dispose(),this.keyEmitter.dispose(),this.titleChangeEmitter.dispose(),this.scrollEmitter.dispose(),this.renderEmitter.dispose(),this.cursorMoveEmitter.dispose()}}startRenderLoop(){let j=()=>{if(!this.isDisposed&&this.isOpen){try{this.renderer.render(this.snapshotBuffer,!1,this.viewportY,this,this.scrollbarOpacity);let V=this.wasmTerm.getCursor();V.y!==this.lastCursorY&&(this.lastCursorY=V.y,this.cursorMoveEmitter.fire())}catch(V){console.error("[ghostty-web] render loop error (recovering):",V)}this.animationFrameId=requestAnimationFrame(j)}};j()}getScrollbackLine(j){return this.wasmTerm?this.wasmTerm.getScrollbackLine(j):null}getScrollbackLength(){return this.wasmTerm?this.wasmTerm.getScrollbackLength():0}cleanupComponents(){this.selectionManager&&(this.selectionManager.dispose(),this.selectionManager=void 0),this.inputHandler&&(this.inputHandler.dispose(),this.inputHandler=void 0),this.renderer&&(this.renderer.dispose(),this.renderer=void 0),this.canvas&&this.canvas.parentNode&&(this.canvas.parentNode.removeChild(this.canvas),this.canvas=void 0),this.textarea&&this.textarea.parentNode&&(this.textarea.parentNode.removeChild(this.textarea),this.textarea=void 0),this.compositionPreview&&this.compositionPreview.parentNode&&(this.compositionPreview.parentNode.removeChild(this.compositionPreview),this.compositionPreview=void 0),this.element&&(this.element.removeEventListener("wheel",this.handleWheel),this.element.removeEventListener("mousedown",this.handleMouseDown,{capture:!0}),this.element.removeEventListener("mousemove",this.handleMouseMove),this.element.removeEventListener("mouseleave",this.handleMouseLeave),this.element.removeEventListener("click",this.handleClick),this.element.removeAttribute("role"),this.element.removeAttribute("aria-label"),this.element.removeAttribute("aria-multiline")),this.isOpen&&typeof document<"u"&&document.removeEventListener("mouseup",this.handleMouseUp),this.scrollbarHideTimeout&&(window.clearTimeout(this.scrollbarHideTimeout),this.scrollbarHideTimeout=void 0),this.linkDetector&&(this.linkDetector.dispose(),this.linkDetector=void 0),this.wasmTerm&&(this.wasmTerm.free(),this.wasmTerm=void 0),this.ghostty=void 0,this.element=void 0,this.textarea=void 0}assertOpen(){if(this.isDisposed)throw Error("Terminal has been disposed");if(!this.isOpen)throw Error("Terminal must be opened before use. Call terminal.open(parent) first.")}processMouseMove(j){if(!this.canvas||!this.renderer||!this.linkDetector||!this.wasmTerm)return;let V=this.canvas.getBoundingClientRect(),$=Math.floor((j.clientX-V.left)/this.renderer.charWidth),Z=Math.floor((j.clientY-V.top)/this.renderer.charHeight),J=0,O=null,P=this.getViewportY(),X=Math.max(0,Math.floor(P));if(X>0){let Y=this.wasmTerm.getScrollbackLength();if(Z=0&&$0)if(Z{var q,T,v,U;if(Y!==this.currentHoveredLink&&((T=(q=this.currentHoveredLink)==null?void 0:q.hover)==null||T.call(q,!1),this.currentHoveredLink=Y,(v=Y==null?void 0:Y.hover)==null||v.call(Y,!0),this.element&&(this.element.style.cursor=Y?"pointer":"text"),this.renderer))if(Y){let G=((U=this.wasmTerm)==null?void 0:U.getScrollbackLength())||0,N=this.getViewportY(),F=Math.max(0,Math.floor(N)),a=Y.range.start.y-G+F,s=Y.range.end.y-G+F;a=0?this.renderer.setHoveredLinkRange({startX:Y.range.start.x,startY:Math.max(0,a),endX:Y.range.end.x,endY:Math.min(this.rows-1,s)}):this.renderer.setHoveredLinkRange(null)}else this.renderer.setHoveredLinkRange(null)}).catch((Y)=>{console.warn("Link detection error:",Y)})}processScrollbarDrag(j){if(!this.canvas||!this.renderer||!this.wasmTerm||this.scrollbarDragStart===null)return;let V=this.wasmTerm.getScrollbackLength();if(V===0)return;let $=this.canvas.getBoundingClientRect(),Z=j.clientY-$.top-this.scrollbarDragStart,J=$.height-8,O=this.rows,P=V+O,X=Math.max(20,O/P*J),K=-Z/(J-X),L=Math.round(K*V),W=this.scrollbarDragStartViewportY+L;this.scrollToLine(Math.max(0,Math.min(V,W)))}showScrollbar(){this.scrollbarHideTimeout&&(window.clearTimeout(this.scrollbarHideTimeout),this.scrollbarHideTimeout=void 0),this.scrollbarVisible?this.scrollbarOpacity=1:(this.scrollbarVisible=!0,this.scrollbarOpacity=0,this.fadeInScrollbar()),this.isDraggingScrollbar||(this.scrollbarHideTimeout=window.setTimeout(()=>{this.hideScrollbar()},this.SCROLLBAR_HIDE_DELAY_MS))}hideScrollbar(){this.scrollbarHideTimeout&&(window.clearTimeout(this.scrollbarHideTimeout),this.scrollbarHideTimeout=void 0),this.scrollbarVisible&&this.fadeOutScrollbar()}fadeInScrollbar(){let j=Date.now(),V=()=>{let $=Date.now()-j,Z=Math.min($/this.SCROLLBAR_FADE_DURATION_MS,1);this.scrollbarOpacity=Z,this.renderer&&this.wasmTerm&&this.renderer.render(this.wasmTerm,!1,this.viewportY,this,this.scrollbarOpacity),Z<1&&requestAnimationFrame(V)};V()}fadeOutScrollbar(){let j=Date.now(),V=this.scrollbarOpacity,$=()=>{let Z=Date.now()-j,J=Math.min(Z/this.SCROLLBAR_FADE_DURATION_MS,1);this.scrollbarOpacity=V*(1-J),this.renderer&&this.wasmTerm&&this.renderer.render(this.wasmTerm,!1,this.viewportY,this,this.scrollbarOpacity),J<1?requestAnimationFrame($):(this.scrollbarVisible=!1,this.scrollbarOpacity=0,this.renderer&&this.wasmTerm&&this.renderer.render(this.wasmTerm,!1,this.viewportY,this,0))};$()}processTerminalResponses(){if(this.wasmTerm)for(;;){let j=this.wasmTerm.readResponse();if(j===null)break;this.dataEmitter.fire(j)}}checkForTitleChange(j){let V=/\x1b\]([012]);([^\x07\x1b]*?)(?:\x07|\x1b\\)/g,$=null;for(;($=V.exec(j))!==null;){let Z=$[1],J=$[2];(Z==="0"||Z==="2")&&J!==this.currentTitle&&(this.currentTitle=J,this.titleChangeEmitter.fire(J))}}getMode(j,V=!1){return this.assertOpen(),this.wasmTerm.getMode(j,V)}hasBracketedPaste(){return this.assertOpen(),this.wasmTerm.hasBracketedPaste()}hasFocusEvents(){return this.assertOpen(),this.wasmTerm.hasFocusEvents()}hasMouseTracking(){return this.assertOpen(),this.wasmTerm.hasMouseTracking()}setSnapshot(j,V){let $=[];for(let Z=0;Z{this._isResizing=!1},50)}}}proposeDimensions(){var j;if(!((j=this._terminal)!=null&&j.element))return;let V=this._terminal.renderer;if(!V||typeof V.getMetrics!="function")return;let $=V.getMetrics();if(!$||$.width===0||$.height===0)return;let Z=this._terminal.element;if(typeof Z.clientWidth>"u")return;let J=window.getComputedStyle(Z),O=Number.parseInt(J.getPropertyValue("padding-top"))||0,P=Number.parseInt(J.getPropertyValue("padding-bottom"))||0,X=Number.parseInt(J.getPropertyValue("padding-left"))||0,K=Number.parseInt(J.getPropertyValue("padding-right"))||0,L=Z.clientWidth,W=Z.clientHeight;if(L===0||W===0)return;let z=L-X-K-v0,H=W-O-P,Y=Math.max(N0,Math.floor(z/$.width)),q=Math.max(_0,Math.floor(H/$.height));return{cols:Y,rows:q}}observeResize(){var j;(j=this._terminal)!=null&&j.element&&(this._resizeObserver||(this._resizeObserver=new ResizeObserver((V)=>{this._isResizing||!V[0]||(this._resizeDebounceTimer&&clearTimeout(this._resizeDebounceTimer),this._resizeDebounceTimer=setTimeout(()=>{this.fit()},F0))}),this._resizeObserver.observe(this._terminal.element)))}}var t=null;function b0(){if(!t)throw Error(`ghostty-web not initialized. Call init() before creating Terminal instances.
Example:
import { init, Terminal } from "ghostty-web";
await init();
@@ -10,7 +10,7 @@ Example:
For tests, pass a Ghostty instance directly:
import { Ghostty, Terminal } from "ghostty-web";
const ghostty = await Ghostty.load();
- const term = new Terminal({ ghostty });`);return t}var k=1000,P0=30000,I=null;async function S0(){if(!I){let j=L0();console.log("[webterm] Loading shared Ghostty WASM:",j),I=await u.load(j)}return I}var K0='ui-monospace, "SFMono-Regular", "FiraCode Nerd Font", "FiraMono Nerd Font", "Fira Code", "Roboto Mono", Menlo, Monaco, Consolas, "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace',M={tango:{background:"#000000",foreground:"#d3d7cf",cursor:"#d3d7cf",cursorAccent:"#000000",selectionBackground:"#d3d7cf",selectionForeground:"#000000",black:"#2e3436",red:"#cc0000",green:"#4e9a06",yellow:"#c4a000",blue:"#3465a4",magenta:"#75507b",cyan:"#06989a",white:"#d3d7cf",brightBlack:"#555753",brightRed:"#ef2929",brightGreen:"#8ae234",brightYellow:"#fce94f",brightBlue:"#729fcf",brightMagenta:"#ad7fa8",brightCyan:"#34e2e2",brightWhite:"#eeeeec"},xterm:{background:"#000000",foreground:"#e5e5e5",cursor:"#e5e5e5",cursorAccent:"#000000",selectionBackground:"#e5e5e5",selectionForeground:"#000000",black:"#000000",red:"#cd0000",green:"#00cd00",yellow:"#cdcd00",blue:"#0000cd",magenta:"#cd00cd",cyan:"#00cdcd",white:"#e5e5e5",brightBlack:"#4d4d4d",brightRed:"#ff0000",brightGreen:"#00ff00",brightYellow:"#ffff00",brightBlue:"#0000ff",brightMagenta:"#ff00ff",brightCyan:"#00ffff",brightWhite:"#ffffff"},monokai:{background:"#272822",foreground:"#fdfff1",cursor:"#fdfff1",cursorAccent:"#272822",selectionBackground:"#fdfff1",selectionForeground:"#272822",black:"#272822",red:"#f92672",green:"#a6e22e",yellow:"#e6db74",blue:"#fd971f",magenta:"#ae81ff",cyan:"#66d9ef",white:"#fdfff1",brightBlack:"#6e7066",brightRed:"#f92672",brightGreen:"#a6e22e",brightYellow:"#e6db74",brightBlue:"#fd971f",brightMagenta:"#ae81ff",brightCyan:"#66d9ef",brightWhite:"#fdfff1"},"monokai-pro":{background:"#2d2a2e",foreground:"#fcfcfa",cursor:"#fcfcfa",cursorAccent:"#2d2a2e",selectionBackground:"#fcfcfa",selectionForeground:"#2d2a2e",black:"#403e41",red:"#ff6188",green:"#a9dc76",yellow:"#ffd866",blue:"#fc9867",magenta:"#ab9df2",cyan:"#78dce8",white:"#fcfcfa",brightBlack:"#727072",brightRed:"#ff6188",brightGreen:"#a9dc76",brightYellow:"#ffd866",brightBlue:"#fc9867",brightMagenta:"#ab9df2",brightCyan:"#78dce8",brightWhite:"#fcfcfa"},ristretto:{background:"#2d2525",foreground:"#fff1f3",cursor:"#fff1f3",cursorAccent:"#2d2525",selectionBackground:"#fff1f3",selectionForeground:"#2d2525",black:"#2c2525",red:"#fd6883",green:"#adda78",yellow:"#f9cc6c",blue:"#f38d70",magenta:"#a8a9eb",cyan:"#85dacc",white:"#f9f8f5",brightBlack:"#655761",brightRed:"#fd6883",brightGreen:"#adda78",brightYellow:"#f9cc6c",brightBlue:"#f38d70",brightMagenta:"#a8a9eb",brightCyan:"#85dacc",brightWhite:"#f9f8f5"},dark:{background:"#1e1e1e",foreground:"#d4d4d4",cursor:"#aeafad",cursorAccent:"#1e1e1e",selectionBackground:"#d4d4d4",selectionForeground:"#1e1e1e",black:"#000000",red:"#cd3131",green:"#0dbc79",yellow:"#e5e510",blue:"#2472c8",magenta:"#bc3fbc",cyan:"#11a8cd",white:"#e5e5e5",brightBlack:"#666666",brightRed:"#f14c4c",brightGreen:"#23d18b",brightYellow:"#f5f543",brightBlue:"#3b8eea",brightMagenta:"#d670d6",brightCyan:"#29b8db",brightWhite:"#ffffff"},light:{background:"#ffffff",foreground:"#383a42",cursor:"#526eff",cursorAccent:"#ffffff",selectionBackground:"#383a42",selectionForeground:"#ffffff",black:"#000000",red:"#e45649",green:"#50a14f",yellow:"#c18401",blue:"#4078f2",magenta:"#a626a4",cyan:"#0184bc",white:"#a0a1a7",brightBlack:"#5c6370",brightRed:"#e06c75",brightGreen:"#98c379",brightYellow:"#d19a66",brightBlue:"#61afef",brightMagenta:"#c678dd",brightCyan:"#56b6c2",brightWhite:"#ffffff"},dracula:{background:"#282a36",foreground:"#f8f8f2",cursor:"#f8f8f2",cursorAccent:"#282a36",selectionBackground:"#f8f8f2",selectionForeground:"#282a36",black:"#21222c",red:"#ff5555",green:"#50fa7b",yellow:"#f1fa8c",blue:"#bd93f9",magenta:"#ff79c6",cyan:"#8be9fd",white:"#f8f8f2",brightBlack:"#6272a4",brightRed:"#ff6e6e",brightGreen:"#69ff94",brightYellow:"#ffffa5",brightBlue:"#d6acff",brightMagenta:"#ff92df",brightCyan:"#a4ffff",brightWhite:"#ffffff"},catppuccin:{background:"#1e1e2e",foreground:"#cdd6f4",cursor:"#f5e0dc",cursorAccent:"#1e1e2e",selectionBackground:"#cdd6f4",selectionForeground:"#1e1e2e",black:"#45475a",red:"#f38ba8",green:"#a6e3a1",yellow:"#f9e2af",blue:"#89b4fa",magenta:"#f5c2e7",cyan:"#94e2d5",white:"#bac2de",brightBlack:"#585b70",brightRed:"#f38ba8",brightGreen:"#a6e3a1",brightYellow:"#f9e2af",brightBlue:"#89b4fa",brightMagenta:"#f5c2e7",brightCyan:"#94e2d5",brightWhite:"#a6adc8"},nord:{background:"#2e3440",foreground:"#d8dee9",cursor:"#d8dee9",cursorAccent:"#2e3440",selectionBackground:"#d8dee9",selectionForeground:"#2e3440",black:"#3b4252",red:"#bf616a",green:"#a3be8c",yellow:"#ebcb8b",blue:"#81a1c1",magenta:"#b48ead",cyan:"#88c0d0",white:"#e5e9f0",brightBlack:"#4c566a",brightRed:"#bf616a",brightGreen:"#a3be8c",brightYellow:"#ebcb8b",brightBlue:"#81a1c1",brightMagenta:"#b48ead",brightCyan:"#8fbcbb",brightWhite:"#eceff4"},gruvbox:{background:"#282828",foreground:"#ebdbb2",cursor:"#ebdbb2",cursorAccent:"#282828",selectionBackground:"#ebdbb2",selectionForeground:"#282828",black:"#282828",red:"#cc241d",green:"#98971a",yellow:"#d79921",blue:"#458588",magenta:"#b16286",cyan:"#689d6a",white:"#a89984",brightBlack:"#928374",brightRed:"#fb4934",brightGreen:"#b8bb26",brightYellow:"#fabd2f",brightBlue:"#83a598",brightMagenta:"#d3869b",brightCyan:"#8ec07c",brightWhite:"#ebdbb2"},solarized:{background:"#002b36",foreground:"#839496",cursor:"#839496",cursorAccent:"#002b36",selectionBackground:"#839496",selectionForeground:"#002b36",black:"#073642",red:"#dc322f",green:"#859900",yellow:"#b58900",blue:"#268bd2",magenta:"#d33682",cyan:"#2aa198",white:"#eee8d5",brightBlack:"#586e75",brightRed:"#cb4b16",brightGreen:"#586e75",brightYellow:"#657b83",brightBlue:"#839496",brightMagenta:"#6c71c4",brightCyan:"#93a1a1",brightWhite:"#fdf6e3"},tokyo:{background:"#1a1b26",foreground:"#a9b1d6",cursor:"#c0caf5",cursorAccent:"#1a1b26",selectionBackground:"#a9b1d6",selectionForeground:"#1a1b26",black:"#15161e",red:"#f7768e",green:"#9ece6a",yellow:"#e0af68",blue:"#7aa2f7",magenta:"#bb9af7",cyan:"#7dcfff",white:"#a9b1d6",brightBlack:"#414868",brightRed:"#f7768e",brightGreen:"#9ece6a",brightYellow:"#e0af68",brightBlue:"#7aa2f7",brightMagenta:"#bb9af7",brightCyan:"#7dcfff",brightWhite:"#c0caf5"},miasma:{background:"#222222",foreground:"#c2c2b0",cursor:"#c2c2b0",cursorAccent:"#222222",selectionBackground:"#c2c2b0",selectionForeground:"#222222",black:"#000000",red:"#685742",green:"#5f875f",yellow:"#b36d43",blue:"#78824b",magenta:"#bb7744",cyan:"#c9a554",white:"#d7c483",brightBlack:"#666666",brightRed:"#685742",brightGreen:"#5f875f",brightYellow:"#b36d43",brightBlue:"#78824b",brightMagenta:"#bb7744",brightCyan:"#c9a554",brightWhite:"#d7c483"},github:{background:"#1c2128",foreground:"#adbac7",cursor:"#adbac7",cursorAccent:"#1c2128",selectionBackground:"#adbac7",selectionForeground:"#1c2128",black:"#545d68",red:"#f47067",green:"#57ab5a",yellow:"#c69026",blue:"#539bf5",magenta:"#b083f0",cyan:"#39c5cf",white:"#909dab",brightBlack:"#636e7b",brightRed:"#ff938a",brightGreen:"#6bc46d",brightYellow:"#daaa3f",brightBlue:"#6cb6ff",brightMagenta:"#dcbdfb",brightCyan:"#56d4dd",brightWhite:"#cdd9e5"},gotham:{background:"#0c1014",foreground:"#99d1ce",cursor:"#99d1ce",cursorAccent:"#0c1014",selectionBackground:"#99d1ce",selectionForeground:"#0c1014",black:"#0c1014",red:"#c23127",green:"#2aa889",yellow:"#edb443",blue:"#195466",magenta:"#4e5166",cyan:"#33859e",white:"#99d1ce",brightBlack:"#0c1014",brightRed:"#c23127",brightGreen:"#2aa889",brightYellow:"#edb443",brightBlue:"#195466",brightMagenta:"#4e5166",brightCyan:"#33859e",brightWhite:"#99d1ce"}};function u0(j){console.log("[webterm:parseConfig] Parsing config from element");let V={};if(j.dataset.fontFamily){let $=j.dataset.fontFamily;if($.startsWith("var(")){let Z=$.match(/var\(([^)]+)\)/);if(Z){let J=Z[1].trim(),O=getComputedStyle(document.documentElement).getPropertyValue(J).trim();if(O)$=O,console.log(`[webterm:parseConfig] Resolved CSS variable ${J} to: "${$}"`);else console.warn(`[webterm:parseConfig] CSS variable ${J} not found, using default font`),$=K0}}V.fontFamily=$,console.log(`[webterm:parseConfig] fontFamily: "${V.fontFamily}"`)}if(j.dataset.fontSize)V.fontSize=parseInt(j.dataset.fontSize,10),console.log(`[webterm:parseConfig] fontSize: ${V.fontSize}`);if(j.dataset.scrollback)V.scrollback=parseInt(j.dataset.scrollback,10),console.log(`[webterm:parseConfig] scrollback: ${V.scrollback}`);if(j.dataset.theme){let $=j.dataset.theme.toLowerCase();if(console.log(`[webterm:parseConfig] theme attribute: "${j.dataset.theme}" -> normalized: "${$}"`),console.log(`[webterm:parseConfig] Available themes: ${Object.keys(M).join(", ")}`),console.log(`[webterm:parseConfig] Theme "${$}" in THEMES? ${$ in M}`),$ in M)V.theme=M[$],console.log(`[webterm:parseConfig] Using built-in theme "${$}":`,JSON.stringify(V.theme,null,2));else{console.log("[webterm:parseConfig] Theme not found in THEMES, trying JSON parse...");try{V.theme=JSON.parse(j.dataset.theme),console.log("[webterm:parseConfig] Parsed custom JSON theme:",V.theme)}catch(Z){console.warn(`[webterm:parseConfig] Unknown theme "${j.dataset.theme}", JSON parse failed:`,Z)}}}else console.log("[webterm:parseConfig] No theme attribute found on element");return console.log("[webterm:parseConfig] Final config:",V),V}function L0(){let j=document.querySelectorAll('script[src*="terminal.js"]');if(j.length>0){let V=j[0].src;return V.substring(0,V.lastIndexOf("/")+1)+"ghostty-vt.wasm"}return"/static/js/ghostty-vt.wasm"}function l(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||"ontouchstart"in window&&navigator.maxTouchPoints>0}var M0={"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+","[":"{","]":"}","\\":"|",";":":","'":'"',",":"<",".":">","/":"?"},k0={"2":"@","3":"[","4":"\\","5":"]","6":"^","7":"_","8":"?"},f0=["\x1BOP","\x1BOQ","\x1BOR","\x1BOS","\x1B[15~","\x1B[17~","\x1B[18~","\x1B[19~","\x1B[20~","\x1B[21~"],p0=["\x1B[23~","\x1B[24~","\x1B[25~","\x1B[26~","\x1B[28~","\x1B[29~","\x1B[31~","\x1B[32~","\x1B[33~","\x1B[34~"];function w0(j){if(j.length!==1)return j;if(j>="a"&&j<="z")return j.toUpperCase();return M0[j]??j}function R0(j){if(j.length!==1)return j;let V=k0[j]??j;if(V==="?")return"";let $=V.toUpperCase().charCodeAt(0);if($>=64&&$<=95)return String.fromCharCode($-64);return j}function C(j,V){if(j.length!==1)return null;let $="1234567890".indexOf(j);if($<0)return null;return V?p0[$]:f0[$]}function S(j){if(!j||j.startsWith("\x1B"))return j;return`\x1B${j}`}function r(j,V,$,Z,J){if(j.length!==1)return j;if(J){let O=C(j,V);if(O)return Z?S(O):O}if($){let O=R0(j);if(O!==j)return Z?S(O):O}if(V){let O=w0(j);return Z?S(O):O}return Z?S(j):j}class n{terminal;fitAddon;socket=null;element;wsUrl;reconnectAttempts=0;maxReconnectAttempts=5;reconnectDelay=1000;heartbeatIntervalMs=15000;stallTimeoutMs=45000;heartbeatTimer;lastMessageAt=0;lastPongAt=0;messageQueue=[];lastValidSize=null;mobileInput=null;mobileKeybar=null;ctrlActive=!1;altActive=!1;shiftActive=!1;fnActive=!1;pendingCtrl=!1;pendingAlt=!1;pendingShift=!1;pendingFn=!1;fontFamily;fontSize;cleanupTimer;resizeObserver=null;mobileKeybarStyle=null;boundHandlers=[];constructor(j,V,$,Z,J,O){this.element=j,this.wsUrl=V,this.terminal=$,this.fitAddon=Z,this.fontFamily=J,this.fontSize=O}addTrackedListener(j,V,$,Z){j.addEventListener(V,$,Z),this.boundHandlers.push({target:j,type:V,handler:$,options:Z})}static async create(j,V,$){console.log("[webterm:create] WebTerminal.create() called"),console.log("[webterm:create] Container:",j),console.log("[webterm:create] wsUrl:",V),console.log("[webterm:create] Config received:",JSON.stringify($,null,2));let Z=L0();console.log("[webterm:create] WASM path:",Z),console.log("[webterm:create] Loading shared Ghostty WASM...");let J=await S0();console.log("[webterm:create] Ghostty loaded:",J);let O=$.theme??M.tango;console.log("[webterm:create] Theme to use (config.theme ?? THEMES.xterm):",JSON.stringify(O,null,2));let X=$.fontFamily?.trim()||K0,P=$.fontSize??16,K=l()?200:1000,L={fontFamily:X,fontSize:P,scrollback:$.scrollback??K,cursorBlink:!0,cursorStyle:"block",theme:O,ghostty:J};console.log("[webterm:create] Full ITerminalOptions:",JSON.stringify(L,null,2)),console.log("[webterm:create] Creating ghostty-web Terminal instance...");let z=new d(L);console.log("[webterm:create] Terminal created:",z),console.log("[webterm:create] Terminal.options:",z.options),console.log("[webterm:create] Creating FitAddon...");let W=new h;console.log("[webterm:create] Loading FitAddon into terminal..."),z.loadAddon(W),console.log("[webterm:create] Calling terminal.open(container)..."),z.open(j),console.log("[webterm:create] terminal.open() completed");let Y=z;if(console.log("[webterm:create] Terminal internal keys:",Object.keys(Y)),Y.renderer){console.log("[webterm:create] Renderer exists:",Y.renderer);let q=Y.renderer;if(console.log("[webterm:create] Renderer keys:",Object.keys(q)),q.theme)console.log("[webterm:create] Renderer.theme:",q.theme);if(q.palette)console.log("[webterm:create] Renderer.palette:",q.palette)}let H=new n(j,V,z,W,X,P);return console.log("[webterm:create] WebTerminal instance created"),H.initialize(),console.log("[webterm:create] WebTerminal initialized"),H}initialize(){console.log("[webterm:init] initialize() called");let j=this.element.querySelector("canvas");if(console.log("[webterm:init] Canvas element:",j),j)console.log("[webterm:init] Canvas dimensions:",{width:j.width,height:j.height,clientWidth:j.clientWidth,clientHeight:j.clientHeight,style:j.style.cssText});if(console.log("[webterm:init] Container dimensions:",{clientWidth:this.element.clientWidth,clientHeight:this.element.clientHeight}),this.waitForFonts().then(()=>{if(console.log("[webterm:init] Fonts loaded, triggering font reload..."),typeof this.terminal.loadFonts==="function")this.terminal.loadFonts(),console.log("[webterm:init] terminal.loadFonts() called");this.fit(),console.log("[webterm:init] fit() completed");let $=this.element.querySelector("canvas");if($)console.log("[webterm:init] Canvas after fit:",{width:$.width,height:$.height,clientWidth:$.clientWidth,clientHeight:$.clientHeight})}),this.setupResizeObserver(),this.addTrackedListener(window,"resize",()=>{this.fit()}),this.terminal.onData(($)=>{this.send(["stdin",$])}),this.terminal.onResize(($)=>{if(this.isValidSize($.cols,$.rows))this.lastValidSize={cols:$.cols,rows:$.rows},this.send(["resize",{width:$.cols,height:$.rows}])}),this.setupMobileKeyboard(),this.setupTouchSelection(),l())this.setupMobileKeybar();this.startResourceCleanup(),this.connect();let V=()=>{if(l())this.focusMobileInput();else this.terminal.focus()};this.addTrackedListener(document,"visibilitychange",()=>{if(document.hidden)this.stopHeartbeatWatchdog();else{if(this.socket?.readyState===WebSocket.OPEN)this.startHeartbeatWatchdog();V()}}),this.addTrackedListener(window,"focus",()=>{V()}),this.addTrackedListener(window,"pageshow",()=>{V()})}setupMobileKeyboard(){let j=document.createElement("textarea");j.setAttribute("autocapitalize","off"),j.setAttribute("autocomplete","off"),j.setAttribute("autocorrect","off"),j.setAttribute("spellcheck","false"),j.setAttribute("inputmode","text"),j.setAttribute("enterkeyhint","send"),j.style.cssText=`
+ const term = new Terminal({ ghostty });`);return t}var f=1000,X0=30000,r=null;async function x0(){if(!r){let j=u0();console.log("[webterm] Loading shared Ghostty WASM:",j),r=await u.load(j)}return r}var K0='ui-monospace, "SFMono-Regular", "FiraCode Nerd Font", "FiraMono Nerd Font", "Fira Code", "Roboto Mono", Menlo, Monaco, Consolas, "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace',k={tango:{background:"#000000",foreground:"#d3d7cf",cursor:"#d3d7cf",cursorAccent:"#000000",selectionBackground:"#d3d7cf",selectionForeground:"#000000",black:"#2e3436",red:"#cc0000",green:"#4e9a06",yellow:"#c4a000",blue:"#3465a4",magenta:"#75507b",cyan:"#06989a",white:"#d3d7cf",brightBlack:"#555753",brightRed:"#ef2929",brightGreen:"#8ae234",brightYellow:"#fce94f",brightBlue:"#729fcf",brightMagenta:"#ad7fa8",brightCyan:"#34e2e2",brightWhite:"#eeeeec"},xterm:{background:"#000000",foreground:"#e5e5e5",cursor:"#e5e5e5",cursorAccent:"#000000",selectionBackground:"#e5e5e5",selectionForeground:"#000000",black:"#000000",red:"#cd0000",green:"#00cd00",yellow:"#cdcd00",blue:"#0000cd",magenta:"#cd00cd",cyan:"#00cdcd",white:"#e5e5e5",brightBlack:"#4d4d4d",brightRed:"#ff0000",brightGreen:"#00ff00",brightYellow:"#ffff00",brightBlue:"#0000ff",brightMagenta:"#ff00ff",brightCyan:"#00ffff",brightWhite:"#ffffff"},monokai:{background:"#272822",foreground:"#fdfff1",cursor:"#fdfff1",cursorAccent:"#272822",selectionBackground:"#fdfff1",selectionForeground:"#272822",black:"#272822",red:"#f92672",green:"#a6e22e",yellow:"#e6db74",blue:"#fd971f",magenta:"#ae81ff",cyan:"#66d9ef",white:"#fdfff1",brightBlack:"#6e7066",brightRed:"#f92672",brightGreen:"#a6e22e",brightYellow:"#e6db74",brightBlue:"#fd971f",brightMagenta:"#ae81ff",brightCyan:"#66d9ef",brightWhite:"#fdfff1"},"monokai-pro":{background:"#2d2a2e",foreground:"#fcfcfa",cursor:"#fcfcfa",cursorAccent:"#2d2a2e",selectionBackground:"#fcfcfa",selectionForeground:"#2d2a2e",black:"#403e41",red:"#ff6188",green:"#a9dc76",yellow:"#ffd866",blue:"#fc9867",magenta:"#ab9df2",cyan:"#78dce8",white:"#fcfcfa",brightBlack:"#727072",brightRed:"#ff6188",brightGreen:"#a9dc76",brightYellow:"#ffd866",brightBlue:"#fc9867",brightMagenta:"#ab9df2",brightCyan:"#78dce8",brightWhite:"#fcfcfa"},ristretto:{background:"#2d2525",foreground:"#fff1f3",cursor:"#fff1f3",cursorAccent:"#2d2525",selectionBackground:"#fff1f3",selectionForeground:"#2d2525",black:"#2c2525",red:"#fd6883",green:"#adda78",yellow:"#f9cc6c",blue:"#f38d70",magenta:"#a8a9eb",cyan:"#85dacc",white:"#f9f8f5",brightBlack:"#655761",brightRed:"#fd6883",brightGreen:"#adda78",brightYellow:"#f9cc6c",brightBlue:"#f38d70",brightMagenta:"#a8a9eb",brightCyan:"#85dacc",brightWhite:"#f9f8f5"},dark:{background:"#1e1e1e",foreground:"#d4d4d4",cursor:"#aeafad",cursorAccent:"#1e1e1e",selectionBackground:"#d4d4d4",selectionForeground:"#1e1e1e",black:"#000000",red:"#cd3131",green:"#0dbc79",yellow:"#e5e510",blue:"#2472c8",magenta:"#bc3fbc",cyan:"#11a8cd",white:"#e5e5e5",brightBlack:"#666666",brightRed:"#f14c4c",brightGreen:"#23d18b",brightYellow:"#f5f543",brightBlue:"#3b8eea",brightMagenta:"#d670d6",brightCyan:"#29b8db",brightWhite:"#ffffff"},light:{background:"#ffffff",foreground:"#383a42",cursor:"#526eff",cursorAccent:"#ffffff",selectionBackground:"#383a42",selectionForeground:"#ffffff",black:"#000000",red:"#e45649",green:"#50a14f",yellow:"#c18401",blue:"#4078f2",magenta:"#a626a4",cyan:"#0184bc",white:"#a0a1a7",brightBlack:"#5c6370",brightRed:"#e06c75",brightGreen:"#98c379",brightYellow:"#d19a66",brightBlue:"#61afef",brightMagenta:"#c678dd",brightCyan:"#56b6c2",brightWhite:"#ffffff"},dracula:{background:"#282a36",foreground:"#f8f8f2",cursor:"#f8f8f2",cursorAccent:"#282a36",selectionBackground:"#f8f8f2",selectionForeground:"#282a36",black:"#21222c",red:"#ff5555",green:"#50fa7b",yellow:"#f1fa8c",blue:"#bd93f9",magenta:"#ff79c6",cyan:"#8be9fd",white:"#f8f8f2",brightBlack:"#6272a4",brightRed:"#ff6e6e",brightGreen:"#69ff94",brightYellow:"#ffffa5",brightBlue:"#d6acff",brightMagenta:"#ff92df",brightCyan:"#a4ffff",brightWhite:"#ffffff"},catppuccin:{background:"#1e1e2e",foreground:"#cdd6f4",cursor:"#f5e0dc",cursorAccent:"#1e1e2e",selectionBackground:"#cdd6f4",selectionForeground:"#1e1e2e",black:"#45475a",red:"#f38ba8",green:"#a6e3a1",yellow:"#f9e2af",blue:"#89b4fa",magenta:"#f5c2e7",cyan:"#94e2d5",white:"#bac2de",brightBlack:"#585b70",brightRed:"#f38ba8",brightGreen:"#a6e3a1",brightYellow:"#f9e2af",brightBlue:"#89b4fa",brightMagenta:"#f5c2e7",brightCyan:"#94e2d5",brightWhite:"#a6adc8"},nord:{background:"#2e3440",foreground:"#d8dee9",cursor:"#d8dee9",cursorAccent:"#2e3440",selectionBackground:"#d8dee9",selectionForeground:"#2e3440",black:"#3b4252",red:"#bf616a",green:"#a3be8c",yellow:"#ebcb8b",blue:"#81a1c1",magenta:"#b48ead",cyan:"#88c0d0",white:"#e5e9f0",brightBlack:"#4c566a",brightRed:"#bf616a",brightGreen:"#a3be8c",brightYellow:"#ebcb8b",brightBlue:"#81a1c1",brightMagenta:"#b48ead",brightCyan:"#8fbcbb",brightWhite:"#eceff4"},gruvbox:{background:"#282828",foreground:"#ebdbb2",cursor:"#ebdbb2",cursorAccent:"#282828",selectionBackground:"#ebdbb2",selectionForeground:"#282828",black:"#282828",red:"#cc241d",green:"#98971a",yellow:"#d79921",blue:"#458588",magenta:"#b16286",cyan:"#689d6a",white:"#a89984",brightBlack:"#928374",brightRed:"#fb4934",brightGreen:"#b8bb26",brightYellow:"#fabd2f",brightBlue:"#83a598",brightMagenta:"#d3869b",brightCyan:"#8ec07c",brightWhite:"#ebdbb2"},solarized:{background:"#002b36",foreground:"#839496",cursor:"#839496",cursorAccent:"#002b36",selectionBackground:"#839496",selectionForeground:"#002b36",black:"#073642",red:"#dc322f",green:"#859900",yellow:"#b58900",blue:"#268bd2",magenta:"#d33682",cyan:"#2aa198",white:"#eee8d5",brightBlack:"#586e75",brightRed:"#cb4b16",brightGreen:"#586e75",brightYellow:"#657b83",brightBlue:"#839496",brightMagenta:"#6c71c4",brightCyan:"#93a1a1",brightWhite:"#fdf6e3"},tokyo:{background:"#1a1b26",foreground:"#a9b1d6",cursor:"#c0caf5",cursorAccent:"#1a1b26",selectionBackground:"#a9b1d6",selectionForeground:"#1a1b26",black:"#15161e",red:"#f7768e",green:"#9ece6a",yellow:"#e0af68",blue:"#7aa2f7",magenta:"#bb9af7",cyan:"#7dcfff",white:"#a9b1d6",brightBlack:"#414868",brightRed:"#f7768e",brightGreen:"#9ece6a",brightYellow:"#e0af68",brightBlue:"#7aa2f7",brightMagenta:"#bb9af7",brightCyan:"#7dcfff",brightWhite:"#c0caf5"},miasma:{background:"#222222",foreground:"#c2c2b0",cursor:"#c2c2b0",cursorAccent:"#222222",selectionBackground:"#c2c2b0",selectionForeground:"#222222",black:"#000000",red:"#685742",green:"#5f875f",yellow:"#b36d43",blue:"#78824b",magenta:"#bb7744",cyan:"#c9a554",white:"#d7c483",brightBlack:"#666666",brightRed:"#685742",brightGreen:"#5f875f",brightYellow:"#b36d43",brightBlue:"#78824b",brightMagenta:"#bb7744",brightCyan:"#c9a554",brightWhite:"#d7c483"},github:{background:"#1c2128",foreground:"#adbac7",cursor:"#adbac7",cursorAccent:"#1c2128",selectionBackground:"#adbac7",selectionForeground:"#1c2128",black:"#545d68",red:"#f47067",green:"#57ab5a",yellow:"#c69026",blue:"#539bf5",magenta:"#b083f0",cyan:"#39c5cf",white:"#909dab",brightBlack:"#636e7b",brightRed:"#ff938a",brightGreen:"#6bc46d",brightYellow:"#daaa3f",brightBlue:"#6cb6ff",brightMagenta:"#dcbdfb",brightCyan:"#56d4dd",brightWhite:"#cdd9e5"},gotham:{background:"#0c1014",foreground:"#99d1ce",cursor:"#99d1ce",cursorAccent:"#0c1014",selectionBackground:"#99d1ce",selectionForeground:"#0c1014",black:"#0c1014",red:"#c23127",green:"#2aa889",yellow:"#edb443",blue:"#195466",magenta:"#4e5166",cyan:"#33859e",white:"#99d1ce",brightBlack:"#0c1014",brightRed:"#c23127",brightGreen:"#2aa889",brightYellow:"#edb443",brightBlue:"#195466",brightMagenta:"#4e5166",brightCyan:"#33859e",brightWhite:"#99d1ce"}};function S0(j){let V={};if(j.dataset.fontFamily){let $=j.dataset.fontFamily;if($.startsWith("var(")){let Z=$.match(/var\(([^)]+)\)/);if(Z){let J=Z[1].trim(),O=getComputedStyle(document.documentElement).getPropertyValue(J).trim();if(O)$=O;else console.warn(`[webterm] CSS variable ${J} not found, using default font`),$=K0}}V.fontFamily=$}if(j.dataset.fontSize)V.fontSize=parseInt(j.dataset.fontSize,10);if(j.dataset.scrollback)V.scrollback=parseInt(j.dataset.scrollback,10);if(j.dataset.theme){let $=j.dataset.theme.toLowerCase();if($ in k)V.theme=k[$];else try{V.theme=JSON.parse(j.dataset.theme)}catch(Z){console.warn(`[webterm] Unknown theme "${j.dataset.theme}"`,Z)}}return V}function u0(){let j=document.querySelectorAll('script[src*="terminal.js"]');if(j.length>0){let V=j[0].src;return V.substring(0,V.lastIndexOf("/")+1)+"ghostty-vt.wasm"}return"/static/js/ghostty-vt.wasm"}function l(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||"ontouchstart"in window&&navigator.maxTouchPoints>0}var f0={"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+","[":"{","]":"}","\\":"|",";":":","'":'"',",":"<",".":">","/":"?"},M0={"2":"@","3":"[","4":"\\","5":"]","6":"^","7":"_","8":"?"},m0=["\x1BOP","\x1BOQ","\x1BOR","\x1BOS","\x1B[15~","\x1B[17~","\x1B[18~","\x1B[19~","\x1B[20~","\x1B[21~"],k0=["\x1B[23~","\x1B[24~","\x1B[25~","\x1B[26~","\x1B[28~","\x1B[29~","\x1B[31~","\x1B[32~","\x1B[33~","\x1B[34~"];function p0(j){if(j.length!==1)return j;if(j>="a"&&j<="z")return j.toUpperCase();return f0[j]??j}function L0(j){if(j.length!==1)return j;let V=M0[j]??j;if(V==="?")return"";let $=V.toUpperCase().charCodeAt(0);if($>=64&&$<=95)return String.fromCharCode($-64);return j}function n(j,V){if(j.length!==1)return null;let $="1234567890".indexOf(j);if($<0)return null;return V?k0[$]:m0[$]}function S(j){if(!j||j.startsWith("\x1B"))return j;return`\x1B${j}`}function I(j,V,$,Z,J){if(j.length!==1)return j;if(J){let O=n(j,V);if(O)return Z?S(O):O}if($){let O=L0(j);if(O!==j)return Z?S(O):O}if(V){let O=p0(j);return Z?S(O):O}return Z?S(j):j}class o{terminal;fitAddon;socket=null;element;wsUrl;reconnectAttempts=0;maxReconnectAttempts=5;reconnectDelay=1000;heartbeatIntervalMs=15000;stallTimeoutMs=45000;heartbeatTimer;lastMessageAt=0;lastPongAt=0;messageQueue=[];lastValidSize=null;mobileInput=null;mobileKeybar=null;ctrlActive=!1;altActive=!1;shiftActive=!1;fnActive=!1;pendingCtrl=!1;pendingAlt=!1;pendingShift=!1;pendingFn=!1;fontFamily;fontSize;cleanupTimer;resizeObserver=null;mobileKeybarStyle=null;boundHandlers=[];constructor(j,V,$,Z,J,O){this.element=j,this.wsUrl=V,this.terminal=$,this.fitAddon=Z,this.fontFamily=J,this.fontSize=O}addTrackedListener(j,V,$,Z){j.addEventListener(V,$,Z),this.boundHandlers.push({target:j,type:V,handler:$,options:Z})}static async create(j,V,$){let Z=await x0(),J=$.theme??k.tango,O=$.fontFamily?.trim()||K0,P=$.fontSize??16,X=l()?200:1000,K={fontFamily:O,fontSize:P,scrollback:$.scrollback??X,cursorBlink:!0,cursorStyle:"block",theme:J,ghostty:Z},L=new d(K),W=new h;L.loadAddon(W),L.open(j);let z=new o(j,V,L,W,O,P);return z.initialize(),z}initialize(){if(this.waitForFonts().then(()=>{if(typeof this.terminal.loadFonts==="function")this.terminal.loadFonts();this.fit()}),this.setupResizeObserver(),this.addTrackedListener(window,"resize",()=>{this.fit()}),this.terminal.onData((V)=>{this.send(["stdin",V])}),this.terminal.onResize((V)=>{if(this.isValidSize(V.cols,V.rows))this.lastValidSize={cols:V.cols,rows:V.rows},this.send(["resize",{width:V.cols,height:V.rows}])}),this.setupMobileKeyboard(),this.setupTouchSelection(),l())this.setupMobileKeybar();this.startResourceCleanup(),this.connect();let j=()=>{if(l())this.focusMobileInput();else this.terminal.focus()};this.addTrackedListener(document,"visibilitychange",()=>{if(document.hidden)this.stopHeartbeatWatchdog();else{if(this.socket?.readyState===WebSocket.OPEN)this.startHeartbeatWatchdog();j()}}),this.addTrackedListener(window,"focus",()=>{j()}),this.addTrackedListener(window,"pageshow",()=>{j()})}setupMobileKeyboard(){let j=document.createElement("textarea");j.setAttribute("autocapitalize","off"),j.setAttribute("autocomplete","off"),j.setAttribute("autocorrect","off"),j.setAttribute("spellcheck","false"),j.setAttribute("inputmode","text"),j.setAttribute("enterkeyhint","send"),j.style.cssText=`
position: absolute;
left: 0;
top: 0;
@@ -26,7 +26,7 @@ For tests, pass a Ghostty instance directly:
font-size: 16px;
caret-color: transparent;
pointer-events: none;
- `,this.element.style.position="relative",this.element.appendChild(j),this.mobileInput=j;let V=(J)=>r(J,this.shiftActive||this.pendingShift,this.ctrlActive||this.pendingCtrl,this.altActive||this.pendingAlt,this.fnActive||this.pendingFn),$=(J,O)=>{if(O)O.preventDefault(),O.stopPropagation();if(!J)return;this.send(["stdin",V(J)]),j.value="",this.deactivateModifiers(),this.pendingCtrl=!1,this.pendingAlt=!1,this.pendingShift=!1,this.pendingFn=!1};j.addEventListener("beforeinput",(J)=>{if(J.inputType==="insertText"&&J.data){$(J.data,J);return}let O=null;switch(J.inputType){case"insertLineBreak":O="\r";break;case"deleteContentBackward":O="";break;case"deleteContentForward":O="\x1B[3~";break}if(O)$(O,J)}),j.addEventListener("input",()=>{$(j.value)}),j.addEventListener("keydown",(J)=>{let O=J.ctrlKey||this.ctrlActive,X=J.shiftKey||this.shiftActive,P=J.altKey||this.altActive,K=this.fnActive;if(O&&J.key.length===1&&!J.altKey&&!J.metaKey){let z=R0(J.key);if(z!==J.key){J.preventDefault(),J.stopPropagation();let W=P?S(z):z;this.send(["stdin",W]),this.deactivateModifiers();return}}if(K&&J.key.length===1&&!J.ctrlKey&&!J.metaKey){let z=C(J.key,X);if(z){J.preventDefault(),J.stopPropagation(),this.send(["stdin",z]),this.deactivateModifiers();return}}let L=null;switch(J.key){case"Escape":L="\x1B";break;case"ArrowUp":case"ArrowDown":case"ArrowRight":case"ArrowLeft":{let z=J.key==="ArrowUp"?"A":J.key==="ArrowDown"?"B":J.key==="ArrowRight"?"C":"D";if(O&&X)L=`\x1B[1;6${z}`;else if(O)L=`\x1B[1;5${z}`;else if(X)L=`\x1B[1;2${z}`;else L=`\x1B[${z}`;break}case"Tab":if(X)L="\x1B[Z";else L="\t";J.preventDefault();break}if(L)J.preventDefault(),J.stopPropagation(),this.send(["stdin",P?S(L):L]),this.deactivateModifiers()}),this.addTrackedListener(document,"keydown",(J)=>{if(!this.ctrlActive&&!this.shiftActive&&!this.altActive&&!this.fnActive)return;if(J.target===this.mobileInput)return;let O=this.ctrlActive,X=this.shiftActive,P=this.altActive,K=this.fnActive,L=!1;if(J.key.length===1&&!J.altKey&&!J.metaKey){let z=r(J.key,X,O,P,K);J.preventDefault(),J.stopPropagation(),this.send(["stdin",z]),L=!0}else{let z=null;switch(J.key){case"Escape":z="\x1B";break;case"ArrowUp":case"ArrowDown":case"ArrowRight":case"ArrowLeft":{let W=J.key==="ArrowUp"?"A":J.key==="ArrowDown"?"B":J.key==="ArrowRight"?"C":"D";if(O&&X)z=`\x1B[1;6${W}`;else if(O)z=`\x1B[1;5${W}`;else if(X)z=`\x1B[1;2${W}`;else z=`\x1B[${W}`;break}case"Tab":if(X)z="\x1B[Z";else z="\t";break}if(z)J.preventDefault(),J.stopPropagation(),this.send(["stdin",P?S(z):z]),L=!0}if(L)this.deactivateModifiers()},{capture:!0});let Z=()=>{this.mobileInput?.focus()};this.addTrackedListener(this.element,"touchend",Z,{passive:!0}),this.addTrackedListener(this.element,"click",Z)}setupTouchSelection(){let j=this.element.querySelector("canvas");if(!j)return;let V=($,Z)=>{let J=j.getBoundingClientRect(),O=new MouseEvent($,{bubbles:!0,cancelable:!0,clientX:Z.clientX,clientY:Z.clientY,button:0,buttons:$==="mouseup"?0:1});j.dispatchEvent(O)};j.addEventListener("touchstart",($)=>{if($.touches.length!==1)return;V("mousedown",$.touches[0]),$.preventDefault()},{passive:!1}),j.addEventListener("touchmove",($)=>{if($.touches.length!==1)return;V("mousemove",$.touches[0]),$.preventDefault()},{passive:!1}),j.addEventListener("touchend",($)=>{let Z=$.changedTouches[0];if(!Z)return;V("mouseup",Z),$.preventDefault()},{passive:!1})}setupMobileKeybar(){let j=document.createElement("div");j.className="mobile-keybar",j.innerHTML=`
+ `,this.element.style.position="relative",this.element.appendChild(j),this.mobileInput=j;let V=(J)=>I(J,this.shiftActive||this.pendingShift,this.ctrlActive||this.pendingCtrl,this.altActive||this.pendingAlt,this.fnActive||this.pendingFn),$=(J,O)=>{if(O)O.preventDefault(),O.stopPropagation();if(!J)return;this.send(["stdin",V(J)]),j.value="",this.deactivateModifiers(),this.pendingCtrl=!1,this.pendingAlt=!1,this.pendingShift=!1,this.pendingFn=!1};j.addEventListener("beforeinput",(J)=>{if(J.inputType==="insertText"&&J.data){$(J.data,J);return}let O=null;switch(J.inputType){case"insertLineBreak":O="\r";break;case"deleteContentBackward":O="";break;case"deleteContentForward":O="\x1B[3~";break}if(O)$(O,J)}),j.addEventListener("input",()=>{$(j.value)}),j.addEventListener("keydown",(J)=>{let O=J.ctrlKey||this.ctrlActive,P=J.shiftKey||this.shiftActive,X=J.altKey||this.altActive,K=this.fnActive;if(O&&J.key.length===1&&!J.altKey&&!J.metaKey){let W=L0(J.key);if(W!==J.key){J.preventDefault(),J.stopPropagation();let z=X?S(W):W;this.send(["stdin",z]),this.deactivateModifiers();return}}if(K&&J.key.length===1&&!J.ctrlKey&&!J.metaKey){let W=n(J.key,P);if(W){J.preventDefault(),J.stopPropagation(),this.send(["stdin",W]),this.deactivateModifiers();return}}let L=null;switch(J.key){case"Escape":L="\x1B";break;case"ArrowUp":case"ArrowDown":case"ArrowRight":case"ArrowLeft":{let W=J.key==="ArrowUp"?"A":J.key==="ArrowDown"?"B":J.key==="ArrowRight"?"C":"D";if(O&&P)L=`\x1B[1;6${W}`;else if(O)L=`\x1B[1;5${W}`;else if(P)L=`\x1B[1;2${W}`;else L=`\x1B[${W}`;break}case"Tab":if(P)L="\x1B[Z";else L="\t";J.preventDefault();break}if(L)J.preventDefault(),J.stopPropagation(),this.send(["stdin",X?S(L):L]),this.deactivateModifiers()}),this.addTrackedListener(document,"keydown",(J)=>{if(!this.ctrlActive&&!this.shiftActive&&!this.altActive&&!this.fnActive)return;if(J.target===this.mobileInput)return;let O=this.ctrlActive,P=this.shiftActive,X=this.altActive,K=this.fnActive,L=!1;if(J.key.length===1&&!J.altKey&&!J.metaKey){let W=I(J.key,P,O,X,K);J.preventDefault(),J.stopPropagation(),this.send(["stdin",W]),L=!0}else{let W=null;switch(J.key){case"Escape":W="\x1B";break;case"ArrowUp":case"ArrowDown":case"ArrowRight":case"ArrowLeft":{let z=J.key==="ArrowUp"?"A":J.key==="ArrowDown"?"B":J.key==="ArrowRight"?"C":"D";if(O&&P)W=`\x1B[1;6${z}`;else if(O)W=`\x1B[1;5${z}`;else if(P)W=`\x1B[1;2${z}`;else W=`\x1B[${z}`;break}case"Tab":if(P)W="\x1B[Z";else W="\t";break}if(W)J.preventDefault(),J.stopPropagation(),this.send(["stdin",X?S(W):W]),L=!0}if(L)this.deactivateModifiers()},{capture:!0});let Z=()=>{this.mobileInput?.focus()};this.addTrackedListener(this.element,"touchend",Z,{passive:!0}),this.addTrackedListener(this.element,"click",Z)}setupTouchSelection(){let j=this.element.querySelector("canvas");if(!j)return;let V=($,Z)=>{let J=j.getBoundingClientRect(),O=new MouseEvent($,{bubbles:!0,cancelable:!0,clientX:Z.clientX,clientY:Z.clientY,button:0,buttons:$==="mouseup"?0:1});j.dispatchEvent(O)};j.addEventListener("touchstart",($)=>{if($.touches.length!==1)return;V("mousedown",$.touches[0]),$.preventDefault()},{passive:!1}),j.addEventListener("touchmove",($)=>{if($.touches.length!==1)return;V("mousemove",$.touches[0]),$.preventDefault()},{passive:!1}),j.addEventListener("touchend",($)=>{let Z=$.changedTouches[0];if(!Z)return;V("mouseup",Z),$.preventDefault()},{passive:!1})}setupMobileKeybar(){let j=document.createElement("div");j.className="mobile-keybar",j.innerHTML=`
⋮⋮
Esc
Ctrl
@@ -89,4 +89,4 @@ For tests, pass a Ghostty instance directly:
grid-column: 6;
grid-row: 2;
}
- `,document.head.appendChild(V),this.mobileKeybarStyle=V,document.body.appendChild(j),this.mobileKeybar=j,j.querySelectorAll("button[data-key]").forEach(($)=>{$.addEventListener("touchstart",(Z)=>{Z.preventDefault();let J=$.dataset.key||"";J=J.replace(/\\x([0-9a-fA-F]{2})/g,(z,W)=>String.fromCharCode(parseInt(W,16))),J=J.replace(/\\x1b/g,"\x1B");let O=this.shiftActive,X=this.ctrlActive,P=this.altActive,K=this.fnActive,L=O||this.pendingShift;if(O&&J==="\t")J="\x1B[Z";else if(X&&O&&J.startsWith("\x1B[")&&J.length===3)J=`\x1B[1;6${J[2]}`;else if(O&&J.startsWith("\x1B[")&&J.length===3)J=`\x1B[1;2${J[2]}`;else if(X&&J.startsWith("\x1B[")&&J.length===3)J=`\x1B[1;5${J[2]}`;if(K&&J.length===1){let z=C(J,L);if(z)J=z}if(J.length===1)J=r(J,O,X,P,K);else if(P)J=S(J);this.send(["stdin",J]),this.deactivateModifiers()})}),j.querySelectorAll("button[data-modifier]").forEach(($)=>{$.addEventListener("touchstart",(Z)=>{Z.preventDefault();let J=$.dataset.modifier;if(J==="ctrl")this.ctrlActive=!this.ctrlActive,this.pendingCtrl=this.ctrlActive,$.classList.toggle("active",this.ctrlActive);else if(J==="alt")this.altActive=!this.altActive,this.pendingAlt=this.altActive,$.classList.toggle("active",this.altActive);else if(J==="shift")this.shiftActive=!this.shiftActive,this.pendingShift=this.shiftActive,$.classList.toggle("active",this.shiftActive);else if(J==="fn")this.fnActive=!this.fnActive,this.pendingFn=this.fnActive,$.classList.toggle("active",this.fnActive);this.focusMobileInput()})}),this.setupKeybarDrag(j)}setupKeybarDrag(j){let V=j.querySelector(".keybar-drag");if(!V)return;let $=!1,Z=0,J=0,O=0,X=0,P=(z)=>{if(z.touches.length!==1)return;$=!0;let W=z.touches[0];Z=W.clientX,J=W.clientY;let Y=j.getBoundingClientRect();O=window.innerWidth-Y.right,X=window.innerHeight-Y.bottom,z.preventDefault()},K=(z)=>{if(!$||z.touches.length!==1)return;let W=z.touches[0],Y=Z-W.clientX,H=J-W.clientY,q=Math.max(0,Math.min(window.innerWidth-100,O+Y)),_=Math.max(0,Math.min(window.innerHeight-50,X+H));j.style.right=`${q}px`,j.style.bottom=`${_}px`,z.preventDefault()},L=()=>{$=!1};V.addEventListener("touchstart",P,{passive:!1}),document.addEventListener("touchmove",K,{passive:!1}),document.addEventListener("touchend",L)}deactivateModifiers(){this.ctrlActive=!1,this.altActive=!1,this.shiftActive=!1,this.fnActive=!1,this.pendingCtrl=!1,this.pendingAlt=!1,this.pendingShift=!1,this.pendingFn=!1,this.mobileKeybar?.querySelectorAll("button[data-modifier]").forEach((j)=>{j.classList.remove("active")})}focusMobileInput(){this.mobileInput?.focus()}async waitForFonts(){if(!("fonts"in document))return;try{await document.fonts.ready}catch{}}fit(){let V=this.terminal.renderer,$,Z;if(V?.getMetrics){let H=V.getMetrics();if(H&&H.width>0&&H.height>0)$=H.width,Z=H.height;else{let q=this.measureCellSize();if(!q){this.fitAddon.fit();return}$=q.width,Z=q.height}}else{let H=this.measureCellSize();if(!H){this.fitAddon.fit();return}$=H.width,Z=H.height}let J=window.getComputedStyle(this.element),O=parseInt(J.paddingTop)||0,X=parseInt(J.paddingBottom)||0,P=parseInt(J.paddingLeft)||0,K=parseInt(J.paddingRight)||0,L=this.element.clientWidth-P-K,z=this.element.clientHeight-O-X;if(L<=0||z<=0)return;let W=Math.max(2,Math.floor(L/$)),Y=Math.max(1,Math.floor(z/Z));if(W!==this.terminal.cols||Y!==this.terminal.rows)this.terminal.resize(W,Y)}measureCellSize(){let j=document.createElement("span");j.style.visibility="hidden",j.style.position="absolute",j.style.fontFamily=this.fontFamily,j.style.fontSize=`${this.fontSize}px`,j.style.lineHeight="normal",j.textContent="W",document.body.appendChild(j);let{offsetWidth:V,offsetHeight:$}=j;if(document.body.removeChild(j),V>0&&$>0)return{width:V,height:$};return null}setupResizeObserver(){this.resizeObserver=new ResizeObserver(()=>{if(this.resizeDebounceTimer)clearTimeout(this.resizeDebounceTimer);this.resizeDebounceTimer=window.setTimeout(()=>{this.fit()},100)}),this.resizeObserver.observe(this.element)}resizeDebounceTimer;isValidSize(j,V){return j>=2&&j<=500&&V>=1&&V<=200}connect(){if(this.socket?.readyState===WebSocket.OPEN)return;this.socket=new WebSocket(this.wsUrl),this.socket.binaryType="arraybuffer",this.socket.addEventListener("open",()=>{this.reconnectAttempts=0,this.startHeartbeatWatchdog(),this.element.classList.add("-connected"),this.element.classList.remove("-disconnected"),this.processMessageQueue();let j=this.terminal.cols,V=this.terminal.rows;if(this.isValidSize(j,V))this.lastValidSize={cols:j,rows:V},this.send(["resize",{width:j,height:V}]);this.terminal.focus()}),this.socket.addEventListener("close",()=>{this.stopHeartbeatWatchdog(),this.element.classList.remove("-connected"),this.element.classList.add("-disconnected"),this.scheduleReconnect()}),this.socket.addEventListener("error",()=>{}),this.socket.addEventListener("message",(j)=>{this.handleMessage(j.data)})}handleTextMessage(j){try{let V=JSON.parse(j),[$,Z]=V;switch($){case"stdout":this.terminal.write(Z);break;case"pong":this.lastPongAt=Date.now();break;default:console.debug("Unknown message type:",$)}}catch{this.terminal.write(j)}}handleMessage(j){if(this.lastMessageAt=Date.now(),j instanceof ArrayBuffer){this.terminal.write(new Uint8Array(j));return}if(j instanceof Blob){j.text().then((V)=>{this.lastMessageAt=Date.now(),this.handleTextMessage(V)}).catch(()=>{});return}this.handleTextMessage(j)}startHeartbeatWatchdog(){this.stopHeartbeatWatchdog();let j=Date.now();this.lastMessageAt=j,this.lastPongAt=j,this.heartbeatTimer=window.setInterval(()=>{if(this.socket?.readyState!==WebSocket.OPEN)return;let V=Date.now(),$=Math.max(this.lastMessageAt,this.lastPongAt);if(V-$>this.stallTimeoutMs){console.warn("WebSocket inbound stream stalled; reconnecting"),this.socket.close();return}this.send(["ping",String(V)])},this.heartbeatIntervalMs)}stopHeartbeatWatchdog(){if(this.heartbeatTimer)clearInterval(this.heartbeatTimer),this.heartbeatTimer=void 0}startResourceCleanup(){this.cleanupTimer=window.setInterval(()=>{this.trimMessageQueue()},P0)}stopResourceCleanup(){if(this.cleanupTimer)clearInterval(this.cleanupTimer),this.cleanupTimer=void 0}trimMessageQueue(){if(this.messageQueue.length>k){let j=this.messageQueue.length-k;this.messageQueue=this.messageQueue.slice(-k),console.warn(`[webterm] Trimmed ${j} stale messages from queue`)}}send(j){if(this.messageQueue.length>=k)this.messageQueue=this.messageQueue.slice(-Math.floor(k/2)),console.warn("[webterm] Message queue overflow; trimmed old messages");this.messageQueue.push(j),this.processMessageQueue()}processMessageQueue(){if(this.socket?.readyState!==WebSocket.OPEN)return;while(this.messageQueue.length>0){let j=this.messageQueue.shift();try{if(j)this.socket.send(JSON.stringify(j))}catch(V){if(console.error("Failed to send message:",V,j),j)this.messageQueue.unshift(j);break}}}scheduleReconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts){console.error("Max reconnection attempts reached");return}this.reconnectAttempts++;let j=this.reconnectDelay*Math.pow(2,this.reconnectAttempts-1);setTimeout(()=>{console.log(`Reconnecting (attempt ${this.reconnectAttempts})...`),this.connect()},j)}dispose(){if(this.stopResourceCleanup(),this.stopHeartbeatWatchdog(),this.resizeDebounceTimer)clearTimeout(this.resizeDebounceTimer),this.resizeDebounceTimer=void 0;this.socket?.close(),this.socket=null,this.messageQueue.length=0;for(let{target:j,type:V,handler:$,options:Z}of this.boundHandlers)j.removeEventListener(V,$,Z);if(this.boundHandlers.length=0,this.resizeObserver)this.resizeObserver.disconnect(),this.resizeObserver=null;if(this.mobileInput)this.mobileInput.remove(),this.mobileInput=null;if(this.mobileKeybar)this.mobileKeybar.remove(),this.mobileKeybar=null;if(this.mobileKeybarStyle)this.mobileKeybarStyle.remove(),this.mobileKeybarStyle=null;this.fitAddon.dispose(),this.terminal.dispose()}setTheme(j){let V=this.terminal.renderer;if(V&&typeof V.setTheme==="function")V.setTheme(j)}static getTheme(j){return M[j.toLowerCase()]}}var o=new Map;setInterval(()=>{for(let[j,V]of o)if(!j.isConnected)V.dispose(),o.delete(j),console.log("[webterm] Cleaned up stale terminal instance")},P0);async function X0(){console.log("[webterm:init] initTerminals() called");let j=document.querySelectorAll(".webterm-terminal");console.log(`[webterm:init] Found ${j.length} .webterm-terminal containers`);for(let V of j){console.log("[webterm:init] Processing container:",V),console.log("[webterm:init] Dataset:",JSON.stringify(V.dataset));let $=V.dataset.sessionWebsocketUrl;if(!$){console.error("Missing data-session-websocket-url on terminal container");continue}let Z=u0(V);console.log("[webterm:init] Parsed config:",JSON.stringify(Z,null,2));try{console.log("[webterm:init] Calling WebTerminal.create()...");let J=await n.create(V,$,Z);console.log("[webterm:init] WebTerminal created successfully"),o.set(V,J)}catch(J){console.error("Failed to create terminal:",J)}}}if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",()=>X0());else X0();export{o as instances,X0 as initTerminals,n as WebTerminal,M as THEMES};
+ `,document.head.appendChild(V),this.mobileKeybarStyle=V,document.body.appendChild(j),this.mobileKeybar=j,j.querySelectorAll("button[data-key]").forEach(($)=>{$.addEventListener("touchstart",(Z)=>{Z.preventDefault();let J=$.dataset.key||"";J=J.replace(/\\x([0-9a-fA-F]{2})/g,(W,z)=>String.fromCharCode(parseInt(z,16))),J=J.replace(/\\x1b/g,"\x1B");let O=this.shiftActive,P=this.ctrlActive,X=this.altActive,K=this.fnActive,L=O||this.pendingShift;if(O&&J==="\t")J="\x1B[Z";else if(P&&O&&J.startsWith("\x1B[")&&J.length===3)J=`\x1B[1;6${J[2]}`;else if(O&&J.startsWith("\x1B[")&&J.length===3)J=`\x1B[1;2${J[2]}`;else if(P&&J.startsWith("\x1B[")&&J.length===3)J=`\x1B[1;5${J[2]}`;if(K&&J.length===1){let W=n(J,L);if(W)J=W}if(J.length===1)J=I(J,O,P,X,K);else if(X)J=S(J);this.send(["stdin",J]),this.deactivateModifiers()})}),j.querySelectorAll("button[data-modifier]").forEach(($)=>{$.addEventListener("touchstart",(Z)=>{Z.preventDefault();let J=$.dataset.modifier;if(J==="ctrl")this.ctrlActive=!this.ctrlActive,this.pendingCtrl=this.ctrlActive,$.classList.toggle("active",this.ctrlActive);else if(J==="alt")this.altActive=!this.altActive,this.pendingAlt=this.altActive,$.classList.toggle("active",this.altActive);else if(J==="shift")this.shiftActive=!this.shiftActive,this.pendingShift=this.shiftActive,$.classList.toggle("active",this.shiftActive);else if(J==="fn")this.fnActive=!this.fnActive,this.pendingFn=this.fnActive,$.classList.toggle("active",this.fnActive);this.focusMobileInput()})}),this.setupKeybarDrag(j)}setupKeybarDrag(j){let V=j.querySelector(".keybar-drag");if(!V)return;let $=!1,Z=0,J=0,O=0,P=0,X=(W)=>{if(W.touches.length!==1)return;$=!0;let z=W.touches[0];Z=z.clientX,J=z.clientY;let H=j.getBoundingClientRect();O=window.innerWidth-H.right,P=window.innerHeight-H.bottom,W.preventDefault()},K=(W)=>{if(!$||W.touches.length!==1)return;let z=W.touches[0],H=Z-z.clientX,Y=J-z.clientY,q=Math.max(0,Math.min(window.innerWidth-100,O+H)),T=Math.max(0,Math.min(window.innerHeight-50,P+Y));j.style.right=`${q}px`,j.style.bottom=`${T}px`,W.preventDefault()},L=()=>{$=!1};V.addEventListener("touchstart",X,{passive:!1}),document.addEventListener("touchmove",K,{passive:!1}),document.addEventListener("touchend",L)}deactivateModifiers(){this.ctrlActive=!1,this.altActive=!1,this.shiftActive=!1,this.fnActive=!1,this.pendingCtrl=!1,this.pendingAlt=!1,this.pendingShift=!1,this.pendingFn=!1,this.mobileKeybar?.querySelectorAll("button[data-modifier]").forEach((j)=>{j.classList.remove("active")})}focusMobileInput(){this.mobileInput?.focus()}async waitForFonts(){if(!("fonts"in document))return;try{await document.fonts.ready}catch{}}fit(){let V=this.terminal.renderer,$,Z;if(V?.getMetrics){let Y=V.getMetrics();if(Y&&Y.width>0&&Y.height>0)$=Y.width,Z=Y.height;else{let q=this.measureCellSize();if(!q){this.fitAddon.fit();return}$=q.width,Z=q.height}}else{let Y=this.measureCellSize();if(!Y){this.fitAddon.fit();return}$=Y.width,Z=Y.height}let J=window.getComputedStyle(this.element),O=parseInt(J.paddingTop)||0,P=parseInt(J.paddingBottom)||0,X=parseInt(J.paddingLeft)||0,K=parseInt(J.paddingRight)||0,L=this.element.clientWidth-X-K,W=this.element.clientHeight-O-P;if(L<=0||W<=0)return;let z=Math.max(2,Math.floor(L/$)),H=Math.max(1,Math.floor(W/Z));if(z!==this.terminal.cols||H!==this.terminal.rows)this.terminal.resize(z,H)}measureCellSize(){let j=document.createElement("span");j.style.visibility="hidden",j.style.position="absolute",j.style.fontFamily=this.fontFamily,j.style.fontSize=`${this.fontSize}px`,j.style.lineHeight="normal",j.textContent="W",document.body.appendChild(j);let{offsetWidth:V,offsetHeight:$}=j;if(document.body.removeChild(j),V>0&&$>0)return{width:V,height:$};return null}setupResizeObserver(){this.resizeObserver=new ResizeObserver(()=>{if(this.resizeDebounceTimer)clearTimeout(this.resizeDebounceTimer);this.resizeDebounceTimer=window.setTimeout(()=>{this.fit()},100)}),this.resizeObserver.observe(this.element)}resizeDebounceTimer;isValidSize(j,V){return j>=2&&j<=500&&V>=1&&V<=200}connect(){if(this.socket?.readyState===WebSocket.OPEN)return;this.socket=new WebSocket(this.wsUrl),this.socket.binaryType="arraybuffer",this.socket.addEventListener("open",()=>{this.reconnectAttempts=0,this.startHeartbeatWatchdog(),this.element.classList.add("-connected"),this.element.classList.remove("-disconnected"),this.processMessageQueue();let j=this.terminal.cols,V=this.terminal.rows;if(this.isValidSize(j,V))this.lastValidSize={cols:j,rows:V},this.send(["resize",{width:j,height:V}]);this.terminal.focus()}),this.socket.addEventListener("close",()=>{this.stopHeartbeatWatchdog(),this.element.classList.remove("-connected"),this.element.classList.add("-disconnected"),this.scheduleReconnect()}),this.socket.addEventListener("error",()=>{}),this.socket.addEventListener("message",(j)=>{this.handleMessage(j.data)})}handleTextMessage(j){try{let V=JSON.parse(j),[$,Z]=V;switch($){case"stdout":this.terminal.write(Z);break;case"pong":this.lastPongAt=Date.now();break;default:console.debug("Unknown message type:",$)}}catch{this.terminal.write(j)}}handleMessage(j){if(this.lastMessageAt=Date.now(),j instanceof ArrayBuffer){this.terminal.write(new Uint8Array(j));return}if(j instanceof Blob){j.text().then((V)=>{this.lastMessageAt=Date.now(),this.handleTextMessage(V)}).catch(()=>{});return}this.handleTextMessage(j)}startHeartbeatWatchdog(){this.stopHeartbeatWatchdog();let j=Date.now();this.lastMessageAt=j,this.lastPongAt=j,this.heartbeatTimer=window.setInterval(()=>{if(this.socket?.readyState!==WebSocket.OPEN)return;let V=Date.now(),$=Math.max(this.lastMessageAt,this.lastPongAt);if(V-$>this.stallTimeoutMs){console.warn("WebSocket inbound stream stalled; reconnecting"),this.socket.close();return}this.send(["ping",String(V)])},this.heartbeatIntervalMs)}stopHeartbeatWatchdog(){if(this.heartbeatTimer)clearInterval(this.heartbeatTimer),this.heartbeatTimer=void 0}startResourceCleanup(){this.cleanupTimer=window.setInterval(()=>{this.trimMessageQueue()},X0)}stopResourceCleanup(){if(this.cleanupTimer)clearInterval(this.cleanupTimer),this.cleanupTimer=void 0}trimMessageQueue(){if(this.messageQueue.length>f){let j=this.messageQueue.length-f;this.messageQueue=this.messageQueue.slice(-f),console.warn(`[webterm] Trimmed ${j} stale messages from queue`)}}send(j){if(this.messageQueue.length>=f)this.messageQueue=this.messageQueue.slice(-Math.floor(f/2)),console.warn("[webterm] Message queue overflow; trimmed old messages");this.messageQueue.push(j),this.processMessageQueue()}processMessageQueue(){if(this.socket?.readyState!==WebSocket.OPEN)return;while(this.messageQueue.length>0){let j=this.messageQueue.shift();try{if(j)this.socket.send(JSON.stringify(j))}catch(V){if(console.error("Failed to send message:",V,j),j)this.messageQueue.unshift(j);break}}}scheduleReconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts){console.error("Max reconnection attempts reached");return}this.reconnectAttempts++;let j=this.reconnectDelay*Math.pow(2,this.reconnectAttempts-1);setTimeout(()=>{console.log(`[webterm] Reconnecting (attempt ${this.reconnectAttempts})...`),this.connect()},j)}dispose(){if(this.stopResourceCleanup(),this.stopHeartbeatWatchdog(),this.resizeDebounceTimer)clearTimeout(this.resizeDebounceTimer),this.resizeDebounceTimer=void 0;this.socket?.close(),this.socket=null,this.messageQueue.length=0;for(let{target:j,type:V,handler:$,options:Z}of this.boundHandlers)j.removeEventListener(V,$,Z);if(this.boundHandlers.length=0,this.resizeObserver)this.resizeObserver.disconnect(),this.resizeObserver=null;if(this.mobileInput)this.mobileInput.remove(),this.mobileInput=null;if(this.mobileKeybar)this.mobileKeybar.remove(),this.mobileKeybar=null;if(this.mobileKeybarStyle)this.mobileKeybarStyle.remove(),this.mobileKeybarStyle=null;this.fitAddon.dispose(),this.terminal.dispose()}setTheme(j){this.terminal.options.theme=j}static getTheme(j){return k[j.toLowerCase()]}}var C=new Map;setInterval(()=>{for(let[j,V]of C)if(!j.isConnected)V.dispose(),C.delete(j),console.log("[webterm] Cleaned up stale terminal instance")},X0);async function P0(){let j=document.querySelectorAll(".webterm-terminal");for(let V of j){let $=V.dataset.sessionWebsocketUrl;if(!$){console.error("[webterm] Missing data-session-websocket-url on terminal container");continue}let Z=S0(V);try{let J=await o.create(V,$,Z);C.set(V,J)}catch(J){console.error("[webterm] Failed to create terminal:",J)}}}if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",()=>P0());else P0();export{C as instances,P0 as initTerminals,o as WebTerminal,k as THEMES};
diff --git a/webterm/static/js/terminal.ts b/webterm/static/js/terminal.ts
index c763b55..f05cdc2 100644
--- a/webterm/static/js/terminal.ts
+++ b/webterm/static/js/terminal.ts
@@ -443,7 +443,6 @@ interface TerminalConfig {
/** Parse configuration from element data attributes */
function parseConfig(element: HTMLElement): TerminalConfig {
- console.log("[webterm:parseConfig] Parsing config from element");
const config: TerminalConfig = {};
if (element.dataset.fontFamily) {
@@ -456,48 +455,34 @@ function parseConfig(element: HTMLElement): TerminalConfig {
const resolved = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
if (resolved) {
fontFamily = resolved;
- console.log(`[webterm:parseConfig] Resolved CSS variable ${varName} to: "${fontFamily}"`);
} else {
- console.warn(`[webterm:parseConfig] CSS variable ${varName} not found, using default font`);
+ console.warn(`[webterm] CSS variable ${varName} not found, using default font`);
fontFamily = DEFAULT_FONT_FAMILY;
}
}
}
config.fontFamily = fontFamily;
- console.log(`[webterm:parseConfig] fontFamily: "${config.fontFamily}"`);
}
if (element.dataset.fontSize) {
config.fontSize = parseInt(element.dataset.fontSize, 10);
- console.log(`[webterm:parseConfig] fontSize: ${config.fontSize}`);
}
if (element.dataset.scrollback) {
config.scrollback = parseInt(element.dataset.scrollback, 10);
- console.log(`[webterm:parseConfig] scrollback: ${config.scrollback}`);
}
if (element.dataset.theme) {
const themeName = element.dataset.theme.toLowerCase();
- console.log(`[webterm:parseConfig] theme attribute: "${element.dataset.theme}" -> normalized: "${themeName}"`);
- console.log(`[webterm:parseConfig] Available themes: ${Object.keys(THEMES).join(", ")}`);
- console.log(`[webterm:parseConfig] Theme "${themeName}" in THEMES? ${themeName in THEMES}`);
-
if (themeName in THEMES) {
config.theme = THEMES[themeName];
- console.log(`[webterm:parseConfig] Using built-in theme "${themeName}":`, JSON.stringify(config.theme, null, 2));
} else {
// Try parsing as JSON for custom themes
- console.log(`[webterm:parseConfig] Theme not found in THEMES, trying JSON parse...`);
try {
config.theme = JSON.parse(element.dataset.theme) as ITheme;
- console.log(`[webterm:parseConfig] Parsed custom JSON theme:`, config.theme);
} catch (e) {
- console.warn(`[webterm:parseConfig] Unknown theme "${element.dataset.theme}", JSON parse failed:`, e);
+ console.warn(`[webterm] Unknown theme "${element.dataset.theme}"`, e);
}
}
- } else {
- console.log(`[webterm:parseConfig] No theme attribute found on element`);
}
- console.log(`[webterm:parseConfig] Final config:`, config);
return config;
}
@@ -725,21 +710,10 @@ class WebTerminal {
wsUrl: string,
config: TerminalConfig
): Promise {
- console.log("[webterm:create] WebTerminal.create() called");
- console.log("[webterm:create] Container:", container);
- console.log("[webterm:create] wsUrl:", wsUrl);
- console.log("[webterm:create] Config received:", JSON.stringify(config, null, 2));
-
- // Determine WASM path and pre-load Ghostty
- const wasmPath = getWasmPath();
- console.log("[webterm:create] WASM path:", wasmPath);
- console.log("[webterm:create] Loading shared Ghostty WASM...");
const ghostty = await getSharedGhostty();
- console.log("[webterm:create] Ghostty loaded:", ghostty);
// Build terminal options
const themeToUse = config.theme ?? THEMES.tango;
- console.log("[webterm:create] Theme to use (config.theme ?? THEMES.xterm):", JSON.stringify(themeToUse, null, 2));
const fontFamily = config.fontFamily?.trim() || DEFAULT_FONT_FAMILY;
const fontSize = config.fontSize ?? 16;
@@ -753,37 +727,13 @@ class WebTerminal {
theme: themeToUse,
ghostty,
};
- console.log("[webterm:create] Full ITerminalOptions:", JSON.stringify(options, null, 2));
- console.log("[webterm:create] Creating ghostty-web Terminal instance...");
const terminal = new Terminal(options);
- console.log("[webterm:create] Terminal created:", terminal);
- console.log("[webterm:create] Terminal.options:", (terminal as unknown as { options?: unknown }).options);
-
- console.log("[webterm:create] Creating FitAddon...");
const fitAddon = new FitAddon();
- console.log("[webterm:create] Loading FitAddon into terminal...");
terminal.loadAddon(fitAddon);
// Open terminal (initializes rendering - WASM already loaded)
- console.log("[webterm:create] Calling terminal.open(container)...");
terminal.open(container);
- console.log("[webterm:create] terminal.open() completed");
-
- // Check internal state after open
- const internalTerminal = terminal as unknown as Record;
- console.log("[webterm:create] Terminal internal keys:", Object.keys(internalTerminal));
- if (internalTerminal.renderer) {
- console.log("[webterm:create] Renderer exists:", internalTerminal.renderer);
- const renderer = internalTerminal.renderer as Record;
- console.log("[webterm:create] Renderer keys:", Object.keys(renderer));
- if (renderer.theme) {
- console.log("[webterm:create] Renderer.theme:", renderer.theme);
- }
- if (renderer.palette) {
- console.log("[webterm:create] Renderer.palette:", renderer.palette);
- }
- }
const instance = new WebTerminal(
container,
@@ -793,73 +743,24 @@ class WebTerminal {
fontFamily,
fontSize
);
- console.log("[webterm:create] WebTerminal instance created");
instance.initialize();
- console.log("[webterm:create] WebTerminal initialized");
return instance;
}
/** Initialize event handlers and connect */
private initialize(): void {
- console.log("[webterm:init] initialize() called");
-
- // Check canvas state immediately
- const canvas = this.element.querySelector("canvas");
- console.log("[webterm:init] Canvas element:", canvas);
- if (canvas) {
- console.log("[webterm:init] Canvas dimensions:", {
- width: canvas.width,
- height: canvas.height,
- clientWidth: canvas.clientWidth,
- clientHeight: canvas.clientHeight,
- style: canvas.style.cssText
- });
- }
- console.log("[webterm:init] Container dimensions:", {
- clientWidth: this.element.clientWidth,
- clientHeight: this.element.clientHeight
- });
-
// Wait for fonts to load before fitting to ensure correct measurements
//
// FONT INITIALIZATION (ghostty-web):
- // -----------------------------------
// The font stack is set in two places:
// 1. At Terminal construction time via ITerminalOptions.fontFamily
- // - This sets the initial font for the renderer
// 2. After web fonts load via terminal.loadFonts()
- // - This re-measures font metrics and triggers a full re-render
- //
- // The loadFonts() method (added in ghostty-web commit feab41f9a8e4491f):
- // - Calls renderer.remeasureFont() to recalculate cell dimensions
- // - Calls handleFontChange() to resize canvas and re-render
- //
- // DO NOT manually set terminal.options.fontFamily or call renderer methods
- // directly - use the public loadFonts() API which handles the full chain.
- //
- // See: https://github.com/rcarmo/ghostty-web/commit/feab41f9a8e4491f04688a6620974c3f7762a3d9
+ // - Re-measures font metrics and triggers a full re-render
this.waitForFonts().then(() => {
- console.log("[webterm:init] Fonts loaded, triggering font reload...");
- // Use the public loadFonts() API which properly handles font re-measurement
- // and triggers handleFontChange() internally. This is the correct approach
- // per ghostty-web commit feab41f9a8e4491f04688a6620974c3f7762a3d9
if (typeof (this.terminal as unknown as { loadFonts?: () => void }).loadFonts === "function") {
(this.terminal as unknown as { loadFonts: () => void }).loadFonts();
- console.log("[webterm:init] terminal.loadFonts() called");
}
this.fit();
- console.log("[webterm:init] fit() completed");
-
- // Check canvas state after fit
- const canvasAfterFit = this.element.querySelector("canvas");
- if (canvasAfterFit) {
- console.log("[webterm:init] Canvas after fit:", {
- width: canvasAfterFit.width,
- height: canvasAfterFit.height,
- clientWidth: canvasAfterFit.clientWidth,
- clientHeight: canvasAfterFit.clientHeight
- });
- }
});
// Setup resize observer (we use our own fit method, not FitAddon's)
@@ -1752,7 +1653,7 @@ class WebTerminal {
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
setTimeout(() => {
- console.log(`Reconnecting (attempt ${this.reconnectAttempts})...`);
+ console.log(`[webterm] Reconnecting (attempt ${this.reconnectAttempts})...`);
this.connect();
}, delay);
}
@@ -1796,11 +1697,9 @@ class WebTerminal {
/** Set terminal theme dynamically (accesses private renderer) */
setTheme(theme: ITheme): void {
- // ghostty-web Terminal doesn't expose setTheme, but the internal renderer has it
- const renderer = (this.terminal as unknown as { renderer?: { setTheme: (t: ITheme) => void } }).renderer;
- if (renderer && typeof renderer.setTheme === "function") {
- renderer.setTheme(theme);
- }
+ // Use the Terminal's options proxy so handleOptionChange fires,
+ // which updates the renderer theme AND triggers a re-render.
+ (this.terminal as unknown as { options: { theme: ITheme } }).options.theme = theme;
}
/** Get a named theme from the built-in themes */
@@ -1825,30 +1724,21 @@ setInterval(() => {
/** Initialize all terminal containers on page load */
async function initTerminals(): Promise {
- console.log("[webterm:init] initTerminals() called");
const containers = document.querySelectorAll(".webterm-terminal");
- console.log(`[webterm:init] Found ${containers.length} .webterm-terminal containers`);
for (const el of containers) {
- console.log("[webterm:init] Processing container:", el);
- console.log("[webterm:init] Dataset:", JSON.stringify(el.dataset));
-
const wsUrl = el.dataset.sessionWebsocketUrl;
if (!wsUrl) {
- console.error("Missing data-session-websocket-url on terminal container");
+ console.error("[webterm] Missing data-session-websocket-url on terminal container");
continue;
}
const config = parseConfig(el);
- console.log("[webterm:init] Parsed config:", JSON.stringify(config, null, 2));
-
try {
- console.log("[webterm:init] Calling WebTerminal.create()...");
const terminal = await WebTerminal.create(el, wsUrl, config);
- console.log("[webterm:init] WebTerminal created successfully");
instances.set(el, terminal);
} catch (e) {
- console.error("Failed to create terminal:", e);
+ console.error("[webterm] Failed to create terminal:", e);
}
}
}