nterminal 1.2.29 → 1.2.31

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.
@@ -0,0 +1 @@
1
+ .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;inset:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.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;inset:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{font-family:monospace;-webkit-user-select:text;user-select:text;white-space:pre}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content}.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}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;background:#0000;transition:opacity .1s linear;z-index:11}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}@font-face{font-family:MesloLGS NF;src:url(/assets/MesloLGS-NF-Regular-Cxr8pvCI.ttf) format("truetype");font-display:block;font-weight:400;font-style:normal}@font-face{font-family:MesloLGS NF;src:url(/assets/MesloLGS-NF-Bold-kN-HYz-g.ttf) format("truetype");font-display:block;font-weight:700;font-style:normal}@font-face{font-family:MesloLGS NF;src:url(/assets/MesloLGS-NF-Italic-CMg1T6-G.ttf) format("truetype");font-display:block;font-weight:400;font-style:italic}@font-face{font-family:MesloLGS NF;src:url(/assets/MesloLGS-NF-Bold-Italic-DwFsXcwX.ttf) format("truetype");font-display:block;font-weight:700;font-style:italic}:root{color:var(--nt-fg);background:var(--nt-bg);font-family:var(--nt-font-ui);font-synthesis:none;text-rendering:optimizeLegibility;text-size-adjust:100%;-webkit-text-size-adjust:100%;--nterminal-viewport-height: 100vh;--nterminal-keyboard-inset-bottom: 0px;--nterminal-safe-area-bottom: env(safe-area-inset-bottom, 0px);--nt-bg: #0a0d10;--nt-bg-2: #0d1216;--nt-bg-sidebar: #08090b;--nt-line: #1a2128;--nt-line-2: #2a3340;--nt-fg: #e2e8ee;--nt-fg-2: #8896a3;--nt-fg-3: #54616d;--nt-accent: #5eead4;--nt-accent-soft: rgba(94, 234, 212, .1);--nt-accent-glow: rgba(94, 234, 212, .55);--nt-ok: #34d399;--nt-danger: #f87171;--nt-warning: #fbbf24;--nt-font-ui: "Inter", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;--nt-font-mono: "JetBrains Mono", "MesloLGS NF", ui-monospace, "SF Mono", monospace}@supports (height: 100dvh){:root{--nterminal-viewport-height: 100dvh}}*{box-sizing:border-box}html,body,#root{width:100%;min-width:320px;height:100%;min-height:100%;margin:0;overflow:hidden;overscroll-behavior:none}body{overflow:hidden;background:var(--nt-bg);touch-action:manipulation}button,input,textarea,select{font:inherit}button{color:inherit}button:disabled{cursor:not-allowed;opacity:.42}input{min-width:0;outline:none}textarea{min-width:0;resize:none;outline:none}.terminal-shell{display:grid;grid-template-columns:232px minmax(0,1fr);width:100%;height:var(--nterminal-viewport-height);min-height:0;overflow:hidden;padding-bottom:var(--nterminal-safe-area-bottom);background:var(--nt-bg);transition:grid-template-columns .16s ease}.terminal-shell[data-collapsed=true]{grid-template-columns:56px minmax(0,1fr)}.workspace-backdrop{display:none}.workspace-sidebar{display:grid;grid-template-rows:auto 1fr auto;min-width:0;min-height:0;border-right:1px solid var(--nt-line);background:var(--nt-bg-sidebar)}.workspace-brand{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;padding:16px 16px 14px}.terminal-shell[data-collapsed=true] .workspace-brand{grid-template-columns:1fr;justify-items:center;gap:6px;padding:14px 0 12px}.workspace-brand-name{color:var(--nt-fg);font-size:13px;font-weight:600;letter-spacing:.02em}.nterminal-mark,.nterminal-wordmark{display:block;flex:0 0 auto}.sidebar-collapse-button,.sidebar-drawer-close-button,.workspace-sidebar-header button,.mobile-menu-button,.tab-add-button,.pane-action-button,.icon-button,.workspace-footer-icon-button,.workspace-close-button,.terminal-pane-close,.empty-workspace button,.secondary-button{display:grid;place-items:center;border:1px solid transparent;padding:0;color:var(--nt-fg-3);background:transparent;cursor:pointer}.sidebar-collapse-button,.sidebar-drawer-close-button{width:22px;height:22px;border-radius:4px}.sidebar-drawer-close-button{display:none}.terminal-shell[data-collapsed=true] .sidebar-collapse-button{width:28px;height:24px}.sidebar-collapse-button:hover:not(:disabled),.sidebar-drawer-close-button:hover:not(:disabled),.workspace-sidebar-header button:hover:not(:disabled),.mobile-menu-button:hover:not(:disabled),.pane-action-button:hover:not(:disabled),.icon-button:hover:not(:disabled),.workspace-footer-icon-button:hover:not(:disabled),.empty-workspace button:hover:not(:disabled),.secondary-button:hover:not(:disabled){color:var(--nt-fg);background:var(--nt-bg-2)}.workspace-nav{display:grid;grid-template-rows:auto 1fr;min-height:0}.workspace-sidebar-content{display:grid;grid-template-rows:auto auto minmax(0,1fr);min-width:0;min-height:0}.workspace-sidebar-servers{border-bottom:1px solid var(--nt-line);padding:0 12px 10px}.server-list{display:flex;flex-direction:column;gap:6px}.server-list-toolbar{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px;padding:2px;border:1px solid var(--nt-line);border-radius:6px;background:var(--nt-bg)}.server-list-view-button{height:24px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--nt-fg-3);font-size:10px;font-weight:650;cursor:pointer}.server-list-view-button:hover{color:var(--nt-fg);background:var(--nt-bg-2)}.server-list-view-button[aria-pressed=true]{border-color:color-mix(in srgb,var(--nt-accent) 40%,transparent);color:var(--nt-accent);background:var(--nt-accent-soft)}.server-list-row{display:flex;align-items:center;gap:4px}.server-list-item{display:grid;grid-template-rows:auto auto;gap:7px;flex:1;min-width:0;min-height:86px;padding:8px;border:1px solid transparent;border-radius:5px;background:transparent;color:var(--nt-fg-3);font-size:12px;cursor:pointer;text-align:left}.server-list-item-header{display:flex;align-items:center;gap:8px;min-width:0}.server-list-item:hover{background:var(--nt-bg-2);color:var(--nt-fg)}.server-list-item--active{border-color:color-mix(in srgb,var(--nt-accent) 55%,transparent);color:var(--nt-accent);background:var(--nt-accent-soft)}.server-list-dot{width:7px;height:7px;border-radius:50%;background:currentColor;flex-shrink:0}.server-list-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.server-list-stats{font-size:10px;font-variant-numeric:tabular-nums;color:var(--nt-fg-3)}.server-list-stats-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px;width:100%}.server-list-stats--offline{display:flex;align-items:center;min-height:48px;padding:0 6px;border:1px solid var(--nt-line);border-radius:5px;color:var(--nt-danger);background:var(--nt-bg)}.server-list-stat-cell{min-width:0;padding:5px 6px;border:1px solid var(--nt-line);border-radius:5px;background:var(--nt-bg)}.server-list-stat-label,.server-list-stat-value,.server-list-stat-detail{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.server-list-stat-label{color:var(--nt-fg-3);font-size:9px;letter-spacing:0}.server-list-stat-value{margin-top:2px;color:var(--nt-fg);font-size:11px;font-weight:650}.server-list-stat-detail{margin-top:1px;color:var(--nt-fg-3);font-size:9px}.server-list-item--active .server-list-stats{color:var(--nt-accent)}.server-list-item--active .server-list-stat-cell{border-color:color-mix(in srgb,var(--nt-accent) 24%,var(--nt-line))}.server-list-item--active .server-list-stat-value{color:var(--nt-accent)}.server-list-stat-value[data-tone=warning]{color:var(--nt-warning)}.server-list-stat-value[data-tone=danger]{color:var(--nt-danger)}.server-list--list .server-list-item{grid-template-rows:auto;min-height:34px;padding:7px 8px}.server-list--compact{display:flex;flex-direction:column;align-items:center;gap:6px;padding:8px 0}.server-list-chip{position:relative;display:grid;place-items:center;width:36px;height:36px;border:1px solid transparent;border-radius:8px;background:var(--nt-bg-2);color:var(--nt-fg-3);font-size:14px;font-weight:600;cursor:pointer;padding:0}.server-list-chip:hover{color:var(--nt-fg);border-color:var(--nt-line)}.server-list-chip--active{border-color:color-mix(in srgb,var(--nt-accent) 55%,transparent);color:var(--nt-accent);background:var(--nt-accent-soft)}.server-list-chip-initial{line-height:1}.server-list-chip-pip{position:absolute;right:3px;bottom:3px;width:7px;height:7px;border-radius:50%;border:1px solid var(--nt-bg-sidebar)}.server-list-chip-pip[data-status=ok]{background:var(--nt-accent)}.server-list-chip-pip[data-status=offline]{background:var(--nt-danger)}.server-list-chip-pip[data-status=unknown]{background:var(--nt-fg-3);opacity:.4}.workspace-nav--collapsed{padding:0}.server-list-editor{flex:1 1 auto;display:flex;align-items:center;min-width:0}.server-list-editor input{width:100%;height:28px;padding:0 22px 0 10px;border-radius:6px;border:1px solid var(--nt-accent);background:var(--nt-bg-2);color:var(--nt-fg);font:inherit;outline:none}.server-list-remove{width:22px;height:22px;border:none;border-radius:4px;background:transparent;color:var(--nt-fg-3);font-size:16px;line-height:1;cursor:pointer;flex-shrink:0}.server-list-remove:hover{color:var(--nt-danger);background:var(--nt-bg-2)}.server-list-add{height:28px;border:1px dashed var(--nt-line);border-radius:5px;background:transparent;color:var(--nt-fg-3);font-size:11px;cursor:pointer}.server-list-add:hover{color:var(--nt-accent);border-color:color-mix(in srgb,var(--nt-accent) 55%,transparent)}.server-add-form{display:flex;flex-direction:column;gap:6px;padding:8px 0 2px}.server-add-form input{height:28px;padding:0 8px;border:1px solid var(--nt-line);border-radius:5px;background:var(--nt-bg-2);color:var(--nt-fg);font-size:12px}.server-add-actions{display:flex;gap:6px}.server-add-actions button{flex:1;height:28px;font-size:11px}.totp-setup{display:flex;flex-direction:column;gap:14px}.totp-setup-instructions{color:var(--nt-fg-2);font-size:13px;line-height:1.5}.totp-qr-container{display:flex;justify-content:center;padding:12px;background:#fff;border-radius:8px;align-self:center}.totp-secret-details{font-size:12px;color:var(--nt-fg-3)}.totp-secret-text{margin-top:6px;padding:8px;border-radius:5px;background:var(--nt-bg-2);font-family:var(--nt-mono, monospace);letter-spacing:.1em;word-break:break-all;color:var(--nt-fg)}.auth-checkbox{display:inline-flex;flex-direction:row;align-items:center;gap:8px;font-size:12.5px;line-height:1.4;color:var(--nt-fg-2);cursor:pointer;-webkit-user-select:none;user-select:none}input[type=checkbox]{appearance:none;-webkit-appearance:none;width:16px;height:16px;margin:0;flex:0 0 16px;border:1px solid var(--nt-line-2);border-radius:4px;background:var(--nt-bg-2);cursor:pointer;position:relative;transition:border-color .12s ease,background .12s ease}input[type=checkbox]:hover{border-color:var(--nt-accent)}input[type=checkbox]:focus-visible{outline:none;border-color:var(--nt-accent);box-shadow:0 0 0 2px var(--nt-accent-soft)}input[type=checkbox]:checked{background:var(--nt-accent);border-color:var(--nt-accent)}input[type=checkbox]:checked:after{content:"";position:absolute;left:4px;top:1px;width:5px;height:9px;border:solid var(--nt-bg);border-width:0 2px 2px 0;transform:rotate(45deg)}input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}.workspace-sidebar-footer-actions{display:flex;flex:0 0 auto;gap:5px}.security-overlay{position:fixed;inset:0;background:#0000008c;display:flex;align-items:center;justify-content:center;z-index:50;padding:16px}.security-panel{width:100%;max-width:520px;max-height:85vh;overflow:auto;background:var(--nt-bg-2);border:1px solid var(--nt-line);border-radius:10px;padding:20px;display:flex;flex-direction:column;gap:18px}.settings-panel{max-width:min(1080px,calc(100vw - 32px));height:min(86vh,820px);max-height:min(86vh,820px);overflow:hidden;padding:0;gap:0}.settings-panel .security-panel-header{flex:0 0 auto;padding:18px 20px;border-bottom:1px solid var(--nt-line)}.settings-layout{display:grid;grid-template-columns:180px minmax(0,1fr);flex:1 1 auto;min-height:0;overflow:hidden}.settings-nav{display:flex;flex-direction:column;gap:4px;min-height:0;padding:12px;border-right:1px solid var(--nt-line);background:color-mix(in srgb,var(--nt-bg) 52%,transparent)}.settings-nav button{position:relative;height:32px;border:0;border-left:2px solid transparent;border-radius:5px;padding:0 10px;color:var(--nt-fg-2);background:transparent;text-align:left;font-size:12px;cursor:pointer}.settings-nav button:hover{color:var(--nt-fg);border-left-color:var(--nt-accent);background:var(--nt-accent-soft)}.settings-nav button[data-active=true]{color:var(--nt-accent);border-left-color:var(--nt-accent);background:var(--nt-accent-soft)}.settings-content{min-height:0;max-height:calc(min(86vh,820px) - 66px);overflow:auto;padding:18px 22px 22px;scroll-behavior:smooth;overscroll-behavior:contain}.settings-section,.settings-section-group>.security-section{scroll-margin-top:16px;padding-bottom:18px;border-bottom:1px solid var(--nt-line)}.settings-section+.settings-section,.settings-section+.settings-section-group,.settings-section-group+.settings-section-group{margin-top:18px}.settings-section-group:last-child>.security-section{border-bottom:0;padding-bottom:0}.settings-section-heading{display:flex;align-items:center;justify-content:space-between;gap:12px}.security-panel-header{display:flex;align-items:center;justify-content:space-between}.security-panel-header h2{margin:0;font-size:16px;color:var(--nt-fg)}.security-close{width:28px;height:28px;border:none;border-radius:5px;background:transparent;color:var(--nt-fg-2);font-size:20px;cursor:pointer}.security-close:hover{background:var(--nt-bg);color:var(--nt-fg)}.security-section h3{margin:0 0 4px;font-size:13px;color:var(--nt-fg)}.settings-control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;align-items:center;margin-top:10px;color:var(--nt-fg-2);font-size:12px}.settings-control-value{color:var(--nt-accent);font-family:var(--nt-font-mono);font-size:12px;font-variant-numeric:tabular-nums}.settings-control input[type=range]{grid-column:1 / -1;width:100%;accent-color:var(--nt-accent)}.settings-control select{grid-column:1 / -1;width:100%;height:30px;border:1px solid var(--nt-line-2);border-radius:5px;padding:0 8px;color:var(--nt-fg);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:12px}.settings-theme-swatches{display:flex;gap:8px;margin-top:8px}.settings-theme-swatch{width:28px;height:18px;border:1px solid var(--nt-line-2);border-radius:5px;background:linear-gradient(90deg,var(--settings-theme-accent) 0 50%,var(--nt-bg) 50% 100%)}.settings-theme-swatch[data-active=true]{border-color:var(--settings-theme-accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--settings-theme-accent) 18%,transparent)}.settings-checkbox{margin-top:12px}.settings-stepper-row{display:grid;grid-template-columns:32px minmax(72px,1fr) 32px 60px;gap:8px;align-items:center;margin-top:10px}.settings-duration-row{grid-template-columns:minmax(72px,1fr) 60px}.settings-stepper-row button{height:28px;border-radius:5px}.settings-stepper-button{width:32px;font-family:var(--nt-font-mono);font-size:13px;line-height:1}.settings-reset-button{padding:0 9px;font-family:var(--nt-font-mono);font-size:10.5px;letter-spacing:0}.settings-shortcut-list{display:grid;gap:8px;margin:12px 0 0;padding:0;list-style:none}.settings-shortcut-list li{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:12px;min-height:42px;border:1px solid var(--nt-line);border-radius:6px;padding:8px 10px}.settings-shortcut-label{display:block;color:var(--nt-fg);font-size:12px}.settings-shortcut-group{display:block;margin-top:2px;color:var(--nt-fg-3);font-size:10.5px}.settings-shortcut-actions{display:flex;align-items:center;gap:6px}.settings-shortcut-recorder{min-width:148px;height:28px;border:1px solid var(--nt-line-2);border-radius:5px;padding:0 9px;color:var(--nt-accent);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:11px;cursor:pointer}.settings-shortcut-recorder[data-recording=true]{border-color:var(--nt-accent);background:var(--nt-accent-soft);box-shadow:0 0 0 2px color-mix(in srgb,var(--nt-accent) 16%,transparent)}.settings-shortcut-reset{height:28px;padding:0 9px;border-radius:5px;font-family:var(--nt-font-mono);font-size:10.5px}.settings-stepper-row input{width:100%;height:28px;border:1px solid var(--nt-line-2);border-radius:5px;padding:0 8px;color:var(--nt-fg);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:12px}.settings-assets-header{display:flex;justify-content:space-between;gap:10px;margin-top:14px;color:var(--nt-fg-2);font-size:12px}.settings-assets-header span:last-child{color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:11px}.settings-asset-input{display:none}.settings-upload-button{width:100%;height:30px;margin-top:8px;border-radius:5px;font-size:11px}.settings-error{margin:8px 0 0;color:var(--nt-danger);font-size:11px}.settings-status{margin:8px 0 0;color:var(--nt-fg-2);font-size:11px}.settings-asset-list{display:grid;gap:8px;margin:10px 0 0;padding:0;list-style:none}.settings-asset-list li{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px;min-height:58px;border:1px solid var(--nt-line);border-radius:6px;padding:6px;background:#ffffff05}.settings-asset-list li[data-enabled=true]{border-color:color-mix(in srgb,var(--nt-accent) 24%,transparent);background:color-mix(in srgb,var(--nt-accent) 3.5%,transparent)}.settings-asset-choice{display:grid;grid-template-columns:16px 64px minmax(0,1fr);align-items:center;gap:8px;min-width:0;cursor:pointer}.settings-asset-choice input{width:14px;height:14px;margin:0;accent-color:var(--nt-accent)}.settings-asset-list video{width:64px;height:46px;object-fit:contain;pointer-events:none}.settings-asset-copy,.settings-asset-copy span,.settings-asset-copy small{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.settings-asset-copy{display:grid;gap:3px}.settings-asset-copy span{color:var(--nt-fg);font-size:11px}.settings-asset-copy small{color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10px}.settings-asset-delete{display:grid;width:24px;height:24px;place-items:center;border-radius:4px;padding:0;font-size:11px}.settings-save-assets-button{width:100%;height:30px;margin-top:8px;border-radius:5px;font-size:11px}.security-list{list-style:none;margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:6px}.security-list li{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 10px;border:1px solid var(--nt-line);border-radius:6px}.server-row-main{display:grid;min-width:0;gap:7px}.security-item-label{display:flex;align-items:center;gap:8px;color:var(--nt-fg);font-size:13px}.security-item-meta{display:block;color:var(--nt-fg-3);font-size:11px;margin-top:2px}.security-badge{font-size:10px;padding:1px 6px;border-radius:999px;background:var(--nt-accent-soft);color:var(--nt-accent)}.security-remove{border:1px solid var(--nt-line);border-radius:5px;background:transparent;color:var(--nt-danger);font-size:11px;padding:4px 10px;cursor:pointer}.security-remove:hover{background:var(--nt-bg)}.security-add-form{display:flex;gap:6px;margin-top:10px;flex-wrap:wrap}.security-add-form input{flex:1;min-width:120px;height:30px;padding:0 8px;border:1px solid var(--nt-line);border-radius:5px;background:var(--nt-bg);color:var(--nt-fg);font-size:12px}.security-add-form button{height:30px;padding:0 14px}.update-actions{display:flex;gap:8px}.update-actions button{height:30px;padding:0 14px;border-radius:5px;font-family:var(--nt-font-mono);font-size:12px;cursor:pointer;transition:color .12s ease,background .12s ease,border-color .12s ease,box-shadow .12s ease}.update-actions button:not(.secondary-button){border:1px solid var(--nt-accent);color:var(--nt-accent);background:var(--nt-accent-soft)}.update-actions button:not(.secondary-button):hover:not(:disabled){color:#062423;background:var(--nt-accent);box-shadow:0 0 16px var(--nt-accent-glow)}.server-reload-required{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 10px;border:1px solid color-mix(in srgb,var(--nt-accent) 38%,transparent);border-radius:6px;color:var(--nt-fg);background:var(--nt-accent-soft);font-size:12px}.server-reload-required button{flex:0 0 auto;height:28px;padding:0 12px;border:1px solid var(--nt-accent);border-radius:5px;color:var(--nt-accent);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:11px;cursor:pointer}.security-list .secondary-button{height:28px;padding:0 12px;border-radius:5px;font-family:var(--nt-font-mono);font-size:11px}.server-row-actions{display:flex;align-items:center;gap:6px;flex:0 0 auto}.server-manual-update{display:grid;max-width:min(460px,100%);gap:6px;color:var(--nt-warning);font-size:11px;line-height:1.35}.server-manual-command{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px}.server-manual-command code{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border:1px solid var(--nt-line);border-radius:5px;padding:5px 6px;color:var(--nt-fg);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:10px}.server-delete-button{height:28px;border:1px solid color-mix(in srgb,var(--nt-danger) 42%,transparent);border-radius:5px;padding:0 12px;color:var(--nt-danger);background:transparent;font-family:var(--nt-font-mono);font-size:11px;cursor:pointer}.server-delete-button:hover:not(:disabled){color:#22080a;background:var(--nt-danger)}.workspace-sidebar-tabs{display:grid;grid-template-columns:1fr 1fr;gap:4px;border-bottom:1px solid var(--nt-line);padding:0 12px 10px}.workspace-sidebar-tabs button{height:26px;border:1px solid var(--nt-line);border-radius:5px;color:var(--nt-fg-3);background:transparent;font-size:11px;cursor:pointer}.workspace-sidebar-tabs button:hover:not(:disabled){color:var(--nt-fg);background:var(--nt-bg-2)}.workspace-sidebar-tabs button[aria-selected=true]{border-color:color-mix(in srgb,var(--nt-accent) 55%,transparent);color:var(--nt-accent);background:var(--nt-accent-soft)}.workspace-sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:4px 16px 8px;color:var(--nt-fg-3);font-size:10.5px;letter-spacing:.06em}.workspace-sidebar-header button{width:20px;height:20px;border-radius:4px;font-size:16px}.workspace-sidebar-header button:hover:not(:disabled),.tab-add-button:hover:not(:disabled){color:var(--nt-accent)}.workspace-list{display:flex;min-height:0;flex-direction:column;overflow:auto;overscroll-behavior:contain;padding-bottom:8px;-webkit-overflow-scrolling:touch}.workspace-entry{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:stretch;min-width:0}.workspace-select-button,.workspace-name-editor{display:grid;min-width:0;gap:2px;border:0;border-left:2px solid transparent;border-radius:0;padding:7px 16px 7px 14px;color:var(--nt-fg-2);background:transparent;text-align:left}.workspace-select-button{cursor:pointer}.workspace-select-button:hover:not(:disabled){color:var(--nt-fg);background:#ffffff04}.workspace-entry[data-active=true] .workspace-select-button,.workspace-entry[data-active=true] .workspace-name-editor{color:var(--nt-fg);border-left-color:var(--nt-accent);background:linear-gradient(90deg,var(--nt-accent-soft),transparent 60%)}.workspace-entry strong{max-width:100%;overflow:hidden;color:currentColor;font-size:12.5px;font-weight:500;text-overflow:ellipsis;white-space:nowrap}.workspace-entry[data-active=true] strong{font-weight:600}.workspace-entry-meta{display:flex;min-width:0;justify-content:space-between;gap:8px;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10.5px}.workspace-entry-cwd{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.workspace-name-editor{background:var(--nt-bg-2);border-left-color:var(--nt-accent)}.workspace-name-editor input{width:100%;border:1px solid var(--nt-accent);border-radius:3px;padding:2px 6px;color:var(--nt-fg);background:var(--nt-bg);box-shadow:0 0 8px var(--nt-accent-soft);font-size:12.5px;font-weight:600}.workspace-close-button{align-self:center;width:22px;height:22px;margin-right:10px;border-radius:3px;opacity:0;transition:opacity .12s ease,color .12s ease,background .12s ease}.workspace-entry:hover .workspace-close-button{opacity:1}.workspace-close-button:hover:not(:disabled),.terminal-pane-close:hover:not(:disabled){color:var(--nt-danger);background:#f871711a}.terminal-shell[data-collapsed=true] .workspace-list{align-items:center}.terminal-shell[data-collapsed=true] .workspace-entry{display:block}.terminal-shell[data-collapsed=true] .workspace-select-button{justify-items:center;border-left:0;padding:10px 0}.workspace-initial{display:grid;width:28px;height:28px;place-items:center;border:1px solid var(--nt-line);border-radius:6px;color:var(--nt-fg-2);background:var(--nt-bg-2);font-family:var(--nt-font-mono);font-size:12px;font-weight:700}.workspace-entry[data-active=true] .workspace-initial{border-color:var(--nt-accent);color:var(--nt-accent);background:var(--nt-accent-soft);box-shadow:0 0 12px var(--nt-accent-soft)}.workspace-sidebar-footer{display:flex;align-items:center;justify-content:space-between;gap:8px;border-top:1px solid var(--nt-line);padding:12px 16px;color:var(--nt-fg-3);font-size:10.5px}.workspace-sidebar-footer>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.workspace-footer-icon-button{position:relative;width:26px;height:26px;border-color:var(--nt-line);border-radius:5px;color:var(--nt-fg-2);background:#0a0d102e}.workspace-footer-icon-button:hover:not(:disabled){border-color:color-mix(in srgb,var(--nt-accent) 40%,transparent);color:var(--nt-accent)}.workspace-footer-icon{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}.update-pulse-dot{position:absolute;top:4px;right:4px;width:7px;height:7px;border-radius:50%;background:var(--nt-accent);box-shadow:0 0 color-mix(in srgb,var(--nt-accent) 45%,transparent);animation:update-pulse 1.4s ease-out infinite;pointer-events:none}.settings-update-dot{top:7px;right:8px}@keyframes update-pulse{0%{box-shadow:0 0 color-mix(in srgb,var(--nt-accent) 45%,transparent)}70%{box-shadow:0 0 0 7px transparent}to{box-shadow:0 0 0 0 transparent}}@media(prefers-reduced-motion:reduce){.update-pulse-dot{animation:none}}.file-explorer{display:grid;grid-template-rows:auto auto minmax(0,1fr);min-width:0;min-height:0;color:var(--nt-fg-2)}.file-explorer-header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px;border-bottom:1px solid var(--nt-line);padding:8px 10px 8px 12px}.file-explorer-path{min-width:0;overflow:hidden;color:var(--nt-fg-2);font-family:var(--nt-font-mono);font-size:10.5px;text-overflow:ellipsis;white-space:nowrap}.file-explorer-toolbar,.file-detail-actions,.file-mode-actions,.file-save-row{display:flex;align-items:center;gap:6px}.file-explorer-toolbar button,.file-detail-actions button,.file-mode-actions button,.file-save-row button,.file-conflict button{min-height:24px;border:1px solid var(--nt-line-2);border-radius:5px;padding:0 8px;color:var(--nt-fg-2);background:transparent;font-size:11px;cursor:pointer}.file-explorer-toolbar button{width:24px;padding:0}.file-explorer-toolbar button:hover:not(:disabled),.file-detail-actions button:hover:not(:disabled),.file-mode-actions button:hover:not(:disabled),.file-save-row button:hover:not(:disabled),.file-conflict button:hover:not(:disabled){border-color:color-mix(in srgb,var(--nt-accent) 55%,transparent);color:var(--nt-fg);background:var(--nt-accent-soft)}.file-explorer-alert,.file-conflict{border-bottom:1px solid rgba(248,113,113,.25);padding:7px 12px;color:#fca5a5;background:#f8717114;font-size:11px}.file-conflict{display:flex;align-items:center;justify-content:space-between;gap:8px;border:1px solid rgba(248,113,113,.25);border-radius:6px}.file-explorer-layout{display:grid;grid-template-rows:minmax(110px,.7fr) minmax(0,1fr);min-width:0;min-height:0}.file-tree{min-width:0;min-height:0;overflow:auto;overscroll-behavior:contain;border-bottom:1px solid var(--nt-line);padding:6px 0;-webkit-overflow-scrolling:touch}.file-tree-children{display:grid}.file-entry-button{display:grid;grid-template-columns:13px minmax(0,1fr) auto;align-items:center;gap:6px;width:100%;min-width:0;border:0;border-left:2px solid transparent;padding:6px 12px 6px calc(10px + (var(--file-tree-depth, 0) * 14px));color:var(--nt-fg-2);background:transparent;text-align:left;cursor:pointer}.file-entry-button:hover:not(:disabled),.file-entry-button[data-active=true]{color:var(--nt-fg);background:#ffffff05}.file-entry-button[data-active=true]{border-left-color:var(--nt-accent)}.file-entry-button[data-kind=directory][data-expanded=true]{background:color-mix(in srgb,var(--nt-accent) 4.5%,transparent)}.file-entry-button:disabled{opacity:.44}.file-entry-disclosure{width:13px;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10px;text-align:center}.file-entry-name{min-width:0;overflow:hidden;font-size:12px;text-overflow:ellipsis;white-space:nowrap}.file-entry-button small{color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10px}.file-entry-button[data-kind=directory] .file-entry-name{color:var(--nt-accent)}.file-detail{display:grid;grid-template-rows:auto auto minmax(0,1fr);gap:8px;min-width:0;min-height:0;overflow:auto;overscroll-behavior:contain;padding:10px 12px 12px;-webkit-overflow-scrolling:touch}.file-detail-header,.file-save-row{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:8px}.file-detail-header strong{min-width:0;overflow:hidden;color:var(--nt-fg);font-size:12px;text-overflow:ellipsis;white-space:nowrap}.file-detail-actions{flex:0 0 auto}.file-detail-empty{display:grid;min-height:92px;place-items:center;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:11px}.file-preview-frame{min-height:180px;overflow:auto}.file-preview-frame img,.file-preview-frame iframe{width:100%;min-height:180px;border:1px solid var(--nt-line);border-radius:6px;background:var(--nt-bg-2)}.file-preview-frame img{height:auto;object-fit:contain}.file-markdown-panel,.file-editor-shell{display:grid;grid-template-rows:auto minmax(0,1fr) auto;gap:8px;min-width:0;min-height:0}.file-markdown-preview{min-height:180px;overflow:auto;overscroll-behavior:contain;border:1px solid var(--nt-line);border-radius:6px;padding:10px;color:var(--nt-fg);background:var(--nt-bg-2);font-size:12px;line-height:1.45;-webkit-overflow-scrolling:touch}.file-text-preview{min-height:180px;overflow:auto;overscroll-behavior:contain;border:1px solid var(--nt-line);border-radius:6px;padding:10px;color:var(--nt-fg);background:var(--nt-bg-2);font-family:var(--nt-font-mono);font-size:12px;line-height:1.45;white-space:pre-wrap;word-break:break-word;-webkit-overflow-scrolling:touch}.file-markdown-preview :first-child{margin-top:0}.file-markdown-preview :last-child{margin-bottom:0}.file-editor{width:100%;min-height:190px;border:1px solid var(--nt-line);border-radius:6px;padding:10px;color:var(--nt-fg);background:var(--nt-bg-2);font-family:var(--nt-font-mono);font-size:12px;line-height:1.45}.file-editor:focus{border-color:color-mix(in srgb,var(--nt-accent) 55%,transparent);box-shadow:0 0 0 2px var(--nt-accent-soft)}.file-save-row{color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10.5px}.terminal-main{position:relative;display:grid;grid-template-rows:40px minmax(0,1fr);min-width:0;min-height:0;background:var(--nt-bg)}.workspace-error{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0;border-bottom:1px solid rgba(248,113,113,.25);padding:6px 12px;color:#fca5a5;background:#f8717114;font-size:12px}.workspace-error>span{min-width:0;overflow-wrap:anywhere}.workspace-error-actions{display:flex;flex:0 0 auto;gap:6px}.workspace-error-actions button{height:26px;border:1px solid rgba(248,113,113,.35);border-radius:5px;padding:0 8px;color:#fecaca;background:#f8717114;font-family:var(--nt-font-mono);font-size:11px;cursor:pointer}.workspace-error-actions button:hover,.workspace-error-actions button:focus-visible{border-color:#f87171a6;outline:none}.terminal-tabs{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:stretch;min-width:0;border-bottom:1px solid var(--nt-line);padding:0 8px 0 4px;background:var(--nt-bg)}.mobile-menu-button{display:none;align-self:center;width:32px;height:32px;margin:0 2px;border-radius:5px}.mobile-tab-current-button,.mobile-tab-add-button{display:none}.hamburger-lines{display:grid;gap:3px}.hamburger-lines span{display:block;width:16px;height:1px;background:currentColor}.terminal-tab-list{display:flex;min-width:0;max-width:100%;align-items:stretch;overflow-x:auto;overscroll-behavior:contain;scrollbar-width:none;-webkit-overflow-scrolling:touch}.terminal-tab-list::-webkit-scrollbar{display:none}.terminal-tab-drop-slot{flex:0 0 6px;position:relative}.terminal-tab-drop-slot:before{content:"";position:absolute;top:8px;bottom:8px;left:50%;width:1px;background:var(--nt-line);pointer-events:none}.terminal-tab-drop-slot:first-child:before,.terminal-tab-drop-slot:last-child:before{display:none}.terminal-tab-drop-slot[data-over=true]:after{content:"";position:absolute;inset:4px 2px;background:var(--nt-accent);border-radius:2px;box-shadow:0 0 8px var(--nt-accent-glow);pointer-events:none}.terminal-tab{position:relative;display:flex;align-items:center;min-width:0;touch-action:none;user-select:none;-webkit-user-select:none}.terminal-tab[data-active=true]:after{position:absolute;right:12px;bottom:-1px;left:12px;height:2px;background:var(--nt-accent);box-shadow:0 0 10px var(--nt-accent-glow);content:""}.terminal-tab-status-dot{display:inline-block;flex:0 0 auto;width:7px;height:7px;border-radius:50%;margin:0 6px 0 10px;background:var(--nt-fg-3);vertical-align:middle}.terminal-tab-status-dot[data-activity=idle]{background:var(--nt-accent);box-shadow:0 0 4px var(--nt-accent-glow)}.terminal-tab-status-dot[data-activity=busy]{background:#f59e0b;box-shadow:0 0 6px #f59e0b8c;animation:nterminal-tab-status-pulse 1.5s ease-in-out infinite}.terminal-tab-status-dot[data-activity=exited]{background:var(--nt-danger)}@keyframes nterminal-tab-status-pulse{0%,to{opacity:1}50%{opacity:.5}}.terminal-tab[data-unread=true]:before{position:absolute;top:4px;right:6px;width:6px;height:6px;border-radius:50%;background:var(--nt-accent);box-shadow:0 0 6px var(--nt-accent-glow);content:"";pointer-events:none}.terminal-tab-select,.terminal-tab-editor{display:flex;flex:1 1 auto;align-items:center;gap:8px;min-width:0;height:40px;border:0;padding:0 4px 0 14px;color:var(--nt-fg-2);background:transparent;font-size:12px}.terminal-tab-select{max-width:200px;cursor:pointer}.terminal-tab-select:hover:not(:disabled),.terminal-tab[data-active=true] .terminal-tab-select{color:var(--nt-fg)}.terminal-tab-select>span,.terminal-tab-editor input{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.terminal-tab small,.terminal-tab-editor small{flex:0 0 auto;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10px}.terminal-tab[data-active=true] small,.terminal-tab-editor small{color:var(--nt-accent)}.terminal-tab-editor{padding:0 12px}.terminal-tab-editor input{width:min(180px,100%);border:1px solid var(--nt-accent);border-radius:3px;padding:2px 6px;color:var(--nt-fg);background:var(--nt-bg-2);box-shadow:0 0 8px var(--nt-accent-soft);font-size:12px}.icon-button{width:18px;height:18px;margin-right:6px;border-radius:3px;opacity:0;transition:opacity .12s ease,color .12s ease,background .12s ease}.terminal-tab:hover .icon-button,.terminal-tab[data-active=true] .icon-button{opacity:1}.icon-button:hover:not(:disabled){color:var(--nt-fg)}.tab-add-button{flex:0 0 auto;width:30px;height:40px;font-size:18px;font-weight:500}.terminal-detached-pills{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:0 6px;flex:0 1 auto;min-width:0}.terminal-detached-pill{display:inline-flex;align-items:stretch;height:24px;font-size:12px;background:var(--nt-bg-2);border:1px solid var(--nt-line);border-radius:999px;overflow:hidden;color:var(--nt-fg-2);transition:border-color .12s ease,color .12s ease}.terminal-detached-pill:hover{border-color:var(--nt-accent);color:var(--nt-fg)}.terminal-detached-pill-open{padding:0 10px;background:transparent;border:0;color:inherit;font:inherit;cursor:pointer;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.terminal-detached-pill-open:focus-visible{outline:2px solid var(--nt-accent-soft);outline-offset:-1px}.terminal-detached-pill-delete{display:inline-flex;align-items:center;justify-content:center;width:0;padding:0;background:transparent;border:0;color:var(--nt-fg-3);font-size:14px;line-height:1;cursor:pointer;overflow:hidden;transition:width .14s ease,color .12s ease}.terminal-detached-pill:hover .terminal-detached-pill-delete,.terminal-detached-pill-delete:focus-visible{width:22px;color:var(--nt-danger)}.mobile-tab-sheet-backdrop{display:none}.mobile-tab-sheet{display:grid;grid-template-rows:auto minmax(0,1fr);width:min(520px,100%);height:min(70vh,560px);max-height:min(70vh,560px);min-height:0;overflow:hidden;border:1px solid var(--nt-line-2);border-radius:8px 8px 6px 6px;background:#0a0d10fa;box-shadow:0 -18px 44px #00000080}.mobile-tab-sheet header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;border-bottom:1px solid var(--nt-line);padding:10px 10px 9px 12px}.mobile-tab-sheet header>div:first-child{display:grid;min-width:0;gap:2px}.mobile-tab-sheet header strong{color:var(--nt-fg);font-size:12px}.mobile-tab-sheet header small{color:var(--nt-fg-3);font-size:10.5px}.mobile-tab-sheet-header-actions{display:flex;gap:6px}.mobile-tab-sheet-header-actions button,.mobile-tab-sheet-actions button{display:grid;min-width:28px;height:28px;place-items:center;border:1px solid var(--nt-line);border-radius:5px;padding:0 8px;color:var(--nt-fg-2);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:11px}.mobile-tab-sheet-header-actions button:hover,.mobile-tab-sheet-actions button:hover,.mobile-tab-sheet-header-actions button:focus-visible,.mobile-tab-sheet-actions button:focus-visible{color:var(--nt-accent);border-color:color-mix(in srgb,var(--nt-accent) 55%,var(--nt-line));outline:none}.mobile-tab-sheet-list{display:grid;min-height:0;gap:6px;overflow:auto;padding:8px;-webkit-overflow-scrolling:touch}.mobile-tab-sheet-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:stretch;gap:6px}.mobile-tab-sheet-row[data-active=true] .mobile-tab-sheet-select,.mobile-tab-sheet-row[data-active=true] .mobile-tab-sheet-editor{border-color:color-mix(in srgb,var(--nt-accent) 62%,var(--nt-line));background:color-mix(in srgb,var(--nt-accent) 11%,var(--nt-bg))}.mobile-tab-sheet-row[data-unread=true] .mobile-tab-sheet-select:after{position:absolute;top:7px;right:7px;width:6px;height:6px;border-radius:50%;background:var(--nt-accent);box-shadow:0 0 6px var(--nt-accent-glow);content:""}.mobile-tab-sheet-select,.mobile-tab-sheet-editor{position:relative;display:grid;grid-template-columns:26px minmax(0,1fr) auto;align-items:center;gap:9px;min-height:52px;min-width:0;border:1px solid var(--nt-line);border-radius:6px;padding:8px;color:var(--nt-fg-2);background:var(--nt-bg);text-align:left}.mobile-tab-sheet-select:hover,.mobile-tab-sheet-select:focus-visible{border-color:color-mix(in srgb,var(--nt-accent) 45%,var(--nt-line));outline:none}.mobile-tab-sheet-editor{grid-template-columns:minmax(0,1fr)}.mobile-tab-sheet-editor input{min-width:0;width:100%;height:34px;border:1px solid var(--nt-accent);border-radius:4px;padding:0 9px;color:var(--nt-fg);background:var(--nt-bg-2);font-family:var(--nt-font-mono);font-size:16px;outline:none}.mobile-tab-sheet-index{display:grid;width:24px;height:24px;place-items:center;border-radius:50%;color:var(--nt-bg);background:var(--nt-fg-3);font-size:11px;font-weight:700}.mobile-tab-sheet-row[data-active=true] .mobile-tab-sheet-index{background:var(--nt-accent)}.mobile-tab-sheet-copy{display:grid;min-width:0;gap:3px}.mobile-tab-sheet-copy strong,.mobile-tab-sheet-copy small{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mobile-tab-sheet-copy strong{color:var(--nt-fg);font-size:12px}.mobile-tab-sheet-copy small,.mobile-tab-sheet-type{color:var(--nt-fg-3);font-size:10.5px}.mobile-tab-sheet-meta{display:inline-flex;align-items:center;gap:5px;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10.5px}.mobile-tab-sheet-meta .terminal-tab-status-dot{margin:0}.mobile-tab-sheet-actions{display:flex;align-items:stretch;gap:5px}.mobile-tab-sheet-close{color:var(--nt-danger)!important}.mobile-tab-sheet-detached{display:grid;gap:6px;margin-top:4px;border-top:1px solid var(--nt-line);padding-top:8px}.mobile-tab-sheet-detached>span{color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10.5px;text-transform:uppercase}.terminal-delete-dialog{max-width:420px;gap:16px}.terminal-delete-dialog-body{display:grid;gap:8px}.terminal-delete-dialog-body p{margin:0;color:var(--nt-fg);font-size:13px}.terminal-delete-dialog-body span{min-width:0;overflow:hidden;padding:9px 10px;border:1px solid var(--nt-line);border-radius:6px;color:var(--nt-fg);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:12px;text-overflow:ellipsis;white-space:nowrap}.terminal-delete-dialog-body small{color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:11px}.terminal-delete-dialog-actions{display:flex;justify-content:flex-end;gap:8px}.terminal-delete-dialog-actions button{height:32px;min-width:82px;border-radius:5px;padding:0 14px;font-family:var(--nt-font-mono);font-size:12px;cursor:pointer}.terminal-delete-confirm{border:1px solid rgba(248,113,113,.55);color:var(--nt-danger);background:#f871711a}.terminal-delete-confirm:hover:not(:disabled){color:#190607;background:var(--nt-danger)}.transcript-overlay{position:absolute;inset:0;padding:0}.transcript-dialog{width:min(90%,calc(100% - 24px));max-width:none;height:min(90%,calc(100% - 24px));max-height:none;min-height:0;gap:8px;overflow:hidden}.transcript-dialog .security-panel-header,.transcript-dialog-controls{flex:0 0 auto}.transcript-dialog-title{display:grid;min-width:0;gap:2px}.transcript-dialog-title h2{margin:0}.transcript-dialog-title span{min-width:0;overflow:hidden;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10.5px;text-overflow:ellipsis;white-space:nowrap}.transcript-dialog-controls{display:flex;align-items:center;gap:12px;justify-content:flex-end;min-width:0}.transcript-tool-toggle{width:auto;margin-right:auto;color:var(--nt-fg-2);font-family:var(--nt-font-mono);font-size:11px}.transcript-result-count{margin-left:0;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:11px;white-space:nowrap}.transcript-search{display:flex;align-items:center;gap:6px;min-width:min(260px,38%);color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:11px}.transcript-search input{width:100%;min-width:0;border:1px solid var(--nt-line);border-radius:4px;padding:5px 7px;color:var(--nt-fg);background:color-mix(in srgb,var(--nt-bg-2) 72%,transparent);font-family:var(--nt-font-mono);font-size:11px}.transcript-search-actions{display:flex;flex:0 0 auto;gap:4px}.transcript-search-actions button{height:28px;min-width:44px;border:1px solid var(--nt-line);border-radius:4px;padding:0 8px;color:var(--nt-fg-2);background:color-mix(in srgb,var(--nt-bg-2) 72%,transparent);font-family:var(--nt-font-mono);font-size:10.5px;cursor:pointer}.transcript-search-actions button:hover:not(:disabled){color:var(--nt-fg);border-color:color-mix(in srgb,var(--nt-accent) 55%,var(--nt-line))}.transcript-dialog-scroll{min-height:0;flex:1 1 auto;overflow:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;border:1px solid var(--nt-line);border-radius:6px;background:var(--nt-bg)}.transcript-output{margin:0;min-height:100%;padding:14px 16px 18px;color:var(--nt-fg);font-family:var(--nt-font-mono);font-size:12px;line-height:1.42;overflow-wrap:anywhere}.transcript-formatted-output{background:linear-gradient(90deg,color-mix(in srgb,var(--nt-accent) 5%,transparent),transparent 38%),var(--nt-bg)}.transcript-entry{scroll-margin:72px;padding:2px 4px 10px 8px;border-left:2px solid transparent}.transcript-entry+.transcript-entry{margin-top:10px}.transcript-entry-search-active{border-left-color:#f7d977;background:#f7d97713}.transcript-entry-header{margin-bottom:5px;color:var(--nt-fg-3);font-size:11px;font-weight:700}.transcript-entry-user .transcript-entry-header{color:#f7d977}.transcript-entry-assistant .transcript-entry-header{color:var(--nt-accent)}.transcript-entry-system .transcript-entry-header{color:var(--nt-fg-3)}.transcript-entry-tool .transcript-entry-header{color:#9aa7b4}.transcript-entry-body{color:var(--nt-fg)}.transcript-entry-body>:first-child{margin-top:0}.transcript-entry-body>:last-child{margin-bottom:0}.transcript-entry-body p,.transcript-entry-body ul,.transcript-entry-body ol,.transcript-entry-body blockquote,.transcript-entry-body table,.transcript-entry-body pre{margin:0 0 8px}.transcript-entry-body ul,.transcript-entry-body ol{padding-left:22px}.transcript-entry-body li+li{margin-top:2px}.transcript-entry-body a{color:#8fd6ff;text-decoration:none}.transcript-entry-body a:hover{text-decoration:underline}.transcript-entry-body blockquote{padding:2px 0 2px 12px;border-left:2px solid var(--nt-line-2);color:var(--nt-fg-2)}.transcript-entry-body h1,.transcript-entry-body h2,.transcript-entry-body h3,.transcript-entry-body h4,.transcript-entry-body h5,.transcript-entry-body h6{margin:10px 0 6px;color:var(--nt-fg);font-size:12.5px;line-height:1.35}.transcript-entry-body code{border:1px solid color-mix(in srgb,var(--nt-line) 72%,transparent);border-radius:4px;padding:1px 4px;background:color-mix(in srgb,var(--nt-bg-2) 78%,transparent);color:#c4f1ff;font-family:var(--nt-font-mono);font-size:.95em}.transcript-markdown-code,.transcript-entry-pre{margin:0 0 8px;border:1px solid var(--nt-line);border-radius:5px;padding:9px 10px;overflow-x:auto;background:color-mix(in srgb,var(--nt-bg-2) 62%,transparent);color:var(--nt-fg);font-family:var(--nt-font-mono);font-size:11.5px;line-height:1.48;white-space:pre-wrap}.transcript-markdown-code code{border:0;border-radius:0;padding:0;background:transparent;color:inherit;font-size:inherit}.transcript-entry-body table{width:100%;border-collapse:collapse;font-size:11.5px}.transcript-entry-body th,.transcript-entry-body td{border:1px solid var(--nt-line);padding:5px 7px;text-align:left;vertical-align:top}.transcript-entry-body th{color:var(--nt-accent);background:color-mix(in srgb,var(--nt-bg-2) 72%,transparent)}.transcript-line{display:block;min-height:1.42em;padding:0 2px}.transcript-line-user{color:#f7d977;font-weight:700}.transcript-line-assistant{color:var(--nt-accent);font-weight:700}.transcript-line-system,.transcript-line-dim{color:var(--nt-fg-4)}.transcript-line-tool,.transcript-line-inline-tool{color:#9aa7b4}.transcript-line-command{color:#8fd6ff}.transcript-line-success{color:var(--nt-ok)}.transcript-line-error{color:var(--nt-danger)}.transcript-search-hit{border-radius:2px;padding:0 1px;color:#121417;background:#f7d977}.transcript-dialog-status,.transcript-dialog-hint,.transcript-dialog-error{padding:10px 12px;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:11px}.transcript-dialog-hint{border-bottom:1px solid var(--nt-line);color:var(--nt-fg-4);text-align:center}.transcript-dialog-error{color:var(--nt-danger)}.terminal-pane-actions{display:flex;align-items:center;gap:2px;padding:0 4px}.pane-action-button{width:28px;height:28px;align-self:center;border-radius:5px}.pane-action-button:hover:not(:disabled){border-color:var(--nt-line)}.split-icon{position:relative;display:block;width:14px;height:14px;border:1px solid currentColor;color:currentColor}.split-icon:before{position:absolute;background:currentColor;content:""}.split-icon-right:before{top:0;bottom:0;left:6px;width:1px}.split-icon-down:before{top:6px;right:0;left:0;height:1px}.workspace-body{position:relative;min-width:0;min-height:0;overflow:hidden;background:var(--nt-bg)}.mobile-pane-switcher{display:none}.mobile-focused-split,.mobile-focused-pane{width:100%;height:100%;min-width:0;min-height:0}.mobile-focused-pane[data-active=false]{display:none}.workspace-server-surface,.workspace-tab-surface{position:absolute;inset:0;min-width:0;min-height:0}.workspace-server-surface[data-active=false],.workspace-tab-surface[data-active=false]{display:none}.workspace-placeholder,.empty-workspace,.missing-pane{display:grid;height:100%;min-height:220px;place-content:center;gap:10px;color:var(--nt-fg-3);font-family:var(--nt-font-mono)}.workspace-placeholder{display:flex;align-items:center;justify-content:center}.workspace-placeholder:before{display:inline-block;color:var(--nt-accent);animation:nt-blink 1.05s steps(2,end) infinite;content:"●"}@keyframes nt-blink{50%{opacity:0}}.empty-workspace{color:var(--nt-fg-2);text-align:center}.empty-workspace button{width:max-content;height:30px;justify-self:center;border-radius:5px;padding:0 12px;color:var(--nt-fg-2)}.split-pane{display:grid;width:100%;height:100%;min-width:0;min-height:0}.split-cell{min-width:0;min-height:0;overflow:hidden}.pane-drop-host{position:relative;width:100%;height:100%;min-width:0;min-height:0}.pane-drop-zone{position:absolute;z-index:5;pointer-events:auto;background:transparent;transition:background 80ms ease}.pane-drop-zone--left{top:0;bottom:0;left:0;width:42%}.pane-drop-zone--right{top:0;bottom:0;right:0;width:42%}.pane-drop-zone--top{top:0;left:42%;right:42%;height:42%}.pane-drop-zone--bottom{bottom:0;left:42%;right:42%;height:42%}.pane-drop-zone[data-over=true]{background:var(--nt-accent-soft);box-shadow:inset 0 0 0 2px var(--nt-accent),0 0 18px var(--nt-accent-glow)}.split-divider{position:relative;background:var(--nt-line);touch-action:none;transition:background .15s ease,box-shadow .15s ease}.split-divider[data-direction=vertical]{cursor:col-resize}.split-divider[data-direction=horizontal]{cursor:row-resize}.split-divider:before{position:absolute;inset:0;content:""}.split-divider[data-direction=vertical]:before{right:-4px;left:-4px}.split-divider[data-direction=horizontal]:before{top:-4px;bottom:-4px}.split-divider:hover,.split-divider.is-dragging{z-index:2;background:var(--nt-accent);box-shadow:0 0 10px var(--nt-accent-glow)}.split-divider-handle{position:absolute;top:50%;left:50%;width:28px;height:4px;border-radius:2px;background:transparent;transform:translate(-50%,-50%)}.split-divider[data-direction=horizontal] .split-divider-handle{width:4px;height:28px}.split-divider:hover .split-divider-handle,.split-divider.is-dragging .split-divider-handle{background:var(--nt-accent)}.terminal-pane{position:relative;display:grid;grid-template-rows:26px minmax(0,1fr);grid-auto-rows:auto;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;background:var(--nt-bg);cursor:text}.compose-drawer{display:flex;flex-direction:column;border-top:1px solid var(--nt-line);background:var(--nt-bg);font-family:var(--nt-font-mono);position:relative;z-index:2;min-width:0;max-width:100%}.compose-drawer--closed{flex:0 0 auto}.compose-drawer-handle{display:flex;align-items:center;gap:6px;width:100%;height:22px;border:0;padding:0 10px;font-size:10.5px;letter-spacing:.06em;text-transform:lowercase;color:var(--nt-fg-3);background:var(--nt-bg-2);cursor:pointer;transition:color .12s ease,background .12s ease}.compose-drawer-handle:hover{color:var(--nt-accent);background:var(--nt-bg)}.compose-drawer-handle-dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--nt-accent);margin-left:2px}.compose-drawer--open{flex:0 0 auto;padding:4px 6px 6px;gap:4px;background:var(--nt-bg-2)}.compose-drawer-header{display:flex;align-items:center;gap:8px;width:100%;height:20px;border:0;padding:0 2px;font-size:10.5px;color:var(--nt-fg-3);background:transparent;cursor:pointer}.compose-drawer-header:hover{color:var(--nt-accent)}.compose-drawer-title{letter-spacing:.06em;text-transform:lowercase;color:var(--nt-fg-2)}.compose-drawer-hint{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--nt-fg-3);font-size:10px}.compose-drawer-close{width:16px;text-align:center;font-size:14px;color:var(--nt-fg-3)}.compose-drawer-input{width:100%;min-width:0;max-width:100%;min-height:38px;max-height:180px;resize:vertical;padding:4px 8px;border:1px solid var(--nt-line);border-radius:4px;background:var(--nt-bg);color:var(--nt-fg);font-family:var(--nt-font-mono);font-size:12.5px;line-height:1.35;outline:none;transition:border-color .12s ease,box-shadow .12s ease}.compose-drawer-input:focus{border-color:var(--nt-accent);box-shadow:0 0 0 2px var(--nt-accent-soft)}.compose-drawer-actions{display:flex;justify-content:flex-end;gap:4px}.compose-drawer-clear,.compose-drawer-send{height:22px;padding:0 10px;border-radius:4px;font-family:var(--nt-font-mono);font-size:10.5px;letter-spacing:.04em;cursor:pointer;transition:color .12s ease,background .12s ease,border-color .12s ease}.compose-drawer-clear{border:1px solid var(--nt-line);color:var(--nt-fg-3);background:transparent}.compose-drawer-clear:hover:not(:disabled){color:var(--nt-fg);background:var(--nt-bg)}.compose-drawer-send{border:1px solid var(--nt-accent);color:var(--nt-accent);background:var(--nt-accent-soft)}.compose-drawer-send:hover:not(:disabled){color:#062423;background:var(--nt-accent);box-shadow:0 0 12px var(--nt-accent-glow)}.compose-drawer-clear:disabled,.compose-drawer-send:disabled{cursor:not-allowed;opacity:.4}.terminal-pane[data-active=true]{background:linear-gradient(180deg,var(--nt-accent-soft),transparent 22%)}.terminal-pane[data-active=true]:before{position:absolute;top:0;bottom:0;left:0;z-index:1;width:2px;background:var(--nt-accent);box-shadow:0 0 14px var(--nt-accent-glow);content:"";pointer-events:none}.terminal-pane-header{display:flex;align-items:center;gap:6px;min-width:0;border-bottom:1px solid var(--nt-line);padding:0 12px;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10.5px;touch-action:none;user-select:none;-webkit-user-select:none}.terminal-pane-dot{flex:0 0 auto;width:5px;height:5px;border-radius:50%;background:var(--nt-fg-3)}.terminal-pane[data-active=true] .terminal-pane-dot{background:var(--nt-accent);box-shadow:0 0 8px var(--nt-accent)}.terminal-pane-cwd{min-width:0;overflow:hidden;color:var(--nt-fg-2);text-overflow:ellipsis;white-space:nowrap}.terminal-pane-spacer{flex:1 1 auto}.terminal-pane-state{flex:0 0 auto;font-weight:500}.terminal-pane-transcript{appearance:none;flex:0 0 auto;padding:1px 5px;border:1px solid color-mix(in srgb,var(--nt-accent) 45%,transparent);border-radius:3px;color:var(--nt-accent);background:color-mix(in srgb,var(--nt-accent) 10%,transparent);font-family:var(--nt-font-mono);font-size:9px;font-weight:700;line-height:1.35;cursor:pointer}.terminal-pane-transcript:hover{border-color:var(--nt-accent);background:color-mix(in srgb,var(--nt-accent) 18%,transparent)}.terminal-pane-transcript:focus-visible{outline:2px solid var(--nt-accent-soft);outline-offset:1px}.terminal-pane-state-running{color:var(--nt-ok)}.terminal-pane-state-exited{color:var(--nt-fg-3)}.terminal-pane-reload{flex:0 0 auto;width:18px;height:18px;border:1px solid var(--nt-line);border-radius:3px;color:var(--nt-fg-3);background:transparent;font-size:12px;line-height:1;cursor:pointer}.terminal-pane-reload:hover{border-color:var(--nt-accent);color:var(--nt-accent);background:color-mix(in srgb,var(--nt-accent) 10%,transparent)}.terminal-pane-reload:focus-visible{outline:2px solid var(--nt-accent-soft);outline-offset:1px}.terminal-pane-close{width:18px;height:18px;margin-left:4px;border-radius:3px;opacity:0;transition:opacity .12s ease,color .12s ease,background .12s ease}.terminal-pane:hover .terminal-pane-close,.terminal-pane[data-active=true] .terminal-pane-close,.editor-pane:hover .terminal-pane-close{opacity:1}.editor-pane{display:grid;grid-template-rows:28px auto auto minmax(0,1fr);width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;background:var(--nt-bg)}.editor-pane-header{display:flex;align-items:center;gap:8px;min-width:0;border-bottom:1px solid var(--nt-line);padding:0 10px;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10.5px}.editor-pane-header strong{min-width:0;overflow:hidden;color:var(--nt-fg);font-size:11px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.editor-pane-header button:not(.terminal-pane-close){min-height:20px;border:1px solid var(--nt-line-2);border-radius:4px;padding:0 8px;color:var(--nt-fg-2);background:transparent;font-size:10.5px}.editor-pane-header button:not(.terminal-pane-close):hover:not(:disabled){border-color:color-mix(in srgb,var(--nt-accent) 55%,transparent);color:var(--nt-fg);background:var(--nt-accent-soft)}.editor-pane-alert{border-bottom:1px solid rgba(248,113,113,.25);padding:7px 10px;color:#fca5a5;background:#f8717114;font-size:11px}.editor-pane-textarea{grid-row:4;width:100%;height:100%;min-width:0;min-height:0;border:0;padding:12px;resize:none;color:var(--nt-fg);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:12px;line-height:1.5;outline:none}.editor-markdown-preview{grid-row:4;width:100%;height:100%;min-width:0;min-height:0;overflow:auto;overscroll-behavior:contain;border:0;padding:14px 16px;color:var(--nt-fg);background:var(--nt-bg);font-size:13px;line-height:1.5;-webkit-overflow-scrolling:touch}.editor-markdown-preview :first-child{margin-top:0}.editor-markdown-preview :last-child{margin-bottom:0}.xterm-container{width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;overscroll-behavior:contain;padding:3px;touch-action:none}.xterm{width:100%;height:100%}.xterm .xterm-viewport::-webkit-scrollbar-button{-webkit-appearance:none;display:none;width:0;height:0}.xterm .xterm-viewport::-webkit-scrollbar-button:single-button,.xterm .xterm-viewport::-webkit-scrollbar-button:start:decrement,.xterm .xterm-viewport::-webkit-scrollbar-button:end:increment{-webkit-appearance:none;display:none;width:0;height:0}.xterm .xterm-viewport::-webkit-scrollbar-corner{background:transparent}.terminal-history-loader{position:absolute;top:34px;left:50%;z-index:3;display:grid;width:24px;height:24px;place-items:center;border:1px solid var(--nt-line);border-radius:999px;background:#0a0d10db;box-shadow:0 0 12px #00000047;transform:translate(-50%);pointer-events:none}.terminal-history-loader span{width:12px;height:12px;border:2px solid rgba(226,232,238,.28);border-top-color:var(--nt-accent);border-radius:50%;animation:terminal-history-spin .7s linear infinite}.terminal-scroll-progress{position:absolute;top:34px;right:10px;z-index:3;padding:2px 7px;border:1px solid rgba(148,163,184,.32);border-radius:6px;background:#0a0d10db;color:#e2e8eee6;font-size:11px;line-height:16px;font-variant-numeric:tabular-nums;opacity:.34;pointer-events:none;transition:opacity .12s ease,border-color .12s ease,color .12s ease}.terminal-scroll-progress[data-active=true]{border-color:#94a3b86b;color:#e2e8eef5;opacity:1}.terminal-scroll-progress[data-pending=true]{border-color:#94a3b83d;color:#e2e8eeb8;opacity:.42}.terminal-scroll-progress[data-buffer=alternate]{border-color:#f59e0b57;color:#fde68ad1}.terminal-scroll-bottom-button{position:absolute;top:auto;right:10px;bottom:10px;z-index:4;display:grid;width:28px;height:28px;place-items:center;border:1px solid color-mix(in srgb,var(--nt-accent) 28%,transparent);border-radius:999px;color:var(--nt-accent);background:#0a0d10db;box-shadow:0 10px 24px #00000052;font-family:var(--nt-font-mono);font-size:15px;line-height:1;cursor:pointer}.terminal-scroll-bottom-button:hover{border-color:color-mix(in srgb,var(--nt-accent) 55%,transparent);background:#0f171bf0}.terminal-scroll-handle{position:absolute;top:64px;right:0;bottom:60px;z-index:4;display:none;width:34px;border:0;padding:0;background:transparent;cursor:ns-resize;touch-action:none}.terminal-scroll-handle span{position:absolute;top:50%;right:8px;width:5px;height:54px;border-radius:999px;background:#94a3b85c;transform:translateY(-50%)}.terminal-scroll-handle:hover span,.terminal-scroll-handle:active span{background:color-mix(in srgb,var(--nt-accent) 68%,transparent)}@keyframes terminal-history-spin{to{transform:rotate(360deg)}}.mobile-terminal-key-bar,.mobile-terminal-key-toggle{display:none}.mobile-terminal-key{display:grid;min-width:34px;height:30px;place-items:center;border:1px solid var(--nt-line-2);border-radius:5px;padding:0 8px;color:var(--nt-fg-2);background:#0d1216f0;font-family:var(--nt-font-mono);font-size:11px;cursor:pointer}.mobile-terminal-key{-webkit-tap-highlight-color:transparent;touch-action:manipulation;border-color:var(--nt-line-2);color:var(--nt-fg-3);background:transparent;box-shadow:none;transition:none}.mobile-terminal-key:hover,.mobile-terminal-key:focus,.mobile-terminal-key:focus-visible,.mobile-terminal-key:active{outline:none;border-color:var(--nt-line-2);color:var(--nt-fg-3);background:transparent;box-shadow:none}.mobile-terminal-key.mobile-terminal-key[data-active=true]{border-color:var(--nt-accent);color:var(--nt-accent);background:var(--nt-accent-soft);box-shadow:0 0 8px var(--nt-accent-glow)}.completion-toast{position:absolute;right:16px;bottom:12px;z-index:13;display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-rows:auto auto;align-items:start;gap:4px 8px;width:min(238px,calc(100% - 32px));border:1px solid color-mix(in srgb,var(--nt-accent) 28%,transparent);border-radius:8px;padding:7px;color:var(--nt-fg);background:#0a0d10f2;box-shadow:0 18px 48px #00000080,0 0 22px color-mix(in srgb,var(--nt-accent) 8%,transparent);cursor:pointer;animation:completion-toast-in .22s ease-out}.completion-toast:focus-visible{outline:2px solid color-mix(in srgb,var(--nt-accent) 60%,transparent);outline-offset:2px}.completion-toast-character{grid-column:1 / -1;width:100%;height:152px;object-fit:contain;pointer-events:none}.completion-toast-copy{display:flex;align-items:center;gap:6px;min-width:0;min-height:18px}.completion-toast-copy strong,.completion-toast-copy span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.completion-toast-copy strong{font-size:10px;font-weight:600}.completion-toast-copy span{color:var(--nt-fg-2);font-family:var(--nt-font-mono);font-size:10px}.completion-toast button{display:grid;width:18px;height:18px;place-items:center;align-self:start;border:1px solid transparent;border-radius:4px;padding:0;color:var(--nt-fg-3);background:transparent;cursor:pointer}.completion-toast button:hover{color:var(--nt-fg);background:var(--nt-bg-2)}@keyframes completion-toast-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@media(prefers-reduced-motion:reduce){.completion-toast{animation:none}}.upload-toast{position:absolute;right:16px;bottom:12px;z-index:12;display:grid;width:min(360px,calc(100% - 32px));max-height:min(360px,calc(100% - 64px));overflow:hidden;border:1px solid var(--nt-line-2);border-radius:8px;color:var(--nt-fg);background:#0a0d10f5;box-shadow:0 18px 48px #00000080,0 0 0 1px #ffffff05}.upload-toast-partial,.upload-toast-failed{border-color:#f8717161}.upload-toast-header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;border-bottom:1px solid var(--nt-line);padding:10px 10px 9px 12px}.upload-toast-header strong{min-width:0;overflow:hidden;color:var(--nt-fg);font-size:12px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.upload-toast-header button{display:grid;width:22px;height:22px;place-items:center;border:1px solid transparent;border-radius:4px;padding:0;color:var(--nt-fg-3);background:transparent;cursor:pointer}.upload-toast-header button:hover{color:var(--nt-fg);background:var(--nt-bg-2)}.upload-toast-body{display:grid;gap:9px;min-width:0;overflow:auto;overscroll-behavior:contain;padding:10px 12px 12px;font-family:var(--nt-font-mono);font-size:11px;-webkit-overflow-scrolling:touch}.upload-queue-body{gap:8px}.upload-queue-item{display:grid;min-width:0;gap:8px;border:1px solid var(--nt-line);border-radius:6px;padding:9px;background:#ffffff04}.upload-queue-item[data-status=failed],.upload-queue-item[data-status=partial]{border-color:#f8717147}.upload-queue-item-header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px}.upload-queue-item-header strong{min-width:0;overflow:hidden;color:var(--nt-fg);font-size:11.5px;text-overflow:ellipsis;white-space:nowrap}.upload-queue-actions{display:flex;align-items:center;gap:4px}.upload-queue-actions button{min-width:24px;height:24px;border:1px solid var(--nt-line);border-radius:4px;padding:0 7px;color:var(--nt-fg-2);background:var(--nt-bg);font-size:10.5px;cursor:pointer}.upload-queue-actions button:hover,.upload-queue-actions button:focus-visible{color:var(--nt-accent);border-color:color-mix(in srgb,var(--nt-accent) 55%,var(--nt-line));outline:none}.upload-toast-body p{margin:0}.upload-progress-row{display:flex;min-width:0;justify-content:space-between;gap:12px;color:var(--nt-fg-2)}.upload-progress-track{height:4px;overflow:hidden;border-radius:2px;background:var(--nt-line)}.upload-progress-track span{display:block;height:100%;max-width:100%;background:var(--nt-accent);transition:width .12s ease}.upload-toast-destination,.upload-toast-muted{min-width:0;overflow:hidden;color:var(--nt-fg-3);text-overflow:ellipsis;white-space:nowrap}.upload-result-list{display:grid;gap:6px;min-width:0;margin:0;padding:0;list-style:none}.upload-result-list li{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px;min-width:0;color:var(--nt-fg-2)}.upload-result-list li:before{width:5px;height:5px;border-radius:50%;background:var(--nt-ok);content:""}.upload-result-list li[data-status=failed]:before{background:var(--nt-danger)}.upload-result-path{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.upload-result-error{max-width:140px;overflow:hidden;color:#fca5a5;text-overflow:ellipsis;white-space:nowrap}.upload-result-renamed{max-width:72px;overflow:hidden;color:var(--nt-warn);text-overflow:ellipsis;white-space:nowrap}.auth-screen{position:relative;display:grid;width:100%;min-height:var(--nterminal-viewport-height);place-items:center;overflow:hidden;padding:24px 24px calc(24px + var(--nterminal-safe-area-bottom));color:var(--nt-fg);background:var(--nt-bg)}.auth-background{position:absolute;inset:0;overflow:hidden;pointer-events:none}.auth-grid{position:absolute;inset:0;background-image:linear-gradient(var(--nt-line) 1px,transparent 1px),linear-gradient(90deg,var(--nt-line) 1px,transparent 1px);background-size:32px 32px;opacity:.22;-webkit-mask-image:radial-gradient(ellipse at 50% 38%,#000 0,transparent 70%);mask-image:radial-gradient(ellipse at 50% 38%,#000 0,transparent 70%)}.auth-glow{position:absolute;top:30%;left:50%;width:720px;height:720px;background:radial-gradient(circle,var(--nt-accent-soft),transparent 60%);filter:blur(20px);transform:translate(-50%,-50%)}.auth-panel{position:relative;display:grid;width:min(440px,100%);gap:18px;border:1px solid var(--nt-line);border-radius:8px;padding:28px;background:linear-gradient(180deg,rgba(255,255,255,.02),transparent);box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff05;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.auth-header{display:grid;gap:18px;border-bottom:1px dashed var(--nt-line);padding-bottom:18px}.auth-brand{display:flex;align-items:center;gap:10px}.eyebrow{margin:0;color:var(--nt-accent);font-family:var(--nt-font-mono);font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase}.auth-host{margin:2px 0 0;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:10.5px}h1{margin:0;color:var(--nt-fg);font-size:22px;line-height:1.2}.auth-form{display:grid;gap:14px}.auth-form label{display:grid;gap:6px;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:11px}.auth-form input:not([type=checkbox]){width:100%;height:38px;border:1px solid var(--nt-line-2);border-radius:6px;padding:0 12px;color:var(--nt-fg);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:14px;letter-spacing:.16em;transition:border-color .12s ease,box-shadow .12s ease,background .12s ease}.auth-form input:not([type=checkbox]):focus{border-color:var(--nt-accent);background:var(--nt-bg-2);box-shadow:0 0 0 3px var(--nt-accent-soft)}.button-row,.workspace-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.auth-form button,.button-row button{display:inline-flex;align-items:center;justify-content:center;height:38px;border-radius:6px;padding:0 18px;font-family:var(--nt-font-mono);font-size:12px;letter-spacing:.04em;cursor:pointer;transition:color .12s ease,background .12s ease,border-color .12s ease,box-shadow .12s ease}.auth-form button[type=submit]{border:1px solid var(--nt-accent);color:var(--nt-accent);background:var(--nt-accent-soft)}.auth-form button[type=submit]:hover:not(:disabled){color:#062423;background:var(--nt-accent);box-shadow:0 0 16px var(--nt-accent-glow)}.secondary-button,.button-row .secondary-button{border:1px solid var(--nt-line-2);color:var(--nt-fg-2);background:transparent}.muted,.form-error{margin:0;color:var(--nt-fg-3);font-family:var(--nt-font-mono);font-size:11px}.form-error{border:1px solid rgba(248,113,113,.25);border-radius:5px;padding:6px 10px;color:var(--nt-danger);background:#f8717114}@media(max-width:760px){input,textarea,select{font-size:16px}.compose-drawer-input{min-height:32px;max-height:70px;resize:none}.compose-drawer--open{padding:3px 6px 4px;gap:3px}.xterm .xterm-helper-textarea{font-size:16px}.terminal-shell,.terminal-shell[data-collapsed=true]{display:block;height:var(--nterminal-viewport-height);min-height:var(--nterminal-viewport-height)}.workspace-backdrop{position:fixed;inset:0;z-index:20;min-height:0;border:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.terminal-shell[data-sidebar-open=true] .workspace-backdrop{display:block}.workspace-sidebar{position:fixed;inset:0 auto 0 0;z-index:30;width:min(280px,86vw);padding-bottom:var(--nterminal-safe-area-bottom);transform:translate(-100%);transition:transform .18s ease;box-shadow:12px 0 32px #00000080}.terminal-shell[data-sidebar-open=true] .workspace-sidebar{transform:translate(0)}.terminal-shell[data-collapsed=true] .workspace-brand{grid-template-columns:minmax(0,1fr) auto;justify-items:stretch;padding:16px 16px 14px}.terminal-shell[data-collapsed=true] .workspace-list{align-items:stretch}.terminal-shell[data-collapsed=true] .workspace-select-button{justify-items:start;padding:7px 16px 7px 14px}.terminal-shell[data-collapsed=true] .workspace-initial,.sidebar-collapse-button{display:none}.sidebar-drawer-close-button{display:grid}.settings-panel{width:calc(100vw - 16px);max-width:none;height:calc(var(--nterminal-viewport-height) - 18px);max-height:none}.settings-layout{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}.settings-nav{position:sticky;top:0;z-index:1;flex-direction:row;gap:6px;overflow-x:auto;border-right:0;border-bottom:1px solid var(--nt-line);scrollbar-width:none}.settings-nav::-webkit-scrollbar{display:none}.settings-nav button{flex:0 0 auto;border-left:0;border-bottom:2px solid transparent;text-align:center}.settings-nav button:hover,.settings-nav button[data-active=true]{border-left-color:transparent;border-bottom-color:var(--nt-accent)}.settings-content{max-height:none;min-height:0;padding:14px 14px calc(14px + var(--nterminal-safe-area-bottom));-webkit-overflow-scrolling:touch}.transcript-dialog{width:calc(100% - 16px);max-width:none;height:calc(100% - 16px);max-height:none;padding:10px;gap:8px}.transcript-dialog-controls{flex-wrap:wrap;justify-content:flex-start}.transcript-tool-toggle{margin-right:0}.transcript-search{flex:1 1 100%;min-width:0}.transcript-result-count{margin-left:0}.transcript-overlay{padding:4px}.settings-shortcut-list li{grid-template-columns:minmax(0,1fr)}.settings-shortcut-actions{justify-content:stretch}.settings-shortcut-recorder{min-width:0;flex:1}.workspace-brand-name,.terminal-shell[data-collapsed=true] .workspace-brand-name{display:block}.terminal-shell[data-collapsed=true] .workspace-sidebar-header,.terminal-shell[data-collapsed=true] .workspace-sidebar-footer{display:flex}.terminal-main{height:100%;grid-template-rows:48px minmax(0,1fr)}.terminal-main[data-mobile-pane-switcher=true]{grid-template-rows:48px 34px minmax(0,1fr)}.workspace-error{align-items:stretch;flex-direction:column;gap:6px}.workspace-error-actions{justify-content:flex-end}.terminal-tabs{grid-template-columns:auto minmax(0,1fr) auto auto;gap:4px;min-height:48px;padding:0 4px}.terminal-tab-list{display:none}.mobile-tab-current-button{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-self:center;align-items:center;gap:7px;min-width:0;height:40px;border:1px solid var(--nt-line);border-radius:6px;padding:0 9px;color:var(--nt-fg);background:var(--nt-bg-2);font-family:var(--nt-font-mono);text-align:left}.mobile-tab-current-button:disabled{opacity:.55}.mobile-tab-current-button .terminal-tab-status-dot{margin:0}.mobile-tab-current-button>span:not(.terminal-tab-status-dot){min-width:0;overflow:hidden;font-size:12px;text-overflow:ellipsis;white-space:nowrap}.mobile-tab-current-button small{color:var(--nt-fg-3);font-size:10px;white-space:nowrap}.mobile-tab-add-button{display:grid;align-self:center;width:38px;height:40px;place-items:center;border:1px solid var(--nt-line);border-radius:6px;color:var(--nt-fg-2);background:var(--nt-bg);font-size:18px}.mobile-tab-sheet-backdrop{position:fixed;inset:0;z-index:42;display:flex;align-items:flex-end;justify-content:center;padding:0 10px calc(10px + var(--nterminal-safe-area-bottom));background:#00000047}.mobile-tab-sheet{display:grid}.terminal-tab{flex:0 0 auto;min-width:96px;max-width:min(46vw,150px)}.terminal-tab[data-active=true]{min-width:min(58vw,178px);max-width:min(58vw,178px)}.terminal-tab:not([data-active=true]) .icon-button{display:none}.terminal-tab-select,.terminal-tab-editor{width:100%;min-width:0;max-width:none}.mobile-pane-switcher{display:flex;min-width:0;min-height:34px;align-items:center;gap:6px;border-bottom:1px solid var(--nt-line);padding:0 8px;background:color-mix(in srgb,var(--nt-bg-2) 70%,var(--nt-bg));color:var(--nt-fg-2);font-family:var(--nt-font-mono);font-size:11px}.mobile-pane-switcher>button{display:grid;width:28px;height:28px;flex:0 0 auto;place-items:center;border:1px solid var(--nt-line);border-radius:5px;color:var(--nt-fg-2);background:var(--nt-bg);font-family:var(--nt-font-mono);font-size:14px;line-height:1;cursor:pointer}.mobile-pane-switcher>button:hover,.mobile-pane-switcher>button:focus-visible{color:var(--nt-accent);border-color:color-mix(in srgb,var(--nt-accent) 55%,var(--nt-line));outline:none}.mobile-pane-switcher>.mobile-pane-current{width:auto;min-width:0;flex:1 1 auto;overflow:hidden;padding:0 8px;text-align:center;text-overflow:ellipsis;white-space:nowrap}.mobile-pane-sheet-backdrop{position:fixed;inset:0;z-index:42;display:flex;align-items:flex-end;justify-content:center;padding:0 10px calc(10px + var(--nterminal-safe-area-bottom));background:#00000047}.mobile-pane-sheet{display:grid;width:min(520px,100%);max-height:min(58vh,420px);min-height:0;overflow:hidden;border:1px solid var(--nt-line-2);border-radius:8px 8px 6px 6px;background:#0a0d10fa;box-shadow:0 -18px 44px #00000080}.mobile-pane-sheet header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;border-bottom:1px solid var(--nt-line);padding:10px 10px 9px 12px}.mobile-pane-sheet header strong{color:var(--nt-fg);font-size:12px}.mobile-pane-sheet header button{display:grid;width:28px;height:28px;place-items:center;border:1px solid var(--nt-line);border-radius:5px;padding:0;color:var(--nt-fg-2);background:var(--nt-bg)}.mobile-pane-sheet-list{display:grid;min-height:0;gap:6px;overflow:auto;padding:8px;-webkit-overflow-scrolling:touch}.mobile-pane-sheet-list button{display:grid;grid-template-columns:26px minmax(0,1fr) auto;align-items:center;gap:9px;min-height:52px;border:1px solid var(--nt-line);border-radius:6px;padding:8px;color:var(--nt-fg-2);background:var(--nt-bg);text-align:left}.mobile-pane-sheet-list button[data-active=true]{border-color:color-mix(in srgb,var(--nt-accent) 62%,var(--nt-line));background:color-mix(in srgb,var(--nt-accent) 11%,var(--nt-bg))}.mobile-pane-sheet-index{display:grid;width:24px;height:24px;place-items:center;border-radius:50%;color:var(--nt-bg);background:var(--nt-fg-3);font-size:11px;font-weight:700}.mobile-pane-sheet-list button[data-active=true] .mobile-pane-sheet-index{background:var(--nt-accent)}.mobile-pane-sheet-copy{display:grid;min-width:0;gap:3px}.mobile-pane-sheet-copy strong,.mobile-pane-sheet-copy small{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mobile-pane-sheet-copy strong{color:var(--nt-fg);font-size:12px}.mobile-pane-sheet-copy small,.mobile-pane-sheet-type{color:var(--nt-fg-3);font-size:10.5px}.mobile-pane-sheet-type{text-transform:uppercase}.mobile-menu-button{display:grid;width:40px;height:40px}.terminal-tab-select,.terminal-tab-editor{gap:4px;height:48px;min-width:0;max-width:none;padding:0 6px 0 12px}.terminal-tab-select small,.terminal-tab-editor small{display:none}.icon-button{width:26px;height:26px;opacity:1}.tab-add-button{width:40px;height:48px}.pane-action-button{width:38px;height:38px}.terminal-pane-close{width:26px;height:26px;opacity:1}.terminal-scroll-bottom-button{top:auto;right:12px;bottom:max(52px,var(--nterminal-safe-area-bottom) + 52px);width:38px;height:38px;font-size:18px}.terminal-scroll-handle{top:76px;right:0;bottom:104px;display:block;width:42px}.terminal-scroll-handle span{right:12px;width:7px;height:76px}.terminal-pane[data-active=true] .xterm-container{padding-bottom:var(--nterminal-safe-area-bottom)}.mobile-terminal-key-bar{position:absolute;right:8px;bottom:max(8px,var(--nterminal-safe-area-bottom));left:8px;z-index:10;display:flex;max-width:calc(100% - 16px);align-items:center;justify-content:center;gap:4px;overflow-x:auto;overscroll-behavior:contain;border:1px solid rgba(42,51,64,.85);border-radius:8px;padding:4px;background:#08090bdb;box-shadow:0 10px 26px #00000059;scrollbar-width:none;-webkit-overflow-scrolling:touch}.mobile-terminal-key-toggle{position:absolute;right:10px;bottom:max(10px,var(--nterminal-safe-area-bottom));z-index:10;display:grid;width:32px;height:32px;place-items:center;border:1px solid rgba(42,51,64,.85);border-radius:50%;color:var(--nt-fg-3);background:#08090bb3;font-size:16px;cursor:pointer;box-shadow:0 4px 14px #00000059}.mobile-terminal-key-toggle:hover{color:var(--nt-accent);border-color:var(--nt-accent)}:root[data-nterminal-keyboard-visible=true] .mobile-terminal-key-toggle{position:fixed;bottom:calc(var(--nterminal-keyboard-inset-bottom) + 8px)}.mobile-terminal-key-bar::-webkit-scrollbar{display:none}:root[data-nterminal-keyboard-visible=true] .terminal-pane[data-active=true] .xterm-container{padding-bottom:3px}:root[data-nterminal-keyboard-visible=true] .mobile-terminal-key-bar{position:fixed;bottom:calc(var(--nterminal-keyboard-inset-bottom) + 4px)}}
@@ -12,8 +12,8 @@
12
12
  <link rel="icon" type="image/png" sizes="64x64" href="/icons/favicon-64.png" />
13
13
  <link rel="icon" type="image/png" sizes="96x96" href="/icons/favicon-96.png" />
14
14
  <title>NTerminal</title>
15
- <script type="module" crossorigin src="/assets/index-CDL1DGH7.js"></script>
16
- <link rel="stylesheet" crossorigin href="/assets/index-BzQHvcFD.css">
15
+ <script type="module" crossorigin src="/assets/index-DJqo3Nxj.js"></script>
16
+ <link rel="stylesheet" crossorigin href="/assets/index-b8JeDU3x.css">
17
17
  </head>
18
18
  <body>
19
19
  <div id="root"></div>
@@ -1,13 +1,13 @@
1
1
  import crypto from 'node:crypto';
2
2
  import net from 'node:net';
3
3
  import os from 'node:os';
4
- import { accessSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { accessSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import { spawnSync } from 'node:child_process';
7
7
  import { fileURLToPath, pathToFileURL } from 'node:url';
8
8
  import * as readline from 'node:readline';
9
9
  import QRCode from 'qrcode';
10
- import { detectPublicIp, ensureFirewallAllowsPort, pickSudoMode, setupNginxProxy, validateDeploymentUrl } from './proxySetup.js';
10
+ import { detectPublicIp, ensureFirewallAllowsPort, normalizeFirewallSource, pickSudoMode, setupNginxProxy, validateDeploymentUrl } from './proxySetup.js';
11
11
  // ---------------------------------------------------------------------------
12
12
  // Pure helpers (unit-tested)
13
13
  // ---------------------------------------------------------------------------
@@ -339,16 +339,96 @@ export function renderUpdateHelperSudoers(user, helperPath = updateHelperPath())
339
339
  ''
340
340
  ].join('\n');
341
341
  }
342
+ export function validateUpdateHelperSafety(need, options = {}) {
343
+ const canWrite = options.canWritePath ?? canWritePath;
344
+ const pathExists = options.pathExists ?? existsSync;
345
+ const resolveRealPath = options.realpath ?? safeRealpath;
346
+ const npmBin = need.npmBin?.trim();
347
+ if (!npmBin || !path.isAbsolute(npmBin)) {
348
+ return { ok: false, reason: 'npm binary path could not be resolved to an absolute path' };
349
+ }
350
+ const resolvedNpmBin = resolveRealPath(npmBin);
351
+ if (!resolvedNpmBin || !path.isAbsolute(resolvedNpmBin)) {
352
+ return { ok: false, reason: `npm binary path could not be verified: ${npmBin}` };
353
+ }
354
+ const writableNpmPath = firstWritablePathInChain(resolvedNpmBin, canWrite, pathExists);
355
+ if (writableNpmPath) {
356
+ return { ok: false, reason: `npm binary path is writable by this user: ${writableNpmPath}` };
357
+ }
358
+ const npmRoot = need.npmRoot?.trim();
359
+ if (!npmRoot || !path.isAbsolute(npmRoot)) {
360
+ return { ok: false, reason: 'npm global root could not be resolved to an absolute path' };
361
+ }
362
+ const writableRootPath = firstWritablePathInChain(npmRoot, canWrite, pathExists);
363
+ if (writableRootPath) {
364
+ return { ok: false, reason: `npm global root is writable by this user: ${writableRootPath}` };
365
+ }
366
+ const npmPrefix = need.npmPrefix?.trim();
367
+ if (npmPrefix) {
368
+ if (!path.isAbsolute(npmPrefix)) {
369
+ return { ok: false, reason: 'npm global prefix is not an absolute path' };
370
+ }
371
+ const writablePrefixPath = firstWritablePathInChain(npmPrefix, canWrite, pathExists);
372
+ if (writablePrefixPath) {
373
+ return { ok: false, reason: `npm global prefix is writable by this user: ${writablePrefixPath}` };
374
+ }
375
+ }
376
+ return { ok: true, npmBin: resolvedNpmBin, ...(npmPrefix ? { npmPrefix } : {}) };
377
+ }
378
+ export function parseUpdateHelperScript(content) {
379
+ const assignments = new Map();
380
+ for (const line of content.split(/\r?\n/)) {
381
+ const match = /^(NPM_BIN|NPM_PREFIX)=(.*)$/.exec(line.trim());
382
+ if (!match) {
383
+ continue;
384
+ }
385
+ const value = parseShellSingleQuoted(match[2] ?? '');
386
+ if (value === null) {
387
+ return null;
388
+ }
389
+ assignments.set(match[1], value);
390
+ }
391
+ const npmBin = assignments.get('NPM_BIN')?.trim();
392
+ if (!npmBin) {
393
+ return null;
394
+ }
395
+ const npmPrefix = assignments.get('NPM_PREFIX')?.trim();
396
+ return { npmBin, ...(npmPrefix ? { npmPrefix } : {}) };
397
+ }
398
+ export function validateConfiguredUpdateHelperSafety(content, npmRoot, options = {}) {
399
+ const info = parseUpdateHelperScript(content);
400
+ if (!info) {
401
+ return { ok: false, reason: 'installed update helper content could not be parsed' };
402
+ }
403
+ return validateUpdateHelperSafety({
404
+ needed: true,
405
+ configured: true,
406
+ reason: 'installed update helper',
407
+ npmRoot,
408
+ npmBin: info.npmBin,
409
+ ...(info.npmPrefix ? { npmPrefix: info.npmPrefix } : {})
410
+ }, options);
411
+ }
342
412
  export function canUseConfiguredUpdateHelper(helperPath = updateHelperPath(), sudo = process.env.NTERMINAL_SUDO_BIN?.trim() || 'sudo', expectedNpmRoot) {
413
+ const resolvedHelperPath = safeRealpath(helperPath);
414
+ if (!resolvedHelperPath || firstWritablePathInChain(resolvedHelperPath, canWritePath, existsSync)) {
415
+ return false;
416
+ }
343
417
  const result = spawnSync(sudo, ['-n', helperPath, 'check'], { encoding: 'utf8' });
344
418
  if (result.status !== 0) {
345
419
  return false;
346
420
  }
347
- if (!expectedNpmRoot) {
348
- return true;
349
- }
350
421
  const helperRoot = result.stdout.trim().split(/\r?\n/).at(-1)?.trim();
351
- return helperRoot === expectedNpmRoot;
422
+ if (!helperRoot || (expectedNpmRoot && helperRoot !== expectedNpmRoot)) {
423
+ return false;
424
+ }
425
+ try {
426
+ const content = readFileSync(resolvedHelperPath, 'utf8');
427
+ return validateConfiguredUpdateHelperSafety(content, helperRoot).ok;
428
+ }
429
+ catch {
430
+ return false;
431
+ }
352
432
  }
353
433
  export function detectUpdateHelperNeed(packageName = 'nterminal') {
354
434
  const npmCommand = process.env.NTERMINAL_NPM_BIN || 'npm';
@@ -408,6 +488,54 @@ function canWritePath(targetPath) {
408
488
  return false;
409
489
  }
410
490
  }
491
+ function safeRealpath(targetPath) {
492
+ try {
493
+ return realpathSync(targetPath);
494
+ }
495
+ catch {
496
+ return null;
497
+ }
498
+ }
499
+ function firstWritablePathInChain(targetPath, canWrite, pathExists) {
500
+ let current = path.resolve(targetPath);
501
+ for (;;) {
502
+ if (pathExists(current) && canWrite(current)) {
503
+ return current;
504
+ }
505
+ const parent = path.dirname(current);
506
+ if (parent === current) {
507
+ return null;
508
+ }
509
+ current = parent;
510
+ }
511
+ }
512
+ function parseShellSingleQuoted(value) {
513
+ const trimmed = value.trim();
514
+ if (!trimmed.startsWith("'")) {
515
+ return null;
516
+ }
517
+ let out = '';
518
+ let index = 0;
519
+ for (;;) {
520
+ if (trimmed[index] !== "'") {
521
+ return null;
522
+ }
523
+ const end = trimmed.indexOf("'", index + 1);
524
+ if (end === -1) {
525
+ return null;
526
+ }
527
+ out += trimmed.slice(index + 1, end);
528
+ index = end + 1;
529
+ if (index === trimmed.length) {
530
+ return out;
531
+ }
532
+ if (!trimmed.startsWith("\\'", index)) {
533
+ return null;
534
+ }
535
+ out += "'";
536
+ index += 2;
537
+ }
538
+ }
411
539
  // Resolve the bind host: existing .env value wins so a re-run never silently
412
540
  // overwrites an operator's manual override, otherwise call the detector
413
541
  // (main: first non-loopback IPv4; secondary: hardcoded 0.0.0.0).
@@ -713,6 +841,26 @@ async function askSecondaryAdvertisedUrl(p, bindHost, port) {
713
841
  console.log('Cloud firewalls/security groups must allow the chosen port from the main server, otherwise registration can succeed but the server will show offline.');
714
842
  return askUrl(p, 'URL the main should use to reach this secondary', suggestion.url, 'http');
715
843
  }
844
+ async function askSecondaryFirewallSource(p, port) {
845
+ console.log('\nFor secondary servers, the browser talks to the main and the main talks to this agent. This port should normally accept only the main server.');
846
+ const restrict = await p.confirm(`Restrict ${port}/tcp to the main server IP/CIDR in the host firewall?`, true);
847
+ if (!restrict) {
848
+ console.log(colorText(`Warning: if the firewall opens ${port}/tcp broadly, the agent token still protects the API but the port is visible to the internet.`, '33', shouldUseColor()));
849
+ return undefined;
850
+ }
851
+ for (;;) {
852
+ const raw = await p.ask('Main server public IP/CIDR allowed to connect (blank = skip OS firewall change)');
853
+ if (!raw.trim()) {
854
+ console.log(colorText(`Skipped host firewall restriction. If this server has a cloud/provider firewall, allow ${port}/tcp from the main server IP only.`, '33', shouldUseColor()));
855
+ return null;
856
+ }
857
+ const source = normalizeFirewallSource(raw);
858
+ if (source) {
859
+ return source;
860
+ }
861
+ console.log('Enter one IP or CIDR range. Domains are not accepted here because firewall rules need a stable source address.');
862
+ }
863
+ }
716
864
  // Normalize a typed URL-or-domain. Accepts bare `cli.example.com` and
717
865
  // prepends https:// (the only accepted scheme), leaves any explicit scheme
718
866
  // alone so the validator can reject non-https with a clear reason. Trims
@@ -803,12 +951,17 @@ function installUpdateHelperFiles(need) {
803
951
  console.log('Could not determine a sudoers-safe username; skipping UI update helper setup.');
804
952
  return false;
805
953
  }
954
+ const safety = validateUpdateHelperSafety(need);
955
+ if (!safety.ok) {
956
+ console.log(`Skipping UI update helper setup: ${safety.reason}. Manual update fallback remains available.`);
957
+ return false;
958
+ }
806
959
  let helperTemp = '';
807
960
  let sudoersTemp = '';
808
961
  try {
809
962
  helperTemp = writeTempFile('nterminal-update-helper', renderUpdateHelperScript({
810
- ...(need.npmBin ? { npmBin: need.npmBin } : {}),
811
- ...(need.npmPrefix ? { npmPrefix: need.npmPrefix } : {})
963
+ npmBin: safety.npmBin,
964
+ ...(safety.npmPrefix ? { npmPrefix: safety.npmPrefix } : {})
812
965
  }));
813
966
  sudoersTemp = writeTempFile('nterminal-update-sudoers', renderUpdateHelperSudoers(user, helperPath));
814
967
  console.log(`Installing ${helperPath} via sudo...`);
@@ -826,7 +979,7 @@ function installUpdateHelperFiles(need) {
826
979
  console.log('sudoers install failed; UI updates will fall back to manual instructions.');
827
980
  return false;
828
981
  }
829
- if (!canUseConfiguredUpdateHelper(helperPath)) {
982
+ if (!canUseConfiguredUpdateHelper(helperPath, process.env.NTERMINAL_SUDO_BIN?.trim() || 'sudo', need.npmRoot)) {
830
983
  console.log('Update helper was installed, but sudo -n verification failed. Manual update fallback remains available.');
831
984
  return false;
832
985
  }
@@ -1087,8 +1240,11 @@ async function onboardSecondary(p) {
1087
1240
  if (isPubliclyBoundHost(host.value)) {
1088
1241
  const portNumber = Number(port.value);
1089
1242
  if (Number.isFinite(portNumber)) {
1090
- const sudoMode = await pickSudoMode(p);
1091
- await ensureFirewallAllowsPort(portNumber, p, sudoMode);
1243
+ const source = await askSecondaryFirewallSource(p, port.value);
1244
+ if (source !== null) {
1245
+ const sudoMode = await pickSudoMode(p);
1246
+ await ensureFirewallAllowsPort(portNumber, p, sudoMode, source ? { source } : {});
1247
+ }
1092
1248
  }
1093
1249
  }
1094
1250
  step('Register with main', 'Optional, but registration is the supported way to add this server to the main.');
@@ -1181,23 +1337,6 @@ async function registerWithMain(p, agent) {
1181
1337
  const payload = buildAgentRegisterPayload(agent.name, agent.selfUrl, agent.agentToken);
1182
1338
  let registered = false;
1183
1339
  try {
1184
- // Remove any existing agent pointing at the same URL to avoid duplicates.
1185
- try {
1186
- const listRes = await fetch(`${mainUrl}/api/agents`, { headers: { cookie }, signal: AbortSignal.timeout(8000) });
1187
- if (listRes.ok) {
1188
- const { agents } = (await listRes.json());
1189
- for (const existing of agents.filter((a) => a.url.replace(/\/$/, '') === payload.url)) {
1190
- await fetch(`${mainUrl}/api/agents/${encodeURIComponent(existing.id)}`, {
1191
- method: 'DELETE',
1192
- headers: { cookie, origin: mainOrigin },
1193
- signal: AbortSignal.timeout(8000)
1194
- });
1195
- }
1196
- }
1197
- }
1198
- catch {
1199
- // best-effort de-dup; ignore
1200
- }
1201
1340
  const regRes = await fetch(`${mainUrl}/api/agents`, {
1202
1341
  method: 'POST',
1203
1342
  headers: { 'content-type': 'application/json', origin: mainOrigin, cookie },
@@ -1224,12 +1363,13 @@ async function registerWithMain(p, agent) {
1224
1363
  // even if registration threw.
1225
1364
  await fetch(`${mainUrl}/api/auth/logout`, {
1226
1365
  method: 'POST',
1227
- headers: { origin: mainOrigin, cookie },
1366
+ headers: { 'content-type': 'application/json', origin: mainOrigin, cookie },
1367
+ body: JSON.stringify({ closeTerminals: false }),
1228
1368
  signal: AbortSignal.timeout(8000)
1229
1369
  }).catch(() => undefined);
1230
1370
  }
1231
- console.log('\nNote: logging into the main from this CLI revoked any active browser session on the main');
1232
- console.log(' (single active session policy). Re-login in your browser.');
1371
+ console.log('\nNote: logging into the main from this CLI may revoke the active browser session on the main');
1372
+ console.log(' (single active session policy), but existing terminal/tmux sessions are left running.');
1233
1373
  return registered;
1234
1374
  }
1235
1375
  function collectCookies(headers) {