termbeam 1.11.0 → 1.12.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/package.json +5 -4
- package/public-react/assets/index-CTOb8Hzh.js +108 -0
- package/public-react/assets/index-DCDkIi8X.css +32 -0
- package/public-react/index.html +27 -0
- package/public-react/manifest.webmanifest +1 -0
- package/public-react/registerSW.js +1 -0
- package/public-react/sw.js +2 -0
- package/src/routes.js +24 -22
- package/src/server.js +7 -2
- package/src/sessions.js +9 -3
- package/src/version.js +23 -5
- package/src/websocket.js +65 -8
- package/public/css/themes.css +0 -217
- package/public/index.html +0 -1521
- package/public/js/keybar.js +0 -180
- package/public/js/search.js +0 -95
- package/public/js/shared.js +0 -39
- package/public/js/terminal-themes.js +0 -291
- package/public/js/themes.js +0 -54
- package/public/manifest.json +0 -14
- package/public/sw.js +0 -88
- package/public/terminal.html +0 -4744
- /package/{public → public-react}/icons/icon-192.png +0 -0
- /package/{public → public-react}/icons/icon-512.png +0 -0
- /package/{public → public-react}/icons/icon.svg +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
._backdrop_barei_1{display:flex;align-items:center;justify-content:center;min-height:100vh;background:var(--bg);padding:1rem}._card_barei_10{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:2.5rem 2rem;width:100%;max-width:360px;display:flex;flex-direction:column;align-items:center;gap:1.5rem;animation:_fade-in_barei_1 .3s ease}._logo_barei_24{font-size:1.5rem;font-weight:700;color:var(--text);display:flex;align-items:center;gap:.5rem;-webkit-user-select:none;user-select:none}._logoIcon_barei_34{font-size:1.75rem}._form_barei_38{width:100%;display:flex;flex-direction:column;gap:.75rem}._input_barei_45{width:100%;padding:.65rem .85rem;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:8px;font-size:.95rem;outline:none;transition:border-color .15s;box-sizing:border-box}._input_barei_45:focus{border-color:var(--accent)}._button_barei_62{width:100%;padding:.65rem;background:var(--accent);color:var(--bg);border:none;border-radius:8px;font-size:.95rem;font-weight:600;cursor:pointer;transition:opacity .15s,transform .1s}._button_barei_62:hover:not(:disabled){opacity:.9}._button_barei_62:active:not(:disabled){transform:scale(.98)}._button_barei_62:disabled{opacity:.6;cursor:not-allowed}._error_barei_90{color:var(--danger);font-size:.85rem;text-align:center;margin:0}._banner_1ribv_1{display:flex;align-items:center;justify-content:center;gap:.75rem;padding:.55rem 1rem;background:var(--accent);color:var(--bg);font-size:.82rem;font-weight:500;animation:_slide-down_1ribv_1 .25s ease}._text_1ribv_14{flex:1;text-align:center}._dismiss_1ribv_19{background:transparent;border:none;color:var(--bg);font-size:1rem;cursor:pointer;opacity:.7;padding:.15rem .35rem;border-radius:4px;transition:opacity .15s;line-height:1}._dismiss_1ribv_19:hover{opacity:1}@keyframes _slide-down_1ribv_1{0%{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}._wrapper_18ism_1{position:relative;overflow:hidden;border-radius:12px}._deleteBackground_18ism_7{position:absolute;top:0;right:0;bottom:0;width:80px;background:var(--danger);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;border-radius:0 12px 12px 0;color:#fff;font-size:12px;font-weight:600;-webkit-user-select:none;user-select:none;cursor:pointer;border:none;padding:0;transition:background .15s}._deleteBackground_18ism_7:active{background:#c0392b}._card_18ism_34{position:relative;display:flex;flex-direction:column;gap:8px;padding:16px;background:var(--surface);border:1px solid var(--border);border-radius:12px;touch-action:pan-y;-webkit-user-select:none;user-select:none;z-index:1;transition:border-color .15s}._card_18ism_34:hover{border-color:var(--accent)}._topRow_18ism_54{display:flex;align-items:center;justify-content:space-between;gap:8px}._nameGroup_18ism_61{display:flex;align-items:center;gap:8px;min-width:0}._colorDot_18ism_68{width:10px;height:10px;border-radius:50%;flex-shrink:0}._name_18ism_61{font-size:17px;font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._pidBadge_18ism_84{font-size:12px;color:var(--text-secondary);background:var(--bg);padding:2px 8px;border-radius:4px;white-space:nowrap;flex-shrink:0}._detailsRow_18ism_95{display:flex;flex-wrap:wrap;gap:6px 16px;font-size:13px;color:var(--text-secondary)}._detailItem_18ism_103{display:inline-flex;align-items:center;gap:4px;white-space:nowrap}._detailItem_18ism_103 svg{flex-shrink:0}._gitRow_18ism_115{display:flex;flex-wrap:wrap;gap:4px 10px;font-size:12px}._gitBadge_18ism_122{display:inline-flex;align-items:center;gap:4px;background:var(--bg);padding:2px 8px;border-radius:4px;color:var(--text-secondary);white-space:nowrap}._gitBadge_18ism_122 svg{flex-shrink:0}._statusClean_18ism_137{color:var(--success)}._statusDirty_18ism_141{color:var(--danger)}._connectBtn_18ism_146{align-self:flex-end;background:var(--accent);color:#fff;border:none;border-radius:8px;padding:8px 20px;font-size:14px;font-weight:600;cursor:pointer;transition:opacity .15s,transform .15s}._connectBtn_18ism_146:hover{opacity:.9}._connectBtn_18ism_146:active{transform:scale(.97)}@media(pointer:coarse){._connectBtn_18ism_146{padding:10px 24px}}._container_tkgsa_1{display:flex;flex-direction:column;min-height:0;flex:1;overflow:hidden}._breadcrumb_tkgsa_9{display:flex;align-items:center;flex-wrap:wrap;gap:.15rem;font-size:.8rem;color:var(--text-secondary);padding:.4rem .6rem;background:var(--bg);border-radius:6px;border:1px solid var(--border-subtle);min-height:2rem;flex-shrink:0}._breadcrumbSegment_tkgsa_24{background:transparent;border:none;color:var(--accent);cursor:pointer;padding:.1rem .2rem;border-radius:3px;font-size:.8rem;transition:background .1s}._breadcrumbSegment_tkgsa_24:hover{background:var(--surface)}._breadcrumbSep_tkgsa_39{color:var(--text-muted);-webkit-user-select:none;user-select:none}._list_tkgsa_44{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:2px;padding:.25rem;background:var(--bg);border-radius:8px;border:1px solid var(--border-subtle);margin:.5rem 0}._entry_tkgsa_58{display:flex;align-items:center;gap:.5rem;padding:.4rem .6rem;border-radius:6px;border:none;background:transparent;color:var(--text);font-size:.82rem;cursor:pointer;text-align:left;width:100%;transition:background .1s}._entry_tkgsa_58:hover{background:var(--surface)}._entryIcon_tkgsa_78{flex-shrink:0;font-size:.9rem}._entryName_tkgsa_83{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._fileEntry_tkgsa_89{color:var(--text-dim);cursor:default}._fileEntry_tkgsa_89:hover{background:transparent}._actions_tkgsa_98{display:flex;gap:.5rem;justify-content:flex-end;flex-shrink:0;padding-top:.5rem}._selectBtn_tkgsa_106{padding:.45rem 1rem;background:var(--accent);color:var(--bg);border:none;border-radius:8px;font-size:.85rem;font-weight:600;cursor:pointer;transition:opacity .15s}._selectBtn_tkgsa_106:hover{opacity:.9}._selectBtn_tkgsa_106:disabled{opacity:.6;cursor:not-allowed}._cancelBtn_tkgsa_127{padding:.45rem 1rem;background:transparent;color:var(--text-secondary);border:1px solid var(--border);border-radius:8px;font-size:.85rem;cursor:pointer;transition:border-color .15s,color .15s}._cancelBtn_tkgsa_127:hover{border-color:var(--text-secondary);color:var(--text)}._loading_tkgsa_145{display:flex;align-items:center;justify-content:center;padding:2rem;color:var(--text-dim);font-size:.85rem}._error_tkgsa_154{color:var(--danger);font-size:.82rem;text-align:center;padding:1rem}._overlay_qhgrm_1{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--overlay-bg);z-index:200;animation:_fade-in_qhgrm_1 .15s ease}._content_qhgrm_9{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90%;max-width:440px;max-height:85dvh;overflow-y:auto;background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:1.5rem;z-index:201;box-shadow:0 8px 24px #0006;animation:_fade-in_qhgrm_1 .2s ease}._title_qhgrm_27{font-size:1.15rem;font-weight:700;color:var(--text);margin:0 0 1.25rem}._form_qhgrm_34{display:flex;flex-direction:column;gap:1rem}._field_qhgrm_40{display:flex;flex-direction:column;gap:.3rem}._label_qhgrm_46{font-size:.78rem;color:var(--text-secondary);font-weight:500;text-transform:uppercase;letter-spacing:.03em}._input_qhgrm_54{padding:.55rem .75rem;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:8px;font-size:.9rem;outline:none;transition:border-color .15s;width:100%;box-sizing:border-box}._input_qhgrm_54:focus{border-color:var(--accent)}._select_qhgrm_71{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23888' d='M2 4l4 4 4-4'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right .6rem center;padding-right:2rem}._dirRow_qhgrm_81{display:flex;gap:.5rem}._dirRow_qhgrm_81 ._input_qhgrm_54{flex:1}._browseBtn_qhgrm_90{padding:.55rem .85rem;background:var(--surface);color:var(--text-secondary);border:1px solid var(--border);border-radius:8px;font-size:.82rem;cursor:pointer;white-space:nowrap;transition:border-color .15s,color .15s}._browseBtn_qhgrm_90:hover{border-color:var(--accent);color:var(--text)}._colorPicker_qhgrm_109{display:flex;gap:.5rem;flex-wrap:wrap}._colorDot_qhgrm_115{width:28px;height:28px;border-radius:50%;border:2px solid transparent;cursor:pointer;transition:border-color .15s,transform .1s}._colorDot_qhgrm_115:hover{transform:scale(1.15)}._colorDotActive_qhgrm_130{border-color:var(--text)}._actions_qhgrm_134{display:flex;gap:.5rem;justify-content:flex-end;margin-top:.5rem}._cancelBtn_qhgrm_141{padding:.55rem 1rem;background:transparent;color:var(--text-secondary);border:1px solid var(--border);border-radius:8px;font-size:.88rem;cursor:pointer;transition:border-color .15s,color .15s}._cancelBtn_qhgrm_141:hover{border-color:var(--text-secondary);color:var(--text)}._submitBtn_qhgrm_159{padding:.55rem 1.25rem;background:var(--accent);color:var(--bg);border:none;border-radius:8px;font-size:.88rem;font-weight:600;cursor:pointer;transition:opacity .15s}._submitBtn_qhgrm_159:hover:not(:disabled){opacity:.9}._submitBtn_qhgrm_159:disabled{opacity:.6;cursor:not-allowed}@media(pointer:coarse){._content_qhgrm_9{top:30%;transform:translate(-50%,-30%)}}._page_3cz84_1{min-height:100vh;min-height:100dvh;background:var(--bg);display:flex;flex-direction:column}._header_3cz84_9{position:relative;padding:20px 16px 12px;border-bottom:1px solid var(--border);text-align:center}._title_3cz84_16{font-size:22px;font-weight:700;color:var(--text);margin:0}._accent_3cz84_23{color:var(--accent)}._tagline_3cz84_27{font-size:13px;color:var(--text-secondary);margin:4px 0 0}._headerBtn_3cz84_33{position:absolute;top:16px;display:flex;align-items:center;justify-content:center;width:32px;height:32px;background:none;border:1px solid var(--border);border-radius:8px;color:var(--text-dim);cursor:pointer;transition:border-color .15s,color .15s;padding:0}._headerBtn_3cz84_33:hover{border-color:var(--accent);color:var(--text)}._shareBtn_3cz84_57{right:96px}._refreshBtn_3cz84_61{right:56px}._themeWrap_3cz84_65{position:absolute;top:16px;right:16px}._themeBtn_3cz84_71{position:static}._themeDropdown_3cz84_75{position:absolute;top:calc(100% + 6px);right:0;min-width:180px;background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:.35rem;z-index:100;box-shadow:0 8px 24px #00000059;animation:_themePickerFadeIn_3cz84_1 .15s ease}@keyframes _themePickerFadeIn_3cz84_1{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}._themeOption_3cz84_100{display:flex;align-items:center;gap:.5rem;padding:.45rem .6rem;border-radius:6px;border:none;background:transparent;color:var(--text);font-size:.82rem;cursor:pointer;width:100%;text-align:left;transition:background .1s}._themeOption_3cz84_100:hover{background:var(--bg)}._themeOptionActive_3cz84_120{color:var(--accent);font-weight:600}._themeSwatch_3cz84_125{width:14px;height:14px;border-radius:4px;border:1px solid var(--border-subtle, rgba(255, 255, 255, .1));flex-shrink:0}._themeCheck_3cz84_133{margin-left:auto;font-size:.75rem;color:var(--accent)}._content_3cz84_139{flex:1;overflow-y:auto}._sessionsList_3cz84_144{display:flex;flex-direction:column;padding:16px;padding-bottom:calc(80px + env(safe-area-inset-bottom,0px));gap:12px}._emptyState_3cz84_152{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;padding:4rem 1rem;color:var(--text-dim);text-align:center}._emptyIcon_3cz84_163{font-size:2.5rem;opacity:.5}._emptyText_3cz84_168{font-size:.95rem}._emptyHint_3cz84_172{font-size:.82rem;color:var(--text-muted)}._newSessionBtn_3cz84_177{position:fixed;bottom:calc(16px + env(safe-area-inset-bottom,0px));left:calc(16px + env(safe-area-inset-left,0px));right:calc(16px + env(safe-area-inset-right,0px));padding:14px;background:var(--accent);color:#fff;border:none;border-radius:12px;font-size:15px;font-weight:600;cursor:pointer;box-shadow:0 2px 8px #0078d44d;z-index:50;transition:transform .15s,opacity .15s;text-align:center}._newSessionBtn_3cz84_177:hover{opacity:.92}._newSessionBtn_3cz84_177:active{transform:scale(.98)}._refreshSpin_3cz84_206{animation:_spin_3cz84_1 .6s linear}@keyframes _spin_3cz84_1{to{transform:rotate(360deg)}}/**
|
|
2
|
+
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
|
3
|
+
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
|
4
|
+
* https://github.com/chjj/term.js
|
|
5
|
+
* @license MIT
|
|
6
|
+
*
|
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
* furnished to do so, subject to the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
|
15
|
+
* all copies or substantial portions of the Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
+
* THE SOFTWARE.
|
|
24
|
+
*
|
|
25
|
+
* Originally forked from (with the author's permission):
|
|
26
|
+
* Fabrice Bellard's javascript vt100 for jslinux:
|
|
27
|
+
* http://bellard.org/jslinux/
|
|
28
|
+
* Copyright (c) 2011 Fabrice Bellard
|
|
29
|
+
* The original design remains. The terminal itself
|
|
30
|
+
* has been extended to include xterm CSI codes, among
|
|
31
|
+
* other features.
|
|
32
|
+
*/.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.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{-webkit-user-select:text;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}._pane_r8ota_1{flex:1;position:relative;overflow:hidden;min-height:0}._terminalContainer_r8ota_8{width:100%;height:100%}._scrollToBottom_r8ota_13{position:absolute;bottom:12px;right:12px;width:36px;height:36px;border-radius:50%;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:.8;transition:opacity .2s;z-index:10}._scrollToBottom_r8ota_13:hover{opacity:1}._exitOverlay_r8ota_37{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:var(--overlay-bg, rgba(0, 0, 0, .6));z-index:5}._exitMessage_r8ota_47{color:var(--text-secondary);font-size:1.1rem;padding:1rem 2rem;background:var(--surface);border-radius:8px;border:1px solid var(--border)}._reconnectOverlay_r8ota_57{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:var(--overlay-bg, rgba(0, 0, 0, .6));z-index:5;animation:_fadeIn_r8ota_1 .2s ease}@keyframes _fadeIn_r8ota_1{0%{opacity:0}to{opacity:1}}._reconnectContent_r8ota_77{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1.5rem 2rem;background:var(--surface);border-radius:12px;border:1px solid var(--border)}._reconnectMessage_r8ota_88{color:var(--text-secondary);font-size:1.1rem}._reconnectActions_r8ota_93{display:flex;gap:.75rem}._reconnectBtn_r8ota_98{padding:.5rem 1.25rem;border-radius:8px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:.9rem;cursor:pointer;text-decoration:none;transition:background .15s,border-color .15s}._reconnectBtn_r8ota_98:hover{background:var(--border)}._tabBar_c6cms_1{display:flex;align-items:center;gap:2px;padding:0 8px;height:38px;background:var(--surface);border-bottom:1px solid var(--border);flex-shrink:0;overflow:hidden}._tabBarInline_c6cms_13{flex:1;display:flex;align-items:center;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;gap:2px;padding:0 4px;height:100%}._tabBarInline_c6cms_13::-webkit-scrollbar{display:none}@media(max-width:640px){._tabBar_c6cms_1{display:none}}@media(pointer:coarse){._addBtnLabel_c6cms_37{display:none}}._tabScroller_c6cms_42{display:flex;align-items:center;gap:2px;overflow-x:auto;overflow-y:hidden;flex:1;min-width:0;scrollbar-width:none}._tabScroller_c6cms_42::-webkit-scrollbar{display:none}._tab_c6cms_1{position:relative;display:flex;align-items:center;gap:6px;padding:4px 10px;border-radius:6px;cursor:grab;white-space:nowrap;-webkit-user-select:none;user-select:none;font-size:12px;font-weight:500;color:var(--text-dim);transition:background .15s,color .15s;border-left:3px solid transparent;flex-shrink:0;height:30px;-webkit-tap-highlight-color:transparent}._tab_c6cms_1:hover{background:var(--border);color:var(--text)}._tabActive_c6cms_84{color:var(--text);font-weight:600;background:var(--bg)}._tabSplit_c6cms_90{background:#0078d41a;color:var(--text)}._colorDot_c6cms_95{width:8px;height:8px;border-radius:50%;flex-shrink:0}._tabName_c6cms_102{max-width:100px;overflow:hidden;text-overflow:ellipsis}._tabActivity_c6cms_108{font-size:10px;color:var(--text-muted);flex-shrink:0}._closeBtn_c6cms_114{display:none;align-items:center;justify-content:center;background:none;border:none;width:18px;height:18px;border-radius:4px;font-size:14px;line-height:1;color:var(--text-muted);cursor:pointer;padding:0;transition:background .15s,color .15s}._tab_c6cms_1:hover ._closeBtn_c6cms_114,._tabActive_c6cms_84 ._closeBtn_c6cms_114{display:flex}._closeBtn_c6cms_114:hover{color:var(--danger);background:transparent}._addBtn_c6cms_37{display:flex;align-items:center;justify-content:center;gap:4px;padding:0 10px;height:28px;border-radius:6px;font-size:13px;color:var(--text-dim);flex-shrink:0;white-space:nowrap;transition:background .15s,color .15s}._addBtn_c6cms_37:hover{background:var(--bg);color:var(--text)}._statusDot_c6cms_165{width:6px;height:6px;border-radius:50%;flex-shrink:0;margin-left:4px}._unreadDot_c6cms_173{width:6px;height:6px;border-radius:50%;background:var(--accent);flex-shrink:0;animation:_pulse_c6cms_1 1.5s ease-in-out infinite}@keyframes _pulse_c6cms_1{0%,to{opacity:1}50%{opacity:.3}}._preview_pobdq_1{position:absolute;top:100%;left:50%;transform:translate(-50%);z-index:100;margin-top:6px;padding:8px 10px;background:var(--surface);border:1px solid var(--border);border-radius:6px;box-shadow:0 4px 16px #0006;min-width:220px;max-width:360px;pointer-events:none;animation:_fade-in_pobdq_1 .15s ease-out}._lines_pobdq_19{font-family:Cascadia Code,Fira Code,Menlo,Consolas,monospace;font-size:11px;line-height:1.4;color:var(--text-secondary);white-space:pre;overflow:hidden;text-overflow:ellipsis}._empty_pobdq_29{color:var(--text-dim);font-style:italic;font-family:inherit;font-size:12px}._touchBar_11u4i_1{position:fixed;bottom:0;left:0;right:0;z-index:50;height:calc(80px + env(safe-area-inset-bottom,0px));padding:6px 4px;padding-bottom:calc(6px + env(safe-area-inset-bottom,0px));background:var(--surface);display:flex;flex-direction:column;gap:4px;justify-content:center;user-select:none;-webkit-user-select:none;touch-action:manipulation}._row_11u4i_20{display:flex;gap:4px;padding:0 2px}._keyBtn_11u4i_26{min-width:0;height:34px;background:var(--key-bg);color:var(--text);border:1px solid var(--key-border);border-radius:6px;font-size:13px;font-weight:500;flex:1 1 0;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 1px 0 var(--key-shadow);-webkit-tap-highlight-color:transparent;font-family:inherit;transition:background .08s}._special_11u4i_46,._modifier_11u4i_52{background:var(--key-special-bg);font-size:12px;font-weight:600}._modifier_11u4i_52._active_11u4i_58{background:var(--accent, #0078d4);box-shadow:0 1px #0003,0 0 10px #0078d480}._iconBtn_11u4i_65{font-size:16px}._keyEnter_11u4i_69{background:var(--accent, #0078d4);color:#fff;font-size:20px}._keyDanger_11u4i_75{background:#5c2222;color:#f87171}._flash_11u4i_80{background:var(--accent, #0078d4)!important;color:#fff!important}._searchBar_1su33_1{position:absolute;top:calc(40px + env(safe-area-inset-top,0px));right:0;z-index:50;display:flex;align-items:center;gap:6px;padding:6px 12px;background:var(--surface, #1e1e2e);border-bottom:1px solid var(--border, #333);box-shadow:0 2px 8px #0000004d;animation:_slide-down_1su33_1 .15s ease}._input_1su33_16{flex:1;min-width:0;padding:6px 10px;border:1px solid var(--border, #444);border-radius:4px;background:var(--input-bg, #111);color:var(--text, #ccc);font-size:13px;outline:none;font-family:inherit}._input_1su33_16:focus{border-color:var(--accent, #4a9eff)}._btn_1su33_33{background:none;border:1px solid var(--border, #444);border-radius:4px;color:var(--text, #ccc);cursor:pointer;padding:4px 8px;font-size:13px;line-height:1}._btn_1su33_33:hover{background:var(--border, #333)}._btnActive_1su33_48{background:var(--accent, #4a9eff);color:#fff;border-color:var(--accent, #4a9eff)}._indicator_1su33_54{font-size:12px;color:var(--text-muted, #666);white-space:nowrap}@keyframes _slide-down_1su33_1{0%{transform:translateY(-100%)}to{transform:translateY(0)}}._overlay_153fc_1{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--overlay-bg);z-index:200;animation:_fadeIn_153fc_1 .15s ease-out}._content_153fc_9{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:24px;width:min(380px,calc(100vw - 32px));max-height:85dvh;overflow-y:auto;z-index:201;animation:_slideUp_153fc_1 .15s ease-out}._title_153fc_25{font-size:1.1rem;font-weight:600;color:var(--text);margin:0 0 8px}._description_153fc_32{font-size:.85rem;color:var(--text-secondary);margin:0 0 20px}._label_153fc_38{display:block;font-size:.8rem;color:var(--text-secondary);margin-bottom:6px}._input_153fc_45{width:100%;padding:10px 12px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:1rem;font-family:inherit;outline:none;box-sizing:border-box}._input_153fc_45:focus{border-color:var(--accent)}._input_153fc_45::-webkit-inner-spin-button,._input_153fc_45::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}._input_153fc_45[type=number]{-moz-appearance:textfield}._actions_153fc_73{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}._cancelBtn_153fc_80{padding:8px 16px;background:transparent;border:1px solid var(--border);border-radius:6px;color:var(--text-secondary);cursor:pointer;font-size:.9rem;transition:border-color .15s}._cancelBtn_153fc_80:hover{border-color:var(--text-secondary)}._openBtn_153fc_95{padding:8px 20px;background:var(--accent);border:none;border-radius:6px;color:#fff;cursor:pointer;font-size:.9rem;font-weight:500;transition:background .15s}._openBtn_153fc_95:hover:not(:disabled){background:var(--accent-hover)}._openBtn_153fc_95:disabled{opacity:.5;cursor:not-allowed}._close_153fc_116{position:absolute;top:12px;right:12px;background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:1.2rem;line-height:1;padding:4px}._close_153fc_116:hover{color:var(--text)}@media(pointer:coarse){._content_153fc_9{top:30%;transform:translate(-50%,-30%)}}@keyframes _fadeIn_153fc_1{0%{opacity:0}to{opacity:1}}@keyframes _slideUp_153fc_1{0%{opacity:0;transform:translate(-50%,-48%)}to{opacity:1;transform:translate(-50%,-50%)}}._backdrop_2adtd_1{position:fixed;top:0;right:0;bottom:0;left:0;z-index:300;background:#0006}._panel_2adtd_8{position:fixed;top:0;right:-100%;width:min(90vw,300px);height:100%;background:var(--bg, #1e1e2e);border-left:1px solid var(--border, #333);z-index:301;box-shadow:0 8px 24px #0006;display:flex;flex-direction:column;transition:right .25s ease}._panelOpen_2adtd_23{right:0}._header_2adtd_27{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--border, #333)}._title_2adtd_35{font-weight:600;font-size:14px;color:var(--text, #ccc)}._closeBtn_2adtd_41{background:none;border:none;color:var(--text, #999);cursor:pointer;font-size:18px;padding:4px 8px;border-radius:4px;line-height:1}._closeBtn_2adtd_41:hover{background:var(--border, #333)}._body_2adtd_56{overflow-y:auto;flex:1;padding:8px}._section_2adtd_62{margin-bottom:8px}._sectionTitle_2adtd_66{font-size:10px;font-weight:700;color:var(--text-muted, #666);text-transform:uppercase;letter-spacing:1px;padding:6px 10px 4px}._btn_2adtd_75{display:flex;align-items:center;gap:10px;width:100%;background:none;border:none;color:var(--text, #ccc);padding:10px 12px;border-radius:6px;font-size:13px;cursor:pointer;transition:background .15s}._btn_2adtd_75:hover{background:var(--surface-hover, rgba(255, 255, 255, .08))}._btn_2adtd_75 svg{width:16px;height:16px;opacity:.7;flex-shrink:0}._list_2adtd_102{flex:1;overflow-y:auto;padding:8px}._item_2adtd_108{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:6px;cursor:pointer;color:var(--text, #ccc);font-size:13px;border:none;background:none;width:100%;text-align:left;transition:background .15s}._item_2adtd_108:hover,._item_2adtd_108[data-selected=true],._item_2adtd_108[aria-selected=true]{background:var(--surface-hover, rgba(255, 255, 255, .08))}._backdrop_1df0i_1{position:fixed;top:0;right:0;bottom:0;left:0;z-index:200;background:var(--overlay-bg);animation:_fade-in_1df0i_1 .2s ease-out}._backdrop_1df0i_1[data-closing=true]{animation:_fade-out_1df0i_1 .2s ease-in forwards}._panel_1df0i_13{position:fixed;top:0;left:0;bottom:0;z-index:201;width:min(85vw,380px);background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden;padding-top:env(safe-area-inset-top,0px);padding-left:env(safe-area-inset-left,0px);padding-bottom:env(safe-area-inset-bottom,0px);box-shadow:0 4px 12px #0000004d;animation:_slide-in-left_1df0i_1 .25s ease-out}._panel_1df0i_13[data-closing=true]{animation:_slide-out-left_1df0i_1 .2s ease-in forwards}._header_1df0i_38{display:flex;flex-direction:column;padding:16px 14px 12px;border-bottom:1px solid var(--border);position:relative}._brand_1df0i_46{display:flex;align-items:center;gap:8px;font-size:18px;font-weight:700;letter-spacing:-.02em;color:var(--text)}._brand_1df0i_46 svg{flex-shrink:0;color:var(--accent)}._version_1df0i_61{font-size:11px;color:var(--text-muted);font-weight:400;margin-top:2px;padding-left:28px}._closeBtn_1df0i_69{position:absolute;top:14px;right:10px;background:none;border:none;color:var(--text-dim);width:30px;height:30px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:20px;transition:background .15s,color .15s;-webkit-tap-highlight-color:transparent}._closeBtn_1df0i_69:hover{background:var(--border);color:var(--text)}._sectionTitle_1df0i_97{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--text-dim);padding:10px 14px 4px}._list_1df0i_108{flex:1 1 0;min-height:0;overflow-y:auto;padding:8px;-webkit-overflow-scrolling:touch;display:flex;flex-direction:column;gap:6px}._card_1df0i_121{background:var(--bg);border:1px solid var(--border);border-radius:10px;cursor:pointer;overflow:hidden;flex-shrink:0;transition:background .15s,border-color .15s;-webkit-tap-highlight-color:transparent}._card_1df0i_121:hover{border-color:var(--accent)}._cardActive_1df0i_138{border-color:var(--accent);border-width:2px}._cardHeader_1df0i_145{display:flex;align-items:center;gap:8px;padding:10px 12px 6px}._cardDot_1df0i_152{width:10px;height:10px;border-radius:50%;flex-shrink:0}._cardName_1df0i_159{font-size:13px;font-weight:600;color:var(--text);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._cardStatus_1df0i_169{width:7px;height:7px;border-radius:50%;flex-shrink:0}._cardClose_1df0i_176{background:none;border:none;color:var(--text-muted);width:26px;height:26px;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0;padding:0;transition:background .15s,color .15s;-webkit-tap-highlight-color:transparent}._cardClose_1df0i_176:hover{background:var(--danger);color:#fff}._cardMeta_1df0i_203{padding:0 12px 4px;font-size:10px;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._cardGit_1df0i_214{display:flex;flex-wrap:wrap;gap:3px 6px;padding:0 12px 4px;font-size:10px;color:var(--text-secondary);align-items:center}._gitBadge_1df0i_224{display:inline-flex;align-items:center;gap:3px;background:var(--surface);padding:1px 6px;border-radius:3px}._gitClean_1df0i_233{color:var(--success)}._gitDirty_1df0i_237{color:#fbbf24}._cardPreview_1df0i_243{margin:0 8px 8px;padding:6px 8px;background:var(--surface);border-radius:6px;font-family:NerdFont,JetBrains Mono,monospace;font-size:9px;line-height:1.3;color:var(--text-secondary);white-space:pre;overflow:hidden;max-height:72px;-webkit-text-size-adjust:none}._cardPreviewEmpty_1df0i_258{font-style:italic;color:var(--text-muted);text-align:center;font-family:inherit;font-size:11px;white-space:normal}._footer_1df0i_269{padding:8px;border-top:1px solid var(--border)}._newBtn_1df0i_274{width:100%;padding:10px;border:1px dashed var(--border);border-radius:8px;background:none;color:var(--accent);font-size:14px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px;transition:background .15s}._newBtn_1df0i_274:hover{background:var(--bg)}._newBtn_1df0i_274:active{transform:scale(.98)}._unreadDot_1df0i_299{width:6px;height:6px;border-radius:50%;background:var(--accent);flex-shrink:0;animation:_pulse_1df0i_1 1.5s ease-in-out infinite}@keyframes _pulse_1df0i_1{0%,to{opacity:1}50%{opacity:.3}}@keyframes _slide-in-left_1df0i_1{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes _slide-out-left_1df0i_1{0%{transform:translate(0)}to{transform:translate(-100%)}}@keyframes _fade-in_1df0i_1{0%{opacity:0}to{opacity:1}}@keyframes _fade-out_1df0i_1{0%{opacity:1}to{opacity:0}}._overlay_h1a26_1{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--overlay-bg);z-index:200;animation:_fadeIn_h1a26_1 .15s ease-out}._content_h1a26_9{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:24px;width:min(440px,calc(100vw - 32px));max-height:85dvh;overflow-y:auto;z-index:201;box-shadow:0 8px 24px #0006;animation:_slideUp_h1a26_1 .15s ease-out}._title_h1a26_26{font-size:1.1rem;font-weight:600;color:var(--text);margin:0 0 16px}._dropZone_h1a26_33{border:2px dashed var(--border);border-radius:8px;padding:32px 16px;text-align:center;cursor:pointer;transition:border-color .15s,background .15s;color:var(--text-secondary);font-size:.9rem}._dropZone_h1a26_33:hover,._dropZoneDragOver_h1a26_47{border-color:var(--accent);background:#4a9eff14;background:color-mix(in srgb,var(--accent) 8%,transparent)}._fileList_h1a26_53{margin-top:12px;max-height:160px;overflow-y:auto;display:flex;flex-direction:column;gap:4px}._fileInfo_h1a26_62{padding:8px 12px;background:var(--bg);border-radius:6px;font-size:.85rem;color:var(--text);display:flex;justify-content:space-between;align-items:center}._fileName_h1a26_73{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:240px}._fileSize_h1a26_81{color:var(--text-dim);flex-shrink:0;margin-left:8px}._targetDirGroup_h1a26_87{margin-top:16px}._label_h1a26_91{display:block;font-size:.8rem;color:var(--text-secondary);margin-bottom:6px}._targetDirRow_h1a26_98{display:flex;gap:8px}._input_h1a26_103{flex:1;padding:8px 12px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.9rem;font-family:inherit;outline:none}._input_h1a26_103:focus{border-color:var(--accent)}._browseBtn_h1a26_119{padding:8px 14px;background:var(--surface);border:1px solid var(--border);border-radius:6px;color:var(--text);cursor:pointer;font-size:.85rem;white-space:nowrap;transition:border-color .15s}._browseBtn_h1a26_119:hover{border-color:var(--accent)}._hint_h1a26_135{margin-top:8px;font-size:.75rem;color:var(--text-dim)}._actions_h1a26_141{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}._cancelBtn_h1a26_148{padding:8px 16px;background:transparent;border:1px solid var(--border);border-radius:6px;color:var(--text-secondary);cursor:pointer;font-size:.9rem;transition:border-color .15s}._cancelBtn_h1a26_148:hover{border-color:var(--text-secondary)}._uploadBtn_h1a26_163{padding:8px 20px;background:var(--accent);border:none;border-radius:6px;color:#fff;cursor:pointer;font-size:.9rem;font-weight:500;transition:background .15s}._uploadBtn_h1a26_163:hover:not(:disabled){background:var(--accent-hover)}._uploadBtn_h1a26_163:disabled{opacity:.5;cursor:not-allowed}._close_h1a26_184{position:absolute;top:12px;right:12px;background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:1.2rem;line-height:1;padding:4px}._close_h1a26_184:hover{color:var(--text)}._progressWrapper_h1a26_201{margin-top:12px;background:var(--bg);border-radius:6px;height:24px;position:relative;overflow:hidden;border:1px solid var(--border)}._progressBar_h1a26_211{height:100%;background:var(--accent);border-radius:5px;transition:width .15s ease-out;min-width:0}._progressLabel_h1a26_219{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:600;color:var(--text);pointer-events:none}._folderBrowserWrapper_h1a26_231{margin-top:12px;border:1px solid var(--border);border-radius:8px;padding:8px;max-height:280px;display:flex;flex-direction:column;overflow:hidden}@media(pointer:coarse){._content_h1a26_9{top:30%;transform:translate(-50%,-30%)}}@keyframes _fadeIn_h1a26_1{0%{opacity:0}to{opacity:1}}@keyframes _slideUp_h1a26_1{0%{opacity:0;transform:translate(-50%,-48%)}to{opacity:1;transform:translate(-50%,-50%)}}._overlay_1mt11_1{position:fixed;top:0;right:0;bottom:0;left:0;z-index:110;display:flex;flex-direction:column;background:var(--overlay-bg);animation:_fade-in_1mt11_1 .15s ease}._header_1mt11_11{display:flex;align-items:center;justify-content:space-between;padding:calc(12px + env(safe-area-inset-top,0px)) 16px 10px;background:var(--surface, #1e1e2e);border-bottom:1px solid var(--border, #333)}._title_1mt11_20{color:var(--text, #ccc);font-size:14px;font-weight:600}._actions_1mt11_26{display:flex;gap:8px}._btnPrimary_1mt11_31{padding:8px 18px;border:none;border-radius:6px;background:var(--accent, #4a9eff);color:#fff;font-size:14px;cursor:pointer}._btnPrimary_1mt11_31:hover{opacity:.9}._btnSecondary_1mt11_45{padding:8px 18px;border:1px solid var(--border, #444);border-radius:6px;background:transparent;color:var(--text, #ccc);font-size:14px;cursor:pointer}._btnSecondary_1mt11_45:hover{background:var(--border, #333)}._content_1mt11_59{flex:1;overflow:auto;padding:16px 16px calc(12px + env(safe-area-inset-bottom,0px))}._loadMoreBtn_1mt11_65{display:block;width:100%;padding:8px;margin-bottom:8px;border:1px solid var(--border, #444);border-radius:6px;background:var(--surface, #1e1e2e);color:var(--accent, #4a9eff);font-size:13px;cursor:pointer;text-align:center}._loadMoreBtn_1mt11_65:hover{background:var(--border, #333)}._pre_1mt11_83{margin:0;font-family:Cascadia Code,Fira Code,monospace;font-size:13px;color:var(--text, #ccc);white-space:pre;word-break:normal;user-select:text;-webkit-user-select:text}@keyframes _fade-in_1mt11_1{0%{opacity:0}to{opacity:1}}._layout_w2cgj_1{--keyboard-height: 0px;position:relative;height:100vh;height:100dvh;overflow:hidden;background:var(--bg);color:var(--text)}._topBar_w2cgj_12{height:calc(40px + env(safe-area-inset-top,0px));padding:0 4px;padding-top:env(safe-area-inset-top,0px);display:flex;align-items:center;background:var(--surface);border-bottom:1px solid var(--border);gap:2px;position:fixed;top:0;left:0;right:0;z-index:60}._left_w2cgj_28{display:flex;align-items:center;gap:4px;flex:1;min-width:0;overflow:hidden}._right_w2cgj_37{display:flex;align-items:center;gap:2px;flex-shrink:0}._barBtn_w2cgj_44{background:none;border:none;color:var(--text-dim);width:30px;height:30px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;text-decoration:none;gap:4px;padding:0;flex-shrink:0;transition:background .15s,color .15s;-webkit-tap-highlight-color:transparent}._barBtn_w2cgj_44:hover{background:var(--border);color:var(--text)}._barBtnWithLabel_w2cgj_70{width:auto;padding:0 10px;gap:3px}._btnLabel_w2cgj_76{font-size:12px;font-weight:600}._stopBtn_w2cgj_81{color:var(--danger);font-size:11px;font-weight:600}._stopBtn_w2cgj_81:hover{background:var(--danger);color:#fff}._stopBtn_w2cgj_81:active{transform:scale(.9)}._statusDot_w2cgj_96{width:8px;height:8px;border-radius:50%;background:var(--danger);flex-shrink:0;transition:background .3s;display:inline-block}._statusConnected_w2cgj_106{background:var(--success)}._sessionName_w2cgj_110{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--text);max-width:120px}._statusText_w2cgj_120{font-size:11px;color:var(--text-secondary);white-space:nowrap;flex-shrink:0}._mobileOnly_w2cgj_127{display:none}@media(max-width:640px){._desktopOnly_w2cgj_132{display:none!important}._mobileOnly_w2cgj_127{display:flex!important}._sessionName_w2cgj_110{max-width:30vw}._statusText_w2cgj_120{display:none}}._terminalArea_w2cgj_147{position:absolute;top:calc(40px + env(safe-area-inset-top,0px));bottom:calc(80px + env(safe-area-inset-bottom,0px) + var(--keyboard-height, 0px));left:0;right:0;display:flex;min-height:0}._split_w2cgj_157{flex-direction:row}._paneWrapper_w2cgj_161{flex:1;display:none;min-height:0;min-width:0;flex-direction:column}._visible_w2cgj_169{display:flex}._split_w2cgj_157 ._visible_w2cgj_169+._visible_w2cgj_169{border-left:2px solid var(--accent)}@media(max-width:640px){._split_w2cgj_157{flex-direction:column}._split_w2cgj_157 ._visible_w2cgj_169+._visible_w2cgj_169{border-left:none;border-top:2px solid var(--accent)}}:root{--bg: #1e1e1e;--surface: #252526;--border: #3c3c3c;--border-subtle: #474747;--text: #d4d4d4;--text-secondary: #858585;--text-dim: #6e6e6e;--text-muted: #555555;--accent: #0078d4;--accent-hover: #1a8ae8;--accent-active: #005a9e;--danger: #f14c4c;--danger-hover: #d73a3a;--success: #89d185;--overlay-bg: rgba(0, 0, 0, .85);--key-bg: #4a4a4c;--key-border: #5a5a5c;--key-shadow: rgba(0, 0, 0, .5);--key-special-bg: #333335}[data-theme=light]{--bg: #ffffff;--surface: #f3f3f3;--border: #e0e0e0;--border-subtle: #d0d0d0;--text: #1e1e1e;--text-secondary: #616161;--text-dim: #767676;--text-muted: #a0a0a0;--accent: #0078d4;--accent-hover: #106ebe;--accent-active: #005a9e;--danger: #e51400;--danger-hover: #c20000;--success: #16825d;--overlay-bg: rgba(0, 0, 0, .5);--key-bg: #ffffff;--key-border: #b5b5b5;--key-shadow: rgba(0, 0, 0, .12);--key-special-bg: #adb5bd}[data-theme=monokai]{--bg: #272822;--surface: #1e1f1c;--border: #49483e;--border-subtle: #5c5c4f;--text: #f8f8f2;--text-secondary: #a59f85;--text-dim: #75715e;--text-muted: #5a5854;--accent: #a6e22e;--accent-hover: #b8f53c;--accent-active: #8acc16;--danger: #f92672;--danger-hover: #e0155d;--success: #a6e22e;--overlay-bg: rgba(0, 0, 0, .75);--key-bg: #49483e;--key-border: #5c5c4f;--key-shadow: rgba(0, 0, 0, .4);--key-special-bg: #3e3d32}[data-theme=solarized-dark]{--bg: #002b36;--surface: #073642;--border: #586e75;--border-subtle: #657b83;--text: #839496;--text-secondary: #657b83;--text-dim: #586e75;--text-muted: #4a5a62;--accent: #268bd2;--accent-hover: #379ce3;--accent-active: #1a7abf;--danger: #dc322f;--danger-hover: #c8221f;--success: #859900;--overlay-bg: rgba(0, 0, 0, .75);--key-bg: #073642;--key-border: #586e75;--key-shadow: rgba(0, 0, 0, .3);--key-special-bg: #002b36}[data-theme=solarized-light]{--bg: #fdf6e3;--surface: #eee8d5;--border: #93a1a1;--border-subtle: #839496;--text: #657b83;--text-secondary: #93a1a1;--text-dim: #a0a0a0;--text-muted: #b0b0b0;--accent: #268bd2;--accent-hover: #379ce3;--accent-active: #1a7abf;--danger: #dc322f;--danger-hover: #c8221f;--success: #859900;--overlay-bg: rgba(0, 0, 0, .4);--key-bg: #ffffff;--key-border: #b5b5b5;--key-shadow: rgba(0, 0, 0, .12);--key-special-bg: #adb5bd}[data-theme=nord]{--bg: #2e3440;--surface: #3b4252;--border: #434c5e;--border-subtle: #4c566a;--text: #d8dee9;--text-secondary: #b0bac9;--text-dim: #7b88a1;--text-muted: #5c6a85;--accent: #88c0d0;--accent-hover: #9fd4e4;--accent-active: #6aafbf;--danger: #bf616a;--danger-hover: #a84d57;--success: #a3be8c;--overlay-bg: rgba(0, 0, 0, .75);--key-bg: #434c5e;--key-border: #4c566a;--key-shadow: rgba(0, 0, 0, .3);--key-special-bg: #3b4252}[data-theme=dracula]{--bg: #282a36;--surface: #343746;--border: #44475a;--border-subtle: #525568;--text: #f8f8f2;--text-secondary: #c1c4d2;--text-dim: #8e92a4;--text-muted: #6272a4;--accent: #bd93f9;--accent-hover: #d0b0ff;--accent-active: #a77de7;--danger: #ff5555;--danger-hover: #e03d3d;--success: #50fa7b;--overlay-bg: rgba(0, 0, 0, .75);--key-bg: #44475a;--key-border: #525568;--key-shadow: rgba(0, 0, 0, .4);--key-special-bg: #343746}[data-theme=github-dark]{--bg: #0d1117;--surface: #161b22;--border: #30363d;--border-subtle: #3d444d;--text: #c9d1d9;--text-secondary: #8b949e;--text-dim: #6e7681;--text-muted: #484f58;--accent: #58a6ff;--accent-hover: #79b8ff;--accent-active: #388bfd;--danger: #f85149;--danger-hover: #da3633;--success: #3fb950;--overlay-bg: rgba(0, 0, 0, .75);--key-bg: #161b22;--key-border: #30363d;--key-shadow: rgba(0, 0, 0, .4);--key-special-bg: #0d1117}[data-theme=one-dark]{--bg: #282c34;--surface: #21252b;--border: #3e4452;--border-subtle: #4b5263;--text: #abb2bf;--text-secondary: #7f848e;--text-dim: #5c6370;--text-muted: #4b5263;--accent: #61afef;--accent-hover: #7dc0ff;--accent-active: #4d9ede;--danger: #e06c75;--danger-hover: #c95c67;--success: #98c379;--overlay-bg: rgba(0, 0, 0, .75);--key-bg: #3e4452;--key-border: #4b5263;--key-shadow: rgba(0, 0, 0, .3);--key-special-bg: #21252b}[data-theme=catppuccin]{--bg: #1e1e2e;--surface: #313244;--border: #45475a;--border-subtle: #585b70;--text: #cdd6f4;--text-secondary: #a6adc8;--text-dim: #7f849c;--text-muted: #585b70;--accent: #89b4fa;--accent-hover: #b4d0ff;--accent-active: #5c9de3;--danger: #f38ba8;--danger-hover: #eb7c9d;--success: #a6e3a1;--overlay-bg: rgba(0, 0, 0, .75);--key-bg: #45475a;--key-border: #585b70;--key-shadow: rgba(0, 0, 0, .3);--key-special-bg: #313244}[data-theme=gruvbox]{--bg: #282828;--surface: #3c3836;--border: #504945;--border-subtle: #665c54;--text: #ebdbb2;--text-secondary: #d5c4a1;--text-dim: #a89984;--text-muted: #7c6f64;--accent: #83a598;--accent-hover: #9dbfb4;--accent-active: #6a8f8a;--danger: #fb4934;--danger-hover: #e33826;--success: #b8bb26;--overlay-bg: rgba(0, 0, 0, .75);--key-bg: #504945;--key-border: #665c54;--key-shadow: rgba(0, 0, 0, .4);--key-special-bg: #3c3836}[data-theme=night-owl]{--bg: #011627;--surface: #0d2a45;--border: #1d3b53;--border-subtle: #264863;--text: #d6deeb;--text-secondary: #8badc1;--text-dim: #5f7e97;--text-muted: #3f5f7d;--accent: #7fdbca;--accent-hover: #9ff0e0;--accent-active: #62c5b5;--danger: #ef5350;--danger-hover: #d83130;--success: #addb67;--overlay-bg: rgba(0, 0, 0, .75);--key-bg: #1d3b53;--key-border: #264863;--key-shadow: rgba(0, 0, 0, .4);--key-special-bg: #0d2a45}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body{height:100vh;height:100dvh;overflow:hidden;overscroll-behavior:none;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-tap-highlight-color:transparent;transition:background .3s,color .3s}#root{height:100%;display:flex;flex-direction:column}a{color:var(--accent);text-decoration:none}button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit;font-size:inherit}input,select,textarea{font-family:inherit;font-size:inherit;color:var(--text);background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:8px 12px;outline:none}input:focus,select:focus,textarea:focus{border-color:var(--accent)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--border-subtle)}[data-sonner-toaster]{z-index:9999!important}@keyframes toast-in{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes toast-out{0%{transform:translateY(0);opacity:1}to{transform:translateY(100%);opacity:0}}@keyframes slide-in-right{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes slide-out-right{0%{transform:translate(0)}to{transform:translate(100%)}}@keyframes slide-in-left{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes slide-out-left{0%{transform:translate(0)}to{transform:translate(-100%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes spin{to{transform:rotate(360deg)}}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta
|
|
6
|
+
name="viewport"
|
|
7
|
+
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
|
|
8
|
+
/>
|
|
9
|
+
<meta name="theme-color" content="#1e1e1e" />
|
|
10
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
11
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
12
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
13
|
+
<link rel="icon" type="image/png" href="/icons/icon-192.png" />
|
|
14
|
+
<link rel="apple-touch-icon" href="/icons/icon-192.png" />
|
|
15
|
+
<link rel="manifest" href="/manifest.webmanifest" />
|
|
16
|
+
<title>TermBeam</title>
|
|
17
|
+
<script>
|
|
18
|
+
const t = localStorage.getItem('termbeam-theme');
|
|
19
|
+
if (t) document.documentElement.setAttribute('data-theme', t);
|
|
20
|
+
</script>
|
|
21
|
+
<script type="module" crossorigin src="/assets/index-CTOb8Hzh.js"></script>
|
|
22
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DCDkIi8X.css">
|
|
23
|
+
<link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
|
|
24
|
+
<body>
|
|
25
|
+
<div id="root"></div>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"TermBeam","short_name":"TermBeam","description":"Beam your terminal to any device","start_url":"/","display":"standalone","background_color":"#1e1e1e","theme_color":"#1e1e1e","lang":"en","scope":"/","icons":[{"src":"/icons/icon-192.png","sizes":"192x192","type":"image/png"},{"src":"/icons/icon-512.png","sizes":"512x512","type":"image/png"},{"src":"/icons/icon-512.png","sizes":"512x512","type":"image/png","purpose":"maskable"}]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
if('serviceWorker' in navigator) {window.addEventListener('load', () => {navigator.serviceWorker.register('/sw.js', { scope: '/' })})}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
try{self["workbox:core:7.3.0"]&&_()}catch{}const Q=(n,...e)=>{let t=n;return e.length>0&&(t+=` :: ${JSON.stringify(e)}`),t},z=Q;class l extends Error{constructor(e,t){const s=z(e,t);super(s),this.name=e,this.details=t}}const d={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:typeof registration<"u"?registration.scope:""},E=n=>[d.prefix,n,d.suffix].filter(e=>e&&e.length>0).join("-"),J=n=>{for(const e of Object.keys(d))n(e)},b={updateDetails:n=>{J(e=>{typeof n[e]=="string"&&(d[e]=n[e])})},getGoogleAnalyticsName:n=>n||E(d.googleAnalytics),getPrecacheName:n=>n||E(d.precache),getPrefix:()=>d.prefix,getRuntimeName:n=>n||E(d.runtime),getSuffix:()=>d.suffix};function K(n,e){const t=e();return n.waitUntil(t),t}try{self["workbox:precaching:7.3.0"]&&_()}catch{}const X="__WB_REVISION__";function Y(n){if(!n)throw new l("add-to-cache-list-unexpected-type",{entry:n});if(typeof n=="string"){const r=new URL(n,location.href);return{cacheKey:r.href,url:r.href}}const{revision:e,url:t}=n;if(!t)throw new l("add-to-cache-list-unexpected-type",{entry:n});if(!e){const r=new URL(t,location.href);return{cacheKey:r.href,url:r.href}}const s=new URL(t,location.href),a=new URL(t,location.href);return s.searchParams.set(X,e),{cacheKey:s.href,url:a.href}}class Z{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:s})=>{if(e.type==="install"&&t&&t.originalRequest&&t.originalRequest instanceof Request){const a=t.originalRequest.url;s?this.notUpdatedURLs.push(a):this.updatedURLs.push(a)}return s}}}class ee{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:t,params:s})=>{const a=(s==null?void 0:s.cacheKey)||this._precacheController.getCacheKeyForURL(t.url);return a?new Request(a,{headers:t.headers}):t},this._precacheController=e}}let m;function te(){if(m===void 0){const n=new Response("");if("body"in n)try{new Response(n.body),m=!0}catch{m=!1}m=!1}return m}async function se(n,e){let t=null;if(n.url&&(t=new URL(n.url).origin),t!==self.location.origin)throw new l("cross-origin-copy-response",{origin:t});const s=n.clone(),r={headers:new Headers(s.headers),status:s.status,statusText:s.statusText},i=te()?s.body:await s.blob();return new Response(i,r)}const ne=n=>new URL(String(n),location.href).href.replace(new RegExp(`^${location.origin}`),"");function S(n,e){const t=new URL(n);for(const s of e)t.searchParams.delete(s);return t.href}async function ae(n,e,t,s){const a=S(e.url,t);if(e.url===a)return n.match(e,s);const r=Object.assign(Object.assign({},s),{ignoreSearch:!0}),i=await n.keys(e,r);for(const c of i){const o=S(c.url,t);if(a===o)return n.match(c,s)}}class re{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}}const j=new Set;async function ie(){for(const n of j)await n()}function F(n){return new Promise(e=>setTimeout(e,n))}try{self["workbox:strategies:7.3.0"]&&_()}catch{}function C(n){return typeof n=="string"?new Request(n):n}class ce{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new re,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(const s of this._plugins)this._pluginStateMap.set(s,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){const{event:t}=this;let s=C(e);if(s.mode==="navigate"&&t instanceof FetchEvent&&t.preloadResponse){const i=await t.preloadResponse;if(i)return i}const a=this.hasCallback("fetchDidFail")?s.clone():null;try{for(const i of this.iterateCallbacks("requestWillFetch"))s=await i({request:s.clone(),event:t})}catch(i){if(i instanceof Error)throw new l("plugin-error-request-will-fetch",{thrownErrorMessage:i.message})}const r=s.clone();try{let i;i=await fetch(s,s.mode==="navigate"?void 0:this._strategy.fetchOptions);for(const c of this.iterateCallbacks("fetchDidSucceed"))i=await c({event:t,request:r,response:i});return i}catch(i){throw a&&await this.runCallbacks("fetchDidFail",{error:i,event:t,originalRequest:a.clone(),request:r.clone()}),i}}async fetchAndCachePut(e){const t=await this.fetch(e),s=t.clone();return this.waitUntil(this.cachePut(e,s)),t}async cacheMatch(e){const t=C(e);let s;const{cacheName:a,matchOptions:r}=this._strategy,i=await this.getCacheKey(t,"read"),c=Object.assign(Object.assign({},r),{cacheName:a});s=await caches.match(i,c);for(const o of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await o({cacheName:a,matchOptions:r,cachedResponse:s,request:i,event:this.event})||void 0;return s}async cachePut(e,t){const s=C(e);await F(0);const a=await this.getCacheKey(s,"write");if(!t)throw new l("cache-put-with-no-response",{url:ne(a.url)});const r=await this._ensureResponseSafeToCache(t);if(!r)return!1;const{cacheName:i,matchOptions:c}=this._strategy,o=await self.caches.open(i),h=this.hasCallback("cacheDidUpdate"),g=h?await ae(o,a.clone(),["__WB_REVISION__"],c):null;try{await o.put(a,h?r.clone():r)}catch(u){if(u instanceof Error)throw u.name==="QuotaExceededError"&&await ie(),u}for(const u of this.iterateCallbacks("cacheDidUpdate"))await u({cacheName:i,oldResponse:g,newResponse:r.clone(),request:a,event:this.event});return!0}async getCacheKey(e,t){const s=`${e.url} | ${t}`;if(!this._cacheKeys[s]){let a=e;for(const r of this.iterateCallbacks("cacheKeyWillBeUsed"))a=C(await r({mode:t,request:a,event:this.event,params:this.params}));this._cacheKeys[s]=a}return this._cacheKeys[s]}hasCallback(e){for(const t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(const s of this.iterateCallbacks(e))await s(t)}*iterateCallbacks(e){for(const t of this._strategy.plugins)if(typeof t[e]=="function"){const s=this._pluginStateMap.get(t);yield r=>{const i=Object.assign(Object.assign({},r),{state:s});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){const e=this._extendLifetimePromises.splice(0),s=(await Promise.allSettled(e)).find(a=>a.status==="rejected");if(s)throw s.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,s=!1;for(const a of this.iterateCallbacks("cacheWillUpdate"))if(t=await a({request:this.request,response:t,event:this.event})||void 0,s=!0,!t)break;return s||t&&t.status!==200&&(t=void 0),t}}class I{constructor(e={}){this.cacheName=b.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){const[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});const t=e.event,s=typeof e.request=="string"?new Request(e.request):e.request,a="params"in e?e.params:void 0,r=new ce(this,{event:t,request:s,params:a}),i=this._getResponse(r,s,t),c=this._awaitComplete(i,r,s,t);return[i,c]}async _getResponse(e,t,s){await e.runCallbacks("handlerWillStart",{event:s,request:t});let a;try{if(a=await this._handle(t,e),!a||a.type==="error")throw new l("no-response",{url:t.url})}catch(r){if(r instanceof Error){for(const i of e.iterateCallbacks("handlerDidError"))if(a=await i({error:r,event:s,request:t}),a)break}if(!a)throw r}for(const r of e.iterateCallbacks("handlerWillRespond"))a=await r({event:s,request:t,response:a});return a}async _awaitComplete(e,t,s,a){let r,i;try{r=await e}catch{}try{await t.runCallbacks("handlerDidRespond",{event:a,request:s,response:r}),await t.doneWaiting()}catch(c){c instanceof Error&&(i=c)}if(await t.runCallbacks("handlerDidComplete",{event:a,request:s,response:r,error:i}),t.destroy(),i)throw i}}class p extends I{constructor(e={}){e.cacheName=b.getPrecacheName(e.cacheName),super(e),this._fallbackToNetwork=e.fallbackToNetwork!==!1,this.plugins.push(p.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){const s=await t.cacheMatch(e);return s||(t.event&&t.event.type==="install"?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,t){let s;const a=t.params||{};if(this._fallbackToNetwork){const r=a.integrity,i=e.integrity,c=!i||i===r;s=await t.fetch(new Request(e,{integrity:e.mode!=="no-cors"?i||r:void 0})),r&&c&&e.mode!=="no-cors"&&(this._useDefaultCacheabilityPluginIfNeeded(),await t.cachePut(e,s.clone()))}else throw new l("missing-precache-entry",{cacheName:this.cacheName,url:e.url});return s}async _handleInstall(e,t){this._useDefaultCacheabilityPluginIfNeeded();const s=await t.fetch(e);if(!await t.cachePut(e,s.clone()))throw new l("bad-precaching-response",{url:e.url,status:s.status});return s}_useDefaultCacheabilityPluginIfNeeded(){let e=null,t=0;for(const[s,a]of this.plugins.entries())a!==p.copyRedirectedCacheableResponsesPlugin&&(a===p.defaultPrecacheCacheabilityPlugin&&(e=s),a.cacheWillUpdate&&t++);t===0?this.plugins.push(p.defaultPrecacheCacheabilityPlugin):t>1&&e!==null&&this.plugins.splice(e,1)}}p.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:n}){return!n||n.status>=400?null:n}};p.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:n}){return n.redirected?await se(n):n}};class oe{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:s=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new p({cacheName:b.getPrecacheName(e),plugins:[...t,new ee({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this._installAndActiveListenersAdded=!0)}addToCacheList(e){const t=[];for(const s of e){typeof s=="string"?t.push(s):s&&s.revision===void 0&&t.push(s.url);const{cacheKey:a,url:r}=Y(s),i=typeof s!="string"&&s.revision?"reload":"default";if(this._urlsToCacheKeys.has(r)&&this._urlsToCacheKeys.get(r)!==a)throw new l("add-to-cache-list-conflicting-entries",{firstEntry:this._urlsToCacheKeys.get(r),secondEntry:a});if(typeof s!="string"&&s.integrity){if(this._cacheKeysToIntegrities.has(a)&&this._cacheKeysToIntegrities.get(a)!==s.integrity)throw new l("add-to-cache-list-conflicting-integrities",{url:r});this._cacheKeysToIntegrities.set(a,s.integrity)}if(this._urlsToCacheKeys.set(r,a),this._urlsToCacheModes.set(r,i),t.length>0){const c=`Workbox is precaching URLs without revision info: ${t.join(", ")}
|
|
2
|
+
This is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(c)}}}install(e){return K(e,async()=>{const t=new Z;this.strategy.plugins.push(t);for(const[r,i]of this._urlsToCacheKeys){const c=this._cacheKeysToIntegrities.get(i),o=this._urlsToCacheModes.get(r),h=new Request(r,{integrity:c,cache:o,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:i},request:h,event:e}))}const{updatedURLs:s,notUpdatedURLs:a}=t;return{updatedURLs:s,notUpdatedURLs:a}})}activate(e){return K(e,async()=>{const t=await self.caches.open(this.strategy.cacheName),s=await t.keys(),a=new Set(this._urlsToCacheKeys.values()),r=[];for(const i of s)a.has(i.url)||(await t.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){const t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){const t=e instanceof Request?e.url:e,s=this.getCacheKeyForURL(t);if(s)return(await self.caches.open(this.strategy.cacheName)).match(s)}createHandlerBoundToURL(e){const t=this.getCacheKeyForURL(e);if(!t)throw new l("non-precached-url",{url:e});return s=>(s.request=new Request(e),s.params=Object.assign({cacheKey:t},s.params),this.strategy.handle(s))}}let D;const q=()=>(D||(D=new oe),D);try{self["workbox:routing:7.3.0"]&&_()}catch{}const H="GET",x=n=>n&&typeof n=="object"?n:{handle:n};class R{constructor(e,t,s=H){this.handler=x(t),this.match=e,this.method=s}setCatchHandler(e){this.catchHandler=x(e)}}class he extends R{constructor(e,t,s){const a=({url:r})=>{const i=e.exec(r.href);if(i&&!(r.origin!==location.origin&&i.index!==0))return i.slice(1)};super(a,t,s)}}class le{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener("fetch",(e=>{const{request:t}=e,s=this.handleRequest({request:t,event:e});s&&e.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(e=>{if(e.data&&e.data.type==="CACHE_URLS"){const{payload:t}=e.data,s=Promise.all(t.urlsToCache.map(a=>{typeof a=="string"&&(a=[a]);const r=new Request(...a);return this.handleRequest({request:r,event:e})}));e.waitUntil(s),e.ports&&e.ports[0]&&s.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){const s=new URL(e.url,location.href);if(!s.protocol.startsWith("http"))return;const a=s.origin===location.origin,{params:r,route:i}=this.findMatchingRoute({event:t,request:e,sameOrigin:a,url:s});let c=i&&i.handler;const o=e.method;if(!c&&this._defaultHandlerMap.has(o)&&(c=this._defaultHandlerMap.get(o)),!c)return;let h;try{h=c.handle({url:s,request:e,event:t,params:r})}catch(u){h=Promise.reject(u)}const g=i&&i.catchHandler;return h instanceof Promise&&(this._catchHandler||g)&&(h=h.catch(async u=>{if(g)try{return await g.handle({url:s,request:e,event:t,params:r})}catch(M){M instanceof Error&&(u=M)}if(this._catchHandler)return this._catchHandler.handle({url:s,request:e,event:t});throw u})),h}findMatchingRoute({url:e,sameOrigin:t,request:s,event:a}){const r=this._routes.get(s.method)||[];for(const i of r){let c;const o=i.match({url:e,sameOrigin:t,request:s,event:a});if(o)return c=o,(Array.isArray(c)&&c.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o=="boolean")&&(c=void 0),{route:i,params:c}}return{}}setDefaultHandler(e,t=H){this._defaultHandlerMap.set(t,x(e))}setCatchHandler(e){this._catchHandler=x(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new l("unregister-route-but-not-found-with-method",{method:e.method});const t=this._routes.get(e.method).indexOf(e);if(t>-1)this._routes.get(e.method).splice(t,1);else throw new l("unregister-route-route-not-registered")}}let w;const ue=()=>(w||(w=new le,w.addFetchListener(),w.addCacheListener()),w);function N(n,e,t){let s;if(typeof n=="string"){const r=new URL(n,location.href),i=({url:c})=>c.href===r.href;s=new R(i,e,t)}else if(n instanceof RegExp)s=new he(n,e,t);else if(typeof n=="function")s=new R(n,e,t);else if(n instanceof R)s=n;else throw new l("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});return ue().registerRoute(s),s}function de(n,e=[]){for(const t of[...n.searchParams.keys()])e.some(s=>s.test(t))&&n.searchParams.delete(t);return n}function*fe(n,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:t="index.html",cleanURLs:s=!0,urlManipulation:a}={}){const r=new URL(n,location.href);r.hash="",yield r.href;const i=de(r,e);if(yield i.href,t&&i.pathname.endsWith("/")){const c=new URL(i.href);c.pathname+=t,yield c.href}if(s){const c=new URL(i.href);c.pathname+=".html",yield c.href}if(a){const c=a({url:r});for(const o of c)yield o.href}}class pe extends R{constructor(e,t){const s=({request:a})=>{const r=e.getURLsToCacheKeys();for(const i of fe(a.url,t)){const c=r.get(i);if(c){const o=e.getIntegrityForCacheKey(c);return{cacheKey:c,integrity:o}}}};super(s,e.strategy)}}function ge(n){const e=q(),t=new pe(e,n);N(t)}const me="-precache-",we=async(n,e=me)=>{const s=(await self.caches.keys()).filter(a=>a.includes(e)&&a.includes(self.registration.scope)&&a!==n);return await Promise.all(s.map(a=>self.caches.delete(a))),s};function ye(){self.addEventListener("activate",(n=>{const e=b.getPrecacheName();n.waitUntil(we(e).then(t=>{}))}))}function _e(n){q().precache(n)}function Re(n,e){_e(n),ge(e)}class be extends I{async _handle(e,t){let s=await t.cacheMatch(e),a;if(!s)try{s=await t.fetchAndCachePut(e)}catch(r){r instanceof Error&&(a=r)}if(!s)throw new l("no-response",{url:e.url,error:a});return s}}class Ce extends I{constructor(e={}){super(e),this._networkTimeoutSeconds=e.networkTimeoutSeconds||0}async _handle(e,t){let s,a;try{const r=[t.fetch(e)];if(this._networkTimeoutSeconds){const i=F(this._networkTimeoutSeconds*1e3);r.push(i)}if(a=await Promise.race(r),!a)throw new Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`)}catch(r){r instanceof Error&&(s=r)}if(!a)throw new l("no-response",{url:e.url,error:s});return a}}function V(n){n.then(()=>{})}const xe=(n,e)=>e.some(t=>n instanceof t);let A,O;function Ee(){return A||(A=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function De(){return O||(O=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const $=new WeakMap,k=new WeakMap,G=new WeakMap,L=new WeakMap,v=new WeakMap;function Le(n){const e=new Promise((t,s)=>{const a=()=>{n.removeEventListener("success",r),n.removeEventListener("error",i)},r=()=>{t(f(n.result)),a()},i=()=>{s(n.error),a()};n.addEventListener("success",r),n.addEventListener("error",i)});return e.then(t=>{t instanceof IDBCursor&&$.set(t,n)}).catch(()=>{}),v.set(e,n),e}function Te(n){if(k.has(n))return;const e=new Promise((t,s)=>{const a=()=>{n.removeEventListener("complete",r),n.removeEventListener("error",i),n.removeEventListener("abort",i)},r=()=>{t(),a()},i=()=>{s(n.error||new DOMException("AbortError","AbortError")),a()};n.addEventListener("complete",r),n.addEventListener("error",i),n.addEventListener("abort",i)});k.set(n,e)}let P={get(n,e,t){if(n instanceof IDBTransaction){if(e==="done")return k.get(n);if(e==="objectStoreNames")return n.objectStoreNames||G.get(n);if(e==="store")return t.objectStoreNames[1]?void 0:t.objectStore(t.objectStoreNames[0])}return f(n[e])},set(n,e,t){return n[e]=t,!0},has(n,e){return n instanceof IDBTransaction&&(e==="done"||e==="store")?!0:e in n}};function Ue(n){P=n(P)}function ke(n){return n===IDBDatabase.prototype.transaction&&!("objectStoreNames"in IDBTransaction.prototype)?function(e,...t){const s=n.call(T(this),e,...t);return G.set(s,e.sort?e.sort():[e]),f(s)}:De().includes(n)?function(...e){return n.apply(T(this),e),f($.get(this))}:function(...e){return f(n.apply(T(this),e))}}function Pe(n){return typeof n=="function"?ke(n):(n instanceof IDBTransaction&&Te(n),xe(n,Ee())?new Proxy(n,P):n)}function f(n){if(n instanceof IDBRequest)return Le(n);if(L.has(n))return L.get(n);const e=Pe(n);return e!==n&&(L.set(n,e),v.set(e,n)),e}const T=n=>v.get(n);function Ie(n,e,{blocked:t,upgrade:s,blocking:a,terminated:r}={}){const i=indexedDB.open(n,e),c=f(i);return s&&i.addEventListener("upgradeneeded",o=>{s(f(i.result),o.oldVersion,o.newVersion,f(i.transaction),o)}),t&&i.addEventListener("blocked",o=>t(o.oldVersion,o.newVersion,o)),c.then(o=>{r&&o.addEventListener("close",()=>r()),a&&o.addEventListener("versionchange",h=>a(h.oldVersion,h.newVersion,h))}).catch(()=>{}),c}function Ne(n,{blocked:e}={}){const t=indexedDB.deleteDatabase(n);return e&&t.addEventListener("blocked",s=>e(s.oldVersion,s)),f(t).then(()=>{})}const ve=["get","getKey","getAll","getAllKeys","count"],Me=["put","add","delete","clear"],U=new Map;function W(n,e){if(!(n instanceof IDBDatabase&&!(e in n)&&typeof e=="string"))return;if(U.get(e))return U.get(e);const t=e.replace(/FromIndex$/,""),s=e!==t,a=Me.includes(t);if(!(t in(s?IDBIndex:IDBObjectStore).prototype)||!(a||ve.includes(t)))return;const r=async function(i,...c){const o=this.transaction(i,a?"readwrite":"readonly");let h=o.store;return s&&(h=h.index(c.shift())),(await Promise.all([h[t](...c),a&&o.done]))[0]};return U.set(e,r),r}Ue(n=>({...n,get:(e,t,s)=>W(e,t)||n.get(e,t,s),has:(e,t)=>!!W(e,t)||n.has(e,t)}));try{self["workbox:expiration:7.3.0"]&&_()}catch{}const Ke="workbox-expiration",y="cache-entries",B=n=>{const e=new URL(n,location.href);return e.hash="",e.href};class Se{constructor(e){this._db=null,this._cacheName=e}_upgradeDb(e){const t=e.createObjectStore(y,{keyPath:"id"});t.createIndex("cacheName","cacheName",{unique:!1}),t.createIndex("timestamp","timestamp",{unique:!1})}_upgradeDbAndDeleteOldDbs(e){this._upgradeDb(e),this._cacheName&&Ne(this._cacheName)}async setTimestamp(e,t){e=B(e);const s={url:e,timestamp:t,cacheName:this._cacheName,id:this._getId(e)},r=(await this.getDb()).transaction(y,"readwrite",{durability:"relaxed"});await r.store.put(s),await r.done}async getTimestamp(e){const s=await(await this.getDb()).get(y,this._getId(e));return s==null?void 0:s.timestamp}async expireEntries(e,t){const s=await this.getDb();let a=await s.transaction(y).store.index("timestamp").openCursor(null,"prev");const r=[];let i=0;for(;a;){const o=a.value;o.cacheName===this._cacheName&&(e&&o.timestamp<e||t&&i>=t?r.push(a.value):i++),a=await a.continue()}const c=[];for(const o of r)await s.delete(y,o.id),c.push(o.url);return c}_getId(e){return this._cacheName+"|"+B(e)}async getDb(){return this._db||(this._db=await Ie(Ke,1,{upgrade:this._upgradeDbAndDeleteOldDbs.bind(this)})),this._db}}class Ae{constructor(e,t={}){this._isRunning=!1,this._rerunRequested=!1,this._maxEntries=t.maxEntries,this._maxAgeSeconds=t.maxAgeSeconds,this._matchOptions=t.matchOptions,this._cacheName=e,this._timestampModel=new Se(e)}async expireEntries(){if(this._isRunning){this._rerunRequested=!0;return}this._isRunning=!0;const e=this._maxAgeSeconds?Date.now()-this._maxAgeSeconds*1e3:0,t=await this._timestampModel.expireEntries(e,this._maxEntries),s=await self.caches.open(this._cacheName);for(const a of t)await s.delete(a,this._matchOptions);this._isRunning=!1,this._rerunRequested&&(this._rerunRequested=!1,V(this.expireEntries()))}async updateTimestamp(e){await this._timestampModel.setTimestamp(e,Date.now())}async isURLExpired(e){if(this._maxAgeSeconds){const t=await this._timestampModel.getTimestamp(e),s=Date.now()-this._maxAgeSeconds*1e3;return t!==void 0?t<s:!0}else return!1}async delete(){this._rerunRequested=!1,await this._timestampModel.expireEntries(1/0)}}function Oe(n){j.add(n)}class We{constructor(e={}){this.cachedResponseWillBeUsed=async({event:t,request:s,cacheName:a,cachedResponse:r})=>{if(!r)return null;const i=this._isResponseDateFresh(r),c=this._getCacheExpiration(a);V(c.expireEntries());const o=c.updateTimestamp(s.url);if(t)try{t.waitUntil(o)}catch{}return i?r:null},this.cacheDidUpdate=async({cacheName:t,request:s})=>{const a=this._getCacheExpiration(t);await a.updateTimestamp(s.url),await a.expireEntries()},this._config=e,this._maxAgeSeconds=e.maxAgeSeconds,this._cacheExpirations=new Map,e.purgeOnQuotaError&&Oe(()=>this.deleteCacheAndMetadata())}_getCacheExpiration(e){if(e===b.getRuntimeName())throw new l("expire-custom-caches-only");let t=this._cacheExpirations.get(e);return t||(t=new Ae(e,this._config),this._cacheExpirations.set(e,t)),t}_isResponseDateFresh(e){if(!this._maxAgeSeconds)return!0;const t=this._getDateHeaderTimestamp(e);if(t===null)return!0;const s=Date.now();return t>=s-this._maxAgeSeconds*1e3}_getDateHeaderTimestamp(e){if(!e.headers.has("date"))return null;const t=e.headers.get("date"),a=new Date(t).getTime();return isNaN(a)?null:a}async deleteCacheAndMetadata(){for(const[e,t]of this._cacheExpirations)await self.caches.delete(e),await t.delete();this._cacheExpirations=new Map}}Re([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"446a97c5170635104f065df281f52c82","url":"index.html"},{"revision":"d88e2aee9b0c1880369e3096b3e8c16b","url":"icons/icon.svg"},{"revision":"8f4b7991b482aa3b6e81505e20cf3ade","url":"icons/icon-512.png"},{"revision":"47445f87979d6df272fe850dcd2dedac","url":"icons/icon-192.png"},{"revision":null,"url":"assets/index-DCDkIi8X.css"},{"revision":null,"url":"assets/index-CTOb8Hzh.js"},{"revision":"47445f87979d6df272fe850dcd2dedac","url":"icons/icon-192.png"},{"revision":"8f4b7991b482aa3b6e81505e20cf3ade","url":"icons/icon-512.png"},{"revision":"964f70d27a7182c01f67415dcc656426","url":"manifest.webmanifest"}]);ye();N(({url:n})=>n.hostname==="cdn.jsdelivr.net"&&n.pathname.endsWith(".ttf"),new be({cacheName:"termbeam-fonts",plugins:[new We({maxEntries:5,maxAgeSeconds:365*24*60*60})]}));N(({url:n})=>n.pathname.startsWith("/api/"),new Ce);self.addEventListener("install",()=>{self.skipWaiting()});self.addEventListener("activate",n=>{n.waitUntil(self.clients.claim())});
|
package/src/routes.js
CHANGED
|
@@ -7,27 +7,10 @@ const { detectShells } = require('./shells');
|
|
|
7
7
|
const log = require('./logger');
|
|
8
8
|
const rateLimit = require('express-rate-limit');
|
|
9
9
|
|
|
10
|
+
const REACT_DIR = path.join(__dirname, '..', 'public-react');
|
|
10
11
|
const PUBLIC_DIR = path.join(__dirname, '..', 'public');
|
|
11
12
|
const uploadedFiles = new Map(); // id -> filepath
|
|
12
13
|
|
|
13
|
-
const pageRateLimit = rateLimit({
|
|
14
|
-
windowMs: 1 * 60 * 1000,
|
|
15
|
-
max: 120,
|
|
16
|
-
standardHeaders: true,
|
|
17
|
-
legacyHeaders: false,
|
|
18
|
-
handler: (_req, res) =>
|
|
19
|
-
res.status(429).json({ error: 'Too many requests, please try again later.' }),
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const apiRateLimit = rateLimit({
|
|
23
|
-
windowMs: 1 * 60 * 1000,
|
|
24
|
-
max: 120,
|
|
25
|
-
standardHeaders: true,
|
|
26
|
-
legacyHeaders: false,
|
|
27
|
-
handler: (_req, res) =>
|
|
28
|
-
res.status(429).json({ error: 'Too many requests, please try again later.' }),
|
|
29
|
-
});
|
|
30
|
-
|
|
31
14
|
const IMAGE_SIGNATURES = [
|
|
32
15
|
{ type: 'image/png', bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a] },
|
|
33
16
|
{ type: 'image/jpeg', bytes: [0xff, 0xd8, 0xff] },
|
|
@@ -53,7 +36,26 @@ function validateMagicBytes(buffer, contentType) {
|
|
|
53
36
|
}
|
|
54
37
|
|
|
55
38
|
function setupRoutes(app, { auth, sessions, config, state }) {
|
|
56
|
-
|
|
39
|
+
const pageRateLimit = rateLimit({
|
|
40
|
+
windowMs: 1 * 60 * 1000,
|
|
41
|
+
max: 120,
|
|
42
|
+
standardHeaders: true,
|
|
43
|
+
legacyHeaders: false,
|
|
44
|
+
handler: (_req, res) =>
|
|
45
|
+
res.status(429).json({ error: 'Too many requests, please try again later.' }),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const apiRateLimit = rateLimit({
|
|
49
|
+
windowMs: 1 * 60 * 1000,
|
|
50
|
+
max: 120,
|
|
51
|
+
standardHeaders: true,
|
|
52
|
+
legacyHeaders: false,
|
|
53
|
+
handler: (_req, res) =>
|
|
54
|
+
res.status(429).json({ error: 'Too many requests, please try again later.' }),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Serve static files — React build first, then public for shared assets (icons, manifest)
|
|
58
|
+
app.use(express.static(REACT_DIR, { index: false }));
|
|
57
59
|
app.use(express.static(PUBLIC_DIR, { index: false }));
|
|
58
60
|
|
|
59
61
|
// Login page
|
|
@@ -134,12 +136,12 @@ function setupRoutes(app, { auth, sessions, config, state }) {
|
|
|
134
136
|
next();
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
// Pages
|
|
139
|
+
// Pages — always serve React SPA
|
|
138
140
|
app.get('/', pageRateLimit, autoLogin, auth.middleware, (_req, res) =>
|
|
139
|
-
res.sendFile('index.html', { root:
|
|
141
|
+
res.sendFile('index.html', { root: REACT_DIR }),
|
|
140
142
|
);
|
|
141
143
|
app.get('/terminal', pageRateLimit, autoLogin, auth.middleware, (_req, res) =>
|
|
142
|
-
res.sendFile('
|
|
144
|
+
res.sendFile('index.html', { root: REACT_DIR }),
|
|
143
145
|
);
|
|
144
146
|
|
|
145
147
|
// Share token — generates a temporary share token for the share button
|
package/src/server.js
CHANGED
|
@@ -275,10 +275,15 @@ function createTermBeamServer(overrides = {}) {
|
|
|
275
275
|
console.log('');
|
|
276
276
|
|
|
277
277
|
// Non-blocking update check — runs after banner, never delays startup.
|
|
278
|
-
// Skip under the Node test runner to avoid network requests in tests.
|
|
278
|
+
// Skip under the Node test runner and CI to avoid network requests in tests.
|
|
279
279
|
// Accept any version containing a semver-like pattern (including dev builds).
|
|
280
280
|
const versionParts = config.version.match(/(\d{1,10})\.(\d{1,10})\.(\d{1,10})/);
|
|
281
|
-
if (
|
|
281
|
+
if (
|
|
282
|
+
versionParts &&
|
|
283
|
+
!process.env.NODE_TEST_CONTEXT &&
|
|
284
|
+
!process.env.CI &&
|
|
285
|
+
!process.argv.includes('--test')
|
|
286
|
+
) {
|
|
282
287
|
const installInfo = detectInstallMethod();
|
|
283
288
|
checkForUpdate({ currentVersion: config.version })
|
|
284
289
|
.then((info) => {
|
package/src/sessions.js
CHANGED
|
@@ -144,9 +144,15 @@ class SessionManager {
|
|
|
144
144
|
ptyProcess.onData((data) => {
|
|
145
145
|
session.lastActivity = Date.now();
|
|
146
146
|
session.scrollbackBuf += data;
|
|
147
|
-
//
|
|
148
|
-
if (session.scrollbackBuf.length >
|
|
149
|
-
|
|
147
|
+
// High/low water scrollback cap: trim to 500k chars when buffer exceeds 1,000,000 chars
|
|
148
|
+
if (session.scrollbackBuf.length > 1000000) {
|
|
149
|
+
let buf = session.scrollbackBuf.slice(-500000);
|
|
150
|
+
// Advance to first newline to avoid starting mid-line
|
|
151
|
+
const nlIdx = buf.indexOf('\n');
|
|
152
|
+
if (nlIdx > 0 && nlIdx < 200) {
|
|
153
|
+
buf = buf.slice(nlIdx + 1);
|
|
154
|
+
}
|
|
155
|
+
session.scrollbackBuf = buf;
|
|
150
156
|
}
|
|
151
157
|
for (const ws of session.clients) {
|
|
152
158
|
if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'output', data }));
|
package/src/version.js
CHANGED
|
@@ -10,17 +10,35 @@ function getVersion() {
|
|
|
10
10
|
return base;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
// Running from source —
|
|
13
|
+
// Running from source — git tags are the version source of truth.
|
|
14
|
+
// This avoids drift between package.json and tagged releases.
|
|
14
15
|
try {
|
|
15
16
|
const gitDesc = execSync('git describe --tags --always --dirty', {
|
|
16
17
|
cwd: path.join(__dirname, '..'),
|
|
17
18
|
encoding: 'utf-8',
|
|
18
19
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
19
20
|
}).trim();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
|
|
22
|
+
const tagMatch = gitDesc.match(/^v(\d+\.\d+\.\d+)(?:-(\d+)-g([0-9a-f]+))?(-dirty)?$/);
|
|
23
|
+
if (tagMatch) {
|
|
24
|
+
const gitVersion = tagMatch[1];
|
|
25
|
+
const commits = tagMatch[2];
|
|
26
|
+
const hash = tagMatch[3];
|
|
27
|
+
const dirty = tagMatch[4];
|
|
28
|
+
|
|
29
|
+
// Exactly on a clean tag — return the tag version
|
|
30
|
+
if (!commits && !dirty) return gitVersion;
|
|
31
|
+
|
|
32
|
+
// Build a combined semver-style dev string
|
|
33
|
+
let ver = `${gitVersion}-dev`;
|
|
34
|
+
if (commits) ver += `.${commits}`;
|
|
35
|
+
const meta = [hash ? `g${hash}` : null, dirty ? 'dirty' : null].filter(Boolean).join('.');
|
|
36
|
+
if (meta) ver += `+${meta}`;
|
|
37
|
+
return ver;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// No semver tag found (e.g. bare commit hash) — fall back to package.json
|
|
41
|
+
return `${base}-dev+${gitDesc}`;
|
|
24
42
|
} catch {
|
|
25
43
|
return `${base}-dev`;
|
|
26
44
|
}
|
package/src/websocket.js
CHANGED
|
@@ -1,14 +1,55 @@
|
|
|
1
1
|
const log = require('./logger');
|
|
2
2
|
|
|
3
|
+
const ACTIVE_THRESHOLD = 60000; // 60 seconds
|
|
4
|
+
|
|
5
|
+
// OSC color query/response sequences (OSC 4/10/11/12) cause garbled output
|
|
6
|
+
// on replay: color queries trigger xterm.js to generate responses that echo
|
|
7
|
+
// through the PTY as visible text, accumulating on each refresh.
|
|
8
|
+
const OSC_COLOR_RE = /\x1b\](?:4;\d+|10|11|12);[^\x07\x1b]*(?:\x07|\x1b\\)/g;
|
|
9
|
+
|
|
10
|
+
// Alternate screen buffer sequences (DECSET/DECRST 1049, 1047, 47) cause
|
|
11
|
+
// screen wipes on replay: entering alt screen hides the normal buffer content
|
|
12
|
+
// and re-entering on replay makes the terminal appear blank/wiped.
|
|
13
|
+
// Matched enter+exit pairs are stripped along with their content (the alt
|
|
14
|
+
// screen output is no longer relevant after the program exits).
|
|
15
|
+
// Unmatched enters/exits are stripped as bare sequences.
|
|
16
|
+
const ALT_SCREEN_PAIR_RE = /\x1b\[\?(1049|1047|47)h[\s\S]*?\x1b\[\?\1l/g;
|
|
17
|
+
const ALT_SCREEN_BARE_RE = /\x1b\[\?(?:1049|1047|47)[hl]/g;
|
|
18
|
+
|
|
19
|
+
// Clear-scrollback (ESC[3J) is destructive on replay — it would wipe
|
|
20
|
+
// the xterm.js scrollback that the user might want to scroll through.
|
|
21
|
+
const CLEAR_SCROLLBACK_RE = /\x1b\[3J/g;
|
|
22
|
+
|
|
23
|
+
function sanitizeForReplay(buf) {
|
|
24
|
+
buf = buf.replace(OSC_COLOR_RE, '');
|
|
25
|
+
buf = buf.replace(ALT_SCREEN_PAIR_RE, '');
|
|
26
|
+
buf = buf.replace(ALT_SCREEN_BARE_RE, '');
|
|
27
|
+
buf = buf.replace(CLEAR_SCROLLBACK_RE, '');
|
|
28
|
+
return buf;
|
|
29
|
+
}
|
|
30
|
+
|
|
3
31
|
function recalcPtySize(session) {
|
|
4
|
-
|
|
5
|
-
let
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
let activeCols = Infinity;
|
|
34
|
+
let activeRows = Infinity;
|
|
35
|
+
let allCols = Infinity;
|
|
36
|
+
let allRows = Infinity;
|
|
37
|
+
let hasActive = false;
|
|
38
|
+
|
|
6
39
|
for (const client of session.clients) {
|
|
7
|
-
if (client._dims)
|
|
8
|
-
|
|
9
|
-
|
|
40
|
+
if (!client._dims) continue;
|
|
41
|
+
allCols = Math.min(allCols, client._dims.cols);
|
|
42
|
+
allRows = Math.min(allRows, client._dims.rows);
|
|
43
|
+
if (client._lastActivity && now - client._lastActivity < ACTIVE_THRESHOLD) {
|
|
44
|
+
activeCols = Math.min(activeCols, client._dims.cols);
|
|
45
|
+
activeRows = Math.min(activeRows, client._dims.rows);
|
|
46
|
+
hasActive = true;
|
|
10
47
|
}
|
|
11
48
|
}
|
|
49
|
+
|
|
50
|
+
const minCols = hasActive ? activeCols : allCols;
|
|
51
|
+
const minRows = hasActive ? activeRows : allRows;
|
|
52
|
+
|
|
12
53
|
if (minCols === Infinity || minRows === Infinity) return;
|
|
13
54
|
if (minCols === session._lastCols && minRows === session._lastRows) return;
|
|
14
55
|
session._lastCols = minCols;
|
|
@@ -39,6 +80,11 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
39
80
|
}
|
|
40
81
|
}
|
|
41
82
|
|
|
83
|
+
const pingInterval = setInterval(() => {
|
|
84
|
+
if (ws.readyState === 1) ws.ping();
|
|
85
|
+
}, 30000);
|
|
86
|
+
if (typeof pingInterval.unref === 'function') pingInterval.unref();
|
|
87
|
+
|
|
42
88
|
let authenticated = !auth.password;
|
|
43
89
|
let attached = null;
|
|
44
90
|
|
|
@@ -95,6 +141,7 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
95
141
|
log.warn(`WS: attach failed — session ${msg.sessionId} not found`);
|
|
96
142
|
return;
|
|
97
143
|
}
|
|
144
|
+
ws._lastActivity = Date.now();
|
|
98
145
|
attached = session;
|
|
99
146
|
// First client: defer adding to session.clients until after the
|
|
100
147
|
// first resize so we can decide whether the PTY needs resizing.
|
|
@@ -104,7 +151,9 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
104
151
|
} else {
|
|
105
152
|
session.clients.add(ws);
|
|
106
153
|
if (session.scrollbackBuf.length > 0) {
|
|
107
|
-
ws.send(
|
|
154
|
+
ws.send(
|
|
155
|
+
JSON.stringify({ type: 'output', data: sanitizeForReplay(session.scrollbackBuf) }),
|
|
156
|
+
);
|
|
108
157
|
}
|
|
109
158
|
}
|
|
110
159
|
ws.send(JSON.stringify({ type: 'attached', sessionId: msg.sessionId }));
|
|
@@ -115,12 +164,14 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
115
164
|
if (!attached) return;
|
|
116
165
|
|
|
117
166
|
if (msg.type === 'input') {
|
|
167
|
+
ws._lastActivity = Date.now();
|
|
118
168
|
attached.pty.write(msg.data);
|
|
119
169
|
} else if (msg.type === 'resize') {
|
|
120
170
|
const cols = Math.floor(msg.cols);
|
|
121
171
|
const rows = Math.floor(msg.rows);
|
|
122
172
|
if (cols > 0 && cols <= 500 && rows > 0 && rows <= 200) {
|
|
123
173
|
ws._dims = { cols, rows };
|
|
174
|
+
ws._lastActivity = Date.now();
|
|
124
175
|
if (ws._pendingResize) {
|
|
125
176
|
ws._pendingResize = false;
|
|
126
177
|
// Only discard scrollback and send SIGWINCH if the PTY was
|
|
@@ -136,7 +187,12 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
136
187
|
} else {
|
|
137
188
|
attached.clients.add(ws);
|
|
138
189
|
if (attached.scrollbackBuf.length > 0) {
|
|
139
|
-
ws.send(
|
|
190
|
+
ws.send(
|
|
191
|
+
JSON.stringify({
|
|
192
|
+
type: 'output',
|
|
193
|
+
data: sanitizeForReplay(attached.scrollbackBuf),
|
|
194
|
+
}),
|
|
195
|
+
);
|
|
140
196
|
}
|
|
141
197
|
}
|
|
142
198
|
} else {
|
|
@@ -150,6 +206,7 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
150
206
|
});
|
|
151
207
|
|
|
152
208
|
ws.on('close', () => {
|
|
209
|
+
clearInterval(pingInterval);
|
|
153
210
|
if (attached) {
|
|
154
211
|
attached.clients.delete(ws);
|
|
155
212
|
recalcPtySize(attached);
|
|
@@ -159,4 +216,4 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
159
216
|
});
|
|
160
217
|
}
|
|
161
218
|
|
|
162
|
-
module.exports = { setupWebSocket };
|
|
219
|
+
module.exports = { setupWebSocket, ACTIVE_THRESHOLD, sanitizeForReplay };
|