app.css 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  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. .nav-logout {
  52. color: rgba(248, 113, 113, 0.92) !important;
  53. }
  54. .nav-logout:hover {
  55. background: rgba(248, 113, 113, 0.14) !important;
  56. color: #fecaca !important;
  57. }
  58. .rail-foot {
  59. margin-top: auto;
  60. }
  61. .self-update .btn {
  62. font-size: 0.75rem;
  63. padding: 0.2rem 0.45rem;
  64. }
  65. #self-version.is-update {
  66. color: #fbbf24 !important;
  67. }
  68. .live .pulse {
  69. width: 8px;
  70. height: 8px;
  71. border-radius: 50%;
  72. background: #94a3b8;
  73. display: inline-block;
  74. }
  75. .live.is-ok .pulse { background: #34d399; box-shadow: 0 0 0 3px rgba(52,211,153,.2); }
  76. .live.is-bad .pulse { background: #f87171; }
  77. .main {
  78. flex: 1 1 auto;
  79. min-width: 0;
  80. width: 100%;
  81. max-width: none;
  82. height: 100%;
  83. padding: 1rem 1.25rem;
  84. display: flex;
  85. flex-direction: column;
  86. overflow: hidden;
  87. }
  88. .top {
  89. flex-shrink: 0;
  90. margin-bottom: 0.85rem !important;
  91. }
  92. .view { display: none; }
  93. .view.is-active {
  94. display: flex;
  95. flex-direction: column;
  96. flex: 1 1 auto;
  97. min-height: 0;
  98. gap: 0.75rem;
  99. overflow: hidden;
  100. }
  101. /* List pages: stretch card + scroll table */
  102. .view.is-active > .card {
  103. flex: 1 1 auto;
  104. min-height: 0;
  105. display: flex;
  106. flex-direction: column;
  107. overflow: hidden;
  108. margin-bottom: 0 !important;
  109. }
  110. .view.is-active > .card > .card-header {
  111. flex-shrink: 0;
  112. }
  113. .view.is-active > .card > .table-responsive {
  114. flex: 1 1 auto;
  115. min-height: 0;
  116. overflow: auto;
  117. }
  118. .view.is-active > .card > .card-body {
  119. flex: 1 1 auto;
  120. min-height: 0;
  121. overflow: auto;
  122. }
  123. /* Console: full width, scroll if needed; cards share space */
  124. #view-console.is-active {
  125. overflow: hidden;
  126. }
  127. #view-console.is-active > .card {
  128. flex: 1 1 auto;
  129. min-height: 0;
  130. display: flex;
  131. flex-direction: column;
  132. overflow: hidden;
  133. }
  134. #view-console .card-body.min-h-0,
  135. .min-h-0 {
  136. min-height: 0;
  137. }
  138. #view-software.is-active {
  139. overflow: auto;
  140. }
  141. #view-software.is-active > .row {
  142. width: 100%;
  143. margin-left: 0;
  144. margin-right: 0;
  145. }
  146. #view-processes.is-active {
  147. overflow: hidden;
  148. display: flex;
  149. flex-direction: column;
  150. min-height: 0;
  151. }
  152. #view-processes.is-active > .card {
  153. flex: 1 1 auto;
  154. min-height: 0;
  155. display: flex;
  156. flex-direction: column;
  157. overflow: hidden;
  158. }
  159. #view-processes .proc-table-wrap {
  160. flex: 1 1 auto;
  161. min-height: 0;
  162. overflow: auto;
  163. }
  164. #view-processes .proc-toolbar {
  165. min-width: 0;
  166. }
  167. #view-processes .proc-toolbar-actions {
  168. min-width: 0;
  169. flex: 1 1 auto;
  170. justify-content: flex-end;
  171. }
  172. #view-processes #proc-search {
  173. width: 16rem;
  174. max-width: 100%;
  175. min-width: 8rem;
  176. flex: 1 1 12rem;
  177. }
  178. #view-processes .proc-toolbar-actions .form-check,
  179. #view-processes #btn-proc-refresh {
  180. flex: 0 0 auto;
  181. }
  182. #view-shell.is-active {
  183. overflow: hidden;
  184. display: flex;
  185. flex-direction: column;
  186. min-height: 0;
  187. }
  188. #view-shell.is-active > .shell-card {
  189. flex: 1 1 auto;
  190. min-height: 0;
  191. display: flex;
  192. flex-direction: column;
  193. overflow: hidden;
  194. }
  195. #view-shell .shell-body {
  196. flex: 1 1 auto;
  197. min-height: 0;
  198. overflow: hidden;
  199. background: #0b1220;
  200. }
  201. #view-shell .shell-terminal {
  202. width: 100%;
  203. height: 100%;
  204. padding: 0.5rem 0.65rem;
  205. box-sizing: border-box;
  206. }
  207. #view-shell .shell-terminal .xterm {
  208. height: 100%;
  209. }
  210. #view-shell .shell-terminal .xterm-viewport {
  211. overflow-y: auto !important;
  212. }
  213. #shell-status.is-online {
  214. color: #0d9488 !important;
  215. }
  216. #shell-status.is-offline {
  217. color: #94a3b8 !important;
  218. }
  219. #shell-status.is-error {
  220. color: #dc2626 !important;
  221. }
  222. #proc-table th.proc-sort {
  223. cursor: pointer;
  224. user-select: none;
  225. white-space: nowrap;
  226. }
  227. #proc-table th.proc-sort.is-active {
  228. color: #0d6efd;
  229. }
  230. #proc-table th.proc-sort.is-active::after {
  231. content: "";
  232. display: inline-block;
  233. margin-left: 0.25rem;
  234. border: 4px solid transparent;
  235. }
  236. #proc-table th.proc-sort.is-active[data-sort-dir="asc"]::after {
  237. border-bottom-color: currentColor;
  238. transform: translateY(-2px);
  239. }
  240. #proc-table th.proc-sort.is-active[data-sort-dir="desc"]::after {
  241. border-top-color: currentColor;
  242. transform: translateY(2px);
  243. }
  244. #proc-table td.proc-cmd,
  245. #proc-table td.proc-cwd {
  246. max-width: 20rem;
  247. overflow: hidden;
  248. text-overflow: ellipsis;
  249. white-space: nowrap;
  250. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  251. font-size: 0.78rem;
  252. }
  253. #proc-table td.proc-name {
  254. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  255. font-size: 0.8rem;
  256. white-space: nowrap;
  257. }
  258. .metrics-range {
  259. width: auto;
  260. min-width: 5.5rem;
  261. font-size: 0.8rem;
  262. padding-top: 0.15rem;
  263. padding-bottom: 0.15rem;
  264. }
  265. .analytics-stat {
  266. background: #f8fafc;
  267. border: 1px solid #e2e8f0;
  268. border-radius: 10px;
  269. padding: 0.7rem 0.85rem;
  270. height: 100%;
  271. }
  272. .analytics-stat .label {
  273. font-size: 0.75rem;
  274. color: #64748b;
  275. margin-bottom: 0.2rem;
  276. }
  277. .analytics-stat .value {
  278. font-size: 1.05rem;
  279. font-weight: 700;
  280. color: #0f172a;
  281. word-break: break-all;
  282. }
  283. .analytics-panel {
  284. border: 1px solid #e2e8f0;
  285. border-radius: 12px;
  286. padding: 0.85rem 1rem;
  287. background: #fff;
  288. }
  289. /* Only stretch panels that share a row, so full-width blocks don't leave tall empty gaps */
  290. .row > [class*="col"] > .analytics-panel {
  291. height: 100%;
  292. }
  293. .analytics-panel-title {
  294. font-weight: 600;
  295. font-size: 0.9rem;
  296. margin-bottom: 0.65rem;
  297. display: flex;
  298. align-items: center;
  299. justify-content: space-between;
  300. gap: 0.5rem;
  301. flex-wrap: wrap;
  302. }
  303. .analytics-panel-title .form-check {
  304. font-weight: 400;
  305. min-height: 0;
  306. }
  307. .analytics-panel-title .form-check-label {
  308. color: #64748b;
  309. font-size: 0.78rem;
  310. user-select: none;
  311. }
  312. .analytics-chart {
  313. position: relative;
  314. height: 220px;
  315. }
  316. .analytics-chart-sm {
  317. height: 200px;
  318. }
  319. .monitor-card {
  320. height: 100%;
  321. min-height: 220px;
  322. padding: 0.85rem 1rem;
  323. background: #fff;
  324. border: 1px solid #e2e8f0;
  325. border-radius: 12px;
  326. display: flex;
  327. flex-direction: column;
  328. }
  329. .monitor-chart {
  330. position: relative;
  331. flex: 1 1 auto;
  332. min-height: 160px;
  333. }
  334. .monitor-chart canvas {
  335. width: 100% !important;
  336. height: 100% !important;
  337. }
  338. .gpu-split {
  339. display: flex;
  340. gap: 0.75rem;
  341. flex: 1 1 auto;
  342. min-height: 160px;
  343. }
  344. .gpu-pane {
  345. flex: 1 1 0;
  346. min-width: 0;
  347. display: flex;
  348. flex-direction: column;
  349. }
  350. .gpu-pane .monitor-chart {
  351. min-height: 140px;
  352. }
  353. .monitor-badge-stack {
  354. line-height: 1.35;
  355. font-size: 0.75rem;
  356. }
  357. .resource-list {
  358. margin-top: 0.65rem;
  359. display: flex;
  360. flex-direction: column;
  361. gap: 0.45rem;
  362. }
  363. .resource-row .resource-meta {
  364. display: flex;
  365. justify-content: space-between;
  366. gap: 0.5rem;
  367. font-size: 0.75rem;
  368. color: #64748b;
  369. }
  370. .resource-row .resource-name {
  371. color: #334155;
  372. font-weight: 600;
  373. overflow: hidden;
  374. text-overflow: ellipsis;
  375. white-space: nowrap;
  376. }
  377. .resource-meter {
  378. height: 6px;
  379. margin-top: 0.2rem;
  380. background: #e2e8f0;
  381. border-radius: 99px;
  382. overflow: hidden;
  383. }
  384. .resource-meter > span {
  385. display: block;
  386. height: 100%;
  387. border-radius: inherit;
  388. background: #d97706;
  389. }
  390. .resource-meter.is-gpu > span {
  391. background: #7c3aed;
  392. }
  393. .soft-card .card-body { padding: 0.75rem 0.9rem; }
  394. .soft-card .meta {
  395. font-size: 0.8rem;
  396. color: #64748b;
  397. margin: 0;
  398. padding-left: 1rem;
  399. }
  400. .soft-card .btn { --bs-btn-padding-y: 0.2rem; --bs-btn-padding-x: 0.5rem; --bs-btn-font-size: 0.8rem; }
  401. .table > :not(caption) > * > * {
  402. padding: 0.4rem 0.65rem;
  403. font-size: 0.875rem;
  404. }
  405. .table thead th {
  406. font-size: 0.78rem;
  407. text-transform: uppercase;
  408. letter-spacing: 0.02em;
  409. color: #64748b;
  410. background: #f8fafc;
  411. }
  412. .file-icon {
  413. width: 1.15rem;
  414. display: inline-flex;
  415. justify-content: center;
  416. margin-right: 0.35rem;
  417. color: #64748b;
  418. }
  419. .file-icon.dir { color: #ca8a04; }
  420. .file-icon.code { color: #2563eb; }
  421. .file-icon.web { color: #ea580c; }
  422. .file-icon.img { color: #db2777; }
  423. .file-icon.cfg { color: #0f766e; }
  424. .file-icon.doc { color: #475569; }
  425. .files-name {
  426. cursor: pointer;
  427. color: #0f172a;
  428. text-decoration: none;
  429. }
  430. .files-name:hover { color: var(--accent); text-decoration: underline; }
  431. .files-check-col {
  432. width: 2rem;
  433. text-align: center;
  434. vertical-align: middle;
  435. }
  436. #files-table tbody tr.is-selected {
  437. background: rgba(37, 99, 235, 0.08);
  438. }
  439. #files-table tbody tr.is-selected:hover {
  440. background: rgba(37, 99, 235, 0.12);
  441. }
  442. #files-bulk-actions .disabled {
  443. pointer-events: none;
  444. }
  445. .crumbs a, .crumbs button {
  446. appearance: none;
  447. border: 0;
  448. background: transparent;
  449. color: var(--accent);
  450. font: inherit;
  451. padding: 0;
  452. cursor: pointer;
  453. }
  454. .crumbs span { color: #94a3b8; margin: 0 0.2rem; }
  455. .toast-host {
  456. z-index: 120;
  457. pointer-events: none;
  458. }
  459. .toast-host .toast-item {
  460. pointer-events: none;
  461. background: #0f172a;
  462. color: #fff;
  463. border-radius: 8px;
  464. padding: 0.55rem 0.85rem;
  465. margin-top: 0.4rem;
  466. font-size: 0.85rem;
  467. box-shadow: 0 8px 24px rgba(15,23,42,.25);
  468. animation: toast-in 0.18s ease;
  469. }
  470. .toast-host .toast-item.ok {
  471. background: #0f766e;
  472. }
  473. .toast-host .toast-item.err { background: #b91c1c; }
  474. @keyframes toast-in {
  475. from { opacity: 0; transform: translateY(6px); }
  476. to { opacity: 1; transform: none; }
  477. }
  478. .alert-dialog-msg {
  479. white-space: pre-wrap;
  480. word-break: break-word;
  481. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  482. font-size: 0.8125rem;
  483. line-height: 1.45;
  484. max-height: min(50vh, 22rem);
  485. overflow: auto;
  486. margin: 0;
  487. color: #991b1b;
  488. background: #fef2f2;
  489. border: 1px solid #fecaca;
  490. border-radius: 0.5rem;
  491. padding: 0.75rem 0.9rem;
  492. }
  493. /* Nested dialogs (e.g. URL proxy form over site settings) */
  494. #modal.modal {
  495. z-index: 2010;
  496. }
  497. .modal-backdrop.modal-backdrop-nested {
  498. z-index: 2000;
  499. }
  500. .task-panel[hidden] { display: none !important; }
  501. .task-panel {
  502. position: fixed; inset: 0; z-index: 70;
  503. display: flex; align-items: center; justify-content: center;
  504. padding: 1.25rem;
  505. }
  506. .task-panel-backdrop {
  507. position: absolute; inset: 0;
  508. background: rgba(15, 23, 42, 0.5);
  509. backdrop-filter: blur(2px);
  510. }
  511. .task-panel-dialog {
  512. position: relative;
  513. z-index: 1;
  514. width: min(920px, 94vw);
  515. height: min(640px, 84vh);
  516. min-width: 480px;
  517. min-height: 320px;
  518. max-width: calc(100vw - 2.5rem);
  519. max-height: calc(100vh - 2.5rem);
  520. background: #0f172a; color: #e2e8f0;
  521. display: flex; flex-direction: column;
  522. border-radius: 12px;
  523. overflow: hidden;
  524. border: 1px solid rgba(255,255,255,.1);
  525. box-shadow: 0 24px 80px rgba(0,0,0,.45);
  526. animation: task-dialog-in .18s ease-out;
  527. }
  528. .task-panel-dialog.is-resizing {
  529. animation: none;
  530. user-select: none;
  531. }
  532. @keyframes task-dialog-in {
  533. from { opacity: 0; transform: translateY(10px) scale(.98); }
  534. to { opacity: 1; transform: none; }
  535. }
  536. /* Shared resize handle for all built-in dialogs */
  537. .dialog-resize {
  538. position: absolute;
  539. right: 0; bottom: 0;
  540. width: 18px; height: 18px;
  541. cursor: nwse-resize;
  542. z-index: 5;
  543. }
  544. .dialog-resize::before {
  545. content: "";
  546. position: absolute;
  547. right: 4px; bottom: 4px;
  548. width: 10px; height: 10px;
  549. border-right: 2px solid rgba(148, 163, 184, 0.85);
  550. border-bottom: 2px solid rgba(148, 163, 184, 0.85);
  551. border-radius: 1px;
  552. }
  553. .dialog-resize:hover::before {
  554. border-color: #0d9488;
  555. }
  556. .modal-content .dialog-resize::before {
  557. border-color: rgba(100, 116, 139, 0.9);
  558. }
  559. .modal-content .dialog-resize:hover::before {
  560. border-color: #0d9488;
  561. }
  562. .is-resizable-dialog {
  563. position: relative;
  564. }
  565. .is-resizable-dialog.is-resizing {
  566. user-select: none;
  567. }
  568. .modal-dialog.is-resizable-dialog.has-custom-size {
  569. max-width: none;
  570. margin-left: auto;
  571. margin-right: auto;
  572. display: flex;
  573. flex-direction: column;
  574. }
  575. .modal-dialog.is-resizable-dialog.has-custom-size .modal-content {
  576. width: 100%;
  577. height: 100%;
  578. display: flex;
  579. flex-direction: column;
  580. overflow: hidden;
  581. position: relative;
  582. }
  583. .modal-dialog.is-resizable-dialog.has-custom-size .modal-header,
  584. .modal-dialog.is-resizable-dialog.has-custom-size .modal-footer {
  585. flex-shrink: 0;
  586. }
  587. .modal-dialog.is-resizable-dialog.has-custom-size .modal-body {
  588. flex: 1 1 auto;
  589. min-height: 0;
  590. overflow: auto;
  591. }
  592. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-body {
  593. min-height: 0;
  594. display: flex;
  595. flex-direction: column;
  596. overflow: hidden;
  597. }
  598. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-layout {
  599. flex: 1;
  600. min-height: 0;
  601. height: 100%;
  602. overflow: hidden;
  603. }
  604. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-panels {
  605. display: flex;
  606. flex-direction: column;
  607. min-height: 0;
  608. overflow: hidden;
  609. }
  610. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-panel.is-active {
  611. flex: 1 1 auto;
  612. min-height: 0;
  613. overflow: auto;
  614. }
  615. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-panel.is-active[data-site-panel="logs"] {
  616. display: flex;
  617. flex-direction: column;
  618. overflow: hidden;
  619. }
  620. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-panel.is-active[data-site-panel="logs"] .site-log-toolbar {
  621. flex: 0 0 auto;
  622. }
  623. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-panel.is-active[data-site-panel="logs"] .site-log-view {
  624. flex: 1 1 auto;
  625. min-height: 0;
  626. max-height: none;
  627. height: auto;
  628. }
  629. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-panel.is-active[data-site-panel="nginx"] {
  630. display: flex;
  631. flex-direction: column;
  632. overflow: hidden;
  633. }
  634. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-panel.is-active[data-site-panel="nginx"] .site-nginx-toolbar,
  635. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-panel.is-active[data-site-panel="nginx"] .site-nginx-actions {
  636. flex: 0 0 auto;
  637. }
  638. .modal-dialog.is-resizable-dialog.has-custom-size .site-settings-panel.is-active[data-site-panel="nginx"] .site-nginx-monaco {
  639. flex: 1 1 auto;
  640. min-height: 0;
  641. height: auto;
  642. }
  643. .task-panel-head {
  644. padding: 0.9rem 1rem;
  645. border-bottom: 1px solid rgba(255,255,255,.08);
  646. flex-shrink: 0;
  647. }
  648. .task-panel-body {
  649. flex: 1; min-height: 0;
  650. display: grid;
  651. grid-template-columns: 240px 1fr;
  652. }
  653. .task-side {
  654. display: flex;
  655. flex-direction: column;
  656. min-height: 0;
  657. border-right: 1px solid rgba(255,255,255,.08);
  658. }
  659. .task-list {
  660. overflow: auto;
  661. padding: 0.4rem;
  662. flex: 1;
  663. min-height: 0;
  664. }
  665. .task-item {
  666. width: 100%;
  667. appearance: none; border: 0; background: transparent; color: inherit;
  668. text-align: left; padding: 0.55rem 0.65rem; border-radius: 8px;
  669. cursor: pointer; display: grid; gap: 2px; font: inherit; font-size: 0.85rem;
  670. }
  671. .task-item:hover { background: rgba(255,255,255,.05); }
  672. .task-item.is-active { background: rgba(13,148,136,.35); }
  673. .task-item.is-running {
  674. border: 1px solid rgba(251, 191, 36, 0.55);
  675. background: rgba(251, 191, 36, 0.12);
  676. }
  677. .task-item.is-running.is-active {
  678. background: rgba(251, 191, 36, 0.22);
  679. border-color: #fbbf24;
  680. }
  681. .task-item .task-running-dot {
  682. width: 7px; height: 7px; border-radius: 50%;
  683. background: #fbbf24; display: inline-block;
  684. margin-right: 0.35rem;
  685. box-shadow: 0 0 0 3px rgba(251,191,36,.2);
  686. animation: task-pulse 1.2s ease infinite;
  687. }
  688. @keyframes task-pulse {
  689. 0%, 100% { opacity: 1; }
  690. 50% { opacity: 0.45; }
  691. }
  692. .task-side-foot .btn.d-none { display: none !important; }
  693. .task-log-wrap { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
  694. .task-log-head { padding: 0.55rem 0.85rem; border-bottom: 1px solid rgba(255,255,255,.08); }
  695. .task-log {
  696. margin: 0; flex: 1; overflow: auto; padding: 0.75rem;
  697. font: 0.78rem/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  698. white-space: pre-wrap; word-break: break-word; background: #020617; color: #cbd5e1;
  699. }
  700. .code-editor {
  701. position: fixed; inset: 0; z-index: 80;
  702. display: flex; align-items: center; justify-content: center;
  703. padding: 1.25rem;
  704. }
  705. .code-editor[hidden] { display: none !important; }
  706. .code-editor-backdrop {
  707. position: absolute; inset: 0;
  708. background: rgba(15, 23, 42, 0.45);
  709. backdrop-filter: blur(2px);
  710. }
  711. .code-editor-window {
  712. position: relative;
  713. z-index: 1;
  714. width: min(1100px, 92vw);
  715. height: min(720px, 82vh);
  716. min-width: 560px;
  717. min-height: 360px;
  718. max-width: calc(100vw - 2.5rem);
  719. max-height: calc(100vh - 2.5rem);
  720. display: flex; flex-direction: column;
  721. background: #1e1e1e; color: #d4d4d4;
  722. border-radius: 12px;
  723. overflow: hidden;
  724. box-shadow: 0 24px 80px rgba(0,0,0,.45);
  725. border: 1px solid #333;
  726. }
  727. .code-editor-bar {
  728. display: flex; align-items: center; justify-content: space-between; gap: 12px;
  729. padding: 0.45rem 0.65rem 0.45rem 0.85rem; background: #252526; border-bottom: 1px solid #333; flex-shrink: 0;
  730. }
  731. .code-editor-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
  732. .code-editor-meta span {
  733. font-size: 0.78rem; color: #9ca3af;
  734. overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  735. }
  736. .code-editor-x {
  737. appearance: none; border: 0; background: transparent;
  738. color: #9ca3af; font-size: 1.45rem; line-height: 1;
  739. width: 2rem; height: 2rem; border-radius: 6px; cursor: pointer;
  740. }
  741. .code-editor-x:hover { background: rgba(255,255,255,.08); color: #fff; }
  742. .code-editor-main {
  743. flex: 1; min-height: 0;
  744. display: grid;
  745. grid-template-columns: 220px 1fr;
  746. }
  747. .code-editor-tree {
  748. display: flex; flex-direction: column; min-height: 0;
  749. background: #181818; border-right: 1px solid #333;
  750. }
  751. .code-editor-tree-head {
  752. display: flex; align-items: center; gap: 6px;
  753. padding: 0.4rem 0.5rem; border-bottom: 1px solid #333; flex-shrink: 0;
  754. }
  755. .code-editor-tree-path {
  756. flex: 1; min-width: 0; font-size: 0.75rem; color: #9ca3af;
  757. overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  758. }
  759. .code-editor-tree-body {
  760. flex: 1; overflow: auto; padding: 0.25rem;
  761. font-size: 0.82rem;
  762. }
  763. .editor-tree-item {
  764. width: 100%;
  765. appearance: none; border: 0; background: transparent; color: #d4d4d4;
  766. text-align: left; padding: 0.28rem 0.45rem; border-radius: 4px;
  767. cursor: pointer; display: flex; align-items: center; gap: 0.35rem;
  768. font: inherit; font-size: 0.82rem;
  769. }
  770. .editor-tree-item:hover { background: rgba(255,255,255,.06); }
  771. .editor-tree-item.is-open { background: rgba(13,148,136,.28); }
  772. .code-editor-workspace {
  773. display: flex; flex-direction: column; min-width: 0; min-height: 0;
  774. }
  775. .code-editor-tabs {
  776. display: flex; gap: 2px; overflow-x: auto; flex-shrink: 0;
  777. background: #252526; border-bottom: 1px solid #333; padding: 0.2rem 0.25rem 0;
  778. min-height: 34px;
  779. }
  780. .editor-tab {
  781. appearance: none; border: 0; background: #2d2d2d; color: #ccc;
  782. padding: 0.35rem 0.55rem; border-radius: 6px 6px 0 0;
  783. display: inline-flex; align-items: center; gap: 0.4rem;
  784. cursor: pointer; font: inherit; font-size: 0.8rem; white-space: nowrap;
  785. border: 1px solid transparent; border-bottom: 0;
  786. }
  787. .editor-tab.is-active {
  788. background: #1e1e1e; color: #fff; border-color: #333;
  789. }
  790. .editor-tab.is-dirty .editor-tab-name::after {
  791. content: " •";
  792. color: #fbbf24;
  793. }
  794. .editor-tab-close {
  795. appearance: none; border: 0; background: transparent; color: #999;
  796. padding: 0 2px; line-height: 1; cursor: pointer; border-radius: 3px;
  797. }
  798. .editor-tab-close:hover { background: rgba(255,255,255,.12); color: #fff; }
  799. .monaco-host { flex: 1; min-height: 0; }
  800. .upload-dropzone {
  801. border: 1.5px dashed #cbd5e1;
  802. border-radius: 10px;
  803. background: #f8fafc;
  804. padding: 1.25rem;
  805. text-align: center;
  806. transition: border-color 0.15s ease, background 0.15s ease;
  807. outline: none;
  808. }
  809. .upload-dropzone.is-dragover {
  810. border-color: var(--accent);
  811. background: rgba(13, 148, 136, 0.08);
  812. }
  813. .upload-dropzone-inner { pointer-events: none; }
  814. .upload-dropzone-inner .btn-group { pointer-events: auto; }
  815. .upload-drop-icon {
  816. font-size: 1.75rem;
  817. color: var(--accent);
  818. display: block;
  819. margin-bottom: 0.35rem;
  820. }
  821. .upload-queue {
  822. max-height: 260px;
  823. overflow: auto;
  824. border: 1px solid #e2e8f0;
  825. border-radius: 8px;
  826. background: #fff;
  827. }
  828. .upload-queue:empty {
  829. display: none;
  830. }
  831. .upload-queue-item {
  832. display: grid;
  833. grid-template-columns: 1fr auto auto;
  834. gap: 0.5rem;
  835. align-items: center;
  836. padding: 0.45rem 0.65rem;
  837. border-bottom: 1px solid #f1f5f9;
  838. font-size: 0.82rem;
  839. }
  840. .upload-queue-item:last-child { border-bottom: 0; }
  841. .upload-queue-item .path {
  842. min-width: 0;
  843. overflow: hidden;
  844. text-overflow: ellipsis;
  845. white-space: nowrap;
  846. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  847. }
  848. .upload-queue-item .size { color: #64748b; }
  849. .upload-queue-item .st { min-width: 3.5rem; text-align: right; color: #64748b; }
  850. .upload-queue-item.is-ok .st { color: #0f766e; }
  851. .upload-queue-item.is-err .st { color: #b91c1c; }
  852. .upload-queue-item.is-run .st { color: #0369a1; }
  853. .progress-bar.bg-teal { background-color: var(--accent) !important; }
  854. .site-modules-list {
  855. display: grid;
  856. gap: 0.65rem;
  857. }
  858. .site-module-card {
  859. display: grid;
  860. grid-template-columns: 1fr auto;
  861. gap: 0.75rem;
  862. align-items: start;
  863. padding: 0.75rem 0.85rem;
  864. border: 1px solid #e2e8f0;
  865. border-radius: 10px;
  866. background: #fff;
  867. }
  868. .site-module-card .meta { min-width: 0; }
  869. .site-module-card .name {
  870. font-weight: 600;
  871. font-size: 0.95rem;
  872. }
  873. .site-module-card .name-en {
  874. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  875. font-size: 0.78rem;
  876. color: #64748b;
  877. }
  878. .site-module-card .desc {
  879. margin-top: 0.25rem;
  880. font-size: 0.82rem;
  881. color: #475569;
  882. }
  883. .site-module-card .actions {
  884. display: flex;
  885. flex-direction: column;
  886. gap: 0.35rem;
  887. align-items: stretch;
  888. min-width: 5.5rem;
  889. }
  890. .site-settings-layout {
  891. display: grid;
  892. grid-template-columns: 168px 1fr;
  893. min-height: 420px;
  894. }
  895. .site-settings-nav {
  896. display: flex;
  897. flex-direction: column;
  898. gap: 2px;
  899. padding: 0.75rem 0.5rem;
  900. background: #f8fafc;
  901. border-right: 1px solid #e2e8f0;
  902. min-height: 0;
  903. overflow: auto;
  904. }
  905. .site-settings-tab {
  906. appearance: none; border: 0; background: transparent;
  907. text-align: left; padding: 0.55rem 0.75rem; border-radius: 8px;
  908. font: inherit; font-size: 0.88rem; color: #334155; cursor: pointer;
  909. }
  910. .site-settings-tab:hover { background: #e2e8f0; }
  911. .site-settings-tab.is-active {
  912. background: rgba(13, 148, 136, 0.14);
  913. color: #0f766e;
  914. font-weight: 600;
  915. }
  916. .site-settings-panels { padding: 1rem 1.15rem; min-width: 0; min-height: 0; }
  917. .site-settings-panel { display: none; }
  918. .site-settings-panel.is-active { display: block; }
  919. .site-nginx-monaco {
  920. min-height: 280px;
  921. height: min(420px, 50vh);
  922. border: 1px solid #1e293b;
  923. border-radius: 8px;
  924. overflow: hidden;
  925. background: #1e1e1e;
  926. }
  927. .soft-cfg-monaco {
  928. min-height: 320px;
  929. height: min(460px, 55vh);
  930. border: 1px solid #1e293b;
  931. border-radius: 8px;
  932. overflow: hidden;
  933. background: #1e1e1e;
  934. }
  935. .site-log-view {
  936. margin: 0;
  937. max-height: min(360px, 50vh);
  938. overflow: auto;
  939. padding: 0.75rem;
  940. background: #0f172a;
  941. color: #cbd5e1;
  942. border-radius: 8px;
  943. font: 0.75rem/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  944. white-space: pre-wrap;
  945. word-break: break-word;
  946. }
  947. .site-log-view.table-mode {
  948. padding: 0;
  949. white-space: normal;
  950. font: inherit;
  951. }
  952. .site-log-view.table-mode table {
  953. width: 100%;
  954. margin: 0;
  955. color: #e2e8f0;
  956. font-size: 0.75rem;
  957. border-collapse: collapse;
  958. table-layout: auto;
  959. }
  960. .site-log-view.table-mode th,
  961. .site-log-view.table-mode td {
  962. padding: 0.4rem 0.55rem;
  963. border-bottom: 1px solid #1e293b;
  964. vertical-align: top;
  965. white-space: nowrap;
  966. }
  967. .site-log-view.table-mode th {
  968. position: sticky;
  969. top: 0;
  970. background: #1e293b;
  971. color: #94a3b8;
  972. font-weight: 600;
  973. z-index: 1;
  974. }
  975. .site-log-view.table-mode td.uri {
  976. white-space: normal;
  977. word-break: break-all;
  978. max-width: clamp(12rem, 32vw, 40rem);
  979. }
  980. .site-log-view.table-mode td.body {
  981. white-space: pre-wrap;
  982. word-break: break-all;
  983. max-width: clamp(10rem, 24vw, 28rem);
  984. color: #94a3b8;
  985. font: 0.7rem/1.35 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  986. }
  987. .site-log-view.table-mode .status-ok { color: #4ade80; }
  988. .site-log-view.table-mode .status-redir { color: #38bdf8; }
  989. .site-log-view.table-mode .status-client { color: #fbbf24; }
  990. .site-log-view.table-mode .status-server { color: #f87171; }
  991. .site-log-empty {
  992. padding: 1rem;
  993. color: #94a3b8;
  994. }
  995. .analytics-ip-link {
  996. color: #0d9488;
  997. cursor: pointer;
  998. text-decoration: underline;
  999. text-underline-offset: 2px;
  1000. }
  1001. .analytics-ip-link:hover {
  1002. color: #0f766e;
  1003. }
  1004. .analytics-self-ip {
  1005. display: inline-block;
  1006. margin-left: 0.35rem;
  1007. padding: 0.05rem 0.4rem;
  1008. border-radius: 4px;
  1009. font-size: 0.7rem;
  1010. font-weight: 600;
  1011. line-height: 1.3;
  1012. color: #0f766e;
  1013. background: #ccfbf1;
  1014. vertical-align: middle;
  1015. }
  1016. .analytics-self-row td {
  1017. background: rgba(13, 148, 136, 0.06);
  1018. }
  1019. .site-log-ip-filter {
  1020. display: inline-flex;
  1021. align-items: center;
  1022. gap: 0.15rem;
  1023. font-weight: 500;
  1024. color: #334155;
  1025. }
  1026. .site-log-search {
  1027. width: min(260px, 100%);
  1028. min-width: 160px;
  1029. }
  1030. .site-log-ip-filter .btn-close {
  1031. width: 0.55em;
  1032. height: 0.55em;
  1033. padding: 0.15em;
  1034. opacity: 0.55;
  1035. }
  1036. .site-log-ip-filter .btn-close:hover {
  1037. opacity: 0.9;
  1038. }
  1039. .sites-link {
  1040. color: var(--accent);
  1041. text-decoration: none;
  1042. cursor: pointer;
  1043. }
  1044. .sites-link:hover { text-decoration: underline; }
  1045. .sites-group-chips .sites-group-chip {
  1046. --bs-btn-padding-y: 0.15rem;
  1047. --bs-btn-padding-x: 0.55rem;
  1048. --bs-btn-font-size: 0.78rem;
  1049. }
  1050. .site-ssl-pem {
  1051. font-size: 0.78rem;
  1052. line-height: 1.4;
  1053. resize: vertical;
  1054. min-height: 140px;
  1055. }
  1056. .site-custom-nginx {
  1057. font-size: 0.8rem;
  1058. line-height: 1.45;
  1059. resize: vertical;
  1060. min-height: 220px;
  1061. }
  1062. .site-ssl-domain-list {
  1063. max-height: 180px;
  1064. overflow: auto;
  1065. }
  1066. .site-ssl-domain-list .form-check {
  1067. margin-bottom: 0.35rem;
  1068. }
  1069. .site-ssl-domain-list .form-check:last-child {
  1070. margin-bottom: 0;
  1071. }
  1072. .site-ssl-domain-list .host {
  1073. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  1074. font-size: 0.82rem;
  1075. }
  1076. .site-ssl-domain-list .port-hint {
  1077. color: #64748b;
  1078. font-size: 0.75rem;
  1079. margin-left: 0.35rem;
  1080. }
  1081. #task-badge .badge { font-size: 0.65rem; }
  1082. #task-badge.has-active { border-color: #fbbf24; }
  1083. @media (max-width: 900px) {
  1084. html, body {
  1085. height: auto;
  1086. overflow: auto;
  1087. }
  1088. .app {
  1089. flex-direction: column;
  1090. height: auto;
  1091. min-height: 100vh;
  1092. }
  1093. .rail {
  1094. width: 100%;
  1095. height: auto;
  1096. position: relative;
  1097. }
  1098. .nav { flex-direction: row; flex-wrap: wrap; }
  1099. .main {
  1100. height: auto;
  1101. overflow: visible;
  1102. }
  1103. .view.is-active {
  1104. overflow: visible;
  1105. height: auto;
  1106. }
  1107. .view.is-active > .card {
  1108. flex: none;
  1109. overflow: visible;
  1110. }
  1111. .view.is-active > .card > .table-responsive,
  1112. .view.is-active > .card > .card-body {
  1113. overflow: visible;
  1114. }
  1115. .task-panel { padding: 0.75rem; }
  1116. .task-panel-dialog { height: min(720px, 90vh); }
  1117. .task-panel-body { grid-template-columns: 1fr; }
  1118. .task-side { border-right: 0; border-bottom: 1px solid rgba(255,255,255,.08); max-height: 220px; }
  1119. }
  1120. .site-modules-list {
  1121. display: grid;
  1122. gap: 0.65rem;
  1123. }
  1124. .site-module-card {
  1125. display: grid;
  1126. grid-template-columns: 1fr auto;
  1127. gap: 0.75rem;
  1128. align-items: start;
  1129. padding: 0.75rem 0.85rem;
  1130. border: 1px solid #e2e8f0;
  1131. border-radius: 10px;
  1132. background: #fff;
  1133. }
  1134. .site-module-card .meta { min-width: 0; }
  1135. .site-module-card .name {
  1136. font-weight: 600;
  1137. font-size: 0.95rem;
  1138. }
  1139. .site-module-card .name-en {
  1140. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  1141. font-size: 0.78rem;
  1142. color: #64748b;
  1143. }
  1144. .site-module-card .desc {
  1145. margin-top: 0.25rem;
  1146. font-size: 0.82rem;
  1147. color: #475569;
  1148. }
  1149. .site-module-card .actions {
  1150. display: flex;
  1151. flex-direction: column;
  1152. gap: 0.35rem;
  1153. align-items: stretch;
  1154. min-width: 5.5rem;
  1155. }
  1156. /* Plans / schedule */
  1157. .plans-cron-layout { min-height: 420px; }
  1158. .schedule-log-view {
  1159. max-height: 520px;
  1160. overflow: auto;
  1161. padding: 0.75rem 1rem;
  1162. font: 0.75rem/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  1163. white-space: pre-wrap;
  1164. word-break: break-word;
  1165. background: #0f172a;
  1166. color: #cbd5e1;
  1167. }
  1168. .schedule-table tr.is-selected { background: rgba(13, 110, 253, 0.08); }
  1169. .schedule-url {
  1170. max-width: 220px;
  1171. overflow: hidden;
  1172. text-overflow: ellipsis;
  1173. white-space: nowrap;
  1174. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  1175. font-size: 0.78rem;
  1176. }
  1177. .sql-editor {
  1178. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  1179. font-size: 0.85rem;
  1180. min-height: 8rem;
  1181. resize: vertical;
  1182. }
  1183. .sql-result-wrap {
  1184. max-height: 28rem;
  1185. overflow: auto;
  1186. }
  1187. #sql-result th,
  1188. #sql-result td {
  1189. white-space: nowrap;
  1190. font-size: 0.8rem;
  1191. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  1192. }