app.css 29 KB

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