pilotswarm 0.0.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -1
- package/mcp/README.md +484 -0
- package/mcp/dist/bin/pilotswarm-mcp.d.ts +3 -0
- package/mcp/dist/bin/pilotswarm-mcp.d.ts.map +1 -0
- package/mcp/dist/bin/pilotswarm-mcp.js +367 -0
- package/mcp/dist/bin/pilotswarm-mcp.js.map +1 -0
- package/mcp/dist/src/auth.d.ts +7 -0
- package/mcp/dist/src/auth.d.ts.map +1 -0
- package/mcp/dist/src/auth.js +99 -0
- package/mcp/dist/src/auth.js.map +1 -0
- package/mcp/dist/src/context.d.ts +48 -0
- package/mcp/dist/src/context.d.ts.map +1 -0
- package/mcp/dist/src/context.js +83 -0
- package/mcp/dist/src/context.js.map +1 -0
- package/mcp/dist/src/index.d.ts +4 -0
- package/mcp/dist/src/index.d.ts.map +1 -0
- package/mcp/dist/src/index.js +3 -0
- package/mcp/dist/src/index.js.map +1 -0
- package/mcp/dist/src/prompts/skills.d.ts +4 -0
- package/mcp/dist/src/prompts/skills.d.ts.map +1 -0
- package/mcp/dist/src/prompts/skills.js +11 -0
- package/mcp/dist/src/prompts/skills.js.map +1 -0
- package/mcp/dist/src/resources/agents.d.ts +4 -0
- package/mcp/dist/src/resources/agents.d.ts.map +1 -0
- package/mcp/dist/src/resources/agents.js +64 -0
- package/mcp/dist/src/resources/agents.js.map +1 -0
- package/mcp/dist/src/resources/facts.d.ts +4 -0
- package/mcp/dist/src/resources/facts.d.ts.map +1 -0
- package/mcp/dist/src/resources/facts.js +125 -0
- package/mcp/dist/src/resources/facts.js.map +1 -0
- package/mcp/dist/src/resources/models.d.ts +4 -0
- package/mcp/dist/src/resources/models.d.ts.map +1 -0
- package/mcp/dist/src/resources/models.js +43 -0
- package/mcp/dist/src/resources/models.js.map +1 -0
- package/mcp/dist/src/resources/sessions.d.ts +4 -0
- package/mcp/dist/src/resources/sessions.d.ts.map +1 -0
- package/mcp/dist/src/resources/sessions.js +190 -0
- package/mcp/dist/src/resources/sessions.js.map +1 -0
- package/mcp/dist/src/resources/subscriptions.d.ts +9 -0
- package/mcp/dist/src/resources/subscriptions.d.ts.map +1 -0
- package/mcp/dist/src/resources/subscriptions.js +157 -0
- package/mcp/dist/src/resources/subscriptions.js.map +1 -0
- package/mcp/dist/src/server.d.ts +4 -0
- package/mcp/dist/src/server.d.ts.map +1 -0
- package/mcp/dist/src/server.js +59 -0
- package/mcp/dist/src/server.js.map +1 -0
- package/mcp/dist/src/tools/agents.d.ts +4 -0
- package/mcp/dist/src/tools/agents.d.ts.map +1 -0
- package/mcp/dist/src/tools/agents.js +317 -0
- package/mcp/dist/src/tools/agents.js.map +1 -0
- package/mcp/dist/src/tools/facts.d.ts +4 -0
- package/mcp/dist/src/tools/facts.d.ts.map +1 -0
- package/mcp/dist/src/tools/facts.js +151 -0
- package/mcp/dist/src/tools/facts.js.map +1 -0
- package/mcp/dist/src/tools/models.d.ts +4 -0
- package/mcp/dist/src/tools/models.d.ts.map +1 -0
- package/mcp/dist/src/tools/models.js +256 -0
- package/mcp/dist/src/tools/models.js.map +1 -0
- package/mcp/dist/src/tools/sessions.d.ts +4 -0
- package/mcp/dist/src/tools/sessions.d.ts.map +1 -0
- package/mcp/dist/src/tools/sessions.js +606 -0
- package/mcp/dist/src/tools/sessions.js.map +1 -0
- package/mcp/dist/src/util/command.d.ts +52 -0
- package/mcp/dist/src/util/command.d.ts.map +1 -0
- package/mcp/dist/src/util/command.js +78 -0
- package/mcp/dist/src/util/command.js.map +1 -0
- package/package.json +81 -6
- package/tui/README.md +35 -0
- package/tui/bin/tui.js +30 -0
- package/tui/plugins/.mcp.json +7 -0
- package/tui/plugins/plugin.json +13 -0
- package/tui/plugins/session-policy.json +8 -0
- package/tui/src/app.js +850 -0
- package/tui/src/auth/cli.js +111 -0
- package/tui/src/auth/entra-auth.js +218 -0
- package/tui/src/bootstrap-env.js +176 -0
- package/tui/src/embedded-workers.js +79 -0
- package/tui/src/http-transport-host.js +106 -0
- package/tui/src/index.js +340 -0
- package/tui/src/node-sdk-transport.js +1793 -0
- package/tui/src/platform.js +984 -0
- package/tui/src/plugin-config.js +239 -0
- package/tui/src/portal.js +7 -0
- package/tui/src/version.js +7 -0
- package/tui/tui-splash.txt +11 -0
- package/ui/core/README.md +6 -0
- package/ui/core/src/commands.js +93 -0
- package/ui/core/src/context-usage.js +212 -0
- package/ui/core/src/controller.js +6104 -0
- package/ui/core/src/formatting.js +1036 -0
- package/ui/core/src/history.js +932 -0
- package/ui/core/src/index.js +13 -0
- package/ui/core/src/layout.js +332 -0
- package/ui/core/src/reducer.js +1935 -0
- package/ui/core/src/selectors.js +5409 -0
- package/ui/core/src/session-errors.js +14 -0
- package/ui/core/src/session-tree.js +151 -0
- package/ui/core/src/state.js +248 -0
- package/ui/core/src/store.js +23 -0
- package/ui/core/src/system-titles.js +24 -0
- package/ui/core/src/themes/catppuccin-mocha.js +56 -0
- package/ui/core/src/themes/cobalt2.js +56 -0
- package/ui/core/src/themes/dark-high-contrast.js +56 -0
- package/ui/core/src/themes/daylight.js +62 -0
- package/ui/core/src/themes/dracula.js +56 -0
- package/ui/core/src/themes/github-dark.js +56 -0
- package/ui/core/src/themes/github-light.js +59 -0
- package/ui/core/src/themes/gruvbox-dark.js +56 -0
- package/ui/core/src/themes/hacker-x-matrix.js +56 -0
- package/ui/core/src/themes/hacker-x-orion-prime.js +56 -0
- package/ui/core/src/themes/helpers.js +79 -0
- package/ui/core/src/themes/high-contrast-mono.js +59 -0
- package/ui/core/src/themes/index.js +52 -0
- package/ui/core/src/themes/light-high-contrast.js +62 -0
- package/ui/core/src/themes/noctis-obscuro.js +56 -0
- package/ui/core/src/themes/noctis.js +56 -0
- package/ui/core/src/themes/paper-ink.js +62 -0
- package/ui/core/src/themes/solarized-ops.js +59 -0
- package/ui/core/src/themes/terminal-green.js +59 -0
- package/ui/core/src/themes/tokyo-night.js +56 -0
- package/ui/react/README.md +5 -0
- package/ui/react/src/chat-status.js +39 -0
- package/ui/react/src/components.js +1989 -0
- package/ui/react/src/index.js +4 -0
- package/ui/react/src/platform.js +15 -0
- package/ui/react/src/use-controller-state.js +38 -0
- package/ui/react/src/web-app.js +4390 -0
- package/web/README.md +198 -0
- package/web/api/router.js +196 -0
- package/web/api/ws.js +152 -0
- package/web/auth/authz/engine.js +204 -0
- package/web/auth/config.js +115 -0
- package/web/auth/index.js +175 -0
- package/web/auth/normalize/entra.js +22 -0
- package/web/auth/providers/entra.js +76 -0
- package/web/auth/providers/none.js +24 -0
- package/web/auth.js +10 -0
- package/web/bin/serve.js +53 -0
- package/web/config.js +20 -0
- package/web/dist/app.js +469 -0
- package/web/dist/assets/index-CBgQQk-j.css +1 -0
- package/web/dist/assets/index-DMefB7Wb.js +24 -0
- package/web/dist/assets/msal-CytV9RFv.js +7 -0
- package/web/dist/assets/pilotswarm-DyBlqVTY.js +87 -0
- package/web/dist/assets/react-BZwsW8pe.js +1 -0
- package/web/dist/index.html +16 -0
- package/web/runtime.js +454 -0
- package/web/server.js +276 -0
- package/index.js +0 -1
package/web/config.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getPluginDirsFromEnv, resolvePortalConfigBundleFromPluginDirs } from "pilotswarm/host";
|
|
2
|
+
|
|
3
|
+
let cachedPortalBundle = null;
|
|
4
|
+
|
|
5
|
+
function getPortalBundle() {
|
|
6
|
+
if (!cachedPortalBundle) {
|
|
7
|
+
cachedPortalBundle = resolvePortalConfigBundleFromPluginDirs(getPluginDirsFromEnv());
|
|
8
|
+
}
|
|
9
|
+
return cachedPortalBundle;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getPortalConfig() {
|
|
13
|
+
return getPortalBundle().portalConfig;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getPortalAssetFile(assetName) {
|
|
17
|
+
const key = String(assetName || "").trim();
|
|
18
|
+
if (!key) return null;
|
|
19
|
+
return getPortalBundle().assetFiles?.[key] || null;
|
|
20
|
+
}
|
package/web/dist/app.js
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { Terminal } from "/xterm/lib/xterm.mjs";
|
|
2
|
+
import { FitAddon } from "/xterm-addon-fit/lib/addon-fit.mjs";
|
|
3
|
+
import { WebLinksAddon } from "/xterm-addon-web-links/lib/addon-web-links.mjs";
|
|
4
|
+
import { DEFAULT_THEME_ID, getTheme, listThemes } from "/ui-core/themes/index.js";
|
|
5
|
+
|
|
6
|
+
// ── Auth state ──────────────────────────────────────────────────
|
|
7
|
+
let authEnabled = false;
|
|
8
|
+
let msalInstance = null;
|
|
9
|
+
let currentAccount = null;
|
|
10
|
+
let accessToken = null;
|
|
11
|
+
|
|
12
|
+
const topbarUser = document.getElementById("topbar-user");
|
|
13
|
+
const topbarSignInBtn = document.getElementById("topbar-signin-btn");
|
|
14
|
+
const topbarSignOutBtn = document.getElementById("topbar-signout-btn");
|
|
15
|
+
const signedOutScreen = document.getElementById("signed-out-screen");
|
|
16
|
+
|
|
17
|
+
async function initAuth() {
|
|
18
|
+
const resp = await fetch("/api/auth-config");
|
|
19
|
+
const config = await resp.json();
|
|
20
|
+
if (!config.enabled) {
|
|
21
|
+
// No auth configured — proceed directly
|
|
22
|
+
authEnabled = false;
|
|
23
|
+
updateAuthUI();
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
authEnabled = true;
|
|
28
|
+
msalInstance = new msal.PublicClientApplication({
|
|
29
|
+
auth: {
|
|
30
|
+
clientId: config.clientId,
|
|
31
|
+
authority: config.authority,
|
|
32
|
+
redirectUri: config.redirectUri,
|
|
33
|
+
},
|
|
34
|
+
cache: {
|
|
35
|
+
cacheLocation: "sessionStorage",
|
|
36
|
+
storeAuthStateInCookie: true,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await msalInstance.initialize();
|
|
41
|
+
|
|
42
|
+
// Handle redirect callback (if returning from redirect flow)
|
|
43
|
+
const redirectResp = await msalInstance.handleRedirectPromise();
|
|
44
|
+
if (redirectResp) {
|
|
45
|
+
currentAccount = redirectResp.account;
|
|
46
|
+
} else {
|
|
47
|
+
const accounts = msalInstance.getAllAccounts();
|
|
48
|
+
if (accounts.length > 0) currentAccount = accounts[0];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (currentAccount) {
|
|
52
|
+
await acquireToken();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
updateAuthUI();
|
|
56
|
+
return !!currentAccount;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function acquireToken() {
|
|
60
|
+
if (!msalInstance || !currentAccount) return null;
|
|
61
|
+
try {
|
|
62
|
+
const resp = await msalInstance.acquireTokenSilent({
|
|
63
|
+
scopes: [`${msalInstance.getConfiguration().auth.clientId}/.default`],
|
|
64
|
+
account: currentAccount,
|
|
65
|
+
});
|
|
66
|
+
accessToken = resp.accessToken || resp.idToken;
|
|
67
|
+
return accessToken;
|
|
68
|
+
} catch {
|
|
69
|
+
try {
|
|
70
|
+
const resp = await msalInstance.acquireTokenPopup({
|
|
71
|
+
scopes: [`${msalInstance.getConfiguration().auth.clientId}/.default`],
|
|
72
|
+
account: currentAccount,
|
|
73
|
+
});
|
|
74
|
+
accessToken = resp.accessToken || resp.idToken;
|
|
75
|
+
return accessToken;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.error("[auth] Token acquisition failed:", err);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function signIn() {
|
|
84
|
+
if (!msalInstance) return;
|
|
85
|
+
try {
|
|
86
|
+
// Mobile browsers block popups — use redirect flow instead
|
|
87
|
+
if (/Mobi|Android/i.test(navigator.userAgent)) {
|
|
88
|
+
await msalInstance.loginRedirect({ scopes: ["openid", "profile"] });
|
|
89
|
+
return; // page will redirect
|
|
90
|
+
}
|
|
91
|
+
const resp = await msalInstance.loginPopup({
|
|
92
|
+
scopes: ["openid", "profile"],
|
|
93
|
+
});
|
|
94
|
+
currentAccount = resp.account;
|
|
95
|
+
await acquireToken();
|
|
96
|
+
updateAuthUI();
|
|
97
|
+
connectWebSocket();
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.error("[auth] Sign-in failed:", err);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function signOut() {
|
|
104
|
+
if (!msalInstance) return;
|
|
105
|
+
msalInstance.logoutPopup({ account: currentAccount });
|
|
106
|
+
currentAccount = null;
|
|
107
|
+
accessToken = null;
|
|
108
|
+
if (ws && ws.readyState === WebSocket.OPEN) ws.close();
|
|
109
|
+
ws = null;
|
|
110
|
+
updateAuthUI();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function updateAuthUI() {
|
|
114
|
+
if (!authEnabled) {
|
|
115
|
+
// Auth not configured — hide all auth UI, show terminal
|
|
116
|
+
topbarSignInBtn.classList.add("hidden");
|
|
117
|
+
topbarSignOutBtn.classList.add("hidden");
|
|
118
|
+
topbarUser.textContent = "";
|
|
119
|
+
signedOutScreen.classList.add("hidden");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const signedIn = !!currentAccount;
|
|
124
|
+
topbarSignInBtn.classList.toggle("hidden", signedIn);
|
|
125
|
+
topbarSignOutBtn.classList.toggle("hidden", !signedIn);
|
|
126
|
+
if (signedIn) {
|
|
127
|
+
const name = currentAccount.name || currentAccount.username || "";
|
|
128
|
+
const email = currentAccount.username || currentAccount.idTokenClaims?.preferred_username || "";
|
|
129
|
+
topbarUser.textContent = email && email !== name ? `${name} (${email})` : name;
|
|
130
|
+
} else {
|
|
131
|
+
topbarUser.textContent = "";
|
|
132
|
+
}
|
|
133
|
+
signedOutScreen.classList.toggle("hidden", signedIn);
|
|
134
|
+
|
|
135
|
+
// Hide overlay + terminal when signed out, show signed-out screen
|
|
136
|
+
if (!signedIn) {
|
|
137
|
+
overlayEl.classList.add("hidden");
|
|
138
|
+
container.style.visibility = "hidden";
|
|
139
|
+
} else {
|
|
140
|
+
container.style.visibility = "visible";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Expose for onclick handlers in HTML
|
|
145
|
+
window.__portalSignIn = signIn;
|
|
146
|
+
window.__portalSignOut = signOut;
|
|
147
|
+
|
|
148
|
+
const THEME_STORAGE_KEY = "pilotswarm.portal.theme";
|
|
149
|
+
const FONT_FAMILY = "ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace";
|
|
150
|
+
const THEMES = listThemes();
|
|
151
|
+
|
|
152
|
+
function readStoredThemeId() {
|
|
153
|
+
try {
|
|
154
|
+
return window.localStorage.getItem(THEME_STORAGE_KEY);
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function writeStoredThemeId(themeId) {
|
|
161
|
+
try {
|
|
162
|
+
window.localStorage.setItem(THEME_STORAGE_KEY, themeId);
|
|
163
|
+
} catch {}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function resolveInitialTheme() {
|
|
167
|
+
const storedThemeId = readStoredThemeId();
|
|
168
|
+
return getTheme(storedThemeId) || getTheme(DEFAULT_THEME_ID) || THEMES[0];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function setThemeCssVariable(name, value) {
|
|
172
|
+
document.documentElement.style.setProperty(name, value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function applyDocumentTheme(theme) {
|
|
176
|
+
const page = theme.page;
|
|
177
|
+
setThemeCssVariable("--app-background", page.background);
|
|
178
|
+
setThemeCssVariable("--app-foreground", page.foreground);
|
|
179
|
+
setThemeCssVariable("--overlay-background", page.overlayBackground);
|
|
180
|
+
setThemeCssVariable("--overlay-foreground", page.overlayForeground);
|
|
181
|
+
setThemeCssVariable("--hint-color", page.hintColor);
|
|
182
|
+
setThemeCssVariable("--modal-backdrop", page.modalBackdrop);
|
|
183
|
+
setThemeCssVariable("--modal-background", page.modalBackground);
|
|
184
|
+
setThemeCssVariable("--modal-border", page.modalBorder);
|
|
185
|
+
setThemeCssVariable("--modal-foreground", page.modalForeground);
|
|
186
|
+
setThemeCssVariable("--modal-muted", page.modalMuted);
|
|
187
|
+
setThemeCssVariable("--modal-selected-background", page.modalSelectedBackground);
|
|
188
|
+
setThemeCssVariable("--modal-selected-border", page.modalSelectedBorder);
|
|
189
|
+
setThemeCssVariable("--modal-selected-foreground", page.modalSelectedForeground);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const overlayEl = document.getElementById("overlay");
|
|
193
|
+
const dotsEl = document.getElementById("dots");
|
|
194
|
+
const modalBackdropEl = document.getElementById("theme-modal-backdrop");
|
|
195
|
+
const modalOptionsEl = document.getElementById("theme-options");
|
|
196
|
+
const modalTitleEl = document.getElementById("theme-modal-title");
|
|
197
|
+
|
|
198
|
+
let currentTheme = resolveInitialTheme();
|
|
199
|
+
let themeModalOpen = false;
|
|
200
|
+
let modalSelectedThemeId = currentTheme.id;
|
|
201
|
+
|
|
202
|
+
applyDocumentTheme(currentTheme);
|
|
203
|
+
|
|
204
|
+
const term = new Terminal({
|
|
205
|
+
cursorBlink: true,
|
|
206
|
+
cursorStyle: "block",
|
|
207
|
+
fontFamily: FONT_FAMILY,
|
|
208
|
+
fontSize: 14,
|
|
209
|
+
lineHeight: 1.1,
|
|
210
|
+
macOptionIsMeta: true,
|
|
211
|
+
allowProposedApi: true,
|
|
212
|
+
theme: { ...currentTheme.terminal },
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const fitAddon = new FitAddon();
|
|
216
|
+
term.loadAddon(fitAddon);
|
|
217
|
+
term.loadAddon(new WebLinksAddon());
|
|
218
|
+
|
|
219
|
+
const container = document.getElementById("terminal");
|
|
220
|
+
term.open(container);
|
|
221
|
+
|
|
222
|
+
// ── WebSocket (created after auth) ──────────────────────────────
|
|
223
|
+
let ws = null;
|
|
224
|
+
|
|
225
|
+
function connectWebSocket() {
|
|
226
|
+
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return;
|
|
227
|
+
|
|
228
|
+
const proto = location.protocol === "https:" ? "wss:" : "ws:";
|
|
229
|
+
const url = `${proto}//${location.host}/ws`;
|
|
230
|
+
|
|
231
|
+
// Pass token via sub-protocol if auth is enabled
|
|
232
|
+
if (authEnabled && accessToken) {
|
|
233
|
+
ws = new WebSocket(url, ["access_token", accessToken]);
|
|
234
|
+
} else {
|
|
235
|
+
ws = new WebSocket(url);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
ws.onopen = () => {
|
|
239
|
+
overlayEl.classList.add("hidden");
|
|
240
|
+
// Re-fit now that layout is fully resolved, then send accurate dimensions
|
|
241
|
+
fitAddon.fit();
|
|
242
|
+
ws.send(JSON.stringify({ type: "resize", cols: term.cols, rows: term.rows }));
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
ws.onmessage = (event) => {
|
|
246
|
+
try {
|
|
247
|
+
const message = JSON.parse(event.data);
|
|
248
|
+
if (message.type === "output") {
|
|
249
|
+
term.write(message.data);
|
|
250
|
+
} else if (message.type === "exit") {
|
|
251
|
+
term.write("\r\n\x1b[90m[Session ended]\x1b[0m\r\n");
|
|
252
|
+
}
|
|
253
|
+
} catch {}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
ws.onclose = (ev) => {
|
|
257
|
+
if (ev.code === 4401) {
|
|
258
|
+
// Auth rejected — show signed-out state
|
|
259
|
+
currentAccount = null;
|
|
260
|
+
accessToken = null;
|
|
261
|
+
updateAuthUI();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
term.write("\r\n\x1b[90m[Disconnected - reload to reconnect]\x1b[0m\r\n");
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
term.onData((data) => {
|
|
269
|
+
if (themeModalOpen) return;
|
|
270
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
271
|
+
ws.send(JSON.stringify({ type: "input", data }));
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
term.onBinary((data) => {
|
|
276
|
+
if (themeModalOpen) return;
|
|
277
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
278
|
+
ws.send(JSON.stringify({ type: "input", data }));
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
window.addEventListener("resize", () => {
|
|
283
|
+
fitAddon.fit();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
term.onResize(({ cols, rows }) => {
|
|
287
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
288
|
+
ws.send(JSON.stringify({ type: "resize", cols, rows }));
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
let dotCount = 0;
|
|
293
|
+
const dotInterval = window.setInterval(() => {
|
|
294
|
+
dotCount = (dotCount + 1) % 4;
|
|
295
|
+
dotsEl.textContent = ".".repeat(dotCount || 1);
|
|
296
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
297
|
+
window.clearInterval(dotInterval);
|
|
298
|
+
}
|
|
299
|
+
}, 400);
|
|
300
|
+
|
|
301
|
+
// ── Boot: auth then connect ─────────────────────────────────────
|
|
302
|
+
(async () => {
|
|
303
|
+
const signedIn = await initAuth();
|
|
304
|
+
if (!authEnabled || signedIn) {
|
|
305
|
+
// Wait for layout to settle before fitting + connecting
|
|
306
|
+
requestAnimationFrame(() => {
|
|
307
|
+
fitAddon.fit();
|
|
308
|
+
term.focus();
|
|
309
|
+
connectWebSocket();
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
})();
|
|
313
|
+
|
|
314
|
+
function renderThemeOptions() {
|
|
315
|
+
modalOptionsEl.replaceChildren();
|
|
316
|
+
|
|
317
|
+
for (const theme of THEMES) {
|
|
318
|
+
const optionEl = document.createElement("button");
|
|
319
|
+
optionEl.type = "button";
|
|
320
|
+
optionEl.className = "theme-option";
|
|
321
|
+
if (theme.id === modalSelectedThemeId) optionEl.classList.add("is-selected");
|
|
322
|
+
|
|
323
|
+
const titleRowEl = document.createElement("div");
|
|
324
|
+
titleRowEl.className = "theme-option-title-row";
|
|
325
|
+
|
|
326
|
+
const titleEl = document.createElement("span");
|
|
327
|
+
titleEl.className = "theme-option-title";
|
|
328
|
+
titleEl.textContent = theme.label;
|
|
329
|
+
titleRowEl.appendChild(titleEl);
|
|
330
|
+
|
|
331
|
+
if (theme.id === currentTheme.id) {
|
|
332
|
+
const currentEl = document.createElement("span");
|
|
333
|
+
currentEl.className = "theme-option-current";
|
|
334
|
+
currentEl.textContent = "Current";
|
|
335
|
+
titleRowEl.appendChild(currentEl);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const descriptionEl = document.createElement("div");
|
|
339
|
+
descriptionEl.className = "theme-option-description";
|
|
340
|
+
descriptionEl.textContent = theme.description;
|
|
341
|
+
|
|
342
|
+
const swatchesEl = document.createElement("div");
|
|
343
|
+
swatchesEl.className = "theme-option-swatches";
|
|
344
|
+
for (const color of [
|
|
345
|
+
theme.terminal.background,
|
|
346
|
+
theme.terminal.blue,
|
|
347
|
+
theme.terminal.green,
|
|
348
|
+
theme.terminal.magenta,
|
|
349
|
+
theme.terminal.yellow,
|
|
350
|
+
]) {
|
|
351
|
+
const swatchEl = document.createElement("span");
|
|
352
|
+
swatchEl.className = "theme-swatch";
|
|
353
|
+
swatchEl.style.backgroundColor = color;
|
|
354
|
+
swatchesEl.appendChild(swatchEl);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
optionEl.append(titleRowEl, descriptionEl, swatchesEl);
|
|
358
|
+
optionEl.addEventListener("click", () => {
|
|
359
|
+
modalSelectedThemeId = theme.id;
|
|
360
|
+
renderThemeOptions();
|
|
361
|
+
applySelectedTheme();
|
|
362
|
+
});
|
|
363
|
+
modalOptionsEl.appendChild(optionEl);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function moveThemeSelection(delta) {
|
|
368
|
+
const currentIndex = THEMES.findIndex((theme) => theme.id === modalSelectedThemeId);
|
|
369
|
+
const safeIndex = currentIndex >= 0 ? currentIndex : 0;
|
|
370
|
+
const nextIndex = (safeIndex + delta + THEMES.length) % THEMES.length;
|
|
371
|
+
modalSelectedThemeId = THEMES[nextIndex].id;
|
|
372
|
+
renderThemeOptions();
|
|
373
|
+
// Scroll selected option into view
|
|
374
|
+
const selected = modalOptionsEl.querySelector(".is-selected");
|
|
375
|
+
if (selected) selected.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function applyTheme(themeId, { persist = true } = {}) {
|
|
379
|
+
const nextTheme = getTheme(themeId);
|
|
380
|
+
if (!nextTheme) return;
|
|
381
|
+
currentTheme = nextTheme;
|
|
382
|
+
modalSelectedThemeId = nextTheme.id;
|
|
383
|
+
applyDocumentTheme(nextTheme);
|
|
384
|
+
term.options.theme = { ...nextTheme.terminal };
|
|
385
|
+
if (typeof term.clearTextureAtlas === "function") term.clearTextureAtlas();
|
|
386
|
+
term.refresh(0, term.rows - 1);
|
|
387
|
+
if (persist) writeStoredThemeId(nextTheme.id);
|
|
388
|
+
renderThemeOptions();
|
|
389
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
390
|
+
ws.send(JSON.stringify({ type: "theme", themeId: nextTheme.id }));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function openThemeModal() {
|
|
395
|
+
themeModalOpen = true;
|
|
396
|
+
modalSelectedThemeId = currentTheme.id;
|
|
397
|
+
modalTitleEl.textContent = "Theme Picker";
|
|
398
|
+
renderThemeOptions();
|
|
399
|
+
modalBackdropEl.classList.remove("hidden");
|
|
400
|
+
modalBackdropEl.setAttribute("aria-hidden", "false");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function closeThemeModal() {
|
|
404
|
+
themeModalOpen = false;
|
|
405
|
+
modalBackdropEl.classList.add("hidden");
|
|
406
|
+
modalBackdropEl.setAttribute("aria-hidden", "true");
|
|
407
|
+
term.focus();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function applySelectedTheme() {
|
|
411
|
+
applyTheme(modalSelectedThemeId);
|
|
412
|
+
closeThemeModal();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
modalBackdropEl.addEventListener("click", (event) => {
|
|
416
|
+
if (event.target === modalBackdropEl) {
|
|
417
|
+
closeThemeModal();
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
window.addEventListener("keydown", (event) => {
|
|
422
|
+
const toggleThemeModal = event.shiftKey
|
|
423
|
+
&& !event.ctrlKey
|
|
424
|
+
&& !event.metaKey
|
|
425
|
+
&& !event.altKey
|
|
426
|
+
&& event.code === "KeyT";
|
|
427
|
+
|
|
428
|
+
if (toggleThemeModal) {
|
|
429
|
+
event.preventDefault();
|
|
430
|
+
event.stopPropagation();
|
|
431
|
+
if (themeModalOpen) {
|
|
432
|
+
closeThemeModal();
|
|
433
|
+
} else {
|
|
434
|
+
openThemeModal();
|
|
435
|
+
}
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (!themeModalOpen) return;
|
|
440
|
+
|
|
441
|
+
event.preventDefault();
|
|
442
|
+
event.stopPropagation();
|
|
443
|
+
|
|
444
|
+
if (event.key === "Escape") {
|
|
445
|
+
closeThemeModal();
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (event.key === "Enter") {
|
|
449
|
+
applySelectedTheme();
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (event.key === "ArrowUp" || event.key === "k" || event.key === "K") {
|
|
453
|
+
moveThemeSelection(-1);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
if (event.key === "ArrowDown" || event.key === "j" || event.key === "J") {
|
|
457
|
+
moveThemeSelection(1);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (event.key === "Home" || event.key === "g") {
|
|
461
|
+
modalSelectedThemeId = THEMES[0].id;
|
|
462
|
+
renderThemeOptions();
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (event.key === "End" || event.key === "G") {
|
|
466
|
+
modalSelectedThemeId = THEMES[THEMES.length - 1].id;
|
|
467
|
+
renderThemeOptions();
|
|
468
|
+
}
|
|
469
|
+
}, true);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--ps-page-background: #0d1117;--ps-page-foreground: #f0f6fc;--ps-surface: #0d1117;--ps-background: #0d1117;--ps-foreground: #f0f6fc;--ps-muted: #8b949e;--ps-border: #6e7681;--ps-selection-background: #58a6ff;--ps-selection-foreground: #0d1117;--ps-highlight-background: #1f6feb;--ps-highlight-foreground: #ffffff;--ps-modal-backdrop: rgba(13, 17, 23, .72);--ps-modal-background: #161b22;--ps-modal-border: #30363d;--ps-modal-foreground: #f0f6fc;--ps-modal-muted: #8b949e;--ps-modal-selected-background: rgba(31, 111, 235, .16);--ps-modal-selected-border: #58a6ff;--ps-modal-selected-foreground: #ffffff;--ps-font-size-base: 13px;--ps-font-size-dense: 12px;--ps-line-height-base: 1.28;--ps-line-height-dense: 1.18;--ps-radius-sm: 6px;--ps-radius-md: 8px;--ps-scrollbar-size: 10px;--ps-scrollbar-track: rgba(110, 118, 129, .14);--ps-scrollbar-thumb: color-mix(in srgb, var(--ps-border) 56%, var(--ps-surface));--ps-scrollbar-thumb-hover: color-mix(in srgb, var(--ps-modal-selected-border) 42%, var(--ps-border))}*{box-sizing:border-box;scrollbar-width:thin;scrollbar-color:var(--ps-scrollbar-thumb) var(--ps-scrollbar-track)}html,body,#root{margin:0;min-height:100%;height:100%;background:var(--ps-page-background);color:var(--ps-page-foreground);font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:var(--ps-font-size-base);line-height:var(--ps-line-height-base)}body{overflow:hidden;-webkit-text-size-adjust:100%}button,input,textarea{font:inherit}button{cursor:pointer}.portal-app-shell,.portal-gate{min-height:var(--ps-app-height, 100%);height:var(--ps-app-height, 100%);display:flex;flex-direction:column;background:radial-gradient(circle at top left,color-mix(in srgb,var(--ps-highlight-background) 18%,transparent),transparent 42%),radial-gradient(circle at top right,color-mix(in srgb,var(--ps-modal-selected-border) 16%,transparent),transparent 38%),var(--ps-page-background)}.portal-gate{align-items:center;justify-content:center;padding:24px}.portal-gate-card{width:min(480px,100%);border:1px solid var(--ps-modal-border);border-radius:var(--ps-radius-md);background:color-mix(in srgb,var(--ps-modal-background) 92%,transparent);box-shadow:0 28px 80px #00000052;padding:28px;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.portal-gate-brand{display:flex;flex-direction:column;align-items:flex-start;gap:14px;margin-bottom:18px}.portal-gate-kicker,.portal-header-kicker{color:var(--ps-muted);font-size:12px;letter-spacing:.08em;text-transform:uppercase}.portal-gate-title{margin:10px 0 14px;font-size:clamp(28px,5vw,42px);line-height:1.05}.portal-gate-copy{margin:0;color:var(--ps-muted);line-height:1.6}.portal-primary-button,.portal-secondary-button,.ps-toolbar-button,.ps-mini-button,.ps-send-button,.ps-modal-button,.ps-mobile-nav-button,.ps-tab,.ps-filter-option,.ps-list-button,.ps-modal-close{display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--ps-modal-border);border-radius:var(--ps-radius-sm);background:color-mix(in srgb,var(--ps-surface) 94%,transparent);color:var(--ps-foreground);transition:background .12s ease,border-color .12s ease,transform .12s ease}.portal-primary-button,.ps-send-button,.ps-modal-button.is-primary{background:var(--ps-highlight-background);border-color:var(--ps-highlight-background);color:var(--ps-highlight-foreground)}.ps-modal-button.is-danger{background:#dc2626;border-color:#dc2626;color:#fff}.ps-modal-button.is-danger:hover{background:#ef4444;border-color:#ef4444}.portal-primary-button,.portal-secondary-button{padding:10px 14px;margin-top:20px}.portal-secondary-button{margin-top:0;flex:0 0 auto}.portal-header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:12px;padding:10px 14px;border-bottom:1px solid color-mix(in srgb,var(--ps-border) 50%,transparent);background:color-mix(in srgb,var(--ps-page-background) 86%,transparent);-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px)}.portal-header-brand{display:flex;align-items:center;gap:12px;min-width:0}.portal-header-brand-copy{display:flex;flex-direction:column;gap:3px;min-width:0}.portal-logo-frame{width:38px;height:38px;border-radius:var(--ps-radius-sm);border:1px solid color-mix(in srgb,var(--ps-modal-selected-border) 52%,var(--ps-modal-border));background:radial-gradient(circle at 28% 24%,color-mix(in srgb,var(--ps-modal-selected-border) 20%,transparent),transparent 58%),linear-gradient(135deg,color-mix(in srgb,var(--ps-highlight-background) 20%,var(--ps-surface)),color-mix(in srgb,var(--ps-background) 92%,transparent));box-shadow:inset 0 1px color-mix(in srgb,white 10%,transparent),0 10px 24px #0000002e;display:grid;place-items:center;flex:0 0 auto;overflow:hidden}.portal-logo-frame.is-large{width:76px;height:76px;border-radius:16px}.portal-logo-frame.has-image{background:radial-gradient(circle at 28% 24%,color-mix(in srgb,white 10%,transparent),transparent 58%),linear-gradient(135deg,color-mix(in srgb,var(--ps-surface) 94%,white 6%),color-mix(in srgb,var(--ps-background) 90%,transparent))}.portal-logo{width:24px;height:24px}.portal-logo-frame.is-large .portal-logo{width:44px;height:44px}.portal-logo-image{width:74%;height:74%;object-fit:contain;display:block}.portal-logo-frame.is-large .portal-logo-image{width:82%;height:82%}.portal-logo-ring,.portal-logo-link{stroke:color-mix(in srgb,var(--ps-modal-selected-border) 82%,var(--ps-page-foreground));stroke-linecap:round;stroke-linejoin:round}.portal-logo-ring{stroke-width:3;opacity:.46}.portal-logo-link{stroke-width:2.5;opacity:.9}.portal-logo-core{fill:var(--ps-highlight-background)}.portal-logo-node{stroke:color-mix(in srgb,var(--ps-page-background) 70%,transparent);stroke-width:1.5}.portal-logo-node-a,.portal-logo-node-d{fill:#7ee787}.portal-logo-node-b,.portal-logo-node-e{fill:#58a6ff}.portal-logo-node-c,.portal-logo-node-f{fill:#d2a8ff}.portal-header-user{display:inline-flex;align-items:flex-start;gap:10px;align-self:start}.portal-header-meta{display:inline-flex;flex-direction:column;align-items:flex-end;gap:4px;min-width:0}.portal-header-version{display:inline-flex;align-items:center;padding:3px 8px;border:1px solid color-mix(in srgb,var(--ps-border) 48%,transparent);border-radius:999px;background:color-mix(in srgb,var(--ps-surface) 90%,transparent);color:var(--ps-muted);font-size:.72rem;line-height:1;white-space:nowrap}.portal-header-status{max-width:14rem;color:var(--ps-muted);font-size:.72rem;line-height:1;text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.portal-header-identity,.portal-header-identity-stack{min-width:0}.portal-header-identity-stack{display:flex;flex-direction:column;align-items:flex-start;gap:1px;text-align:left}.portal-header-name,.portal-header-email,.portal-header-identity{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.portal-header-name{color:var(--ps-foreground);font-size:.9rem;font-weight:700;line-height:1.05}.portal-header-email{color:var(--ps-muted);font-size:.78rem;line-height:1.05}.portal-header-identity{color:var(--ps-foreground);font-size:.84rem}.portal-header-identity.is-muted{color:var(--ps-muted)}.portal-main{flex:1;min-height:0;padding:10px;overflow:hidden}.ps-web-shell{min-height:0;height:100%;display:flex;flex-direction:column;gap:8px;overflow:hidden}.ps-toolbar{display:flex;align-items:center;justify-content:space-between;gap:6px}.ps-toolbar.is-mobile{flex-direction:column;align-items:stretch;justify-content:flex-start}.ps-toolbar-row{display:flex;align-items:center;gap:6px;min-width:0}.ps-toolbar-row-actions{display:flex;align-items:center;gap:6px;min-width:0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;scrollbar-width:none}.ps-toolbar-row-actions::-webkit-scrollbar{display:none}.ps-toolbar-row-primary,.ps-toolbar-row-secondary{min-width:0}.ps-toolbar-row-secondary{justify-content:space-between}.ps-toolbar-actions{display:flex;flex-wrap:wrap;gap:6px;min-width:0}.ps-toolbar-status{flex:0 1 auto;color:var(--ps-muted);font-size:10px;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ps-toolbar-button,.ps-mini-button,.ps-modal-button,.ps-mobile-nav-button,.ps-tab,.ps-filter-option,.ps-modal-close{padding:6px 10px;min-height:34px;font-size:.92rem;line-height:1}.ps-workspace{flex:1;min-height:0;overflow:hidden}.ps-workspace>*{min-height:0}.ps-workspace-full{min-height:0;height:100%;display:flex}.ps-workspace-full>.ps-panel{flex:1;min-height:0}.ps-workspace-grid{display:grid;grid-template-rows:minmax(0,1fr);gap:8px;min-width:0;width:100%;max-width:100%;min-height:0;height:100%;overflow:hidden}.ps-workspace-column{display:grid;gap:8px;min-width:0;width:100%;max-width:100%;min-height:0;overflow:hidden}.ps-workspace-main-grid{display:grid;grid-template-rows:minmax(0,1fr);gap:8px;min-width:0;width:100%;max-width:100%;min-height:0;height:100%;overflow:hidden}.ps-workspace-pane-slot{min-width:0;width:100%;max-width:100%;min-height:0;display:flex;flex-direction:column;overflow:hidden}.ps-workspace-pane-slot>.ps-panel{flex:1;min-width:0;width:100%;max-width:100%;min-height:0}.ps-chat-focus-shell{min-height:0;min-width:0;width:100%;max-width:100%;height:100%;display:grid;grid-template-rows:auto minmax(0,1fr);gap:8px;overflow:hidden}.ps-chat-focus-rail{display:flex;align-items:center;gap:6px;padding:4px 6px;border:1px solid color-mix(in srgb,var(--ps-border) 42%,transparent);border-radius:var(--ps-radius-sm);background:color-mix(in srgb,var(--ps-surface) 94%,transparent)}.ps-chat-focus-button{flex:0 0 auto}.ps-chat-focus-status{margin-left:auto;color:var(--ps-muted);font-size:.72rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ps-chat-focus-body{position:relative;min-height:0;min-width:0;display:flex;overflow:hidden}.ps-chat-focus-body>.ps-panel{flex:1;min-height:0;min-width:0}.ps-chat-focus-body .ps-panel-body,.ps-chat-focus-body .ps-scroll-panel{min-width:0}.ps-chat-focus-body .ps-line,.ps-chat-focus-body .ps-chat-card-line{overflow-wrap:anywhere}.ps-chat-focus-overlay{position:absolute;top:10px;bottom:10px;width:min(34rem,calc(100% - 20px));max-width:min(34rem,42%);min-width:0;z-index:6;pointer-events:none;overflow:hidden}.ps-chat-focus-overlay.is-left{left:10px}.ps-chat-focus-overlay.is-right{right:10px}.ps-chat-focus-overlay>.ps-panel{height:100%;min-width:0;max-width:100%;pointer-events:auto;box-shadow:0 20px 48px #00000057,inset 0 0 0 1px color-mix(in srgb,var(--ps-page-background) 35%,transparent)}.ps-mobile-workspace{min-height:0;min-width:0;width:100%;max-width:100%;height:100%;display:grid;grid-template-rows:auto minmax(0,1fr);gap:8px;overflow:hidden}.ps-mobile-session-pane{max-height:min(14vh,96px)}.ps-mobile-session-pane .ps-action-list{min-height:0}.ps-mobile-session-pane .ps-action-list .ps-line{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;overflow-wrap:normal;word-break:normal}.ps-mobile-session-collapsed .ps-panel-body{flex:0 0 auto}.ps-mobile-session-summary{padding:0 10px 10px;color:var(--ps-muted);font-size:var(--ps-font-size-dense);line-height:var(--ps-line-height-dense)}.ps-mobile-chat-pane{min-height:0;min-width:0;width:100%;max-width:100%;display:flex;flex-direction:column;overflow:hidden}.ps-mobile-chat-pane>.ps-panel{flex:1;min-width:0;width:100%;max-width:100%;min-height:0}.ps-mobile-pane-fill{min-height:0;height:100%;display:flex;flex-direction:column;overflow:hidden}.ps-mobile-pane-fill>.ps-panel{flex:1;min-height:0}.ps-column-resizer{align-self:stretch;width:16px;min-height:0;padding:0;border:0;border-radius:999px;background:transparent;display:grid;place-items:center;cursor:col-resize;position:relative}.ps-row-resizer{justify-self:stretch;height:16px;min-width:0;padding:0;border:0;border-radius:999px;background:transparent;display:grid;place-items:center;cursor:row-resize;position:relative}.ps-column-resizer:before{content:"";position:absolute;top:10px;bottom:10px;left:50%;width:1px;transform:translate(-50%);background:color-mix(in srgb,var(--ps-border) 52%,transparent)}.ps-row-resizer:before{content:"";position:absolute;left:10px;right:10px;top:50%;height:1px;transform:translateY(-50%);background:color-mix(in srgb,var(--ps-border) 52%,transparent)}.ps-column-resizer-handle{position:relative;z-index:1;display:grid;gap:4px;padding:10px 4px;border-radius:999px;border:1px solid color-mix(in srgb,var(--ps-modal-border) 78%,transparent);background:color-mix(in srgb,var(--ps-surface) 94%,transparent);box-shadow:0 6px 20px #0000002e}.ps-row-resizer-handle{position:relative;z-index:1;display:flex;gap:4px;padding:4px 10px;border-radius:999px;border:1px solid color-mix(in srgb,var(--ps-modal-border) 78%,transparent);background:color-mix(in srgb,var(--ps-surface) 94%,transparent);box-shadow:0 6px 20px #0000002e}.ps-column-resizer-dot,.ps-row-resizer-dot{width:3px;height:3px;border-radius:999px;background:color-mix(in srgb,var(--ps-modal-selected-border) 84%,var(--ps-page-foreground))}.ps-column-resizer:hover .ps-column-resizer-handle,.ps-column-resizer.is-dragging .ps-column-resizer-handle,.ps-column-resizer:focus-visible .ps-column-resizer-handle{border-color:color-mix(in srgb,var(--ps-modal-selected-border) 72%,var(--ps-modal-border));background:color-mix(in srgb,var(--ps-modal-selected-background) 72%,var(--ps-surface))}.ps-row-resizer:hover .ps-row-resizer-handle,.ps-row-resizer.is-dragging .ps-row-resizer-handle,.ps-row-resizer:focus-visible .ps-row-resizer-handle{border-color:color-mix(in srgb,var(--ps-modal-selected-border) 72%,var(--ps-modal-border));background:color-mix(in srgb,var(--ps-modal-selected-background) 72%,var(--ps-surface))}.ps-column-resizer:focus-visible{outline:none}.ps-row-resizer:focus-visible{outline:none}body.is-resizing-pane-x,body.is-resizing-pane-x *{cursor:col-resize!important;-webkit-user-select:none!important;user-select:none!important}body.is-resizing-pane-y,body.is-resizing-pane-y *{cursor:row-resize!important;-webkit-user-select:none!important;user-select:none!important}.ps-panel{position:relative;min-height:0;display:flex;flex-direction:column;overflow:hidden;border:2px solid var(--ps-panel-accent, var(--ps-border));border-radius:var(--ps-radius-sm);background:color-mix(in srgb,var(--ps-surface) 96%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--ps-page-background) 35%,transparent)}.ps-panel.is-focused{box-shadow:0 0 0 1px color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 40%,transparent),inset 0 0 0 1px color-mix(in srgb,var(--ps-page-background) 35%,transparent)}.ps-panel-header{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:30px;padding:4px 10px;border-bottom:1px solid color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 28%,transparent);background:linear-gradient(180deg,color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 16%,var(--ps-surface)),color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 10%,var(--ps-surface)));margin-bottom:0}.ps-panel-title{display:inline-flex;align-items:center;gap:4px;padding:0;background:transparent;font-size:.94rem;font-weight:700;line-height:1.02}.ps-panel-header-right{display:inline-flex;align-items:center;gap:8px;min-width:0;margin-left:auto}.ps-panel-title-right{display:inline-flex;align-items:center;gap:0;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.82rem;line-height:1}.ps-panel-actions{display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px;padding-left:0;background:transparent}.ps-panel-body,.ps-scroll-panel{flex:1;min-width:0;width:100%;max-width:100%;min-height:0}.ps-panel-body{display:flex;flex-direction:column;min-height:0;padding-top:2px}.ps-scroll-panel{overflow:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;touch-action:pan-x pan-y;scrollbar-gutter:stable both-edges;padding:6px 10px 10px;font-size:var(--ps-font-size-dense);line-height:var(--ps-line-height-dense)}.ps-panel-sticky{flex:0 0 auto;padding:6px 10px 0;border-bottom:1px solid color-mix(in srgb,var(--ps-border) 35%,transparent);font-size:var(--ps-font-size-dense);line-height:var(--ps-line-height-dense)}.ps-panel-sticky.is-scroll-sync{overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;touch-action:pan-x;scrollbar-gutter:stable both-edges}.ps-panel-bottom-sticky{flex:0 0 auto;max-height:34%;overflow:auto;padding:4px 10px 8px;border-top:1px solid color-mix(in srgb,var(--ps-border) 45%,transparent);background:color-mix(in srgb,var(--ps-surface) 94%,#0f172a 6%);font-size:var(--ps-font-size-dense);line-height:var(--ps-line-height-dense);scrollbar-gutter:stable both-edges}.ps-line{min-height:1.16rem;white-space:pre-wrap;line-height:1.16rem;word-break:break-word}.ps-panel.has-preserved-sticky .ps-panel-sticky .ps-line,.ps-scroll-panel.is-preserve .ps-line{white-space:pre;word-break:normal;overflow-wrap:normal}.ps-scroll-panel.is-preserve .ps-line{min-width:max-content}.ps-chat-preserve-block{max-width:100%;overflow-x:auto}.ps-scroll-panel.is-wrapped .ps-line.ps-line-preserve,.ps-line-preserve{white-space:pre;word-break:normal;overflow-wrap:normal;min-width:max-content}.ps-sequence-turn-divider{width:100%;margin:4px 0;padding:2px 6px;border:0;--ps-sequence-divider: #4f6f96;border-top:1px solid color-mix(in srgb,var(--ps-sequence-divider) 82%,transparent);border-bottom:1px solid color-mix(in srgb,var(--ps-sequence-divider) 42%,transparent);border-radius:0;background:transparent;color:color-mix(in srgb,var(--ps-sequence-divider) 78%,var(--ps-text-muted));font:inherit;text-align:center;cursor:pointer}.ps-sequence-turn-divider:hover,.ps-sequence-turn-divider:focus-visible{color:var(--ps-text);background:color-mix(in srgb,var(--ps-surface-raised) 72%,transparent);outline:none}.ps-sequence-turn-caret{color:var(--ps-sequence-divider)}.ps-sequence-turn-rule{color:color-mix(in srgb,var(--ps-sequence-divider) 76%,var(--ps-text-muted))}.ps-sequence-turn-key{color:var(--ps-text-muted)}.ps-sequence-turn-model{color:var(--ps-text)}.ps-sequence-turn-tokens{color:var(--ps-success)}.ps-sequence-turn-duration{color:var(--ps-warning)}.ps-sequence-turn-details{margin:0 12px 8px;padding:8px 10px;border-left:2px solid var(--ps-accent);color:var(--ps-text-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;line-height:1.5}.ps-sequence-turn-detail-label{color:var(--ps-accent)}.ps-scroll-panel.is-wrapped .ps-line,.ps-modal-details .ps-line,.ps-filter-column .ps-line{white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;max-width:100%}.ps-chat-card{margin:8px auto 8px 0;border:1px solid color-mix(in srgb,var(--ps-chat-card-accent, var(--ps-border)) 78%,transparent);border-radius:var(--ps-radius-sm);background:color-mix(in srgb,var(--ps-surface) 98%,transparent);overflow:hidden;width:fit-content;max-width:min(100%,82ch)}.ps-chat-card-header{padding:8px 12px 6px;border-bottom:1px solid color-mix(in srgb,var(--ps-chat-card-accent, var(--ps-border)) 28%,transparent);background:color-mix(in srgb,var(--ps-chat-card-accent, var(--ps-surface)) 10%,transparent)}.ps-chat-card-body{padding:8px 12px 10px}.ps-chat-card-line{white-space:pre-wrap;word-break:break-word}.ps-chat-card-line+.ps-chat-card-line{margin-top:6px}.ps-chat-panel .ps-line,.ps-chat-panel .ps-chat-card-line{min-height:1.24rem;line-height:1.24rem}.ps-system-notice{margin:10px 0 12px;color:var(--ps-muted)}.ps-system-notice[open]{margin-bottom:14px}.ps-system-notice-summary{cursor:pointer;list-style:none;-webkit-user-select:none;user-select:none;opacity:.86;transition:opacity .12s ease}.ps-system-notice-summary:hover,.ps-system-notice[open] .ps-system-notice-summary{opacity:1}.ps-system-notice-summary::-webkit-details-marker{display:none}.ps-system-notice-summary-text{display:inline-block;white-space:pre-wrap}.ps-system-notice-summary:before{content:"▸ ";color:color-mix(in srgb,var(--ps-muted) 84%,transparent)}.ps-system-notice[open] .ps-system-notice-summary:before{content:"▾ "}.ps-system-notice-body{margin-top:8px;padding-left:12px;border-left:1px solid color-mix(in srgb,var(--ps-border) 42%,transparent);color:var(--ps-muted)}.ps-system-notice-body .ps-markdown-preview{gap:10px}.ps-system-notice-body .ps-md-paragraph,.ps-system-notice-body .ps-md-list,.ps-system-notice-body .ps-md-quote{color:var(--ps-muted)}.ps-chat-table-wrap{margin:8px auto 8px 0;border:1px solid color-mix(in srgb,var(--ps-border) 55%,transparent);border-radius:var(--ps-radius-sm);overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;width:fit-content;max-width:100%;scrollbar-width:thin;scrollbar-color:var(--ps-scrollbar-thumb) transparent}.ps-chat-table-wrap::-webkit-scrollbar,.ps-md-table-wrap::-webkit-scrollbar,.ps-md-code-pre::-webkit-scrollbar{width:var(--ps-scrollbar-size);height:var(--ps-scrollbar-size)}.ps-chat-table-wrap::-webkit-scrollbar-track,.ps-md-table-wrap::-webkit-scrollbar-track,.ps-md-code-pre::-webkit-scrollbar-track{background:transparent}.ps-chat-table-wrap::-webkit-scrollbar-thumb,.ps-md-table-wrap::-webkit-scrollbar-thumb,.ps-md-code-pre::-webkit-scrollbar-thumb{background:var(--ps-scrollbar-thumb);border:2px solid transparent;border-radius:999px;background-clip:padding-box;min-height:28px;min-width:28px}.ps-chat-table-wrap::-webkit-scrollbar-thumb:hover,.ps-md-table-wrap::-webkit-scrollbar-thumb:hover,.ps-md-code-pre::-webkit-scrollbar-thumb:hover{background:var(--ps-scrollbar-thumb-hover);border:2px solid transparent;background-clip:padding-box}.ps-chat-table-wrap.is-fit-width,.ps-md-table-wrap.is-fit-width{width:fit-content;max-width:100%}.ps-chat-table{width:max-content;border-collapse:collapse;table-layout:auto}.ps-chat-table.is-fit-width,.ps-md-table.is-fit-width{width:fit-content;max-width:100%;min-width:var(--ps-table-min-width, 0px);table-layout:auto}.ps-chat-table th,.ps-chat-table td{padding:6px 8px;border-right:1px solid color-mix(in srgb,var(--ps-border) 34%,transparent);border-bottom:1px solid color-mix(in srgb,var(--ps-border) 34%,transparent);white-space:pre-wrap;word-break:break-word;vertical-align:top}.ps-chat-table.is-fit-width th,.ps-chat-table.is-fit-width td,.ps-md-table.is-fit-width th,.ps-md-table.is-fit-width td{min-width:0;white-space:normal;word-break:break-word;overflow-wrap:anywhere}.ps-chat-table-wrap.is-fit-width.has-flex-column,.ps-md-table-wrap.is-fit-width.has-flex-column{width:100%;max-width:100%}.ps-chat-table.is-fit-width.has-flex-column,.ps-md-table.is-fit-width.has-flex-column{width:max(100%,var(--ps-table-min-width, 0px));min-width:var(--ps-table-min-width, 0px);table-layout:fixed}.ps-chat-table.is-fit-width.has-flex-column th,.ps-chat-table.is-fit-width.has-flex-column td,.ps-md-table.is-fit-width.has-flex-column th,.ps-md-table.is-fit-width.has-flex-column td{overflow-wrap:normal;word-break:normal}.ps-chat-table.is-fit-width.has-flex-column th.is-flex-column,.ps-chat-table.is-fit-width.has-flex-column td.is-flex-column,.ps-md-table.is-fit-width.has-flex-column th.is-flex-column,.ps-md-table.is-fit-width.has-flex-column td.is-flex-column{white-space:normal;overflow-wrap:anywhere;word-break:break-word}.ps-chat-table th:last-child,.ps-chat-table td:last-child{border-right:0}.ps-chat-table tbody tr:last-child td{border-bottom:0}.ps-chat-table th{text-align:left;font-weight:700;color:var(--ps-foreground);background:color-mix(in srgb,var(--ps-highlight-background) 10%,var(--ps-surface))}.ps-table-cell-value{min-width:0}.ps-chat-code-block{margin:8px auto 8px 0;width:min(100%,120ch);max-width:min(100%,120ch)}.ps-action-list{display:flex;flex-direction:column;flex:1 1 auto;gap:4px;padding:6px 10px 10px;min-width:0;min-height:0;width:100%;max-width:100%;overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch;touch-action:pan-y;scrollbar-gutter:stable both-edges;font-size:var(--ps-font-size-dense);line-height:var(--ps-line-height-dense)}.ps-session-pane.ps-chat-focus-pane .ps-action-list.ps-session-list{overflow-x:auto;touch-action:pan-x pan-y;padding-bottom:calc(6px + var(--ps-scrollbar-size))}.ps-session-pane.ps-chat-focus-pane .ps-session-list-button{width:max-content;min-width:100%;max-width:none;overflow:visible}.ps-session-pane.ps-chat-focus-pane .ps-session-row-content{display:inline-block;min-width:max-content;overflow:visible;max-width:none}.ps-session-pane.ps-chat-focus-pane .ps-action-list .ps-line{max-width:none;min-width:0;white-space:nowrap;overflow:visible;text-overflow:clip}.ps-list-button{width:100%;min-width:0;max-width:100%;overflow:hidden;text-align:left;padding:7px 9px}.ps-action-list .ps-line{display:block;min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ps-session-pane .ps-panel-body{padding:2px 8px 8px}.ps-session-pane .ps-session-list{gap:1px;padding:2px 0 6px}.ps-session-pane:not(.ps-mobile-session-pane) .ps-session-list{overflow-x:auto;overflow-y:auto;touch-action:pan-x pan-y;padding-bottom:calc(6px + var(--ps-scrollbar-size))}.ps-session-pane:not(.ps-mobile-session-pane) .ps-session-list-button{width:max-content;min-width:100%;max-width:none;overflow:visible}.ps-session-pane .ps-session-list-button{display:block;border:0;border-radius:2px;background:transparent;box-shadow:none;color:inherit;padding:3px 8px;min-height:1.55rem;white-space:normal}.ps-session-pane .ps-session-list-button:hover{transform:none;border-color:transparent;background:color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 7%,transparent)}.ps-session-pane .ps-session-list-button.is-selected{border-color:transparent;background:color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 15%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 80%,transparent),inset 3px 0 color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 90%,transparent);color:inherit}.ps-session-pane .ps-session-list-button.is-multiselected{background:color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 22%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 60%,transparent),inset 3px 0 color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 95%,transparent)}.ps-session-pane .ps-session-list-button.is-pinned{background:color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 5%,transparent)}.ps-session-pane .ps-session-list-button:focus-visible{outline:none;box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 80%,transparent),inset 3px 0 color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 90%,transparent),0 0 0 1px color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 35%,transparent)}.ps-session-row-content{display:block;min-width:0;max-width:100%;overflow:visible}.ps-session-row-title,.ps-session-row-badges,.ps-session-row-selected-meta{min-width:0;max-width:100%}.ps-session-row-title{line-height:1.25}.ps-session-row-meta{font-size:.94em}.ps-session-row-badges,.ps-session-row-selected-meta{margin-top:2px;padding-inline-start:18px;line-height:1.22;font-size:.94em}.ps-session-row-selected-meta{padding-top:2px;opacity:.94}.ps-session-pane:not(.ps-mobile-session-pane) .ps-session-row-content,.ps-session-pane:not(.ps-mobile-session-pane) .ps-session-row-title,.ps-session-pane:not(.ps-mobile-session-pane) .ps-session-row-badges,.ps-session-pane:not(.ps-mobile-session-pane) .ps-session-row-selected-meta{display:block;width:max-content;min-width:max-content;max-width:none}.ps-session-pane .ps-action-list .ps-line{white-space:nowrap;overflow:visible;text-overflow:clip;overflow-wrap:normal;word-break:normal;max-width:none}.ps-session-pane.ps-mobile-session-pane .ps-action-list .ps-line{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;overflow-wrap:normal;word-break:normal}.ps-list-button.is-selected,.ps-toolbar-button.is-active,.ps-mini-button.is-active,.ps-tab.is-active,.ps-mobile-nav-button.is-active,.ps-filter-option.is-selected{border-color:var(--ps-modal-selected-border);background:var(--ps-modal-selected-background);color:var(--ps-modal-selected-foreground)}.ps-empty-state{color:var(--ps-muted);padding:6px 0}.ps-tab-row{display:flex;flex-wrap:wrap;gap:4px;padding:2px 10px 4px;align-items:center}.ps-panel .ps-mini-button,.ps-panel .ps-tab{padding:4px 8px;min-height:30px}.ps-files-grid{flex:1;min-height:0;display:grid;grid-template-rows:minmax(180px,.9fr) minmax(220px,1.3fr);gap:8px;padding:0 10px 10px}.ps-scroll-panel.is-preview,.ps-markdown-scroll{padding-top:10px}.ps-markdown-preview{display:flex;flex-direction:column;gap:14px;padding-right:4px;line-height:1.55}.ps-binary-preview-card{display:flex;flex-direction:column;gap:10px;margin:10px 10px 12px;padding:18px 18px 20px;border:1px solid color-mix(in srgb,var(--ps-border) 52%,transparent);border-radius:calc(var(--ps-radius-md) + 2px);background:radial-gradient(circle at top right,color-mix(in srgb,var(--ps-highlight-background) 18%,transparent),transparent 42%),linear-gradient(180deg,color-mix(in srgb,var(--ps-surface) 96%,transparent),color-mix(in srgb,var(--ps-background) 96%,transparent));box-shadow:inset 0 1px color-mix(in srgb,white 5%,transparent)}.ps-binary-preview-kicker{font-size:.76rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ps-muted)}.ps-binary-preview-title{margin:0;font-size:1.08rem;line-height:1.25;font-weight:800;color:var(--ps-foreground);word-break:break-word}.ps-binary-preview-meta,.ps-binary-preview-time{color:color-mix(in srgb,var(--ps-foreground) 76%,var(--ps-muted));font-size:.9rem;line-height:1.45;word-break:break-word}.ps-binary-preview-copy{margin:4px 0 0;color:var(--ps-foreground);line-height:1.6}.ps-md-heading{margin:0;font-weight:800;line-height:1.2;color:var(--ps-foreground)}.ps-md-heading.is-h1{font-size:1.45rem}.ps-md-heading.is-h2{font-size:1.28rem}.ps-md-heading.is-h3{font-size:1.12rem}.ps-md-heading.is-h4,.ps-md-heading.is-h5,.ps-md-heading.is-h6{font-size:1rem}.ps-md-paragraph,.ps-md-quote,.ps-md-list{margin:0}.ps-md-list{padding-left:22px;display:flex;flex-direction:column;gap:6px}.ps-md-list-item{line-height:1.5}.ps-md-quote{margin:0;padding:10px 14px;border-left:3px solid color-mix(in srgb,var(--ps-modal-selected-border) 78%,transparent);background:color-mix(in srgb,var(--ps-highlight-background) 8%,transparent);color:color-mix(in srgb,var(--ps-foreground) 88%,var(--ps-muted))}.ps-md-inline-code{padding:1px 6px;border-radius:999px;background:color-mix(in srgb,var(--ps-highlight-background) 12%,var(--ps-surface));border:1px solid color-mix(in srgb,var(--ps-border) 35%,transparent);font-size:.94em}.ps-md-code-block{border:1px solid color-mix(in srgb,var(--ps-border) 45%,transparent);border-radius:var(--ps-radius-sm);overflow:hidden;background:color-mix(in srgb,var(--ps-background) 96%,black 4%)}.ps-md-code-header{padding:7px 12px;font-size:.82rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ps-muted);border-bottom:1px solid color-mix(in srgb,var(--ps-border) 32%,transparent);background:color-mix(in srgb,var(--ps-surface) 92%,transparent)}.ps-md-code-pre{margin:0;padding:14px 16px;overflow:auto;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;touch-action:pan-x pan-y;line-height:1.55;font-size:.95rem;white-space:pre;max-width:100%;scrollbar-width:thin;scrollbar-color:var(--ps-scrollbar-thumb) transparent}.ps-md-code-pre code{font:inherit;color:#9fe3ff}.ps-md-table-wrap{overflow-x:auto;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;touch-action:pan-x pan-y;border:1px solid color-mix(in srgb,var(--ps-border) 40%,transparent);border-radius:var(--ps-radius-sm);max-width:100%;scrollbar-width:thin;scrollbar-color:var(--ps-scrollbar-thumb) transparent}.ps-md-table{width:100%;border-collapse:collapse}.ps-md-table th,.ps-md-table td{padding:9px 12px;border-bottom:1px solid color-mix(in srgb,var(--ps-border) 25%,transparent);text-align:left;vertical-align:top;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}.ps-md-table th{background:color-mix(in srgb,var(--ps-highlight-background) 10%,var(--ps-surface));font-weight:700}.ps-md-table tbody tr:last-child td{border-bottom:0}.ps-md-link{text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere;word-break:break-word}.ps-chat-panel .ps-panel-header{padding-bottom:8px}.ps-chat-panel .ps-panel-body{padding-top:2px}.ps-chat-panel .ps-scroll-panel{padding-top:6px}.ps-chat-panel .ps-panel-bottom-sticky{box-shadow:0 -10px 22px color-mix(in srgb,#000 24%,transparent)}.ps-status-strip{display:flex;justify-content:space-between;gap:10px;padding:0 2px;color:var(--ps-muted);font-size:9px}.ps-status-left,.ps-status-right{min-width:0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ps-chat-composer{display:flex;flex-direction:column;gap:4px;padding:8px 10px 10px;border-top:1px solid color-mix(in srgb,var(--ps-panel-accent, var(--ps-border)) 32%,transparent);background:color-mix(in srgb,var(--ps-surface) 98%,transparent);isolation:isolate}.ps-prompt-shell{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:8px;align-items:center;padding:0}.ps-prompt-shell.is-mobile{grid-template-columns:auto minmax(0,1fr) auto;gap:8px}.ps-prompt-actions{display:inline-flex;align-items:center;gap:8px}.ps-hidden-file-input{display:none}.ps-prompt-label{padding:0 4px 0 2px;color:var(--ps-muted);text-transform:uppercase;font-size:10px;letter-spacing:.08em;align-self:center}.ps-prompt-shell.is-mobile .ps-prompt-label{padding:0 2px 0 0}.ps-prompt-input,.ps-modal-input{width:100%;min-height:40px;resize:vertical;border:1px solid color-mix(in srgb,var(--ps-border) 60%,transparent);border-radius:var(--ps-radius-sm);background:var(--ps-background);background-clip:padding-box;color:var(--ps-foreground);caret-color:var(--ps-foreground);padding:8px 10px;outline:none;font-size:var(--ps-font-size-dense);line-height:var(--ps-line-height-dense)}.ps-prompt-input::placeholder,.ps-modal-input::placeholder{color:color-mix(in srgb,var(--ps-muted) 74%,transparent);opacity:1}.ps-prompt-shell.is-mobile .ps-prompt-input{font-size:16px;line-height:1.25}.ps-prompt-input:focus,.ps-modal-input:focus{border-color:var(--ps-modal-selected-border);box-shadow:0 0 0 3px color-mix(in srgb,var(--ps-modal-selected-border) 20%,transparent)}.ps-prompt-shell.is-drag-over .ps-prompt-input{border-color:var(--ps-modal-selected-border);background:color-mix(in srgb,var(--ps-modal-selected-background) 16%,var(--ps-background));box-shadow:0 0 0 3px color-mix(in srgb,var(--ps-modal-selected-border) 18%,transparent)}.ps-send-button{min-height:40px;padding:0 12px}.ps-stop-button{display:inline-flex;align-items:center;justify-content:center;min-height:40px;min-width:40px;padding:0;font-size:15px;line-height:1;border:1px solid color-mix(in srgb,#e5534b 55%,var(--ps-modal-border));border-radius:var(--ps-radius-sm);background:color-mix(in srgb,#e5534b 16%,var(--ps-surface));color:#ff8a80;transition:background .12s ease,border-color .12s ease,transform .12s ease}.ps-stop-button:hover:not(:disabled){background:color-mix(in srgb,#e5534b 30%,var(--ps-surface));border-color:#e5534b}.ps-stop-button:disabled{cursor:default}.ps-stop-button.is-stopping{animation:ps-stop-pulse 1s ease-in-out infinite}@keyframes ps-stop-pulse{50%{opacity:.35}}.ps-send-button.is-inline{min-height:40px;min-width:40px;padding:0;border-radius:var(--ps-radius-sm);font-size:18px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.ps-mobile-nav{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}.ps-modal-backdrop{position:fixed;inset:0;background:var(--ps-modal-backdrop);display:flex;align-items:center;justify-content:center;padding:16px;z-index:50}.ps-modal{width:min(900px,100%);max-height:min(86vh,900px);overflow:auto;-webkit-overflow-scrolling:touch;touch-action:pan-y;scrollbar-gutter:stable both-edges;border:1px solid var(--ps-modal-border);border-radius:var(--ps-radius-md);background:var(--ps-modal-background);color:var(--ps-modal-foreground);padding:18px;box-shadow:0 30px 80px #0006}.ps-scroll-panel::-webkit-scrollbar,.ps-action-list::-webkit-scrollbar,.ps-panel-sticky.is-scroll-sync::-webkit-scrollbar,.ps-modal::-webkit-scrollbar{width:var(--ps-scrollbar-size);height:var(--ps-scrollbar-size)}.ps-scroll-panel::-webkit-scrollbar-track,.ps-action-list::-webkit-scrollbar-track,.ps-panel-sticky.is-scroll-sync::-webkit-scrollbar-track,.ps-modal::-webkit-scrollbar-track{background:transparent}.ps-scroll-panel::-webkit-scrollbar-thumb,.ps-action-list::-webkit-scrollbar-thumb,.ps-panel-sticky.is-scroll-sync::-webkit-scrollbar-thumb,.ps-modal::-webkit-scrollbar-thumb{background:var(--ps-scrollbar-thumb);border:2px solid transparent;border-radius:999px;background-clip:padding-box;min-height:28px}.ps-scroll-panel::-webkit-scrollbar-thumb:hover,.ps-action-list::-webkit-scrollbar-thumb:hover,.ps-panel-sticky.is-scroll-sync::-webkit-scrollbar-thumb:hover,.ps-modal::-webkit-scrollbar-thumb:hover{background:var(--ps-scrollbar-thumb-hover);border:2px solid transparent;background-clip:padding-box}.ps-scroll-panel::-webkit-scrollbar-corner,.ps-action-list::-webkit-scrollbar-corner,.ps-panel-sticky.is-scroll-sync::-webkit-scrollbar-corner,.ps-modal::-webkit-scrollbar-corner{background:transparent}.ps-modal.is-narrow{width:min(560px,100%)}.ps-modal.is-wide{width:min(980px,100%)}.ps-modal.is-theme-picker{max-height:min(57vh,600px)}.ps-modal-header,.ps-modal-footer{display:flex;align-items:center;justify-content:space-between;gap:12px}.ps-modal-title,.ps-modal-details-title,.ps-filter-title{font-size:16px;font-weight:700}.ps-modal-grid{display:grid;grid-template-columns:minmax(280px,1fr) minmax(260px,.9fr);gap:16px;margin:16px 0}.ps-modal-list,.ps-modal-details{display:flex;flex-direction:column;gap:10px}.ps-modal-list-heading-line,.ps-modal-list-line,.ps-modal-detail-line{display:block;width:100%;text-align:left}.ps-modal-list-button.is-hanging .ps-modal-list-line{padding-left:1.5rem;text-indent:-1.15rem}.ps-filter-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;margin:18px 0}.ps-filter-column{display:flex;flex-direction:column;gap:10px}.ps-list-button,.ps-filter-option{justify-content:flex-start;text-align:left}.ps-modal-close{color:var(--ps-muted)}.portal-primary-button:hover,.portal-secondary-button:hover,.ps-toolbar-button:hover,.ps-mini-button:hover,.ps-send-button:hover,.ps-modal-button:hover,.ps-mobile-nav-button:hover,.ps-tab:hover,.ps-filter-option:hover,.ps-list-button:hover,.ps-modal-close:hover,.ps-column-resizer:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--ps-modal-selected-border) 70%,var(--ps-modal-border))}@media(max-width:920px){:root{--ps-font-size-base: 13px;--ps-font-size-dense: 12px}.portal-main{padding:8px}.portal-header{grid-template-columns:minmax(0,1fr) auto;align-items:start;padding:8px 10px}.portal-header-user{gap:8px;align-self:start}.ps-toolbar{align-items:stretch}.ps-toolbar.is-mobile{gap:6px}.ps-toolbar.is-mobile .ps-toolbar-row{flex-wrap:nowrap}.ps-toolbar.is-mobile .ps-toolbar-status{margin-left:auto;max-width:44%;min-width:0;text-align:right}.ps-toolbar-status{max-width:40%;font-size:9px}.ps-chat-focus-rail{flex-wrap:wrap}.ps-chat-focus-status{width:100%;margin-left:0}.ps-chat-focus-overlay{left:8px;right:8px;width:auto;max-width:none;min-width:0}.ps-chat-focus-shell .ps-chat-table-wrap,.ps-chat-focus-shell .ps-md-table-wrap,.ps-chat-focus-shell .ps-chat-card,.ps-chat-focus-shell .ps-chat-code-block{max-width:100%}.ps-chat-table-wrap.is-fit-width.has-flex-column,.ps-md-table-wrap.is-fit-width.has-flex-column{overflow-x:hidden}.ps-chat-table.is-fit-width.has-flex-column,.ps-md-table.is-fit-width.has-flex-column{display:block;width:100%;min-width:0;table-layout:auto}.ps-chat-table.is-fit-width.has-flex-column colgroup,.ps-chat-table.is-fit-width.has-flex-column thead,.ps-md-table.is-fit-width.has-flex-column colgroup,.ps-md-table.is-fit-width.has-flex-column thead{display:none}.ps-chat-table.is-fit-width.has-flex-column tbody,.ps-md-table.is-fit-width.has-flex-column tbody{display:flex;flex-direction:column}.ps-chat-table.is-fit-width.has-flex-column tr,.ps-md-table.is-fit-width.has-flex-column tr{display:grid;gap:0;border-bottom:1px solid color-mix(in srgb,var(--ps-border) 34%,transparent)}.ps-chat-table.is-fit-width.has-flex-column tr:last-child,.ps-md-table.is-fit-width.has-flex-column tr:last-child{border-bottom:0}.ps-chat-table.is-fit-width.has-flex-column td,.ps-md-table.is-fit-width.has-flex-column td{display:grid;grid-template-columns:minmax(7ch,.34fr) minmax(0,1fr);gap:8px;align-items:start;padding:6px 8px;border-right:0;border-bottom:0;min-width:0;overflow-wrap:anywhere;word-break:break-word}.ps-md-table.is-fit-width.has-flex-column td{padding:7px 10px}.ps-chat-table.is-fit-width.has-flex-column td[data-label]:before,.ps-md-table.is-fit-width.has-flex-column td[data-label]:before{content:attr(data-label);min-width:0;color:var(--ps-muted);font-weight:700;overflow-wrap:anywhere;word-break:break-word}.ps-chat-table.is-fit-width.has-flex-column .ps-table-cell-value,.ps-md-table.is-fit-width.has-flex-column .ps-table-cell-value{min-width:0;overflow-wrap:anywhere;word-break:break-word}.ps-chat-composer{padding:4px 6px 6px}.ps-status-right{display:none}.ps-status-strip{justify-content:flex-start}.ps-modal-grid{grid-template-columns:1fr}.ps-files-grid{grid-template-rows:minmax(160px,1fr) minmax(260px,1.4fr)}.ps-panel-header{flex-wrap:nowrap}.ps-panel-title{min-width:0;flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ps-panel-actions{flex:0 0 auto;flex-wrap:nowrap}.ps-column-resizer{display:none}.ps-mobile-session-pane{max-height:min(22vh,148px)}.ps-mobile-session-pane .ps-panel-body,.ps-mobile-session-pane .ps-action-list{min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;touch-action:pan-y}.ps-modal{max-height:82svh;padding:12px}.ps-modal-header,.ps-modal-footer{flex-wrap:wrap;row-gap:8px}.ps-mobile-pane-fill>.ps-panel .ps-scroll-panel,.ps-mobile-pane-fill>.ps-panel .ps-panel-body{flex:1}}.ps-admin-console{display:flex;flex-direction:column;gap:16px;padding:18px 22px;flex:1;min-height:0;overflow:auto;background:var(--ps-panel-background, var(--ps-background));border:1px solid var(--ps-border);border-radius:6px;margin:8px}.ps-admin-console__header{display:flex;justify-content:space-between;align-items:center;gap:12px;border-bottom:1px solid var(--ps-border);padding-bottom:8px}.ps-admin-console__header h2{margin:0;font-size:16px}.ps-admin-console__identity dl{display:grid;grid-template-columns:max-content 1fr;gap:4px 12px;margin:0;font-size:13px}.ps-admin-console__identity dt{color:var(--ps-muted)}.ps-admin-console__identity dd{margin:0}.ps-admin-console__section{display:flex;flex-direction:column;gap:8px;border:1px solid var(--ps-border);border-radius:6px;padding:12px 14px}.ps-admin-console__section h3{margin:0;font-size:14px}.ps-admin-console__hint{margin:0;color:var(--ps-muted);font-size:12px}.ps-admin-console__status{margin:0;font-size:12px;color:var(--ps-muted)}.ps-admin-console__status.is-error{color:var(--ps-error, #d33)}.ps-admin-console__error{border:1px solid var(--ps-error, #d33);background:color-mix(in srgb,var(--ps-error, #d33) 10%,transparent);color:var(--ps-error, #d33);padding:8px 10px;border-radius:4px;font-size:12px}.ps-admin-console__form{display:flex;flex-direction:column;gap:8px}.ps-admin-console__form input{font-family:var(--ps-font-mono, monospace);padding:6px 8px;border:1px solid var(--ps-border);border-radius:4px;background:var(--ps-background);color:var(--ps-foreground)}.ps-admin-console__actions{display:flex;gap:8px;flex-wrap:wrap}
|