@dkkoval/tui-preview 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var K=Object.defineProperty;var X=(t,e,n)=>e in t?K(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var W=(t,e,n)=>X(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const M=require("react/jsx-runtime"),b=require("react"),j=["#000000","#cd0000","#00cd00","#cdcd00","#0000ee","#cd00cd","#00cdcd","#e5e5e5","#7f7f7f","#ff0000","#00ff00","#ffff00","#5c5cff","#ff00ff","#00ffff","#ffffff"];function Q(t){if(t<16)return j[t];if(t<232){const n=t-16,o=Math.floor(n/36),a=Math.floor(n%36/6),r=n%6,i=c=>c===0?0:c*40+55;return`rgb(${i(o)},${i(a)},${i(r)})`}const e=(t-232)*10+8;return`rgb(${e},${e},${e})`}function V(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function ee(t){let e=null,n=null,o=!1,a=!1,r=!1;const i=[];let c="",u="";function h(){const s=[];return e&&s.push(`color:${e}`),n&&s.push(`background-color:${n}`),o&&s.push("font-weight:bold"),a&&s.push("font-style:italic"),r&&s.push("text-decoration:underline"),s.join(";")}function m(){c&&(i.push(u?`<span style="${u}">${V(c)}</span>`:V(c)),c="")}function v(s){m();let f=0;for(;f<s.length;){const l=s[f];l===0?(e=null,n=null,o=!1,a=!1,r=!1):l===1?o=!0:l===22?o=!1:l===3?a=!0:l===23?a=!1:l===4?r=!0:l===24?r=!1:l>=30&&l<=37?e=j[l-30]:l===38?s[f+1]===2&&f+4<s.length?(e=`rgb(${s[f+2]},${s[f+3]},${s[f+4]})`,f+=4):s[f+1]===5&&f+2<s.length&&(e=Q(s[f+2]),f+=2):l===39?e=null:l>=40&&l<=47?n=j[l-40]:l===48?s[f+1]===2&&f+4<s.length?(n=`rgb(${s[f+2]},${s[f+3]},${s[f+4]})`,f+=4):s[f+1]===5&&f+2<s.length&&(n=Q(s[f+2]),f+=2):l===49?n=null:l>=90&&l<=97?e=j[l-82]:l>=100&&l<=107&&(n=j[l-92]),f++}u=h()}let d=0;for(;d<t.length;){const s=t[d];if(s==="\x1B"&&d+1<t.length&&t[d+1]==="["){const f=d+2;let l=f;for(;l<t.length&&(t.charCodeAt(l)<64||t.charCodeAt(l)>126);)l++;if(l<t.length){if(t[l]==="m"){const F=t.slice(f,l),I=F?F.split(";").map(y=>parseInt(y,10)||0):[0];v(I)}d=l+1}else d++}else if(s==="\r")d++;else{const f=h();f!==u&&(m(),u=f),c+=s,d++}}return m(),i.join("")}let p=null;function te(){return p||(p=Promise.resolve().then(()=>require("./ghostty-web-DkOZu5AZ.cjs")).then(async t=>(await t.init(),t))),p}const k={cols:80,rows:24},G={},ne=[];function re(t){return"wasm"in t}function Y(t){const e=t??ne;return typeof e=="function"?e:()=>e}function ie(t){return t.cols!==void 0||t.rows!==void 0?{fit:"none",size:{cols:Math.max(1,t.cols??k.cols),rows:Math.max(1,t.rows??k.rows)}}:{fit:"container",size:k}}let Z=!1;function se(t){!t||Z||(Z=!0,console.warn("[tui-preview] Legacy props (`app`, `args`, `cols`, `rows`, `fontSize`, `fontFamily`, `theme`) are deprecated. Use `wasm`, `argv`, `fit`, `size`, and `terminal`."))}function oe(t){var o,a,r,i,c,u,h,m,v;if(re(t)){const d=t.fit??(t.size?"none":"container"),s=d==="none"?{cols:Math.max(1,((o=t.size)==null?void 0:o.cols)??k.cols),rows:Math.max(1,((a=t.size)==null?void 0:a.rows)??k.rows)}:{cols:Math.max(1,((r=t.size)==null?void 0:r.cols)??k.cols),rows:Math.max(1,((i=t.size)==null?void 0:i.rows)??k.rows)};return{wasm:t.wasm,env:t.env??G,interactive:t.interactive??!0,mode:t.mode??"terminal",fit:d,size:s,terminal:{fontSize:((c=t.terminal)==null?void 0:c.fontSize)??14,fontFamily:((u=t.terminal)==null?void 0:u.fontFamily)??"monospace",cursorBlink:((h=t.terminal)==null?void 0:h.cursorBlink)??!0,convertEol:((m=t.terminal)==null?void 0:m.convertEol)??!0,theme:(v=t.terminal)==null?void 0:v.theme},resolveArgv:Y(t.argv),onExit:t.onExit,onError:t.onError,onStatusChange:t.onStatusChange,usedLegacyProps:!1}}const{fit:e,size:n}=ie(t);return{wasm:t.app,env:t.env??G,interactive:t.interactive??!0,mode:"terminal",fit:e,size:n,terminal:{fontSize:t.fontSize??14,fontFamily:t.fontFamily??"monospace",cursorBlink:!0,convertEol:!0,theme:t.theme},resolveArgv:Y(t.args),onExit:t.onExit,onError:t.onError,onStatusChange:t.onStatusChange,usedLegacyProps:!0}}const z=0,ce=6,P=8,le=0,J=1,ae=2;class N{constructor(e){W(this,"inputQueue",[]);W(this,"memory");this.opts=e}pushInput(e){const n=typeof e=="string"?new TextEncoder().encode(e):e;this.inputQueue.push(n)}attachMemory(e){this.memory=e}view(){return new DataView(this.memory.buffer)}u8(){return new Uint8Array(this.memory.buffer)}get imports(){return{args_sizes_get:(e,n)=>{const{args:o}=this.opts,a=new TextEncoder,r=o.reduce((i,c)=>i+a.encode(c).length+1,0);return this.view().setUint32(e,o.length,!0),this.view().setUint32(n,r,!0),z},args_get:(e,n)=>{const o=new TextEncoder,a=this.u8(),r=this.view();let i=n;return this.opts.args.forEach((c,u)=>{const h=o.encode(c);a.set(h,i),a[i+h.length]=0,r.setUint32(e+u*4,i,!0),i+=h.length+1}),z},environ_sizes_get:(e,n)=>{const o=this.envEntries(),a=new TextEncoder,r=o.reduce((i,c)=>i+a.encode(c).length+1,0);return this.view().setUint32(e,o.length,!0),this.view().setUint32(n,r,!0),z},environ_get:(e,n)=>{const o=new TextEncoder,a=this.u8(),r=this.view();let i=n;return this.envEntries().forEach((c,u)=>{const h=o.encode(c);a.set(h,i),a[i+h.length]=0,r.setUint32(e+u*4,i,!0),i+=h.length+1}),z},fd_write:(e,n,o,a)=>{if(e!==J&&e!==ae)return P;const r=this.view(),i=this.u8();let c=0;const u=[];for(let v=0;v<o;v++){const d=r.getUint32(n+v*8,!0),s=r.getUint32(n+v*8+4,!0);u.push(i.slice(d,d+s)),c+=s}const h=new Uint8Array(c);let m=0;for(const v of u)h.set(v,m),m+=v.length;return e===J?this.opts.stdout(h):this.opts.stderr(h),r.setUint32(a,c,!0),z},fd_read:(e,n,o,a)=>{if(e!==le)return P;const r=this.inputQueue.shift();if(!r)return ce;const i=this.view(),c=this.u8();let u=0;for(let h=0;h<o&&u<r.length;h++){const m=i.getUint32(n+h*8,!0),v=i.getUint32(n+h*8+4,!0),d=Math.min(v,r.length-u);c.set(r.subarray(u,u+d),m),u+=d}return i.setUint32(a,u,!0),z},poll_oneoff:(e,n,o,a)=>{const r=this.view();let i=0;for(let c=0;c<o;c++){const u=e+c*48,h=r.getUint8(u+8);if(h===0&&this.inputQueue.length>0){const m=n+i*32;r.setBigUint64(m,r.getBigUint64(u,!0),!0),r.setUint16(m+8,0,!0),r.setUint8(m+10,h),i++}}return r.setUint32(a,i,!0),z},proc_exit:e=>{throw this.opts.onExit(e),new H(e)},random_get:(e,n)=>(crypto.getRandomValues(new Uint8Array(this.memory.buffer,e,n)),z),fd_close:()=>z,fd_seek:()=>z,fd_fdstat_get:(e,n)=>(this.view().setUint8(n,e<=2?2:0),z),fd_prestat_get:()=>P,fd_prestat_dir_name:()=>P,path_open:()=>P,sched_yield:()=>z,clock_time_get:(e,n,o)=>{const a=BigInt(Date.now())*1000000n;return this.view().setBigUint64(o,a,!0),z}}}envEntries(){const e={TERM:"xterm-256color",COLORTERM:"truecolor",...this.opts.env};return Object.entries(e).map(([n,o])=>`${n}=${o}`)}}class H extends Error{constructor(e){super(`WASI exit: ${e}`),this.code=e}}async function O(t,e){const o=await(await fetch(t)).arrayBuffer(),a=await WebAssembly.compile(o),r={wasi_snapshot_preview1:e.imports},i=await WebAssembly.instantiate(a,r);e.attachMemory(i.exports.memory);const c=i.exports._start;if(!c)throw new Error("WASM module has no _start export");return{run:async()=>{try{c()}catch(u){if(!(u instanceof H))throw u}}}}function ue(t){var F,I;const e=b.useMemo(()=>oe(t),[t]),n=b.useRef(null),o=b.useRef(null),a=b.useRef(null),[r,i]=b.useState("loading"),[c,u]=b.useState(""),h=b.useRef(null),[m,v]=b.useState(e.size),[d,s]=b.useState(null);b.useEffect(()=>{se(e.usedLegacyProps)},[e.usedLegacyProps]),b.useEffect(()=>{e.mode==="terminal"&&v(e.size)},[e.mode,e.fit,e.size.cols,e.size.rows]),b.useEffect(()=>{if(e.mode!=="terminal"||e.fit!=="container"||!n.current)return;const y=new ResizeObserver(([_])=>{var E,A;const{width:T,height:x}=_.contentRect;if(T>0&&x>0){const w=((E=h.current)==null?void 0:E.w)??e.terminal.fontSize*.6,g=((A=h.current)==null?void 0:A.h)??e.terminal.fontSize*1.2;v({cols:Math.max(1,Math.floor(T/w)),rows:Math.max(1,Math.floor(x/g))})}});return y.observe(n.current),()=>y.disconnect()},[e.mode,e.fit,e.terminal.fontSize]),b.useEffect(()=>{if(e.mode!=="terminal"||!m||!o.current)return;let y=!1;const _=o.current,T=m,x=w=>{var g;i(w),(g=e.onStatusChange)==null||g.call(e,w)},E=w=>{var g;x("error"),u(w instanceof Error?w.message:String(w)),(g=e.onError)==null||g.call(e,w)};x("loading"),u("");async function A(){try{const w=await te();if(y)return;_.innerHTML="";const g=new w.Terminal({cols:T.cols,rows:T.rows,fontSize:e.terminal.fontSize,fontFamily:e.terminal.fontFamily,theme:e.terminal.theme,disableStdin:!e.interactive,cursorBlink:e.terminal.cursorBlink,convertEol:e.terminal.convertEol});a.current=g,g.open(_);let U=g.cols,R=g.rows;if(e.fit==="container"){const S=new w.FitAddon;g.loadAddon(S),S.fit(),U=g.cols,R=g.rows,n.current&&U>0&&R>0&&(h.current={w:n.current.clientWidth/U,h:n.current.clientHeight/R})}const D=e.resolveArgv({cols:U,rows:R}),B=new TextDecoder,C=new N({args:[e.wasm.toString(),...D],env:e.env,stdout:S=>g.write(B.decode(S)),stderr:S=>g.write(B.decode(S)),onExit:S=>{var q;y||(x("exited"),(q=e.onExit)==null||q.call(e,S))}});e.interactive&&g.onData(S=>C.pushInput(S));const $=await O(e.wasm,C);if(y)return;x("running"),queueMicrotask(()=>{y||$.run().catch(S=>{y||E(S)})})}catch(w){y||E(w)}}return A(),()=>{var w;y=!0,(w=a.current)==null||w.dispose(),a.current=null}},[e.mode,m,e.wasm,e.resolveArgv,e.env,e.fit,e.interactive,e.onExit,e.onError,e.onStatusChange,e.terminal.fontSize,e.terminal.fontFamily,e.terminal.theme,e.terminal.cursorBlink,e.terminal.convertEol]),b.useEffect(()=>{if(e.mode!=="static")return;let y=!1;const _=x=>{var E;i(x),(E=e.onStatusChange)==null||E.call(e,x)};_("loading"),u(""),s(null);async function T(){var x;try{const E=new TextDecoder,A=[],w=new N({args:[e.wasm.toString(),...e.resolveArgv(e.size)],env:e.env,stdout:U=>A.push(new Uint8Array(U)),stderr:()=>{},onExit:U=>{var R;if(!y){const D=A.reduce(($,S)=>$+S.length,0),B=new Uint8Array(D);let C=0;for(const $ of A)B.set($,C),C+=$.length;s(ee(E.decode(B))),_("exited"),(R=e.onExit)==null||R.call(e,U)}}}),g=await O(e.wasm,w);if(y)return;_("running"),await g.run()}catch(E){y||(_("error"),u(E instanceof Error?E.message:String(E)),(x=e.onError)==null||x.call(e,E))}}return T(),()=>{y=!0}},[e.mode,e.wasm,e.resolveArgv,e.env,e.size.cols,e.size.rows,e.onExit,e.onError,e.onStatusChange]);const f=((F=e.terminal.theme)==null?void 0:F.background)??"#1a1b26",l=((I=e.terminal.theme)==null?void 0:I.foreground)??"#a9b1d6";return e.mode==="static"?M.jsxs("div",{className:t.className,style:{position:"relative",background:f,borderRadius:6,overflow:"hidden",...t.style},children:[r==="loading"&&M.jsx("div",{style:L,children:"Loading…"}),r==="error"&&M.jsxs("div",{style:{...L,color:"#f7768e"},children:["Error: ",c]}),d!==null&&M.jsx("pre",{style:{margin:0,padding:"0.5em",fontFamily:e.terminal.fontFamily,fontSize:e.terminal.fontSize,color:l,lineHeight:1.2,background:"transparent",overflow:"auto"},dangerouslySetInnerHTML:{__html:d}})]}):M.jsxs("div",{ref:n,className:t.className,style:{position:"relative",display:"inline-block",background:f,borderRadius:6,overflow:"hidden",...t.style},children:[M.jsx("div",{ref:o,style:{display:r==="error"?"none":void 0}}),r==="loading"&&M.jsx("div",{style:L,children:"Loading…"}),r==="error"&&M.jsxs("div",{style:{...L,color:"#f7768e"},children:["Error: ",c]})]})}const L={padding:"1rem",fontFamily:"monospace",fontSize:14,color:"#a9b1d6"};exports.TuiPreview=ue;exports.WasiBridge=N;exports.WasiExitError=H;exports.instantiateApp=O;
@@ -0,0 +1,3 @@
1
+ export { TuiPreview } from "./TuiPreview.js";
2
+ export type { GhosttyTheme, ResolvedTuiPreviewOptions, TuiArgv, TuiFitMode, TuiRenderMode, TuiPreviewCommonProps, TuiPreviewLegacyProps, TuiPreviewModernProps, TuiPreviewProps, TuiPreviewStatus, TuiRuntimeSize, TuiTerminalOptions, WasiOptions, } from "./types.js";
3
+ export { WasiBridge, WasiExitError, instantiateApp } from "./wasi.js";
package/dist/index.js ADDED
@@ -0,0 +1,515 @@
1
+ var te = Object.defineProperty;
2
+ var ne = (t, e, n) => e in t ? te(t, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[e] = n;
3
+ var O = (t, e, n) => ne(t, typeof e != "symbol" ? e + "" : e, n);
4
+ import { jsxs as D, jsx as p } from "react/jsx-runtime";
5
+ import { useMemo as re, useRef as P, useState as W, useEffect as C } from "react";
6
+ const I = [
7
+ "#000000",
8
+ "#cd0000",
9
+ "#00cd00",
10
+ "#cdcd00",
11
+ "#0000ee",
12
+ "#cd00cd",
13
+ "#00cdcd",
14
+ "#e5e5e5",
15
+ "#7f7f7f",
16
+ "#ff0000",
17
+ "#00ff00",
18
+ "#ffff00",
19
+ "#5c5cff",
20
+ "#ff00ff",
21
+ "#00ffff",
22
+ "#ffffff"
23
+ ];
24
+ function V(t) {
25
+ if (t < 16) return I[t];
26
+ if (t < 232) {
27
+ const n = t - 16, s = Math.floor(n / 36), a = Math.floor(n % 36 / 6), r = n % 6, i = (c) => c === 0 ? 0 : c * 40 + 55;
28
+ return `rgb(${i(s)},${i(a)},${i(r)})`;
29
+ }
30
+ const e = (t - 232) * 10 + 8;
31
+ return `rgb(${e},${e},${e})`;
32
+ }
33
+ function q(t) {
34
+ return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
35
+ }
36
+ function ie(t) {
37
+ let e = null, n = null, s = !1, a = !1, r = !1;
38
+ const i = [];
39
+ let c = "", u = "";
40
+ function h() {
41
+ const o = [];
42
+ return e && o.push(`color:${e}`), n && o.push(`background-color:${n}`), s && o.push("font-weight:bold"), a && o.push("font-style:italic"), r && o.push("text-decoration:underline"), o.join(";");
43
+ }
44
+ function m() {
45
+ c && (i.push(u ? `<span style="${u}">${q(c)}</span>` : q(c)), c = "");
46
+ }
47
+ function v(o) {
48
+ m();
49
+ let f = 0;
50
+ for (; f < o.length; ) {
51
+ const l = o[f];
52
+ l === 0 ? (e = null, n = null, s = !1, a = !1, r = !1) : l === 1 ? s = !0 : l === 22 ? s = !1 : l === 3 ? a = !0 : l === 23 ? a = !1 : l === 4 ? r = !0 : l === 24 ? r = !1 : l >= 30 && l <= 37 ? e = I[l - 30] : l === 38 ? o[f + 1] === 2 && f + 4 < o.length ? (e = `rgb(${o[f + 2]},${o[f + 3]},${o[f + 4]})`, f += 4) : o[f + 1] === 5 && f + 2 < o.length && (e = V(o[f + 2]), f += 2) : l === 39 ? e = null : l >= 40 && l <= 47 ? n = I[l - 40] : l === 48 ? o[f + 1] === 2 && f + 4 < o.length ? (n = `rgb(${o[f + 2]},${o[f + 3]},${o[f + 4]})`, f += 4) : o[f + 1] === 5 && f + 2 < o.length && (n = V(o[f + 2]), f += 2) : l === 49 ? n = null : l >= 90 && l <= 97 ? e = I[l - 82] : l >= 100 && l <= 107 && (n = I[l - 92]), f++;
53
+ }
54
+ u = h();
55
+ }
56
+ let d = 0;
57
+ for (; d < t.length; ) {
58
+ const o = t[d];
59
+ if (o === "\x1B" && d + 1 < t.length && t[d + 1] === "[") {
60
+ const f = d + 2;
61
+ let l = f;
62
+ for (; l < t.length && (t.charCodeAt(l) < 64 || t.charCodeAt(l) > 126); )
63
+ l++;
64
+ if (l < t.length) {
65
+ if (t[l] === "m") {
66
+ const $ = t.slice(f, l), L = $ ? $.split(";").map((y) => parseInt(y, 10) || 0) : [0];
67
+ v(L);
68
+ }
69
+ d = l + 1;
70
+ } else
71
+ d++;
72
+ } else if (o === "\r")
73
+ d++;
74
+ else {
75
+ const f = h();
76
+ f !== u && (m(), u = f), c += o, d++;
77
+ }
78
+ }
79
+ return m(), i.join("");
80
+ }
81
+ let j = null;
82
+ function oe() {
83
+ return j || (j = import("./ghostty-web-BfBVpf8G.js").then(async (t) => (await t.init(), t))), j;
84
+ }
85
+ const T = { cols: 80, rows: 24 }, G = {}, se = [];
86
+ function ce(t) {
87
+ return "wasm" in t;
88
+ }
89
+ function Y(t) {
90
+ const e = t ?? se;
91
+ return typeof e == "function" ? e : () => e;
92
+ }
93
+ function le(t) {
94
+ return t.cols !== void 0 || t.rows !== void 0 ? {
95
+ fit: "none",
96
+ size: {
97
+ cols: Math.max(1, t.cols ?? T.cols),
98
+ rows: Math.max(1, t.rows ?? T.rows)
99
+ }
100
+ } : { fit: "container", size: T };
101
+ }
102
+ let Z = !1;
103
+ function ae(t) {
104
+ !t || Z || (Z = !0, console.warn(
105
+ "[tui-preview] Legacy props (`app`, `args`, `cols`, `rows`, `fontSize`, `fontFamily`, `theme`) are deprecated. Use `wasm`, `argv`, `fit`, `size`, and `terminal`."
106
+ ));
107
+ }
108
+ function ue(t) {
109
+ var s, a, r, i, c, u, h, m, v;
110
+ if (ce(t)) {
111
+ const d = t.fit ?? (t.size ? "none" : "container"), o = d === "none" ? {
112
+ cols: Math.max(1, ((s = t.size) == null ? void 0 : s.cols) ?? T.cols),
113
+ rows: Math.max(1, ((a = t.size) == null ? void 0 : a.rows) ?? T.rows)
114
+ } : {
115
+ cols: Math.max(1, ((r = t.size) == null ? void 0 : r.cols) ?? T.cols),
116
+ rows: Math.max(1, ((i = t.size) == null ? void 0 : i.rows) ?? T.rows)
117
+ };
118
+ return {
119
+ wasm: t.wasm,
120
+ env: t.env ?? G,
121
+ interactive: t.interactive ?? !0,
122
+ mode: t.mode ?? "terminal",
123
+ fit: d,
124
+ size: o,
125
+ terminal: {
126
+ fontSize: ((c = t.terminal) == null ? void 0 : c.fontSize) ?? 14,
127
+ fontFamily: ((u = t.terminal) == null ? void 0 : u.fontFamily) ?? "monospace",
128
+ cursorBlink: ((h = t.terminal) == null ? void 0 : h.cursorBlink) ?? !0,
129
+ convertEol: ((m = t.terminal) == null ? void 0 : m.convertEol) ?? !0,
130
+ theme: (v = t.terminal) == null ? void 0 : v.theme
131
+ },
132
+ resolveArgv: Y(t.argv),
133
+ onExit: t.onExit,
134
+ onError: t.onError,
135
+ onStatusChange: t.onStatusChange,
136
+ usedLegacyProps: !1
137
+ };
138
+ }
139
+ const { fit: e, size: n } = le(t);
140
+ return {
141
+ wasm: t.app,
142
+ env: t.env ?? G,
143
+ interactive: t.interactive ?? !0,
144
+ mode: "terminal",
145
+ fit: e,
146
+ size: n,
147
+ terminal: {
148
+ fontSize: t.fontSize ?? 14,
149
+ fontFamily: t.fontFamily ?? "monospace",
150
+ cursorBlink: !0,
151
+ convertEol: !0,
152
+ theme: t.theme
153
+ },
154
+ resolveArgv: Y(t.args),
155
+ onExit: t.onExit,
156
+ onError: t.onError,
157
+ onStatusChange: t.onStatusChange,
158
+ usedLegacyProps: !0
159
+ };
160
+ }
161
+ const z = 0, fe = 6, B = 8, he = 0, J = 1, de = 2;
162
+ class K {
163
+ constructor(e) {
164
+ O(this, "inputQueue", []);
165
+ O(this, "memory");
166
+ this.opts = e;
167
+ }
168
+ /** Push keyboard data from the terminal into the app's stdin */
169
+ pushInput(e) {
170
+ const n = typeof e == "string" ? new TextEncoder().encode(e) : e;
171
+ this.inputQueue.push(n);
172
+ }
173
+ /** Attach the WASM instance's memory after instantiation */
174
+ attachMemory(e) {
175
+ this.memory = e;
176
+ }
177
+ view() {
178
+ return new DataView(this.memory.buffer);
179
+ }
180
+ u8() {
181
+ return new Uint8Array(this.memory.buffer);
182
+ }
183
+ // ── WASI imports object ────────────────────────────────────────────────
184
+ get imports() {
185
+ return {
186
+ args_sizes_get: (e, n) => {
187
+ const { args: s } = this.opts, a = new TextEncoder(), r = s.reduce((i, c) => i + a.encode(c).length + 1, 0);
188
+ return this.view().setUint32(e, s.length, !0), this.view().setUint32(n, r, !0), z;
189
+ },
190
+ args_get: (e, n) => {
191
+ const s = new TextEncoder(), a = this.u8(), r = this.view();
192
+ let i = n;
193
+ return this.opts.args.forEach((c, u) => {
194
+ const h = s.encode(c);
195
+ a.set(h, i), a[i + h.length] = 0, r.setUint32(e + u * 4, i, !0), i += h.length + 1;
196
+ }), z;
197
+ },
198
+ environ_sizes_get: (e, n) => {
199
+ const s = this.envEntries(), a = new TextEncoder(), r = s.reduce((i, c) => i + a.encode(c).length + 1, 0);
200
+ return this.view().setUint32(e, s.length, !0), this.view().setUint32(n, r, !0), z;
201
+ },
202
+ environ_get: (e, n) => {
203
+ const s = new TextEncoder(), a = this.u8(), r = this.view();
204
+ let i = n;
205
+ return this.envEntries().forEach((c, u) => {
206
+ const h = s.encode(c);
207
+ a.set(h, i), a[i + h.length] = 0, r.setUint32(e + u * 4, i, !0), i += h.length + 1;
208
+ }), z;
209
+ },
210
+ fd_write: (e, n, s, a) => {
211
+ if (e !== J && e !== de) return B;
212
+ const r = this.view(), i = this.u8();
213
+ let c = 0;
214
+ const u = [];
215
+ for (let v = 0; v < s; v++) {
216
+ const d = r.getUint32(n + v * 8, !0), o = r.getUint32(n + v * 8 + 4, !0);
217
+ u.push(i.slice(d, d + o)), c += o;
218
+ }
219
+ const h = new Uint8Array(c);
220
+ let m = 0;
221
+ for (const v of u)
222
+ h.set(v, m), m += v.length;
223
+ return e === J ? this.opts.stdout(h) : this.opts.stderr(h), r.setUint32(a, c, !0), z;
224
+ },
225
+ fd_read: (e, n, s, a) => {
226
+ if (e !== he) return B;
227
+ const r = this.inputQueue.shift();
228
+ if (!r) return fe;
229
+ const i = this.view(), c = this.u8();
230
+ let u = 0;
231
+ for (let h = 0; h < s && u < r.length; h++) {
232
+ const m = i.getUint32(n + h * 8, !0), v = i.getUint32(n + h * 8 + 4, !0), d = Math.min(v, r.length - u);
233
+ c.set(r.subarray(u, u + d), m), u += d;
234
+ }
235
+ return i.setUint32(a, u, !0), z;
236
+ },
237
+ poll_oneoff: (e, n, s, a) => {
238
+ const r = this.view();
239
+ let i = 0;
240
+ for (let c = 0; c < s; c++) {
241
+ const u = e + c * 48, h = r.getUint8(u + 8);
242
+ if (h === 0 && this.inputQueue.length > 0) {
243
+ const m = n + i * 32;
244
+ r.setBigUint64(m, r.getBigUint64(u, !0), !0), r.setUint16(m + 8, 0, !0), r.setUint8(m + 10, h), i++;
245
+ }
246
+ }
247
+ return r.setUint32(a, i, !0), z;
248
+ },
249
+ proc_exit: (e) => {
250
+ throw this.opts.onExit(e), new ee(e);
251
+ },
252
+ random_get: (e, n) => (crypto.getRandomValues(new Uint8Array(this.memory.buffer, e, n)), z),
253
+ // Stubs for calls TUI apps may make but we don't need to implement.
254
+ fd_close: () => z,
255
+ fd_seek: () => z,
256
+ fd_fdstat_get: (e, n) => (this.view().setUint8(n, e <= 2 ? 2 : 0), z),
257
+ fd_prestat_get: () => B,
258
+ fd_prestat_dir_name: () => B,
259
+ path_open: () => B,
260
+ sched_yield: () => z,
261
+ clock_time_get: (e, n, s) => {
262
+ const a = BigInt(Date.now()) * 1000000n;
263
+ return this.view().setBigUint64(s, a, !0), z;
264
+ }
265
+ };
266
+ }
267
+ envEntries() {
268
+ const e = {
269
+ TERM: "xterm-256color",
270
+ COLORTERM: "truecolor",
271
+ ...this.opts.env
272
+ };
273
+ return Object.entries(e).map(([n, s]) => `${n}=${s}`);
274
+ }
275
+ }
276
+ class ee extends Error {
277
+ constructor(e) {
278
+ super(`WASI exit: ${e}`), this.code = e;
279
+ }
280
+ }
281
+ async function X(t, e) {
282
+ const s = await (await fetch(t)).arrayBuffer(), a = await WebAssembly.compile(s), r = {
283
+ wasi_snapshot_preview1: e.imports
284
+ }, i = await WebAssembly.instantiate(a, r);
285
+ e.attachMemory(i.exports.memory);
286
+ const c = i.exports._start;
287
+ if (!c) throw new Error("WASM module has no _start export");
288
+ return {
289
+ run: async () => {
290
+ try {
291
+ c();
292
+ } catch (u) {
293
+ if (!(u instanceof ee)) throw u;
294
+ }
295
+ }
296
+ };
297
+ }
298
+ function ve(t) {
299
+ var $, L;
300
+ const e = re(() => ue(t), [t]), n = P(null), s = P(null), a = P(null), [r, i] = W("loading"), [c, u] = W(""), h = P(null), [m, v] = W(e.size), [d, o] = W(null);
301
+ C(() => {
302
+ ae(e.usedLegacyProps);
303
+ }, [e.usedLegacyProps]), C(() => {
304
+ e.mode === "terminal" && v(e.size);
305
+ }, [e.mode, e.fit, e.size.cols, e.size.rows]), C(() => {
306
+ if (e.mode !== "terminal" || e.fit !== "container" || !n.current) return;
307
+ const y = new ResizeObserver(([_]) => {
308
+ var E, x;
309
+ const { width: U, height: b } = _.contentRect;
310
+ if (U > 0 && b > 0) {
311
+ const w = ((E = h.current) == null ? void 0 : E.w) ?? e.terminal.fontSize * 0.6, g = ((x = h.current) == null ? void 0 : x.h) ?? e.terminal.fontSize * 1.2;
312
+ v({
313
+ cols: Math.max(1, Math.floor(U / w)),
314
+ rows: Math.max(1, Math.floor(b / g))
315
+ });
316
+ }
317
+ });
318
+ return y.observe(n.current), () => y.disconnect();
319
+ }, [e.mode, e.fit, e.terminal.fontSize]), C(() => {
320
+ if (e.mode !== "terminal" || !m || !s.current) return;
321
+ let y = !1;
322
+ const _ = s.current, U = m, b = (w) => {
323
+ var g;
324
+ i(w), (g = e.onStatusChange) == null || g.call(e, w);
325
+ }, E = (w) => {
326
+ var g;
327
+ b("error"), u(w instanceof Error ? w.message : String(w)), (g = e.onError) == null || g.call(e, w);
328
+ };
329
+ b("loading"), u("");
330
+ async function x() {
331
+ try {
332
+ const w = await oe();
333
+ if (y) return;
334
+ _.innerHTML = "";
335
+ const g = new w.Terminal({
336
+ cols: U.cols,
337
+ rows: U.rows,
338
+ fontSize: e.terminal.fontSize,
339
+ fontFamily: e.terminal.fontFamily,
340
+ theme: e.terminal.theme,
341
+ disableStdin: !e.interactive,
342
+ cursorBlink: e.terminal.cursorBlink,
343
+ convertEol: e.terminal.convertEol
344
+ });
345
+ a.current = g, g.open(_);
346
+ let A = g.cols, M = g.rows;
347
+ if (e.fit === "container") {
348
+ const S = new w.FitAddon();
349
+ g.loadAddon(S), S.fit(), A = g.cols, M = g.rows, n.current && A > 0 && M > 0 && (h.current = {
350
+ w: n.current.clientWidth / A,
351
+ h: n.current.clientHeight / M
352
+ });
353
+ }
354
+ const H = e.resolveArgv({ cols: A, rows: M }), R = new TextDecoder(), F = new K({
355
+ args: [e.wasm.toString(), ...H],
356
+ env: e.env,
357
+ stdout: (S) => g.write(R.decode(S)),
358
+ stderr: (S) => g.write(R.decode(S)),
359
+ onExit: (S) => {
360
+ var Q;
361
+ y || (b("exited"), (Q = e.onExit) == null || Q.call(e, S));
362
+ }
363
+ });
364
+ e.interactive && g.onData((S) => F.pushInput(S));
365
+ const k = await X(e.wasm, F);
366
+ if (y) return;
367
+ b("running"), queueMicrotask(() => {
368
+ y || k.run().catch((S) => {
369
+ y || E(S);
370
+ });
371
+ });
372
+ } catch (w) {
373
+ y || E(w);
374
+ }
375
+ }
376
+ return x(), () => {
377
+ var w;
378
+ y = !0, (w = a.current) == null || w.dispose(), a.current = null;
379
+ };
380
+ }, [
381
+ e.mode,
382
+ m,
383
+ e.wasm,
384
+ e.resolveArgv,
385
+ e.env,
386
+ e.fit,
387
+ e.interactive,
388
+ e.onExit,
389
+ e.onError,
390
+ e.onStatusChange,
391
+ e.terminal.fontSize,
392
+ e.terminal.fontFamily,
393
+ e.terminal.theme,
394
+ e.terminal.cursorBlink,
395
+ e.terminal.convertEol
396
+ ]), C(() => {
397
+ if (e.mode !== "static") return;
398
+ let y = !1;
399
+ const _ = (b) => {
400
+ var E;
401
+ i(b), (E = e.onStatusChange) == null || E.call(e, b);
402
+ };
403
+ _("loading"), u(""), o(null);
404
+ async function U() {
405
+ var b;
406
+ try {
407
+ const E = new TextDecoder(), x = [], w = new K({
408
+ args: [e.wasm.toString(), ...e.resolveArgv(e.size)],
409
+ env: e.env,
410
+ stdout: (A) => x.push(new Uint8Array(A)),
411
+ stderr: () => {
412
+ },
413
+ onExit: (A) => {
414
+ var M;
415
+ if (!y) {
416
+ const H = x.reduce((k, S) => k + S.length, 0), R = new Uint8Array(H);
417
+ let F = 0;
418
+ for (const k of x)
419
+ R.set(k, F), F += k.length;
420
+ o(ie(E.decode(R))), _("exited"), (M = e.onExit) == null || M.call(e, A);
421
+ }
422
+ }
423
+ }), g = await X(e.wasm, w);
424
+ if (y) return;
425
+ _("running"), await g.run();
426
+ } catch (E) {
427
+ y || (_("error"), u(E instanceof Error ? E.message : String(E)), (b = e.onError) == null || b.call(e, E));
428
+ }
429
+ }
430
+ return U(), () => {
431
+ y = !0;
432
+ };
433
+ }, [
434
+ e.mode,
435
+ e.wasm,
436
+ e.resolveArgv,
437
+ e.env,
438
+ e.size.cols,
439
+ e.size.rows,
440
+ e.onExit,
441
+ e.onError,
442
+ e.onStatusChange
443
+ ]);
444
+ const f = (($ = e.terminal.theme) == null ? void 0 : $.background) ?? "#1a1b26", l = ((L = e.terminal.theme) == null ? void 0 : L.foreground) ?? "#a9b1d6";
445
+ return e.mode === "static" ? /* @__PURE__ */ D(
446
+ "div",
447
+ {
448
+ className: t.className,
449
+ style: {
450
+ position: "relative",
451
+ background: f,
452
+ borderRadius: 6,
453
+ overflow: "hidden",
454
+ ...t.style
455
+ },
456
+ children: [
457
+ r === "loading" && /* @__PURE__ */ p("div", { style: N, children: "Loading…" }),
458
+ r === "error" && /* @__PURE__ */ D("div", { style: { ...N, color: "#f7768e" }, children: [
459
+ "Error: ",
460
+ c
461
+ ] }),
462
+ d !== null && /* @__PURE__ */ p(
463
+ "pre",
464
+ {
465
+ style: {
466
+ margin: 0,
467
+ padding: "0.5em",
468
+ fontFamily: e.terminal.fontFamily,
469
+ fontSize: e.terminal.fontSize,
470
+ color: l,
471
+ lineHeight: 1.2,
472
+ background: "transparent",
473
+ overflow: "auto"
474
+ },
475
+ dangerouslySetInnerHTML: { __html: d }
476
+ }
477
+ )
478
+ ]
479
+ }
480
+ ) : /* @__PURE__ */ D(
481
+ "div",
482
+ {
483
+ ref: n,
484
+ className: t.className,
485
+ style: {
486
+ position: "relative",
487
+ display: "inline-block",
488
+ background: f,
489
+ borderRadius: 6,
490
+ overflow: "hidden",
491
+ ...t.style
492
+ },
493
+ children: [
494
+ /* @__PURE__ */ p("div", { ref: s, style: { display: r === "error" ? "none" : void 0 } }),
495
+ r === "loading" && /* @__PURE__ */ p("div", { style: N, children: "Loading…" }),
496
+ r === "error" && /* @__PURE__ */ D("div", { style: { ...N, color: "#f7768e" }, children: [
497
+ "Error: ",
498
+ c
499
+ ] })
500
+ ]
501
+ }
502
+ );
503
+ }
504
+ const N = {
505
+ padding: "1rem",
506
+ fontFamily: "monospace",
507
+ fontSize: 14,
508
+ color: "#a9b1d6"
509
+ };
510
+ export {
511
+ ve as TuiPreview,
512
+ K as WasiBridge,
513
+ ee as WasiExitError,
514
+ X as instantiateApp
515
+ };
@@ -0,0 +1,113 @@
1
+ export interface TuiRuntimeSize {
2
+ cols: number;
3
+ rows: number;
4
+ }
5
+ export type TuiArgv = string[] | ((size: TuiRuntimeSize) => string[]);
6
+ export type TuiFitMode = "container" | "none";
7
+ export type TuiRenderMode = "terminal" | "static";
8
+ export type TuiPreviewStatus = "loading" | "running" | "exited" | "error";
9
+ export interface TuiTerminalOptions {
10
+ /** Font size in pixels. Default: 14 */
11
+ fontSize?: number;
12
+ /** CSS font family. Default: monospace */
13
+ fontFamily?: string;
14
+ /** ghostty-web theme overrides */
15
+ theme?: Partial<GhosttyTheme>;
16
+ /** Cursor blinking. Default: true */
17
+ cursorBlink?: boolean;
18
+ /** Convert LF to CRLF. Default: true */
19
+ convertEol?: boolean;
20
+ }
21
+ export interface TuiPreviewCommonProps {
22
+ /** Environment variables (TERM, COLORTERM are set automatically) */
23
+ env?: Record<string, string>;
24
+ /** Whether the user can type into the terminal. Default: true */
25
+ interactive?: boolean;
26
+ /** Called when the app exits */
27
+ onExit?: (code: number) => void;
28
+ /** Called on runtime errors */
29
+ onError?: (error: unknown) => void;
30
+ /** Called whenever status changes */
31
+ onStatusChange?: (status: TuiPreviewStatus) => void;
32
+ className?: string;
33
+ style?: React.CSSProperties;
34
+ }
35
+ export interface TuiPreviewModernProps extends TuiPreviewCommonProps {
36
+ /** URL or path to a wasm32-wasi binary */
37
+ wasm: string | URL;
38
+ /** CLI argv (without argv[0]), static or size-aware */
39
+ argv?: TuiArgv;
40
+ /**
41
+ * Render mode.
42
+ * - `"terminal"` (default): full ghostty-web terminal, supports interactive apps.
43
+ * - `"static"`: runs the app once, captures stdout, renders ANSI output as HTML.
44
+ * No cursor, no input. Ideal for non-interactive previews in docs.
45
+ */
46
+ mode?: TuiRenderMode;
47
+ /** "container" auto-fit or "none" fixed size. Default: "container" */
48
+ fit?: TuiFitMode;
49
+ /** Fixed size (fit="none") or initial fallback (fit="container") */
50
+ size?: TuiRuntimeSize;
51
+ /** Terminal renderer options */
52
+ terminal?: TuiTerminalOptions;
53
+ }
54
+ /**
55
+ * Legacy API retained for backwards compatibility.
56
+ * Prefer `TuiPreviewModernProps`.
57
+ */
58
+ export interface TuiPreviewLegacyProps extends TuiPreviewCommonProps {
59
+ app: string | URL;
60
+ args?: TuiArgv;
61
+ cols?: number;
62
+ rows?: number;
63
+ fontSize?: number;
64
+ fontFamily?: string;
65
+ theme?: Partial<GhosttyTheme>;
66
+ }
67
+ export type TuiPreviewProps = TuiPreviewModernProps | TuiPreviewLegacyProps;
68
+ export interface GhosttyTheme {
69
+ background: string;
70
+ foreground: string;
71
+ cursor: string;
72
+ selectionBackground: string;
73
+ selectionForeground: string;
74
+ black: string;
75
+ red: string;
76
+ green: string;
77
+ yellow: string;
78
+ blue: string;
79
+ magenta: string;
80
+ cyan: string;
81
+ white: string;
82
+ brightBlack: string;
83
+ brightRed: string;
84
+ brightGreen: string;
85
+ brightYellow: string;
86
+ brightBlue: string;
87
+ brightMagenta: string;
88
+ brightCyan: string;
89
+ brightWhite: string;
90
+ }
91
+ export interface WasiOptions {
92
+ args: string[];
93
+ env: Record<string, string>;
94
+ stdout: (data: Uint8Array) => void;
95
+ stderr: (data: Uint8Array) => void;
96
+ onExit: (code: number) => void;
97
+ }
98
+ export interface ResolvedTuiPreviewOptions {
99
+ wasm: string | URL;
100
+ env: Record<string, string>;
101
+ interactive: boolean;
102
+ mode: TuiRenderMode;
103
+ fit: TuiFitMode;
104
+ size: TuiRuntimeSize;
105
+ terminal: Required<Omit<TuiTerminalOptions, "theme">> & {
106
+ theme?: Partial<GhosttyTheme>;
107
+ };
108
+ resolveArgv: (size: TuiRuntimeSize) => string[];
109
+ onExit?: (code: number) => void;
110
+ onError?: (error: unknown) => void;
111
+ onStatusChange?: (status: TuiPreviewStatus) => void;
112
+ usedLegacyProps: boolean;
113
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/wasi.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { WasiBridge, WasiExitError, instantiateApp } from "./core/wasi.js";
package/dist/wasi.js ADDED
@@ -0,0 +1,2 @@
1
+ // Backwards-compatible re-export.
2
+ export { WasiBridge, WasiExitError, instantiateApp } from "./core/wasi.js";