app.css 24 KB

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