v0.13.5 web ui updates
test / test (push) Canceled after 0s

This commit is contained in:
lixen committed 2026-10-15 22:02:30 -04:00
1 parent caf30df968
commit 87dcb3e550
13 files changed
+333 -156

No files matched your search

+6 -2
View File
@@ -79,18 +79,22 @@ wasm:
@echo "Built WASM client: $(WASM_BINARY)"
@echo "Size: $$(du -h $(WASM_BINARY) | cut -f1)"
# Download xterm.js and all addons
# Download xterm.js, its addons and licence. jsDelivr's header comment and the
# trailing sourceMappingURL, to a map no one ships, are stripped: the files
# are then the ones vif's web/vendor/ and lixen.com serve.
.PHONY: wasm-deps
wasm-deps:
@echo "Downloading xterm.js $(XTERM_VERSION) and addons..."
@mkdir -p $(WASM_LIB_DIR)
@cd $(WASM_LIB_DIR) && \
$(DOWNLOAD) https://cdn.jsdelivr.net/npm/@xterm/xterm@$(XTERM_VERSION)/LICENSE && \
$(DOWNLOAD) https://cdn.jsdelivr.net/npm/@xterm/xterm@$(XTERM_VERSION)/lib/xterm.min.js && \
$(DOWNLOAD) https://cdn.jsdelivr.net/npm/@xterm/xterm@$(XTERM_VERSION)/css/xterm.min.css && \
$(DOWNLOAD) https://cdn.jsdelivr.net/npm/@xterm/addon-fit@$(XTERM_FIT_VERSION)/lib/addon-fit.min.js && \
$(DOWNLOAD) https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@$(XTERM_WEBGL_VERSION)/lib/addon-webgl.min.js && \
$(DOWNLOAD) https://cdn.jsdelivr.net/npm/@xterm/addon-web-links@$(XTERM_LINKS_VERSION)/lib/addon-web-links.min.js && \
$(DOWNLOAD) https://cdn.jsdelivr.net/npm/@xterm/addon-unicode11@$(XTERM_UNICODE_VERSION)/lib/addon-unicode11.min.js
$(DOWNLOAD) https://cdn.jsdelivr.net/npm/@xterm/addon-unicode11@$(XTERM_UNICODE_VERSION)/lib/addon-unicode11.min.js && \
for f in *.min.js *.min.css; do sed -e '1,/^ \*\/$$/d' -e '$$d' "$$f" > "$$f.tmp" && mv "$$f.tmp" "$$f"; done
@echo "Downloaded to $(WASM_LIB_DIR)/"
@ls -la $(WASM_LIB_DIR)/
+6 -3
View File
@@ -212,9 +212,12 @@ server's API paths:
API base from its script element, `<script src="app.js" data-api="…">`,
which the embedded server sets from `-web-api-url`; without one, as copied,
it uses the `/chess` API prefix.
- **WASM terminal client (if published):** `make wasm` (xterm.js is committed
under `lib/`), then copy `web/chess-client-wasm/`. It derives its API base
as `<origin>/chess`. Its API paths are compiled into `chess-client.wasm`,
- **WASM terminal client (if published):** `make wasm` (xterm.js and its
licence are committed under `lib/`), then copy `web/chess-client-wasm/`.
`terminal.js` runs the client in the page's `#chess-terminal`, whose
`data-wasm-url` names the binary; a site with a page of its own loads it
into the same markup (`index.html`). The client derives its API base as
`<origin>/chess`. Its API paths are compiled into `chess-client.wasm`,
so rebuild it whenever `internal/client/api` changes; publish it together
with the `wasm_exec.js` that `make wasm` copies from the same Go toolchain.
@@ -46,7 +46,7 @@
</div>
<div class="controls">
<button id="new-game-btn" class="btn btn-primary">New Game</button>
<button id="new-game-btn" class="btn btn-primary">New</button>
<button id="undo-btn" class="btn btn-secondary" disabled>Undo</button>
<button id="draw-btn" class="btn btn-secondary" title="Offer a draw" disabled>Draw</button>
<button id="resign-btn" class="btn btn-secondary" title="Resign this game" disabled>Resign</button>
+7 -4
View File
@@ -8,13 +8,16 @@
<link rel="stylesheet" href="terminal.css">
</head>
<body>
<div id="terminal"></div>
<div class="chess-terminal" id="chess-terminal" data-wasm-url="chess-client.wasm">
<div class="chess-terminal-screen" id="chess-terminal-screen"></div>
<p class="chess-terminal-status" id="chess-terminal-status" role="status">
<span id="chess-terminal-state">Loading the client…</span><span class="chess-terminal-progress" id="chess-terminal-progress" aria-hidden="true"></span>
</p>
</div>
<script src="lib/xterm.min.js"></script>
<script src="lib/addon-fit.min.js"></script>
<script src="lib/addon-webgl.min.js"></script>
<script src="lib/addon-web-links.min.js"></script>
<script src="lib/addon-unicode11.min.js"></script>
<script src="wasm_exec.js"></script>
<script src="terminal.js"></script>
</body>
</html>>
</html>
+21
View File
@@ -0,0 +1,21 @@
Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-7
View File
@@ -1,8 +1 @@
/**
* Skipped minification because the original files appears to be already minified.
* Original file: /npm/@xterm/addon-fit@0.10.0/lib/addon-fit.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})()));
//# sourceMappingURL=addon-fit.js.map
-7
View File
@@ -1,8 +1 @@
/**
* Skipped minification because the original files appears to be already minified.
* Original file: /npm/@xterm/addon-unicode11@0.8.0/lib/addon-unicode11.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Unicode11Addon=t():e.Unicode11Addon=t()}(this,(()=>(()=>{"use strict";var e={433:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeV11=void 0;const r=i(938),s=[[768,879],[1155,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1541],[1552,1562],[1564,1564],[1611,1631],[1648,1648],[1750,1757],[1759,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2045,2045],[2070,2073],[2075,2083],[2085,2087],[2089,2093],[2137,2139],[2259,2306],[2362,2362],[2364,2364],[2369,2376],[2381,2381],[2385,2391],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2558,2558],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2641,2641],[2672,2673],[2677,2677],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2810,2815],[2817,2817],[2876,2876],[2879,2879],[2881,2884],[2893,2893],[2902,2902],[2914,2915],[2946,2946],[3008,3008],[3021,3021],[3072,3072],[3076,3076],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3170,3171],[3201,3201],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3328,3329],[3387,3388],[3393,3396],[3405,3405],[3426,3427],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3981,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4151],[4153,4154],[4157,4158],[4184,4185],[4190,4192],[4209,4212],[4226,4226],[4229,4230],[4237,4237],[4253,4253],[4448,4607],[4957,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6158],[6277,6278],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6683,6683],[6742,6742],[6744,6750],[6752,6752],[6754,6754],[6757,6764],[6771,6780],[6783,6783],[6832,6846],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7040,7041],[7074,7077],[7080,7081],[7083,7085],[7142,7142],[7144,7145],[7149,7149],[7151,7153],[7212,7219],[7222,7223],[7376,7378],[7380,7392],[7394,7400],[7405,7405],[7412,7412],[7416,7417],[7616,7673],[7675,7679],[8203,8207],[8234,8238],[8288,8292],[8294,8303],[8400,8432],[11503,11505],[11647,11647],[11744,11775],[12330,12333],[12441,12442],[42607,42610],[42612,42621],[42654,42655],[42736,42737],[43010,43010],[43014,43014],[43019,43019],[43045,43046],[43204,43205],[43232,43249],[43263,43263],[43302,43309],[43335,43345],[43392,43394],[43443,43443],[43446,43449],[43452,43453],[43493,43493],[43561,43566],[43569,43570],[43573,43574],[43587,43587],[43596,43596],[43644,43644],[43696,43696],[43698,43700],[43703,43704],[43710,43711],[43713,43713],[43756,43757],[43766,43766],[44005,44005],[44008,44008],[44013,44013],[64286,64286],[65024,65039],[65056,65071],[65279,65279],[65529,65531]],n=[[66045,66045],[66272,66272],[66422,66426],[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[68325,68326],[68900,68903],[69446,69456],[69633,69633],[69688,69702],[69759,69761],[69811,69814],[69817,69818],[69821,69821],[69837,69837],[69888,69890],[69927,69931],[69933,69940],[70003,70003],[70016,70017],[70070,70078],[70089,70092],[70191,70193],[70196,70196],[70198,70199],[70206,70206],[70367,70367],[70371,70378],[70400,70401],[70459,70460],[70464,70464],[70502,70508],[70512,70516],[70712,70719],[70722,70724],[70726,70726],[70750,70750],[70835,70840],[70842,70842],[70847,70848],[70850,70851],[71090,71093],[71100,71101],[71103,71104],[71132,71133],[71219,71226],[71229,71229],[71231,71232],[71339,71339],[71341,71341],[71344,71349],[71351,71351],[71453,71455],[71458,71461],[71463,71467],[71727,71735],[71737,71738],[72148,72151],[72154,72155],[72160,72160],[72193,72202],[72243,72248],[72251,72254],[72263,72263],[72273,72278],[72281,72283],[72330,72342],[72344,72345],[72752,72758],[72760,72765],[72767,72767],[72850,72871],[72874,72880],[72882,72883],[72885,72886],[73009,73014],[73018,73018],[73020,73021],[73023,73029],[73031,73031],[73104,73105],[73109,73109],[73111,73111],[73459,73460],[78896,78904],[92912,92916],[92976,92982],[94031,94031],[94095,94098],[113821,113822],[113824,113827],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[121344,121398],[121403,121452],[121461,121461],[121476,121476],[121499,121503],[121505,121519],[122880,122886],[122888,122904],[122907,122913],[122915,122916],[122918,122922],[123184,123190],[123628,123631],[125136,125142],[125252,125258],[917505,917505],[917536,917631],[917760,917999]],o=[[4352,4447],[8986,8987],[9001,9002],[9193,9196],[9200,9200],[9203,9203],[9725,9726],[9748,9749],[9800,9811],[9855,9855],[9875,9875],[9889,9889],[9898,9899],[9917,9918],[9924,9925],[9934,9934],[9940,9940],[9962,9962],[9970,9971],[9973,9973],[9978,9978],[9981,9981],[9989,9989],[9994,9995],[10024,10024],[Line truncated
//# sourceMappingURL=addon-unicode11.js.map
-7
View File
@@ -1,8 +1 @@
/**
* Skipped minification because the original files appears to be already minified.
* Original file: /npm/@xterm/addon-web-links@0.11.0/lib/addon-web-links.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.WebLinksAddon=t():e.WebLinksAddon=t()}(self,(()=>(()=>{"use strict";var e={6:(e,t)=>{function n(e){try{const t=new URL(e),n=t.password&&t.username?`${t.protocol}//${t.username}:${t.password}@${t.host}`:t.username?`${t.protocol}//${t.username}@${t.host}`:`${t.protocol}//${t.host}`;return e.toLocaleLowerCase().startsWith(n.toLocaleLowerCase())}catch(e){return!1}}Object.defineProperty(t,"__esModule",{value:!0}),t.LinkComputer=t.WebLinkProvider=void 0,t.WebLinkProvider=class{constructor(e,t,n,o={}){this._terminal=e,this._regex=t,this._handler=n,this._options=o}provideLinks(e,t){const n=o.computeLink(e,this._regex,this._terminal,this._handler);t(this._addCallbacks(n))}_addCallbacks(e){return e.map((e=>(e.leave=this._options.leave,e.hover=(t,n)=>{if(this._options.hover){const{range:o}=e;this._options.hover(t,n,o)}},e)))}};class o{static computeLink(e,t,r,i){const s=new RegExp(t.source,(t.flags||"")+"g"),[a,c]=o._getWindowedLineStrings(e-1,r),l=a.join("");let d;const p=[];for(;d=s.exec(l);){const e=d[0];if(!n(e))continue;const[t,s]=o._mapStrIdx(r,c,0,d.index),[a,l]=o._mapStrIdx(r,t,s,e.length);if(-1===t||-1===s||-1===a||-1===l)continue;const h={start:{x:s+1,y:t+1},end:{x:l,y:a+1}};p.push({range:h,text:e,activate:i})}return p}static _getWindowedLineStrings(e,t){let n,o=e,r=e,i=0,s="";const a=[];if(n=t.buffer.active.getLine(e)){const e=n.translateToString(!0);if(n.isWrapped&&" "!==e[0]){for(i=0;(n=t.buffer.active.getLine(--o))&&i<2048&&(s=n.translateToString(!0),i+=s.length,a.push(s),n.isWrapped&&-1===s.indexOf(" ")););a.reverse()}for(a.push(e),i=0;(n=t.buffer.active.getLine(++r))&&n.isWrapped&&i<2048&&(s=n.translateToString(!0),i+=s.length,a.push(s),-1===s.indexOf(" ")););}return[a,o]}static _mapStrIdx(e,t,n,o){const r=e.buffer.active,i=r.getNullCell();let s=n;for(;o;){const e=r.getLine(t);if(!e)return[-1,-1];for(let n=s;n<e.length;++n){e.getCell(n,i);const s=i.getChars();if(i.getWidth()&&(o-=s.length||1,n===e.length-1&&""===s)){const e=r.getLine(t+1);e&&e.isWrapped&&(e.getCell(0,i),2===i.getWidth()&&(o+=1))}if(o<0)return[t,n]}t++,s=0}return[t,s]}}t.LinkComputer=o}},t={};function n(o){var r=t[o];if(void 0!==r)return r.exports;var i=t[o]={exports:{}};return e[o](i,i.exports,n),i.exports}var o={};return(()=>{var e=o;Object.defineProperty(e,"__esModule",{value:!0}),e.WebLinksAddon=void 0;const t=n(6),r=/(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/;function i(e,t){const n=window.open();if(n){try{n.opener=null}catch{}n.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}e.WebLinksAddon=class{constructor(e=i,t={}){this._handler=e,this._options=t}activate(e){this._terminal=e;const n=this._options,o=n.urlRegex||r;this._linkProvider=this._terminal.registerLinkProvider(new t.WebLinkProvider(this._terminal,o,this._handler,n))}dispose(){this._linkProvider?.dispose()}}})(),o})()));
//# sourceMappingURL=addon-web-links.js.map
-7
View File
@@ -1,8 +1 @@
/**
* Skipped minification because the original files appears to be already minified.
* Original file: /npm/@xterm/addon-webgl@0.18.0/lib/addon-webgl.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.WebglAddon=t():e.WebglAddon=t()}(self,(()=>(()=>{"use strict";var e={965:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.GlyphRenderer=void 0;const s=i(374),r=i(509),o=i(855),n=i(859),a=i(381),h=11,l=h*Float32Array.BYTES_PER_ELEMENT;let c,d=0,_=0,u=0;class g extends n.Disposable{constructor(e,t,i,o){super(),this._terminal=e,this._gl=t,this._dimensions=i,this._optionsService=o,this._activeBuffer=0,this._vertices={count:0,attributes:new Float32Array(0),attributesBuffers:[new Float32Array(0),new Float32Array(0)]};const h=this._gl;void 0===r.TextureAtlas.maxAtlasPages&&(r.TextureAtlas.maxAtlasPages=Math.min(32,(0,s.throwIfFalsy)(h.getParameter(h.MAX_TEXTURE_IMAGE_UNITS))),r.TextureAtlas.maxTextureSize=(0,s.throwIfFalsy)(h.getParameter(h.MAX_TEXTURE_SIZE))),this._program=(0,s.throwIfFalsy)((0,a.createProgram)(h,"#version 300 es\nlayout (location = 0) in vec2 a_unitquad;\nlayout (location = 1) in vec2 a_cellpos;\nlayout (location = 2) in vec2 a_offset;\nlayout (location = 3) in vec2 a_size;\nlayout (location = 4) in float a_texpage;\nlayout (location = 5) in vec2 a_texcoord;\nlayout (location = 6) in vec2 a_texsize;\n\nuniform mat4 u_projection;\nuniform vec2 u_resolution;\n\nout vec2 v_texcoord;\nflat out int v_texpage;\n\nvoid main() {\n vec2 zeroToOne = (a_offset / u_resolution) + a_cellpos + (a_unitquad * a_size);\n gl_Position = u_projection * vec4(zeroToOne, 0.0, 1.0);\n v_texpage = int(a_texpage);\n v_texcoord = a_texcoord + a_unitquad * a_texsize;\n}",function(e){let t="";for(let i=1;i<e;i++)t+=` else if (v_texpage == ${i}) { outColor = texture(u_texture[${i}], v_texcoord); }`;return`#version 300 es\nprecision lowp float;\n\nin vec2 v_texcoord;\nflat in int v_texpage;\n\nuniform sampler2D u_texture[${e}];\n\nout vec4 outColor;\n\nvoid main() {\n if (v_texpage == 0) {\n outColor = texture(u_texture[0], v_texcoord);\n } ${t}\n}`}(r.TextureAtlas.maxAtlasPages))),this.register((0,n.toDisposable)((()=>h.deleteProgram(this._program)))),this._projectionLocation=(0,s.throwIfFalsy)(h.getUniformLocation(this._program,"u_projection")),this._resolutionLocation=(0,s.throwIfFalsy)(h.getUniformLocation(this._program,"u_resolution")),this._textureLocation=(0,s.throwIfFalsy)(h.getUniformLocation(this._program,"u_texture")),this._vertexArrayObject=h.createVertexArray(),h.bindVertexArray(this._vertexArrayObject);const c=new Float32Array([0,0,1,0,0,1,1,1]),d=h.createBuffer();this.register((0,n.toDisposable)((()=>h.deleteBuffer(d)))),h.bindBuffer(h.ARRAY_BUFFER,d),h.bufferData(h.ARRAY_BUFFER,c,h.STATIC_DRAW),h.enableVertexAttribArray(0),h.vertexAttribPointer(0,2,this._gl.FLOAT,!1,0,0);const _=new Uint8Array([0,1,2,3]),u=h.createBuffer();this.register((0,n.toDisposable)((()=>h.deleteBuffer(u)))),h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,u),h.bufferData(h.ELEMENT_ARRAY_BUFFER,_,h.STATIC_DRAW),this._attributesBuffer=(0,s.throwIfFalsy)(h.createBuffer()),this.register((0,n.toDisposable)((()=>h.deleteBuffer(this._attributesBuffer)))),h.bindBuffer(h.ARRAY_BUFFER,this._attributesBuffer),h.enableVertexAttribArray(2),h.vertexAttribPointer(2,2,h.FLOAT,!1,l,0),h.vertexAttribDivisor(2,1),h.enableVertexAttribArray(3),h.vertexAttribPointer(3,2,h.FLOAT,!1,l,2*Float32Array.BYTES_PER_ELEMENT),h.vertexAttribDivisor(3,1),h.enableVertexAttribArray(4),h.vertexAttribPointer(4,1,h.FLOAT,!1,l,4*Float32Array.BYTES_PER_ELEMENT),h.vertexAttribDivisor(4,1),h.enableVertexAttribArray(5),h.vertexAttribPointer(5,2,h.FLOAT,!1,l,5*Float32Array.BYTES_PER_ELEMENT),h.vertexAttribDivisor(5,1),h.enableVertexAttribArray(6),h.vertexAttribPointer(6,2,h.FLOAT,!1,l,7*Float32Array.BYTES_PER_ELEMENT),h.vertexAttribDivisor(6,1),h.enableVertexAttribArray(1),h.vertexAttribPointer(1,2,h.FLOAT,!1,l,9*Float32Array.BYTES_PER_ELEMENT),h.vertexAttribDivisor(1,1),h.useProgram(this._program);const g=new Int32Array(r.TextureAtlas.maxAtlasPages);for(let e=0;e<r.TextureAtlas.maxAtlasPages;e++)g[e]=e;h.uniform1iv(this._textureLocation,g),h.uniformMatrix4fv(this._projectionLocation,!1,a.PROJECTION_MATRIX),this._atlasTextures=[];for(let e=0;e<r.TextureAtlas.maxAtlasPages;e++){const t=new a.GLTexture((0,s.throwIfFalsy)(h.createTexture()));this.register((0,n.toDisposable)((()=>h.deleteTexture(t.texture)))),h.activeTexture(h.TEXTURE0+e),h.bindTexture(h.TEXTURE_2D,t.texture),h.texParameteri(h.TEXTURE_2D,h.TEXTURE_WRAP_S,h.CLAMP_TO_EDGE),h.texParameteri(h.TEXTURE_2D,h.TEXTURE_WRAP_T,h.CLAMP_TO_EDGE),h.texImage2D(h.TEXTURE_2D,0,h.RGBA,1,1,0,h.RGBA,h.UNSIGNED_BYTE,new Uint8Array([255,0,0,255])),this._atlasTextures[e]=t}h.enable(h.BLEND),h.blendFunc(h.SRC_ALPHA,h.ONE_MINUS_SRC_ALPHA),this.handleResize()}beginFrame(){return!this._atlas||this._atlas.beginFrame()}updateCell(e,t,i,s,r,o,n,a,h){this._updateCell(this._vertices.attributes,e,t,i,s,r,o,n,a,h)}_updateCell(e,t,i,r,n,a,l,g,v,f){d=(i*this._terminal.cols+t)*h,r!Line truncated
//# sourceMappingURL=addon-webgl.js.map
-7
View File
@@ -1,8 +1 @@
/**
* Minified by jsDelivr using clean-css v5.3.3.
* Original file: /npm/@xterm/xterm@5.5.0/css/xterm.css
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:0}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm .xterm-cursor-pointer,.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}
/*# sourceMappingURL=/sm/97377c0c258e109358121823f5790146c714989366481f90e554c42277efb500.map */
-7
View File
@@ -1,8 +1 @@
/**
* Skipped minification because the original files appears to be already minified.
* Original file: /npm/@xterm/xterm@5.5.0/lib/xterm.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i=t();for(var s in i)("object"==typeof exports?exports:e)[s]=i[s]}}(globalThis,(()=>(()=>{"use strict";var e={4567:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.AccessibilityManager=void 0;const n=i(9042),o=i(9924),a=i(844),h=i(4725),c=i(2585),l=i(3656);let d=t.AccessibilityManager=class extends a.Disposable{constructor(e,t,i,s){super(),this._terminal=e,this._coreBrowserService=i,this._renderService=s,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let e=0;e<this._terminal.rows;e++)this._rowElements[e]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[e]);if(this._topBoundaryFocusListener=e=>this._handleBoundaryFocus(e,0),this._bottomBoundaryFocusListener=e=>this._handleBoundaryFocus(e,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new o.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((e=>this._handleResize(e.rows)))),this.register(this._terminal.onRender((e=>this._refreshRows(e.start,e.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((e=>this._handleChar(e)))),this.register(this._terminal.onLineFeed((()=>this._handleChar("\n")))),this.register(this._terminal.onA11yTab((e=>this._handleTab(e)))),this.register(this._terminal.onKey((e=>this._handleKey(e.key)))),this.register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this.register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this.register((0,l.addDisposableDomListener)(document,"selectionchange",(()=>this._handleSelectionChange()))),this.register(this._coreBrowserService.onDprChange((()=>this._refreshRowsDimensions()))),this._refreshRows(),this.register((0,a.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let t=0;t<e;t++)this._handleChar(" ")}_handleChar(e){this._liveRegionLineCount<21&&(this._charsToConsume.length>0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,"\n"===e&&(this._liveRegionLineCount++,21===this._liveRegionLineCount&&(this._liveRegion.textContent+=n.tooMuchOutput)))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0}_handleKey(e){this._clearLiveRegion(),/\p{Control}/u.test(e)||this._charsToConsume.push(e)}_refreshRows(e,t){this._liveRegionDebouncer.refresh(e,t,this._terminal.rows)}_renderRows(e,t){const i=this._terminal.buffer,s=i.lines.length.toString();for(let r=e;r<=t;r++){const e=i.lines.get(i.ydisp+r),t=[],n=e?.translateToString(!0,void 0,void 0,t)||"",o=(i.ydisp+r+1).toString(),a=this._rowElements[r];a&&(0===n.length?(a.innerText=" ",this._rowColumns.set(a,[0,1])):(a.textContent=n,this._rowColumns.set(a,t)),a.setAttribute("aria-posinset",o),a.setAttribute("aria-setsize",s))}this._announceCharacters()}_announceCharacters(){0!==this._charsToAnnounce.length&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,t){const i=e.target,s=this._rowElements[0===t?1:this._rowElements.length-2];if(i.getAttribute("aria-posinset")===(0===t?"1":`${this._terminal.buffer.lines.length}`))return;if(e.relatedTarget!==s)return;let r,n;if(0===t?(r=i,n=this._rowElements.pop(),this._rowContainer.removeChild(n)):(r=this._rowElements.shiftLine truncated
//# sourceMappingURL=xterm.js.map
+45 -8
View File
@@ -1,6 +1,5 @@
:root {
--host-bg: #11111b;
--host-surface: #1a1b26;
}
* {
@@ -16,11 +15,49 @@ html, body {
background: var(--host-bg);
}
#terminal {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
/* terminal.js reads the --term-* properties for xterm's theme. FitAddon sizes
the grid from #chess-terminal-screen, so the box needs a definite height. */
.chess-terminal {
--term-bg: #1a1b26;
--term-fg: #a9b1d6;
--term-cursor: #a9b1d6;
--term-selection: rgba(169, 177, 214, 0.35);
display: grid;
height: 100%;
background: var(--term-bg);
}
/* The status line shares the screen's grid cell. */
.chess-terminal > * {
grid-area: 1 / 1;
min-width: 0;
min-height: 0;
}
.chess-terminal-status {
/* Above xterm's positioned layers, which would otherwise paint over it. */
position: relative;
z-index: 1;
place-self: center;
margin: 0;
padding: 0 1.5rem;
text-align: center;
color: var(--term-fg);
font-family: sans-serif;
pointer-events: none;
}
.chess-terminal-status.is-error {
color: #f7768e;
pointer-events: auto;
}
.chess-terminal-progress {
font-variant-numeric: tabular-nums;
}
.chess-terminal-progress:not(:empty)::before {
content: " ";
}
+247 -96
View File
@@ -1,130 +1,281 @@
// Runs the WASM client in #chess-terminal: its data-wasm-url names the binary,
// #chess-terminal-screen holds xterm, #chess-terminal-status (-state and
// -progress spans) reports loading. xterm with its fit and WebGL addons and
// wasm_exec.js load first. lixen.com wraps this same file in its own page.
(() => {
'use strict';
const host = document.getElementById('chess-terminal');
if (!host) return;
const screenEl = document.getElementById('chess-terminal-screen');
const status = document.getElementById('chess-terminal-status');
const stateEl = document.getElementById('chess-terminal-state');
const progress = document.getElementById('chess-terminal-progress');
const wasmURL = host.dataset.wasmUrl;
const setState = (text, isError) => {
stateEl.textContent = text;
progress.textContent = '';
status.hidden = !text && !isError;
status.classList.toggle('is-error', !!isError);
};
// A page script that should have removed .no-js may have been blocked; its
// no-JS message would then cover a working terminal.
document.documentElement.classList.remove('no-js');
if (typeof Terminal === 'undefined' || typeof FitAddon === 'undefined') {
setState('Terminal library failed to load. Reload the page to retry.', true);
return;
}
if (typeof Go === 'undefined' || !globalThis.fs) {
setState('WebAssembly support files failed to load. Reload the page to retry.', true);
return;
}
// The theme's colours are the host's CSS custom properties.
const css = getComputedStyle(host);
const token = (name, fallback) => css.getPropertyValue(name).trim() || fallback;
const term = new Terminal({
cursorBlink: true,
convertEol: true,
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
scrollback: 1000,
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace',
fontSize: 14,
allowProposedApi: true,
theme: {
background: '#1a1b26',
foreground: '#a9b1d6',
cursor: '#a9b1d6',
selection: 'rgba(169, 177, 214, 0.3)'
background: token('--term-bg', '#1a1b26'),
foreground: token('--term-fg', '#a9b1d6'),
cursor: token('--term-cursor', '#a9b1d6'),
// xterm 5 silently ignores the old `selection` key.
selectionBackground: token('--term-selection', 'rgba(169,177,214,0.35)')
}
});
// Load addons
const fitAddon = new FitAddon.FitAddon();
const webglAddon = new WebglAddon.WebglAddon();
const webLinksAddon = new WebLinksAddon.WebLinksAddon();
const unicode11Addon = new Unicode11Addon.Unicode11Addon();
term.loadAddon(fitAddon);
term.loadAddon(webLinksAddon);
term.loadAddon(unicode11Addon);
term.unicode.activeVersion = '11';
term.open(document.getElementById('terminal'));
term.open(screenEl);
// WebGL addon must load after open()
// WebGL must load after open(); the DOM renderer is the fallback.
try {
term.loadAddon(webglAddon);
webglAddon.onContextLoss(() => {
webglAddon.dispose();
});
const webgl = new WebglAddon.WebglAddon();
webgl.onContextLoss(() => webgl.dispose());
term.loadAddon(webgl);
} catch (e) {
console.warn('WebGL addon failed, using canvas renderer:', e);
console.warn('xterm: WebGL renderer unavailable, using the DOM renderer', e);
}
fitAddon.fit();
term.focus();
let inputBuffer = '';
let inputResolver = null;
term.onData(data => {
if (inputResolver) {
if (data === '\r') {
term.write('\r\n');
const result = inputBuffer;
inputBuffer = '';
const resolver = inputResolver;
inputResolver = null;
resolver(result);
} else if (data === '\x7f' || data === '\x08') {
if (inputBuffer.length > 0) {
inputBuffer = inputBuffer.slice(0, -1);
term.write('\b \b');
}
} else if (data === '\x03') {
term.write('^C\r\n');
inputBuffer = '';
if (inputResolver) {
inputResolver('');
inputResolver = null;
}
} else if (data >= ' ' && data <= '~') {
inputBuffer += data;
term.write(data);
}
// xterm captures Tab, so Escape, which the line editor ignores, is the
// keyboard way out of the terminal (WCAG 2.1.2).
term.attachCustomKeyEventHandler((e) => {
if (e.type === 'keydown' && e.key === 'Escape') {
term.blur();
return false;
}
return true;
});
// fit() floors rows against the cell height it knew at call time, which a
// zoom step can leave stale: drop a row while .xterm-screen (rows x cell
// height) paints taller than the box.
const availHeight = () => {
const cs = getComputedStyle(screenEl);
return screenEl.clientHeight
- parseFloat(cs.paddingTop) - parseFloat(cs.paddingBottom);
};
const snapRows = () => {
const scr = term.element && term.element.querySelector('.xterm-screen');
if (!scr || !term.rows) return;
const cell = scr.getBoundingClientRect().height / term.rows;
if (!cell) return;
const max = Math.max(1, Math.floor(availHeight() / cell));
if (term.rows > max) term.resize(term.cols, max);
};
const fitNow = (passes) => {
fitAddon.fit();
snapRows();
if (passes > 1) requestAnimationFrame(() => fitNow(passes - 1));
};
let fitTimer = 0;
const scheduleFit = () => {
clearTimeout(fitTimer);
fitTimer = setTimeout(() => fitNow(2), 50);
};
// A resolution query matches one ratio only, so re-arm on every change.
let dprQuery = null;
const onDprChange = () => { watchDpr(); scheduleFit(); };
const watchDpr = () => {
if (dprQuery) dprQuery.removeEventListener('change', onDprChange);
dprQuery = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
dprQuery.addEventListener('change', onDprChange);
};
fitNow(2);
new ResizeObserver(scheduleFit).observe(screenEl);
window.addEventListener('resize', scheduleFit);
watchDpr();
if (document.fonts && document.fonts.ready) document.fonts.ready.then(scheduleFit);
screenEl.addEventListener('click', () => term.focus());
// Go's js/wasm routes os.Stdout and os.Stderr through fs.write, runtime
// prints (panics) through fs.writeSync and os.Stdin through fs.read: fds
// 0-2 go to xterm, the rest to wasm_exec.js's own implementations.
const fs = globalThis.fs;
const nativeWrite = fs.write;
const nativeWriteSync = fs.writeSync;
const nativeRead = fs.read;
const encoder = new TextEncoder();
const decoder = new TextDecoder();
// One streaming decoder per fd: a UTF-8 sequence split across two writes
// must not lend its tail to the other stream.
const decoders = { 1: new TextDecoder('utf-8'), 2: new TextDecoder('utf-8') };
const toTerminal = (fd, bytes) => term.write(decoders[fd].decode(bytes, { stream: true }));
if (!globalThis.fs) {
globalThis.fs = {};
}
const originalWrite = globalThis.fs.write;
const originalRead = globalThis.fs.read;
globalThis.fs.write = function(fd, buf, offset, length, position, callback) {
fs.writeSync = function (fd, buf) {
if (fd === 1 || fd === 2) {
const text = decoder.decode(buf.slice(offset, offset + length));
term.write(text);
callback(null, length);
} else if (originalWrite) {
originalWrite.call(this, fd, buf, offset, length, position, callback);
} else {
callback(new Error('Invalid fd'));
toTerminal(fd, buf);
return buf.length;
}
return nativeWriteSync.call(this, fd, buf);
};
globalThis.fs.read = function(fd, buf, offset, length, position, callback) {
if (fd === 0) {
const promise = new Promise(resolve => {
inputResolver = resolve;
fs.write = function (fd, buf, offset, length, position, callback) {
if (fd === 1 || fd === 2) {
toTerminal(fd, buf.subarray(offset, offset + length));
callback(null, length); // synchronous, exactly as wasm_exec.js does
return;
}
nativeWrite.call(this, fd, buf, offset, length, position, callback);
};
// Line editor. Input is taken only while Go blocks in read: keys buffered
// across a read would echo twice. Per character, so a pasted "e2e4\r"
// submits; a multi-line paste submits its first line and drops the rest,
// which Go's next read would echo over a screen the client has repainted.
let line = '';
let pendingRead = null;
let esc = 0; // 0 none, 1 saw ESC, 2 inside CSI/SS3
term.onData((data) => {
if (!pendingRead) return;
for (const ch of data) {
// Escape sequences (an arrow key) type nothing; CSI/SS3 end on 0x40-0x7E.
if (esc === 1) { esc = (ch === '[' || ch === 'O') ? 2 : 0; continue; }
if (esc === 2) { if (ch >= '@' && ch <= '~') esc = 0; continue; }
if (ch === '\x1b') { esc = 1; continue; }
if (ch === '\r') {
term.write('\r\n');
const resolve = pendingRead, value = line;
pendingRead = null; line = ''; esc = 0;
resolve(value);
return;
}
if (ch === '\x03') { // Ctrl-C
term.write('^C\r\n');
const resolve = pendingRead;
pendingRead = null; line = ''; esc = 0;
resolve('');
return;
}
if (ch === '\x7f' || ch === '\b') {
if (line.length > 0) { line = line.slice(0, -1); term.write('\b \b'); }
continue;
}
if (ch >= ' ' && ch <= '~') { line += ch; term.write(ch); }
}
});
promise.then(line => {
const input = encoder.encode(line + '\n');
const n = Math.min(length, input.length);
buf.set(input.slice(0, n), offset);
// The bytes of a submitted line that did not fit the caller's buffer.
let residual = null;
fs.read = function (fd, buf, offset, length, position, callback) {
if (fd !== 0) {
nativeRead.call(this, fd, buf, offset, length, position, callback);
return;
}
const deliver = (bytes) => {
const n = Math.min(length, bytes.length);
buf.set(bytes.subarray(0, n), offset);
residual = n < bytes.length ? bytes.subarray(n) : null;
callback(null, n);
});
} else if (originalRead) {
originalRead.call(this, fd, buf, offset, length, position, callback);
} else {
callback(new Error('Invalid fd'));
};
if (residual) {
// Resolving inside the fs.read call would re-enter the Go runtime
// through _resume() while it is still on the stack.
const pending = residual;
residual = null;
queueMicrotask(() => deliver(pending));
return;
}
new Promise((resolve) => { pendingRead = resolve; })
.then((value) => deliver(encoder.encode(value + '\n')));
};
const MB = 1048576;
// One unit on the pair: "2.1 / 10.3 MB".
const fmtBytes = (received, total) => total
? (received / MB).toFixed(1) + ' / ' + (total / MB).toFixed(1) + ' MB'
: (received / MB).toFixed(1) + ' MB';
(async () => {
const go = new Go();
let instance;
WebAssembly.instantiateStreaming(fetch('chess-client.wasm'), go.importObject)
.then(result => {
go.run(result.instance);
})
.catch(err => {
term.writeln('\r\n\x1b[31mError loading WASM: ' + err + '\x1b[0m');
console.error('WASM load error:', err);
});
try {
const response = await fetch(wasmURL);
if (!response.ok) throw new Error('HTTP ' + response.status);
// Resize handling with debounce for performance
let resizeTimeout;
const resizeObserver = new ResizeObserver(() => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => fitAddon.fit(), 16);
});
resizeObserver.observe(document.getElementById('terminal'));
let bytes;
if (response.body) {
const reader = response.body.getReader();
const chunks = [];
let received = 0;
// The transferred length: compressed in transit, it is smaller
// than the decoded chunks, so it is dropped once they pass it.
let total = Number(response.headers.get('content-length')) || 0;
for (;;) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
received += value.length;
if (total && received > total) total = 0;
progress.textContent = fmtBytes(received, total);
}
bytes = new Uint8Array(received);
let offset = 0;
for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length; }
} else {
bytes = new Uint8Array(await response.arrayBuffer());
}
setState('Compiling the client…');
const result = await WebAssembly.instantiate(bytes, go.importObject);
instance = result.instance;
} catch (err) {
console.error('chess terminal: WASM load failed', err);
setState('Could not load the client (' + err.message + '). Reload to retry.', true);
return;
}
setState('');
// The page is the terminal, opened on purpose; Escape is the way out.
term.focus();
go.run(instance).then(
// Unreached while the client restarts on exit (exit_wasm.go).
() => setState('Session ended. Reload the page to start a new one.'),
(err) => {
console.error('chess terminal: client crashed', err);
setState('Client crashed: ' + err.message, true);
}
);
})();
})();