app.css 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. :root {
  2. --rail: #1e293b;
  3. --rail-ink: #e2e8f0;
  4. --accent: #0d9488;
  5. }
  6. html, body {
  7. height: 100%;
  8. margin: 0;
  9. overflow: hidden;
  10. background: #f1f5f9;
  11. }
  12. .app {
  13. height: 100%;
  14. width: 100%;
  15. min-height: 100%;
  16. }
  17. .rail {
  18. width: 220px;
  19. background: var(--rail);
  20. color: var(--rail-ink);
  21. display: flex;
  22. flex-direction: column;
  23. height: 100%;
  24. position: sticky;
  25. top: 0;
  26. flex-shrink: 0;
  27. }
  28. .brand-mark {
  29. width: 32px;
  30. height: 32px;
  31. border-radius: 8px;
  32. display: grid;
  33. place-items: center;
  34. font-weight: 800;
  35. color: #083f3b;
  36. background: linear-gradient(145deg, #5eead4, #0f766e);
  37. }
  38. .nav-item {
  39. color: rgba(226, 232, 240, 0.78) !important;
  40. border-radius: 8px;
  41. padding: 0.45rem 0.75rem !important;
  42. border: 0;
  43. background: transparent;
  44. font-size: 0.92rem;
  45. }
  46. .nav-item:hover { background: rgba(255,255,255,.06); color: #fff !important; }
  47. .nav-item.is-active {
  48. background: rgba(13, 148, 136, 0.28) !important;
  49. color: #fff !important;
  50. }
  51. .live .pulse {
  52. width: 8px;
  53. height: 8px;
  54. border-radius: 50%;
  55. background: #94a3b8;
  56. display: inline-block;
  57. }
  58. .live.is-ok .pulse { background: #34d399; box-shadow: 0 0 0 3px rgba(52,211,153,.2); }
  59. .live.is-bad .pulse { background: #f87171; }
  60. .main {
  61. flex: 1 1 auto;
  62. min-width: 0;
  63. width: 100%;
  64. max-width: none;
  65. height: 100%;
  66. padding: 1rem 1.25rem;
  67. display: flex;
  68. flex-direction: column;
  69. overflow: hidden;
  70. }
  71. .top {
  72. flex-shrink: 0;
  73. margin-bottom: 0.85rem !important;
  74. }
  75. .view { display: none; }
  76. .view.is-active {
  77. display: flex;
  78. flex-direction: column;
  79. flex: 1 1 auto;
  80. min-height: 0;
  81. gap: 0.75rem;
  82. overflow: hidden;
  83. }
  84. /* List pages: stretch card + scroll table */
  85. .view.is-active > .card {
  86. flex: 1 1 auto;
  87. min-height: 0;
  88. display: flex;
  89. flex-direction: column;
  90. overflow: hidden;
  91. margin-bottom: 0 !important;
  92. }
  93. .view.is-active > .card > .card-header {
  94. flex-shrink: 0;
  95. }
  96. .view.is-active > .card > .table-responsive {
  97. flex: 1 1 auto;
  98. min-height: 0;
  99. overflow: auto;
  100. }
  101. .view.is-active > .card > .card-body {
  102. flex: 1 1 auto;
  103. min-height: 0;
  104. overflow: auto;
  105. }
  106. /* Console: full width, scroll if needed; cards share space */
  107. #view-console.is-active {
  108. overflow: hidden;
  109. }
  110. #view-console.is-active > .card {
  111. flex: 1 1 auto;
  112. min-height: 0;
  113. display: flex;
  114. flex-direction: column;
  115. overflow: hidden;
  116. }
  117. #view-console .card-body.min-h-0,
  118. .min-h-0 {
  119. min-height: 0;
  120. }
  121. #view-software.is-active {
  122. overflow: auto;
  123. }
  124. #view-software.is-active > .row {
  125. width: 100%;
  126. margin-left: 0;
  127. margin-right: 0;
  128. }
  129. .metrics-range {
  130. width: auto;
  131. min-width: 5.5rem;
  132. font-size: 0.8rem;
  133. padding-top: 0.15rem;
  134. padding-bottom: 0.15rem;
  135. }
  136. .analytics-stat {
  137. background: #f8fafc;
  138. border: 1px solid #e2e8f0;
  139. border-radius: 10px;
  140. padding: 0.7rem 0.85rem;
  141. height: 100%;
  142. }
  143. .analytics-stat .label {
  144. font-size: 0.75rem;
  145. color: #64748b;
  146. margin-bottom: 0.2rem;
  147. }
  148. .analytics-stat .value {
  149. font-size: 1.05rem;
  150. font-weight: 700;
  151. color: #0f172a;
  152. word-break: break-all;
  153. }
  154. .analytics-panel {
  155. border: 1px solid #e2e8f0;
  156. border-radius: 12px;
  157. padding: 0.85rem 1rem;
  158. background: #fff;
  159. height: 100%;
  160. }
  161. .analytics-panel-title {
  162. font-weight: 600;
  163. font-size: 0.9rem;
  164. margin-bottom: 0.65rem;
  165. }
  166. .analytics-chart {
  167. position: relative;
  168. height: 220px;
  169. }
  170. .analytics-chart-sm {
  171. height: 200px;
  172. }
  173. .monitor-card {
  174. height: 100%;
  175. min-height: 220px;
  176. padding: 0.85rem 1rem;
  177. background: #fff;
  178. border: 1px solid #e2e8f0;
  179. border-radius: 12px;
  180. display: flex;
  181. flex-direction: column;
  182. }
  183. .monitor-chart {
  184. position: relative;
  185. flex: 1 1 auto;
  186. min-height: 160px;
  187. }
  188. .monitor-chart canvas {
  189. width: 100% !important;
  190. height: 100% !important;
  191. }
  192. .soft-card .card-body { padding: 0.75rem 0.9rem; }
  193. .soft-card .meta {
  194. font-size: 0.8rem;
  195. color: #64748b;
  196. margin: 0;
  197. padding-left: 1rem;
  198. }
  199. .soft-card .btn { --bs-btn-padding-y: 0.2rem; --bs-btn-padding-x: 0.5rem; --bs-btn-font-size: 0.8rem; }
  200. .table > :not(caption) > * > * {
  201. padding: 0.4rem 0.65rem;
  202. font-size: 0.875rem;
  203. }
  204. .table thead th {
  205. font-size: 0.78rem;
  206. text-transform: uppercase;
  207. letter-spacing: 0.02em;
  208. color: #64748b;
  209. background: #f8fafc;
  210. }
  211. .file-icon {
  212. width: 1.15rem;
  213. display: inline-flex;
  214. justify-content: center;
  215. margin-right: 0.35rem;
  216. color: #64748b;
  217. }
  218. .file-icon.dir { color: #ca8a04; }
  219. .file-icon.code { color: #2563eb; }
  220. .file-icon.web { color: #ea580c; }
  221. .file-icon.img { color: #db2777; }
  222. .file-icon.cfg { color: #0f766e; }
  223. .file-icon.doc { color: #475569; }
  224. .files-name {
  225. cursor: pointer;
  226. color: #0f172a;
  227. text-decoration: none;
  228. }
  229. .files-name:hover { color: var(--accent); text-decoration: underline; }
  230. .crumbs a, .crumbs button {
  231. appearance: none;
  232. border: 0;
  233. background: transparent;
  234. color: var(--accent);
  235. font: inherit;
  236. padding: 0;
  237. cursor: pointer;
  238. }
  239. .crumbs span { color: #94a3b8; margin: 0 0.2rem; }
  240. .toast-host {
  241. z-index: 120;
  242. pointer-events: none;
  243. }
  244. .toast-host .toast-item {
  245. pointer-events: none;
  246. background: #0f172a;
  247. color: #fff;
  248. border-radius: 8px;
  249. padding: 0.55rem 0.85rem;
  250. margin-top: 0.4rem;
  251. font-size: 0.85rem;
  252. box-shadow: 0 8px 24px rgba(15,23,42,.25);
  253. animation: toast-in 0.18s ease;
  254. }
  255. .toast-host .toast-item.ok {
  256. background: #0f766e;
  257. }
  258. .toast-host .toast-item.err { background: #b91c1c; }
  259. @keyframes toast-in {
  260. from { opacity: 0; transform: translateY(6px); }
  261. to { opacity: 1; transform: none; }
  262. }
  263. .task-panel[hidden] { display: none !important; }
  264. .task-panel { position: fixed; inset: 0; z-index: 70; }
  265. .task-panel-backdrop {
  266. position: absolute; inset: 0;
  267. background: rgba(15, 23, 42, 0.45);
  268. }
  269. .task-panel-sheet {
  270. position: absolute; top: 0; left: 0; bottom: 0;
  271. width: min(720px, 100vw);
  272. background: #0f172a; color: #e2e8f0;
  273. display: flex; flex-direction: column;
  274. box-shadow: 16px 0 40px rgba(0,0,0,.35);
  275. }
  276. .task-panel-head {
  277. padding: 0.9rem 1rem;
  278. border-bottom: 1px solid rgba(255,255,255,.08);
  279. }
  280. .task-panel-body {
  281. flex: 1; min-height: 0;
  282. display: grid;
  283. grid-template-columns: 240px 1fr;
  284. }
  285. .task-side {
  286. display: flex;
  287. flex-direction: column;
  288. min-height: 0;
  289. border-right: 1px solid rgba(255,255,255,.08);
  290. }
  291. .task-list {
  292. overflow: auto;
  293. padding: 0.4rem;
  294. flex: 1;
  295. min-height: 0;
  296. }
  297. .task-item {
  298. width: 100%;
  299. appearance: none; border: 0; background: transparent; color: inherit;
  300. text-align: left; padding: 0.55rem 0.65rem; border-radius: 8px;
  301. cursor: pointer; display: grid; gap: 2px; font: inherit; font-size: 0.85rem;
  302. }
  303. .task-item:hover { background: rgba(255,255,255,.05); }
  304. .task-item.is-active { background: rgba(13,148,136,.35); }
  305. .task-item.is-running {
  306. border: 1px solid rgba(251, 191, 36, 0.55);
  307. background: rgba(251, 191, 36, 0.12);
  308. }
  309. .task-item.is-running.is-active {
  310. background: rgba(251, 191, 36, 0.22);
  311. border-color: #fbbf24;
  312. }
  313. .task-item .task-running-dot {
  314. width: 7px; height: 7px; border-radius: 50%;
  315. background: #fbbf24; display: inline-block;
  316. margin-right: 0.35rem;
  317. box-shadow: 0 0 0 3px rgba(251,191,36,.2);
  318. animation: task-pulse 1.2s ease infinite;
  319. }
  320. @keyframes task-pulse {
  321. 0%, 100% { opacity: 1; }
  322. 50% { opacity: 0.45; }
  323. }
  324. .task-side-foot .btn.d-none { display: none !important; }
  325. .task-log-wrap { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
  326. .task-log-head { padding: 0.55rem 0.85rem; border-bottom: 1px solid rgba(255,255,255,.08); }
  327. .task-log {
  328. margin: 0; flex: 1; overflow: auto; padding: 0.75rem;
  329. font: 0.78rem/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  330. white-space: pre-wrap; word-break: break-word; background: #020617; color: #cbd5e1;
  331. }
  332. .code-editor {
  333. position: fixed; inset: 0; z-index: 80;
  334. display: flex; align-items: center; justify-content: center;
  335. padding: 1.25rem;
  336. }
  337. .code-editor[hidden] { display: none !important; }
  338. .code-editor-backdrop {
  339. position: absolute; inset: 0;
  340. background: rgba(15, 23, 42, 0.45);
  341. backdrop-filter: blur(2px);
  342. }
  343. .code-editor-window {
  344. position: relative;
  345. z-index: 1;
  346. width: min(1100px, 92vw);
  347. height: min(720px, 82vh);
  348. display: flex; flex-direction: column;
  349. background: #1e1e1e; color: #d4d4d4;
  350. border-radius: 12px;
  351. overflow: hidden;
  352. box-shadow: 0 24px 80px rgba(0,0,0,.45);
  353. border: 1px solid #333;
  354. }
  355. .code-editor-bar {
  356. display: flex; align-items: center; justify-content: space-between; gap: 12px;
  357. padding: 0.45rem 0.65rem 0.45rem 0.85rem; background: #252526; border-bottom: 1px solid #333; flex-shrink: 0;
  358. }
  359. .code-editor-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
  360. .code-editor-meta span {
  361. font-size: 0.78rem; color: #9ca3af;
  362. overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  363. }
  364. .code-editor-x {
  365. appearance: none; border: 0; background: transparent;
  366. color: #9ca3af; font-size: 1.45rem; line-height: 1;
  367. width: 2rem; height: 2rem; border-radius: 6px; cursor: pointer;
  368. }
  369. .code-editor-x:hover { background: rgba(255,255,255,.08); color: #fff; }
  370. .code-editor-main {
  371. flex: 1; min-height: 0;
  372. display: grid;
  373. grid-template-columns: 220px 1fr;
  374. }
  375. .code-editor-tree {
  376. display: flex; flex-direction: column; min-height: 0;
  377. background: #181818; border-right: 1px solid #333;
  378. }
  379. .code-editor-tree-head {
  380. display: flex; align-items: center; gap: 6px;
  381. padding: 0.4rem 0.5rem; border-bottom: 1px solid #333; flex-shrink: 0;
  382. }
  383. .code-editor-tree-path {
  384. flex: 1; min-width: 0; font-size: 0.75rem; color: #9ca3af;
  385. overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  386. }
  387. .code-editor-tree-body {
  388. flex: 1; overflow: auto; padding: 0.25rem;
  389. font-size: 0.82rem;
  390. }
  391. .editor-tree-item {
  392. width: 100%;
  393. appearance: none; border: 0; background: transparent; color: #d4d4d4;
  394. text-align: left; padding: 0.28rem 0.45rem; border-radius: 4px;
  395. cursor: pointer; display: flex; align-items: center; gap: 0.35rem;
  396. font: inherit; font-size: 0.82rem;
  397. }
  398. .editor-tree-item:hover { background: rgba(255,255,255,.06); }
  399. .editor-tree-item.is-open { background: rgba(13,148,136,.28); }
  400. .code-editor-workspace {
  401. display: flex; flex-direction: column; min-width: 0; min-height: 0;
  402. }
  403. .code-editor-tabs {
  404. display: flex; gap: 2px; overflow-x: auto; flex-shrink: 0;
  405. background: #252526; border-bottom: 1px solid #333; padding: 0.2rem 0.25rem 0;
  406. min-height: 34px;
  407. }
  408. .editor-tab {
  409. appearance: none; border: 0; background: #2d2d2d; color: #ccc;
  410. padding: 0.35rem 0.55rem; border-radius: 6px 6px 0 0;
  411. display: inline-flex; align-items: center; gap: 0.4rem;
  412. cursor: pointer; font: inherit; font-size: 0.8rem; white-space: nowrap;
  413. border: 1px solid transparent; border-bottom: 0;
  414. }
  415. .editor-tab.is-active {
  416. background: #1e1e1e; color: #fff; border-color: #333;
  417. }
  418. .editor-tab.is-dirty .editor-tab-name::after {
  419. content: " •";
  420. color: #fbbf24;
  421. }
  422. .editor-tab-close {
  423. appearance: none; border: 0; background: transparent; color: #999;
  424. padding: 0 2px; line-height: 1; cursor: pointer; border-radius: 3px;
  425. }
  426. .editor-tab-close:hover { background: rgba(255,255,255,.12); color: #fff; }
  427. .monaco-host { flex: 1; min-height: 0; }
  428. .upload-dropzone {
  429. border: 1.5px dashed #cbd5e1;
  430. border-radius: 10px;
  431. background: #f8fafc;
  432. padding: 1.25rem;
  433. text-align: center;
  434. transition: border-color 0.15s ease, background 0.15s ease;
  435. outline: none;
  436. }
  437. .upload-dropzone.is-dragover {
  438. border-color: var(--accent);
  439. background: rgba(13, 148, 136, 0.08);
  440. }
  441. .upload-dropzone-inner { pointer-events: none; }
  442. .upload-dropzone-inner .btn-group { pointer-events: auto; }
  443. .upload-drop-icon {
  444. font-size: 1.75rem;
  445. color: var(--accent);
  446. display: block;
  447. margin-bottom: 0.35rem;
  448. }
  449. .upload-queue {
  450. max-height: 260px;
  451. overflow: auto;
  452. border: 1px solid #e2e8f0;
  453. border-radius: 8px;
  454. background: #fff;
  455. }
  456. .upload-queue:empty {
  457. display: none;
  458. }
  459. .upload-queue-item {
  460. display: grid;
  461. grid-template-columns: 1fr auto auto;
  462. gap: 0.5rem;
  463. align-items: center;
  464. padding: 0.45rem 0.65rem;
  465. border-bottom: 1px solid #f1f5f9;
  466. font-size: 0.82rem;
  467. }
  468. .upload-queue-item:last-child { border-bottom: 0; }
  469. .upload-queue-item .path {
  470. min-width: 0;
  471. overflow: hidden;
  472. text-overflow: ellipsis;
  473. white-space: nowrap;
  474. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  475. }
  476. .upload-queue-item .size { color: #64748b; }
  477. .upload-queue-item .st { min-width: 3.5rem; text-align: right; color: #64748b; }
  478. .upload-queue-item.is-ok .st { color: #0f766e; }
  479. .upload-queue-item.is-err .st { color: #b91c1c; }
  480. .upload-queue-item.is-run .st { color: #0369a1; }
  481. .progress-bar.bg-teal { background-color: var(--accent) !important; }
  482. .site-settings-body { min-height: 420px; }
  483. .site-settings-layout {
  484. display: grid;
  485. grid-template-columns: 168px 1fr;
  486. min-height: 420px;
  487. }
  488. .site-settings-nav {
  489. display: flex;
  490. flex-direction: column;
  491. gap: 2px;
  492. padding: 0.75rem 0.5rem;
  493. background: #f8fafc;
  494. border-right: 1px solid #e2e8f0;
  495. }
  496. .site-settings-tab {
  497. appearance: none; border: 0; background: transparent;
  498. text-align: left; padding: 0.55rem 0.75rem; border-radius: 8px;
  499. font: inherit; font-size: 0.88rem; color: #334155; cursor: pointer;
  500. }
  501. .site-settings-tab:hover { background: #e2e8f0; }
  502. .site-settings-tab.is-active {
  503. background: rgba(13, 148, 136, 0.14);
  504. color: #0f766e;
  505. font-weight: 600;
  506. }
  507. .site-settings-panels { padding: 1rem 1.15rem; min-width: 0; }
  508. .site-settings-panel { display: none; }
  509. .site-settings-panel.is-active { display: block; }
  510. .site-conf-editor {
  511. font-size: 0.8rem;
  512. line-height: 1.45;
  513. resize: vertical;
  514. min-height: 280px;
  515. }
  516. .site-log-view {
  517. margin: 0;
  518. max-height: 360px;
  519. overflow: auto;
  520. padding: 0.75rem;
  521. background: #0f172a;
  522. color: #cbd5e1;
  523. border-radius: 8px;
  524. font: 0.75rem/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  525. white-space: pre-wrap;
  526. word-break: break-word;
  527. }
  528. .site-log-view.table-mode {
  529. padding: 0;
  530. white-space: normal;
  531. font: inherit;
  532. }
  533. .site-log-view.table-mode table {
  534. width: 100%;
  535. margin: 0;
  536. color: #e2e8f0;
  537. font-size: 0.75rem;
  538. border-collapse: collapse;
  539. }
  540. .site-log-view.table-mode th,
  541. .site-log-view.table-mode td {
  542. padding: 0.4rem 0.55rem;
  543. border-bottom: 1px solid #1e293b;
  544. vertical-align: top;
  545. white-space: nowrap;
  546. }
  547. .site-log-view.table-mode th {
  548. position: sticky;
  549. top: 0;
  550. background: #1e293b;
  551. color: #94a3b8;
  552. font-weight: 600;
  553. z-index: 1;
  554. }
  555. .site-log-view.table-mode td.uri {
  556. white-space: normal;
  557. word-break: break-all;
  558. max-width: 220px;
  559. }
  560. .site-log-view.table-mode .status-ok { color: #4ade80; }
  561. .site-log-view.table-mode .status-redir { color: #38bdf8; }
  562. .site-log-view.table-mode .status-client { color: #fbbf24; }
  563. .site-log-view.table-mode .status-server { color: #f87171; }
  564. .site-log-empty {
  565. padding: 1rem;
  566. color: #94a3b8;
  567. }
  568. .sites-link {
  569. color: var(--accent);
  570. text-decoration: none;
  571. cursor: pointer;
  572. }
  573. .sites-link:hover { text-decoration: underline; }
  574. .site-ssl-domain-list {
  575. max-height: 180px;
  576. overflow: auto;
  577. }
  578. .site-ssl-domain-list .form-check {
  579. margin-bottom: 0.35rem;
  580. }
  581. .site-ssl-domain-list .form-check:last-child {
  582. margin-bottom: 0;
  583. }
  584. .site-ssl-domain-list .host {
  585. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  586. font-size: 0.82rem;
  587. }
  588. .site-ssl-domain-list .port-hint {
  589. color: #64748b;
  590. font-size: 0.75rem;
  591. margin-left: 0.35rem;
  592. }
  593. #task-badge .badge { font-size: 0.65rem; }
  594. #task-badge.has-active { border-color: #fbbf24; }
  595. @media (max-width: 900px) {
  596. html, body {
  597. height: auto;
  598. overflow: auto;
  599. }
  600. .app {
  601. flex-direction: column;
  602. height: auto;
  603. min-height: 100vh;
  604. }
  605. .rail {
  606. width: 100%;
  607. height: auto;
  608. position: relative;
  609. }
  610. .nav { flex-direction: row; flex-wrap: wrap; }
  611. .main {
  612. height: auto;
  613. overflow: visible;
  614. }
  615. .view.is-active {
  616. overflow: visible;
  617. height: auto;
  618. }
  619. .view.is-active > .card {
  620. flex: none;
  621. overflow: visible;
  622. }
  623. .view.is-active > .card > .table-responsive,
  624. .view.is-active > .card > .card-body {
  625. overflow: visible;
  626. }
  627. .task-panel-body { grid-template-columns: 1fr; }
  628. .task-side { border-right: 0; border-bottom: 1px solid rgba(255,255,255,.08); max-height: 220px; }
  629. }