deckide 3.3.1 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -2,17 +2,16 @@ import fsSync from 'node:fs';
2
2
  import crypto from 'node:crypto';
3
3
  import path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
- import childProcess from 'node:child_process';
6
5
  import { Hono } from 'hono';
7
6
  import { serve } from '@hono/node-server';
8
7
  import { serveStatic } from '@hono/node-server/serve-static';
9
8
  import { bodyLimit } from 'hono/body-limit';
10
9
  import { DatabaseSync } from 'node:sqlite';
11
- import { PORT, HOST, NODE_ENV, BASIC_AUTH_USER, BASIC_AUTH_PASSWORD, CORS_ORIGIN, MAX_FILE_SIZE, MAX_REQUEST_BODY_SIZE, TRUST_PROXY, TERMINAL_BUFFER_LIMIT, hasStatic, distDir, dbPath, daemonInfoPath, } from './config.js';
10
+ import { PORT, HOST, NODE_ENV, BASIC_AUTH_USER, BASIC_AUTH_PASSWORD, CORS_ORIGIN, MAX_FILE_SIZE, MAX_REQUEST_BODY_SIZE, TRUST_PROXY, hasStatic, distDir, dbPath, } from './config.js';
12
11
  import { securityHeaders } from './middleware/security.js';
13
12
  import { corsMiddleware } from './middleware/cors.js';
14
13
  import { basicAuthMiddleware, generateWsToken, isBasicAuthEnabled } from './middleware/auth.js';
15
- import { checkDatabaseIntegrity, handleDatabaseCorruption, initializeDatabase, loadPersistedState, loadPersistedTerminals, } from './utils/database.js';
14
+ import { checkDatabaseIntegrity, handleDatabaseCorruption, initializeDatabase, loadPersistedState, } from './utils/database.js';
16
15
  import { createWorkspaceRouter, getConfigHandler } from './routes/workspaces.js';
17
16
  import { createDeckRouter } from './routes/decks.js';
18
17
  import { createFileRouter } from './routes/files.js';
@@ -20,7 +19,6 @@ import { createTerminalRouter } from './routes/terminals.js';
20
19
  import { createGitRouter } from './routes/git.js';
21
20
  import { createSettingsRouter } from './routes/settings.js';
22
21
  import { setupWebSocketServer, getConnectionLimit, setConnectionLimit, getConnectionStats, clearAllConnections, } from './websocket.js';
23
- import { PtyClient } from './pty-client.js';
24
22
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
25
23
  // Request ID and logging middleware
26
24
  const requestIdMiddleware = async (c, next) => {
@@ -37,59 +35,6 @@ const requestIdMiddleware = async (c, next) => {
37
35
  console.log(`[${requestId}] ${method} ${path_} ${status} ${duration}ms`);
38
36
  }
39
37
  };
40
- /** Wait for the daemon to write its info file after startup. */
41
- async function waitForDaemonInfo(maxWaitMs = 8000) {
42
- const start = Date.now();
43
- while (Date.now() - start < maxWaitMs) {
44
- if (fsSync.existsSync(daemonInfoPath)) {
45
- try {
46
- return JSON.parse(fsSync.readFileSync(daemonInfoPath, 'utf-8'));
47
- }
48
- catch {
49
- // File may be partially written, retry
50
- }
51
- }
52
- await new Promise((r) => setTimeout(r, 100));
53
- }
54
- throw new Error('PTY daemon did not start within 8 seconds');
55
- }
56
- /** Connect to an existing daemon or spawn a new one. Returns a connected PtyClient. */
57
- async function ensureDaemon() {
58
- const client = new PtyClient();
59
- // Try connecting to an existing daemon
60
- if (fsSync.existsSync(daemonInfoPath)) {
61
- try {
62
- const info = JSON.parse(fsSync.readFileSync(daemonInfoPath, 'utf-8'));
63
- await client.connect(info.port);
64
- console.log(`[SERVER] Connected to existing PTY daemon on port ${info.port} (pid ${info.pid})`);
65
- return client;
66
- }
67
- catch {
68
- console.log('[SERVER] Existing PTY daemon is gone, starting a new one...');
69
- try {
70
- fsSync.unlinkSync(daemonInfoPath);
71
- }
72
- catch { /* ignore */ }
73
- }
74
- }
75
- // Spawn a new daemon process (detached so it survives server restarts)
76
- const daemonScript = path.join(__dirname, 'pty-daemon.js');
77
- const child = childProcess.spawn(process.execPath, [daemonScript], {
78
- detached: true,
79
- stdio: 'ignore',
80
- windowsHide: true,
81
- env: {
82
- ...process.env,
83
- DAEMON_INFO_PATH: daemonInfoPath,
84
- TERMINAL_BUFFER_LIMIT: String(TERMINAL_BUFFER_LIMIT),
85
- },
86
- });
87
- child.unref(); // Don't keep this process alive
88
- const info = await waitForDaemonInfo();
89
- await client.connect(info.port);
90
- console.log(`[SERVER] Started PTY daemon on port ${info.port} (pid ${info.pid})`);
91
- return client;
92
- }
93
38
  export async function createServer() {
94
39
  // Check database integrity before opening
95
40
  if (fsSync.existsSync(dbPath) && !checkDatabaseIntegrity(dbPath)) {
@@ -103,8 +48,6 @@ export async function createServer() {
103
48
  const decks = new Map();
104
49
  const terminals = new Map();
105
50
  loadPersistedState(db, workspaces, workspacePathIndex, decks);
106
- // Start or reconnect to the PTY daemon
107
- const ptyClient = await ensureDaemon();
108
51
  // Create Hono app
109
52
  const app = new Hono();
110
53
  app.use('*', securityHeaders);
@@ -122,17 +65,9 @@ export async function createServer() {
122
65
  app.route('/api/settings', createSettingsRouter());
123
66
  app.route('/api/workspaces', createWorkspaceRouter(db, workspaces, workspacePathIndex));
124
67
  app.route('/api/decks', createDeckRouter(db, workspaces, decks));
125
- const { router: terminalRouter, restoreTerminals } = createTerminalRouter(db, decks, terminals, ptyClient);
68
+ const terminalRouter = createTerminalRouter(db, decks, terminals);
126
69
  app.route('/api/terminals', terminalRouter);
127
70
  app.route('/api/git', createGitRouter(workspaces));
128
- // Restore terminals: daemon is the source of truth for existence, DB provides metadata
129
- const daemonTerminals = await ptyClient.list();
130
- const persistedTerminals = loadPersistedTerminals(db, decks);
131
- if (daemonTerminals.length > 0 || persistedTerminals.length > 0) {
132
- console.log(`[TERMINAL] Restoring ${daemonTerminals.length} live terminal(s) ` +
133
- `(${persistedTerminals.length} DB entries)...`);
134
- await restoreTerminals(persistedTerminals, daemonTerminals);
135
- }
136
71
  app.get('/api/config', getConfigHandler());
137
72
  app.get('/api/ws-token', (c) => c.json({ token: generateWsToken(), authEnabled: isBasicAuthEnabled() }));
138
73
  app.get('/api/ws/stats', (c) => c.json({ limit: getConnectionLimit(), connections: getConnectionStats() }));
@@ -177,29 +112,23 @@ export async function createServer() {
177
112
  console.log(` - CORS Origin: ${CORS_ORIGIN || (NODE_ENV === 'development' ? '*' : 'NOT SET')}`);
178
113
  console.log(` - Environment: ${NODE_ENV}`);
179
114
  });
180
- // Graceful shutdown - optionally terminate daemon.
115
+ // Graceful shutdown
181
116
  let shutdownPromise = null;
182
- let shouldTerminateDaemon = false;
183
- const onShutdown = (options = {}) => {
184
- if (options.terminateDaemon) {
185
- shouldTerminateDaemon = true;
186
- }
187
- if (shutdownPromise) {
117
+ const onShutdown = () => {
118
+ if (shutdownPromise)
188
119
  return shutdownPromise;
189
- }
190
120
  shutdownPromise = (async () => {
191
- if (shouldTerminateDaemon) {
192
- try {
193
- const stopped = await ptyClient.shutdown();
194
- if (!stopped) {
195
- console.warn('[SHUTDOWN] PTY daemon shutdown was not acknowledged');
121
+ // Kill all terminals
122
+ terminals.forEach((session) => {
123
+ session.sockets.forEach((socket) => {
124
+ try {
125
+ socket.close(1000, 'Server shutting down');
196
126
  }
197
- }
198
- catch (err) {
199
- console.warn('[SHUTDOWN] Failed to request PTY daemon shutdown:', err);
200
- }
201
- }
202
- ptyClient.destroy();
127
+ catch { /* ignore */ }
128
+ });
129
+ session.kill();
130
+ });
131
+ terminals.clear();
203
132
  try {
204
133
  db.close();
205
134
  }
@@ -208,32 +137,24 @@ export async function createServer() {
208
137
  return shutdownPromise;
209
138
  };
210
139
  process.on('SIGINT', () => {
211
- console.log('\n[SHUTDOWN] Received SIGINT, saving state...');
212
- void onShutdown({ terminateDaemon: true }).finally(() => process.exit(0));
140
+ console.log('\n[SHUTDOWN] Received SIGINT...');
141
+ void onShutdown().finally(() => process.exit(0));
213
142
  });
214
143
  process.on('SIGTERM', () => {
215
- console.log('[SHUTDOWN] Received SIGTERM, saving state...');
216
- void onShutdown({ terminateDaemon: true }).finally(() => process.exit(0));
144
+ console.log('[SHUTDOWN] Received SIGTERM...');
145
+ void onShutdown().finally(() => process.exit(0));
217
146
  });
218
147
  process.on('SIGHUP', () => {
219
- console.log('[SHUTDOWN] Received SIGHUP, saving state...');
220
- void onShutdown({ terminateDaemon: true }).finally(() => process.exit(0));
148
+ console.log('[SHUTDOWN] Received SIGHUP...');
149
+ void onShutdown().finally(() => process.exit(0));
221
150
  });
222
- // HTTP shutdown endpoint — allows cross-platform graceful shutdown from Electron
151
+ // HTTP shutdown endpoint
223
152
  app.post('/api/shutdown', async (c) => {
224
- let terminateDaemon = false;
225
- try {
226
- const body = await c.req.json();
227
- terminateDaemon = body?.terminateDaemon === true;
228
- }
229
- catch {
230
- // Body is optional; default is false.
231
- }
232
153
  setTimeout(() => {
233
- console.log(`[SHUTDOWN] Shutdown requested via HTTP API${terminateDaemon ? ' (terminate daemon)' : ''}`);
234
- void onShutdown({ terminateDaemon }).finally(() => process.exit(0));
154
+ console.log('[SHUTDOWN] Shutdown requested via HTTP API');
155
+ void onShutdown().finally(() => process.exit(0));
235
156
  }, 50);
236
- return c.json({ ok: true, terminateDaemon });
157
+ return c.json({ ok: true });
237
158
  });
238
159
  const originalExceptionHandler = process.listeners('uncaughtException')[0];
239
160
  process.removeAllListeners('uncaughtException');
@@ -242,8 +163,8 @@ export async function createServer() {
242
163
  console.log('[node-pty] AttachConsole error suppressed');
243
164
  return;
244
165
  }
245
- console.error('[SHUTDOWN] Uncaught exception, saving state before exit...');
246
- void onShutdown({ terminateDaemon: true }).finally(() => {
166
+ console.error('[SHUTDOWN] Uncaught exception...');
167
+ void onShutdown().finally(() => {
247
168
  if (originalExceptionHandler) {
248
169
  originalExceptionHandler(error);
249
170
  }
@@ -102,30 +102,6 @@ export function loadPersistedState(db, workspaces, workspacePathIndex, decks) {
102
102
  decks.set(deck.id, deck);
103
103
  });
104
104
  }
105
- // Terminal persistence functions
106
- export function loadPersistedTerminals(db, decks) {
107
- const rows = db
108
- .prepare('SELECT id, deck_id, title, command, created_at FROM terminals ORDER BY created_at ASC')
109
- .all();
110
- const terminals = [];
111
- rows.forEach((row) => {
112
- const deckId = String(row.deck_id);
113
- // Only load terminals for existing decks
114
- if (!decks.has(deckId)) {
115
- // Clean up orphaned terminal
116
- db.prepare('DELETE FROM terminals WHERE id = ?').run(String(row.id));
117
- return;
118
- }
119
- terminals.push({
120
- id: String(row.id),
121
- deckId,
122
- title: String(row.title),
123
- command: row.command ? String(row.command) : null,
124
- createdAt: String(row.created_at)
125
- });
126
- });
127
- return terminals;
128
- }
129
105
  export function saveTerminal(db, id, deckId, title, command, createdAt) {
130
106
  const stmt = db.prepare('INSERT OR REPLACE INTO terminals (id, deck_id, title, command, created_at) VALUES (?, ?, ?, ?, ?)');
131
107
  stmt.run(id, deckId, title, command, createdAt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deckide",
3
- "version": "3.3.1",
3
+ "version": "3.5.0",
4
4
  "description": "Deck IDE - Browser-based IDE with terminal, file explorer, and git integration",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,32 @@
1
+ @import"https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Noto+Sans+JP:wght@400;500;600&display=swap";/**
2
+ * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
+ * https://github.com/chjj/term.js
5
+ * @license MIT
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ *
25
+ * Originally forked from (with the author's permission):
26
+ * Fabrice Bellard's javascript vt100 for jslinux:
27
+ * http://bellard.org/jslinux/
28
+ * Copyright (c) 2011 Fabrice Bellard
29
+ * The original design remains. The terminal itself
30
+ * has been extended to include xterm CSI codes, among
31
+ * other features.
32
+ */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility,.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.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}/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:"JetBrains Mono", monospace;--color-red-400:oklch(70.4% .191 22.216);--color-black:#000;--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg:#f3f3f3;--color-bg-soft:#e8e8e8;--color-panel:#fff;--color-sidebar:#f3f3f3;--color-activitybar:#2c2c2c;--color-activitybar-fg:#fff;--color-activitybar-inactive:#fff9;--color-border:#e5e5e5;--color-ink:#333;--color-ink-muted:#6e6e6e;--color-muted:#717171;--color-accent:#007acc;--color-statusbar:#007acc;--color-statusbar-fg:#fff;--color-list-hover:#0000000a;--color-list-active:#0078d41a;--color-focus:#0090f1;--color-title-bar:#ddd;--color-git-modified:#cca700;--color-git-untracked:#4ec9b0;--color-git-deleted:#f14c4c;--color-git-staged:#73c991;--color-git-renamed:#4fc1ff;--color-git-conflicted:#ff6b6b;--font-ui:"Noto Sans JP", sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*,:before,:after{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{font-family:var(--font-ui);background:var(--color-bg);color:var(--color-ink);font-size:13px;line-height:1.4;overflow:hidden}button,input,select,textarea{color:inherit;font-family:inherit}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--color-focus);outline-offset:2px}@supports (height:100dvh){html,body,#root{height:100dvh}}*{scrollbar-width:thin;scrollbar-color:#8080804d transparent}:root[data-theme=dark] *{scrollbar-color:#ffffff26 transparent}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:#8080804d;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#80808080}:root[data-theme=dark] ::-webkit-scrollbar-thumb{background:#ffffff26}:root[data-theme=dark] ::-webkit-scrollbar-thumb:hover{background:#ffffff4d}::-webkit-scrollbar-corner{background:0 0}}@layer components{.activity-bar-item.active:before{content:"";background:var(--color-activitybar-fg);width:2px;position:absolute;top:0;bottom:0;left:0}.workspace-editor-grid .activity-bar-item.active:before{background:var(--color-accent)}.app>nav.activity-bar .activity-bar-item.active:before{width:auto;height:2px;inset:0 0 auto}.git-tab.active:after{content:"";background:var(--color-accent);height:2px;position:absolute;bottom:0;left:0;right:0}.editor-tab:hover .editor-tab-close,.editor-tab.active .editor-tab-close{opacity:1}.editor-tab.dirty .editor-tab-close{opacity:0}.editor-tab.dirty:hover .editor-tab-close{opacity:1}.editor-tab.dirty:hover .editor-tab-dirty{display:none}.deck-tab:hover .deck-tab-close,.deck-tab.active .deck-tab-close{opacity:.5}.deck-tab-close:hover{color:var(--color-ink);background:#ffffff1f;opacity:1!important}.git-file-row:hover .git-file-actions{opacity:1}.tree-row.is-open .tree-chevron-icon{transform:rotate(90deg)}.terminal-split-container.is-drop-target:after{content:"デッキをここにドロップして分割";border:2px dashed var(--color-accent);color:var(--color-ink-muted);pointer-events:none;z-index:2;background:#0078d414;border-radius:6px;justify-content:center;align-items:center;font-size:12px;font-weight:600;display:flex;position:absolute;top:10px;right:10px;bottom:10px;left:10px}.git-tree-modified .tree-label{color:var(--color-git-modified)}.git-tree-untracked .tree-label{color:var(--color-git-untracked)}.git-tree-deleted .tree-label{color:var(--color-git-deleted)}.git-tree-staged .tree-label{color:var(--color-git-staged)}.git-tree-renamed .tree-label{color:var(--color-git-renamed)}.git-tree-conflicted .tree-label{color:var(--color-git-conflicted)}.sidebar-content .panel{box-shadow:none;background:0 0;border:none;border-radius:0}.sidebar-content .panel-header{background:var(--color-sidebar);z-index:1;margin-bottom:0;padding:8px 12px;position:sticky;top:0}.sidebar-content .panel-body{padding:0}}@layer utilities{.fixed{position:fixed}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.z-50{z-index:50}.z-\[500\]{z-index:500}.z-\[999\]{z-index:999}.z-\[1000\]{z-index:1000}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing) * 0)}.my-1{margin-block:calc(var(--spacing) * 1)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-7{height:calc(var(--spacing) * 7)}.h-\[22px\]{height:22px}.h-\[280px\]{height:280px}.h-full{height:100%}.h-px{height:1px}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-\[28px\]{min-height:28px}.min-h-\[40px\]{min-height:40px}.min-h-\[44px\]{min-height:44px}.min-h-\[50px\]{min-height:50px}.min-h-screen{min-height:100vh}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-7{width:calc(var(--spacing) * 7)}.w-\[22px\]{width:22px}.w-\[500px\]{width:500px}.w-full{width:100%}.max-w-\[500px\]{max-width:500px}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[44px\]{min-width:44px}.min-w-\[140px\]{min-width:140px}.min-w-\[160px\]{min-width:160px}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.grid-rows-\[35px_minmax\(0\,1fr\)\]{grid-template-rows:35px minmax(0,1fr)}.flex-col{flex-direction:column}.place-items-center{place-items:center}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[3px\]{border-radius:3px}.rounded-\[6px\]{border-radius:6px}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-accent{border-color:var(--color-accent)}.border-border{border-color:var(--color-border)}.bg-\[\#f14c4c\]{background-color:#f14c4c}.bg-accent{background-color:var(--color-accent)}.bg-accent\/20{background-color:#007acc33}@supports (color:color-mix(in lab,red,red)){.bg-accent\/20{background-color:color-mix(in oklab,var(--color-accent) 20%,transparent)}}.bg-bg{background-color:var(--color-bg)}.bg-bg-soft{background-color:var(--color-bg-soft)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-border{background-color:var(--color-border)}.bg-list-active{background-color:var(--color-list-active)}.bg-panel{background-color:var(--color-panel)}.bg-sidebar{background-color:var(--color-sidebar)}.bg-title-bar{background-color:var(--color-title-bar)}.bg-transparent{background-color:#0000}.fill-current{fill:currentColor}.p-0{padding:calc(var(--spacing) * 0)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.font-\[inherit\]{font-family:inherit}.font-mono{font-family:var(--font-mono)}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.75rem\]{font-size:.75rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.leading-none{--tw-leading:1;line-height:1}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.5px\]{--tw-tracking:.5px;letter-spacing:.5px}.break-words{overflow-wrap:break-word}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#f14c4c\]{color:#f14c4c}.text-accent{color:var(--color-accent)}.text-ink{color:var(--color-ink)}.text-ink-muted{color:var(--color-ink-muted)}.text-muted{color:var(--color-muted)}.text-red-400{color:var(--color-red-400)}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.shadow-\[0_4px_12px_rgba\(0\,0\,0\,0\.15\)\]{--tw-shadow:0 4px 12px var(--tw-shadow-color,#00000026);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:bg-\[rgba\(0\,0\,0\,0\.08\)\]:hover{background-color:#00000014}.hover\:bg-\[rgba\(241\,76\,76\,0\.1\)\]:hover{background-color:#f14c4c1a}.hover\:bg-list-hover:hover{background-color:var(--color-list-hover)}.hover\:bg-red-400\/10:hover{background-color:#ff65681a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-400\/10:hover{background-color:color-mix(in oklab,var(--color-red-400) 10%,transparent)}}.hover\:text-ink:hover{color:var(--color-ink)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:opacity-90:hover{opacity:.9}}.focus\:border-focus:focus{border-color:var(--color-focus)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-default:disabled{cursor:default}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media (hover:hover){.dark\:hover\:bg-\[rgba\(255\,255\,255\,0\.08\)\]:where([data-theme=dark],[data-theme=dark] *):hover{background-color:#ffffff14}}.scrollbar-none{scrollbar-width:none}.scrollbar-none::-webkit-scrollbar{display:none}.spin{animation:1s linear infinite spin}.slide-in{animation:.2s slideIn}}:root[data-theme=dark]{--color-bg:#1e1e1e;--color-bg-soft:#252526;--color-panel:#1e1e1e;--color-sidebar:#252526;--color-activitybar:#333;--color-activitybar-inactive:#fff6;--color-border:#3c3c3c;--color-ink:#ccc;--color-ink-muted:#9e9e9e;--color-muted:#8b8b8b;--color-accent:#0078d4;--color-list-hover:#ffffff0a;--color-list-active:#0078d433;--color-focus:#007fd4;--color-title-bar:#3c3c3c}@keyframes spin{to{transform:rotate(360deg)}}@keyframes slideIn{0%{transform:translate(-100%)}to{transform:translate(0)}}.app{grid-template-rows:minmax(0,1fr) 22px;grid-template-columns:48px minmax(0,1fr);height:100vh;display:grid;overflow:hidden}.activity-bar{background:var(--color-activitybar);flex-direction:column;grid-row:1/3;width:48px;display:flex}.activity-bar-top{flex-direction:column;display:flex}.activity-bar-bottom{flex-direction:column;margin-top:auto;display:flex}.activity-bar-item{width:48px;height:48px;color:var(--color-activitybar-inactive);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;transition:color .15s;display:flex;position:relative}.activity-bar-item:hover,.activity-bar-item.active{color:var(--color-activitybar-fg)}.activity-bar-item svg{width:24px;height:24px}.activity-bar-badge{text-align:center;background:var(--color-accent);color:#fff;border-radius:8px;min-width:16px;height:16px;padding:0 4px;font-size:10px;font-weight:600;line-height:16px;position:absolute;top:8px;right:6px}.main{background:var(--color-bg);flex-direction:column;min-height:0;display:flex}.workspace-editor-overlay{background:var(--color-bg);z-index:30;grid-template-rows:35px minmax(0,1fr);display:grid;position:fixed;top:0;right:0;bottom:0;left:0}.workspace-editor-header{background:var(--color-title-bar);border-bottom:1px solid var(--color-border);align-items:center;gap:12px;padding:0 12px;display:flex}.workspace-meta{color:var(--color-muted);font-size:12px;font-family:var(--font-mono);text-overflow:ellipsis;white-space:nowrap;text-align:center;flex:1;overflow:hidden}.workspace-editor-grid{grid-template-rows:minmax(0,1fr);grid-template-columns:48px 220px minmax(0,1fr);height:100%;min-height:0;display:grid}.sidebar-panel{background:var(--color-sidebar);border-right:1px solid var(--color-border);flex-direction:column;height:100%;min-height:0;display:flex}.sidebar-content{flex:1;min-height:0;overflow:auto}.workspace-editor-grid>.activity-bar{background:var(--color-activitybar);border-right:1px solid var(--color-border);grid-row:unset;flex-direction:column;width:48px;display:flex}.workspace-editor-grid .activity-bar-item{color:var(--color-activitybar-fg);opacity:.6;transition:opacity .15s}.workspace-editor-grid .activity-bar-item:hover,.workspace-editor-grid .activity-bar-item.active{opacity:1}.panel{background:var(--color-panel);border:1px solid var(--color-border);border-radius:0;flex-direction:column;min-height:0;padding:0;display:flex}.panel-header{border-bottom:1px solid var(--color-border);background:var(--color-sidebar);justify-content:space-between;align-items:center;gap:8px;padding:8px 12px;display:flex}.panel-title{text-transform:uppercase;letter-spacing:.5px;color:var(--color-ink-muted);font-size:11px;font-weight:600}.panel-subtitle{color:var(--color-muted);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.panel-body{flex:1;min-height:0;overflow:auto}.modal{background:var(--color-panel);border:1px solid var(--color-border);border-radius:4px;width:480px;max-width:96vw;max-height:90dvh;padding:16px;overflow-y:auto;box-shadow:0 8px 32px #0000004d}.modal-close-btn{color:var(--color-muted);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;width:32px;height:32px;padding:0;font-size:24px;line-height:1;display:flex}.modal-close-btn:hover{color:var(--color-ink);background:var(--color-list-hover)}.tree-body{padding:4px 0}.tree-row{color:var(--color-ink);text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:4px;width:100%;min-height:22px;padding:2px 8px 2px 4px;font-size:13px;transition:background .1s;display:flex}.tree-row:hover{background:var(--color-list-hover)}.tree-row:focus-visible{outline:2px solid var(--color-focus);outline-offset:-2px}.tree-chevron{width:16px;height:16px;color:var(--color-ink-muted);flex-shrink:0;justify-content:center;align-items:center;display:flex}.tree-chevron-icon{width:12px;height:12px;transition:transform .1s}.tree-icon{flex-shrink:0;justify-content:center;align-items:center;width:16px;height:16px;display:flex}.tree-svg{width:16px;height:16px}.tree-label{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.tree-meta{color:var(--color-muted);font-size:11px;font-family:var(--font-mono);margin-left:auto}.tree-state{color:var(--color-muted);padding:8px 12px;font-size:12px}.tree-actions{align-items:center;gap:4px;display:flex}.tree-input-row{align-items:center;gap:4px;min-height:22px;padding:2px 8px 2px 4px;display:flex}.tree-input{border:1px solid var(--color-accent);background:var(--color-panel);min-width:0;color:var(--color-ink);border-radius:2px;outline:none;flex:1;padding:2px 6px;font-size:13px}.tree-input::placeholder{color:var(--color-muted)}.editor-container{background:var(--color-panel);flex-direction:column;height:100%;display:flex;overflow:hidden}.editor-container.editor-empty{justify-content:center;align-items:center;display:flex}.editor-welcome{color:var(--color-ink-muted);text-align:center;flex-direction:column;align-items:center;gap:16px;padding:60px 40px;display:flex}.editor-welcome-icon{opacity:.2;width:80px;height:80px}.editor-welcome-icon svg{width:100%;height:100%;stroke:var(--color-ink)}.editor-welcome-text{color:var(--color-ink-muted);font-size:18px;font-weight:400}.editor-welcome-hint{opacity:.6;font-size:13px}.editor-tabs{background:var(--color-bg-soft);border-bottom:1px solid var(--color-border);flex-shrink:0;align-items:stretch;min-height:35px;display:flex}.editor-tabs-list{scrollbar-width:none;display:flex;overflow-x:auto}.editor-tabs-list::-webkit-scrollbar{display:none}.editor-tab{background:var(--color-bg-soft);border:none;border-right:1px solid var(--color-border);min-width:100px;max-width:180px;height:35px;color:var(--color-ink-muted);cursor:pointer;align-items:center;gap:6px;padding:0 10px;font-size:13px;transition:background .1s,color .1s;display:flex;position:relative}.editor-tab:hover{background:var(--color-panel);color:var(--color-ink)}.editor-tab.active{background:var(--color-panel);color:var(--color-ink);border-bottom:1px solid var(--color-panel);margin-bottom:-1px}.editor-tab-icon{font-size:10px;font-weight:600;font-family:var(--font-mono);flex-shrink:0}.editor-tab-name{text-overflow:ellipsis;white-space:nowrap;text-align:left;flex:1;overflow:hidden}.editor-tab-dirty{color:var(--color-ink);flex-shrink:0;font-size:18px;line-height:1}.editor-tab-saving{flex-shrink:0;width:14px;height:14px}.editor-tab-saving svg{width:100%;height:100%;stroke:var(--color-ink-muted)}.editor-tab-close{width:20px;height:20px;color:var(--color-ink-muted);cursor:pointer;opacity:0;background:0 0;border:none;border-radius:3px;flex-shrink:0;justify-content:center;align-items:center;padding:0;transition:opacity .1s,background .1s;display:flex}.editor-tab-close svg{width:16px;height:16px}.editor-tab-close:hover{background:var(--color-list-hover);color:var(--color-ink)}.editor-breadcrumb{background:var(--color-panel);border-bottom:1px solid var(--color-border);color:var(--color-ink-muted);flex-shrink:0;align-items:center;height:22px;padding:0 12px;font-size:12px;display:flex}.editor-breadcrumb-path{text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono);overflow:hidden}.editor-content{flex:1;min-height:0;position:relative}.editor-no-file{height:100%;color:var(--color-ink-muted);justify-content:center;align-items:center;display:flex}.editor-statusbar{background:var(--color-statusbar);height:22px;color:var(--color-statusbar-fg);flex-shrink:0;justify-content:space-between;align-items:center;padding:0 12px;font-size:12px;display:flex}.editor-statusbar-left,.editor-statusbar-right{align-items:center;gap:14px;display:flex}.editor-status-item{cursor:default;align-items:center;gap:4px;display:flex}.editor-status-item svg{width:14px;height:14px}.terminal-layout{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex;overflow:hidden}.terminal-topbar{background:var(--color-sidebar);border-bottom:1px solid var(--color-border);flex-shrink:0;align-items:stretch;min-width:0;display:flex;overflow:hidden}.topbar-left{flex:1;align-items:stretch;min-width:0;display:flex;overflow:hidden}.deck-tabs{scrollbar-width:none;flex:1;align-items:stretch;min-width:0;display:flex;overflow-x:auto}.deck-tabs::-webkit-scrollbar{display:none}.deck-tab{border:none;border-top:2px solid #0000;border-right:1px solid var(--color-border);min-width:80px;max-width:200px;color:var(--color-muted);cursor:pointer;white-space:nowrap;background:0 0;flex-shrink:0;align-items:center;gap:4px;padding:0 8px 0 12px;font-size:12px;font-weight:500;transition:background .12s,color .12s;display:flex}.deck-tab[draggable=true]{cursor:grab}.deck-tab.is-dragging{opacity:.5}.deck-tab.is-drag-over{background:var(--color-list-active);color:var(--color-ink)}.deck-tab:hover{background:var(--color-list-hover);color:var(--color-ink)}.deck-tab.active{background:var(--color-panel);color:var(--color-ink);border-top-color:var(--color-accent)}.deck-tab-name{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.deck-tab-close{opacity:0;width:16px;height:16px;color:var(--color-muted);border-radius:3px;flex-shrink:0;justify-content:center;align-items:center;font-size:13px;line-height:1;transition:opacity .12s,background .12s;display:flex}.deck-tab-add{border-top:none;justify-content:center;min-width:35px;max-width:35px;padding:0;font-size:16px;font-weight:300}.deck-tab-add:hover{background:var(--color-list-hover);color:var(--color-ink)}.terminal-split-container{flex:1;gap:4px;width:100%;min-width:0;max-width:100%;min-height:0;padding:4px;display:grid;position:relative}.deck-split-pane{border:1px solid var(--color-border);border-radius:4px;flex-direction:column;min-width:0;min-height:0;display:flex;overflow:hidden}.topbar-btn-sm{border:1px solid var(--color-border);color:var(--color-muted);cursor:pointer;background:0 0;border-radius:3px;padding:2px 6px;font-size:11px;font-weight:600;transition:all .15s}.topbar-btn-sm:hover{background:var(--color-list-hover);color:var(--color-ink);border-color:var(--color-accent)}.topbar-btn-sm.topbar-btn-claude{color:#d97757}.topbar-btn-sm.topbar-btn-claude:hover{background:#d9775726;border-color:#d97757}.topbar-btn-sm.topbar-btn-codex{color:#00a86b}.topbar-btn-sm.topbar-btn-codex:hover{background:#00a86b26;border-color:#00a86b}.terminal-pane{flex-direction:column;flex:1;min-width:0;min-height:0;padding:4px;display:flex}.terminal-grid{flex:1;gap:4px;width:100%;min-width:0;min-height:0;display:grid;overflow:hidden}.terminal-empty{background:var(--color-bg-soft);border:1px dashed var(--color-border);color:var(--color-muted);border-radius:4px;flex:1;justify-content:center;align-items:center;font-size:12px;display:flex}.terminal-tile{border:1px solid var(--color-border);color:#d4d4d4;background:#1e1e1e;border-radius:4px;flex-direction:column;min-width:0;min-height:0;display:flex;overflow:hidden}.terminal-tile-header{background:#2d2d2d;border-bottom:1px solid #404040;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;font-size:12px;display:flex}.terminal-tile-header span{text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono);flex:1;overflow:hidden}.terminal-close-btn{color:#858585;cursor:pointer;background:0 0;border:none;border-radius:3px;flex-shrink:0;justify-content:center;align-items:center;width:20px;height:20px;transition:background .1s,color .1s;display:flex}.terminal-close-btn:hover{color:#fff;background:#404040}.terminal-tile-body{flex:1;min-height:0}.git-modified{color:var(--color-git-modified)}.git-untracked{color:var(--color-git-untracked)}.git-deleted{color:var(--color-git-deleted)}.git-staged{color:var(--color-git-staged)}.git-renamed{color:var(--color-git-renamed)}.git-conflicted{color:var(--color-git-conflicted)}.git-tabs{border-bottom:1px solid var(--color-border);background:var(--color-sidebar);flex-shrink:0;display:flex}.git-tab{color:var(--color-ink-muted);cursor:pointer;background:0 0;border:none;align-items:center;gap:6px;padding:6px 12px;font-size:12px;transition:color .1s,background .1s;display:flex;position:relative}.git-tab:hover{color:var(--color-ink);background:var(--color-list-hover)}.git-tab.active{color:var(--color-ink)}.git-file-row{align-items:center;gap:4px;transition:background .1s;display:flex}.git-file-row:hover{background:var(--color-list-hover)}.git-file-main{color:var(--color-ink);cursor:pointer;text-align:left;background:0 0;border:none;flex:1;align-items:center;gap:6px;min-width:0;min-height:22px;padding:4px 12px;display:flex}.git-status-badge{width:16px;height:16px;font-size:11px;font-weight:600;font-family:var(--font-mono);flex-shrink:0;justify-content:center;align-items:center;display:flex}.git-file-path{text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.git-file-actions{opacity:0;gap:2px;padding-right:8px;transition:opacity .1s;display:flex}.git-action-btn{width:22px;height:22px;color:var(--color-ink-muted);cursor:pointer;background:0 0;border:none;border-radius:3px;justify-content:center;align-items:center;padding:0;transition:background .1s,color .1s;display:flex}.git-action-btn:hover{background:var(--color-list-hover);color:var(--color-ink)}.git-action-btn svg{fill:currentColor;width:14px;height:14px}.status-float{right:20px;bottom:calc(40px + env(safe-area-inset-bottom,0px));background:var(--color-panel);border:1px solid var(--color-border);color:var(--color-ink);border-radius:4px;max-width:min(400px,100vw - 40px);padding:8px 14px;font-size:12px;position:fixed;box-shadow:0 4px 16px #0003}.sidebar-toggle-btn,.sidebar-overlay{display:none}@media (max-width:1024px){.workspace-editor-grid{grid-template-rows:200px minmax(0,1fr);grid-template-columns:48px 1fr}.workspace-editor-grid>.activity-bar{grid-row:1/-1}.sidebar-panel{border-right:none;border-bottom:1px solid var(--color-border)}}@media (max-width:720px){.app{padding-bottom:env(safe-area-inset-bottom);grid-template-rows:minmax(0,1fr) 48px 22px;grid-template-columns:1fr}.app>nav.activity-bar{width:auto;height:48px;padding:0 max(8px,env(safe-area-inset-right,0px)) 0 max(8px,env(safe-area-inset-left,0px));border-top:1px solid var(--color-border);flex-direction:row;grid-row:2;justify-content:space-between;gap:8px}.activity-bar-top,.activity-bar-bottom{flex-direction:row;align-items:center;gap:4px}.activity-bar-bottom{margin-top:0;margin-left:0}.main{grid-row:1;min-width:0;overflow:hidden}.sidebar-toggle-btn{justify-content:center;align-items:center;display:flex}.activity-bar-item{min-width:44px;min-height:44px}.deck-tab{min-height:40px;padding:0 8px 0 14px;font-size:13px}.topbar-btn-sm{min-width:44px;min-height:40px;padding:0 10px}.tree-row,.editor-tab{min-height:36px}.modal-close-btn{min-width:44px;min-height:44px}.modal{border-radius:8px;max-width:calc(100vw - 16px);max-height:90dvh;margin:8px}.deck-tabs{-webkit-overflow-scrolling:touch;scrollbar-width:none;flex-wrap:nowrap;overflow-x:auto}.deck-tabs::-webkit-scrollbar{display:none}.terminal-split-container{scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;touch-action:pan-x;scrollbar-width:none;width:100%;max-width:100%;overflow:scroll hidden;flex-direction:row!important;gap:0!important;padding:0!important;display:flex!important}.terminal-split-container::-webkit-scrollbar{display:none}.deck-split-pane{scroll-snap-align:start;border-left:none;border-right:none;border-radius:0;flex:0 0 100%;width:100%;max-width:100%;overflow:hidden}.terminal-pane{scroll-snap-type:y mandatory;-webkit-overflow-scrolling:touch;overscroll-behavior-y:contain;touch-action:pan-y;scrollbar-width:none;width:100%;max-width:100%;height:100%;overflow:hidden scroll;padding:0!important}.terminal-pane::-webkit-scrollbar{display:none}.terminal-grid{width:100%;height:100%;grid-template-columns:unset!important;grid-template-rows:unset!important;flex-direction:column!important;flex:none!important;gap:0!important;display:flex!important;overflow:visible!important}.terminal-tile{scroll-snap-align:start;border-left:none;border-right:none;border-radius:0;flex:none;width:100%;max-width:100%;height:100%}.status-float{right:12px;bottom:calc(78px + env(safe-area-inset-bottom,0px));max-width:calc(100vw - 24px)}.workspace-editor-grid{grid-template-rows:minmax(0,1fr);grid-template-columns:1fr;position:relative}.workspace-editor-grid>.activity-bar{z-index:101;border-right:1px solid var(--color-border);border-top:none;flex-direction:column;width:48px;height:auto;transition:transform .2s;position:absolute;top:0;bottom:0;left:0;transform:translate(-100%)}.sidebar-panel{z-index:100;border-bottom:none;border-right:1px solid var(--color-border);width:260px;transition:transform .2s;position:absolute;top:0;bottom:0;left:48px;overflow-y:auto;transform:translate(calc(-100% - 48px))}.drawer-open .workspace-editor-grid>.activity-bar,.drawer-open .sidebar-panel{transform:translate(0)}.sidebar-overlay{z-index:99;background:#0006;display:none;position:absolute;top:0;right:0;bottom:0;left:0}.drawer-open .sidebar-overlay{display:block}.workspace-editor-header{gap:8px;padding:0 8px}.workspace-editor-header .sidebar-toggle-btn{order:3;margin-left:auto}.workspace-editor-header .workspace-meta{text-align:left;min-width:0}}@media (pointer:coarse){.modal-close-btn:hover,.tree-row:hover,.editor-tab:hover,.deck-tab:hover,.deck-tab-add:hover,.topbar-btn-sm:hover,.terminal-close-btn:hover,.git-file-row:hover,.git-action-btn:hover,.git-tab:hover{border-color:inherit;color:inherit;opacity:1;background:0 0}.editor-tab:hover{color:var(--color-ink-muted);background:0 0}.git-file-actions{opacity:1}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}