termbridge 0.3.0 → 0.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termbridge",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "Local-first terminal beaming with tmux + Cloudflare tunnel and a mobile-friendly UI.",
6
6
  "license": "MIT",
@@ -100,4 +100,4 @@ void main() {
100
100
 
101
101
  document.documentElement.setAttribute("data-standalone", !!standalone);
102
102
  document.documentElement.setAttribute("data-silk-native-page-scroll-replaced", !(mobile && !standalone));
103
- `}}),j.jsx(x5,{}),j.jsx(A,{...ji("view",["axis","pageScroll","skipScrollAnimation","scrollOngoing"],{className:a,dataSilk:[l]}),...F,ref:q,children:n})]})});c6.displayName="Scroll.View";let Kg=We.forwardRef(({side:t,...e},i)=>{let{axis:n}=S.useContext(Nf)||{},a=Ui("Scroll",{side:t,axis:n});return j.jsx("div",{...a("spy",["side","axis"]),ref:i,...e})});Kg.displayName="Scroll.Anonymous";let u6=We.forwardRef((t,e)=>{let{asChild:i,className:n,"data-silk":a,...l}=t,u=i?Fi:"div",{contentRef:h,styleAttributes:f,nativeFocusScrollPrevention:d,swipeTrapIncapable:p,focusable:v,axis:g,pageScroll:y,nativePageScrollReplacement:b,scrollContainerRef:C,swipeTrapObserverRequired:x,startScrollSpyRef:k,endScrollSpyRef:R,startSpacerRef:L,endSpacerRef:E,scrollHandler:H,scrollPadding:U,scrollTimelineName:I}=S.useContext(Nf)||{},N=Rs(h,e);return j.jsxs("div",{...f("scrollContainer",["axis","pageScroll","scrollTrapX","scrollTrapY","scrollGestureOvershoot","scrollDisabled","overflowX","overflowY","skipScrollAnimation","scrollAnchoring","scrollSnapType","nativeScrollbar"],{dataSilk:[a,"0ad",d&&"0ah",p&&"0ai"]}),style:{scrollPadding:U,scrollTimeline:I+" "+g},tabIndex:v?0:d?-1:void 0,role:y&&!b?void 0:"region",onScroll:H,ref:C,children:[x&&j.jsx(Kg,{side:"start",ref:k}),g==="y"&&j.jsx("div",{ref:L,...f("startSpacer",["axis","pageScroll"])}),j.jsx(u,{...f("content",["axis","scrollTrapX","scrollTrapY","overflowX","overflowY"],{className:n,dataSilk:[a]}),...l,ref:N}),g==="y"&&j.jsx("div",{ref:E,...f("endSpacer",["axis","pageScroll"])}),x&&j.jsx(Kg,{side:"end",ref:R})]})});u6.displayName="Scroll.Content";let h6=We.forwardRef(({asChild:t,...e},i)=>{let n=t?Fi:"button",{forComponent:a,onPress:l,onClick:u,children:h,action:f,...d}=e,{type:p,...v}=f||{},{scrollTo:g,scrollBy:y}=C5(a),b=S.useRef(null),C=Rs(b,i);return j.jsx(n,{onClick:x=>{var k;let{forceFocus:R,runAction:L}=ln({nativeEvent:x,defaultBehavior:{forceFocus:!0,runAction:!0},handler:l});if(R&&((k=b.current)===null||k===void 0||k.focus({preventScroll:!0})),L){if(p==="scroll-to")return g(v);if(p==="scroll-by")return y(v)}u?.(x)},ref:C,...d,children:h})});h6.displayName="Sheet.Trigger";let d6=t=>S.useContext(t??Za)||{},f6=We.forwardRef((t,e)=>{let{asChild:i,className:n,"data-silk":a,timing:l,forComponent:u,tabIndex:h,...f}=t,d=i?Fi:"div",{sheetId:p}=u?d6(u):{},v=p??"any",g=S.useCallback(L=>{qe.addAutoFocusTarget({layerId:v,element:L,timing:l})},[v,l]),y=S.useCallback(L=>{qe.removeAutoFocusTarget(L)},[]),b=S.useRef(null),C=S.useCallback(L=>{L?g(L):y(b.current),b.current=L},[g,y]),x=S.useRef(!0);S.useEffect(()=>(b.current&&!x.current&&g(b.current),x.current=!1,()=>{b.current&&y(b.current)}),[g,y]);let k=Rs(C,e),R=Ui("AutoFocusTarget",{});return j.jsx(d,{...R("root",[],{className:n,dataSilk:[a]}),...i||Number.isInteger(h)?{}:{tabIndex:-1},...f,ref:k})});f6.displayName="AutoFocusTarget.Root";let m6=We.forwardRef((t,e)=>{let{asChild:i,disabled:n=!1,children:a,contentGetter:l,forComponent:u,...h}=t,f=S.useRef(null),d=Rs(f,e),p=S.useMemo(()=>!!a,[a]);return S.useEffect(()=>{let v,g;if(!n){let y,b=!1;if(p&&f.current?y=f.current:l&&(y=_f(l))&&(b=!0),y){let C=Array.isArray(u)?u:u?[u]:[];v=qe.addIsland({componentId:C,element:y}),b&&(g=new MutationObserver(()=>{_f(l)||(qe.removeIsland(v),g.disconnect())})).observe(y.parentElement,{childList:!0})}}return()=>{n||(qe.removeIsland(v),g?.disconnect())}},[e,n,p,l,u]),a?j.jsx(Kn.Root,{active:!n,asChild:i,"data-silk":"0ag",...h,ref:d,children:a}):null});m6.displayName="Island.Root";let _6=We.forwardRef((t,e)=>{let{asChild:i,children:n,...a}=t;return j.jsx(Kn.Stabiliser,{asChild:i,...a,ref:e,children:n})});_6.displayName="Island.Content";let p6=We.forwardRef(({asChild:t,children:e,contentGetter:i,disabled:n=!1,selfManagedInertOutside:a=!0,...l},u)=>{let h=Vo(),f=S.useRef(null),d=Rs(f,u),p=t?Fi:"div",v=S.useMemo(()=>!!e,[e]);return vi(()=>{let g;if(!n&&h){let y,b=!1;v&&f.current?y=f.current:i&&(y=_f(i))&&(b=!0),y&&(qe.updateLayer({external:!0,layerId:h,viewElement:y,inertOutside:a,onPresentAutoFocus:{focus:!1},onDismissAutoFocus:{focus:!1},defaultClickOutsideAction:()=>{},defaultEscapeKeyDownAction:()=>{}}),b&&(g=new MutationObserver(()=>{_f(i)||(qe.removeLayer(h),g.disconnect())})).observe(y.parentElement,{childList:!0}))}return()=>{!n&&h&&(qe.removeLayer(h),g?.disconnect())}},[v,i,n,h,a]),j.jsx(p,{ref:d,...l,children:e})});p6.displayName="ExternalOverlay.Root";const k5=We.forwardRef(({children:t,license:e="commercial",...i},n)=>j.jsx(Qa.Root,{license:e,...i,ref:n,children:t}));k5.displayName="BottomSheet.Root";const E5=We.forwardRef(({children:t,className:e,...i},n)=>j.jsx(Qa.View,{className:`BottomSheet-view ${e??""}`.trim(),nativeEdgeSwipePrevention:!0,...i,ref:n,children:t}));E5.displayName="BottomSheet.View";const T5=We.forwardRef(({className:t,...e},i)=>j.jsx(Qa.Backdrop,{className:`BottomSheet-backdrop ${t??""}`.trim(),themeColorDimming:!1,...e,ref:i}));T5.displayName="BottomSheet.Backdrop";const R5=We.forwardRef(({children:t,className:e,...i},n)=>j.jsxs(Qa.Content,{className:`BottomSheet-content ${e??""}`.trim(),...i,ref:n,children:[j.jsx(Qa.BleedingBackground,{className:"BottomSheet-bleedingBackground"}),t]}));R5.displayName="BottomSheet.Content";const M5=We.forwardRef(({className:t,...e},i)=>j.jsx(Qa.Handle,{className:`BottomSheet-handle ${t??""}`.trim(),action:"dismiss",...e,ref:i}));M5.displayName="BottomSheet.Handle";const g6=Qa.Portal,v6=Qa.Trigger,Ia={Root:k5,Portal:g6,View:E5,Backdrop:T5,Content:R5,Trigger:v6,Handle:M5},y6=t=>{const e=t.status==="running"?"Running":"Closed",i=t.source==="tmux"?"Tmux":"Mock";return`${e} - ${i}`},S6=({terminals:t,activeTerminalId:e,listState:i,onSelectTerminal:n})=>{const a=i==="loading"?"Loading terminals":i==="error"?"Unable to load terminals":i==="empty"?"No terminals available":null;return j.jsxs(Ia.Root,{children:[j.jsx(Ia.Trigger,{asChild:!0,children:j.jsx(Od,{type:"button",variant:"secondary",size:"icon","aria-label":"Terminals",className:"h-11 w-11 rounded-full bg-input/50",children:j.jsx($O,{className:"size-4","aria-hidden":"true"})})}),j.jsx(Ia.Portal,{children:j.jsxs(Ia.View,{children:[j.jsx(Ia.Backdrop,{}),j.jsxs(Ia.Content,{className:"px-4 pb-[calc(env(safe-area-inset-bottom,0px)+16px)] pt-3",children:[j.jsx("div",{className:"flex items-center justify-center pb-3",children:j.jsx(Ia.Handle,{})}),j.jsxs("div",{className:"flex items-center justify-between px-1 pb-4",children:[j.jsxs("div",{className:"space-y-1",children:[j.jsx("p",{className:"text-xs font-semibold text-muted-foreground",children:"Terminals"}),j.jsx("p",{className:"text-sm font-medium text-foreground",children:"Select a session"})]}),i==="ready"?j.jsxs("span",{className:"text-xs text-muted-foreground",children:[t.length," total"]}):null]}),j.jsx("div",{className:"no-scrollbar max-h-[min(55vh,360px)] space-y-2 overflow-y-auto px-1",children:a?j.jsx("div",{className:"rounded-2xl border border-border/60 bg-background/70 px-4 py-5 text-sm text-muted-foreground",children:a}):t.map(l=>{const u=l.id===e,h=l.label||l.id;return j.jsx(Ia.Trigger,{asChild:!0,action:"dismiss",children:j.jsx("button",{type:"button","aria-label":`Open ${h}`,"aria-current":u?"page":void 0,className:`w-full rounded-2xl border px-4 py-3 text-left transition ${u?"border-primary/60 bg-primary/10 text-foreground":"border-transparent bg-background/70 text-foreground hover:bg-background/90"}`,onClick:()=>n(l.id),children:j.jsxs("div",{className:"flex items-center justify-between gap-3",children:[j.jsxs("div",{className:"min-w-0",children:[j.jsx("span",{className:"text-sm font-medium",children:h}),j.jsx("div",{className:"pt-1 text-xs text-muted-foreground",children:y6(l)})]}),u?j.jsx("span",{className:"flex h-8 w-8 items-center justify-center text-primary",children:j.jsx(DO,{className:"size-6","aria-hidden":"true"})}):null]})})},l.id)})})]})]})})]})},b6=({clientRef:t,terminals:e,activeTerminalId:i,listState:n,onSelectTerminal:a})=>{const l=S.useRef(null),[u,h]=S.useState(""),[f,d]=S.useState(!1),[p,v]=S.useState(!1),[g,y]=S.useState(!1);S.useEffect(()=>{if(!f){v(!1),y(!1);return}const k=l.current,R=()=>{const U=k.scrollWidth-k.clientWidth,I=k.scrollLeft>0,N=k.scrollLeft<U-1;v(I),y(U>0&&N)};R();const L=()=>R();k.addEventListener("scroll",L,{passive:!0});const E=window.ResizeObserver,H=typeof E=="function"?new E(R):null;return H?.observe(k),window.addEventListener("resize",R),()=>{k.removeEventListener("scroll",L),H?.disconnect(),window.removeEventListener("resize",R)}},[f]);const b=S.useMemo(()=>[{id:"enter",label:"Enter",kind:"input",data:"\r",icon:PO},{id:"up",label:"Up",kind:"control",key:"up",icon:AO},{id:"down",label:"Down",kind:"control",key:"down",icon:wO},{id:"left",label:"Left",kind:"control",key:"left",icon:xO},{id:"right",label:"Right",kind:"control",key:"right",icon:RO},{id:"tab",label:"Tab",kind:"control",key:"tab",icon:EO},{id:"esc",label:"Esc",kind:"control",key:"esc",icon:jO},{id:"ctrl-c",label:"Ctrl+C",kind:"control",key:"ctrl_c",icon:BO}],[]),C=()=>{const k=t.current;k&&(u&&k.sendInput(u),k.sendInput("\r"),h(""))},x=k=>{const R=t.current;if(R){if(k.kind==="input"){R.sendInput(k.data);return}R.sendControl(k.key)}};return j.jsxs("div",{className:"min-w-0",children:[f?j.jsx("div",{className:"pt-3",children:j.jsxs("div",{className:"relative",children:[j.jsx("div",{className:"h-auto",children:j.jsx("div",{ref:l,id:"terminal-actions","data-testid":"terminal-actions-scroll",className:"no-scrollbar flex h-full w-full items-center gap-2 overflow-x-auto px-3 snap-x snap-mandatory scroll-px-3",children:b.map(k=>{const R=k.icon;return j.jsxs(Od,{type:"button",variant:"secondary",size:"sm","aria-label":k.label,className:"bg-input/50 snap-start whitespace-nowrap px-3 py-2",onClick:()=>x(k),children:[j.jsx(R,{className:"size-4","aria-hidden":"true"}),j.jsx("span",{children:k.label})]},k.id)})})}),p?j.jsx("div",{"data-testid":"terminal-actions-gradient-left",className:"pointer-events-none absolute inset-y-0 left-0 z-10 w-16 bg-gradient-to-r from-background to-transparent"}):null,g?j.jsx("div",{"data-testid":"terminal-actions-gradient-right",className:"pointer-events-none absolute inset-y-0 right-0 z-10 w-16 bg-gradient-to-l from-background to-transparent"}):null]})}):null,j.jsx("div",{className:"px-3 py-3",children:j.jsxs("div",{className:"flex w-full min-w-0 items-center gap-3",children:[j.jsx(Od,{type:"button",variant:"secondary",size:"icon","aria-label":"Add","aria-expanded":f,"aria-controls":"terminal-actions",className:"h-11 w-11 rounded-full bg-input/50",onClick:()=>d(k=>!k),children:j.jsx(IO,{className:`size-4 transition-transform duration-200 ${f?"rotate-45":""}`})}),j.jsxs("div",{className:"relative flex-1 min-w-0",children:[j.jsx(OR,{placeholder:"Message","aria-label":"Message",className:"h-11 rounded-full border-none bg-input/50 pl-4 pr-8",value:u,onChange:k=>h(k.target.value),onKeyDown:k=>{k.key==="Enter"&&(k.preventDefault(),C())}}),j.jsx(Od,{type:"button",variant:"default",size:"sm","aria-label":"Send",className:"absolute right-1 top-1/2 h-9 w-9 -translate-y-1/2 rounded-full",onClick:C,children:j.jsx(UO,{className:"size-4"})})]}),j.jsx("div",{className:"-ml-1 flex-shrink-0",children:j.jsx(S6,{terminals:e,activeTerminalId:i,listState:n,onSelectTerminal:a})})]})})]})},w6=({activeView:t,onViewChange:e})=>j.jsx(lM,{value:t,onValueChange:i=>e(i),className:"px-3 py-2",children:j.jsxs(uM,{className:"w-full",children:[j.jsx(Ob,{value:"terminal",className:"flex-1",children:"Terminal"}),j.jsx(Ob,{value:"preview",className:"flex-1",children:"Preview"})]})}),A5=({terminalId:t,onSelectTerminal:e})=>{const i=S.useRef(null),n=S.useRef(null),[a,l]=S.useState("loading"),[u,h]=S.useState([]),[f,d]=S.useState(null),[p,v]=S.useState("connecting"),[g,y]=S.useState(null),[b,C]=S.useState(null),[x,k]=S.useState("terminal");S.useEffect(()=>{let I=!0;return(async()=>{try{const[F,J,G]=await Promise.all([fetch("/__tb/api/terminals"),fetch("/__tb/api/csrf"),fetch("/__tb/api/config")]);if(!F.ok||!J.ok||!G.ok)throw new Error("api request failed");const[D,O,P]=await Promise.all([F.json(),J.json(),G.json()]),$=D.terminals;if(!I)return;if(h($),d(O.csrfToken),y(P.proxyPort),C(P.devProxyUrl),$.length===0){l("empty");return}l("ready")}catch{I&&l("error")}})(),()=>{I=!1}},[]);const R=S.useMemo(()=>a!=="ready"||!t?null:u.some(I=>I.id===t)?t:null,[a,t,u]);S.useEffect(()=>{if(a!=="ready"||u.length===0||(t?u.some(F=>F.id===t):!1))return;const N=u[0]?.id;N&&e&&e(N)},[a,u,t,e]),S.useEffect(()=>{if(a!=="ready"||!R||!f||!i.current)return;v("connecting");const I=_O(i.current,R,f);n.current=I;const N=I.onConnectionStateChange(F=>{v(F)});return()=>{N(),n.current=null,I.destroy()}},[a,R,f]);const L=a==="loading"?"Loading terminal":a==="empty"?"No terminals available":a==="error"?"Unable to load terminals":R?null:"Loading terminal",E=e??(()=>{}),H=R?j.jsx("span",{className:`absolute right-2 top-2 z-10 inline-block h-2 w-2 rounded-full ${p==="connected"?"bg-green-500":p==="reconnecting"?"animate-pulse bg-yellow-500":p==="connecting"?"animate-pulse bg-blue-400":"bg-red-500"}`,"data-testid":"connection-indicator","data-state":p,"aria-label":p}):null,U=g!==null;return j.jsxs("div",{className:`relative grid h-full w-full grid-cols-1 bg-background text-foreground ${U?"grid-rows-[auto_minmax(0,1fr)_auto]":"grid-rows-[minmax(0,1fr)_auto]"}`,children:[U?j.jsx(w6,{activeView:x,onViewChange:k}):null,L&&x==="terminal"?j.jsx("div",{className:"terminal-status absolute inset-0 z-20 flex items-center justify-center bg-background/85 text-xs font-medium text-muted-foreground",role:"status",children:L}):null,x==="terminal"?H:null,j.jsxs("div",{className:"min-h-0 min-w-0 relative",children:[U?j.jsx("iframe",{src:b??"/",title:"Preview",className:`h-full w-full border-0 ${x==="preview"?"":"invisible absolute inset-0"}`,sandbox:"allow-same-origin allow-scripts allow-forms allow-popups","data-testid":"preview-iframe"}):null,j.jsx("div",{ref:i,className:`terminal-host h-full w-full ${x==="preview"?"invisible absolute inset-0":""}`,"data-testid":"terminal-host"})]}),j.jsx(b6,{clientRef:n,terminals:u,activeTerminalId:R,listState:a,onSelectTerminal:E})]})},C6=()=>{const t=Fu(),e=S.useCallback(i=>{t({to:"/terminal/$terminalId",params:{terminalId:i}})},[t]);return j.jsx(A5,{terminalId:null,onSelectTerminal:e})},x6=()=>{const t=Fu(),{terminalId:e}=wf({from:"/terminal/$terminalId"}),i=S.useCallback(n=>{t({to:"/terminal/$terminalId",params:{terminalId:n}})},[t]);return j.jsx(A5,{terminalId:e,onSelectTerminal:i})},Tv=PE({component:()=>j.jsx(Kw,{})}),k6=rv({getParentRoute:()=>Tv,path:"/",component:C6}),E6=rv({getParentRoute:()=>Tv,path:"/terminal/$terminalId",component:x6}),T6=Tv.addChildren([k6,E6]),R6=VE({routeTree:T6,basepath:"/__tb/app"}),L5="termbridge.theme",M6=()=>{if(typeof window>"u")return null;const t=window.localStorage.getItem(L5);return t==="light"||t==="dark"||t==="system"?t:null},A6=t=>{typeof window>"u"||window.localStorage.setItem(L5,t)},L6=()=>(hM({getStoredTheme:M6,setStoredTheme:A6}),j.jsx(KE,{router:R6})),D6=t=>{lk.createRoot(t).render(j.jsx(L6,{}))},ww=document.getElementById("root");ww&&D6(ww);
103
+ `}}),j.jsx(x5,{}),j.jsx(A,{...ji("view",["axis","pageScroll","skipScrollAnimation","scrollOngoing"],{className:a,dataSilk:[l]}),...F,ref:q,children:n})]})});c6.displayName="Scroll.View";let Kg=We.forwardRef(({side:t,...e},i)=>{let{axis:n}=S.useContext(Nf)||{},a=Ui("Scroll",{side:t,axis:n});return j.jsx("div",{...a("spy",["side","axis"]),ref:i,...e})});Kg.displayName="Scroll.Anonymous";let u6=We.forwardRef((t,e)=>{let{asChild:i,className:n,"data-silk":a,...l}=t,u=i?Fi:"div",{contentRef:h,styleAttributes:f,nativeFocusScrollPrevention:d,swipeTrapIncapable:p,focusable:v,axis:g,pageScroll:y,nativePageScrollReplacement:b,scrollContainerRef:C,swipeTrapObserverRequired:x,startScrollSpyRef:k,endScrollSpyRef:R,startSpacerRef:L,endSpacerRef:E,scrollHandler:H,scrollPadding:U,scrollTimelineName:I}=S.useContext(Nf)||{},N=Rs(h,e);return j.jsxs("div",{...f("scrollContainer",["axis","pageScroll","scrollTrapX","scrollTrapY","scrollGestureOvershoot","scrollDisabled","overflowX","overflowY","skipScrollAnimation","scrollAnchoring","scrollSnapType","nativeScrollbar"],{dataSilk:[a,"0ad",d&&"0ah",p&&"0ai"]}),style:{scrollPadding:U,scrollTimeline:I+" "+g},tabIndex:v?0:d?-1:void 0,role:y&&!b?void 0:"region",onScroll:H,ref:C,children:[x&&j.jsx(Kg,{side:"start",ref:k}),g==="y"&&j.jsx("div",{ref:L,...f("startSpacer",["axis","pageScroll"])}),j.jsx(u,{...f("content",["axis","scrollTrapX","scrollTrapY","overflowX","overflowY"],{className:n,dataSilk:[a]}),...l,ref:N}),g==="y"&&j.jsx("div",{ref:E,...f("endSpacer",["axis","pageScroll"])}),x&&j.jsx(Kg,{side:"end",ref:R})]})});u6.displayName="Scroll.Content";let h6=We.forwardRef(({asChild:t,...e},i)=>{let n=t?Fi:"button",{forComponent:a,onPress:l,onClick:u,children:h,action:f,...d}=e,{type:p,...v}=f||{},{scrollTo:g,scrollBy:y}=C5(a),b=S.useRef(null),C=Rs(b,i);return j.jsx(n,{onClick:x=>{var k;let{forceFocus:R,runAction:L}=ln({nativeEvent:x,defaultBehavior:{forceFocus:!0,runAction:!0},handler:l});if(R&&((k=b.current)===null||k===void 0||k.focus({preventScroll:!0})),L){if(p==="scroll-to")return g(v);if(p==="scroll-by")return y(v)}u?.(x)},ref:C,...d,children:h})});h6.displayName="Sheet.Trigger";let d6=t=>S.useContext(t??Za)||{},f6=We.forwardRef((t,e)=>{let{asChild:i,className:n,"data-silk":a,timing:l,forComponent:u,tabIndex:h,...f}=t,d=i?Fi:"div",{sheetId:p}=u?d6(u):{},v=p??"any",g=S.useCallback(L=>{qe.addAutoFocusTarget({layerId:v,element:L,timing:l})},[v,l]),y=S.useCallback(L=>{qe.removeAutoFocusTarget(L)},[]),b=S.useRef(null),C=S.useCallback(L=>{L?g(L):y(b.current),b.current=L},[g,y]),x=S.useRef(!0);S.useEffect(()=>(b.current&&!x.current&&g(b.current),x.current=!1,()=>{b.current&&y(b.current)}),[g,y]);let k=Rs(C,e),R=Ui("AutoFocusTarget",{});return j.jsx(d,{...R("root",[],{className:n,dataSilk:[a]}),...i||Number.isInteger(h)?{}:{tabIndex:-1},...f,ref:k})});f6.displayName="AutoFocusTarget.Root";let m6=We.forwardRef((t,e)=>{let{asChild:i,disabled:n=!1,children:a,contentGetter:l,forComponent:u,...h}=t,f=S.useRef(null),d=Rs(f,e),p=S.useMemo(()=>!!a,[a]);return S.useEffect(()=>{let v,g;if(!n){let y,b=!1;if(p&&f.current?y=f.current:l&&(y=_f(l))&&(b=!0),y){let C=Array.isArray(u)?u:u?[u]:[];v=qe.addIsland({componentId:C,element:y}),b&&(g=new MutationObserver(()=>{_f(l)||(qe.removeIsland(v),g.disconnect())})).observe(y.parentElement,{childList:!0})}}return()=>{n||(qe.removeIsland(v),g?.disconnect())}},[e,n,p,l,u]),a?j.jsx(Kn.Root,{active:!n,asChild:i,"data-silk":"0ag",...h,ref:d,children:a}):null});m6.displayName="Island.Root";let _6=We.forwardRef((t,e)=>{let{asChild:i,children:n,...a}=t;return j.jsx(Kn.Stabiliser,{asChild:i,...a,ref:e,children:n})});_6.displayName="Island.Content";let p6=We.forwardRef(({asChild:t,children:e,contentGetter:i,disabled:n=!1,selfManagedInertOutside:a=!0,...l},u)=>{let h=Vo(),f=S.useRef(null),d=Rs(f,u),p=t?Fi:"div",v=S.useMemo(()=>!!e,[e]);return vi(()=>{let g;if(!n&&h){let y,b=!1;v&&f.current?y=f.current:i&&(y=_f(i))&&(b=!0),y&&(qe.updateLayer({external:!0,layerId:h,viewElement:y,inertOutside:a,onPresentAutoFocus:{focus:!1},onDismissAutoFocus:{focus:!1},defaultClickOutsideAction:()=>{},defaultEscapeKeyDownAction:()=>{}}),b&&(g=new MutationObserver(()=>{_f(i)||(qe.removeLayer(h),g.disconnect())})).observe(y.parentElement,{childList:!0}))}return()=>{!n&&h&&(qe.removeLayer(h),g?.disconnect())}},[v,i,n,h,a]),j.jsx(p,{ref:d,...l,children:e})});p6.displayName="ExternalOverlay.Root";const k5=We.forwardRef(({children:t,license:e="commercial",...i},n)=>j.jsx(Qa.Root,{license:e,...i,ref:n,children:t}));k5.displayName="BottomSheet.Root";const E5=We.forwardRef(({children:t,className:e,...i},n)=>j.jsx(Qa.View,{className:`BottomSheet-view ${e??""}`.trim(),nativeEdgeSwipePrevention:!0,...i,ref:n,children:t}));E5.displayName="BottomSheet.View";const T5=We.forwardRef(({className:t,...e},i)=>j.jsx(Qa.Backdrop,{className:`BottomSheet-backdrop ${t??""}`.trim(),themeColorDimming:!1,...e,ref:i}));T5.displayName="BottomSheet.Backdrop";const R5=We.forwardRef(({children:t,className:e,...i},n)=>j.jsxs(Qa.Content,{className:`BottomSheet-content ${e??""}`.trim(),...i,ref:n,children:[j.jsx(Qa.BleedingBackground,{className:"BottomSheet-bleedingBackground"}),t]}));R5.displayName="BottomSheet.Content";const M5=We.forwardRef(({className:t,...e},i)=>j.jsx(Qa.Handle,{className:`BottomSheet-handle ${t??""}`.trim(),action:"dismiss",...e,ref:i}));M5.displayName="BottomSheet.Handle";const g6=Qa.Portal,v6=Qa.Trigger,Ia={Root:k5,Portal:g6,View:E5,Backdrop:T5,Content:R5,Trigger:v6,Handle:M5},y6=t=>{const e=t.status==="running"?"Running":"Closed",i=t.source==="tmux"?"Tmux":"Mock";return`${e} - ${i}`},S6=({terminals:t,activeTerminalId:e,listState:i,onSelectTerminal:n})=>{const a=i==="loading"?"Loading terminals":i==="error"?"Unable to load terminals":i==="empty"?"No terminals available":null;return j.jsxs(Ia.Root,{children:[j.jsx(Ia.Trigger,{asChild:!0,children:j.jsx(Od,{type:"button",variant:"secondary",size:"icon","aria-label":"Terminals",className:"h-11 w-11 rounded-full bg-input/50",children:j.jsx($O,{className:"size-4","aria-hidden":"true"})})}),j.jsx(Ia.Portal,{children:j.jsxs(Ia.View,{children:[j.jsx(Ia.Backdrop,{}),j.jsxs(Ia.Content,{className:"px-4 pb-[calc(env(safe-area-inset-bottom,0px)+16px)] pt-3",children:[j.jsx("div",{className:"flex items-center justify-center pb-3",children:j.jsx(Ia.Handle,{})}),j.jsxs("div",{className:"flex items-center justify-between px-1 pb-4",children:[j.jsxs("div",{className:"space-y-1",children:[j.jsx("p",{className:"text-xs font-semibold text-muted-foreground",children:"Terminals"}),j.jsx("p",{className:"text-sm font-medium text-foreground",children:"Select a session"})]}),i==="ready"?j.jsxs("span",{className:"text-xs text-muted-foreground",children:[t.length," total"]}):null]}),j.jsx("div",{className:"no-scrollbar max-h-[min(55vh,360px)] space-y-2 overflow-y-auto px-1",children:a?j.jsx("div",{className:"rounded-2xl border border-border/60 bg-background/70 px-4 py-5 text-sm text-muted-foreground",children:a}):t.map(l=>{const u=l.id===e,h=l.label||l.id;return j.jsx(Ia.Trigger,{asChild:!0,action:"dismiss",children:j.jsx("button",{type:"button","aria-label":`Open ${h}`,"aria-current":u?"page":void 0,className:`w-full rounded-2xl border px-4 py-3 text-left transition ${u?"border-primary/60 bg-primary/10 text-foreground":"border-transparent bg-background/70 text-foreground hover:bg-background/90"}`,onClick:()=>n(l.id),children:j.jsxs("div",{className:"flex items-center justify-between gap-3",children:[j.jsxs("div",{className:"min-w-0",children:[j.jsx("span",{className:"text-sm font-medium",children:h}),j.jsx("div",{className:"pt-1 text-xs text-muted-foreground",children:y6(l)})]}),u?j.jsx("span",{className:"flex h-8 w-8 items-center justify-center text-primary",children:j.jsx(DO,{className:"size-6","aria-hidden":"true"})}):null]})})},l.id)})})]})]})})]})},b6=({clientRef:t,terminals:e,activeTerminalId:i,listState:n,onSelectTerminal:a})=>{const l=S.useRef(null),[u,h]=S.useState(""),[f,d]=S.useState(!1),[p,v]=S.useState(!1),[g,y]=S.useState(!1);S.useEffect(()=>{if(!f){v(!1),y(!1);return}const k=l.current,R=()=>{const U=k.scrollWidth-k.clientWidth,I=k.scrollLeft>0,N=k.scrollLeft<U-1;v(I),y(U>0&&N)};R();const L=()=>R();k.addEventListener("scroll",L,{passive:!0});const E=window.ResizeObserver,H=typeof E=="function"?new E(R):null;return H?.observe(k),window.addEventListener("resize",R),()=>{k.removeEventListener("scroll",L),H?.disconnect(),window.removeEventListener("resize",R)}},[f]);const b=S.useMemo(()=>[{id:"enter",label:"Enter",kind:"input",data:"\r",icon:PO},{id:"up",label:"Up",kind:"control",key:"up",icon:AO},{id:"down",label:"Down",kind:"control",key:"down",icon:wO},{id:"left",label:"Left",kind:"control",key:"left",icon:xO},{id:"right",label:"Right",kind:"control",key:"right",icon:RO},{id:"tab",label:"Tab",kind:"control",key:"tab",icon:EO},{id:"esc",label:"Esc",kind:"control",key:"esc",icon:jO},{id:"ctrl-c",label:"Ctrl+C",kind:"control",key:"ctrl_c",icon:BO}],[]),C=()=>{const k=t.current;k&&(u&&k.sendInput(u),k.sendInput("\r"),h(""))},x=k=>{const R=t.current;if(R){if(k.kind==="input"){R.sendInput(k.data);return}R.sendControl(k.key)}};return j.jsxs("div",{className:"min-w-0",children:[f?j.jsx("div",{className:"pt-3",children:j.jsxs("div",{className:"relative",children:[j.jsx("div",{className:"h-auto",children:j.jsx("div",{ref:l,id:"terminal-actions","data-testid":"terminal-actions-scroll",className:"no-scrollbar flex h-full w-full items-center gap-2 overflow-x-auto px-3 snap-x snap-mandatory scroll-px-3",children:b.map(k=>{const R=k.icon;return j.jsxs(Od,{type:"button",variant:"secondary",size:"sm","aria-label":k.label,className:"bg-input/50 snap-start whitespace-nowrap px-3 py-2",onClick:()=>x(k),children:[j.jsx(R,{className:"size-4","aria-hidden":"true"}),j.jsx("span",{children:k.label})]},k.id)})})}),p?j.jsx("div",{"data-testid":"terminal-actions-gradient-left",className:"pointer-events-none absolute inset-y-0 left-0 z-10 w-16 bg-gradient-to-r from-background to-transparent"}):null,g?j.jsx("div",{"data-testid":"terminal-actions-gradient-right",className:"pointer-events-none absolute inset-y-0 right-0 z-10 w-16 bg-gradient-to-l from-background to-transparent"}):null]})}):null,j.jsx("div",{className:"px-3 py-3",children:j.jsxs("div",{className:"flex w-full min-w-0 items-center gap-3",children:[j.jsx(Od,{type:"button",variant:"secondary",size:"icon","aria-label":"Add","aria-expanded":f,"aria-controls":"terminal-actions",className:"h-11 w-11 rounded-full bg-input/50",onClick:()=>d(k=>!k),children:j.jsx(IO,{className:`size-4 transition-transform duration-200 ${f?"rotate-45":""}`})}),j.jsxs("div",{className:"relative flex-1 min-w-0",children:[j.jsx(OR,{placeholder:"Message","aria-label":"Message",className:"h-11 rounded-full border-none bg-input/50 pl-4 pr-8",value:u,onChange:k=>h(k.target.value),onKeyDown:k=>{k.key==="Enter"&&(k.preventDefault(),C())}}),j.jsx(Od,{type:"button",variant:"default",size:"sm","aria-label":"Send",className:"absolute right-1 top-1/2 h-9 w-9 -translate-y-1/2 rounded-full",onClick:C,children:j.jsx(UO,{className:"size-4"})})]}),j.jsx("div",{className:"-ml-1 flex-shrink-0",children:j.jsx(S6,{terminals:e,activeTerminalId:i,listState:n,onSelectTerminal:a})})]})})]})},w6=({activeView:t,onViewChange:e})=>j.jsx(lM,{value:t,onValueChange:i=>e(i),className:"px-3 py-2",children:j.jsxs(uM,{className:"w-full",children:[j.jsx(Ob,{value:"terminal",className:"flex-1",children:"Terminal"}),j.jsx(Ob,{value:"preview",className:"flex-1",children:"Preview"})]})}),A5=({terminalId:t,onSelectTerminal:e})=>{const i=S.useRef(null),n=S.useRef(null),[a,l]=S.useState("loading"),[u,h]=S.useState([]),[f,d]=S.useState(null),[p,v]=S.useState("connecting"),[g,y]=S.useState(null),[b,C]=S.useState(null),[x,k]=S.useState("terminal");S.useEffect(()=>{let I=!0;return(async()=>{try{const[F,J,G]=await Promise.all([fetch("/__tb/api/terminals"),fetch("/__tb/api/csrf"),fetch("/__tb/api/config")]);if(!F.ok||!J.ok||!G.ok)throw new Error("api request failed");const[D,O,P]=await Promise.all([F.json(),J.json(),G.json()]),$=D.terminals;if(!I)return;if(h($),d(O.csrfToken),y(P.proxyPort),C(P.devProxyUrl),$.length===0){l("empty");return}l("ready")}catch{I&&l("error")}})(),()=>{I=!1}},[]);const R=S.useMemo(()=>a!=="ready"||!t?null:u.some(I=>I.id===t)?t:null,[a,t,u]);S.useEffect(()=>{if(a!=="ready"||u.length===0||(t?u.some(F=>F.id===t):!1))return;const N=u[0]?.id;N&&e&&e(N)},[a,u,t,e]),S.useEffect(()=>{if(a!=="ready"||!R||!f||!i.current)return;v("connecting");const I=_O(i.current,R,f);n.current=I;const N=I.onConnectionStateChange(F=>{v(F)});return()=>{N(),n.current=null,I.destroy()}},[a,R,f]);const L=a==="loading"?"Loading terminal":a==="empty"?"No terminals available":a==="error"?"Unable to load terminals":R?null:"Loading terminal",E=e??(()=>{}),H=R?j.jsx("span",{role:"status",className:`absolute right-2 top-2 z-10 inline-block h-2 w-2 rounded-full ${p==="connected"?"bg-green-500":p==="reconnecting"?"animate-pulse bg-yellow-500":p==="connecting"?"animate-pulse bg-blue-400":"bg-red-500"}`,"data-testid":"connection-indicator","data-state":p,"aria-label":p}):null,U=g!==null;return j.jsxs("div",{className:`relative grid h-full w-full grid-cols-1 bg-background text-foreground ${U?"grid-rows-[auto_minmax(0,1fr)_auto]":"grid-rows-[minmax(0,1fr)_auto]"}`,children:[U?j.jsx(w6,{activeView:x,onViewChange:k}):null,L&&x==="terminal"?j.jsx("div",{className:"terminal-status absolute inset-0 z-20 flex items-center justify-center bg-background/85 text-xs font-medium text-muted-foreground",role:"status",children:L}):null,x==="terminal"?H:null,j.jsxs("div",{className:"min-h-0 min-w-0 relative",children:[U?j.jsx("iframe",{src:b??"/",title:"Preview",className:`h-full w-full border-0 ${x==="preview"?"":"invisible absolute inset-0"}`,sandbox:"allow-same-origin allow-scripts allow-forms allow-popups","data-testid":"preview-iframe"}):null,j.jsx("div",{ref:i,className:`terminal-host h-full w-full ${x==="preview"?"invisible absolute inset-0":""}`,"data-testid":"terminal-host"})]}),j.jsx(b6,{clientRef:n,terminals:u,activeTerminalId:R,listState:a,onSelectTerminal:E})]})},C6=()=>{const t=Fu(),e=S.useCallback(i=>{t({to:"/terminal/$terminalId",params:{terminalId:i}})},[t]);return j.jsx(A5,{terminalId:null,onSelectTerminal:e})},x6=()=>{const t=Fu(),{terminalId:e}=wf({from:"/terminal/$terminalId"}),i=S.useCallback(n=>{t({to:"/terminal/$terminalId",params:{terminalId:n}})},[t]);return j.jsx(A5,{terminalId:e,onSelectTerminal:i})},Tv=PE({component:()=>j.jsx(Kw,{})}),k6=rv({getParentRoute:()=>Tv,path:"/",component:C6}),E6=rv({getParentRoute:()=>Tv,path:"/terminal/$terminalId",component:x6}),T6=Tv.addChildren([k6,E6]),R6=VE({routeTree:T6,basepath:"/__tb/app"}),L5="termbridge.theme",M6=()=>{if(typeof window>"u")return null;const t=window.localStorage.getItem(L5);return t==="light"||t==="dark"||t==="system"?t:null},A6=t=>{typeof window>"u"||window.localStorage.setItem(L5,t)},L6=()=>(hM({getStoredTheme:M6,setStoredTheme:A6}),j.jsx(KE,{router:R6})),D6=t=>{lk.createRoot(t).render(j.jsx(L6,{}))},ww=document.getElementById("root");ww&&D6(ww);
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Termbridge</title>
7
- <script type="module" crossorigin src="/__tb/app/assets/index-JYV1Fxtz.js"></script>
7
+ <script type="module" crossorigin src="/__tb/app/assets/index-CI7cgRhu.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/__tb/app/assets/index-B6CAkCwJ.css">
9
9
  </head>
10
10
  <body>