@soyrageagency/docker-mcp 1.1.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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +882 -0
  3. package/dist/ai/credentials.d.ts +72 -0
  4. package/dist/ai/credentials.js +157 -0
  5. package/dist/ai/credentials.js.map +1 -0
  6. package/dist/ai/login.d.ts +22 -0
  7. package/dist/ai/login.js +189 -0
  8. package/dist/ai/login.js.map +1 -0
  9. package/dist/ai/provider.d.ts +29 -0
  10. package/dist/ai/provider.js +113 -0
  11. package/dist/ai/provider.js.map +1 -0
  12. package/dist/assets.d.ts +20 -0
  13. package/dist/assets.js +56 -0
  14. package/dist/assets.js.map +1 -0
  15. package/dist/branding.d.ts +31 -0
  16. package/dist/branding.js +61 -0
  17. package/dist/branding.js.map +1 -0
  18. package/dist/config.d.ts +65 -0
  19. package/dist/config.js +128 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/docker/client.d.ts +66 -0
  22. package/dist/docker/client.js +161 -0
  23. package/dist/docker/client.js.map +1 -0
  24. package/dist/docker/compose.d.ts +74 -0
  25. package/dist/docker/compose.js +126 -0
  26. package/dist/docker/compose.js.map +1 -0
  27. package/dist/entry.d.ts +15 -0
  28. package/dist/entry.js +33 -0
  29. package/dist/entry.js.map +1 -0
  30. package/dist/index.d.ts +14 -0
  31. package/dist/index.js +100 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/logger.d.ts +25 -0
  34. package/dist/logger.js +68 -0
  35. package/dist/logger.js.map +1 -0
  36. package/dist/panel/index.d.ts +16 -0
  37. package/dist/panel/index.js +53 -0
  38. package/dist/panel/index.js.map +1 -0
  39. package/dist/panel/public/app.js +1004 -0
  40. package/dist/panel/public/index.html +243 -0
  41. package/dist/panel/public/styles.css +428 -0
  42. package/dist/panel/server.d.ts +23 -0
  43. package/dist/panel/server.js +355 -0
  44. package/dist/panel/server.js.map +1 -0
  45. package/dist/panel/service.d.ts +327 -0
  46. package/dist/panel/service.js +1356 -0
  47. package/dist/panel/service.js.map +1 -0
  48. package/dist/plugins.d.ts +50 -0
  49. package/dist/plugins.js +121 -0
  50. package/dist/plugins.js.map +1 -0
  51. package/dist/ragedocker.d.ts +21 -0
  52. package/dist/ragedocker.js +164 -0
  53. package/dist/ragedocker.js.map +1 -0
  54. package/dist/tools/about.d.ts +15 -0
  55. package/dist/tools/about.js +55 -0
  56. package/dist/tools/about.js.map +1 -0
  57. package/dist/tools/compose.d.ts +19 -0
  58. package/dist/tools/compose.js +145 -0
  59. package/dist/tools/compose.js.map +1 -0
  60. package/dist/tools/containers.d.ts +13 -0
  61. package/dist/tools/containers.js +119 -0
  62. package/dist/tools/containers.js.map +1 -0
  63. package/dist/tools/context.d.ts +37 -0
  64. package/dist/tools/context.js +13 -0
  65. package/dist/tools/context.js.map +1 -0
  66. package/dist/tools/images.d.ts +11 -0
  67. package/dist/tools/images.js +38 -0
  68. package/dist/tools/images.js.map +1 -0
  69. package/dist/tools/index.d.ts +15 -0
  70. package/dist/tools/index.js +34 -0
  71. package/dist/tools/index.js.map +1 -0
  72. package/dist/tools/lifecycle.d.ts +16 -0
  73. package/dist/tools/lifecycle.js +165 -0
  74. package/dist/tools/lifecycle.js.map +1 -0
  75. package/dist/tools/logs.d.ts +15 -0
  76. package/dist/tools/logs.js +110 -0
  77. package/dist/tools/logs.js.map +1 -0
  78. package/dist/tools/system.d.ts +14 -0
  79. package/dist/tools/system.js +108 -0
  80. package/dist/tools/system.js.map +1 -0
  81. package/dist/tui/ansi.d.ts +66 -0
  82. package/dist/tui/ansi.js +118 -0
  83. package/dist/tui/ansi.js.map +1 -0
  84. package/dist/tui/app.d.ts +160 -0
  85. package/dist/tui/app.js +1339 -0
  86. package/dist/tui/app.js.map +1 -0
  87. package/dist/tui/box.d.ts +18 -0
  88. package/dist/tui/box.js +38 -0
  89. package/dist/tui/box.js.map +1 -0
  90. package/dist/tui/index.d.ts +19 -0
  91. package/dist/tui/index.js +66 -0
  92. package/dist/tui/index.js.map +1 -0
  93. package/dist/update/channel.d.ts +82 -0
  94. package/dist/update/channel.js +178 -0
  95. package/dist/update/channel.js.map +1 -0
  96. package/dist/updates.json +42 -0
  97. package/dist/utils/format.d.ts +24 -0
  98. package/dist/utils/format.js +69 -0
  99. package/dist/utils/format.js.map +1 -0
  100. package/dist/utils/result.d.ts +35 -0
  101. package/dist/utils/result.js +31 -0
  102. package/dist/utils/result.js.map +1 -0
  103. package/package.json +90 -0
  104. package/scripts/install.mjs +132 -0
@@ -0,0 +1,243 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Docker Panel · SoyRage Agency</title>
7
+ <meta name="description" content="Minimalist interactive Docker panel by SoyRage Agency — soyrage.es" />
8
+ <!--
9
+ Docker MCP Server · Interactive Panel
10
+ Crafted by SoyRage Agency — https://soyrage.es/
11
+ Attribution must remain intact (see LICENSE).
12
+ -->
13
+ <link rel="stylesheet" href="styles.css" />
14
+ </head>
15
+ <body>
16
+ <!-- Update / announcement banner (populated by app.js from /api/updates) -->
17
+ <div id="update-banner" class="update-banner hidden" role="status" aria-live="polite"></div>
18
+
19
+ <header class="topbar">
20
+ <div class="brand">
21
+ <span class="logo">SOY<span class="accent">R</span>AGE</span>
22
+ <span class="divider">/</span>
23
+ <span class="product">Docker&nbsp;Panel</span>
24
+ </div>
25
+
26
+ <nav class="tabs" id="tabs">
27
+ <button class="tab active" data-tab="overview">Overview</button>
28
+ <button class="tab" data-tab="terminal">Terminal</button>
29
+ <button class="tab" data-tab="files">Files</button>
30
+ <button class="tab" data-tab="compose">Compose</button>
31
+ <button class="tab" data-tab="backups">Backups</button>
32
+ <button class="tab" data-tab="system">System</button>
33
+ <button class="tab" data-tab="alerts">Alerts <span id="alert-count" class="pill hidden">0</span></button>
34
+ </nav>
35
+
36
+ <div class="topbar-right">
37
+ <span id="badge-demo" class="badge badge-demo hidden">DEMO</span>
38
+ <span id="badge-ro" class="badge badge-ro hidden">READ-ONLY</span>
39
+ <span id="engine" class="engine">—</span>
40
+ <a class="site" href="https://soyrage.es/" target="_blank" rel="noreferrer">soyrage.es ↗</a>
41
+ </div>
42
+ </header>
43
+
44
+ <main class="wrap">
45
+ <!-- ============ OVERVIEW ============ -->
46
+ <section id="tab-overview" class="tabpane">
47
+ <div class="stats" id="stats" aria-label="Host statistics"></div>
48
+
49
+ <section class="panel">
50
+ <div class="panel-head"><h2>Trends</h2><span class="muted">last ~2 min · sampled every 5s</span></div>
51
+ <div class="trend-grid">
52
+ <div class="trend">
53
+ <div class="trend-h"><span class="k">CPU load</span><span id="cpu-now" class="v mono">—</span></div>
54
+ <div id="chart-cpu" class="chart"></div>
55
+ </div>
56
+ <div class="trend">
57
+ <div class="trend-h"><span class="k">Memory used</span><span id="mem-now" class="v mono">—</span></div>
58
+ <div id="chart-mem" class="chart"></div>
59
+ </div>
60
+ </div>
61
+ </section>
62
+
63
+ <section class="panel">
64
+ <div class="panel-head">
65
+ <h2>Containers</h2>
66
+ <div class="tools">
67
+ <input id="search" class="input" type="search" placeholder="Filter containers…" />
68
+ <label class="chk"><input type="checkbox" id="auto" checked /> Auto</label>
69
+ <button id="refresh" class="ghost" title="Refresh">Refresh</button>
70
+ </div>
71
+ </div>
72
+ <div class="table-wrap">
73
+ <table class="grid">
74
+ <thead>
75
+ <tr>
76
+ <th>State</th><th>Name</th><th>Image</th><th>CPU</th><th>Memory</th>
77
+ <th>Ports</th><th class="center">Auto</th><th class="right">Actions</th>
78
+ </tr>
79
+ </thead>
80
+ <tbody id="containers"></tbody>
81
+ </table>
82
+ </div>
83
+ </section>
84
+ <section class="panel">
85
+ <div class="panel-head"><h2>Images</h2></div>
86
+ <div class="table-wrap">
87
+ <table class="grid">
88
+ <thead><tr><th>Repository:Tag</th><th>Image ID</th><th class="right">Size</th></tr></thead>
89
+ <tbody id="images"></tbody>
90
+ </table>
91
+ </div>
92
+ </section>
93
+ </section>
94
+
95
+ <!-- ============ TERMINAL ============ -->
96
+ <section id="tab-terminal" class="tabpane hidden">
97
+ <section class="panel term-panel">
98
+ <div class="panel-head">
99
+ <h2>Terminal</h2>
100
+ <span class="muted">docker · smart suggestions — <b>Tab</b> to complete, <b>↑/↓</b> to pick, <b>Enter</b> to run</span>
101
+ </div>
102
+ <div class="term-out" id="term-out"></div>
103
+ <div class="term-input-wrap">
104
+ <span class="prompt">❯</span>
105
+ <input id="term-input" class="term-input" autocomplete="off" spellcheck="false"
106
+ placeholder="e.g. docker ps -a · docker logs web · docker compose up -d" />
107
+ <div id="term-sugg" class="suggestions hidden"></div>
108
+ </div>
109
+ </section>
110
+ </section>
111
+
112
+ <!-- ============ FILES ============ -->
113
+ <section id="tab-files" class="tabpane hidden">
114
+ <section class="panel">
115
+ <div class="panel-head">
116
+ <h2>File explorer</h2>
117
+ <div class="tools">
118
+ <select id="fs-container" class="input"></select>
119
+ <button id="fs-up" class="ghost" title="Up one level">Up</button>
120
+ </div>
121
+ </div>
122
+ <div class="crumbs" id="fs-crumbs"></div>
123
+ <div class="table-wrap">
124
+ <table class="grid">
125
+ <thead><tr><th>Type</th><th>Name</th><th class="right">Size</th></tr></thead>
126
+ <tbody id="fs-list"></tbody>
127
+ </table>
128
+ </div>
129
+ </section>
130
+ </section>
131
+
132
+ <!-- ============ COMPOSE ============ -->
133
+ <section id="tab-compose" class="tabpane hidden">
134
+ <section class="panel">
135
+ <div class="panel-head">
136
+ <h2>docker-compose editor</h2>
137
+ <div class="tools">
138
+ <select id="cmp-file" class="input"></select>
139
+ <button id="cmp-reload" class="ghost" title="Reload">Reload</button>
140
+ </div>
141
+ </div>
142
+ <div class="form-row wrap">
143
+ <button id="cmp-save" class="primary">Save</button>
144
+ <button id="cmp-up" class="ghost">compose up -d</button>
145
+ <button id="cmp-restart" class="ghost">restart</button>
146
+ <button id="cmp-down" class="ghost">down</button>
147
+ <span id="cmp-status" class="muted"></span>
148
+ </div>
149
+ <textarea id="cmp-editor" class="editor" spellcheck="false" style="min-height:52vh"></textarea>
150
+ </section>
151
+ </section>
152
+
153
+ <!-- ============ BACKUPS ============ -->
154
+ <section id="tab-backups" class="tabpane hidden">
155
+ <section class="panel">
156
+ <div class="panel-head"><h2>Create snapshot</h2><span id="bk-dir" class="muted mono"></span></div>
157
+ <div class="form-row">
158
+ <select id="bk-container" class="input"></select>
159
+ <select id="bk-type" class="input">
160
+ <option value="commit">Image snapshot (docker commit)</option>
161
+ <option value="export">Filesystem export (.tar)</option>
162
+ </select>
163
+ <button id="bk-create" class="primary">Snapshot now</button>
164
+ </div>
165
+ </section>
166
+
167
+ <section class="panel">
168
+ <div class="panel-head"><h2>Scheduled backup</h2></div>
169
+ <div class="form-row wrap">
170
+ <label class="chk"><input type="checkbox" id="sc-enabled" /> Enabled</label>
171
+ <span class="muted">at</span>
172
+ <input id="sc-time" class="input" type="time" value="03:00" />
173
+ <select id="sc-type" class="input">
174
+ <option value="commit">commit</option>
175
+ <option value="export">export</option>
176
+ </select>
177
+ <input id="sc-containers" class="input" placeholder="all (or: web,api)" />
178
+ <input id="sc-email" class="input" type="email" placeholder="notify email (via webhook)" />
179
+ <button id="sc-save" class="primary">Save</button>
180
+ </div>
181
+ <div class="pad muted" id="sc-hint"></div>
182
+ </section>
183
+
184
+ <section class="panel">
185
+ <div class="panel-head"><h2>Snapshots</h2><button id="bk-refresh" class="ghost">Refresh</button></div>
186
+ <div class="table-wrap">
187
+ <table class="grid">
188
+ <thead><tr><th>Created</th><th>Container</th><th>Type</th><th>Reference</th><th>Destination</th><th class="right">Size</th><th class="right">Restore</th></tr></thead>
189
+ <tbody id="bk-list"></tbody>
190
+ </table>
191
+ </div>
192
+ </section>
193
+ </section>
194
+
195
+ <!-- ============ SYSTEM ============ -->
196
+ <section id="tab-system" class="tabpane hidden">
197
+ <section class="panel">
198
+ <div class="panel-head"><h2>Networks</h2></div>
199
+ <div class="table-wrap">
200
+ <table class="grid">
201
+ <thead><tr><th>Name</th><th>ID</th><th>Driver</th><th>Scope</th></tr></thead>
202
+ <tbody id="net-list"></tbody>
203
+ </table>
204
+ </div>
205
+ </section>
206
+ <section class="panel">
207
+ <div class="panel-head"><h2>Volumes</h2></div>
208
+ <div class="table-wrap">
209
+ <table class="grid">
210
+ <thead><tr><th>Name</th><th>Driver</th><th>Mountpoint</th></tr></thead>
211
+ <tbody id="vol-list"></tbody>
212
+ </table>
213
+ </div>
214
+ </section>
215
+ </section>
216
+
217
+ <!-- ============ ALERTS ============ -->
218
+ <section id="tab-alerts" class="tabpane hidden">
219
+ <section class="panel">
220
+ <div class="panel-head"><h2>Alerts &amp; log watch</h2><button id="alerts-refresh" class="ghost">Refresh</button></div>
221
+ <div id="alerts-list" class="alerts"></div>
222
+ </section>
223
+ </section>
224
+ </main>
225
+
226
+ <!-- Drawer (logs / files / details / editor) -->
227
+ <div id="drawer" class="drawer hidden" role="dialog" aria-modal="true">
228
+ <div class="drawer-head">
229
+ <h3 id="drawer-title">Logs</h3>
230
+ <button id="drawer-close" class="ghost" aria-label="Close">✕</button>
231
+ </div>
232
+ <div id="drawer-body" class="drawer-body"></div>
233
+ </div>
234
+ <div id="scrim" class="scrim hidden"></div>
235
+
236
+ <footer class="footer">
237
+ <span>Crafted by <a href="https://soyrage.es/" target="_blank" rel="noreferrer">SoyRage Agency</a> · soyrage.es</span>
238
+ <span class="muted">Monitoring: <a href="/metrics" target="_blank" rel="noreferrer">/metrics</a> (Prometheus · Zabbix) · Docker MCP Server</span>
239
+ </footer>
240
+
241
+ <script src="app.js"></script>
242
+ </body>
243
+ </html>
@@ -0,0 +1,428 @@
1
+ /*
2
+ * Docker Panel — SoyRage brand: minimalist cream, ink black, one blue accent.
3
+ * Crafted by SoyRage Agency — https://soyrage.es/
4
+ * Attribution must remain intact (see LICENSE).
5
+ */
6
+
7
+ :root {
8
+ --bg: #f3f1ea;
9
+ --bg-elev: #ffffff;
10
+ --bg-elev-2: #faf9f5;
11
+ --border: #e7e3da;
12
+ --text: #111111;
13
+ --muted: #8b8b86;
14
+ --accent: #3b9ee8;
15
+ --accent-soft: rgba(59, 158, 232, 0.12);
16
+ --green: #3a8f5c;
17
+ --amber: #b8892a;
18
+ --red: #c8524a;
19
+ --grid: rgba(17, 17, 17, 0.035);
20
+ --radius: 16px;
21
+ --mono: "SFMono-Regular", "JetBrains Mono", "Cascadia Code", Consolas, monospace;
22
+ --sans: "Inter", "Segoe UI", -apple-system, system-ui, sans-serif;
23
+ }
24
+
25
+ * { box-sizing: border-box; }
26
+
27
+ /* Custom scrollbars — themed, subtle, consistent across the panel */
28
+ * { scrollbar-width: thin; scrollbar-color: #d8d3c8 transparent; }
29
+ ::-webkit-scrollbar { width: 10px; height: 10px; }
30
+ ::-webkit-scrollbar-track { background: transparent; }
31
+ ::-webkit-scrollbar-thumb {
32
+ background: #d8d3c8; border-radius: 8px;
33
+ border: 2px solid rgba(0, 0, 0, 0.001); background-clip: padding-box;
34
+ }
35
+ ::-webkit-scrollbar-thumb:hover { background: #c7c1b5; }
36
+ ::-webkit-scrollbar-corner { background: transparent; }
37
+
38
+ html, body {
39
+ margin: 0;
40
+ background-color: var(--bg);
41
+ background-image:
42
+ linear-gradient(var(--grid) 1px, transparent 1px),
43
+ linear-gradient(90deg, var(--grid) 1px, transparent 1px);
44
+ background-size: 46px 46px;
45
+ color: var(--text);
46
+ font-family: var(--sans);
47
+ -webkit-font-smoothing: antialiased;
48
+ }
49
+
50
+ a { color: var(--accent); text-decoration: none; }
51
+ a:hover { text-decoration: underline; }
52
+ .hidden { display: none !important; }
53
+ .muted { color: var(--muted); }
54
+ .right { text-align: right; }
55
+ .center { text-align: center; }
56
+ .pad { padding: 22px; }
57
+ .ok { color: var(--green); }
58
+
59
+ /* Tabs */
60
+ .tabs { display: flex; gap: 4px; }
61
+ .tab {
62
+ background: transparent; border: none; color: var(--muted);
63
+ font-size: 13px; font-weight: 600; padding: 8px 14px; border-radius: 9px;
64
+ cursor: pointer; transition: all 0.14s ease; display: inline-flex; gap: 6px; align-items: center;
65
+ }
66
+ .tab:hover { color: var(--text); background: rgba(17,17,17,0.04); }
67
+ .tab.active { color: var(--text); background: var(--accent-soft); }
68
+ .pill {
69
+ font: 700 10px var(--mono); min-width: 16px; text-align: center;
70
+ padding: 1px 5px; border-radius: 999px; background: var(--amber); color: #10131a;
71
+ }
72
+ .pill.crit { background: var(--red); color: #fff; }
73
+
74
+ /* Inputs & tools */
75
+ .tools { display: flex; align-items: center; gap: 10px; }
76
+ .input {
77
+ background: var(--bg-elev-2); border: 1px solid var(--border); color: var(--text);
78
+ border-radius: 9px; padding: 7px 11px; font-size: 13px; font-family: inherit; outline: none;
79
+ }
80
+ .input:focus { border-color: var(--accent); }
81
+ select.input { cursor: pointer; }
82
+ .chk { color: var(--muted); font-size: 12px; display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }
83
+
84
+ /* Auto-restart toggle */
85
+ .toggle {
86
+ width: 30px; height: 30px; border-radius: 8px; cursor: pointer; font-size: 13px;
87
+ border: 1px solid var(--border); background: var(--bg-elev-2); color: var(--muted);
88
+ transition: all 0.14s ease;
89
+ }
90
+ .toggle:hover { color: var(--text); border-color: var(--accent); }
91
+ .toggle.on { color: var(--green); border-color: var(--green); background: rgba(58,208,127,0.14); box-shadow: 0 0 8px rgba(58,208,127,0.25); }
92
+
93
+ /* Breadcrumbs */
94
+ .crumbs { padding: 10px 18px; border-bottom: 1px solid var(--border); font-family: var(--mono); font-size: 12px; color: var(--muted); }
95
+ .crumb { cursor: pointer; color: var(--accent); }
96
+ .crumb:hover { text-decoration: underline; }
97
+ .crumbs .sep { margin: 0 4px; color: var(--muted); }
98
+ .grid td.dir { color: var(--accent); }
99
+
100
+ /* Alerts */
101
+ .alerts { display: flex; flex-direction: column; }
102
+ .alert {
103
+ display: grid; grid-template-columns: 84px 120px 1fr auto; gap: 12px; align-items: center;
104
+ padding: 12px 18px; border-bottom: 1px solid #f0ede5; font-size: 13px;
105
+ }
106
+ .alert:last-child { border-bottom: none; }
107
+ .alert .lvl {
108
+ font: 700 10px var(--mono); text-transform: uppercase; letter-spacing: 0.5px;
109
+ padding: 3px 8px; border-radius: 999px; text-align: center;
110
+ }
111
+ .alert.critical .lvl { background: rgba(240,98,91,0.16); color: var(--red); border: 1px solid rgba(240,98,91,0.4); }
112
+ .alert.warning .lvl { background: rgba(245,185,66,0.14); color: var(--amber); border: 1px solid rgba(245,185,66,0.4); }
113
+ .alert.info .lvl { background: var(--accent-soft); color: var(--accent); border: 1px solid rgba(59,158,240,0.4); }
114
+ .alert .msg { color: var(--text); }
115
+ .alert .when { font-size: 11px; white-space: nowrap; }
116
+
117
+ /* Scroll lock (drawer bugfix) */
118
+ body.locked { overflow: hidden; }
119
+
120
+ /* Inline SVG icons (Lucide) — professional, no emojis */
121
+ .ico { width: 15px; height: 15px; display: inline-block; vertical-align: -3px; flex: none; }
122
+ .ghost .ico, .primary .ico { width: 14px; height: 14px; margin-right: 5px; vertical-align: -2px; }
123
+ .act .ico { width: 15px; height: 15px; }
124
+ .toggle .ico { width: 15px; height: 15px; }
125
+ .fic { width: 34px; color: var(--muted); }
126
+ .grid td.dir + td, .name.dir { color: var(--accent); }
127
+ .prompt { display: inline-flex; align-items: center; }
128
+ .prompt .ico { width: 14px; height: 14px; color: var(--accent); vertical-align: middle; }
129
+
130
+ /* Buttons & forms */
131
+ .primary {
132
+ background: var(--text); color: #fff; border: none;
133
+ border-radius: 999px; padding: 8px 16px; font-size: 13px; font-weight: 600; cursor: pointer;
134
+ }
135
+ .primary:hover { background: #000; }
136
+ .primary:disabled { opacity: 0.5; cursor: not-allowed; }
137
+ .form-row { display: flex; align-items: center; gap: 10px; padding: 16px 18px; }
138
+ .form-row.wrap { flex-wrap: wrap; }
139
+ .tagpill { font: 600 11px var(--mono); padding: 2px 8px; border-radius: 6px; background: var(--accent-soft); color: var(--accent); }
140
+
141
+ /* Terminal */
142
+ .term-panel { display: flex; flex-direction: column; }
143
+ .term-out {
144
+ font-family: var(--mono); font-size: 12.5px; line-height: 1.55; color: #2a2a28;
145
+ padding: 16px 18px; height: 460px; overflow-y: auto; white-space: pre-wrap; word-break: break-word;
146
+ }
147
+ .to-cmd { color: var(--text); margin-top: 8px; }
148
+ .to-cmd .prompt { color: var(--accent); }
149
+ .to-out { color: #55554f; margin: 2px 0 6px; }
150
+ .to-out.err { color: var(--red); }
151
+ .to-out b { color: var(--text); }
152
+ .term-input-wrap { position: relative; display: flex; align-items: center; gap: 8px; border-top: 1px solid var(--border); padding: 12px 16px; }
153
+ .prompt { color: var(--accent); font-family: var(--mono); font-weight: 700; }
154
+ .term-input {
155
+ flex: 1; background: transparent; border: none; outline: none; color: var(--text);
156
+ font-family: var(--mono); font-size: 13px;
157
+ }
158
+ .suggestions {
159
+ position: absolute; left: 34px; right: 16px; bottom: calc(100% - 2px);
160
+ background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 10px;
161
+ overflow: hidden; box-shadow: 0 -12px 32px rgba(17,17,17,0.14); z-index: 8;
162
+ }
163
+ .sugg { display: flex; justify-content: space-between; gap: 16px; padding: 8px 12px; cursor: pointer; }
164
+ .sugg:hover, .sugg.sel { background: var(--accent-soft); }
165
+ .sugg-cmd { color: var(--text); font-size: 12.5px; }
166
+ .sugg-desc { color: var(--muted); font-size: 11.5px; white-space: nowrap; }
167
+
168
+ /* Topbar */
169
+ .topbar {
170
+ position: sticky;
171
+ top: 0;
172
+ z-index: 5;
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: space-between;
176
+ padding: 16px 26px;
177
+ background: rgba(243, 241, 234, 0.85);
178
+ backdrop-filter: blur(10px);
179
+ border-bottom: 1px solid var(--border);
180
+ }
181
+ .brand { display: flex; align-items: center; gap: 12px; }
182
+ .logo {
183
+ font-weight: 800;
184
+ font-size: 20px;
185
+ letter-spacing: 1px;
186
+ color: var(--text);
187
+ }
188
+ .logo .accent { color: var(--accent); }
189
+ .divider { color: var(--muted); font-weight: 300; }
190
+ .product { color: var(--muted); font-weight: 600; letter-spacing: 0.3px; }
191
+ .topbar-right { display: flex; align-items: center; gap: 12px; font-size: 13px; }
192
+ .engine { color: var(--muted); font-family: var(--mono); font-size: 12px; }
193
+ .site { font-family: var(--mono); font-size: 12px; }
194
+
195
+ .badge {
196
+ font: 700 10px var(--mono);
197
+ letter-spacing: 1px;
198
+ padding: 3px 8px;
199
+ border-radius: 999px;
200
+ border: 1px solid transparent;
201
+ }
202
+ .badge-demo { color: var(--amber); background: rgba(245, 185, 66, 0.12); border-color: rgba(245, 185, 66, 0.4); }
203
+ .badge-ro { color: var(--accent); background: var(--accent-soft); border-color: rgba(59, 158, 240, 0.4); }
204
+
205
+ /* Layout */
206
+ .wrap { max-width: 1120px; margin: 0 auto; padding: 26px; position: relative; z-index: 1; }
207
+
208
+ /* Stat cards */
209
+ .stats {
210
+ display: grid;
211
+ grid-template-columns: repeat(5, 1fr);
212
+ gap: 14px;
213
+ margin-bottom: 22px;
214
+ }
215
+ .stat {
216
+ background: #fff;
217
+ border: 1px solid var(--border);
218
+ border-radius: var(--radius);
219
+ padding: 16px 18px;
220
+ }
221
+ .stat:nth-child(5n+1) { background: #dbe8f2; }
222
+ .stat:nth-child(5n+2) { background: #dcebdf; }
223
+ .stat:nth-child(5n+3) { background: #f0ebcf; }
224
+ .stat:nth-child(5n+4) { background: #f1ddd9; }
225
+ .stat:nth-child(5n+5) { background: #e6e6f2; }
226
+ .stat .k { color: rgba(17,17,17,0.55); font-size: 11px; text-transform: uppercase; letter-spacing: 1px; font-weight: 700; }
227
+ .stat .v { font-size: 26px; font-weight: 800; letter-spacing: -0.02em; margin-top: 6px; }
228
+ .stat .v small { font-size: 13px; color: var(--muted); font-weight: 500; }
229
+ .stat .sub { color: var(--muted); font-size: 11px; margin-top: 2px; font-family: var(--mono); }
230
+
231
+ /* Meter bars (stat cards + table usage) */
232
+ .meter {
233
+ height: 5px;
234
+ margin-top: 10px;
235
+ border-radius: 999px;
236
+ background: rgba(17, 17, 17, 0.09);
237
+ overflow: hidden;
238
+ }
239
+ .meter > span {
240
+ display: block;
241
+ height: 100%;
242
+ border-radius: 999px;
243
+ background: linear-gradient(90deg, var(--accent), #6cc0ff);
244
+ transition: width 0.5s ease;
245
+ }
246
+ .usage { min-width: 92px; }
247
+ .usage .meter { margin-top: 6px; height: 4px; }
248
+ .meter.cool > span { background: linear-gradient(90deg, var(--green), #7ee4ad); }
249
+ .meter.warm > span { background: linear-gradient(90deg, var(--amber), #ffd27a); }
250
+ .meter.hot > span { background: linear-gradient(90deg, var(--red), #ff8f89); }
251
+
252
+ /* Trends charts */
253
+ .trend-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--border); }
254
+ .trend { background: var(--bg-elev); padding: 14px 18px 6px; }
255
+ .trend-h { display: flex; align-items: baseline; justify-content: space-between; }
256
+ .trend-h .k { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.8px; }
257
+ .trend-h .v { font-size: 16px; font-weight: 700; }
258
+ .chart { margin-top: 8px; height: 64px; }
259
+ .chart .spark { width: 100%; height: 64px; display: block; }
260
+ @media (max-width: 720px) { .trend-grid { grid-template-columns: 1fr; } }
261
+
262
+ /* Panels */
263
+ .panel {
264
+ background: var(--bg-elev);
265
+ border: 1px solid var(--border);
266
+ border-radius: var(--radius);
267
+ margin-bottom: 22px;
268
+ overflow: hidden;
269
+ }
270
+ .panel-head {
271
+ display: flex; align-items: center; justify-content: space-between;
272
+ padding: 14px 18px;
273
+ border-bottom: 1px solid var(--border);
274
+ }
275
+ .panel-head h2 { margin: 0; font-size: 15px; letter-spacing: 0.3px; }
276
+
277
+ .ghost {
278
+ background: transparent;
279
+ color: var(--muted);
280
+ border: 1px solid var(--border);
281
+ border-radius: 9px;
282
+ padding: 6px 11px;
283
+ font-size: 12px;
284
+ cursor: pointer;
285
+ transition: all 0.15s ease;
286
+ }
287
+ .ghost:hover { color: var(--text); border-color: var(--accent); }
288
+
289
+ /* Tables */
290
+ .table-wrap { overflow-x: auto; }
291
+ .grid { width: 100%; border-collapse: collapse; font-size: 13px; }
292
+ .grid th {
293
+ text-align: left;
294
+ color: var(--muted);
295
+ font-weight: 600;
296
+ font-size: 11px;
297
+ text-transform: uppercase;
298
+ letter-spacing: 0.6px;
299
+ padding: 11px 18px;
300
+ border-bottom: 1px solid var(--border);
301
+ }
302
+ .grid td { padding: 12px 18px; border-bottom: 1px solid #f0ede5; }
303
+ .grid tbody tr { transition: background 0.12s ease; }
304
+ .grid tbody tr:hover { background: rgba(59, 158, 240, 0.05); }
305
+ .grid tbody tr:last-child td { border-bottom: none; }
306
+ .name { font-weight: 600; }
307
+ .mono { font-family: var(--mono); color: var(--muted); font-size: 12px; }
308
+ .clickable { cursor: pointer; }
309
+
310
+ /* State dot */
311
+ .dot { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; color: var(--muted); }
312
+ .dot::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
313
+ .dot.running { color: var(--green); }
314
+ .dot.running::before { background: var(--green); box-shadow: 0 0 8px rgba(58, 208, 127, 0.7); }
315
+ .dot.exited::before, .dot.dead::before { background: var(--red); }
316
+ .dot.paused::before, .dot.created::before { background: var(--amber); }
317
+
318
+ /* Port chips */
319
+ .chips { display: flex; flex-wrap: wrap; gap: 5px; }
320
+ .chip {
321
+ font-family: var(--mono);
322
+ font-size: 11px;
323
+ color: var(--accent);
324
+ background: var(--accent-soft);
325
+ border: 1px solid rgba(59, 158, 240, 0.25);
326
+ padding: 2px 7px;
327
+ border-radius: 6px;
328
+ }
329
+
330
+ /* Action buttons */
331
+ .actions { display: flex; gap: 6px; justify-content: flex-end; }
332
+ .act {
333
+ width: 30px; height: 30px;
334
+ display: inline-grid; place-items: center;
335
+ border-radius: 8px;
336
+ border: 1px solid var(--border);
337
+ background: var(--bg-elev-2);
338
+ color: var(--muted);
339
+ cursor: pointer;
340
+ font-size: 13px;
341
+ transition: all 0.14s ease;
342
+ }
343
+ .act:hover { color: var(--text); border-color: var(--accent); background: var(--accent-soft); }
344
+ .act:disabled { opacity: 0.35; cursor: not-allowed; }
345
+
346
+ /* Logs drawer */
347
+ .scrim { position: fixed; inset: 0; background: rgba(17, 17, 17, 0.35); z-index: 20; }
348
+ .drawer {
349
+ position: fixed;
350
+ top: 0; right: 0; bottom: 0;
351
+ width: min(720px, 92vw);
352
+ background: var(--bg-elev);
353
+ border-left: 1px solid var(--border);
354
+ z-index: 21;
355
+ display: flex;
356
+ flex-direction: column;
357
+ box-shadow: -18px 0 40px rgba(17, 17, 17, 0.14);
358
+ }
359
+ .drawer-head {
360
+ display: flex; align-items: center; justify-content: space-between;
361
+ padding: 16px 18px; border-bottom: 1px solid var(--border);
362
+ }
363
+ .drawer-head h3 { margin: 0; font-size: 14px; font-family: var(--mono); }
364
+ .drawer-body { flex: 1; display: flex; flex-direction: column; min-height: 0; }
365
+ .logs {
366
+ margin: 0; padding: 18px;
367
+ overflow: auto; flex: 1;
368
+ font-family: var(--mono); font-size: 12.5px; line-height: 1.6;
369
+ color: #2a2a28; white-space: pre-wrap;
370
+ }
371
+
372
+ /* File editor */
373
+ .editor-bar {
374
+ display: flex; align-items: center; justify-content: space-between;
375
+ padding: 12px 18px; border-bottom: 1px solid var(--border); gap: 12px;
376
+ }
377
+ .editor-actions { display: flex; gap: 8px; }
378
+ .editor {
379
+ flex: 1; min-height: 260px; width: 100%; resize: none;
380
+ background: #faf9f5; color: #23231f; border: none; outline: none;
381
+ padding: 16px 18px; font-family: var(--mono); font-size: 12.5px; line-height: 1.6;
382
+ tab-size: 2;
383
+ }
384
+ .ed-status { padding: 9px 18px; font-size: 12px; border-top: 1px solid var(--border); }
385
+
386
+ /* AI suggestion in the terminal */
387
+ .sugg.ai { background: rgba(59, 158, 240, 0.10); }
388
+ .sugg.ai .sugg-cmd { color: var(--accent); }
389
+ .sugg.ai .ico { width: 13px; height: 13px; vertical-align: -2px; }
390
+
391
+ /* Footer */
392
+ .footer {
393
+ position: relative; z-index: 1;
394
+ display: flex; align-items: center; justify-content: space-between;
395
+ max-width: 1120px; margin: 0 auto; padding: 20px 26px 40px;
396
+ color: var(--muted); font-size: 12px;
397
+ }
398
+
399
+ /* Update / announcement banner */
400
+ .update-banner {
401
+ position: relative; z-index: 2;
402
+ max-width: 1120px; margin: 16px auto 0; padding: 14px 18px;
403
+ background: var(--bg-elev); border: 1px solid var(--border);
404
+ border-left: 3px solid var(--accent); border-radius: var(--radius);
405
+ box-shadow: 0 1px 2px rgba(17, 17, 17, 0.04);
406
+ display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
407
+ }
408
+ .update-banner.critical { border-left-color: var(--red); }
409
+ .update-banner.announce { border-left-color: var(--amber); }
410
+ .update-banner .ub-main { display: flex; align-items: center; gap: 10px; flex: 1 1 auto; min-width: 0; }
411
+ .update-banner .ub-tag {
412
+ font: 600 10px/1 var(--mono); letter-spacing: 0.08em; text-transform: uppercase;
413
+ padding: 4px 8px; border-radius: 999px; background: var(--accent-soft); color: var(--accent); white-space: nowrap;
414
+ }
415
+ .update-banner.critical .ub-tag { background: rgba(200, 82, 74, 0.12); color: var(--red); }
416
+ .update-banner.announce .ub-tag { background: rgba(184, 137, 42, 0.12); color: var(--amber); }
417
+ .update-banner .ub-text { font-size: 13px; color: var(--text); }
418
+ .update-banner .ub-list {
419
+ flex: 1 1 100%; margin: 0; padding-left: 18px; color: var(--muted); font-size: 12.5px; line-height: 1.6;
420
+ }
421
+ .update-banner .ub-actions { display: flex; gap: 8px; margin-left: auto; }
422
+ .primary.sm, .ghost.sm { padding: 6px 12px; font-size: 12px; }
423
+
424
+ @media (max-width: 900px) {
425
+ .stats { grid-template-columns: repeat(2, 1fr); }
426
+ .update-banner { margin: 12px 12px 0; }
427
+ .update-banner .ub-actions { margin-left: 0; flex: 1 1 100%; }
428
+ }